mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-29 07:18:51 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
81
drivers/iio/Kconfig
Normal file
81
drivers/iio/Kconfig
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#
|
||||
# Industrial I/O subsystem configuration
|
||||
#
|
||||
|
||||
menuconfig IIO
|
||||
tristate "Industrial I/O support"
|
||||
select ANON_INODES
|
||||
help
|
||||
The industrial I/O subsystem provides a unified framework for
|
||||
drivers for many different types of embedded sensors using a
|
||||
number of different physical interfaces (i2c, spi, etc).
|
||||
|
||||
if IIO
|
||||
|
||||
config IIO_BUFFER
|
||||
bool "Enable buffer support within IIO"
|
||||
help
|
||||
Provide core support for various buffer based data
|
||||
acquisition methods.
|
||||
|
||||
if IIO_BUFFER
|
||||
|
||||
config IIO_BUFFER_CB
|
||||
boolean "IIO callback buffer used for push in-kernel interfaces"
|
||||
help
|
||||
Should be selected by any drivers that do in-kernel push
|
||||
usage. That is, those where the data is pushed to the consumer.
|
||||
|
||||
config IIO_KFIFO_BUF
|
||||
select IIO_TRIGGER
|
||||
tristate "Industrial I/O buffering based on kfifo"
|
||||
help
|
||||
A simple fifo based on kfifo. Note that this currently provides
|
||||
no buffer events so it is up to userspace to work out how
|
||||
often to read from the buffer.
|
||||
|
||||
config IIO_TRIGGERED_BUFFER
|
||||
tristate
|
||||
select IIO_TRIGGER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Provides helper functions for setting up triggered buffers.
|
||||
|
||||
endif # IIO_BUFFER
|
||||
|
||||
config IIO_TRIGGER
|
||||
boolean "Enable triggered sampling support"
|
||||
help
|
||||
Provides IIO core support for triggers. Currently these
|
||||
are used to initialize capture of samples to push into
|
||||
buffers. The triggers are effectively a 'capture
|
||||
data now' interrupt.
|
||||
|
||||
config IIO_CONSUMERS_PER_TRIGGER
|
||||
int "Maximum number of consumers per trigger"
|
||||
depends on IIO_TRIGGER
|
||||
default "2"
|
||||
help
|
||||
This value controls the maximum number of consumers that a
|
||||
given trigger may handle. Default is 2.
|
||||
|
||||
source "drivers/iio/accel/Kconfig"
|
||||
source "drivers/iio/adc/Kconfig"
|
||||
source "drivers/iio/amplifiers/Kconfig"
|
||||
source "drivers/iio/common/Kconfig"
|
||||
source "drivers/iio/dac/Kconfig"
|
||||
source "drivers/iio/frequency/Kconfig"
|
||||
source "drivers/iio/gyro/Kconfig"
|
||||
source "drivers/iio/humidity/Kconfig"
|
||||
source "drivers/iio/imu/Kconfig"
|
||||
source "drivers/iio/light/Kconfig"
|
||||
source "drivers/iio/magnetometer/Kconfig"
|
||||
source "drivers/iio/orientation/Kconfig"
|
||||
if IIO_TRIGGER
|
||||
source "drivers/iio/trigger/Kconfig"
|
||||
endif #IIO_TRIGGER
|
||||
source "drivers/iio/pressure/Kconfig"
|
||||
source "drivers/iio/proximity/Kconfig"
|
||||
source "drivers/iio/temperature/Kconfig"
|
||||
|
||||
endif # IIO
|
||||
29
drivers/iio/Makefile
Normal file
29
drivers/iio/Makefile
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#
|
||||
# Makefile for the industrial I/O core.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO) += industrialio.o
|
||||
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
|
||||
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
|
||||
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
|
||||
industrialio-$(CONFIG_IIO_BUFFER_CB) += buffer_cb.o
|
||||
|
||||
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
|
||||
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
|
||||
|
||||
obj-y += accel/
|
||||
obj-y += adc/
|
||||
obj-y += amplifiers/
|
||||
obj-y += common/
|
||||
obj-y += dac/
|
||||
obj-y += gyro/
|
||||
obj-y += frequency/
|
||||
obj-y += humidity/
|
||||
obj-y += imu/
|
||||
obj-y += light/
|
||||
obj-y += magnetometer/
|
||||
obj-y += orientation/
|
||||
obj-y += pressure/
|
||||
obj-y += proximity/
|
||||
obj-y += temperature/
|
||||
obj-y += trigger/
|
||||
108
drivers/iio/accel/Kconfig
Normal file
108
drivers/iio/accel/Kconfig
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
#
|
||||
# Accelerometer drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Accelerometers"
|
||||
|
||||
config BMA180
|
||||
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here if you want to build a driver for the Bosch BMA180 or
|
||||
BMA250 triaxial acceleration sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called bma180.
|
||||
|
||||
config BMC150_ACCEL
|
||||
tristate "Bosch BMC150 Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the following Bosch accelerometers:
|
||||
BMC150, BMI055, BMA250E, BMA222E, BMA255, BMA280.
|
||||
|
||||
Currently this only supports the device via an i2c interface.
|
||||
|
||||
This is a combo module with both accelerometer and magnetometer.
|
||||
This driver is only implementing accelerometer part, which has
|
||||
its own address and register map.
|
||||
|
||||
config HID_SENSOR_ACCEL_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Accelerometers 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
accelerometers 3D.
|
||||
|
||||
config IIO_ST_ACCEL_3AXIS
|
||||
tristate "STMicroelectronics accelerometers 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_ACCEL_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_ACCEL_SPI_3AXIS if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics accelerometers:
|
||||
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
|
||||
LIS331DLH, LSM303DL, LSM303DLM, LSM330.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
- st_accel (core functions for the driver [it is mandatory]);
|
||||
- st_accel_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_accel_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_ACCEL_I2C_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_ACCEL_SPI_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_ACCEL_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config KXSD9
|
||||
tristate "Kionix KXSD9 Accelerometer Driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for the Kionix KXSD9 accelerometer.
|
||||
Currently this only supports the device via an SPI interface.
|
||||
|
||||
config MMA8452
|
||||
tristate "Freescale MMA8452Q Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Freescale MMA8452Q 3-axis
|
||||
accelerometer.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mma8452.
|
||||
|
||||
config KXCJK1013
|
||||
tristate "Kionix 3-Axis Accelerometer Driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say Y here if you want to build a driver for the Kionix KXCJK-1013
|
||||
triaxial acceleration sensor. This driver also supports KXCJ9-1008
|
||||
and KXTJ2-1009.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called kxcjk-1013.
|
||||
|
||||
endmenu
|
||||
18
drivers/iio/accel/Makefile
Normal file
18
drivers/iio/accel/Makefile
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Makefile for industrial I/O accelerometer drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_BMA180) += bma180.o
|
||||
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel.o
|
||||
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
|
||||
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
|
||||
obj-$(CONFIG_KXSD9) += kxsd9.o
|
||||
obj-$(CONFIG_MMA8452) += mma8452.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
|
||||
st_accel-y := st_accel_core.o
|
||||
st_accel-$(CONFIG_IIO_BUFFER) += st_accel_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_I2C_3AXIS) += st_accel_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_ACCEL_SPI_3AXIS) += st_accel_spi.o
|
||||
862
drivers/iio/accel/bma180.c
Normal file
862
drivers/iio/accel/bma180.c
Normal file
|
|
@ -0,0 +1,862 @@
|
|||
/*
|
||||
* bma180.c - IIO driver for Bosch BMA180 triaxial acceleration sensor
|
||||
*
|
||||
* Copyright 2013 Oleksandr Kravchenko <x0199363@ti.com>
|
||||
*
|
||||
* Support for BMA250 (c) Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* SPI is not supported by driver
|
||||
* BMA180: 7-bit I2C slave address 0x40 or 0x41
|
||||
* BMA250: 7-bit I2C slave address 0x18 or 0x19
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define BMA180_DRV_NAME "bma180"
|
||||
#define BMA180_IRQ_NAME "bma180_event"
|
||||
|
||||
enum {
|
||||
BMA180,
|
||||
BMA250,
|
||||
};
|
||||
|
||||
struct bma180_data;
|
||||
|
||||
struct bma180_part_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned num_channels;
|
||||
const int *scale_table;
|
||||
unsigned num_scales;
|
||||
const int *bw_table;
|
||||
unsigned num_bw;
|
||||
|
||||
u8 int_reset_reg, int_reset_mask;
|
||||
u8 sleep_reg, sleep_mask;
|
||||
u8 bw_reg, bw_mask;
|
||||
u8 scale_reg, scale_mask;
|
||||
u8 power_reg, power_mask, lowpower_val;
|
||||
u8 int_enable_reg, int_enable_mask;
|
||||
u8 softreset_reg;
|
||||
|
||||
int (*chip_config)(struct bma180_data *data);
|
||||
void (*chip_disable)(struct bma180_data *data);
|
||||
};
|
||||
|
||||
/* Register set */
|
||||
#define BMA180_CHIP_ID 0x00 /* Need to distinguish BMA180 from other */
|
||||
#define BMA180_ACC_X_LSB 0x02 /* First of 6 registers of accel data */
|
||||
#define BMA180_TEMP 0x08
|
||||
#define BMA180_CTRL_REG0 0x0d
|
||||
#define BMA180_RESET 0x10
|
||||
#define BMA180_BW_TCS 0x20
|
||||
#define BMA180_CTRL_REG3 0x21
|
||||
#define BMA180_TCO_Z 0x30
|
||||
#define BMA180_OFFSET_LSB1 0x35
|
||||
|
||||
/* BMA180_CTRL_REG0 bits */
|
||||
#define BMA180_DIS_WAKE_UP BIT(0) /* Disable wake up mode */
|
||||
#define BMA180_SLEEP BIT(1) /* 1 - chip will sleep */
|
||||
#define BMA180_EE_W BIT(4) /* Unlock writing to addr from 0x20 */
|
||||
#define BMA180_RESET_INT BIT(6) /* Reset pending interrupts */
|
||||
|
||||
/* BMA180_CTRL_REG3 bits */
|
||||
#define BMA180_NEW_DATA_INT BIT(1) /* Intr every new accel data is ready */
|
||||
|
||||
/* BMA180_OFFSET_LSB1 skipping mode bit */
|
||||
#define BMA180_SMP_SKIP BIT(0)
|
||||
|
||||
/* Bit masks for registers bit fields */
|
||||
#define BMA180_RANGE 0x0e /* Range of measured accel values */
|
||||
#define BMA180_BW 0xf0 /* Accel bandwidth */
|
||||
#define BMA180_MODE_CONFIG 0x03 /* Config operation modes */
|
||||
|
||||
/* We have to write this value in reset register to do soft reset */
|
||||
#define BMA180_RESET_VAL 0xb6
|
||||
|
||||
#define BMA180_ID_REG_VAL 0x03
|
||||
|
||||
/* Chip power modes */
|
||||
#define BMA180_LOW_POWER 0x03
|
||||
|
||||
#define BMA250_RANGE_REG 0x0f
|
||||
#define BMA250_BW_REG 0x10
|
||||
#define BMA250_POWER_REG 0x11
|
||||
#define BMA250_RESET_REG 0x14
|
||||
#define BMA250_INT_ENABLE_REG 0x17
|
||||
#define BMA250_INT_MAP_REG 0x1a
|
||||
#define BMA250_INT_RESET_REG 0x21
|
||||
|
||||
#define BMA250_RANGE_MASK GENMASK(3, 0) /* Range of accel values */
|
||||
#define BMA250_BW_MASK GENMASK(4, 0) /* Accel bandwidth */
|
||||
#define BMA250_SUSPEND_MASK BIT(7) /* chip will sleep */
|
||||
#define BMA250_LOWPOWER_MASK BIT(6)
|
||||
#define BMA250_DATA_INTEN_MASK BIT(4)
|
||||
#define BMA250_INT1_DATA_MASK BIT(0)
|
||||
#define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */
|
||||
|
||||
struct bma180_data {
|
||||
struct i2c_client *client;
|
||||
struct iio_trigger *trig;
|
||||
const struct bma180_part_info *part_info;
|
||||
struct mutex mutex;
|
||||
bool sleep_state;
|
||||
int scale;
|
||||
int bw;
|
||||
bool pmode;
|
||||
u8 buff[16]; /* 3x 16-bit + 8-bit + padding + timestamp */
|
||||
};
|
||||
|
||||
enum bma180_chan {
|
||||
AXIS_X,
|
||||
AXIS_Y,
|
||||
AXIS_Z,
|
||||
TEMP
|
||||
};
|
||||
|
||||
static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */
|
||||
static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 };
|
||||
|
||||
static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */
|
||||
static int bma250_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0,
|
||||
0, 0, 306458 };
|
||||
|
||||
static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (data->sleep_state)
|
||||
return -EBUSY;
|
||||
|
||||
switch (chan) {
|
||||
case TEMP:
|
||||
ret = i2c_smbus_read_byte_data(data->client, BMA180_TEMP);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev, "failed to read temp register\n");
|
||||
break;
|
||||
default:
|
||||
ret = i2c_smbus_read_word_data(data->client,
|
||||
BMA180_ACC_X_LSB + chan * 2);
|
||||
if (ret < 0)
|
||||
dev_err(&data->client->dev,
|
||||
"failed to read accel_%c register\n",
|
||||
'x' + chan);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_set_bits(struct bma180_data *data, u8 reg, u8 mask, u8 val)
|
||||
{
|
||||
int ret = i2c_smbus_read_byte_data(data->client, reg);
|
||||
u8 reg_val = (ret & ~mask) | (val << (ffs(mask) - 1));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return i2c_smbus_write_byte_data(data->client, reg, reg_val);
|
||||
}
|
||||
|
||||
static int bma180_reset_intr(struct bma180_data *data)
|
||||
{
|
||||
int ret = bma180_set_bits(data, data->part_info->int_reset_reg,
|
||||
data->part_info->int_reset_mask, 1);
|
||||
|
||||
if (ret)
|
||||
dev_err(&data->client->dev, "failed to reset interrupt\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_set_new_data_intr_state(struct bma180_data *data, bool state)
|
||||
{
|
||||
int ret = bma180_set_bits(data, data->part_info->int_enable_reg,
|
||||
data->part_info->int_enable_mask, state);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_reset_intr(data);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set new data interrupt state %d\n", state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_set_sleep_state(struct bma180_data *data, bool state)
|
||||
{
|
||||
int ret = bma180_set_bits(data, data->part_info->sleep_reg,
|
||||
data->part_info->sleep_mask, state);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set sleep state %d\n", state);
|
||||
return ret;
|
||||
}
|
||||
data->sleep_state = state;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma180_set_ee_writing_state(struct bma180_data *data, bool state)
|
||||
{
|
||||
int ret = bma180_set_bits(data, BMA180_CTRL_REG0, BMA180_EE_W, state);
|
||||
|
||||
if (ret)
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set ee writing state %d\n", state);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_set_bw(struct bma180_data *data, int val)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (data->sleep_state)
|
||||
return -EBUSY;
|
||||
|
||||
for (i = 0; i < data->part_info->num_bw; ++i) {
|
||||
if (data->part_info->bw_table[i] == val) {
|
||||
ret = bma180_set_bits(data, data->part_info->bw_reg,
|
||||
data->part_info->bw_mask, i);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set bandwidth\n");
|
||||
return ret;
|
||||
}
|
||||
data->bw = val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bma180_set_scale(struct bma180_data *data, int val)
|
||||
{
|
||||
int ret, i;
|
||||
|
||||
if (data->sleep_state)
|
||||
return -EBUSY;
|
||||
|
||||
for (i = 0; i < data->part_info->num_scales; ++i)
|
||||
if (data->part_info->scale_table[i] == val) {
|
||||
ret = bma180_set_bits(data, data->part_info->scale_reg,
|
||||
data->part_info->scale_mask, i);
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev,
|
||||
"failed to set scale\n");
|
||||
return ret;
|
||||
}
|
||||
data->scale = val;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int bma180_set_pmode(struct bma180_data *data, bool mode)
|
||||
{
|
||||
u8 reg_val = mode ? data->part_info->lowpower_val : 0;
|
||||
int ret = bma180_set_bits(data, data->part_info->power_reg,
|
||||
data->part_info->power_mask, reg_val);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&data->client->dev, "failed to set power mode\n");
|
||||
return ret;
|
||||
}
|
||||
data->pmode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bma180_soft_reset(struct bma180_data *data)
|
||||
{
|
||||
int ret = i2c_smbus_write_byte_data(data->client,
|
||||
data->part_info->softreset_reg, BMA180_RESET_VAL);
|
||||
|
||||
if (ret)
|
||||
dev_err(&data->client->dev, "failed to reset the chip\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_chip_init(struct bma180_data *data)
|
||||
{
|
||||
/* Try to read chip_id register. It must return 0x03. */
|
||||
int ret = i2c_smbus_read_byte_data(data->client, BMA180_CHIP_ID);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != BMA180_ID_REG_VAL)
|
||||
return -ENODEV;
|
||||
|
||||
ret = bma180_soft_reset(data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* No serial transaction should occur within minimum 10 us
|
||||
* after soft_reset command
|
||||
*/
|
||||
msleep(20);
|
||||
|
||||
ret = bma180_set_new_data_intr_state(data, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return bma180_set_pmode(data, false);
|
||||
}
|
||||
|
||||
static int bma180_chip_config(struct bma180_data *data)
|
||||
{
|
||||
int ret = bma180_chip_init(data);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bits(data, BMA180_CTRL_REG0, BMA180_DIS_WAKE_UP, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_ee_writing_state(data, true);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bits(data, BMA180_OFFSET_LSB1, BMA180_SMP_SKIP, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bw(data, 20); /* 20 Hz */
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_scale(data, 2452); /* 2 G */
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(&data->client->dev, "failed to config the chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma250_chip_config(struct bma180_data *data)
|
||||
{
|
||||
int ret = bma180_chip_init(data);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bw(data, 16); /* 16 Hz */
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_scale(data, 38344); /* 2 G */
|
||||
if (ret)
|
||||
goto err;
|
||||
ret = bma180_set_bits(data, BMA250_INT_MAP_REG,
|
||||
BMA250_INT1_DATA_MASK, 1);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
dev_err(&data->client->dev, "failed to config the chip\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bma180_chip_disable(struct bma180_data *data)
|
||||
{
|
||||
if (bma180_set_new_data_intr_state(data, false))
|
||||
goto err;
|
||||
if (bma180_set_ee_writing_state(data, false))
|
||||
goto err;
|
||||
if (bma180_set_sleep_state(data, true))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
dev_err(&data->client->dev, "failed to disable the chip\n");
|
||||
}
|
||||
|
||||
static void bma250_chip_disable(struct bma180_data *data)
|
||||
{
|
||||
if (bma180_set_new_data_intr_state(data, false))
|
||||
goto err;
|
||||
if (bma180_set_sleep_state(data, true))
|
||||
goto err;
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
dev_err(&data->client->dev, "failed to disable the chip\n");
|
||||
}
|
||||
|
||||
static ssize_t bma180_show_avail(char *buf, const int *vals, unsigned n,
|
||||
bool micros)
|
||||
{
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!vals[i])
|
||||
continue;
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
micros ? "0.%06d " : "%d ", vals[i]);
|
||||
}
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t bma180_show_filter_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return bma180_show_avail(buf, data->part_info->bw_table,
|
||||
data->part_info->num_bw, false);
|
||||
}
|
||||
|
||||
static ssize_t bma180_show_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
return bma180_show_avail(buf, data->part_info->scale_table,
|
||||
data->part_info->num_scales, true);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
|
||||
S_IRUGO, bma180_show_filter_freq_avail, NULL, 0);
|
||||
|
||||
static IIO_DEVICE_ATTR(in_accel_scale_available,
|
||||
S_IRUGO, bma180_show_scale_avail, NULL, 0);
|
||||
|
||||
static struct attribute *bma180_attributes[] = {
|
||||
&iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.
|
||||
dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group bma180_attrs_group = {
|
||||
.attrs = bma180_attributes,
|
||||
};
|
||||
|
||||
static int bma180_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&data->mutex);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&data->mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = bma180_get_data_reg(data, chan->scan_index);
|
||||
mutex_unlock(&data->mutex);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret >> chan->scan_type.shift,
|
||||
chan->scan_type.realbits - 1);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
*val = data->bw;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ACCEL:
|
||||
*val = 0;
|
||||
*val2 = data->scale;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 500;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 48; /* 0 LSB @ 24 degree C */
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int bma180_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma180_set_scale(data, val2);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma180_set_bw(data, val);
|
||||
mutex_unlock(&data->mutex);
|
||||
return ret;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info bma180_info = {
|
||||
.attrs = &bma180_attrs_group,
|
||||
.read_raw = bma180_read_raw,
|
||||
.write_raw = bma180_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const bma180_power_modes[] = { "low_noise", "low_power" };
|
||||
|
||||
static int bma180_get_power_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->pmode;
|
||||
}
|
||||
|
||||
static int bma180_set_power_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma180_set_pmode(data, mode);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum bma180_power_mode_enum = {
|
||||
.items = bma180_power_modes,
|
||||
.num_items = ARRAY_SIZE(bma180_power_modes),
|
||||
.get = bma180_get_power_mode,
|
||||
.set = bma180_set_power_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info bma180_ext_info[] = {
|
||||
IIO_ENUM("power_mode", true, &bma180_power_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define BMA180_ACC_CHANNEL(_axis, _bits) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##_axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
|
||||
.scan_index = AXIS_##_axis, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = _bits, \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - _bits, \
|
||||
}, \
|
||||
.ext_info = bma180_ext_info, \
|
||||
}
|
||||
|
||||
#define BMA180_TEMP_CHANNEL { \
|
||||
.type = IIO_TEMP, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = TEMP, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 8, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec bma180_channels[] = {
|
||||
BMA180_ACC_CHANNEL(X, 14),
|
||||
BMA180_ACC_CHANNEL(Y, 14),
|
||||
BMA180_ACC_CHANNEL(Z, 14),
|
||||
BMA180_TEMP_CHANNEL,
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec bma250_channels[] = {
|
||||
BMA180_ACC_CHANNEL(X, 10),
|
||||
BMA180_ACC_CHANNEL(Y, 10),
|
||||
BMA180_ACC_CHANNEL(Z, 10),
|
||||
BMA180_TEMP_CHANNEL,
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
};
|
||||
|
||||
static const struct bma180_part_info bma180_part_info[] = {
|
||||
[BMA180] = {
|
||||
bma180_channels, ARRAY_SIZE(bma180_channels),
|
||||
bma180_scale_table, ARRAY_SIZE(bma180_scale_table),
|
||||
bma180_bw_table, ARRAY_SIZE(bma180_bw_table),
|
||||
BMA180_CTRL_REG0, BMA180_RESET_INT,
|
||||
BMA180_CTRL_REG0, BMA180_SLEEP,
|
||||
BMA180_BW_TCS, BMA180_BW,
|
||||
BMA180_OFFSET_LSB1, BMA180_RANGE,
|
||||
BMA180_TCO_Z, BMA180_MODE_CONFIG, BMA180_LOW_POWER,
|
||||
BMA180_CTRL_REG3, BMA180_NEW_DATA_INT,
|
||||
BMA180_RESET,
|
||||
bma180_chip_config,
|
||||
bma180_chip_disable,
|
||||
},
|
||||
[BMA250] = {
|
||||
bma250_channels, ARRAY_SIZE(bma250_channels),
|
||||
bma250_scale_table, ARRAY_SIZE(bma250_scale_table),
|
||||
bma250_bw_table, ARRAY_SIZE(bma250_bw_table),
|
||||
BMA250_INT_RESET_REG, BMA250_INT_RESET_MASK,
|
||||
BMA250_POWER_REG, BMA250_SUSPEND_MASK,
|
||||
BMA250_BW_REG, BMA250_BW_MASK,
|
||||
BMA250_RANGE_REG, BMA250_RANGE_MASK,
|
||||
BMA250_POWER_REG, BMA250_LOWPOWER_MASK, 1,
|
||||
BMA250_INT_ENABLE_REG, BMA250_DATA_INTEN_MASK,
|
||||
BMA250_RESET_REG,
|
||||
bma250_chip_config,
|
||||
bma250_chip_disable,
|
||||
},
|
||||
};
|
||||
|
||||
static irqreturn_t bma180_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int64_t time_ns = iio_get_time_ns();
|
||||
int bit, ret, i = 0;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
|
||||
for_each_set_bit(bit, indio_dev->buffer->scan_mask,
|
||||
indio_dev->masklength) {
|
||||
ret = bma180_get_data_reg(data, bit);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->mutex);
|
||||
goto err;
|
||||
}
|
||||
((s16 *)data->buff)[i++] = ret;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns);
|
||||
err:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int bma180_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bma180_set_new_data_intr_state(data, state);
|
||||
}
|
||||
|
||||
static int bma180_trig_try_reen(struct iio_trigger *trig)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
|
||||
return bma180_reset_intr(data);
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops bma180_trigger_ops = {
|
||||
.set_trigger_state = bma180_data_rdy_trigger_set_state,
|
||||
.try_reenable = bma180_trig_try_reen,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int bma180_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct bma180_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
data->part_info = &bma180_part_info[id->driver_data];
|
||||
|
||||
ret = data->part_info->chip_config(data);
|
||||
if (ret < 0)
|
||||
goto err_chip_disable;
|
||||
|
||||
mutex_init(&data->mutex);
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->channels = data->part_info->channels;
|
||||
indio_dev->num_channels = data->part_info->num_channels;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &bma180_info;
|
||||
|
||||
if (client->irq > 0) {
|
||||
data->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!data->trig) {
|
||||
ret = -ENOMEM;
|
||||
goto err_chip_disable;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&client->dev, client->irq,
|
||||
iio_trigger_generic_data_rdy_poll, IRQF_TRIGGER_RISING,
|
||||
"bma180_event", data->trig);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "unable to request IRQ\n");
|
||||
goto err_trigger_free;
|
||||
}
|
||||
|
||||
data->trig->dev.parent = &client->dev;
|
||||
data->trig->ops = &bma180_trigger_ops;
|
||||
iio_trigger_set_drvdata(data->trig, indio_dev);
|
||||
indio_dev->trig = iio_trigger_get(data->trig);
|
||||
|
||||
ret = iio_trigger_register(data->trig);
|
||||
if (ret)
|
||||
goto err_trigger_free;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
bma180_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to setup iio triggered buffer\n");
|
||||
goto err_trigger_unregister;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "unable to register iio device\n");
|
||||
goto err_buffer_cleanup;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
err_trigger_unregister:
|
||||
if (data->trig)
|
||||
iio_trigger_unregister(data->trig);
|
||||
err_trigger_free:
|
||||
iio_trigger_free(data->trig);
|
||||
err_chip_disable:
|
||||
data->part_info->chip_disable(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (data->trig) {
|
||||
iio_trigger_unregister(data->trig);
|
||||
iio_trigger_free(data->trig);
|
||||
}
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
data->part_info->chip_disable(data);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bma180_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma180_set_sleep_state(data, true);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bma180_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
|
||||
struct bma180_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->mutex);
|
||||
ret = bma180_set_sleep_state(data, false);
|
||||
mutex_unlock(&data->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(bma180_pm_ops, bma180_suspend, bma180_resume);
|
||||
#define BMA180_PM_OPS (&bma180_pm_ops)
|
||||
#else
|
||||
#define BMA180_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct i2c_device_id bma180_ids[] = {
|
||||
{ "bma180", BMA180 },
|
||||
{ "bma250", BMA250 },
|
||||
{ }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, bma180_ids);
|
||||
|
||||
static struct i2c_driver bma180_driver = {
|
||||
.driver = {
|
||||
.name = "bma180",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = BMA180_PM_OPS,
|
||||
},
|
||||
.probe = bma180_probe,
|
||||
.remove = bma180_remove,
|
||||
.id_table = bma180_ids,
|
||||
};
|
||||
|
||||
module_i2c_driver(bma180_driver);
|
||||
|
||||
MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>");
|
||||
MODULE_AUTHOR("Texas Instruments, Inc.");
|
||||
MODULE_DESCRIPTION("Bosch BMA180/BMA250 triaxial acceleration sensor");
|
||||
MODULE_LICENSE("GPL");
|
||||
1456
drivers/iio/accel/bmc150-accel.c
Normal file
1456
drivers/iio/accel/bmc150-accel.c
Normal file
File diff suppressed because it is too large
Load diff
430
drivers/iio/accel/hid-sensor-accel-3d.c
Normal file
430
drivers/iio/accel/hid-sensor-accel-3d.c
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
enum accel_3d_channel {
|
||||
CHANNEL_SCAN_INDEX_X,
|
||||
CHANNEL_SCAN_INDEX_Y,
|
||||
CHANNEL_SCAN_INDEX_Z,
|
||||
ACCEL_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
struct accel_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
|
||||
u32 accel_val[ACCEL_3D_CHANNEL_MAX];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
};
|
||||
|
||||
static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
|
||||
HID_USAGE_SENSOR_ACCEL_X_AXIS,
|
||||
HID_USAGE_SENSOR_ACCEL_Y_AXIS,
|
||||
HID_USAGE_SENSOR_ACCEL_Z_AXIS
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec accel_3d_channels[] = {
|
||||
{
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_ACCEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int accel_3d_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&accel_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&accel_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
report_id = accel_state->accel[chan->scan_index].report_id;
|
||||
address = accel_3d_addresses[chan->scan_index];
|
||||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
accel_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, address,
|
||||
report_id);
|
||||
else {
|
||||
*val = 0;
|
||||
hid_sensor_power_state(&accel_state->common_attributes,
|
||||
false);
|
||||
return -EINVAL;
|
||||
}
|
||||
hid_sensor_power_state(&accel_state->common_attributes, false);
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = accel_state->scale_pre_decml;
|
||||
*val2 = accel_state->scale_post_decml;
|
||||
ret_type = accel_state->scale_precision;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = accel_state->value_offset;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret_type = hid_sensor_read_samp_freq_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret_type = hid_sensor_read_raw_hyst_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int accel_3d_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&accel_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info accel_3d_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &accel_3d_read_raw,
|
||||
.write_raw = &accel_3d_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n");
|
||||
if (atomic_read(&accel_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
accel_state->accel_val,
|
||||
sizeof(accel_state->accel_val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
int offset;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ACCEL_X_AXIS:
|
||||
case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
|
||||
case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
|
||||
offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
|
||||
accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int accel_3d_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct accel_3d_state *st)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
|
||||
&st->accel[CHANNEL_SCAN_INDEX_X + i]);
|
||||
if (ret < 0)
|
||||
break;
|
||||
accel_3d_adjust_channel_bit_mask(channels,
|
||||
CHANNEL_SCAN_INDEX_X + i,
|
||||
st->accel[CHANNEL_SCAN_INDEX_X + i].size);
|
||||
}
|
||||
dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
|
||||
st->accel[0].index,
|
||||
st->accel[0].report_id,
|
||||
st->accel[1].index, st->accel[1].report_id,
|
||||
st->accel[2].index, st->accel[2].report_id);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
&st->accel[CHANNEL_SCAN_INDEX_X],
|
||||
&st->scale_pre_decml, &st->scale_post_decml);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0) {
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_DATA_ACCELERATION,
|
||||
&st->common_attributes.sensitivity);
|
||||
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
|
||||
st->common_attributes.sensitivity.index,
|
||||
st->common_attributes.sensitivity.report_id);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_accel_3d_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "accel_3d";
|
||||
struct iio_dev *indio_dev;
|
||||
struct accel_3d_state *accel_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct accel_3d_state));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
accel_state = iio_priv(indio_dev);
|
||||
accel_state->common_attributes.hsdev = hsdev;
|
||||
accel_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_ACCEL_3D,
|
||||
&accel_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(accel_3d_channels, sizeof(accel_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = accel_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_ACCEL_3D, accel_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &accel_3d_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
atomic_set(&accel_state->common_attributes.data_ready, 0);
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&accel_state->common_attributes);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
accel_state->callbacks.send_event = accel_3d_proc_event;
|
||||
accel_state->callbacks.capture_sample = accel_3d_capture_sample;
|
||||
accel_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
|
||||
&accel_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&accel_state->common_attributes);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_accel_3d_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct accel_3d_state *accel_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&accel_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_accel_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200073",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_accel_3d_ids);
|
||||
|
||||
static struct platform_driver hid_accel_3d_platform_driver = {
|
||||
.id_table = hid_accel_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = hid_accel_3d_probe,
|
||||
.remove = hid_accel_3d_remove,
|
||||
};
|
||||
module_platform_driver(hid_accel_3d_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Accel 3D");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
1429
drivers/iio/accel/kxcjk-1013.c
Normal file
1429
drivers/iio/accel/kxcjk-1013.c
Normal file
File diff suppressed because it is too large
Load diff
276
drivers/iio/accel/kxsd9.c
Normal file
276
drivers/iio/accel/kxsd9.c
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* kxsd9.c simple support for the Kionix KXSD9 3D
|
||||
* accelerometer.
|
||||
*
|
||||
* Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* The i2c interface is very similar, so shouldn't be a problem once
|
||||
* I have a suitable wire made up.
|
||||
*
|
||||
* TODO: Support the motion detector
|
||||
* Uses register address incrementing so could have a
|
||||
* heavily optimized ring buffer access function.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define KXSD9_REG_X 0x00
|
||||
#define KXSD9_REG_Y 0x02
|
||||
#define KXSD9_REG_Z 0x04
|
||||
#define KXSD9_REG_AUX 0x06
|
||||
#define KXSD9_REG_RESET 0x0a
|
||||
#define KXSD9_REG_CTRL_C 0x0c
|
||||
|
||||
#define KXSD9_FS_MASK 0x03
|
||||
|
||||
#define KXSD9_REG_CTRL_B 0x0d
|
||||
#define KXSD9_REG_CTRL_A 0x0e
|
||||
|
||||
#define KXSD9_READ(a) (0x80 | (a))
|
||||
#define KXSD9_WRITE(a) (a)
|
||||
|
||||
#define KXSD9_STATE_RX_SIZE 2
|
||||
#define KXSD9_STATE_TX_SIZE 2
|
||||
/**
|
||||
* struct kxsd9_state - device related storage
|
||||
* @buf_lock: protect the rx and tx buffers.
|
||||
* @us: spi device
|
||||
* @rx: single rx buffer storage
|
||||
* @tx: single tx buffer storage
|
||||
**/
|
||||
struct kxsd9_state {
|
||||
struct mutex buf_lock;
|
||||
struct spi_device *us;
|
||||
u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
|
||||
u8 tx[KXSD9_STATE_TX_SIZE];
|
||||
};
|
||||
|
||||
#define KXSD9_SCALE_2G "0.011978"
|
||||
#define KXSD9_SCALE_4G "0.023927"
|
||||
#define KXSD9_SCALE_6G "0.035934"
|
||||
#define KXSD9_SCALE_8G "0.047853"
|
||||
|
||||
/* reverse order */
|
||||
static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
|
||||
|
||||
static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
|
||||
{
|
||||
int ret, i;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
bool foundit = false;
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
if (micro == kxsd9_micro_scales[i]) {
|
||||
foundit = true;
|
||||
break;
|
||||
}
|
||||
if (!foundit)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
|
||||
st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
|
||||
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
|
||||
{
|
||||
int ret;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
.delay_usecs = 200,
|
||||
.tx_buf = st->tx,
|
||||
}, {
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
.rx_buf = st->rx,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx[0] = KXSD9_READ(address);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (!ret)
|
||||
ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(accel_scale_available,
|
||||
KXSD9_SCALE_2G " "
|
||||
KXSD9_SCALE_4G " "
|
||||
KXSD9_SCALE_6G " "
|
||||
KXSD9_SCALE_8G);
|
||||
|
||||
static struct attribute *kxsd9_attributes[] = {
|
||||
&iio_const_attr_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static int kxsd9_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (mask == IIO_CHAN_INFO_SCALE) {
|
||||
/* Check no integer component */
|
||||
if (val)
|
||||
return -EINVAL;
|
||||
ret = kxsd9_write_scale(indio_dev, val2);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kxsd9_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
struct kxsd9_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = kxsd9_read(indio_dev, chan->address);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
*val = ret;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
*val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
}
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
};
|
||||
#define KXSD9_ACCEL_CHAN(axis) \
|
||||
{ \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = KXSD9_REG_##axis, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec kxsd9_channels[] = {
|
||||
KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
|
||||
{
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.indexed = 1,
|
||||
.address = KXSD9_REG_AUX,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct attribute_group kxsd9_attribute_group = {
|
||||
.attrs = kxsd9_attributes,
|
||||
};
|
||||
|
||||
static int kxsd9_power_up(struct kxsd9_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
st->tx[0] = 0x0d;
|
||||
st->tx[1] = 0x40;
|
||||
ret = spi_write(st->us, st->tx, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->tx[0] = 0x0c;
|
||||
st->tx[1] = 0x9b;
|
||||
return spi_write(st->us, st->tx, 2);
|
||||
};
|
||||
|
||||
static const struct iio_info kxsd9_info = {
|
||||
.read_raw = &kxsd9_read_raw,
|
||||
.write_raw = &kxsd9_write_raw,
|
||||
.attrs = &kxsd9_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int kxsd9_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct kxsd9_state *st;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
indio_dev->channels = kxsd9_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &kxsd9_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
spi->mode = SPI_MODE_0;
|
||||
spi_setup(spi);
|
||||
kxsd9_power_up(st);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int kxsd9_remove(struct spi_device *spi)
|
||||
{
|
||||
iio_device_unregister(spi_get_drvdata(spi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id kxsd9_id[] = {
|
||||
{"kxsd9", 0},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, kxsd9_id);
|
||||
|
||||
static struct spi_driver kxsd9_driver = {
|
||||
.driver = {
|
||||
.name = "kxsd9",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = kxsd9_probe,
|
||||
.remove = kxsd9_remove,
|
||||
.id_table = kxsd9_id,
|
||||
};
|
||||
module_spi_driver(kxsd9_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
|
||||
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
451
drivers/iio/accel/mma8452.c
Normal file
451
drivers/iio/accel/mma8452.c
Normal file
|
|
@ -0,0 +1,451 @@
|
|||
/*
|
||||
* mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
|
||||
*
|
||||
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* 7-bit I2C slave address 0x1c/0x1d (pin selectable)
|
||||
*
|
||||
* TODO: interrupt, thresholding, orientation / freefall events, autosleep
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define MMA8452_STATUS 0x00
|
||||
#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
|
||||
#define MMA8452_OUT_Y 0x03
|
||||
#define MMA8452_OUT_Z 0x05
|
||||
#define MMA8452_WHO_AM_I 0x0d
|
||||
#define MMA8452_DATA_CFG 0x0e
|
||||
#define MMA8452_OFF_X 0x2f
|
||||
#define MMA8452_OFF_Y 0x30
|
||||
#define MMA8452_OFF_Z 0x31
|
||||
#define MMA8452_CTRL_REG1 0x2a
|
||||
#define MMA8452_CTRL_REG2 0x2b
|
||||
|
||||
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
|
||||
|
||||
#define MMA8452_CTRL_DR_MASK (BIT(5) | BIT(4) | BIT(3))
|
||||
#define MMA8452_CTRL_DR_SHIFT 3
|
||||
#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
|
||||
#define MMA8452_CTRL_ACTIVE BIT(0)
|
||||
|
||||
#define MMA8452_DATA_CFG_FS_MASK (BIT(1) | BIT(0))
|
||||
#define MMA8452_DATA_CFG_FS_2G 0
|
||||
#define MMA8452_DATA_CFG_FS_4G 1
|
||||
#define MMA8452_DATA_CFG_FS_8G 2
|
||||
|
||||
#define MMA8452_DEVICE_ID 0x2a
|
||||
|
||||
struct mma8452_data {
|
||||
struct i2c_client *client;
|
||||
struct mutex lock;
|
||||
u8 ctrl_reg1;
|
||||
u8 data_cfg;
|
||||
};
|
||||
|
||||
static int mma8452_drdy(struct mma8452_data *data)
|
||||
{
|
||||
int tries = 150;
|
||||
|
||||
while (tries-- > 0) {
|
||||
int ret = i2c_smbus_read_byte_data(data->client,
|
||||
MMA8452_STATUS);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
|
||||
return 0;
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
dev_err(&data->client->dev, "data not ready\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
|
||||
{
|
||||
int ret = mma8452_drdy(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return i2c_smbus_read_i2c_block_data(data->client,
|
||||
MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_int_plus_micros(char *buf,
|
||||
const int (*vals)[2], int n)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (n-- > 0)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%d.%06d ", vals[n][0], vals[n][1]);
|
||||
|
||||
/* replace trailing space by newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
|
||||
int val, int val2)
|
||||
{
|
||||
while (n-- > 0)
|
||||
if (val == vals[n][0] && val2 == vals[n][1])
|
||||
return n;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const int mma8452_samp_freq[8][2] = {
|
||||
{800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
|
||||
{6, 250000}, {1, 560000}
|
||||
};
|
||||
|
||||
/*
|
||||
* Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048
|
||||
* The userspace interface uses m/s^2 and we declare micro units
|
||||
* So scale factor is given by:
|
||||
* g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665
|
||||
*/
|
||||
static const int mma8452_scales[3][2] = {
|
||||
{0, 9577}, {0, 19154}, {0, 38307}
|
||||
};
|
||||
|
||||
static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
|
||||
ARRAY_SIZE(mma8452_samp_freq));
|
||||
}
|
||||
|
||||
static ssize_t mma8452_show_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return mma8452_show_int_plus_micros(buf, mma8452_scales,
|
||||
ARRAY_SIZE(mma8452_scales));
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
|
||||
static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
|
||||
mma8452_show_scale_avail, NULL, 0);
|
||||
|
||||
static int mma8452_get_samp_freq_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
|
||||
ARRAY_SIZE(mma8452_samp_freq), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_get_scale_index(struct mma8452_data *data,
|
||||
int val, int val2)
|
||||
{
|
||||
return mma8452_get_int_plus_micros_index(mma8452_scales,
|
||||
ARRAY_SIZE(mma8452_scales), val, val2);
|
||||
}
|
||||
|
||||
static int mma8452_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
__be16 buffer[3];
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
ret = mma8452_read(data, buffer);
|
||||
mutex_unlock(&data->lock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(
|
||||
be16_to_cpu(buffer[chan->scan_index]) >> 4, 11);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
|
||||
*val = mma8452_scales[i][0];
|
||||
*val2 = mma8452_scales[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
|
||||
MMA8452_CTRL_DR_SHIFT;
|
||||
*val = mma8452_samp_freq[i][0];
|
||||
*val2 = mma8452_samp_freq[i][1];
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = i2c_smbus_read_byte_data(data->client, MMA8452_OFF_X +
|
||||
chan->scan_index);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 7);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mma8452_standby(struct mma8452_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
|
||||
}
|
||||
|
||||
static int mma8452_active(struct mma8452_data *data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
}
|
||||
|
||||
static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
/* config can only be changed when in standby */
|
||||
ret = mma8452_standby(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(data->client, reg, val);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = mma8452_active(data);
|
||||
if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
ret = 0;
|
||||
fail:
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
i = mma8452_get_samp_freq_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
|
||||
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
|
||||
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
|
||||
return mma8452_change_config(data, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
i = mma8452_get_scale_index(data, val, val2);
|
||||
if (i < 0)
|
||||
return -EINVAL;
|
||||
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
|
||||
data->data_cfg |= i;
|
||||
return mma8452_change_config(data, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -128 || val > 127)
|
||||
return -EINVAL;
|
||||
return mma8452_change_config(data, MMA8452_OFF_X +
|
||||
chan->scan_index, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t mma8452_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct mma8452_data *data = iio_priv(indio_dev);
|
||||
u8 buffer[16]; /* 3 16-bit channels + padding + ts */
|
||||
int ret;
|
||||
|
||||
ret = mma8452_read(data, (__be16 *) buffer);
|
||||
if (ret < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
#define MMA8452_CHANNEL(axis, idx) { \
|
||||
.type = IIO_ACCEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_##axis, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = idx, \
|
||||
.scan_type = { \
|
||||
.sign = 's', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 4, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mma8452_channels[] = {
|
||||
MMA8452_CHANNEL(X, 0),
|
||||
MMA8452_CHANNEL(Y, 1),
|
||||
MMA8452_CHANNEL(Z, 2),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
};
|
||||
|
||||
static struct attribute *mma8452_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group mma8452_group = {
|
||||
.attrs = mma8452_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info mma8452_info = {
|
||||
.attrs = &mma8452_group,
|
||||
.read_raw = &mma8452_read_raw,
|
||||
.write_raw = &mma8452_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long mma8452_scan_masks[] = {0x7, 0};
|
||||
|
||||
static int mma8452_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mma8452_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != MMA8452_DEVICE_ID)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
data = iio_priv(indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
indio_dev->info = &mma8452_info;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mma8452_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
|
||||
indio_dev->available_scan_masks = mma8452_scan_masks;
|
||||
|
||||
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
|
||||
(MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
|
||||
data->ctrl_reg1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
|
||||
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
|
||||
data->data_cfg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
mma8452_trigger_handler, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto buffer_cleanup;
|
||||
return 0;
|
||||
|
||||
buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mma8452_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
mma8452_standby(iio_priv(indio_dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mma8452_suspend(struct device *dev)
|
||||
{
|
||||
return mma8452_standby(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
|
||||
static int mma8452_resume(struct device *dev)
|
||||
{
|
||||
return mma8452_active(iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev))));
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
|
||||
#define MMA8452_PM_OPS (&mma8452_pm_ops)
|
||||
#else
|
||||
#define MMA8452_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mma8452_id[] = {
|
||||
{ "mma8452", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mma8452_id);
|
||||
|
||||
static const struct of_device_id mma8452_dt_ids[] = {
|
||||
{ .compatible = "fsl,mma8452" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver mma8452_driver = {
|
||||
.driver = {
|
||||
.name = "mma8452",
|
||||
.of_match_table = of_match_ptr(mma8452_dt_ids),
|
||||
.pm = MMA8452_PM_OPS,
|
||||
},
|
||||
.probe = mma8452_probe,
|
||||
.remove = mma8452_remove,
|
||||
.id_table = mma8452_id,
|
||||
};
|
||||
module_i2c_driver(mma8452_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("Freescale MMA8452 accelerometer driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
56
drivers/iio/accel/st_accel.h
Normal file
56
drivers/iio/accel/st_accel.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
* v. 1.0.0
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef ST_ACCEL_H
|
||||
#define ST_ACCEL_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
|
||||
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
|
||||
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
|
||||
#define LSM330DL_ACCEL_DEV_NAME "lsm330dl_accel"
|
||||
#define LSM330DLC_ACCEL_DEV_NAME "lsm330dlc_accel"
|
||||
#define LIS331DLH_ACCEL_DEV_NAME "lis331dlh"
|
||||
#define LSM303DL_ACCEL_DEV_NAME "lsm303dl_accel"
|
||||
#define LSM303DLH_ACCEL_DEV_NAME "lsm303dlh_accel"
|
||||
#define LSM303DLM_ACCEL_DEV_NAME "lsm303dlm_accel"
|
||||
#define LSM330_ACCEL_DEV_NAME "lsm330_accel"
|
||||
|
||||
/**
|
||||
* struct st_sensors_platform_data - default accel platform data
|
||||
* @drdy_int_pin: default accel DRDY is available on INT1 pin.
|
||||
*/
|
||||
static const struct st_sensors_platform_data default_accel_pdata = {
|
||||
.drdy_int_pin = 1,
|
||||
};
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata);
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev);
|
||||
|
||||
#ifdef CONFIG_IIO_BUFFER
|
||||
int st_accel_allocate_ring(struct iio_dev *indio_dev);
|
||||
void st_accel_deallocate_ring(struct iio_dev *indio_dev);
|
||||
int st_accel_trig_set_state(struct iio_trigger *trig, bool state);
|
||||
#define ST_ACCEL_TRIGGER_SET_STATE (&st_accel_trig_set_state)
|
||||
#else /* CONFIG_IIO_BUFFER */
|
||||
static inline int st_accel_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void st_accel_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
}
|
||||
#define ST_ACCEL_TRIGGER_SET_STATE NULL
|
||||
#endif /* CONFIG_IIO_BUFFER */
|
||||
|
||||
#endif /* ST_ACCEL_H */
|
||||
105
drivers/iio/accel/st_accel_buffer.c
Normal file
105
drivers/iio/accel/st_accel_buffer.c
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
int st_accel_trig_set_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
|
||||
return st_sensors_set_dataready_irq(indio_dev, state);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
return st_sensors_set_enable(indio_dev, true);
|
||||
}
|
||||
|
||||
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (adata->buffer_data == NULL) {
|
||||
err = -ENOMEM;
|
||||
goto allocate_memory_error;
|
||||
}
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev,
|
||||
(u8)indio_dev->active_scan_mask[0]);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
err = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_postenable_error;
|
||||
|
||||
return err;
|
||||
|
||||
st_accel_buffer_postenable_error:
|
||||
kfree(adata->buffer_data);
|
||||
allocate_memory_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
err = iio_triggered_buffer_predisable(indio_dev);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
if (err < 0)
|
||||
goto st_accel_buffer_predisable_error;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
|
||||
st_accel_buffer_predisable_error:
|
||||
kfree(adata->buffer_data);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
|
||||
.preenable = &st_accel_buffer_preenable,
|
||||
.postenable = &st_accel_buffer_postenable,
|
||||
.predisable = &st_accel_buffer_predisable,
|
||||
};
|
||||
|
||||
int st_accel_allocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&st_sensors_trigger_handler, &st_accel_buffer_setup_ops);
|
||||
}
|
||||
|
||||
void st_accel_deallocate_ring(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
540
drivers/iio/accel/st_accel_core.c
Normal file
540
drivers/iio/accel/st_accel_core.c
Normal file
|
|
@ -0,0 +1,540 @@
|
|||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
#define ST_ACCEL_NUMBER_DATA_CHANNELS 3
|
||||
|
||||
/* DEFAULT VALUE FOR SENSORS */
|
||||
#define ST_ACCEL_DEFAULT_OUT_X_L_ADDR 0x28
|
||||
#define ST_ACCEL_DEFAULT_OUT_Y_L_ADDR 0x2a
|
||||
#define ST_ACCEL_DEFAULT_OUT_Z_L_ADDR 0x2c
|
||||
|
||||
/* FULLSCALE */
|
||||
#define ST_ACCEL_FS_AVL_2G 2
|
||||
#define ST_ACCEL_FS_AVL_4G 4
|
||||
#define ST_ACCEL_FS_AVL_6G 6
|
||||
#define ST_ACCEL_FS_AVL_8G 8
|
||||
#define ST_ACCEL_FS_AVL_16G 16
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 1 */
|
||||
#define ST_ACCEL_1_WAI_EXP 0x33
|
||||
#define ST_ACCEL_1_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_1_ODR_MASK 0xf0
|
||||
#define ST_ACCEL_1_ODR_AVL_1HZ_VAL 0x01
|
||||
#define ST_ACCEL_1_ODR_AVL_10HZ_VAL 0x02
|
||||
#define ST_ACCEL_1_ODR_AVL_25HZ_VAL 0x03
|
||||
#define ST_ACCEL_1_ODR_AVL_50HZ_VAL 0x04
|
||||
#define ST_ACCEL_1_ODR_AVL_100HZ_VAL 0x05
|
||||
#define ST_ACCEL_1_ODR_AVL_200HZ_VAL 0x06
|
||||
#define ST_ACCEL_1_ODR_AVL_400HZ_VAL 0x07
|
||||
#define ST_ACCEL_1_ODR_AVL_1600HZ_VAL 0x08
|
||||
#define ST_ACCEL_1_FS_ADDR 0x23
|
||||
#define ST_ACCEL_1_FS_MASK 0x30
|
||||
#define ST_ACCEL_1_FS_AVL_2_VAL 0x00
|
||||
#define ST_ACCEL_1_FS_AVL_4_VAL 0x01
|
||||
#define ST_ACCEL_1_FS_AVL_8_VAL 0x02
|
||||
#define ST_ACCEL_1_FS_AVL_16_VAL 0x03
|
||||
#define ST_ACCEL_1_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
|
||||
#define ST_ACCEL_1_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
|
||||
#define ST_ACCEL_1_FS_AVL_8_GAIN IIO_G_TO_M_S_2(4000)
|
||||
#define ST_ACCEL_1_FS_AVL_16_GAIN IIO_G_TO_M_S_2(12000)
|
||||
#define ST_ACCEL_1_BDU_ADDR 0x23
|
||||
#define ST_ACCEL_1_BDU_MASK 0x80
|
||||
#define ST_ACCEL_1_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_1_DRDY_IRQ_INT1_MASK 0x10
|
||||
#define ST_ACCEL_1_DRDY_IRQ_INT2_MASK 0x08
|
||||
#define ST_ACCEL_1_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 2 */
|
||||
#define ST_ACCEL_2_WAI_EXP 0x32
|
||||
#define ST_ACCEL_2_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_2_ODR_MASK 0x18
|
||||
#define ST_ACCEL_2_ODR_AVL_50HZ_VAL 0x00
|
||||
#define ST_ACCEL_2_ODR_AVL_100HZ_VAL 0x01
|
||||
#define ST_ACCEL_2_ODR_AVL_400HZ_VAL 0x02
|
||||
#define ST_ACCEL_2_ODR_AVL_1000HZ_VAL 0x03
|
||||
#define ST_ACCEL_2_PW_ADDR 0x20
|
||||
#define ST_ACCEL_2_PW_MASK 0xe0
|
||||
#define ST_ACCEL_2_FS_ADDR 0x23
|
||||
#define ST_ACCEL_2_FS_MASK 0x30
|
||||
#define ST_ACCEL_2_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_2_FS_AVL_4_VAL 0X01
|
||||
#define ST_ACCEL_2_FS_AVL_8_VAL 0x03
|
||||
#define ST_ACCEL_2_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1000)
|
||||
#define ST_ACCEL_2_FS_AVL_4_GAIN IIO_G_TO_M_S_2(2000)
|
||||
#define ST_ACCEL_2_FS_AVL_8_GAIN IIO_G_TO_M_S_2(3900)
|
||||
#define ST_ACCEL_2_BDU_ADDR 0x23
|
||||
#define ST_ACCEL_2_BDU_MASK 0x80
|
||||
#define ST_ACCEL_2_DRDY_IRQ_ADDR 0x22
|
||||
#define ST_ACCEL_2_DRDY_IRQ_INT1_MASK 0x02
|
||||
#define ST_ACCEL_2_DRDY_IRQ_INT2_MASK 0x10
|
||||
#define ST_ACCEL_2_MULTIREAD_BIT true
|
||||
|
||||
/* CUSTOM VALUES FOR SENSOR 3 */
|
||||
#define ST_ACCEL_3_WAI_EXP 0x40
|
||||
#define ST_ACCEL_3_ODR_ADDR 0x20
|
||||
#define ST_ACCEL_3_ODR_MASK 0xf0
|
||||
#define ST_ACCEL_3_ODR_AVL_3HZ_VAL 0x01
|
||||
#define ST_ACCEL_3_ODR_AVL_6HZ_VAL 0x02
|
||||
#define ST_ACCEL_3_ODR_AVL_12HZ_VAL 0x03
|
||||
#define ST_ACCEL_3_ODR_AVL_25HZ_VAL 0x04
|
||||
#define ST_ACCEL_3_ODR_AVL_50HZ_VAL 0x05
|
||||
#define ST_ACCEL_3_ODR_AVL_100HZ_VAL 0x06
|
||||
#define ST_ACCEL_3_ODR_AVL_200HZ_VAL 0x07
|
||||
#define ST_ACCEL_3_ODR_AVL_400HZ_VAL 0x08
|
||||
#define ST_ACCEL_3_ODR_AVL_800HZ_VAL 0x09
|
||||
#define ST_ACCEL_3_ODR_AVL_1600HZ_VAL 0x0a
|
||||
#define ST_ACCEL_3_FS_ADDR 0x24
|
||||
#define ST_ACCEL_3_FS_MASK 0x38
|
||||
#define ST_ACCEL_3_FS_AVL_2_VAL 0X00
|
||||
#define ST_ACCEL_3_FS_AVL_4_VAL 0X01
|
||||
#define ST_ACCEL_3_FS_AVL_6_VAL 0x02
|
||||
#define ST_ACCEL_3_FS_AVL_8_VAL 0x03
|
||||
#define ST_ACCEL_3_FS_AVL_16_VAL 0x04
|
||||
#define ST_ACCEL_3_FS_AVL_2_GAIN IIO_G_TO_M_S_2(61)
|
||||
#define ST_ACCEL_3_FS_AVL_4_GAIN IIO_G_TO_M_S_2(122)
|
||||
#define ST_ACCEL_3_FS_AVL_6_GAIN IIO_G_TO_M_S_2(183)
|
||||
#define ST_ACCEL_3_FS_AVL_8_GAIN IIO_G_TO_M_S_2(244)
|
||||
#define ST_ACCEL_3_FS_AVL_16_GAIN IIO_G_TO_M_S_2(732)
|
||||
#define ST_ACCEL_3_BDU_ADDR 0x20
|
||||
#define ST_ACCEL_3_BDU_MASK 0x08
|
||||
#define ST_ACCEL_3_DRDY_IRQ_ADDR 0x23
|
||||
#define ST_ACCEL_3_DRDY_IRQ_INT1_MASK 0x80
|
||||
#define ST_ACCEL_3_DRDY_IRQ_INT2_MASK 0x00
|
||||
#define ST_ACCEL_3_IG1_EN_ADDR 0x23
|
||||
#define ST_ACCEL_3_IG1_EN_MASK 0x08
|
||||
#define ST_ACCEL_3_MULTIREAD_BIT false
|
||||
|
||||
static const struct iio_chan_spec st_accel_12bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec st_accel_16bit_channels[] = {
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_X_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Y_L_ADDR),
|
||||
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
|
||||
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
|
||||
ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16,
|
||||
ST_ACCEL_DEFAULT_OUT_Z_L_ADDR),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3)
|
||||
};
|
||||
|
||||
static const struct st_sensors st_accel_sensors[] = {
|
||||
{
|
||||
.wai = ST_ACCEL_1_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS3DH_ACCEL_DEV_NAME,
|
||||
[1] = LSM303DLHC_ACCEL_DEV_NAME,
|
||||
[2] = LSM330D_ACCEL_DEV_NAME,
|
||||
[3] = LSM330DL_ACCEL_DEV_NAME,
|
||||
[4] = LSM330DLC_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
|
||||
{ 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
|
||||
{ 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
|
||||
{ 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_1_ODR_ADDR,
|
||||
.mask = ST_ACCEL_1_ODR_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_1_FS_ADDR,
|
||||
.mask = ST_ACCEL_1_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_1_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_1_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_1_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_8_GAIN,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = ST_ACCEL_1_FS_AVL_16_VAL,
|
||||
.gain = ST_ACCEL_1_FS_AVL_16_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_1_BDU_ADDR,
|
||||
.mask = ST_ACCEL_1_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_2_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LIS331DLH_ACCEL_DEV_NAME,
|
||||
[1] = LSM303DL_ACCEL_DEV_NAME,
|
||||
[2] = LSM303DLH_ACCEL_DEV_NAME,
|
||||
[3] = LSM303DLM_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_2_ODR_ADDR,
|
||||
.mask = ST_ACCEL_2_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 50, ST_ACCEL_2_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_2_ODR_AVL_100HZ_VAL, },
|
||||
{ 400, ST_ACCEL_2_ODR_AVL_400HZ_VAL, },
|
||||
{ 1000, ST_ACCEL_2_ODR_AVL_1000HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_2_PW_ADDR,
|
||||
.mask = ST_ACCEL_2_PW_MASK,
|
||||
.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_2_FS_ADDR,
|
||||
.mask = ST_ACCEL_2_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_2_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_2_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_2_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_2_FS_AVL_8_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_2_BDU_ADDR,
|
||||
.mask = ST_ACCEL_2_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_2_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_2_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_2_DRDY_IRQ_INT2_MASK,
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_2_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
{
|
||||
.wai = ST_ACCEL_3_WAI_EXP,
|
||||
.sensors_supported = {
|
||||
[0] = LSM330_ACCEL_DEV_NAME,
|
||||
},
|
||||
.ch = (struct iio_chan_spec *)st_accel_16bit_channels,
|
||||
.odr = {
|
||||
.addr = ST_ACCEL_3_ODR_ADDR,
|
||||
.mask = ST_ACCEL_3_ODR_MASK,
|
||||
.odr_avl = {
|
||||
{ 3, ST_ACCEL_3_ODR_AVL_3HZ_VAL },
|
||||
{ 6, ST_ACCEL_3_ODR_AVL_6HZ_VAL, },
|
||||
{ 12, ST_ACCEL_3_ODR_AVL_12HZ_VAL, },
|
||||
{ 25, ST_ACCEL_3_ODR_AVL_25HZ_VAL, },
|
||||
{ 50, ST_ACCEL_3_ODR_AVL_50HZ_VAL, },
|
||||
{ 100, ST_ACCEL_3_ODR_AVL_100HZ_VAL, },
|
||||
{ 200, ST_ACCEL_3_ODR_AVL_200HZ_VAL, },
|
||||
{ 400, ST_ACCEL_3_ODR_AVL_400HZ_VAL, },
|
||||
{ 800, ST_ACCEL_3_ODR_AVL_800HZ_VAL, },
|
||||
{ 1600, ST_ACCEL_3_ODR_AVL_1600HZ_VAL, },
|
||||
},
|
||||
},
|
||||
.pw = {
|
||||
.addr = ST_ACCEL_3_ODR_ADDR,
|
||||
.mask = ST_ACCEL_3_ODR_MASK,
|
||||
.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
|
||||
},
|
||||
.enable_axis = {
|
||||
.addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
|
||||
.mask = ST_SENSORS_DEFAULT_AXIS_MASK,
|
||||
},
|
||||
.fs = {
|
||||
.addr = ST_ACCEL_3_FS_ADDR,
|
||||
.mask = ST_ACCEL_3_FS_MASK,
|
||||
.fs_avl = {
|
||||
[0] = {
|
||||
.num = ST_ACCEL_FS_AVL_2G,
|
||||
.value = ST_ACCEL_3_FS_AVL_2_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_2_GAIN,
|
||||
},
|
||||
[1] = {
|
||||
.num = ST_ACCEL_FS_AVL_4G,
|
||||
.value = ST_ACCEL_3_FS_AVL_4_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_4_GAIN,
|
||||
},
|
||||
[2] = {
|
||||
.num = ST_ACCEL_FS_AVL_6G,
|
||||
.value = ST_ACCEL_3_FS_AVL_6_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_6_GAIN,
|
||||
},
|
||||
[3] = {
|
||||
.num = ST_ACCEL_FS_AVL_8G,
|
||||
.value = ST_ACCEL_3_FS_AVL_8_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_8_GAIN,
|
||||
},
|
||||
[4] = {
|
||||
.num = ST_ACCEL_FS_AVL_16G,
|
||||
.value = ST_ACCEL_3_FS_AVL_16_VAL,
|
||||
.gain = ST_ACCEL_3_FS_AVL_16_GAIN,
|
||||
},
|
||||
},
|
||||
},
|
||||
.bdu = {
|
||||
.addr = ST_ACCEL_3_BDU_ADDR,
|
||||
.mask = ST_ACCEL_3_BDU_MASK,
|
||||
},
|
||||
.drdy_irq = {
|
||||
.addr = ST_ACCEL_3_DRDY_IRQ_ADDR,
|
||||
.mask_int1 = ST_ACCEL_3_DRDY_IRQ_INT1_MASK,
|
||||
.mask_int2 = ST_ACCEL_3_DRDY_IRQ_INT2_MASK,
|
||||
.ig1 = {
|
||||
.en_addr = ST_ACCEL_3_IG1_EN_ADDR,
|
||||
.en_mask = ST_ACCEL_3_IG1_EN_MASK,
|
||||
},
|
||||
},
|
||||
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
|
||||
.bootime = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static int st_accel_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = st_sensors_read_info_raw(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto read_error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
*val2 = adata->current_fullscale->gain;
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = adata->odr;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
read_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_accel_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = st_sensors_set_fullscale_by_gain(indio_dev, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val2)
|
||||
return -EINVAL;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
err = st_sensors_set_odr(indio_dev, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return err;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
|
||||
static ST_SENSORS_DEV_ATTR_SCALE_AVAIL(in_accel_scale_available);
|
||||
|
||||
static struct attribute *st_accel_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_accel_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group st_accel_attribute_group = {
|
||||
.attrs = st_accel_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info accel_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &st_accel_attribute_group,
|
||||
.read_raw = &st_accel_read_raw,
|
||||
.write_raw = &st_accel_write_raw,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_IIO_TRIGGER
|
||||
static const struct iio_trigger_ops st_accel_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = ST_ACCEL_TRIGGER_SET_STATE,
|
||||
};
|
||||
#define ST_ACCEL_TRIGGER_OPS (&st_accel_trigger_ops)
|
||||
#else
|
||||
#define ST_ACCEL_TRIGGER_OPS NULL
|
||||
#endif
|
||||
|
||||
int st_accel_common_probe(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *plat_data)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
int irq = adata->get_irq_data_ready(indio_dev);
|
||||
int err;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &accel_info;
|
||||
|
||||
st_sensors_power_enable(indio_dev);
|
||||
|
||||
err = st_sensors_check_device_support(indio_dev,
|
||||
ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
|
||||
adata->multiread_bit = adata->sensor->multi_read_bit;
|
||||
indio_dev->channels = adata->sensor->ch;
|
||||
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
|
||||
|
||||
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&adata->sensor->fs.fs_avl[0];
|
||||
adata->odr = adata->sensor->odr.odr_avl[0].hz;
|
||||
|
||||
if (!plat_data)
|
||||
plat_data =
|
||||
(struct st_sensors_platform_data *)&default_accel_pdata;
|
||||
|
||||
err = st_sensors_init_sensor(indio_dev, plat_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_accel_allocate_ring(indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (irq > 0) {
|
||||
err = st_sensors_allocate_trigger(indio_dev,
|
||||
ST_ACCEL_TRIGGER_OPS);
|
||||
if (err < 0)
|
||||
goto st_accel_probe_trigger_error;
|
||||
}
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto st_accel_device_register_error;
|
||||
|
||||
dev_info(&indio_dev->dev, "registered accelerometer %s\n",
|
||||
indio_dev->name);
|
||||
|
||||
return 0;
|
||||
|
||||
st_accel_device_register_error:
|
||||
if (irq > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
st_accel_probe_trigger_error:
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_probe);
|
||||
|
||||
void st_accel_common_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *adata = iio_priv(indio_dev);
|
||||
|
||||
st_sensors_power_disable(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (adata->get_irq_data_ready(indio_dev) > 0)
|
||||
st_sensors_deallocate_trigger(indio_dev);
|
||||
|
||||
st_accel_deallocate_ring(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL(st_accel_common_remove);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
130
drivers/iio/accel/st_accel_i2c.c
Normal file
130
drivers/iio/accel/st_accel_i2c.c
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id st_accel_of_match[] = {
|
||||
{
|
||||
.compatible = "st,lsm303dlh-accel",
|
||||
.data = LSM303DLH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dlhc-accel",
|
||||
.data = LSM303DLHC_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis3dh-accel",
|
||||
.data = LIS3DH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330d-accel",
|
||||
.data = LSM330D_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dl-accel",
|
||||
.data = LSM330DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330dlc-accel",
|
||||
.data = LSM330DLC_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lis331dlh-accel",
|
||||
.data = LIS331DLH_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dl-accel",
|
||||
.data = LSM303DL_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm303dlm-accel",
|
||||
.data = LSM303DLM_ACCEL_DEV_NAME,
|
||||
},
|
||||
{
|
||||
.compatible = "st,lsm330-accel",
|
||||
.data = LSM330_ACCEL_DEV_NAME,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st_accel_of_match);
|
||||
#else
|
||||
#define st_accel_of_match NULL
|
||||
#endif
|
||||
|
||||
static int st_accel_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &client->dev;
|
||||
st_sensors_of_i2c_probe(client, st_accel_of_match);
|
||||
|
||||
st_sensors_i2c_configure(indio_dev, client, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev, client->dev.platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_accel_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
st_accel_common_remove(i2c_get_clientdata(client));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
|
||||
|
||||
static struct i2c_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-accel-i2c",
|
||||
.of_match_table = of_match_ptr(st_accel_of_match),
|
||||
},
|
||||
.probe = st_accel_i2c_probe,
|
||||
.remove = st_accel_i2c_remove,
|
||||
.id_table = st_accel_id_table,
|
||||
};
|
||||
module_i2c_driver(st_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
78
drivers/iio/accel/st_accel_spi.c
Normal file
78
drivers/iio/accel/st_accel_spi.c
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* STMicroelectronics accelerometers driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
#include "st_accel.h"
|
||||
|
||||
static int st_accel_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct st_sensor_data *adata;
|
||||
int err;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adata));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adata = iio_priv(indio_dev);
|
||||
adata->dev = &spi->dev;
|
||||
|
||||
st_sensors_spi_configure(indio_dev, spi, adata);
|
||||
|
||||
err = st_accel_common_probe(indio_dev, spi->dev.platform_data);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int st_accel_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
st_accel_common_remove(spi_get_drvdata(spi));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id st_accel_id_table[] = {
|
||||
{ LSM303DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DLHC_ACCEL_DEV_NAME },
|
||||
{ LIS3DH_ACCEL_DEV_NAME },
|
||||
{ LSM330D_ACCEL_DEV_NAME },
|
||||
{ LSM330DL_ACCEL_DEV_NAME },
|
||||
{ LSM330DLC_ACCEL_DEV_NAME },
|
||||
{ LIS331DLH_ACCEL_DEV_NAME },
|
||||
{ LSM303DL_ACCEL_DEV_NAME },
|
||||
{ LSM303DLM_ACCEL_DEV_NAME },
|
||||
{ LSM330_ACCEL_DEV_NAME },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
|
||||
|
||||
static struct spi_driver st_accel_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "st-accel-spi",
|
||||
},
|
||||
.probe = st_accel_spi_probe,
|
||||
.remove = st_accel_spi_remove,
|
||||
.id_table = st_accel_id_table,
|
||||
};
|
||||
module_spi_driver(st_accel_driver);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics accelerometers spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
302
drivers/iio/adc/Kconfig
Normal file
302
drivers/iio/adc/Kconfig
Normal file
|
|
@ -0,0 +1,302 @@
|
|||
#
|
||||
# ADC drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Analog to digital converters"
|
||||
|
||||
config AD_SIGMA_DELTA
|
||||
tristate
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
|
||||
config AD7266
|
||||
tristate "Analog Devices AD7265/AD7266 ADC driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7265 and AD7266
|
||||
ADCs.
|
||||
|
||||
config AD7291
|
||||
tristate "Analog Devices AD7291 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7291
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7291.
|
||||
|
||||
config AD7298
|
||||
tristate "Analog Devices AD7298 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7298
|
||||
8 Channel ADC with temperature sensor.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7298.
|
||||
|
||||
config AD7476
|
||||
tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
|
||||
AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
|
||||
AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7476.
|
||||
|
||||
config AD7791
|
||||
tristate "Analog Devices AD7791 ADC driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
|
||||
AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
|
||||
N (but it is safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the module will be
|
||||
called ad7791.
|
||||
|
||||
config AD7793
|
||||
tristate "Analog Devices AD7793 and similar ADCs driver"
|
||||
depends on SPI
|
||||
select AD_SIGMA_DELTA
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
|
||||
AD7794 and AD7795 SPI analog to digital converters (ADC).
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called AD7793.
|
||||
|
||||
config AD7887
|
||||
tristate "Analog Devices AD7887 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7887 SPI analog to digital converter (ADC).
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7887.
|
||||
|
||||
config AD7923
|
||||
tristate "Analog Devices AD7923 and similar ADCs driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices
|
||||
AD7904, AD7914, AD7923, AD7924 4 Channel ADCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7923.
|
||||
|
||||
config AD799X
|
||||
tristate "Analog Devices AD799x ADC driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices:
|
||||
ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998
|
||||
i2c analog to digital converters (ADC). Provides direct access
|
||||
via sysfs.
|
||||
|
||||
config AT91_ADC
|
||||
tristate "Atmel AT91 ADC"
|
||||
depends on ARCH_AT91
|
||||
depends on INPUT
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select SYSFS
|
||||
help
|
||||
Say yes here to build support for Atmel AT91 ADC.
|
||||
|
||||
config EXYNOS_ADC
|
||||
tristate "Exynos ADC driver support"
|
||||
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
|
||||
help
|
||||
Core support for the ADC block found in the Samsung EXYNOS series
|
||||
of SoCs for drivers such as the touchscreen and hwmon to use to share
|
||||
this resource.
|
||||
|
||||
config LP8788_ADC
|
||||
tristate "LP8788 ADC driver"
|
||||
depends on MFD_LP8788
|
||||
help
|
||||
Say yes here to build support for TI LP8788 ADC.
|
||||
|
||||
config MAX1027
|
||||
tristate "Maxim max1027 ADC driver"
|
||||
depends on SPI
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for Maxim SPI ADC models
|
||||
max1027, max1029 and max1031.
|
||||
|
||||
config MAX1363
|
||||
tristate "Maxim max1363 ADC driver"
|
||||
depends on I2C
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to build support for many Maxim i2c analog to digital
|
||||
converters (ADC). (max1361, max1362, max1363, max1364, max1036,
|
||||
max1037, max1038, max1039, max1136, max1136, max1137, max1138,
|
||||
max1139, max1236, max1237, max11238, max1239, max11600, max11601,
|
||||
max11602, max11603, max11604, max11605, max11606, max11607,
|
||||
max11608, max11609, max11610, max11611, max11612, max11613,
|
||||
max11614, max11615, max11616, max11617, max11644, max11645,
|
||||
max11646, max11647) Provides direct access via sysfs and buffered
|
||||
data via the iio dev interface.
|
||||
|
||||
config MCP320X
|
||||
tristate "Microchip Technology MCP3204/08"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's MCP3204 or
|
||||
MCP3208 analog to digital converter.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp320x.
|
||||
|
||||
config MCP3422
|
||||
tristate "Microchip Technology MCP3422/3/4/6/7/8 driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Microchip Technology's
|
||||
MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428
|
||||
analog to digital converters.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called mcp3422.
|
||||
|
||||
config MEN_Z188_ADC
|
||||
tristate "MEN 16z188 ADC IP Core support"
|
||||
depends on MCB
|
||||
help
|
||||
Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB
|
||||
carrier.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called men_z188_adc.
|
||||
|
||||
config NAU7802
|
||||
tristate "Nuvoton NAU7802 ADC driver"
|
||||
depends on I2C
|
||||
help
|
||||
Say yes here to build support for Nuvoton NAU7802 ADC.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called nau7802.
|
||||
|
||||
config ROCKCHIP_SARADC
|
||||
tristate "Rockchip SARADC driver"
|
||||
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
|
||||
help
|
||||
Say yes here to build support for the SARADC found in SoCs from
|
||||
Rockchip.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rockchip_saradc.
|
||||
|
||||
config TI_ADC081C
|
||||
tristate "Texas Instruments ADC081C021/027"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC081C021
|
||||
and ADC081C027 ADC chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc081c.
|
||||
|
||||
config TI_ADC128S052
|
||||
tristate "Texas Instruments ADC128S052"
|
||||
depends on SPI
|
||||
help
|
||||
If you say yes here you get support for Texas Instruments ADC128S052
|
||||
chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called ti-adc128s052.
|
||||
|
||||
config TI_AM335X_ADC
|
||||
tristate "TI's AM335X ADC driver"
|
||||
depends on MFD_TI_AM335X_TSCADC
|
||||
select IIO_BUFFER
|
||||
select IIO_KFIFO_BUF
|
||||
help
|
||||
Say yes here to build support for Texas Instruments ADC
|
||||
driver which is also a MFD client.
|
||||
|
||||
config TWL4030_MADC
|
||||
tristate "TWL4030 MADC (Monitoring A/D Converter)"
|
||||
depends on TWL4030_CORE
|
||||
help
|
||||
This driver provides support for Triton TWL4030-MADC. The
|
||||
driver supports both RT and SW conversion methods.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called twl4030-madc.
|
||||
|
||||
config TWL6030_GPADC
|
||||
tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"
|
||||
depends on TWL4030_CORE
|
||||
default n
|
||||
help
|
||||
Say yes here if you want support for the TWL6030/TWL6032 General
|
||||
Purpose A/D Converter. This will add support for battery type
|
||||
detection, battery voltage and temperature measurement, die
|
||||
temperature measurement, system supply voltage, audio accessory,
|
||||
USB ID detection.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called twl6030-gpadc.
|
||||
|
||||
config VF610_ADC
|
||||
tristate "Freescale vf610 ADC driver"
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to support for Vybrid board analog-to-digital converter.
|
||||
Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX.
|
||||
|
||||
This driver can also be built as a module. If so, the module will be
|
||||
called vf610_adc.
|
||||
|
||||
config VIPERBOARD_ADC
|
||||
tristate "Viperboard ADC support"
|
||||
depends on MFD_VIPERBOARD && USB
|
||||
help
|
||||
Say yes here to access the ADC part of the Nano River
|
||||
Technologies Viperboard.
|
||||
|
||||
config XILINX_XADC
|
||||
tristate "Xilinx XADC driver"
|
||||
depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
help
|
||||
Say yes here to have support for the Xilinx XADC. The driver does support
|
||||
both the ZYNQ interface to the XADC as well as the AXI-XADC interface.
|
||||
|
||||
The driver can also be build as a module. If so, the module will be called
|
||||
xilinx-xadc.
|
||||
|
||||
endmenu
|
||||
34
drivers/iio/adc/Makefile
Normal file
34
drivers/iio/adc/Makefile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# Makefile for IIO ADC drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
|
||||
obj-$(CONFIG_AD7266) += ad7266.o
|
||||
obj-$(CONFIG_AD7291) += ad7291.o
|
||||
obj-$(CONFIG_AD7298) += ad7298.o
|
||||
obj-$(CONFIG_AD7923) += ad7923.o
|
||||
obj-$(CONFIG_AD7476) += ad7476.o
|
||||
obj-$(CONFIG_AD7791) += ad7791.o
|
||||
obj-$(CONFIG_AD7793) += ad7793.o
|
||||
obj-$(CONFIG_AD7887) += ad7887.o
|
||||
obj-$(CONFIG_AD799X) += ad799x.o
|
||||
obj-$(CONFIG_AT91_ADC) += at91_adc.o
|
||||
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
|
||||
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
|
||||
obj-$(CONFIG_MAX1027) += max1027.o
|
||||
obj-$(CONFIG_MAX1363) += max1363.o
|
||||
obj-$(CONFIG_MCP320X) += mcp320x.o
|
||||
obj-$(CONFIG_MCP3422) += mcp3422.o
|
||||
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
|
||||
obj-$(CONFIG_NAU7802) += nau7802.o
|
||||
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
|
||||
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
|
||||
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
|
||||
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
|
||||
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
|
||||
obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o
|
||||
obj-$(CONFIG_VF610_ADC) += vf610_adc.o
|
||||
obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o
|
||||
xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o
|
||||
obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o
|
||||
522
drivers/iio/adc/ad7266.c
Normal file
522
drivers/iio/adc/ad7266.c
Normal file
|
|
@ -0,0 +1,522 @@
|
|||
/*
|
||||
* AD7266/65 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/platform_data/ad7266.h>
|
||||
|
||||
struct ad7266_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned long vref_mv;
|
||||
|
||||
struct spi_transfer single_xfer[3];
|
||||
struct spi_message single_msg;
|
||||
|
||||
enum ad7266_range range;
|
||||
enum ad7266_mode mode;
|
||||
bool fixed_addr;
|
||||
struct gpio gpios[3];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* The buffer needs to be large enough to hold two samples (4 bytes) and
|
||||
* the naturally aligned timestamp (8 bytes).
|
||||
*/
|
||||
struct {
|
||||
__be16 sample[2];
|
||||
s64 timestamp;
|
||||
} data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7266_wakeup(struct ad7266_state *st)
|
||||
{
|
||||
/* Any read with >= 2 bytes will wake the device */
|
||||
return spi_read(st->spi, &st->data.sample[0], 2);
|
||||
}
|
||||
|
||||
static int ad7266_powerdown(struct ad7266_state *st)
|
||||
{
|
||||
/* Any read with < 2 bytes will powerdown the device */
|
||||
return spi_read(st->spi, &st->data.sample[0], 1);
|
||||
}
|
||||
|
||||
static int ad7266_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
return ad7266_wakeup(st);
|
||||
}
|
||||
|
||||
static int ad7266_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
return ad7266_powerdown(st);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
|
||||
.preenable = &ad7266_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad7266_postdisable,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7266_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_read(st->spi, st->data.sample, 4);
|
||||
if (ret == 0) {
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, &st->data,
|
||||
pf->timestamp);
|
||||
}
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7266_select_input(struct ad7266_state *st, unsigned int nr)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (st->fixed_addr)
|
||||
return;
|
||||
|
||||
switch (st->mode) {
|
||||
case AD7266_MODE_SINGLE_ENDED:
|
||||
nr >>= 1;
|
||||
break;
|
||||
case AD7266_MODE_PSEUDO_DIFF:
|
||||
nr |= 1;
|
||||
break;
|
||||
case AD7266_MODE_DIFF:
|
||||
nr &= ~1;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
gpio_set_value(st->gpios[i].gpio, (bool)(nr & BIT(i)));
|
||||
}
|
||||
|
||||
static int ad7266_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
unsigned int nr = find_first_bit(scan_mask, indio_dev->masklength);
|
||||
|
||||
ad7266_select_input(st, nr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7266_read_single(struct ad7266_state *st, int *val,
|
||||
unsigned int address)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ad7266_select_input(st, address);
|
||||
|
||||
ret = spi_sync(st->spi, &st->single_msg);
|
||||
*val = be16_to_cpu(st->data.sample[address % 2]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7266_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
unsigned long scale_mv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
ret = ad7266_read_single(st, val, chan->address);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (*val >> 2) & 0xfff;
|
||||
if (chan->scan_type.sign == 's')
|
||||
*val = sign_extend32(*val, 11);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_mv = st->vref_mv;
|
||||
if (st->mode == AD7266_MODE_DIFF)
|
||||
scale_mv *= 2;
|
||||
if (st->range == AD7266_RANGE_2VREF)
|
||||
scale_mv *= 2;
|
||||
|
||||
*val = scale_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (st->range == AD7266_RANGE_2VREF &&
|
||||
st->mode != AD7266_MODE_DIFF)
|
||||
*val = 2048;
|
||||
else
|
||||
*val = 0;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define AD7266_CHAN(_chan, _sign) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = (_sign), \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_##_name[] = { \
|
||||
AD7266_CHAN(0, (_sign)), \
|
||||
AD7266_CHAN(1, (_sign)), \
|
||||
AD7266_CHAN(2, (_sign)), \
|
||||
AD7266_CHAN(3, (_sign)), \
|
||||
AD7266_CHAN(4, (_sign)), \
|
||||
AD7266_CHAN(5, (_sign)), \
|
||||
AD7266_CHAN(6, (_sign)), \
|
||||
AD7266_CHAN(7, (_sign)), \
|
||||
AD7266_CHAN(8, (_sign)), \
|
||||
AD7266_CHAN(9, (_sign)), \
|
||||
AD7266_CHAN(10, (_sign)), \
|
||||
AD7266_CHAN(11, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(13), \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_##_name##_fixed[] = { \
|
||||
AD7266_CHAN(0, (_sign)), \
|
||||
AD7266_CHAN(1, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(u, 'u');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS(s, 's');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(u, 'u');
|
||||
static AD7266_DECLARE_SINGLE_ENDED_CHANNELS_FIXED(s, 's');
|
||||
|
||||
#define AD7266_CHAN_DIFF(_chan, _sign) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_chan) * 2, \
|
||||
.channel2 = (_chan) * 2 + 1, \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
| BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_index = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = _sign, \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.differential = 1, \
|
||||
}
|
||||
|
||||
#define AD7266_DECLARE_DIFF_CHANNELS(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_diff_##_name[] = { \
|
||||
AD7266_CHAN_DIFF(0, (_sign)), \
|
||||
AD7266_CHAN_DIFF(1, (_sign)), \
|
||||
AD7266_CHAN_DIFF(2, (_sign)), \
|
||||
AD7266_CHAN_DIFF(3, (_sign)), \
|
||||
AD7266_CHAN_DIFF(4, (_sign)), \
|
||||
AD7266_CHAN_DIFF(5, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(6), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_DIFF_CHANNELS(s, 's');
|
||||
static AD7266_DECLARE_DIFF_CHANNELS(u, 'u');
|
||||
|
||||
#define AD7266_DECLARE_DIFF_CHANNELS_FIXED(_name, _sign) \
|
||||
const struct iio_chan_spec ad7266_channels_diff_fixed_##_name[] = { \
|
||||
AD7266_CHAN_DIFF(0, (_sign)), \
|
||||
AD7266_CHAN_DIFF(1, (_sign)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2), \
|
||||
}
|
||||
|
||||
static AD7266_DECLARE_DIFF_CHANNELS_FIXED(s, 's');
|
||||
static AD7266_DECLARE_DIFF_CHANNELS_FIXED(u, 'u');
|
||||
|
||||
static const struct iio_info ad7266_info = {
|
||||
.read_raw = &ad7266_read_raw,
|
||||
.update_scan_mode = &ad7266_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
0x0c0,
|
||||
0x300,
|
||||
0xc00,
|
||||
0x000,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks_diff[] = {
|
||||
0x003,
|
||||
0x00c,
|
||||
0x030,
|
||||
0x000,
|
||||
};
|
||||
|
||||
static const unsigned long ad7266_available_scan_masks_fixed[] = {
|
||||
0x003,
|
||||
0x000,
|
||||
};
|
||||
|
||||
struct ad7266_chan_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned long *scan_masks;
|
||||
};
|
||||
|
||||
#define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \
|
||||
(((_differential) << 2) | ((_signed) << 1) | ((_fixed) << 0))
|
||||
|
||||
static const struct ad7266_chan_info ad7266_chan_infos[] = {
|
||||
[AD7266_CHAN_INFO_INDEX(0, 0, 0)] = {
|
||||
.channels = ad7266_channels_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_u),
|
||||
.scan_masks = ad7266_available_scan_masks,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 0, 1)] = {
|
||||
.channels = ad7266_channels_u_fixed,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_u_fixed),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 1, 0)] = {
|
||||
.channels = ad7266_channels_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_s),
|
||||
.scan_masks = ad7266_available_scan_masks,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(0, 1, 1)] = {
|
||||
.channels = ad7266_channels_s_fixed,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_s_fixed),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 0, 0)] = {
|
||||
.channels = ad7266_channels_diff_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_u),
|
||||
.scan_masks = ad7266_available_scan_masks_diff,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 0, 1)] = {
|
||||
.channels = ad7266_channels_diff_fixed_u,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_u),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 1, 0)] = {
|
||||
.channels = ad7266_channels_diff_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_s),
|
||||
.scan_masks = ad7266_available_scan_masks_diff,
|
||||
},
|
||||
[AD7266_CHAN_INFO_INDEX(1, 1, 1)] = {
|
||||
.channels = ad7266_channels_diff_fixed_s,
|
||||
.num_channels = ARRAY_SIZE(ad7266_channels_diff_fixed_s),
|
||||
.scan_masks = ad7266_available_scan_masks_fixed,
|
||||
},
|
||||
};
|
||||
|
||||
static void ad7266_init_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
bool is_differential, is_signed;
|
||||
const struct ad7266_chan_info *chan_info;
|
||||
int i;
|
||||
|
||||
is_differential = st->mode != AD7266_MODE_SINGLE_ENDED;
|
||||
is_signed = (st->range == AD7266_RANGE_2VREF) |
|
||||
(st->mode == AD7266_MODE_DIFF);
|
||||
|
||||
i = AD7266_CHAN_INFO_INDEX(is_differential, is_signed, st->fixed_addr);
|
||||
chan_info = &ad7266_chan_infos[i];
|
||||
|
||||
indio_dev->channels = chan_info->channels;
|
||||
indio_dev->num_channels = chan_info->num_channels;
|
||||
indio_dev->available_scan_masks = chan_info->scan_masks;
|
||||
indio_dev->masklength = chan_info->num_channels - 1;
|
||||
}
|
||||
|
||||
static const char * const ad7266_gpio_labels[] = {
|
||||
"AD0", "AD1", "AD2",
|
||||
};
|
||||
|
||||
static int ad7266_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7266_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7266_state *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (!IS_ERR_OR_NULL(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref_mv = ret / 1000;
|
||||
} else {
|
||||
/* Use internal reference */
|
||||
st->vref_mv = 2500;
|
||||
}
|
||||
|
||||
if (pdata) {
|
||||
st->fixed_addr = pdata->fixed_addr;
|
||||
st->mode = pdata->mode;
|
||||
st->range = pdata->range;
|
||||
|
||||
if (!st->fixed_addr) {
|
||||
for (i = 0; i < ARRAY_SIZE(st->gpios); ++i) {
|
||||
st->gpios[i].gpio = pdata->addr_gpios[i];
|
||||
st->gpios[i].flags = GPIOF_OUT_INIT_LOW;
|
||||
st->gpios[i].label = ad7266_gpio_labels[i];
|
||||
}
|
||||
ret = gpio_request_array(st->gpios,
|
||||
ARRAY_SIZE(st->gpios));
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
} else {
|
||||
st->fixed_addr = true;
|
||||
st->range = AD7266_RANGE_VREF;
|
||||
st->mode = AD7266_MODE_DIFF;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &ad7266_info;
|
||||
|
||||
ad7266_init_channels(indio_dev);
|
||||
|
||||
/* wakeup */
|
||||
st->single_xfer[0].rx_buf = &st->data.sample[0];
|
||||
st->single_xfer[0].len = 2;
|
||||
st->single_xfer[0].cs_change = 1;
|
||||
/* conversion */
|
||||
st->single_xfer[1].rx_buf = st->data.sample;
|
||||
st->single_xfer[1].len = 4;
|
||||
st->single_xfer[1].cs_change = 1;
|
||||
/* powerdown */
|
||||
st->single_xfer[2].tx_buf = &st->data.sample[0];
|
||||
st->single_xfer[2].len = 1;
|
||||
|
||||
spi_message_init(&st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[0], &st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[1], &st->single_msg);
|
||||
spi_message_add_tail(&st->single_xfer[2], &st->single_msg);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad7266_trigger_handler, &iio_triggered_buffer_setup_ops);
|
||||
if (ret)
|
||||
goto error_free_gpios;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_buffer_cleanup;
|
||||
|
||||
return 0;
|
||||
|
||||
error_buffer_cleanup:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_gpios:
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
error_disable_reg:
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7266_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7266_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (!st->fixed_addr)
|
||||
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
|
||||
if (!IS_ERR_OR_NULL(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7266_id[] = {
|
||||
{"ad7265", 0},
|
||||
{"ad7266", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7266_id);
|
||||
|
||||
static struct spi_driver ad7266_driver = {
|
||||
.driver = {
|
||||
.name = "ad7266",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7266_probe,
|
||||
.remove = ad7266_remove,
|
||||
.id_table = ad7266_id,
|
||||
};
|
||||
module_spi_driver(ad7266_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7266/65 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
585
drivers/iio/adc/ad7291.c
Normal file
585
drivers/iio/adc/ad7291.c
Normal file
|
|
@ -0,0 +1,585 @@
|
|||
/*
|
||||
* AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
|
||||
#include <linux/platform_data/ad7291.h>
|
||||
|
||||
/*
|
||||
* Simplified handling
|
||||
*
|
||||
* If no events enabled - single polled channel read
|
||||
* If event enabled direct reads disable unless channel
|
||||
* is in the read mask.
|
||||
*
|
||||
* The noise-delayed bit as per datasheet suggestion is always enabled.
|
||||
*/
|
||||
|
||||
/*
|
||||
* AD7291 registers definition
|
||||
*/
|
||||
#define AD7291_COMMAND 0x00
|
||||
#define AD7291_VOLTAGE 0x01
|
||||
#define AD7291_T_SENSE 0x02
|
||||
#define AD7291_T_AVERAGE 0x03
|
||||
#define AD7291_DATA_HIGH(x) ((x) * 3 + 0x4)
|
||||
#define AD7291_DATA_LOW(x) ((x) * 3 + 0x5)
|
||||
#define AD7291_HYST(x) ((x) * 3 + 0x6)
|
||||
#define AD7291_VOLTAGE_ALERT_STATUS 0x1F
|
||||
#define AD7291_T_ALERT_STATUS 0x20
|
||||
|
||||
#define AD7291_BITS 12
|
||||
#define AD7291_VOLTAGE_LIMIT_COUNT 8
|
||||
|
||||
|
||||
/*
|
||||
* AD7291 command
|
||||
*/
|
||||
#define AD7291_AUTOCYCLE BIT(0)
|
||||
#define AD7291_RESET BIT(1)
|
||||
#define AD7291_ALERT_CLEAR BIT(2)
|
||||
#define AD7291_ALERT_POLARITY BIT(3)
|
||||
#define AD7291_EXT_REF BIT(4)
|
||||
#define AD7291_NOISE_DELAY BIT(5)
|
||||
#define AD7291_T_SENSE_MASK BIT(7)
|
||||
#define AD7291_VOLTAGE_MASK GENMASK(15, 8)
|
||||
#define AD7291_VOLTAGE_OFFSET 8
|
||||
|
||||
/*
|
||||
* AD7291 value masks
|
||||
*/
|
||||
#define AD7291_VALUE_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* AD7291 alert register bits
|
||||
*/
|
||||
#define AD7291_T_LOW BIT(0)
|
||||
#define AD7291_T_HIGH BIT(1)
|
||||
#define AD7291_T_AVG_LOW BIT(2)
|
||||
#define AD7291_T_AVG_HIGH BIT(3)
|
||||
#define AD7291_V_LOW(x) BIT((x) * 2)
|
||||
#define AD7291_V_HIGH(x) BIT((x) * 2 + 1)
|
||||
|
||||
|
||||
struct ad7291_chip_info {
|
||||
struct i2c_client *client;
|
||||
struct regulator *reg;
|
||||
u16 command;
|
||||
u16 c_mask; /* Active voltage channels for events */
|
||||
struct mutex state_lock;
|
||||
};
|
||||
|
||||
static int ad7291_i2c_read(struct ad7291_chip_info *chip, u8 reg, u16 *data)
|
||||
{
|
||||
struct i2c_client *client = chip->client;
|
||||
int ret = 0;
|
||||
|
||||
ret = i2c_smbus_read_word_swapped(client, reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "I2C read error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*data = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7291_i2c_write(struct ad7291_chip_info *chip, u8 reg, u16 data)
|
||||
{
|
||||
return i2c_smbus_write_word_swapped(chip->client, reg, data);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7291_event_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct ad7291_chip_info *chip = iio_priv(private);
|
||||
u16 t_status, v_status;
|
||||
u16 command;
|
||||
int i;
|
||||
s64 timestamp = iio_get_time_ns();
|
||||
|
||||
if (ad7291_i2c_read(chip, AD7291_T_ALERT_STATUS, &t_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (ad7291_i2c_read(chip, AD7291_VOLTAGE_ALERT_STATUS, &v_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(t_status || v_status))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
command = chip->command | AD7291_ALERT_CLEAR;
|
||||
ad7291_i2c_write(chip, AD7291_COMMAND, command);
|
||||
|
||||
command = chip->command & ~AD7291_ALERT_CLEAR;
|
||||
ad7291_i2c_write(chip, AD7291_COMMAND, command);
|
||||
|
||||
/* For now treat t_sense and t_sense_average the same */
|
||||
if ((t_status & AD7291_T_LOW) || (t_status & AD7291_T_AVG_LOW))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
if ((t_status & AD7291_T_HIGH) || (t_status & AD7291_T_AVG_HIGH))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
|
||||
for (i = 0; i < AD7291_VOLTAGE_LIMIT_COUNT; i++) {
|
||||
if (v_status & AD7291_V_LOW(i))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
timestamp);
|
||||
if (v_status & AD7291_V_HIGH(i))
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
i,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
timestamp);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static unsigned int ad7291_threshold_reg(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
offset = chan->channel;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
offset = AD7291_VOLTAGE_OFFSET;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
return AD7291_DATA_HIGH(offset);
|
||||
else
|
||||
return AD7291_DATA_LOW(offset);
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
return AD7291_HYST(offset);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7291_read_event_value(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 ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u16 uval;
|
||||
|
||||
ret = ad7291_i2c_read(chip, ad7291_threshold_reg(chan, dir, info),
|
||||
&uval);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE)
|
||||
*val = uval & AD7291_VALUE_MASK;
|
||||
|
||||
else
|
||||
*val = sign_extend32(uval, 11);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int ad7291_write_event_value(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 ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
if (info == IIO_EV_INFO_HYSTERESIS || chan->type == IIO_VOLTAGE) {
|
||||
if (val > AD7291_VALUE_MASK || val < 0)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (val > 2047 || val < -2048)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ad7291_i2c_write(chip, ad7291_threshold_reg(chan, dir, info),
|
||||
val);
|
||||
}
|
||||
|
||||
static int ad7291_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
/*
|
||||
* To be enabled the channel must simply be on. If any are enabled
|
||||
* we are in continuous sampling mode
|
||||
*/
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
return !!(chip->c_mask & BIT(15 - chan->channel));
|
||||
case IIO_TEMP:
|
||||
/* always on */
|
||||
return 1;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int ad7291_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
int ret = 0;
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
u16 regval;
|
||||
|
||||
mutex_lock(&chip->state_lock);
|
||||
regval = chip->command;
|
||||
/*
|
||||
* To be enabled the channel must simply be on. If any are enabled
|
||||
* use continuous sampling mode.
|
||||
* Possible to disable temp as well but that makes single read tricky.
|
||||
*/
|
||||
|
||||
mask = BIT(15 - chan->channel);
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if ((!state) && (chip->c_mask & mask))
|
||||
chip->c_mask &= ~mask;
|
||||
else if (state && (!(chip->c_mask & mask)))
|
||||
chip->c_mask |= mask;
|
||||
else
|
||||
break;
|
||||
|
||||
regval &= ~AD7291_AUTOCYCLE;
|
||||
regval |= chip->c_mask;
|
||||
if (chip->c_mask) /* Enable autocycle? */
|
||||
regval |= AD7291_AUTOCYCLE;
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
|
||||
chip->command = regval;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7291_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
u16 regval;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
mutex_lock(&chip->state_lock);
|
||||
/* If in autocycle mode drop through */
|
||||
if (chip->command & AD7291_AUTOCYCLE) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return -EBUSY;
|
||||
}
|
||||
/* Enable this channel alone */
|
||||
regval = chip->command & (~AD7291_VOLTAGE_MASK);
|
||||
regval |= BIT(15 - chan->channel);
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, regval);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
/* Read voltage */
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_VOLTAGE);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return ret;
|
||||
}
|
||||
*val = ret & AD7291_VALUE_MASK;
|
||||
mutex_unlock(&chip->state_lock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
/* Assumes tsense bit of command register always set */
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_T_SENSE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 11);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_AVERAGE_RAW:
|
||||
ret = i2c_smbus_read_word_swapped(chip->client,
|
||||
AD7291_T_AVERAGE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(ret, 11);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chip->reg) {
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(chip->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
*val = vref / 1000;
|
||||
} else {
|
||||
*val = 2500;
|
||||
}
|
||||
*val2 = AD7291_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/*
|
||||
* One LSB of the ADC corresponds to 0.25 deg C.
|
||||
* The temperature reading is in 12-bit twos
|
||||
* complement format
|
||||
*/
|
||||
*val = 250;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_event_spec ad7291_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS),
|
||||
},
|
||||
};
|
||||
|
||||
#define AD7291_VOLTAGE_CHAN(_chan) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.indexed = 1, \
|
||||
.channel = _chan, \
|
||||
.event_spec = ad7291_events, \
|
||||
.num_event_specs = ARRAY_SIZE(ad7291_events), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7291_channels[] = {
|
||||
AD7291_VOLTAGE_CHAN(0),
|
||||
AD7291_VOLTAGE_CHAN(1),
|
||||
AD7291_VOLTAGE_CHAN(2),
|
||||
AD7291_VOLTAGE_CHAN(3),
|
||||
AD7291_VOLTAGE_CHAN(4),
|
||||
AD7291_VOLTAGE_CHAN(5),
|
||||
AD7291_VOLTAGE_CHAN(6),
|
||||
AD7291_VOLTAGE_CHAN(7),
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.event_spec = ad7291_events,
|
||||
.num_event_specs = ARRAY_SIZE(ad7291_events),
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info ad7291_info = {
|
||||
.read_raw = &ad7291_read_raw,
|
||||
.read_event_config = &ad7291_read_event_config,
|
||||
.write_event_config = &ad7291_write_event_config,
|
||||
.read_event_value = &ad7291_read_event_value,
|
||||
.write_event_value = &ad7291_write_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7291_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ad7291_platform_data *pdata = client->dev.platform_data;
|
||||
struct ad7291_chip_info *chip;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
chip = iio_priv(indio_dev);
|
||||
|
||||
if (pdata && pdata->use_external_ref) {
|
||||
chip->reg = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(chip->reg))
|
||||
return PTR_ERR(chip->reg);
|
||||
|
||||
ret = regulator_enable(chip->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
mutex_init(&chip->state_lock);
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
chip->client = client;
|
||||
|
||||
chip->command = AD7291_NOISE_DELAY |
|
||||
AD7291_T_SENSE_MASK | /* Tsense always enabled */
|
||||
AD7291_ALERT_POLARITY; /* set irq polarity low level */
|
||||
|
||||
if (pdata && pdata->use_external_ref)
|
||||
chip->command |= AD7291_EXT_REF;
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->channels = ad7291_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7291_channels);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &ad7291_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, AD7291_RESET);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = ad7291_i2c_write(chip, AD7291_COMMAND, chip->command);
|
||||
if (ret) {
|
||||
ret = -EIO;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = request_threaded_irq(client->irq,
|
||||
NULL,
|
||||
&ad7291_event_handler,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
id->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unreg_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_unreg_irq:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
error_disable_reg:
|
||||
if (chip->reg)
|
||||
regulator_disable(chip->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7291_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ad7291_chip_info *chip = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
if (chip->reg)
|
||||
regulator_disable(chip->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7291_id[] = {
|
||||
{ "ad7291", 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad7291_id);
|
||||
|
||||
static struct i2c_driver ad7291_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = ad7291_probe,
|
||||
.remove = ad7291_remove,
|
||||
.id_table = ad7291_id,
|
||||
};
|
||||
module_i2c_driver(ad7291_driver);
|
||||
|
||||
MODULE_AUTHOR("Sonic Zhang <sonic.zhang@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7291 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
391
drivers/iio/adc/ad7298.c
Normal file
391
drivers/iio/adc/ad7298.c
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* AD7298 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/platform_data/ad7298.h>
|
||||
|
||||
#define AD7298_WRITE BIT(15) /* write to the control register */
|
||||
#define AD7298_REPEAT BIT(14) /* repeated conversion enable */
|
||||
#define AD7298_CH(x) BIT(13 - (x)) /* channel select */
|
||||
#define AD7298_TSENSE BIT(5) /* temperature conversion enable */
|
||||
#define AD7298_EXTREF BIT(2) /* external reference enable */
|
||||
#define AD7298_TAVG BIT(1) /* temperature sensor averaging enable */
|
||||
#define AD7298_PDD BIT(0) /* partial power down enable */
|
||||
|
||||
#define AD7298_MAX_CHAN 8
|
||||
#define AD7298_INTREF_mV 2500
|
||||
|
||||
#define AD7298_CH_TEMP 9
|
||||
|
||||
struct ad7298_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned ext_ref;
|
||||
struct spi_transfer ring_xfer[10];
|
||||
struct spi_transfer scan_single_xfer[3];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[12] ____cacheline_aligned;
|
||||
__be16 tx_buf[2];
|
||||
};
|
||||
|
||||
#define AD7298_V_CHAN(index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7298_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = AD7298_CH_TEMP,
|
||||
.scan_index = -1,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
},
|
||||
},
|
||||
AD7298_V_CHAN(0),
|
||||
AD7298_V_CHAN(1),
|
||||
AD7298_V_CHAN(2),
|
||||
AD7298_V_CHAN(3),
|
||||
AD7298_V_CHAN(4),
|
||||
AD7298_V_CHAN(5),
|
||||
AD7298_V_CHAN(6),
|
||||
AD7298_V_CHAN(7),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
};
|
||||
|
||||
/**
|
||||
* ad7298_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7298_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
int i, m;
|
||||
unsigned short command;
|
||||
int scan_count;
|
||||
|
||||
/* Now compute overall size */
|
||||
scan_count = bitmap_weight(active_scan_mask, indio_dev->masklength);
|
||||
|
||||
command = AD7298_WRITE | st->ext_ref;
|
||||
|
||||
for (i = 0, m = AD7298_CH(0); i < AD7298_MAX_CHAN; i++, m >>= 1)
|
||||
if (test_bit(i, active_scan_mask))
|
||||
command |= m;
|
||||
|
||||
st->tx_buf[0] = cpu_to_be16(command);
|
||||
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = 2;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
st->ring_xfer[1].tx_buf = &st->tx_buf[1];
|
||||
st->ring_xfer[1].len = 2;
|
||||
st->ring_xfer[1].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[1], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < scan_count; i++) {
|
||||
st->ring_xfer[i + 2].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 2].len = 2;
|
||||
st->ring_xfer[i + 2].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 2], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7298_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7298_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7298_scan_direct(struct ad7298_state *st, unsigned ch)
|
||||
{
|
||||
int ret;
|
||||
st->tx_buf[0] = cpu_to_be16(AD7298_WRITE | st->ext_ref |
|
||||
(AD7298_CH(0) >> ch));
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ad7298_scan_temp(struct ad7298_state *st, int *val)
|
||||
{
|
||||
int ret;
|
||||
__be16 buf;
|
||||
|
||||
buf = cpu_to_be16(AD7298_WRITE | AD7298_TSENSE |
|
||||
AD7298_TAVG | st->ext_ref);
|
||||
|
||||
ret = spi_write(st->spi, (u8 *)&buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
buf = cpu_to_be16(0);
|
||||
|
||||
ret = spi_write(st->spi, (u8 *)&buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
usleep_range(101, 1000); /* sleep > 100us */
|
||||
|
||||
ret = spi_read(st->spi, (u8 *)&buf, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(be16_to_cpu(buf), 11);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7298_get_ref_voltage(struct ad7298_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
if (st->ext_ref) {
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
return vref / 1000;
|
||||
} else {
|
||||
return AD7298_INTREF_mV;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad7298_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
if (chan->address == AD7298_CH_TEMP)
|
||||
ret = ad7298_scan_temp(st, val);
|
||||
else
|
||||
ret = ad7298_scan_direct(st, chan->address);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address != AD7298_CH_TEMP)
|
||||
*val = ret & GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
*val = ad7298_get_ref_voltage(st);
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
*val = ad7298_get_ref_voltage(st);
|
||||
*val2 = 10;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 1093 - 2732500 / ad7298_get_ref_voltage(st);
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7298_info = {
|
||||
.read_raw = &ad7298_read_raw,
|
||||
.update_scan_mode = ad7298_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7298_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7298_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7298_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
if (pdata && pdata->ext_ref)
|
||||
st->ext_ref = AD7298_EXTREF;
|
||||
|
||||
if (st->ext_ref) {
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad7298_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7298_channels);
|
||||
indio_dev->info = &ad7298_info;
|
||||
|
||||
/* Setup default message */
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].tx_buf = &st->tx_buf[1];
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
st->scan_single_xfer[1].cs_change = 1;
|
||||
st->scan_single_xfer[2].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[2].len = 2;
|
||||
|
||||
spi_message_init(&st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[2], &st->scan_single_msg);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7298_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (st->ext_ref)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7298_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7298_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (st->ext_ref)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7298_id[] = {
|
||||
{"ad7298", 0},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7298_id);
|
||||
|
||||
static struct spi_driver ad7298_driver = {
|
||||
.driver = {
|
||||
.name = "ad7298",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7298_probe,
|
||||
.remove = ad7298_remove,
|
||||
.id_table = ad7298_id,
|
||||
};
|
||||
module_spi_driver(ad7298_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
315
drivers/iio/adc/ad7476.c
Normal file
315
drivers/iio/adc/ad7476.c
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
/*
|
||||
* AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
struct ad7476_state;
|
||||
|
||||
struct ad7476_chip_info {
|
||||
unsigned int int_vref_uv;
|
||||
struct iio_chan_spec channel[2];
|
||||
void (*reset)(struct ad7476_state *);
|
||||
};
|
||||
|
||||
struct ad7476_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad7476_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer xfer;
|
||||
struct spi_message msg;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* Make the buffer large enough for one 16 bit sample and one 64 bit
|
||||
* aligned 64 bit timestamp.
|
||||
*/
|
||||
unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad7476_supported_device_ids {
|
||||
ID_AD7091R,
|
||||
ID_AD7276,
|
||||
ID_AD7277,
|
||||
ID_AD7278,
|
||||
ID_AD7466,
|
||||
ID_AD7467,
|
||||
ID_AD7468,
|
||||
ID_AD7495,
|
||||
ID_AD7940,
|
||||
};
|
||||
|
||||
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->msg);
|
||||
if (b_sent < 0)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7091_reset(struct ad7476_state *st)
|
||||
{
|
||||
/* Any transfers with 8 scl cycles will reset the device */
|
||||
spi_read(st->spi, st->data, 1);
|
||||
}
|
||||
|
||||
static int ad7476_scan_direct(struct ad7476_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = spi_sync(st->spi, &st->msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpup((__be16 *)st->data);
|
||||
}
|
||||
|
||||
static int ad7476_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7476_scan_direct(st);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> st->chip_info->channel[0].scan_type.shift) &
|
||||
GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (!st->chip_info->int_vref_uv) {
|
||||
scale_uv = regulator_get_voltage(st->reg);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
} else {
|
||||
scale_uv = st->chip_info->int_vref_uv;
|
||||
}
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define _AD7476_CHAN(bits, _shift, _info_mask_sep) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.info_mask_separate = _info_mask_sep, \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (_shift), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
|
||||
BIT(IIO_CHAN_INFO_RAW))
|
||||
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
|
||||
|
||||
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
|
||||
[ID_AD7091R] = {
|
||||
.channel[0] = AD7091R_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.reset = ad7091_reset,
|
||||
},
|
||||
[ID_AD7276] = {
|
||||
.channel[0] = AD7940_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7277] = {
|
||||
.channel[0] = AD7940_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7278] = {
|
||||
.channel[0] = AD7940_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7466] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7467] = {
|
||||
.channel[0] = AD7476_CHAN(10),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7468] = {
|
||||
.channel[0] = AD7476_CHAN(8),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
[ID_AD7495] = {
|
||||
.channel[0] = AD7476_CHAN(12),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
.int_vref_uv = 2500000,
|
||||
},
|
||||
[ID_AD7940] = {
|
||||
.channel[0] = AD7940_CHAN(14),
|
||||
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7476_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &ad7476_read_raw,
|
||||
};
|
||||
|
||||
static int ad7476_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7476_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->chip_info =
|
||||
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
/* Establish that the iio_dev is a child of the spi device */
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->info = &ad7476_info;
|
||||
/* Setup default message */
|
||||
|
||||
st->xfer.rx_buf = &st->data;
|
||||
st->xfer.len = st->chip_info->channel[0].scan_type.storagebits / 8;
|
||||
|
||||
spi_message_init(&st->msg);
|
||||
spi_message_add_tail(&st->xfer, &st->msg);
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7476_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
if (st->chip_info->reset)
|
||||
st->chip_info->reset(st);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_ring_unregister;
|
||||
return 0;
|
||||
|
||||
error_ring_unregister:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7476_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7476_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7476_id[] = {
|
||||
{"ad7091r", ID_AD7091R},
|
||||
{"ad7273", ID_AD7277},
|
||||
{"ad7274", ID_AD7276},
|
||||
{"ad7276", ID_AD7276},
|
||||
{"ad7277", ID_AD7277},
|
||||
{"ad7278", ID_AD7278},
|
||||
{"ad7466", ID_AD7466},
|
||||
{"ad7467", ID_AD7467},
|
||||
{"ad7468", ID_AD7468},
|
||||
{"ad7475", ID_AD7466},
|
||||
{"ad7476", ID_AD7466},
|
||||
{"ad7476a", ID_AD7466},
|
||||
{"ad7477", ID_AD7467},
|
||||
{"ad7477a", ID_AD7467},
|
||||
{"ad7478", ID_AD7468},
|
||||
{"ad7478a", ID_AD7468},
|
||||
{"ad7495", ID_AD7495},
|
||||
{"ad7910", ID_AD7467},
|
||||
{"ad7920", ID_AD7466},
|
||||
{"ad7940", ID_AD7940},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7476_id);
|
||||
|
||||
static struct spi_driver ad7476_driver = {
|
||||
.driver = {
|
||||
.name = "ad7476",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7476_probe,
|
||||
.remove = ad7476_remove,
|
||||
.id_table = ad7476_id,
|
||||
};
|
||||
module_spi_driver(ad7476_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
453
drivers/iio/adc/ad7791.c
Normal file
453
drivers/iio/adc/ad7791.c
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
|
||||
#include <linux/platform_data/ad7791.h>
|
||||
|
||||
#define AD7791_REG_COMM 0x0 /* For writes */
|
||||
#define AD7791_REG_STATUS 0x0 /* For reads */
|
||||
#define AD7791_REG_MODE 0x1
|
||||
#define AD7791_REG_FILTER 0x2
|
||||
#define AD7791_REG_DATA 0x3
|
||||
|
||||
#define AD7791_MODE_CONTINUOUS 0x00
|
||||
#define AD7791_MODE_SINGLE 0x02
|
||||
#define AD7791_MODE_POWERDOWN 0x03
|
||||
|
||||
#define AD7791_CH_AIN1P_AIN1N 0x00
|
||||
#define AD7791_CH_AIN2 0x01
|
||||
#define AD7791_CH_AIN1N_AIN1N 0x02
|
||||
#define AD7791_CH_AVDD_MONITOR 0x03
|
||||
|
||||
#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
|
||||
#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
|
||||
#define AD7791_FILTER_CLK_MASK (0x3 << 4)
|
||||
#define AD7791_FILTER_RATE_120 0x0
|
||||
#define AD7791_FILTER_RATE_100 0x1
|
||||
#define AD7791_FILTER_RATE_33_3 0x2
|
||||
#define AD7791_FILTER_RATE_20 0x3
|
||||
#define AD7791_FILTER_RATE_16_6 0x4
|
||||
#define AD7791_FILTER_RATE_16_7 0x5
|
||||
#define AD7791_FILTER_RATE_13_3 0x6
|
||||
#define AD7791_FILTER_RATE_9_5 0x7
|
||||
#define AD7791_FILTER_RATE_MASK 0x7
|
||||
|
||||
#define AD7791_MODE_BUFFER BIT(1)
|
||||
#define AD7791_MODE_UNIPOLAR BIT(2)
|
||||
#define AD7791_MODE_BURNOUT BIT(3)
|
||||
#define AD7791_MODE_SEL_MASK (0x3 << 6)
|
||||
#define AD7791_MODE_SEL(x) ((x) << 6)
|
||||
|
||||
#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
|
||||
(bits), (storagebits), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
|
||||
(bits), (storagebits), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
|
||||
static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
|
||||
static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
|
||||
|
||||
enum {
|
||||
AD7787,
|
||||
AD7788,
|
||||
AD7789,
|
||||
AD7790,
|
||||
AD7791,
|
||||
};
|
||||
|
||||
enum ad7791_chip_info_flags {
|
||||
AD7791_FLAG_HAS_FILTER = (1 << 0),
|
||||
AD7791_FLAG_HAS_BUFFER = (1 << 1),
|
||||
AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
|
||||
AD7791_FLAG_HAS_BURNOUT = (1 << 3),
|
||||
};
|
||||
|
||||
struct ad7791_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
enum ad7791_chip_info_flags flags;
|
||||
};
|
||||
|
||||
static const struct ad7791_chip_info ad7791_chip_infos[] = {
|
||||
[AD7787] = {
|
||||
.channels = ad7787_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7787_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7788] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7789] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_UNIPOLAR,
|
||||
},
|
||||
[AD7790] = {
|
||||
.channels = ad7790_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7790_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
[AD7791] = {
|
||||
.channels = ad7791_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7791_channels),
|
||||
.flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
|
||||
AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
|
||||
},
|
||||
};
|
||||
|
||||
struct ad7791_state {
|
||||
struct ad_sigma_delta sd;
|
||||
uint8_t mode;
|
||||
uint8_t filter;
|
||||
|
||||
struct regulator *reg;
|
||||
const struct ad7791_chip_info *info;
|
||||
};
|
||||
|
||||
static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
|
||||
{
|
||||
return container_of(sd, struct ad7791_state, sd);
|
||||
}
|
||||
|
||||
static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
|
||||
{
|
||||
ad_sd_set_comm(sd, channel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7791_set_mode(struct ad_sigma_delta *sd,
|
||||
enum ad_sigma_delta_mode mode)
|
||||
{
|
||||
struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
|
||||
|
||||
switch (mode) {
|
||||
case AD_SD_MODE_CONTINUOUS:
|
||||
mode = AD7791_MODE_CONTINUOUS;
|
||||
break;
|
||||
case AD_SD_MODE_SINGLE:
|
||||
mode = AD7791_MODE_SINGLE;
|
||||
break;
|
||||
case AD_SD_MODE_IDLE:
|
||||
case AD_SD_MODE_POWERDOWN:
|
||||
mode = AD7791_MODE_POWERDOWN;
|
||||
break;
|
||||
}
|
||||
|
||||
st->mode &= ~AD7791_MODE_SEL_MASK;
|
||||
st->mode |= AD7791_MODE_SEL(mode);
|
||||
|
||||
return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
|
||||
}
|
||||
|
||||
static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
|
||||
.set_channel = ad7791_set_channel,
|
||||
.set_mode = ad7791_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 4,
|
||||
.read_mask = BIT(3),
|
||||
};
|
||||
|
||||
static int ad7791_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/**
|
||||
* Unipolar: 0 to VREF
|
||||
* Bipolar -VREF to VREF
|
||||
**/
|
||||
if (unipolar)
|
||||
*val = 0;
|
||||
else
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* The monitor channel uses an internal reference. */
|
||||
if (chan->address == AD7791_CH_AVDD_MONITOR) {
|
||||
/*
|
||||
* The signal is attenuated by a factor of 5 and
|
||||
* compared against a 1.17V internal reference.
|
||||
*/
|
||||
*val = 1170 * 5;
|
||||
} else {
|
||||
int voltage_uv;
|
||||
|
||||
voltage_uv = regulator_get_voltage(st->reg);
|
||||
if (voltage_uv < 0)
|
||||
return voltage_uv;
|
||||
|
||||
*val = voltage_uv / 1000;
|
||||
}
|
||||
if (unipolar)
|
||||
*val2 = chan->scan_type.realbits;
|
||||
else
|
||||
*val2 = chan->scan_type.realbits - 1;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const char * const ad7791_sample_freq_avail[] = {
|
||||
[AD7791_FILTER_RATE_120] = "120",
|
||||
[AD7791_FILTER_RATE_100] = "100",
|
||||
[AD7791_FILTER_RATE_33_3] = "33.3",
|
||||
[AD7791_FILTER_RATE_20] = "20",
|
||||
[AD7791_FILTER_RATE_16_6] = "16.6",
|
||||
[AD7791_FILTER_RATE_16_7] = "16.7",
|
||||
[AD7791_FILTER_RATE_13_3] = "13.3",
|
||||
[AD7791_FILTER_RATE_9_5] = "9.5",
|
||||
};
|
||||
|
||||
static ssize_t ad7791_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
|
||||
|
||||
return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
|
||||
}
|
||||
|
||||
static ssize_t ad7791_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
|
||||
if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->filter &= ~AD7791_FILTER_RATE_MASK;
|
||||
st->filter |= i;
|
||||
ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
|
||||
sizeof(st->filter), st->filter);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7791_read_frequency,
|
||||
ad7791_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
|
||||
|
||||
static struct attribute *ad7791_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7791_attribute_group = {
|
||||
.attrs = ad7791_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.attrs = &ad7791_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7791_no_filter_info = {
|
||||
.read_raw = &ad7791_read_raw,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7791_setup(struct ad7791_state *st,
|
||||
struct ad7791_platform_data *pdata)
|
||||
{
|
||||
/* Set to poweron-reset default values */
|
||||
st->mode = AD7791_MODE_BUFFER;
|
||||
st->filter = AD7791_FILTER_RATE_16_6;
|
||||
|
||||
if (!pdata)
|
||||
return 0;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
|
||||
st->mode &= ~AD7791_MODE_BUFFER;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
|
||||
pdata->burnout_current)
|
||||
st->mode |= AD7791_MODE_BURNOUT;
|
||||
|
||||
if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
|
||||
st->mode |= AD7791_MODE_UNIPOLAR;
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
|
||||
st->mode);
|
||||
}
|
||||
|
||||
static int ad7791_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7791_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7791_state *st;
|
||||
int ret;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "Missing IRQ.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
if (st->info->flags & AD7791_FLAG_HAS_FILTER)
|
||||
indio_dev->info = &ad7791_info;
|
||||
else
|
||||
indio_dev->info = &ad7791_no_filter_info;
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad7791_setup(st, pdata);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7791_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7791_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7791_spi_ids[] = {
|
||||
{ "ad7787", AD7787 },
|
||||
{ "ad7788", AD7788 },
|
||||
{ "ad7789", AD7789 },
|
||||
{ "ad7790", AD7790 },
|
||||
{ "ad7791", AD7791 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
|
||||
|
||||
static struct spi_driver ad7791_driver = {
|
||||
.driver = {
|
||||
.name = "ad7791",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7791_probe,
|
||||
.remove = ad7791_remove,
|
||||
.id_table = ad7791_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad7791_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
865
drivers/iio/adc/ad7793.c
Normal file
865
drivers/iio/adc/ad7793.c
Normal file
|
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
* AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011-2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
#include <linux/platform_data/ad7793.h>
|
||||
|
||||
/* Registers */
|
||||
#define AD7793_REG_COMM 0 /* Communications Register (WO, 8-bit) */
|
||||
#define AD7793_REG_STAT 0 /* Status Register (RO, 8-bit) */
|
||||
#define AD7793_REG_MODE 1 /* Mode Register (RW, 16-bit */
|
||||
#define AD7793_REG_CONF 2 /* Configuration Register (RW, 16-bit) */
|
||||
#define AD7793_REG_DATA 3 /* Data Register (RO, 16-/24-bit) */
|
||||
#define AD7793_REG_ID 4 /* ID Register (RO, 8-bit) */
|
||||
#define AD7793_REG_IO 5 /* IO Register (RO, 8-bit) */
|
||||
#define AD7793_REG_OFFSET 6 /* Offset Register (RW, 16-bit
|
||||
* (AD7792)/24-bit (AD7793)) */
|
||||
#define AD7793_REG_FULLSALE 7 /* Full-Scale Register
|
||||
* (RW, 16-bit (AD7792)/24-bit (AD7793)) */
|
||||
|
||||
/* Communications Register Bit Designations (AD7793_REG_COMM) */
|
||||
#define AD7793_COMM_WEN (1 << 7) /* Write Enable */
|
||||
#define AD7793_COMM_WRITE (0 << 6) /* Write Operation */
|
||||
#define AD7793_COMM_READ (1 << 6) /* Read Operation */
|
||||
#define AD7793_COMM_ADDR(x) (((x) & 0x7) << 3) /* Register Address */
|
||||
#define AD7793_COMM_CREAD (1 << 2) /* Continuous Read of Data Register */
|
||||
|
||||
/* Status Register Bit Designations (AD7793_REG_STAT) */
|
||||
#define AD7793_STAT_RDY (1 << 7) /* Ready */
|
||||
#define AD7793_STAT_ERR (1 << 6) /* Error (Overrange, Underrange) */
|
||||
#define AD7793_STAT_CH3 (1 << 2) /* Channel 3 */
|
||||
#define AD7793_STAT_CH2 (1 << 1) /* Channel 2 */
|
||||
#define AD7793_STAT_CH1 (1 << 0) /* Channel 1 */
|
||||
|
||||
/* Mode Register Bit Designations (AD7793_REG_MODE) */
|
||||
#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */
|
||||
#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */
|
||||
#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */
|
||||
#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */
|
||||
|
||||
#define AD7793_MODE_CONT 0 /* Continuous Conversion Mode */
|
||||
#define AD7793_MODE_SINGLE 1 /* Single Conversion Mode */
|
||||
#define AD7793_MODE_IDLE 2 /* Idle Mode */
|
||||
#define AD7793_MODE_PWRDN 3 /* Power-Down Mode */
|
||||
#define AD7793_MODE_CAL_INT_ZERO 4 /* Internal Zero-Scale Calibration */
|
||||
#define AD7793_MODE_CAL_INT_FULL 5 /* Internal Full-Scale Calibration */
|
||||
#define AD7793_MODE_CAL_SYS_ZERO 6 /* System Zero-Scale Calibration */
|
||||
#define AD7793_MODE_CAL_SYS_FULL 7 /* System Full-Scale Calibration */
|
||||
|
||||
#define AD7793_CLK_INT 0 /* Internal 64 kHz Clock not
|
||||
* available at the CLK pin */
|
||||
#define AD7793_CLK_INT_CO 1 /* Internal 64 kHz Clock available
|
||||
* at the CLK pin */
|
||||
#define AD7793_CLK_EXT 2 /* External 64 kHz Clock */
|
||||
#define AD7793_CLK_EXT_DIV2 3 /* External Clock divided by 2 */
|
||||
|
||||
/* Configuration Register Bit Designations (AD7793_REG_CONF) */
|
||||
#define AD7793_CONF_VBIAS(x) (((x) & 0x3) << 14) /* Bias Voltage
|
||||
* Generator Enable */
|
||||
#define AD7793_CONF_BO_EN (1 << 13) /* Burnout Current Enable */
|
||||
#define AD7793_CONF_UNIPOLAR (1 << 12) /* Unipolar/Bipolar Enable */
|
||||
#define AD7793_CONF_BOOST (1 << 11) /* Boost Enable */
|
||||
#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */
|
||||
#define AD7793_CONF_REFSEL(x) ((x) << 6) /* INT/EXT Reference Select */
|
||||
#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */
|
||||
#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */
|
||||
#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */
|
||||
|
||||
#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */
|
||||
#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */
|
||||
#define AD7793_CH_AIN3P_AIN3M 2 /* AIN3(+) - AIN3(-) */
|
||||
#define AD7793_CH_AIN1M_AIN1M 3 /* AIN1(-) - AIN1(-) */
|
||||
#define AD7793_CH_TEMP 6 /* Temp Sensor */
|
||||
#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */
|
||||
|
||||
#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */
|
||||
#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */
|
||||
#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */
|
||||
#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */
|
||||
|
||||
/* ID Register Bit Designations (AD7793_REG_ID) */
|
||||
#define AD7785_ID 0xB
|
||||
#define AD7792_ID 0xA
|
||||
#define AD7793_ID 0xB
|
||||
#define AD7794_ID 0xF
|
||||
#define AD7795_ID 0xF
|
||||
#define AD7796_ID 0xA
|
||||
#define AD7797_ID 0xB
|
||||
#define AD7798_ID 0x8
|
||||
#define AD7799_ID 0x9
|
||||
#define AD7793_ID_MASK 0xF
|
||||
|
||||
/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */
|
||||
#define AD7793_IO_IEXC1_IOUT1_IEXC2_IOUT2 0 /* IEXC1 connect to IOUT1,
|
||||
* IEXC2 connect to IOUT2 */
|
||||
#define AD7793_IO_IEXC1_IOUT2_IEXC2_IOUT1 1 /* IEXC1 connect to IOUT2,
|
||||
* IEXC2 connect to IOUT1 */
|
||||
#define AD7793_IO_IEXC1_IEXC2_IOUT1 2 /* Both current sources
|
||||
* IEXC1,2 connect to IOUT1 */
|
||||
#define AD7793_IO_IEXC1_IEXC2_IOUT2 3 /* Both current sources
|
||||
* IEXC1,2 connect to IOUT2 */
|
||||
|
||||
#define AD7793_IO_IXCEN_10uA (1 << 0) /* Excitation Current 10uA */
|
||||
#define AD7793_IO_IXCEN_210uA (2 << 0) /* Excitation Current 210uA */
|
||||
#define AD7793_IO_IXCEN_1mA (3 << 0) /* Excitation Current 1mA */
|
||||
|
||||
/* NOTE:
|
||||
* The AD7792/AD7793 features a dual use data out ready DOUT/RDY output.
|
||||
* In order to avoid contentions on the SPI bus, it's therefore necessary
|
||||
* to use spi bus locking.
|
||||
*
|
||||
* The DOUT/RDY output must also be wired to an interrupt capable GPIO.
|
||||
*/
|
||||
|
||||
#define AD7793_FLAG_HAS_CLKSEL BIT(0)
|
||||
#define AD7793_FLAG_HAS_REFSEL BIT(1)
|
||||
#define AD7793_FLAG_HAS_VBIAS BIT(2)
|
||||
#define AD7793_HAS_EXITATION_CURRENT BIT(3)
|
||||
#define AD7793_FLAG_HAS_GAIN BIT(4)
|
||||
#define AD7793_FLAG_HAS_BUFFER BIT(5)
|
||||
|
||||
struct ad7793_chip_info {
|
||||
unsigned int id;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
unsigned int flags;
|
||||
|
||||
const struct iio_info *iio_info;
|
||||
const u16 *sample_freq_avail;
|
||||
};
|
||||
|
||||
struct ad7793_state {
|
||||
const struct ad7793_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
u16 int_vref_mv;
|
||||
u16 mode;
|
||||
u16 conf;
|
||||
u32 scale_avail[8][2];
|
||||
|
||||
struct ad_sigma_delta sd;
|
||||
|
||||
};
|
||||
|
||||
enum ad7793_supported_device_ids {
|
||||
ID_AD7785,
|
||||
ID_AD7792,
|
||||
ID_AD7793,
|
||||
ID_AD7794,
|
||||
ID_AD7795,
|
||||
ID_AD7796,
|
||||
ID_AD7797,
|
||||
ID_AD7798,
|
||||
ID_AD7799,
|
||||
};
|
||||
|
||||
static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd)
|
||||
{
|
||||
return container_of(sd, struct ad7793_state, sd);
|
||||
}
|
||||
|
||||
static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
|
||||
{
|
||||
struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
|
||||
|
||||
st->conf &= ~AD7793_CONF_CHAN_MASK;
|
||||
st->conf |= AD7793_CONF_CHAN(channel);
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf);
|
||||
}
|
||||
|
||||
static int ad7793_set_mode(struct ad_sigma_delta *sd,
|
||||
enum ad_sigma_delta_mode mode)
|
||||
{
|
||||
struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
|
||||
|
||||
st->mode &= ~AD7793_MODE_SEL_MASK;
|
||||
st->mode |= AD7793_MODE_SEL(mode);
|
||||
|
||||
return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode);
|
||||
}
|
||||
|
||||
static const struct ad_sigma_delta_info ad7793_sigma_delta_info = {
|
||||
.set_channel = ad7793_set_channel,
|
||||
.set_mode = ad7793_set_mode,
|
||||
.has_registers = true,
|
||||
.addr_shift = 3,
|
||||
.read_mask = BIT(6),
|
||||
};
|
||||
|
||||
static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
|
||||
{AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M},
|
||||
{AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M},
|
||||
{AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M},
|
||||
{AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN2P_AIN2M},
|
||||
{AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN3P_AIN3M},
|
||||
{AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN3P_AIN3M}
|
||||
};
|
||||
|
||||
static int ad7793_calibrate_all(struct ad7793_state *st)
|
||||
{
|
||||
return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr,
|
||||
ARRAY_SIZE(ad7793_calib_arr));
|
||||
}
|
||||
|
||||
static int ad7793_check_platform_data(struct ad7793_state *st,
|
||||
const struct ad7793_platform_data *pdata)
|
||||
{
|
||||
if ((pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT1 ||
|
||||
pdata->current_source_direction == AD7793_IEXEC1_IEXEC2_IOUT2) &&
|
||||
((pdata->exitation_current != AD7793_IX_10uA) &&
|
||||
(pdata->exitation_current != AD7793_IX_210uA)))
|
||||
return -EINVAL;
|
||||
|
||||
if (!(st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL) &&
|
||||
pdata->clock_src != AD7793_CLK_SRC_INT)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(st->chip_info->flags & AD7793_FLAG_HAS_REFSEL) &&
|
||||
pdata->refsel != AD7793_REFSEL_REFIN1)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS) &&
|
||||
pdata->bias_voltage != AD7793_BIAS_VOLTAGE_DISABLED)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) &&
|
||||
pdata->exitation_current != AD7793_IX_DISABLED)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7793_setup(struct iio_dev *indio_dev,
|
||||
const struct ad7793_platform_data *pdata,
|
||||
unsigned int vref_mv)
|
||||
{
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
int i, ret = -1;
|
||||
unsigned long long scale_uv;
|
||||
u32 id;
|
||||
|
||||
ret = ad7793_check_platform_data(st, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* reset the serial interface */
|
||||
ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
usleep_range(500, 2000); /* Wait for at least 500us */
|
||||
|
||||
/* write/read test for device presence */
|
||||
ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
id &= AD7793_ID_MASK;
|
||||
|
||||
if (id != st->chip_info->id) {
|
||||
dev_err(&st->sd.spi->dev, "device ID query failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
st->mode = AD7793_MODE_RATE(1);
|
||||
st->conf = 0;
|
||||
|
||||
if (st->chip_info->flags & AD7793_FLAG_HAS_CLKSEL)
|
||||
st->mode |= AD7793_MODE_CLKSRC(pdata->clock_src);
|
||||
if (st->chip_info->flags & AD7793_FLAG_HAS_REFSEL)
|
||||
st->conf |= AD7793_CONF_REFSEL(pdata->refsel);
|
||||
if (st->chip_info->flags & AD7793_FLAG_HAS_VBIAS)
|
||||
st->conf |= AD7793_CONF_VBIAS(pdata->bias_voltage);
|
||||
if (pdata->buffered || !(st->chip_info->flags & AD7793_FLAG_HAS_BUFFER))
|
||||
st->conf |= AD7793_CONF_BUF;
|
||||
if (pdata->boost_enable &&
|
||||
(st->chip_info->flags & AD7793_FLAG_HAS_VBIAS))
|
||||
st->conf |= AD7793_CONF_BOOST;
|
||||
if (pdata->burnout_current)
|
||||
st->conf |= AD7793_CONF_BO_EN;
|
||||
if (pdata->unipolar)
|
||||
st->conf |= AD7793_CONF_UNIPOLAR;
|
||||
|
||||
if (!(st->chip_info->flags & AD7793_FLAG_HAS_GAIN))
|
||||
st->conf |= AD7793_CONF_GAIN(7);
|
||||
|
||||
ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = ad7793_set_channel(&st->sd, 0);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (st->chip_info->flags & AD7793_HAS_EXITATION_CURRENT) {
|
||||
ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO, 1,
|
||||
pdata->exitation_current |
|
||||
(pdata->current_source_direction << 2));
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ad7793_calibrate_all(st);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* Populate available ADC input ranges */
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
|
||||
scale_uv = ((u64)vref_mv * 100000000)
|
||||
>> (st->chip_info->channels[0].scan_type.realbits -
|
||||
(!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1));
|
||||
scale_uv >>= i;
|
||||
|
||||
st->scale_avail[i][1] = do_div(scale_uv, 100000000) * 10;
|
||||
st->scale_avail[i][0] = scale_uv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
dev_err(&st->sd.spi->dev, "setup failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const u16 ad7793_sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39,
|
||||
33, 19, 17, 16, 12, 10, 8, 6, 4};
|
||||
|
||||
static const u16 ad7797_sample_freq_avail[16] = {0, 0, 0, 123, 62, 50, 0,
|
||||
33, 0, 17, 16, 12, 10, 8, 6, 4};
|
||||
|
||||
static ssize_t ad7793_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
st->chip_info->sample_freq_avail[AD7793_MODE_RATE(st->mode)]);
|
||||
}
|
||||
|
||||
static ssize_t ad7793_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
long lval;
|
||||
int i, ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
ret = kstrtol(buf, 10, &lval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (lval == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
if (lval == st->chip_info->sample_freq_avail[i]) {
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->mode &= ~AD7793_MODE_RATE(-1);
|
||||
st->mode |= AD7793_MODE_RATE(i);
|
||||
ad_sd_write_reg(&st->sd, AD7793_REG_MODE,
|
||||
sizeof(st->mode), st->mode);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad7793_read_frequency,
|
||||
ad7793_write_frequency);
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
|
||||
"470 242 123 62 50 39 33 19 17 16 12 10 8 6 4");
|
||||
|
||||
static IIO_CONST_ATTR_NAMED(sampling_frequency_available_ad7797,
|
||||
sampling_frequency_available, "123 62 50 33 17 16 12 10 8 6 4");
|
||||
|
||||
static ssize_t ad7793_show_scale_available(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
int i, len = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
len += sprintf(buf + len, "%d.%09u ", st->scale_avail[i][0],
|
||||
st->scale_avail[i][1]);
|
||||
|
||||
len += sprintf(buf + len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available,
|
||||
in_voltage-voltage_scale_available, S_IRUGO,
|
||||
ad7793_show_scale_available, NULL, 0);
|
||||
|
||||
static struct attribute *ad7793_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_m_in_scale_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7793_attribute_group = {
|
||||
.attrs = ad7793_attributes,
|
||||
};
|
||||
|
||||
static struct attribute *ad7797_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available_ad7797.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7797_attribute_group = {
|
||||
.attrs = ad7797_attributes,
|
||||
};
|
||||
|
||||
static int ad7793_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned long long scale_uv;
|
||||
bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->differential) {
|
||||
*val = st->
|
||||
scale_avail[(st->conf >> 8) & 0x7][0];
|
||||
*val2 = st->
|
||||
scale_avail[(st->conf >> 8) & 0x7][1];
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
} else {
|
||||
/* 1170mV / 2^23 * 6 */
|
||||
scale_uv = (1170ULL * 1000000000ULL * 6ULL);
|
||||
}
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
/* 1170mV / 0.81 mV/C / 2^23 */
|
||||
scale_uv = 1444444444444444ULL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1));
|
||||
*val = 0;
|
||||
*val2 = scale_uv;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (!unipolar)
|
||||
*val = -(1 << (chan->scan_type.realbits - 1));
|
||||
else
|
||||
*val = 0;
|
||||
|
||||
/* Kelvin to Celsius */
|
||||
if (chan->type == IIO_TEMP) {
|
||||
unsigned long long offset;
|
||||
unsigned int shift;
|
||||
|
||||
shift = chan->scan_type.realbits - (unipolar ? 0 : 1);
|
||||
offset = 273ULL << shift;
|
||||
do_div(offset, 1444);
|
||||
*val -= offset;
|
||||
}
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7793_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
int ret, i;
|
||||
unsigned int tmp;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = -EINVAL;
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
if (val2 == st->scale_avail[i][1]) {
|
||||
ret = 0;
|
||||
tmp = st->conf;
|
||||
st->conf &= ~AD7793_CONF_GAIN(-1);
|
||||
st->conf |= AD7793_CONF_GAIN(i);
|
||||
|
||||
if (tmp == st->conf)
|
||||
break;
|
||||
|
||||
ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
|
||||
sizeof(st->conf), st->conf);
|
||||
ad7793_calibrate_all(st);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7793_info = {
|
||||
.read_raw = &ad7793_read_raw,
|
||||
.write_raw = &ad7793_write_raw,
|
||||
.write_raw_get_fmt = &ad7793_write_raw_get_fmt,
|
||||
.attrs = &ad7793_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7797_info = {
|
||||
.read_raw = &ad7793_read_raw,
|
||||
.write_raw = &ad7793_write_raw,
|
||||
.write_raw_get_fmt = &ad7793_write_raw_get_fmt,
|
||||
.attrs = &ad7793_attribute_group,
|
||||
.validate_trigger = ad_sd_validate_trigger,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \
|
||||
AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \
|
||||
AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \
|
||||
AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \
|
||||
AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \
|
||||
AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(6), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(9), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7797_CHANNELS(_name, _b, _sb) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(1, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_TEMP_CHANNEL(2, AD7793_CH_TEMP, (_b), (_sb), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(3, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7799_CHANNELS(_name, _b, _sb) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \
|
||||
AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \
|
||||
AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
|
||||
AD_SD_SUPPLY_CHANNEL(4, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4);
|
||||
static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0);
|
||||
static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0);
|
||||
static DECLARE_AD7795_CHANNELS(ad7794, 16, 32);
|
||||
static DECLARE_AD7795_CHANNELS(ad7795, 24, 32);
|
||||
static DECLARE_AD7797_CHANNELS(ad7796, 16, 16);
|
||||
static DECLARE_AD7797_CHANNELS(ad7797, 24, 32);
|
||||
static DECLARE_AD7799_CHANNELS(ad7798, 16, 16);
|
||||
static DECLARE_AD7799_CHANNELS(ad7799, 24, 32);
|
||||
|
||||
static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
|
||||
[ID_AD7785] = {
|
||||
.id = AD7785_ID,
|
||||
.channels = ad7785_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7785_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL |
|
||||
AD7793_FLAG_HAS_REFSEL |
|
||||
AD7793_FLAG_HAS_VBIAS |
|
||||
AD7793_HAS_EXITATION_CURRENT |
|
||||
AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7792] = {
|
||||
.id = AD7792_ID,
|
||||
.channels = ad7792_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7792_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL |
|
||||
AD7793_FLAG_HAS_REFSEL |
|
||||
AD7793_FLAG_HAS_VBIAS |
|
||||
AD7793_HAS_EXITATION_CURRENT |
|
||||
AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7793] = {
|
||||
.id = AD7793_ID,
|
||||
.channels = ad7793_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7793_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL |
|
||||
AD7793_FLAG_HAS_REFSEL |
|
||||
AD7793_FLAG_HAS_VBIAS |
|
||||
AD7793_HAS_EXITATION_CURRENT |
|
||||
AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7794] = {
|
||||
.id = AD7794_ID,
|
||||
.channels = ad7794_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7794_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL |
|
||||
AD7793_FLAG_HAS_REFSEL |
|
||||
AD7793_FLAG_HAS_VBIAS |
|
||||
AD7793_HAS_EXITATION_CURRENT |
|
||||
AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7795] = {
|
||||
.id = AD7795_ID,
|
||||
.channels = ad7795_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7795_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL |
|
||||
AD7793_FLAG_HAS_REFSEL |
|
||||
AD7793_FLAG_HAS_VBIAS |
|
||||
AD7793_HAS_EXITATION_CURRENT |
|
||||
AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7796] = {
|
||||
.id = AD7796_ID,
|
||||
.channels = ad7796_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7796_channels),
|
||||
.iio_info = &ad7797_info,
|
||||
.sample_freq_avail = ad7797_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL,
|
||||
},
|
||||
[ID_AD7797] = {
|
||||
.id = AD7797_ID,
|
||||
.channels = ad7797_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7797_channels),
|
||||
.iio_info = &ad7797_info,
|
||||
.sample_freq_avail = ad7797_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_CLKSEL,
|
||||
},
|
||||
[ID_AD7798] = {
|
||||
.id = AD7798_ID,
|
||||
.channels = ad7798_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7798_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
[ID_AD7799] = {
|
||||
.id = AD7799_ID,
|
||||
.channels = ad7799_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7799_channels),
|
||||
.iio_info = &ad7793_info,
|
||||
.sample_freq_avail = ad7793_sample_freq_avail,
|
||||
.flags = AD7793_FLAG_HAS_GAIN |
|
||||
AD7793_FLAG_HAS_BUFFER,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad7793_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct ad7793_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7793_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, vref_mv = 0;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "no platform data?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_err(&spi->dev, "no IRQ?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info);
|
||||
|
||||
if (pdata->refsel != AD7793_REFSEL_INTERNAL) {
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vref_mv = regulator_get_voltage(st->reg);
|
||||
if (vref_mv < 0) {
|
||||
ret = vref_mv;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
vref_mv /= 1000;
|
||||
} else {
|
||||
vref_mv = 1170; /* Build-in ref */
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
indio_dev->info = st->chip_info->iio_info;
|
||||
|
||||
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = ad7793_setup(indio_dev, pdata, vref_mv);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
error_disable_reg:
|
||||
if (pdata->refsel != AD7793_REFSEL_INTERNAL)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7793_remove(struct spi_device *spi)
|
||||
{
|
||||
const struct ad7793_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7793_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
ad_sd_cleanup_buffer_and_trigger(indio_dev);
|
||||
|
||||
if (pdata->refsel != AD7793_REFSEL_INTERNAL)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7793_id[] = {
|
||||
{"ad7785", ID_AD7785},
|
||||
{"ad7792", ID_AD7792},
|
||||
{"ad7793", ID_AD7793},
|
||||
{"ad7794", ID_AD7794},
|
||||
{"ad7795", ID_AD7795},
|
||||
{"ad7796", ID_AD7796},
|
||||
{"ad7797", ID_AD7797},
|
||||
{"ad7798", ID_AD7798},
|
||||
{"ad7799", ID_AD7799},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7793_id);
|
||||
|
||||
static struct spi_driver ad7793_driver = {
|
||||
.driver = {
|
||||
.name = "ad7793",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7793_probe,
|
||||
.remove = ad7793_remove,
|
||||
.id_table = ad7793_id,
|
||||
};
|
||||
module_spi_driver(ad7793_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
369
drivers/iio/adc/ad7887.c
Normal file
369
drivers/iio/adc/ad7887.c
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* AD7887 SPI ADC driver
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#include <linux/platform_data/ad7887.h>
|
||||
|
||||
#define AD7887_REF_DIS BIT(5) /* on-chip reference disable */
|
||||
#define AD7887_DUAL BIT(4) /* dual-channel mode */
|
||||
#define AD7887_CH_AIN1 BIT(3) /* convert on channel 1, DUAL=1 */
|
||||
#define AD7887_CH_AIN0 0 /* convert on channel 0, DUAL=0,1 */
|
||||
#define AD7887_PM_MODE1 0 /* CS based shutdown */
|
||||
#define AD7887_PM_MODE2 1 /* full on */
|
||||
#define AD7887_PM_MODE3 2 /* auto shutdown after conversion */
|
||||
#define AD7887_PM_MODE4 3 /* standby mode */
|
||||
|
||||
enum ad7887_channels {
|
||||
AD7887_CH0,
|
||||
AD7887_CH0_CH1,
|
||||
AD7887_CH1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad7887_chip_info - chip specifc information
|
||||
* @int_vref_mv: the internal reference voltage
|
||||
* @channel: channel specification
|
||||
*/
|
||||
struct ad7887_chip_info {
|
||||
u16 int_vref_mv;
|
||||
struct iio_chan_spec channel[3];
|
||||
};
|
||||
|
||||
struct ad7887_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad7887_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
struct spi_transfer xfer[4];
|
||||
struct spi_message msg[3];
|
||||
struct spi_message *ring_msg;
|
||||
unsigned char tx_cmd_buf[4];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
* Buffer needs to be large enough to hold two 16 bit samples and a
|
||||
* 64 bit aligned 64 bit timestamp.
|
||||
*/
|
||||
unsigned char data[ALIGN(4, sizeof(s64)) + sizeof(s64)]
|
||||
____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad7887_supported_device_ids {
|
||||
ID_AD7887
|
||||
};
|
||||
|
||||
static int ad7887_ring_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* We know this is a single long so can 'cheat' */
|
||||
switch (*indio_dev->active_scan_mask) {
|
||||
case (1 << 0):
|
||||
st->ring_msg = &st->msg[AD7887_CH0];
|
||||
break;
|
||||
case (1 << 1):
|
||||
st->ring_msg = &st->msg[AD7887_CH1];
|
||||
/* Dummy read: push CH1 setting down to hardware */
|
||||
spi_sync(st->spi, st->ring_msg);
|
||||
break;
|
||||
case ((1 << 1) | (1 << 0)):
|
||||
st->ring_msg = &st->msg[AD7887_CH0_CH1];
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7887_ring_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
|
||||
/* dummy read: restore default CH0 settin */
|
||||
return spi_sync(st->spi, &st->msg[AD7887_CH0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7887_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7887_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->data,
|
||||
iio_get_time_ns());
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad7887_ring_setup_ops = {
|
||||
.preenable = &ad7887_ring_preenable,
|
||||
.postenable = &iio_triggered_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad7887_ring_postdisable,
|
||||
};
|
||||
|
||||
static int ad7887_scan_direct(struct ad7887_state *st, unsigned ch)
|
||||
{
|
||||
int ret = spi_sync(st->spi, &st->msg[ch]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return (st->data[(ch * 2)] << 8) | st->data[(ch * 2) + 1];
|
||||
}
|
||||
|
||||
static int ad7887_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7887_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> chan->scan_type.shift;
|
||||
*val &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (st->reg) {
|
||||
*val = regulator_get_voltage(st->reg);
|
||||
if (*val < 0)
|
||||
return *val;
|
||||
*val /= 1000;
|
||||
} else {
|
||||
*val = st->chip_info->int_vref_mv;
|
||||
}
|
||||
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {
|
||||
/*
|
||||
* More devices added in future
|
||||
*/
|
||||
[ID_AD7887] = {
|
||||
.channel[0] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 1,
|
||||
.scan_index = 1,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.channel[1] = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = 0,
|
||||
.scan_index = 0,
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 12,
|
||||
.storagebits = 16,
|
||||
.shift = 0,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
.channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info ad7887_info = {
|
||||
.read_raw = &ad7887_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7887_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7887_platform_data *pdata = spi->dev.platform_data;
|
||||
struct ad7887_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
uint8_t mode;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
if (!pdata || !pdata->use_onchip_ref) {
|
||||
st->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
/* Estabilish that the iio_dev is a child of the spi device */
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad7887_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
/* Setup default message */
|
||||
|
||||
mode = AD7887_PM_MODE4;
|
||||
if (!pdata || !pdata->use_onchip_ref)
|
||||
mode |= AD7887_REF_DIS;
|
||||
if (pdata && pdata->en_dual)
|
||||
mode |= AD7887_DUAL;
|
||||
|
||||
st->tx_cmd_buf[0] = AD7887_CH_AIN0 | mode;
|
||||
|
||||
st->xfer[0].rx_buf = &st->data[0];
|
||||
st->xfer[0].tx_buf = &st->tx_cmd_buf[0];
|
||||
st->xfer[0].len = 2;
|
||||
|
||||
spi_message_init(&st->msg[AD7887_CH0]);
|
||||
spi_message_add_tail(&st->xfer[0], &st->msg[AD7887_CH0]);
|
||||
|
||||
if (pdata && pdata->en_dual) {
|
||||
st->tx_cmd_buf[2] = AD7887_CH_AIN1 | mode;
|
||||
|
||||
st->xfer[1].rx_buf = &st->data[0];
|
||||
st->xfer[1].tx_buf = &st->tx_cmd_buf[2];
|
||||
st->xfer[1].len = 2;
|
||||
|
||||
st->xfer[2].rx_buf = &st->data[2];
|
||||
st->xfer[2].tx_buf = &st->tx_cmd_buf[0];
|
||||
st->xfer[2].len = 2;
|
||||
|
||||
spi_message_init(&st->msg[AD7887_CH0_CH1]);
|
||||
spi_message_add_tail(&st->xfer[1], &st->msg[AD7887_CH0_CH1]);
|
||||
spi_message_add_tail(&st->xfer[2], &st->msg[AD7887_CH0_CH1]);
|
||||
|
||||
st->xfer[3].rx_buf = &st->data[2];
|
||||
st->xfer[3].tx_buf = &st->tx_cmd_buf[2];
|
||||
st->xfer[3].len = 2;
|
||||
|
||||
spi_message_init(&st->msg[AD7887_CH1]);
|
||||
spi_message_add_tail(&st->xfer[3], &st->msg[AD7887_CH1]);
|
||||
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = 3;
|
||||
} else {
|
||||
indio_dev->channels = &st->chip_info->channel[1];
|
||||
indio_dev->num_channels = 2;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad7887_trigger_handler, &ad7887_ring_setup_ops);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_unregister_ring;
|
||||
|
||||
return 0;
|
||||
error_unregister_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
if (st->reg)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7887_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7887_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
if (st->reg)
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7887_id[] = {
|
||||
{"ad7887", ID_AD7887},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7887_id);
|
||||
|
||||
static struct spi_driver ad7887_driver = {
|
||||
.driver = {
|
||||
.name = "ad7887",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7887_probe,
|
||||
.remove = ad7887_remove,
|
||||
.id_table = ad7887_id,
|
||||
};
|
||||
module_spi_driver(ad7887_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
371
drivers/iio/adc/ad7923.c
Normal file
371
drivers/iio/adc/ad7923.c
Normal file
|
|
@ -0,0 +1,371 @@
|
|||
/*
|
||||
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD7923_WRITE_CR (1 << 11) /* write control register */
|
||||
#define AD7923_RANGE (1 << 1) /* range to REFin */
|
||||
#define AD7923_CODING (1 << 0) /* coding is straight binary */
|
||||
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
|
||||
#define AD7923_PM_MODE_FS (2) /* full shutdown */
|
||||
#define AD7923_PM_MODE_OPS (3) /* normal operation */
|
||||
#define AD7923_CHANNEL_0 (0) /* analog input 0 */
|
||||
#define AD7923_CHANNEL_1 (1) /* analog input 1 */
|
||||
#define AD7923_CHANNEL_2 (2) /* analog input 2 */
|
||||
#define AD7923_CHANNEL_3 (3) /* analog input 3 */
|
||||
#define AD7923_SEQUENCE_OFF (0) /* no sequence fonction */
|
||||
#define AD7923_SEQUENCE_PROTECT (2) /* no interrupt write cycle */
|
||||
#define AD7923_SEQUENCE_ON (3) /* continuous sequence */
|
||||
|
||||
#define AD7923_MAX_CHAN 4
|
||||
|
||||
#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
|
||||
#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
|
||||
#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
|
||||
+ ((sequence & 2) << 9))
|
||||
/* write sequence fonction */
|
||||
/* left shift for CR : bit 11 transmit in first */
|
||||
#define AD7923_SHIFT_REGISTER 4
|
||||
|
||||
/* val = value, dec = left shift, bits = number of bits of the mask */
|
||||
#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
|
||||
|
||||
struct ad7923_state {
|
||||
struct spi_device *spi;
|
||||
struct spi_transfer ring_xfer[5];
|
||||
struct spi_transfer scan_single_xfer[2];
|
||||
struct spi_message ring_msg;
|
||||
struct spi_message scan_single_msg;
|
||||
|
||||
struct regulator *reg;
|
||||
|
||||
unsigned int settings;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 rx_buf[4] ____cacheline_aligned;
|
||||
__be16 tx_buf[4];
|
||||
};
|
||||
|
||||
struct ad7923_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
enum ad7923_id {
|
||||
AD7904,
|
||||
AD7914,
|
||||
AD7924,
|
||||
};
|
||||
|
||||
#define AD7923_V_CHAN(index, bits) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = index, \
|
||||
.scan_index = index, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD7923_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name ## _channels[] = { \
|
||||
AD7923_V_CHAN(0, bits), \
|
||||
AD7923_V_CHAN(1, bits), \
|
||||
AD7923_V_CHAN(2, bits), \
|
||||
AD7923_V_CHAN(3, bits), \
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4), \
|
||||
}
|
||||
|
||||
static DECLARE_AD7923_CHANNELS(ad7904, 8);
|
||||
static DECLARE_AD7923_CHANNELS(ad7914, 10);
|
||||
static DECLARE_AD7923_CHANNELS(ad7924, 12);
|
||||
|
||||
static const struct ad7923_chip_info ad7923_chip_info[] = {
|
||||
[AD7904] = {
|
||||
.channels = ad7904_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7904_channels),
|
||||
},
|
||||
[AD7914] = {
|
||||
.channels = ad7914_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7914_channels),
|
||||
},
|
||||
[AD7924] = {
|
||||
.channels = ad7924_channels,
|
||||
.num_channels = ARRAY_SIZE(ad7924_channels),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
|
||||
**/
|
||||
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *active_scan_mask)
|
||||
{
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int i, cmd, len;
|
||||
|
||||
len = 0;
|
||||
for_each_set_bit(i, active_scan_mask, AD7923_MAX_CHAN) {
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(i) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[len++] = cpu_to_be16(cmd);
|
||||
}
|
||||
/* build spi ring message */
|
||||
st->ring_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->ring_xfer[0].len = len;
|
||||
st->ring_xfer[0].cs_change = 1;
|
||||
|
||||
spi_message_init(&st->ring_msg);
|
||||
spi_message_add_tail(&st->ring_xfer[0], &st->ring_msg);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
st->ring_xfer[i + 1].rx_buf = &st->rx_buf[i];
|
||||
st->ring_xfer[i + 1].len = 2;
|
||||
st->ring_xfer[i + 1].cs_change = 1;
|
||||
spi_message_add_tail(&st->ring_xfer[i + 1], &st->ring_msg);
|
||||
}
|
||||
/* make sure last transfer cs_change is not set */
|
||||
st->ring_xfer[i + 1].cs_change = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad7923_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad7923_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
|
||||
b_sent = spi_sync(st->spi, &st->ring_msg);
|
||||
if (b_sent)
|
||||
goto done;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
|
||||
done:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad7923_scan_direct(struct ad7923_state *st, unsigned ch)
|
||||
{
|
||||
int ret, cmd;
|
||||
|
||||
cmd = AD7923_WRITE_CR | AD7923_CHANNEL_WRITE(ch) |
|
||||
AD7923_SEQUENCE_WRITE(AD7923_SEQUENCE_OFF) |
|
||||
st->settings;
|
||||
cmd <<= AD7923_SHIFT_REGISTER;
|
||||
st->tx_buf[0] = cpu_to_be16(cmd);
|
||||
|
||||
ret = spi_sync(st->spi, &st->scan_single_msg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->rx_buf[0]);
|
||||
}
|
||||
|
||||
static int ad7923_get_range(struct ad7923_state *st)
|
||||
{
|
||||
int vref;
|
||||
|
||||
vref = regulator_get_voltage(st->reg);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
vref /= 1000;
|
||||
|
||||
if (!(st->settings & AD7923_RANGE))
|
||||
vref *= 2;
|
||||
|
||||
return vref;
|
||||
}
|
||||
|
||||
static int ad7923_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad7923_scan_direct(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (chan->address == EXTRACT(ret, 12, 4))
|
||||
*val = EXTRACT(ret, 0, 12);
|
||||
else
|
||||
return -EIO;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = ad7923_get_range(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7923_info = {
|
||||
.read_raw = &ad7923_read_raw,
|
||||
.update_scan_mode = ad7923_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad7923_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7923_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ad7923_chip_info *info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->settings = AD7923_CODING | AD7923_RANGE |
|
||||
AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
|
||||
|
||||
info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = info->channels;
|
||||
indio_dev->num_channels = info->num_channels;
|
||||
indio_dev->info = &ad7923_info;
|
||||
|
||||
/* Setup default message */
|
||||
|
||||
st->scan_single_xfer[0].tx_buf = &st->tx_buf[0];
|
||||
st->scan_single_xfer[0].len = 2;
|
||||
st->scan_single_xfer[0].cs_change = 1;
|
||||
st->scan_single_xfer[1].rx_buf = &st->rx_buf[0];
|
||||
st->scan_single_xfer[1].len = 2;
|
||||
|
||||
spi_message_init(&st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[0], &st->scan_single_msg);
|
||||
spi_message_add_tail(&st->scan_single_xfer[1], &st->scan_single_msg);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "refin");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad7923_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7923_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7923_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7923_id[] = {
|
||||
{"ad7904", AD7904},
|
||||
{"ad7914", AD7914},
|
||||
{"ad7923", AD7924},
|
||||
{"ad7924", AD7924},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7923_id);
|
||||
|
||||
static struct spi_driver ad7923_driver = {
|
||||
.driver = {
|
||||
.name = "ad7923",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7923_probe,
|
||||
.remove = ad7923_remove,
|
||||
.id_table = ad7923_id,
|
||||
};
|
||||
module_spi_driver(ad7923_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
905
drivers/iio/adc/ad799x.c
Normal file
905
drivers/iio/adc/ad799x.c
Normal file
|
|
@ -0,0 +1,905 @@
|
|||
/*
|
||||
* iio/adc/ad799x.c
|
||||
* Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* based on iio/adc/max1363
|
||||
* Copyright (C) 2008-2010 Jonathan Cameron
|
||||
*
|
||||
* based on linux/drivers/i2c/chips/max123x
|
||||
* Copyright (C) 2002-2004 Stefan Eletzhofer
|
||||
*
|
||||
* based on linux/drivers/acron/char/pcf8583.c
|
||||
* Copyright (C) 2000 Russell King
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* ad799x.c
|
||||
*
|
||||
* Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
|
||||
* ad7998 and similar chips.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define AD799X_CHANNEL_SHIFT 4
|
||||
|
||||
/*
|
||||
* AD7991, AD7995 and AD7999 defines
|
||||
*/
|
||||
|
||||
#define AD7991_REF_SEL 0x08
|
||||
#define AD7991_FLTR 0x04
|
||||
#define AD7991_BIT_TRIAL_DELAY 0x02
|
||||
#define AD7991_SAMPLE_DELAY 0x01
|
||||
|
||||
/*
|
||||
* AD7992, AD7993, AD7994, AD7997 and AD7998 defines
|
||||
*/
|
||||
|
||||
#define AD7998_FLTR BIT(3)
|
||||
#define AD7998_ALERT_EN BIT(2)
|
||||
#define AD7998_BUSY_ALERT BIT(1)
|
||||
#define AD7998_BUSY_ALERT_POL BIT(0)
|
||||
|
||||
#define AD7998_CONV_RES_REG 0x0
|
||||
#define AD7998_ALERT_STAT_REG 0x1
|
||||
#define AD7998_CONF_REG 0x2
|
||||
#define AD7998_CYCLE_TMR_REG 0x3
|
||||
|
||||
#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4)
|
||||
#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5)
|
||||
#define AD7998_HYST_REG(x) ((x) * 3 + 0x6)
|
||||
|
||||
#define AD7998_CYC_MASK GENMASK(2, 0)
|
||||
#define AD7998_CYC_DIS 0x0
|
||||
#define AD7998_CYC_TCONF_32 0x1
|
||||
#define AD7998_CYC_TCONF_64 0x2
|
||||
#define AD7998_CYC_TCONF_128 0x3
|
||||
#define AD7998_CYC_TCONF_256 0x4
|
||||
#define AD7998_CYC_TCONF_512 0x5
|
||||
#define AD7998_CYC_TCONF_1024 0x6
|
||||
#define AD7998_CYC_TCONF_2048 0x7
|
||||
|
||||
#define AD7998_ALERT_STAT_CLEAR 0xFF
|
||||
|
||||
/*
|
||||
* AD7997 and AD7997 defines
|
||||
*/
|
||||
|
||||
#define AD7997_8_READ_SINGLE BIT(7)
|
||||
#define AD7997_8_READ_SEQUENCE (BIT(6) | BIT(5) | BIT(4))
|
||||
|
||||
enum {
|
||||
ad7991,
|
||||
ad7995,
|
||||
ad7999,
|
||||
ad7992,
|
||||
ad7993,
|
||||
ad7994,
|
||||
ad7997,
|
||||
ad7998
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad799x_chip_config - chip specific information
|
||||
* @channel: channel specification
|
||||
* @default_config: device default configuration
|
||||
* @info: pointer to iio_info struct
|
||||
*/
|
||||
struct ad799x_chip_config {
|
||||
const struct iio_chan_spec channel[9];
|
||||
u16 default_config;
|
||||
const struct iio_info *info;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad799x_chip_info - chip specific information
|
||||
* @num_channels: number of channels
|
||||
* @noirq_config: device configuration w/o IRQ
|
||||
* @irq_config: device configuration w/IRQ
|
||||
*/
|
||||
struct ad799x_chip_info {
|
||||
int num_channels;
|
||||
const struct ad799x_chip_config noirq_config;
|
||||
const struct ad799x_chip_config irq_config;
|
||||
};
|
||||
|
||||
struct ad799x_state {
|
||||
struct i2c_client *client;
|
||||
const struct ad799x_chip_config *chip_config;
|
||||
struct regulator *reg;
|
||||
struct regulator *vref;
|
||||
unsigned id;
|
||||
u16 config;
|
||||
|
||||
u8 *rx_buf;
|
||||
unsigned int transfer_size;
|
||||
};
|
||||
|
||||
static int ad799x_write_config(struct ad799x_state *st, u16 val)
|
||||
{
|
||||
switch (st->id) {
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG,
|
||||
val);
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
return i2c_smbus_write_byte_data(st->client, AD7998_CONF_REG,
|
||||
val);
|
||||
default:
|
||||
/* Will be written when doing a conversion */
|
||||
st->config = val;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad799x_read_config(struct ad799x_state *st)
|
||||
{
|
||||
switch (st->id) {
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG);
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG);
|
||||
default:
|
||||
/* No readback support */
|
||||
return st->config;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ad799x_trigger_handler() bh of trigger launched polling to ring buffer
|
||||
*
|
||||
* Currently there is no option in this driver to disable the saving of
|
||||
* timestamps within the ring.
|
||||
**/
|
||||
static irqreturn_t ad799x_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int b_sent;
|
||||
u8 cmd;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7991:
|
||||
case ad7995:
|
||||
case ad7999:
|
||||
cmd = st->config |
|
||||
(*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT);
|
||||
break;
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) |
|
||||
AD7998_CONV_RES_REG;
|
||||
break;
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG;
|
||||
break;
|
||||
default:
|
||||
cmd = 0;
|
||||
}
|
||||
|
||||
b_sent = i2c_smbus_read_i2c_block_data(st->client,
|
||||
cmd, st->transfer_size, st->rx_buf);
|
||||
if (b_sent < 0)
|
||||
goto out;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
|
||||
iio_get_time_ns());
|
||||
out:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ad799x_update_scan_mode(struct iio_dev *indio_dev,
|
||||
const unsigned long *scan_mask)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
kfree(st->rx_buf);
|
||||
st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
|
||||
if (!st->rx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
st->config &= ~(GENMASK(7, 0) << AD799X_CHANNEL_SHIFT);
|
||||
st->config |= (*scan_mask << AD799X_CHANNEL_SHIFT);
|
||||
return ad799x_write_config(st, st->config);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch)
|
||||
{
|
||||
u8 cmd;
|
||||
|
||||
switch (st->id) {
|
||||
case ad7991:
|
||||
case ad7995:
|
||||
case ad7999:
|
||||
cmd = st->config | (BIT(ch) << AD799X_CHANNEL_SHIFT);
|
||||
break;
|
||||
case ad7992:
|
||||
case ad7993:
|
||||
case ad7994:
|
||||
cmd = BIT(ch) << AD799X_CHANNEL_SHIFT;
|
||||
break;
|
||||
case ad7997:
|
||||
case ad7998:
|
||||
cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return i2c_smbus_read_word_swapped(st->client, cmd);
|
||||
}
|
||||
|
||||
static int ad799x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
ret = -EBUSY;
|
||||
else
|
||||
ret = ad799x_scan_direct(st, chan->scan_index);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> chan->scan_type.shift) &
|
||||
GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(st->vref);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
static const unsigned int ad7998_frequencies[] = {
|
||||
[AD7998_CYC_DIS] = 0,
|
||||
[AD7998_CYC_TCONF_32] = 15625,
|
||||
[AD7998_CYC_TCONF_64] = 7812,
|
||||
[AD7998_CYC_TCONF_128] = 3906,
|
||||
[AD7998_CYC_TCONF_512] = 976,
|
||||
[AD7998_CYC_TCONF_1024] = 488,
|
||||
[AD7998_CYC_TCONF_2048] = 244,
|
||||
};
|
||||
|
||||
static ssize_t ad799x_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%u\n", ad7998_frequencies[ret & AD7998_CYC_MASK]);
|
||||
}
|
||||
|
||||
static ssize_t ad799x_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
long val;
|
||||
int ret, i;
|
||||
|
||||
ret = kstrtol(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG);
|
||||
if (ret < 0)
|
||||
goto error_ret_mutex;
|
||||
/* Wipe the bits clean */
|
||||
ret &= ~AD7998_CYC_MASK;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++)
|
||||
if (val == ad7998_frequencies[i])
|
||||
break;
|
||||
if (i == ARRAY_SIZE(ad7998_frequencies)) {
|
||||
ret = -EINVAL;
|
||||
goto error_ret_mutex;
|
||||
}
|
||||
|
||||
ret = i2c_smbus_write_byte_data(st->client, AD7998_CYCLE_TMR_REG,
|
||||
ret | i);
|
||||
if (ret < 0)
|
||||
goto error_ret_mutex;
|
||||
ret = len;
|
||||
|
||||
error_ret_mutex:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (!(st->config & AD7998_ALERT_EN))
|
||||
return 0;
|
||||
|
||||
if ((st->config >> AD799X_CHANNEL_SHIFT) & BIT(chan->scan_index))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad799x_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
enum iio_event_type type,
|
||||
enum iio_event_direction dir,
|
||||
int state)
|
||||
{
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
ret = -EBUSY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (state)
|
||||
st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT;
|
||||
else
|
||||
st->config &= ~(BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT);
|
||||
|
||||
if (st->config >> AD799X_CHANNEL_SHIFT)
|
||||
st->config |= AD7998_ALERT_EN;
|
||||
else
|
||||
st->config &= ~AD7998_ALERT_EN;
|
||||
|
||||
ret = ad799x_write_config(st, st->config);
|
||||
|
||||
done:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir,
|
||||
enum iio_event_info info)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
return AD7998_DATALOW_REG(chan->channel);
|
||||
else
|
||||
return AD7998_DATAHIGH_REG(chan->channel);
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
return AD7998_HYST_REG(chan->channel);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad799x_write_event_value(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)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_write_word_swapped(st->client,
|
||||
ad799x_threshold_reg(chan, dir, info),
|
||||
val << chan->scan_type.shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_read_event_value(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)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = i2c_smbus_read_word_swapped(st->client,
|
||||
ad799x_threshold_reg(chan, dir, info));
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = (ret >> chan->scan_type.shift) &
|
||||
GENMASK(chan->scan_type.realbits - 1 , 0);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static irqreturn_t ad799x_event_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct ad799x_state *st = iio_priv(private);
|
||||
int i, ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, AD7998_ALERT_STAT_REG);
|
||||
if (ret <= 0)
|
||||
goto done;
|
||||
|
||||
if (i2c_smbus_write_byte_data(st->client, AD7998_ALERT_STAT_REG,
|
||||
AD7998_ALERT_STAT_CLEAR) < 0)
|
||||
goto done;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (ret & BIT(i))
|
||||
iio_push_event(indio_dev,
|
||||
i & 0x1 ?
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
(i >> 1),
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING) :
|
||||
IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
|
||||
(i >> 1),
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
done:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
ad799x_read_frequency,
|
||||
ad799x_write_frequency);
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0");
|
||||
|
||||
static struct attribute *ad799x_event_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad799x_event_attrs_group = {
|
||||
.attrs = ad799x_event_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static const struct iio_info ad7991_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7993_4_7_8_noirq_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
.update_scan_mode = ad799x_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_info ad7993_4_7_8_irq_info = {
|
||||
.read_raw = &ad799x_read_raw,
|
||||
.event_attrs = &ad799x_event_attrs_group,
|
||||
.read_event_config = &ad799x_read_event_config,
|
||||
.write_event_config = &ad799x_write_event_config,
|
||||
.read_event_value = &ad799x_read_event_value,
|
||||
.write_event_value = &ad799x_write_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
.update_scan_mode = ad799x_update_scan_mode,
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad799x_events[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_EITHER,
|
||||
.mask_separate = BIT(IIO_EV_INFO_HYSTERESIS),
|
||||
},
|
||||
};
|
||||
|
||||
#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (_index), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = (_index), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_realbits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (_realbits), \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
.event_spec = _ev_spec, \
|
||||
.num_event_specs = _num_ev_spec, \
|
||||
}
|
||||
|
||||
#define AD799X_CHANNEL(_index, _realbits) \
|
||||
_AD799X_CHANNEL(_index, _realbits, NULL, 0)
|
||||
|
||||
#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \
|
||||
_AD799X_CHANNEL(_index, _realbits, ad799x_events, \
|
||||
ARRAY_SIZE(ad799x_events))
|
||||
|
||||
static const struct ad799x_chip_info ad799x_chip_info_tbl[] = {
|
||||
[ad7991] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7995] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7999] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 8),
|
||||
AD799X_CHANNEL(1, 8),
|
||||
AD799X_CHANNEL(2, 8),
|
||||
AD799X_CHANNEL(3, 8),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7991_info,
|
||||
},
|
||||
},
|
||||
[ad7992] = {
|
||||
.num_channels = 3,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(3),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7993] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7994] = {
|
||||
.num_channels = 5,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(4),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7997] = {
|
||||
.num_channels = 9,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 10),
|
||||
AD799X_CHANNEL(1, 10),
|
||||
AD799X_CHANNEL(2, 10),
|
||||
AD799X_CHANNEL(3, 10),
|
||||
AD799X_CHANNEL(4, 10),
|
||||
AD799X_CHANNEL(5, 10),
|
||||
AD799X_CHANNEL(6, 10),
|
||||
AD799X_CHANNEL(7, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 10),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 10),
|
||||
AD799X_CHANNEL(4, 10),
|
||||
AD799X_CHANNEL(5, 10),
|
||||
AD799X_CHANNEL(6, 10),
|
||||
AD799X_CHANNEL(7, 10),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
[ad7998] = {
|
||||
.num_channels = 9,
|
||||
.noirq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL(0, 12),
|
||||
AD799X_CHANNEL(1, 12),
|
||||
AD799X_CHANNEL(2, 12),
|
||||
AD799X_CHANNEL(3, 12),
|
||||
AD799X_CHANNEL(4, 12),
|
||||
AD799X_CHANNEL(5, 12),
|
||||
AD799X_CHANNEL(6, 12),
|
||||
AD799X_CHANNEL(7, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.info = &ad7993_4_7_8_noirq_info,
|
||||
},
|
||||
.irq_config = {
|
||||
.channel = {
|
||||
AD799X_CHANNEL_WITH_EVENTS(0, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(1, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(2, 12),
|
||||
AD799X_CHANNEL_WITH_EVENTS(3, 12),
|
||||
AD799X_CHANNEL(4, 12),
|
||||
AD799X_CHANNEL(5, 12),
|
||||
AD799X_CHANNEL(6, 12),
|
||||
AD799X_CHANNEL(7, 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(8),
|
||||
},
|
||||
.default_config = AD7998_ALERT_EN | AD7998_BUSY_ALERT,
|
||||
.info = &ad7993_4_7_8_irq_info,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static int ad799x_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct ad799x_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
const struct ad799x_chip_info *chip_info =
|
||||
&ad799x_chip_info_tbl[id->driver_data];
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for device removal purposes */
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
st->id = id->driver_data;
|
||||
if (client->irq > 0 && chip_info->irq_config.info)
|
||||
st->chip_config = &chip_info->irq_config;
|
||||
else
|
||||
st->chip_config = &chip_info->noirq_config;
|
||||
|
||||
/* TODO: Add pdata options for filtering and bit delay */
|
||||
|
||||
st->reg = devm_regulator_get(&client->dev, "vcc");
|
||||
if (IS_ERR(st->reg))
|
||||
return PTR_ERR(st->reg);
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
st->vref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(st->vref)) {
|
||||
ret = PTR_ERR(st->vref);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
ret = regulator_enable(st->vref);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = st->chip_config->info;
|
||||
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_config->channel;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
ret = ad799x_write_config(st, st->chip_config->default_config);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
ret = ad799x_read_config(st);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
st->config = ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, NULL,
|
||||
&ad799x_trigger_handler, NULL);
|
||||
if (ret)
|
||||
goto error_disable_vref;
|
||||
|
||||
if (client->irq > 0) {
|
||||
ret = devm_request_threaded_irq(&client->dev,
|
||||
client->irq,
|
||||
NULL,
|
||||
ad799x_event_handler,
|
||||
IRQF_TRIGGER_FALLING |
|
||||
IRQF_ONESHOT,
|
||||
client->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
}
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_ring;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_ring:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_disable_vref:
|
||||
regulator_disable(st->vref);
|
||||
error_disable_reg:
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad799x_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct ad799x_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
regulator_disable(st->vref);
|
||||
regulator_disable(st->reg);
|
||||
kfree(st->rx_buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad799x_id[] = {
|
||||
{ "ad7991", ad7991 },
|
||||
{ "ad7995", ad7995 },
|
||||
{ "ad7999", ad7999 },
|
||||
{ "ad7992", ad7992 },
|
||||
{ "ad7993", ad7993 },
|
||||
{ "ad7994", ad7994 },
|
||||
{ "ad7997", ad7997 },
|
||||
{ "ad7998", ad7998 },
|
||||
{}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, ad799x_id);
|
||||
|
||||
static struct i2c_driver ad799x_driver = {
|
||||
.driver = {
|
||||
.name = "ad799x",
|
||||
},
|
||||
.probe = ad799x_probe,
|
||||
.remove = ad799x_remove,
|
||||
.id_table = ad799x_id,
|
||||
};
|
||||
module_i2c_driver(ad799x_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
553
drivers/iio/adc/ad_sigma_delta.c
Normal file
553
drivers/iio/adc/ad_sigma_delta.c
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* Support code for Analog Devices Sigma-Delta ADCs
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/adc/ad_sigma_delta.h>
|
||||
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
|
||||
#define AD_SD_COMM_CHAN_MASK 0x3
|
||||
|
||||
#define AD_SD_REG_COMM 0x00
|
||||
#define AD_SD_REG_DATA 0x03
|
||||
|
||||
/**
|
||||
* ad_sd_set_comm() - Set communications register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @comm: New value for the communications register
|
||||
*/
|
||||
void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
|
||||
{
|
||||
/* Some variants use the lower two bits of the communications register
|
||||
* to select the channel */
|
||||
sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_set_comm);
|
||||
|
||||
/**
|
||||
* ad_sd_write_reg() - Write a register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @reg: Address of the register
|
||||
* @size: Size of the register (0-3)
|
||||
* @val: Value to write to the register
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
|
||||
unsigned int size, unsigned int val)
|
||||
{
|
||||
uint8_t *data = sigma_delta->data;
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = data,
|
||||
.len = size + 1,
|
||||
.cs_change = sigma_delta->bus_locked,
|
||||
};
|
||||
struct spi_message m;
|
||||
int ret;
|
||||
|
||||
data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
|
||||
|
||||
switch (size) {
|
||||
case 3:
|
||||
data[1] = val >> 16;
|
||||
data[2] = val >> 8;
|
||||
data[3] = val;
|
||||
break;
|
||||
case 2:
|
||||
put_unaligned_be16(val, &data[1]);
|
||||
break;
|
||||
case 1:
|
||||
data[1] = val;
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
spi_message_add_tail(&t, &m);
|
||||
|
||||
if (sigma_delta->bus_locked)
|
||||
ret = spi_sync_locked(sigma_delta->spi, &m);
|
||||
else
|
||||
ret = spi_sync(sigma_delta->spi, &m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_write_reg);
|
||||
|
||||
static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int reg, unsigned int size, uint8_t *val)
|
||||
{
|
||||
uint8_t *data = sigma_delta->data;
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = data,
|
||||
.len = 1,
|
||||
}, {
|
||||
.rx_buf = val,
|
||||
.len = size,
|
||||
.cs_change = sigma_delta->bus_locked,
|
||||
},
|
||||
};
|
||||
struct spi_message m;
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
if (sigma_delta->info->has_registers) {
|
||||
data[0] = reg << sigma_delta->info->addr_shift;
|
||||
data[0] |= sigma_delta->info->read_mask;
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
}
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
if (sigma_delta->bus_locked)
|
||||
ret = spi_sync_locked(sigma_delta->spi, &m);
|
||||
else
|
||||
ret = spi_sync(sigma_delta->spi, &m);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_read_reg() - Read a register
|
||||
*
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @reg: Address of the register
|
||||
* @size: Size of the register (1-4)
|
||||
* @val: Read value
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int reg, unsigned int size, unsigned int *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
switch (size) {
|
||||
case 4:
|
||||
*val = get_unaligned_be32(sigma_delta->data);
|
||||
break;
|
||||
case 3:
|
||||
*val = (sigma_delta->data[0] << 16) |
|
||||
(sigma_delta->data[1] << 8) |
|
||||
sigma_delta->data[2];
|
||||
break;
|
||||
case 2:
|
||||
*val = get_unaligned_be16(sigma_delta->data);
|
||||
break;
|
||||
case 1:
|
||||
*val = sigma_delta->data[0];
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_read_reg);
|
||||
|
||||
static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
|
||||
unsigned int mode, unsigned int channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad_sigma_delta_set_channel(sigma_delta, channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, mode);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
|
||||
if (ret == 0) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
ret = -EIO;
|
||||
} else {
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_calibrate_all() - Performs channel calibration
|
||||
* @sigma_delta: The sigma delta device
|
||||
* @cb: Array of channels and calibration type to perform
|
||||
* @n: Number of items in cb
|
||||
*
|
||||
* Returns 0 on success, an error code otherwise.
|
||||
**/
|
||||
int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
|
||||
const struct ad_sd_calib_data *cb, unsigned int n)
|
||||
{
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
|
||||
|
||||
/**
|
||||
* ad_sigma_delta_single_conversion() - Performs a single data conversion
|
||||
* @indio_dev: The IIO device
|
||||
* @chan: The conversion is done for this channel
|
||||
* @val: Pointer to the location where to store the read value
|
||||
*
|
||||
* Returns: 0 on success, an error value otherwise.
|
||||
*/
|
||||
int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int sample, raw_sample;
|
||||
int ret = 0;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ad_sigma_delta_set_channel(sigma_delta, chan->address);
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
ret = wait_for_completion_interruptible_timeout(
|
||||
&sigma_delta->completion, HZ);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
|
||||
if (ret == 0)
|
||||
ret = -EIO;
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
|
||||
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
|
||||
&raw_sample);
|
||||
|
||||
out:
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sample = raw_sample >> chan->scan_type.shift;
|
||||
sample &= (1 << chan->scan_type.realbits) - 1;
|
||||
*val = sample;
|
||||
|
||||
ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
|
||||
|
||||
static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
unsigned int channel;
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_postenable(indio_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
channel = find_first_bit(indio_dev->active_scan_mask,
|
||||
indio_dev->masklength);
|
||||
ret = ad_sigma_delta_set_channel(sigma_delta,
|
||||
indio_dev->channels[channel].address);
|
||||
if (ret)
|
||||
goto err_predisable;
|
||||
|
||||
spi_bus_lock(sigma_delta->spi->master);
|
||||
sigma_delta->bus_locked = true;
|
||||
ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
|
||||
if (ret)
|
||||
goto err_unlock;
|
||||
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
spi_bus_unlock(sigma_delta->spi->master);
|
||||
err_predisable:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
reinit_completion(&sigma_delta->completion);
|
||||
wait_for_completion_timeout(&sigma_delta->completion, HZ);
|
||||
|
||||
if (!sigma_delta->irq_dis) {
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
}
|
||||
|
||||
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
|
||||
|
||||
sigma_delta->bus_locked = false;
|
||||
return spi_bus_unlock(sigma_delta->spi->master);
|
||||
}
|
||||
|
||||
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 reg_size;
|
||||
uint8_t data[16];
|
||||
int ret;
|
||||
|
||||
memset(data, 0x00, 16);
|
||||
|
||||
reg_size = indio_dev->channels[0].scan_type.realbits +
|
||||
indio_dev->channels[0].scan_type.shift;
|
||||
reg_size = DIV_ROUND_UP(reg_size, 8);
|
||||
|
||||
switch (reg_size) {
|
||||
case 4:
|
||||
case 2:
|
||||
case 1:
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[0]);
|
||||
break;
|
||||
case 3:
|
||||
/* We store 24 bit samples in a 32 bit word. Keep the upper
|
||||
* byte set to zero. */
|
||||
ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
|
||||
reg_size, &data[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
sigma_delta->irq_dis = false;
|
||||
enable_irq(sigma_delta->spi->irq);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
|
||||
.postenable = &ad_sd_buffer_postenable,
|
||||
.predisable = &iio_triggered_buffer_predisable,
|
||||
.postdisable = &ad_sd_buffer_postdisable,
|
||||
.validate_scan_mask = &iio_validate_scan_mask_onehot,
|
||||
};
|
||||
|
||||
static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = private;
|
||||
|
||||
complete(&sigma_delta->completion);
|
||||
disable_irq_nosync(irq);
|
||||
sigma_delta->irq_dis = true;
|
||||
iio_trigger_poll(sigma_delta->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
|
||||
* @indio_dev: The IIO device
|
||||
* @trig: The new trigger
|
||||
*
|
||||
* Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
|
||||
* device, -EINVAL otherwise.
|
||||
*/
|
||||
int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
if (sigma_delta->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
|
||||
|
||||
static const struct iio_trigger_ops ad_sd_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
int ret;
|
||||
|
||||
sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (sigma_delta->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
sigma_delta->trig->ops = &ad_sd_trigger_ops;
|
||||
init_completion(&sigma_delta->completion);
|
||||
|
||||
ret = request_irq(sigma_delta->spi->irq,
|
||||
ad_sd_data_rdy_trig_poll,
|
||||
IRQF_TRIGGER_LOW,
|
||||
indio_dev->name,
|
||||
sigma_delta);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
if (!sigma_delta->irq_dis) {
|
||||
sigma_delta->irq_dis = true;
|
||||
disable_irq_nosync(sigma_delta->spi->irq);
|
||||
}
|
||||
sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
|
||||
iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta);
|
||||
|
||||
ret = iio_trigger_register(sigma_delta->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = iio_trigger_get(sigma_delta->trig);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(sigma_delta->spi->irq, sigma_delta);
|
||||
error_free_trig:
|
||||
iio_trigger_free(sigma_delta->trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ad_sd_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sigma_delta->trig);
|
||||
free_irq(sigma_delta->spi->irq, sigma_delta);
|
||||
iio_trigger_free(sigma_delta->trig);
|
||||
}
|
||||
|
||||
/**
|
||||
* ad_sd_setup_buffer_and_trigger() -
|
||||
* @indio_dev: The IIO device
|
||||
*/
|
||||
int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&ad_sd_trigger_handler, &ad_sd_buffer_setup_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad_sd_probe_trigger(indio_dev);
|
||||
if (ret) {
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger);
|
||||
|
||||
/**
|
||||
* ad_sd_cleanup_buffer_and_trigger() -
|
||||
* @indio_dev: The IIO device
|
||||
*/
|
||||
void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
ad_sd_remove_trigger(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger);
|
||||
|
||||
/**
|
||||
* ad_sd_init() - Initializes a ad_sigma_delta struct
|
||||
* @sigma_delta: The ad_sigma_delta device
|
||||
* @indio_dev: The IIO device which the Sigma Delta device is used for
|
||||
* @spi: The SPI device for the ad_sigma_delta device
|
||||
* @info: Device specific callbacks and options
|
||||
*
|
||||
* This function needs to be called before any other operations are performed on
|
||||
* the ad_sigma_delta struct.
|
||||
*/
|
||||
int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, const struct ad_sigma_delta_info *info)
|
||||
{
|
||||
sigma_delta->spi = spi;
|
||||
sigma_delta->info = info;
|
||||
iio_device_set_drvdata(indio_dev, sigma_delta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ad_sd_init);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1439
drivers/iio/adc/at91_adc.c
Normal file
1439
drivers/iio/adc/at91_adc.c
Normal file
File diff suppressed because it is too large
Load diff
889
drivers/iio/adc/exynos_adc.c
Normal file
889
drivers/iio/adc/exynos_adc.c
Normal file
|
|
@ -0,0 +1,889 @@
|
|||
/*
|
||||
* exynos_adc.c - Support for ADC in EXYNOS SoCs
|
||||
*
|
||||
* 8 ~ 10 channel, 10/12-bit ADC
|
||||
*
|
||||
* Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <soc/samsung/exynos-powermode.h>
|
||||
|
||||
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
|
||||
#define ADC_V1_CON(x) ((x) + 0x00)
|
||||
#define ADC_V1_DLY(x) ((x) + 0x08)
|
||||
#define ADC_V1_DATX(x) ((x) + 0x0C)
|
||||
#define ADC_V1_INTCLR(x) ((x) + 0x18)
|
||||
#define ADC_V1_MUX(x) ((x) + 0x1c)
|
||||
|
||||
/* S3C2410 ADC registers definitions */
|
||||
#define ADC_S3C2410_MUX(x) ((x) + 0x18)
|
||||
|
||||
/* Future ADC_V2 registers definitions */
|
||||
#define ADC_V2_CON1(x) ((x) + 0x00)
|
||||
#define ADC_V2_CON2(x) ((x) + 0x04)
|
||||
#define ADC_V2_STAT(x) ((x) + 0x08)
|
||||
#define ADC_V2_INT_EN(x) ((x) + 0x10)
|
||||
#define ADC_V2_INT_ST(x) ((x) + 0x14)
|
||||
#define ADC_V2_VER(x) ((x) + 0x20)
|
||||
|
||||
/* Sharing ADC_V3 registers definitions */
|
||||
#define ADC_V3_DAT(x) ((x) + 0x08)
|
||||
#define ADC_V3_DAT_SUM(x) ((x) + 0x0C)
|
||||
#define ADC_V3_DBG_DATA(x) ((x) + 0x1C)
|
||||
|
||||
/* Bit definitions for ADC_V1 */
|
||||
#define ADC_V1_CON_RES (1u << 16)
|
||||
#define ADC_V1_CON_PRSCEN (1u << 14)
|
||||
#define ADC_V1_CON_PRSCLV(x) (((x) & 0xFF) << 6)
|
||||
#define ADC_V1_CON_STANDBY (1u << 2)
|
||||
|
||||
/* Bit definitions for S3C2410 ADC */
|
||||
#define ADC_S3C2410_CON_SELMUX(x) (((x) & 7) << 3)
|
||||
#define ADC_S3C2410_DATX_MASK 0x3FF
|
||||
#define ADC_S3C2416_CON_RES_SEL (1u << 3)
|
||||
|
||||
/* Bit definitions for ADC_V2 */
|
||||
#define ADC_V2_CON1_SOFT_RESET (1u << 2)
|
||||
#define ADC_V2_CON1_SOFT_NON_RESET (1u << 1)
|
||||
|
||||
#define ADC_V2_CON2_OSEL (1u << 10)
|
||||
#define ADC_V2_CON2_ESEL (1u << 9)
|
||||
#define ADC_V2_CON2_HIGHF (1u << 8)
|
||||
#define ADC_V2_CON2_C_TIME(x) (((x) & 7) << 4)
|
||||
#define ADC_V2_CON2_ACH_SEL(x) (((x) & 0xF) << 0)
|
||||
#define ADC_V2_CON2_ACH_MASK 0xF
|
||||
|
||||
/* Bit definitions for ADC_V3 */
|
||||
#define ADC_V3_DAT_FLAG (1u << 31)
|
||||
|
||||
#define MAX_ADC_V3_CHANNELS 8
|
||||
#define MAX_ADC_V2_CHANNELS 10
|
||||
#define MAX_ADC_V1_CHANNELS 8
|
||||
#define MAX_EXYNOS3250_ADC_CHANNELS 2
|
||||
|
||||
/* Bit definitions common for ADC_V1, ADC_V2, ADC_V3 */
|
||||
#define ADC_CON_EN_START (1u << 0)
|
||||
#define ADC_DATX_MASK 0xFFF
|
||||
|
||||
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
|
||||
|
||||
struct exynos_adc {
|
||||
struct exynos_adc_data *data;
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
void __iomem *enable_reg;
|
||||
struct clk *clk;
|
||||
struct clk *sclk;
|
||||
unsigned int irq;
|
||||
struct regulator *vdd;
|
||||
bool needs_adc_phy;
|
||||
|
||||
struct completion completion;
|
||||
|
||||
u32 value;
|
||||
unsigned int version;
|
||||
int idle_ip_index;
|
||||
};
|
||||
|
||||
struct exynos_adc_data {
|
||||
int num_channels;
|
||||
bool needs_sclk;
|
||||
u32 mask;
|
||||
|
||||
void (*init_hw)(struct exynos_adc *info);
|
||||
void (*exit_hw)(struct exynos_adc *info);
|
||||
void (*clear_irq)(struct exynos_adc *info);
|
||||
void (*start_conv)(struct exynos_adc *info, unsigned long addr);
|
||||
irqreturn_t (*adc_isr)(int irq, void *dev_id);
|
||||
};
|
||||
|
||||
static void exynos_adc_unprepare_clk(struct exynos_adc *info)
|
||||
{
|
||||
if (info->data->needs_sclk)
|
||||
clk_unprepare(info->sclk);
|
||||
clk_unprepare(info->clk);
|
||||
}
|
||||
|
||||
static int exynos_adc_prepare_clk(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare(info->clk);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed preparing adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
ret = clk_prepare(info->sclk);
|
||||
if (ret) {
|
||||
clk_unprepare(info->clk);
|
||||
dev_err(info->dev,
|
||||
"failed preparing sclk_adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_adc_disable_clk(struct exynos_adc *info)
|
||||
{
|
||||
if (info->data->needs_sclk)
|
||||
clk_disable(info->sclk);
|
||||
clk_disable(info->clk);
|
||||
}
|
||||
|
||||
static int exynos_adc_enable_clk(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(info->dev, "failed enabling adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
ret = clk_enable(info->sclk);
|
||||
if (ret) {
|
||||
clk_disable(info->clk);
|
||||
dev_err(info->dev,
|
||||
"failed enabling sclk_adc clock: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_enable_access(struct exynos_adc *info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 0);
|
||||
if (info->needs_adc_phy)
|
||||
writel(1, info->enable_reg);
|
||||
|
||||
if (info->vdd) {
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
ret = exynos_adc_prepare_clk(info);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = exynos_adc_enable_clk(info);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_adc_disable_access(struct exynos_adc *info)
|
||||
{
|
||||
exynos_adc_disable_clk(info);
|
||||
exynos_adc_unprepare_clk(info);
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
|
||||
if (info->needs_adc_phy)
|
||||
writel(0, info->enable_reg);
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* set default prescaler values and Enable prescaler */
|
||||
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
|
||||
|
||||
/* Enable 12-bit ADC resolution */
|
||||
con1 |= ADC_V1_CON_RES;
|
||||
writel(con1, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con;
|
||||
|
||||
con = readl(ADC_V1_CON(info->regs));
|
||||
con |= ADC_V1_CON_STANDBY;
|
||||
writel(con, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_clear_irq(struct exynos_adc *info)
|
||||
{
|
||||
writel(1, ADC_V1_INTCLR(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v1_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
writel(addr, ADC_V1_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_adc_v1_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
u32 mask = info->data->mask;
|
||||
|
||||
/* Read value */
|
||||
info->value = readl(ADC_V1_DATX(info->regs)) & mask;
|
||||
|
||||
/* clear irq */
|
||||
if (info->data->clear_irq)
|
||||
info->data->clear_irq(info);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v1_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.clear_irq = exynos_adc_v1_clear_irq,
|
||||
.start_conv = exynos_adc_v1_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* Enable 12 bit ADC resolution */
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
con1 |= ADC_S3C2416_CON_RES_SEL;
|
||||
writel(con1, ADC_V1_CON(info->regs));
|
||||
|
||||
/* Select channel for S3C2416 */
|
||||
writel(addr, ADC_S3C2410_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c2416_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c2416_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c2443_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
/* Select channel for S3C2433 */
|
||||
writel(addr, ADC_S3C2410_MUX(info->regs));
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c2443_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c2443_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_s3c64xx_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1;
|
||||
|
||||
con1 = readl(ADC_V1_CON(info->regs));
|
||||
con1 &= ~ADC_S3C2410_CON_SELMUX(0x7);
|
||||
con1 |= ADC_S3C2410_CON_SELMUX(addr);
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
|
||||
}
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c24xx_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_S3C2410_DATX_MASK, /* 10 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.start_conv = exynos_adc_s3c64xx_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static struct exynos_adc_data const exynos_adc_s3c64xx_data = {
|
||||
.num_channels = MAX_ADC_V1_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v1_init_hw,
|
||||
.exit_hw = exynos_adc_v1_exit_hw,
|
||||
.clear_irq = exynos_adc_v1_clear_irq,
|
||||
.start_conv = exynos_adc_s3c64xx_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_v2_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
|
||||
ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(6);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~(ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL |
|
||||
ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(7));
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_clear_irq(struct exynos_adc *info)
|
||||
{
|
||||
writel(1, ADC_V2_INT_ST(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v2_start_conv(struct exynos_adc *info,
|
||||
unsigned long addr)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_ACH_MASK;
|
||||
con2 |= ADC_V2_CON2_ACH_SEL(addr);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
con1 = readl(ADC_V2_CON1(info->regs));
|
||||
writel(con1 | ADC_CON_EN_START, ADC_V2_CON1(info->regs));
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v2_data = {
|
||||
.num_channels = MAX_ADC_V2_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static const struct exynos_adc_data exynos3250_adc_data = {
|
||||
.num_channels = MAX_EXYNOS3250_ADC_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
.needs_sclk = true,
|
||||
|
||||
.init_hw = exynos_adc_v2_init_hw,
|
||||
.exit_hw = exynos_adc_v2_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v1_isr,
|
||||
};
|
||||
|
||||
static void exynos_adc_v3_init_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con1, con2;
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con1 = ADC_V2_CON1_SOFT_NON_RESET;
|
||||
writel(con1, ADC_V2_CON1(info->regs));
|
||||
|
||||
con2 = ADC_V2_CON2_C_TIME(6);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Enable interrupts */
|
||||
writel(1, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static void exynos_adc_v3_exit_hw(struct exynos_adc *info)
|
||||
{
|
||||
u32 con2;
|
||||
|
||||
con2 = readl(ADC_V2_CON2(info->regs));
|
||||
con2 &= ~ADC_V2_CON2_C_TIME(7);
|
||||
writel(con2, ADC_V2_CON2(info->regs));
|
||||
|
||||
/* Disable interrupts */
|
||||
writel(0, ADC_V2_INT_EN(info->regs));
|
||||
}
|
||||
|
||||
static irqreturn_t exynos_adc_v3_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct exynos_adc *info = (struct exynos_adc *)dev_id;
|
||||
u32 mask = info->data->mask;
|
||||
|
||||
/* Read value */
|
||||
info->value = readl(ADC_V3_DAT(info->regs)) & mask;
|
||||
|
||||
/* clear irq */
|
||||
if (info->data->clear_irq)
|
||||
info->data->clear_irq(info);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct exynos_adc_data exynos_adc_v3_data = {
|
||||
.num_channels = MAX_ADC_V3_CHANNELS,
|
||||
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
|
||||
|
||||
.init_hw = exynos_adc_v3_init_hw,
|
||||
.exit_hw = exynos_adc_v3_exit_hw,
|
||||
.clear_irq = exynos_adc_v2_clear_irq,
|
||||
.start_conv = exynos_adc_v2_start_conv,
|
||||
.adc_isr = exynos_adc_v3_isr,
|
||||
};
|
||||
|
||||
static const struct of_device_id exynos_adc_match[] = {
|
||||
{
|
||||
.compatible = "samsung,s3c2410-adc",
|
||||
.data = &exynos_adc_s3c24xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2416-adc",
|
||||
.data = &exynos_adc_s3c2416_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2440-adc",
|
||||
.data = &exynos_adc_s3c24xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c2443-adc",
|
||||
.data = &exynos_adc_s3c2443_data,
|
||||
}, {
|
||||
.compatible = "samsung,s3c6410-adc",
|
||||
.data = &exynos_adc_s3c64xx_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v1",
|
||||
.data = &exynos_adc_v1_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v2",
|
||||
.data = &exynos_adc_v2_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos-adc-v3",
|
||||
.data = &exynos_adc_v3_data,
|
||||
}, {
|
||||
.compatible = "samsung,exynos3250-adc",
|
||||
.data = &exynos3250_adc_data,
|
||||
},
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, exynos_adc_match);
|
||||
|
||||
static struct exynos_adc_data *exynos_adc_get_data(struct platform_device *pdev)
|
||||
{
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_node(exynos_adc_match, pdev->dev.of_node);
|
||||
return (struct exynos_adc_data *)match->data;
|
||||
}
|
||||
|
||||
static int exynos_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
if (mask != IIO_CHAN_INFO_RAW)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
enable_irq(info->irq);
|
||||
if (info->data->init_hw)
|
||||
info->data->init_hw(info);
|
||||
|
||||
/* Select the channel to be used and Trigger conversion */
|
||||
if (info->data->start_conv)
|
||||
info->data->start_conv(info, chan->address);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, EXYNOS_ADC_TIMEOUT);
|
||||
if (timeout == 0) {
|
||||
dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n");
|
||||
ret = -ETIMEDOUT;
|
||||
} else {
|
||||
*val = info->value;
|
||||
*val2 = 0;
|
||||
ret = IIO_VAL_INT;
|
||||
}
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
disable_irq(info->irq);
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int exynos_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (readval == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info exynos_adc_iio_info = {
|
||||
.read_raw = &exynos_read_raw,
|
||||
.debugfs_reg_access = &exynos_adc_reg_access,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define ADC_CHANNEL(_index, _id) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.address = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.datasheet_name = _id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec exynos_adc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
ADC_CHANNEL(3, "adc3"),
|
||||
ADC_CHANNEL(4, "adc4"),
|
||||
ADC_CHANNEL(5, "adc5"),
|
||||
ADC_CHANNEL(6, "adc6"),
|
||||
ADC_CHANNEL(7, "adc7"),
|
||||
ADC_CHANNEL(8, "adc8"),
|
||||
ADC_CHANNEL(9, "adc9"),
|
||||
};
|
||||
|
||||
static int exynos_adc_remove_devices(struct device *dev, void *c)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
platform_device_unregister(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct exynos_adc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
int ret = -ENODEV;
|
||||
int irq;
|
||||
|
||||
if (!np)
|
||||
return ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
info->data = exynos_adc_get_data(pdev);
|
||||
if (!info->data) {
|
||||
dev_err(&pdev->dev, "failed getting exynos_adc_data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (of_find_property(np, "samsung,adc-phy-control", NULL))
|
||||
info->needs_adc_phy = true;
|
||||
else
|
||||
info->needs_adc_phy = false;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
|
||||
if (info->needs_adc_phy) {
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
info->enable_reg = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->enable_reg))
|
||||
return PTR_ERR(info->enable_reg);
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
info->irq = irq;
|
||||
info->dev = &pdev->dev;
|
||||
info->idle_ip_index = exynos_get_idle_ip_index(dev_name(&pdev->dev));
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "gate_adcif");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
return PTR_ERR(info->clk);
|
||||
}
|
||||
|
||||
if (info->data->needs_sclk) {
|
||||
info->sclk = devm_clk_get(&pdev->dev, "sclk");
|
||||
if (IS_ERR(info->sclk)) {
|
||||
dev_err(&pdev->dev,
|
||||
"failed getting sclk clock, err = %ld\n",
|
||||
PTR_ERR(info->sclk));
|
||||
return PTR_ERR(info->sclk);
|
||||
}
|
||||
}
|
||||
|
||||
info->vdd = devm_regulator_get(&pdev->dev, "vdd");
|
||||
if (IS_ERR(info->vdd)) {
|
||||
dev_err(&pdev->dev, "failed getting regulator, err = %ld\n",
|
||||
PTR_ERR(info->vdd));
|
||||
info->vdd = NULL;
|
||||
}
|
||||
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 0);
|
||||
if (info->vdd) {
|
||||
ret = regulator_enable(info->vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
ret = exynos_adc_prepare_clk(info);
|
||||
if (ret)
|
||||
goto err_disable_reg;
|
||||
|
||||
ret = exynos_adc_enable_clk(info);
|
||||
if (ret)
|
||||
goto err_unprepare_clk;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &exynos_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = exynos_adc_iio_channels;
|
||||
indio_dev->num_channels = info->data->num_channels;
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, info->irq, info->data->adc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
|
||||
info->irq);
|
||||
goto err_disable_clk;
|
||||
}
|
||||
|
||||
disable_irq(info->irq);
|
||||
exynos_adc_disable_clk(info);
|
||||
exynos_adc_unprepare_clk(info);
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
exynos_update_ip_idle_status(info->idle_ip_index, 1);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
|
||||
ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed adding child nodes\n");
|
||||
goto err_of_populate;
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Probed successfully driver.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
err_of_populate:
|
||||
device_for_each_child(&indio_dev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
iio_device_unregister(indio_dev);
|
||||
err_irq:
|
||||
free_irq(info->irq, info);
|
||||
err_disable_clk:
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
exynos_adc_disable_clk(info);
|
||||
err_unprepare_clk:
|
||||
exynos_adc_unprepare_clk(info);
|
||||
err_disable_reg:
|
||||
if (info->vdd)
|
||||
regulator_disable(info->vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int exynos_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
device_for_each_child(&indio_dev->dev, NULL,
|
||||
exynos_adc_remove_devices);
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int exynos_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->exit_hw)
|
||||
info->data->exit_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct exynos_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = exynos_adc_enable_access(info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->data->init_hw)
|
||||
info->data->init_hw(info);
|
||||
|
||||
exynos_adc_disable_access(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(exynos_adc_pm_ops,
|
||||
exynos_adc_suspend,
|
||||
exynos_adc_resume);
|
||||
|
||||
static struct platform_driver exynos_adc_driver = {
|
||||
.probe = exynos_adc_probe,
|
||||
.remove = exynos_adc_remove,
|
||||
.driver = {
|
||||
.name = "exynos-adc",
|
||||
.of_match_table = exynos_adc_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(exynos_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Naveen Krishna Chatradhi <ch.naveen@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung EXYNOS5 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
254
drivers/iio/adc/lp8788_adc.c
Normal file
254
drivers/iio/adc/lp8788_adc.c
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*
|
||||
* TI LP8788 MFD - ADC driver
|
||||
*
|
||||
* Copyright 2012 Texas Instruments
|
||||
*
|
||||
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/driver.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/mfd/lp8788.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* register address */
|
||||
#define LP8788_ADC_CONF 0x60
|
||||
#define LP8788_ADC_RAW 0x61
|
||||
#define LP8788_ADC_DONE 0x63
|
||||
|
||||
#define ADC_CONV_START 1
|
||||
|
||||
struct lp8788_adc {
|
||||
struct lp8788 *lp;
|
||||
struct iio_map *map;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const int lp8788_scale[LPADC_MAX] = {
|
||||
[LPADC_VBATT_5P5] = 1343101,
|
||||
[LPADC_VIN_CHG] = 3052503,
|
||||
[LPADC_IBATT] = 610500,
|
||||
[LPADC_IC_TEMP] = 61050,
|
||||
[LPADC_VBATT_6P0] = 1465201,
|
||||
[LPADC_VBATT_5P0] = 1221001,
|
||||
[LPADC_ADC1] = 610500,
|
||||
[LPADC_ADC2] = 610500,
|
||||
[LPADC_VDD] = 1025641,
|
||||
[LPADC_VCOIN] = 757020,
|
||||
[LPADC_ADC3] = 610500,
|
||||
[LPADC_ADC4] = 610500,
|
||||
};
|
||||
|
||||
static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
|
||||
int *val)
|
||||
{
|
||||
unsigned int msb;
|
||||
unsigned int lsb;
|
||||
unsigned int result;
|
||||
u8 data;
|
||||
u8 rawdata[2];
|
||||
int size = ARRAY_SIZE(rawdata);
|
||||
int retry = 5;
|
||||
int ret;
|
||||
|
||||
data = (id << 1) | ADC_CONV_START;
|
||||
ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
/* retry until adc conversion is done */
|
||||
data = 0;
|
||||
while (retry--) {
|
||||
usleep_range(100, 200);
|
||||
|
||||
ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
/* conversion done */
|
||||
if (data)
|
||||
break;
|
||||
}
|
||||
|
||||
ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
|
||||
if (ret)
|
||||
goto err_io;
|
||||
|
||||
msb = (rawdata[0] << 4) & 0x00000ff0;
|
||||
lsb = (rawdata[1] >> 4) & 0x0000000f;
|
||||
result = msb | lsb;
|
||||
*val = result;
|
||||
|
||||
return 0;
|
||||
|
||||
err_io:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct lp8788_adc *adc = iio_priv(indio_dev);
|
||||
enum lp8788_adc_id id = chan->channel;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = lp8788_scale[id] / 1000000;
|
||||
*val2 = lp8788_scale[id] % 1000000;
|
||||
ret = IIO_VAL_INT_PLUS_MICRO;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info lp8788_adc_info = {
|
||||
.read_raw = &lp8788_adc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define LP8788_CHAN(_id, _type) { \
|
||||
.type = _type, \
|
||||
.indexed = 1, \
|
||||
.channel = LPADC_##_id, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = #_id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec lp8788_adc_channels[] = {
|
||||
[LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
|
||||
[LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
|
||||
[LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT),
|
||||
[LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP),
|
||||
[LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
|
||||
[LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
|
||||
[LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE),
|
||||
[LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE),
|
||||
[LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE),
|
||||
[LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
|
||||
[LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE),
|
||||
[LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE),
|
||||
};
|
||||
|
||||
/* default maps used by iio consumer (lp8788-charger driver) */
|
||||
static struct iio_map lp8788_default_iio_maps[] = {
|
||||
{
|
||||
.consumer_dev_name = "lp8788-charger",
|
||||
.consumer_channel = "lp8788_vbatt_5p0",
|
||||
.adc_channel_label = "VBATT_5P0",
|
||||
},
|
||||
{
|
||||
.consumer_dev_name = "lp8788-charger",
|
||||
.consumer_channel = "lp8788_adc1",
|
||||
.adc_channel_label = "ADC1",
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static int lp8788_iio_map_register(struct iio_dev *indio_dev,
|
||||
struct lp8788_platform_data *pdata,
|
||||
struct lp8788_adc *adc)
|
||||
{
|
||||
struct iio_map *map;
|
||||
int ret;
|
||||
|
||||
map = (!pdata || !pdata->adc_pdata) ?
|
||||
lp8788_default_iio_maps : pdata->adc_pdata;
|
||||
|
||||
ret = iio_map_array_register(indio_dev, map);
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "iio map err: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
adc->map = map;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lp8788_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
|
||||
struct iio_dev *indio_dev;
|
||||
struct lp8788_adc *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->lp = lp;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = pdev->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &lp8788_adc_info;
|
||||
indio_dev->channels = lp8788_adc_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "iio dev register err: %d\n", ret);
|
||||
goto err_iio_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_iio_device:
|
||||
iio_map_array_unregister(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int lp8788_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_map_array_unregister(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver lp8788_adc_driver = {
|
||||
.probe = lp8788_adc_probe,
|
||||
.remove = lp8788_adc_remove,
|
||||
.driver = {
|
||||
.name = LP8788_DEV_ADC,
|
||||
},
|
||||
};
|
||||
module_platform_driver(lp8788_adc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
|
||||
MODULE_AUTHOR("Milo Kim");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:lp8788-adc");
|
||||
521
drivers/iio/adc/max1027.c
Normal file
521
drivers/iio/adc/max1027.c
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
/*
|
||||
* iio/adc/max1027.c
|
||||
* Copyright (C) 2014 Philippe Reynes
|
||||
*
|
||||
* based on linux/drivers/iio/ad7923.c
|
||||
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
|
||||
* Copyright 2012 CS Systemes d'Information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* max1027.c
|
||||
*
|
||||
* Partial support for max1027 and similar chips.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
|
||||
#define MAX1027_CONV_REG BIT(7)
|
||||
#define MAX1027_SETUP_REG BIT(6)
|
||||
#define MAX1027_AVG_REG BIT(5)
|
||||
#define MAX1027_RST_REG BIT(4)
|
||||
|
||||
/* conversion register */
|
||||
#define MAX1027_TEMP BIT(0)
|
||||
#define MAX1027_SCAN_0_N (0x00 << 1)
|
||||
#define MAX1027_SCAN_N_M (0x01 << 1)
|
||||
#define MAX1027_SCAN_N (0x02 << 1)
|
||||
#define MAX1027_NOSCAN (0x03 << 1)
|
||||
#define MAX1027_CHAN(n) ((n) << 3)
|
||||
|
||||
/* setup register */
|
||||
#define MAX1027_UNIPOLAR 0x02
|
||||
#define MAX1027_BIPOLAR 0x03
|
||||
#define MAX1027_REF_MODE0 (0x00 << 2)
|
||||
#define MAX1027_REF_MODE1 (0x01 << 2)
|
||||
#define MAX1027_REF_MODE2 (0x02 << 2)
|
||||
#define MAX1027_REF_MODE3 (0x03 << 2)
|
||||
#define MAX1027_CKS_MODE0 (0x00 << 4)
|
||||
#define MAX1027_CKS_MODE1 (0x01 << 4)
|
||||
#define MAX1027_CKS_MODE2 (0x02 << 4)
|
||||
#define MAX1027_CKS_MODE3 (0x03 << 4)
|
||||
|
||||
/* averaging register */
|
||||
#define MAX1027_NSCAN_4 0x00
|
||||
#define MAX1027_NSCAN_8 0x01
|
||||
#define MAX1027_NSCAN_12 0x02
|
||||
#define MAX1027_NSCAN_16 0x03
|
||||
#define MAX1027_NAVG_4 (0x00 << 2)
|
||||
#define MAX1027_NAVG_8 (0x01 << 2)
|
||||
#define MAX1027_NAVG_16 (0x02 << 2)
|
||||
#define MAX1027_NAVG_32 (0x03 << 2)
|
||||
#define MAX1027_AVG_EN BIT(4)
|
||||
|
||||
enum max1027_id {
|
||||
max1027,
|
||||
max1029,
|
||||
max1031,
|
||||
};
|
||||
|
||||
static const struct spi_device_id max1027_id[] = {
|
||||
{"max1027", max1027},
|
||||
{"max1029", max1029},
|
||||
{"max1031", max1031},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, max1027_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max1027_adc_dt_ids[] = {
|
||||
{ .compatible = "maxim,max1027" },
|
||||
{ .compatible = "maxim,max1029" },
|
||||
{ .compatible = "maxim,max1031" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max1027_adc_dt_ids);
|
||||
#endif
|
||||
|
||||
#define MAX1027_V_CHAN(index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = index + 1, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 10, \
|
||||
.storagebits = 16, \
|
||||
.shift = 2, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define MAX1027_T_CHAN \
|
||||
{ \
|
||||
.type = IIO_TEMP, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_index = 0, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
.endianness = IIO_BE, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max1027_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1029_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11)
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec max1031_channels[] = {
|
||||
MAX1027_T_CHAN,
|
||||
MAX1027_V_CHAN(0),
|
||||
MAX1027_V_CHAN(1),
|
||||
MAX1027_V_CHAN(2),
|
||||
MAX1027_V_CHAN(3),
|
||||
MAX1027_V_CHAN(4),
|
||||
MAX1027_V_CHAN(5),
|
||||
MAX1027_V_CHAN(6),
|
||||
MAX1027_V_CHAN(7),
|
||||
MAX1027_V_CHAN(8),
|
||||
MAX1027_V_CHAN(9),
|
||||
MAX1027_V_CHAN(10),
|
||||
MAX1027_V_CHAN(11),
|
||||
MAX1027_V_CHAN(12),
|
||||
MAX1027_V_CHAN(13),
|
||||
MAX1027_V_CHAN(14),
|
||||
MAX1027_V_CHAN(15)
|
||||
};
|
||||
|
||||
static const unsigned long max1027_available_scan_masks[] = {
|
||||
0x000001ff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
static const unsigned long max1029_available_scan_masks[] = {
|
||||
0x00001fff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
static const unsigned long max1031_available_scan_masks[] = {
|
||||
0x0001ffff,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
struct max1027_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
const unsigned long *available_scan_masks;
|
||||
};
|
||||
|
||||
static const struct max1027_chip_info max1027_chip_info_tbl[] = {
|
||||
[max1027] = {
|
||||
.channels = max1027_channels,
|
||||
.num_channels = ARRAY_SIZE(max1027_channels),
|
||||
.available_scan_masks = max1027_available_scan_masks,
|
||||
},
|
||||
[max1029] = {
|
||||
.channels = max1029_channels,
|
||||
.num_channels = ARRAY_SIZE(max1029_channels),
|
||||
.available_scan_masks = max1029_available_scan_masks,
|
||||
},
|
||||
[max1031] = {
|
||||
.channels = max1031_channels,
|
||||
.num_channels = ARRAY_SIZE(max1031_channels),
|
||||
.available_scan_masks = max1031_available_scan_masks,
|
||||
},
|
||||
};
|
||||
|
||||
struct max1027_state {
|
||||
const struct max1027_chip_info *info;
|
||||
struct spi_device *spi;
|
||||
struct iio_trigger *trig;
|
||||
__be16 *buffer;
|
||||
struct mutex lock;
|
||||
|
||||
u8 reg ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int max1027_read_single_value(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
int ret;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (iio_buffer_enabled(indio_dev)) {
|
||||
dev_warn(&indio_dev->dev, "trigger mode already enabled");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/* Start acquisition on conversion register write */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_REF_MODE2 | MAX1027_CKS_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Failed to configure setup register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configure conversion register with the requested chan */
|
||||
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
|
||||
MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"Failed to configure conversion register\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* For an unknown reason, when we use the mode "10" (write
|
||||
* conversion register), the interrupt doesn't occur every time.
|
||||
* So we just wait 1 ms.
|
||||
*/
|
||||
mdelay(1);
|
||||
|
||||
/* Read result */
|
||||
ret = spi_read(st->spi, st->buffer, (chan->type == IIO_TEMP) ? 4 : 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = be16_to_cpu(st->buffer[0]);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int max1027_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
int ret = 0;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max1027_read_single_value(indio_dev, chan, val);
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_TEMP:
|
||||
*val = 1;
|
||||
*val2 = 8;
|
||||
ret = IIO_VAL_FRACTIONAL;
|
||||
break;
|
||||
case IIO_VOLTAGE:
|
||||
*val = 2500;
|
||||
*val2 = 10;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1027_debugfs_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
u8 *val = (u8 *)st->buffer;
|
||||
|
||||
if (readval != NULL)
|
||||
return -EINVAL;
|
||||
|
||||
*val = (u8)writeval;
|
||||
return spi_write(st->spi, val, 1);
|
||||
}
|
||||
|
||||
static int max1027_validate_trigger(struct iio_dev *indio_dev,
|
||||
struct iio_trigger *trig)
|
||||
{
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
if (st->trig != trig)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (state) {
|
||||
/* Start acquisition on cnvst */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE0 |
|
||||
MAX1027_REF_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Scan from 0 to max */
|
||||
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(0) |
|
||||
MAX1027_SCAN_N_M | MAX1027_TEMP;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
/* Start acquisition on conversion register write */
|
||||
st->reg = MAX1027_SETUP_REG | MAX1027_CKS_MODE2 |
|
||||
MAX1027_REF_MODE2;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max1027_validate_device(struct iio_trigger *trig,
|
||||
struct iio_dev *indio_dev)
|
||||
{
|
||||
struct iio_dev *indio = iio_trigger_get_drvdata(trig);
|
||||
|
||||
if (indio != indio_dev)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t max1027_trigger_handler(int irq, void *private)
|
||||
{
|
||||
struct iio_poll_func *pf = (struct iio_poll_func *)private;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct max1027_state *st = iio_priv(indio_dev);
|
||||
|
||||
pr_debug("%s(irq=%d, private=0x%p)\n", __func__, irq, private);
|
||||
|
||||
/* fill buffer with all channel */
|
||||
spi_read(st->spi, st->buffer, indio_dev->masklength * 2);
|
||||
|
||||
iio_push_to_buffers(indio_dev, st->buffer);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops max1027_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.validate_device = &max1027_validate_device,
|
||||
.set_trigger_state = &max1027_set_trigger_state,
|
||||
};
|
||||
|
||||
static const struct iio_info max1027_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &max1027_read_raw,
|
||||
.validate_trigger = &max1027_validate_trigger,
|
||||
.debugfs_reg_access = &max1027_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int max1027_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max1027_state *st;
|
||||
|
||||
pr_debug("%s: probe(spi = 0x%p)\n", __func__, spi);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
pr_err("Can't allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
st->spi = spi;
|
||||
st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
mutex_init(&st->lock);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &max1027_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->info->channels;
|
||||
indio_dev->num_channels = st->info->num_channels;
|
||||
indio_dev->available_scan_masks = st->info->available_scan_masks;
|
||||
|
||||
st->buffer = devm_kmalloc(&indio_dev->dev,
|
||||
indio_dev->num_channels * 2,
|
||||
GFP_KERNEL);
|
||||
if (st->buffer == NULL) {
|
||||
dev_err(&indio_dev->dev, "Can't allocate bufffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
&max1027_trigger_handler, NULL);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to setup buffer\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-trigger",
|
||||
indio_dev->name);
|
||||
if (st->trig == NULL) {
|
||||
ret = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
|
||||
goto fail_trigger_alloc;
|
||||
}
|
||||
|
||||
st->trig->ops = &max1027_trigger_ops;
|
||||
st->trig->dev.parent = &spi->dev;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
iio_trigger_register(st->trig);
|
||||
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_FALLING,
|
||||
spi->dev.driver->name, st->trig);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
/* Disable averaging */
|
||||
st->reg = MAX1027_AVG_REG;
|
||||
ret = spi_write(st->spi, &st->reg, 1);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "Failed to register iio device\n");
|
||||
goto fail_dev_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_dev_register:
|
||||
fail_trigger_alloc:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max1027_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
|
||||
pr_debug("%s: remove(spi = 0x%p)\n", __func__, spi);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver max1027_driver = {
|
||||
.driver = {
|
||||
.name = "max1027",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max1027_probe,
|
||||
.remove = max1027_remove,
|
||||
.id_table = max1027_id,
|
||||
};
|
||||
module_spi_driver(max1027_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
|
||||
MODULE_DESCRIPTION("MAX1027/MAX1029/MAX1031 ADC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1701
drivers/iio/adc/max1363.c
Normal file
1701
drivers/iio/adc/max1363.c
Normal file
File diff suppressed because it is too large
Load diff
249
drivers/iio/adc/mcp320x.c
Normal file
249
drivers/iio/adc/mcp320x.c
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
|
||||
*
|
||||
* Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
|
||||
* Datasheet can be found here:
|
||||
* http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MCP_SINGLE_ENDED (1 << 3)
|
||||
#define MCP_START_BIT (1 << 4)
|
||||
|
||||
enum {
|
||||
mcp3204,
|
||||
mcp3208,
|
||||
};
|
||||
|
||||
struct mcp320x {
|
||||
struct spi_device *spi;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer transfer[2];
|
||||
|
||||
u8 tx_buf;
|
||||
u8 rx_buf[2];
|
||||
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
|
||||
{
|
||||
int ret;
|
||||
|
||||
adc->tx_buf = msg;
|
||||
ret = spi_sync(adc->spi, &adc->msg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ((adc->rx_buf[0] & 0x3f) << 6) |
|
||||
(adc->rx_buf[1] >> 2);
|
||||
}
|
||||
|
||||
static int mcp320x_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp320x *adc = iio_priv(indio_dev);
|
||||
int ret = -EINVAL;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (channel->differential)
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | channel->address);
|
||||
else
|
||||
ret = mcp320x_adc_conversion(adc,
|
||||
MCP_START_BIT | MCP_SINGLE_ENDED |
|
||||
channel->address);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
*val = ret;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Digital output code = (4096 * Vin) / Vref */
|
||||
ret = regulator_get_voltage(adc->reg);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
ret = IIO_VAL_FRACTIONAL_LOG2;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MCP320X_VOLTAGE_CHANNEL(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num), \
|
||||
.address = (num), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
#define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num * 2), \
|
||||
.channel2 = (num * 2 + 1), \
|
||||
.address = (num * 2), \
|
||||
.differential = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp3204_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL(2),
|
||||
MCP320X_VOLTAGE_CHANNEL(3),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3208_channels[] = {
|
||||
MCP320X_VOLTAGE_CHANNEL(0),
|
||||
MCP320X_VOLTAGE_CHANNEL(1),
|
||||
MCP320X_VOLTAGE_CHANNEL(2),
|
||||
MCP320X_VOLTAGE_CHANNEL(3),
|
||||
MCP320X_VOLTAGE_CHANNEL(4),
|
||||
MCP320X_VOLTAGE_CHANNEL(5),
|
||||
MCP320X_VOLTAGE_CHANNEL(6),
|
||||
MCP320X_VOLTAGE_CHANNEL(7),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(0),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(1),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(2),
|
||||
MCP320X_VOLTAGE_CHANNEL_DIFF(3),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp320x_info = {
|
||||
.read_raw = mcp320x_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
struct mcp3208_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
static const struct mcp3208_chip_info mcp3208_chip_infos[] = {
|
||||
[mcp3204] = {
|
||||
.channels = mcp3204_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3204_channels)
|
||||
},
|
||||
[mcp3208] = {
|
||||
.channels = mcp3208_channels,
|
||||
.num_channels = ARRAY_SIZE(mcp3208_channels)
|
||||
},
|
||||
};
|
||||
|
||||
static int mcp320x_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp320x *adc;
|
||||
const struct mcp3208_chip_info *chip_info;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp320x_info;
|
||||
|
||||
chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->channels = chip_info->channels;
|
||||
indio_dev->num_channels = chip_info->num_channels;
|
||||
|
||||
adc->transfer[0].tx_buf = &adc->tx_buf;
|
||||
adc->transfer[0].len = sizeof(adc->tx_buf);
|
||||
adc->transfer[1].rx_buf = adc->rx_buf;
|
||||
adc->transfer[1].len = sizeof(adc->rx_buf);
|
||||
|
||||
spi_message_init_with_transfers(&adc->msg, adc->transfer,
|
||||
ARRAY_SIZE(adc->transfer));
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
return PTR_ERR(adc->reg);
|
||||
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0)
|
||||
goto reg_disable;
|
||||
|
||||
return 0;
|
||||
|
||||
reg_disable:
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp320x_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp320x *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id mcp320x_id[] = {
|
||||
{ "mcp3204", mcp3204 },
|
||||
{ "mcp3208", mcp3208 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp320x_id);
|
||||
|
||||
static struct spi_driver mcp320x_driver = {
|
||||
.driver = {
|
||||
.name = "mcp320x",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mcp320x_probe,
|
||||
.remove = mcp320x_remove,
|
||||
.id_table = mcp320x_id,
|
||||
};
|
||||
module_spi_driver(mcp320x_driver);
|
||||
|
||||
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
417
drivers/iio/adc/mcp3422.c
Normal file
417
drivers/iio/adc/mcp3422.c
Normal file
|
|
@ -0,0 +1,417 @@
|
|||
/*
|
||||
* mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family
|
||||
*
|
||||
* Copyright (C) 2013, Angelo Compagnucci
|
||||
* Author: Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf
|
||||
* http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf
|
||||
*
|
||||
* This driver exports the value of analog input voltage to sysfs, the
|
||||
* voltage unit is nV.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
/* Masks */
|
||||
#define MCP3422_CHANNEL_MASK 0x60
|
||||
#define MCP3422_PGA_MASK 0x03
|
||||
#define MCP3422_SRATE_MASK 0x0C
|
||||
#define MCP3422_SRATE_240 0x0
|
||||
#define MCP3422_SRATE_60 0x1
|
||||
#define MCP3422_SRATE_15 0x2
|
||||
#define MCP3422_SRATE_3 0x3
|
||||
#define MCP3422_PGA_1 0
|
||||
#define MCP3422_PGA_2 1
|
||||
#define MCP3422_PGA_4 2
|
||||
#define MCP3422_PGA_8 3
|
||||
#define MCP3422_CONT_SAMPLING 0x10
|
||||
|
||||
#define MCP3422_CHANNEL(config) (((config) & MCP3422_CHANNEL_MASK) >> 5)
|
||||
#define MCP3422_PGA(config) ((config) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE(config) (((config) & MCP3422_SRATE_MASK) >> 2)
|
||||
|
||||
#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK)
|
||||
#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK)
|
||||
#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK)
|
||||
|
||||
#define MCP3422_CHAN(_index) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \
|
||||
| BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const int mcp3422_scales[4][4] = {
|
||||
{ 1000000, 500000, 250000, 125000 },
|
||||
{ 250000 , 125000, 62500 , 31250 },
|
||||
{ 62500 , 31250 , 15625 , 7812 },
|
||||
{ 15625 , 7812 , 3906 , 1953 } };
|
||||
|
||||
/* Constant msleep times for data acquisitions */
|
||||
static const int mcp3422_read_times[4] = {
|
||||
[MCP3422_SRATE_240] = 1000 / 240,
|
||||
[MCP3422_SRATE_60] = 1000 / 60,
|
||||
[MCP3422_SRATE_15] = 1000 / 15,
|
||||
[MCP3422_SRATE_3] = 1000 / 3 };
|
||||
|
||||
/* sample rates to integer conversion table */
|
||||
static const int mcp3422_sample_rates[4] = {
|
||||
[MCP3422_SRATE_240] = 240,
|
||||
[MCP3422_SRATE_60] = 60,
|
||||
[MCP3422_SRATE_15] = 15,
|
||||
[MCP3422_SRATE_3] = 3 };
|
||||
|
||||
/* sample rates to sign extension table */
|
||||
static const int mcp3422_sign_extend[4] = {
|
||||
[MCP3422_SRATE_240] = 11,
|
||||
[MCP3422_SRATE_60] = 13,
|
||||
[MCP3422_SRATE_15] = 15,
|
||||
[MCP3422_SRATE_3] = 17 };
|
||||
|
||||
/* Client data (each client gets its own) */
|
||||
struct mcp3422 {
|
||||
struct i2c_client *i2c;
|
||||
u8 id;
|
||||
u8 config;
|
||||
u8 pga[4];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
ret = i2c_master_send(adc->i2c, &newconfig, 1);
|
||||
if (ret > 0) {
|
||||
adc->config = newconfig;
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 buf[4] = {0, 0, 0, 0};
|
||||
u32 temp;
|
||||
|
||||
if (sample_rate == MCP3422_SRATE_3) {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 4);
|
||||
temp = buf[0] << 16 | buf[1] << 8 | buf[2];
|
||||
*config = buf[3];
|
||||
} else {
|
||||
ret = i2c_master_recv(adc->i2c, buf, 3);
|
||||
temp = buf[0] << 8 | buf[1];
|
||||
*config = buf[2];
|
||||
}
|
||||
|
||||
*value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp3422_read_channel(struct mcp3422 *adc,
|
||||
struct iio_chan_spec const *channel, int *value)
|
||||
{
|
||||
int ret;
|
||||
u8 config;
|
||||
u8 req_channel = channel->channel;
|
||||
|
||||
if (req_channel != MCP3422_CHANNEL(adc->config)) {
|
||||
config = adc->config;
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
ret = mcp3422_update_config(adc, config);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]);
|
||||
}
|
||||
|
||||
return mcp3422_read(adc, value, &config);
|
||||
}
|
||||
|
||||
static int mcp3422_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *val1,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
int err;
|
||||
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
u8 pga = MCP3422_PGA(adc->config);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = mcp3422_read_channel(adc, channel, val1);
|
||||
if (err < 0)
|
||||
return -EINVAL;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
*val1 = 0;
|
||||
*val2 = mcp3422_scales[sample_rate][pga];
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)];
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int val1,
|
||||
int val2, long mask)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(iio);
|
||||
u8 temp;
|
||||
u8 config = adc->config;
|
||||
u8 req_channel = channel->channel;
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(config);
|
||||
u8 i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
if (val1 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) {
|
||||
if (val2 == mcp3422_scales[sample_rate][i]) {
|
||||
adc->pga[req_channel] = i;
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_PGA_MASK;
|
||||
config |= MCP3422_PGA_VALUE(adc->pga[req_channel]);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
switch (val1) {
|
||||
case 240:
|
||||
temp = MCP3422_SRATE_240;
|
||||
break;
|
||||
case 60:
|
||||
temp = MCP3422_SRATE_60;
|
||||
break;
|
||||
case 15:
|
||||
temp = MCP3422_SRATE_15;
|
||||
break;
|
||||
case 3:
|
||||
if (adc->id > 4)
|
||||
return -EINVAL;
|
||||
temp = MCP3422_SRATE_3;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
config &= ~MCP3422_CHANNEL_MASK;
|
||||
config |= MCP3422_CHANNEL_VALUE(req_channel);
|
||||
config &= ~MCP3422_SRATE_MASK;
|
||||
config |= MCP3422_SAMPLE_RATE_VALUE(temp);
|
||||
|
||||
return mcp3422_update_config(adc, config);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, long mask)
|
||||
{
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t mcp3422_show_samp_freqs(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
|
||||
|
||||
if (adc->id > 4)
|
||||
return sprintf(buf, "240 60 15\n");
|
||||
|
||||
return sprintf(buf, "240 60 15 3\n");
|
||||
}
|
||||
|
||||
static ssize_t mcp3422_show_scales(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev));
|
||||
u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config);
|
||||
|
||||
return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n",
|
||||
mcp3422_scales[sample_rate][0],
|
||||
mcp3422_scales[sample_rate][1],
|
||||
mcp3422_scales[sample_rate][2],
|
||||
mcp3422_scales[sample_rate][3]);
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
|
||||
mcp3422_show_samp_freqs, NULL, 0);
|
||||
static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO,
|
||||
mcp3422_show_scales, NULL, 0);
|
||||
|
||||
static struct attribute *mcp3422_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
&iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp3422_attribute_group = {
|
||||
.attrs = mcp3422_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3422_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp3424_channels[] = {
|
||||
MCP3422_CHAN(0),
|
||||
MCP3422_CHAN(1),
|
||||
MCP3422_CHAN(2),
|
||||
MCP3422_CHAN(3),
|
||||
};
|
||||
|
||||
static const struct iio_info mcp3422_info = {
|
||||
.read_raw = mcp3422_read_raw,
|
||||
.write_raw = mcp3422_write_raw,
|
||||
.write_raw_get_fmt = mcp3422_write_raw_get_fmt,
|
||||
.attrs = &mcp3422_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp3422_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp3422 *adc;
|
||||
int err;
|
||||
u8 config;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->i2c = client;
|
||||
adc->id = (u8)(id->driver_data);
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &mcp3422_info;
|
||||
|
||||
switch (adc->id) {
|
||||
case 2:
|
||||
case 3:
|
||||
case 6:
|
||||
case 7:
|
||||
indio_dev->channels = mcp3422_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels);
|
||||
break;
|
||||
case 4:
|
||||
case 8:
|
||||
indio_dev->channels = mcp3424_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels);
|
||||
break;
|
||||
}
|
||||
|
||||
/* meaningful default configuration */
|
||||
config = (MCP3422_CONT_SAMPLING
|
||||
| MCP3422_CHANNEL_VALUE(1)
|
||||
| MCP3422_PGA_VALUE(MCP3422_PGA_1)
|
||||
| MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240));
|
||||
mcp3422_update_config(adc, config);
|
||||
|
||||
err = devm_iio_device_register(&client->dev, indio_dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp3422_id[] = {
|
||||
{ "mcp3422", 2 },
|
||||
{ "mcp3423", 3 },
|
||||
{ "mcp3424", 4 },
|
||||
{ "mcp3426", 6 },
|
||||
{ "mcp3427", 7 },
|
||||
{ "mcp3428", 8 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp3422_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id mcp3422_of_match[] = {
|
||||
{ .compatible = "mcp3422" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mcp3422_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver mcp3422_driver = {
|
||||
.driver = {
|
||||
.name = "mcp3422",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mcp3422_of_match),
|
||||
},
|
||||
.probe = mcp3422_probe,
|
||||
.id_table = mcp3422_id,
|
||||
};
|
||||
module_i2c_driver(mcp3422_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
173
drivers/iio/adc/men_z188_adc.c
Normal file
173
drivers/iio/adc/men_z188_adc.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* MEN 16z188 Analog to Digial Converter
|
||||
*
|
||||
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
|
||||
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mcb.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define Z188_ADC_MAX_CHAN 8
|
||||
#define Z188_ADC_GAIN 0x0700000
|
||||
#define Z188_MODE_VOLTAGE BIT(27)
|
||||
#define Z188_CFG_AUTO 0x1
|
||||
#define Z188_CTRL_REG 0x40
|
||||
|
||||
#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc)
|
||||
#define ADC_OVR(x) ((x) & 0x1)
|
||||
|
||||
struct z188_adc {
|
||||
struct resource *mem;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
#define Z188_ADC_CHANNEL(idx) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec z188_adc_iio_channels[] = {
|
||||
Z188_ADC_CHANNEL(0),
|
||||
Z188_ADC_CHANNEL(1),
|
||||
Z188_ADC_CHANNEL(2),
|
||||
Z188_ADC_CHANNEL(3),
|
||||
Z188_ADC_CHANNEL(4),
|
||||
Z188_ADC_CHANNEL(5),
|
||||
Z188_ADC_CHANNEL(6),
|
||||
Z188_ADC_CHANNEL(7),
|
||||
};
|
||||
|
||||
static int z188_iio_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
struct z188_adc *adc = iio_priv(iio_dev);
|
||||
int ret;
|
||||
u16 tmp;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
tmp = readw(adc->base + chan->channel * 4);
|
||||
|
||||
if (ADC_OVR(tmp)) {
|
||||
dev_info(&iio_dev->dev,
|
||||
"Oversampling error on ADC channel %d\n",
|
||||
chan->channel);
|
||||
return -EIO;
|
||||
}
|
||||
*val = ADC_DATA(tmp);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct iio_info z188_adc_info = {
|
||||
.read_raw = &z188_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static void men_z188_config_channels(void __iomem *addr)
|
||||
{
|
||||
int i;
|
||||
u32 cfg;
|
||||
u32 ctl;
|
||||
|
||||
ctl = readl(addr + Z188_CTRL_REG);
|
||||
ctl |= Z188_CFG_AUTO;
|
||||
writel(ctl, addr + Z188_CTRL_REG);
|
||||
|
||||
for (i = 0; i < Z188_ADC_MAX_CHAN; i++) {
|
||||
cfg = readl(addr + i);
|
||||
cfg &= ~Z188_ADC_GAIN;
|
||||
cfg |= Z188_MODE_VOLTAGE;
|
||||
writel(cfg, addr + i);
|
||||
}
|
||||
}
|
||||
|
||||
static int men_z188_probe(struct mcb_device *dev,
|
||||
const struct mcb_device_id *id)
|
||||
{
|
||||
struct z188_adc *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
indio_dev->name = "z188-adc";
|
||||
indio_dev->dev.parent = &dev->dev;
|
||||
indio_dev->info = &z188_adc_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = z188_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels);
|
||||
|
||||
mem = mcb_request_mem(dev, "z188-adc");
|
||||
if (IS_ERR(mem))
|
||||
return PTR_ERR(mem);
|
||||
|
||||
adc->base = ioremap(mem->start, resource_size(mem));
|
||||
if (adc->base == NULL)
|
||||
goto err;
|
||||
|
||||
men_z188_config_channels(adc->base);
|
||||
|
||||
adc->mem = mem;
|
||||
mcb_set_drvdata(dev, indio_dev);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
err:
|
||||
mcb_release_mem(mem);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
static void men_z188_remove(struct mcb_device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = mcb_get_drvdata(dev);
|
||||
struct z188_adc *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
iounmap(adc->base);
|
||||
mcb_release_mem(adc->mem);
|
||||
}
|
||||
|
||||
static const struct mcb_device_id men_z188_ids[] = {
|
||||
{ .device = 0xbc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
|
||||
|
||||
static struct mcb_driver men_z188_driver = {
|
||||
.driver = {
|
||||
.name = "z188-adc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = men_z188_probe,
|
||||
.remove = men_z188_remove,
|
||||
.id_table = men_z188_ids,
|
||||
};
|
||||
module_mcb_driver(men_z188_driver);
|
||||
|
||||
MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core");
|
||||
MODULE_ALIAS("mcb:16z188");
|
||||
582
drivers/iio/adc/nau7802.c
Normal file
582
drivers/iio/adc/nau7802.c
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
/*
|
||||
* Driver for the Nuvoton NAU7802 ADC
|
||||
*
|
||||
* Copyright 2013 Free Electrons
|
||||
*
|
||||
* Licensed under the GPLv2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define NAU7802_REG_PUCTRL 0x00
|
||||
#define NAU7802_PUCTRL_RR(x) (x << 0)
|
||||
#define NAU7802_PUCTRL_RR_BIT NAU7802_PUCTRL_RR(1)
|
||||
#define NAU7802_PUCTRL_PUD(x) (x << 1)
|
||||
#define NAU7802_PUCTRL_PUD_BIT NAU7802_PUCTRL_PUD(1)
|
||||
#define NAU7802_PUCTRL_PUA(x) (x << 2)
|
||||
#define NAU7802_PUCTRL_PUA_BIT NAU7802_PUCTRL_PUA(1)
|
||||
#define NAU7802_PUCTRL_PUR(x) (x << 3)
|
||||
#define NAU7802_PUCTRL_PUR_BIT NAU7802_PUCTRL_PUR(1)
|
||||
#define NAU7802_PUCTRL_CS(x) (x << 4)
|
||||
#define NAU7802_PUCTRL_CS_BIT NAU7802_PUCTRL_CS(1)
|
||||
#define NAU7802_PUCTRL_CR(x) (x << 5)
|
||||
#define NAU7802_PUCTRL_CR_BIT NAU7802_PUCTRL_CR(1)
|
||||
#define NAU7802_PUCTRL_AVDDS(x) (x << 7)
|
||||
#define NAU7802_PUCTRL_AVDDS_BIT NAU7802_PUCTRL_AVDDS(1)
|
||||
#define NAU7802_REG_CTRL1 0x01
|
||||
#define NAU7802_CTRL1_VLDO(x) (x << 3)
|
||||
#define NAU7802_CTRL1_GAINS(x) (x)
|
||||
#define NAU7802_CTRL1_GAINS_BITS 0x07
|
||||
#define NAU7802_REG_CTRL2 0x02
|
||||
#define NAU7802_CTRL2_CHS(x) (x << 7)
|
||||
#define NAU7802_CTRL2_CRS(x) (x << 4)
|
||||
#define NAU7802_SAMP_FREQ_320 0x07
|
||||
#define NAU7802_CTRL2_CHS_BIT NAU7802_CTRL2_CHS(1)
|
||||
#define NAU7802_REG_ADC_B2 0x12
|
||||
#define NAU7802_REG_ADC_B1 0x13
|
||||
#define NAU7802_REG_ADC_B0 0x14
|
||||
#define NAU7802_REG_ADC_CTRL 0x15
|
||||
|
||||
#define NAU7802_MIN_CONVERSIONS 6
|
||||
|
||||
struct nau7802_state {
|
||||
struct i2c_client *client;
|
||||
s32 last_value;
|
||||
struct mutex lock;
|
||||
struct mutex data_lock;
|
||||
u32 vref_mv;
|
||||
u32 conversion_count;
|
||||
u32 min_conversions;
|
||||
u8 sample_rate;
|
||||
u32 scale_avail[8];
|
||||
struct completion value_ok;
|
||||
};
|
||||
|
||||
#define NAU7802_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (chan), \
|
||||
.scan_index = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec nau7802_chan_array[] = {
|
||||
NAU7802_CHANNEL(0),
|
||||
NAU7802_CHANNEL(1),
|
||||
};
|
||||
|
||||
static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80,
|
||||
10, 10, 10, 320};
|
||||
|
||||
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320");
|
||||
|
||||
static struct attribute *nau7802_attributes[] = {
|
||||
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group nau7802_attribute_group = {
|
||||
.attrs = nau7802_attributes,
|
||||
};
|
||||
|
||||
static int nau7802_set_gain(struct nau7802_state *st, int gain)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->lock);
|
||||
st->conversion_count = 0;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
|
||||
if (ret < 0)
|
||||
goto nau7802_sysfs_set_gain_out;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
|
||||
(ret & (~NAU7802_CTRL1_GAINS_BITS)) |
|
||||
gain);
|
||||
|
||||
nau7802_sysfs_set_gain_out:
|
||||
mutex_unlock(&st->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_read_conversion(struct nau7802_state *st)
|
||||
{
|
||||
int data;
|
||||
|
||||
mutex_lock(&st->data_lock);
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B2);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value = data << 16;
|
||||
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B1);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value |= data << 8;
|
||||
|
||||
data = i2c_smbus_read_byte_data(st->client, NAU7802_REG_ADC_B0);
|
||||
if (data < 0)
|
||||
goto nau7802_read_conversion_out;
|
||||
st->last_value |= data;
|
||||
|
||||
st->last_value = sign_extend32(st->last_value, 23);
|
||||
|
||||
nau7802_read_conversion_out:
|
||||
mutex_unlock(&st->data_lock);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/*
|
||||
* Conversions are synchronised on the rising edge of NAU7802_PUCTRL_CS_BIT
|
||||
*/
|
||||
static int nau7802_sync(struct nau7802_state *st)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
ret | NAU7802_PUCTRL_CS_BIT);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int status;
|
||||
|
||||
status = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (status < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (!(status & NAU7802_PUCTRL_CR_BIT))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (nau7802_read_conversion(st) < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
/*
|
||||
* Because there is actually only one ADC for both channels, we have to
|
||||
* wait for enough conversions to happen before getting a significant
|
||||
* value when changing channels and the values are far apart.
|
||||
*/
|
||||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
|
||||
complete_all(&st->value_ok);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int nau7802_read_irq(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
reinit_completion(&st->value_ok);
|
||||
enable_irq(st->client->irq);
|
||||
|
||||
nau7802_sync(st);
|
||||
|
||||
/* read registers to ensure we flush everything */
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
goto read_chan_info_failure;
|
||||
|
||||
/* Wait for a conversion to finish */
|
||||
ret = wait_for_completion_interruptible_timeout(&st->value_ok,
|
||||
msecs_to_jiffies(1000));
|
||||
if (ret == 0)
|
||||
ret = -ETIMEDOUT;
|
||||
|
||||
if (ret < 0)
|
||||
goto read_chan_info_failure;
|
||||
|
||||
disable_irq(st->client->irq);
|
||||
|
||||
*val = st->last_value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
|
||||
read_chan_info_failure:
|
||||
disable_irq(st->client->irq);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_read_poll(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
nau7802_sync(st);
|
||||
|
||||
/* read registers to ensure we flush everything */
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Because there is actually only one ADC for both channels, we have to
|
||||
* wait for enough conversions to happen before getting a significant
|
||||
* value when changing channels and the values are far appart.
|
||||
*/
|
||||
do {
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (!(ret & NAU7802_PUCTRL_CR_BIT)) {
|
||||
if (st->sample_rate != NAU7802_SAMP_FREQ_320)
|
||||
msleep(20);
|
||||
else
|
||||
mdelay(4);
|
||||
ret = i2c_smbus_read_byte_data(st->client,
|
||||
NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = nau7802_read_conversion(st);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
|
||||
st->conversion_count++;
|
||||
} while (st->conversion_count < NAU7802_MIN_CONVERSIONS);
|
||||
|
||||
*val = st->last_value;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int nau7802_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&st->lock);
|
||||
/*
|
||||
* Select the channel to use
|
||||
* - Channel 1 is value 0 in the CHS register
|
||||
* - Channel 2 is value 1 in the CHS register
|
||||
*/
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (((ret & NAU7802_CTRL2_CHS_BIT) && !chan->channel) ||
|
||||
(!(ret & NAU7802_CTRL2_CHS_BIT) &&
|
||||
chan->channel)) {
|
||||
st->conversion_count = 0;
|
||||
ret = i2c_smbus_write_byte_data(st->client,
|
||||
NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CHS(chan->channel) |
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (st->client->irq)
|
||||
ret = nau7802_read_irq(indio_dev, chan, val);
|
||||
else
|
||||
ret = nau7802_read_poll(indio_dev, chan, val);
|
||||
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_CTRL1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* We have 24 bits of signed data, that means 23 bits of data
|
||||
* plus the sign bit
|
||||
*/
|
||||
*val = st->vref_mv;
|
||||
*val2 = 23 + (ret & NAU7802_CTRL1_GAINS_BITS);
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = nau7802_sample_freq_avail[st->sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nau7802_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
int i, ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
if (val2 == st->scale_avail[i])
|
||||
return nau7802_set_gain(st, i);
|
||||
|
||||
break;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0; i < ARRAY_SIZE(nau7802_sample_freq_avail); i++)
|
||||
if (val == nau7802_sample_freq_avail[i]) {
|
||||
mutex_lock(&st->lock);
|
||||
st->sample_rate = i;
|
||||
st->conversion_count = 0;
|
||||
ret = i2c_smbus_write_byte_data(st->client,
|
||||
NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
mutex_unlock(&st->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int nau7802_write_raw_get_fmt(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
long mask)
|
||||
{
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
}
|
||||
|
||||
static const struct iio_info nau7802_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &nau7802_read_raw,
|
||||
.write_raw = &nau7802_write_raw,
|
||||
.write_raw_get_fmt = nau7802_write_raw_get_fmt,
|
||||
.attrs = &nau7802_attribute_group,
|
||||
};
|
||||
|
||||
static int nau7802_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct nau7802_state *st;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
int i, ret;
|
||||
u8 data;
|
||||
u32 tmp = 0;
|
||||
|
||||
if (!client->dev.of_node) {
|
||||
dev_err(&client->dev, "No device tree node available.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = dev_name(&client->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &nau7802_info;
|
||||
|
||||
st->client = client;
|
||||
|
||||
/* Reset the device */
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
NAU7802_PUCTRL_RR_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enter normal operation mode */
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL,
|
||||
NAU7802_PUCTRL_PUD_BIT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* After about 200 usecs, the device should be ready and then
|
||||
* the Power Up bit will be set to 1. If not, wait for it.
|
||||
*/
|
||||
udelay(210);
|
||||
ret = i2c_smbus_read_byte_data(st->client, NAU7802_REG_PUCTRL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!(ret & NAU7802_PUCTRL_PUR_BIT))
|
||||
return ret;
|
||||
|
||||
of_property_read_u32(np, "nuvoton,vldo", &tmp);
|
||||
st->vref_mv = tmp;
|
||||
|
||||
data = NAU7802_PUCTRL_PUD_BIT | NAU7802_PUCTRL_PUA_BIT |
|
||||
NAU7802_PUCTRL_CS_BIT;
|
||||
if (tmp >= 2400)
|
||||
data |= NAU7802_PUCTRL_AVDDS_BIT;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_PUCTRL, data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_ADC_CTRL, 0x30);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (tmp >= 2400) {
|
||||
data = NAU7802_CTRL1_VLDO((4500 - tmp) / 300);
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL1,
|
||||
data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Populate available ADC input ranges */
|
||||
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
|
||||
st->scale_avail[i] = (((u64)st->vref_mv) * 1000000000ULL)
|
||||
>> (23 + i);
|
||||
|
||||
init_completion(&st->value_ok);
|
||||
|
||||
/*
|
||||
* The ADC fires continuously and we can't do anything about
|
||||
* it. So we need to have the IRQ disabled by default, and we
|
||||
* will enable them back when we will need them..
|
||||
*/
|
||||
if (client->irq) {
|
||||
ret = request_threaded_irq(client->irq,
|
||||
NULL,
|
||||
nau7802_eoc_trigger,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
client->dev.driver->name,
|
||||
indio_dev);
|
||||
if (ret) {
|
||||
/*
|
||||
* What may happen here is that our IRQ controller is
|
||||
* not able to get level interrupt but this is required
|
||||
* by this ADC as when going over 40 sample per second,
|
||||
* the interrupt line may stay high between conversions.
|
||||
* So, we continue no matter what but we switch to
|
||||
* polling mode.
|
||||
*/
|
||||
dev_info(&client->dev,
|
||||
"Failed to allocate IRQ, using polling mode\n");
|
||||
client->irq = 0;
|
||||
} else
|
||||
disable_irq(client->irq);
|
||||
}
|
||||
|
||||
if (!client->irq) {
|
||||
/*
|
||||
* We are polling, use the fastest sample rate by
|
||||
* default
|
||||
*/
|
||||
st->sample_rate = NAU7802_SAMP_FREQ_320;
|
||||
ret = i2c_smbus_write_byte_data(st->client, NAU7802_REG_CTRL2,
|
||||
NAU7802_CTRL2_CRS(st->sample_rate));
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
}
|
||||
|
||||
/* Setup the ADC channels available on the board */
|
||||
indio_dev->num_channels = ARRAY_SIZE(nau7802_chan_array);
|
||||
indio_dev->channels = nau7802_chan_array;
|
||||
|
||||
mutex_init(&st->lock);
|
||||
mutex_init(&st->data_lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "Couldn't register the device.\n");
|
||||
goto error_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_device_register:
|
||||
mutex_destroy(&st->lock);
|
||||
mutex_destroy(&st->data_lock);
|
||||
error_free_irq:
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int nau7802_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct nau7802_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
mutex_destroy(&st->lock);
|
||||
mutex_destroy(&st->data_lock);
|
||||
if (client->irq)
|
||||
free_irq(client->irq, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id nau7802_i2c_id[] = {
|
||||
{ "nau7802", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, nau7802_i2c_id);
|
||||
|
||||
static const struct of_device_id nau7802_dt_ids[] = {
|
||||
{ .compatible = "nuvoton,nau7802" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, nau7802_dt_ids);
|
||||
|
||||
static struct i2c_driver nau7802_driver = {
|
||||
.probe = nau7802_probe,
|
||||
.remove = nau7802_remove,
|
||||
.id_table = nau7802_i2c_id,
|
||||
.driver = {
|
||||
.name = "nau7802",
|
||||
.of_match_table = nau7802_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(nau7802_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Nuvoton NAU7802 ADC Driver");
|
||||
MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
|
||||
MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>");
|
||||
316
drivers/iio/adc/rockchip_saradc.c
Normal file
316
drivers/iio/adc/rockchip_saradc.c
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
* Rockchip Successive Approximation Register (SAR) A/D Converter
|
||||
* Copyright (C) 2014 ROCKCHIP, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define SARADC_DATA 0x00
|
||||
#define SARADC_DATA_MASK 0x3ff
|
||||
|
||||
#define SARADC_STAS 0x04
|
||||
#define SARADC_STAS_BUSY BIT(0)
|
||||
|
||||
#define SARADC_CTRL 0x08
|
||||
#define SARADC_CTRL_IRQ_STATUS BIT(6)
|
||||
#define SARADC_CTRL_IRQ_ENABLE BIT(5)
|
||||
#define SARADC_CTRL_POWER_CTRL BIT(3)
|
||||
#define SARADC_CTRL_CHN_MASK 0x7
|
||||
|
||||
#define SARADC_DLY_PU_SOC 0x0c
|
||||
#define SARADC_DLY_PU_SOC_MASK 0x3f
|
||||
|
||||
#define SARADC_BITS 10
|
||||
#define SARADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
struct rockchip_saradc {
|
||||
void __iomem *regs;
|
||||
struct clk *pclk;
|
||||
struct clk *clk;
|
||||
struct completion completion;
|
||||
struct regulator *vref;
|
||||
u16 last_val;
|
||||
};
|
||||
|
||||
static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
/* 8 clock periods as delay between power up and start cmd */
|
||||
writel_relaxed(8, info->regs + SARADC_DLY_PU_SOC);
|
||||
|
||||
/* Select the channel to be used and trigger conversion */
|
||||
writel(SARADC_CTRL_POWER_CTRL
|
||||
| (chan->channel & SARADC_CTRL_CHN_MASK)
|
||||
| SARADC_CTRL_IRQ_ENABLE,
|
||||
info->regs + SARADC_CTRL);
|
||||
|
||||
if (!wait_for_completion_timeout(&info->completion,
|
||||
SARADC_TIMEOUT)) {
|
||||
writel_relaxed(0, info->regs + SARADC_CTRL);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
*val = info->last_val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ret = regulator_get_voltage(info->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to get voltage\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = SARADC_BITS;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct rockchip_saradc *info = (struct rockchip_saradc *)dev_id;
|
||||
|
||||
/* Read value */
|
||||
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
|
||||
info->last_val &= SARADC_DATA_MASK;
|
||||
|
||||
/* Clear irq & power down adc */
|
||||
writel_relaxed(0, info->regs + SARADC_CTRL);
|
||||
|
||||
complete(&info->completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info rockchip_saradc_iio_info = {
|
||||
.read_raw = rockchip_saradc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define ADC_CHANNEL(_index, _id) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.datasheet_name = _id, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
|
||||
ADC_CHANNEL(0, "adc0"),
|
||||
ADC_CHANNEL(1, "adc1"),
|
||||
ADC_CHANNEL(2, "adc2"),
|
||||
};
|
||||
|
||||
static int rockchip_saradc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rockchip_saradc *info = NULL;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct iio_dev *indio_dev = NULL;
|
||||
struct resource *mem;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
info = iio_priv(indio_dev);
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return irq;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
|
||||
0, dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->pclk = devm_clk_get(&pdev->dev, "apb_pclk");
|
||||
if (IS_ERR(info->pclk)) {
|
||||
dev_err(&pdev->dev, "failed to get pclk\n");
|
||||
return PTR_ERR(info->pclk);
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "saradc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get adc clock\n");
|
||||
return PTR_ERR(info->clk);
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref)) {
|
||||
dev_err(&pdev->dev, "failed to get regulator, %ld\n",
|
||||
PTR_ERR(info->vref));
|
||||
return PTR_ERR(info->vref);
|
||||
}
|
||||
|
||||
/*
|
||||
* Use a default of 1MHz for the converter clock.
|
||||
* This may become user-configurable in the future.
|
||||
*/
|
||||
ret = clk_set_rate(info->clk, 1000000);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable vref regulator\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(info->pclk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable pclk\n");
|
||||
goto err_reg_voltage;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to enable converter clock\n");
|
||||
goto err_pclk;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &rockchip_saradc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
indio_dev->channels = rockchip_saradc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_clk;
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
clk_disable_unprepare(info->clk);
|
||||
err_pclk:
|
||||
clk_disable_unprepare(info->pclk);
|
||||
err_reg_voltage:
|
||||
regulator_disable(info->vref);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rockchip_saradc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
clk_disable_unprepare(info->clk);
|
||||
clk_disable_unprepare(info->pclk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int rockchip_saradc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
clk_disable_unprepare(info->pclk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rockchip_saradc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct rockchip_saradc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->pclk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(rockchip_saradc_pm_ops,
|
||||
rockchip_saradc_suspend, rockchip_saradc_resume);
|
||||
|
||||
static const struct of_device_id rockchip_saradc_match[] = {
|
||||
{ .compatible = "rockchip,saradc" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
|
||||
|
||||
static struct platform_driver rockchip_saradc_driver = {
|
||||
.probe = rockchip_saradc_probe,
|
||||
.remove = rockchip_saradc_remove,
|
||||
.driver = {
|
||||
.name = "rockchip-saradc",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = rockchip_saradc_match,
|
||||
.pm = &rockchip_saradc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rockchip_saradc_driver);
|
||||
154
drivers/iio/adc/ti-adc081c.c
Normal file
154
drivers/iio/adc/ti-adc081c.c
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Avionic Design GmbH
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc081c {
|
||||
struct i2c_client *i2c;
|
||||
struct regulator *ref;
|
||||
};
|
||||
|
||||
#define REG_CONV_RES 0x00
|
||||
|
||||
static int adc081c_read_raw(struct iio_dev *iio,
|
||||
struct iio_chan_spec const *channel, int *value,
|
||||
int *shift, long mask)
|
||||
{
|
||||
struct adc081c *adc = iio_priv(iio);
|
||||
int err;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
err = i2c_smbus_read_word_swapped(adc->i2c, REG_CONV_RES);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*value = (err >> 4) & 0xff;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
err = regulator_get_voltage(adc->ref);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
*value = err / 1000;
|
||||
*shift = 8;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc081c_channel = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
};
|
||||
|
||||
static const struct iio_info adc081c_info = {
|
||||
.read_raw = adc081c_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adc081c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct iio_dev *iio;
|
||||
struct adc081c *adc;
|
||||
int err;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
|
||||
return -ENODEV;
|
||||
|
||||
iio = devm_iio_device_alloc(&client->dev, sizeof(*adc));
|
||||
if (!iio)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(iio);
|
||||
adc->i2c = client;
|
||||
|
||||
adc->ref = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(adc->ref))
|
||||
return PTR_ERR(adc->ref);
|
||||
|
||||
err = regulator_enable(adc->ref);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
iio->dev.parent = &client->dev;
|
||||
iio->name = dev_name(&client->dev);
|
||||
iio->modes = INDIO_DIRECT_MODE;
|
||||
iio->info = &adc081c_info;
|
||||
|
||||
iio->channels = &adc081c_channel;
|
||||
iio->num_channels = 1;
|
||||
|
||||
err = iio_device_register(iio);
|
||||
if (err < 0)
|
||||
goto regulator_disable;
|
||||
|
||||
i2c_set_clientdata(client, iio);
|
||||
|
||||
return 0;
|
||||
|
||||
regulator_disable:
|
||||
regulator_disable(adc->ref);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int adc081c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *iio = i2c_get_clientdata(client);
|
||||
struct adc081c *adc = iio_priv(iio);
|
||||
|
||||
iio_device_unregister(iio);
|
||||
regulator_disable(adc->ref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id adc081c_id[] = {
|
||||
{ "adc081c", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, adc081c_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id adc081c_of_match[] = {
|
||||
{ .compatible = "ti,adc081c" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, adc081c_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver adc081c_driver = {
|
||||
.driver = {
|
||||
.name = "adc081c",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(adc081c_of_match),
|
||||
},
|
||||
.probe = adc081c_probe,
|
||||
.remove = adc081c_remove,
|
||||
.id_table = adc081c_id,
|
||||
};
|
||||
module_i2c_driver(adc081c_driver);
|
||||
|
||||
MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
179
drivers/iio/adc/ti-adc128s052.c
Normal file
179
drivers/iio/adc/ti-adc128s052.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
|
||||
*
|
||||
* Driver for Texas Instruments' ADC128S052 ADC chip.
|
||||
* Datasheet can be found here:
|
||||
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
struct adc128 {
|
||||
struct spi_device *spi;
|
||||
|
||||
struct regulator *reg;
|
||||
struct mutex lock;
|
||||
|
||||
u8 buffer[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adc128_adc_conversion(struct adc128 *adc, u8 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&adc->lock);
|
||||
|
||||
adc->buffer[0] = channel << 3;
|
||||
adc->buffer[1] = 0;
|
||||
|
||||
ret = spi_write(adc->spi, &adc->buffer, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&adc->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = spi_read(adc->spi, &adc->buffer, 2);
|
||||
|
||||
mutex_unlock(&adc->lock);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ((adc->buffer[0] << 8 | adc->buffer[1]) & 0xFFF);
|
||||
}
|
||||
|
||||
static int adc128_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *channel, int *val,
|
||||
int *val2, long mask)
|
||||
{
|
||||
struct adc128 *adc = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
|
||||
ret = adc128_adc_conversion(adc, channel->channel);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
|
||||
ret = regulator_get_voltage(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret / 1000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#define ADC128_VOLTAGE_CHANNEL(num) \
|
||||
{ \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = (num), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adc128_channels[] = {
|
||||
ADC128_VOLTAGE_CHANNEL(0),
|
||||
ADC128_VOLTAGE_CHANNEL(1),
|
||||
ADC128_VOLTAGE_CHANNEL(2),
|
||||
ADC128_VOLTAGE_CHANNEL(3),
|
||||
ADC128_VOLTAGE_CHANNEL(4),
|
||||
ADC128_VOLTAGE_CHANNEL(5),
|
||||
ADC128_VOLTAGE_CHANNEL(6),
|
||||
ADC128_VOLTAGE_CHANNEL(7),
|
||||
};
|
||||
|
||||
static const struct iio_info adc128_info = {
|
||||
.read_raw = adc128_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adc128_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adc128 *adc;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->spi = spi;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &adc128_info;
|
||||
|
||||
indio_dev->channels = adc128_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adc128_channels);
|
||||
|
||||
adc->reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(adc->reg))
|
||||
return PTR_ERR(adc->reg);
|
||||
|
||||
ret = regulator_enable(adc->reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
mutex_init(&adc->lock);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adc128_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adc128 *adc = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(adc->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adc128_id[] = {
|
||||
{ "adc128s052", 0},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adc128_id);
|
||||
|
||||
static struct spi_driver adc128_driver = {
|
||||
.driver = {
|
||||
.name = "adc128s052",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adc128_probe,
|
||||
.remove = adc128_remove,
|
||||
.id_table = adc128_id,
|
||||
};
|
||||
module_spi_driver(adc128_driver);
|
||||
|
||||
MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>");
|
||||
MODULE_DESCRIPTION("Texas Instruments ADC128S052");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
558
drivers/iio/adc/ti_am335x_adc.c
Normal file
558
drivers/iio/adc/ti_am335x_adc.c
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
/*
|
||||
* TI ADC MFD driver
|
||||
*
|
||||
* Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/iio/machine.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
#include <linux/mfd/ti_am335x_tscadc.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/kfifo_buf.h>
|
||||
|
||||
struct tiadc_device {
|
||||
struct ti_tscadc_dev *mfd_tscadc;
|
||||
int channels;
|
||||
u8 channel_line[8];
|
||||
u8 channel_step[8];
|
||||
int buffer_en_ch_steps;
|
||||
u16 data[8];
|
||||
};
|
||||
|
||||
static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg)
|
||||
{
|
||||
return readl(adc->mfd_tscadc->tscadc_base + reg);
|
||||
}
|
||||
|
||||
static void tiadc_writel(struct tiadc_device *adc, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
writel(val, adc->mfd_tscadc->tscadc_base + reg);
|
||||
}
|
||||
|
||||
static u32 get_adc_step_mask(struct tiadc_device *adc_dev)
|
||||
{
|
||||
u32 step_en;
|
||||
|
||||
step_en = ((1 << adc_dev->channels) - 1);
|
||||
step_en <<= TOTAL_STEPS - adc_dev->channels + 1;
|
||||
return step_en;
|
||||
}
|
||||
|
||||
static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) {
|
||||
if (chan->channel == adc_dev->channel_line[i]) {
|
||||
u32 step;
|
||||
|
||||
step = adc_dev->channel_step[i];
|
||||
/* +1 for the charger */
|
||||
return 1 << (step + 1);
|
||||
}
|
||||
}
|
||||
WARN_ON(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan)
|
||||
{
|
||||
return 1 << adc_dev->channel_step[chan];
|
||||
}
|
||||
|
||||
static void tiadc_step_config(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int stepconfig;
|
||||
int i, steps;
|
||||
|
||||
/*
|
||||
* There are 16 configurable steps and 8 analog input
|
||||
* lines available which are shared between Touchscreen and ADC.
|
||||
*
|
||||
* Steps backwards i.e. from 16 towards 0 are used by ADC
|
||||
* depending on number of input lines needed.
|
||||
* Channel would represent which analog input
|
||||
* needs to be given to ADC to digitalize data.
|
||||
*/
|
||||
|
||||
steps = TOTAL_STEPS - adc_dev->channels;
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1
|
||||
| STEPCONFIG_MODE_SWCNT;
|
||||
else
|
||||
stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;
|
||||
|
||||
for (i = 0; i < adc_dev->channels; i++) {
|
||||
int chan;
|
||||
|
||||
chan = adc_dev->channel_line[i];
|
||||
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
|
||||
stepconfig | STEPCONFIG_INP(chan));
|
||||
tiadc_writel(adc_dev, REG_STEPDELAY(steps),
|
||||
STEPCONFIG_OPENDLY);
|
||||
adc_dev->channel_step[i] = steps;
|
||||
steps++;
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_irq_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int status, config;
|
||||
status = tiadc_readl(adc_dev, REG_IRQSTATUS);
|
||||
|
||||
/*
|
||||
* ADC and touchscreen share the IRQ line.
|
||||
* FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only
|
||||
*/
|
||||
if (status & IRQENB_FIFO1OVRRUN) {
|
||||
/* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */
|
||||
config = tiadc_readl(adc_dev, REG_CTRL);
|
||||
config &= ~(CNTRLREG_TSCSSENB);
|
||||
tiadc_writel(adc_dev, REG_CTRL, config);
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN
|
||||
| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB));
|
||||
return IRQ_HANDLED;
|
||||
} else if (status & IRQENB_FIFO1THRES) {
|
||||
/* Disable irq and wake worker thread */
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t tiadc_worker_h(int irq, void *private)
|
||||
{
|
||||
struct iio_dev *indio_dev = private;
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, k, fifo1count, read;
|
||||
u16 *data = adc_dev->data;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (k = 0; k < fifo1count; k = k + i) {
|
||||
for (i = 0; i < (indio_dev->scan_bytes)/2; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
data[i] = read & FIFOREAD_DATA_MASK;
|
||||
}
|
||||
iio_push_to_buffers(indio_dev, (u8 *) data);
|
||||
}
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, fifo1count, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN |
|
||||
IRQENB_FIFO1UNDRFLW));
|
||||
|
||||
/* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_buffer *buffer = indio_dev->buffer;
|
||||
unsigned int enb = 0;
|
||||
u8 bit;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels)
|
||||
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
|
||||
adc_dev->buffer_en_ch_steps = enb;
|
||||
|
||||
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
|
||||
tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
|
||||
| IRQENB_FIFO1OVRRUN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int fifo1count, i, read;
|
||||
|
||||
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
|
||||
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
|
||||
adc_dev->buffer_en_ch_steps = 0;
|
||||
|
||||
/* Flush FIFO of leftover data in the time it takes to disable adc */
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
for (i = 0; i < fifo1count; i++)
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_buffer_postdisable(struct iio_dev *indio_dev)
|
||||
{
|
||||
tiadc_step_config(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = {
|
||||
.preenable = &tiadc_buffer_preenable,
|
||||
.postenable = &tiadc_buffer_postenable,
|
||||
.predisable = &tiadc_buffer_predisable,
|
||||
.postdisable = &tiadc_buffer_postdisable,
|
||||
};
|
||||
|
||||
static int tiadc_iio_buffered_hardware_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(indio_dev);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
|
||||
iio_device_attach_buffer(indio_dev, buffer);
|
||||
|
||||
ret = request_threaded_irq(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_HARDWARE;
|
||||
|
||||
ret = iio_buffer_register(indio_dev,
|
||||
indio_dev->channels,
|
||||
indio_dev->num_channels);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(irq, indio_dev);
|
||||
error_kfifo_free:
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
|
||||
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
|
||||
iio_kfifo_free(indio_dev->buffer);
|
||||
iio_buffer_unregister(indio_dev);
|
||||
}
|
||||
|
||||
|
||||
static const char * const chan_name_ain[] = {
|
||||
"AIN0",
|
||||
"AIN1",
|
||||
"AIN2",
|
||||
"AIN3",
|
||||
"AIN4",
|
||||
"AIN5",
|
||||
"AIN6",
|
||||
"AIN7",
|
||||
};
|
||||
|
||||
static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *chan_array;
|
||||
struct iio_chan_spec *chan;
|
||||
int i;
|
||||
|
||||
indio_dev->num_channels = channels;
|
||||
chan_array = kcalloc(channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
if (chan_array == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
chan = chan_array;
|
||||
for (i = 0; i < channels; i++, chan++) {
|
||||
|
||||
chan->type = IIO_VOLTAGE;
|
||||
chan->indexed = 1;
|
||||
chan->channel = adc_dev->channel_line[i];
|
||||
chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
|
||||
chan->datasheet_name = chan_name_ain[chan->channel];
|
||||
chan->scan_index = i;
|
||||
chan->scan_type.sign = 'u';
|
||||
chan->scan_type.realbits = 12;
|
||||
chan->scan_type.storagebits = 16;
|
||||
}
|
||||
|
||||
indio_dev->channels = chan_array;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tiadc_channels_remove(struct iio_dev *indio_dev)
|
||||
{
|
||||
kfree(indio_dev->channels);
|
||||
}
|
||||
|
||||
static int tiadc_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
int i, map_val;
|
||||
unsigned int fifo1count, read, stepid;
|
||||
bool found = false;
|
||||
u32 step_en;
|
||||
unsigned long timeout;
|
||||
|
||||
if (iio_buffer_enabled(indio_dev))
|
||||
return -EBUSY;
|
||||
|
||||
step_en = get_adc_chan_step_mask(adc_dev, chan);
|
||||
if (!step_en)
|
||||
return -EINVAL;
|
||||
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
while (fifo1count--)
|
||||
tiadc_readl(adc_dev, REG_FIFO1);
|
||||
|
||||
am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en);
|
||||
|
||||
timeout = jiffies + usecs_to_jiffies
|
||||
(IDLE_TIMEOUT * adc_dev->channels);
|
||||
/* Wait for Fifo threshold interrupt */
|
||||
while (1) {
|
||||
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
|
||||
if (fifo1count)
|
||||
break;
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
|
||||
return -EAGAIN;
|
||||
}
|
||||
}
|
||||
map_val = adc_dev->channel_step[chan->scan_index];
|
||||
|
||||
/*
|
||||
* We check the complete FIFO. We programmed just one entry but in case
|
||||
* something went wrong we left empty handed (-EAGAIN previously) and
|
||||
* then the value apeared somehow in the FIFO we would have two entries.
|
||||
* Therefore we read every item and keep only the latest version of the
|
||||
* requested channel.
|
||||
*/
|
||||
for (i = 0; i < fifo1count; i++) {
|
||||
read = tiadc_readl(adc_dev, REG_FIFO1);
|
||||
stepid = read & FIFOREAD_CHNLID_MASK;
|
||||
stepid = stepid >> 0x10;
|
||||
|
||||
if (stepid == map_val) {
|
||||
read = read & FIFOREAD_DATA_MASK;
|
||||
found = true;
|
||||
*val = (u16) read;
|
||||
}
|
||||
}
|
||||
am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);
|
||||
|
||||
if (found == false)
|
||||
return -EBUSY;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info tiadc_info = {
|
||||
.read_raw = &tiadc_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int tiadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct tiadc_device *adc_dev;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct property *prop;
|
||||
const __be32 *cur;
|
||||
int err;
|
||||
u32 val;
|
||||
int channels = 0;
|
||||
|
||||
if (!node) {
|
||||
dev_err(&pdev->dev, "Could not find valid DT data.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev,
|
||||
sizeof(struct tiadc_device));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&pdev->dev, "failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
adc_dev = iio_priv(indio_dev);
|
||||
|
||||
adc_dev->mfd_tscadc = ti_tscadc_dev_get(pdev);
|
||||
|
||||
of_property_for_each_u32(node, "ti,adc-channels", prop, cur, val) {
|
||||
adc_dev->channel_line[channels] = val;
|
||||
channels++;
|
||||
}
|
||||
adc_dev->channels = channels;
|
||||
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &tiadc_info;
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);
|
||||
|
||||
err = tiadc_channel_init(indio_dev, adc_dev->channels);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tiadc_iio_buffered_hardware_setup(indio_dev,
|
||||
&tiadc_worker_h,
|
||||
&tiadc_irq_h,
|
||||
adc_dev->mfd_tscadc->irq,
|
||||
IRQF_SHARED,
|
||||
&tiadc_buffer_setup_ops);
|
||||
|
||||
if (err)
|
||||
goto err_free_channels;
|
||||
|
||||
err = iio_device_register(indio_dev);
|
||||
if (err)
|
||||
goto err_buffer_unregister;
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_buffer_unregister:
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
err_free_channels:
|
||||
tiadc_channels_remove(indio_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int tiadc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
u32 step_en;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
tiadc_iio_buffered_hardware_remove(indio_dev);
|
||||
tiadc_channels_remove(indio_dev);
|
||||
|
||||
step_en = get_adc_step_mask(adc_dev);
|
||||
am335x_tsc_se_clr(adc_dev->mfd_tscadc, step_en);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int tiadc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
struct ti_tscadc_dev *tscadc_dev;
|
||||
unsigned int idle;
|
||||
|
||||
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
||||
if (!device_may_wakeup(tscadc_dev->dev)) {
|
||||
idle = tiadc_readl(adc_dev, REG_CTRL);
|
||||
idle &= ~(CNTRLREG_TSCSSENB);
|
||||
tiadc_writel(adc_dev, REG_CTRL, (idle |
|
||||
CNTRLREG_POWERDOWN));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tiadc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct tiadc_device *adc_dev = iio_priv(indio_dev);
|
||||
unsigned int restore;
|
||||
|
||||
/* Make sure ADC is powered up */
|
||||
restore = tiadc_readl(adc_dev, REG_CTRL);
|
||||
restore &= ~(CNTRLREG_POWERDOWN);
|
||||
tiadc_writel(adc_dev, REG_CTRL, restore);
|
||||
|
||||
tiadc_step_config(indio_dev);
|
||||
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc,
|
||||
adc_dev->buffer_en_ch_steps);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tiadc_pm_ops = {
|
||||
.suspend = tiadc_suspend,
|
||||
.resume = tiadc_resume,
|
||||
};
|
||||
#define TIADC_PM_OPS (&tiadc_pm_ops)
|
||||
#else
|
||||
#define TIADC_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct of_device_id ti_adc_dt_ids[] = {
|
||||
{ .compatible = "ti,am3359-adc", },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
|
||||
|
||||
static struct platform_driver tiadc_driver = {
|
||||
.driver = {
|
||||
.name = "TI-am335x-adc",
|
||||
.pm = TIADC_PM_OPS,
|
||||
.of_match_table = ti_adc_dt_ids,
|
||||
},
|
||||
.probe = tiadc_probe,
|
||||
.remove = tiadc_remove,
|
||||
};
|
||||
module_platform_driver(tiadc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TI ADC controller driver");
|
||||
MODULE_AUTHOR("Rachna Patil <rachna@ti.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
895
drivers/iio/adc/twl4030-madc.c
Normal file
895
drivers/iio/adc/twl4030-madc.c
Normal file
|
|
@ -0,0 +1,895 @@
|
|||
/*
|
||||
*
|
||||
* TWL4030 MADC module driver-This driver monitors the real time
|
||||
* conversion of analog signals like battery temperature,
|
||||
* battery type, battery level etc.
|
||||
*
|
||||
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
|
||||
* J Keerthy <j-keerthy@ti.com>
|
||||
*
|
||||
* Based on twl4030-madc.c
|
||||
* Copyright (C) 2008 Nokia Corporation
|
||||
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
|
||||
*
|
||||
* Amit Kucheria <amit.kucheria@canonical.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c/twl.h>
|
||||
#include <linux/i2c/twl4030-madc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
/**
|
||||
* struct twl4030_madc_data - a container for madc info
|
||||
* @dev: Pointer to device structure for madc
|
||||
* @lock: Mutex protecting this data structure
|
||||
* @requests: Array of request struct corresponding to SW1, SW2 and RT
|
||||
* @use_second_irq: IRQ selection (main or co-processor)
|
||||
* @imr: Interrupt mask register of MADC
|
||||
* @isr: Interrupt status register of MADC
|
||||
*/
|
||||
struct twl4030_madc_data {
|
||||
struct device *dev;
|
||||
struct mutex lock; /* mutex protecting this data structure */
|
||||
struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS];
|
||||
bool use_second_irq;
|
||||
u8 imr;
|
||||
u8 isr;
|
||||
};
|
||||
|
||||
static int twl4030_madc_read(struct iio_dev *iio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct twl4030_madc_data *madc = iio_priv(iio_dev);
|
||||
struct twl4030_madc_request req;
|
||||
int ret;
|
||||
|
||||
req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1;
|
||||
|
||||
req.channels = BIT(chan->channel);
|
||||
req.active = false;
|
||||
req.func_cb = NULL;
|
||||
req.type = TWL4030_MADC_WAIT;
|
||||
req.raw = !(mask == IIO_CHAN_INFO_PROCESSED);
|
||||
req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW);
|
||||
|
||||
ret = twl4030_madc_conversion(&req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = req.rbuf[chan->channel];
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info twl4030_madc_iio_info = {
|
||||
.read_raw = &twl4030_madc_read,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
|
||||
.type = _type, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_PROCESSED), \
|
||||
.datasheet_name = _name, \
|
||||
.indexed = 1, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec twl4030_madc_iio_channels[] = {
|
||||
TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"),
|
||||
TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"),
|
||||
TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"),
|
||||
TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"),
|
||||
TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"),
|
||||
TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"),
|
||||
TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"),
|
||||
TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"),
|
||||
TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"),
|
||||
TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"),
|
||||
TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"),
|
||||
TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"),
|
||||
TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"),
|
||||
TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"),
|
||||
TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"),
|
||||
TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"),
|
||||
};
|
||||
|
||||
static struct twl4030_madc_data *twl4030_madc;
|
||||
|
||||
struct twl4030_prescale_divider_ratios {
|
||||
s16 numerator;
|
||||
s16 denominator;
|
||||
};
|
||||
|
||||
static const struct twl4030_prescale_divider_ratios
|
||||
twl4030_divider_ratios[16] = {
|
||||
{1, 1}, /* CHANNEL 0 No Prescaler */
|
||||
{1, 1}, /* CHANNEL 1 No Prescaler */
|
||||
{6, 10}, /* CHANNEL 2 */
|
||||
{6, 10}, /* CHANNEL 3 */
|
||||
{6, 10}, /* CHANNEL 4 */
|
||||
{6, 10}, /* CHANNEL 5 */
|
||||
{6, 10}, /* CHANNEL 6 */
|
||||
{6, 10}, /* CHANNEL 7 */
|
||||
{3, 14}, /* CHANNEL 8 */
|
||||
{1, 3}, /* CHANNEL 9 */
|
||||
{1, 1}, /* CHANNEL 10 No Prescaler */
|
||||
{15, 100}, /* CHANNEL 11 */
|
||||
{1, 4}, /* CHANNEL 12 */
|
||||
{1, 1}, /* CHANNEL 13 Reserved channels */
|
||||
{1, 1}, /* CHANNEL 14 Reseved channels */
|
||||
{5, 11}, /* CHANNEL 15 */
|
||||
};
|
||||
|
||||
|
||||
/* Conversion table from -3 to 55 degrees Celcius */
|
||||
static int twl4030_therm_tbl[] = {
|
||||
30800, 29500, 28300, 27100,
|
||||
26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700,
|
||||
17900, 17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100,
|
||||
12600, 12100, 11600, 11200, 10800, 10400, 10000, 9630, 9280,
|
||||
8950, 8620, 8310, 8020, 7730, 7460, 7200, 6950, 6710,
|
||||
6470, 6250, 6040, 5830, 5640, 5450, 5260, 5090, 4920,
|
||||
4760, 4600, 4450, 4310, 4170, 4040, 3910, 3790, 3670,
|
||||
3550
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure containing the registers
|
||||
* of different conversion methods supported by MADC.
|
||||
* Hardware or RT real time conversion request initiated by external host
|
||||
* processor for RT Signal conversions.
|
||||
* External host processors can also request for non RT conversions
|
||||
* SW1 and SW2 software conversions also called asynchronous or GPC request.
|
||||
*/
|
||||
static
|
||||
const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
|
||||
[TWL4030_MADC_RT] = {
|
||||
.sel = TWL4030_MADC_RTSELECT_LSB,
|
||||
.avg = TWL4030_MADC_RTAVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_RTCH0_LSB,
|
||||
},
|
||||
[TWL4030_MADC_SW1] = {
|
||||
.sel = TWL4030_MADC_SW1SELECT_LSB,
|
||||
.avg = TWL4030_MADC_SW1AVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_GPCH0_LSB,
|
||||
.ctrl = TWL4030_MADC_CTRL_SW1,
|
||||
},
|
||||
[TWL4030_MADC_SW2] = {
|
||||
.sel = TWL4030_MADC_SW2SELECT_LSB,
|
||||
.avg = TWL4030_MADC_SW2AVERAGE_LSB,
|
||||
.rbase = TWL4030_MADC_GPCH0_LSB,
|
||||
.ctrl = TWL4030_MADC_CTRL_SW2,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* twl4030_madc_channel_raw_read() - Function to read a particular channel value
|
||||
* @madc: pointer to struct twl4030_madc_data
|
||||
* @reg: lsb of ADC Channel
|
||||
*
|
||||
* Return: 0 on success, an error code otherwise.
|
||||
*/
|
||||
static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
|
||||
{
|
||||
u16 val;
|
||||
int ret;
|
||||
/*
|
||||
* For each ADC channel, we have MSB and LSB register pair. MSB address
|
||||
* is always LSB address+1. reg parameter is the address of LSB register
|
||||
*/
|
||||
ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read register 0x%X\n", reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return (int)(val >> 6);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return battery temperature in degrees Celsius
|
||||
* Or < 0 on failure.
|
||||
*/
|
||||
static int twl4030battery_temperature(int raw_volt)
|
||||
{
|
||||
u8 val;
|
||||
int temp, curr, volt, res, ret;
|
||||
|
||||
volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R;
|
||||
/* Getting and calculating the supply current in micro amperes */
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
|
||||
REG_BCICTL2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10;
|
||||
/* Getting and calculating the thermistor resistance in ohms */
|
||||
res = volt * 1000 / curr;
|
||||
/* calculating temperature */
|
||||
for (temp = 58; temp >= 0; temp--) {
|
||||
int actual = twl4030_therm_tbl[temp];
|
||||
if ((actual - res) >= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return temp + 1;
|
||||
}
|
||||
|
||||
static int twl4030battery_current(int raw_volt)
|
||||
{
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val,
|
||||
TWL4030_BCI_BCICTL1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */
|
||||
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1;
|
||||
else /* slope of 0.88 mV/mA */
|
||||
return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function to read channel values
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @reg_base - Base address of the first channel
|
||||
* @Channels - 16 bit bitmap. If the bit is set, channel's value is read
|
||||
* @buf - The channel values are stored here. if read fails error
|
||||
* @raw - Return raw values without conversion
|
||||
* value is stored
|
||||
* Returns the number of successfully read channels.
|
||||
*/
|
||||
static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
|
||||
u8 reg_base, unsigned
|
||||
long channels, int *buf,
|
||||
bool raw)
|
||||
{
|
||||
int count = 0;
|
||||
int i;
|
||||
u8 reg;
|
||||
|
||||
for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) {
|
||||
reg = reg_base + (2 * i);
|
||||
buf[i] = twl4030_madc_channel_raw_read(madc, reg);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "Unable to read register 0x%X\n",
|
||||
reg);
|
||||
return buf[i];
|
||||
}
|
||||
if (raw) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
switch (i) {
|
||||
case 10:
|
||||
buf[i] = twl4030battery_current(buf[i]);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "err reading current\n");
|
||||
return buf[i];
|
||||
} else {
|
||||
count++;
|
||||
buf[i] = buf[i] - 750;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
buf[i] = twl4030battery_temperature(buf[i]);
|
||||
if (buf[i] < 0) {
|
||||
dev_err(madc->dev, "err reading temperature\n");
|
||||
return buf[i];
|
||||
} else {
|
||||
buf[i] -= 3;
|
||||
count++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
count++;
|
||||
/* Analog Input (V) = conv_result * step_size / R
|
||||
* conv_result = decimal value of 10-bit conversion
|
||||
* result
|
||||
* step size = 1.5 / (2 ^ 10 -1)
|
||||
* R = Prescaler ratio for input channels.
|
||||
* Result given in mV hence multiplied by 1000.
|
||||
*/
|
||||
buf[i] = (buf[i] * 3 * 1000 *
|
||||
twl4030_divider_ratios[i].denominator)
|
||||
/ (2 * 1023 *
|
||||
twl4030_divider_ratios[i].numerator);
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enables irq.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @id - irq number to be enabled
|
||||
* can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
|
||||
* corresponding to RT, SW1, SW2 conversion requests.
|
||||
* If the i2c read fails it returns an error else returns 0.
|
||||
*/
|
||||
static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
val &= ~(1 << id);
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to write imr register 0x%X\n", madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disables irq.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @id - irq number to be disabled
|
||||
* can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2
|
||||
* corresponding to RT, SW1, SW2 conversion requests.
|
||||
* Returns error if i2c read/write fails.
|
||||
*/
|
||||
static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
return ret;
|
||||
}
|
||||
val |= (1 << id);
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to write imr register 0x%X\n", madc->imr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc)
|
||||
{
|
||||
struct twl4030_madc_data *madc = _madc;
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
u8 isr_val, imr_val;
|
||||
int i, len, ret;
|
||||
struct twl4030_madc_request *r;
|
||||
|
||||
mutex_lock(&madc->lock);
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read isr register 0x%X\n",
|
||||
madc->isr);
|
||||
goto err_i2c;
|
||||
}
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read imr register 0x%X\n",
|
||||
madc->imr);
|
||||
goto err_i2c;
|
||||
}
|
||||
isr_val &= ~imr_val;
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
if (!(isr_val & (1 << i)))
|
||||
continue;
|
||||
ret = twl4030_madc_disable_irq(madc, i);
|
||||
if (ret < 0)
|
||||
dev_dbg(madc->dev, "Disable interrupt failed %d\n", i);
|
||||
madc->requests[i].result_pending = 1;
|
||||
}
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
/* No pending results for this method, move to next one */
|
||||
if (!r->result_pending)
|
||||
continue;
|
||||
method = &twl4030_conversion_methods[r->method];
|
||||
/* Read results */
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Return results to caller */
|
||||
if (r->func_cb != NULL) {
|
||||
r->func_cb(len, r->channels, r->rbuf);
|
||||
r->func_cb = NULL;
|
||||
}
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
||||
err_i2c:
|
||||
/*
|
||||
* In case of error check whichever request is active
|
||||
* and service the same.
|
||||
*/
|
||||
for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
|
||||
r = &madc->requests[i];
|
||||
if (r->active == 0)
|
||||
continue;
|
||||
method = &twl4030_conversion_methods[r->method];
|
||||
/* Read results */
|
||||
len = twl4030_madc_read_channels(madc, method->rbase,
|
||||
r->channels, r->rbuf, r->raw);
|
||||
/* Return results to caller */
|
||||
if (r->func_cb != NULL) {
|
||||
r->func_cb(len, r->channels, r->rbuf);
|
||||
r->func_cb = NULL;
|
||||
}
|
||||
/* Free request */
|
||||
r->result_pending = 0;
|
||||
r->active = 0;
|
||||
}
|
||||
mutex_unlock(&madc->lock);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
|
||||
struct twl4030_madc_request *req)
|
||||
{
|
||||
struct twl4030_madc_request *p;
|
||||
int ret;
|
||||
|
||||
p = &madc->requests[req->method];
|
||||
memcpy(p, req, sizeof(*req));
|
||||
ret = twl4030_madc_enable_irq(madc, req->method);
|
||||
if (ret < 0) {
|
||||
dev_err(madc->dev, "enable irq failed!!\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function which enables the madc conversion
|
||||
* by writing to the control register.
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1
|
||||
* corresponding to RT SW1 or SW2 conversion methods.
|
||||
* Returns 0 if succeeds else a negative error value
|
||||
*/
|
||||
static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
|
||||
int conv_method)
|
||||
{
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
int ret = 0;
|
||||
|
||||
if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2)
|
||||
return -ENOTSUPP;
|
||||
|
||||
method = &twl4030_conversion_methods[conv_method];
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START,
|
||||
method->ctrl);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write ctrl register 0x%X\n",
|
||||
method->ctrl);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function that waits for conversion to be ready
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @timeout_ms - timeout value in milliseconds
|
||||
* @status_reg - ctrl register
|
||||
* returns 0 if succeeds else a negative error value
|
||||
*/
|
||||
static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc,
|
||||
unsigned int timeout_ms,
|
||||
u8 status_reg)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int ret;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
do {
|
||||
u8 reg;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg);
|
||||
if (ret) {
|
||||
dev_err(madc->dev,
|
||||
"unable to read status register 0x%X\n",
|
||||
status_reg);
|
||||
return ret;
|
||||
}
|
||||
if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
|
||||
return 0;
|
||||
usleep_range(500, 2000);
|
||||
} while (!time_after(jiffies, timeout));
|
||||
dev_err(madc->dev, "conversion timeout!\n");
|
||||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* An exported function which can be called from other kernel drivers.
|
||||
* @req twl4030_madc_request structure
|
||||
* req->rbuf will be filled with read values of channels based on the
|
||||
* channel index. If a particular channel reading fails there will
|
||||
* be a negative error value in the corresponding array element.
|
||||
* returns 0 if succeeds else error value
|
||||
*/
|
||||
int twl4030_madc_conversion(struct twl4030_madc_request *req)
|
||||
{
|
||||
const struct twl4030_madc_conversion_method *method;
|
||||
int ret;
|
||||
|
||||
if (!req || !twl4030_madc)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&twl4030_madc->lock);
|
||||
if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Do we have a conversion request ongoing */
|
||||
if (twl4030_madc->requests[req->method].active) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
method = &twl4030_conversion_methods[req->method];
|
||||
/* Select channels to be converted */
|
||||
ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel);
|
||||
if (ret) {
|
||||
dev_err(twl4030_madc->dev,
|
||||
"unable to write sel register 0x%X\n", method->sel);
|
||||
goto out;
|
||||
}
|
||||
/* Select averaging for all channels if do_avg is set */
|
||||
if (req->do_avg) {
|
||||
ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels,
|
||||
method->avg);
|
||||
if (ret) {
|
||||
dev_err(twl4030_madc->dev,
|
||||
"unable to write avg register 0x%X\n",
|
||||
method->avg);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) {
|
||||
ret = twl4030_madc_set_irq(twl4030_madc, req);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
twl4030_madc->requests[req->method].active = 1;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
/* With RT method we should not be here anymore */
|
||||
if (req->method == TWL4030_MADC_RT) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = twl4030_madc_start_conversion(twl4030_madc, req->method);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
twl4030_madc->requests[req->method].active = 1;
|
||||
/* Wait until conversion is ready (ctrl register returns EOC) */
|
||||
ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl);
|
||||
if (ret) {
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
goto out;
|
||||
}
|
||||
ret = twl4030_madc_read_channels(twl4030_madc, method->rbase,
|
||||
req->channels, req->rbuf, req->raw);
|
||||
twl4030_madc->requests[req->method].active = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&twl4030_madc->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(twl4030_madc_conversion);
|
||||
|
||||
int twl4030_get_madc_conversion(int channel_no)
|
||||
{
|
||||
struct twl4030_madc_request req;
|
||||
int temp = 0;
|
||||
int ret;
|
||||
|
||||
req.channels = (1 << channel_no);
|
||||
req.method = TWL4030_MADC_SW2;
|
||||
req.active = 0;
|
||||
req.raw = 0;
|
||||
req.func_cb = NULL;
|
||||
ret = twl4030_madc_conversion(&req);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (req.rbuf[channel_no] > 0)
|
||||
temp = req.rbuf[channel_no];
|
||||
|
||||
return temp;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion);
|
||||
|
||||
/**
|
||||
* twl4030_madc_set_current_generator() - setup bias current
|
||||
*
|
||||
* @madc: pointer to twl4030_madc_data struct
|
||||
* @chan: can be one of the two values:
|
||||
* TWL4030_BCI_ITHEN
|
||||
* Enables bias current for main battery type reading
|
||||
* TWL4030_BCI_TYPEN
|
||||
* Enables bias current for main battery temperature sensing
|
||||
* @on: enable or disable chan.
|
||||
*
|
||||
* Function to enable or disable bias current for
|
||||
* main battery type reading or temperature sensing
|
||||
*/
|
||||
static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
|
||||
int chan, int on)
|
||||
{
|
||||
int ret;
|
||||
int regmask;
|
||||
u8 regval;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
|
||||
if (on)
|
||||
regval |= regmask;
|
||||
else
|
||||
regval &= ~regmask;
|
||||
|
||||
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
regval, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Function that sets MADC software power on bit to enable MADC
|
||||
* @madc - pointer to twl4030_madc_data struct
|
||||
* @on - Enable or disable MADC software power on bit.
|
||||
* returns error if i2c read/write fails else 0
|
||||
*/
|
||||
static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
|
||||
{
|
||||
u8 regval;
|
||||
int ret;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_MADC_CTRL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n",
|
||||
TWL4030_MADC_CTRL1);
|
||||
return ret;
|
||||
}
|
||||
if (on)
|
||||
regval |= TWL4030_MADC_MADCON;
|
||||
else
|
||||
regval &= ~TWL4030_MADC_MADCON;
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1);
|
||||
if (ret) {
|
||||
dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n",
|
||||
TWL4030_MADC_CTRL1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize MADC and request for threaded irq
|
||||
*/
|
||||
static int twl4030_madc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct twl4030_madc_data *madc;
|
||||
struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int irq, ret;
|
||||
u8 regval;
|
||||
struct iio_dev *iio_dev = NULL;
|
||||
|
||||
if (!pdata && !np) {
|
||||
dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc));
|
||||
if (!iio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
madc = iio_priv(iio_dev);
|
||||
madc->dev = &pdev->dev;
|
||||
|
||||
iio_dev->name = dev_name(&pdev->dev);
|
||||
iio_dev->dev.parent = &pdev->dev;
|
||||
iio_dev->dev.of_node = pdev->dev.of_node;
|
||||
iio_dev->info = &twl4030_madc_iio_info;
|
||||
iio_dev->modes = INDIO_DIRECT_MODE;
|
||||
iio_dev->channels = twl4030_madc_iio_channels;
|
||||
iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels);
|
||||
|
||||
/*
|
||||
* Phoenix provides 2 interrupt lines. The first one is connected to
|
||||
* the OMAP. The other one can be connected to the other processor such
|
||||
* as modem. Hence two separate ISR and IMR registers.
|
||||
*/
|
||||
if (pdata)
|
||||
madc->use_second_irq = (pdata->irq_line != 1);
|
||||
else
|
||||
madc->use_second_irq = of_property_read_bool(np,
|
||||
"ti,system-uses-second-madc-irq");
|
||||
|
||||
madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 :
|
||||
TWL4030_MADC_IMR1;
|
||||
madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 :
|
||||
TWL4030_MADC_ISR1;
|
||||
|
||||
ret = twl4030_madc_set_power(madc, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = twl4030_madc_set_current_generator(madc, 0, 1);
|
||||
if (ret < 0)
|
||||
goto err_current_generator;
|
||||
|
||||
ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
®val, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
goto err_i2c;
|
||||
}
|
||||
regval |= TWL4030_BCI_MESBAT;
|
||||
ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
|
||||
regval, TWL4030_BCI_BCICTL1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n",
|
||||
TWL4030_BCI_BCICTL1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* Check that MADC clock is on */
|
||||
ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
/* If MADC clk is not on, turn it on */
|
||||
if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) {
|
||||
dev_info(&pdev->dev, "clk disabled, enabling\n");
|
||||
regval |= TWL4030_GPBR1_MADC_HFCLK_EN;
|
||||
ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval,
|
||||
TWL4030_REG_GPBR1);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n",
|
||||
TWL4030_REG_GPBR1);
|
||||
goto err_i2c;
|
||||
}
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, iio_dev);
|
||||
mutex_init(&madc->lock);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
twl4030_madc_threaded_irq_handler,
|
||||
IRQF_TRIGGER_RISING, "twl4030_madc", madc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not request irq\n");
|
||||
goto err_i2c;
|
||||
}
|
||||
twl4030_madc = madc;
|
||||
|
||||
ret = iio_device_register(iio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register iio device\n");
|
||||
goto err_i2c;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_i2c:
|
||||
twl4030_madc_set_current_generator(madc, 0, 0);
|
||||
err_current_generator:
|
||||
twl4030_madc_set_power(madc, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int twl4030_madc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *iio_dev = platform_get_drvdata(pdev);
|
||||
struct twl4030_madc_data *madc = iio_priv(iio_dev);
|
||||
|
||||
iio_device_unregister(iio_dev);
|
||||
|
||||
twl4030_madc_set_current_generator(madc, 0, 0);
|
||||
twl4030_madc_set_power(madc, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id twl_madc_of_match[] = {
|
||||
{ .compatible = "ti,twl4030-madc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, twl_madc_of_match);
|
||||
#endif
|
||||
|
||||
static struct platform_driver twl4030_madc_driver = {
|
||||
.probe = twl4030_madc_probe,
|
||||
.remove = twl4030_madc_remove,
|
||||
.driver = {
|
||||
.name = "twl4030_madc",
|
||||
.of_match_table = of_match_ptr(twl_madc_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(twl4030_madc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("TWL4030 ADC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("J Keerthy");
|
||||
MODULE_ALIAS("platform:twl4030_madc");
|
||||
1009
drivers/iio/adc/twl6030-gpadc.c
Normal file
1009
drivers/iio/adc/twl6030-gpadc.c
Normal file
File diff suppressed because it is too large
Load diff
741
drivers/iio/adc/vf610_adc.c
Normal file
741
drivers/iio/adc/vf610_adc.c
Normal file
|
|
@ -0,0 +1,741 @@
|
|||
/*
|
||||
* Freescale Vybrid vf610 ADC driver
|
||||
*
|
||||
* Copyright 2013 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/driver.h>
|
||||
|
||||
/* This will be the driver name the kernel reports */
|
||||
#define DRIVER_NAME "vf610-adc"
|
||||
|
||||
/* Vybrid/IMX ADC registers */
|
||||
#define VF610_REG_ADC_HC0 0x00
|
||||
#define VF610_REG_ADC_HC1 0x04
|
||||
#define VF610_REG_ADC_HS 0x08
|
||||
#define VF610_REG_ADC_R0 0x0c
|
||||
#define VF610_REG_ADC_R1 0x10
|
||||
#define VF610_REG_ADC_CFG 0x14
|
||||
#define VF610_REG_ADC_GC 0x18
|
||||
#define VF610_REG_ADC_GS 0x1c
|
||||
#define VF610_REG_ADC_CV 0x20
|
||||
#define VF610_REG_ADC_OFS 0x24
|
||||
#define VF610_REG_ADC_CAL 0x28
|
||||
#define VF610_REG_ADC_PCTL 0x30
|
||||
|
||||
/* Configuration register field define */
|
||||
#define VF610_ADC_MODE_BIT8 0x00
|
||||
#define VF610_ADC_MODE_BIT10 0x04
|
||||
#define VF610_ADC_MODE_BIT12 0x08
|
||||
#define VF610_ADC_MODE_MASK 0x0c
|
||||
#define VF610_ADC_BUSCLK2_SEL 0x01
|
||||
#define VF610_ADC_ALTCLK_SEL 0x02
|
||||
#define VF610_ADC_ADACK_SEL 0x03
|
||||
#define VF610_ADC_ADCCLK_MASK 0x03
|
||||
#define VF610_ADC_CLK_DIV2 0x20
|
||||
#define VF610_ADC_CLK_DIV4 0x40
|
||||
#define VF610_ADC_CLK_DIV8 0x60
|
||||
#define VF610_ADC_CLK_MASK 0x60
|
||||
#define VF610_ADC_ADLSMP_LONG 0x10
|
||||
#define VF610_ADC_ADSTS_MASK 0x300
|
||||
#define VF610_ADC_ADLPC_EN 0x80
|
||||
#define VF610_ADC_ADHSC_EN 0x400
|
||||
#define VF610_ADC_REFSEL_VALT 0x100
|
||||
#define VF610_ADC_REFSEL_VBG 0x1000
|
||||
#define VF610_ADC_ADTRG_HARD 0x2000
|
||||
#define VF610_ADC_AVGS_8 0x4000
|
||||
#define VF610_ADC_AVGS_16 0x8000
|
||||
#define VF610_ADC_AVGS_32 0xC000
|
||||
#define VF610_ADC_AVGS_MASK 0xC000
|
||||
#define VF610_ADC_OVWREN 0x10000
|
||||
|
||||
/* General control register field define */
|
||||
#define VF610_ADC_ADACKEN 0x1
|
||||
#define VF610_ADC_DMAEN 0x2
|
||||
#define VF610_ADC_ACREN 0x4
|
||||
#define VF610_ADC_ACFGT 0x8
|
||||
#define VF610_ADC_ACFE 0x10
|
||||
#define VF610_ADC_AVGEN 0x20
|
||||
#define VF610_ADC_ADCON 0x40
|
||||
#define VF610_ADC_CAL 0x80
|
||||
|
||||
/* Other field define */
|
||||
#define VF610_ADC_ADCHC(x) ((x) & 0xF)
|
||||
#define VF610_ADC_AIEN (0x1 << 7)
|
||||
#define VF610_ADC_CONV_DISABLE 0x1F
|
||||
#define VF610_ADC_HS_COCO0 0x1
|
||||
#define VF610_ADC_CALF 0x2
|
||||
#define VF610_ADC_TIMEOUT msecs_to_jiffies(100)
|
||||
|
||||
enum clk_sel {
|
||||
VF610_ADCIOC_BUSCLK_SET,
|
||||
VF610_ADCIOC_ALTCLK_SET,
|
||||
VF610_ADCIOC_ADACK_SET,
|
||||
};
|
||||
|
||||
enum vol_ref {
|
||||
VF610_ADCIOC_VR_VREF_SET,
|
||||
VF610_ADCIOC_VR_VALT_SET,
|
||||
VF610_ADCIOC_VR_VBG_SET,
|
||||
};
|
||||
|
||||
enum average_sel {
|
||||
VF610_ADC_SAMPLE_1,
|
||||
VF610_ADC_SAMPLE_4,
|
||||
VF610_ADC_SAMPLE_8,
|
||||
VF610_ADC_SAMPLE_16,
|
||||
VF610_ADC_SAMPLE_32,
|
||||
};
|
||||
|
||||
struct vf610_adc_feature {
|
||||
enum clk_sel clk_sel;
|
||||
enum vol_ref vol_ref;
|
||||
|
||||
int clk_div;
|
||||
int sample_rate;
|
||||
int res_mode;
|
||||
|
||||
bool lpm;
|
||||
bool calibration;
|
||||
bool ovwren;
|
||||
};
|
||||
|
||||
struct vf610_adc {
|
||||
struct device *dev;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
|
||||
u32 vref_uv;
|
||||
u32 value;
|
||||
struct regulator *vref;
|
||||
struct vf610_adc_feature adc_feature;
|
||||
|
||||
u32 sample_freq_avail[5];
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 };
|
||||
|
||||
#define VF610_ADC_CHAN(_idx, _chan_type) { \
|
||||
.type = (_chan_type), \
|
||||
.indexed = 1, \
|
||||
.channel = (_idx), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
|
||||
VF610_ADC_CHAN(0, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(1, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(2, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(3, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(4, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(5, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(6, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(7, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(8, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(9, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(10, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(11, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(12, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(13, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(14, IIO_VOLTAGE),
|
||||
VF610_ADC_CHAN(15, IIO_VOLTAGE),
|
||||
/* sentinel */
|
||||
};
|
||||
|
||||
static inline void vf610_adc_calculate_rates(struct vf610_adc *info)
|
||||
{
|
||||
unsigned long adck_rate, ipg_rate = clk_get_rate(info->clk);
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Calculate ADC sample frequencies
|
||||
* Sample time unit is ADCK cycles. ADCK clk source is ipg clock,
|
||||
* which is the same as bus clock.
|
||||
*
|
||||
* ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder)
|
||||
* SFCAdder: fixed to 6 ADCK cycles
|
||||
* AverageNum: 1, 4, 8, 16, 32 samples for hardware average.
|
||||
* BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode
|
||||
* LSTAdder(Long Sample Time): fixed to 3 ADCK cycles
|
||||
*/
|
||||
adck_rate = ipg_rate / info->adc_feature.clk_div;
|
||||
for (i = 0; i < ARRAY_SIZE(vf610_hw_avgs); i++)
|
||||
info->sample_freq_avail[i] =
|
||||
adck_rate / (6 + vf610_hw_avgs[i] * (25 + 3));
|
||||
}
|
||||
|
||||
static inline void vf610_adc_cfg_init(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
|
||||
/* set default Configuration for ADC controller */
|
||||
adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
|
||||
adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
|
||||
|
||||
adc_feature->calibration = true;
|
||||
adc_feature->ovwren = true;
|
||||
|
||||
adc_feature->res_mode = 12;
|
||||
adc_feature->sample_rate = 1;
|
||||
adc_feature->lpm = true;
|
||||
|
||||
/* Use a save ADCK which is below 20MHz on all devices */
|
||||
adc_feature->clk_div = 8;
|
||||
|
||||
vf610_adc_calculate_rates(info);
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_post_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &info->adc_feature;
|
||||
int cfg_data = 0;
|
||||
int gc_data = 0;
|
||||
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_ALTCLK_SET:
|
||||
cfg_data |= VF610_ADC_ALTCLK_SEL;
|
||||
break;
|
||||
case VF610_ADCIOC_ADACK_SET:
|
||||
cfg_data |= VF610_ADC_ADACK_SEL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* low power set for calibration */
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
/* enable high speed for calibration */
|
||||
cfg_data |= VF610_ADC_ADHSC_EN;
|
||||
|
||||
/* voltage reference */
|
||||
switch (adc_feature->vol_ref) {
|
||||
case VF610_ADCIOC_VR_VREF_SET:
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VALT_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VALT;
|
||||
break;
|
||||
case VF610_ADCIOC_VR_VBG_SET:
|
||||
cfg_data |= VF610_ADC_REFSEL_VBG;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error voltage reference\n");
|
||||
}
|
||||
|
||||
/* data overwrite enable */
|
||||
if (adc_feature->ovwren)
|
||||
cfg_data |= VF610_ADC_OVWREN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_calibration(struct vf610_adc *info)
|
||||
{
|
||||
int adc_gc, hc_cfg;
|
||||
int timeout;
|
||||
|
||||
if (!info->adc_feature.calibration)
|
||||
return;
|
||||
|
||||
/* enable calibration interrupt */
|
||||
hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GC);
|
||||
writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
timeout = wait_for_completion_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (timeout == 0)
|
||||
dev_err(info->dev, "Timeout for adc calibration\n");
|
||||
|
||||
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
|
||||
if (adc_gc & VF610_ADC_CALF)
|
||||
dev_err(info->dev, "ADC calibration failed\n");
|
||||
|
||||
info->adc_feature.calibration = false;
|
||||
}
|
||||
|
||||
static void vf610_adc_cfg_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADLPC_EN;
|
||||
if (adc_feature->lpm)
|
||||
cfg_data |= VF610_ADC_ADLPC_EN;
|
||||
|
||||
cfg_data &= ~VF610_ADC_ADHSC_EN;
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
}
|
||||
|
||||
static void vf610_adc_sample_set(struct vf610_adc *info)
|
||||
{
|
||||
struct vf610_adc_feature *adc_feature = &(info->adc_feature);
|
||||
int cfg_data, gc_data;
|
||||
|
||||
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
|
||||
gc_data = readl(info->regs + VF610_REG_ADC_GC);
|
||||
|
||||
/* resolution mode */
|
||||
cfg_data &= ~VF610_ADC_MODE_MASK;
|
||||
switch (adc_feature->res_mode) {
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_MODE_BIT8;
|
||||
break;
|
||||
case 10:
|
||||
cfg_data |= VF610_ADC_MODE_BIT10;
|
||||
break;
|
||||
case 12:
|
||||
cfg_data |= VF610_ADC_MODE_BIT12;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error resolution mode\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* clock select and clock divider */
|
||||
cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK);
|
||||
switch (adc_feature->clk_div) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
cfg_data |= VF610_ADC_CLK_DIV2;
|
||||
break;
|
||||
case 4:
|
||||
cfg_data |= VF610_ADC_CLK_DIV4;
|
||||
break;
|
||||
case 8:
|
||||
cfg_data |= VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
case 16:
|
||||
switch (adc_feature->clk_sel) {
|
||||
case VF610_ADCIOC_BUSCLK_SET:
|
||||
cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "error clk divider\n");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use the short sample mode */
|
||||
cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK);
|
||||
|
||||
/* update hardware average selection */
|
||||
cfg_data &= ~VF610_ADC_AVGS_MASK;
|
||||
gc_data &= ~VF610_ADC_AVGEN;
|
||||
switch (adc_feature->sample_rate) {
|
||||
case VF610_ADC_SAMPLE_1:
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_4:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_8:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_8;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_16:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_16;
|
||||
break;
|
||||
case VF610_ADC_SAMPLE_32:
|
||||
gc_data |= VF610_ADC_AVGEN;
|
||||
cfg_data |= VF610_ADC_AVGS_32;
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev,
|
||||
"error hardware sample average select\n");
|
||||
}
|
||||
|
||||
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
|
||||
writel(gc_data, info->regs + VF610_REG_ADC_GC);
|
||||
}
|
||||
|
||||
static void vf610_adc_hw_init(struct vf610_adc *info)
|
||||
{
|
||||
/* CFG: Feature set */
|
||||
vf610_adc_cfg_post_set(info);
|
||||
vf610_adc_sample_set(info);
|
||||
|
||||
/* adc calibration */
|
||||
vf610_adc_calibration(info);
|
||||
|
||||
/* CFG: power and speed set */
|
||||
vf610_adc_cfg_set(info);
|
||||
}
|
||||
|
||||
static int vf610_adc_read_data(struct vf610_adc *info)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = readl(info->regs + VF610_REG_ADC_R0);
|
||||
|
||||
switch (info->adc_feature.res_mode) {
|
||||
case 8:
|
||||
result &= 0xFF;
|
||||
break;
|
||||
case 10:
|
||||
result &= 0x3FF;
|
||||
break;
|
||||
case 12:
|
||||
result &= 0xFFF;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct vf610_adc *info = (struct vf610_adc *)dev_id;
|
||||
int coco;
|
||||
|
||||
coco = readl(info->regs + VF610_REG_ADC_HS);
|
||||
if (coco & VF610_ADC_HS_COCO0) {
|
||||
info->value = vf610_adc_read_data(info);
|
||||
complete(&info->completion);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static ssize_t vf610_show_samp_freq_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(dev_to_iio_dev(dev));
|
||||
size_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(info->sample_freq_avail); i++)
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len,
|
||||
"%u ", info->sample_freq_avail[i]);
|
||||
|
||||
/* replace trailing space by newline */
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(vf610_show_samp_freq_avail);
|
||||
|
||||
static struct attribute *vf610_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group vf610_attribute_group = {
|
||||
.attrs = vf610_attributes,
|
||||
};
|
||||
|
||||
static int vf610_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
unsigned int hc_cfg;
|
||||
long ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
reinit_completion(&info->completion);
|
||||
|
||||
hc_cfg = VF610_ADC_ADCHC(chan->channel);
|
||||
hc_cfg |= VF610_ADC_AIEN;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
ret = wait_for_completion_interruptible_timeout
|
||||
(&info->completion, VF610_ADC_TIMEOUT);
|
||||
if (ret == 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*val = info->value;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return IIO_VAL_INT;
|
||||
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = info->vref_uv / 1000;
|
||||
*val2 = info->adc_feature.res_mode;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
*val = info->sample_freq_avail[info->adc_feature.sample_rate];
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int i;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
for (i = 0;
|
||||
i < ARRAY_SIZE(info->sample_freq_avail);
|
||||
i++)
|
||||
if (val == info->sample_freq_avail[i]) {
|
||||
info->adc_feature.sample_rate = i;
|
||||
vf610_adc_sample_set(info);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vf610_adc_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
if ((readval == NULL) ||
|
||||
(!(reg % 4) || (reg > VF610_REG_ADC_PCTL)))
|
||||
return -EINVAL;
|
||||
|
||||
*readval = readl(info->regs + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_info vf610_adc_iio_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &vf610_read_raw,
|
||||
.write_raw = &vf610_write_raw,
|
||||
.debugfs_reg_access = &vf610_adc_reg_access,
|
||||
.attrs = &vf610_attribute_group,
|
||||
};
|
||||
|
||||
static const struct of_device_id vf610_adc_match[] = {
|
||||
{ .compatible = "fsl,vf610-adc", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, vf610_adc_match);
|
||||
|
||||
static int vf610_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vf610_adc *info;
|
||||
struct iio_dev *indio_dev;
|
||||
struct resource *mem;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "Failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info = iio_priv(indio_dev);
|
||||
info->dev = &pdev->dev;
|
||||
|
||||
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
info->regs = devm_ioremap_resource(&pdev->dev, mem);
|
||||
if (IS_ERR(info->regs))
|
||||
return PTR_ERR(info->regs);
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_err(&pdev->dev, "no irq resource?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(info->dev, irq,
|
||||
vf610_adc_isr, 0,
|
||||
dev_name(&pdev->dev), info);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->clk = devm_clk_get(&pdev->dev, "adc");
|
||||
if (IS_ERR(info->clk)) {
|
||||
dev_err(&pdev->dev, "failed getting clock, err = %ld\n",
|
||||
PTR_ERR(info->clk));
|
||||
ret = PTR_ERR(info->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
info->vref = devm_regulator_get(&pdev->dev, "vref");
|
||||
if (IS_ERR(info->vref))
|
||||
return PTR_ERR(info->vref);
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->vref_uv = regulator_get_voltage(info->vref);
|
||||
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
init_completion(&info->completion);
|
||||
|
||||
indio_dev->name = dev_name(&pdev->dev);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->dev.of_node = pdev->dev.of_node;
|
||||
indio_dev->info = &vf610_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vf610_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels);
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not prepare or enable the clock.\n");
|
||||
goto error_adc_clk_enable;
|
||||
}
|
||||
|
||||
vf610_adc_cfg_init(info);
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Couldn't register the device.\n");
|
||||
goto error_iio_device_register;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
error_iio_device_register:
|
||||
clk_disable_unprepare(info->clk);
|
||||
error_adc_clk_enable:
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int vf610_adc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(info->vref);
|
||||
clk_disable_unprepare(info->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int vf610_adc_suspend(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int hc_cfg;
|
||||
|
||||
/* ADC controller enters to stop mode */
|
||||
hc_cfg = readl(info->regs + VF610_REG_ADC_HC0);
|
||||
hc_cfg |= VF610_ADC_CONV_DISABLE;
|
||||
writel(hc_cfg, info->regs + VF610_REG_ADC_HC0);
|
||||
|
||||
clk_disable_unprepare(info->clk);
|
||||
regulator_disable(info->vref);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vf610_adc_resume(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct vf610_adc *info = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regulator_enable(info->vref);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(info->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
vf610_adc_hw_init(info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
|
||||
vf610_adc_suspend,
|
||||
vf610_adc_resume);
|
||||
|
||||
static struct platform_driver vf610_adc_driver = {
|
||||
.probe = vf610_adc_probe,
|
||||
.remove = vf610_adc_remove,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.of_match_table = vf610_adc_match,
|
||||
.pm = &vf610_adc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(vf610_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>");
|
||||
MODULE_DESCRIPTION("Freescale VF610 ADC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
157
drivers/iio/adc/viperboard_adc.c
Normal file
157
drivers/iio/adc/viperboard_adc.c
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Nano River Technologies viperboard IIO ADC driver
|
||||
*
|
||||
* (C) 2012 by Lemonage GmbH
|
||||
* Author: Lars Poeschel <poeschel@lemonage.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <linux/usb.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/mfd/viperboard.h>
|
||||
|
||||
#define VPRBRD_ADC_CMD_GET 0x00
|
||||
|
||||
struct vprbrd_adc_msg {
|
||||
u8 cmd;
|
||||
u8 chan;
|
||||
u8 val;
|
||||
} __packed;
|
||||
|
||||
struct vprbrd_adc {
|
||||
struct vprbrd *vb;
|
||||
};
|
||||
|
||||
#define VPRBRD_ADC_CHANNEL(_index) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.channel = _index, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
}
|
||||
|
||||
static struct iio_chan_spec const vprbrd_adc_iio_channels[] = {
|
||||
VPRBRD_ADC_CHANNEL(0),
|
||||
VPRBRD_ADC_CHANNEL(1),
|
||||
VPRBRD_ADC_CHANNEL(2),
|
||||
VPRBRD_ADC_CHANNEL(3),
|
||||
};
|
||||
|
||||
static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long info)
|
||||
{
|
||||
int ret, error = 0;
|
||||
struct vprbrd_adc *adc = iio_priv(iio_dev);
|
||||
struct vprbrd *vb = adc->vb;
|
||||
struct vprbrd_adc_msg *admsg = (struct vprbrd_adc_msg *)vb->buf;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&vb->lock);
|
||||
|
||||
admsg->cmd = VPRBRD_ADC_CMD_GET;
|
||||
admsg->chan = chan->channel;
|
||||
admsg->val = 0x00;
|
||||
|
||||
ret = usb_control_msg(vb->usb_dev,
|
||||
usb_sndctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
|
||||
VPRBRD_USB_TYPE_OUT, 0x0000, 0x0000, admsg,
|
||||
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
|
||||
if (ret != sizeof(struct vprbrd_adc_msg)) {
|
||||
dev_err(&iio_dev->dev, "usb send error on adc read\n");
|
||||
error = -EREMOTEIO;
|
||||
}
|
||||
|
||||
ret = usb_control_msg(vb->usb_dev,
|
||||
usb_rcvctrlpipe(vb->usb_dev, 0), VPRBRD_USB_REQUEST_ADC,
|
||||
VPRBRD_USB_TYPE_IN, 0x0000, 0x0000, admsg,
|
||||
sizeof(struct vprbrd_adc_msg), VPRBRD_USB_TIMEOUT_MS);
|
||||
|
||||
*val = admsg->val;
|
||||
|
||||
mutex_unlock(&vb->lock);
|
||||
|
||||
if (ret != sizeof(struct vprbrd_adc_msg)) {
|
||||
dev_err(&iio_dev->dev, "usb recv error on adc read\n");
|
||||
error = -EREMOTEIO;
|
||||
}
|
||||
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
error:
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct iio_info vprbrd_adc_iio_info = {
|
||||
.read_raw = &vprbrd_iio_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int vprbrd_adc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
|
||||
struct vprbrd_adc *adc;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
/* registering iio */
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
|
||||
if (!indio_dev) {
|
||||
dev_err(&pdev->dev, "failed allocating iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
adc = iio_priv(indio_dev);
|
||||
adc->vb = vb;
|
||||
indio_dev->name = "viperboard adc";
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &vprbrd_adc_iio_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = vprbrd_adc_iio_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels);
|
||||
|
||||
ret = devm_iio_device_register(&pdev->dev, indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not register iio (adc)");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver vprbrd_adc_driver = {
|
||||
.driver = {
|
||||
.name = "viperboard-adc",
|
||||
},
|
||||
.probe = vprbrd_adc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(vprbrd_adc_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars Poeschel <poeschel@lemonage.de>");
|
||||
MODULE_DESCRIPTION("IIO ADC driver for Nano River Techs Viperboard");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:viperboard-adc");
|
||||
1336
drivers/iio/adc/xilinx-xadc-core.c
Normal file
1336
drivers/iio/adc/xilinx-xadc-core.c
Normal file
File diff suppressed because it is too large
Load diff
248
drivers/iio/adc/xilinx-xadc-events.c
Normal file
248
drivers/iio/adc/xilinx-xadc-events.c
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "xilinx-xadc.h"
|
||||
|
||||
static const struct iio_chan_spec *xadc_event_to_channel(
|
||||
struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
switch (event) {
|
||||
case XADC_THRESHOLD_OT_MAX:
|
||||
case XADC_THRESHOLD_TEMP_MAX:
|
||||
return &indio_dev->channels[0];
|
||||
case XADC_THRESHOLD_VCCINT_MAX:
|
||||
case XADC_THRESHOLD_VCCAUX_MAX:
|
||||
return &indio_dev->channels[event];
|
||||
default:
|
||||
return &indio_dev->channels[event-1];
|
||||
}
|
||||
}
|
||||
|
||||
static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event)
|
||||
{
|
||||
const struct iio_chan_spec *chan;
|
||||
|
||||
/* Temperature threshold error, we don't handle this yet */
|
||||
if (event == 0)
|
||||
return;
|
||||
|
||||
chan = xadc_event_to_channel(indio_dev, event);
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* The temperature channel only supports over-temperature
|
||||
* events.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
} else {
|
||||
/*
|
||||
* For other channels we don't know whether it is a upper or
|
||||
* lower threshold event. Userspace will have to check the
|
||||
* channel value if it wants to know.
|
||||
*/
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(chan->type, chan->channel,
|
||||
IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
}
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for_each_set_bit(i, &events, 8)
|
||||
xadc_handle_event(indio_dev, i);
|
||||
}
|
||||
|
||||
static unsigned xadc_get_threshold_offset(const struct iio_chan_spec *chan,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
unsigned int offset;
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
offset = XADC_THRESHOLD_OT_MAX;
|
||||
} else {
|
||||
if (chan->channel < 2)
|
||||
offset = chan->channel + 1;
|
||||
else
|
||||
offset = chan->channel + 6;
|
||||
}
|
||||
|
||||
if (dir == IIO_EV_DIR_FALLING)
|
||||
offset += 4;
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan)
|
||||
{
|
||||
if (chan->type == IIO_TEMP) {
|
||||
return XADC_ALARM_OT_MASK;
|
||||
} else {
|
||||
switch (chan->channel) {
|
||||
case 0:
|
||||
return XADC_ALARM_VCCINT_MASK;
|
||||
case 1:
|
||||
return XADC_ALARM_VCCAUX_MASK;
|
||||
case 2:
|
||||
return XADC_ALARM_VCCBRAM_MASK;
|
||||
case 3:
|
||||
return XADC_ALARM_VCCPINT_MASK;
|
||||
case 4:
|
||||
return XADC_ALARM_VCCPAUX_MASK;
|
||||
case 5:
|
||||
return XADC_ALARM_VCCODDR_MASK;
|
||||
default:
|
||||
/* We will never get here */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan));
|
||||
}
|
||||
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
unsigned int alarm = xadc_get_alarm_mask(chan);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
uint16_t cfg, old_cfg;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
if (state)
|
||||
xadc->alarm_mask |= alarm;
|
||||
else
|
||||
xadc->alarm_mask &= ~alarm;
|
||||
|
||||
xadc->ops->update_alarm(xadc, xadc->alarm_mask);
|
||||
|
||||
ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
|
||||
old_cfg = cfg;
|
||||
cfg |= XADC_CONF1_ALARM_MASK;
|
||||
cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */
|
||||
cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */
|
||||
cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */
|
||||
if (old_cfg != cfg)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg);
|
||||
|
||||
err_out:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register value is msb aligned, the lower 4 bits are ignored */
|
||||
#define XADC_THRESHOLD_VALUE_SHIFT 4
|
||||
|
||||
int xadc_read_event_value(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)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
*val = xadc->threshold[offset];
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
*val = xadc->temp_hysteresis;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*val >>= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
int xadc_write_event_value(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)
|
||||
{
|
||||
unsigned int offset = xadc_get_threshold_offset(chan, dir);
|
||||
struct xadc *xadc = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
val <<= XADC_THRESHOLD_VALUE_SHIFT;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
|
||||
switch (info) {
|
||||
case IIO_EV_INFO_VALUE:
|
||||
xadc->threshold[offset] = val;
|
||||
break;
|
||||
case IIO_EV_INFO_HYSTERESIS:
|
||||
xadc->temp_hysteresis = val;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (chan->type == IIO_TEMP) {
|
||||
/*
|
||||
* According to the datasheet we need to set the lower 4 bits to
|
||||
* 0x3, otherwise 125 degree celsius will be used as the
|
||||
* threshold.
|
||||
*/
|
||||
val |= 0x3;
|
||||
|
||||
/*
|
||||
* Since we store the hysteresis as relative (to the threshold)
|
||||
* value, but the hardware expects an absolute value we need to
|
||||
* recalcualte this value whenever the hysteresis or the
|
||||
* threshold changes.
|
||||
*/
|
||||
if (xadc->threshold[offset] < xadc->temp_hysteresis)
|
||||
xadc->threshold[offset + 4] = 0;
|
||||
else
|
||||
xadc->threshold[offset + 4] = xadc->threshold[offset] -
|
||||
xadc->temp_hysteresis;
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4),
|
||||
xadc->threshold[offset + 4]);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (info == IIO_EV_INFO_VALUE)
|
||||
ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&xadc->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
209
drivers/iio/adc/xilinx-xadc.h
Normal file
209
drivers/iio/adc/xilinx-xadc.h
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/*
|
||||
* Xilinx XADC driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clauen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#ifndef __IIO_XILINX_XADC__
|
||||
#define __IIO_XILINX_XADC__
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct iio_dev;
|
||||
struct clk;
|
||||
struct xadc_ops;
|
||||
struct platform_device;
|
||||
|
||||
void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events);
|
||||
|
||||
int xadc_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir);
|
||||
int xadc_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state);
|
||||
int xadc_read_event_value(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);
|
||||
int xadc_write_event_value(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);
|
||||
|
||||
enum xadc_external_mux_mode {
|
||||
XADC_EXTERNAL_MUX_NONE,
|
||||
XADC_EXTERNAL_MUX_SINGLE,
|
||||
XADC_EXTERNAL_MUX_DUAL,
|
||||
};
|
||||
|
||||
struct xadc {
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
|
||||
const struct xadc_ops *ops;
|
||||
|
||||
uint16_t threshold[16];
|
||||
uint16_t temp_hysteresis;
|
||||
unsigned int alarm_mask;
|
||||
|
||||
uint16_t *data;
|
||||
|
||||
struct iio_trigger *trigger;
|
||||
struct iio_trigger *convst_trigger;
|
||||
struct iio_trigger *samplerate_trigger;
|
||||
|
||||
enum xadc_external_mux_mode external_mux_mode;
|
||||
|
||||
unsigned int zynq_alarm;
|
||||
unsigned int zynq_masked_alarm;
|
||||
unsigned int zynq_intmask;
|
||||
struct delayed_work zynq_unmask_work;
|
||||
|
||||
struct mutex mutex;
|
||||
spinlock_t lock;
|
||||
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
struct xadc_ops {
|
||||
int (*read)(struct xadc *, unsigned int, uint16_t *);
|
||||
int (*write)(struct xadc *, unsigned int, uint16_t);
|
||||
int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev,
|
||||
int irq);
|
||||
void (*update_alarm)(struct xadc *, unsigned int);
|
||||
unsigned long (*get_dclk_rate)(struct xadc *);
|
||||
irqreturn_t (*interrupt_handler)(int, void *);
|
||||
irqreturn_t (*threaded_interrupt_handler)(int, void *);
|
||||
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->read(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
lockdep_assert_held(&xadc->mutex);
|
||||
return xadc->ops->write(xadc, reg, val);
|
||||
}
|
||||
|
||||
static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_read_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg,
|
||||
uint16_t val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&xadc->mutex);
|
||||
ret = _xadc_write_adc_reg(xadc, reg, val);
|
||||
mutex_unlock(&xadc->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* XADC hardmacro register definitions */
|
||||
#define XADC_REG_TEMP 0x00
|
||||
#define XADC_REG_VCCINT 0x01
|
||||
#define XADC_REG_VCCAUX 0x02
|
||||
#define XADC_REG_VPVN 0x03
|
||||
#define XADC_REG_VREFP 0x04
|
||||
#define XADC_REG_VREFN 0x05
|
||||
#define XADC_REG_VCCBRAM 0x06
|
||||
|
||||
#define XADC_REG_VCCPINT 0x0d
|
||||
#define XADC_REG_VCCPAUX 0x0e
|
||||
#define XADC_REG_VCCO_DDR 0x0f
|
||||
#define XADC_REG_VAUX(x) (0x10 + (x))
|
||||
|
||||
#define XADC_REG_MAX_TEMP 0x20
|
||||
#define XADC_REG_MAX_VCCINT 0x21
|
||||
#define XADC_REG_MAX_VCCAUX 0x22
|
||||
#define XADC_REG_MAX_VCCBRAM 0x23
|
||||
#define XADC_REG_MIN_TEMP 0x24
|
||||
#define XADC_REG_MIN_VCCINT 0x25
|
||||
#define XADC_REG_MIN_VCCAUX 0x26
|
||||
#define XADC_REG_MIN_VCCBRAM 0x27
|
||||
#define XADC_REG_MAX_VCCPINT 0x28
|
||||
#define XADC_REG_MAX_VCCPAUX 0x29
|
||||
#define XADC_REG_MAX_VCCO_DDR 0x2a
|
||||
#define XADC_REG_MIN_VCCPINT 0x2b
|
||||
#define XADC_REG_MIN_VCCPAUX 0x2c
|
||||
#define XADC_REG_MIN_VCCO_DDR 0x2d
|
||||
|
||||
#define XADC_REG_CONF0 0x40
|
||||
#define XADC_REG_CONF1 0x41
|
||||
#define XADC_REG_CONF2 0x42
|
||||
#define XADC_REG_SEQ(x) (0x48 + (x))
|
||||
#define XADC_REG_INPUT_MODE(x) (0x4c + (x))
|
||||
#define XADC_REG_THRESHOLD(x) (0x50 + (x))
|
||||
|
||||
#define XADC_REG_FLAG 0x3f
|
||||
|
||||
#define XADC_CONF0_EC BIT(9)
|
||||
#define XADC_CONF0_ACQ BIT(8)
|
||||
#define XADC_CONF0_MUX BIT(11)
|
||||
#define XADC_CONF0_CHAN(x) (x)
|
||||
|
||||
#define XADC_CONF1_SEQ_MASK (0xf << 12)
|
||||
#define XADC_CONF1_SEQ_DEFAULT (0 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_PASS (1 << 12)
|
||||
#define XADC_CONF1_SEQ_CONTINUOUS (2 << 12)
|
||||
#define XADC_CONF1_SEQ_SINGLE_CHANNEL (3 << 12)
|
||||
#define XADC_CONF1_SEQ_SIMULTANEOUS (4 << 12)
|
||||
#define XADC_CONF1_SEQ_INDEPENDENT (8 << 12)
|
||||
#define XADC_CONF1_ALARM_MASK 0x0f0f
|
||||
|
||||
#define XADC_CONF2_DIV_MASK 0xff00
|
||||
#define XADC_CONF2_DIV_OFFSET 8
|
||||
|
||||
#define XADC_CONF2_PD_MASK (0x3 << 4)
|
||||
#define XADC_CONF2_PD_NONE (0x0 << 4)
|
||||
#define XADC_CONF2_PD_ADC_B (0x2 << 4)
|
||||
#define XADC_CONF2_PD_BOTH (0x3 << 4)
|
||||
|
||||
#define XADC_ALARM_TEMP_MASK BIT(0)
|
||||
#define XADC_ALARM_VCCINT_MASK BIT(1)
|
||||
#define XADC_ALARM_VCCAUX_MASK BIT(2)
|
||||
#define XADC_ALARM_OT_MASK BIT(3)
|
||||
#define XADC_ALARM_VCCBRAM_MASK BIT(4)
|
||||
#define XADC_ALARM_VCCPINT_MASK BIT(5)
|
||||
#define XADC_ALARM_VCCPAUX_MASK BIT(6)
|
||||
#define XADC_ALARM_VCCODDR_MASK BIT(7)
|
||||
|
||||
#define XADC_THRESHOLD_TEMP_MAX 0x0
|
||||
#define XADC_THRESHOLD_VCCINT_MAX 0x1
|
||||
#define XADC_THRESHOLD_VCCAUX_MAX 0x2
|
||||
#define XADC_THRESHOLD_OT_MAX 0x3
|
||||
#define XADC_THRESHOLD_TEMP_MIN 0x4
|
||||
#define XADC_THRESHOLD_VCCINT_MIN 0x5
|
||||
#define XADC_THRESHOLD_VCCAUX_MIN 0x6
|
||||
#define XADC_THRESHOLD_OT_MIN 0x7
|
||||
#define XADC_THRESHOLD_VCCBRAM_MAX 0x8
|
||||
#define XADC_THRESHOLD_VCCPINT_MAX 0x9
|
||||
#define XADC_THRESHOLD_VCCPAUX_MAX 0xa
|
||||
#define XADC_THRESHOLD_VCCODDR_MAX 0xb
|
||||
#define XADC_THRESHOLD_VCCBRAM_MIN 0xc
|
||||
#define XADC_THRESHOLD_VCCPINT_MIN 0xd
|
||||
#define XADC_THRESHOLD_VCCPAUX_MIN 0xe
|
||||
#define XADC_THRESHOLD_VCCODDR_MIN 0xf
|
||||
|
||||
#endif
|
||||
19
drivers/iio/amplifiers/Kconfig
Normal file
19
drivers/iio/amplifiers/Kconfig
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
#
|
||||
# Gain Amplifiers, etc.
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Amplifiers"
|
||||
|
||||
config AD8366
|
||||
tristate "Analog Devices AD8366 VGA"
|
||||
depends on SPI
|
||||
select BITREVERSE
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD8366
|
||||
SPI Dual-Digital Variable Gain Amplifier (VGA).
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad8366.
|
||||
|
||||
endmenu
|
||||
6
drivers/iio/amplifiers/Makefile
Normal file
6
drivers/iio/amplifiers/Makefile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# Makefile iio/amplifiers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD8366) += ad8366.o
|
||||
213
drivers/iio/amplifiers/ad8366.c
Normal file
213
drivers/iio/amplifiers/ad8366.c
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA)
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitrev.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
struct ad8366_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned char ch[2];
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
unsigned char data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad8366_write(struct iio_dev *indio_dev,
|
||||
unsigned char ch_a, char unsigned ch_b)
|
||||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ch_a = bitrev8(ch_a & 0x3F);
|
||||
ch_b = bitrev8(ch_b & 0x3F);
|
||||
|
||||
st->data[0] = ch_b >> 4;
|
||||
st->data[1] = (ch_b << 4) | (ch_a >> 2);
|
||||
|
||||
ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data));
|
||||
if (ret < 0)
|
||||
dev_err(&indio_dev->dev, "write failed (%d)", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad8366_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
unsigned code;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
code = st->ch[chan->channel];
|
||||
|
||||
/* Values in dB */
|
||||
code = code * 253 + 4500;
|
||||
*val = code / 1000;
|
||||
*val2 = (code % 1000) * 1000;
|
||||
|
||||
ret = IIO_VAL_INT_PLUS_MICRO_DB;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
static int ad8366_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
unsigned code;
|
||||
int ret;
|
||||
|
||||
if (val < 0 || val2 < 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* Values in dB */
|
||||
code = (((u8)val * 1000) + ((u32)val2 / 1000));
|
||||
|
||||
if (code > 20500 || code < 4500)
|
||||
return -EINVAL;
|
||||
|
||||
code = (code - 4500) / 253;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_HARDWAREGAIN:
|
||||
st->ch[chan->channel] = code;
|
||||
ret = ad8366_write(indio_dev, st->ch[0], st->ch[1]);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad8366_info = {
|
||||
.read_raw = &ad8366_read_raw,
|
||||
.write_raw = &ad8366_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD8366_CHAN(_channel) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = _channel, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN),\
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad8366_channels[] = {
|
||||
AD8366_CHAN(0),
|
||||
AD8366_CHAN(1),
|
||||
};
|
||||
|
||||
static int ad8366_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad8366_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad8366_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad8366_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ad8366_write(indio_dev, 0 , 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad8366_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad8366_state *st = iio_priv(indio_dev);
|
||||
struct regulator *reg = st->reg;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad8366_id[] = {
|
||||
{"ad8366", 0},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct spi_driver ad8366_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad8366_probe,
|
||||
.remove = ad8366_remove,
|
||||
.id_table = ad8366_id,
|
||||
};
|
||||
|
||||
module_spi_driver(ad8366_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD8366 VGA");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
124
drivers/iio/buffer_cb.c
Normal file
124
drivers/iio/buffer_cb.c
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/consumer.h>
|
||||
|
||||
struct iio_cb_buffer {
|
||||
struct iio_buffer buffer;
|
||||
int (*cb)(const void *data, void *private);
|
||||
void *private;
|
||||
struct iio_channel *channels;
|
||||
};
|
||||
|
||||
static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
|
||||
{
|
||||
return container_of(buffer, struct iio_cb_buffer, buffer);
|
||||
}
|
||||
|
||||
static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
|
||||
return cb_buff->cb(data, cb_buff->private);
|
||||
}
|
||||
|
||||
static void iio_buffer_cb_release(struct iio_buffer *buffer)
|
||||
{
|
||||
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
kfree(cb_buff);
|
||||
}
|
||||
|
||||
static const struct iio_buffer_access_funcs iio_cb_access = {
|
||||
.store_to = &iio_buffer_cb_store_to,
|
||||
.release = &iio_buffer_cb_release,
|
||||
};
|
||||
|
||||
struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
|
||||
int (*cb)(const void *data,
|
||||
void *private),
|
||||
void *private)
|
||||
{
|
||||
int ret;
|
||||
struct iio_cb_buffer *cb_buff;
|
||||
struct iio_dev *indio_dev;
|
||||
struct iio_channel *chan;
|
||||
|
||||
cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
|
||||
if (cb_buff == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
iio_buffer_init(&cb_buff->buffer);
|
||||
|
||||
cb_buff->private = private;
|
||||
cb_buff->cb = cb;
|
||||
cb_buff->buffer.access = &iio_cb_access;
|
||||
INIT_LIST_HEAD(&cb_buff->buffer.demux_list);
|
||||
|
||||
cb_buff->channels = iio_channel_get_all(dev);
|
||||
if (IS_ERR(cb_buff->channels)) {
|
||||
ret = PTR_ERR(cb_buff->channels);
|
||||
goto error_free_cb_buff;
|
||||
}
|
||||
|
||||
indio_dev = cb_buff->channels[0].indio_dev;
|
||||
cb_buff->buffer.scan_mask
|
||||
= kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
|
||||
GFP_KERNEL);
|
||||
if (cb_buff->buffer.scan_mask == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_release_channels;
|
||||
}
|
||||
chan = &cb_buff->channels[0];
|
||||
while (chan->indio_dev) {
|
||||
if (chan->indio_dev != indio_dev) {
|
||||
ret = -EINVAL;
|
||||
goto error_free_scan_mask;
|
||||
}
|
||||
set_bit(chan->channel->scan_index,
|
||||
cb_buff->buffer.scan_mask);
|
||||
chan++;
|
||||
}
|
||||
|
||||
return cb_buff;
|
||||
|
||||
error_free_scan_mask:
|
||||
kfree(cb_buff->buffer.scan_mask);
|
||||
error_release_channels:
|
||||
iio_channel_release_all(cb_buff->channels);
|
||||
error_free_cb_buff:
|
||||
kfree(cb_buff);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
|
||||
|
||||
int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
return iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
&cb_buff->buffer,
|
||||
NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
|
||||
|
||||
void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
iio_update_buffers(cb_buff->channels[0].indio_dev,
|
||||
NULL,
|
||||
&cb_buff->buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
|
||||
|
||||
void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)
|
||||
{
|
||||
iio_channel_release_all(cb_buff->channels);
|
||||
iio_buffer_put(&cb_buff->buffer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_release_all_cb);
|
||||
|
||||
struct iio_channel
|
||||
*iio_channel_cb_get_channels(const struct iio_cb_buffer *cb_buffer)
|
||||
{
|
||||
return cb_buffer->channels;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
|
||||
6
drivers/iio/common/Kconfig
Normal file
6
drivers/iio/common/Kconfig
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#
|
||||
# IIO common modules
|
||||
#
|
||||
|
||||
source "drivers/iio/common/hid-sensors/Kconfig"
|
||||
source "drivers/iio/common/st_sensors/Kconfig"
|
||||
11
drivers/iio/common/Makefile
Normal file
11
drivers/iio/common/Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#
|
||||
# Makefile for the IIO common modules.
|
||||
# Common modules contains modules, which can be shared among multiple
|
||||
# IIO modules. For example if the trigger processing is common for
|
||||
# multiple IIO modules then this can be moved to a common module
|
||||
# instead of duplicating in each module.
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-y += hid-sensors/
|
||||
obj-y += st_sensors/
|
||||
28
drivers/iio/common/hid-sensors/Kconfig
Normal file
28
drivers/iio/common/hid-sensors/Kconfig
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#
|
||||
# Hid Sensor common modules
|
||||
#
|
||||
menu "Hid Sensor IIO Common"
|
||||
|
||||
config HID_SENSOR_IIO_COMMON
|
||||
tristate "Common modules for all HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_HUB
|
||||
select HID_SENSOR_IIO_TRIGGER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for HID sensor to use
|
||||
HID sensor common processing for attributes and IIO triggers.
|
||||
There are many attributes which can be shared among multiple
|
||||
HID sensor drivers, this module contains processing for those
|
||||
attributes.
|
||||
|
||||
config HID_SENSOR_IIO_TRIGGER
|
||||
tristate "Common module (trigger) for all HID Sensor IIO drivers"
|
||||
depends on HID_SENSOR_HUB && HID_SENSOR_IIO_COMMON
|
||||
select IIO_TRIGGER
|
||||
help
|
||||
Say yes here to build trigger support for HID sensors.
|
||||
Triggers will be send if all requested attributes were read.
|
||||
|
||||
If this driver is compiled as a module, it will be named
|
||||
hid-sensor-trigger.
|
||||
|
||||
endmenu
|
||||
7
drivers/iio/common/hid-sensors/Makefile
Normal file
7
drivers/iio/common/hid-sensors/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Makefile for the Hid sensor common modules.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
|
||||
obj-$(CONFIG_HID_SENSOR_IIO_TRIGGER) += hid-sensor-trigger.o
|
||||
hid-sensor-iio-common-y := hid-sensor-attributes.o
|
||||
397
drivers/iio/common/hid-sensors/hid-sensor-attributes.c
Normal file
397
drivers/iio/common/hid-sensors/hid-sensor-attributes.c
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
static struct {
|
||||
u32 usage_id;
|
||||
int unit; /* 0 for default others from HID sensor spec */
|
||||
int scale_val0; /* scale, whole number */
|
||||
int scale_val1; /* scale, fraction in micros */
|
||||
} unit_conversion[] = {
|
||||
{HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650},
|
||||
{HID_USAGE_SENSOR_ACCEL_3D,
|
||||
HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0},
|
||||
{HID_USAGE_SENSOR_ACCEL_3D,
|
||||
HID_USAGE_SENSOR_UNITS_G, 9, 806650},
|
||||
|
||||
{HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453},
|
||||
{HID_USAGE_SENSOR_GYRO_3D,
|
||||
HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0},
|
||||
{HID_USAGE_SENSOR_GYRO_3D,
|
||||
HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453},
|
||||
|
||||
{HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000},
|
||||
{HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453},
|
||||
{HID_USAGE_SENSOR_INCLINOMETER_3D,
|
||||
HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453},
|
||||
{HID_USAGE_SENSOR_INCLINOMETER_3D,
|
||||
HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_ALS, 0, 1, 0},
|
||||
{HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0},
|
||||
|
||||
{HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0},
|
||||
{HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0},
|
||||
};
|
||||
|
||||
static int pow_10(unsigned power)
|
||||
{
|
||||
int i;
|
||||
int ret = 1;
|
||||
for (i = 0; i < power; ++i)
|
||||
ret = ret * 10;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void simple_div(int dividend, int divisor, int *whole,
|
||||
int *micro_frac)
|
||||
{
|
||||
int rem;
|
||||
int exp = 0;
|
||||
|
||||
*micro_frac = 0;
|
||||
if (divisor == 0) {
|
||||
*whole = 0;
|
||||
return;
|
||||
}
|
||||
*whole = dividend/divisor;
|
||||
rem = dividend % divisor;
|
||||
if (rem) {
|
||||
while (rem <= divisor) {
|
||||
rem *= 10;
|
||||
exp++;
|
||||
}
|
||||
*micro_frac = (rem / divisor) * pow_10(6-exp);
|
||||
}
|
||||
}
|
||||
|
||||
static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
|
||||
{
|
||||
*val1 = no/pow_10(exp);
|
||||
*val2 = no%pow_10(exp) * pow_10(6-exp);
|
||||
}
|
||||
|
||||
/*
|
||||
VTF format uses exponent and variable size format.
|
||||
For example if the size is 2 bytes
|
||||
0x0067 with VTF16E14 format -> +1.03
|
||||
To convert just change to 0x67 to decimal and use two decimal as E14 stands
|
||||
for 10^-2.
|
||||
Negative numbers are 2's complement
|
||||
*/
|
||||
static void convert_from_vtf_format(u32 value, int size, int exp,
|
||||
int *val1, int *val2)
|
||||
{
|
||||
int sign = 1;
|
||||
|
||||
if (value & BIT(size*8 - 1)) {
|
||||
value = ((1LL << (size * 8)) - value);
|
||||
sign = -1;
|
||||
}
|
||||
exp = hid_sensor_convert_exponent(exp);
|
||||
if (exp >= 0) {
|
||||
*val1 = sign * value * pow_10(exp);
|
||||
*val2 = 0;
|
||||
} else {
|
||||
split_micro_fraction(value, -exp, val1, val2);
|
||||
if (*val1)
|
||||
*val1 = sign * (*val1);
|
||||
else
|
||||
*val2 = sign * (*val2);
|
||||
}
|
||||
}
|
||||
|
||||
static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
|
||||
{
|
||||
u32 value;
|
||||
int sign = 1;
|
||||
|
||||
if (val1 < 0 || val2 < 0)
|
||||
sign = -1;
|
||||
exp = hid_sensor_convert_exponent(exp);
|
||||
if (exp < 0) {
|
||||
value = abs(val1) * pow_10(-exp);
|
||||
value += abs(val2) / pow_10(6+exp);
|
||||
} else
|
||||
value = abs(val1) / pow_10(exp);
|
||||
if (sign < 0)
|
||||
value = ((1LL << (size * 8)) - value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)
|
||||
{
|
||||
s32 value = 0;
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_get_feature(st->hsdev,
|
||||
st->poll.report_id,
|
||||
st->poll.index, &value);
|
||||
|
||||
if (ret < 0 || value < 0) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
|
||||
value = value * 1000;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_read_poll_value);
|
||||
|
||||
int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
|
||||
int *val1, int *val2)
|
||||
{
|
||||
s32 value;
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_get_feature(st->hsdev,
|
||||
st->poll.report_id,
|
||||
st->poll.index, &value);
|
||||
if (ret < 0 || value < 0) {
|
||||
*val1 = *val2 = 0;
|
||||
return -EINVAL;
|
||||
} else {
|
||||
if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
|
||||
simple_div(1000, value, val1, val2);
|
||||
else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
|
||||
simple_div(1, value, val1, val2);
|
||||
else {
|
||||
*val1 = *val2 = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_read_samp_freq_value);
|
||||
|
||||
int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
|
||||
int val1, int val2)
|
||||
{
|
||||
s32 value;
|
||||
int ret;
|
||||
|
||||
if (val1 < 0 || val2 < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
value = val1 * pow_10(6) + val2;
|
||||
if (value) {
|
||||
if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
|
||||
value = pow_10(9)/value;
|
||||
else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
|
||||
value = pow_10(6)/value;
|
||||
else
|
||||
value = 0;
|
||||
}
|
||||
ret = sensor_hub_set_feature(st->hsdev,
|
||||
st->poll.report_id,
|
||||
st->poll.index, value);
|
||||
if (ret < 0 || value < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
|
||||
|
||||
int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
|
||||
int *val1, int *val2)
|
||||
{
|
||||
s32 value;
|
||||
int ret;
|
||||
|
||||
ret = sensor_hub_get_feature(st->hsdev,
|
||||
st->sensitivity.report_id,
|
||||
st->sensitivity.index, &value);
|
||||
if (ret < 0 || value < 0) {
|
||||
*val1 = *val2 = 0;
|
||||
return -EINVAL;
|
||||
} else {
|
||||
convert_from_vtf_format(value, st->sensitivity.size,
|
||||
st->sensitivity.unit_expo,
|
||||
val1, val2);
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value);
|
||||
|
||||
int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
|
||||
int val1, int val2)
|
||||
{
|
||||
s32 value;
|
||||
int ret;
|
||||
|
||||
value = convert_to_vtf_format(st->sensitivity.size,
|
||||
st->sensitivity.unit_expo,
|
||||
val1, val2);
|
||||
ret = sensor_hub_set_feature(st->hsdev,
|
||||
st->sensitivity.report_id,
|
||||
st->sensitivity.index, value);
|
||||
if (ret < 0 || value < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
|
||||
|
||||
/*
|
||||
* This fuction applies the unit exponent to the scale.
|
||||
* For example:
|
||||
* 9.806650 ->exp:2-> val0[980]val1[665000]
|
||||
* 9.000806 ->exp:2-> val0[900]val1[80600]
|
||||
* 0.174535 ->exp:2-> val0[17]val1[453500]
|
||||
* 1.001745 ->exp:0-> val0[1]val1[1745]
|
||||
* 1.001745 ->exp:2-> val0[100]val1[174500]
|
||||
* 1.001745 ->exp:4-> val0[10017]val1[450000]
|
||||
* 9.806650 ->exp:-2-> val0[0]val1[98066]
|
||||
*/
|
||||
static void adjust_exponent_micro(int *val0, int *val1, int scale0,
|
||||
int scale1, int exp)
|
||||
{
|
||||
int i;
|
||||
int x;
|
||||
int res;
|
||||
int rem;
|
||||
|
||||
if (exp > 0) {
|
||||
*val0 = scale0 * pow_10(exp);
|
||||
res = 0;
|
||||
if (exp > 6) {
|
||||
*val1 = 0;
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < exp; ++i) {
|
||||
x = scale1 / pow_10(5 - i);
|
||||
res += (pow_10(exp - 1 - i) * x);
|
||||
scale1 = scale1 % pow_10(5 - i);
|
||||
}
|
||||
*val0 += res;
|
||||
*val1 = scale1 * pow_10(exp);
|
||||
} else if (exp < 0) {
|
||||
exp = abs(exp);
|
||||
if (exp > 6) {
|
||||
*val0 = *val1 = 0;
|
||||
return;
|
||||
}
|
||||
*val0 = scale0 / pow_10(exp);
|
||||
rem = scale0 % pow_10(exp);
|
||||
res = 0;
|
||||
for (i = 0; i < (6 - exp); ++i) {
|
||||
x = scale1 / pow_10(5 - i);
|
||||
res += (pow_10(5 - exp - i) * x);
|
||||
scale1 = scale1 % pow_10(5 - i);
|
||||
}
|
||||
*val1 = rem * pow_10(6 - exp) + res;
|
||||
} else {
|
||||
*val0 = scale0;
|
||||
*val1 = scale1;
|
||||
}
|
||||
}
|
||||
|
||||
int hid_sensor_format_scale(u32 usage_id,
|
||||
struct hid_sensor_hub_attribute_info *attr_info,
|
||||
int *val0, int *val1)
|
||||
{
|
||||
int i;
|
||||
int exp;
|
||||
|
||||
*val0 = 1;
|
||||
*val1 = 0;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) {
|
||||
if (unit_conversion[i].usage_id == usage_id &&
|
||||
unit_conversion[i].unit == attr_info->units) {
|
||||
exp = hid_sensor_convert_exponent(
|
||||
attr_info->unit_expo);
|
||||
adjust_exponent_micro(val0, val1,
|
||||
unit_conversion[i].scale_val0,
|
||||
unit_conversion[i].scale_val1, exp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_format_scale);
|
||||
|
||||
static
|
||||
int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_common *st)
|
||||
{
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
|
||||
&st->poll);
|
||||
/* Default unit of measure is milliseconds */
|
||||
if (st->poll.units == 0)
|
||||
st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
|
||||
u32 usage_id,
|
||||
struct hid_sensor_common *st)
|
||||
{
|
||||
|
||||
|
||||
hid_sensor_get_reporting_interval(hsdev, usage_id, st);
|
||||
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_PROP_REPORT_STATE,
|
||||
&st->report_state);
|
||||
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_PROY_POWER_STATE,
|
||||
&st->power_state);
|
||||
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
|
||||
&st->sensitivity);
|
||||
|
||||
hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
|
||||
st->poll.index, st->poll.report_id,
|
||||
st->report_state.index, st->report_state.report_id,
|
||||
st->power_state.index, st->power_state.report_id,
|
||||
st->sensitivity.index, st->sensitivity.report_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_parse_common_attributes);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_DESCRIPTION("HID Sensor common attribute processing");
|
||||
MODULE_LICENSE("GPL");
|
||||
139
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
Normal file
139
drivers/iio/common/hid-sensors/hid-sensor-trigger.c
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include "hid-sensor-trigger.h"
|
||||
|
||||
int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
|
||||
{
|
||||
int state_val;
|
||||
int report_val;
|
||||
|
||||
if (state) {
|
||||
if (sensor_hub_device_open(st->hsdev))
|
||||
return -EIO;
|
||||
|
||||
atomic_inc(&st->data_ready);
|
||||
|
||||
state_val = hid_sensor_get_usage_index(st->hsdev,
|
||||
st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);
|
||||
report_val = hid_sensor_get_usage_index(st->hsdev,
|
||||
st->report_state.report_id,
|
||||
st->report_state.index,
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
|
||||
} else {
|
||||
if (!atomic_dec_and_test(&st->data_ready))
|
||||
return 0;
|
||||
sensor_hub_device_close(st->hsdev);
|
||||
state_val = hid_sensor_get_usage_index(st->hsdev,
|
||||
st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM);
|
||||
report_val = hid_sensor_get_usage_index(st->hsdev,
|
||||
st->report_state.report_id,
|
||||
st->report_state.index,
|
||||
HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
|
||||
}
|
||||
|
||||
if (state_val >= 0) {
|
||||
state_val += st->power_state.logical_minimum;
|
||||
sensor_hub_set_feature(st->hsdev, st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
(s32)state_val);
|
||||
}
|
||||
|
||||
if (report_val >= 0) {
|
||||
report_val += st->report_state.logical_minimum;
|
||||
sensor_hub_set_feature(st->hsdev, st->report_state.report_id,
|
||||
st->report_state.index,
|
||||
(s32)report_val);
|
||||
}
|
||||
|
||||
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
|
||||
st->power_state.index,
|
||||
&state_val);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_power_state);
|
||||
|
||||
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state);
|
||||
}
|
||||
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
|
||||
{
|
||||
iio_trigger_unregister(attrb->trigger);
|
||||
iio_trigger_free(attrb->trigger);
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_remove_trigger);
|
||||
|
||||
static const struct iio_trigger_ops hid_sensor_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &hid_sensor_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
struct hid_sensor_common *attrb)
|
||||
{
|
||||
int ret;
|
||||
struct iio_trigger *trig;
|
||||
|
||||
trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
|
||||
if (trig == NULL) {
|
||||
dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
|
||||
ret = -ENOMEM;
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
trig->dev.parent = indio_dev->dev.parent;
|
||||
iio_trigger_set_drvdata(trig, attrb);
|
||||
trig->ops = &hid_sensor_trigger_ops;
|
||||
ret = iio_trigger_register(trig);
|
||||
|
||||
if (ret) {
|
||||
dev_err(&indio_dev->dev, "Trigger Register Failed\n");
|
||||
goto error_free_trig;
|
||||
}
|
||||
attrb->trigger = trig;
|
||||
indio_dev->trig = iio_trigger_get(trig);
|
||||
|
||||
return ret;
|
||||
|
||||
error_free_trig:
|
||||
iio_trigger_free(trig);
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(hid_sensor_setup_trigger);
|
||||
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_DESCRIPTION("HID Sensor trigger processing");
|
||||
MODULE_LICENSE("GPL");
|
||||
27
drivers/iio/common/hid-sensors/hid-sensor-trigger.h
Normal file
27
drivers/iio/common/hid-sensors/hid-sensor-trigger.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#ifndef _HID_SENSOR_TRIGGER_H
|
||||
#define _HID_SENSOR_TRIGGER_H
|
||||
|
||||
int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
|
||||
struct hid_sensor_common *attrb);
|
||||
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb);
|
||||
int hid_sensor_power_state(struct hid_sensor_common *st, bool state);
|
||||
|
||||
#endif
|
||||
14
drivers/iio/common/st_sensors/Kconfig
Normal file
14
drivers/iio/common/st_sensors/Kconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# STMicroelectronics sensors common library
|
||||
#
|
||||
|
||||
config IIO_ST_SENSORS_I2C
|
||||
tristate
|
||||
|
||||
config IIO_ST_SENSORS_SPI
|
||||
tristate
|
||||
|
||||
config IIO_ST_SENSORS_CORE
|
||||
tristate
|
||||
select IIO_ST_SENSORS_I2C if I2C
|
||||
select IIO_ST_SENSORS_SPI if SPI_MASTER
|
||||
10
drivers/iio/common/st_sensors/Makefile
Normal file
10
drivers/iio/common/st_sensors/Makefile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# Makefile for the STMicroelectronics sensor common modules.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_I2C) += st_sensors_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_SPI) += st_sensors_spi.o
|
||||
obj-$(CONFIG_IIO_ST_SENSORS_CORE) += st_sensors.o
|
||||
st_sensors-y := st_sensors_core.o
|
||||
st_sensors-$(CONFIG_IIO_BUFFER) += st_sensors_buffer.o
|
||||
st_sensors-$(CONFIG_IIO_TRIGGER) += st_sensors_trigger.o
|
||||
128
drivers/iio/common/st_sensors/st_sensors_buffer.c
Normal file
128
drivers/iio/common/st_sensors/st_sensors_buffer.c
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* STMicroelectronics sensors buffer library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/irqreturn.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
|
||||
{
|
||||
u8 *addr;
|
||||
int i, n = 0, len;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned int num_data_channels = sdata->num_data_channels;
|
||||
unsigned int byte_for_channel =
|
||||
indio_dev->channels[0].scan_type.storagebits >> 3;
|
||||
|
||||
addr = kmalloc(num_data_channels, GFP_KERNEL);
|
||||
if (!addr) {
|
||||
len = -ENOMEM;
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_data_channels; i++) {
|
||||
if (test_bit(i, indio_dev->active_scan_mask)) {
|
||||
addr[n] = indio_dev->channels[i].address;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
switch (n) {
|
||||
case 1:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], byte_for_channel, buf, sdata->multiread_bit);
|
||||
break;
|
||||
case 2:
|
||||
if ((addr[1] - addr[0]) == byte_for_channel) {
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0], byte_for_channel * n,
|
||||
buf, sdata->multiread_bit);
|
||||
} else {
|
||||
u8 *rx_array;
|
||||
rx_array = kmalloc(byte_for_channel * num_data_channels,
|
||||
GFP_KERNEL);
|
||||
if (!rx_array) {
|
||||
len = -ENOMEM;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb,
|
||||
sdata->dev, addr[0],
|
||||
byte_for_channel * num_data_channels,
|
||||
rx_array, sdata->multiread_bit);
|
||||
if (len < 0) {
|
||||
kfree(rx_array);
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
for (i = 0; i < n * byte_for_channel; i++) {
|
||||
if (i < n)
|
||||
buf[i] = rx_array[i];
|
||||
else
|
||||
buf[i] = rx_array[n + i];
|
||||
}
|
||||
kfree(rx_array);
|
||||
len = byte_for_channel * n;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
len = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
addr[0], byte_for_channel * num_data_channels,
|
||||
buf, sdata->multiread_bit);
|
||||
break;
|
||||
default:
|
||||
len = -EINVAL;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
if (len != byte_for_channel * n) {
|
||||
len = -EIO;
|
||||
goto st_sensors_free_memory;
|
||||
}
|
||||
|
||||
st_sensors_free_memory:
|
||||
kfree(addr);
|
||||
st_sensors_get_buffer_element_error:
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_get_buffer_element);
|
||||
|
||||
irqreturn_t st_sensors_trigger_handler(int irq, void *p)
|
||||
{
|
||||
int len;
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
len = st_sensors_get_buffer_element(indio_dev, sdata->buffer_data);
|
||||
if (len < 0)
|
||||
goto st_sensors_get_buffer_element_error;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, sdata->buffer_data,
|
||||
pf->timestamp);
|
||||
|
||||
st_sensors_get_buffer_element_error:
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_trigger_handler);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors buffer");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
548
drivers/iio/common/st_sensors/st_sensors_core.c
Normal file
548
drivers/iio/common/st_sensors/st_sensors_core.c
Normal file
|
|
@ -0,0 +1,548 @@
|
|||
/*
|
||||
* STMicroelectronics sensors core library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_WAI_ADDRESS 0x0f
|
||||
|
||||
static inline u32 st_sensors_get_unaligned_le24(const u8 *p)
|
||||
{
|
||||
return (s32)((p[0] | p[1] << 8 | p[2] << 16) << 8) >> 8;
|
||||
}
|
||||
|
||||
static int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
|
||||
u8 reg_addr, u8 mask, u8 data)
|
||||
{
|
||||
int err;
|
||||
u8 new_data;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
|
||||
if (err < 0)
|
||||
goto st_sensors_write_data_with_mask_error;
|
||||
|
||||
new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
|
||||
err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
|
||||
|
||||
st_sensors_write_data_with_mask_error:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_match_odr(struct st_sensors *sensor,
|
||||
unsigned int odr, struct st_sensor_odr_avl *odr_out)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sensor->odr.odr_avl[i].hz == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->odr.odr_avl[i].hz == odr) {
|
||||
odr_out->hz = sensor->odr.odr_avl[i].hz;
|
||||
odr_out->value = sensor->odr.odr_avl[i].value;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_odr_avl odr_out = {0, 0};
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_odr(sdata->sensor, odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
if (sdata->enabled == true) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr,
|
||||
sdata->sensor->odr.mask,
|
||||
odr_out.value);
|
||||
} else {
|
||||
err = 0;
|
||||
}
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->odr.addr, sdata->sensor->odr.mask,
|
||||
odr_out.value);
|
||||
}
|
||||
if (err >= 0)
|
||||
sdata->odr = odr_out.hz;
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_odr);
|
||||
|
||||
static int st_sensors_match_fs(struct st_sensors *sensor,
|
||||
unsigned int fs, int *index_fs_avl)
|
||||
{
|
||||
int i, ret = -EINVAL;
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sensor->fs.fs_avl[i].num == 0)
|
||||
goto st_sensors_match_odr_error;
|
||||
|
||||
if (sensor->fs.fs_avl[i].num == fs) {
|
||||
*index_fs_avl = i;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
st_sensors_match_odr_error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
|
||||
unsigned int fs)
|
||||
{
|
||||
int err, i = 0;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = st_sensors_match_fs(sdata->sensor, fs, &i);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->fs.addr,
|
||||
sdata->sensor->fs.mask,
|
||||
sdata->sensor->fs.fs_avl[i].value);
|
||||
if (err < 0)
|
||||
goto st_accel_set_fullscale_error;
|
||||
|
||||
sdata->current_fullscale = (struct st_sensor_fullscale_avl *)
|
||||
&sdata->sensor->fs.fs_avl[i];
|
||||
return err;
|
||||
|
||||
st_accel_set_fullscale_error:
|
||||
dev_err(&indio_dev->dev, "failed to set new fullscale.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
u8 tmp_value;
|
||||
int err = -EINVAL;
|
||||
bool found = false;
|
||||
struct st_sensor_odr_avl odr_out = {0, 0};
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
if (enable) {
|
||||
tmp_value = sdata->sensor->pw.value_on;
|
||||
if ((sdata->sensor->odr.addr == sdata->sensor->pw.addr) &&
|
||||
(sdata->sensor->odr.mask == sdata->sensor->pw.mask)) {
|
||||
err = st_sensors_match_odr(sdata->sensor,
|
||||
sdata->odr, &odr_out);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
tmp_value = odr_out.value;
|
||||
found = true;
|
||||
}
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask, tmp_value);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
sdata->enabled = true;
|
||||
|
||||
if (found)
|
||||
sdata->odr = odr_out.hz;
|
||||
} else {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->pw.addr,
|
||||
sdata->sensor->pw.mask,
|
||||
sdata->sensor->pw.value_off);
|
||||
if (err < 0)
|
||||
goto set_enable_error;
|
||||
|
||||
sdata->enabled = false;
|
||||
}
|
||||
|
||||
set_enable_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_enable);
|
||||
|
||||
int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->enable_axis.addr,
|
||||
sdata->sensor->enable_axis.mask, axis_enable);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_axis_enable);
|
||||
|
||||
void st_sensors_power_enable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
int err;
|
||||
|
||||
/* Regulators not mandatory, but if requested we should enable them. */
|
||||
pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd");
|
||||
if (!IS_ERR(pdata->vdd)) {
|
||||
err = regulator_enable(pdata->vdd);
|
||||
if (err != 0)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd supply\n");
|
||||
}
|
||||
|
||||
pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio");
|
||||
if (!IS_ERR(pdata->vdd_io)) {
|
||||
err = regulator_enable(pdata->vdd_io);
|
||||
if (err != 0)
|
||||
dev_warn(&indio_dev->dev,
|
||||
"Failed to enable specified Vdd_IO supply\n");
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_enable);
|
||||
|
||||
void st_sensors_power_disable(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *pdata = iio_priv(indio_dev);
|
||||
|
||||
if (!IS_ERR(pdata->vdd))
|
||||
regulator_disable(pdata->vdd);
|
||||
|
||||
if (!IS_ERR(pdata->vdd_io))
|
||||
regulator_disable(pdata->vdd_io);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_power_disable);
|
||||
|
||||
static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
switch (pdata->drdy_int_pin) {
|
||||
case 1:
|
||||
if (sdata->sensor->drdy_irq.mask_int1 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT1 not available.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
sdata->drdy_int_pin = 1;
|
||||
break;
|
||||
case 2:
|
||||
if (sdata->sensor->drdy_irq.mask_int2 == 0) {
|
||||
dev_err(&indio_dev->dev,
|
||||
"DRDY on INT2 not available.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
sdata->drdy_int_pin = 2;
|
||||
break;
|
||||
default:
|
||||
dev_err(&indio_dev->dev, "DRDY on pdata not valid.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
struct st_sensors_platform_data *defdata)
|
||||
{
|
||||
struct st_sensors_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
u32 val;
|
||||
|
||||
if (!np)
|
||||
return NULL;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!of_property_read_u32(np, "st,drdy-int-pin", &val) && (val <= 2))
|
||||
pdata->drdy_int_pin = (u8) val;
|
||||
else
|
||||
pdata->drdy_int_pin = defdata ? defdata->drdy_int_pin : 1;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct st_sensors_platform_data *st_sensors_of_probe(struct device *dev,
|
||||
struct st_sensors_platform_data *defdata)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
int st_sensors_init_sensor(struct iio_dev *indio_dev,
|
||||
struct st_sensors_platform_data *pdata)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
struct st_sensors_platform_data *of_pdata;
|
||||
int err = 0;
|
||||
|
||||
mutex_init(&sdata->tb.buf_lock);
|
||||
|
||||
/* If OF/DT pdata exists, it will take precedence of anything else */
|
||||
of_pdata = st_sensors_of_probe(indio_dev->dev.parent, pdata);
|
||||
if (of_pdata)
|
||||
pdata = of_pdata;
|
||||
|
||||
if (pdata) {
|
||||
err = st_sensors_set_drdy_int_pin(indio_dev, pdata);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (sdata->current_fullscale) {
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->current_fullscale->num);
|
||||
if (err < 0)
|
||||
return err;
|
||||
} else
|
||||
dev_info(&indio_dev->dev, "Full-scale not possible\n");
|
||||
|
||||
err = st_sensors_set_odr(indio_dev, sdata->odr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* set BDU */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_init_sensor);
|
||||
|
||||
int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
|
||||
{
|
||||
int err;
|
||||
u8 drdy_mask;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
if (!sdata->sensor->drdy_irq.addr)
|
||||
return 0;
|
||||
|
||||
/* Enable/Disable the interrupt generator 1. */
|
||||
if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.ig1.en_addr,
|
||||
sdata->sensor->drdy_irq.ig1.en_mask, (int)enable);
|
||||
if (err < 0)
|
||||
goto st_accel_set_dataready_irq_error;
|
||||
}
|
||||
|
||||
if (sdata->drdy_int_pin == 1)
|
||||
drdy_mask = sdata->sensor->drdy_irq.mask_int1;
|
||||
else
|
||||
drdy_mask = sdata->sensor->drdy_irq.mask_int2;
|
||||
|
||||
/* Enable/Disable the interrupt generator for data ready. */
|
||||
err = st_sensors_write_data_with_mask(indio_dev,
|
||||
sdata->sensor->drdy_irq.addr, drdy_mask, (int)enable);
|
||||
|
||||
st_accel_set_dataready_irq_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_dataready_irq);
|
||||
|
||||
int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
|
||||
{
|
||||
int err = -EINVAL, i;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if ((sdata->sensor->fs.fs_avl[i].gain == scale) &&
|
||||
(sdata->sensor->fs.fs_avl[i].gain != 0)) {
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (err < 0)
|
||||
goto st_sensors_match_scale_error;
|
||||
|
||||
err = st_sensors_set_fullscale(indio_dev,
|
||||
sdata->sensor->fs.fs_avl[i].num);
|
||||
|
||||
st_sensors_match_scale_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
|
||||
|
||||
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *data)
|
||||
{
|
||||
int err;
|
||||
u8 *outdata;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
unsigned int byte_for_channel = ch->scan_type.storagebits >> 3;
|
||||
|
||||
outdata = kmalloc(byte_for_channel, GFP_KERNEL);
|
||||
if (!outdata)
|
||||
return -ENOMEM;
|
||||
|
||||
err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
|
||||
ch->address, byte_for_channel,
|
||||
outdata, sdata->multiread_bit);
|
||||
if (err < 0)
|
||||
goto st_sensors_free_memory;
|
||||
|
||||
if (byte_for_channel == 2)
|
||||
*data = (s16)get_unaligned_le16(outdata);
|
||||
else if (byte_for_channel == 3)
|
||||
*data = (s32)st_sensors_get_unaligned_le24(outdata);
|
||||
|
||||
st_sensors_free_memory:
|
||||
kfree(outdata);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int st_sensors_read_info_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *ch, int *val)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
|
||||
err = -EBUSY;
|
||||
goto out;
|
||||
} else {
|
||||
err = st_sensors_set_enable(indio_dev, true);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
msleep((sdata->sensor->bootime * 1000) / sdata->odr);
|
||||
err = st_sensors_read_axis_data(indio_dev, ch, val);
|
||||
if (err < 0)
|
||||
goto out;
|
||||
|
||||
*val = *val >> ch->scan_type.shift;
|
||||
|
||||
err = st_sensors_set_enable(indio_dev, false);
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_read_info_raw);
|
||||
|
||||
int st_sensors_check_device_support(struct iio_dev *indio_dev,
|
||||
int num_sensors_list, const struct st_sensors *sensors)
|
||||
{
|
||||
u8 wai;
|
||||
int i, n, err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
|
||||
ST_SENSORS_DEFAULT_WAI_ADDRESS, &wai);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to read Who-Am-I register.\n");
|
||||
goto read_wai_error;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_sensors_list; i++) {
|
||||
if (sensors[i].wai == wai)
|
||||
break;
|
||||
}
|
||||
if (i == num_sensors_list)
|
||||
goto device_not_supported;
|
||||
|
||||
for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
|
||||
if (strcmp(indio_dev->name,
|
||||
&sensors[i].sensors_supported[n][0]) == 0)
|
||||
break;
|
||||
}
|
||||
if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
|
||||
dev_err(&indio_dev->dev, "device name and WhoAmI mismatch.\n");
|
||||
goto sensor_name_mismatch;
|
||||
}
|
||||
|
||||
sdata->sensor = (struct st_sensors *)&sensors[i];
|
||||
|
||||
return i;
|
||||
|
||||
device_not_supported:
|
||||
dev_err(&indio_dev->dev, "device not supported: WhoAmI (0x%x).\n", wai);
|
||||
sensor_name_mismatch:
|
||||
err = -ENODEV;
|
||||
read_wai_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_check_device_support);
|
||||
|
||||
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_ODR_LIST_MAX; i++) {
|
||||
if (sdata->sensor->odr.odr_avl[i].hz == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
|
||||
sdata->sensor->odr.odr_avl[i].hz);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_sampling_frequency_avail);
|
||||
|
||||
ssize_t st_sensors_sysfs_scale_avail(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int i, len = 0;
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
|
||||
if (sdata->sensor->fs.fs_avl[i].num == 0)
|
||||
break;
|
||||
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
|
||||
sdata->sensor->fs.fs_avl[i].gain);
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
buf[len - 1] = '\n';
|
||||
|
||||
return len;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_sysfs_scale_avail);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors core");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
111
drivers/iio/common/st_sensors/st_sensors_i2c.c
Normal file
111
drivers/iio/common/st_sensors/st_sensors_i2c.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* STMicroelectronics sensors i2c library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_i2c.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_I2C_MULTIREAD 0x80
|
||||
|
||||
static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_i2c_client(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
|
||||
if (err < 0)
|
||||
goto st_accel_i2c_read_byte_error;
|
||||
|
||||
*res_byte = err & 0xff;
|
||||
|
||||
st_accel_i2c_read_byte_error:
|
||||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
if (multiread_bit)
|
||||
reg_addr |= ST_SENSORS_I2C_MULTIREAD;
|
||||
|
||||
return i2c_smbus_read_i2c_block_data(to_i2c_client(dev),
|
||||
reg_addr, len, data);
|
||||
}
|
||||
|
||||
static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
|
||||
.read_byte = st_sensors_i2c_read_byte,
|
||||
.write_byte = st_sensors_i2c_write_byte,
|
||||
.read_multiple_byte = st_sensors_i2c_read_multiple_byte,
|
||||
};
|
||||
|
||||
void st_sensors_i2c_configure(struct iio_dev *indio_dev,
|
||||
struct i2c_client *client, struct st_sensor_data *sdata)
|
||||
{
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->name;
|
||||
|
||||
sdata->tf = &st_sensors_tf_i2c;
|
||||
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_i2c_configure);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/**
|
||||
* st_sensors_of_i2c_probe() - device tree probe for ST I2C sensors
|
||||
* @client: the I2C client device for the sensor
|
||||
* @match: the OF match table for the device, containing compatible strings
|
||||
* but also a .data field with the corresponding internal kernel name
|
||||
* used by this sensor.
|
||||
*
|
||||
* In effect this function matches a compatible string to an internal kernel
|
||||
* name for a certain sensor device, so that the rest of the autodetection can
|
||||
* rely on that name from this point on. I2C client devices will be renamed
|
||||
* to match the internal kernel convention.
|
||||
*/
|
||||
void st_sensors_of_i2c_probe(struct i2c_client *client,
|
||||
const struct of_device_id *match)
|
||||
{
|
||||
const struct of_device_id *of_id;
|
||||
|
||||
of_id = of_match_device(match, &client->dev);
|
||||
if (!of_id)
|
||||
return;
|
||||
|
||||
/* The name from the OF match takes precedence if present */
|
||||
strncpy(client->name, of_id->data, sizeof(client->name));
|
||||
client->name[sizeof(client->name) - 1] = '\0';
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_of_i2c_probe);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors i2c driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
121
drivers/iio/common/st_sensors/st_sensors_spi.c
Normal file
121
drivers/iio/common/st_sensors/st_sensors_spi.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* STMicroelectronics sensors spi library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors_spi.h>
|
||||
|
||||
|
||||
#define ST_SENSORS_SPI_MULTIREAD 0xc0
|
||||
#define ST_SENSORS_SPI_READ 0x80
|
||||
|
||||
static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
return to_spi_device(sdata->dev)->irq;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 1,
|
||||
},
|
||||
{
|
||||
.rx_buf = tb->rx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = len,
|
||||
}
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
if ((multiread_bit) && (len > 1))
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
|
||||
else
|
||||
tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
|
||||
if (err)
|
||||
goto acc_spi_read_error;
|
||||
|
||||
memcpy(data, tb->rx_buf, len*sizeof(u8));
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return len;
|
||||
|
||||
acc_spi_read_error:
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 *res_byte)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_read_multiple_byte(
|
||||
struct st_sensor_transfer_buffer *tb, struct device *dev,
|
||||
u8 reg_addr, int len, u8 *data, bool multiread_bit)
|
||||
{
|
||||
return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
|
||||
}
|
||||
|
||||
static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
|
||||
struct device *dev, u8 reg_addr, u8 data)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = tb->tx_buf,
|
||||
.bits_per_word = 8,
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
mutex_lock(&tb->buf_lock);
|
||||
tb->tx_buf[0] = reg_addr;
|
||||
tb->tx_buf[1] = data;
|
||||
|
||||
err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
|
||||
mutex_unlock(&tb->buf_lock);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct st_sensor_transfer_function st_sensors_tf_spi = {
|
||||
.read_byte = st_sensors_spi_read_byte,
|
||||
.write_byte = st_sensors_spi_write_byte,
|
||||
.read_multiple_byte = st_sensors_spi_read_multiple_byte,
|
||||
};
|
||||
|
||||
void st_sensors_spi_configure(struct iio_dev *indio_dev,
|
||||
struct spi_device *spi, struct st_sensor_data *sdata)
|
||||
{
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi->modalias;
|
||||
|
||||
sdata->tf = &st_sensors_tf_spi;
|
||||
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_spi_configure);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
77
drivers/iio/common/st_sensors/st_sensors_trigger.c
Normal file
77
drivers/iio/common/st_sensors/st_sensors_trigger.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* STMicroelectronics sensors trigger library driver
|
||||
*
|
||||
* Copyright 2012-2013 STMicroelectronics Inc.
|
||||
*
|
||||
* Denis Ciocca <denis.ciocca@st.com>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/common/st_sensors.h>
|
||||
|
||||
|
||||
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
|
||||
const struct iio_trigger_ops *trigger_ops)
|
||||
{
|
||||
int err;
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
|
||||
if (sdata->trig == NULL) {
|
||||
err = -ENOMEM;
|
||||
dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n");
|
||||
goto iio_trigger_alloc_error;
|
||||
}
|
||||
|
||||
err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
|
||||
iio_trigger_generic_data_rdy_poll,
|
||||
NULL,
|
||||
IRQF_TRIGGER_RISING,
|
||||
sdata->trig->name,
|
||||
sdata->trig);
|
||||
if (err)
|
||||
goto request_irq_error;
|
||||
|
||||
iio_trigger_set_drvdata(sdata->trig, indio_dev);
|
||||
sdata->trig->ops = trigger_ops;
|
||||
sdata->trig->dev.parent = sdata->dev;
|
||||
|
||||
err = iio_trigger_register(sdata->trig);
|
||||
if (err < 0) {
|
||||
dev_err(&indio_dev->dev, "failed to register iio trigger.\n");
|
||||
goto iio_trigger_register_error;
|
||||
}
|
||||
indio_dev->trig = iio_trigger_get(sdata->trig);
|
||||
|
||||
return 0;
|
||||
|
||||
iio_trigger_register_error:
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
request_irq_error:
|
||||
iio_trigger_free(sdata->trig);
|
||||
iio_trigger_alloc_error:
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_allocate_trigger);
|
||||
|
||||
void st_sensors_deallocate_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct st_sensor_data *sdata = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(sdata->trig);
|
||||
free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
|
||||
iio_trigger_free(sdata->trig);
|
||||
}
|
||||
EXPORT_SYMBOL(st_sensors_deallocate_trigger);
|
||||
|
||||
MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
|
||||
MODULE_DESCRIPTION("STMicroelectronics ST-sensors trigger");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
184
drivers/iio/dac/Kconfig
Normal file
184
drivers/iio/dac/Kconfig
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
#
|
||||
# DAC drivers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Digital to analog converters"
|
||||
|
||||
config AD5064
|
||||
tristate "Analog Devices AD5064 and similar multi-channel DAC driver"
|
||||
depends on (SPI_MASTER && I2C!=m) || I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5024, AD5025, AD5044,
|
||||
AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R, AD5648, AD5666, AD5668,
|
||||
AD5669R Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5064.
|
||||
|
||||
config AD5360
|
||||
tristate "Analog Devices AD5360/61/62/63/70/71/73 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5360, AD5361,
|
||||
AD5362, AD5363, AD5370, AD5371, AD5373 multi-channel
|
||||
Digital to Analog Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5360.
|
||||
|
||||
config AD5380
|
||||
tristate "Analog Devices AD5380/81/82/83/84/90/91/92 DAC driver"
|
||||
depends on (SPI_MASTER && I2C!=m) || I2C
|
||||
select REGMAP_I2C if I2C
|
||||
select REGMAP_SPI if SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5380, AD5381,
|
||||
AD5382, AD5383, AD5384, AD5390, AD5391, AD5392 multi-channel
|
||||
Digital to Analog Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5380.
|
||||
|
||||
config AD5421
|
||||
tristate "Analog Devices AD5421 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5421 loop-powered
|
||||
digital-to-analog convertors (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad5421.
|
||||
|
||||
config AD5446
|
||||
tristate "Analog Devices AD5446 and similar single channel DACs driver"
|
||||
depends on (SPI_MASTER && I2C!=m) || I2C
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
|
||||
AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
|
||||
AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
|
||||
AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5446.
|
||||
|
||||
config AD5449
|
||||
tristate "Analog Devices AD5449 and similar DACs driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5415, AD5426, AD5429,
|
||||
AD5432, AD5439, AD5443, AD5449 Digital to Analog Converters.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5449.
|
||||
|
||||
config AD5504
|
||||
tristate "Analog Devices AD5504/AD5501 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5504, AD5501,
|
||||
High Voltage Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5504.
|
||||
|
||||
config AD5624R_SPI
|
||||
tristate "Analog Devices AD5624/44/64R DAC spi driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5624R, AD5644R and
|
||||
AD5664R converters (DAC). This driver uses the common SPI interface.
|
||||
|
||||
config AD5686
|
||||
tristate "Analog Devices AD5686R/AD5685R/AD5684R DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5686R, AD5685R,
|
||||
AD5684R, AD5791 Voltage Output Digital to
|
||||
Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5686.
|
||||
|
||||
config AD5755
|
||||
tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5755, AD5755-1,
|
||||
AD5757, AD5735, AD5737 quad channel Digital to
|
||||
Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5755.
|
||||
|
||||
config AD5764
|
||||
tristate "Analog Devices AD5764/64R/44/44R DAC driver"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5764, AD5764R, AD5744,
|
||||
AD5744R Digital to Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5764.
|
||||
|
||||
config AD5791
|
||||
tristate "Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD5760, AD5780,
|
||||
AD5781, AD5790, AD5791 High Resolution Voltage Output Digital to
|
||||
Analog Converter.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad5791.
|
||||
|
||||
config AD7303
|
||||
tristate "Analog Devices AD7303 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD7303 Digital to Analog
|
||||
Converters (DAC).
|
||||
|
||||
To compile this driver as module choose M here: the module will be called
|
||||
ad7303.
|
||||
|
||||
config MAX517
|
||||
tristate "Maxim MAX517/518/519 DAC driver"
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for the Maxim chips MAX517,
|
||||
MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs).
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called max517.
|
||||
|
||||
config MAX5821
|
||||
tristate "Maxim MAX5821 DAC driver"
|
||||
depends on I2C
|
||||
depends on OF
|
||||
help
|
||||
Say yes here to build support for Maxim MAX5821
|
||||
10 bits DAC.
|
||||
|
||||
config MCP4725
|
||||
tristate "MCP4725 DAC driver"
|
||||
depends on I2C
|
||||
---help---
|
||||
Say Y here if you want to build a driver for the Microchip
|
||||
MCP 4725 12-bit digital-to-analog converter (DAC) with I2C
|
||||
interface.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4725.
|
||||
|
||||
config MCP4922
|
||||
tristate "MCP4902, MCP4912, MCP4922 DAC driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build the driver for the Microchip MCP4902
|
||||
MCP4912, and MCP4922 DAC devices.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called mcp4922.
|
||||
|
||||
endmenu
|
||||
22
drivers/iio/dac/Makefile
Normal file
22
drivers/iio/dac/Makefile
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# Makefile for industrial I/O DAC drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD5360) += ad5360.o
|
||||
obj-$(CONFIG_AD5380) += ad5380.o
|
||||
obj-$(CONFIG_AD5421) += ad5421.o
|
||||
obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
|
||||
obj-$(CONFIG_AD5064) += ad5064.o
|
||||
obj-$(CONFIG_AD5504) += ad5504.o
|
||||
obj-$(CONFIG_AD5446) += ad5446.o
|
||||
obj-$(CONFIG_AD5449) += ad5449.o
|
||||
obj-$(CONFIG_AD5755) += ad5755.o
|
||||
obj-$(CONFIG_AD5764) += ad5764.o
|
||||
obj-$(CONFIG_AD5791) += ad5791.o
|
||||
obj-$(CONFIG_AD5686) += ad5686.o
|
||||
obj-$(CONFIG_AD7303) += ad7303.o
|
||||
obj-$(CONFIG_MAX517) += max517.o
|
||||
obj-$(CONFIG_MAX5821) += max5821.o
|
||||
obj-$(CONFIG_MCP4725) += mcp4725.o
|
||||
obj-$(CONFIG_MCP4922) += mcp4922.o
|
||||
684
drivers/iio/dac/ad5064.c
Normal file
684
drivers/iio/dac/ad5064.c
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
/*
|
||||
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5628, AD5629R,
|
||||
* AD5648, AD5666, AD5668, AD5669R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5064_MAX_DAC_CHANNELS 8
|
||||
#define AD5064_MAX_VREFS 4
|
||||
|
||||
#define AD5064_ADDR(x) ((x) << 20)
|
||||
#define AD5064_CMD(x) ((x) << 24)
|
||||
|
||||
#define AD5064_ADDR_ALL_DAC 0xF
|
||||
|
||||
#define AD5064_CMD_WRITE_INPUT_N 0x0
|
||||
#define AD5064_CMD_UPDATE_DAC_N 0x1
|
||||
#define AD5064_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
#define AD5064_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5064_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5064_CMD_CLEAR 0x5
|
||||
#define AD5064_CMD_LDAC_MASK 0x6
|
||||
#define AD5064_CMD_RESET 0x7
|
||||
#define AD5064_CMD_CONFIG 0x8
|
||||
|
||||
#define AD5064_CONFIG_DAISY_CHAIN_ENABLE BIT(1)
|
||||
#define AD5064_CONFIG_INT_VREF_ENABLE BIT(0)
|
||||
|
||||
#define AD5064_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5064_LDAC_PWRDN_1K 0x1
|
||||
#define AD5064_LDAC_PWRDN_100K 0x2
|
||||
#define AD5064_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5064_chip_info - chip specific information
|
||||
* @shared_vref: whether the vref supply is shared between channels
|
||||
* @internal_vref: internal reference voltage. 0 if the chip has no internal
|
||||
* vref.
|
||||
* @channel: channel specification
|
||||
* @num_channels: number of channels
|
||||
*/
|
||||
|
||||
struct ad5064_chip_info {
|
||||
bool shared_vref;
|
||||
unsigned long internal_vref;
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
};
|
||||
|
||||
struct ad5064_state;
|
||||
|
||||
typedef int (*ad5064_write_func)(struct ad5064_state *st, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val);
|
||||
|
||||
/**
|
||||
* struct ad5064_state - driver instance specific data
|
||||
* @dev: the device for this driver instance
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulators
|
||||
* @pwr_down: whether channel is powered down
|
||||
* @pwr_down_mode: channel's current power down mode
|
||||
* @dac_cache: current DAC raw value (chip does not support readback)
|
||||
* @use_internal_vref: set to true if the internal reference voltage should be
|
||||
* used.
|
||||
* @write: register write callback
|
||||
* @data: i2c/spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5064_state {
|
||||
struct device *dev;
|
||||
const struct ad5064_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[AD5064_MAX_VREFS];
|
||||
bool pwr_down[AD5064_MAX_DAC_CHANNELS];
|
||||
u8 pwr_down_mode[AD5064_MAX_DAC_CHANNELS];
|
||||
unsigned int dac_cache[AD5064_MAX_DAC_CHANNELS];
|
||||
bool use_internal_vref;
|
||||
|
||||
ad5064_write_func write;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
u8 i2c[3];
|
||||
__be32 spi;
|
||||
} data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5064_type {
|
||||
ID_AD5024,
|
||||
ID_AD5025,
|
||||
ID_AD5044,
|
||||
ID_AD5045,
|
||||
ID_AD5064,
|
||||
ID_AD5064_1,
|
||||
ID_AD5065,
|
||||
ID_AD5628_1,
|
||||
ID_AD5628_2,
|
||||
ID_AD5648_1,
|
||||
ID_AD5648_2,
|
||||
ID_AD5666_1,
|
||||
ID_AD5666_2,
|
||||
ID_AD5668_1,
|
||||
ID_AD5668_2,
|
||||
};
|
||||
|
||||
static int ad5064_write(struct ad5064_state *st, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val, unsigned int shift)
|
||||
{
|
||||
val <<= shift;
|
||||
|
||||
return st->write(st, cmd, addr, val);
|
||||
}
|
||||
|
||||
static int ad5064_sync_powerdown_mode(struct ad5064_state *st,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
val = (0x1 << chan->address);
|
||||
|
||||
if (st->pwr_down[chan->channel])
|
||||
val |= st->pwr_down_mode[chan->channel] << 8;
|
||||
|
||||
ret = ad5064_write(st, AD5064_CMD_POWERDOWN_DAC, 0, val, 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const ad5064_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5064_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode[chan->channel] - 1;
|
||||
}
|
||||
|
||||
static int ad5064_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down_mode[chan->channel] = mode + 1;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5064_powerdown_mode_enum = {
|
||||
.items = ad5064_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5064_powerdown_modes),
|
||||
.get = ad5064_get_powerdown_mode,
|
||||
.set = ad5064_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down[chan->channel]);
|
||||
}
|
||||
|
||||
static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down[chan->channel] = pwr_down;
|
||||
|
||||
ret = ad5064_sync_powerdown_mode(st, chan);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5064_get_vref(struct ad5064_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
if (st->use_internal_vref)
|
||||
return st->chip_info->internal_vref;
|
||||
|
||||
i = st->chip_info->shared_vref ? 0 : chan->channel;
|
||||
return regulator_get_voltage(st->vref_reg[i].consumer);
|
||||
}
|
||||
|
||||
static int ad5064_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int scale_uv;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = ad5064_get_vref(st, chan);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5064_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->channel] = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5064_info = {
|
||||
.read_raw = ad5064_read_raw,
|
||||
.write_raw = ad5064_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5064_read_dac_powerdown,
|
||||
.write = ad5064_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5064_CHANNEL(chan, addr, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = addr, \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 20 - bits, \
|
||||
}, \
|
||||
.ext_info = ad5064_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5064_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5064_CHANNEL(0, 0, bits), \
|
||||
AD5064_CHANNEL(1, 1, bits), \
|
||||
AD5064_CHANNEL(2, 2, bits), \
|
||||
AD5064_CHANNEL(3, 3, bits), \
|
||||
AD5064_CHANNEL(4, 4, bits), \
|
||||
AD5064_CHANNEL(5, 5, bits), \
|
||||
AD5064_CHANNEL(6, 6, bits), \
|
||||
AD5064_CHANNEL(7, 7, bits), \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5065_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5064_CHANNEL(0, 0, bits), \
|
||||
AD5064_CHANNEL(1, 3, bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5064_CHANNELS(ad5024_channels, 12);
|
||||
static DECLARE_AD5064_CHANNELS(ad5044_channels, 14);
|
||||
static DECLARE_AD5064_CHANNELS(ad5064_channels, 16);
|
||||
|
||||
static DECLARE_AD5065_CHANNELS(ad5025_channels, 12);
|
||||
static DECLARE_AD5065_CHANNELS(ad5045_channels, 14);
|
||||
static DECLARE_AD5065_CHANNELS(ad5065_channels, 16);
|
||||
|
||||
static const struct ad5064_chip_info ad5064_chip_info_tbl[] = {
|
||||
[ID_AD5024] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5025] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5025_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5044] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5045] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5045_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5064] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5064_1] = {
|
||||
.shared_vref = true,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5065] = {
|
||||
.shared_vref = false,
|
||||
.channels = ad5065_channels,
|
||||
.num_channels = 2,
|
||||
},
|
||||
[ID_AD5628_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5628_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5024_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5648_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5044_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5666_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5666_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 4,
|
||||
},
|
||||
[ID_AD5668_1] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 2500000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
[ID_AD5668_2] = {
|
||||
.shared_vref = true,
|
||||
.internal_vref = 5000000,
|
||||
.channels = ad5064_channels,
|
||||
.num_channels = 8,
|
||||
},
|
||||
};
|
||||
|
||||
static inline unsigned int ad5064_num_vref(struct ad5064_state *st)
|
||||
{
|
||||
return st->chip_info->shared_vref ? 1 : st->chip_info->num_channels;
|
||||
}
|
||||
|
||||
static const char * const ad5064_vref_names[] = {
|
||||
"vrefA",
|
||||
"vrefB",
|
||||
"vrefC",
|
||||
"vrefD",
|
||||
};
|
||||
|
||||
static const char * const ad5064_vref_name(struct ad5064_state *st,
|
||||
unsigned int vref)
|
||||
{
|
||||
return st->chip_info->shared_vref ? "vref" : ad5064_vref_names[vref];
|
||||
}
|
||||
|
||||
static int ad5064_probe(struct device *dev, enum ad5064_type type,
|
||||
const char *name, ad5064_write_func write)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5064_state *st;
|
||||
unsigned int midscale;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->chip_info = &ad5064_chip_info_tbl[type];
|
||||
st->dev = dev;
|
||||
st->write = write;
|
||||
|
||||
for (i = 0; i < ad5064_num_vref(st); ++i)
|
||||
st->vref_reg[i].supply = ad5064_vref_name(st, i);
|
||||
|
||||
ret = devm_regulator_bulk_get(dev, ad5064_num_vref(st),
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
if (!st->chip_info->internal_vref)
|
||||
return ret;
|
||||
st->use_internal_vref = true;
|
||||
ret = ad5064_write(st, AD5064_CMD_CONFIG, 0,
|
||||
AD5064_CONFIG_INT_VREF_ENABLE, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable internal vref: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
ret = regulator_bulk_enable(ad5064_num_vref(st), st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ad5064_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
midscale = (1 << indio_dev->channels[0].scan_type.realbits) / 2;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
st->pwr_down_mode[i] = AD5064_LDAC_PWRDN_1K;
|
||||
st->dac_cache[i] = midscale;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5064_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5064_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!st->use_internal_vref)
|
||||
regulator_bulk_disable(ad5064_num_vref(st), st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static int ad5064_spi_write(struct ad5064_state *st, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
|
||||
st->data.spi = cpu_to_be32(AD5064_CMD(cmd) | AD5064_ADDR(addr) | val);
|
||||
return spi_write(spi, &st->data.spi, sizeof(st->data.spi));
|
||||
}
|
||||
|
||||
static int ad5064_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return ad5064_probe(&spi->dev, id->driver_data, id->name,
|
||||
ad5064_spi_write);
|
||||
}
|
||||
|
||||
static int ad5064_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5064_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5064_spi_ids[] = {
|
||||
{"ad5024", ID_AD5024},
|
||||
{"ad5025", ID_AD5025},
|
||||
{"ad5044", ID_AD5044},
|
||||
{"ad5045", ID_AD5045},
|
||||
{"ad5064", ID_AD5064},
|
||||
{"ad5064-1", ID_AD5064_1},
|
||||
{"ad5065", ID_AD5065},
|
||||
{"ad5628-1", ID_AD5628_1},
|
||||
{"ad5628-2", ID_AD5628_2},
|
||||
{"ad5648-1", ID_AD5648_1},
|
||||
{"ad5648-2", ID_AD5648_2},
|
||||
{"ad5666-1", ID_AD5666_1},
|
||||
{"ad5666-2", ID_AD5666_2},
|
||||
{"ad5668-1", ID_AD5668_1},
|
||||
{"ad5668-2", ID_AD5668_2},
|
||||
{"ad5668-3", ID_AD5668_2}, /* similar enough to ad5668-2 */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5064_spi_ids);
|
||||
|
||||
static struct spi_driver ad5064_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5064",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5064_spi_probe,
|
||||
.remove = ad5064_spi_remove,
|
||||
.id_table = ad5064_spi_ids,
|
||||
};
|
||||
|
||||
static int __init ad5064_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ad5064_spi_driver);
|
||||
}
|
||||
|
||||
static void ad5064_spi_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&ad5064_spi_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5064_spi_register_driver(void) { return 0; }
|
||||
static inline void ad5064_spi_unregister_driver(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ad5064_i2c_write(struct ad5064_state *st, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val)
|
||||
{
|
||||
struct i2c_client *i2c = to_i2c_client(st->dev);
|
||||
|
||||
st->data.i2c[0] = (cmd << 4) | addr;
|
||||
put_unaligned_be16(val, &st->data.i2c[1]);
|
||||
return i2c_master_send(i2c, st->data.i2c, 3);
|
||||
}
|
||||
|
||||
static int ad5064_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return ad5064_probe(&i2c->dev, id->driver_data, id->name,
|
||||
ad5064_i2c_write);
|
||||
}
|
||||
|
||||
static int ad5064_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5064_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5064_i2c_ids[] = {
|
||||
{"ad5629-1", ID_AD5628_1},
|
||||
{"ad5629-2", ID_AD5628_2},
|
||||
{"ad5629-3", ID_AD5628_2}, /* similar enough to ad5629-2 */
|
||||
{"ad5669-1", ID_AD5668_1},
|
||||
{"ad5669-2", ID_AD5668_2},
|
||||
{"ad5669-3", ID_AD5668_2}, /* similar enough to ad5669-2 */
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5064_i2c_ids);
|
||||
|
||||
static struct i2c_driver ad5064_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad5064",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5064_i2c_probe,
|
||||
.remove = ad5064_i2c_remove,
|
||||
.id_table = ad5064_i2c_ids,
|
||||
};
|
||||
|
||||
static int __init ad5064_i2c_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&ad5064_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit ad5064_i2c_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&ad5064_i2c_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5064_i2c_register_driver(void) { return 0; }
|
||||
static inline void ad5064_i2c_unregister_driver(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
static int __init ad5064_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5064_spi_register_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5064_i2c_register_driver();
|
||||
if (ret) {
|
||||
ad5064_spi_unregister_driver();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(ad5064_init);
|
||||
|
||||
static void __exit ad5064_exit(void)
|
||||
{
|
||||
ad5064_i2c_unregister_driver();
|
||||
ad5064_spi_unregister_driver();
|
||||
}
|
||||
module_exit(ad5064_exit);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5024 and similar multi-channel DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
562
drivers/iio/dac/ad5360.c
Normal file
562
drivers/iio/dac/ad5360.c
Normal file
|
|
@ -0,0 +1,562 @@
|
|||
/*
|
||||
* Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373
|
||||
* multi-channel Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5360_CMD(x) ((x) << 22)
|
||||
#define AD5360_ADDR(x) ((x) << 16)
|
||||
|
||||
#define AD5360_READBACK_TYPE(x) ((x) << 13)
|
||||
#define AD5360_READBACK_ADDR(x) ((x) << 7)
|
||||
|
||||
#define AD5360_CHAN_ADDR(chan) ((chan) + 0x8)
|
||||
|
||||
#define AD5360_CMD_WRITE_DATA 0x3
|
||||
#define AD5360_CMD_WRITE_OFFSET 0x2
|
||||
#define AD5360_CMD_WRITE_GAIN 0x1
|
||||
#define AD5360_CMD_SPECIAL_FUNCTION 0x0
|
||||
|
||||
/* Special function register addresses */
|
||||
#define AD5360_REG_SF_NOP 0x0
|
||||
#define AD5360_REG_SF_CTRL 0x1
|
||||
#define AD5360_REG_SF_OFS(x) (0x2 + (x))
|
||||
#define AD5360_REG_SF_READBACK 0x5
|
||||
|
||||
#define AD5360_SF_CTRL_PWR_DOWN BIT(0)
|
||||
|
||||
#define AD5360_READBACK_X1A 0x0
|
||||
#define AD5360_READBACK_X1B 0x1
|
||||
#define AD5360_READBACK_OFFSET 0x2
|
||||
#define AD5360_READBACK_GAIN 0x3
|
||||
#define AD5360_READBACK_SF 0x4
|
||||
|
||||
|
||||
/**
|
||||
* struct ad5360_chip_info - chip specific information
|
||||
* @channel_template: channel specification template
|
||||
* @num_channels: number of channels
|
||||
* @channels_per_group: number of channels per group
|
||||
* @num_vrefs: number of vref supplies for the chip
|
||||
*/
|
||||
|
||||
struct ad5360_chip_info {
|
||||
struct iio_chan_spec channel_template;
|
||||
unsigned int num_channels;
|
||||
unsigned int channels_per_group;
|
||||
unsigned int num_vrefs;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5360_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulators
|
||||
* @ctrl: control register cache
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5360_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5360_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[3];
|
||||
unsigned int ctrl;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5360_type {
|
||||
ID_AD5360,
|
||||
ID_AD5361,
|
||||
ID_AD5362,
|
||||
ID_AD5363,
|
||||
ID_AD5370,
|
||||
ID_AD5371,
|
||||
ID_AD5372,
|
||||
ID_AD5373,
|
||||
};
|
||||
|
||||
#define AD5360_CHANNEL(bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct ad5360_chip_info ad5360_chip_info_tbl[] = {
|
||||
[ID_AD5360] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 16,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5361] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5362] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 8,
|
||||
.channels_per_group = 4,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5363] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.channels_per_group = 4,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5370] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 40,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5371] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 3,
|
||||
},
|
||||
[ID_AD5372] = {
|
||||
.channel_template = AD5360_CHANNEL(16),
|
||||
.num_channels = 32,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
[ID_AD5373] = {
|
||||
.channel_template = AD5360_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.channels_per_group = 8,
|
||||
.num_vrefs = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static unsigned int ad5360_get_channel_vref_index(struct ad5360_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
/* The first groups have their own vref, while the remaining groups
|
||||
* share the last vref */
|
||||
i = channel / st->chip_info->channels_per_group;
|
||||
if (i >= st->chip_info->num_vrefs)
|
||||
i = st->chip_info->num_vrefs - 1;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int ad5360_get_channel_vref(struct ad5360_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
unsigned int i = ad5360_get_channel_vref_index(st, channel);
|
||||
|
||||
return regulator_get_voltage(st->vref_reg[i].consumer);
|
||||
}
|
||||
|
||||
|
||||
static int ad5360_write_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int cmd, unsigned int addr, unsigned int val,
|
||||
unsigned int shift)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
val <<= shift;
|
||||
val |= AD5360_CMD(cmd) | AD5360_ADDR(addr);
|
||||
st->data[0].d32 = cpu_to_be32(val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5360_write(struct iio_dev *indio_dev, unsigned int cmd,
|
||||
unsigned int addr, unsigned int val, unsigned int shift)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5360_write_unlocked(indio_dev, cmd, addr, val, shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5360_read(struct iio_dev *indio_dev, unsigned int type,
|
||||
unsigned int addr)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5360_CMD(AD5360_CMD_SPECIAL_FUNCTION) |
|
||||
AD5360_ADDR(AD5360_REG_SF_READBACK) |
|
||||
AD5360_READBACK_TYPE(type) |
|
||||
AD5360_READBACK_ADDR(addr));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad5360_read_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN));
|
||||
}
|
||||
|
||||
static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
|
||||
unsigned int clr)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
unsigned int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->ctrl |= set;
|
||||
st->ctrl &= ~clr;
|
||||
|
||||
ret = ad5360_write_unlocked(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
|
||||
AD5360_REG_SF_CTRL, st->ctrl, 0);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t ad5360_write_dac_powerdown(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
ret = ad5360_update_ctrl(indio_dev, AD5360_SF_CTRL_PWR_DOWN, 0);
|
||||
else
|
||||
ret = ad5360_update_ctrl(indio_dev, 0, AD5360_SF_CTRL_PWR_DOWN);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(out_voltage_powerdown,
|
||||
S_IRUGO | S_IWUSR,
|
||||
ad5360_read_dac_powerdown,
|
||||
ad5360_write_dac_powerdown, 0);
|
||||
|
||||
static struct attribute *ad5360_attributes[] = {
|
||||
&iio_dev_attr_out_voltage_powerdown.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group ad5360_attribute_group = {
|
||||
.attrs = ad5360_attributes,
|
||||
};
|
||||
|
||||
static int ad5360_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
int max_val = (1 << chan->scan_type.realbits);
|
||||
unsigned int ofs_index;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_DATA,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_OFFSET,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5360_write(indio_dev, AD5360_CMD_WRITE_GAIN,
|
||||
chan->address, val, chan->scan_type.shift);
|
||||
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
if (val <= -max_val || val > 0)
|
||||
return -EINVAL;
|
||||
|
||||
val = -val;
|
||||
|
||||
/* offset is supposed to have the same scale as raw, but it
|
||||
* is always 14bits wide, so on a chip where the raw value has
|
||||
* more bits, we need to shift offset. */
|
||||
val >>= (chan->scan_type.realbits - 14);
|
||||
|
||||
/* There is one DAC offset register per vref. Changing one
|
||||
* channels offset will also change the offset for all other
|
||||
* channels which share the same vref supply. */
|
||||
ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
|
||||
return ad5360_write(indio_dev, AD5360_CMD_SPECIAL_FUNCTION,
|
||||
AD5360_REG_SF_OFS(ofs_index), val, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5360_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
unsigned int ofs_index;
|
||||
int scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_X1A,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret >> chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
scale_uv = ad5360_get_channel_vref(st, chan->channel);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
/* vout = 4 * vref * dac_code */
|
||||
*val = scale_uv * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_GAIN,
|
||||
chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
ofs_index = ad5360_get_channel_vref_index(st, chan->channel);
|
||||
ret = ad5360_read(indio_dev, AD5360_READBACK_SF,
|
||||
AD5360_REG_SF_OFS(ofs_index));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret <<= (chan->scan_type.realbits - 14);
|
||||
*val = -ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5360_info = {
|
||||
.read_raw = ad5360_read_raw,
|
||||
.write_raw = ad5360_write_raw,
|
||||
.attrs = &ad5360_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const ad5360_vref_name[] = {
|
||||
"vref0", "vref1", "vref2"
|
||||
};
|
||||
|
||||
static int ad5360_alloc_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
channels[i] = st->chip_info->channel_template;
|
||||
channels[i].channel = i;
|
||||
channels[i].address = AD5360_CHAN_ADDR(i);
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5360_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5360_type type = spi_get_device_id(spi)->driver_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5360_state *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&spi->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->chip_info = &ad5360_chip_info_tbl[type];
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5360_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = ad5360_alloc_channels(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to allocate channel spec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < st->chip_info->num_vrefs; ++i)
|
||||
st->vref_reg[i].supply = ad5360_vref_name[i];
|
||||
|
||||
ret = devm_regulator_bulk_get(&st->spi->dev, st->chip_info->num_vrefs,
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to request vref regulators: %d\n", ret);
|
||||
goto error_free_channels;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulators: %d\n", ret);
|
||||
goto error_free_channels;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
error_free_channels:
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5360_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5360_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
regulator_bulk_disable(st->chip_info->num_vrefs, st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5360_ids[] = {
|
||||
{ "ad5360", ID_AD5360 },
|
||||
{ "ad5361", ID_AD5361 },
|
||||
{ "ad5362", ID_AD5362 },
|
||||
{ "ad5363", ID_AD5363 },
|
||||
{ "ad5370", ID_AD5370 },
|
||||
{ "ad5371", ID_AD5371 },
|
||||
{ "ad5372", ID_AD5372 },
|
||||
{ "ad5373", ID_AD5373 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5360_ids);
|
||||
|
||||
static struct spi_driver ad5360_driver = {
|
||||
.driver = {
|
||||
.name = "ad5360",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5360_probe,
|
||||
.remove = ad5360_remove,
|
||||
.id_table = ad5360_ids,
|
||||
};
|
||||
module_spi_driver(ad5360_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5360/61/62/63/70/71/72/73 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
654
drivers/iio/dac/ad5380.c
Normal file
654
drivers/iio/dac/ad5380.c
Normal file
|
|
@ -0,0 +1,654 @@
|
|||
/*
|
||||
* Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
|
||||
* multi-channel Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5380_REG_DATA(x) (((x) << 2) | 3)
|
||||
#define AD5380_REG_OFFSET(x) (((x) << 2) | 2)
|
||||
#define AD5380_REG_GAIN(x) (((x) << 2) | 1)
|
||||
#define AD5380_REG_SF_PWR_DOWN (8 << 2)
|
||||
#define AD5380_REG_SF_PWR_UP (9 << 2)
|
||||
#define AD5380_REG_SF_CTRL (12 << 2)
|
||||
|
||||
#define AD5380_CTRL_PWR_DOWN_MODE_OFFSET 13
|
||||
#define AD5380_CTRL_INT_VREF_2V5 BIT(12)
|
||||
#define AD5380_CTRL_INT_VREF_EN BIT(10)
|
||||
|
||||
/**
|
||||
* struct ad5380_chip_info - chip specific information
|
||||
* @channel_template: channel specification template
|
||||
* @num_channels: number of channels
|
||||
* @int_vref: internal vref in uV
|
||||
*/
|
||||
|
||||
struct ad5380_chip_info {
|
||||
struct iio_chan_spec channel_template;
|
||||
unsigned int num_channels;
|
||||
unsigned int int_vref;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5380_state - driver instance specific data
|
||||
* @regmap: regmap instance used by the device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulator
|
||||
* @vref: actual reference voltage used in uA
|
||||
* @pwr_down: whether the chip is currently in power down mode
|
||||
*/
|
||||
|
||||
struct ad5380_state {
|
||||
struct regmap *regmap;
|
||||
const struct ad5380_chip_info *chip_info;
|
||||
struct regulator *vref_reg;
|
||||
int vref;
|
||||
bool pwr_down;
|
||||
};
|
||||
|
||||
enum ad5380_type {
|
||||
ID_AD5380_3,
|
||||
ID_AD5380_5,
|
||||
ID_AD5381_3,
|
||||
ID_AD5381_5,
|
||||
ID_AD5382_3,
|
||||
ID_AD5382_5,
|
||||
ID_AD5383_3,
|
||||
ID_AD5383_5,
|
||||
ID_AD5390_3,
|
||||
ID_AD5390_5,
|
||||
ID_AD5391_3,
|
||||
ID_AD5391_5,
|
||||
ID_AD5392_3,
|
||||
ID_AD5392_5,
|
||||
};
|
||||
|
||||
static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (pwr_down)
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_DOWN, 0);
|
||||
else
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_PWR_UP, 0);
|
||||
|
||||
st->pwr_down = pwr_down;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const char * const ad5380_powerdown_modes[] = {
|
||||
"100kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5380_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
unsigned int mode;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(st->regmap, AD5380_REG_SF_CTRL, &mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mode = (mode >> AD5380_CTRL_PWR_DOWN_MODE_OFFSET) & 1;
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int ad5380_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(st->regmap, AD5380_REG_SF_CTRL,
|
||||
1 << AD5380_CTRL_PWR_DOWN_MODE_OFFSET,
|
||||
mode << AD5380_CTRL_PWR_DOWN_MODE_OFFSET);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5380_powerdown_mode_enum = {
|
||||
.items = ad5380_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5380_powerdown_modes),
|
||||
.get = ad5380_get_powerdown_mode,
|
||||
.set = ad5380_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static unsigned int ad5380_info_to_reg(struct iio_chan_spec const *chan,
|
||||
long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
return AD5380_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5380_REG_OFFSET(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return AD5380_REG_GAIN(chan->address);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5380_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
const unsigned int max_val = (1 << chan->scan_type.realbits);
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(st->regmap,
|
||||
ad5380_info_to_reg(chan, info),
|
||||
val << chan->scan_type.shift);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val += (1 << chan->scan_type.realbits) / 2;
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return regmap_write(st->regmap,
|
||||
AD5380_REG_OFFSET(chan->address),
|
||||
val << chan->scan_type.shift);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5380_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = regmap_read(st->regmap, ad5380_info_to_reg(chan, info),
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = regmap_read(st->regmap, AD5380_REG_OFFSET(chan->address),
|
||||
val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
val -= (1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 2 * st->vref;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5380_info = {
|
||||
.read_raw = ad5380_read_raw,
|
||||
.write_raw = ad5380_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct iio_chan_spec_ext_info ad5380_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5380_read_dac_powerdown,
|
||||
.write = ad5380_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5380_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5380_CHANNEL(_bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 14 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5380_ext_info, \
|
||||
}
|
||||
|
||||
static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {
|
||||
[ID_AD5380_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5380_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 40,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5381_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5381_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5382_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5382_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5383_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5383_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 32,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5390_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5390_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5391_3] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5391_5] = {
|
||||
.channel_template = AD5380_CHANNEL(12),
|
||||
.num_channels = 16,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
[ID_AD5392_3] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 1250,
|
||||
},
|
||||
[ID_AD5392_5] = {
|
||||
.channel_template = AD5380_CHANNEL(14),
|
||||
.num_channels = 8,
|
||||
.int_vref = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5380_alloc_channels(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *channels;
|
||||
unsigned int i;
|
||||
|
||||
channels = kcalloc(st->chip_info->num_channels,
|
||||
sizeof(struct iio_chan_spec), GFP_KERNEL);
|
||||
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i) {
|
||||
channels[i] = st->chip_info->channel_template;
|
||||
channels[i].channel = i;
|
||||
channels[i].address = i;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5380_probe(struct device *dev, struct regmap *regmap,
|
||||
enum ad5380_type type, const char *name)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5380_state *st;
|
||||
unsigned int ctrl = 0;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
|
||||
st->chip_info = &ad5380_chip_info_tbl[type];
|
||||
st->regmap = regmap;
|
||||
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ad5380_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
ret = ad5380_alloc_channels(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to allocate channel spec: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (st->chip_info->int_vref == 2500)
|
||||
ctrl |= AD5380_CTRL_INT_VREF_2V5;
|
||||
|
||||
st->vref_reg = devm_regulator_get(dev, "vref");
|
||||
if (!IS_ERR(st->vref_reg)) {
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to enable vref regulators: %d\n",
|
||||
ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(st->vref_reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
st->vref = ret / 1000;
|
||||
} else {
|
||||
st->vref = st->chip_info->int_vref;
|
||||
ctrl |= AD5380_CTRL_INT_VREF_EN;
|
||||
}
|
||||
|
||||
ret = regmap_write(st->regmap, AD5380_REG_SF_CTRL, ctrl);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to write to device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->vref_reg))
|
||||
regulator_disable(st->vref_reg);
|
||||
error_free_reg:
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5380_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5380_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
if (!IS_ERR(st->vref_reg)) {
|
||||
regulator_disable(st->vref_reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ad5380_reg_false(struct device *dev, unsigned int reg)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config ad5380_regmap_config = {
|
||||
.reg_bits = 10,
|
||||
.val_bits = 14,
|
||||
|
||||
.max_register = AD5380_REG_DATA(40),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
|
||||
.volatile_reg = ad5380_reg_false,
|
||||
.readable_reg = ad5380_reg_false,
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static int ad5380_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_spi(spi, &ad5380_regmap_config);
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return ad5380_probe(&spi->dev, regmap, id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int ad5380_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5380_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5380_spi_ids[] = {
|
||||
{ "ad5380-3", ID_AD5380_3 },
|
||||
{ "ad5380-5", ID_AD5380_5 },
|
||||
{ "ad5381-3", ID_AD5381_3 },
|
||||
{ "ad5381-5", ID_AD5381_5 },
|
||||
{ "ad5382-3", ID_AD5382_3 },
|
||||
{ "ad5382-5", ID_AD5382_5 },
|
||||
{ "ad5383-3", ID_AD5383_3 },
|
||||
{ "ad5383-5", ID_AD5383_5 },
|
||||
{ "ad5384-3", ID_AD5380_3 },
|
||||
{ "ad5384-5", ID_AD5380_5 },
|
||||
{ "ad5390-3", ID_AD5390_3 },
|
||||
{ "ad5390-5", ID_AD5390_5 },
|
||||
{ "ad5391-3", ID_AD5391_3 },
|
||||
{ "ad5391-5", ID_AD5391_5 },
|
||||
{ "ad5392-3", ID_AD5392_3 },
|
||||
{ "ad5392-5", ID_AD5392_5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5380_spi_ids);
|
||||
|
||||
static struct spi_driver ad5380_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5380",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5380_spi_probe,
|
||||
.remove = ad5380_spi_remove,
|
||||
.id_table = ad5380_spi_ids,
|
||||
};
|
||||
|
||||
static inline int ad5380_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ad5380_spi_driver);
|
||||
}
|
||||
|
||||
static inline void ad5380_spi_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&ad5380_spi_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5380_spi_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ad5380_spi_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ad5380_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct regmap *regmap;
|
||||
|
||||
regmap = devm_regmap_init_i2c(i2c, &ad5380_regmap_config);
|
||||
|
||||
if (IS_ERR(regmap))
|
||||
return PTR_ERR(regmap);
|
||||
|
||||
return ad5380_probe(&i2c->dev, regmap, id->driver_data, id->name);
|
||||
}
|
||||
|
||||
static int ad5380_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5380_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5380_i2c_ids[] = {
|
||||
{ "ad5380-3", ID_AD5380_3 },
|
||||
{ "ad5380-5", ID_AD5380_5 },
|
||||
{ "ad5381-3", ID_AD5381_3 },
|
||||
{ "ad5381-5", ID_AD5381_5 },
|
||||
{ "ad5382-3", ID_AD5382_3 },
|
||||
{ "ad5382-5", ID_AD5382_5 },
|
||||
{ "ad5383-3", ID_AD5383_3 },
|
||||
{ "ad5383-5", ID_AD5383_5 },
|
||||
{ "ad5384-3", ID_AD5380_3 },
|
||||
{ "ad5384-5", ID_AD5380_5 },
|
||||
{ "ad5390-3", ID_AD5390_3 },
|
||||
{ "ad5390-5", ID_AD5390_5 },
|
||||
{ "ad5391-3", ID_AD5391_3 },
|
||||
{ "ad5391-5", ID_AD5391_5 },
|
||||
{ "ad5392-3", ID_AD5392_3 },
|
||||
{ "ad5392-5", ID_AD5392_5 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5380_i2c_ids);
|
||||
|
||||
static struct i2c_driver ad5380_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad5380",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5380_i2c_probe,
|
||||
.remove = ad5380_i2c_remove,
|
||||
.id_table = ad5380_i2c_ids,
|
||||
};
|
||||
|
||||
static inline int ad5380_i2c_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&ad5380_i2c_driver);
|
||||
}
|
||||
|
||||
static inline void ad5380_i2c_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&ad5380_i2c_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5380_i2c_register_driver(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ad5380_i2c_unregister_driver(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init ad5380_spi_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5380_spi_register_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5380_i2c_register_driver();
|
||||
if (ret) {
|
||||
ad5380_spi_unregister_driver();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(ad5380_spi_init);
|
||||
|
||||
static void __exit ad5380_spi_exit(void)
|
||||
{
|
||||
ad5380_i2c_unregister_driver();
|
||||
ad5380_spi_unregister_driver();
|
||||
|
||||
}
|
||||
module_exit(ad5380_spi_exit);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5380/81/82/83/84/90/91/92 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
536
drivers/iio/dac/ad5421.c
Normal file
536
drivers/iio/dac/ad5421.c
Normal file
|
|
@ -0,0 +1,536 @@
|
|||
/*
|
||||
* AD5421 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/dac/ad5421.h>
|
||||
|
||||
|
||||
#define AD5421_REG_DAC_DATA 0x1
|
||||
#define AD5421_REG_CTRL 0x2
|
||||
#define AD5421_REG_OFFSET 0x3
|
||||
#define AD5421_REG_GAIN 0x4
|
||||
/* load dac and fault shared the same register number. Writing to it will cause
|
||||
* a dac load command, reading from it will return the fault status register */
|
||||
#define AD5421_REG_LOAD_DAC 0x5
|
||||
#define AD5421_REG_FAULT 0x5
|
||||
#define AD5421_REG_FORCE_ALARM_CURRENT 0x6
|
||||
#define AD5421_REG_RESET 0x7
|
||||
#define AD5421_REG_START_CONVERSION 0x8
|
||||
#define AD5421_REG_NOOP 0x9
|
||||
|
||||
#define AD5421_CTRL_WATCHDOG_DISABLE BIT(12)
|
||||
#define AD5421_CTRL_AUTO_FAULT_READBACK BIT(11)
|
||||
#define AD5421_CTRL_MIN_CURRENT BIT(9)
|
||||
#define AD5421_CTRL_ADC_SOURCE_TEMP BIT(8)
|
||||
#define AD5421_CTRL_ADC_ENABLE BIT(7)
|
||||
#define AD5421_CTRL_PWR_DOWN_INT_VREF BIT(6)
|
||||
|
||||
#define AD5421_FAULT_SPI BIT(15)
|
||||
#define AD5421_FAULT_PEC BIT(14)
|
||||
#define AD5421_FAULT_OVER_CURRENT BIT(13)
|
||||
#define AD5421_FAULT_UNDER_CURRENT BIT(12)
|
||||
#define AD5421_FAULT_TEMP_OVER_140 BIT(11)
|
||||
#define AD5421_FAULT_TEMP_OVER_100 BIT(10)
|
||||
#define AD5421_FAULT_UNDER_VOLTAGE_6V BIT(9)
|
||||
#define AD5421_FAULT_UNDER_VOLTAGE_12V BIT(8)
|
||||
|
||||
/* These bits will cause the fault pin to go high */
|
||||
#define AD5421_FAULT_TRIGGER_IRQ \
|
||||
(AD5421_FAULT_SPI | AD5421_FAULT_PEC | AD5421_FAULT_OVER_CURRENT | \
|
||||
AD5421_FAULT_UNDER_CURRENT | AD5421_FAULT_TEMP_OVER_140)
|
||||
|
||||
/**
|
||||
* struct ad5421_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @ctrl: control register cache
|
||||
* @current_range: current range which the device is configured for
|
||||
* @data: spi transfer buffers
|
||||
* @fault_mask: software masking of events
|
||||
*/
|
||||
struct ad5421_state {
|
||||
struct spi_device *spi;
|
||||
unsigned int ctrl;
|
||||
enum ad5421_current_range current_range;
|
||||
unsigned int fault_mask;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_current_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
}, {
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_FALLING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_event_spec ad5421_temp_event[] = {
|
||||
{
|
||||
.type = IIO_EV_TYPE_THRESH,
|
||||
.dir = IIO_EV_DIR_RISING,
|
||||
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
|
||||
BIT(IIO_EV_INFO_ENABLE),
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec ad5421_channels[] = {
|
||||
{
|
||||
.type = IIO_CURRENT,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.scan_type = {
|
||||
.sign = 'u',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
},
|
||||
.event_spec = ad5421_current_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_current_event),
|
||||
},
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel = -1,
|
||||
.event_spec = ad5421_temp_event,
|
||||
.num_event_specs = ARRAY_SIZE(ad5421_temp_event),
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5421_write_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((reg << 16) | val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5421_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5421_write_unlocked(indio_dev, reg, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5421_read(struct iio_dev *indio_dev, unsigned int reg)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5421_update_ctrl(struct iio_dev *indio_dev, unsigned int set,
|
||||
unsigned int clr)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->ctrl &= ~clr;
|
||||
st->ctrl |= set;
|
||||
|
||||
ret = ad5421_write_unlocked(indio_dev, AD5421_REG_CTRL, st->ctrl);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t ad5421_fault_handler(int irq, void *data)
|
||||
{
|
||||
struct iio_dev *indio_dev = data;
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int fault;
|
||||
unsigned int old_fault = 0;
|
||||
unsigned int events;
|
||||
|
||||
fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
|
||||
if (!fault)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* If we had a fault, this might mean that the DAC has lost its state
|
||||
* and has been reset. Make sure that the control register actually
|
||||
* contains what we expect it to contain. Otherwise the watchdog might
|
||||
* be enabled and we get watchdog timeout faults, which will render the
|
||||
* DAC unusable. */
|
||||
ad5421_update_ctrl(indio_dev, 0, 0);
|
||||
|
||||
|
||||
/* The fault pin stays high as long as a fault condition is present and
|
||||
* it is not possible to mask fault conditions. For certain fault
|
||||
* conditions for example like over-temperature it takes some time
|
||||
* until the fault condition disappears. If we would exit the interrupt
|
||||
* handler immediately after handling the event it would be entered
|
||||
* again instantly. Thus we fall back to polling in case we detect that
|
||||
* a interrupt condition is still present.
|
||||
*/
|
||||
do {
|
||||
/* 0xffff is a invalid value for the register and will only be
|
||||
* read if there has been a communication error */
|
||||
if (fault == 0xffff)
|
||||
fault = 0;
|
||||
|
||||
/* we are only interested in new events */
|
||||
events = (old_fault ^ fault) & fault;
|
||||
events &= st->fault_mask;
|
||||
|
||||
if (events & AD5421_FAULT_OVER_CURRENT) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
if (events & AD5421_FAULT_UNDER_CURRENT) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_CURRENT,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_FALLING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
if (events & AD5421_FAULT_TEMP_OVER_140) {
|
||||
iio_push_event(indio_dev,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_MAG,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
}
|
||||
|
||||
old_fault = fault;
|
||||
fault = ad5421_read(indio_dev, AD5421_REG_FAULT);
|
||||
|
||||
/* still active? go to sleep for some time */
|
||||
if (fault & AD5421_FAULT_TRIGGER_IRQ)
|
||||
msleep(1000);
|
||||
|
||||
} while (fault & AD5421_FAULT_TRIGGER_IRQ);
|
||||
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad5421_get_current_min_max(struct ad5421_state *st,
|
||||
unsigned int *min, unsigned int *max)
|
||||
{
|
||||
/* The current range is configured using external pins, which are
|
||||
* usually hard-wired and not run-time switchable. */
|
||||
switch (st->current_range) {
|
||||
case AD5421_CURRENT_RANGE_4mA_20mA:
|
||||
*min = 4000;
|
||||
*max = 20000;
|
||||
break;
|
||||
case AD5421_CURRENT_RANGE_3mA8_21mA:
|
||||
*min = 3800;
|
||||
*max = 21000;
|
||||
break;
|
||||
case AD5421_CURRENT_RANGE_3mA2_24mA:
|
||||
*min = 3200;
|
||||
*max = 24000;
|
||||
break;
|
||||
default:
|
||||
*min = 0;
|
||||
*max = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int ad5421_get_offset(struct ad5421_state *st)
|
||||
{
|
||||
unsigned int min, max;
|
||||
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
return (min * (1 << 16)) / (max - min);
|
||||
}
|
||||
|
||||
static int ad5421_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long m)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int min, max;
|
||||
int ret;
|
||||
|
||||
if (chan->type != IIO_CURRENT)
|
||||
return -EINVAL;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ad5421_get_current_min_max(st, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = (1 << 16) * 1000;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5421_get_offset(st);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_OFFSET);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret - 32768;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_GAIN);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5421_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
const unsigned int max_val = 1 << 16;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_DAC_DATA, val);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
val += 32768;
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_OFFSET, val);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5421_write(indio_dev, AD5421_REG_GAIN, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5421_write_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir, int state)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
mask = AD5421_FAULT_TEMP_OVER_140;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (state)
|
||||
st->fault_mask |= mask;
|
||||
else
|
||||
st->fault_mask &= ~mask;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5421_read_event_config(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, enum iio_event_type type,
|
||||
enum iio_event_direction dir)
|
||||
{
|
||||
struct ad5421_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
if (dir == IIO_EV_DIR_RISING)
|
||||
mask = AD5421_FAULT_OVER_CURRENT;
|
||||
else
|
||||
mask = AD5421_FAULT_UNDER_CURRENT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
mask = AD5421_FAULT_TEMP_OVER_140;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return (bool)(st->fault_mask & mask);
|
||||
}
|
||||
|
||||
static int ad5421_read_event_value(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)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (chan->type) {
|
||||
case IIO_CURRENT:
|
||||
ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
*val = 140000;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5421_info = {
|
||||
.read_raw = ad5421_read_raw,
|
||||
.write_raw = ad5421_write_raw,
|
||||
.read_event_config = ad5421_read_event_config,
|
||||
.write_event_config = ad5421_write_event_config,
|
||||
.read_event_value = ad5421_read_event_value,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5421_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5421_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5421_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&spi->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = "ad5421";
|
||||
indio_dev->info = &ad5421_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad5421_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad5421_channels);
|
||||
|
||||
st->ctrl = AD5421_CTRL_WATCHDOG_DISABLE |
|
||||
AD5421_CTRL_AUTO_FAULT_READBACK;
|
||||
|
||||
if (pdata) {
|
||||
st->current_range = pdata->current_range;
|
||||
if (pdata->external_vref)
|
||||
st->ctrl |= AD5421_CTRL_PWR_DOWN_INT_VREF;
|
||||
} else {
|
||||
st->current_range = AD5421_CURRENT_RANGE_4mA_20mA;
|
||||
}
|
||||
|
||||
/* write initial ctrl register value */
|
||||
ad5421_update_ctrl(indio_dev, 0, 0);
|
||||
|
||||
if (spi->irq) {
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL,
|
||||
ad5421_fault_handler,
|
||||
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
|
||||
"ad5421 fault",
|
||||
indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct spi_driver ad5421_driver = {
|
||||
.driver = {
|
||||
.name = "ad5421",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5421_probe,
|
||||
};
|
||||
module_spi_driver(ad5421_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5421 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:ad5421");
|
||||
623
drivers/iio/dac/ad5446.c
Normal file
623
drivers/iio/dac/ad5446.c
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
/*
|
||||
* AD5446 SPI DAC driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define MODE_PWRDWN_1k 0x1
|
||||
#define MODE_PWRDWN_100k 0x2
|
||||
#define MODE_PWRDWN_TRISTATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
*/
|
||||
|
||||
struct ad5446_state {
|
||||
struct device *dev;
|
||||
const struct ad5446_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned cached_val;
|
||||
unsigned pwr_down_mode;
|
||||
unsigned pwr_down;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_chip_info - chip specific information
|
||||
* @channel: channel spec for the DAC
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
* @write: chip specific helper function to write to the register
|
||||
*/
|
||||
|
||||
struct ad5446_chip_info {
|
||||
struct iio_chan_spec channel;
|
||||
u16 int_vref_mv;
|
||||
int (*write)(struct ad5446_state *st, unsigned val);
|
||||
};
|
||||
|
||||
static const char * const ad5446_powerdown_modes[] = {
|
||||
"1kohm_to_gnd", "100kohm_to_gnd", "three_state"
|
||||
};
|
||||
|
||||
static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode + 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode - 1;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5446_powerdown_mode_enum = {
|
||||
.items = ad5446_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5446_powerdown_modes),
|
||||
.get = ad5446_get_powerdown_mode,
|
||||
.set = ad5446_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
unsigned int shift;
|
||||
unsigned int val;
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->pwr_down = powerdown;
|
||||
|
||||
if (st->pwr_down) {
|
||||
shift = chan->scan_type.realbits + chan->scan_type.shift;
|
||||
val = st->pwr_down_mode << shift;
|
||||
} else {
|
||||
val = st->cached_val;
|
||||
}
|
||||
|
||||
ret = st->chip_info->write(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5446_read_dac_powerdown,
|
||||
.write = ad5446_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define _AD5446_CHANNEL(bits, storage, _shift, ext) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = (storage), \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = (ext), \
|
||||
}
|
||||
|
||||
#define AD5446_CHANNEL(bits, storage, shift) \
|
||||
_AD5446_CHANNEL(bits, storage, shift, NULL)
|
||||
|
||||
#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
|
||||
_AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown)
|
||||
|
||||
static int ad5446_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->cached_val;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5446_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
val <<= chan->scan_type.shift;
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->cached_val = val;
|
||||
if (!st->pwr_down)
|
||||
ret = st->chip_info->write(st, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5446_info = {
|
||||
.read_raw = ad5446_read_raw,
|
||||
.write_raw = ad5446_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5446_probe(struct device *dev, const char *name,
|
||||
const struct ad5446_chip_info *chip_info)
|
||||
{
|
||||
struct ad5446_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
struct regulator *reg;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
reg = devm_regulator_get(dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_disable_reg;
|
||||
}
|
||||
st = iio_priv(indio_dev);
|
||||
st->chip_info = chip_info;
|
||||
|
||||
dev_set_drvdata(dev, indio_dev);
|
||||
st->reg = reg;
|
||||
st->dev = dev;
|
||||
|
||||
/* Establish that the iio_dev is a child of the device */
|
||||
indio_dev->dev.parent = dev;
|
||||
indio_dev->name = name;
|
||||
indio_dev->info = &ad5446_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = &st->chip_info->channel;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
st->pwr_down_mode = MODE_PWRDWN_1k;
|
||||
|
||||
if (st->chip_info->int_vref_mv)
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
else if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
dev_warn(dev, "reference voltage unspecified\n");
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5446_remove(struct device *dev)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_get_drvdata(dev);
|
||||
struct ad5446_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SPI_MASTER)
|
||||
|
||||
static int ad5446_write(struct ad5446_state *st, unsigned val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
__be16 data = cpu_to_be16(val);
|
||||
|
||||
return spi_write(spi, &data, sizeof(data));
|
||||
}
|
||||
|
||||
static int ad5660_write(struct ad5446_state *st, unsigned val)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(st->dev);
|
||||
uint8_t data[3];
|
||||
|
||||
data[0] = (val >> 16) & 0xFF;
|
||||
data[1] = (val >> 8) & 0xFF;
|
||||
data[2] = val & 0xFF;
|
||||
|
||||
return spi_write(spi, data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* ad5446_supported_spi_device_ids:
|
||||
* The AD5620/40/60 parts are available in different fixed internal reference
|
||||
* voltage options. The actual part numbers may look differently
|
||||
* (and a bit cryptic), however this style is used to make clear which
|
||||
* parts are supported here.
|
||||
*/
|
||||
enum ad5446_supported_spi_device_ids {
|
||||
ID_AD5300,
|
||||
ID_AD5310,
|
||||
ID_AD5320,
|
||||
ID_AD5444,
|
||||
ID_AD5446,
|
||||
ID_AD5450,
|
||||
ID_AD5451,
|
||||
ID_AD5541A,
|
||||
ID_AD5512A,
|
||||
ID_AD5553,
|
||||
ID_AD5601,
|
||||
ID_AD5611,
|
||||
ID_AD5621,
|
||||
ID_AD5641,
|
||||
ID_AD5620_2500,
|
||||
ID_AD5620_1250,
|
||||
ID_AD5640_2500,
|
||||
ID_AD5640_1250,
|
||||
ID_AD5660_2500,
|
||||
ID_AD5660_1250,
|
||||
ID_AD5662,
|
||||
};
|
||||
|
||||
static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
|
||||
[ID_AD5300] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5310] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5320] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5444] = {
|
||||
.channel = AD5446_CHANNEL(12, 16, 2),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5446] = {
|
||||
.channel = AD5446_CHANNEL(14, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5450] = {
|
||||
.channel = AD5446_CHANNEL(8, 16, 6),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5451] = {
|
||||
.channel = AD5446_CHANNEL(10, 16, 4),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5541A] = {
|
||||
.channel = AD5446_CHANNEL(16, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5512A] = {
|
||||
.channel = AD5446_CHANNEL(12, 16, 4),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5553] = {
|
||||
.channel = AD5446_CHANNEL(14, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5601] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5611] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5621] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5641] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5620_2500] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
|
||||
.int_vref_mv = 2500,
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5620_1250] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
|
||||
.int_vref_mv = 1250,
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5640_2500] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
|
||||
.int_vref_mv = 2500,
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5640_1250] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
|
||||
.int_vref_mv = 1250,
|
||||
.write = ad5446_write,
|
||||
},
|
||||
[ID_AD5660_2500] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
|
||||
.int_vref_mv = 2500,
|
||||
.write = ad5660_write,
|
||||
},
|
||||
[ID_AD5660_1250] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
|
||||
.int_vref_mv = 1250,
|
||||
.write = ad5660_write,
|
||||
},
|
||||
[ID_AD5662] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
|
||||
.write = ad5660_write,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct spi_device_id ad5446_spi_ids[] = {
|
||||
{"ad5300", ID_AD5300},
|
||||
{"ad5310", ID_AD5310},
|
||||
{"ad5320", ID_AD5320},
|
||||
{"ad5444", ID_AD5444},
|
||||
{"ad5446", ID_AD5446},
|
||||
{"ad5450", ID_AD5450},
|
||||
{"ad5451", ID_AD5451},
|
||||
{"ad5452", ID_AD5444}, /* ad5452 is compatible to the ad5444 */
|
||||
{"ad5453", ID_AD5446}, /* ad5453 is compatible to the ad5446 */
|
||||
{"ad5512a", ID_AD5512A},
|
||||
{"ad5541a", ID_AD5541A},
|
||||
{"ad5542a", ID_AD5541A}, /* ad5541a and ad5542a are compatible */
|
||||
{"ad5543", ID_AD5541A}, /* ad5541a and ad5543 are compatible */
|
||||
{"ad5553", ID_AD5553},
|
||||
{"ad5601", ID_AD5601},
|
||||
{"ad5611", ID_AD5611},
|
||||
{"ad5621", ID_AD5621},
|
||||
{"ad5641", ID_AD5641},
|
||||
{"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */
|
||||
{"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */
|
||||
{"ad5640-2500", ID_AD5640_2500},
|
||||
{"ad5640-1250", ID_AD5640_1250},
|
||||
{"ad5660-2500", ID_AD5660_2500},
|
||||
{"ad5660-1250", ID_AD5660_1250},
|
||||
{"ad5662", ID_AD5662},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
|
||||
|
||||
static int ad5446_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
|
||||
return ad5446_probe(&spi->dev, id->name,
|
||||
&ad5446_spi_chip_info[id->driver_data]);
|
||||
}
|
||||
|
||||
static int ad5446_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
return ad5446_remove(&spi->dev);
|
||||
}
|
||||
|
||||
static struct spi_driver ad5446_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5446",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5446_spi_probe,
|
||||
.remove = ad5446_spi_remove,
|
||||
.id_table = ad5446_spi_ids,
|
||||
};
|
||||
|
||||
static int __init ad5446_spi_register_driver(void)
|
||||
{
|
||||
return spi_register_driver(&ad5446_spi_driver);
|
||||
}
|
||||
|
||||
static void ad5446_spi_unregister_driver(void)
|
||||
{
|
||||
spi_unregister_driver(&ad5446_spi_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5446_spi_register_driver(void) { return 0; }
|
||||
static inline void ad5446_spi_unregister_driver(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_I2C)
|
||||
|
||||
static int ad5622_write(struct ad5446_state *st, unsigned val)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(st->dev);
|
||||
__be16 data = cpu_to_be16(val);
|
||||
|
||||
return i2c_master_send(client, (char *)&data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* ad5446_supported_i2c_device_ids:
|
||||
* The AD5620/40/60 parts are available in different fixed internal reference
|
||||
* voltage options. The actual part numbers may look differently
|
||||
* (and a bit cryptic), however this style is used to make clear which
|
||||
* parts are supported here.
|
||||
*/
|
||||
enum ad5446_supported_i2c_device_ids {
|
||||
ID_AD5602,
|
||||
ID_AD5612,
|
||||
ID_AD5622,
|
||||
};
|
||||
|
||||
static const struct ad5446_chip_info ad5446_i2c_chip_info[] = {
|
||||
[ID_AD5602] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
|
||||
.write = ad5622_write,
|
||||
},
|
||||
[ID_AD5612] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
|
||||
.write = ad5622_write,
|
||||
},
|
||||
[ID_AD5622] = {
|
||||
.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
|
||||
.write = ad5622_write,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5446_i2c_probe(struct i2c_client *i2c,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
return ad5446_probe(&i2c->dev, id->name,
|
||||
&ad5446_i2c_chip_info[id->driver_data]);
|
||||
}
|
||||
|
||||
static int ad5446_i2c_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return ad5446_remove(&i2c->dev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad5446_i2c_ids[] = {
|
||||
{"ad5301", ID_AD5602},
|
||||
{"ad5311", ID_AD5612},
|
||||
{"ad5321", ID_AD5622},
|
||||
{"ad5602", ID_AD5602},
|
||||
{"ad5612", ID_AD5612},
|
||||
{"ad5622", ID_AD5622},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
|
||||
|
||||
static struct i2c_driver ad5446_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad5446",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5446_i2c_probe,
|
||||
.remove = ad5446_i2c_remove,
|
||||
.id_table = ad5446_i2c_ids,
|
||||
};
|
||||
|
||||
static int __init ad5446_i2c_register_driver(void)
|
||||
{
|
||||
return i2c_add_driver(&ad5446_i2c_driver);
|
||||
}
|
||||
|
||||
static void __exit ad5446_i2c_unregister_driver(void)
|
||||
{
|
||||
i2c_del_driver(&ad5446_i2c_driver);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int ad5446_i2c_register_driver(void) { return 0; }
|
||||
static inline void ad5446_i2c_unregister_driver(void) { }
|
||||
|
||||
#endif
|
||||
|
||||
static int __init ad5446_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ad5446_spi_register_driver();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5446_i2c_register_driver();
|
||||
if (ret) {
|
||||
ad5446_spi_unregister_driver();
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
module_init(ad5446_init);
|
||||
|
||||
static void __exit ad5446_exit(void)
|
||||
{
|
||||
ad5446_i2c_unregister_driver();
|
||||
ad5446_spi_unregister_driver();
|
||||
}
|
||||
module_exit(ad5446_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
369
drivers/iio/dac/ad5449.c
Normal file
369
drivers/iio/dac/ad5449.c
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
/*
|
||||
* AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog
|
||||
* Converter driver.
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/ad5449.h>
|
||||
|
||||
#define AD5449_MAX_CHANNELS 2
|
||||
#define AD5449_MAX_VREFS 2
|
||||
|
||||
#define AD5449_CMD_NOOP 0x0
|
||||
#define AD5449_CMD_LOAD_AND_UPDATE(x) (0x1 + (x) * 3)
|
||||
#define AD5449_CMD_READ(x) (0x2 + (x) * 3)
|
||||
#define AD5449_CMD_LOAD(x) (0x3 + (x) * 3)
|
||||
#define AD5449_CMD_CTRL 13
|
||||
|
||||
#define AD5449_CTRL_SDO_OFFSET 10
|
||||
#define AD5449_CTRL_DAISY_CHAIN BIT(9)
|
||||
#define AD5449_CTRL_HCLR_TO_MIDSCALE BIT(8)
|
||||
#define AD5449_CTRL_SAMPLE_RISING BIT(7)
|
||||
|
||||
/**
|
||||
* struct ad5449_chip_info - chip specific information
|
||||
* @channels: Channel specification
|
||||
* @num_channels: Number of channels
|
||||
* @has_ctrl: Chip has a control register
|
||||
*/
|
||||
struct ad5449_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
unsigned int num_channels;
|
||||
bool has_ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5449 - driver instance specific data
|
||||
* @spi: the SPI device for this driver instance
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @vref_reg: vref supply regulators
|
||||
* @has_sdo: whether the SDO line is connected
|
||||
* @dac_cache: Cache for the DAC values
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
struct ad5449 {
|
||||
struct spi_device *spi;
|
||||
const struct ad5449_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[AD5449_MAX_VREFS];
|
||||
|
||||
bool has_sdo;
|
||||
uint16_t dac_cache[AD5449_MAX_CHANNELS];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5449_type {
|
||||
ID_AD5426,
|
||||
ID_AD5429,
|
||||
ID_AD5432,
|
||||
ID_AD5439,
|
||||
ID_AD5443,
|
||||
ID_AD5449,
|
||||
};
|
||||
|
||||
static int ad5449_write(struct iio_dev *indio_dev, unsigned int addr,
|
||||
unsigned int val)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0] = cpu_to_be16((addr << 12) | val);
|
||||
ret = spi_write(st->spi, st->data, 2);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0],
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1],
|
||||
.rx_buf = &st->data[1],
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0] = cpu_to_be16(addr << 12);
|
||||
st->data[1] = cpu_to_be16(AD5449_CMD_NOOP);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
goto out_unlock;
|
||||
|
||||
*val = be16_to_cpu(st->data[1]);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
struct regulator_bulk_data *reg;
|
||||
int scale_uv;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (st->has_sdo) {
|
||||
ret = ad5449_read(indio_dev,
|
||||
AD5449_CMD_READ(chan->address), val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= 0xfff;
|
||||
} else {
|
||||
*val = st->dac_cache[chan->address];
|
||||
}
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
reg = &st->vref_reg[chan->channel];
|
||||
scale_uv = regulator_get_voltage(reg->consumer);
|
||||
if (scale_uv < 0)
|
||||
return scale_uv;
|
||||
|
||||
*val = scale_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5449_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val < 0 || val >= (1 << chan->scan_type.realbits))
|
||||
return -EINVAL;
|
||||
|
||||
ret = ad5449_write(indio_dev,
|
||||
AD5449_CMD_LOAD_AND_UPDATE(chan->address),
|
||||
val << chan->scan_type.shift);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->address] = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5449_info = {
|
||||
.read_raw = ad5449_read_raw,
|
||||
.write_raw = ad5449_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define AD5449_CHANNEL(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5449_CHANNELS(name, bits) \
|
||||
const struct iio_chan_spec name[] = { \
|
||||
AD5449_CHANNEL(0, bits), \
|
||||
AD5449_CHANNEL(1, bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5449_CHANNELS(ad5429_channels, 8);
|
||||
static DECLARE_AD5449_CHANNELS(ad5439_channels, 10);
|
||||
static DECLARE_AD5449_CHANNELS(ad5449_channels, 12);
|
||||
|
||||
static const struct ad5449_chip_info ad5449_chip_info[] = {
|
||||
[ID_AD5426] = {
|
||||
.channels = ad5429_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5429] = {
|
||||
.channels = ad5429_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
[ID_AD5432] = {
|
||||
.channels = ad5439_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5439] = {
|
||||
.channels = ad5439_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
[ID_AD5443] = {
|
||||
.channels = ad5449_channels,
|
||||
.num_channels = 1,
|
||||
.has_ctrl = false,
|
||||
},
|
||||
[ID_AD5449] = {
|
||||
.channels = ad5449_channels,
|
||||
.num_channels = 2,
|
||||
.has_ctrl = true,
|
||||
},
|
||||
};
|
||||
|
||||
static const char *ad5449_vref_name(struct ad5449 *st, int n)
|
||||
{
|
||||
if (st->chip_info->num_channels == 1)
|
||||
return "VREF";
|
||||
|
||||
if (n == 0)
|
||||
return "VREFA";
|
||||
else
|
||||
return "VREFB";
|
||||
}
|
||||
|
||||
static int ad5449_spi_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5449_platform_data *pdata = spi->dev.platform_data;
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5449 *st;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->chip_info = &ad5449_chip_info[id->driver_data];
|
||||
st->spi = spi;
|
||||
|
||||
for (i = 0; i < st->chip_info->num_channels; ++i)
|
||||
st->vref_reg[i].supply = ad5449_vref_name(st, i);
|
||||
|
||||
ret = devm_regulator_bulk_get(&spi->dev, st->chip_info->num_channels,
|
||||
st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_bulk_enable(st->chip_info->num_channels, st->vref_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &ad5449_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = st->chip_info->num_channels;
|
||||
|
||||
if (st->chip_info->has_ctrl) {
|
||||
unsigned int ctrl = 0x00;
|
||||
if (pdata) {
|
||||
if (pdata->hardware_clear_to_midscale)
|
||||
ctrl |= AD5449_CTRL_HCLR_TO_MIDSCALE;
|
||||
ctrl |= pdata->sdo_mode << AD5449_CTRL_SDO_OFFSET;
|
||||
st->has_sdo = pdata->sdo_mode != AD5449_SDO_DISABLED;
|
||||
} else {
|
||||
st->has_sdo = true;
|
||||
}
|
||||
ad5449_write(indio_dev, AD5449_CMD_CTRL, ctrl);
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5449_spi_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5449 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
regulator_bulk_disable(st->chip_info->num_channels, st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5449_spi_ids[] = {
|
||||
{ "ad5415", ID_AD5449 },
|
||||
{ "ad5426", ID_AD5426 },
|
||||
{ "ad5429", ID_AD5429 },
|
||||
{ "ad5432", ID_AD5432 },
|
||||
{ "ad5439", ID_AD5439 },
|
||||
{ "ad5443", ID_AD5443 },
|
||||
{ "ad5449", ID_AD5449 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5449_spi_ids);
|
||||
|
||||
static struct spi_driver ad5449_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad5449",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5449_spi_probe,
|
||||
.remove = ad5449_spi_remove,
|
||||
.id_table = ad5449_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad5449_spi_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5449 and similar DACs");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
377
drivers/iio/dac/ad5504.c
Normal file
377
drivers/iio/dac/ad5504.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* AD5504, AD5501 High Voltage Digital to Analog Converter
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/dac/ad5504.h>
|
||||
|
||||
#define AD5504_RES_MASK GENMASK(11, 0)
|
||||
#define AD5504_CMD_READ BIT(15)
|
||||
#define AD5504_CMD_WRITE 0
|
||||
#define AD5504_ADDR(addr) ((addr) << 12)
|
||||
|
||||
/* Registers */
|
||||
#define AD5504_ADDR_NOOP 0
|
||||
#define AD5504_ADDR_DAC(x) ((x) + 1)
|
||||
#define AD5504_ADDR_ALL_DAC 5
|
||||
#define AD5504_ADDR_CTRL 7
|
||||
|
||||
/* Control Register */
|
||||
#define AD5504_DAC_PWR(ch) ((ch) << 2)
|
||||
#define AD5504_DAC_PWRDWN_MODE(mode) ((mode) << 6)
|
||||
#define AD5504_DAC_PWRDN_20K 0
|
||||
#define AD5504_DAC_PWRDN_3STATE 1
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask power down mask
|
||||
* @pwr_down_mode current power down mode
|
||||
* @data: transfer buffer
|
||||
*/
|
||||
struct ad5504_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
|
||||
__be16 data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5504_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5504_supported_device_ids {
|
||||
ID_AD5504,
|
||||
ID_AD5501,
|
||||
};
|
||||
|
||||
static int ad5504_spi_write(struct ad5504_state *st, u8 addr, u16 val)
|
||||
{
|
||||
st->data[0] = cpu_to_be16(AD5504_CMD_WRITE | AD5504_ADDR(addr) |
|
||||
(val & AD5504_RES_MASK));
|
||||
|
||||
return spi_write(st->spi, &st->data[0], 2);
|
||||
}
|
||||
|
||||
static int ad5504_spi_read(struct ad5504_state *st, u8 addr)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer t = {
|
||||
.tx_buf = &st->data[0],
|
||||
.rx_buf = &st->data[1],
|
||||
.len = 2,
|
||||
};
|
||||
|
||||
st->data[0] = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr));
|
||||
ret = spi_sync_transfer(st->spi, &t, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be16_to_cpu(st->data[1]) & AD5504_RES_MASK;
|
||||
}
|
||||
|
||||
static int ad5504_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5504_spi_read(st, chan->address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = ret;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5504_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5504_spi_write(st, chan->address, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const ad5504_powerdown_modes[] = {
|
||||
"20kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5504_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5504_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5504_powerdown_mode_enum = {
|
||||
.items = ad5504_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5504_powerdown_modes),
|
||||
.get = ad5504_get_powerdown_mode,
|
||||
.set = ad5504_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!(st->pwr_down_mask & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
st->pwr_down_mask |= (1 << chan->channel);
|
||||
else
|
||||
st->pwr_down_mask &= ~(1 << chan->channel);
|
||||
|
||||
ret = ad5504_spi_write(st, AD5504_ADDR_CTRL,
|
||||
AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |
|
||||
AD5504_DAC_PWR(st->pwr_down_mask));
|
||||
|
||||
/* writes to the CTRL register must be followed by a NOOP */
|
||||
ad5504_spi_write(st, AD5504_ADDR_NOOP, 0);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static IIO_CONST_ATTR(temp0_thresh_rising_value, "110000");
|
||||
static IIO_CONST_ATTR(temp0_thresh_rising_en, "1");
|
||||
|
||||
static struct attribute *ad5504_ev_attributes[] = {
|
||||
&iio_const_attr_temp0_thresh_rising_value.dev_attr.attr,
|
||||
&iio_const_attr_temp0_thresh_rising_en.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ad5504_ev_attribute_group = {
|
||||
.attrs = ad5504_ev_attributes,
|
||||
.name = "events",
|
||||
};
|
||||
|
||||
static irqreturn_t ad5504_event_handler(int irq, void *private)
|
||||
{
|
||||
iio_push_event(private,
|
||||
IIO_UNMOD_EVENT_CODE(IIO_TEMP,
|
||||
0,
|
||||
IIO_EV_TYPE_THRESH,
|
||||
IIO_EV_DIR_RISING),
|
||||
iio_get_time_ns());
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5504_info = {
|
||||
.write_raw = ad5504_write_raw,
|
||||
.read_raw = ad5504_read_raw,
|
||||
.event_attrs = &ad5504_ev_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5504_read_dac_powerdown,
|
||||
.write = ad5504_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5504_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5504_CHANNEL(_chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = AD5504_ADDR_DAC(_chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = 12, \
|
||||
.storagebits = 16, \
|
||||
}, \
|
||||
.ext_info = ad5504_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5504_channels[] = {
|
||||
AD5504_CHANNEL(0),
|
||||
AD5504_CHANNEL(1),
|
||||
AD5504_CHANNEL(2),
|
||||
AD5504_CHANNEL(3),
|
||||
};
|
||||
|
||||
static int ad5504_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5504_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5504_state *st;
|
||||
struct regulator *reg;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(reg)) {
|
||||
ret = regulator_enable(reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st = iio_priv(indio_dev);
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else if (pdata)
|
||||
st->vref_mv = pdata->vref_mv;
|
||||
else
|
||||
dev_warn(&spi->dev, "reference voltage unspecified\n");
|
||||
|
||||
st->reg = reg;
|
||||
st->spi = spi;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(st->spi)->name;
|
||||
indio_dev->info = &ad5504_info;
|
||||
if (spi_get_device_id(st->spi)->driver_data == ID_AD5501)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
indio_dev->num_channels = 4;
|
||||
indio_dev->channels = ad5504_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
if (spi->irq) {
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq,
|
||||
NULL,
|
||||
&ad5504_event_handler,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
spi_get_device_id(st->spi)->name,
|
||||
indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(reg))
|
||||
regulator_disable(reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5504_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5504_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5504_id[] = {
|
||||
{"ad5504", ID_AD5504},
|
||||
{"ad5501", ID_AD5501},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5504_id);
|
||||
|
||||
static struct spi_driver ad5504_driver = {
|
||||
.driver = {
|
||||
.name = "ad5504",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5504_probe,
|
||||
.remove = ad5504_remove,
|
||||
.id_table = ad5504_id,
|
||||
};
|
||||
module_spi_driver(ad5504_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
79
drivers/iio/dac/ad5624r.h
Normal file
79
drivers/iio/dac/ad5624r.h
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* AD5624R SPI DAC driver
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
#ifndef SPI_AD5624R_H_
|
||||
#define SPI_AD5624R_H_
|
||||
|
||||
#define AD5624R_DAC_CHANNELS 4
|
||||
|
||||
#define AD5624R_ADDR_DAC0 0x0
|
||||
#define AD5624R_ADDR_DAC1 0x1
|
||||
#define AD5624R_ADDR_DAC2 0x2
|
||||
#define AD5624R_ADDR_DAC3 0x3
|
||||
#define AD5624R_ADDR_ALL_DAC 0x7
|
||||
|
||||
#define AD5624R_CMD_WRITE_INPUT_N 0x0
|
||||
#define AD5624R_CMD_UPDATE_DAC_N 0x1
|
||||
#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_ALL 0x2
|
||||
#define AD5624R_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5624R_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5624R_CMD_RESET 0x5
|
||||
#define AD5624R_CMD_LDAC_SETUP 0x6
|
||||
#define AD5624R_CMD_INTERNAL_REFER_SETUP 0x7
|
||||
|
||||
#define AD5624R_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5624R_LDAC_PWRDN_1K 0x1
|
||||
#define AD5624R_LDAC_PWRDN_100K 0x2
|
||||
#define AD5624R_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5624r_chip_info - chip specific information
|
||||
* @channels: channel spec for the DAC
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
*/
|
||||
|
||||
struct ad5624r_chip_info {
|
||||
const struct iio_chan_spec *channels;
|
||||
u16 int_vref_mv;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @indio_dev: the industrial I/O device
|
||||
* @us: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask power down mask
|
||||
* @pwr_down_mode current power down mode
|
||||
*/
|
||||
|
||||
struct ad5624r_state {
|
||||
struct spi_device *us;
|
||||
const struct ad5624r_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5624r_supported_device_ids:
|
||||
* The AD5624/44/64 parts are available in different
|
||||
* fixed internal reference voltage options.
|
||||
*/
|
||||
|
||||
enum ad5624r_supported_device_ids {
|
||||
ID_AD5624R3,
|
||||
ID_AD5644R3,
|
||||
ID_AD5664R3,
|
||||
ID_AD5624R5,
|
||||
ID_AD5644R5,
|
||||
ID_AD5664R5,
|
||||
};
|
||||
|
||||
#endif /* SPI_AD5624R_H_ */
|
||||
319
drivers/iio/dac/ad5624r_spi.c
Normal file
319
drivers/iio/dac/ad5624r_spi.c
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
|
||||
*
|
||||
* Copyright 2010-2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include "ad5624r.h"
|
||||
|
||||
static int ad5624r_spi_write(struct spi_device *spi,
|
||||
u8 cmd, u8 addr, u16 val, u8 len)
|
||||
{
|
||||
u32 data;
|
||||
u8 msg[3];
|
||||
|
||||
/*
|
||||
* The input shift register is 24 bits wide. The first two bits are
|
||||
* don't care bits. The next three are the command bits, C2 to C0,
|
||||
* followed by the 3-bit DAC address, A2 to A0, and then the
|
||||
* 16-, 14-, 12-bit data-word. The data-word comprises the 16-,
|
||||
* 14-, 12-bit input code followed by 0, 2, or 4 don't care bits,
|
||||
* for the AD5664R, AD5644R, and AD5624R, respectively.
|
||||
*/
|
||||
data = (0 << 22) | (cmd << 19) | (addr << 16) | (val << (16 - len));
|
||||
msg[0] = data >> 16;
|
||||
msg[1] = data >> 8;
|
||||
msg[2] = data;
|
||||
|
||||
return spi_write(spi, msg, 3);
|
||||
}
|
||||
|
||||
static int ad5624r_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5624r_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5624r_spi_write(st->us,
|
||||
AD5624R_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address, val,
|
||||
chan->scan_type.shift);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const char * const ad5624r_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state"
|
||||
};
|
||||
|
||||
static int ad5624r_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5624r_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5624r_powerdown_mode_enum = {
|
||||
.items = ad5624r_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5624r_powerdown_modes),
|
||||
.get = ad5624r_get_powerdown_mode,
|
||||
.set = ad5624r_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
!!(st->pwr_down_mask & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (pwr_down)
|
||||
st->pwr_down_mask |= (1 << chan->channel);
|
||||
else
|
||||
st->pwr_down_mask &= ~(1 << chan->channel);
|
||||
|
||||
ret = ad5624r_spi_write(st->us, AD5624R_CMD_POWERDOWN_DAC, 0,
|
||||
(st->pwr_down_mode << 4) |
|
||||
st->pwr_down_mask, 16);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5624r_info = {
|
||||
.write_raw = ad5624r_write_raw,
|
||||
.read_raw = ad5624r_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5624r_read_dac_powerdown,
|
||||
.write = ad5624r_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5624r_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5624R_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (_chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5624r_ext_info, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5624R_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD5624R_CHANNEL(0, _bits), \
|
||||
AD5624R_CHANNEL(1, _bits), \
|
||||
AD5624R_CHANNEL(2, _bits), \
|
||||
AD5624R_CHANNEL(3, _bits), \
|
||||
}
|
||||
|
||||
static DECLARE_AD5624R_CHANNELS(ad5624r, 12);
|
||||
static DECLARE_AD5624R_CHANNELS(ad5644r, 14);
|
||||
static DECLARE_AD5624R_CHANNELS(ad5664r, 16);
|
||||
|
||||
static const struct ad5624r_chip_info ad5624r_chip_info_tbl[] = {
|
||||
[ID_AD5624R3] = {
|
||||
.channels = ad5624r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5624R5] = {
|
||||
.channels = ad5624r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5644R3] = {
|
||||
.channels = ad5644r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5644R5] = {
|
||||
.channels = ad5644r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5664R3] = {
|
||||
.channels = ad5664r_channels,
|
||||
.int_vref_mv = 1250,
|
||||
},
|
||||
[ID_AD5664R5] = {
|
||||
.channels = ad5664r_channels,
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5624r_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5624r_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->chip_info =
|
||||
&ad5624r_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
|
||||
st->us = spi;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5624r_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
indio_dev->num_channels = AD5624R_DAC_CHANNELS;
|
||||
|
||||
ret = ad5624r_spi_write(spi, AD5624R_CMD_INTERNAL_REFER_SETUP, 0,
|
||||
!!voltage_uv, 16);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5624r_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5624r_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5624r_id[] = {
|
||||
{"ad5624r3", ID_AD5624R3},
|
||||
{"ad5644r3", ID_AD5644R3},
|
||||
{"ad5664r3", ID_AD5664R3},
|
||||
{"ad5624r5", ID_AD5624R5},
|
||||
{"ad5644r5", ID_AD5644R5},
|
||||
{"ad5664r5", ID_AD5664R5},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5624r_id);
|
||||
|
||||
static struct spi_driver ad5624r_driver = {
|
||||
.driver = {
|
||||
.name = "ad5624r",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5624r_probe,
|
||||
.remove = ad5624r_remove,
|
||||
.id_table = ad5624r_id,
|
||||
};
|
||||
module_spi_driver(ad5624r_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5624/44/64R DAC spi driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
408
drivers/iio/dac/ad5686.c
Normal file
408
drivers/iio/dac/ad5686.c
Normal file
|
|
@ -0,0 +1,408 @@
|
|||
/*
|
||||
* AD5686R, AD5685R, AD5684R Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5686_DAC_CHANNELS 4
|
||||
|
||||
#define AD5686_ADDR(x) ((x) << 16)
|
||||
#define AD5686_CMD(x) ((x) << 20)
|
||||
|
||||
#define AD5686_ADDR_DAC(chan) (0x1 << (chan))
|
||||
#define AD5686_ADDR_ALL_DAC 0xF
|
||||
|
||||
#define AD5686_CMD_NOOP 0x0
|
||||
#define AD5686_CMD_WRITE_INPUT_N 0x1
|
||||
#define AD5686_CMD_UPDATE_DAC_N 0x2
|
||||
#define AD5686_CMD_WRITE_INPUT_N_UPDATE_N 0x3
|
||||
#define AD5686_CMD_POWERDOWN_DAC 0x4
|
||||
#define AD5686_CMD_LDAC_MASK 0x5
|
||||
#define AD5686_CMD_RESET 0x6
|
||||
#define AD5686_CMD_INTERNAL_REFER_SETUP 0x7
|
||||
#define AD5686_CMD_DAISY_CHAIN_ENABLE 0x8
|
||||
#define AD5686_CMD_READBACK_ENABLE 0x9
|
||||
|
||||
#define AD5686_LDAC_PWRDN_NONE 0x0
|
||||
#define AD5686_LDAC_PWRDN_1K 0x1
|
||||
#define AD5686_LDAC_PWRDN_100K 0x2
|
||||
#define AD5686_LDAC_PWRDN_3STATE 0x3
|
||||
|
||||
/**
|
||||
* struct ad5686_chip_info - chip specific information
|
||||
* @int_vref_mv: AD5620/40/60: the internal reference voltage
|
||||
* @channel: channel specification
|
||||
*/
|
||||
|
||||
struct ad5686_chip_info {
|
||||
u16 int_vref_mv;
|
||||
struct iio_chan_spec channel[AD5686_DAC_CHANNELS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5446_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @reg: supply regulator
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @pwr_down_mask: power down mask
|
||||
* @pwr_down_mode: current power down mode
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5686_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5686_chip_info *chip_info;
|
||||
struct regulator *reg;
|
||||
unsigned short vref_mv;
|
||||
unsigned pwr_down_mask;
|
||||
unsigned pwr_down_mode;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5686_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5686_supported_device_ids {
|
||||
ID_AD5684,
|
||||
ID_AD5685,
|
||||
ID_AD5686,
|
||||
};
|
||||
static int ad5686_spi_write(struct ad5686_state *st,
|
||||
u8 cmd, u8 addr, u16 val, u8 shift)
|
||||
{
|
||||
val <<= shift;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
|
||||
AD5686_ADDR(addr) |
|
||||
val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
|
||||
{
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[2].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
int ret;
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_READBACK_ENABLE) |
|
||||
AD5686_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5686_CMD(AD5686_CMD_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return be32_to_cpu(st->data[2].d32);
|
||||
}
|
||||
|
||||
static const char * const ad5686_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"three_state"
|
||||
};
|
||||
|
||||
static int ad5686_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
return ((st->pwr_down_mode >> (chan->channel * 2)) & 0x3) - 1;
|
||||
}
|
||||
|
||||
static int ad5686_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode &= ~(0x3 << (chan->channel * 2));
|
||||
st->pwr_down_mode |= ((mode + 1) << (chan->channel * 2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5686_powerdown_mode_enum = {
|
||||
.items = ad5686_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5686_powerdown_modes),
|
||||
.get = ad5686_get_powerdown_mode,
|
||||
.set = ad5686_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", !!(st->pwr_down_mask &
|
||||
(0x3 << (chan->channel * 2))));
|
||||
}
|
||||
|
||||
static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool readin;
|
||||
int ret;
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &readin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (readin)
|
||||
st->pwr_down_mask |= (0x3 << (chan->channel * 2));
|
||||
else
|
||||
st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
|
||||
|
||||
ret = ad5686_spi_write(st, AD5686_CMD_POWERDOWN_DAC, 0,
|
||||
st->pwr_down_mask & st->pwr_down_mode, 0);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5686_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5686_spi_read(st, chan->address);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5686_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5686_spi_write(st,
|
||||
AD5686_CMD_WRITE_INPUT_N_UPDATE_N,
|
||||
chan->address,
|
||||
val,
|
||||
chan->scan_type.shift);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5686_info = {
|
||||
.read_raw = ad5686_read_raw,
|
||||
.write_raw = ad5686_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5686_read_dac_powerdown,
|
||||
.write = ad5686_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5868_CHANNEL(chan, bits, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
|
||||
.address = AD5686_ADDR_DAC(chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = ad5686_ext_info, \
|
||||
}
|
||||
|
||||
static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
|
||||
[ID_AD5684] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 12, 4),
|
||||
.channel[1] = AD5868_CHANNEL(1, 12, 4),
|
||||
.channel[2] = AD5868_CHANNEL(2, 12, 4),
|
||||
.channel[3] = AD5868_CHANNEL(3, 12, 4),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5685] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 14, 2),
|
||||
.channel[1] = AD5868_CHANNEL(1, 14, 2),
|
||||
.channel[2] = AD5868_CHANNEL(2, 14, 2),
|
||||
.channel[3] = AD5868_CHANNEL(3, 14, 2),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
[ID_AD5686] = {
|
||||
.channel[0] = AD5868_CHANNEL(0, 16, 0),
|
||||
.channel[1] = AD5868_CHANNEL(1, 16, 0),
|
||||
.channel[2] = AD5868_CHANNEL(2, 16, 0),
|
||||
.channel[3] = AD5868_CHANNEL(3, 16, 0),
|
||||
.int_vref_mv = 2500,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static int ad5686_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5686_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret, voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg;
|
||||
|
||||
voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->chip_info =
|
||||
&ad5686_chip_info_tbl[spi_get_device_id(spi)->driver_data];
|
||||
|
||||
if (voltage_uv)
|
||||
st->vref_mv = voltage_uv / 1000;
|
||||
else
|
||||
st->vref_mv = st->chip_info->int_vref_mv;
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
/* Set all the power down mode for all channels to 1K pulldown */
|
||||
st->pwr_down_mode = 0x55;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5686_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = st->chip_info->channel;
|
||||
indio_dev->num_channels = AD5686_DAC_CHANNELS;
|
||||
|
||||
ret = ad5686_spi_write(st, AD5686_CMD_INTERNAL_REFER_SETUP, 0,
|
||||
!!voltage_uv, 0);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5686_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5686_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5686_id[] = {
|
||||
{"ad5684", ID_AD5684},
|
||||
{"ad5685", ID_AD5685},
|
||||
{"ad5686", ID_AD5686},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5686_id);
|
||||
|
||||
static struct spi_driver ad5686_driver = {
|
||||
.driver = {
|
||||
.name = "ad5686",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5686_probe,
|
||||
.remove = ad5686_remove,
|
||||
.id_table = ad5686_id,
|
||||
};
|
||||
module_spi_driver(ad5686_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
622
drivers/iio/dac/ad5755.c
Normal file
622
drivers/iio/dac/ad5755.c
Normal file
|
|
@ -0,0 +1,622 @@
|
|||
/*
|
||||
* AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/platform_data/ad5755.h>
|
||||
|
||||
#define AD5755_NUM_CHANNELS 4
|
||||
|
||||
#define AD5755_ADDR(x) ((x) << 16)
|
||||
|
||||
#define AD5755_WRITE_REG_DATA(chan) (chan)
|
||||
#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan))
|
||||
#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan))
|
||||
#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan))
|
||||
|
||||
#define AD5755_READ_REG_DATA(chan) (chan)
|
||||
#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan))
|
||||
#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan))
|
||||
#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan))
|
||||
#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan))
|
||||
#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan))
|
||||
#define AD5755_READ_REG_STATUS 0x18
|
||||
#define AD5755_READ_REG_MAIN 0x19
|
||||
#define AD5755_READ_REG_DC_DC 0x1a
|
||||
|
||||
#define AD5755_CTRL_REG_SLEW 0x0
|
||||
#define AD5755_CTRL_REG_MAIN 0x1
|
||||
#define AD5755_CTRL_REG_DAC 0x2
|
||||
#define AD5755_CTRL_REG_DC_DC 0x3
|
||||
#define AD5755_CTRL_REG_SW 0x4
|
||||
|
||||
#define AD5755_READ_FLAG 0x800000
|
||||
|
||||
#define AD5755_NOOP 0x1CE000
|
||||
|
||||
#define AD5755_DAC_INT_EN BIT(8)
|
||||
#define AD5755_DAC_CLR_EN BIT(7)
|
||||
#define AD5755_DAC_OUT_EN BIT(6)
|
||||
#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5)
|
||||
#define AD5755_DAC_DC_DC_EN BIT(4)
|
||||
#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3)
|
||||
|
||||
#define AD5755_DC_DC_MAXV 0
|
||||
#define AD5755_DC_DC_FREQ_SHIFT 2
|
||||
#define AD5755_DC_DC_PHASE_SHIFT 4
|
||||
#define AD5755_EXT_DC_DC_COMP_RES BIT(6)
|
||||
|
||||
#define AD5755_SLEW_STEP_SIZE_SHIFT 0
|
||||
#define AD5755_SLEW_RATE_SHIFT 3
|
||||
#define AD5755_SLEW_ENABLE BIT(12)
|
||||
|
||||
/**
|
||||
* struct ad5755_chip_info - chip specific information
|
||||
* @channel_template: channel specification
|
||||
* @calib_shift: shift for the calibration data registers
|
||||
* @has_voltage_out: whether the chip has voltage outputs
|
||||
*/
|
||||
struct ad5755_chip_info {
|
||||
const struct iio_chan_spec channel_template;
|
||||
unsigned int calib_shift;
|
||||
bool has_voltage_out;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5755_state - driver instance specific data
|
||||
* @spi: spi device the driver is attached to
|
||||
* @chip_info: chip model specific constants, available modes etc
|
||||
* @pwr_down: bitmask which contains hether a channel is powered down or not
|
||||
* @ctrl: software shadow of the channel ctrl registers
|
||||
* @channels: iio channel spec for the device
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
struct ad5755_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5755_chip_info *chip_info;
|
||||
unsigned int pwr_down;
|
||||
unsigned int ctrl[AD5755_NUM_CHANNELS];
|
||||
struct iio_chan_spec channels[AD5755_NUM_CHANNELS];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5755_type {
|
||||
ID_AD5755,
|
||||
ID_AD5757,
|
||||
ID_AD5735,
|
||||
ID_AD5737,
|
||||
};
|
||||
|
||||
static int ad5755_write_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((reg << 16) | val);
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev,
|
||||
unsigned int channel, unsigned int reg, unsigned int val)
|
||||
{
|
||||
return ad5755_write_unlocked(indio_dev,
|
||||
AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val);
|
||||
}
|
||||
|
||||
static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5755_write_unlocked(indio_dev, reg, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
|
||||
unsigned int reg, unsigned int val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
|
||||
st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
ret = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev,
|
||||
unsigned int channel, unsigned int set, unsigned int clr)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
st->ctrl[channel] |= set;
|
||||
st->ctrl[channel] &= ~clr;
|
||||
|
||||
ret = ad5755_write_ctrl_unlocked(indio_dev, channel,
|
||||
AD5755_CTRL_REG_DAC, st->ctrl[channel]);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev,
|
||||
unsigned int channel, bool pwr_down)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
unsigned int mask = BIT(channel);
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if ((bool)(st->pwr_down & mask) == pwr_down)
|
||||
goto out_unlock;
|
||||
|
||||
if (!pwr_down) {
|
||||
st->pwr_down &= ~mask;
|
||||
ad5755_update_dac_ctrl(indio_dev, channel,
|
||||
AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0);
|
||||
udelay(200);
|
||||
ad5755_update_dac_ctrl(indio_dev, channel,
|
||||
AD5755_DAC_OUT_EN, 0);
|
||||
} else {
|
||||
st->pwr_down |= mask;
|
||||
ad5755_update_dac_ctrl(indio_dev, channel,
|
||||
0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN |
|
||||
AD5755_DAC_DC_DC_EN);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const int ad5755_min_max_table[][2] = {
|
||||
[AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
|
||||
[AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
|
||||
[AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
|
||||
[AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
|
||||
[AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
|
||||
[AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
|
||||
[AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
|
||||
};
|
||||
|
||||
static void ad5755_get_min_max(struct ad5755_state *st,
|
||||
struct iio_chan_spec const *chan, int *min, int *max)
|
||||
{
|
||||
enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
|
||||
*min = ad5755_min_max_table[mode][0];
|
||||
*max = ad5755_min_max_table[mode][1];
|
||||
}
|
||||
|
||||
static inline int ad5755_get_offset(struct ad5755_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int min, max;
|
||||
|
||||
ad5755_get_min_max(st, chan, &min, &max);
|
||||
return (min * (1 << chan->scan_type.realbits)) / (max - min);
|
||||
}
|
||||
|
||||
static int ad5755_chan_reg_info(struct ad5755_state *st,
|
||||
struct iio_chan_spec const *chan, long info, bool write,
|
||||
unsigned int *reg, unsigned int *shift, unsigned int *offset)
|
||||
{
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (write)
|
||||
*reg = AD5755_WRITE_REG_DATA(chan->address);
|
||||
else
|
||||
*reg = AD5755_READ_REG_DATA(chan->address);
|
||||
*shift = chan->scan_type.shift;
|
||||
*offset = 0;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (write)
|
||||
*reg = AD5755_WRITE_REG_OFFSET(chan->address);
|
||||
else
|
||||
*reg = AD5755_READ_REG_OFFSET(chan->address);
|
||||
*shift = st->chip_info->calib_shift;
|
||||
*offset = 32768;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (write)
|
||||
*reg = AD5755_WRITE_REG_GAIN(chan->address);
|
||||
else
|
||||
*reg = AD5755_READ_REG_GAIN(chan->address);
|
||||
*shift = st->chip_info->calib_shift;
|
||||
*offset = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5755_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
unsigned int reg, shift, offset;
|
||||
int min, max;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
ad5755_get_min_max(st, chan, &min, &max);
|
||||
*val = max - min;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = ad5755_get_offset(st, chan);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
ret = ad5755_chan_reg_info(st, chan, info, false,
|
||||
®, &shift, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5755_read(indio_dev, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = (ret - offset) >> shift;
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad5755_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
unsigned int shift, reg, offset;
|
||||
int ret;
|
||||
|
||||
ret = ad5755_chan_reg_info(st, chan, info, true,
|
||||
®, &shift, &offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val <<= shift;
|
||||
val += offset;
|
||||
|
||||
if (val < 0 || val > 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
return ad5755_write(indio_dev, reg, val);
|
||||
}
|
||||
|
||||
static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
|
||||
const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n",
|
||||
(bool)(st->pwr_down & (1 << chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
|
||||
struct iio_chan_spec const *chan, const char *buf, size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down);
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5755_info = {
|
||||
.read_raw = ad5755_read_raw,
|
||||
.write_raw = ad5755_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad5755_read_powerdown,
|
||||
.write = ad5755_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5755_CHANNEL(_bits) { \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
.ext_info = ad5755_ext_info, \
|
||||
}
|
||||
|
||||
static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
|
||||
[ID_AD5735] = {
|
||||
.channel_template = AD5755_CHANNEL(14),
|
||||
.has_voltage_out = true,
|
||||
.calib_shift = 4,
|
||||
},
|
||||
[ID_AD5737] = {
|
||||
.channel_template = AD5755_CHANNEL(14),
|
||||
.has_voltage_out = false,
|
||||
.calib_shift = 4,
|
||||
},
|
||||
[ID_AD5755] = {
|
||||
.channel_template = AD5755_CHANNEL(16),
|
||||
.has_voltage_out = true,
|
||||
.calib_shift = 0,
|
||||
},
|
||||
[ID_AD5757] = {
|
||||
.channel_template = AD5755_CHANNEL(16),
|
||||
.has_voltage_out = false,
|
||||
.calib_shift = 0,
|
||||
},
|
||||
};
|
||||
|
||||
static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case AD5755_MODE_VOLTAGE_0V_5V:
|
||||
case AD5755_MODE_VOLTAGE_0V_10V:
|
||||
case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
|
||||
case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
|
||||
return st->chip_info->has_voltage_out;
|
||||
case AD5755_MODE_CURRENT_4mA_20mA:
|
||||
case AD5755_MODE_CURRENT_0mA_20mA:
|
||||
case AD5755_MODE_CURRENT_0mA_24mA:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5755_setup_pdata(struct iio_dev *indio_dev,
|
||||
const struct ad5755_platform_data *pdata)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
unsigned int val;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE ||
|
||||
pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ ||
|
||||
pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5)
|
||||
return -EINVAL;
|
||||
|
||||
val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV;
|
||||
val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT;
|
||||
val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT;
|
||||
if (pdata->ext_dc_dc_compenstation_resistor)
|
||||
val |= AD5755_EXT_DC_DC_COMP_RES;
|
||||
|
||||
ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
|
||||
val = pdata->dac[i].slew.step_size <<
|
||||
AD5755_SLEW_STEP_SIZE_SHIFT;
|
||||
val |= pdata->dac[i].slew.rate <<
|
||||
AD5755_SLEW_RATE_SHIFT;
|
||||
if (pdata->dac[i].slew.enable)
|
||||
val |= AD5755_SLEW_ENABLE;
|
||||
|
||||
ret = ad5755_write_ctrl(indio_dev, i,
|
||||
AD5755_CTRL_REG_SLEW, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
|
||||
if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
|
||||
return -EINVAL;
|
||||
|
||||
val = 0;
|
||||
if (!pdata->dac[i].ext_current_sense_resistor)
|
||||
val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
|
||||
if (pdata->dac[i].enable_voltage_overrange)
|
||||
val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
|
||||
val |= pdata->dac[i].mode;
|
||||
|
||||
ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool ad5755_is_voltage_mode(enum ad5755_mode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case AD5755_MODE_VOLTAGE_0V_5V:
|
||||
case AD5755_MODE_VOLTAGE_0V_10V:
|
||||
case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
|
||||
case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int ad5755_init_channels(struct iio_dev *indio_dev,
|
||||
const struct ad5755_platform_data *pdata)
|
||||
{
|
||||
struct ad5755_state *st = iio_priv(indio_dev);
|
||||
struct iio_chan_spec *channels = st->channels;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
|
||||
channels[i] = st->chip_info->channel_template;
|
||||
channels[i].channel = i;
|
||||
channels[i].address = i;
|
||||
if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
|
||||
channels[i].type = IIO_VOLTAGE;
|
||||
else
|
||||
channels[i].type = IIO_CURRENT;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define AD5755_DEFAULT_DAC_PDATA { \
|
||||
.mode = AD5755_MODE_CURRENT_4mA_20mA, \
|
||||
.ext_current_sense_resistor = true, \
|
||||
.enable_voltage_overrange = false, \
|
||||
.slew = { \
|
||||
.enable = false, \
|
||||
.rate = AD5755_SLEW_RATE_64k, \
|
||||
.step_size = AD5755_SLEW_STEP_SIZE_1, \
|
||||
}, \
|
||||
}
|
||||
|
||||
static const struct ad5755_platform_data ad5755_default_pdata = {
|
||||
.ext_dc_dc_compenstation_resistor = false,
|
||||
.dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
|
||||
.dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
|
||||
.dc_dc_maxv = AD5755_DC_DC_MAXV_23V,
|
||||
.dac = {
|
||||
[0] = AD5755_DEFAULT_DAC_PDATA,
|
||||
[1] = AD5755_DEFAULT_DAC_PDATA,
|
||||
[2] = AD5755_DEFAULT_DAC_PDATA,
|
||||
[3] = AD5755_DEFAULT_DAC_PDATA,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5755_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5755_type type = spi_get_device_id(spi)->driver_data;
|
||||
const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5755_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&spi->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->chip_info = &ad5755_chip_info_tbl[type];
|
||||
st->spi = spi;
|
||||
st->pwr_down = 0xf;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5755_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = AD5755_NUM_CHANNELS;
|
||||
|
||||
if (!pdata)
|
||||
pdata = &ad5755_default_pdata;
|
||||
|
||||
ret = ad5755_init_channels(indio_dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ad5755_setup_pdata(indio_dev, pdata);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5755_id[] = {
|
||||
{ "ad5755", ID_AD5755 },
|
||||
{ "ad5755-1", ID_AD5755 },
|
||||
{ "ad5757", ID_AD5757 },
|
||||
{ "ad5735", ID_AD5735 },
|
||||
{ "ad5737", ID_AD5737 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5755_id);
|
||||
|
||||
static struct spi_driver ad5755_driver = {
|
||||
.driver = {
|
||||
.name = "ad5755",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5755_probe,
|
||||
.id_table = ad5755_id,
|
||||
};
|
||||
module_spi_driver(ad5755_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
370
drivers/iio/dac/ad5764.c
Normal file
370
drivers/iio/dac/ad5764.c
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
/*
|
||||
* Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
|
||||
* Digital to Analog Converters driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define AD5764_REG_SF_NOP 0x0
|
||||
#define AD5764_REG_SF_CONFIG 0x1
|
||||
#define AD5764_REG_SF_CLEAR 0x4
|
||||
#define AD5764_REG_SF_LOAD 0x5
|
||||
#define AD5764_REG_DATA(x) ((2 << 3) | (x))
|
||||
#define AD5764_REG_COARSE_GAIN(x) ((3 << 3) | (x))
|
||||
#define AD5764_REG_FINE_GAIN(x) ((4 << 3) | (x))
|
||||
#define AD5764_REG_OFFSET(x) ((5 << 3) | (x))
|
||||
|
||||
#define AD5764_NUM_CHANNELS 4
|
||||
|
||||
/**
|
||||
* struct ad5764_chip_info - chip specific information
|
||||
* @int_vref: Value of the internal reference voltage in uV - 0 if external
|
||||
* reference voltage is used
|
||||
* @channel channel specification
|
||||
*/
|
||||
|
||||
struct ad5764_chip_info {
|
||||
unsigned long int_vref;
|
||||
const struct iio_chan_spec *channels;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5764_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @chip_info: chip info
|
||||
* @vref_reg: vref supply regulators
|
||||
* @data: spi transfer buffers
|
||||
*/
|
||||
|
||||
struct ad5764_state {
|
||||
struct spi_device *spi;
|
||||
const struct ad5764_chip_info *chip_info;
|
||||
struct regulator_bulk_data vref_reg[2];
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
enum ad5764_type {
|
||||
ID_AD5744,
|
||||
ID_AD5744R,
|
||||
ID_AD5764,
|
||||
ID_AD5764R,
|
||||
};
|
||||
|
||||
#define AD5764_CHANNEL(_chan, _bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (_chan), \
|
||||
.address = (_chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE) | \
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (_bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 16 - (_bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define DECLARE_AD5764_CHANNELS(_name, _bits) \
|
||||
const struct iio_chan_spec _name##_channels[] = { \
|
||||
AD5764_CHANNEL(0, (_bits)), \
|
||||
AD5764_CHANNEL(1, (_bits)), \
|
||||
AD5764_CHANNEL(2, (_bits)), \
|
||||
AD5764_CHANNEL(3, (_bits)), \
|
||||
};
|
||||
|
||||
static DECLARE_AD5764_CHANNELS(ad5764, 16);
|
||||
static DECLARE_AD5764_CHANNELS(ad5744, 14);
|
||||
|
||||
static const struct ad5764_chip_info ad5764_chip_infos[] = {
|
||||
[ID_AD5744] = {
|
||||
.int_vref = 0,
|
||||
.channels = ad5744_channels,
|
||||
},
|
||||
[ID_AD5744R] = {
|
||||
.int_vref = 5000000,
|
||||
.channels = ad5744_channels,
|
||||
},
|
||||
[ID_AD5764] = {
|
||||
.int_vref = 0,
|
||||
.channels = ad5764_channels,
|
||||
},
|
||||
[ID_AD5764R] = {
|
||||
.int_vref = 5000000,
|
||||
.channels = ad5764_channels,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5764_write(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int val)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
st->data[0].d32 = cpu_to_be32((reg << 16) | val);
|
||||
|
||||
ret = spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_read(struct iio_dev *indio_dev, unsigned int reg,
|
||||
unsigned int *val)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->data[1].d8[1],
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
st->data[0].d32 = cpu_to_be32((1 << 23) | (reg << 16));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
|
||||
if (ret >= 0)
|
||||
*val = be32_to_cpu(st->data[1].d32) & 0xffff;
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_chan_info_to_reg(struct iio_chan_spec const *chan, long info)
|
||||
{
|
||||
switch (info) {
|
||||
case 0:
|
||||
return AD5764_REG_DATA(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return AD5764_REG_OFFSET(chan->address);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
return AD5764_REG_FINE_GAIN(chan->address);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad5764_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long info)
|
||||
{
|
||||
const int max_val = (1 << chan->scan_type.realbits);
|
||||
unsigned int reg;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= max_val || val < 0)
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val >= 128 || val < -128)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val >= 32 || val < -32)
|
||||
return -EINVAL;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = ad5764_chan_info_to_reg(chan, info);
|
||||
return ad5764_write(indio_dev, reg, (u16)val);
|
||||
}
|
||||
|
||||
static int ad5764_get_channel_vref(struct ad5764_state *st,
|
||||
unsigned int channel)
|
||||
{
|
||||
if (st->chip_info->int_vref)
|
||||
return st->chip_info->int_vref;
|
||||
else
|
||||
return regulator_get_voltage(st->vref_reg[channel / 2].consumer);
|
||||
}
|
||||
|
||||
static int ad5764_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
unsigned int reg;
|
||||
int vref;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
reg = AD5764_REG_DATA(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
reg = AD5764_REG_OFFSET(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(*val, 7);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
reg = AD5764_REG_FINE_GAIN(chan->address);
|
||||
ret = ad5764_read(indio_dev, reg, val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = sign_extend32(*val, 5);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* vout = 4 * vref + ((dac_code / 65536) - 0.5) */
|
||||
vref = ad5764_get_channel_vref(st, chan->channel);
|
||||
if (vref < 0)
|
||||
return vref;
|
||||
|
||||
*val = vref * 4 / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = -(1 << chan->scan_type.realbits) / 2;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info ad5764_info = {
|
||||
.read_raw = ad5764_read_raw,
|
||||
.write_raw = ad5764_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5764_probe(struct spi_device *spi)
|
||||
{
|
||||
enum ad5764_type type = spi_get_device_id(spi)->driver_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5764_state *st;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
dev_err(&spi->dev, "Failed to allocate iio device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
st->chip_info = &ad5764_chip_infos[type];
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->info = &ad5764_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->num_channels = AD5764_NUM_CHANNELS;
|
||||
indio_dev->channels = st->chip_info->channels;
|
||||
|
||||
if (st->chip_info->int_vref == 0) {
|
||||
st->vref_reg[0].supply = "vrefAB";
|
||||
st->vref_reg[1].supply = "vrefCD";
|
||||
|
||||
ret = devm_regulator_bulk_get(&st->spi->dev,
|
||||
ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to request vref regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(ARRAY_SIZE(st->vref_reg),
|
||||
st->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulators: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (st->chip_info->int_vref == 0)
|
||||
regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5764_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5764_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->chip_info->int_vref == 0)
|
||||
regulator_bulk_disable(ARRAY_SIZE(st->vref_reg), st->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5764_ids[] = {
|
||||
{ "ad5744", ID_AD5744 },
|
||||
{ "ad5744r", ID_AD5744R },
|
||||
{ "ad5764", ID_AD5764 },
|
||||
{ "ad5764r", ID_AD5764R },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5764_ids);
|
||||
|
||||
static struct spi_driver ad5764_driver = {
|
||||
.driver = {
|
||||
.name = "ad5764",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5764_probe,
|
||||
.remove = ad5764_remove,
|
||||
.id_table = ad5764_ids,
|
||||
};
|
||||
module_spi_driver(ad5764_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5744/AD5744R/AD5764/AD5764R DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
474
drivers/iio/dac/ad5791.c
Normal file
474
drivers/iio/dac/ad5791.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog
|
||||
* Converter
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/dac/ad5791.h>
|
||||
|
||||
#define AD5791_DAC_MASK GENMASK(19, 0)
|
||||
|
||||
#define AD5791_CMD_READ BIT(23)
|
||||
#define AD5791_CMD_WRITE 0
|
||||
#define AD5791_ADDR(addr) ((addr) << 20)
|
||||
|
||||
/* Registers */
|
||||
#define AD5791_ADDR_NOOP 0
|
||||
#define AD5791_ADDR_DAC0 1
|
||||
#define AD5791_ADDR_CTRL 2
|
||||
#define AD5791_ADDR_CLRCODE 3
|
||||
#define AD5791_ADDR_SW_CTRL 4
|
||||
|
||||
/* Control Register */
|
||||
#define AD5791_CTRL_RBUF BIT(1)
|
||||
#define AD5791_CTRL_OPGND BIT(2)
|
||||
#define AD5791_CTRL_DACTRI BIT(3)
|
||||
#define AD5791_CTRL_BIN2SC BIT(4)
|
||||
#define AD5791_CTRL_SDODIS BIT(5)
|
||||
#define AD5761_CTRL_LINCOMP(x) ((x) << 6)
|
||||
|
||||
#define AD5791_LINCOMP_0_10 0
|
||||
#define AD5791_LINCOMP_10_12 1
|
||||
#define AD5791_LINCOMP_12_16 2
|
||||
#define AD5791_LINCOMP_16_19 3
|
||||
#define AD5791_LINCOMP_19_20 12
|
||||
|
||||
#define AD5780_LINCOMP_0_10 0
|
||||
#define AD5780_LINCOMP_10_20 12
|
||||
|
||||
/* Software Control Register */
|
||||
#define AD5791_SWCTRL_LDAC BIT(0)
|
||||
#define AD5791_SWCTRL_CLR BIT(1)
|
||||
#define AD5791_SWCTRL_RESET BIT(2)
|
||||
|
||||
#define AD5791_DAC_PWRDN_6K 0
|
||||
#define AD5791_DAC_PWRDN_3STATE 1
|
||||
|
||||
/**
|
||||
* struct ad5791_chip_info - chip specific information
|
||||
* @get_lin_comp: function pointer to the device specific function
|
||||
*/
|
||||
|
||||
struct ad5791_chip_info {
|
||||
int (*get_lin_comp) (unsigned int span);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ad5791_state - driver instance specific data
|
||||
* @spi: spi_device
|
||||
* @reg_vdd: positive supply regulator
|
||||
* @reg_vss: negative supply regulator
|
||||
* @chip_info: chip model specific constants
|
||||
* @vref_mv: actual reference voltage used
|
||||
* @vref_neg_mv: voltage of the negative supply
|
||||
* @pwr_down_mode current power down mode
|
||||
*/
|
||||
|
||||
struct ad5791_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg_vdd;
|
||||
struct regulator *reg_vss;
|
||||
const struct ad5791_chip_info *chip_info;
|
||||
unsigned short vref_mv;
|
||||
unsigned int vref_neg_mv;
|
||||
unsigned ctrl;
|
||||
unsigned pwr_down_mode;
|
||||
bool pwr_down;
|
||||
|
||||
union {
|
||||
__be32 d32;
|
||||
u8 d8[4];
|
||||
} data[3] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
/**
|
||||
* ad5791_supported_device_ids:
|
||||
*/
|
||||
|
||||
enum ad5791_supported_device_ids {
|
||||
ID_AD5760,
|
||||
ID_AD5780,
|
||||
ID_AD5781,
|
||||
ID_AD5791,
|
||||
};
|
||||
|
||||
static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val)
|
||||
{
|
||||
st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE |
|
||||
AD5791_ADDR(addr) |
|
||||
(val & AD5791_DAC_MASK));
|
||||
|
||||
return spi_write(st->spi, &st->data[0].d8[1], 3);
|
||||
}
|
||||
|
||||
static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->data[0].d8[1],
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.tx_buf = &st->data[1].d8[1],
|
||||
.rx_buf = &st->data[2].d8[1],
|
||||
.bits_per_word = 8,
|
||||
.len = 3,
|
||||
},
|
||||
};
|
||||
|
||||
st->data[0].d32 = cpu_to_be32(AD5791_CMD_READ |
|
||||
AD5791_ADDR(addr));
|
||||
st->data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP));
|
||||
|
||||
ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers));
|
||||
|
||||
*val = be32_to_cpu(st->data[2].d32);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char * const ad5791_powerdown_modes[] = {
|
||||
"6kohm_to_gnd",
|
||||
"three_state",
|
||||
};
|
||||
|
||||
static int ad5791_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
return st->pwr_down_mode;
|
||||
}
|
||||
|
||||
static int ad5791_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned int mode)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
st->pwr_down_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum ad5791_powerdown_mode_enum = {
|
||||
.items = ad5791_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(ad5791_powerdown_modes),
|
||||
.get = ad5791_get_powerdown_mode,
|
||||
.set = ad5791_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->pwr_down);
|
||||
}
|
||||
|
||||
static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!pwr_down) {
|
||||
st->ctrl &= ~(AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
|
||||
} else {
|
||||
if (st->pwr_down_mode == AD5791_DAC_PWRDN_6K)
|
||||
st->ctrl |= AD5791_CTRL_OPGND;
|
||||
else if (st->pwr_down_mode == AD5791_DAC_PWRDN_3STATE)
|
||||
st->ctrl |= AD5791_CTRL_DACTRI;
|
||||
}
|
||||
st->pwr_down = pwr_down;
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static int ad5791_get_lin_comp(unsigned int span)
|
||||
{
|
||||
if (span <= 10000)
|
||||
return AD5791_LINCOMP_0_10;
|
||||
else if (span <= 12000)
|
||||
return AD5791_LINCOMP_10_12;
|
||||
else if (span <= 16000)
|
||||
return AD5791_LINCOMP_12_16;
|
||||
else if (span <= 19000)
|
||||
return AD5791_LINCOMP_16_19;
|
||||
else
|
||||
return AD5791_LINCOMP_19_20;
|
||||
}
|
||||
|
||||
static int ad5780_get_lin_comp(unsigned int span)
|
||||
{
|
||||
if (span <= 10000)
|
||||
return AD5780_LINCOMP_0_10;
|
||||
else
|
||||
return AD5780_LINCOMP_10_20;
|
||||
}
|
||||
static const struct ad5791_chip_info ad5791_chip_info_tbl[] = {
|
||||
[ID_AD5760] = {
|
||||
.get_lin_comp = ad5780_get_lin_comp,
|
||||
},
|
||||
[ID_AD5780] = {
|
||||
.get_lin_comp = ad5780_get_lin_comp,
|
||||
},
|
||||
[ID_AD5781] = {
|
||||
.get_lin_comp = ad5791_get_lin_comp,
|
||||
},
|
||||
[ID_AD5791] = {
|
||||
.get_lin_comp = ad5791_get_lin_comp,
|
||||
},
|
||||
};
|
||||
|
||||
static int ad5791_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
u64 val64;
|
||||
int ret;
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = ad5791_spi_read(st, chan->address, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val &= AD5791_DAC_MASK;
|
||||
*val >>= chan->scan_type.shift;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = st->vref_mv;
|
||||
*val2 = (1 << chan->scan_type.realbits) - 1;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);
|
||||
do_div(val64, st->vref_mv);
|
||||
*val = -val64;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.shared = IIO_SHARED_BY_TYPE,
|
||||
.read = ad5791_read_dac_powerdown,
|
||||
.write = ad5791_write_dac_powerdown,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE,
|
||||
&ad5791_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD5791_CHAN(bits, _shift) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.address = AD5791_ADDR_DAC0, \
|
||||
.channel = 0, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
|
||||
BIT(IIO_CHAN_INFO_OFFSET), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 24, \
|
||||
.shift = (_shift), \
|
||||
}, \
|
||||
.ext_info = ad5791_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad5791_channels[] = {
|
||||
[ID_AD5760] = AD5791_CHAN(16, 4),
|
||||
[ID_AD5780] = AD5791_CHAN(18, 2),
|
||||
[ID_AD5781] = AD5791_CHAN(18, 2),
|
||||
[ID_AD5791] = AD5791_CHAN(20, 0)
|
||||
};
|
||||
|
||||
static int ad5791_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
val &= GENMASK(chan->scan_type.realbits - 1, 0);
|
||||
val <<= chan->scan_type.shift;
|
||||
|
||||
return ad5791_spi_write(st, chan->address, val);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_info ad5791_info = {
|
||||
.read_raw = &ad5791_read_raw,
|
||||
.write_raw = &ad5791_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int ad5791_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad5791_platform_data *pdata = spi->dev.platform_data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad5791_state *st;
|
||||
int ret, pos_voltage_uv = 0, neg_voltage_uv = 0;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->reg_vdd = devm_regulator_get(&spi->dev, "vdd");
|
||||
if (!IS_ERR(st->reg_vdd)) {
|
||||
ret = regulator_enable(st->reg_vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regulator_get_voltage(st->reg_vdd);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_pos;
|
||||
|
||||
pos_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->reg_vss = devm_regulator_get(&spi->dev, "vss");
|
||||
if (!IS_ERR(st->reg_vss)) {
|
||||
ret = regulator_enable(st->reg_vss);
|
||||
if (ret)
|
||||
goto error_disable_reg_pos;
|
||||
|
||||
ret = regulator_get_voltage(st->reg_vss);
|
||||
if (ret < 0)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
neg_voltage_uv = ret;
|
||||
}
|
||||
|
||||
st->pwr_down = true;
|
||||
st->spi = spi;
|
||||
|
||||
if (!IS_ERR(st->reg_vss) && !IS_ERR(st->reg_vdd)) {
|
||||
st->vref_mv = (pos_voltage_uv + neg_voltage_uv) / 1000;
|
||||
st->vref_neg_mv = neg_voltage_uv / 1000;
|
||||
} else if (pdata) {
|
||||
st->vref_mv = pdata->vref_pos_mv + pdata->vref_neg_mv;
|
||||
st->vref_neg_mv = pdata->vref_neg_mv;
|
||||
} else {
|
||||
dev_warn(&spi->dev, "reference voltage unspecified\n");
|
||||
}
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
st->chip_info = &ad5791_chip_info_tbl[spi_get_device_id(spi)
|
||||
->driver_data];
|
||||
|
||||
|
||||
st->ctrl = AD5761_CTRL_LINCOMP(st->chip_info->get_lin_comp(st->vref_mv))
|
||||
| ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |
|
||||
AD5791_CTRL_BIN2SC;
|
||||
|
||||
ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl |
|
||||
AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &ad5791_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels
|
||||
= &ad5791_channels[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->name = spi_get_device_id(st->spi)->name;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg_neg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg_neg:
|
||||
if (!IS_ERR(st->reg_vss))
|
||||
regulator_disable(st->reg_vss);
|
||||
error_disable_reg_pos:
|
||||
if (!IS_ERR(st->reg_vdd))
|
||||
regulator_disable(st->reg_vdd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad5791_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad5791_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
if (!IS_ERR(st->reg_vdd))
|
||||
regulator_disable(st->reg_vdd);
|
||||
|
||||
if (!IS_ERR(st->reg_vss))
|
||||
regulator_disable(st->reg_vss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad5791_id[] = {
|
||||
{"ad5760", ID_AD5760},
|
||||
{"ad5780", ID_AD5780},
|
||||
{"ad5781", ID_AD5781},
|
||||
{"ad5790", ID_AD5791},
|
||||
{"ad5791", ID_AD5791},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad5791_id);
|
||||
|
||||
static struct spi_driver ad5791_driver = {
|
||||
.driver = {
|
||||
.name = "ad5791",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad5791_probe,
|
||||
.remove = ad5791_remove,
|
||||
.id_table = ad5791_id,
|
||||
};
|
||||
module_spi_driver(ad5791_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
303
drivers/iio/dac/ad7303.c
Normal file
303
drivers/iio/dac/ad7303.c
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* AD7303 Digital to analog converters driver
|
||||
*
|
||||
* Copyright 2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/platform_data/ad7303.h>
|
||||
|
||||
#define AD7303_CFG_EXTERNAL_VREF BIT(15)
|
||||
#define AD7303_CFG_POWER_DOWN(ch) BIT(11 + (ch))
|
||||
#define AD7303_CFG_ADDR_OFFSET 10
|
||||
|
||||
#define AD7303_CMD_UPDATE_DAC (0x3 << 8)
|
||||
|
||||
/**
|
||||
* struct ad7303_state - driver instance specific data
|
||||
* @spi: the device for this driver instance
|
||||
* @config: cached config register value
|
||||
* @dac_cache: current DAC raw value (chip does not support readback)
|
||||
* @data: spi transfer buffer
|
||||
*/
|
||||
|
||||
struct ad7303_state {
|
||||
struct spi_device *spi;
|
||||
uint16_t config;
|
||||
uint8_t dac_cache[2];
|
||||
|
||||
struct regulator *vdd_reg;
|
||||
struct regulator *vref_reg;
|
||||
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be16 data ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int ad7303_write(struct ad7303_state *st, unsigned int chan,
|
||||
uint8_t val)
|
||||
{
|
||||
st->data = cpu_to_be16(AD7303_CMD_UPDATE_DAC |
|
||||
(chan << AD7303_CFG_ADDR_OFFSET) |
|
||||
st->config | val);
|
||||
|
||||
return spi_write(st->spi, &st->data, sizeof(st->data));
|
||||
}
|
||||
|
||||
static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", (bool)(st->config &
|
||||
AD7303_CFG_POWER_DOWN(chan->channel)));
|
||||
}
|
||||
|
||||
static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
bool pwr_down;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &pwr_down);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
if (pwr_down)
|
||||
st->config |= AD7303_CFG_POWER_DOWN(chan->channel);
|
||||
else
|
||||
st->config &= ~AD7303_CFG_POWER_DOWN(chan->channel);
|
||||
|
||||
/* There is no noop cmd which allows us to only update the powerdown
|
||||
* mode, so just write one of the DAC channels again */
|
||||
ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int ad7303_get_vref(struct ad7303_state *st,
|
||||
struct iio_chan_spec const *chan)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (st->config & AD7303_CFG_EXTERNAL_VREF)
|
||||
return regulator_get_voltage(st->vref_reg);
|
||||
|
||||
ret = regulator_get_voltage(st->vdd_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return ret / 2;
|
||||
}
|
||||
|
||||
static int ad7303_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
int vref_uv;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = st->dac_cache[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
vref_uv = ad7303_get_vref(st, chan);
|
||||
if (vref_uv < 0)
|
||||
return vref_uv;
|
||||
|
||||
*val = 2 * vref_uv / 1000;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ad7303_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val >= (1 << chan->scan_type.realbits) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = ad7303_write(st, chan->address, val);
|
||||
if (ret == 0)
|
||||
st->dac_cache[chan->channel] = val;
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info ad7303_info = {
|
||||
.read_raw = ad7303_read_raw,
|
||||
.write_raw = ad7303_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = ad7303_read_dac_powerdown,
|
||||
.write = ad7303_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
#define AD7303_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.address = (chan), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = '8', \
|
||||
.storagebits = '8', \
|
||||
.shift = '0', \
|
||||
}, \
|
||||
.ext_info = ad7303_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec ad7303_channels[] = {
|
||||
AD7303_CHANNEL(0),
|
||||
AD7303_CHANNEL(1),
|
||||
};
|
||||
|
||||
static int ad7303_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct iio_dev *indio_dev;
|
||||
struct ad7303_state *st;
|
||||
bool ext_ref;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
st->spi = spi;
|
||||
|
||||
st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd");
|
||||
if (IS_ERR(st->vdd_reg))
|
||||
return PTR_ERR(st->vdd_reg);
|
||||
|
||||
ret = regulator_enable(st->vdd_reg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
ext_ref = of_property_read_bool(spi->dev.of_node,
|
||||
"REF-supply");
|
||||
} else {
|
||||
struct ad7303_platform_data *pdata = spi->dev.platform_data;
|
||||
if (pdata && pdata->use_external_ref)
|
||||
ext_ref = true;
|
||||
else
|
||||
ext_ref = false;
|
||||
}
|
||||
|
||||
if (ext_ref) {
|
||||
st->vref_reg = devm_regulator_get(&spi->dev, "REF");
|
||||
if (IS_ERR(st->vref_reg)) {
|
||||
ret = PTR_ERR(st->vref_reg);
|
||||
goto err_disable_vdd_reg;
|
||||
}
|
||||
|
||||
ret = regulator_enable(st->vref_reg);
|
||||
if (ret)
|
||||
goto err_disable_vdd_reg;
|
||||
|
||||
st->config |= AD7303_CFG_EXTERNAL_VREF;
|
||||
}
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->info = &ad7303_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = ad7303_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(ad7303_channels);
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto err_disable_vref_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
err_disable_vref_reg:
|
||||
if (st->vref_reg)
|
||||
regulator_disable(st->vref_reg);
|
||||
err_disable_vdd_reg:
|
||||
regulator_disable(st->vdd_reg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7303_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct ad7303_state *st = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->vref_reg)
|
||||
regulator_disable(st->vref_reg);
|
||||
regulator_disable(st->vdd_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id ad7303_spi_ids[] = {
|
||||
{ "ad7303", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ad7303_spi_ids);
|
||||
|
||||
static struct spi_driver ad7303_driver = {
|
||||
.driver = {
|
||||
.name = "ad7303",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7303_probe,
|
||||
.remove = ad7303_remove,
|
||||
.id_table = ad7303_spi_ids,
|
||||
};
|
||||
module_spi_driver(ad7303_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices AD7303 DAC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
222
drivers/iio/dac/max517.c
Normal file
222
drivers/iio/dac/max517.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* max517.c - Support for Maxim MAX517, MAX518 and MAX519
|
||||
*
|
||||
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/dac/max517.h>
|
||||
|
||||
#define MAX517_DRV_NAME "max517"
|
||||
|
||||
/* Commands */
|
||||
#define COMMAND_CHANNEL0 0x00
|
||||
#define COMMAND_CHANNEL1 0x01 /* for MAX518 and MAX519 */
|
||||
#define COMMAND_PD 0x08 /* Power Down */
|
||||
|
||||
enum max517_device_ids {
|
||||
ID_MAX517,
|
||||
ID_MAX518,
|
||||
ID_MAX519,
|
||||
};
|
||||
|
||||
struct max517_data {
|
||||
struct i2c_client *client;
|
||||
unsigned short vref_mv[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* channel: bit 0: channel 1
|
||||
* bit 1: channel 2
|
||||
* (this way, it's possible to set both channels at once)
|
||||
*/
|
||||
static int max517_set_value(struct iio_dev *indio_dev,
|
||||
long val, int channel)
|
||||
{
|
||||
struct max517_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[2];
|
||||
int res;
|
||||
|
||||
if (val < 0 || val > 255)
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = channel;
|
||||
outbuf[1] = val;
|
||||
|
||||
res = i2c_master_send(client, outbuf, 2);
|
||||
if (res < 0)
|
||||
return res;
|
||||
else if (res != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max517_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long m)
|
||||
{
|
||||
struct max517_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (m) {
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
/* Corresponds to Vref / 2^(bits) */
|
||||
*val = data->vref_mv[chan->channel];
|
||||
*val2 = 8;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int max517_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan, int val, int val2, long mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = max517_set_value(indio_dev, val, chan->channel);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max517_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf = COMMAND_PD;
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
|
||||
}
|
||||
|
||||
static int max517_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf = 0;
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
|
||||
#define MAX517_PM_OPS (&max517_pm_ops)
|
||||
#else
|
||||
#define MAX517_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static const struct iio_info max517_info = {
|
||||
.read_raw = max517_read_raw,
|
||||
.write_raw = max517_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define MAX517_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
|
||||
BIT(IIO_CHAN_INFO_SCALE), \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max517_channels[] = {
|
||||
MAX517_CHANNEL(0),
|
||||
MAX517_CHANNEL(1)
|
||||
};
|
||||
|
||||
static int max517_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max517_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct max517_platform_data *platform_data = client->dev.platform_data;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
/* establish that the iio_dev is a child of the i2c device */
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
|
||||
/* reduced channel set for MAX517 */
|
||||
if (id->driver_data == ID_MAX517)
|
||||
indio_dev->num_channels = 1;
|
||||
else
|
||||
indio_dev->num_channels = 2;
|
||||
indio_dev->channels = max517_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max517_info;
|
||||
|
||||
/*
|
||||
* Reference voltage on MAX518 and default is 5V, else take vref_mv
|
||||
* from platform_data
|
||||
*/
|
||||
if (id->driver_data == ID_MAX518 || !platform_data) {
|
||||
data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */
|
||||
} else {
|
||||
data->vref_mv[0] = platform_data->vref_mv[0];
|
||||
data->vref_mv[1] = platform_data->vref_mv[1];
|
||||
}
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int max517_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max517_id[] = {
|
||||
{ "max517", ID_MAX517 },
|
||||
{ "max518", ID_MAX518 },
|
||||
{ "max519", ID_MAX519 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max517_id);
|
||||
|
||||
static struct i2c_driver max517_driver = {
|
||||
.driver = {
|
||||
.name = MAX517_DRV_NAME,
|
||||
.pm = MAX517_PM_OPS,
|
||||
},
|
||||
.probe = max517_probe,
|
||||
.remove = max517_remove,
|
||||
.id_table = max517_id,
|
||||
};
|
||||
module_i2c_driver(max517_driver);
|
||||
|
||||
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
|
||||
MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
405
drivers/iio/dac/max5821.c
Normal file
405
drivers/iio/dac/max5821.c
Normal file
|
|
@ -0,0 +1,405 @@
|
|||
/*
|
||||
* iio/dac/max5821.c
|
||||
* Copyright (C) 2014 Philippe Reynes
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define MAX5821_MAX_DAC_CHANNELS 2
|
||||
|
||||
/* command bytes */
|
||||
#define MAX5821_LOAD_DAC_A_IN_REG_B 0x00
|
||||
#define MAX5821_LOAD_DAC_B_IN_REG_A 0x10
|
||||
#define MAX5821_EXTENDED_COMMAND_MODE 0xf0
|
||||
#define MAX5821_READ_DAC_A_COMMAND 0xf1
|
||||
#define MAX5821_READ_DAC_B_COMMAND 0xf2
|
||||
|
||||
#define MAX5821_EXTENDED_POWER_UP 0x00
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE0 0x01
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE1 0x02
|
||||
#define MAX5821_EXTENDED_POWER_DOWN_MODE2 0x03
|
||||
#define MAX5821_EXTENDED_DAC_A 0x04
|
||||
#define MAX5821_EXTENDED_DAC_B 0x08
|
||||
|
||||
enum max5821_device_ids {
|
||||
ID_MAX5821,
|
||||
};
|
||||
|
||||
struct max5821_data {
|
||||
struct i2c_client *client;
|
||||
struct regulator *vref_reg;
|
||||
unsigned short vref_mv;
|
||||
bool powerdown[MAX5821_MAX_DAC_CHANNELS];
|
||||
u8 powerdown_mode[MAX5821_MAX_DAC_CHANNELS];
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static const char * const max5821_powerdown_modes[] = {
|
||||
"three_state",
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
};
|
||||
|
||||
enum {
|
||||
MAX5821_THREE_STATE,
|
||||
MAX5821_1KOHM_TO_GND,
|
||||
MAX5821_100KOHM_TO_GND
|
||||
};
|
||||
|
||||
static int max5821_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
return st->powerdown_mode[chan->channel];
|
||||
}
|
||||
|
||||
static int max5821_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
unsigned int mode)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
st->powerdown_mode[chan->channel] = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct iio_enum max5821_powerdown_mode_enum = {
|
||||
.items = max5821_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(max5821_powerdown_modes),
|
||||
.get = max5821_get_powerdown_mode,
|
||||
.set = max5821_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static ssize_t max5821_read_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct max5821_data *st = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", st->powerdown[chan->channel]);
|
||||
}
|
||||
|
||||
static int max5821_sync_powerdown_mode(struct max5821_data *data,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
u8 outbuf[2];
|
||||
|
||||
outbuf[0] = MAX5821_EXTENDED_COMMAND_MODE;
|
||||
|
||||
if (chan->channel == 0)
|
||||
outbuf[1] = MAX5821_EXTENDED_DAC_A;
|
||||
else
|
||||
outbuf[1] = MAX5821_EXTENDED_DAC_B;
|
||||
|
||||
if (data->powerdown[chan->channel])
|
||||
outbuf[1] |= data->powerdown_mode[chan->channel] + 1;
|
||||
else
|
||||
outbuf[1] |= MAX5821_EXTENDED_POWER_UP;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
static ssize_t max5821_write_dac_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
bool powerdown;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &powerdown);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->powerdown[chan->channel] = powerdown;
|
||||
|
||||
ret = max5821_sync_powerdown_mode(data, chan);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info max5821_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = max5821_read_dac_powerdown,
|
||||
.write = max5821_write_dac_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
#define MAX5821_CHANNEL(chan) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.indexed = 1, \
|
||||
.output = 1, \
|
||||
.channel = (chan), \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.ext_info = max5821_ext_info, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec max5821_channels[] = {
|
||||
MAX5821_CHANNEL(0),
|
||||
MAX5821_CHANNEL(1)
|
||||
};
|
||||
|
||||
static const u8 max5821_read_dac_command[] = {
|
||||
MAX5821_READ_DAC_A_COMMAND,
|
||||
MAX5821_READ_DAC_B_COMMAND
|
||||
};
|
||||
|
||||
static const u8 max5821_load_dac_command[] = {
|
||||
MAX5821_LOAD_DAC_A_IN_REG_B,
|
||||
MAX5821_LOAD_DAC_B_IN_REG_A
|
||||
};
|
||||
|
||||
static int max5821_get_value(struct iio_dev *indio_dev,
|
||||
int *val, int channel)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[1];
|
||||
u8 inbuf[2];
|
||||
int ret;
|
||||
|
||||
if ((channel != 0) && (channel != 1))
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = max5821_read_dac_command[channel];
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
|
||||
ret = i2c_master_send(client, outbuf, 1);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (ret != 1) {
|
||||
mutex_unlock(&data->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(client, inbuf, 2);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&data->lock);
|
||||
return ret;
|
||||
} else if (ret != 2) {
|
||||
mutex_unlock(&data->lock);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
*val = ((inbuf[0] & 0x0f) << 6) | (inbuf[1] >> 2);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int max5821_set_value(struct iio_dev *indio_dev,
|
||||
int val, int channel)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
struct i2c_client *client = data->client;
|
||||
u8 outbuf[2];
|
||||
int ret;
|
||||
|
||||
if ((val < 0) || (val > 1023))
|
||||
return -EINVAL;
|
||||
|
||||
if ((channel != 0) && (channel != 1))
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = max5821_load_dac_command[channel];
|
||||
outbuf[0] |= val >> 6;
|
||||
outbuf[1] = (val & 0x3f) << 2;
|
||||
|
||||
ret = i2c_master_send(client, outbuf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max5821_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return max5821_get_value(indio_dev, val, chan->channel);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = data->vref_mv;
|
||||
*val2 = 10;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int max5821_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return max5821_set_value(indio_dev, val, chan->channel);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int max5821_suspend(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
MAX5821_EXTENDED_DAC_B |
|
||||
MAX5821_EXTENDED_POWER_DOWN_MODE2 };
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
|
||||
}
|
||||
|
||||
static int max5821_resume(struct device *dev)
|
||||
{
|
||||
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
|
||||
MAX5821_EXTENDED_DAC_A |
|
||||
MAX5821_EXTENDED_DAC_B |
|
||||
MAX5821_EXTENDED_POWER_UP };
|
||||
|
||||
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume);
|
||||
#define MAX5821_PM_OPS (&max5821_pm_ops)
|
||||
#else
|
||||
#define MAX5821_PM_OPS NULL
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static const struct iio_info max5821_info = {
|
||||
.read_raw = max5821_read_raw,
|
||||
.write_raw = max5821_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int max5821_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct max5821_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
mutex_init(&data->lock);
|
||||
|
||||
/* max5821 start in powerdown mode 100Kohm to ground */
|
||||
for (tmp = 0; tmp < MAX5821_MAX_DAC_CHANNELS; tmp++) {
|
||||
data->powerdown[tmp] = true;
|
||||
data->powerdown_mode[tmp] = MAX5821_100KOHM_TO_GND;
|
||||
}
|
||||
|
||||
data->vref_reg = devm_regulator_get(&client->dev, "vref");
|
||||
if (IS_ERR(data->vref_reg)) {
|
||||
ret = PTR_ERR(data->vref_reg);
|
||||
dev_err(&client->dev,
|
||||
"Failed to get vref regulator: %d\n", ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_enable(data->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable vref regulator: %d\n", ret);
|
||||
goto error_free_reg;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(data->vref_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to get voltage on regulator: %d\n", ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
data->vref_mv = ret / 1000;
|
||||
|
||||
indio_dev->name = id->name;
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->num_channels = ARRAY_SIZE(max5821_channels);
|
||||
indio_dev->channels = max5821_channels;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->info = &max5821_info;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(data->vref_reg);
|
||||
|
||||
error_free_reg:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max5821_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
struct max5821_data *data = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
regulator_disable(data->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id max5821_id[] = {
|
||||
{ "max5821", ID_MAX5821 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max5821_id);
|
||||
|
||||
static const struct of_device_id max5821_of_match[] = {
|
||||
{ .compatible = "maxim,max5821" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct i2c_driver max5821_driver = {
|
||||
.driver = {
|
||||
.name = "max5821",
|
||||
.pm = MAX5821_PM_OPS,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max5821_probe,
|
||||
.remove = max5821_remove,
|
||||
.id_table = max5821_id,
|
||||
};
|
||||
module_i2c_driver(max5821_driver);
|
||||
|
||||
MODULE_AUTHOR("Philippe Reynes <tremyfr@yahoo.fr>");
|
||||
MODULE_DESCRIPTION("MAX5821 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
349
drivers/iio/dac/mcp4725.c
Normal file
349
drivers/iio/dac/mcp4725.c
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* mcp4725.c - Support for Microchip MCP4725
|
||||
*
|
||||
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
|
||||
*
|
||||
* Based on max517 by Roland Stigge <stigge@antcom.de>
|
||||
*
|
||||
* This file is subject to the terms and conditions of version 2 of
|
||||
* the GNU General Public License. See the file COPYING in the main
|
||||
* directory of this archive for more details.
|
||||
*
|
||||
* driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
|
||||
* (7-bit I2C slave address 0x60, the three LSBs can be configured in
|
||||
* hardware)
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#include <linux/iio/dac/mcp4725.h>
|
||||
|
||||
#define MCP4725_DRV_NAME "mcp4725"
|
||||
|
||||
struct mcp4725_data {
|
||||
struct i2c_client *client;
|
||||
u16 vref_mv;
|
||||
u16 dac_value;
|
||||
bool powerdown;
|
||||
unsigned powerdown_mode;
|
||||
};
|
||||
|
||||
static int mcp4725_suspend(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
u8 outbuf[2];
|
||||
|
||||
outbuf[0] = (data->powerdown_mode + 1) << 4;
|
||||
outbuf[1] = 0;
|
||||
data->powerdown = true;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
static int mcp4725_resume(struct device *dev)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
|
||||
to_i2c_client(dev)));
|
||||
u8 outbuf[2];
|
||||
|
||||
/* restore previous DAC value */
|
||||
outbuf[0] = (data->dac_value >> 8) & 0xf;
|
||||
outbuf[1] = data->dac_value & 0xff;
|
||||
data->powerdown = false;
|
||||
|
||||
return i2c_master_send(data->client, outbuf, 2);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
|
||||
#define MCP4725_PM_OPS (&mcp4725_pm_ops)
|
||||
#else
|
||||
#define MCP4725_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static ssize_t mcp4725_store_eeprom(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
int tries = 20;
|
||||
u8 inoutbuf[3];
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &state);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
inoutbuf[0] = 0x60; /* write EEPROM */
|
||||
inoutbuf[1] = data->dac_value >> 4;
|
||||
inoutbuf[2] = (data->dac_value & 0xf) << 4;
|
||||
|
||||
ret = i2c_master_send(data->client, inoutbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
/* wait for write complete, takes up to 50ms */
|
||||
while (tries--) {
|
||||
msleep(20);
|
||||
ret = i2c_master_recv(data->client, inoutbuf, 3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 3)
|
||||
return -EIO;
|
||||
|
||||
if (inoutbuf[0] & 0x80)
|
||||
break;
|
||||
}
|
||||
|
||||
if (tries < 0) {
|
||||
dev_err(&data->client->dev,
|
||||
"mcp4725_store_eeprom() failed, incomplete\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static IIO_DEVICE_ATTR(store_eeprom, S_IWUSR, NULL, mcp4725_store_eeprom, 0);
|
||||
|
||||
static struct attribute *mcp4725_attributes[] = {
|
||||
&iio_dev_attr_store_eeprom.dev_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct attribute_group mcp4725_attribute_group = {
|
||||
.attrs = mcp4725_attributes,
|
||||
};
|
||||
|
||||
static const char * const mcp4725_powerdown_modes[] = {
|
||||
"1kohm_to_gnd",
|
||||
"100kohm_to_gnd",
|
||||
"500kohm_to_gnd"
|
||||
};
|
||||
|
||||
static int mcp4725_get_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
return data->powerdown_mode;
|
||||
}
|
||||
|
||||
static int mcp4725_set_powerdown_mode(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, unsigned mode)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
data->powerdown_mode = mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan, char *buf)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
return sprintf(buf, "%d\n", data->powerdown);
|
||||
}
|
||||
|
||||
static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev,
|
||||
uintptr_t private, const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
bool state;
|
||||
int ret;
|
||||
|
||||
ret = strtobool(buf, &state);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (state)
|
||||
ret = mcp4725_suspend(&data->client->dev);
|
||||
else
|
||||
ret = mcp4725_resume(&data->client->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct iio_enum mcp4725_powerdown_mode_enum = {
|
||||
.items = mcp4725_powerdown_modes,
|
||||
.num_items = ARRAY_SIZE(mcp4725_powerdown_modes),
|
||||
.get = mcp4725_get_powerdown_mode,
|
||||
.set = mcp4725_set_powerdown_mode,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {
|
||||
{
|
||||
.name = "powerdown",
|
||||
.read = mcp4725_read_powerdown,
|
||||
.write = mcp4725_write_powerdown,
|
||||
.shared = IIO_SEPARATE,
|
||||
},
|
||||
IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),
|
||||
IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec mcp4725_channel = {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
.ext_info = mcp4725_ext_info,
|
||||
};
|
||||
|
||||
static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
u8 outbuf[2];
|
||||
int ret;
|
||||
|
||||
if (val >= (1 << 12) || val < 0)
|
||||
return -EINVAL;
|
||||
|
||||
outbuf[0] = (val >> 8) & 0xf;
|
||||
outbuf[1] = val & 0xff;
|
||||
|
||||
ret = i2c_master_send(data->client, outbuf, 2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != 2)
|
||||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcp4725_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = data->dac_value;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = data->vref_mv;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int mcp4725_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val, int val2, long mask)
|
||||
{
|
||||
struct mcp4725_data *data = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
ret = mcp4725_set_value(indio_dev, val);
|
||||
data->dac_value = val;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info mcp4725_info = {
|
||||
.read_raw = mcp4725_read_raw,
|
||||
.write_raw = mcp4725_write_raw,
|
||||
.attrs = &mcp4725_attribute_group,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp4725_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct mcp4725_data *data;
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp4725_platform_data *platform_data = client->dev.platform_data;
|
||||
u8 inbuf[3];
|
||||
u8 pd;
|
||||
int err;
|
||||
|
||||
if (!platform_data || !platform_data->vref_mv) {
|
||||
dev_err(&client->dev, "invalid platform data");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
data = iio_priv(indio_dev);
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
data->client = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->info = &mcp4725_info;
|
||||
indio_dev->channels = &mcp4725_channel;
|
||||
indio_dev->num_channels = 1;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
data->vref_mv = platform_data->vref_mv;
|
||||
|
||||
/* read current DAC value */
|
||||
err = i2c_master_recv(client, inbuf, 3);
|
||||
if (err < 0) {
|
||||
dev_err(&client->dev, "failed to read DAC value");
|
||||
return err;
|
||||
}
|
||||
pd = (inbuf[0] >> 1) & 0x3;
|
||||
data->powerdown = pd > 0 ? true : false;
|
||||
data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */
|
||||
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int mcp4725_remove(struct i2c_client *client)
|
||||
{
|
||||
iio_device_unregister(i2c_get_clientdata(client));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id mcp4725_id[] = {
|
||||
{ "mcp4725", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, mcp4725_id);
|
||||
|
||||
static struct i2c_driver mcp4725_driver = {
|
||||
.driver = {
|
||||
.name = MCP4725_DRV_NAME,
|
||||
.pm = MCP4725_PM_OPS,
|
||||
},
|
||||
.probe = mcp4725_probe,
|
||||
.remove = mcp4725_remove,
|
||||
.id_table = mcp4725_id,
|
||||
};
|
||||
module_i2c_driver(mcp4725_driver);
|
||||
|
||||
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
|
||||
MODULE_DESCRIPTION("MCP4725 12-bit DAC");
|
||||
MODULE_LICENSE("GPL");
|
||||
216
drivers/iio/dac/mcp4922.c
Normal file
216
drivers/iio/dac/mcp4922.c
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* mcp4922.c
|
||||
*
|
||||
* Driver for Microchip Digital to Analog Converters.
|
||||
* Supports MCP4902, MCP4912, and MCP4922.
|
||||
*
|
||||
* Copyright (c) 2014 EMAC Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define MCP4922_NUM_CHANNELS 2
|
||||
|
||||
enum mcp4922_supported_device_ids {
|
||||
ID_MCP4902,
|
||||
ID_MCP4912,
|
||||
ID_MCP4922,
|
||||
};
|
||||
|
||||
struct mcp4922_state {
|
||||
struct spi_device *spi;
|
||||
unsigned int value[MCP4922_NUM_CHANNELS];
|
||||
unsigned int vref_mv;
|
||||
struct regulator *vref_reg;
|
||||
u8 mosi[2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
#define MCP4922_CHAN(chan, bits) { \
|
||||
.type = IIO_VOLTAGE, \
|
||||
.output = 1, \
|
||||
.indexed = 1, \
|
||||
.channel = chan, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.scan_type = { \
|
||||
.sign = 'u', \
|
||||
.realbits = (bits), \
|
||||
.storagebits = 16, \
|
||||
.shift = 12 - (bits), \
|
||||
}, \
|
||||
}
|
||||
|
||||
static int mcp4922_spi_write(struct mcp4922_state *state, u8 addr, u32 val)
|
||||
{
|
||||
state->mosi[1] = val & 0xff;
|
||||
state->mosi[0] = (addr == 0) ? 0x00 : 0x80;
|
||||
state->mosi[0] |= 0x30 | ((val >> 8) & 0x0f);
|
||||
|
||||
return spi_write(state->spi, state->mosi, 2);
|
||||
}
|
||||
|
||||
static int mcp4922_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct mcp4922_state *state = iio_priv(indio_dev);
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
*val = state->value[chan->channel];
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = state->vref_mv;
|
||||
*val2 = chan->scan_type.realbits;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mcp4922_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct mcp4922_state *state = iio_priv(indio_dev);
|
||||
|
||||
if (val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
if (val > GENMASK(chan->scan_type.realbits-1, 0))
|
||||
return -EINVAL;
|
||||
val <<= chan->scan_type.shift;
|
||||
state->value[chan->channel] = val;
|
||||
return mcp4922_spi_write(state, chan->channel, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec mcp4922_channels[3][MCP4922_NUM_CHANNELS] = {
|
||||
[ID_MCP4902] = { MCP4922_CHAN(0, 8), MCP4922_CHAN(1, 8) },
|
||||
[ID_MCP4912] = { MCP4922_CHAN(0, 10), MCP4922_CHAN(1, 10) },
|
||||
[ID_MCP4922] = { MCP4922_CHAN(0, 12), MCP4922_CHAN(1, 12) },
|
||||
};
|
||||
|
||||
static const struct iio_info mcp4922_info = {
|
||||
.read_raw = &mcp4922_read_raw,
|
||||
.write_raw = &mcp4922_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int mcp4922_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct mcp4922_state *state;
|
||||
const struct spi_device_id *id;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
state = iio_priv(indio_dev);
|
||||
state->spi = spi;
|
||||
state->vref_reg = devm_regulator_get(&spi->dev, "vref");
|
||||
if (IS_ERR(state->vref_reg)) {
|
||||
dev_err(&spi->dev, "Vref regulator not specified\n");
|
||||
return PTR_ERR(state->vref_reg);
|
||||
}
|
||||
|
||||
ret = regulator_enable(state->vref_reg);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to enable vref regulator: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regulator_get_voltage(state->vref_reg);
|
||||
if (ret < 0) {
|
||||
dev_err(&spi->dev, "Failed to read vref regulator: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
state->vref_mv = ret / 1000;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
id = spi_get_device_id(spi);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &mcp4922_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = mcp4922_channels[id->driver_data];
|
||||
indio_dev->num_channels = MCP4922_NUM_CHANNELS;
|
||||
indio_dev->name = id->name;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "Failed to register iio device: %d\n",
|
||||
ret);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mcp4922_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct mcp4922_state *state;
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
state = iio_priv(indio_dev);
|
||||
regulator_disable(state->vref_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id mcp4922_id[] = {
|
||||
{"mcp4902", ID_MCP4902},
|
||||
{"mcp4912", ID_MCP4912},
|
||||
{"mcp4922", ID_MCP4922},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, mcp4922_id);
|
||||
|
||||
static struct spi_driver mcp4922_driver = {
|
||||
.driver = {
|
||||
.name = "mcp4922",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mcp4922_probe,
|
||||
.remove = mcp4922_remove,
|
||||
.id_table = mcp4922_id,
|
||||
};
|
||||
module_spi_driver(mcp4922_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Welling <mwelling@ieee.org>");
|
||||
MODULE_DESCRIPTION("Microchip MCP4902, MCP4912, MCP4922 DAC");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
42
drivers/iio/frequency/Kconfig
Normal file
42
drivers/iio/frequency/Kconfig
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
#
|
||||
# Frequency
|
||||
# Direct Digital Synthesis drivers (DDS)
|
||||
# Clock Distribution device drivers
|
||||
# Phase-Locked Loop (PLL) frequency synthesizers
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Frequency Synthesizers DDS/PLL"
|
||||
|
||||
menu "Clock Generator/Distribution"
|
||||
|
||||
config AD9523
|
||||
tristate "Analog Devices AD9523 Low Jitter Clock Generator"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices AD9523 Low Jitter
|
||||
Clock Generator. The driver provides direct access via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad9523.
|
||||
|
||||
endmenu
|
||||
|
||||
#
|
||||
# Phase-Locked Loop (PLL) frequency synthesizers
|
||||
#
|
||||
|
||||
menu "Phase-Locked Loop (PLL) frequency synthesizers"
|
||||
|
||||
config ADF4350
|
||||
tristate "Analog Devices ADF4350/ADF4351 Wideband Synthesizers"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADF4350/ADF4351
|
||||
Wideband Synthesizers. The driver provides direct access via sysfs.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called adf4350.
|
||||
|
||||
endmenu
|
||||
endmenu
|
||||
7
drivers/iio/frequency/Makefile
Normal file
7
drivers/iio/frequency/Makefile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#
|
||||
# Makefile iio/frequency
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_AD9523) += ad9523.o
|
||||
obj-$(CONFIG_ADF4350) += adf4350.o
|
||||
1040
drivers/iio/frequency/ad9523.c
Normal file
1040
drivers/iio/frequency/ad9523.c
Normal file
File diff suppressed because it is too large
Load diff
642
drivers/iio/frequency/adf4350.c
Normal file
642
drivers/iio/frequency/adf4350.c
Normal file
|
|
@ -0,0 +1,642 @@
|
|||
/*
|
||||
* ADF4350/ADF4351 SPI Wideband Synthesizer driver
|
||||
*
|
||||
* Copyright 2012-2013 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gcd.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <asm/div64.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/frequency/adf4350.h>
|
||||
|
||||
enum {
|
||||
ADF4350_FREQ,
|
||||
ADF4350_FREQ_REFIN,
|
||||
ADF4350_FREQ_RESOLUTION,
|
||||
ADF4350_PWRDOWN,
|
||||
};
|
||||
|
||||
struct adf4350_state {
|
||||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
struct adf4350_platform_data *pdata;
|
||||
struct clk *clk;
|
||||
unsigned long clkin;
|
||||
unsigned long chspc; /* Channel Spacing */
|
||||
unsigned long fpfd; /* Phase Frequency Detector */
|
||||
unsigned long min_out_freq;
|
||||
unsigned r0_fract;
|
||||
unsigned r0_int;
|
||||
unsigned r1_mod;
|
||||
unsigned r4_rf_div_sel;
|
||||
unsigned long regs[6];
|
||||
unsigned long regs_hw[6];
|
||||
unsigned long long freq_req;
|
||||
/*
|
||||
* DMA (thus cache coherency maintenance) requires the
|
||||
* transfer buffers to live in their own cache lines.
|
||||
*/
|
||||
__be32 val ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static struct adf4350_platform_data default_pdata = {
|
||||
.channel_spacing = 10000,
|
||||
.r2_user_settings = ADF4350_REG2_PD_POLARITY_POS |
|
||||
ADF4350_REG2_CHARGE_PUMP_CURR_uA(2500),
|
||||
.r3_user_settings = ADF4350_REG3_12BIT_CLKDIV_MODE(0),
|
||||
.r4_user_settings = ADF4350_REG4_OUTPUT_PWR(3) |
|
||||
ADF4350_REG4_MUTE_TILL_LOCK_EN,
|
||||
.gpio_lock_detect = -1,
|
||||
};
|
||||
|
||||
static int adf4350_sync_config(struct adf4350_state *st)
|
||||
{
|
||||
int ret, i, doublebuf = 0;
|
||||
|
||||
for (i = ADF4350_REG5; i >= ADF4350_REG0; i--) {
|
||||
if ((st->regs_hw[i] != st->regs[i]) ||
|
||||
((i == ADF4350_REG0) && doublebuf)) {
|
||||
|
||||
switch (i) {
|
||||
case ADF4350_REG1:
|
||||
case ADF4350_REG4:
|
||||
doublebuf = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
st->val = cpu_to_be32(st->regs[i] | i);
|
||||
ret = spi_write(st->spi, &st->val, 4);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
st->regs_hw[i] = st->regs[i];
|
||||
dev_dbg(&st->spi->dev, "[%d] 0x%X\n",
|
||||
i, (u32)st->regs[i] | i);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adf4350_reg_access(struct iio_dev *indio_dev,
|
||||
unsigned reg, unsigned writeval,
|
||||
unsigned *readval)
|
||||
{
|
||||
struct adf4350_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
if (reg > ADF4350_REG5)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (readval == NULL) {
|
||||
st->regs[reg] = writeval & ~(BIT(0) | BIT(1) | BIT(2));
|
||||
ret = adf4350_sync_config(st);
|
||||
} else {
|
||||
*readval = st->regs_hw[reg];
|
||||
ret = 0;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adf4350_tune_r_cnt(struct adf4350_state *st, unsigned short r_cnt)
|
||||
{
|
||||
struct adf4350_platform_data *pdata = st->pdata;
|
||||
|
||||
do {
|
||||
r_cnt++;
|
||||
st->fpfd = (st->clkin * (pdata->ref_doubler_en ? 2 : 1)) /
|
||||
(r_cnt * (pdata->ref_div2_en ? 2 : 1));
|
||||
} while (st->fpfd > ADF4350_MAX_FREQ_PFD);
|
||||
|
||||
return r_cnt;
|
||||
}
|
||||
|
||||
static int adf4350_set_freq(struct adf4350_state *st, unsigned long long freq)
|
||||
{
|
||||
struct adf4350_platform_data *pdata = st->pdata;
|
||||
u64 tmp;
|
||||
u32 div_gcd, prescaler, chspc;
|
||||
u16 mdiv, r_cnt = 0;
|
||||
u8 band_sel_div;
|
||||
|
||||
if (freq > ADF4350_MAX_OUT_FREQ || freq < st->min_out_freq)
|
||||
return -EINVAL;
|
||||
|
||||
if (freq > ADF4350_MAX_FREQ_45_PRESC) {
|
||||
prescaler = ADF4350_REG1_PRESCALER;
|
||||
mdiv = 75;
|
||||
} else {
|
||||
prescaler = 0;
|
||||
mdiv = 23;
|
||||
}
|
||||
|
||||
st->r4_rf_div_sel = 0;
|
||||
|
||||
while (freq < ADF4350_MIN_VCO_FREQ) {
|
||||
freq <<= 1;
|
||||
st->r4_rf_div_sel++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow a predefined reference division factor
|
||||
* if not set, compute our own
|
||||
*/
|
||||
if (pdata->ref_div_factor)
|
||||
r_cnt = pdata->ref_div_factor - 1;
|
||||
|
||||
chspc = st->chspc;
|
||||
|
||||
do {
|
||||
do {
|
||||
do {
|
||||
r_cnt = adf4350_tune_r_cnt(st, r_cnt);
|
||||
st->r1_mod = st->fpfd / chspc;
|
||||
if (r_cnt > ADF4350_MAX_R_CNT) {
|
||||
/* try higher spacing values */
|
||||
chspc++;
|
||||
r_cnt = 0;
|
||||
}
|
||||
} while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
|
||||
} while (r_cnt == 0);
|
||||
|
||||
tmp = freq * (u64)st->r1_mod + (st->fpfd >> 1);
|
||||
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
|
||||
st->r0_fract = do_div(tmp, st->r1_mod);
|
||||
st->r0_int = tmp;
|
||||
} while (mdiv > st->r0_int);
|
||||
|
||||
band_sel_div = DIV_ROUND_UP(st->fpfd, ADF4350_MAX_BANDSEL_CLK);
|
||||
|
||||
if (st->r0_fract && st->r1_mod) {
|
||||
div_gcd = gcd(st->r1_mod, st->r0_fract);
|
||||
st->r1_mod /= div_gcd;
|
||||
st->r0_fract /= div_gcd;
|
||||
} else {
|
||||
st->r0_fract = 0;
|
||||
st->r1_mod = 1;
|
||||
}
|
||||
|
||||
dev_dbg(&st->spi->dev, "VCO: %llu Hz, PFD %lu Hz\n"
|
||||
"REF_DIV %d, R0_INT %d, R0_FRACT %d\n"
|
||||
"R1_MOD %d, RF_DIV %d\nPRESCALER %s, BAND_SEL_DIV %d\n",
|
||||
freq, st->fpfd, r_cnt, st->r0_int, st->r0_fract, st->r1_mod,
|
||||
1 << st->r4_rf_div_sel, prescaler ? "8/9" : "4/5",
|
||||
band_sel_div);
|
||||
|
||||
st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
|
||||
ADF4350_REG0_FRACT(st->r0_fract);
|
||||
|
||||
st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
|
||||
ADF4350_REG1_MOD(st->r1_mod) |
|
||||
prescaler;
|
||||
|
||||
st->regs[ADF4350_REG2] =
|
||||
ADF4350_REG2_10BIT_R_CNT(r_cnt) |
|
||||
ADF4350_REG2_DOUBLE_BUFF_EN |
|
||||
(pdata->ref_doubler_en ? ADF4350_REG2_RMULT2_EN : 0) |
|
||||
(pdata->ref_div2_en ? ADF4350_REG2_RDIV2_EN : 0) |
|
||||
(pdata->r2_user_settings & (ADF4350_REG2_PD_POLARITY_POS |
|
||||
ADF4350_REG2_LDP_6ns | ADF4350_REG2_LDF_INT_N |
|
||||
ADF4350_REG2_CHARGE_PUMP_CURR_uA(5000) |
|
||||
ADF4350_REG2_MUXOUT(0x7) | ADF4350_REG2_NOISE_MODE(0x3)));
|
||||
|
||||
st->regs[ADF4350_REG3] = pdata->r3_user_settings &
|
||||
(ADF4350_REG3_12BIT_CLKDIV(0xFFF) |
|
||||
ADF4350_REG3_12BIT_CLKDIV_MODE(0x3) |
|
||||
ADF4350_REG3_12BIT_CSR_EN |
|
||||
ADF4351_REG3_CHARGE_CANCELLATION_EN |
|
||||
ADF4351_REG3_ANTI_BACKLASH_3ns_EN |
|
||||
ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH);
|
||||
|
||||
st->regs[ADF4350_REG4] =
|
||||
ADF4350_REG4_FEEDBACK_FUND |
|
||||
ADF4350_REG4_RF_DIV_SEL(st->r4_rf_div_sel) |
|
||||
ADF4350_REG4_8BIT_BAND_SEL_CLKDIV(band_sel_div) |
|
||||
ADF4350_REG4_RF_OUT_EN |
|
||||
(pdata->r4_user_settings &
|
||||
(ADF4350_REG4_OUTPUT_PWR(0x3) |
|
||||
ADF4350_REG4_AUX_OUTPUT_PWR(0x3) |
|
||||
ADF4350_REG4_AUX_OUTPUT_EN |
|
||||
ADF4350_REG4_AUX_OUTPUT_FUND |
|
||||
ADF4350_REG4_MUTE_TILL_LOCK_EN));
|
||||
|
||||
st->regs[ADF4350_REG5] = ADF4350_REG5_LD_PIN_MODE_DIGITAL;
|
||||
st->freq_req = freq;
|
||||
|
||||
return adf4350_sync_config(st);
|
||||
}
|
||||
|
||||
static ssize_t adf4350_write(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
const char *buf, size_t len)
|
||||
{
|
||||
struct adf4350_state *st = iio_priv(indio_dev);
|
||||
unsigned long long readin;
|
||||
unsigned long tmp;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoull(buf, 10, &readin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch ((u32)private) {
|
||||
case ADF4350_FREQ:
|
||||
ret = adf4350_set_freq(st, readin);
|
||||
break;
|
||||
case ADF4350_FREQ_REFIN:
|
||||
if (readin > ADF4350_MAX_FREQ_REFIN) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (st->clk) {
|
||||
tmp = clk_round_rate(st->clk, readin);
|
||||
if (tmp != readin) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
ret = clk_set_rate(st->clk, tmp);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
st->clkin = readin;
|
||||
ret = adf4350_set_freq(st, st->freq_req);
|
||||
break;
|
||||
case ADF4350_FREQ_RESOLUTION:
|
||||
if (readin == 0)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
st->chspc = readin;
|
||||
break;
|
||||
case ADF4350_PWRDOWN:
|
||||
if (readin)
|
||||
st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
|
||||
else
|
||||
st->regs[ADF4350_REG2] &= ~ADF4350_REG2_POWER_DOWN_EN;
|
||||
|
||||
adf4350_sync_config(st);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t adf4350_read(struct iio_dev *indio_dev,
|
||||
uintptr_t private,
|
||||
const struct iio_chan_spec *chan,
|
||||
char *buf)
|
||||
{
|
||||
struct adf4350_state *st = iio_priv(indio_dev);
|
||||
unsigned long long val;
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
switch ((u32)private) {
|
||||
case ADF4350_FREQ:
|
||||
val = (u64)((st->r0_int * st->r1_mod) + st->r0_fract) *
|
||||
(u64)st->fpfd;
|
||||
do_div(val, st->r1_mod * (1 << st->r4_rf_div_sel));
|
||||
/* PLL unlocked? return error */
|
||||
if (gpio_is_valid(st->pdata->gpio_lock_detect))
|
||||
if (!gpio_get_value(st->pdata->gpio_lock_detect)) {
|
||||
dev_dbg(&st->spi->dev, "PLL un-locked\n");
|
||||
ret = -EBUSY;
|
||||
}
|
||||
break;
|
||||
case ADF4350_FREQ_REFIN:
|
||||
if (st->clk)
|
||||
st->clkin = clk_get_rate(st->clk);
|
||||
|
||||
val = st->clkin;
|
||||
break;
|
||||
case ADF4350_FREQ_RESOLUTION:
|
||||
val = st->chspc;
|
||||
break;
|
||||
case ADF4350_PWRDOWN:
|
||||
val = !!(st->regs[ADF4350_REG2] & ADF4350_REG2_POWER_DOWN_EN);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
val = 0;
|
||||
}
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
|
||||
}
|
||||
|
||||
#define _ADF4350_EXT_INFO(_name, _ident) { \
|
||||
.name = _name, \
|
||||
.read = adf4350_read, \
|
||||
.write = adf4350_write, \
|
||||
.private = _ident, \
|
||||
.shared = IIO_SEPARATE, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec_ext_info adf4350_ext_info[] = {
|
||||
/* Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
|
||||
* values > 2^32 in order to support the entire frequency range
|
||||
* in Hz. Using scale is a bit ugly.
|
||||
*/
|
||||
_ADF4350_EXT_INFO("frequency", ADF4350_FREQ),
|
||||
_ADF4350_EXT_INFO("frequency_resolution", ADF4350_FREQ_RESOLUTION),
|
||||
_ADF4350_EXT_INFO("refin_frequency", ADF4350_FREQ_REFIN),
|
||||
_ADF4350_EXT_INFO("powerdown", ADF4350_PWRDOWN),
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adf4350_chan = {
|
||||
.type = IIO_ALTVOLTAGE,
|
||||
.indexed = 1,
|
||||
.output = 1,
|
||||
.ext_info = adf4350_ext_info,
|
||||
};
|
||||
|
||||
static const struct iio_info adf4350_info = {
|
||||
.debugfs_reg_access = &adf4350_reg_access,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
|
||||
{
|
||||
struct device_node *np = dev->of_node;
|
||||
struct adf4350_platform_data *pdata;
|
||||
unsigned int tmp;
|
||||
int ret;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata) {
|
||||
dev_err(dev, "could not allocate memory for platform data\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
|
||||
|
||||
tmp = 10000;
|
||||
of_property_read_u32(np, "adi,channel-spacing", &tmp);
|
||||
pdata->channel_spacing = tmp;
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,power-up-frequency", &tmp);
|
||||
pdata->power_up_frequency = tmp;
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,reference-div-factor", &tmp);
|
||||
pdata->ref_div_factor = tmp;
|
||||
|
||||
ret = of_get_gpio(np, 0);
|
||||
if (ret < 0)
|
||||
pdata->gpio_lock_detect = -1;
|
||||
else
|
||||
pdata->gpio_lock_detect = ret;
|
||||
|
||||
pdata->ref_doubler_en = of_property_read_bool(np,
|
||||
"adi,reference-doubler-enable");
|
||||
pdata->ref_div2_en = of_property_read_bool(np,
|
||||
"adi,reference-div2-enable");
|
||||
|
||||
/* r2_user_settings */
|
||||
pdata->r2_user_settings = of_property_read_bool(np,
|
||||
"adi,phase-detector-polarity-positive-enable") ?
|
||||
ADF4350_REG2_PD_POLARITY_POS : 0;
|
||||
pdata->r2_user_settings |= of_property_read_bool(np,
|
||||
"adi,lock-detect-precision-6ns-enable") ?
|
||||
ADF4350_REG2_LDP_6ns : 0;
|
||||
pdata->r2_user_settings |= of_property_read_bool(np,
|
||||
"adi,lock-detect-function-integer-n-enable") ?
|
||||
ADF4350_REG2_LDF_INT_N : 0;
|
||||
|
||||
tmp = 2500;
|
||||
of_property_read_u32(np, "adi,charge-pump-current", &tmp);
|
||||
pdata->r2_user_settings |= ADF4350_REG2_CHARGE_PUMP_CURR_uA(tmp);
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,muxout-select", &tmp);
|
||||
pdata->r2_user_settings |= ADF4350_REG2_MUXOUT(tmp);
|
||||
|
||||
pdata->r2_user_settings |= of_property_read_bool(np,
|
||||
"adi,low-spur-mode-enable") ?
|
||||
ADF4350_REG2_NOISE_MODE(0x3) : 0;
|
||||
|
||||
/* r3_user_settings */
|
||||
|
||||
pdata->r3_user_settings = of_property_read_bool(np,
|
||||
"adi,cycle-slip-reduction-enable") ?
|
||||
ADF4350_REG3_12BIT_CSR_EN : 0;
|
||||
pdata->r3_user_settings |= of_property_read_bool(np,
|
||||
"adi,charge-cancellation-enable") ?
|
||||
ADF4351_REG3_CHARGE_CANCELLATION_EN : 0;
|
||||
|
||||
pdata->r3_user_settings |= of_property_read_bool(np,
|
||||
"adi,anti-backlash-3ns-enable") ?
|
||||
ADF4351_REG3_ANTI_BACKLASH_3ns_EN : 0;
|
||||
pdata->r3_user_settings |= of_property_read_bool(np,
|
||||
"adi,band-select-clock-mode-high-enable") ?
|
||||
ADF4351_REG3_BAND_SEL_CLOCK_MODE_HIGH : 0;
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,12bit-clk-divider", &tmp);
|
||||
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV(tmp);
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,clk-divider-mode", &tmp);
|
||||
pdata->r3_user_settings |= ADF4350_REG3_12BIT_CLKDIV_MODE(tmp);
|
||||
|
||||
/* r4_user_settings */
|
||||
|
||||
pdata->r4_user_settings = of_property_read_bool(np,
|
||||
"adi,aux-output-enable") ?
|
||||
ADF4350_REG4_AUX_OUTPUT_EN : 0;
|
||||
pdata->r4_user_settings |= of_property_read_bool(np,
|
||||
"adi,aux-output-fundamental-enable") ?
|
||||
ADF4350_REG4_AUX_OUTPUT_FUND : 0;
|
||||
pdata->r4_user_settings |= of_property_read_bool(np,
|
||||
"adi,mute-till-lock-enable") ?
|
||||
ADF4350_REG4_MUTE_TILL_LOCK_EN : 0;
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,output-power", &tmp);
|
||||
pdata->r4_user_settings |= ADF4350_REG4_OUTPUT_PWR(tmp);
|
||||
|
||||
tmp = 0;
|
||||
of_property_read_u32(np, "adi,aux-output-power", &tmp);
|
||||
pdata->r4_user_settings |= ADF4350_REG4_AUX_OUTPUT_PWR(tmp);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static
|
||||
struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int adf4350_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adf4350_platform_data *pdata;
|
||||
struct iio_dev *indio_dev;
|
||||
struct adf4350_state *st;
|
||||
struct clk *clk = NULL;
|
||||
int ret;
|
||||
|
||||
if (spi->dev.of_node) {
|
||||
pdata = adf4350_parse_dt(&spi->dev);
|
||||
if (pdata == NULL)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
pdata = spi->dev.platform_data;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_warn(&spi->dev, "no platform data? using default\n");
|
||||
pdata = &default_pdata;
|
||||
}
|
||||
|
||||
if (!pdata->clkin) {
|
||||
clk = devm_clk_get(&spi->dev, "clkin");
|
||||
if (IS_ERR(clk))
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (indio_dev == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto error_disable_clk;
|
||||
}
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
st->reg = devm_regulator_get(&spi->dev, "vcc");
|
||||
if (!IS_ERR(st->reg)) {
|
||||
ret = regulator_enable(st->reg);
|
||||
if (ret)
|
||||
goto error_disable_clk;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->spi = spi;
|
||||
st->pdata = pdata;
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = (pdata->name[0] != 0) ? pdata->name :
|
||||
spi_get_device_id(spi)->name;
|
||||
|
||||
indio_dev->info = &adf4350_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels = &adf4350_chan;
|
||||
indio_dev->num_channels = 1;
|
||||
|
||||
st->chspc = pdata->channel_spacing;
|
||||
if (clk) {
|
||||
st->clk = clk;
|
||||
st->clkin = clk_get_rate(clk);
|
||||
} else {
|
||||
st->clkin = pdata->clkin;
|
||||
}
|
||||
|
||||
st->min_out_freq = spi_get_device_id(spi)->driver_data == 4351 ?
|
||||
ADF4351_MIN_OUT_FREQ : ADF4350_MIN_OUT_FREQ;
|
||||
|
||||
memset(st->regs_hw, 0xFF, sizeof(st->regs_hw));
|
||||
|
||||
if (gpio_is_valid(pdata->gpio_lock_detect)) {
|
||||
ret = devm_gpio_request(&spi->dev, pdata->gpio_lock_detect,
|
||||
indio_dev->name);
|
||||
if (ret) {
|
||||
dev_err(&spi->dev, "fail to request lock detect GPIO-%d",
|
||||
pdata->gpio_lock_detect);
|
||||
goto error_disable_reg;
|
||||
}
|
||||
gpio_direction_input(pdata->gpio_lock_detect);
|
||||
}
|
||||
|
||||
if (pdata->power_up_frequency) {
|
||||
ret = adf4350_set_freq(st, pdata->power_up_frequency);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_disable_reg;
|
||||
|
||||
return 0;
|
||||
|
||||
error_disable_reg:
|
||||
if (!IS_ERR(st->reg))
|
||||
regulator_disable(st->reg);
|
||||
error_disable_clk:
|
||||
if (clk)
|
||||
clk_disable_unprepare(clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adf4350_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adf4350_state *st = iio_priv(indio_dev);
|
||||
struct regulator *reg = st->reg;
|
||||
|
||||
st->regs[ADF4350_REG2] |= ADF4350_REG2_POWER_DOWN_EN;
|
||||
adf4350_sync_config(st);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (st->clk)
|
||||
clk_disable_unprepare(st->clk);
|
||||
|
||||
if (!IS_ERR(reg)) {
|
||||
regulator_disable(reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adf4350_id[] = {
|
||||
{"adf4350", 4350},
|
||||
{"adf4351", 4351},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct spi_driver adf4350_driver = {
|
||||
.driver = {
|
||||
.name = "adf4350",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adf4350_probe,
|
||||
.remove = adf4350_remove,
|
||||
.id_table = adf4350_id,
|
||||
};
|
||||
module_spi_driver(adf4350_driver);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADF4350/ADF4351 PLL");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
112
drivers/iio/gyro/Kconfig
Normal file
112
drivers/iio/gyro/Kconfig
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
#
|
||||
# IIO Digital Gyroscope Sensor drivers configuration
|
||||
#
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
|
||||
menu "Digital gyroscope sensors"
|
||||
|
||||
config ADIS16080
|
||||
tristate "Analog Devices ADIS16080/100 Yaw Rate Gyroscope with SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16080, ADIS16100 Yaw
|
||||
Rate Gyroscope with SPI.
|
||||
|
||||
config ADIS16130
|
||||
tristate "Analog Devices ADIS16130 High Precision Angular Rate Sensor driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16130 High Precision
|
||||
Angular Rate Sensor driver.
|
||||
|
||||
config ADIS16136
|
||||
tristate "Analog devices ADIS16136 and similar gyroscopes driver"
|
||||
depends on SPI_MASTER
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for the Analog Devices ADIS16133, ADIS16135,
|
||||
ADIS16136 gyroscope devices.
|
||||
|
||||
config ADIS16260
|
||||
tristate "Analog Devices ADIS16260 Digital Gyroscope Sensor SPI driver"
|
||||
depends on SPI
|
||||
select IIO_ADIS_LIB
|
||||
select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADIS16260 ADIS16265
|
||||
ADIS16250 ADIS16255 and ADIS16251 programmable digital gyroscope sensors.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called adis16260.
|
||||
|
||||
config ADXRS450
|
||||
tristate "Analog Devices ADXRS450/3 Digital Output Gyroscope SPI driver"
|
||||
depends on SPI
|
||||
help
|
||||
Say yes here to build support for Analog Devices ADXRS450 and ADXRS453
|
||||
programmable digital output gyroscope.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called adxrs450.
|
||||
|
||||
config BMG160
|
||||
tristate "BOSCH BMG160 Gyro Sensor"
|
||||
depends on I2C
|
||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to build support for Bosch BMG160 Tri-axis Gyro Sensor
|
||||
driver. This driver also supports BMI055 gyroscope.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called bmg160.
|
||||
|
||||
config HID_SENSOR_GYRO_3D
|
||||
depends on HID_SENSOR_HUB
|
||||
select IIO_BUFFER
|
||||
select IIO_TRIGGERED_BUFFER
|
||||
select HID_SENSOR_IIO_COMMON
|
||||
select HID_SENSOR_IIO_TRIGGER
|
||||
tristate "HID Gyroscope 3D"
|
||||
help
|
||||
Say yes here to build support for the HID SENSOR
|
||||
Gyroscope 3D.
|
||||
|
||||
config IIO_ST_GYRO_3AXIS
|
||||
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
|
||||
depends on (I2C || SPI_MASTER) && SYSFS
|
||||
select IIO_ST_SENSORS_CORE
|
||||
select IIO_ST_GYRO_I2C_3AXIS if (I2C)
|
||||
select IIO_ST_GYRO_SPI_3AXIS if (SPI_MASTER)
|
||||
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
|
||||
help
|
||||
Say yes here to build support for STMicroelectronics gyroscopes:
|
||||
L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330.
|
||||
|
||||
This driver can also be built as a module. If so, these modules
|
||||
will be created:
|
||||
- st_gyro (core functions for the driver [it is mandatory]);
|
||||
- st_gyro_i2c (necessary for the I2C devices [optional*]);
|
||||
- st_gyro_spi (necessary for the SPI devices [optional*]);
|
||||
|
||||
(*) one of these is necessary to do something.
|
||||
|
||||
config IIO_ST_GYRO_I2C_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_GYRO_3AXIS
|
||||
depends on IIO_ST_SENSORS_I2C
|
||||
|
||||
config IIO_ST_GYRO_SPI_3AXIS
|
||||
tristate
|
||||
depends on IIO_ST_GYRO_3AXIS
|
||||
depends on IIO_ST_SENSORS_SPI
|
||||
|
||||
config ITG3200
|
||||
tristate "InvenSense ITG3200 Digital 3-Axis Gyroscope I2C driver"
|
||||
depends on I2C
|
||||
select IIO_TRIGGERED_BUFFER if IIO_BUFFER
|
||||
help
|
||||
Say yes here to add support for the InvenSense ITG3200 digital
|
||||
3-axis gyroscope sensor.
|
||||
|
||||
endmenu
|
||||
24
drivers/iio/gyro/Makefile
Normal file
24
drivers/iio/gyro/Makefile
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
#
|
||||
# Makefile for industrial I/O gyroscope sensor drivers
|
||||
#
|
||||
|
||||
# When adding new entries keep the list in alphabetical order
|
||||
obj-$(CONFIG_ADIS16080) += adis16080.o
|
||||
obj-$(CONFIG_ADIS16130) += adis16130.o
|
||||
obj-$(CONFIG_ADIS16136) += adis16136.o
|
||||
obj-$(CONFIG_ADIS16260) += adis16260.o
|
||||
obj-$(CONFIG_ADXRS450) += adxrs450.o
|
||||
obj-$(CONFIG_BMG160) += bmg160.o
|
||||
|
||||
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
|
||||
|
||||
itg3200-y := itg3200_core.o
|
||||
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
|
||||
obj-$(CONFIG_ITG3200) += itg3200.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_GYRO_3AXIS) += st_gyro.o
|
||||
st_gyro-y := st_gyro_core.o
|
||||
st_gyro-$(CONFIG_IIO_BUFFER) += st_gyro_buffer.o
|
||||
|
||||
obj-$(CONFIG_IIO_ST_GYRO_I2C_3AXIS) += st_gyro_i2c.o
|
||||
obj-$(CONFIG_IIO_ST_GYRO_SPI_3AXIS) += st_gyro_spi.o
|
||||
241
drivers/iio/gyro/adis16080.c
Normal file
241
drivers/iio/gyro/adis16080.c
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/*
|
||||
* ADIS16080/100 Yaw Rate Gyroscope with SPI driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define ADIS16080_DIN_GYRO (0 << 10) /* Gyroscope output */
|
||||
#define ADIS16080_DIN_TEMP (1 << 10) /* Temperature output */
|
||||
#define ADIS16080_DIN_AIN1 (2 << 10)
|
||||
#define ADIS16080_DIN_AIN2 (3 << 10)
|
||||
|
||||
/*
|
||||
* 1: Write contents on DIN to control register.
|
||||
* 0: No changes to control register.
|
||||
*/
|
||||
|
||||
#define ADIS16080_DIN_WRITE (1 << 15)
|
||||
|
||||
struct adis16080_chip_info {
|
||||
int scale_val;
|
||||
int scale_val2;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adis16080_state - device instance specific data
|
||||
* @us: actual spi_device to write data
|
||||
* @info: chip specific parameters
|
||||
* @buf: transmit or receive buffer
|
||||
**/
|
||||
struct adis16080_state {
|
||||
struct spi_device *us;
|
||||
const struct adis16080_chip_info *info;
|
||||
|
||||
__be16 buf ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adis16080_read_sample(struct iio_dev *indio_dev,
|
||||
u16 addr, int *val)
|
||||
{
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer t[] = {
|
||||
{
|
||||
.tx_buf = &st->buf,
|
||||
.len = 2,
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->buf,
|
||||
.len = 2,
|
||||
},
|
||||
};
|
||||
|
||||
st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE);
|
||||
|
||||
ret = spi_sync_transfer(st->us, t, ARRAY_SIZE(t));
|
||||
if (ret == 0)
|
||||
*val = sign_extend32(be16_to_cpu(st->buf), 11);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16080_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis16080_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16080_read_sample(indio_dev, chan->address, val);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = st->info->scale_val;
|
||||
*val2 = st->info->scale_val2;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_VOLTAGE:
|
||||
/* VREF = 5V, 12 bits */
|
||||
*val = 5000;
|
||||
*val2 = 12;
|
||||
return IIO_VAL_FRACTIONAL_LOG2;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = 85000 - 25000;
|
||||
*val2 = 585;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_VOLTAGE:
|
||||
/* 2.5 V = 0 */
|
||||
*val = 2048;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
/* 85 C = 585, 25 C = 0 */
|
||||
*val = DIV_ROUND_CLOSEST(25 * 585, 85 - 25);
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16080_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ADIS16080_DIN_GYRO,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_AIN1,
|
||||
}, {
|
||||
.type = IIO_VOLTAGE,
|
||||
.indexed = 1,
|
||||
.channel = 1,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_AIN2,
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16080_DIN_TEMP,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info adis16080_info = {
|
||||
.read_raw = &adis16080_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_ADIS16080,
|
||||
ID_ADIS16100,
|
||||
};
|
||||
|
||||
static const struct adis16080_chip_info adis16080_chip_info[] = {
|
||||
[ID_ADIS16080] = {
|
||||
/* 80 degree = 819, 819 rad = 46925 degree */
|
||||
.scale_val = 80,
|
||||
.scale_val2 = 46925,
|
||||
},
|
||||
[ID_ADIS16100] = {
|
||||
/* 300 degree = 1230, 1230 rad = 70474 degree */
|
||||
.scale_val = 300,
|
||||
.scale_val2 = 70474,
|
||||
},
|
||||
};
|
||||
|
||||
static int adis16080_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct adis16080_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
/* Allocate the comms buffers */
|
||||
st->us = spi;
|
||||
st->info = &adis16080_chip_info[id->driver_data];
|
||||
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->channels = adis16080_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16080_channels);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16080_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return iio_device_register(indio_dev);
|
||||
}
|
||||
|
||||
static int adis16080_remove(struct spi_device *spi)
|
||||
{
|
||||
iio_device_unregister(spi_get_drvdata(spi));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16080_ids[] = {
|
||||
{ "adis16080", ID_ADIS16080 },
|
||||
{ "adis16100", ID_ADIS16100 },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16080_ids);
|
||||
|
||||
static struct spi_driver adis16080_driver = {
|
||||
.driver = {
|
||||
.name = "adis16080",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adis16080_probe,
|
||||
.remove = adis16080_remove,
|
||||
.id_table = adis16080_ids,
|
||||
};
|
||||
module_spi_driver(adis16080_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16080/100 Yaw Rate Gyroscope Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
179
drivers/iio/gyro/adis16130.c
Normal file
179
drivers/iio/gyro/adis16130.c
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
|
||||
#define ADIS16130_CON 0x0
|
||||
#define ADIS16130_CON_RD (1 << 6)
|
||||
#define ADIS16130_IOP 0x1
|
||||
|
||||
/* 1 = data-ready signal low when unread data on all channels; */
|
||||
#define ADIS16130_IOP_ALL_RDY (1 << 3)
|
||||
#define ADIS16130_IOP_SYNC (1 << 0) /* 1 = synchronization enabled */
|
||||
#define ADIS16130_RATEDATA 0x8 /* Gyroscope output, rate of rotation */
|
||||
#define ADIS16130_TEMPDATA 0xA /* Temperature output */
|
||||
#define ADIS16130_RATECS 0x28 /* Gyroscope channel setup */
|
||||
#define ADIS16130_RATECS_EN (1 << 3) /* 1 = channel enable; */
|
||||
#define ADIS16130_TEMPCS 0x2A /* Temperature channel setup */
|
||||
#define ADIS16130_TEMPCS_EN (1 << 3)
|
||||
#define ADIS16130_RATECONV 0x30
|
||||
#define ADIS16130_TEMPCONV 0x32
|
||||
#define ADIS16130_MODE 0x38
|
||||
#define ADIS16130_MODE_24BIT (1 << 1) /* 1 = 24-bit resolution; */
|
||||
|
||||
/**
|
||||
* struct adis16130_state - device instance specific data
|
||||
* @us: actual spi_device to write data
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @buf: unified tx/rx buffer
|
||||
**/
|
||||
struct adis16130_state {
|
||||
struct spi_device *us;
|
||||
struct mutex buf_lock;
|
||||
u8 buf[4] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
struct adis16130_state *st = iio_priv(indio_dev);
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = st->buf,
|
||||
.rx_buf = st->buf,
|
||||
.len = 4,
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
|
||||
st->buf[0] = ADIS16130_CON_RD | reg_addr;
|
||||
st->buf[1] = st->buf[2] = st->buf[3] = 0;
|
||||
|
||||
ret = spi_sync_transfer(st->us, &xfer, 1);
|
||||
if (ret == 0)
|
||||
*val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3];
|
||||
mutex_unlock(&st->buf_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16130_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
u32 temp;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
/* Take the iio_dev status lock */
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
ret = adis16130_spi_read(indio_dev, chan->address, &temp);
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
if (ret)
|
||||
return ret;
|
||||
*val = temp;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
/* 0 degree = 838860, 250 degree = 14260608 */
|
||||
*val = 250;
|
||||
*val2 = 336440817; /* RAD_TO_DEGREE(14260608 - 8388608) */
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_TEMP:
|
||||
/* 0C = 8036283, 105C = 9516048 */
|
||||
*val = 105000;
|
||||
*val2 = 9516048 - 8036283;
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = -8388608;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_TEMP:
|
||||
*val = -8036283;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16130_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16130_RATEDATA,
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_OFFSET),
|
||||
.address = ADIS16130_TEMPDATA,
|
||||
}
|
||||
};
|
||||
|
||||
static const struct iio_info adis16130_info = {
|
||||
.read_raw = &adis16130_read_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16130_probe(struct spi_device *spi)
|
||||
{
|
||||
struct adis16130_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
indio_dev->channels = adis16130_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16130_channels);
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16130_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
return devm_iio_device_register(&spi->dev, indio_dev);
|
||||
}
|
||||
|
||||
static struct spi_driver adis16130_driver = {
|
||||
.driver = {
|
||||
.name = "adis16130",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adis16130_probe,
|
||||
};
|
||||
module_spi_driver(adis16130_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16130 High Precision Angular Rate");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("spi:adis16130");
|
||||
577
drivers/iio/gyro/adis16136.c
Normal file
577
drivers/iio/gyro/adis16136.c
Normal file
|
|
@ -0,0 +1,577 @@
|
|||
/*
|
||||
* ADIS16133/ADIS16135/ADIS16136 gyroscope driver
|
||||
*
|
||||
* Copyright 2012 Analog Devices Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#define ADIS16136_REG_FLASH_CNT 0x00
|
||||
#define ADIS16136_REG_TEMP_OUT 0x02
|
||||
#define ADIS16136_REG_GYRO_OUT2 0x04
|
||||
#define ADIS16136_REG_GYRO_OUT 0x06
|
||||
#define ADIS16136_REG_GYRO_OFF2 0x08
|
||||
#define ADIS16136_REG_GYRO_OFF 0x0A
|
||||
#define ADIS16136_REG_ALM_MAG1 0x10
|
||||
#define ADIS16136_REG_ALM_MAG2 0x12
|
||||
#define ADIS16136_REG_ALM_SAMPL1 0x14
|
||||
#define ADIS16136_REG_ALM_SAMPL2 0x16
|
||||
#define ADIS16136_REG_ALM_CTRL 0x18
|
||||
#define ADIS16136_REG_GPIO_CTRL 0x1A
|
||||
#define ADIS16136_REG_MSC_CTRL 0x1C
|
||||
#define ADIS16136_REG_SMPL_PRD 0x1E
|
||||
#define ADIS16136_REG_AVG_CNT 0x20
|
||||
#define ADIS16136_REG_DEC_RATE 0x22
|
||||
#define ADIS16136_REG_SLP_CTRL 0x24
|
||||
#define ADIS16136_REG_DIAG_STAT 0x26
|
||||
#define ADIS16136_REG_GLOB_CMD 0x28
|
||||
#define ADIS16136_REG_LOT1 0x32
|
||||
#define ADIS16136_REG_LOT2 0x34
|
||||
#define ADIS16136_REG_LOT3 0x36
|
||||
#define ADIS16136_REG_PROD_ID 0x38
|
||||
#define ADIS16136_REG_SERIAL_NUM 0x3A
|
||||
|
||||
#define ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL 2
|
||||
#define ADIS16136_DIAG_STAT_SPI_FAIL 3
|
||||
#define ADIS16136_DIAG_STAT_SELF_TEST_FAIL 5
|
||||
#define ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL 6
|
||||
|
||||
#define ADIS16136_MSC_CTRL_MEMORY_TEST BIT(11)
|
||||
#define ADIS16136_MSC_CTRL_SELF_TEST BIT(10)
|
||||
|
||||
struct adis16136_chip_info {
|
||||
unsigned int precision;
|
||||
unsigned int fullscale;
|
||||
};
|
||||
|
||||
struct adis16136 {
|
||||
const struct adis16136_chip_info *chip_info;
|
||||
|
||||
struct adis adis;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
static ssize_t adis16136_show_serial(struct file *file,
|
||||
char __user *userbuf, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct adis16136 *adis16136 = file->private_data;
|
||||
uint16_t lot1, lot2, lot3, serial;
|
||||
char buf[20];
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SERIAL_NUM,
|
||||
&serial);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT1, &lot1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT2, &lot2);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_LOT3, &lot3);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf), "%.4x%.4x%.4x-%.4x\n", lot1, lot2,
|
||||
lot3, serial);
|
||||
|
||||
return simple_read_from_buffer(userbuf, count, ppos, buf, len);
|
||||
}
|
||||
|
||||
static const struct file_operations adis16136_serial_fops = {
|
||||
.open = simple_open,
|
||||
.read = adis16136_show_serial,
|
||||
.llseek = default_llseek,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int adis16136_show_product_id(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = arg;
|
||||
u16 prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = prod_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16136_product_id_fops,
|
||||
adis16136_show_product_id, NULL, "%llu\n");
|
||||
|
||||
static int adis16136_show_flash_count(void *arg, u64 *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = arg;
|
||||
uint16_t flash_count;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_FLASH_CNT,
|
||||
&flash_count);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = flash_count;
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SIMPLE_ATTRIBUTE(adis16136_flash_count_fops,
|
||||
adis16136_show_flash_count, NULL, "%lld\n");
|
||||
|
||||
static int adis16136_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_serial_fops);
|
||||
debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_product_id_fops);
|
||||
debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
|
||||
adis16136, &adis16136_flash_count_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int adis16136_debugfs_init(struct iio_dev *indio_dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int adis16136_set_freq(struct adis16136 *adis16136, unsigned int freq)
|
||||
{
|
||||
unsigned int t;
|
||||
|
||||
t = 32768 / freq;
|
||||
if (t < 0xf)
|
||||
t = 0xf;
|
||||
else if (t > 0xffff)
|
||||
t = 0xffff;
|
||||
else
|
||||
t--;
|
||||
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, t);
|
||||
}
|
||||
|
||||
static int adis16136_get_freq(struct adis16136 *adis16136, unsigned int *freq)
|
||||
{
|
||||
uint16_t t;
|
||||
int ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_SMPL_PRD, &t);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*freq = 32768 / (t + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t adis16136_write_frequency(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf, size_t len)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtouint(buf, 10, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val == 0)
|
||||
return -EINVAL;
|
||||
|
||||
ret = adis16136_set_freq(adis16136, val);
|
||||
|
||||
return ret ? ret : len;
|
||||
}
|
||||
|
||||
static ssize_t adis16136_read_frequency(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return sprintf(buf, "%d\n", freq);
|
||||
}
|
||||
|
||||
static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
|
||||
adis16136_read_frequency,
|
||||
adis16136_write_frequency);
|
||||
|
||||
static const unsigned adis16136_3db_divisors[] = {
|
||||
[0] = 2, /* Special case */
|
||||
[1] = 6,
|
||||
[2] = 12,
|
||||
[3] = 25,
|
||||
[4] = 50,
|
||||
[5] = 100,
|
||||
[6] = 200,
|
||||
[7] = 200, /* Not a valid setting */
|
||||
};
|
||||
|
||||
static int adis16136_set_filter(struct iio_dev *indio_dev, int val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
int i, ret;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = ARRAY_SIZE(adis16136_3db_divisors) - 1; i >= 1; i--) {
|
||||
if (freq / adis16136_3db_divisors[i] >= val)
|
||||
break;
|
||||
}
|
||||
|
||||
return adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i);
|
||||
}
|
||||
|
||||
static int adis16136_get_filter(struct iio_dev *indio_dev, int *val)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int freq;
|
||||
uint16_t val16;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
ret = adis16136_get_freq(adis16136, &freq);
|
||||
if (ret < 0)
|
||||
goto err_unlock;
|
||||
|
||||
*val = freq / adis16136_3db_divisors[val16 & 0x07];
|
||||
|
||||
err_unlock:
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
|
||||
return ret ? ret : IIO_VAL_INT;
|
||||
}
|
||||
|
||||
static int adis16136_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int *val, int *val2, long info)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
uint32_t val32;
|
||||
int ret;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan, 0, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = adis16136->chip_info->precision;
|
||||
*val2 = (adis16136->chip_info->fullscale << 16);
|
||||
return IIO_VAL_FRACTIONAL;
|
||||
case IIO_TEMP:
|
||||
*val = 10;
|
||||
*val2 = 697000; /* 0.010697 degree Celsius */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = adis_read_reg_32(&adis16136->adis,
|
||||
ADIS16136_REG_GYRO_OFF2, &val32);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(val32, 31);
|
||||
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16136_get_filter(indio_dev, val);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int adis16136_write_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan, int val, int val2, long info)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
return adis_write_reg_32(&adis16136->adis,
|
||||
ADIS16136_REG_GYRO_OFF2, val);
|
||||
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
|
||||
return adis16136_set_filter(indio_dev, val);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
enum {
|
||||
ADIS16136_SCAN_GYRO,
|
||||
ADIS16136_SCAN_TEMP,
|
||||
};
|
||||
|
||||
static const struct iio_chan_spec adis16136_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
|
||||
|
||||
.address = ADIS16136_REG_GYRO_OUT2,
|
||||
.scan_index = ADIS16136_SCAN_GYRO,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 32,
|
||||
.storagebits = 32,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.address = ADIS16136_REG_TEMP_OUT,
|
||||
.scan_index = ADIS16136_SCAN_TEMP,
|
||||
.scan_type = {
|
||||
.sign = 's',
|
||||
.realbits = 16,
|
||||
.storagebits = 16,
|
||||
.endianness = IIO_BE,
|
||||
},
|
||||
},
|
||||
IIO_CHAN_SOFT_TIMESTAMP(2),
|
||||
};
|
||||
|
||||
static struct attribute *adis16136_attributes[] = {
|
||||
&iio_dev_attr_sampling_frequency.dev_attr.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group adis16136_attribute_group = {
|
||||
.attrs = adis16136_attributes,
|
||||
};
|
||||
|
||||
static const struct iio_info adis16136_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.attrs = &adis16136_attribute_group,
|
||||
.read_raw = &adis16136_read_raw,
|
||||
.write_raw = &adis16136_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.debugfs_reg_access = adis_debugfs_reg_access,
|
||||
};
|
||||
|
||||
static int adis16136_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = adis_write_reg_16(&adis16136->adis, ADIS16136_REG_SLP_CTRL, 0xff);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev,
|
||||
"Could not power down device: %d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16136_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
unsigned int device_id;
|
||||
uint16_t prod_id;
|
||||
int ret;
|
||||
|
||||
ret = adis_initial_startup(&adis16136->adis);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_read_reg_16(&adis16136->adis, ADIS16136_REG_PROD_ID,
|
||||
&prod_id);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sscanf(indio_dev->name, "adis%u\n", &device_id);
|
||||
|
||||
if (prod_id != device_id)
|
||||
dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
|
||||
device_id, prod_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const adis16136_status_error_msgs[] = {
|
||||
[ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL] = "Flash update failed",
|
||||
[ADIS16136_DIAG_STAT_SPI_FAIL] = "SPI failure",
|
||||
[ADIS16136_DIAG_STAT_SELF_TEST_FAIL] = "Self test error",
|
||||
[ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL] = "Flash checksum error",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16136_data = {
|
||||
.diag_stat_reg = ADIS16136_REG_DIAG_STAT,
|
||||
.glob_cmd_reg = ADIS16136_REG_GLOB_CMD,
|
||||
.msc_ctrl_reg = ADIS16136_REG_MSC_CTRL,
|
||||
|
||||
.self_test_mask = ADIS16136_MSC_CTRL_SELF_TEST,
|
||||
.startup_delay = 80,
|
||||
|
||||
.read_delay = 10,
|
||||
.write_delay = 10,
|
||||
|
||||
.status_error_msgs = adis16136_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16136_DIAG_STAT_FLASH_UPDATE_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SPI_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_SELF_TEST_FAIL) |
|
||||
BIT(ADIS16136_DIAG_STAT_FLASH_CHKSUM_FAIL),
|
||||
};
|
||||
|
||||
enum adis16136_id {
|
||||
ID_ADIS16133,
|
||||
ID_ADIS16135,
|
||||
ID_ADIS16136,
|
||||
};
|
||||
|
||||
static const struct adis16136_chip_info adis16136_chip_info[] = {
|
||||
[ID_ADIS16133] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(1200),
|
||||
.fullscale = 24000,
|
||||
},
|
||||
[ID_ADIS16135] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(300),
|
||||
.fullscale = 24000,
|
||||
},
|
||||
[ID_ADIS16136] = {
|
||||
.precision = IIO_DEGREE_TO_RAD(450),
|
||||
.fullscale = 24623,
|
||||
},
|
||||
};
|
||||
|
||||
static int adis16136_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct spi_device_id *id = spi_get_device_id(spi);
|
||||
struct adis16136 *adis16136;
|
||||
struct iio_dev *indio_dev;
|
||||
int ret;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis16136));
|
||||
if (indio_dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
adis16136 = iio_priv(indio_dev);
|
||||
|
||||
adis16136->chip_info = &adis16136_chip_info[id->driver_data];
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->channels = adis16136_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16136_channels);
|
||||
indio_dev->info = &adis16136_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(&adis16136->adis, indio_dev, spi, &adis16136_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(&adis16136->adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis16136_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_stop_device;
|
||||
|
||||
adis16136_debugfs_init(indio_dev);
|
||||
|
||||
return 0;
|
||||
|
||||
error_stop_device:
|
||||
adis16136_stop_device(indio_dev);
|
||||
error_cleanup_buffer:
|
||||
adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16136_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis16136 *adis16136 = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16136_stop_device(indio_dev);
|
||||
|
||||
adis_cleanup_buffer_and_trigger(&adis16136->adis, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adis16136_ids[] = {
|
||||
{ "adis16133", ID_ADIS16133 },
|
||||
{ "adis16135", ID_ADIS16135 },
|
||||
{ "adis16136", ID_ADIS16136 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16136_ids);
|
||||
|
||||
static struct spi_driver adis16136_driver = {
|
||||
.driver = {
|
||||
.name = "adis16136",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = adis16136_ids,
|
||||
.probe = adis16136_probe,
|
||||
.remove = adis16136_remove,
|
||||
};
|
||||
module_spi_driver(adis16136_driver);
|
||||
|
||||
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16133/ADIS16135/ADIS16136 gyroscope driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
389
drivers/iio/gyro/adis16260.c
Normal file
389
drivers/iio/gyro/adis16260.c
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/*
|
||||
* ADIS16260/ADIS16265 Programmable Digital Gyroscope Sensor Driver
|
||||
*
|
||||
* Copyright 2010 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2 or later.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/imu/adis.h>
|
||||
|
||||
#define ADIS16260_STARTUP_DELAY 220 /* ms */
|
||||
|
||||
#define ADIS16260_FLASH_CNT 0x00 /* Flash memory write count */
|
||||
#define ADIS16260_SUPPLY_OUT 0x02 /* Power supply measurement */
|
||||
#define ADIS16260_GYRO_OUT 0x04 /* X-axis gyroscope output */
|
||||
#define ADIS16260_AUX_ADC 0x0A /* analog input channel measurement */
|
||||
#define ADIS16260_TEMP_OUT 0x0C /* internal temperature measurement */
|
||||
#define ADIS16260_ANGL_OUT 0x0E /* angle displacement */
|
||||
#define ADIS16260_GYRO_OFF 0x14 /* Calibration, offset/bias adjustment */
|
||||
#define ADIS16260_GYRO_SCALE 0x16 /* Calibration, scale adjustment */
|
||||
#define ADIS16260_ALM_MAG1 0x20 /* Alarm 1 magnitude/polarity setting */
|
||||
#define ADIS16260_ALM_MAG2 0x22 /* Alarm 2 magnitude/polarity setting */
|
||||
#define ADIS16260_ALM_SMPL1 0x24 /* Alarm 1 dynamic rate of change setting */
|
||||
#define ADIS16260_ALM_SMPL2 0x26 /* Alarm 2 dynamic rate of change setting */
|
||||
#define ADIS16260_ALM_CTRL 0x28 /* Alarm control */
|
||||
#define ADIS16260_AUX_DAC 0x30 /* Auxiliary DAC data */
|
||||
#define ADIS16260_GPIO_CTRL 0x32 /* Control, digital I/O line */
|
||||
#define ADIS16260_MSC_CTRL 0x34 /* Control, data ready, self-test settings */
|
||||
#define ADIS16260_SMPL_PRD 0x36 /* Control, internal sample rate */
|
||||
#define ADIS16260_SENS_AVG 0x38 /* Control, dynamic range, filtering */
|
||||
#define ADIS16260_SLP_CNT 0x3A /* Control, sleep mode initiation */
|
||||
#define ADIS16260_DIAG_STAT 0x3C /* Diagnostic, error flags */
|
||||
#define ADIS16260_GLOB_CMD 0x3E /* Control, global commands */
|
||||
#define ADIS16260_LOT_ID1 0x52 /* Lot Identification Code 1 */
|
||||
#define ADIS16260_LOT_ID2 0x54 /* Lot Identification Code 2 */
|
||||
#define ADIS16260_PROD_ID 0x56 /* Product identifier;
|
||||
* convert to decimal = 16,265/16,260 */
|
||||
#define ADIS16260_SERIAL_NUM 0x58 /* Serial number */
|
||||
|
||||
#define ADIS16260_ERROR_ACTIVE (1<<14)
|
||||
#define ADIS16260_NEW_DATA (1<<15)
|
||||
|
||||
/* MSC_CTRL */
|
||||
#define ADIS16260_MSC_CTRL_MEM_TEST (1<<11)
|
||||
/* Internal self-test enable */
|
||||
#define ADIS16260_MSC_CTRL_INT_SELF_TEST (1<<10)
|
||||
#define ADIS16260_MSC_CTRL_NEG_SELF_TEST (1<<9)
|
||||
#define ADIS16260_MSC_CTRL_POS_SELF_TEST (1<<8)
|
||||
#define ADIS16260_MSC_CTRL_DATA_RDY_EN (1<<2)
|
||||
#define ADIS16260_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1)
|
||||
#define ADIS16260_MSC_CTRL_DATA_RDY_DIO2 (1<<0)
|
||||
|
||||
/* SMPL_PRD */
|
||||
/* Time base (tB): 0 = 1.953 ms, 1 = 60.54 ms */
|
||||
#define ADIS16260_SMPL_PRD_TIME_BASE (1<<7)
|
||||
#define ADIS16260_SMPL_PRD_DIV_MASK 0x7F
|
||||
|
||||
/* SLP_CNT */
|
||||
#define ADIS16260_SLP_CNT_POWER_OFF 0x80
|
||||
|
||||
/* DIAG_STAT */
|
||||
#define ADIS16260_DIAG_STAT_ALARM2 (1<<9)
|
||||
#define ADIS16260_DIAG_STAT_ALARM1 (1<<8)
|
||||
#define ADIS16260_DIAG_STAT_FLASH_CHK_BIT 6
|
||||
#define ADIS16260_DIAG_STAT_SELF_TEST_BIT 5
|
||||
#define ADIS16260_DIAG_STAT_OVERFLOW_BIT 4
|
||||
#define ADIS16260_DIAG_STAT_SPI_FAIL_BIT 3
|
||||
#define ADIS16260_DIAG_STAT_FLASH_UPT_BIT 2
|
||||
#define ADIS16260_DIAG_STAT_POWER_HIGH_BIT 1
|
||||
#define ADIS16260_DIAG_STAT_POWER_LOW_BIT 0
|
||||
|
||||
/* GLOB_CMD */
|
||||
#define ADIS16260_GLOB_CMD_SW_RESET (1<<7)
|
||||
#define ADIS16260_GLOB_CMD_FLASH_UPD (1<<3)
|
||||
#define ADIS16260_GLOB_CMD_DAC_LATCH (1<<2)
|
||||
#define ADIS16260_GLOB_CMD_FAC_CALIB (1<<1)
|
||||
#define ADIS16260_GLOB_CMD_AUTO_NULL (1<<0)
|
||||
|
||||
#define ADIS16260_SPI_SLOW (u32)(300 * 1000)
|
||||
#define ADIS16260_SPI_BURST (u32)(1000 * 1000)
|
||||
#define ADIS16260_SPI_FAST (u32)(2000 * 1000)
|
||||
|
||||
/* At the moment triggers are only used for ring buffer
|
||||
* filling. This may change!
|
||||
*/
|
||||
|
||||
#define ADIS16260_SCAN_GYRO 0
|
||||
#define ADIS16260_SCAN_SUPPLY 1
|
||||
#define ADIS16260_SCAN_AUX_ADC 2
|
||||
#define ADIS16260_SCAN_TEMP 3
|
||||
#define ADIS16260_SCAN_ANGL 4
|
||||
|
||||
/* Power down the device */
|
||||
static int adis16260_stop_device(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct adis *adis = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u16 val = ADIS16260_SLP_CNT_POWER_OFF;
|
||||
|
||||
ret = adis_write_reg_16(adis, ADIS16260_SLP_CNT, val);
|
||||
if (ret)
|
||||
dev_err(&indio_dev->dev, "problem with turning device off: SLP_CNT");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adis16260_channels[] = {
|
||||
ADIS_GYRO_CHAN(X, ADIS16260_GYRO_OUT, ADIS16260_SCAN_GYRO,
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_CALIBSCALE),
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 14),
|
||||
ADIS_INCLI_CHAN(X, ADIS16260_ANGL_OUT, ADIS16260_SCAN_ANGL, 0,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 14),
|
||||
ADIS_TEMP_CHAN(ADIS16260_TEMP_OUT, ADIS16260_SCAN_TEMP,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||
ADIS_SUPPLY_CHAN(ADIS16260_SUPPLY_OUT, ADIS16260_SCAN_SUPPLY,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||
ADIS_AUX_ADC_CHAN(ADIS16260_AUX_ADC, ADIS16260_SCAN_AUX_ADC,
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ), 12),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(5),
|
||||
};
|
||||
|
||||
static const u8 adis16260_addresses[][2] = {
|
||||
[ADIS16260_SCAN_GYRO] = { ADIS16260_GYRO_OFF, ADIS16260_GYRO_SCALE },
|
||||
};
|
||||
|
||||
static int adis16260_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *adis = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 addr;
|
||||
s16 val16;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
return adis_single_conversion(indio_dev, chan,
|
||||
ADIS16260_ERROR_ACTIVE, val);
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
if (spi_get_device_id(adis->spi)->driver_data) {
|
||||
/* 0.01832 degree / sec */
|
||||
*val2 = IIO_DEGREE_TO_RAD(18320);
|
||||
} else {
|
||||
/* 0.07326 degree / sec */
|
||||
*val2 = IIO_DEGREE_TO_RAD(73260);
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_INCLI:
|
||||
*val = 0;
|
||||
*val2 = IIO_DEGREE_TO_RAD(36630);
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_VOLTAGE:
|
||||
if (chan->channel == 0) {
|
||||
*val = 1;
|
||||
*val2 = 831500; /* 1.8315 mV */
|
||||
} else {
|
||||
*val = 0;
|
||||
*val2 = 610500; /* 610.5 uV */
|
||||
}
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
case IIO_TEMP:
|
||||
*val = 145;
|
||||
*val2 = 300000; /* 0.1453 C */
|
||||
return IIO_VAL_INT_PLUS_MICRO;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = 250000 / 1453; /* 25 C = 0x00 */
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
addr = adis16260_addresses[chan->scan_index][0];
|
||||
ret = adis_read_reg_16(adis, addr, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = sign_extend32(val16, 11);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
addr = adis16260_addresses[chan->scan_index][1];
|
||||
ret = adis_read_reg_16(adis, addr, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = val16;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = adis_read_reg_16(adis, ADIS16260_SMPL_PRD, &val16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (spi_get_device_id(adis->spi)->driver_data)
|
||||
/* If an adis16251 */
|
||||
*val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ?
|
||||
8 : 256;
|
||||
else
|
||||
*val = (val16 & ADIS16260_SMPL_PRD_TIME_BASE) ?
|
||||
66 : 2048;
|
||||
*val /= (val16 & ADIS16260_SMPL_PRD_DIV_MASK) + 1;
|
||||
return IIO_VAL_INT;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int adis16260_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct adis *adis = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 addr;
|
||||
u8 t;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -2048 || val >= 2048)
|
||||
return -EINVAL;
|
||||
|
||||
addr = adis16260_addresses[chan->scan_index][0];
|
||||
return adis_write_reg_16(adis, addr, val);
|
||||
case IIO_CHAN_INFO_CALIBSCALE:
|
||||
if (val < 0 || val >= 4096)
|
||||
return -EINVAL;
|
||||
|
||||
addr = adis16260_addresses[chan->scan_index][1];
|
||||
return adis_write_reg_16(adis, addr, val);
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
if (spi_get_device_id(adis->spi)->driver_data)
|
||||
t = 256 / val;
|
||||
else
|
||||
t = 2048 / val;
|
||||
|
||||
if (t > ADIS16260_SMPL_PRD_DIV_MASK)
|
||||
t = ADIS16260_SMPL_PRD_DIV_MASK;
|
||||
else if (t > 0)
|
||||
t--;
|
||||
|
||||
if (t >= 0x0A)
|
||||
adis->spi->max_speed_hz = ADIS16260_SPI_SLOW;
|
||||
else
|
||||
adis->spi->max_speed_hz = ADIS16260_SPI_FAST;
|
||||
ret = adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct iio_info adis16260_info = {
|
||||
.read_raw = &adis16260_read_raw,
|
||||
.write_raw = &adis16260_write_raw,
|
||||
.update_scan_mode = adis_update_scan_mode,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const char * const adis1620_status_error_msgs[] = {
|
||||
[ADIS16260_DIAG_STAT_FLASH_CHK_BIT] = "Flash checksum error",
|
||||
[ADIS16260_DIAG_STAT_SELF_TEST_BIT] = "Self test error",
|
||||
[ADIS16260_DIAG_STAT_OVERFLOW_BIT] = "Sensor overrange",
|
||||
[ADIS16260_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
|
||||
[ADIS16260_DIAG_STAT_FLASH_UPT_BIT] = "Flash update failed",
|
||||
[ADIS16260_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 5.25",
|
||||
[ADIS16260_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 4.75",
|
||||
};
|
||||
|
||||
static const struct adis_data adis16260_data = {
|
||||
.write_delay = 30,
|
||||
.read_delay = 30,
|
||||
.msc_ctrl_reg = ADIS16260_MSC_CTRL,
|
||||
.glob_cmd_reg = ADIS16260_GLOB_CMD,
|
||||
.diag_stat_reg = ADIS16260_DIAG_STAT,
|
||||
|
||||
.self_test_mask = ADIS16260_MSC_CTRL_MEM_TEST,
|
||||
.startup_delay = ADIS16260_STARTUP_DELAY,
|
||||
|
||||
.status_error_msgs = adis1620_status_error_msgs,
|
||||
.status_error_mask = BIT(ADIS16260_DIAG_STAT_FLASH_CHK_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_SELF_TEST_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_OVERFLOW_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_SPI_FAIL_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_FLASH_UPT_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_POWER_HIGH_BIT) |
|
||||
BIT(ADIS16260_DIAG_STAT_POWER_LOW_BIT),
|
||||
};
|
||||
|
||||
static int adis16260_probe(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev;
|
||||
struct adis *adis;
|
||||
int ret;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adis));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
adis = iio_priv(indio_dev);
|
||||
/* this is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->name = spi_get_device_id(spi)->name;
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adis16260_info;
|
||||
indio_dev->channels = adis16260_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(adis16260_channels);
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = adis_init(adis, indio_dev, spi, &adis16260_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adis_setup_buffer_and_trigger(adis, indio_dev, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adis_initial_startup(adis);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer_trigger;
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_cleanup_buffer_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_cleanup_buffer_trigger:
|
||||
adis_cleanup_buffer_and_trigger(adis, indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adis16260_remove(struct spi_device *spi)
|
||||
{
|
||||
struct iio_dev *indio_dev = spi_get_drvdata(spi);
|
||||
struct adis *adis = iio_priv(indio_dev);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
adis16260_stop_device(indio_dev);
|
||||
adis_cleanup_buffer_and_trigger(adis, indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* These parts do not need to be differentiated until someone adds
|
||||
* support for the on chip filtering.
|
||||
*/
|
||||
static const struct spi_device_id adis16260_id[] = {
|
||||
{"adis16260", 0},
|
||||
{"adis16265", 0},
|
||||
{"adis16250", 0},
|
||||
{"adis16255", 0},
|
||||
{"adis16251", 1},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adis16260_id);
|
||||
|
||||
static struct spi_driver adis16260_driver = {
|
||||
.driver = {
|
||||
.name = "adis16260",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adis16260_probe,
|
||||
.remove = adis16260_remove,
|
||||
.id_table = adis16260_id,
|
||||
};
|
||||
module_spi_driver(adis16260_driver);
|
||||
|
||||
MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADIS16260/5 Digital Gyroscope Sensor");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
468
drivers/iio/gyro/adxrs450.c
Normal file
468
drivers/iio/gyro/adxrs450.c
Normal file
|
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* ADXRS450/ADXRS453 Digital Output Gyroscope Driver
|
||||
*
|
||||
* Copyright 2011 Analog Devices Inc.
|
||||
*
|
||||
* Licensed under the GPL-2.
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
|
||||
#define ADXRS450_STARTUP_DELAY 50 /* ms */
|
||||
|
||||
/* The MSB for the spi commands */
|
||||
#define ADXRS450_SENSOR_DATA (0x20 << 24)
|
||||
#define ADXRS450_WRITE_DATA (0x40 << 24)
|
||||
#define ADXRS450_READ_DATA (0x80 << 24)
|
||||
|
||||
#define ADXRS450_RATE1 0x00 /* Rate Registers */
|
||||
#define ADXRS450_TEMP1 0x02 /* Temperature Registers */
|
||||
#define ADXRS450_LOCST1 0x04 /* Low CST Memory Registers */
|
||||
#define ADXRS450_HICST1 0x06 /* High CST Memory Registers */
|
||||
#define ADXRS450_QUAD1 0x08 /* Quad Memory Registers */
|
||||
#define ADXRS450_FAULT1 0x0A /* Fault Registers */
|
||||
#define ADXRS450_PID1 0x0C /* Part ID Register 1 */
|
||||
#define ADXRS450_SNH 0x0E /* Serial Number Registers, 4 bytes */
|
||||
#define ADXRS450_SNL 0x10
|
||||
#define ADXRS450_DNC1 0x12 /* Dynamic Null Correction Registers */
|
||||
/* Check bits */
|
||||
#define ADXRS450_P 0x01
|
||||
#define ADXRS450_CHK 0x02
|
||||
#define ADXRS450_CST 0x04
|
||||
#define ADXRS450_PWR 0x08
|
||||
#define ADXRS450_POR 0x10
|
||||
#define ADXRS450_NVM 0x20
|
||||
#define ADXRS450_Q 0x40
|
||||
#define ADXRS450_PLL 0x80
|
||||
#define ADXRS450_UV 0x100
|
||||
#define ADXRS450_OV 0x200
|
||||
#define ADXRS450_AMP 0x400
|
||||
#define ADXRS450_FAIL 0x800
|
||||
|
||||
#define ADXRS450_WRERR_MASK (0x7 << 29)
|
||||
|
||||
#define ADXRS450_MAX_RX 4
|
||||
#define ADXRS450_MAX_TX 4
|
||||
|
||||
#define ADXRS450_GET_ST(a) ((a >> 26) & 0x3)
|
||||
|
||||
enum {
|
||||
ID_ADXRS450,
|
||||
ID_ADXRS453,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct adxrs450_state - device instance specific data
|
||||
* @us: actual spi_device
|
||||
* @buf_lock: mutex to protect tx and rx
|
||||
* @tx: transmit buffer
|
||||
* @rx: receive buffer
|
||||
**/
|
||||
struct adxrs450_state {
|
||||
struct spi_device *us;
|
||||
struct mutex buf_lock;
|
||||
__be32 tx ____cacheline_aligned;
|
||||
__be32 rx;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* adxrs450_spi_read_reg_16() - read 2 bytes from a register pair
|
||||
* @indio_dev: device associated with child of actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers, which should be
|
||||
* an even address, the second register's address is reg_address + 1.
|
||||
* @val: somewhere to pass back the value read
|
||||
**/
|
||||
static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u16 *val)
|
||||
{
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
u32 tx;
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->tx),
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->rx),
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
tx = ADXRS450_READ_DATA | (reg_address << 17);
|
||||
|
||||
if (!(hweight32(tx) & 1))
|
||||
tx |= ADXRS450_P;
|
||||
|
||||
st->tx = cpu_to_be32(tx);
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = (be32_to_cpu(st->rx) >> 5) & 0xFFFF;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adxrs450_spi_write_reg_16() - write 2 bytes data to a register pair
|
||||
* @indio_dev: device associated with child of actual actual iio_dev
|
||||
* @reg_address: the address of the lower of the two registers,which should be
|
||||
* an even address, the second register's address is reg_address + 1.
|
||||
* @val: value to be written.
|
||||
**/
|
||||
static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev,
|
||||
u8 reg_address,
|
||||
u16 val)
|
||||
{
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
u32 tx;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
tx = ADXRS450_WRITE_DATA | (reg_address << 17) | (val << 1);
|
||||
|
||||
if (!(hweight32(tx) & 1))
|
||||
tx |= ADXRS450_P;
|
||||
|
||||
st->tx = cpu_to_be32(tx);
|
||||
ret = spi_write(st->us, &st->tx, sizeof(st->tx));
|
||||
if (ret)
|
||||
dev_err(&st->us->dev, "problem while writing 16 bit register 0x%02x\n",
|
||||
reg_address);
|
||||
usleep_range(100, 1000); /* enforce sequential transfer delay 0.1ms */
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adxrs450_spi_sensor_data() - read 2 bytes sensor data
|
||||
* @indio_dev: device associated with child of actual iio_dev
|
||||
* @val: somewhere to pass back the value read
|
||||
**/
|
||||
static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)
|
||||
{
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
struct spi_transfer xfers[] = {
|
||||
{
|
||||
.tx_buf = &st->tx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->tx),
|
||||
.cs_change = 1,
|
||||
}, {
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->rx),
|
||||
},
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA);
|
||||
|
||||
ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading sensor data\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = (be32_to_cpu(st->rx) >> 10) & 0xFFFF;
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* adxrs450_spi_initial() - use for initializing procedure.
|
||||
* @st: device instance specific data
|
||||
* @val: somewhere to pass back the value read
|
||||
* @chk: Whether to perform fault check
|
||||
**/
|
||||
static int adxrs450_spi_initial(struct adxrs450_state *st,
|
||||
u32 *val, char chk)
|
||||
{
|
||||
int ret;
|
||||
u32 tx;
|
||||
struct spi_transfer xfers = {
|
||||
.tx_buf = &st->tx,
|
||||
.rx_buf = &st->rx,
|
||||
.bits_per_word = 8,
|
||||
.len = sizeof(st->tx),
|
||||
};
|
||||
|
||||
mutex_lock(&st->buf_lock);
|
||||
tx = ADXRS450_SENSOR_DATA;
|
||||
if (chk)
|
||||
tx |= (ADXRS450_CHK | ADXRS450_P);
|
||||
st->tx = cpu_to_be32(tx);
|
||||
ret = spi_sync_transfer(st->us, &xfers, 1);
|
||||
if (ret) {
|
||||
dev_err(&st->us->dev, "Problem while reading initializing data\n");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
*val = be32_to_cpu(st->rx);
|
||||
|
||||
error_ret:
|
||||
mutex_unlock(&st->buf_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Recommended Startup Sequence by spec */
|
||||
static int adxrs450_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
u32 t;
|
||||
u16 data;
|
||||
int ret;
|
||||
struct adxrs450_state *st = iio_priv(indio_dev);
|
||||
|
||||
msleep(ADXRS450_STARTUP_DELAY*2);
|
||||
ret = adxrs450_spi_initial(st, &t, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (t != 0x01)
|
||||
dev_warn(&st->us->dev, "The initial power on response is not correct! Restart without reset?\n");
|
||||
|
||||
msleep(ADXRS450_STARTUP_DELAY);
|
||||
ret = adxrs450_spi_initial(st, &t, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
msleep(ADXRS450_STARTUP_DELAY);
|
||||
ret = adxrs450_spi_initial(st, &t, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) {
|
||||
dev_err(&st->us->dev, "The second response is not correct!\n");
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
ret = adxrs450_spi_initial(st, &t, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (((t & 0xff) | 0x01) != 0xff || ADXRS450_GET_ST(t) != 2) {
|
||||
dev_err(&st->us->dev, "The third response is not correct!\n");
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_FAULT1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (data & 0x0fff) {
|
||||
dev_err(&st->us->dev, "The device is not in normal status!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int adxrs450_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
if (val < -0x400 || val >= 0x400)
|
||||
return -EINVAL;
|
||||
ret = adxrs450_spi_write_reg_16(indio_dev,
|
||||
ADXRS450_DNC1, val);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int adxrs450_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val,
|
||||
int *val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
s16 t;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
ret = adxrs450_spi_sensor_data(indio_dev, &t);
|
||||
if (ret)
|
||||
break;
|
||||
*val = t;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_TEMP:
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev,
|
||||
ADXRS450_TEMP1, &t);
|
||||
if (ret)
|
||||
break;
|
||||
*val = (t >> 6) + 225;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
switch (chan->type) {
|
||||
case IIO_ANGL_VEL:
|
||||
*val = 0;
|
||||
*val2 = 218166;
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_TEMP:
|
||||
*val = 200;
|
||||
*val2 = 0;
|
||||
return IIO_VAL_INT;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
case IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW:
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_QUAD1, &t);
|
||||
if (ret)
|
||||
break;
|
||||
*val = t;
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_CALIBBIAS:
|
||||
ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_DNC1, &t);
|
||||
if (ret)
|
||||
break;
|
||||
*val = sign_extend32(t, 9);
|
||||
ret = IIO_VAL_INT;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec adxrs450_channels[2][2] = {
|
||||
[ID_ADXRS450] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_CALIBBIAS) |
|
||||
BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
},
|
||||
[ID_ADXRS453] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW),
|
||||
}, {
|
||||
.type = IIO_TEMP,
|
||||
.indexed = 1,
|
||||
.channel = 0,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const struct iio_info adxrs450_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &adxrs450_read_raw,
|
||||
.write_raw = &adxrs450_write_raw,
|
||||
};
|
||||
|
||||
static int adxrs450_probe(struct spi_device *spi)
|
||||
{
|
||||
int ret;
|
||||
struct adxrs450_state *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
/* setup the industrialio driver allocated elements */
|
||||
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
st = iio_priv(indio_dev);
|
||||
st->us = spi;
|
||||
mutex_init(&st->buf_lock);
|
||||
/* This is only used for removal purposes */
|
||||
spi_set_drvdata(spi, indio_dev);
|
||||
|
||||
indio_dev->dev.parent = &spi->dev;
|
||||
indio_dev->info = &adxrs450_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
indio_dev->channels =
|
||||
adxrs450_channels[spi_get_device_id(spi)->driver_data];
|
||||
indio_dev->num_channels = ARRAY_SIZE(adxrs450_channels);
|
||||
indio_dev->name = spi->dev.driver->name;
|
||||
|
||||
ret = devm_iio_device_register(&spi->dev, indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Get the device into a sane initial state */
|
||||
ret = adxrs450_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_device_id adxrs450_id[] = {
|
||||
{"adxrs450", ID_ADXRS450},
|
||||
{"adxrs453", ID_ADXRS453},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, adxrs450_id);
|
||||
|
||||
static struct spi_driver adxrs450_driver = {
|
||||
.driver = {
|
||||
.name = "adxrs450",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = adxrs450_probe,
|
||||
.id_table = adxrs450_id,
|
||||
};
|
||||
module_spi_driver(adxrs450_driver);
|
||||
|
||||
MODULE_AUTHOR("Cliff Cai <cliff.cai@xxxxxxxxxx>");
|
||||
MODULE_DESCRIPTION("Analog Devices ADXRS450/ADXRS453 Gyroscope SPI driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
1273
drivers/iio/gyro/bmg160.c
Normal file
1273
drivers/iio/gyro/bmg160.c
Normal file
File diff suppressed because it is too large
Load diff
427
drivers/iio/gyro/hid-sensor-gyro-3d.c
Normal file
427
drivers/iio/gyro/hid-sensor-gyro-3d.c
Normal file
|
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
* HID Sensors Driver
|
||||
* Copyright (c) 2012, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hid-sensor-hub.h>
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include "../common/hid-sensors/hid-sensor-trigger.h"
|
||||
|
||||
enum gyro_3d_channel {
|
||||
CHANNEL_SCAN_INDEX_X,
|
||||
CHANNEL_SCAN_INDEX_Y,
|
||||
CHANNEL_SCAN_INDEX_Z,
|
||||
GYRO_3D_CHANNEL_MAX,
|
||||
};
|
||||
|
||||
struct gyro_3d_state {
|
||||
struct hid_sensor_hub_callbacks callbacks;
|
||||
struct hid_sensor_common common_attributes;
|
||||
struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
|
||||
u32 gyro_val[GYRO_3D_CHANNEL_MAX];
|
||||
int scale_pre_decml;
|
||||
int scale_post_decml;
|
||||
int scale_precision;
|
||||
int value_offset;
|
||||
};
|
||||
|
||||
static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
|
||||
HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS,
|
||||
HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS,
|
||||
HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS
|
||||
};
|
||||
|
||||
/* Channel definitions */
|
||||
static const struct iio_chan_spec gyro_3d_channels[] = {
|
||||
{
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_X,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_X,
|
||||
}, {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Y,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Y,
|
||||
}, {
|
||||
.type = IIO_ANGL_VEL,
|
||||
.modified = 1,
|
||||
.channel2 = IIO_MOD_Z,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE) |
|
||||
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
|
||||
BIT(IIO_CHAN_INFO_HYSTERESIS),
|
||||
.scan_index = CHANNEL_SCAN_INDEX_Z,
|
||||
}
|
||||
};
|
||||
|
||||
/* Adjust channel real bits based on report descriptor */
|
||||
static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
|
||||
int channel, int size)
|
||||
{
|
||||
channels[channel].scan_type.sign = 's';
|
||||
/* Real storage bits will change based on the report desc. */
|
||||
channels[channel].scan_type.realbits = size * 8;
|
||||
/* Maximum size of a sample to capture is u32 */
|
||||
channels[channel].scan_type.storagebits = sizeof(u32) * 8;
|
||||
}
|
||||
|
||||
/* Channel read_raw handler */
|
||||
static int gyro_3d_read_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int *val, int *val2,
|
||||
long mask)
|
||||
{
|
||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
int report_id = -1;
|
||||
u32 address;
|
||||
int ret_type;
|
||||
s32 poll_value;
|
||||
|
||||
*val = 0;
|
||||
*val2 = 0;
|
||||
switch (mask) {
|
||||
case 0:
|
||||
poll_value = hid_sensor_read_poll_value(
|
||||
&gyro_state->common_attributes);
|
||||
if (poll_value < 0)
|
||||
return -EINVAL;
|
||||
|
||||
hid_sensor_power_state(&gyro_state->common_attributes, true);
|
||||
msleep_interruptible(poll_value * 2);
|
||||
report_id = gyro_state->gyro[chan->scan_index].report_id;
|
||||
address = gyro_3d_addresses[chan->scan_index];
|
||||
if (report_id >= 0)
|
||||
*val = sensor_hub_input_attr_get_raw_value(
|
||||
gyro_state->common_attributes.hsdev,
|
||||
HID_USAGE_SENSOR_GYRO_3D, address,
|
||||
report_id);
|
||||
else {
|
||||
*val = 0;
|
||||
hid_sensor_power_state(&gyro_state->common_attributes,
|
||||
false);
|
||||
return -EINVAL;
|
||||
}
|
||||
hid_sensor_power_state(&gyro_state->common_attributes, false);
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = gyro_state->scale_pre_decml;
|
||||
*val2 = gyro_state->scale_post_decml;
|
||||
ret_type = gyro_state->scale_precision;
|
||||
break;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
*val = gyro_state->value_offset;
|
||||
ret_type = IIO_VAL_INT;
|
||||
break;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret_type = hid_sensor_read_samp_freq_value(
|
||||
&gyro_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret_type = hid_sensor_read_raw_hyst_value(
|
||||
&gyro_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret_type = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret_type;
|
||||
}
|
||||
|
||||
/* Channel write_raw handler */
|
||||
static int gyro_3d_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
int ret = 0;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = hid_sensor_write_samp_freq_value(
|
||||
&gyro_state->common_attributes, val, val2);
|
||||
break;
|
||||
case IIO_CHAN_INFO_HYSTERESIS:
|
||||
ret = hid_sensor_write_raw_hyst_value(
|
||||
&gyro_state->common_attributes, val, val2);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct iio_info gyro_3d_info = {
|
||||
.driver_module = THIS_MODULE,
|
||||
.read_raw = &gyro_3d_read_raw,
|
||||
.write_raw = &gyro_3d_write_raw,
|
||||
};
|
||||
|
||||
/* Function to push data to buffer */
|
||||
static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data,
|
||||
int len)
|
||||
{
|
||||
dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
|
||||
iio_push_to_buffers(indio_dev, data);
|
||||
}
|
||||
|
||||
/* Callback handler to send event after all samples are received and captured */
|
||||
static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
|
||||
dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n");
|
||||
if (atomic_read(&gyro_state->common_attributes.data_ready))
|
||||
hid_sensor_push_data(indio_dev,
|
||||
gyro_state->gyro_val,
|
||||
sizeof(gyro_state->gyro_val));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Capture samples in local storage */
|
||||
static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
|
||||
unsigned usage_id,
|
||||
size_t raw_len, char *raw_data,
|
||||
void *priv)
|
||||
{
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(priv);
|
||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
int offset;
|
||||
int ret = -EINVAL;
|
||||
|
||||
switch (usage_id) {
|
||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS:
|
||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
|
||||
case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
|
||||
offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
|
||||
gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
|
||||
*(u32 *)raw_data;
|
||||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Parse report which is specific to an usage id*/
|
||||
static int gyro_3d_parse_report(struct platform_device *pdev,
|
||||
struct hid_sensor_hub_device *hsdev,
|
||||
struct iio_chan_spec *channels,
|
||||
unsigned usage_id,
|
||||
struct gyro_3d_state *st)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
|
||||
ret = sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_INPUT_REPORT,
|
||||
usage_id,
|
||||
HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i,
|
||||
&st->gyro[CHANNEL_SCAN_INDEX_X + i]);
|
||||
if (ret < 0)
|
||||
break;
|
||||
gyro_3d_adjust_channel_bit_mask(channels,
|
||||
CHANNEL_SCAN_INDEX_X + i,
|
||||
st->gyro[CHANNEL_SCAN_INDEX_X + i].size);
|
||||
}
|
||||
dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
|
||||
st->gyro[0].index,
|
||||
st->gyro[0].report_id,
|
||||
st->gyro[1].index, st->gyro[1].report_id,
|
||||
st->gyro[2].index, st->gyro[2].report_id);
|
||||
|
||||
st->scale_precision = hid_sensor_format_scale(
|
||||
HID_USAGE_SENSOR_GYRO_3D,
|
||||
&st->gyro[CHANNEL_SCAN_INDEX_X],
|
||||
&st->scale_pre_decml, &st->scale_post_decml);
|
||||
|
||||
/* Set Sensitivity field ids, when there is no individual modifier */
|
||||
if (st->common_attributes.sensitivity.index < 0) {
|
||||
sensor_hub_input_get_attribute_info(hsdev,
|
||||
HID_FEATURE_REPORT, usage_id,
|
||||
HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS |
|
||||
HID_USAGE_SENSOR_DATA_ANGL_VELOCITY,
|
||||
&st->common_attributes.sensitivity);
|
||||
dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n",
|
||||
st->common_attributes.sensitivity.index,
|
||||
st->common_attributes.sensitivity.report_id);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to initialize the processing for usage id */
|
||||
static int hid_gyro_3d_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
static const char *name = "gyro_3d";
|
||||
struct iio_dev *indio_dev;
|
||||
struct gyro_3d_state *gyro_state;
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_chan_spec *channels;
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*gyro_state));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, indio_dev);
|
||||
|
||||
gyro_state = iio_priv(indio_dev);
|
||||
gyro_state->common_attributes.hsdev = hsdev;
|
||||
gyro_state->common_attributes.pdev = pdev;
|
||||
|
||||
ret = hid_sensor_parse_common_attributes(hsdev,
|
||||
HID_USAGE_SENSOR_GYRO_3D,
|
||||
&gyro_state->common_attributes);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup common attributes\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
channels = kmemdup(gyro_3d_channels, sizeof(gyro_3d_channels),
|
||||
GFP_KERNEL);
|
||||
if (!channels) {
|
||||
dev_err(&pdev->dev, "failed to duplicate channels\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = gyro_3d_parse_report(pdev, hsdev, channels,
|
||||
HID_USAGE_SENSOR_GYRO_3D, gyro_state);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to setup attributes\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
|
||||
indio_dev->channels = channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
|
||||
indio_dev->dev.parent = &pdev->dev;
|
||||
indio_dev->info = &gyro_3d_info;
|
||||
indio_dev->name = name;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
NULL, NULL);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
|
||||
goto error_free_dev_mem;
|
||||
}
|
||||
atomic_set(&gyro_state->common_attributes.data_ready, 0);
|
||||
ret = hid_sensor_setup_trigger(indio_dev, name,
|
||||
&gyro_state->common_attributes);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "trigger setup failed\n");
|
||||
goto error_unreg_buffer_funcs;
|
||||
}
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "device register failed\n");
|
||||
goto error_remove_trigger;
|
||||
}
|
||||
|
||||
gyro_state->callbacks.send_event = gyro_3d_proc_event;
|
||||
gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
|
||||
gyro_state->callbacks.pdev = pdev;
|
||||
ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
|
||||
&gyro_state->callbacks);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "callback reg failed\n");
|
||||
goto error_iio_unreg;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
error_iio_unreg:
|
||||
iio_device_unregister(indio_dev);
|
||||
error_remove_trigger:
|
||||
hid_sensor_remove_trigger(&gyro_state->common_attributes);
|
||||
error_unreg_buffer_funcs:
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
error_free_dev_mem:
|
||||
kfree(indio_dev->channels);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Function to deinitialize the processing for usage id */
|
||||
static int hid_gyro_3d_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
|
||||
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
|
||||
struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
|
||||
|
||||
sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
|
||||
iio_device_unregister(indio_dev);
|
||||
hid_sensor_remove_trigger(&gyro_state->common_attributes);
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
kfree(indio_dev->channels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device_id hid_gyro_3d_ids[] = {
|
||||
{
|
||||
/* Format: HID-SENSOR-usage_id_in_hex_lowercase */
|
||||
.name = "HID-SENSOR-200076",
|
||||
},
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, hid_gyro_3d_ids);
|
||||
|
||||
static struct platform_driver hid_gyro_3d_platform_driver = {
|
||||
.id_table = hid_gyro_3d_ids,
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
},
|
||||
.probe = hid_gyro_3d_probe,
|
||||
.remove = hid_gyro_3d_remove,
|
||||
};
|
||||
module_platform_driver(hid_gyro_3d_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("HID Sensor Gyroscope 3D");
|
||||
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
153
drivers/iio/gyro/itg3200_buffer.c
Normal file
153
drivers/iio/gyro/itg3200_buffer.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* itg3200_buffer.c -- support InvenSense ITG3200
|
||||
* Digital 3-Axis Gyroscope driver
|
||||
*
|
||||
* Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
|
||||
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
#include <linux/iio/trigger.h>
|
||||
#include <linux/iio/trigger_consumer.h>
|
||||
#include <linux/iio/triggered_buffer.h>
|
||||
#include <linux/iio/gyro/itg3200.h>
|
||||
|
||||
|
||||
static int itg3200_read_all_channels(struct i2c_client *i2c, __be16 *buf)
|
||||
{
|
||||
u8 tx = 0x80 | ITG3200_REG_TEMP_OUT_H;
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags,
|
||||
.len = 1,
|
||||
.buf = &tx,
|
||||
},
|
||||
{
|
||||
.addr = i2c->addr,
|
||||
.flags = i2c->flags | I2C_M_RD,
|
||||
.len = ITG3200_SCAN_ELEMENTS * sizeof(s16),
|
||||
.buf = (char *)&buf,
|
||||
},
|
||||
};
|
||||
|
||||
return i2c_transfer(i2c->adapter, msg, 2);
|
||||
}
|
||||
|
||||
static irqreturn_t itg3200_trigger_handler(int irq, void *p)
|
||||
{
|
||||
struct iio_poll_func *pf = p;
|
||||
struct iio_dev *indio_dev = pf->indio_dev;
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
__be16 buf[ITG3200_SCAN_ELEMENTS + sizeof(s64)/sizeof(u16)];
|
||||
|
||||
int ret = itg3200_read_all_channels(st->i2c, buf);
|
||||
if (ret < 0)
|
||||
goto error_ret;
|
||||
|
||||
iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
|
||||
|
||||
iio_trigger_notify_done(indio_dev->trig);
|
||||
|
||||
error_ret:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
int itg3200_buffer_configure(struct iio_dev *indio_dev)
|
||||
{
|
||||
return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
|
||||
itg3200_trigger_handler, NULL);
|
||||
}
|
||||
|
||||
void itg3200_buffer_unconfigure(struct iio_dev *indio_dev)
|
||||
{
|
||||
iio_triggered_buffer_cleanup(indio_dev);
|
||||
}
|
||||
|
||||
|
||||
static int itg3200_data_rdy_trigger_set_state(struct iio_trigger *trig,
|
||||
bool state)
|
||||
{
|
||||
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
|
||||
int ret;
|
||||
u8 msc;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, &msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
if (state)
|
||||
msc |= ITG3200_IRQ_DATA_RDY_ENABLE;
|
||||
else
|
||||
msc &= ~ITG3200_IRQ_DATA_RDY_ENABLE;
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev, ITG3200_REG_IRQ_CONFIG, msc);
|
||||
if (ret)
|
||||
goto error_ret;
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static const struct iio_trigger_ops itg3200_trigger_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.set_trigger_state = &itg3200_data_rdy_trigger_set_state,
|
||||
};
|
||||
|
||||
int itg3200_probe_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
int ret;
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
|
||||
indio_dev->id);
|
||||
if (!st->trig)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = request_irq(st->i2c->irq,
|
||||
&iio_trigger_generic_data_rdy_poll,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"itg3200_data_rdy",
|
||||
st->trig);
|
||||
if (ret)
|
||||
goto error_free_trig;
|
||||
|
||||
|
||||
st->trig->dev.parent = &st->i2c->dev;
|
||||
st->trig->ops = &itg3200_trigger_ops;
|
||||
iio_trigger_set_drvdata(st->trig, indio_dev);
|
||||
ret = iio_trigger_register(st->trig);
|
||||
if (ret)
|
||||
goto error_free_irq;
|
||||
|
||||
/* select default trigger */
|
||||
indio_dev->trig = iio_trigger_get(st->trig);
|
||||
|
||||
return 0;
|
||||
|
||||
error_free_irq:
|
||||
free_irq(st->i2c->irq, st->trig);
|
||||
error_free_trig:
|
||||
iio_trigger_free(st->trig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void itg3200_remove_trigger(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
iio_trigger_unregister(st->trig);
|
||||
free_irq(st->i2c->irq, st->trig);
|
||||
iio_trigger_free(st->trig);
|
||||
}
|
||||
374
drivers/iio/gyro/itg3200_core.c
Normal file
374
drivers/iio/gyro/itg3200_core.c
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* itg3200_core.c -- support InvenSense ITG3200
|
||||
* Digital 3-Axis Gyroscope driver
|
||||
*
|
||||
* Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
|
||||
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
|
||||
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* TODO:
|
||||
* - Support digital low pass filter
|
||||
* - Support power management
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/iio/iio.h>
|
||||
#include <linux/iio/sysfs.h>
|
||||
#include <linux/iio/events.h>
|
||||
#include <linux/iio/buffer.h>
|
||||
|
||||
#include <linux/iio/gyro/itg3200.h>
|
||||
|
||||
|
||||
int itg3200_write_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address, u8 val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
|
||||
return i2c_smbus_write_byte_data(st->i2c, 0x80 | reg_address, val);
|
||||
}
|
||||
|
||||
int itg3200_read_reg_8(struct iio_dev *indio_dev,
|
||||
u8 reg_address, u8 *val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(st->i2c, reg_address);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
*val = ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int itg3200_read_reg_s16(struct iio_dev *indio_dev, u8 lower_reg_address,
|
||||
int *val)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
struct i2c_client *client = st->i2c;
|
||||
int ret;
|
||||
s16 out;
|
||||
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags,
|
||||
.len = 1,
|
||||
.buf = (char *)&lower_reg_address,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = client->flags | I2C_M_RD,
|
||||
.len = 2,
|
||||
.buf = (char *)&out,
|
||||
},
|
||||
};
|
||||
|
||||
lower_reg_address |= 0x80;
|
||||
ret = i2c_transfer(client->adapter, msg, 2);
|
||||
be16_to_cpus(&out);
|
||||
*val = out;
|
||||
|
||||
return (ret == 2) ? 0 : ret;
|
||||
}
|
||||
|
||||
static int itg3200_read_raw(struct iio_dev *indio_dev,
|
||||
const struct iio_chan_spec *chan,
|
||||
int *val, int *val2, long info)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 reg;
|
||||
u8 regval;
|
||||
|
||||
switch (info) {
|
||||
case IIO_CHAN_INFO_RAW:
|
||||
reg = (u8)chan->address;
|
||||
ret = itg3200_read_reg_s16(indio_dev, reg, val);
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SCALE:
|
||||
*val = 0;
|
||||
if (chan->type == IIO_TEMP)
|
||||
*val2 = 1000000000/280;
|
||||
else
|
||||
*val2 = 1214142; /* (1 / 14,375) * (PI / 180) */
|
||||
return IIO_VAL_INT_PLUS_NANO;
|
||||
case IIO_CHAN_INFO_OFFSET:
|
||||
/* Only the temperature channel has an offset */
|
||||
*val = 23000;
|
||||
return IIO_VAL_INT;
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, ®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val = (regval & ITG3200_DLPF_CFG_MASK) ? 1000 : 8000;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev,
|
||||
ITG3200_REG_SAMPLE_RATE_DIV,
|
||||
®val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*val /= regval + 1;
|
||||
return IIO_VAL_INT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int itg3200_write_raw(struct iio_dev *indio_dev,
|
||||
struct iio_chan_spec const *chan,
|
||||
int val,
|
||||
int val2,
|
||||
long mask)
|
||||
{
|
||||
int ret;
|
||||
u8 t;
|
||||
|
||||
switch (mask) {
|
||||
case IIO_CHAN_INFO_SAMP_FREQ:
|
||||
if (val == 0 || val2 != 0)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&indio_dev->mlock);
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &t);
|
||||
if (ret) {
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
}
|
||||
t = ((t & ITG3200_DLPF_CFG_MASK) ? 1000u : 8000u) / val - 1;
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_SAMPLE_RATE_DIV,
|
||||
t);
|
||||
|
||||
mutex_unlock(&indio_dev->mlock);
|
||||
return ret;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset device and internal registers to the power-up-default settings
|
||||
* Use the gyro clock as reference, as suggested by the datasheet
|
||||
*/
|
||||
static int itg3200_reset(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
|
||||
dev_dbg(&st->i2c->dev, "reset device");
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_POWER_MANAGEMENT,
|
||||
ITG3200_RESET);
|
||||
if (ret) {
|
||||
dev_err(&st->i2c->dev, "error resetting device");
|
||||
goto error_ret;
|
||||
}
|
||||
|
||||
/* Wait for PLL (1ms according to datasheet) */
|
||||
udelay(1500);
|
||||
|
||||
ret = itg3200_write_reg_8(indio_dev,
|
||||
ITG3200_REG_IRQ_CONFIG,
|
||||
ITG3200_IRQ_ACTIVE_HIGH |
|
||||
ITG3200_IRQ_PUSH_PULL |
|
||||
ITG3200_IRQ_LATCH_50US_PULSE |
|
||||
ITG3200_IRQ_LATCH_CLEAR_ANY);
|
||||
|
||||
if (ret)
|
||||
dev_err(&st->i2c->dev, "error init device");
|
||||
|
||||
error_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* itg3200_enable_full_scale() - Disables the digital low pass filter */
|
||||
static int itg3200_enable_full_scale(struct iio_dev *indio_dev)
|
||||
{
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_DLPF, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
val |= ITG3200_DLPF_FS_SEL_2000;
|
||||
return itg3200_write_reg_8(indio_dev, ITG3200_REG_DLPF, val);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int itg3200_initial_setup(struct iio_dev *indio_dev)
|
||||
{
|
||||
struct itg3200 *st = iio_priv(indio_dev);
|
||||
int ret;
|
||||
u8 val;
|
||||
|
||||
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if (((val >> 1) & 0x3f) != 0x34) {
|
||||
dev_err(&st->i2c->dev, "invalid reg value 0x%02x", val);
|
||||
ret = -ENXIO;
|
||||
goto err_ret;
|
||||
}
|
||||
|
||||
ret = itg3200_reset(indio_dev);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = itg3200_enable_full_scale(indio_dev);
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ITG3200_ST \
|
||||
{ .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE }
|
||||
|
||||
#define ITG3200_GYRO_CHAN(_mod) { \
|
||||
.type = IIO_ANGL_VEL, \
|
||||
.modified = 1, \
|
||||
.channel2 = IIO_MOD_ ## _mod, \
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
|
||||
.address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \
|
||||
.scan_index = ITG3200_SCAN_GYRO_ ## _mod, \
|
||||
.scan_type = ITG3200_ST, \
|
||||
}
|
||||
|
||||
static const struct iio_chan_spec itg3200_channels[] = {
|
||||
{
|
||||
.type = IIO_TEMP,
|
||||
.channel2 = IIO_NO_MOD,
|
||||
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
|
||||
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
|
||||
BIT(IIO_CHAN_INFO_SCALE),
|
||||
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
|
||||
.address = ITG3200_REG_TEMP_OUT_H,
|
||||
.scan_index = ITG3200_SCAN_TEMP,
|
||||
.scan_type = ITG3200_ST,
|
||||
},
|
||||
ITG3200_GYRO_CHAN(X),
|
||||
ITG3200_GYRO_CHAN(Y),
|
||||
ITG3200_GYRO_CHAN(Z),
|
||||
IIO_CHAN_SOFT_TIMESTAMP(ITG3200_SCAN_ELEMENTS),
|
||||
};
|
||||
|
||||
static const struct iio_info itg3200_info = {
|
||||
.read_raw = &itg3200_read_raw,
|
||||
.write_raw = &itg3200_write_raw,
|
||||
.driver_module = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const unsigned long itg3200_available_scan_masks[] = { 0xffffffff, 0x0 };
|
||||
|
||||
static int itg3200_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct itg3200 *st;
|
||||
struct iio_dev *indio_dev;
|
||||
|
||||
dev_dbg(&client->dev, "probe I2C dev with IRQ %i", client->irq);
|
||||
|
||||
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
|
||||
if (!indio_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
st = iio_priv(indio_dev);
|
||||
|
||||
i2c_set_clientdata(client, indio_dev);
|
||||
st->i2c = client;
|
||||
|
||||
indio_dev->dev.parent = &client->dev;
|
||||
indio_dev->name = client->dev.driver->name;
|
||||
indio_dev->channels = itg3200_channels;
|
||||
indio_dev->num_channels = ARRAY_SIZE(itg3200_channels);
|
||||
indio_dev->available_scan_masks = itg3200_available_scan_masks;
|
||||
indio_dev->info = &itg3200_info;
|
||||
indio_dev->modes = INDIO_DIRECT_MODE;
|
||||
|
||||
ret = itg3200_buffer_configure(indio_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (client->irq) {
|
||||
ret = itg3200_probe_trigger(indio_dev);
|
||||
if (ret)
|
||||
goto error_unconfigure_buffer;
|
||||
}
|
||||
|
||||
ret = itg3200_initial_setup(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
ret = iio_device_register(indio_dev);
|
||||
if (ret)
|
||||
goto error_remove_trigger;
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_trigger:
|
||||
if (client->irq)
|
||||
itg3200_remove_trigger(indio_dev);
|
||||
error_unconfigure_buffer:
|
||||
itg3200_buffer_unconfigure(indio_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int itg3200_remove(struct i2c_client *client)
|
||||
{
|
||||
struct iio_dev *indio_dev = i2c_get_clientdata(client);
|
||||
|
||||
iio_device_unregister(indio_dev);
|
||||
|
||||
if (client->irq)
|
||||
itg3200_remove_trigger(indio_dev);
|
||||
|
||||
itg3200_buffer_unconfigure(indio_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id itg3200_id[] = {
|
||||
{ "itg3200", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, itg3200_id);
|
||||
|
||||
static struct i2c_driver itg3200_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "itg3200",
|
||||
},
|
||||
.id_table = itg3200_id,
|
||||
.probe = itg3200_probe,
|
||||
.remove = itg3200_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(itg3200_driver);
|
||||
|
||||
MODULE_AUTHOR("Christian Strobel <christian.strobel@iis.fraunhofer.de>");
|
||||
MODULE_DESCRIPTION("ITG3200 Gyroscope I2C driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue