summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig5
-rw-r--r--drivers/iio/accel/Kconfig31
-rw-r--r--drivers/iio/accel/Makefile6
-rw-r--r--drivers/iio/accel/bma180.c2
-rw-r--r--drivers/iio/accel/bmc150-accel.c1053
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c11
-rw-r--r--drivers/iio/accel/kxcjk-1013.c66
-rw-r--r--drivers/iio/accel/mma8452.c2
-rw-r--r--drivers/iio/accel/mma9551.c638
-rw-r--r--drivers/iio/accel/mma9551_core.c798
-rw-r--r--drivers/iio/accel/mma9551_core.h81
-rw-r--r--drivers/iio/accel/mma9553.c1330
-rw-r--r--drivers/iio/accel/ssp_accel_sensor.c169
-rw-r--r--drivers/iio/accel/st_accel.h4
-rw-r--r--drivers/iio/accel/st_accel_core.c103
-rw-r--r--drivers/iio/accel/st_accel_i2c.c7
-rw-r--r--drivers/iio/accel/st_accel_spi.c3
-rw-r--r--drivers/iio/adc/Kconfig62
-rw-r--r--drivers/iio/adc/Makefile5
-rw-r--r--drivers/iio/adc/ad7793.c2
-rw-r--r--drivers/iio/adc/ad799x.c15
-rw-r--r--drivers/iio/adc/at91_adc.c5
-rw-r--r--drivers/iio/adc/axp288_adc.c261
-rw-r--r--drivers/iio/adc/cc10001_adc.c423
-rw-r--r--drivers/iio/adc/da9150-gpadc.c407
-rw-r--r--drivers/iio/adc/exynos_adc.c62
-rw-r--r--drivers/iio/adc/mcp320x.c222
-rw-r--r--drivers/iio/adc/mcp3422.c17
-rw-r--r--drivers/iio/adc/men_z188_adc.c1
-rw-r--r--drivers/iio/adc/qcom-spmi-iadc.c596
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c1016
-rw-r--r--drivers/iio/adc/rockchip_saradc.c65
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c19
-rw-r--r--drivers/iio/adc/vf610_adc.c141
-rw-r--r--drivers/iio/amplifiers/ad8366.c4
-rw-r--r--drivers/iio/common/Kconfig1
-rw-r--r--drivers/iio/common/Makefile1
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c24
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c88
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.h5
-rw-r--r--drivers/iio/common/ssp_sensors/Kconfig26
-rw-r--r--drivers/iio/common/ssp_sensors/Makefile8
-rw-r--r--drivers/iio/common/ssp_sensors/ssp.h257
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_dev.c714
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_iio.c107
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_iio_sensor.h71
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_spi.c608
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c126
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_i2c.c1
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_spi.c3
-rw-r--r--drivers/iio/dac/Kconfig11
-rw-r--r--drivers/iio/dac/ad5686.c2
-rw-r--r--drivers/iio/dac/max517.c45
-rw-r--r--drivers/iio/frequency/ad9523.c2
-rw-r--r--drivers/iio/frequency/adf4350.c7
-rw-r--r--drivers/iio/gyro/Makefile2
-rw-r--r--drivers/iio/gyro/bmg160.c65
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c11
-rw-r--r--drivers/iio/gyro/itg3200_core.c29
-rw-r--r--drivers/iio/gyro/ssp_gyro_sensor.c168
-rw-r--r--drivers/iio/gyro/st_gyro.h3
-rw-r--r--drivers/iio/gyro/st_gyro_core.c102
-rw-r--r--drivers/iio/gyro/st_gyro_i2c.c4
-rw-r--r--drivers/iio/gyro/st_gyro_spi.c4
-rw-r--r--drivers/iio/humidity/Kconfig10
-rw-r--r--drivers/iio/humidity/Makefile1
-rw-r--r--drivers/iio/humidity/dht11.c69
-rw-r--r--drivers/iio/humidity/si7020.c161
-rw-r--r--drivers/iio/iio_core.h9
-rw-r--r--drivers/iio/imu/Kconfig11
-rw-r--r--drivers/iio/imu/Makefile2
-rw-r--r--drivers/iio/imu/adis16400_core.c3
-rw-r--r--drivers/iio/imu/adis_trigger.c2
-rw-r--r--drivers/iio/imu/inv_mpu6050/Kconfig1
-rw-r--r--drivers/iio/imu/inv_mpu6050/Makefile2
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c211
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_core.c193
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h9
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c25
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c39
-rw-r--r--drivers/iio/imu/kmx61.c1579
-rw-r--r--drivers/iio/industrialio-buffer.c573
-rw-r--r--drivers/iio/industrialio-core.c62
-rw-r--r--drivers/iio/industrialio-event.c16
-rw-r--r--drivers/iio/industrialio-triggered-buffer.c13
-rw-r--r--drivers/iio/inkern.c64
-rw-r--r--drivers/iio/kfifo_buf.c98
-rw-r--r--drivers/iio/light/Kconfig36
-rw-r--r--drivers/iio/light/Makefile3
-rw-r--r--drivers/iio/light/cm32181.c2
-rw-r--r--drivers/iio/light/cm3232.c439
-rw-r--r--drivers/iio/light/cm3323.c286
-rw-r--r--drivers/iio/light/gp2ap020a00f.c6
-rw-r--r--drivers/iio/light/hid-sensor-als.c12
-rw-r--r--drivers/iio/light/hid-sensor-prox.c13
-rw-r--r--drivers/iio/light/jsa1212.c471
-rw-r--r--drivers/iio/light/lm3533-als.c2
-rw-r--r--drivers/iio/light/ltr501.c18
-rw-r--r--drivers/iio/light/tcs3414.c4
-rw-r--r--drivers/iio/magnetometer/Kconfig15
-rw-r--r--drivers/iio/magnetometer/Makefile1
-rw-r--r--drivers/iio/magnetometer/ak09911.c326
-rw-r--r--drivers/iio/magnetometer/ak8975.c505
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c12
-rw-r--r--drivers/iio/magnetometer/mag3110.c18
-rw-r--r--drivers/iio/magnetometer/st_magn.h3
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c18
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c3
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c3
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c12
-rw-r--r--drivers/iio/pressure/Kconfig38
-rw-r--r--drivers/iio/pressure/Makefile4
-rw-r--r--drivers/iio/pressure/bmp280.c415
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c12
-rw-r--r--drivers/iio/pressure/ms5611.h44
-rw-r--r--drivers/iio/pressure/ms5611_core.c215
-rw-r--r--drivers/iio/pressure/ms5611_i2c.c128
-rw-r--r--drivers/iio/pressure/ms5611_spi.c127
-rw-r--r--drivers/iio/pressure/st_pressure.h3
-rw-r--r--drivers/iio/pressure/st_pressure_buffer.c12
-rw-r--r--drivers/iio/pressure/st_pressure_core.c49
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c11
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c11
-rw-r--r--drivers/iio/proximity/Kconfig17
-rw-r--r--drivers/iio/proximity/Makefile1
-rw-r--r--drivers/iio/proximity/as3935.c34
-rw-r--r--drivers/iio/proximity/sx9500.c748
-rw-r--r--drivers/iio/temperature/mlx90614.c97
-rw-r--r--drivers/iio/trigger/iio-trig-sysfs.c2
129 files changed, 15819 insertions, 1637 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 345395e..4011eff 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -21,13 +21,12 @@ config IIO_BUFFER
if IIO_BUFFER
config IIO_BUFFER_CB
-boolean "IIO callback buffer used for push in-kernel interfaces"
+ bool "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
@@ -44,7 +43,7 @@ config IIO_TRIGGERED_BUFFER
endif # IIO_BUFFER
config IIO_TRIGGER
- boolean "Enable triggered sampling support"
+ bool "Enable triggered sampling support"
help
Provides IIO core support for triggers. Currently these
are used to initialize capture of samples to push into
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 9b9be87..7c9a9a9 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -43,6 +43,9 @@ config HID_SENSOR_ACCEL_3D
Say yes here to build support for the HID SENSOR
accelerometers 3D.
+ To compile this driver as a module, choose M here: the
+ module will be called hid-sensor-accel-3d.
+
config IIO_ST_ACCEL_3AXIS
tristate "STMicroelectronics accelerometers 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
@@ -80,6 +83,9 @@ config KXSD9
Say yes here to build support for the Kionix KXSD9 accelerometer.
Currently this only supports the device via an SPI interface.
+ To compile this driver as a module, choose M here: the module
+ will be called kxsd9.
+
config MMA8452
tristate "Freescale MMA8452Q Accelerometer Driver"
depends on I2C
@@ -105,4 +111,29 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
+config MMA9551_CORE
+ tristate
+
+config MMA9551
+ tristate "Freescale MMA9551L Intelligent Motion-Sensing Platform Driver"
+ depends on I2C
+ select MMA9551_CORE
+
+ help
+ Say yes here to build support for the Freescale MMA9551L
+ Intelligent Motion-Sensing Platform Driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mma9551.
+
+config MMA9553
+ tristate "Freescale MMA9553L Intelligent Pedometer Platform Driver"
+ depends on I2C
+ select MMA9551_CORE
+ help
+ Say yes here to build support for the Freescale MMA9553L
+ Intelligent Pedometer Platform Driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mma9553.
endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index a593996..99d89e4 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -10,6 +10,12 @@ obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
obj-$(CONFIG_MMA8452) += mma8452.o
+obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
+obj-$(CONFIG_MMA9551) += mma9551.o
+obj-$(CONFIG_MMA9553) += mma9553.o
+
+obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.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
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index 1096da3..75c6d21 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -659,7 +659,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = bma180_get_data_reg(data, bit);
if (ret < 0) {
diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c
index 22c096c..73e8773 100644
--- a/drivers/iio/accel/bmc150-accel.c
+++ b/drivers/iio/accel/bmc150-accel.c
@@ -44,6 +44,9 @@
#define BMC150_ACCEL_REG_INT_STATUS_2 0x0B
#define BMC150_ACCEL_ANY_MOTION_MASK 0x07
+#define BMC150_ACCEL_ANY_MOTION_BIT_X BIT(0)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Y BIT(1)
+#define BMC150_ACCEL_ANY_MOTION_BIT_Z BIT(2)
#define BMC150_ACCEL_ANY_MOTION_BIT_SIGN BIT(3)
#define BMC150_ACCEL_REG_PMU_LPW 0x11
@@ -67,7 +70,9 @@
#define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2)
#define BMC150_ACCEL_REG_INT_MAP_1 0x1A
-#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0)
+#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0)
+#define BMC150_ACCEL_INT_MAP_1_BIT_FWM BIT(1)
+#define BMC150_ACCEL_INT_MAP_1_BIT_FFULL BIT(2)
#define BMC150_ACCEL_REG_INT_RST_LATCH 0x21
#define BMC150_ACCEL_INT_MODE_LATCH_RESET 0x80
@@ -80,7 +85,9 @@
#define BMC150_ACCEL_INT_EN_BIT_SLP_Z BIT(2)
#define BMC150_ACCEL_REG_INT_EN_1 0x17
-#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4)
+#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4)
+#define BMC150_ACCEL_INT_EN_BIT_FFULL_EN BIT(5)
+#define BMC150_ACCEL_INT_EN_BIT_FWM_EN BIT(6)
#define BMC150_ACCEL_REG_INT_OUT_CTRL 0x20
#define BMC150_ACCEL_INT_OUT_CTRL_INT1_LVL BIT(0)
@@ -92,9 +99,9 @@
#define BMC150_ACCEL_SLOPE_THRES_MASK 0xFF
/* Slope duration in terms of number of samples */
-#define BMC150_ACCEL_DEF_SLOPE_DURATION 2
+#define BMC150_ACCEL_DEF_SLOPE_DURATION 1
/* in terms of multiples of g's/LSB, based on range */
-#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 5
+#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 1
#define BMC150_ACCEL_REG_XOUT_L 0x02
@@ -119,6 +126,12 @@
#define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2))
#define BMC150_AUTO_SUSPEND_DELAY_MS 2000
+#define BMC150_ACCEL_REG_FIFO_STATUS 0x0E
+#define BMC150_ACCEL_REG_FIFO_CONFIG0 0x30
+#define BMC150_ACCEL_REG_FIFO_CONFIG1 0x3E
+#define BMC150_ACCEL_REG_FIFO_DATA 0x3F
+#define BMC150_ACCEL_FIFO_LENGTH 32
+
enum bmc150_accel_axis {
AXIS_X,
AXIS_Y,
@@ -144,20 +157,46 @@ struct bmc150_accel_chip_info {
const struct bmc150_scale_info scale_table[4];
};
+struct bmc150_accel_interrupt {
+ const struct bmc150_accel_interrupt_info *info;
+ atomic_t users;
+};
+
+struct bmc150_accel_trigger {
+ struct bmc150_accel_data *data;
+ struct iio_trigger *indio_trig;
+ int (*setup)(struct bmc150_accel_trigger *t, bool state);
+ int intr;
+ bool enabled;
+};
+
+enum bmc150_accel_interrupt_id {
+ BMC150_ACCEL_INT_DATA_READY,
+ BMC150_ACCEL_INT_ANY_MOTION,
+ BMC150_ACCEL_INT_WATERMARK,
+ BMC150_ACCEL_INTERRUPTS,
+};
+
+enum bmc150_accel_trigger_id {
+ BMC150_ACCEL_TRIGGER_DATA_READY,
+ BMC150_ACCEL_TRIGGER_ANY_MOTION,
+ BMC150_ACCEL_TRIGGERS,
+};
+
struct bmc150_accel_data {
struct i2c_client *client;
- struct iio_trigger *dready_trig;
- struct iio_trigger *motion_trig;
+ struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS];
+ atomic_t active_intr;
+ struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS];
struct mutex mutex;
+ u8 fifo_mode, watermark;
s16 buffer[8];
u8 bw_bits;
u32 slope_dur;
u32 slope_thres;
u32 range;
int ev_enable_state;
- bool dready_trigger_on;
- bool motion_trigger_on;
- int64_t timestamp;
+ int64_t timestamp, old_timestamp;
const struct bmc150_accel_chip_info *chip_info;
};
@@ -165,14 +204,14 @@ static const struct {
int val;
int val2;
u8 bw_bits;
-} bmc150_accel_samp_freq_table[] = { {7, 810000, 0x08},
- {15, 630000, 0x09},
- {31, 250000, 0x0A},
- {62, 500000, 0x0B},
- {125, 0, 0x0C},
- {250, 0, 0x0D},
- {500, 0, 0x0E},
- {1000, 0, 0x0F} };
+} bmc150_accel_samp_freq_table[] = { {15, 620000, 0x08},
+ {31, 260000, 0x09},
+ {62, 500000, 0x0A},
+ {125, 0, 0x0B},
+ {250, 0, 0x0C},
+ {500, 0, 0x0D},
+ {1000, 0, 0x0E},
+ {2000, 0, 0x0F} };
static const struct {
int bw_bits;
@@ -266,6 +305,46 @@ static int bmc150_accel_set_bw(struct bmc150_accel_data *data, int val,
return -EINVAL;
}
+static int bmc150_accel_update_slope(struct bmc150_accel_data *data)
+{
+ int ret, val;
+
+ ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_6,
+ data->slope_thres);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_6\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_5\n");
+ return ret;
+ }
+
+ val = (ret & ~BMC150_ACCEL_SLOPE_DUR_MASK) | data->slope_dur;
+ ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_5,
+ val);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error write reg_int_5\n");
+ return ret;
+ }
+
+ dev_dbg(&data->client->dev, "%s: %x %x\n", __func__, data->slope_thres,
+ data->slope_dur);
+
+ return ret;
+}
+
+static int bmc150_accel_any_motion_setup(struct bmc150_accel_trigger *t,
+ bool state)
+{
+ if (state)
+ return bmc150_accel_update_slope(t->data);
+
+ return 0;
+}
+
static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
{
int ret;
@@ -304,32 +383,12 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
data->range = BMC150_ACCEL_DEF_RANGE_4G;
- /* Set default slope duration */
- ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_5);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_5\n");
- return ret;
- }
- data->slope_dur |= BMC150_ACCEL_DEF_SLOPE_DURATION;
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_5,
- data->slope_dur);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_5\n");
- return ret;
- }
- dev_dbg(&data->client->dev, "slope_dur %x\n", data->slope_dur);
-
- /* Set default slope thresholds */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_6,
- BMC150_ACCEL_DEF_SLOPE_THRESHOLD);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_6\n");
- return ret;
- }
+ /* Set default slope duration and thresholds */
data->slope_thres = BMC150_ACCEL_DEF_SLOPE_THRESHOLD;
- dev_dbg(&data->client->dev, "slope_thres %x\n", data->slope_thres);
+ data->slope_dur = BMC150_ACCEL_DEF_SLOPE_DURATION;
+ ret = bmc150_accel_update_slope(data);
+ if (ret < 0)
+ return ret;
/* Set default as latched interrupts */
ret = i2c_smbus_write_byte_data(data->client,
@@ -345,155 +404,6 @@ static int bmc150_accel_chip_init(struct bmc150_accel_data *data)
return 0;
}
-static int bmc150_accel_setup_any_motion_interrupt(
- struct bmc150_accel_data *data,
- bool status)
-{
- int ret;
-
- /* Enable/Disable INT1 mapping */
- ret = i2c_smbus_read_byte_data(data->client,
- BMC150_ACCEL_REG_INT_MAP_0);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_map_0\n");
- return ret;
- }
- if (status)
- ret |= BMC150_ACCEL_INT_MAP_0_BIT_SLOPE;
- else
- ret &= ~BMC150_ACCEL_INT_MAP_0_BIT_SLOPE;
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_MAP_0,
- ret);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_map_0\n");
- return ret;
- }
-
- if (status) {
- /* Set slope duration (no of samples) */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_5,
- data->slope_dur);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error write reg_int_5\n");
- return ret;
- }
-
- /* Set slope thresholds */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_6,
- data->slope_thres);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error write reg_int_6\n");
- return ret;
- }
-
- /*
- * New data interrupt is always non-latched,
- * which will have higher priority, so no need
- * to set latched mode, we will be flooded anyway with INTR
- */
- if (!data->dready_trigger_on) {
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error writing reg_int_rst_latch\n");
- return ret;
- }
- }
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_EN_0,
- BMC150_ACCEL_INT_EN_BIT_SLP_X |
- BMC150_ACCEL_INT_EN_BIT_SLP_Y |
- BMC150_ACCEL_INT_EN_BIT_SLP_Z);
- } else
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_EN_0,
- 0);
-
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en_0\n");
- return ret;
- }
-
- return 0;
-}
-
-static int bmc150_accel_setup_new_data_interrupt(struct bmc150_accel_data *data,
- bool status)
-{
- int ret;
-
- /* Enable/Disable INT1 mapping */
- ret = i2c_smbus_read_byte_data(data->client,
- BMC150_ACCEL_REG_INT_MAP_1);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error reading reg_int_map_1\n");
- return ret;
- }
- if (status)
- ret |= BMC150_ACCEL_INT_MAP_1_BIT_DATA;
- else
- ret &= ~BMC150_ACCEL_INT_MAP_1_BIT_DATA;
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_MAP_1,
- ret);
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_map_1\n");
- return ret;
- }
-
- if (status) {
- /*
- * Set non latched mode interrupt and clear any latched
- * interrupt
- */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_NON_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error writing reg_int_rst_latch\n");
- return ret;
- }
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_EN_1,
- BMC150_ACCEL_INT_EN_BIT_DATA_EN);
-
- } else {
- /* Restore default interrupt mode */
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_RST_LATCH,
- BMC150_ACCEL_INT_MODE_LATCH_INT |
- BMC150_ACCEL_INT_MODE_LATCH_RESET);
- if (ret < 0) {
- dev_err(&data->client->dev,
- "Error writing reg_int_rst_latch\n");
- return ret;
- }
-
- ret = i2c_smbus_write_byte_data(data->client,
- BMC150_ACCEL_REG_INT_EN_1,
- 0);
- }
-
- if (ret < 0) {
- dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
- return ret;
- }
-
- return 0;
-}
-
static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
int *val2)
{
@@ -510,7 +420,7 @@ static int bmc150_accel_get_bw(struct bmc150_accel_data *data, int *val,
return -EINVAL;
}
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int bmc150_accel_get_startup_times(struct bmc150_accel_data *data)
{
int i;
@@ -536,6 +446,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: bmc150_accel_set_power_state for %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
return ret;
}
@@ -548,6 +461,120 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on)
}
#endif
+static const struct bmc150_accel_interrupt_info {
+ u8 map_reg;
+ u8 map_bitmask;
+ u8 en_reg;
+ u8 en_bitmask;
+} bmc150_accel_interrupts[BMC150_ACCEL_INTERRUPTS] = {
+ { /* data ready interrupt */
+ .map_reg = BMC150_ACCEL_REG_INT_MAP_1,
+ .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_DATA,
+ .en_reg = BMC150_ACCEL_REG_INT_EN_1,
+ .en_bitmask = BMC150_ACCEL_INT_EN_BIT_DATA_EN,
+ },
+ { /* motion interrupt */
+ .map_reg = BMC150_ACCEL_REG_INT_MAP_0,
+ .map_bitmask = BMC150_ACCEL_INT_MAP_0_BIT_SLOPE,
+ .en_reg = BMC150_ACCEL_REG_INT_EN_0,
+ .en_bitmask = BMC150_ACCEL_INT_EN_BIT_SLP_X |
+ BMC150_ACCEL_INT_EN_BIT_SLP_Y |
+ BMC150_ACCEL_INT_EN_BIT_SLP_Z
+ },
+ { /* fifo watermark interrupt */
+ .map_reg = BMC150_ACCEL_REG_INT_MAP_1,
+ .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FWM,
+ .en_reg = BMC150_ACCEL_REG_INT_EN_1,
+ .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN,
+ },
+};
+
+static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev,
+ struct bmc150_accel_data *data)
+{
+ int i;
+
+ for (i = 0; i < BMC150_ACCEL_INTERRUPTS; i++)
+ data->interrupts[i].info = &bmc150_accel_interrupts[i];
+}
+
+static int bmc150_accel_set_interrupt(struct bmc150_accel_data *data, int i,
+ bool state)
+{
+ struct bmc150_accel_interrupt *intr = &data->interrupts[i];
+ const struct bmc150_accel_interrupt_info *info = intr->info;
+ int ret;
+
+ if (state) {
+ if (atomic_inc_return(&intr->users) > 1)
+ return 0;
+ } else {
+ if (atomic_dec_return(&intr->users) > 0)
+ return 0;
+ }
+
+ /*
+ * We will expect the enable and disable to do operation in
+ * in reverse order. This will happen here anyway as our
+ * resume operation uses sync mode runtime pm calls, the
+ * suspend operation will be delayed by autosuspend delay
+ * So the disable operation will still happen in reverse of
+ * enable operation. When runtime pm is disabled the mode
+ * is always on so sequence doesn't matter
+ */
+ ret = bmc150_accel_set_power_state(data, state);
+ if (ret < 0)
+ return ret;
+
+ /* map the interrupt to the appropriate pins */
+ ret = i2c_smbus_read_byte_data(data->client, info->map_reg);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_map\n");
+ goto out_fix_power_state;
+ }
+ if (state)
+ ret |= info->map_bitmask;
+ else
+ ret &= ~info->map_bitmask;
+
+ ret = i2c_smbus_write_byte_data(data->client, info->map_reg,
+ ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_map\n");
+ goto out_fix_power_state;
+ }
+
+ /* enable/disable the interrupt */
+ ret = i2c_smbus_read_byte_data(data->client, info->en_reg);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_en\n");
+ goto out_fix_power_state;
+ }
+
+ if (state)
+ ret |= info->en_bitmask;
+ else
+ ret &= ~info->en_bitmask;
+
+ ret = i2c_smbus_write_byte_data(data->client, info->en_reg, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_en\n");
+ goto out_fix_power_state;
+ }
+
+ if (state)
+ atomic_inc(&data->active_intr);
+ else
+ atomic_dec(&data->active_intr);
+
+ return 0;
+
+out_fix_power_state:
+ bmc150_accel_set_power_state(data, false);
+ return ret;
+}
+
+
static int bmc150_accel_set_scale(struct bmc150_accel_data *data, int val)
{
int ret, i;
@@ -726,7 +753,7 @@ static int bmc150_accel_read_event(struct iio_dev *indio_dev,
*val = data->slope_thres;
break;
case IIO_EV_INFO_PERIOD:
- *val = data->slope_dur & BMC150_ACCEL_SLOPE_DUR_MASK;
+ *val = data->slope_dur;
break;
default:
return -EINVAL;
@@ -749,11 +776,10 @@ static int bmc150_accel_write_event(struct iio_dev *indio_dev,
switch (info) {
case IIO_EV_INFO_VALUE:
- data->slope_thres = val;
+ data->slope_thres = val & 0xFF;
break;
case IIO_EV_INFO_PERIOD:
- data->slope_dur &= ~BMC150_ACCEL_SLOPE_DUR_MASK;
- data->slope_dur |= val & BMC150_ACCEL_SLOPE_DUR_MASK;
+ data->slope_dur = val & BMC150_ACCEL_SLOPE_DUR_MASK;
break;
default:
return -EINVAL;
@@ -782,34 +808,13 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev,
struct bmc150_accel_data *data = iio_priv(indio_dev);
int ret;
- if (state && data->ev_enable_state)
+ if (state == data->ev_enable_state)
return 0;
mutex_lock(&data->mutex);
- if (!state && data->motion_trigger_on) {
- data->ev_enable_state = 0;
- mutex_unlock(&data->mutex);
- return 0;
- }
-
- /*
- * We will expect the enable and disable to do operation in
- * in reverse order. This will happen here anyway as our
- * resume operation uses sync mode runtime pm calls, the
- * suspend operation will be delayed by autosuspend delay
- * So the disable operation will still happen in reverse of
- * enable operation. When runtime pm is disabled the mode
- * is always on so sequence doesn't matter
- */
-
- ret = bmc150_accel_set_power_state(data, state);
- if (ret < 0) {
- mutex_unlock(&data->mutex);
- return ret;
- }
-
- ret = bmc150_accel_setup_any_motion_interrupt(data, state);
+ ret = bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_ANY_MOTION,
+ state);
if (ret < 0) {
mutex_unlock(&data->mutex);
return ret;
@@ -825,15 +830,226 @@ static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev,
struct iio_trigger *trig)
{
struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int i;
- if (data->dready_trig != trig && data->motion_trig != trig)
- return -EINVAL;
+ for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
+ if (data->triggers[i].indio_trig == trig)
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t bmc150_accel_get_fifo_watermark(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int wm;
+
+ mutex_lock(&data->mutex);
+ wm = data->watermark;
+ mutex_unlock(&data->mutex);
+
+ return sprintf(buf, "%d\n", wm);
+}
+
+static ssize_t bmc150_accel_get_fifo_state(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ bool state;
+
+ mutex_lock(&data->mutex);
+ state = data->fifo_mode;
+ mutex_unlock(&data->mutex);
+
+ return sprintf(buf, "%d\n", state);
+}
+
+static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
+static IIO_CONST_ATTR(hwfifo_watermark_max,
+ __stringify(BMC150_ACCEL_FIFO_LENGTH));
+static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO,
+ bmc150_accel_get_fifo_state, NULL, 0);
+static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO,
+ bmc150_accel_get_fifo_watermark, NULL, 0);
+
+static const struct attribute *bmc150_accel_fifo_attributes[] = {
+ &iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
+ &iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark.dev_attr.attr,
+ &iio_dev_attr_hwfifo_enabled.dev_attr.attr,
+ NULL,
+};
+
+static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ if (val > BMC150_ACCEL_FIFO_LENGTH)
+ val = BMC150_ACCEL_FIFO_LENGTH;
+
+ mutex_lock(&data->mutex);
+ data->watermark = val;
+ mutex_unlock(&data->mutex);
return 0;
}
+/*
+ * We must read at least one full frame in one burst, otherwise the rest of the
+ * frame data is discarded.
+ */
+static int bmc150_accel_fifo_transfer(const struct i2c_client *client,
+ char *buffer, int samples)
+{
+ int sample_length = 3 * 2;
+ u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA;
+ int ret = -EIO;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = &reg_fifo_data,
+ .len = sizeof(reg_fifo_data),
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = (u8 *)buffer,
+ .len = samples * sample_length,
+ }
+ };
+
+ ret = i2c_transfer(client->adapter, msg, 2);
+ if (ret != 2)
+ ret = -EIO;
+ else
+ ret = 0;
+ } else {
+ int i, step = I2C_SMBUS_BLOCK_MAX / sample_length;
+
+ for (i = 0; i < samples * sample_length; i += step) {
+ ret = i2c_smbus_read_i2c_block_data(client,
+ reg_fifo_data, step,
+ &buffer[i]);
+ if (ret != step) {
+ ret = -EIO;
+ break;
+ }
+
+ ret = 0;
+ }
+ }
+
+ if (ret)
+ dev_err(&client->dev, "Error transferring data from fifo\n");
+
+ return ret;
+}
+
+static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev,
+ unsigned samples, bool irq)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret, i;
+ u8 count;
+ u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3];
+ int64_t tstamp;
+ uint64_t sample_period;
+ ret = i2c_smbus_read_byte_data(data->client,
+ BMC150_ACCEL_REG_FIFO_STATUS);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_fifo_status\n");
+ return ret;
+ }
+
+ count = ret & 0x7F;
+
+ if (!count)
+ return 0;
+
+ /*
+ * If we getting called from IRQ handler we know the stored timestamp is
+ * fairly accurate for the last stored sample. Otherwise, if we are
+ * called as a result of a read operation from userspace and hence
+ * before the watermark interrupt was triggered, take a timestamp
+ * now. We can fall anywhere in between two samples so the error in this
+ * case is at most one sample period.
+ */
+ if (!irq) {
+ data->old_timestamp = data->timestamp;
+ data->timestamp = iio_get_time_ns();
+ }
+
+ /*
+ * Approximate timestamps for each of the sample based on the sampling
+ * frequency, timestamp for last sample and number of samples.
+ *
+ * Note that we can't use the current bandwidth settings to compute the
+ * sample period because the sample rate varies with the device
+ * (e.g. between 31.70ms to 32.20ms for a bandwidth of 15.63HZ). That
+ * small variation adds when we store a large number of samples and
+ * creates significant jitter between the last and first samples in
+ * different batches (e.g. 32ms vs 21ms).
+ *
+ * To avoid this issue we compute the actual sample period ourselves
+ * based on the timestamp delta between the last two flush operations.
+ */
+ sample_period = (data->timestamp - data->old_timestamp);
+ do_div(sample_period, count);
+ tstamp = data->timestamp - (count - 1) * sample_period;
+
+ if (samples && count > samples)
+ count = samples;
+
+ ret = bmc150_accel_fifo_transfer(data->client, (u8 *)buffer, count);
+ if (ret)
+ return ret;
+
+ /*
+ * Ideally we want the IIO core to handle the demux when running in fifo
+ * mode but not when running in triggered buffer mode. Unfortunately
+ * this does not seem to be possible, so stick with driver demux for
+ * now.
+ */
+ for (i = 0; i < count; i++) {
+ u16 sample[8];
+ int j, bit;
+
+ j = 0;
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength)
+ memcpy(&sample[j++], &buffer[i * 3 + bit], 2);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, sample, tstamp);
+
+ tstamp += sample_period;
+ }
+
+ return count;
+}
+
+static int bmc150_accel_fifo_flush(struct iio_dev *indio_dev, unsigned samples)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = __bmc150_accel_fifo_flush(indio_dev, samples, false);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
- "7.810000 15.630000 31.250000 62.500000 125 250 500 1000");
+ "15.620000 31.260000 62.50000 125 250 500 1000 2000");
static struct attribute *bmc150_accel_attributes[] = {
&iio_const_attr_sampling_frequency_available.dev_attr.attr,
@@ -846,7 +1062,7 @@ static const struct attribute_group bmc150_accel_attrs_group = {
static const struct iio_event_spec bmc150_accel_event = {
.type = IIO_EV_TYPE_ROC,
- .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .dir = IIO_EV_DIR_EITHER,
.mask_separate = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE) |
BIT(IIO_EV_INFO_PERIOD)
@@ -971,6 +1187,20 @@ static const struct iio_info bmc150_accel_info = {
.driver_module = THIS_MODULE,
};
+static const struct iio_info bmc150_accel_info_fifo = {
+ .attrs = &bmc150_accel_attrs_group,
+ .read_raw = bmc150_accel_read_raw,
+ .write_raw = bmc150_accel_write_raw,
+ .read_event_value = bmc150_accel_read_event,
+ .write_event_value = bmc150_accel_write_event,
+ .write_event_config = bmc150_accel_write_event_config,
+ .read_event_config = bmc150_accel_read_event_config,
+ .validate_trigger = bmc150_accel_validate_trigger,
+ .hwfifo_set_watermark = bmc150_accel_set_watermark,
+ .hwfifo_flush_to_buffer = bmc150_accel_fifo_flush,
+ .driver_module = THIS_MODULE,
+};
+
static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
@@ -979,7 +1209,7 @@ static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p)
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMC150_ACCEL_AXIS_TO_REG(bit));
@@ -1001,12 +1231,12 @@ err_read:
static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
- struct bmc150_accel_data *data = iio_priv(indio_dev);
+ struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
+ struct bmc150_accel_data *data = t->data;
int ret;
/* new data interrupts don't need ack */
- if (data->dready_trigger_on)
+ if (t == &t->data->triggers[BMC150_ACCEL_TRIGGER_DATA_READY])
return 0;
mutex_lock(&data->mutex);
@@ -1025,42 +1255,35 @@ static int bmc150_accel_trig_try_reen(struct iio_trigger *trig)
return 0;
}
-static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig,
+static int bmc150_accel_trigger_set_state(struct iio_trigger *trig,
bool state)
{
- struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
- struct bmc150_accel_data *data = iio_priv(indio_dev);
+ struct bmc150_accel_trigger *t = iio_trigger_get_drvdata(trig);
+ struct bmc150_accel_data *data = t->data;
int ret;
mutex_lock(&data->mutex);
- if (!state && data->ev_enable_state && data->motion_trigger_on) {
- data->motion_trigger_on = false;
+ if (t->enabled == state) {
mutex_unlock(&data->mutex);
return 0;
}
- /*
- * Refer to comment in bmc150_accel_write_event_config for
- * enable/disable operation order
- */
- ret = bmc150_accel_set_power_state(data, state);
- if (ret < 0) {
- mutex_unlock(&data->mutex);
- return ret;
+ if (t->setup) {
+ ret = t->setup(t, state);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
}
- if (data->motion_trig == trig)
- ret = bmc150_accel_setup_any_motion_interrupt(data, state);
- else
- ret = bmc150_accel_setup_new_data_interrupt(data, state);
+
+ ret = bmc150_accel_set_interrupt(data, t->intr, state);
if (ret < 0) {
mutex_unlock(&data->mutex);
return ret;
}
- if (data->motion_trig == trig)
- data->motion_trigger_on = state;
- else
- data->dready_trigger_on = state;
+
+ t->enabled = state;
mutex_unlock(&data->mutex);
@@ -1068,23 +1291,22 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig,
}
static const struct iio_trigger_ops bmc150_accel_trigger_ops = {
- .set_trigger_state = bmc150_accel_data_rdy_trigger_set_state,
+ .set_trigger_state = bmc150_accel_trigger_set_state,
.try_reenable = bmc150_accel_trig_try_reen,
.owner = THIS_MODULE,
};
-static irqreturn_t bmc150_accel_event_handler(int irq, void *private)
+static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev)
{
- struct iio_dev *indio_dev = private;
struct bmc150_accel_data *data = iio_priv(indio_dev);
- int ret;
int dir;
+ int ret;
ret = i2c_smbus_read_byte_data(data->client,
BMC150_ACCEL_REG_INT_STATUS_2);
if (ret < 0) {
dev_err(&data->client->dev, "Error reading reg_int_status_2\n");
- goto ack_intr_status;
+ return ret;
}
if (ret & BMC150_ACCEL_ANY_MOTION_BIT_SIGN)
@@ -1092,39 +1314,94 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private)
else
dir = IIO_EV_DIR_RISING;
- if (ret & BMC150_ACCEL_ANY_MOTION_MASK)
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL,
0,
- IIO_MOD_X_OR_Y_OR_Z,
+ IIO_MOD_Z,
IIO_EV_TYPE_ROC,
- IIO_EV_DIR_EITHER),
+ dir),
data->timestamp);
-ack_intr_status:
- if (!data->dready_trigger_on)
+ return ret;
+}
+
+static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ bool ack = false;
+ int ret;
+
+ mutex_lock(&data->mutex);
+
+ if (data->fifo_mode) {
+ ret = __bmc150_accel_fifo_flush(indio_dev,
+ BMC150_ACCEL_FIFO_LENGTH, true);
+ if (ret > 0)
+ ack = true;
+ }
+
+ if (data->ev_enable_state) {
+ ret = bmc150_accel_handle_roc_event(indio_dev);
+ if (ret > 0)
+ ack = true;
+ }
+
+ if (ack) {
ret = i2c_smbus_write_byte_data(data->client,
BMC150_ACCEL_REG_INT_RST_LATCH,
BMC150_ACCEL_INT_MODE_LATCH_INT |
BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ if (ret)
+ dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
+ ret = IRQ_HANDLED;
+ } else {
+ ret = IRQ_NONE;
+ }
- return IRQ_HANDLED;
+ mutex_unlock(&data->mutex);
+
+ return ret;
}
-static irqreturn_t bmc150_accel_data_rdy_trig_poll(int irq, void *private)
+static irqreturn_t bmc150_accel_irq_handler(int irq, void *private)
{
struct iio_dev *indio_dev = private;
struct bmc150_accel_data *data = iio_priv(indio_dev);
+ bool ack = false;
+ int i;
+ data->old_timestamp = data->timestamp;
data->timestamp = iio_get_time_ns();
- if (data->dready_trigger_on)
- iio_trigger_poll(data->dready_trig);
- else if (data->motion_trigger_on)
- iio_trigger_poll(data->motion_trig);
+ for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
+ if (data->triggers[i].enabled) {
+ iio_trigger_poll(data->triggers[i].indio_trig);
+ ack = true;
+ break;
+ }
+ }
- if (data->ev_enable_state)
+ if (data->ev_enable_state || data->fifo_mode)
return IRQ_WAKE_THREAD;
- else
+
+ if (ack)
return IRQ_HANDLED;
+
+ return IRQ_NONE;
}
static const char *bmc150_accel_match_acpi_device(struct device *dev, int *data)
@@ -1154,23 +1431,171 @@ static int bmc150_accel_gpio_probe(struct i2c_client *client,
dev = &client->dev;
/* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0);
+ gpio = devm_gpiod_get_index(dev, BMC150_ACCEL_GPIO_NAME, 0, GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(dev, "Failed: gpio get index\n");
return PTR_ERR(gpio);
}
- ret = gpiod_direction_input(gpio);
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static const struct {
+ int intr;
+ const char *name;
+ int (*setup)(struct bmc150_accel_trigger *t, bool state);
+} bmc150_accel_triggers[BMC150_ACCEL_TRIGGERS] = {
+ {
+ .intr = 0,
+ .name = "%s-dev%d",
+ },
+ {
+ .intr = 1,
+ .name = "%s-any-motion-dev%d",
+ .setup = bmc150_accel_any_motion_setup,
+ },
+};
+
+static void bmc150_accel_unregister_triggers(struct bmc150_accel_data *data,
+ int from)
+{
+ int i;
+
+ for (i = from; i >= 0; i++) {
+ if (data->triggers[i].indio_trig) {
+ iio_trigger_unregister(data->triggers[i].indio_trig);
+ data->triggers[i].indio_trig = NULL;
+ }
+ }
+}
+
+static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev,
+ struct bmc150_accel_data *data)
+{
+ int i, ret;
+
+ for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) {
+ struct bmc150_accel_trigger *t = &data->triggers[i];
+
+ t->indio_trig = devm_iio_trigger_alloc(&data->client->dev,
+ bmc150_accel_triggers[i].name,
+ indio_dev->name,
+ indio_dev->id);
+ if (!t->indio_trig) {
+ ret = -ENOMEM;
+ break;
+ }
+
+ t->indio_trig->dev.parent = &data->client->dev;
+ t->indio_trig->ops = &bmc150_accel_trigger_ops;
+ t->intr = bmc150_accel_triggers[i].intr;
+ t->data = data;
+ t->setup = bmc150_accel_triggers[i].setup;
+ iio_trigger_set_drvdata(t->indio_trig, t);
+
+ ret = iio_trigger_register(t->indio_trig);
+ if (ret)
+ break;
+ }
+
if (ret)
+ bmc150_accel_unregister_triggers(data, i - 1);
+
+ return ret;
+}
+
+#define BMC150_ACCEL_FIFO_MODE_STREAM 0x80
+#define BMC150_ACCEL_FIFO_MODE_FIFO 0x40
+#define BMC150_ACCEL_FIFO_MODE_BYPASS 0x00
+
+static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data)
+{
+ u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, reg, data->fifo_mode);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_fifo_config1\n");
return ret;
+ }
- ret = gpiod_to_irq(gpio);
+ if (!data->fifo_mode)
+ return 0;
- dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+ ret = i2c_smbus_write_byte_data(data->client,
+ BMC150_ACCEL_REG_FIFO_CONFIG0,
+ data->watermark);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error writing reg_fifo_config0\n");
return ret;
}
+static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ return iio_triggered_buffer_postenable(indio_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->watermark)
+ goto out;
+
+ ret = bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK,
+ true);
+ if (ret)
+ goto out;
+
+ data->fifo_mode = BMC150_ACCEL_FIFO_MODE_FIFO;
+
+ ret = bmc150_accel_fifo_set_mode(data);
+ if (ret) {
+ data->fifo_mode = 0;
+ bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK,
+ false);
+ }
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bmc150_accel_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ return iio_triggered_buffer_predisable(indio_dev);
+
+ mutex_lock(&data->mutex);
+
+ if (!data->fifo_mode)
+ goto out;
+
+ bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, false);
+ __bmc150_accel_fifo_flush(indio_dev, BMC150_ACCEL_FIFO_LENGTH, false);
+ data->fifo_mode = 0;
+ bmc150_accel_fifo_set_mode(data);
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = {
+ .postenable = bmc150_accel_buffer_postenable,
+ .predisable = bmc150_accel_buffer_predisable,
+};
+
static int bmc150_accel_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1217,53 +1642,51 @@ static int bmc150_accel_probe(struct i2c_client *client,
if (client->irq >= 0) {
ret = devm_request_threaded_irq(
&client->dev, client->irq,
- bmc150_accel_data_rdy_trig_poll,
- bmc150_accel_event_handler,
+ bmc150_accel_irq_handler,
+ bmc150_accel_irq_thread_handler,
IRQF_TRIGGER_RISING,
BMC150_ACCEL_IRQ_NAME,
indio_dev);
if (ret)
return ret;
- data->dready_trig = devm_iio_trigger_alloc(&client->dev,
- "%s-dev%d",
- indio_dev->name,
- indio_dev->id);
- if (!data->dready_trig)
- return -ENOMEM;
-
- data->motion_trig = devm_iio_trigger_alloc(&client->dev,
- "%s-any-motion-dev%d",
- indio_dev->name,
- indio_dev->id);
- if (!data->motion_trig)
- return -ENOMEM;
-
- data->dready_trig->dev.parent = &client->dev;
- data->dready_trig->ops = &bmc150_accel_trigger_ops;
- iio_trigger_set_drvdata(data->dready_trig, indio_dev);
- ret = iio_trigger_register(data->dready_trig);
- if (ret)
+ /*
+ * Set latched mode interrupt. While certain interrupts are
+ * non-latched regardless of this settings (e.g. new data) we
+ * want to use latch mode when we can to prevent interrupt
+ * flooding.
+ */
+ ret = i2c_smbus_write_byte_data(data->client,
+ BMC150_ACCEL_REG_INT_RST_LATCH,
+ BMC150_ACCEL_INT_MODE_LATCH_RESET);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n");
return ret;
-
- data->motion_trig->dev.parent = &client->dev;
- data->motion_trig->ops = &bmc150_accel_trigger_ops;
- iio_trigger_set_drvdata(data->motion_trig, indio_dev);
- ret = iio_trigger_register(data->motion_trig);
- if (ret) {
- data->motion_trig = NULL;
- goto err_trigger_unregister;
}
+ bmc150_accel_interrupts_setup(indio_dev, data);
+
+ ret = bmc150_accel_triggers_setup(indio_dev, data);
+ if (ret)
+ return ret;
+
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
bmc150_accel_trigger_handler,
- NULL);
+ &bmc150_accel_buffer_ops);
if (ret < 0) {
dev_err(&client->dev,
"Failed: iio triggered buffer setup\n");
goto err_trigger_unregister;
}
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) ||
+ i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->info = &bmc150_accel_info_fifo;
+ indio_dev->buffer->attrs = bmc150_accel_fifo_attributes;
+ }
}
ret = iio_device_register(indio_dev);
@@ -1286,13 +1709,10 @@ static int bmc150_accel_probe(struct i2c_client *client,
err_iio_unregister:
iio_device_unregister(indio_dev);
err_buffer_cleanup:
- if (data->dready_trig)
+ if (indio_dev->pollfunc)
iio_triggered_buffer_cleanup(indio_dev);
err_trigger_unregister:
- if (data->dready_trig)
- iio_trigger_unregister(data->dready_trig);
- if (data->motion_trig)
- iio_trigger_unregister(data->motion_trig);
+ bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
return ret;
}
@@ -1308,11 +1728,7 @@ static int bmc150_accel_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
- if (data->dready_trig) {
- iio_triggered_buffer_cleanup(indio_dev);
- iio_trigger_unregister(data->dready_trig);
- iio_trigger_unregister(data->motion_trig);
- }
+ bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1);
mutex_lock(&data->mutex);
bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_DEEP_SUSPEND, 0);
@@ -1340,24 +1756,28 @@ static int bmc150_accel_resume(struct device *dev)
struct bmc150_accel_data *data = iio_priv(indio_dev);
mutex_lock(&data->mutex);
- if (data->dready_trigger_on || data->motion_trigger_on ||
- data->ev_enable_state)
+ if (atomic_read(&data->active_intr))
bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
+ bmc150_accel_fifo_set_mode(data);
mutex_unlock(&data->mutex);
return 0;
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int bmc150_accel_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct bmc150_accel_data *data = iio_priv(indio_dev);
+ int ret;
dev_dbg(&data->client->dev, __func__);
+ ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ if (ret < 0)
+ return -EAGAIN;
- return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0);
+ return 0;
}
static int bmc150_accel_runtime_resume(struct device *dev)
@@ -1372,6 +1792,9 @@ static int bmc150_accel_runtime_resume(struct device *dev)
ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0);
if (ret < 0)
return ret;
+ ret = bmc150_accel_fifo_set_mode(data);
+ if (ret < 0)
+ return ret;
sleep_val = bmc150_accel_get_startup_times(data);
if (sleep_val < 20)
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index d5d95317..2b4fad6 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -111,26 +111,20 @@ static int accel_3d_read_raw(struct iio_dev *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);
+ report_id,
+ SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&accel_state->common_attributes,
@@ -419,6 +413,7 @@ static struct platform_driver hid_accel_3d_platform_driver = {
.id_table = hid_accel_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_accel_3d_probe,
.remove = hid_accel_3d_remove,
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index a23e58c..51da369 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -108,6 +108,7 @@ struct kxcjk1013_data {
bool motion_trigger_on;
int64_t timestamp;
enum kx_chipset chipset;
+ bool is_smo8500_device;
};
enum kxcjk1013_axis {
@@ -269,6 +270,8 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index)
return ret;
}
+ ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 |
+ KXCJK1013_REG_CTRL1_BIT_GSEL1);
ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3);
ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4);
@@ -358,7 +361,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data)
return 0;
}
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
{
int i;
@@ -375,6 +378,7 @@ static int kxcjk1013_get_startup_times(struct kxcjk1013_data *data)
static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
{
+#ifdef CONFIG_PM
int ret;
if (on)
@@ -386,8 +390,11 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on)
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: kxcjk1013_set_power_state for %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
return ret;
}
+#endif
return 0;
}
@@ -856,6 +863,8 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
ret = kxcjk1013_setup_any_motion_interrupt(data, state);
if (ret < 0) {
+ kxcjk1013_set_power_state(data, false);
+ data->ev_enable_state = 0;
mutex_unlock(&data->mutex);
return ret;
}
@@ -947,7 +956,7 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p)
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = kxcjk1013_get_acc_reg(data, bit);
if (ret < 0) {
@@ -1006,6 +1015,7 @@ static int kxcjk1013_data_rdy_trigger_set_state(struct iio_trigger *trig,
else
ret = kxcjk1013_setup_new_data_interrupt(data, state);
if (ret < 0) {
+ kxcjk1013_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
@@ -1129,12 +1139,16 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private)
}
static const char *kxcjk1013_match_acpi_device(struct device *dev,
- enum kx_chipset *chipset)
+ enum kx_chipset *chipset,
+ bool *is_smo8500_device)
{
const struct acpi_device_id *id;
+
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
+ if (strcmp(id->id, "SMO8500") == 0)
+ *is_smo8500_device = true;
*chipset = (enum kx_chipset)id->driver_data;
return dev_name(dev);
@@ -1149,20 +1163,18 @@ static int kxcjk1013_gpio_probe(struct i2c_client *client,
if (!client)
return -EINVAL;
+ if (data->is_smo8500_device)
+ return -ENOTSUPP;
dev = &client->dev;
/* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0);
+ gpio = devm_gpiod_get_index(dev, "kxcjk1013_int", 0, GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
}
- ret = gpiod_direction_input(gpio);
- if (ret)
- return ret;
-
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
@@ -1198,7 +1210,8 @@ static int kxcjk1013_probe(struct i2c_client *client,
name = id->name;
} else if (ACPI_HANDLE(&client->dev)) {
name = kxcjk1013_match_acpi_device(&client->dev,
- &data->chipset);
+ &data->chipset,
+ &data->is_smo8500_device);
} else
return -ENODEV;
@@ -1226,21 +1239,25 @@ static int kxcjk1013_probe(struct i2c_client *client,
KXCJK1013_IRQ_NAME,
indio_dev);
if (ret)
- return ret;
+ goto err_poweroff;
data->dready_trig = devm_iio_trigger_alloc(&client->dev,
"%s-dev%d",
indio_dev->name,
indio_dev->id);
- if (!data->dready_trig)
- return -ENOMEM;
+ if (!data->dready_trig) {
+ ret = -ENOMEM;
+ goto err_poweroff;
+ }
data->motion_trig = devm_iio_trigger_alloc(&client->dev,
"%s-any-motion-dev%d",
indio_dev->name,
indio_dev->id);
- if (!data->motion_trig)
- return -ENOMEM;
+ if (!data->motion_trig) {
+ ret = -ENOMEM;
+ goto err_poweroff;
+ }
data->dready_trig->dev.parent = &client->dev;
data->dready_trig->ops = &kxcjk1013_trigger_ops;
@@ -1249,7 +1266,7 @@ static int kxcjk1013_probe(struct i2c_client *client,
iio_trigger_get(indio_dev->trig);
ret = iio_trigger_register(data->dready_trig);
if (ret)
- return ret;
+ goto err_poweroff;
data->motion_trig->dev.parent = &client->dev;
data->motion_trig->ops = &kxcjk1013_trigger_ops;
@@ -1298,6 +1315,8 @@ err_trigger_unregister:
iio_trigger_unregister(data->dready_trig);
if (data->motion_trig)
iio_trigger_unregister(data->motion_trig);
+err_poweroff:
+ kxcjk1013_set_mode(data, STANDBY);
return ret;
}
@@ -1347,23 +1366,26 @@ static int kxcjk1013_resume(struct device *dev)
int ret = 0;
mutex_lock(&data->mutex);
- /* Check, if the suspend occured while active */
- if (data->dready_trigger_on || data->motion_trigger_on ||
- data->ev_enable_state)
- ret = kxcjk1013_set_mode(data, OPERATION);
+ ret = kxcjk1013_set_mode(data, OPERATION);
mutex_unlock(&data->mutex);
return ret;
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int kxcjk1013_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct kxcjk1013_data *data = iio_priv(indio_dev);
+ int ret;
- return kxcjk1013_set_mode(data, STANDBY);
+ ret = kxcjk1013_set_mode(data, STANDBY);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "powering off device failed\n");
+ return -EAGAIN;
+ }
+ return 0;
}
static int kxcjk1013_runtime_resume(struct device *dev)
@@ -1397,6 +1419,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXTJ1009", KXTJ21009},
+ {"SMO8500", KXCJ91008},
{ },
};
MODULE_DEVICE_TABLE(acpi, kx_acpi_match);
@@ -1405,6 +1428,7 @@ static const struct i2c_device_id kxcjk1013_id[] = {
{"kxcjk1013", KXCJK1013},
{"kxcj91008", KXCJ91008},
{"kxtj21009", KXTJ21009},
+ {"SMO8500", KXCJ91008},
{}
};
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 3c12d49..5b80657 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -111,7 +111,7 @@ static const int mma8452_samp_freq[8][2] = {
{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:
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
new file mode 100644
index 0000000..7db7cc0
--- /dev/null
+++ b/drivers/iio/accel/mma9551.c
@@ -0,0 +1,638 @@
+/*
+ * Freescale MMA9551L Intelligent Motion-Sensing Platform driver
+ * Copyright (c) 2014, 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/pm_runtime.h>
+#include "mma9551_core.h"
+
+#define MMA9551_DRV_NAME "mma9551"
+#define MMA9551_IRQ_NAME "mma9551_event"
+#define MMA9551_GPIO_NAME "mma9551_int"
+#define MMA9551_GPIO_COUNT 4
+
+/* Tilt application (inclination in IIO terms). */
+#define MMA9551_TILT_XZ_ANG_REG 0x00
+#define MMA9551_TILT_YZ_ANG_REG 0x01
+#define MMA9551_TILT_XY_ANG_REG 0x02
+#define MMA9551_TILT_ANGFLG BIT(7)
+#define MMA9551_TILT_QUAD_REG 0x03
+#define MMA9551_TILT_XY_QUAD_SHIFT 0
+#define MMA9551_TILT_YZ_QUAD_SHIFT 2
+#define MMA9551_TILT_XZ_QUAD_SHIFT 4
+#define MMA9551_TILT_CFG_REG 0x01
+#define MMA9551_TILT_ANG_THRESH_MASK GENMASK(3, 0)
+
+#define MMA9551_DEFAULT_SAMPLE_RATE 122 /* Hz */
+
+/* Tilt events are mapped to the first three GPIO pins. */
+enum mma9551_tilt_axis {
+ mma9551_x = 0,
+ mma9551_y,
+ mma9551_z,
+};
+
+struct mma9551_data {
+ struct i2c_client *client;
+ struct mutex mutex;
+ int event_enabled[3];
+ int irqs[MMA9551_GPIO_COUNT];
+};
+
+static int mma9551_read_incli_chan(struct i2c_client *client,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ u8 quad_shift, angle, quadrant;
+ u16 reg_addr;
+ int ret;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg_addr = MMA9551_TILT_YZ_ANG_REG;
+ quad_shift = MMA9551_TILT_YZ_QUAD_SHIFT;
+ break;
+ case IIO_MOD_Y:
+ reg_addr = MMA9551_TILT_XZ_ANG_REG;
+ quad_shift = MMA9551_TILT_XZ_QUAD_SHIFT;
+ break;
+ case IIO_MOD_Z:
+ reg_addr = MMA9551_TILT_XY_ANG_REG;
+ quad_shift = MMA9551_TILT_XY_QUAD_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = mma9551_set_power_state(client, true);
+ if (ret < 0)
+ return ret;
+
+ ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
+ reg_addr, &angle);
+ if (ret < 0)
+ goto out_poweroff;
+
+ ret = mma9551_read_status_byte(client, MMA9551_APPID_TILT,
+ MMA9551_TILT_QUAD_REG, &quadrant);
+ if (ret < 0)
+ goto out_poweroff;
+
+ angle &= ~MMA9551_TILT_ANGFLG;
+ quadrant = (quadrant >> quad_shift) & 0x03;
+
+ if (quadrant == 1 || quadrant == 3)
+ *val = 90 * (quadrant + 1) - angle;
+ else
+ *val = angle + 90 * quadrant;
+
+ ret = IIO_VAL_INT;
+
+out_poweroff:
+ mma9551_set_power_state(client, false);
+ return ret;
+}
+
+static int mma9551_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_INCLI:
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_incli_chan(data->client, chan, val);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_accel_chan(data->client,
+ chan, val, val2);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ return mma9551_read_accel_scale(val, val2);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_INCLI:
+ /* IIO counts axes from 1, because IIO_NO_MOD is 0. */
+ return data->event_enabled[chan->channel2 - 1];
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9551_config_incli_event(struct iio_dev *indio_dev,
+ enum iio_modifier axis,
+ int state)
+{
+ struct mma9551_data *data = iio_priv(indio_dev);
+ enum mma9551_tilt_axis mma_axis;
+ int ret;
+
+ /* IIO counts axes from 1, because IIO_NO_MOD is 0. */
+ mma_axis = axis - 1;
+
+ if (data->event_enabled[mma_axis] == state)
+ return 0;
+
+ if (state == 0) {
+ ret = mma9551_gpio_config(data->client,
+ (enum mma9551_gpio_pin)mma_axis,
+ MMA9551_APPID_NONE, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = mma9551_set_power_state(data->client, false);
+ if (ret < 0)
+ return ret;
+ } else {
+ int bitnum;
+
+ /* Bit 7 of each angle register holds the angle flag. */
+ switch (axis) {
+ case IIO_MOD_X:
+ bitnum = 7 + 8 * MMA9551_TILT_YZ_ANG_REG;
+ break;
+ case IIO_MOD_Y:
+ bitnum = 7 + 8 * MMA9551_TILT_XZ_ANG_REG;
+ break;
+ case IIO_MOD_Z:
+ bitnum = 7 + 8 * MMA9551_TILT_XY_ANG_REG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+
+ ret = mma9551_set_power_state(data->client, true);
+ if (ret < 0)
+ return ret;
+
+ ret = mma9551_gpio_config(data->client,
+ (enum mma9551_gpio_pin)mma_axis,
+ MMA9551_APPID_TILT, bitnum, 0);
+ if (ret < 0) {
+ mma9551_set_power_state(data->client, false);
+ return ret;
+ }
+ }
+
+ data->event_enabled[mma_axis] = state;
+
+ return ret;
+}
+
+static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_INCLI:
+ mutex_lock(&data->mutex);
+ ret = mma9551_config_incli_event(indio_dev,
+ chan->channel2, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_INCLI:
+ if (val2 != 0 || val < 1 || val > 10)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9551_update_config_bits(data->client,
+ MMA9551_APPID_TILT,
+ MMA9551_TILT_CFG_REG,
+ MMA9551_TILT_ANG_THRESH_MASK,
+ val);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9551_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 mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 tmp;
+
+ switch (chan->type) {
+ case IIO_INCLI:
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_config_byte(data->client,
+ MMA9551_APPID_TILT,
+ MMA9551_TILT_CFG_REG, &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ *val = tmp & MMA9551_TILT_ANG_THRESH_MASK;
+ *val2 = 0;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_event_spec mma9551_incli_event = {
+ .type = IIO_EV_TYPE_ROC,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE),
+};
+
+#define MMA9551_INCLI_CHANNEL(axis) { \
+ .type = IIO_INCLI, \
+ .modified = 1, \
+ .channel2 = axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .event_spec = &mma9551_incli_event, \
+ .num_event_specs = 1, \
+}
+
+static const struct iio_chan_spec mma9551_channels[] = {
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
+
+ MMA9551_INCLI_CHANNEL(IIO_MOD_X),
+ MMA9551_INCLI_CHANNEL(IIO_MOD_Y),
+ MMA9551_INCLI_CHANNEL(IIO_MOD_Z),
+};
+
+static const struct iio_info mma9551_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mma9551_read_raw,
+ .read_event_config = mma9551_read_event_config,
+ .write_event_config = mma9551_write_event_config,
+ .read_event_value = mma9551_read_event_value,
+ .write_event_value = mma9551_write_event_value,
+};
+
+static irqreturn_t mma9551_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int i, ret, mma_axis = -1;
+ u16 reg;
+ u8 val;
+
+ mutex_lock(&data->mutex);
+
+ for (i = 0; i < 3; i++)
+ if (irq == data->irqs[i]) {
+ mma_axis = i;
+ break;
+ }
+
+ if (mma_axis == -1) {
+ /* IRQ was triggered on 4th line, which we don't use. */
+ dev_warn(&data->client->dev,
+ "irq triggered on unused line %d\n", data->irqs[3]);
+ goto out;
+ }
+
+ switch (mma_axis) {
+ case mma9551_x:
+ reg = MMA9551_TILT_YZ_ANG_REG;
+ break;
+ case mma9551_y:
+ reg = MMA9551_TILT_XZ_ANG_REG;
+ break;
+ case mma9551_z:
+ reg = MMA9551_TILT_XY_ANG_REG;
+ break;
+ }
+
+ /*
+ * Read the angle even though we don't use it, otherwise we
+ * won't get any further interrupts.
+ */
+ ret = mma9551_read_status_byte(data->client, MMA9551_APPID_TILT,
+ reg, &val);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "error %d reading tilt register in IRQ\n", ret);
+ goto out;
+ }
+
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_INCLI, 0, (mma_axis + 1),
+ IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING),
+ iio_get_time_ns());
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int mma9551_init(struct mma9551_data *data)
+{
+ int ret;
+
+ ret = mma9551_read_version(data->client);
+ if (ret)
+ return ret;
+
+ return mma9551_set_device_state(data->client, true);
+}
+
+static int mma9551_gpio_probe(struct iio_dev *indio_dev)
+{
+ struct gpio_desc *gpio;
+ int i, ret;
+ struct mma9551_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
+
+ for (i = 0; i < MMA9551_GPIO_COUNT; i++) {
+ gpio = devm_gpiod_get_index(dev, MMA9551_GPIO_NAME, i,
+ GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "acpi gpio get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+ if (ret < 0)
+ return ret;
+
+ data->irqs[i] = ret;
+ ret = devm_request_threaded_irq(dev, data->irqs[i],
+ NULL, mma9551_event_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ MMA9551_IRQ_NAME, indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "request irq %d failed\n", data->irqs[i]);
+ return ret;
+ }
+
+ dev_dbg(dev, "gpio resource, no:%d irq:%d\n",
+ desc_to_gpio(gpio), data->irqs[i]);
+ }
+
+ return 0;
+}
+
+static const char *mma9551_match_acpi_device(struct device *dev)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+
+ return dev_name(dev);
+}
+
+static int mma9551_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mma9551_data *data;
+ struct iio_dev *indio_dev;
+ const char *name = NULL;
+ 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;
+
+ if (id)
+ name = id->name;
+ else if (ACPI_HANDLE(&client->dev))
+ name = mma9551_match_acpi_device(&client->dev);
+
+ ret = mma9551_init(data);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&data->mutex);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = mma9551_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mma9551_channels);
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mma9551_info;
+
+ ret = mma9551_gpio_probe(indio_dev);
+ if (ret < 0)
+ goto out_poweroff;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto out_poweroff;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto out_iio_unregister;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ MMA9551_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ return 0;
+
+out_iio_unregister:
+ iio_device_unregister(indio_dev);
+out_poweroff:
+ mma9551_set_device_state(client, false);
+
+ return ret;
+}
+
+static int mma9551_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mma9551_data *data = iio_priv(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_lock(&data->mutex);
+ mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mma9551_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "powering off device failed\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int mma9551_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = mma9551_set_device_state(data->client, true);
+ if (ret < 0)
+ return ret;
+
+ mma9551_sleep(MMA9551_DEFAULT_SAMPLE_RATE);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int mma9551_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int mma9551_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9551_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops mma9551_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mma9551_suspend, mma9551_resume)
+ SET_RUNTIME_PM_OPS(mma9551_runtime_suspend,
+ mma9551_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id mma9551_acpi_match[] = {
+ {"MMA9551", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, mma9551_acpi_match);
+
+static const struct i2c_device_id mma9551_id[] = {
+ {"mma9551", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mma9551_id);
+
+static struct i2c_driver mma9551_driver = {
+ .driver = {
+ .name = MMA9551_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(mma9551_acpi_match),
+ .pm = &mma9551_pm_ops,
+ },
+ .probe = mma9551_probe,
+ .remove = mma9551_remove,
+ .id_table = mma9551_id,
+};
+
+module_i2c_driver(mma9551_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMA9551L motion-sensing platform driver");
diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
new file mode 100644
index 0000000..7f55a6d
--- /dev/null
+++ b/drivers/iio/accel/mma9551_core.c
@@ -0,0 +1,798 @@
+/*
+ * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
+ * Copyright (c) 2014, 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/pm_runtime.h>
+#include "mma9551_core.h"
+
+/* Command masks for mailbox write command */
+#define MMA9551_CMD_READ_VERSION_INFO 0x00
+#define MMA9551_CMD_READ_CONFIG 0x10
+#define MMA9551_CMD_WRITE_CONFIG 0x20
+#define MMA9551_CMD_READ_STATUS 0x30
+
+/* Mailbox read command */
+#define MMA9551_RESPONSE_COCO BIT(7)
+
+/* Error-Status codes returned in mailbox read command */
+#define MMA9551_MCI_ERROR_NONE 0x00
+#define MMA9551_MCI_ERROR_PARAM 0x04
+#define MMA9551_MCI_INVALID_COUNT 0x19
+#define MMA9551_MCI_ERROR_COMMAND 0x1C
+#define MMA9551_MCI_ERROR_INVALID_LENGTH 0x21
+#define MMA9551_MCI_ERROR_FIFO_BUSY 0x22
+#define MMA9551_MCI_ERROR_FIFO_ALLOCATED 0x23
+#define MMA9551_MCI_ERROR_FIFO_OVERSIZE 0x24
+
+/* GPIO Application */
+#define MMA9551_GPIO_POL_MSB 0x08
+#define MMA9551_GPIO_POL_LSB 0x09
+
+/* Sleep/Wake application */
+#define MMA9551_SLEEP_CFG 0x06
+#define MMA9551_SLEEP_CFG_SNCEN BIT(0)
+#define MMA9551_SLEEP_CFG_FLEEN BIT(1)
+#define MMA9551_SLEEP_CFG_SCHEN BIT(2)
+
+/* AFE application */
+#define MMA9551_AFE_X_ACCEL_REG 0x00
+#define MMA9551_AFE_Y_ACCEL_REG 0x02
+#define MMA9551_AFE_Z_ACCEL_REG 0x04
+
+/* Reset/Suspend/Clear application */
+#define MMA9551_RSC_RESET 0x00
+#define MMA9551_RSC_OFFSET(mask) (3 - (ffs(mask) - 1) / 8)
+#define MMA9551_RSC_VAL(mask) (mask >> (((ffs(mask) - 1) / 8) * 8))
+
+/*
+ * A response is composed of:
+ * - control registers: MB0-3
+ * - data registers: MB4-31
+ *
+ * A request is composed of:
+ * - mbox to write to (always 0)
+ * - control registers: MB1-4
+ * - data registers: MB5-31
+ */
+#define MMA9551_MAILBOX_CTRL_REGS 4
+#define MMA9551_MAX_MAILBOX_DATA_REGS 28
+#define MMA9551_MAILBOX_REGS 32
+
+#define MMA9551_I2C_READ_RETRIES 5
+#define MMA9551_I2C_READ_DELAY 50 /* us */
+
+struct mma9551_mbox_request {
+ u8 start_mbox; /* Always 0. */
+ u8 app_id;
+ /*
+ * See Section 5.3.1 of the MMA955xL Software Reference Manual.
+ *
+ * Bit 7: reserved, always 0
+ * Bits 6-4: command
+ * Bits 3-0: upper bits of register offset
+ */
+ u8 cmd_off;
+ u8 lower_off;
+ u8 nbytes;
+ u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS - 1];
+} __packed;
+
+struct mma9551_mbox_response {
+ u8 app_id;
+ /*
+ * See Section 5.3.3 of the MMA955xL Software Reference Manual.
+ *
+ * Bit 7: COCO
+ * Bits 6-0: Error code.
+ */
+ u8 coco_err;
+ u8 nbytes;
+ u8 req_bytes;
+ u8 buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+} __packed;
+
+struct mma9551_version_info {
+ __be32 device_id;
+ u8 rom_version[2];
+ u8 fw_version[2];
+ u8 hw_version[2];
+ u8 fw_build[2];
+};
+
+static int mma9551_transfer(struct i2c_client *client,
+ u8 app_id, u8 command, u16 offset,
+ u8 *inbytes, int num_inbytes,
+ u8 *outbytes, int num_outbytes)
+{
+ struct mma9551_mbox_request req;
+ struct mma9551_mbox_response rsp;
+ struct i2c_msg in, out;
+ u8 req_len, err_code;
+ int ret, retries;
+
+ if (offset >= 1 << 12) {
+ dev_err(&client->dev, "register offset too large\n");
+ return -EINVAL;
+ }
+
+ req_len = 1 + MMA9551_MAILBOX_CTRL_REGS + num_inbytes;
+ req.start_mbox = 0;
+ req.app_id = app_id;
+ req.cmd_off = command | (offset >> 8);
+ req.lower_off = offset;
+
+ if (command == MMA9551_CMD_WRITE_CONFIG)
+ req.nbytes = num_inbytes;
+ else
+ req.nbytes = num_outbytes;
+ if (num_inbytes)
+ memcpy(req.buf, inbytes, num_inbytes);
+
+ out.addr = client->addr;
+ out.flags = 0;
+ out.len = req_len;
+ out.buf = (u8 *)&req;
+
+ ret = i2c_transfer(client->adapter, &out, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c write failed\n");
+ return ret;
+ }
+
+ retries = MMA9551_I2C_READ_RETRIES;
+ do {
+ udelay(MMA9551_I2C_READ_DELAY);
+
+ in.addr = client->addr;
+ in.flags = I2C_M_RD;
+ in.len = sizeof(rsp);
+ in.buf = (u8 *)&rsp;
+
+ ret = i2c_transfer(client->adapter, &in, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c read failed\n");
+ return ret;
+ }
+
+ if (rsp.coco_err & MMA9551_RESPONSE_COCO)
+ break;
+ } while (--retries > 0);
+
+ if (retries == 0) {
+ dev_err(&client->dev,
+ "timed out while waiting for command response\n");
+ return -ETIMEDOUT;
+ }
+
+ if (rsp.app_id != app_id) {
+ dev_err(&client->dev,
+ "app_id mismatch in response got %02x expected %02x\n",
+ rsp.app_id, app_id);
+ return -EINVAL;
+ }
+
+ err_code = rsp.coco_err & ~MMA9551_RESPONSE_COCO;
+ if (err_code != MMA9551_MCI_ERROR_NONE) {
+ dev_err(&client->dev, "read returned error %x\n", err_code);
+ return -EINVAL;
+ }
+
+ if (rsp.nbytes != rsp.req_bytes) {
+ dev_err(&client->dev,
+ "output length mismatch got %d expected %d\n",
+ rsp.nbytes, rsp.req_bytes);
+ return -EINVAL;
+ }
+
+ if (num_outbytes)
+ memcpy(outbytes, rsp.buf, num_outbytes);
+
+ return 0;
+}
+
+/**
+ * mma9551_read_config_byte() - read 1 configuration byte
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Pointer to store value read
+ *
+ * Read one configuration byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed
+ * by one or more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 *val)
+{
+ return mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+ reg, NULL, 0, val, 1);
+}
+EXPORT_SYMBOL(mma9551_read_config_byte);
+
+/**
+ * mma9551_write_config_byte() - write 1 configuration byte
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Value to write
+ *
+ * Write one configuration byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 val)
+{
+ return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
+ &val, 1, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_byte);
+
+/**
+ * mma9551_read_status_byte() - read 1 status byte
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Pointer to store value read
+ *
+ * Read one status byte from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 *val)
+{
+ return mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+ reg, NULL, 0, val, 1);
+}
+EXPORT_SYMBOL(mma9551_read_status_byte);
+
+/**
+ * mma9551_read_config_word() - read 1 config word
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Pointer to store value read
+ *
+ * Read one configuration word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 *val)
+{
+ int ret;
+ __be16 v;
+
+ ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+ reg, NULL, 0, (u8 *)&v, 2);
+ *val = be16_to_cpu(v);
+
+ return ret;
+}
+EXPORT_SYMBOL(mma9551_read_config_word);
+
+/**
+ * mma9551_write_config_word() - write 1 config word
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Value to write
+ *
+ * Write one configuration word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 val)
+{
+ __be16 v = cpu_to_be16(val);
+
+ return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG, reg,
+ (u8 *) &v, 2, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_word);
+
+/**
+ * mma9551_read_status_word() - read 1 status word
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @val: Pointer to store value read
+ *
+ * Read one status word from the device using MMA955xL command format.
+ * Commands to the MMA955xL platform consist of a write followed by one or
+ * more reads.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 *val)
+{
+ int ret;
+ __be16 v;
+
+ ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+ reg, NULL, 0, (u8 *)&v, 2);
+ *val = be16_to_cpu(v);
+
+ return ret;
+}
+EXPORT_SYMBOL(mma9551_read_status_word);
+
+/**
+ * mma9551_read_config_words() - read multiple config words
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @len: Length of array to read in bytes
+ * @val: Array of words to read
+ *
+ * Read multiple configuration registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf)
+{
+ int ret, i;
+ int len_words = len / sizeof(u16);
+ __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+ ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_CONFIG,
+ reg, NULL, 0, (u8 *) be_buf, len);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < len_words; i++)
+ buf[i] = be16_to_cpu(be_buf[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(mma9551_read_config_words);
+
+/**
+ * mma9551_read_status_words() - read multiple status words
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @len: Length of array to read in bytes
+ * @val: Array of words to read
+ *
+ * Read multiple status registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf)
+{
+ int ret, i;
+ int len_words = len / sizeof(u16);
+ __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+ ret = mma9551_transfer(client, app_id, MMA9551_CMD_READ_STATUS,
+ reg, NULL, 0, (u8 *) be_buf, len);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < len_words; i++)
+ buf[i] = be16_to_cpu(be_buf[i]);
+
+ return 0;
+}
+EXPORT_SYMBOL(mma9551_read_status_words);
+
+/**
+ * mma9551_write_config_words() - write multiple config words
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @len: Length of array to write in bytes
+ * @val: Array of words to write
+ *
+ * Write multiple configuration registers (word-sized registers).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf)
+{
+ int i;
+ int len_words = len / sizeof(u16);
+ __be16 be_buf[MMA9551_MAX_MAILBOX_DATA_REGS];
+
+ for (i = 0; i < len_words; i++)
+ be_buf[i] = cpu_to_be16(buf[i]);
+
+ return mma9551_transfer(client, app_id, MMA9551_CMD_WRITE_CONFIG,
+ reg, (u8 *) be_buf, len, NULL, 0);
+}
+EXPORT_SYMBOL(mma9551_write_config_words);
+
+/**
+ * mma9551_update_config_bits() - update bits in register
+ * @client: I2C client
+ * @app_id: Application ID
+ * @reg: Application register
+ * @mask: Mask for the bits to update
+ * @val: Value of the bits to update
+ *
+ * Update bits in the given register using a bit mask.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 mask, u8 val)
+{
+ int ret;
+ u8 tmp, orig;
+
+ ret = mma9551_read_config_byte(client, app_id, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ if (tmp == orig)
+ return 0;
+
+ return mma9551_write_config_byte(client, app_id, reg, tmp);
+}
+EXPORT_SYMBOL(mma9551_update_config_bits);
+
+/**
+ * mma9551_gpio_config() - configure gpio
+ * @client: I2C client
+ * @pin: GPIO pin to configure
+ * @app_id: Application ID
+ * @bitnum: Bit number of status register being assigned to the GPIO pin.
+ * @polarity: The polarity parameter is described in section 6.2.2, page 66,
+ * of the Software Reference Manual. Basically, polarity=0 means
+ * the interrupt line has the same value as the selected bit,
+ * while polarity=1 means the line is inverted.
+ *
+ * Assign a bit from an application’s status register to a specific GPIO pin.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
+ u8 app_id, u8 bitnum, int polarity)
+{
+ u8 reg, pol_mask, pol_val;
+ int ret;
+
+ if (pin > mma9551_gpio_max) {
+ dev_err(&client->dev, "bad GPIO pin\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Pin 6 is configured by regs 0x00 and 0x01, pin 7 by 0x02 and
+ * 0x03, and so on.
+ */
+ reg = pin * 2;
+
+ ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
+ reg, app_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "error setting GPIO app_id\n");
+ return ret;
+ }
+
+ ret = mma9551_write_config_byte(client, MMA9551_APPID_GPIO,
+ reg + 1, bitnum);
+ if (ret < 0) {
+ dev_err(&client->dev, "error setting GPIO bit number\n");
+ return ret;
+ }
+
+ switch (pin) {
+ case mma9551_gpio6:
+ reg = MMA9551_GPIO_POL_LSB;
+ pol_mask = 1 << 6;
+ break;
+ case mma9551_gpio7:
+ reg = MMA9551_GPIO_POL_LSB;
+ pol_mask = 1 << 7;
+ break;
+ case mma9551_gpio8:
+ reg = MMA9551_GPIO_POL_MSB;
+ pol_mask = 1 << 0;
+ break;
+ case mma9551_gpio9:
+ reg = MMA9551_GPIO_POL_MSB;
+ pol_mask = 1 << 1;
+ break;
+ }
+ pol_val = polarity ? pol_mask : 0;
+
+ ret = mma9551_update_config_bits(client, MMA9551_APPID_GPIO, reg,
+ pol_mask, pol_val);
+ if (ret < 0)
+ dev_err(&client->dev, "error setting GPIO polarity\n");
+
+ return ret;
+}
+EXPORT_SYMBOL(mma9551_gpio_config);
+
+/**
+ * mma9551_read_version() - read device version information
+ * @client: I2C client
+ *
+ * Read version information and print device id and firmware version.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_read_version(struct i2c_client *client)
+{
+ struct mma9551_version_info info;
+ int ret;
+
+ ret = mma9551_transfer(client, MMA9551_APPID_VERSION, 0x00, 0x00,
+ NULL, 0, (u8 *)&info, sizeof(info));
+ if (ret < 0)
+ return ret;
+
+ dev_info(&client->dev, "device ID 0x%x, firmware version %02x.%02x\n",
+ be32_to_cpu(info.device_id), info.fw_version[0],
+ info.fw_version[1]);
+
+ return 0;
+}
+EXPORT_SYMBOL(mma9551_read_version);
+
+/**
+ * mma9551_set_device_state() - sets HW power mode
+ * @client: I2C client
+ * @enable: Use true to power on device, false to cause the device
+ * to enter sleep.
+ *
+ * Set power on/off for device using the Sleep/Wake Application.
+ * When enable is true, power on chip and enable doze mode.
+ * When enable is false, enter sleep mode (device remains in the
+ * lowest-power mode).
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_set_device_state(struct i2c_client *client, bool enable)
+{
+ return mma9551_update_config_bits(client, MMA9551_APPID_SLEEP_WAKE,
+ MMA9551_SLEEP_CFG,
+ MMA9551_SLEEP_CFG_SNCEN |
+ MMA9551_SLEEP_CFG_FLEEN |
+ MMA9551_SLEEP_CFG_SCHEN,
+ enable ? MMA9551_SLEEP_CFG_SCHEN |
+ MMA9551_SLEEP_CFG_FLEEN :
+ MMA9551_SLEEP_CFG_SNCEN);
+}
+EXPORT_SYMBOL(mma9551_set_device_state);
+
+/**
+ * mma9551_set_power_state() - sets runtime PM state
+ * @client: I2C client
+ * @on: Use true to power on device, false to power off
+ *
+ * Resume or suspend the device using Runtime PM.
+ * The device will suspend after the autosuspend delay.
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_set_power_state(struct i2c_client *client, bool on)
+{
+#ifdef CONFIG_PM
+ int ret;
+
+ if (on)
+ ret = pm_runtime_get_sync(&client->dev);
+ else {
+ pm_runtime_mark_last_busy(&client->dev);
+ ret = pm_runtime_put_autosuspend(&client->dev);
+ }
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed to change power state to %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&client->dev);
+
+ return ret;
+ }
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL(mma9551_set_power_state);
+
+/**
+ * mma9551_sleep() - sleep
+ * @freq: Application frequency
+ *
+ * Firmware applications run at a certain frequency on the
+ * device. Sleep for one application cycle to make sure the
+ * application had time to run once and initialize set values.
+ */
+void mma9551_sleep(int freq)
+{
+ int sleep_val = 1000 / freq;
+
+ if (sleep_val < 20)
+ usleep_range(sleep_val * 1000, 20000);
+ else
+ msleep_interruptible(sleep_val);
+}
+EXPORT_SYMBOL(mma9551_sleep);
+
+/**
+ * mma9551_read_accel_chan() - read accelerometer channel
+ * @client: I2C client
+ * @chan: IIO channel
+ * @val: Pointer to the accelerometer value read
+ * @val2: Unused
+ *
+ * Read accelerometer value for the specified channel.
+ *
+ * Locking note: This function must be called with the device lock held.
+ * Locking is not handled inside the function. Callers should ensure they
+ * serialize access to the HW.
+ *
+ * Returns: IIO_VAL_INT on success, negative value on failure.
+ */
+int mma9551_read_accel_chan(struct i2c_client *client,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2)
+{
+ u16 reg_addr;
+ s16 raw_accel;
+ int ret;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ reg_addr = MMA9551_AFE_X_ACCEL_REG;
+ break;
+ case IIO_MOD_Y:
+ reg_addr = MMA9551_AFE_Y_ACCEL_REG;
+ break;
+ case IIO_MOD_Z:
+ reg_addr = MMA9551_AFE_Z_ACCEL_REG;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = mma9551_set_power_state(client, true);
+ if (ret < 0)
+ return ret;
+
+ ret = mma9551_read_status_word(client, MMA9551_APPID_AFE,
+ reg_addr, &raw_accel);
+ if (ret < 0)
+ goto out_poweroff;
+
+ *val = raw_accel;
+
+ ret = IIO_VAL_INT;
+
+out_poweroff:
+ mma9551_set_power_state(client, false);
+ return ret;
+}
+EXPORT_SYMBOL(mma9551_read_accel_chan);
+
+/**
+ * mma9551_read_accel_scale() - read accelerometer scale
+ * @val: Pointer to the accelerometer scale (int value)
+ * @val2: Pointer to the accelerometer scale (micro value)
+ *
+ * Read accelerometer scale.
+ *
+ * Returns: IIO_VAL_INT_PLUS_MICRO.
+ */
+int mma9551_read_accel_scale(int *val, int *val2)
+{
+ *val = 0;
+ *val2 = 2440;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(mma9551_read_accel_scale);
+
+/**
+ * mma9551_app_reset() - reset application
+ * @client: I2C client
+ * @app_mask: Application to reset
+ *
+ * Reset the given application (using the Reset/Suspend/Clear
+ * Control Application)
+ *
+ * Returns: 0 on success, negative value on failure.
+ */
+int mma9551_app_reset(struct i2c_client *client, u32 app_mask)
+{
+ return mma9551_write_config_byte(client, MMA9551_APPID_RCS,
+ MMA9551_RSC_RESET +
+ MMA9551_RSC_OFFSET(app_mask),
+ MMA9551_RSC_VAL(app_mask));
+}
+EXPORT_SYMBOL(mma9551_app_reset);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMA955xL sensors core");
diff --git a/drivers/iio/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
new file mode 100644
index 0000000..edaa56b
--- /dev/null
+++ b/drivers/iio/accel/mma9551_core.h
@@ -0,0 +1,81 @@
+/*
+ * Common code for Freescale MMA955x Intelligent Sensor Platform drivers
+ * Copyright (c) 2014, 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.
+ */
+
+#ifndef _MMA9551_CORE_H_
+#define _MMA9551_CORE_H_
+
+/* Applications IDs */
+#define MMA9551_APPID_VERSION 0x00
+#define MMA9551_APPID_GPIO 0x03
+#define MMA9551_APPID_AFE 0x06
+#define MMA9551_APPID_TILT 0x0B
+#define MMA9551_APPID_SLEEP_WAKE 0x12
+#define MMA9551_APPID_PEDOMETER 0x15
+#define MMA9551_APPID_RCS 0x17
+#define MMA9551_APPID_NONE 0xff
+
+/* Reset/Suspend/Clear application app masks */
+#define MMA9551_RSC_PED BIT(21)
+
+#define MMA9551_AUTO_SUSPEND_DELAY_MS 2000
+
+enum mma9551_gpio_pin {
+ mma9551_gpio6 = 0,
+ mma9551_gpio7,
+ mma9551_gpio8,
+ mma9551_gpio9,
+ mma9551_gpio_max = mma9551_gpio9,
+};
+
+#define MMA9551_ACCEL_CHANNEL(axis) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+int mma9551_read_config_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 *val);
+int mma9551_write_config_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 val);
+int mma9551_read_status_byte(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 *val);
+int mma9551_read_config_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 *val);
+int mma9551_write_config_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 val);
+int mma9551_read_status_word(struct i2c_client *client, u8 app_id,
+ u16 reg, u16 *val);
+int mma9551_read_config_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf);
+int mma9551_read_status_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf);
+int mma9551_write_config_words(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 len, u16 *buf);
+int mma9551_update_config_bits(struct i2c_client *client, u8 app_id,
+ u16 reg, u8 mask, u8 val);
+int mma9551_gpio_config(struct i2c_client *client, enum mma9551_gpio_pin pin,
+ u8 app_id, u8 bitnum, int polarity);
+int mma9551_read_version(struct i2c_client *client);
+int mma9551_set_device_state(struct i2c_client *client, bool enable);
+int mma9551_set_power_state(struct i2c_client *client, bool on);
+void mma9551_sleep(int freq);
+int mma9551_read_accel_chan(struct i2c_client *client,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2);
+int mma9551_read_accel_scale(int *val, int *val2);
+int mma9551_app_reset(struct i2c_client *client, u32 app_mask);
+
+#endif /* _MMA9551_CORE_H_ */
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
new file mode 100644
index 0000000..2df1af7
--- /dev/null
+++ b/drivers/iio/accel/mma9553.c
@@ -0,0 +1,1330 @@
+/*
+ * Freescale MMA9553L Intelligent Pedometer driver
+ * Copyright (c) 2014, 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.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/pm_runtime.h>
+#include "mma9551_core.h"
+
+#define MMA9553_DRV_NAME "mma9553"
+#define MMA9553_IRQ_NAME "mma9553_event"
+#define MMA9553_GPIO_NAME "mma9553_int"
+
+/* Pedometer configuration registers (R/W) */
+#define MMA9553_REG_CONF_SLEEPMIN 0x00
+#define MMA9553_REG_CONF_SLEEPMAX 0x02
+#define MMA9553_REG_CONF_SLEEPTHD 0x04
+#define MMA9553_MASK_CONF_WORD GENMASK(15, 0)
+
+#define MMA9553_REG_CONF_CONF_STEPLEN 0x06
+#define MMA9553_MASK_CONF_CONFIG BIT(15)
+#define MMA9553_MASK_CONF_ACT_DBCNTM BIT(14)
+#define MMA9553_MASK_CONF_SLP_DBCNTM BIT(13)
+#define MMA9553_MASK_CONF_STEPLEN GENMASK(7, 0)
+
+#define MMA9553_REG_CONF_HEIGHT_WEIGHT 0x08
+#define MMA9553_MASK_CONF_HEIGHT GENMASK(15, 8)
+#define MMA9553_MASK_CONF_WEIGHT GENMASK(7, 0)
+
+#define MMA9553_REG_CONF_FILTER 0x0A
+#define MMA9553_MASK_CONF_FILTSTEP GENMASK(15, 8)
+#define MMA9553_MASK_CONF_MALE BIT(7)
+#define MMA9553_MASK_CONF_FILTTIME GENMASK(6, 0)
+
+#define MMA9553_REG_CONF_SPEED_STEP 0x0C
+#define MMA9553_MASK_CONF_SPDPRD GENMASK(15, 8)
+#define MMA9553_MASK_CONF_STEPCOALESCE GENMASK(7, 0)
+
+#define MMA9553_REG_CONF_ACTTHD 0x0E
+
+/* Pedometer status registers (R-only) */
+#define MMA9553_REG_STATUS 0x00
+#define MMA9553_MASK_STATUS_MRGFL BIT(15)
+#define MMA9553_MASK_STATUS_SUSPCHG BIT(14)
+#define MMA9553_MASK_STATUS_STEPCHG BIT(13)
+#define MMA9553_MASK_STATUS_ACTCHG BIT(12)
+#define MMA9553_MASK_STATUS_SUSP BIT(11)
+#define MMA9553_MASK_STATUS_ACTIVITY (BIT(10) | BIT(9) | BIT(8))
+#define MMA9553_MASK_STATUS_VERSION 0x00FF
+
+#define MMA9553_REG_STEPCNT 0x02
+#define MMA9553_REG_DISTANCE 0x04
+#define MMA9553_REG_SPEED 0x06
+#define MMA9553_REG_CALORIES 0x08
+#define MMA9553_REG_SLEEPCNT 0x0A
+
+/* Pedometer events are always mapped to this pin. */
+#define MMA9553_DEFAULT_GPIO_PIN mma9551_gpio6
+#define MMA9553_DEFAULT_GPIO_POLARITY 0
+
+/* Bitnum used for gpio configuration = bit number in high status byte */
+#define STATUS_TO_BITNUM(bit) (ffs(bit) - 9)
+
+#define MMA9553_DEFAULT_SAMPLE_RATE 30 /* Hz */
+
+/*
+ * The internal activity level must be stable for ACTTHD samples before
+ * ACTIVITY is updated.The ACTIVITY variable contains the current activity
+ * level and is updated every time a step is detected or once a second
+ * if there are no steps.
+ */
+#define MMA9553_ACTIVITY_THD_TO_SEC(thd) ((thd) / MMA9553_DEFAULT_SAMPLE_RATE)
+#define MMA9553_ACTIVITY_SEC_TO_THD(sec) ((sec) * MMA9553_DEFAULT_SAMPLE_RATE)
+
+/*
+ * Autonomously suspend pedometer if acceleration vector magnitude
+ * is near 1g (4096 at 0.244 mg/LSB resolution) for 30 seconds.
+ */
+#define MMA9553_DEFAULT_SLEEPMIN 3688 /* 0,9 g */
+#define MMA9553_DEFAULT_SLEEPMAX 4508 /* 1,1 g */
+#define MMA9553_DEFAULT_SLEEPTHD (MMA9553_DEFAULT_SAMPLE_RATE * 30)
+
+#define MMA9553_CONFIG_RETRIES 2
+
+/* Status register - activity field */
+enum activity_level {
+ ACTIVITY_UNKNOWN,
+ ACTIVITY_REST,
+ ACTIVITY_WALKING,
+ ACTIVITY_JOGGING,
+ ACTIVITY_RUNNING,
+};
+
+static struct mma9553_event_info {
+ enum iio_chan_type type;
+ enum iio_modifier mod;
+ enum iio_event_direction dir;
+} mma9553_events_info[] = {
+ {
+ .type = IIO_STEPS,
+ .mod = IIO_NO_MOD,
+ .dir = IIO_EV_DIR_NONE,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_STILL,
+ .dir = IIO_EV_DIR_RISING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_STILL,
+ .dir = IIO_EV_DIR_FALLING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_WALKING,
+ .dir = IIO_EV_DIR_RISING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_WALKING,
+ .dir = IIO_EV_DIR_FALLING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_JOGGING,
+ .dir = IIO_EV_DIR_RISING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_JOGGING,
+ .dir = IIO_EV_DIR_FALLING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_RUNNING,
+ .dir = IIO_EV_DIR_RISING,
+ },
+ {
+ .type = IIO_ACTIVITY,
+ .mod = IIO_MOD_RUNNING,
+ .dir = IIO_EV_DIR_FALLING,
+ },
+};
+
+#define MMA9553_EVENTS_INFO_SIZE ARRAY_SIZE(mma9553_events_info)
+
+struct mma9553_event {
+ struct mma9553_event_info *info;
+ bool enabled;
+};
+
+struct mma9553_conf_regs {
+ u16 sleepmin;
+ u16 sleepmax;
+ u16 sleepthd;
+ u16 config;
+ u16 height_weight;
+ u16 filter;
+ u16 speed_step;
+ u16 actthd;
+} __packed;
+
+struct mma9553_data {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct mma9553_conf_regs conf;
+ struct mma9553_event events[MMA9553_EVENTS_INFO_SIZE];
+ int num_events;
+ u8 gpio_bitnum;
+ /*
+ * This is used for all features that depend on step count:
+ * step count, distance, speed, calories.
+ */
+ bool stepcnt_enabled;
+ u16 stepcnt;
+ u8 activity;
+ s64 timestamp;
+};
+
+static u8 mma9553_get_bits(u16 val, u16 mask)
+{
+ return (val & mask) >> (ffs(mask) - 1);
+}
+
+static u16 mma9553_set_bits(u16 current_val, u16 val, u16 mask)
+{
+ return (current_val & ~mask) | (val << (ffs(mask) - 1));
+}
+
+static enum iio_modifier mma9553_activity_to_mod(enum activity_level activity)
+{
+ switch (activity) {
+ case ACTIVITY_RUNNING:
+ return IIO_MOD_RUNNING;
+ case ACTIVITY_JOGGING:
+ return IIO_MOD_JOGGING;
+ case ACTIVITY_WALKING:
+ return IIO_MOD_WALKING;
+ case ACTIVITY_REST:
+ return IIO_MOD_STILL;
+ case ACTIVITY_UNKNOWN:
+ default:
+ return IIO_NO_MOD;
+ }
+}
+
+static void mma9553_init_events(struct mma9553_data *data)
+{
+ int i;
+
+ data->num_events = MMA9553_EVENTS_INFO_SIZE;
+ for (i = 0; i < data->num_events; i++) {
+ data->events[i].info = &mma9553_events_info[i];
+ data->events[i].enabled = false;
+ }
+}
+
+static struct mma9553_event *mma9553_get_event(struct mma9553_data *data,
+ enum iio_chan_type type,
+ enum iio_modifier mod,
+ enum iio_event_direction dir)
+{
+ int i;
+
+ for (i = 0; i < data->num_events; i++)
+ if (data->events[i].info->type == type &&
+ data->events[i].info->mod == mod &&
+ data->events[i].info->dir == dir)
+ return &data->events[i];
+
+ return NULL;
+}
+
+static bool mma9553_is_any_event_enabled(struct mma9553_data *data,
+ bool check_type,
+ enum iio_chan_type type)
+{
+ int i;
+
+ for (i = 0; i < data->num_events; i++)
+ if ((check_type && data->events[i].info->type == type &&
+ data->events[i].enabled) ||
+ (!check_type && data->events[i].enabled))
+ return true;
+
+ return false;
+}
+
+static int mma9553_set_config(struct mma9553_data *data, u16 reg,
+ u16 *p_reg_val, u16 val, u16 mask)
+{
+ int ret, retries;
+ u16 reg_val, config;
+
+ reg_val = *p_reg_val;
+ if (val == mma9553_get_bits(reg_val, mask))
+ return 0;
+
+ reg_val = mma9553_set_bits(reg_val, val, mask);
+ ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
+ reg, reg_val);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "error writing config register 0x%x\n", reg);
+ return ret;
+ }
+
+ *p_reg_val = reg_val;
+
+ /* Reinitializes the pedometer with current configuration values */
+ config = mma9553_set_bits(data->conf.config, 1,
+ MMA9553_MASK_CONF_CONFIG);
+
+ ret = mma9551_write_config_word(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_CONF_STEPLEN, config);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "error writing config register 0x%x\n",
+ MMA9553_REG_CONF_CONF_STEPLEN);
+ return ret;
+ }
+
+ retries = MMA9553_CONFIG_RETRIES;
+ do {
+ mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
+ ret = mma9551_read_config_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_CONF_STEPLEN,
+ &config);
+ if (ret < 0)
+ return ret;
+ } while (mma9553_get_bits(config, MMA9553_MASK_CONF_CONFIG) &&
+ --retries > 0);
+
+ return 0;
+}
+
+static int mma9553_read_activity_stepcnt(struct mma9553_data *data,
+ u8 *activity, u16 *stepcnt)
+{
+ u32 status_stepcnt;
+ u16 status;
+ int ret;
+
+ ret = mma9551_read_status_words(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_STATUS, sizeof(u32),
+ (u16 *) &status_stepcnt);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "error reading status and stepcnt\n");
+ return ret;
+ }
+
+ status = status_stepcnt & MMA9553_MASK_CONF_WORD;
+ *activity = mma9553_get_bits(status, MMA9553_MASK_STATUS_ACTIVITY);
+ *stepcnt = status_stepcnt >> 16;
+
+ return 0;
+}
+
+static int mma9553_conf_gpio(struct mma9553_data *data)
+{
+ u8 bitnum = 0, appid = MMA9551_APPID_PEDOMETER;
+ int ret;
+ struct mma9553_event *ev_step_detect;
+ bool activity_enabled;
+
+ activity_enabled =
+ mma9553_is_any_event_enabled(data, true, IIO_ACTIVITY);
+ ev_step_detect =
+ mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+
+ /*
+ * If both step detector and activity are enabled, use the MRGFL bit.
+ * This bit is the logical OR of the SUSPCHG, STEPCHG, and ACTCHG flags.
+ */
+ if (activity_enabled && ev_step_detect->enabled)
+ bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_MRGFL);
+ else if (ev_step_detect->enabled)
+ bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_STEPCHG);
+ else if (activity_enabled)
+ bitnum = STATUS_TO_BITNUM(MMA9553_MASK_STATUS_ACTCHG);
+ else /* Reset */
+ appid = MMA9551_APPID_NONE;
+
+ if (data->gpio_bitnum == bitnum)
+ return 0;
+
+ /* Save initial values for activity and stepcnt */
+ if (activity_enabled || ev_step_detect->enabled)
+ mma9553_read_activity_stepcnt(data, &data->activity,
+ &data->stepcnt);
+
+ ret = mma9551_gpio_config(data->client,
+ MMA9553_DEFAULT_GPIO_PIN,
+ appid, bitnum, MMA9553_DEFAULT_GPIO_POLARITY);
+ if (ret < 0)
+ return ret;
+ data->gpio_bitnum = bitnum;
+
+ return 0;
+}
+
+static int mma9553_init(struct mma9553_data *data)
+{
+ int ret;
+
+ ret = mma9551_read_version(data->client);
+ if (ret)
+ return ret;
+
+ /*
+ * Read all the pedometer configuration registers. This is used as
+ * a device identification command to differentiate the MMA9553L
+ * from the MMA9550L.
+ */
+ ret =
+ mma9551_read_config_words(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_SLEEPMIN,
+ sizeof(data->conf), (u16 *) &data->conf);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "device is not MMA9553L: failed to read cfg regs\n");
+ return ret;
+ }
+
+
+ /* Reset gpio */
+ data->gpio_bitnum = -1;
+ ret = mma9553_conf_gpio(data);
+ if (ret < 0)
+ return ret;
+
+ ret = mma9551_app_reset(data->client, MMA9551_RSC_PED);
+ if (ret < 0)
+ return ret;
+
+ /* Init config registers */
+ data->conf.sleepmin = MMA9553_DEFAULT_SLEEPMIN;
+ data->conf.sleepmax = MMA9553_DEFAULT_SLEEPMAX;
+ data->conf.sleepthd = MMA9553_DEFAULT_SLEEPTHD;
+ data->conf.config =
+ mma9553_set_bits(data->conf.config, 1, MMA9553_MASK_CONF_CONFIG);
+ /*
+ * Clear the activity debounce counter when the activity level changes,
+ * so that the confidence level applies for any activity level.
+ */
+ data->conf.config = mma9553_set_bits(data->conf.config, 1,
+ MMA9553_MASK_CONF_ACT_DBCNTM);
+ ret =
+ mma9551_write_config_words(data->client, MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CONF_SLEEPMIN,
+ sizeof(data->conf), (u16 *) &data->conf);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to write configuration registers\n");
+ return ret;
+ }
+
+ return mma9551_set_device_state(data->client, true);
+}
+
+static int mma9553_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+ u16 tmp;
+ u8 activity;
+ bool powered_on;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_STEPS:
+ /*
+ * The HW only counts steps and other dependent
+ * parameters (speed, distance, calories, activity)
+ * if power is on (from enabling an event or the
+ * step counter */
+ powered_on =
+ mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev,
+ "No channels enabled\n");
+ return -EINVAL;
+ }
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_STEPCNT,
+ &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ *val = tmp;
+ return IIO_VAL_INT;
+ case IIO_DISTANCE:
+ powered_on =
+ mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev,
+ "No channels enabled\n");
+ return -EINVAL;
+ }
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_DISTANCE,
+ &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ *val = tmp;
+ return IIO_VAL_INT;
+ case IIO_ACTIVITY:
+ powered_on =
+ mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev,
+ "No channels enabled\n");
+ return -EINVAL;
+ }
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_STATUS,
+ &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+
+ activity =
+ mma9553_get_bits(tmp, MMA9553_MASK_STATUS_ACTIVITY);
+
+ /*
+ * The device does not support confidence value levels,
+ * so we will always have 100% for current activity and
+ * 0% for the others.
+ */
+ if (chan->channel2 == mma9553_activity_to_mod(activity))
+ *val = 100;
+ else
+ *val = 0;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_VELOCITY: /* m/h */
+ if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+ return -EINVAL;
+ powered_on =
+ mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev,
+ "No channels enabled\n");
+ return -EINVAL;
+ }
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_SPEED, &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ *val = tmp;
+ return IIO_VAL_INT;
+ case IIO_ENERGY: /* Cal or kcal */
+ powered_on =
+ mma9553_is_any_event_enabled(data, false, 0) ||
+ data->stepcnt_enabled;
+ if (!powered_on) {
+ dev_err(&data->client->dev,
+ "No channels enabled\n");
+ return -EINVAL;
+ }
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_status_word(data->client,
+ MMA9551_APPID_PEDOMETER,
+ MMA9553_REG_CALORIES,
+ &tmp);
+ mutex_unlock(&data->mutex);
+ if (ret < 0)
+ return ret;
+ *val = tmp;
+ return IIO_VAL_INT;
+ case IIO_ACCEL:
+ mutex_lock(&data->mutex);
+ ret = mma9551_read_accel_chan(data->client,
+ chan, val, val2);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_VELOCITY: /* m/h to m/s */
+ if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+ return -EINVAL;
+ *val = 0;
+ *val2 = 277; /* 0.000277 */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_ENERGY: /* Cal or kcal to J */
+ *val = 4184;
+ return IIO_VAL_INT;
+ case IIO_ACCEL:
+ return mma9551_read_accel_scale(val, val2);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_ENABLE:
+ *val = data->stepcnt_enabled;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBHEIGHT:
+ tmp = mma9553_get_bits(data->conf.height_weight,
+ MMA9553_MASK_CONF_HEIGHT);
+ *val = tmp / 100; /* cm to m */
+ *val2 = (tmp % 100) * 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBWEIGHT:
+ *val = mma9553_get_bits(data->conf.height_weight,
+ MMA9553_MASK_CONF_WEIGHT);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_DEBOUNCE_COUNT:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = mma9553_get_bits(data->conf.filter,
+ MMA9553_MASK_CONF_FILTSTEP);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_DEBOUNCE_TIME:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = mma9553_get_bits(data->conf.filter,
+ MMA9553_MASK_CONF_FILTTIME);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_VELOCITY:
+ if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+ return -EINVAL;
+ *val = mma9553_get_bits(data->conf.speed_step,
+ MMA9553_MASK_CONF_SPDPRD);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9553_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret, tmp;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_ENABLE:
+ if (data->stepcnt_enabled == !!val)
+ return 0;
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_power_state(data->client, val);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ data->stepcnt_enabled = val;
+ mutex_unlock(&data->mutex);
+ return 0;
+ case IIO_CHAN_INFO_CALIBHEIGHT:
+ /* m to cm */
+ tmp = val * 100 + val2 / 10000;
+ if (tmp < 0 || tmp > 255)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data,
+ MMA9553_REG_CONF_HEIGHT_WEIGHT,
+ &data->conf.height_weight,
+ tmp, MMA9553_MASK_CONF_HEIGHT);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_CHAN_INFO_CALIBWEIGHT:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data,
+ MMA9553_REG_CONF_HEIGHT_WEIGHT,
+ &data->conf.height_weight,
+ val, MMA9553_MASK_CONF_WEIGHT);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_CHAN_INFO_DEBOUNCE_COUNT:
+ switch (chan->type) {
+ case IIO_STEPS:
+ /*
+ * Set to 0 to disable step filtering. If the value
+ * specified is greater than 6, then 6 will be used.
+ */
+ if (val < 0)
+ return -EINVAL;
+ if (val > 6)
+ val = 6;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data, MMA9553_REG_CONF_FILTER,
+ &data->conf.filter, val,
+ MMA9553_MASK_CONF_FILTSTEP);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_DEBOUNCE_TIME:
+ switch (chan->type) {
+ case IIO_STEPS:
+ if (val < 0 || val > 127)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data, MMA9553_REG_CONF_FILTER,
+ &data->conf.filter, val,
+ MMA9553_MASK_CONF_FILTTIME);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_VELOCITY:
+ if (chan->channel2 != IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z)
+ return -EINVAL;
+ /*
+ * If set to a value greater than 5, then 5 will be
+ * used. Warning: Do not set SPDPRD to 0 or 1 as
+ * this may cause undesirable behavior.
+ */
+ if (val < 2)
+ return -EINVAL;
+ if (val > 5)
+ val = 5;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data,
+ MMA9553_REG_CONF_SPEED_STEP,
+ &data->conf.speed_step, val,
+ MMA9553_MASK_CONF_SPDPRD);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+ struct mma9553_event *event;
+
+ event = mma9553_get_event(data, chan->type, chan->channel2, dir);
+ if (!event)
+ return -EINVAL;
+
+ return event->enabled;
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+ struct mma9553_event *event;
+ int ret;
+
+ event = mma9553_get_event(data, chan->type, chan->channel2, dir);
+ if (!event)
+ return -EINVAL;
+
+ if (event->enabled == state)
+ return 0;
+
+ mutex_lock(&data->mutex);
+
+ ret = mma9551_set_power_state(data->client, state);
+ if (ret < 0)
+ goto err_out;
+ event->enabled = state;
+
+ ret = mma9553_conf_gpio(data);
+ if (ret < 0)
+ goto err_conf_gpio;
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+
+err_conf_gpio:
+ if (state) {
+ event->enabled = false;
+ mma9551_set_power_state(data->client, false);
+ }
+err_out:
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+
+ *val2 = 0;
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (chan->type) {
+ case IIO_STEPS:
+ *val = mma9553_get_bits(data->conf.speed_step,
+ MMA9553_MASK_CONF_STEPCOALESCE);
+ return IIO_VAL_INT;
+ case IIO_ACTIVITY:
+ /*
+ * The device does not support confidence value levels.
+ * We set an average of 50%.
+ */
+ *val = 50;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_PERIOD:
+ switch (chan->type) {
+ case IIO_ACTIVITY:
+ *val = MMA9553_ACTIVITY_THD_TO_SEC(data->conf.actthd);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9553_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 mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (chan->type) {
+ case IIO_STEPS:
+ if (val < 0 || val > 255)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data,
+ MMA9553_REG_CONF_SPEED_STEP,
+ &data->conf.speed_step, val,
+ MMA9553_MASK_CONF_STEPCOALESCE);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ case IIO_EV_INFO_PERIOD:
+ switch (chan->type) {
+ case IIO_ACTIVITY:
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data, MMA9553_REG_CONF_ACTTHD,
+ &data->conf.actthd,
+ MMA9553_ACTIVITY_SEC_TO_THD
+ (val), MMA9553_MASK_CONF_WORD);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mma9553_get_calibgender_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mma9553_data *data = iio_priv(indio_dev);
+ u8 gender;
+
+ gender = mma9553_get_bits(data->conf.filter, MMA9553_MASK_CONF_MALE);
+ /*
+ * HW expects 0 for female and 1 for male,
+ * while iio index is 0 for male and 1 for female
+ */
+ return !gender;
+}
+
+static int mma9553_set_calibgender_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct mma9553_data *data = iio_priv(indio_dev);
+ u8 gender = !mode;
+ int ret;
+
+ if ((mode != 0) && (mode != 1))
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ ret = mma9553_set_config(data, MMA9553_REG_CONF_FILTER,
+ &data->conf.filter, gender,
+ MMA9553_MASK_CONF_MALE);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct iio_event_spec mma9553_step_event = {
+ .type = IIO_EV_TYPE_CHANGE,
+ .dir = IIO_EV_DIR_NONE,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE),
+};
+
+static const struct iio_event_spec mma9553_activity_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_PERIOD),
+ },
+};
+
+static const char * const calibgender_modes[] = { "male", "female" };
+
+static const struct iio_enum mma9553_calibgender_enum = {
+ .items = calibgender_modes,
+ .num_items = ARRAY_SIZE(calibgender_modes),
+ .get = mma9553_get_calibgender_mode,
+ .set = mma9553_set_calibgender_mode,
+};
+
+static const struct iio_chan_spec_ext_info mma9553_ext_info[] = {
+ IIO_ENUM("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum),
+ IIO_ENUM_AVAILABLE("calibgender", &mma9553_calibgender_enum),
+ {},
+};
+
+#define MMA9553_PEDOMETER_CHANNEL(_type, _mask) { \
+ .type = _type, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_ENABLE) | \
+ BIT(IIO_CHAN_INFO_CALIBHEIGHT) | \
+ _mask, \
+ .ext_info = mma9553_ext_info, \
+}
+
+#define MMA9553_ACTIVITY_CHANNEL(_chan2) { \
+ .type = IIO_ACTIVITY, \
+ .modified = 1, \
+ .channel2 = _chan2, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT), \
+ .event_spec = mma9553_activity_events, \
+ .num_event_specs = ARRAY_SIZE(mma9553_activity_events), \
+ .ext_info = mma9553_ext_info, \
+}
+
+static const struct iio_chan_spec mma9553_channels[] = {
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_X),
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_Y),
+ MMA9551_ACCEL_CHANNEL(IIO_MOD_Z),
+
+ {
+ .type = IIO_STEPS,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_ENABLE) |
+ BIT(IIO_CHAN_INFO_DEBOUNCE_COUNT) |
+ BIT(IIO_CHAN_INFO_DEBOUNCE_TIME),
+ .event_spec = &mma9553_step_event,
+ .num_event_specs = 1,
+ },
+
+ MMA9553_PEDOMETER_CHANNEL(IIO_DISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)),
+ {
+ .type = IIO_VELOCITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_ENABLE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBHEIGHT),
+ .ext_info = mma9553_ext_info,
+ },
+ MMA9553_PEDOMETER_CHANNEL(IIO_ENERGY, BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_CALIBWEIGHT)),
+
+ MMA9553_ACTIVITY_CHANNEL(IIO_MOD_RUNNING),
+ MMA9553_ACTIVITY_CHANNEL(IIO_MOD_JOGGING),
+ MMA9553_ACTIVITY_CHANNEL(IIO_MOD_WALKING),
+ MMA9553_ACTIVITY_CHANNEL(IIO_MOD_STILL),
+};
+
+static const struct iio_info mma9553_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mma9553_read_raw,
+ .write_raw = mma9553_write_raw,
+ .read_event_config = mma9553_read_event_config,
+ .write_event_config = mma9553_write_event_config,
+ .read_event_value = mma9553_read_event_value,
+ .write_event_value = mma9553_write_event_value,
+};
+
+static irqreturn_t mma9553_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct mma9553_data *data = iio_priv(indio_dev);
+
+ data->timestamp = iio_get_time_ns();
+ /*
+ * Since we only configure the interrupt pin when an
+ * event is enabled, we are sure we have at least
+ * one event enabled at this point.
+ */
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mma9553_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct mma9553_data *data = iio_priv(indio_dev);
+ u16 stepcnt;
+ u8 activity;
+ struct mma9553_event *ev_activity, *ev_prev_activity, *ev_step_detect;
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9553_read_activity_stepcnt(data, &activity, &stepcnt);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return IRQ_HANDLED;
+ }
+
+ ev_prev_activity =
+ mma9553_get_event(data, IIO_ACTIVITY,
+ mma9553_activity_to_mod(data->activity),
+ IIO_EV_DIR_FALLING);
+ ev_activity =
+ mma9553_get_event(data, IIO_ACTIVITY,
+ mma9553_activity_to_mod(activity),
+ IIO_EV_DIR_RISING);
+ ev_step_detect =
+ mma9553_get_event(data, IIO_STEPS, IIO_NO_MOD, IIO_EV_DIR_NONE);
+
+ if (ev_step_detect->enabled && (stepcnt != data->stepcnt)) {
+ data->stepcnt = stepcnt;
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
+ IIO_EV_DIR_NONE, IIO_EV_TYPE_CHANGE, 0, 0, 0),
+ data->timestamp);
+ }
+
+ if (activity != data->activity) {
+ data->activity = activity;
+ /* ev_activity can be NULL if activity == ACTIVITY_UNKNOWN */
+ if (ev_prev_activity && ev_prev_activity->enabled)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+ ev_prev_activity->info->mod,
+ IIO_EV_DIR_FALLING,
+ IIO_EV_TYPE_THRESH, 0, 0, 0),
+ data->timestamp);
+
+ if (ev_activity && ev_activity->enabled)
+ iio_push_event(indio_dev,
+ IIO_EVENT_CODE(IIO_ACTIVITY, 0,
+ ev_activity->info->mod,
+ IIO_EV_DIR_RISING,
+ IIO_EV_TYPE_THRESH, 0, 0, 0),
+ data->timestamp);
+ }
+ mutex_unlock(&data->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int mma9553_gpio_probe(struct i2c_client *client)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* data ready gpio interrupt pin */
+ gpio = devm_gpiod_get_index(dev, MMA9553_GPIO_NAME, 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "acpi gpio get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "gpio resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static const char *mma9553_match_acpi_device(struct device *dev)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+
+ return dev_name(dev);
+}
+
+static int mma9553_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mma9553_data *data;
+ struct iio_dev *indio_dev;
+ const char *name = NULL;
+ 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;
+
+ if (id)
+ name = id->name;
+ else if (ACPI_HANDLE(&client->dev))
+ name = mma9553_match_acpi_device(&client->dev);
+ else
+ return -ENOSYS;
+
+ mutex_init(&data->mutex);
+ mma9553_init_events(data);
+
+ ret = mma9553_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = mma9553_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mma9553_channels);
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mma9553_info;
+
+ if (client->irq < 0)
+ client->irq = mma9553_gpio_probe(client);
+
+ if (client->irq >= 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ mma9553_irq_handler,
+ mma9553_event_handler,
+ IRQF_TRIGGER_RISING,
+ MMA9553_IRQ_NAME, indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "request irq %d failed\n",
+ client->irq);
+ goto out_poweroff;
+ }
+
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "unable to register iio device\n");
+ goto out_poweroff;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto out_iio_unregister;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev,
+ MMA9551_AUTO_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ dev_dbg(&indio_dev->dev, "Registered device %s\n", name);
+
+ return 0;
+
+out_iio_unregister:
+ iio_device_unregister(indio_dev);
+out_poweroff:
+ mma9551_set_device_state(client, false);
+ return ret;
+}
+
+static int mma9553_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mma9553_data *data = iio_priv(indio_dev);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_lock(&data->mutex);
+ mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mma9553_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "powering off device failed\n");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static int mma9553_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = mma9551_set_device_state(data->client, true);
+ if (ret < 0)
+ return ret;
+
+ mma9551_sleep(MMA9553_DEFAULT_SAMPLE_RATE);
+
+ return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int mma9553_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, false);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int mma9553_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct mma9553_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+ ret = mma9551_set_device_state(data->client, true);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+#endif
+
+static const struct dev_pm_ops mma9553_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mma9553_suspend, mma9553_resume)
+ SET_RUNTIME_PM_OPS(mma9553_runtime_suspend,
+ mma9553_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id mma9553_acpi_match[] = {
+ {"MMA9553", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, mma9553_acpi_match);
+
+static const struct i2c_device_id mma9553_id[] = {
+ {"mma9553", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, mma9553_id);
+
+static struct i2c_driver mma9553_driver = {
+ .driver = {
+ .name = MMA9553_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(mma9553_acpi_match),
+ .pm = &mma9553_pm_ops,
+ },
+ .probe = mma9553_probe,
+ .remove = mma9553_remove,
+ .id_table = mma9553_id,
+};
+
+module_i2c_driver(mma9553_driver);
+
+MODULE_AUTHOR("Irina Tirdea <irina.tirdea@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MMA9553L pedometer platform driver");
diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
new file mode 100644
index 0000000..4ae05fc
--- /dev/null
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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/iio/common/ssp_sensors.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "../common/ssp_sensors/ssp_iio_sensor.h"
+
+#define SSP_CHANNEL_COUNT 3
+
+#define SSP_ACCEL_NAME "ssp-accelerometer"
+static const char ssp_accel_device_name[] = SSP_ACCEL_NAME;
+
+enum ssp_accel_3d_channel {
+ SSP_CHANNEL_SCAN_INDEX_X,
+ SSP_CHANNEL_SCAN_INDEX_Y,
+ SSP_CHANNEL_SCAN_INDEX_Z,
+ SSP_CHANNEL_SCAN_INDEX_TIME,
+};
+
+static int ssp_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ u32 t;
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ t = ssp_get_sensor_delay(data, SSP_ACCELEROMETER_SENSOR);
+ ssp_convert_to_freq(t, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ssp_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ int ret;
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ssp_convert_to_time(val, val2);
+ ret = ssp_change_delay(data, SSP_ACCELEROMETER_SENSOR, ret);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "accel sensor enable fail\n");
+
+ return ret;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static struct iio_info ssp_accel_iio_info = {
+ .read_raw = &ssp_accel_read_raw,
+ .write_raw = &ssp_accel_write_raw,
+};
+
+static const unsigned long ssp_accel_scan_mask[] = { 0x7, 0, };
+
+static const struct iio_chan_spec ssp_acc_channels[] = {
+ SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
+ SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
+ SSP_CHANNEL_AG(IIO_ACCEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
+ SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
+};
+
+static int ssp_process_accel_data(struct iio_dev *indio_dev, void *buf,
+ int64_t timestamp)
+{
+ return ssp_common_process_data(indio_dev, buf, SSP_ACCELEROMETER_SIZE,
+ timestamp);
+}
+
+static const struct iio_buffer_setup_ops ssp_accel_buffer_ops = {
+ .postenable = &ssp_common_buffer_postenable,
+ .postdisable = &ssp_common_buffer_postdisable,
+};
+
+static int ssp_accel_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct ssp_sensor_data *spd;
+ struct iio_buffer *buffer;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ spd = iio_priv(indio_dev);
+
+ spd->process_data = ssp_process_accel_data;
+ spd->type = SSP_ACCELEROMETER_SENSOR;
+
+ indio_dev->name = ssp_accel_device_name;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &ssp_accel_iio_info;
+ indio_dev->modes = INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = ssp_acc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels);
+ indio_dev->available_scan_masks = ssp_accel_scan_mask;
+
+ buffer = devm_iio_kfifo_allocate(&pdev->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->setup_ops = &ssp_accel_buffer_ops;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /* ssp registering should be done after all iio setup */
+ ssp_register_consumer(indio_dev, SSP_ACCELEROMETER_SENSOR);
+
+ return 0;
+}
+
+static int ssp_accel_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver ssp_accel_driver = {
+ .driver = {
+ .name = SSP_ACCEL_NAME,
+ },
+ .probe = ssp_accel_probe,
+ .remove = ssp_accel_remove,
+};
+
+module_platform_driver(ssp_accel_driver);
+
+MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
+MODULE_DESCRIPTION("Samsung sensorhub accelerometers driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index c387763..7ee9724 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -14,6 +14,7 @@
#include <linux/types.h>
#include <linux/iio/common/st_sensors.h>
+#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel"
#define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel"
#define LIS3DH_ACCEL_DEV_NAME "lis3dh"
#define LSM330D_ACCEL_DEV_NAME "lsm330d_accel"
@@ -33,8 +34,7 @@ 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);
+int st_accel_common_probe(struct iio_dev *indio_dev);
void st_accel_common_remove(struct iio_dev *indio_dev);
#ifdef CONFIG_IIO_BUFFER
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 0878648..58d1d13 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -129,6 +129,30 @@
#define ST_ACCEL_3_IG1_EN_MASK 0x08
#define ST_ACCEL_3_MULTIREAD_BIT false
+/* CUSTOM VALUES FOR SENSOR 4 */
+#define ST_ACCEL_4_WAI_EXP 0x3a
+#define ST_ACCEL_4_ODR_ADDR 0x20
+#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */
+#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00
+#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01
+#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02
+#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03
+#define ST_ACCEL_4_PW_ADDR 0x20
+#define ST_ACCEL_4_PW_MASK 0xc0
+#define ST_ACCEL_4_FS_ADDR 0x21
+#define ST_ACCEL_4_FS_MASK 0x80
+#define ST_ACCEL_4_FS_AVL_2_VAL 0X00
+#define ST_ACCEL_4_FS_AVL_6_VAL 0X01
+#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024)
+#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340)
+#define ST_ACCEL_4_BDU_ADDR 0x21
+#define ST_ACCEL_4_BDU_MASK 0x40
+#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21
+#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04
+#define ST_ACCEL_4_IG1_EN_ADDR 0x21
+#define ST_ACCEL_4_IG1_EN_MASK 0x08
+#define ST_ACCEL_4_MULTIREAD_BIT true
+
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),
@@ -161,7 +185,7 @@ static const struct iio_chan_spec st_accel_16bit_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3)
};
-static const struct st_sensors st_accel_sensors[] = {
+static const struct st_sensor_settings st_accel_sensors_settings[] = {
{
.wai = ST_ACCEL_1_WAI_EXP,
.sensors_supported = {
@@ -373,6 +397,63 @@ static const struct st_sensors st_accel_sensors[] = {
.multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT,
.bootime = 2,
},
+ {
+ .wai = ST_ACCEL_4_WAI_EXP,
+ .sensors_supported = {
+ [0] = LIS3LV02DL_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_12bit_channels,
+ .odr = {
+ .addr = ST_ACCEL_4_ODR_ADDR,
+ .mask = ST_ACCEL_4_ODR_MASK,
+ .odr_avl = {
+ { 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL },
+ { 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, },
+ { 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, },
+ { 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_ACCEL_4_PW_ADDR,
+ .mask = ST_ACCEL_4_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_4_FS_ADDR,
+ .mask = ST_ACCEL_4_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = ST_ACCEL_4_FS_AVL_2_VAL,
+ .gain = ST_ACCEL_4_FS_AVL_2_GAIN,
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_6G,
+ .value = ST_ACCEL_4_FS_AVL_6_VAL,
+ .gain = ST_ACCEL_4_FS_AVL_6_GAIN,
+ },
+ },
+ },
+ .bdu = {
+ .addr = ST_ACCEL_4_BDU_ADDR,
+ .mask = ST_ACCEL_4_BDU_MASK,
+ },
+ .drdy_irq = {
+ .addr = ST_ACCEL_4_DRDY_IRQ_ADDR,
+ .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK,
+ .ig1 = {
+ .en_addr = ST_ACCEL_4_IG1_EN_ADDR,
+ .en_mask = ST_ACCEL_4_IG1_EN_MASK,
+ },
+ },
+ .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT,
+ .bootime = 2, /* guess */
+ },
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
@@ -457,8 +538,7 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {
#define ST_ACCEL_TRIGGER_OPS NULL
#endif
-int st_accel_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *plat_data)
+int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
int irq = adata->get_irq_data_ready(indio_dev);
@@ -470,24 +550,25 @@ int st_accel_common_probe(struct iio_dev *indio_dev,
st_sensors_power_enable(indio_dev);
err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_accel_sensors), st_accel_sensors);
+ ARRAY_SIZE(st_accel_sensors_settings),
+ st_accel_sensors_settings);
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;
+ adata->multiread_bit = adata->sensor_settings->multi_read_bit;
+ indio_dev->channels = adata->sensor_settings->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;
+ &adata->sensor_settings->fs.fs_avl[0];
+ adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
- if (!plat_data)
- plat_data =
+ if (!adata->dev->platform_data)
+ adata->dev->platform_data =
(struct st_sensors_platform_data *)&default_accel_pdata;
- err = st_sensors_init_sensor(indio_dev, plat_data);
+ err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data);
if (err < 0)
return err;
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 7164aef..6b720c1 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -21,6 +21,10 @@
#ifdef CONFIG_OF
static const struct of_device_id st_accel_of_match[] = {
{
+ .compatible = "st,lis3lv02dl-accel",
+ .data = LIS3LV02DL_ACCEL_DEV_NAME,
+ },
+ {
.compatible = "st,lsm303dlh-accel",
.data = LSM303DLH_ACCEL_DEV_NAME,
},
@@ -79,12 +83,11 @@ static int st_accel_i2c_probe(struct i2c_client *client,
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);
+ err = st_accel_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index 1956396..12ec293 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -29,11 +29,10 @@ static int st_accel_spi_probe(struct spi_device *spi)
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);
+ err = st_accel_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 88bdc8f..e36a73e 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -127,6 +127,35 @@ config AT91_ADC
help
Say yes here to build support for Atmel AT91 ADC.
+config AXP288_ADC
+ tristate "X-Powers AXP288 ADC driver"
+ depends on MFD_AXP20X
+ help
+ Say yes here to have support for X-Powers power management IC (PMIC) ADC
+ device. Depending on platform configuration, this general purpose ADC can
+ be used for sampling sensors such as thermal resistors.
+
+config DA9150_GPADC
+ tristate "Dialog DA9150 GPADC driver support"
+ depends on MFD_DA9150
+ help
+ Say yes here to build support for Dialog DA9150 GPADC.
+
+ This driver can also be built as a module. If chosen, the module name
+ will be da9150-gpadc.
+
+config CC10001_ADC
+ tristate "Cosmic Circuits 10001 ADC driver"
+ depends on HAVE_CLK || REGULATOR
+ depends on HAS_IOMEM
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Cosmic Circuits 10001 ADC.
+
+ This driver can also be built as a module. If so, the module will be
+ called cc10001_adc.
+
config EXYNOS_ADC
tristate "Exynos ADC driver support"
depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
@@ -167,10 +196,11 @@ config MAX1363
data via the iio dev interface.
config MCP320X
- tristate "Microchip Technology MCP3204/08"
+ tristate "Microchip Technology MCP3x01/02/04/08"
depends on SPI
help
- Say yes here to build support for Microchip Technology's MCP3204 or
+ Say yes here to build support for Microchip Technology's
+ MCP3001, MCP3002, MCP3004, MCP3008, MCP3201, MCP3202, MCP3204 or
MCP3208 analog to digital converter.
This driver can also be built as a module. If so, the module will be
@@ -206,6 +236,34 @@ config NAU7802
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config QCOM_SPMI_IADC
+ tristate "Qualcomm SPMI PMIC current ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the IIO Current ADC driver for Qualcomm QPNP IADC Chip.
+
+ The driver supports single mode operation to read from one of two
+ channels (external or internal). Hardware have additional
+ channels internally used for gain and offset calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-iadc.
+
+config QCOM_SPMI_VADC
+ tristate "Qualcomm SPMI PMIC voltage ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ This is the IIO Voltage ADC driver for Qualcomm QPNP VADC Chip.
+
+ The driver supports multiple channels read. The VADC is a 15-bit
+ sigma-delta ADC. Some of the channels are internally used for
+ calibration.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-vadc.
+
config ROCKCHIP_SARADC
tristate "Rockchip SARADC driver"
depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index cb88a6a..3930e63 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -14,6 +14,9 @@ obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
+obj-$(CONFIG_DA9150_GPADC) += da9150-gpadc.o
+obj-$(CONFIG_CC10001_ADC) += cc10001_adc.o
obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_MAX1027) += max1027.o
@@ -22,6 +25,8 @@ 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_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
+obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 4dddeab..b84922a 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -861,5 +861,5 @@ static struct spi_driver ad7793_driver = {
module_spi_driver(ad7793_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
+MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c
index e37412d..b99de00 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -143,9 +143,15 @@ static int ad799x_write_config(struct ad799x_state *st, u16 val)
case ad7998:
return i2c_smbus_write_word_swapped(st->client, AD7998_CONF_REG,
val);
- default:
+ 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;
}
}
@@ -155,8 +161,13 @@ static int ad799x_read_config(struct ad799x_state *st)
case ad7997:
case ad7998:
return i2c_smbus_read_word_swapped(st->client, AD7998_CONF_REG);
- default:
+ case ad7992:
+ case ad7993:
+ case ad7994:
return i2c_smbus_read_byte_data(st->client, AD7998_CONF_REG);
+ default:
+ /* No readback support */
+ return st->config;
}
}
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index ff61ae5..8a0eb4a 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -544,7 +544,6 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
{
struct iio_dev *idev = iio_trigger_get_drvdata(trig);
struct at91_adc_state *st = iio_priv(idev);
- struct iio_buffer *buffer = idev->buffer;
struct at91_adc_reg_desc *reg = st->registers;
u32 status = at91_adc_readl(st, reg->trigger_register);
int value;
@@ -564,7 +563,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
at91_adc_writel(st, reg->trigger_register,
status | value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHER,
@@ -579,7 +578,7 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
at91_adc_writel(st, reg->trigger_register,
status & ~value);
- for_each_set_bit(bit, buffer->scan_mask,
+ for_each_set_bit(bit, idev->active_scan_mask,
st->num_channels) {
struct iio_chan_spec const *chan = idev->channels + bit;
at91_adc_writel(st, AT91_ADC_CHDR,
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
new file mode 100644
index 0000000..08bcfb0
--- /dev/null
+++ b/drivers/iio/adc/axp288_adc.c
@@ -0,0 +1,261 @@
+/*
+ * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
+ *
+ * Copyright (C) 2014 Intel Corporation
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * 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.
+ *
+ * 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/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/platform_device.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+
+#define AXP288_ADC_EN_MASK 0xF1
+#define AXP288_ADC_TS_PIN_GPADC 0xF2
+#define AXP288_ADC_TS_PIN_ON 0xF3
+
+enum axp288_adc_id {
+ AXP288_ADC_TS,
+ AXP288_ADC_PMIC,
+ AXP288_ADC_GP,
+ AXP288_ADC_BATT_CHRG_I,
+ AXP288_ADC_BATT_DISCHRG_I,
+ AXP288_ADC_BATT_V,
+ AXP288_ADC_NR_CHAN,
+};
+
+struct axp288_adc_info {
+ int irq;
+ struct regmap *regmap;
+};
+
+static const struct iio_chan_spec const axp288_adc_channels[] = {
+ {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 0,
+ .address = AXP288_TS_ADC_H,
+ .datasheet_name = "TS_PIN",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 1,
+ .address = AXP288_PMIC_ADC_H,
+ .datasheet_name = "PMIC_TEMP",
+ }, {
+ .indexed = 1,
+ .type = IIO_TEMP,
+ .channel = 2,
+ .address = AXP288_GP_ADC_H,
+ .datasheet_name = "GPADC",
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 3,
+ .address = AXP20X_BATT_CHRG_I_H,
+ .datasheet_name = "BATT_CHG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_CURRENT,
+ .channel = 4,
+ .address = AXP20X_BATT_DISCHRG_I_H,
+ .datasheet_name = "BATT_DISCHRG_I",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ }, {
+ .indexed = 1,
+ .type = IIO_VOLTAGE,
+ .channel = 5,
+ .address = AXP20X_BATT_V_H,
+ .datasheet_name = "BATT_V",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \
+ _consumer_channel) \
+ { \
+ .adc_channel_label = _adc_channel_label, \
+ .consumer_dev_name = _consumer_dev_name, \
+ .consumer_channel = _consumer_channel, \
+ }
+
+/* for consumer drivers */
+static struct iio_map axp288_adc_default_maps[] = {
+ AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"),
+ AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"),
+ AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"),
+ AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"),
+ AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"),
+ AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"),
+ {},
+};
+
+static int axp288_adc_read_channel(int *val, unsigned long address,
+ struct regmap *regmap)
+{
+ u8 buf[2];
+
+ if (regmap_bulk_read(regmap, address, buf, 2))
+ return -EIO;
+ *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F);
+
+ return IIO_VAL_INT;
+}
+
+static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
+ unsigned long address)
+{
+ /* channels other than GPADC do not need to switch TS pin */
+ if (address != AXP288_GP_ADC_H)
+ return 0;
+
+ return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+}
+
+static int axp288_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct axp288_adc_info *info = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+ chan->address)) {
+ dev_err(&indio_dev->dev, "GPADC mode\n");
+ ret = -EINVAL;
+ break;
+ }
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+ chan->address))
+ dev_err(&indio_dev->dev, "TS pin restore\n");
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = axp288_adc_read_channel(val, chan->address, info->regmap);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int axp288_adc_set_state(struct regmap *regmap)
+{
+ /* ADC should be always enabled for internal FG to function */
+ if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
+ return -EIO;
+
+ return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+}
+
+static const struct iio_info axp288_adc_iio_info = {
+ .read_raw = &axp288_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int axp288_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct axp288_adc_info *info;
+ struct iio_dev *indio_dev;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+ info->irq = platform_get_irq(pdev, 0);
+ if (info->irq < 0) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return info->irq;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+ info->regmap = axp20x->regmap;
+ /*
+ * Set ADC to enabled state at all time, including system suspend.
+ * otherwise internal fuel gauge functionality may be affected.
+ */
+ ret = axp288_adc_set_state(axp20x->regmap);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to enable ADC device\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->channels = axp288_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels);
+ indio_dev->info = &axp288_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ ret = iio_map_array_register(indio_dev, axp288_adc_default_maps);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "unable to register iio device\n");
+ goto err_array_unregister;
+ }
+ return 0;
+
+err_array_unregister:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int axp288_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_device_id axp288_adc_id_table[] = {
+ { .name = "axp288_adc" },
+ {},
+};
+
+static struct platform_driver axp288_adc_driver = {
+ .probe = axp288_adc_probe,
+ .remove = axp288_adc_remove,
+ .id_table = axp288_adc_id_table,
+ .driver = {
+ .name = "axp288_adc",
+ },
+};
+
+MODULE_DEVICE_TABLE(platform, axp288_adc_id_table);
+
+module_platform_driver(axp288_adc_driver);
+
+MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>");
+MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
new file mode 100644
index 0000000..51e2a83
--- /dev/null
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2014-2015 Imagination Technologies Ltd.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* Registers */
+#define CC10001_ADC_CONFIG 0x00
+#define CC10001_ADC_START_CONV BIT(4)
+#define CC10001_ADC_MODE_SINGLE_CONV BIT(5)
+
+#define CC10001_ADC_DDATA_OUT 0x04
+#define CC10001_ADC_EOC 0x08
+#define CC10001_ADC_EOC_SET BIT(0)
+
+#define CC10001_ADC_CHSEL_SAMPLED 0x0c
+#define CC10001_ADC_POWER_UP 0x10
+#define CC10001_ADC_POWER_UP_SET BIT(0)
+#define CC10001_ADC_DEBUG 0x14
+#define CC10001_ADC_DATA_COUNT 0x20
+
+#define CC10001_ADC_DATA_MASK GENMASK(9, 0)
+#define CC10001_ADC_NUM_CHANNELS 8
+#define CC10001_ADC_CH_MASK GENMASK(2, 0)
+
+#define CC10001_INVALID_SAMPLED 0xffff
+#define CC10001_MAX_POLL_COUNT 20
+
+/*
+ * As per device specification, wait six clock cycles after power-up to
+ * activate START. Since adding two more clock cycles delay does not
+ * impact the performance too much, we are adding two additional cycles delay
+ * intentionally here.
+ */
+#define CC10001_WAIT_CYCLES 8
+
+struct cc10001_adc_device {
+ void __iomem *reg_base;
+ struct clk *adc_clk;
+ struct regulator *reg;
+ u16 *buf;
+
+ struct mutex lock;
+ unsigned long channel_map;
+ unsigned int start_delay_ns;
+ unsigned int eoc_delay_ns;
+};
+
+static inline void cc10001_adc_write_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg, u32 val)
+{
+ writel(val, adc_dev->reg_base + reg);
+}
+
+static inline u32 cc10001_adc_read_reg(struct cc10001_adc_device *adc_dev,
+ u32 reg)
+{
+ return readl(adc_dev->reg_base + reg);
+}
+
+static void cc10001_adc_start(struct cc10001_adc_device *adc_dev,
+ unsigned int channel)
+{
+ u32 val;
+
+ /* Channel selection and mode of operation */
+ val = (channel & CC10001_ADC_CH_MASK) | CC10001_ADC_MODE_SINGLE_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+
+ val = cc10001_adc_read_reg(adc_dev, CC10001_ADC_CONFIG);
+ val = val | CC10001_ADC_START_CONV;
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_CONFIG, val);
+}
+
+static u16 cc10001_adc_poll_done(struct iio_dev *indio_dev,
+ unsigned int channel,
+ unsigned int delay)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int poll_count = 0;
+
+ while (!(cc10001_adc_read_reg(adc_dev, CC10001_ADC_EOC) &
+ CC10001_ADC_EOC_SET)) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ poll_count = 0;
+ while ((cc10001_adc_read_reg(adc_dev, CC10001_ADC_CHSEL_SAMPLED) &
+ CC10001_ADC_CH_MASK) != channel) {
+
+ ndelay(delay);
+ if (poll_count++ == CC10001_MAX_POLL_COUNT)
+ return CC10001_INVALID_SAMPLED;
+ }
+
+ /* Read the 10 bit output register */
+ return cc10001_adc_read_reg(adc_dev, CC10001_ADC_DDATA_OUT) &
+ CC10001_ADC_DATA_MASK;
+}
+
+static irqreturn_t cc10001_adc_trigger_h(int irq, void *p)
+{
+ struct cc10001_adc_device *adc_dev;
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev;
+ unsigned int delay_ns;
+ unsigned int channel;
+ bool sample_invalid;
+ u16 *data;
+ int i;
+
+ indio_dev = pf->indio_dev;
+ adc_dev = iio_priv(indio_dev);
+ data = adc_dev->buf;
+
+ mutex_lock(&adc_dev->lock);
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
+ CC10001_ADC_POWER_UP_SET);
+
+ /* Wait for 8 (6+2) clock cycles before activating START */
+ ndelay(adc_dev->start_delay_ns);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ i = 0;
+ sample_invalid = false;
+ for_each_set_bit(channel, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+
+ cc10001_adc_start(adc_dev, channel);
+
+ data[i] = cc10001_adc_poll_done(indio_dev, channel, delay_ns);
+ if (data[i] == CC10001_INVALID_SAMPLED) {
+ dev_warn(&indio_dev->dev,
+ "invalid sample on channel %d\n", channel);
+ sample_invalid = true;
+ goto done;
+ }
+ i++;
+ }
+
+done:
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+
+ mutex_unlock(&adc_dev->lock);
+
+ if (!sample_invalid)
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns());
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static u16 cc10001_adc_read_raw_voltage(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ unsigned int delay_ns;
+ u16 val;
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP,
+ CC10001_ADC_POWER_UP_SET);
+
+ /* Wait for 8 (6+2) clock cycles before activating START */
+ ndelay(adc_dev->start_delay_ns);
+
+ /* Calculate delay step for eoc and sampled data */
+ delay_ns = adc_dev->eoc_delay_ns / CC10001_MAX_POLL_COUNT;
+
+ cc10001_adc_start(adc_dev, chan->channel);
+
+ val = cc10001_adc_poll_done(indio_dev, chan->channel, delay_ns);
+
+ cc10001_adc_write_reg(adc_dev, CC10001_ADC_POWER_UP, 0);
+
+ return val;
+}
+
+static int cc10001_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&adc_dev->lock);
+ *val = cc10001_adc_read_raw_voltage(indio_dev, chan);
+ mutex_unlock(&adc_dev->lock);
+
+ if (*val == CC10001_INVALID_SAMPLED)
+ return -EIO;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc_dev->reg);
+ if (ret)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cc10001_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ kfree(adc_dev->buf);
+ adc_dev->buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (!adc_dev->buf)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static const struct iio_info cc10001_adc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cc10001_adc_read_raw,
+ .update_scan_mode = &cc10001_update_scan_mode,
+};
+
+static int cc10001_adc_channel_init(struct iio_dev *indio_dev)
+{
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+ struct iio_chan_spec *chan_array, *timestamp;
+ unsigned int bit, idx = 0;
+
+ indio_dev->num_channels = bitmap_weight(&adc_dev->channel_map,
+ CC10001_ADC_NUM_CHANNELS);
+
+ chan_array = devm_kcalloc(&indio_dev->dev, indio_dev->num_channels + 1,
+ sizeof(struct iio_chan_spec),
+ GFP_KERNEL);
+ if (!chan_array)
+ return -ENOMEM;
+
+ for_each_set_bit(bit, &adc_dev->channel_map, CC10001_ADC_NUM_CHANNELS) {
+ struct iio_chan_spec *chan = &chan_array[idx];
+
+ chan->type = IIO_VOLTAGE;
+ chan->indexed = 1;
+ chan->channel = bit;
+ chan->scan_index = idx;
+ chan->scan_type.sign = 'u';
+ chan->scan_type.realbits = 10;
+ chan->scan_type.storagebits = 16;
+ chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ idx++;
+ }
+
+ timestamp = &chan_array[idx];
+ timestamp->type = IIO_TIMESTAMP;
+ timestamp->channel = -1;
+ timestamp->scan_index = idx;
+ timestamp->scan_type.sign = 's';
+ timestamp->scan_type.realbits = 64;
+ timestamp->scan_type.storagebits = 64;
+
+ indio_dev->channels = chan_array;
+
+ return 0;
+}
+
+static int cc10001_adc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct cc10001_adc_device *adc_dev;
+ unsigned long adc_clk_rate;
+ struct resource *res;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ adc_dev = iio_priv(indio_dev);
+
+ adc_dev->channel_map = GENMASK(CC10001_ADC_NUM_CHANNELS - 1, 0);
+ if (!of_property_read_u32(node, "adc-reserved-channels", &ret))
+ adc_dev->channel_map &= ~ret;
+
+ adc_dev->reg = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(adc_dev->reg))
+ return PTR_ERR(adc_dev->reg);
+
+ ret = regulator_enable(adc_dev->reg);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &cc10001_adc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(adc_dev->reg_base)) {
+ ret = PTR_ERR(adc_dev->reg_base);
+ goto err_disable_reg;
+ }
+
+ adc_dev->adc_clk = devm_clk_get(&pdev->dev, "adc");
+ if (IS_ERR(adc_dev->adc_clk)) {
+ dev_err(&pdev->dev, "failed to get the clock\n");
+ ret = PTR_ERR(adc_dev->adc_clk);
+ goto err_disable_reg;
+ }
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable the clock\n");
+ goto err_disable_reg;
+ }
+
+ adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+ if (!adc_clk_rate) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "null clock rate!\n");
+ goto err_disable_clk;
+ }
+
+ adc_dev->eoc_delay_ns = NSEC_PER_SEC / adc_clk_rate;
+ adc_dev->start_delay_ns = adc_dev->eoc_delay_ns * CC10001_WAIT_CYCLES;
+
+ /* Setup the ADC channels available on the device */
+ ret = cc10001_adc_channel_init(indio_dev);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ mutex_init(&adc_dev->lock);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &cc10001_adc_trigger_h, NULL);
+ if (ret < 0)
+ goto err_disable_clk;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto err_cleanup_buffer;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ return 0;
+
+err_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_disable_clk:
+ clk_disable_unprepare(adc_dev->adc_clk);
+err_disable_reg:
+ regulator_disable(adc_dev->reg);
+ return ret;
+}
+
+static int cc10001_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct cc10001_adc_device *adc_dev = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ clk_disable_unprepare(adc_dev->adc_clk);
+ regulator_disable(adc_dev->reg);
+
+ return 0;
+}
+
+static const struct of_device_id cc10001_adc_dt_ids[] = {
+ { .compatible = "cosmic,10001-adc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, cc10001_adc_dt_ids);
+
+static struct platform_driver cc10001_adc_driver = {
+ .driver = {
+ .name = "cc10001-adc",
+ .of_match_table = cc10001_adc_dt_ids,
+ },
+ .probe = cc10001_adc_probe,
+ .remove = cc10001_adc_remove,
+};
+module_platform_driver(cc10001_adc_driver);
+
+MODULE_AUTHOR("Phani Movva <Phani.Movva@imgtec.com>");
+MODULE_DESCRIPTION("Cosmic Circuits ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
new file mode 100644
index 0000000..3445107
--- /dev/null
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -0,0 +1,407 @@
+/*
+ * DA9150 GPADC Driver
+ *
+ * Copyright (c) 2014 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/completion.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/machine.h>
+#include <linux/iio/driver.h>
+#include <linux/mfd/da9150/core.h>
+#include <linux/mfd/da9150/registers.h>
+
+/* Channels */
+enum da9150_gpadc_hw_channel {
+ DA9150_GPADC_HW_CHAN_GPIOA_2V = 0,
+ DA9150_GPADC_HW_CHAN_GPIOA_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V,
+ DA9150_GPADC_HW_CHAN_GPIOB_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V,
+ DA9150_GPADC_HW_CHAN_GPIOC_2V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V,
+ DA9150_GPADC_HW_CHAN_GPIOD_2V_,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE,
+ DA9150_GPADC_HW_CHAN_IBUS_SENSE_,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV,
+ DA9150_GPADC_HW_CHAN_VBUS_DIV_,
+ DA9150_GPADC_HW_CHAN_ID,
+ DA9150_GPADC_HW_CHAN_ID_,
+ DA9150_GPADC_HW_CHAN_VSYS,
+ DA9150_GPADC_HW_CHAN_VSYS_,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V,
+ DA9150_GPADC_HW_CHAN_GPIOA_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V,
+ DA9150_GPADC_HW_CHAN_GPIOB_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V,
+ DA9150_GPADC_HW_CHAN_GPIOC_6V_,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V,
+ DA9150_GPADC_HW_CHAN_GPIOD_6V_,
+ DA9150_GPADC_HW_CHAN_VBAT,
+ DA9150_GPADC_HW_CHAN_VBAT_,
+ DA9150_GPADC_HW_CHAN_TBAT,
+ DA9150_GPADC_HW_CHAN_TBAT_,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE,
+ DA9150_GPADC_HW_CHAN_TJUNC_CORE_,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP,
+ DA9150_GPADC_HW_CHAN_TJUNC_OVP_,
+};
+
+enum da9150_gpadc_channel {
+ DA9150_GPADC_CHAN_GPIOA = 0,
+ DA9150_GPADC_CHAN_GPIOB,
+ DA9150_GPADC_CHAN_GPIOC,
+ DA9150_GPADC_CHAN_GPIOD,
+ DA9150_GPADC_CHAN_IBUS,
+ DA9150_GPADC_CHAN_VBUS,
+ DA9150_GPADC_CHAN_VSYS,
+ DA9150_GPADC_CHAN_VBAT,
+ DA9150_GPADC_CHAN_TBAT,
+ DA9150_GPADC_CHAN_TJUNC_CORE,
+ DA9150_GPADC_CHAN_TJUNC_OVP,
+};
+
+/* Private data */
+struct da9150_gpadc {
+ struct da9150 *da9150;
+ struct device *dev;
+
+ struct mutex lock;
+ struct completion complete;
+};
+
+
+static irqreturn_t da9150_gpadc_irq(int irq, void *data)
+{
+
+ struct da9150_gpadc *gpadc = data;
+
+ complete(&gpadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int da9150_gpadc_read_adc(struct da9150_gpadc *gpadc, int hw_chan)
+{
+ u8 result_regs[2];
+ int result;
+
+ mutex_lock(&gpadc->lock);
+
+ /* Set channel & enable measurement */
+ da9150_reg_write(gpadc->da9150, DA9150_GPADC_MAN,
+ (DA9150_GPADC_EN_MASK |
+ hw_chan << DA9150_GPADC_MUX_SHIFT));
+
+ /* Consume left-over completion from a previous timeout */
+ try_wait_for_completion(&gpadc->complete);
+
+ /* Check for actual completion */
+ wait_for_completion_timeout(&gpadc->complete, msecs_to_jiffies(5));
+
+ /* Read result and status from device */
+ da9150_bulk_read(gpadc->da9150, DA9150_GPADC_RES_A, 2, result_regs);
+
+ mutex_unlock(&gpadc->lock);
+
+ /* Check to make sure device really has completed reading */
+ if (result_regs[1] & DA9150_GPADC_RUN_MASK) {
+ dev_err(gpadc->dev, "Timeout on channel %d of GPADC\n",
+ hw_chan);
+ return -ETIMEDOUT;
+ }
+
+ /* LSBs - 2 bits */
+ result = (result_regs[1] & DA9150_GPADC_RES_L_MASK) >>
+ DA9150_GPADC_RES_L_SHIFT;
+ /* MSBs - 8 bits */
+ result |= result_regs[0] << DA9150_GPADC_RES_L_BITS;
+
+ return result;
+}
+
+static inline int da9150_gpadc_gpio_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (6 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_ibus_current_avg(int raw_val)
+{
+ /* Convert to mA */
+ return (4 * ((raw_val * 1000) + 500)) / 2048;
+}
+
+static inline int da9150_gpadc_vbus_21v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (21 * ((raw_val * 1000) + 500)) / 1024;
+}
+
+static inline int da9150_gpadc_vsys_6v_voltage_now(int raw_val)
+{
+ /* Convert to mV */
+ return (3 * ((raw_val * 1000) + 500)) / 512;
+}
+
+static int da9150_gpadc_read_processed(struct da9150_gpadc *gpadc, int channel,
+ int hw_chan, int *val)
+{
+ int raw_val;
+
+ raw_val = da9150_gpadc_read_adc(gpadc, hw_chan);
+ if (raw_val < 0)
+ return raw_val;
+
+ switch (channel) {
+ case DA9150_GPADC_CHAN_GPIOA:
+ case DA9150_GPADC_CHAN_GPIOB:
+ case DA9150_GPADC_CHAN_GPIOC:
+ case DA9150_GPADC_CHAN_GPIOD:
+ *val = da9150_gpadc_gpio_6v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_IBUS:
+ *val = da9150_gpadc_ibus_current_avg(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VBUS:
+ *val = da9150_gpadc_vbus_21v_voltage_now(raw_val);
+ break;
+ case DA9150_GPADC_CHAN_VSYS:
+ *val = da9150_gpadc_vsys_6v_voltage_now(raw_val);
+ break;
+ default:
+ /* No processing for other channels so return raw value */
+ *val = raw_val;
+ break;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int da9150_gpadc_read_scale(int channel, int *val, int *val2)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 2932;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = 1000000;
+ *val2 = 4420;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_offset(int channel, int *val)
+{
+ switch (channel) {
+ case DA9150_GPADC_CHAN_VBAT:
+ *val = 1500000 / 2932;
+ return IIO_VAL_INT;
+ case DA9150_GPADC_CHAN_TJUNC_CORE:
+ case DA9150_GPADC_CHAN_TJUNC_OVP:
+ *val = -144;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int da9150_gpadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da9150_gpadc *gpadc = iio_priv(indio_dev);
+
+ if ((chan->channel < DA9150_GPADC_CHAN_GPIOA) ||
+ (chan->channel > DA9150_GPADC_CHAN_TJUNC_OVP))
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ return da9150_gpadc_read_processed(gpadc, chan->channel,
+ chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ return da9150_gpadc_read_scale(chan->channel, val, val2);
+ case IIO_CHAN_INFO_OFFSET:
+ return da9150_gpadc_read_offset(chan->channel, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info da9150_gpadc_info = {
+ .read_raw = &da9150_gpadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define DA9150_GPADC_CHANNEL(_id, _hw_id, _type, chan_info, \
+ _ext_name) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = DA9150_GPADC_CHAN_##_id, \
+ .address = DA9150_GPADC_HW_CHAN_##_hw_id, \
+ .info_mask_separate = chan_info, \
+ .extend_name = _ext_name, \
+ .datasheet_name = #_id, \
+}
+
+#define DA9150_GPADC_CHANNEL_RAW(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW), _ext_name)
+
+#define DA9150_GPADC_CHANNEL_SCALED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ _ext_name)
+
+#define DA9150_GPADC_CHANNEL_PROCESSED(_id, _hw_id, _type, _ext_name) \
+ DA9150_GPADC_CHANNEL(_id, _hw_id, _type, \
+ BIT(IIO_CHAN_INFO_PROCESSED), _ext_name)
+
+/* Supported channels */
+static const struct iio_chan_spec da9150_gpadc_channels[] = {
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOA, GPIOA_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOB, GPIOB_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOC, GPIOC_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(GPIOD, GPIOD_6V, IIO_VOLTAGE, NULL),
+ DA9150_GPADC_CHANNEL_PROCESSED(IBUS, IBUS_SENSE, IIO_CURRENT, "ibus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VBUS, VBUS_DIV_, IIO_VOLTAGE, "vbus"),
+ DA9150_GPADC_CHANNEL_PROCESSED(VSYS, VSYS, IIO_VOLTAGE, "vsys"),
+ DA9150_GPADC_CHANNEL_SCALED(VBAT, VBAT, IIO_VOLTAGE, "vbat"),
+ DA9150_GPADC_CHANNEL_RAW(TBAT, TBAT, IIO_VOLTAGE, "tbat"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_CORE, TJUNC_CORE, IIO_TEMP,
+ "tjunc_core"),
+ DA9150_GPADC_CHANNEL_SCALED(TJUNC_OVP, TJUNC_OVP, IIO_TEMP,
+ "tjunc_ovp"),
+};
+
+/* Default maps used by da9150-charger */
+static struct iio_map da9150_gpadc_default_maps[] = {
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_IBUS",
+ .adc_channel_label = "IBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBUS",
+ .adc_channel_label = "VBUS",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_TJUNC",
+ .adc_channel_label = "TJUNC_CORE",
+ },
+ {
+ .consumer_dev_name = "da9150-charger",
+ .consumer_channel = "CHAN_VBAT",
+ .adc_channel_label = "VBAT",
+ },
+ {},
+};
+
+static int da9150_gpadc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct da9150 *da9150 = dev_get_drvdata(dev->parent);
+ struct da9150_gpadc *gpadc;
+ struct iio_dev *indio_dev;
+ int irq, ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gpadc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+ gpadc = iio_priv(indio_dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+ gpadc->da9150 = da9150;
+ gpadc->dev = dev;
+ mutex_init(&gpadc->lock);
+ init_completion(&gpadc->complete);
+
+ irq = platform_get_irq_byname(pdev, "GPADC");
+ if (irq < 0) {
+ dev_err(dev, "Failed to get IRQ: %d\n", irq);
+ return irq;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
+ IRQF_ONESHOT, "GPADC", gpadc);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);
+ return ret;
+ }
+
+ ret = iio_map_array_register(indio_dev, da9150_gpadc_default_maps);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO maps: %d\n", ret);
+ return ret;
+ }
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &da9150_gpadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da9150_gpadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(da9150_gpadc_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device: %d\n", ret);
+ goto iio_map_unreg;
+ }
+
+ return 0;
+
+iio_map_unreg:
+ iio_map_array_unregister(indio_dev);
+
+ return ret;
+}
+
+static int da9150_gpadc_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 da9150_gpadc_driver = {
+ .driver = {
+ .name = "da9150-gpadc",
+ },
+ .probe = da9150_gpadc_probe,
+ .remove = da9150_gpadc_remove,
+};
+
+module_platform_driver(da9150_gpadc_driver);
+
+MODULE_DESCRIPTION("GPADC Driver for DA9150");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index 43620fd..3a2dbb3 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -39,6 +39,8 @@
#include <linux/iio/iio.h>
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
/* S3C/EXYNOS4412/5250 ADC_V1 registers definitions */
#define ADC_V1_CON(x) ((x) + 0x00)
@@ -90,11 +92,14 @@
#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100))
+#define EXYNOS_ADCV1_PHY_OFFSET 0x0718
+#define EXYNOS_ADCV2_PHY_OFFSET 0x0720
+
struct exynos_adc {
struct exynos_adc_data *data;
struct device *dev;
void __iomem *regs;
- void __iomem *enable_reg;
+ struct regmap *pmu_map;
struct clk *clk;
struct clk *sclk;
unsigned int irq;
@@ -110,6 +115,7 @@ struct exynos_adc_data {
int num_channels;
bool needs_sclk;
bool needs_adc_phy;
+ int phy_offset;
u32 mask;
void (*init_hw)(struct exynos_adc *info);
@@ -183,7 +189,7 @@ static void exynos_adc_v1_init_hw(struct exynos_adc *info)
u32 con1;
if (info->data->needs_adc_phy)
- writel(1, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
/* set default prescaler values and Enable prescaler */
con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN;
@@ -198,7 +204,7 @@ static void exynos_adc_v1_exit_hw(struct exynos_adc *info)
u32 con;
if (info->data->needs_adc_phy)
- writel(0, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
con = readl(ADC_V1_CON(info->regs));
con |= ADC_V1_CON_STANDBY;
@@ -225,6 +231,7 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
.init_hw = exynos_adc_v1_init_hw,
.exit_hw = exynos_adc_v1_exit_hw,
@@ -314,7 +321,7 @@ static void exynos_adc_v2_init_hw(struct exynos_adc *info)
u32 con1, con2;
if (info->data->needs_adc_phy)
- writel(1, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
con1 = ADC_V2_CON1_SOFT_RESET;
writel(con1, ADC_V2_CON1(info->regs));
@@ -332,7 +339,7 @@ static void exynos_adc_v2_exit_hw(struct exynos_adc *info)
u32 con;
if (info->data->needs_adc_phy)
- writel(0, info->enable_reg);
+ regmap_write(info->pmu_map, info->data->phy_offset, 0);
con = readl(ADC_V2_CON1(info->regs));
con &= ~ADC_CON_EN_START;
@@ -362,6 +369,7 @@ static const struct exynos_adc_data exynos_adc_v2_data = {
.num_channels = MAX_ADC_V2_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV2_PHY_OFFSET,
.init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw,
@@ -374,6 +382,7 @@ static const struct exynos_adc_data exynos3250_adc_data = {
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
.needs_sclk = true,
.needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
.init_hw = exynos_adc_v2_init_hw,
.exit_hw = exynos_adc_v2_exit_hw,
@@ -381,6 +390,35 @@ static const struct exynos_adc_data exynos3250_adc_data = {
.start_conv = exynos_adc_v2_start_conv,
};
+static void exynos_adc_exynos7_init_hw(struct exynos_adc *info)
+{
+ u32 con1, con2;
+
+ if (info->data->needs_adc_phy)
+ regmap_write(info->pmu_map, info->data->phy_offset, 1);
+
+ con1 = ADC_V2_CON1_SOFT_RESET;
+ writel(con1, ADC_V2_CON1(info->regs));
+
+ con2 = readl(ADC_V2_CON2(info->regs));
+ con2 &= ~ADC_V2_CON2_C_TIME(7);
+ con2 |= ADC_V2_CON2_C_TIME(0);
+ writel(con2, ADC_V2_CON2(info->regs));
+
+ /* Enable interrupts */
+ writel(1, ADC_V2_INT_EN(info->regs));
+}
+
+static const struct exynos_adc_data exynos7_adc_data = {
+ .num_channels = MAX_ADC_V1_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_exynos7_init_hw,
+ .exit_hw = exynos_adc_v2_exit_hw,
+ .clear_irq = exynos_adc_v2_clear_irq,
+ .start_conv = exynos_adc_v2_start_conv,
+};
+
static const struct of_device_id exynos_adc_match[] = {
{
.compatible = "samsung,s3c2410-adc",
@@ -406,6 +444,9 @@ static const struct of_device_id exynos_adc_match[] = {
}, {
.compatible = "samsung,exynos3250-adc",
.data = &exynos3250_adc_data,
+ }, {
+ .compatible = "samsung,exynos7-adc",
+ .data = &exynos7_adc_data,
},
{},
};
@@ -558,10 +599,13 @@ static int exynos_adc_probe(struct platform_device *pdev)
if (info->data->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);
+ info->pmu_map = syscon_regmap_lookup_by_phandle(
+ pdev->dev.of_node,
+ "samsung,syscon-phandle");
+ if (IS_ERR(info->pmu_map)) {
+ dev_err(&pdev->dev, "syscon regmap lookup failed.\n");
+ return PTR_ERR(info->pmu_map);
+ }
}
irq = platform_get_irq(pdev, 0);
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index 28a086e..efbfd12 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -1,9 +1,30 @@
/*
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
+ * Copyright (C) 2014 Rose Technology
+ * Allan Bendorff Jensen <abj@rosetechnology.dk>
+ * Soren Andersen <san@rosetechnology.dk>
+ *
+ * Driver for following ADC chips from Microchip Technology's:
+ * 10 Bit converter
+ * MCP3001
+ * MCP3002
+ * MCP3004
+ * MCP3008
+ * ------------
+ * 12 bit converter
+ * MCP3201
+ * MCP3202
+ * MCP3204
+ * MCP3208
+ * ------------
*
- * Driver for Microchip Technology's MCP3204 and MCP3208 ADC chips.
* Datasheet can be found here:
- * http://ww1.microchip.com/downloads/en/devicedoc/21298c.pdf
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
+ * http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
*
* 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
@@ -11,19 +32,29 @@
*/
#include <linux/err.h>
+#include <linux/delay.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 {
+ mcp3001,
+ mcp3002,
+ mcp3004,
+ mcp3008,
+ mcp3201,
+ mcp3202,
mcp3204,
mcp3208,
};
+struct mcp320x_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int resolution;
+};
+
struct mcp320x {
struct spi_device *spi;
struct spi_message msg;
@@ -34,19 +65,69 @@ struct mcp320x {
struct regulator *reg;
struct mutex lock;
+ const struct mcp320x_chip_info *chip_info;
};
-static int mcp320x_adc_conversion(struct mcp320x *adc, u8 msg)
+static int mcp320x_channel_to_tx_data(int device_index,
+ const unsigned int channel, bool differential)
+{
+ int start_bit = 1;
+
+ switch (device_index) {
+ case mcp3001:
+ case mcp3201:
+ return 0;
+ case mcp3002:
+ case mcp3202:
+ return ((start_bit << 4) | (!differential << 3) |
+ (channel << 2));
+ case mcp3004:
+ case mcp3204:
+ case mcp3008:
+ case mcp3208:
+ return ((start_bit << 6) | (!differential << 5) |
+ (channel << 2));
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mcp320x_adc_conversion(struct mcp320x *adc, u8 channel,
+ bool differential, int device_index)
{
int ret;
- adc->tx_buf = msg;
- ret = spi_sync(adc->spi, &adc->msg);
- if (ret < 0)
- return ret;
+ adc->rx_buf[0] = 0;
+ adc->rx_buf[1] = 0;
+ adc->tx_buf = mcp320x_channel_to_tx_data(device_index,
+ channel, differential);
+
+ if (device_index != mcp3001 && device_index != mcp3201) {
+ ret = spi_sync(adc->spi, &adc->msg);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = spi_read(adc->spi, &adc->rx_buf, sizeof(adc->rx_buf));
+ if (ret < 0)
+ return ret;
+ }
- return ((adc->rx_buf[0] & 0x3f) << 6) |
- (adc->rx_buf[1] >> 2);
+ switch (device_index) {
+ case mcp3001:
+ return (adc->rx_buf[0] << 5 | adc->rx_buf[1] >> 3);
+ case mcp3002:
+ case mcp3004:
+ case mcp3008:
+ return (adc->rx_buf[0] << 2 | adc->rx_buf[1] >> 6);
+ case mcp3201:
+ return (adc->rx_buf[0] << 7 | adc->rx_buf[1] >> 1);
+ case mcp3202:
+ case mcp3204:
+ case mcp3208:
+ return (adc->rx_buf[0] << 4 | adc->rx_buf[1] >> 4);
+ default:
+ return -EINVAL;
+ }
}
static int mcp320x_read_raw(struct iio_dev *indio_dev,
@@ -55,18 +136,17 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
{
struct mcp320x *adc = iio_priv(indio_dev);
int ret = -EINVAL;
+ int device_index = 0;
mutex_lock(&adc->lock);
+ device_index = spi_get_device_id(adc->spi)->driver_data;
+
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);
+ ret = mcp320x_adc_conversion(adc, channel->address,
+ channel->differential, device_index);
+
if (ret < 0)
goto out;
@@ -75,18 +155,15 @@ static int mcp320x_read_raw(struct iio_dev *indio_dev,
break;
case IIO_CHAN_INFO_SCALE:
- /* Digital output code = (4096 * Vin) / Vref */
ret = regulator_get_voltage(adc->reg);
if (ret < 0)
goto out;
+ /* convert regulator output voltage to mV */
*val = ret / 1000;
- *val2 = 12;
+ *val2 = adc->chip_info->resolution;
ret = IIO_VAL_FRACTIONAL_LOG2;
break;
-
- default:
- break;
}
out:
@@ -117,6 +194,16 @@ out:
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
+static const struct iio_chan_spec mcp3201_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
+static const struct iio_chan_spec mcp3202_channels[] = {
+ MCP320X_VOLTAGE_CHANNEL(0),
+ MCP320X_VOLTAGE_CHANNEL(1),
+ MCP320X_VOLTAGE_CHANNEL_DIFF(0),
+};
+
static const struct iio_chan_spec mcp3204_channels[] = {
MCP320X_VOLTAGE_CHANNEL(0),
MCP320X_VOLTAGE_CHANNEL(1),
@@ -146,19 +233,46 @@ static const struct iio_info mcp320x_info = {
.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[] = {
+static const struct mcp320x_chip_info mcp320x_chip_infos[] = {
+ [mcp3001] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 10
+ },
+ [mcp3002] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 10
+ },
+ [mcp3004] = {
+ .channels = mcp3204_channels,
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 10
+ },
+ [mcp3008] = {
+ .channels = mcp3208_channels,
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 10
+ },
+ [mcp3201] = {
+ .channels = mcp3201_channels,
+ .num_channels = ARRAY_SIZE(mcp3201_channels),
+ .resolution = 12
+ },
+ [mcp3202] = {
+ .channels = mcp3202_channels,
+ .num_channels = ARRAY_SIZE(mcp3202_channels),
+ .resolution = 12
+ },
[mcp3204] = {
.channels = mcp3204_channels,
- .num_channels = ARRAY_SIZE(mcp3204_channels)
+ .num_channels = ARRAY_SIZE(mcp3204_channels),
+ .resolution = 12
},
[mcp3208] = {
.channels = mcp3208_channels,
- .num_channels = ARRAY_SIZE(mcp3208_channels)
+ .num_channels = ARRAY_SIZE(mcp3208_channels),
+ .resolution = 12
},
};
@@ -166,7 +280,7 @@ static int mcp320x_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
struct mcp320x *adc;
- const struct mcp3208_chip_info *chip_info;
+ const struct mcp320x_chip_info *chip_info;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
@@ -181,7 +295,7 @@ static int mcp320x_probe(struct spi_device *spi)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mcp320x_info;
- chip_info = &mcp3208_chip_infos[spi_get_device_id(spi)->driver_data];
+ chip_info = &mcp320x_chip_infos[spi_get_device_id(spi)->driver_data];
indio_dev->channels = chip_info->channels;
indio_dev->num_channels = chip_info->num_channels;
@@ -226,7 +340,45 @@ static int mcp320x_remove(struct spi_device *spi)
return 0;
}
+#if defined(CONFIG_OF)
+static const struct of_device_id mcp320x_dt_ids[] = {
+ {
+ .compatible = "mcp3001",
+ .data = &mcp320x_chip_infos[mcp3001],
+ }, {
+ .compatible = "mcp3002",
+ .data = &mcp320x_chip_infos[mcp3002],
+ }, {
+ .compatible = "mcp3004",
+ .data = &mcp320x_chip_infos[mcp3004],
+ }, {
+ .compatible = "mcp3008",
+ .data = &mcp320x_chip_infos[mcp3008],
+ }, {
+ .compatible = "mcp3201",
+ .data = &mcp320x_chip_infos[mcp3201],
+ }, {
+ .compatible = "mcp3202",
+ .data = &mcp320x_chip_infos[mcp3202],
+ }, {
+ .compatible = "mcp3204",
+ .data = &mcp320x_chip_infos[mcp3204],
+ }, {
+ .compatible = "mcp3208",
+ .data = &mcp320x_chip_infos[mcp3208],
+ }, {
+ }
+};
+MODULE_DEVICE_TABLE(of, mcp320x_dt_ids);
+#endif
+
static const struct spi_device_id mcp320x_id[] = {
+ { "mcp3001", mcp3001 },
+ { "mcp3002", mcp3002 },
+ { "mcp3004", mcp3004 },
+ { "mcp3008", mcp3008 },
+ { "mcp3201", mcp3201 },
+ { "mcp3202", mcp3202 },
{ "mcp3204", mcp3204 },
{ "mcp3208", mcp3208 },
{ }
@@ -245,5 +397,5 @@ static struct spi_driver mcp320x_driver = {
module_spi_driver(mcp320x_driver);
MODULE_AUTHOR("Oskar Andero <oskar.andero@gmail.com>");
-MODULE_DESCRIPTION("Microchip Technology MCP3204/08");
+MODULE_DESCRIPTION("Microchip Technology MCP3x01/02/04/08");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 5167225..b96c636 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -58,20 +58,11 @@
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
-/* LSB is in nV to eliminate floating point */
-static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625};
-
-/*
- * scales calculated as:
- * rates_to_lsb[sample_rate] / (1 << pga);
- * pga is 1 for 0, 2
- */
-
static const int mcp3422_scales[4][4] = {
- { 1000000, 250000, 62500, 15625 },
- { 500000 , 125000, 31250, 7812 },
- { 250000 , 62500 , 15625, 3906 },
- { 125000 , 31250 , 7812 , 1953 } };
+ { 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] = {
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index b58d630..d095efe 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev)
static const struct mcb_device_id men_z188_ids[] = {
{ .device = 0xbc },
+ { }
};
MODULE_DEVICE_TABLE(mcb, men_z188_ids);
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
new file mode 100644
index 0000000..fabd24e
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -0,0 +1,596 @@
+/*
+ * Copyright (c) 2012-2014, The Linux Foundation. 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 version 2 and
+ * only 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* IADC register and bit definition */
+#define IADC_REVISION2 0x1
+#define IADC_REVISION2_SUPPORTED_IADC 1
+
+#define IADC_PERPH_TYPE 0x4
+#define IADC_PERPH_TYPE_ADC 8
+
+#define IADC_PERPH_SUBTYPE 0x5
+#define IADC_PERPH_SUBTYPE_IADC 3
+
+#define IADC_STATUS1 0x8
+#define IADC_STATUS1_OP_MODE 4
+#define IADC_STATUS1_REQ_STS BIT(1)
+#define IADC_STATUS1_EOC BIT(0)
+#define IADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define IADC_MODE_CTL 0x40
+#define IADC_OP_MODE_SHIFT 3
+#define IADC_OP_MODE_NORMAL 0
+#define IADC_TRIM_EN BIT(0)
+
+#define IADC_EN_CTL1 0x46
+#define IADC_EN_CTL1_SET BIT(7)
+
+#define IADC_CH_SEL_CTL 0x48
+
+#define IADC_DIG_PARAM 0x50
+#define IADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define IADC_HW_SETTLE_DELAY 0x51
+
+#define IADC_CONV_REQ 0x52
+#define IADC_CONV_REQ_SET BIT(7)
+
+#define IADC_FAST_AVG_CTL 0x5a
+#define IADC_FAST_AVG_EN 0x5b
+#define IADC_FAST_AVG_EN_SET BIT(7)
+
+#define IADC_PERH_RESET_CTL3 0xda
+#define IADC_FOLLOW_WARM_RB BIT(2)
+
+#define IADC_DATA 0x60 /* 16 bits */
+
+#define IADC_SEC_ACCESS 0xd0
+#define IADC_SEC_ACCESS_DATA 0xa5
+
+#define IADC_NOMINAL_RSENSE 0xf4
+#define IADC_NOMINAL_RSENSE_SIGN_MASK BIT(7)
+
+#define IADC_REF_GAIN_MICRO_VOLTS 17857
+
+#define IADC_INT_RSENSE_DEVIATION 15625 /* nano Ohms per bit */
+
+#define IADC_INT_RSENSE_IDEAL_VALUE 10000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_VALUE 7800 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_GF 9000 /* micro Ohms */
+#define IADC_INT_RSENSE_DEFAULT_SMIC 9700 /* micro Ohms */
+
+#define IADC_CONV_TIME_MIN_US 2000
+#define IADC_CONV_TIME_MAX_US 2100
+
+#define IADC_DEF_PRESCALING 0 /* 1:1 */
+#define IADC_DEF_DECIMATION 0 /* 512 */
+#define IADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
+#define IADC_DEF_AVG_SAMPLES 0 /* 1 sample */
+
+/* IADC channel list */
+#define IADC_INT_RSENSE 0
+#define IADC_EXT_RSENSE 1
+#define IADC_GAIN_17P857MV 3
+#define IADC_EXT_OFFSET_CSP_CSN 5
+#define IADC_INT_OFFSET_CSP2_CSN2 6
+
+/**
+ * struct iadc_chip - IADC Current ADC device structure.
+ * @regmap: regmap for register read/write.
+ * @dev: This device pointer.
+ * @base: base offset for the ADC peripheral.
+ * @rsense: Values of the internal and external sense resister in micro Ohms.
+ * @poll_eoc: Poll for end of conversion instead of waiting for IRQ.
+ * @offset: Raw offset values for the internal and external channels.
+ * @gain: Raw gain of the channels.
+ * @lock: ADC lock for access to the peripheral.
+ * @complete: ADC notification after end of conversion interrupt is received.
+ */
+struct iadc_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ bool poll_eoc;
+ u32 rsense[2];
+ u16 offset[2];
+ u16 gain;
+ struct mutex lock;
+ struct completion complete;
+};
+
+static int iadc_read(struct iadc_chip *iadc, u16 offset, u8 *data)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(iadc->regmap, iadc->base + offset, &val);
+ if (ret < 0)
+ return ret;
+
+ *data = val;
+ return 0;
+}
+
+static int iadc_write(struct iadc_chip *iadc, u16 offset, u8 data)
+{
+ return regmap_write(iadc->regmap, iadc->base + offset, data);
+}
+
+static int iadc_reset(struct iadc_chip *iadc)
+{
+ u8 data;
+ int ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_read(iadc, IADC_PERH_RESET_CTL3, &data);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_SEC_ACCESS, IADC_SEC_ACCESS_DATA);
+ if (ret < 0)
+ return ret;
+
+ data |= IADC_FOLLOW_WARM_RB;
+
+ return iadc_write(iadc, IADC_PERH_RESET_CTL3, data);
+}
+
+static int iadc_set_state(struct iadc_chip *iadc, bool state)
+{
+ return iadc_write(iadc, IADC_EN_CTL1, state ? IADC_EN_CTL1_SET : 0);
+}
+
+static void iadc_status_show(struct iadc_chip *iadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_MODE_CTL, &mode);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_DIG_PARAM, &dig);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CH_SEL_CTL, &chan);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_CONV_REQ, &req);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return;
+
+ ret = iadc_read(iadc, IADC_EN_CTL1, &en);
+ if (ret < 0)
+ return;
+
+ dev_err(iadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int iadc_configure(struct iadc_chip *iadc, int channel)
+{
+ u8 decim, mode;
+ int ret;
+
+ /* Mode selection */
+ mode = (IADC_OP_MODE_NORMAL << IADC_OP_MODE_SHIFT) | IADC_TRIM_EN;
+ ret = iadc_write(iadc, IADC_MODE_CTL, mode);
+ if (ret < 0)
+ return ret;
+
+ /* Channel selection */
+ ret = iadc_write(iadc, IADC_CH_SEL_CTL, channel);
+ if (ret < 0)
+ return ret;
+
+ /* Digital parameter setup */
+ decim = IADC_DEF_DECIMATION << IADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = iadc_write(iadc, IADC_DIG_PARAM, decim);
+ if (ret < 0)
+ return ret;
+
+ /* HW settle time delay */
+ ret = iadc_write(iadc, IADC_HW_SETTLE_DELAY, IADC_DEF_HW_SETTLE_TIME);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_write(iadc, IADC_FAST_AVG_CTL, IADC_DEF_AVG_SAMPLES);
+ if (ret < 0)
+ return ret;
+
+ if (IADC_DEF_AVG_SAMPLES)
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, IADC_FAST_AVG_EN_SET);
+ else
+ ret = iadc_write(iadc, IADC_FAST_AVG_EN, 0);
+
+ if (ret < 0)
+ return ret;
+
+ if (!iadc->poll_eoc)
+ reinit_completion(&iadc->complete);
+
+ ret = iadc_set_state(iadc, true);
+ if (ret < 0)
+ return ret;
+
+ /* Request conversion */
+ return iadc_write(iadc, IADC_CONV_REQ, IADC_CONV_REQ_SET);
+}
+
+static int iadc_poll_wait_eoc(struct iadc_chip *iadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ int ret;
+ u8 sta1;
+
+ retry = interval_us / IADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = iadc_read(iadc, IADC_STATUS1, &sta1);
+ if (ret < 0)
+ return ret;
+
+ sta1 &= IADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == IADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(IADC_CONV_TIME_MIN_US, IADC_CONV_TIME_MAX_US);
+ }
+
+ iadc_status_show(iadc);
+
+ return -ETIMEDOUT;
+}
+
+static int iadc_read_result(struct iadc_chip *iadc, u16 *data)
+{
+ return regmap_bulk_read(iadc->regmap, iadc->base + IADC_DATA, data, 2);
+}
+
+static int iadc_do_conversion(struct iadc_chip *iadc, int chan, u16 *data)
+{
+ unsigned int wait;
+ int ret;
+
+ ret = iadc_configure(iadc, chan);
+ if (ret < 0)
+ goto exit;
+
+ wait = BIT(IADC_DEF_AVG_SAMPLES) * IADC_CONV_TIME_MIN_US * 2;
+
+ if (iadc->poll_eoc) {
+ ret = iadc_poll_wait_eoc(iadc, wait);
+ } else {
+ ret = wait_for_completion_timeout(&iadc->complete,
+ usecs_to_jiffies(wait));
+ if (!ret)
+ ret = -ETIMEDOUT;
+ else
+ /* double check conversion status */
+ ret = iadc_poll_wait_eoc(iadc, IADC_CONV_TIME_MIN_US);
+ }
+
+ if (!ret)
+ ret = iadc_read_result(iadc, data);
+exit:
+ iadc_set_state(iadc, false);
+ if (ret < 0)
+ dev_err(iadc->dev, "conversion failed\n");
+
+ return ret;
+}
+
+static int iadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct iadc_chip *iadc = iio_priv(indio_dev);
+ s32 isense_ua, vsense_uv;
+ u16 adc_raw, vsense_raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&iadc->lock);
+ ret = iadc_do_conversion(iadc, chan->channel, &adc_raw);
+ mutex_unlock(&iadc->lock);
+ if (ret < 0)
+ return ret;
+
+ vsense_raw = adc_raw - iadc->offset[chan->channel];
+
+ vsense_uv = vsense_raw * IADC_REF_GAIN_MICRO_VOLTS;
+ vsense_uv /= (s32)iadc->gain - iadc->offset[chan->channel];
+
+ isense_ua = vsense_uv / iadc->rsense[chan->channel];
+
+ dev_dbg(iadc->dev, "off %d gain %d adc %d %duV I %duA\n",
+ iadc->offset[chan->channel], iadc->gain,
+ adc_raw, vsense_uv, isense_ua);
+
+ *val = isense_ua;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info iadc_info = {
+ .read_raw = iadc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static irqreturn_t iadc_isr(int irq, void *dev_id)
+{
+ struct iadc_chip *iadc = dev_id;
+
+ complete(&iadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int iadc_update_offset(struct iadc_chip *iadc)
+{
+ int ret;
+
+ ret = iadc_do_conversion(iadc, IADC_GAIN_17P857MV, &iadc->gain);
+ if (ret < 0)
+ return ret;
+
+ ret = iadc_do_conversion(iadc, IADC_INT_OFFSET_CSP2_CSN2,
+ &iadc->offset[IADC_INT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_INT_RSENSE]) {
+ dev_err(iadc->dev, "error: internal offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ ret = iadc_do_conversion(iadc, IADC_EXT_OFFSET_CSP_CSN,
+ &iadc->offset[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ return ret;
+
+ if (iadc->gain == iadc->offset[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "error: external offset == gain %d\n",
+ iadc->gain);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_version_check(struct iadc_chip *iadc)
+{
+ u8 val;
+ int ret;
+
+ ret = iadc_read(iadc, IADC_PERPH_TYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_TYPE_ADC) {
+ dev_err(iadc->dev, "%d is not ADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_PERPH_SUBTYPE, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_PERPH_SUBTYPE_IADC) {
+ dev_err(iadc->dev, "%d is not IADC\n", val);
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_REVISION2, &val);
+ if (ret < 0)
+ return ret;
+
+ if (val < IADC_REVISION2_SUPPORTED_IADC) {
+ dev_err(iadc->dev, "revision %d not supported\n", val);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int iadc_rsense_read(struct iadc_chip *iadc, struct device_node *node)
+{
+ int ret, sign, int_sense;
+ u8 deviation;
+
+ ret = of_property_read_u32(node, "qcom,external-resistor-micro-ohms",
+ &iadc->rsense[IADC_EXT_RSENSE]);
+ if (ret < 0)
+ iadc->rsense[IADC_EXT_RSENSE] = IADC_INT_RSENSE_IDEAL_VALUE;
+
+ if (!iadc->rsense[IADC_EXT_RSENSE]) {
+ dev_err(iadc->dev, "external resistor can't be zero Ohms");
+ return -EINVAL;
+ }
+
+ ret = iadc_read(iadc, IADC_NOMINAL_RSENSE, &deviation);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Deviation value stored is an offset from 10 mili Ohms, bit 7 is
+ * the sign, the remaining bits have an LSB of 15625 nano Ohms.
+ */
+ sign = (deviation & IADC_NOMINAL_RSENSE_SIGN_MASK) ? -1 : 1;
+
+ deviation &= ~IADC_NOMINAL_RSENSE_SIGN_MASK;
+
+ /* Scale it to nono Ohms */
+ int_sense = IADC_INT_RSENSE_IDEAL_VALUE * 1000;
+ int_sense += sign * deviation * IADC_INT_RSENSE_DEVIATION;
+ int_sense /= 1000; /* micro Ohms */
+
+ iadc->rsense[IADC_INT_RSENSE] = int_sense;
+ return 0;
+}
+
+static const struct iio_chan_spec iadc_channels[] = {
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "INTERNAL_RSENSE",
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+ {
+ .type = IIO_CURRENT,
+ .datasheet_name = "EXTERNAL_RSENSE",
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ },
+};
+
+static int iadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct iadc_chip *iadc;
+ int ret, irq_eoc;
+ u32 res;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*iadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ iadc = iio_priv(indio_dev);
+ iadc->dev = dev;
+
+ iadc->regmap = dev_get_regmap(dev->parent, NULL);
+ if (!iadc->regmap)
+ return -ENODEV;
+
+ init_completion(&iadc->complete);
+ mutex_init(&iadc->lock);
+
+ ret = of_property_read_u32(node, "reg", &res);
+ if (ret < 0)
+ return -ENODEV;
+
+ iadc->base = res;
+
+ ret = iadc_version_check(iadc);
+ if (ret < 0)
+ return -ENODEV;
+
+ ret = iadc_rsense_read(iadc, node);
+ if (ret < 0)
+ return -ENODEV;
+
+ dev_dbg(iadc->dev, "sense resistors %d and %d micro Ohm\n",
+ iadc->rsense[IADC_INT_RSENSE],
+ iadc->rsense[IADC_EXT_RSENSE]);
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc == -EPROBE_DEFER)
+ return irq_eoc;
+
+ if (irq_eoc < 0)
+ iadc->poll_eoc = true;
+
+ ret = iadc_reset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ if (!iadc->poll_eoc) {
+ ret = devm_request_irq(dev, irq_eoc, iadc_isr, 0,
+ "spmi-iadc", iadc);
+ if (!ret)
+ enable_irq_wake(irq_eoc);
+ else
+ return ret;
+ } else {
+ device_init_wakeup(iadc->dev, 1);
+ }
+
+ ret = iadc_update_offset(iadc);
+ if (ret < 0) {
+ dev_err(dev, "failed offset calibration\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &iadc_info;
+ indio_dev->channels = iadc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(iadc_channels);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id iadc_match_table[] = {
+ { .compatible = "qcom,spmi-iadc" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, iadc_match_table);
+
+static struct platform_driver iadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-iadc",
+ .of_match_table = iadc_match_table,
+ },
+ .probe = iadc_probe,
+};
+
+module_platform_driver(iadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-iadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC current ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
new file mode 100644
index 0000000..3211729
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -0,0 +1,1016 @@
+/*
+ * Copyright (c) 2012-2014, The Linux Foundation. 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 version 2 and
+ * only 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.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/log2.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+
+/* VADC register and bit definitions */
+#define VADC_REVISION2 0x1
+#define VADC_REVISION2_SUPPORTED_VADC 1
+
+#define VADC_PERPH_TYPE 0x4
+#define VADC_PERPH_TYPE_ADC 8
+
+#define VADC_PERPH_SUBTYPE 0x5
+#define VADC_PERPH_SUBTYPE_VADC 1
+
+#define VADC_STATUS1 0x8
+#define VADC_STATUS1_OP_MODE 4
+#define VADC_STATUS1_REQ_STS BIT(1)
+#define VADC_STATUS1_EOC BIT(0)
+#define VADC_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define VADC_MODE_CTL 0x40
+#define VADC_OP_MODE_SHIFT 3
+#define VADC_OP_MODE_NORMAL 0
+#define VADC_AMUX_TRIM_EN BIT(1)
+#define VADC_ADC_TRIM_EN BIT(0)
+
+#define VADC_EN_CTL1 0x46
+#define VADC_EN_CTL1_SET BIT(7)
+
+#define VADC_ADC_CH_SEL_CTL 0x48
+
+#define VADC_ADC_DIG_PARAM 0x50
+#define VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT 2
+
+#define VADC_HW_SETTLE_DELAY 0x51
+
+#define VADC_CONV_REQ 0x52
+#define VADC_CONV_REQ_SET BIT(7)
+
+#define VADC_FAST_AVG_CTL 0x5a
+#define VADC_FAST_AVG_EN 0x5b
+#define VADC_FAST_AVG_EN_SET BIT(7)
+
+#define VADC_ACCESS 0xd0
+#define VADC_ACCESS_DATA 0xa5
+
+#define VADC_PERH_RESET_CTL3 0xda
+#define VADC_FOLLOW_WARM_RB BIT(2)
+
+#define VADC_DATA 0x60 /* 16 bits */
+
+#define VADC_CONV_TIME_MIN_US 2000
+#define VADC_CONV_TIME_MAX_US 2100
+
+/* Min ADC code represents 0V */
+#define VADC_MIN_ADC_CODE 0x6000
+/* Max ADC code represents full-scale range of 1.8V */
+#define VADC_MAX_ADC_CODE 0xa800
+
+#define VADC_ABSOLUTE_RANGE_UV 625000
+#define VADC_RATIOMETRIC_RANGE_UV 1800000
+
+#define VADC_DEF_PRESCALING 0 /* 1:1 */
+#define VADC_DEF_DECIMATION 0 /* 512 */
+#define VADC_DEF_HW_SETTLE_TIME 0 /* 0 us */
+#define VADC_DEF_AVG_SAMPLES 0 /* 1 sample */
+#define VADC_DEF_CALIB_TYPE VADC_CALIB_ABSOLUTE
+
+#define VADC_DECIMATION_MIN 512
+#define VADC_DECIMATION_MAX 4096
+
+#define VADC_HW_SETTLE_DELAY_MAX 10000
+#define VADC_AVG_SAMPLES_MAX 512
+
+#define KELVINMIL_CELSIUSMIL 273150
+
+#define VADC_CHAN_MIN VADC_USBIN
+#define VADC_CHAN_MAX VADC_LR_MUX3_BUF_PU1_PU2_XO_THERM
+
+/*
+ * VADC_CALIB_ABSOLUTE: uses the 625mV and 1.25V as reference channels.
+ * VADC_CALIB_RATIOMETRIC: uses the reference voltage (1.8V) and GND for
+ * calibration.
+ */
+enum vadc_calibration {
+ VADC_CALIB_ABSOLUTE = 0,
+ VADC_CALIB_RATIOMETRIC
+};
+
+/**
+ * struct vadc_linear_graph - Represent ADC characteristics.
+ * @dy: numerator slope to calculate the gain.
+ * @dx: denominator slope to calculate the gain.
+ * @gnd: A/D word of the ground reference used for the channel.
+ *
+ * Each ADC device has different offset and gain parameters which are
+ * computed to calibrate the device.
+ */
+struct vadc_linear_graph {
+ s32 dy;
+ s32 dx;
+ s32 gnd;
+};
+
+/**
+ * struct vadc_prescale_ratio - Represent scaling ratio for ADC input.
+ * @num: the inverse numerator of the gain applied to the input channel.
+ * @den: the inverse denominator of the gain applied to the input channel.
+ */
+struct vadc_prescale_ratio {
+ u32 num;
+ u32 den;
+};
+
+/**
+ * struct vadc_channel_prop - VADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @calibration: calibration type.
+ * @decimation: sampling rate supported for the channel.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ */
+struct vadc_channel_prop {
+ unsigned int channel;
+ enum vadc_calibration calibration;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+};
+
+/**
+ * struct vadc_priv - VADC private structure.
+ * @regmap: pointer to struct regmap.
+ * @dev: pointer to struct device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of VADC channels.
+ * @chan_props: array of VADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @are_ref_measured: are reference points measured.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: VADC result notification after interrupt is received.
+ * @graph: store parameters for calibration.
+ * @lock: ADC lock for access to the peripheral.
+ */
+struct vadc_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct vadc_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool are_ref_measured;
+ bool poll_eoc;
+ struct completion complete;
+ struct vadc_linear_graph graph[2];
+ struct mutex lock;
+};
+
+static const struct vadc_prescale_ratio vadc_prescale_ratios[] = {
+ {.num = 1, .den = 1},
+ {.num = 1, .den = 3},
+ {.num = 1, .den = 4},
+ {.num = 1, .den = 6},
+ {.num = 1, .den = 20},
+ {.num = 1, .den = 8},
+ {.num = 10, .den = 81},
+ {.num = 1, .den = 10}
+};
+
+static int vadc_read(struct vadc_priv *vadc, u16 offset, u8 *data)
+{
+ return regmap_bulk_read(vadc->regmap, vadc->base + offset, data, 1);
+}
+
+static int vadc_write(struct vadc_priv *vadc, u16 offset, u8 data)
+{
+ return regmap_write(vadc->regmap, vadc->base + offset, data);
+}
+
+static int vadc_reset(struct vadc_priv *vadc)
+{
+ u8 data;
+ int ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ ret = vadc_read(vadc, VADC_PERH_RESET_CTL3, &data);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_ACCESS, VADC_ACCESS_DATA);
+ if (ret)
+ return ret;
+
+ data |= VADC_FOLLOW_WARM_RB;
+
+ return vadc_write(vadc, VADC_PERH_RESET_CTL3, data);
+}
+
+static int vadc_set_state(struct vadc_priv *vadc, bool state)
+{
+ return vadc_write(vadc, VADC_EN_CTL1, state ? VADC_EN_CTL1_SET : 0);
+}
+
+static void vadc_show_status(struct vadc_priv *vadc)
+{
+ u8 mode, sta1, chan, dig, en, req;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_MODE_CTL, &mode);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_DIG_PARAM, &dig);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_ADC_CH_SEL_CTL, &chan);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_CONV_REQ, &req);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return;
+
+ ret = vadc_read(vadc, VADC_EN_CTL1, &en);
+ if (ret)
+ return;
+
+ dev_err(vadc->dev,
+ "mode:%02x en:%02x chan:%02x dig:%02x req:%02x sta1:%02x\n",
+ mode, en, chan, dig, req, sta1);
+}
+
+static int vadc_configure(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop)
+{
+ u8 decimation, mode_ctrl;
+ int ret;
+
+ /* Mode selection */
+ mode_ctrl = (VADC_OP_MODE_NORMAL << VADC_OP_MODE_SHIFT) |
+ VADC_ADC_TRIM_EN | VADC_AMUX_TRIM_EN;
+ ret = vadc_write(vadc, VADC_MODE_CTL, mode_ctrl);
+ if (ret)
+ return ret;
+
+ /* Channel selection */
+ ret = vadc_write(vadc, VADC_ADC_CH_SEL_CTL, prop->channel);
+ if (ret)
+ return ret;
+
+ /* Digital parameter setup */
+ decimation = prop->decimation << VADC_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+ ret = vadc_write(vadc, VADC_ADC_DIG_PARAM, decimation);
+ if (ret)
+ return ret;
+
+ /* HW settle time delay */
+ ret = vadc_write(vadc, VADC_HW_SETTLE_DELAY, prop->hw_settle_time);
+ if (ret)
+ return ret;
+
+ ret = vadc_write(vadc, VADC_FAST_AVG_CTL, prop->avg_samples);
+ if (ret)
+ return ret;
+
+ if (prop->avg_samples)
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, VADC_FAST_AVG_EN_SET);
+ else
+ ret = vadc_write(vadc, VADC_FAST_AVG_EN, 0);
+
+ return ret;
+}
+
+static int vadc_poll_wait_eoc(struct vadc_priv *vadc, unsigned int interval_us)
+{
+ unsigned int count, retry;
+ u8 sta1;
+ int ret;
+
+ retry = interval_us / VADC_CONV_TIME_MIN_US;
+
+ for (count = 0; count < retry; count++) {
+ ret = vadc_read(vadc, VADC_STATUS1, &sta1);
+ if (ret)
+ return ret;
+
+ sta1 &= VADC_STATUS1_REQ_STS_EOC_MASK;
+ if (sta1 == VADC_STATUS1_EOC)
+ return 0;
+
+ usleep_range(VADC_CONV_TIME_MIN_US, VADC_CONV_TIME_MAX_US);
+ }
+
+ vadc_show_status(vadc);
+
+ return -ETIMEDOUT;
+}
+
+static int vadc_read_result(struct vadc_priv *vadc, u16 *data)
+{
+ int ret;
+
+ ret = regmap_bulk_read(vadc->regmap, vadc->base + VADC_DATA, data, 2);
+ if (ret)
+ return ret;
+
+ *data = clamp_t(u16, *data, VADC_MIN_ADC_CODE, VADC_MAX_ADC_CODE);
+
+ return 0;
+}
+
+static struct vadc_channel_prop *vadc_get_channel(struct vadc_priv *vadc,
+ unsigned int num)
+{
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->chan_props[i].channel == num)
+ return &vadc->chan_props[i];
+
+ dev_dbg(vadc->dev, "no such channel %02x\n", num);
+
+ return NULL;
+}
+
+static int vadc_do_conversion(struct vadc_priv *vadc,
+ struct vadc_channel_prop *prop, u16 *data)
+{
+ unsigned int timeout;
+ int ret;
+
+ mutex_lock(&vadc->lock);
+
+ ret = vadc_configure(vadc, prop);
+ if (ret)
+ goto unlock;
+
+ if (!vadc->poll_eoc)
+ reinit_completion(&vadc->complete);
+
+ ret = vadc_set_state(vadc, true);
+ if (ret)
+ goto unlock;
+
+ ret = vadc_write(vadc, VADC_CONV_REQ, VADC_CONV_REQ_SET);
+ if (ret)
+ goto err_disable;
+
+ timeout = BIT(prop->avg_samples) * VADC_CONV_TIME_MIN_US * 2;
+
+ if (vadc->poll_eoc) {
+ ret = vadc_poll_wait_eoc(vadc, timeout);
+ } else {
+ ret = wait_for_completion_timeout(&vadc->complete, timeout);
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto err_disable;
+ }
+
+ /* Double check conversion status */
+ ret = vadc_poll_wait_eoc(vadc, VADC_CONV_TIME_MIN_US);
+ if (ret)
+ goto err_disable;
+ }
+
+ ret = vadc_read_result(vadc, data);
+
+err_disable:
+ vadc_set_state(vadc, false);
+ if (ret)
+ dev_err(vadc->dev, "conversion failed\n");
+unlock:
+ mutex_unlock(&vadc->lock);
+ return ret;
+}
+
+static int vadc_measure_ref_points(struct vadc_priv *vadc)
+{
+ struct vadc_channel_prop *prop;
+ u16 read_1, read_2;
+ int ret;
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dx = VADC_RATIOMETRIC_RANGE_UV;
+ vadc->graph[VADC_CALIB_ABSOLUTE].dx = VADC_ABSOLUTE_RANGE_UV;
+
+ prop = vadc_get_channel(vadc, VADC_REF_1250MV);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ /* Try with buffered 625mV channel first */
+ prop = vadc_get_channel(vadc, VADC_SPARE1);
+ if (!prop)
+ prop = vadc_get_channel(vadc, VADC_REF_625MV);
+
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_ABSOLUTE].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_ABSOLUTE].gnd = read_2;
+
+ /* Ratiometric calibration */
+ prop = vadc_get_channel(vadc, VADC_VDD_VADC);
+ ret = vadc_do_conversion(vadc, prop, &read_1);
+ if (ret)
+ goto err;
+
+ prop = vadc_get_channel(vadc, VADC_GND_REF);
+ ret = vadc_do_conversion(vadc, prop, &read_2);
+ if (ret)
+ goto err;
+
+ if (read_1 == read_2) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ vadc->graph[VADC_CALIB_RATIOMETRIC].dy = read_1 - read_2;
+ vadc->graph[VADC_CALIB_RATIOMETRIC].gnd = read_2;
+err:
+ if (ret)
+ dev_err(vadc->dev, "measure reference points failed\n");
+
+ return ret;
+}
+
+static s32 vadc_calibrate(struct vadc_priv *vadc,
+ const struct vadc_channel_prop *prop, u16 adc_code)
+{
+ const struct vadc_prescale_ratio *prescale;
+ s32 voltage;
+
+ voltage = adc_code - vadc->graph[prop->calibration].gnd;
+ voltage *= vadc->graph[prop->calibration].dx;
+ voltage = voltage / vadc->graph[prop->calibration].dy;
+
+ if (prop->calibration == VADC_CALIB_ABSOLUTE)
+ voltage += vadc->graph[prop->calibration].dx;
+
+ if (voltage < 0)
+ voltage = 0;
+
+ prescale = &vadc_prescale_ratios[prop->prescale];
+
+ voltage = voltage * prescale->den;
+
+ return voltage / prescale->num;
+}
+
+static int vadc_decimation_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
+ value > VADC_DECIMATION_MAX)
+ return -EINVAL;
+
+ return __ffs64(value / VADC_DECIMATION_MIN);
+}
+
+static int vadc_prescaling_from_dt(u32 num, u32 den)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(vadc_prescale_ratios); pre++)
+ if (vadc_prescale_ratios[pre].num == num &&
+ vadc_prescale_ratios[pre].den == den)
+ break;
+
+ if (pre == ARRAY_SIZE(vadc_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int vadc_hw_settle_time_from_dt(u32 value)
+{
+ if ((value <= 1000 && value % 100) || (value > 1000 && value % 2000))
+ return -EINVAL;
+
+ if (value <= 1000)
+ value /= 100;
+ else
+ value = value / 2000 + 10;
+
+ return value;
+}
+
+static int vadc_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > VADC_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs64(value);
+}
+
+static int vadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ struct vadc_channel_prop *prop;
+ u16 adc_code;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ *val = vadc_calibrate(vadc, prop, adc_code);
+
+ /* 2mV/K, return milli Celsius */
+ *val /= 2;
+ *val -= KELVINMIL_CELSIUSMIL;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ prop = &vadc->chan_props[chan->address];
+ ret = vadc_do_conversion(vadc, prop, &adc_code);
+ if (ret)
+ break;
+
+ *val = vadc_calibrate(vadc, prop, adc_code);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int vadc_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct vadc_priv *vadc = iio_priv(indio_dev);
+ unsigned int i;
+
+ for (i = 0; i < vadc->nchannels; i++)
+ if (vadc->iio_chans[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static const struct iio_info vadc_info = {
+ .read_raw = vadc_read_raw,
+ .of_xlate = vadc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+struct vadc_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+};
+
+#define VADC_CHAN(_dname, _type, _mask, _pre) \
+ [VADC_##_dname] = { \
+ .datasheet_name = __stringify(_dname), \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask \
+ }, \
+
+#define VADC_CHAN_TEMP(_dname, _pre) \
+ VADC_CHAN(_dname, IIO_TEMP, BIT(IIO_CHAN_INFO_PROCESSED), _pre) \
+
+#define VADC_CHAN_VOLT(_dname, _pre) \
+ VADC_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \
+ _pre) \
+
+/*
+ * The array represents all possible ADC channels found in the supported PMICs.
+ * Every index in the array is equal to the channel number per datasheet. The
+ * gaps in the array should be treated as reserved channels.
+ */
+static const struct vadc_channels vadc_chans[] = {
+ VADC_CHAN_VOLT(USBIN, 4)
+ VADC_CHAN_VOLT(DCIN, 4)
+ VADC_CHAN_VOLT(VCHG_SNS, 3)
+ VADC_CHAN_VOLT(SPARE1_03, 1)
+ VADC_CHAN_VOLT(USB_ID_MV, 1)
+ VADC_CHAN_VOLT(VCOIN, 1)
+ VADC_CHAN_VOLT(VBAT_SNS, 1)
+ VADC_CHAN_VOLT(VSYS, 1)
+ VADC_CHAN_TEMP(DIE_TEMP, 0)
+ VADC_CHAN_VOLT(REF_625MV, 0)
+ VADC_CHAN_VOLT(REF_1250MV, 0)
+ VADC_CHAN_VOLT(CHG_TEMP, 0)
+ VADC_CHAN_VOLT(SPARE1, 0)
+ VADC_CHAN_VOLT(SPARE2, 0)
+ VADC_CHAN_VOLT(GND_REF, 0)
+ VADC_CHAN_VOLT(VDD_VADC, 0)
+
+ VADC_CHAN_VOLT(P_MUX1_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX2_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX3_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX4_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX5_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX6_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX7_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX8_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX9_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX10_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX11_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX12_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX13_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX14_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX15_1_1, 0)
+ VADC_CHAN_VOLT(P_MUX16_1_1, 0)
+
+ VADC_CHAN_VOLT(P_MUX1_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX2_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX3_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX4_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX5_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX6_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX7_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX8_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX9_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX10_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX11_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX12_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX13_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX14_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX15_1_3, 1)
+ VADC_CHAN_VOLT(P_MUX16_1_3, 1)
+
+ VADC_CHAN_VOLT(LR_MUX1_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_USB_ID, 0)
+ VADC_CHAN_VOLT(AMUX_PU1, 0)
+ VADC_CHAN_VOLT(AMUX_PU2, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU1_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU1_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU1_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU1_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU1_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU1_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU1_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU1_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU1_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU1_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU2_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU2_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU2_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU2_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU2_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU2_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU2_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU2_XO_THERM, 0)
+
+ VADC_CHAN_VOLT(LR_MUX1_PU1_PU2_BAT_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX2_PU1_PU2_BAT_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_PU1_PU2_XO_THERM, 0)
+ VADC_CHAN_VOLT(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
+ VADC_CHAN_VOLT(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
+ VADC_CHAN_VOLT(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
+ VADC_CHAN_VOLT(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
+ VADC_CHAN_VOLT(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
+ VADC_CHAN_VOLT(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)
+ VADC_CHAN_VOLT(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
+};
+
+static int vadc_get_dt_channel_data(struct device *dev,
+ struct vadc_channel_prop *prop,
+ struct device_node *node)
+{
+ const char *name = node->name;
+ u32 chan, value, varr[2];
+ int ret;
+
+ ret = of_property_read_u32(node, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > VADC_CHAN_MAX || chan < VADC_CHAN_MIN) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ ret = of_property_read_u32(node, "qcom,decimation", &value);
+ if (!ret) {
+ ret = vadc_decimation_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = VADC_DEF_DECIMATION;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = vadc_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ return ret;
+ }
+ prop->prescale = ret;
+ } else {
+ prop->prescale = vadc_chans[prop->channel].prescale_index;
+ }
+
+ ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ if (!ret) {
+ ret = vadc_hw_settle_time_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = vadc_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (of_property_read_bool(node, "qcom,ratiometric"))
+ prop->calibration = VADC_CALIB_RATIOMETRIC;
+ else
+ prop->calibration = VADC_CALIB_ABSOLUTE;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
+{
+ const struct vadc_channels *vadc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct vadc_channel_prop prop;
+ struct device_node *child;
+ unsigned int index = 0;
+ int ret;
+
+ vadc->nchannels = of_get_available_child_count(node);
+ if (!vadc->nchannels)
+ return -EINVAL;
+
+ vadc->iio_chans = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->iio_chans), GFP_KERNEL);
+ if (!vadc->iio_chans)
+ return -ENOMEM;
+
+ vadc->chan_props = devm_kcalloc(vadc->dev, vadc->nchannels,
+ sizeof(*vadc->chan_props), GFP_KERNEL);
+ if (!vadc->chan_props)
+ return -ENOMEM;
+
+ iio_chan = vadc->iio_chans;
+
+ for_each_available_child_of_node(node, child) {
+ ret = vadc_get_dt_channel_data(vadc->dev, &prop, child);
+ if (ret)
+ return ret;
+
+ vadc->chan_props[index] = prop;
+
+ vadc_chan = &vadc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = vadc_chan->datasheet_name;
+ iio_chan->info_mask_separate = vadc_chan->info_mask;
+ iio_chan->type = vadc_chan->type;
+ iio_chan->indexed = 1;
+ iio_chan->address = index++;
+
+ iio_chan++;
+ }
+
+ /* These channels are mandatory, they are used as reference points */
+ if (!vadc_get_channel(vadc, VADC_REF_1250MV)) {
+ dev_err(vadc->dev, "Please define 1.25V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_REF_625MV)) {
+ dev_err(vadc->dev, "Please define 0.625V channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_VDD_VADC)) {
+ dev_err(vadc->dev, "Please define VDD channel\n");
+ return -ENODEV;
+ }
+
+ if (!vadc_get_channel(vadc, VADC_GND_REF)) {
+ dev_err(vadc->dev, "Please define GND channel\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static irqreturn_t vadc_isr(int irq, void *dev_id)
+{
+ struct vadc_priv *vadc = dev_id;
+
+ complete(&vadc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int vadc_check_revision(struct vadc_priv *vadc)
+{
+ u8 val;
+ int ret;
+
+ ret = vadc_read(vadc, VADC_PERPH_TYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_TYPE_ADC) {
+ dev_err(vadc->dev, "%d is not ADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_PERPH_SUBTYPE, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_PERPH_SUBTYPE_VADC) {
+ dev_err(vadc->dev, "%d is not VADC\n", val);
+ return -ENODEV;
+ }
+
+ ret = vadc_read(vadc, VADC_REVISION2, &val);
+ if (ret)
+ return ret;
+
+ if (val < VADC_REVISION2_SUPPORTED_VADC) {
+ dev_err(vadc->dev, "revision %d not supported\n", val);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int vadc_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct vadc_priv *vadc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = of_property_read_u32(node, "reg", &reg);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ vadc = iio_priv(indio_dev);
+ vadc->regmap = regmap;
+ vadc->dev = dev;
+ vadc->base = reg;
+ vadc->are_ref_measured = false;
+ init_completion(&vadc->complete);
+ mutex_init(&vadc->lock);
+
+ ret = vadc_check_revision(vadc);
+ if (ret)
+ return ret;
+
+ ret = vadc_get_dt_data(vadc, node);
+ if (ret)
+ return ret;
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ vadc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, vadc_isr, 0,
+ "spmi-vadc", vadc);
+ if (ret)
+ return ret;
+ }
+
+ ret = vadc_reset(vadc);
+ if (ret) {
+ dev_err(dev, "reset failed\n");
+ return ret;
+ }
+
+ ret = vadc_measure_ref_points(vadc);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &vadc_info;
+ indio_dev->channels = vadc->iio_chans;
+ indio_dev->num_channels = vadc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id vadc_match_table[] = {
+ { .compatible = "qcom,spmi-vadc" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vadc_match_table);
+
+static struct platform_driver vadc_driver = {
+ .driver = {
+ .name = "qcom-spmi-vadc",
+ .of_match_table = vadc_match_table,
+ },
+ .probe = vadc_probe,
+};
+module_platform_driver(vadc_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-vadc");
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC voltage ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Stanimir Varbanov <svarbanov@mm-sol.com>");
+MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>");
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index e074a0b..8d4e019 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -18,13 +18,13 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/of_device.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)
@@ -38,15 +38,22 @@
#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_data {
+ int num_bits;
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ unsigned long clk_rate;
+};
+
struct rockchip_saradc {
void __iomem *regs;
struct clk *pclk;
struct clk *clk;
struct completion completion;
struct regulator *vref;
+ const struct rockchip_saradc_data *data;
u16 last_val;
};
@@ -90,7 +97,7 @@ static int rockchip_saradc_read_raw(struct iio_dev *indio_dev,
}
*val = ret / 1000;
- *val2 = SARADC_BITS;
+ *val2 = info->data->num_bits;
return IIO_VAL_FRACTIONAL_LOG2;
default:
return -EINVAL;
@@ -103,7 +110,7 @@ static irqreturn_t rockchip_saradc_isr(int irq, void *dev_id)
/* Read value */
info->last_val = readl_relaxed(info->regs + SARADC_DATA);
- info->last_val &= SARADC_DATA_MASK;
+ info->last_val &= GENMASK(info->data->num_bits - 1, 0);
/* Clear irq & power down adc */
writel_relaxed(0, info->regs + SARADC_CTRL);
@@ -133,12 +140,44 @@ static const struct iio_chan_spec rockchip_saradc_iio_channels[] = {
ADC_CHANNEL(2, "adc2"),
};
+static const struct rockchip_saradc_data saradc_data = {
+ .num_bits = 10,
+ .channels = rockchip_saradc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_saradc_iio_channels),
+ .clk_rate = 1000000,
+};
+
+static const struct iio_chan_spec rockchip_rk3066_tsadc_iio_channels[] = {
+ ADC_CHANNEL(0, "adc0"),
+ ADC_CHANNEL(1, "adc1"),
+};
+
+static const struct rockchip_saradc_data rk3066_tsadc_data = {
+ .num_bits = 12,
+ .channels = rockchip_rk3066_tsadc_iio_channels,
+ .num_channels = ARRAY_SIZE(rockchip_rk3066_tsadc_iio_channels),
+ .clk_rate = 50000,
+};
+
+static const struct of_device_id rockchip_saradc_match[] = {
+ {
+ .compatible = "rockchip,saradc",
+ .data = &saradc_data,
+ }, {
+ .compatible = "rockchip,rk3066-tsadc",
+ .data = &rk3066_tsadc_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_saradc_match);
+
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;
+ const struct of_device_id *match;
int ret;
int irq;
@@ -152,6 +191,9 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
}
info = iio_priv(indio_dev);
+ match = of_match_device(rockchip_saradc_match, &pdev->dev);
+ info->data = match->data;
+
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
info->regs = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(info->regs))
@@ -192,10 +234,10 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
}
/*
- * Use a default of 1MHz for the converter clock.
+ * Use a default value for the converter clock.
* This may become user-configurable in the future.
*/
- ret = clk_set_rate(info->clk, 1000000);
+ ret = clk_set_rate(info->clk, info->data->clk_rate);
if (ret < 0) {
dev_err(&pdev->dev, "failed to set adc clk rate, %d\n", ret);
return ret;
@@ -227,8 +269,8 @@ static int rockchip_saradc_probe(struct platform_device *pdev)
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);
+ indio_dev->channels = info->data->channels;
+ indio_dev->num_channels = info->data->num_channels;
ret = iio_device_register(indio_dev);
if (ret)
@@ -296,18 +338,11 @@ static int rockchip_saradc_resume(struct device *dev)
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,
},
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index b730864..a0e7161 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -86,19 +86,18 @@ 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;
+ int i, steps = 0;
/*
* 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
+ * Steps forwards i.e. from 0 towards 16 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;
@@ -189,12 +188,11 @@ static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
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)
+ for_each_set_bit(bit, indio_dev->active_scan_mask, adc_dev->channels)
enb |= (get_adc_step_bit(adc_dev, bit) << 1);
adc_dev->buffer_en_ch_steps = enb;
@@ -250,7 +248,7 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
- buffer = iio_kfifo_allocate(indio_dev);
+ buffer = iio_kfifo_allocate();
if (!buffer)
return -ENOMEM;
@@ -264,16 +262,8 @@ static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev,
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;
@@ -285,7 +275,6 @@ static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev)
free_irq(adc_dev->mfd_tscadc->irq, indio_dev);
iio_kfifo_free(indio_dev->buffer);
- iio_buffer_unregister(indio_dev);
}
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index 4a10ae9..56292ae 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -91,7 +91,7 @@
#define VF610_ADC_CAL 0x80
/* Other field define */
-#define VF610_ADC_ADCHC(x) ((x) & 0xF)
+#define VF610_ADC_ADCHC(x) ((x) & 0x1F)
#define VF610_ADC_AIEN (0x1 << 7)
#define VF610_ADC_CONV_DISABLE 0x1F
#define VF610_ADC_HS_COCO0 0x1
@@ -141,9 +141,13 @@ struct vf610_adc {
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, \
@@ -153,6 +157,12 @@ struct vf610_adc {
BIT(IIO_CHAN_INFO_SAMP_FREQ), \
}
+#define VF610_ADC_TEMPERATURE_CHAN(_idx, _chan_type) { \
+ .type = (_chan_type), \
+ .channel = (_idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(0, IIO_VOLTAGE),
VF610_ADC_CHAN(1, IIO_VOLTAGE),
@@ -170,38 +180,51 @@ static const struct iio_chan_spec vf610_adc_iio_channels[] = {
VF610_ADC_CHAN(13, IIO_VOLTAGE),
VF610_ADC_CHAN(14, IIO_VOLTAGE),
VF610_ADC_CHAN(15, IIO_VOLTAGE),
+ VF610_ADC_TEMPERATURE_CHAN(26, IIO_TEMP),
/* sentinel */
};
-/*
- * ADC sample frequency, unit is ADCK cycles.
- * ADC 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
- *
- * By default, enable 12 bit resolution mode, clock source
- * set to ipg clock, So get below frequency group:
- */
-static const u32 vf610_sample_freq_avail[5] =
-{1941176, 559332, 286957, 145374, 73171};
+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 */
- info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET;
- info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET;
+ adc_feature->clk_sel = VF610_ADCIOC_BUSCLK_SET;
+ adc_feature->vol_ref = VF610_ADCIOC_VR_VREF_SET;
- info->adc_feature.calibration = true;
- info->adc_feature.ovwren = true;
+ adc_feature->calibration = true;
+ adc_feature->ovwren = true;
- info->adc_feature.clk_div = 1;
- info->adc_feature.res_mode = 12;
- info->adc_feature.sample_rate = 1;
- info->adc_feature.lpm = 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)
@@ -252,7 +275,6 @@ static void vf610_adc_cfg_post_set(struct vf610_adc *info)
static void vf610_adc_calibration(struct vf610_adc *info)
{
int adc_gc, hc_cfg;
- int timeout;
if (!info->adc_feature.calibration)
return;
@@ -264,9 +286,7 @@ static void vf610_adc_calibration(struct vf610_adc *info)
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)
+ if (!wait_for_completion_timeout(&info->completion, VF610_ADC_TIMEOUT))
dev_err(info->dev, "Timeout for adc calibration\n");
adc_gc = readl(info->regs + VF610_REG_ADC_GS);
@@ -283,12 +303,10 @@ static void vf610_adc_cfg_set(struct vf610_adc *info)
cfg_data = readl(info->regs + VF610_REG_ADC_CFG);
- /* low power configuration */
cfg_data &= ~VF610_ADC_ADLPC_EN;
if (adc_feature->lpm)
cfg_data |= VF610_ADC_ADLPC_EN;
- /* disable high speed */
cfg_data &= ~VF610_ADC_ADHSC_EN;
writel(cfg_data, info->regs + VF610_REG_ADC_CFG);
@@ -428,10 +446,27 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171");
+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_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
NULL
};
@@ -451,6 +486,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
reinit_completion(&info->completion);
@@ -468,7 +504,23 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
return ret;
}
- *val = info->value;
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *val = info->value;
+ break;
+ case IIO_TEMP:
+ /*
+ * Calculate in degree Celsius times 1000
+ * Using sensor slope of 1.84 mV/°C and
+ * V at 25°C of 696 mV
+ */
+ *val = 25000 - ((int)info->value - 864) * 1000000 / 1840;
+ break;
+ default:
+ mutex_unlock(&indio_dev->mlock);
+ return -EINVAL;
+ }
+
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
@@ -478,7 +530,7 @@ static int vf610_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = vf610_sample_freq_avail[info->adc_feature.sample_rate];
+ *val = info->sample_freq_avail[info->adc_feature.sample_rate];
*val2 = 0;
return IIO_VAL_INT;
@@ -501,9 +553,9 @@ static int vf610_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
for (i = 0;
- i < ARRAY_SIZE(vf610_sample_freq_avail);
+ i < ARRAY_SIZE(info->sample_freq_avail);
i++)
- if (val == vf610_sample_freq_avail[i]) {
+ if (val == info->sample_freq_avail[i]) {
info->adc_feature.sample_rate = i;
vf610_adc_sample_set(info);
return 0;
@@ -569,9 +621,9 @@ static int vf610_adc_probe(struct platform_device *pdev)
return PTR_ERR(info->regs);
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
+ if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
- return -EINVAL;
+ return irq;
}
ret = devm_request_irq(info->dev, irq,
@@ -586,8 +638,7 @@ static int vf610_adc_probe(struct platform_device *pdev)
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;
+ return PTR_ERR(info->clk);
}
info->vref = devm_regulator_get(&pdev->dev, "vref");
@@ -681,17 +732,19 @@ static int vf610_adc_resume(struct device *dev)
ret = clk_prepare_enable(info->clk);
if (ret)
- return ret;
+ goto disable_reg;
vf610_adc_hw_init(info);
return 0;
+
+disable_reg:
+ regulator_disable(info->vref);
+ return ret;
}
#endif
-static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops,
- vf610_adc_suspend,
- vf610_adc_resume);
+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,
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
index ba6f6a9..c0d364e 100644
--- a/drivers/iio/amplifiers/ad8366.c
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -31,7 +31,7 @@ struct ad8366_state {
};
static int ad8366_write(struct iio_dev *indio_dev,
- unsigned char ch_a, char unsigned ch_b)
+ unsigned char ch_a, unsigned char ch_b)
{
struct ad8366_state *st = iio_priv(indio_dev);
int ret;
@@ -166,7 +166,7 @@ static int ad8366_probe(struct spi_device *spi)
if (ret)
goto error_disable_reg;
- ad8366_write(indio_dev, 0 , 0);
+ ad8366_write(indio_dev, 0, 0);
return 0;
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 0b6e97d1..790f106 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -3,4 +3,5 @@
#
source "drivers/iio/common/hid-sensors/Kconfig"
+source "drivers/iio/common/ssp_sensors/Kconfig"
source "drivers/iio/common/st_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 3112df0..b1e4d9c 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -8,4 +8,5 @@
# When adding new entries keep the list in alphabetical order
obj-y += hid-sensors/
+obj-y += ssp_sensors/
obj-y += st_sensors/
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index 25b01e1..e81f434 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -153,8 +153,8 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st)
int ret;
ret = sensor_hub_get_feature(st->hsdev,
- st->poll.report_id,
- st->poll.index, &value);
+ st->poll.report_id,
+ st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0) {
return -EINVAL;
@@ -174,8 +174,8 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st,
int ret;
ret = sensor_hub_get_feature(st->hsdev,
- st->poll.report_id,
- st->poll.index, &value);
+ st->poll.report_id,
+ st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@@ -212,9 +212,8 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
else
value = 0;
}
- ret = sensor_hub_set_feature(st->hsdev,
- st->poll.report_id,
- st->poll.index, value);
+ ret = sensor_hub_set_feature(st->hsdev, st->poll.report_id,
+ st->poll.index, sizeof(value), &value);
if (ret < 0 || value < 0)
ret = -EINVAL;
@@ -229,8 +228,9 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st,
int ret;
ret = sensor_hub_get_feature(st->hsdev,
- st->sensitivity.report_id,
- st->sensitivity.index, &value);
+ st->sensitivity.report_id,
+ st->sensitivity.index, sizeof(value),
+ &value);
if (ret < 0 || value < 0) {
*val1 = *val2 = 0;
return -EINVAL;
@@ -253,9 +253,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
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);
+ ret = sensor_hub_set_feature(st->hsdev, st->sensitivity.report_id,
+ st->sensitivity.index, sizeof(value),
+ &value);
if (ret < 0 || value < 0)
ret = -EINVAL;
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 92068cd..610fc98 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -22,16 +22,18 @@
#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/trigger.h>
#include <linux/iio/sysfs.h>
#include "hid-sensor-trigger.h"
-int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
+static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state)
{
int state_val;
int report_val;
+ s32 poll_value = 0;
if (state) {
if (sensor_hub_device_open(st->hsdev))
@@ -47,6 +49,8 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
st->report_state.report_id,
st->report_state.index,
HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM);
+
+ poll_value = hid_sensor_read_poll_value(st);
} else {
if (!atomic_dec_and_test(&st->data_ready))
return 0;
@@ -64,24 +68,51 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
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);
+ st->power_state.index, sizeof(state_val),
+ &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);
+ st->report_state.index,
+ sizeof(report_val),
+ &report_val);
}
sensor_hub_get_feature(st->hsdev, st->power_state.report_id,
- st->power_state.index,
- &state_val);
+ st->power_state.index,
+ sizeof(state_val), &state_val);
+ if (state && poll_value)
+ msleep_interruptible(poll_value * 2);
+
return 0;
}
EXPORT_SYMBOL(hid_sensor_power_state);
+int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
+{
+#ifdef CONFIG_PM
+ int ret;
+
+ if (state)
+ ret = pm_runtime_get_sync(&st->pdev->dev);
+ else {
+ pm_runtime_mark_last_busy(&st->pdev->dev);
+ ret = pm_runtime_put_autosuspend(&st->pdev->dev);
+ }
+ if (ret < 0) {
+ if (state)
+ pm_runtime_put_noidle(&st->pdev->dev);
+ return ret;
+ }
+
+ return 0;
+#else
+ return _hid_sensor_power_state(st, state);
+#endif
+}
+
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@@ -125,8 +156,21 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
attrb->trigger = trig;
indio_dev->trig = iio_trigger_get(trig);
- return ret;
+ ret = pm_runtime_set_active(&indio_dev->dev);
+ if (ret)
+ goto error_unreg_trigger;
+ iio_device_set_drvdata(indio_dev, attrb);
+ pm_suspend_ignore_children(&attrb->pdev->dev, true);
+ pm_runtime_enable(&attrb->pdev->dev);
+ /* Default to 3 seconds, but can be changed from sysfs */
+ pm_runtime_set_autosuspend_delay(&attrb->pdev->dev,
+ 3000);
+ pm_runtime_use_autosuspend(&attrb->pdev->dev);
+
+ return ret;
+error_unreg_trigger:
+ iio_trigger_unregister(trig);
error_free_trig:
iio_trigger_free(trig);
error_ret:
@@ -134,6 +178,34 @@ error_ret:
}
EXPORT_SYMBOL(hid_sensor_setup_trigger);
+#ifdef CONFIG_PM
+static int hid_sensor_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+
+ return _hid_sensor_power_state(attrb, false);
+}
+
+static int hid_sensor_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+
+ return _hid_sensor_power_state(attrb, true);
+}
+
+#endif
+
+const struct dev_pm_ops hid_sensor_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
+ SET_RUNTIME_PM_OPS(hid_sensor_suspend,
+ hid_sensor_resume, NULL)
+};
+EXPORT_SYMBOL(hid_sensor_pm_ops);
+
MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
MODULE_DESCRIPTION("HID Sensor trigger processing");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
index 0f8e78c..9f4713f 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -19,6 +19,11 @@
#ifndef _HID_SENSOR_TRIGGER_H
#define _HID_SENSOR_TRIGGER_H
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+
+extern const struct dev_pm_ops hid_sensor_pm_ops;
+
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);
diff --git a/drivers/iio/common/ssp_sensors/Kconfig b/drivers/iio/common/ssp_sensors/Kconfig
new file mode 100644
index 0000000..0ea4faf
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/Kconfig
@@ -0,0 +1,26 @@
+#
+# SSP sensor drivers and commons configuration
+#
+menu "SSP Sensor Common"
+
+config IIO_SSP_SENSORS_COMMONS
+ tristate "Commons for all SSP Sensor IIO drivers"
+ depends on IIO_SSP_SENSORHUB
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ help
+ Say yes here to build commons for SSP sensors.
+ To compile this as a module, choose M here: the module
+ will be called ssp_iio.
+
+config IIO_SSP_SENSORHUB
+ tristate "Samsung Sensorhub driver"
+ depends on SPI
+ select MFD_CORE
+ help
+ SSP driver for sensorhub.
+ If you say yes here you get ssp support for sensorhub.
+ To compile this driver as a module, choose M here: the
+ module will be called sensorhub.
+
+endmenu
diff --git a/drivers/iio/common/ssp_sensors/Makefile b/drivers/iio/common/ssp_sensors/Makefile
new file mode 100644
index 0000000..1e0389e
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for SSP sensor drivers and commons.
+#
+
+sensorhub-objs := ssp_dev.o ssp_spi.o
+obj-$(CONFIG_IIO_SSP_SENSORHUB) += sensorhub.o
+
+obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_iio.o
diff --git a/drivers/iio/common/ssp_sensors/ssp.h b/drivers/iio/common/ssp_sensors/ssp.h
new file mode 100644
index 0000000..b910e91
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp.h
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SSP_SENSORHUB_H__
+#define __SSP_SENSORHUB_H__
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/iio/common/ssp_sensors.h>
+#include <linux/iio/iio.h>
+#include <linux/spi/spi.h>
+
+#define SSP_DEVICE_ID 0x55
+
+#ifdef SSP_DBG
+#define ssp_dbg(format, ...) pr_info("[SSP] "format, ##__VA_ARGS__)
+#else
+#define ssp_dbg(format, ...)
+#endif
+
+#define SSP_SW_RESET_TIME 3000
+/* Sensor polling in ms */
+#define SSP_DEFAULT_POLLING_DELAY 200
+#define SSP_DEFAULT_RETRIES 3
+#define SSP_DATA_PACKET_SIZE 960
+#define SSP_HEADER_BUFFER_SIZE 4
+
+enum {
+ SSP_KERNEL_BINARY = 0,
+ SSP_KERNEL_CRASHED_BINARY,
+};
+
+enum {
+ SSP_INITIALIZATION_STATE = 0,
+ SSP_NO_SENSOR_STATE,
+ SSP_ADD_SENSOR_STATE,
+ SSP_RUNNING_SENSOR_STATE,
+};
+
+/* Firmware download STATE */
+enum {
+ SSP_FW_DL_STATE_FAIL = -1,
+ SSP_FW_DL_STATE_NONE = 0,
+ SSP_FW_DL_STATE_NEED_TO_SCHEDULE,
+ SSP_FW_DL_STATE_SCHEDULED,
+ SSP_FW_DL_STATE_DOWNLOADING,
+ SSP_FW_DL_STATE_SYNC,
+ SSP_FW_DL_STATE_DONE,
+};
+
+#define SSP_INVALID_REVISION 99999
+#define SSP_INVALID_REVISION2 0xffffff
+
+/* AP -> SSP Instruction */
+#define SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD 0xa1
+#define SSP_MSG2SSP_INST_BYPASS_SENSOR_RM 0xa2
+#define SSP_MSG2SSP_INST_REMOVE_ALL 0xa3
+#define SSP_MSG2SSP_INST_CHANGE_DELAY 0xa4
+#define SSP_MSG2SSP_INST_LIBRARY_ADD 0xb1
+#define SSP_MSG2SSP_INST_LIBRARY_REMOVE 0xb2
+#define SSP_MSG2SSP_INST_LIB_NOTI 0xb4
+#define SSP_MSG2SSP_INST_LIB_DATA 0xc1
+
+#define SSP_MSG2SSP_AP_MCU_SET_GYRO_CAL 0xcd
+#define SSP_MSG2SSP_AP_MCU_SET_ACCEL_CAL 0xce
+#define SSP_MSG2SSP_AP_STATUS_SHUTDOWN 0xd0
+#define SSP_MSG2SSP_AP_STATUS_WAKEUP 0xd1
+#define SSP_MSG2SSP_AP_STATUS_SLEEP 0xd2
+#define SSP_MSG2SSP_AP_STATUS_RESUME 0xd3
+#define SSP_MSG2SSP_AP_STATUS_SUSPEND 0xd4
+#define SSP_MSG2SSP_AP_STATUS_RESET 0xd5
+#define SSP_MSG2SSP_AP_STATUS_POW_CONNECTED 0xd6
+#define SSP_MSG2SSP_AP_STATUS_POW_DISCONNECTED 0xd7
+#define SSP_MSG2SSP_AP_TEMPHUMIDITY_CAL_DONE 0xda
+#define SSP_MSG2SSP_AP_MCU_SET_DUMPMODE 0xdb
+#define SSP_MSG2SSP_AP_MCU_DUMP_CHECK 0xdc
+#define SSP_MSG2SSP_AP_MCU_BATCH_FLUSH 0xdd
+#define SSP_MSG2SSP_AP_MCU_BATCH_COUNT 0xdf
+
+#define SSP_MSG2SSP_AP_WHOAMI 0x0f
+#define SSP_MSG2SSP_AP_FIRMWARE_REV 0xf0
+#define SSP_MSG2SSP_AP_SENSOR_FORMATION 0xf1
+#define SSP_MSG2SSP_AP_SENSOR_PROXTHRESHOLD 0xf2
+#define SSP_MSG2SSP_AP_SENSOR_BARCODE_EMUL 0xf3
+#define SSP_MSG2SSP_AP_SENSOR_SCANNING 0xf4
+#define SSP_MSG2SSP_AP_SET_MAGNETIC_HWOFFSET 0xf5
+#define SSP_MSG2SSP_AP_GET_MAGNETIC_HWOFFSET 0xf6
+#define SSP_MSG2SSP_AP_SENSOR_GESTURE_CURRENT 0xf7
+#define SSP_MSG2SSP_AP_GET_THERM 0xf8
+#define SSP_MSG2SSP_AP_GET_BIG_DATA 0xf9
+#define SSP_MSG2SSP_AP_SET_BIG_DATA 0xfa
+#define SSP_MSG2SSP_AP_START_BIG_DATA 0xfb
+#define SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX 0xfd
+#define SSP_MSG2SSP_AP_SENSOR_TILT 0xea
+#define SSP_MSG2SSP_AP_MCU_SET_TIME 0xfe
+#define SSP_MSG2SSP_AP_MCU_GET_TIME 0xff
+
+#define SSP_MSG2SSP_AP_FUSEROM 0x01
+
+/* voice data */
+#define SSP_TYPE_WAKE_UP_VOICE_SERVICE 0x01
+#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_AM 0x01
+#define SSP_TYPE_WAKE_UP_VOICE_SOUND_SOURCE_GRAMMER 0x02
+
+/* Factory Test */
+#define SSP_ACCELEROMETER_FACTORY 0x80
+#define SSP_GYROSCOPE_FACTORY 0x81
+#define SSP_GEOMAGNETIC_FACTORY 0x82
+#define SSP_PRESSURE_FACTORY 0x85
+#define SSP_GESTURE_FACTORY 0x86
+#define SSP_TEMPHUMIDITY_CRC_FACTORY 0x88
+#define SSP_GYROSCOPE_TEMP_FACTORY 0x8a
+#define SSP_GYROSCOPE_DPS_FACTORY 0x8b
+#define SSP_MCU_FACTORY 0x8c
+#define SSP_MCU_SLEEP_FACTORY 0x8d
+
+/* SSP -> AP ACK about write CMD */
+#define SSP_MSG_ACK 0x80 /* ACK from SSP to AP */
+#define SSP_MSG_NAK 0x70 /* NAK from SSP to AP */
+
+struct ssp_sensorhub_info {
+ char *fw_name;
+ char *fw_crashed_name;
+ unsigned int fw_rev;
+ const u8 * const mag_table;
+ const unsigned int mag_length;
+};
+
+/* ssp_msg options bit */
+#define SSP_RW 0
+#define SSP_INDEX 3
+
+#define SSP_AP2HUB_READ 0
+#define SSP_AP2HUB_WRITE 1
+#define SSP_HUB2AP_WRITE 2
+#define SSP_AP2HUB_READY 3
+#define SSP_AP2HUB_RETURN 4
+
+/**
+ * struct ssp_data - ssp platformdata structure
+ * @spi: spi device
+ * @sensorhub_info: info about sensorhub board specific features
+ * @wdt_timer: watchdog timer
+ * @work_wdt: watchdog work
+ * @work_firmware: firmware upgrade work queue
+ * @work_refresh: refresh work queue for reset request from MCU
+ * @shut_down: shut down flag
+ * @mcu_dump_mode: mcu dump mode for debug
+ * @time_syncing: time syncing indication flag
+ * @timestamp: previous time in ns calculated for time syncing
+ * @check_status: status table for each sensor
+ * @com_fail_cnt: communication fail count
+ * @reset_cnt: reset count
+ * @timeout_cnt: timeout count
+ * @available_sensors: available sensors seen by sensorhub (bit array)
+ * @cur_firm_rev: cached current firmware revision
+ * @last_resume_state: last AP resume/suspend state used to handle the PM
+ * state of ssp
+ * @last_ap_state: (obsolete) sleep notification for MCU
+ * @sensor_enable: sensor enable mask
+ * @delay_buf: data acquisition intervals table
+ * @batch_latency_buf: yet unknown but existing in communication protocol
+ * @batch_opt_buf: yet unknown but existing in communication protocol
+ * @accel_position: yet unknown but existing in communication protocol
+ * @mag_position: yet unknown but existing in communication protocol
+ * @fw_dl_state: firmware download state
+ * @comm_lock: lock protecting the handshake
+ * @pending_lock: lock protecting pending list and completion
+ * @mcu_reset_gpio: mcu reset line
+ * @ap_mcu_gpio: ap to mcu gpio line
+ * @mcu_ap_gpio: mcu to ap gpio line
+ * @pending_list: pending list for messages queued to be sent/read
+ * @sensor_devs: registered IIO devices table
+ * @enable_refcount: enable reference count for wdt (watchdog timer)
+ * @header_buffer: cache aligned buffer for packet header
+ */
+struct ssp_data {
+ struct spi_device *spi;
+ struct ssp_sensorhub_info *sensorhub_info;
+ struct timer_list wdt_timer;
+ struct work_struct work_wdt;
+ struct delayed_work work_refresh;
+
+ bool shut_down;
+ bool mcu_dump_mode;
+ bool time_syncing;
+ int64_t timestamp;
+
+ int check_status[SSP_SENSOR_MAX];
+
+ unsigned int com_fail_cnt;
+ unsigned int reset_cnt;
+ unsigned int timeout_cnt;
+
+ unsigned int available_sensors;
+ unsigned int cur_firm_rev;
+
+ char last_resume_state;
+ char last_ap_state;
+
+ unsigned int sensor_enable;
+ u32 delay_buf[SSP_SENSOR_MAX];
+ s32 batch_latency_buf[SSP_SENSOR_MAX];
+ s8 batch_opt_buf[SSP_SENSOR_MAX];
+
+ int accel_position;
+ int mag_position;
+ int fw_dl_state;
+
+ struct mutex comm_lock;
+ struct mutex pending_lock;
+
+ int mcu_reset_gpio;
+ int ap_mcu_gpio;
+ int mcu_ap_gpio;
+
+ struct list_head pending_list;
+
+ struct iio_dev *sensor_devs[SSP_SENSOR_MAX];
+ atomic_t enable_refcount;
+
+ __le16 header_buffer[SSP_HEADER_BUFFER_SIZE / sizeof(__le16)]
+ ____cacheline_aligned;
+};
+
+void ssp_clean_pending_list(struct ssp_data *data);
+
+int ssp_command(struct ssp_data *data, char command, int arg);
+
+int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
+ u8 *send_buf, u8 length);
+
+int ssp_irq_msg(struct ssp_data *data);
+
+int ssp_get_chipid(struct ssp_data *data);
+
+int ssp_set_magnetic_matrix(struct ssp_data *data);
+
+unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data);
+
+unsigned int ssp_get_firmware_rev(struct ssp_data *data);
+
+int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay);
+
+#endif /* __SSP_SENSORHUB_H__ */
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
new file mode 100644
index 0000000..9a40097
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_platform.h>
+#include "ssp.h"
+
+#define SSP_WDT_TIME 10000
+#define SSP_LIMIT_RESET_CNT 20
+#define SSP_LIMIT_TIMEOUT_CNT 3
+
+/* It is possible that it is max clk rate for version 1.0 of bootcode */
+#define SSP_BOOT_SPI_HZ 400000
+
+/*
+ * These fields can look enigmatic but this structure is used mainly to flat
+ * some values and depends on command type.
+ */
+struct ssp_instruction {
+ __le32 a;
+ __le32 b;
+ u8 c;
+} __attribute__((__packed__));
+
+static const u8 ssp_magnitude_table[] = {110, 85, 171, 71, 203, 195, 0, 67,
+ 208, 56, 175, 244, 206, 213, 0, 92, 250, 0, 55, 48, 189, 252, 171,
+ 243, 13, 45, 250};
+
+static const struct ssp_sensorhub_info ssp_rinato_info = {
+ .fw_name = "ssp_B2.fw",
+ .fw_crashed_name = "ssp_crashed.fw",
+ .fw_rev = 14052300,
+ .mag_table = ssp_magnitude_table,
+ .mag_length = ARRAY_SIZE(ssp_magnitude_table),
+};
+
+static const struct ssp_sensorhub_info ssp_thermostat_info = {
+ .fw_name = "thermostat_B2.fw",
+ .fw_crashed_name = "ssp_crashed.fw",
+ .fw_rev = 14080600,
+ .mag_table = ssp_magnitude_table,
+ .mag_length = ARRAY_SIZE(ssp_magnitude_table),
+};
+
+static const struct mfd_cell sensorhub_sensor_devs[] = {
+ {
+ .name = "ssp-accelerometer",
+ },
+ {
+ .name = "ssp-gyroscope",
+ },
+};
+
+static void ssp_toggle_mcu_reset_gpio(struct ssp_data *data)
+{
+ gpio_set_value(data->mcu_reset_gpio, 0);
+ usleep_range(1000, 1200);
+ gpio_set_value(data->mcu_reset_gpio, 1);
+ msleep(50);
+}
+
+static void ssp_sync_available_sensors(struct ssp_data *data)
+{
+ int i, ret;
+
+ for (i = 0; i < SSP_SENSOR_MAX; ++i) {
+ if (data->available_sensors & BIT(i)) {
+ ret = ssp_enable_sensor(data, i, data->delay_buf[i]);
+ if (ret < 0) {
+ dev_err(&data->spi->dev,
+ "Sync sensor nr: %d fail\n", i);
+ continue;
+ }
+ }
+ }
+
+ ret = ssp_command(data, SSP_MSG2SSP_AP_MCU_SET_DUMPMODE,
+ data->mcu_dump_mode);
+ if (ret < 0)
+ dev_err(&data->spi->dev,
+ "SSP_MSG2SSP_AP_MCU_SET_DUMPMODE failed\n");
+}
+
+static void ssp_enable_mcu(struct ssp_data *data, bool enable)
+{
+ dev_info(&data->spi->dev, "current shutdown = %d, old = %d\n", enable,
+ data->shut_down);
+
+ if (enable && data->shut_down) {
+ data->shut_down = false;
+ enable_irq(data->spi->irq);
+ enable_irq_wake(data->spi->irq);
+ } else if (!enable && !data->shut_down) {
+ data->shut_down = true;
+ disable_irq(data->spi->irq);
+ disable_irq_wake(data->spi->irq);
+ } else {
+ dev_warn(&data->spi->dev, "current shutdown = %d, old = %d\n",
+ enable, data->shut_down);
+ }
+}
+
+/*
+ * This function is the first one which communicates with the mcu so it is
+ * possible that the first attempt will fail
+ */
+static int ssp_check_fwbl(struct ssp_data *data)
+{
+ int retries = 0;
+
+ while (retries++ < 5) {
+ data->cur_firm_rev = ssp_get_firmware_rev(data);
+ if (data->cur_firm_rev == SSP_INVALID_REVISION ||
+ data->cur_firm_rev == SSP_INVALID_REVISION2) {
+ dev_warn(&data->spi->dev,
+ "Invalid revision, trying %d time\n", retries);
+ } else {
+ break;
+ }
+ }
+
+ if (data->cur_firm_rev == SSP_INVALID_REVISION ||
+ data->cur_firm_rev == SSP_INVALID_REVISION2) {
+ dev_err(&data->spi->dev, "SSP_INVALID_REVISION\n");
+ return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
+ }
+
+ dev_info(&data->spi->dev,
+ "MCU Firm Rev : Old = %8u, New = %8u\n",
+ data->cur_firm_rev,
+ data->sensorhub_info->fw_rev);
+
+ if (data->cur_firm_rev != data->sensorhub_info->fw_rev)
+ return SSP_FW_DL_STATE_NEED_TO_SCHEDULE;
+
+ return SSP_FW_DL_STATE_NONE;
+}
+
+static void ssp_reset_mcu(struct ssp_data *data)
+{
+ ssp_enable_mcu(data, false);
+ ssp_clean_pending_list(data);
+ ssp_toggle_mcu_reset_gpio(data);
+ ssp_enable_mcu(data, true);
+}
+
+static void ssp_wdt_work_func(struct work_struct *work)
+{
+ struct ssp_data *data = container_of(work, struct ssp_data, work_wdt);
+
+ dev_err(&data->spi->dev, "%s - Sensor state: 0x%x, RC: %u, CC: %u\n",
+ __func__, data->available_sensors, data->reset_cnt,
+ data->com_fail_cnt);
+
+ ssp_reset_mcu(data);
+ data->com_fail_cnt = 0;
+ data->timeout_cnt = 0;
+}
+
+static void ssp_wdt_timer_func(unsigned long ptr)
+{
+ struct ssp_data *data = (struct ssp_data *)ptr;
+
+ switch (data->fw_dl_state) {
+ case SSP_FW_DL_STATE_FAIL:
+ case SSP_FW_DL_STATE_DOWNLOADING:
+ case SSP_FW_DL_STATE_SYNC:
+ goto _mod;
+ }
+
+ if (data->timeout_cnt > SSP_LIMIT_TIMEOUT_CNT ||
+ data->com_fail_cnt > SSP_LIMIT_RESET_CNT)
+ queue_work(system_power_efficient_wq, &data->work_wdt);
+_mod:
+ mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
+}
+
+static void ssp_enable_wdt_timer(struct ssp_data *data)
+{
+ mod_timer(&data->wdt_timer, jiffies + msecs_to_jiffies(SSP_WDT_TIME));
+}
+
+static void ssp_disable_wdt_timer(struct ssp_data *data)
+{
+ del_timer_sync(&data->wdt_timer);
+ cancel_work_sync(&data->work_wdt);
+}
+
+/**
+ * ssp_get_sensor_delay() - gets sensor data acquisition period
+ * @data: sensorhub structure
+ * @type: SSP sensor type
+ *
+ * Returns acquisition period in ms
+ */
+u32 ssp_get_sensor_delay(struct ssp_data *data, enum ssp_sensor_type type)
+{
+ return data->delay_buf[type];
+}
+EXPORT_SYMBOL(ssp_get_sensor_delay);
+
+/**
+ * ssp_enable_sensor() - enables data acquisition for sensor
+ * @data: sensorhub structure
+ * @type: SSP sensor type
+ * @delay: delay in ms
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_enable_sensor(struct ssp_data *data, enum ssp_sensor_type type,
+ u32 delay)
+{
+ int ret;
+ struct ssp_instruction to_send;
+
+ to_send.a = cpu_to_le32(delay);
+ to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
+ to_send.c = data->batch_opt_buf[type];
+
+ switch (data->check_status[type]) {
+ case SSP_INITIALIZATION_STATE:
+ /* do calibration step, now just enable */
+ case SSP_ADD_SENSOR_STATE:
+ ret = ssp_send_instruction(data,
+ SSP_MSG2SSP_INST_BYPASS_SENSOR_ADD,
+ type,
+ (u8 *)&to_send, sizeof(to_send));
+ if (ret < 0) {
+ dev_err(&data->spi->dev, "Enabling sensor failed\n");
+ data->check_status[type] = SSP_NO_SENSOR_STATE;
+ goto derror;
+ }
+
+ data->sensor_enable |= BIT(type);
+ data->check_status[type] = SSP_RUNNING_SENSOR_STATE;
+ break;
+ case SSP_RUNNING_SENSOR_STATE:
+ ret = ssp_send_instruction(data,
+ SSP_MSG2SSP_INST_CHANGE_DELAY, type,
+ (u8 *)&to_send, sizeof(to_send));
+ if (ret < 0) {
+ dev_err(&data->spi->dev,
+ "Changing sensor delay failed\n");
+ goto derror;
+ }
+ break;
+ default:
+ data->check_status[type] = SSP_ADD_SENSOR_STATE;
+ break;
+ }
+
+ data->delay_buf[type] = delay;
+
+ if (atomic_inc_return(&data->enable_refcount) == 1)
+ ssp_enable_wdt_timer(data);
+
+ return 0;
+
+derror:
+ return ret;
+}
+EXPORT_SYMBOL(ssp_enable_sensor);
+
+/**
+ * ssp_change_delay() - changes data acquisition for sensor
+ * @data: sensorhub structure
+ * @type: SSP sensor type
+ * @delay: delay in ms
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_change_delay(struct ssp_data *data, enum ssp_sensor_type type,
+ u32 delay)
+{
+ int ret;
+ struct ssp_instruction to_send;
+
+ to_send.a = cpu_to_le32(delay);
+ to_send.b = cpu_to_le32(data->batch_latency_buf[type]);
+ to_send.c = data->batch_opt_buf[type];
+
+ ret = ssp_send_instruction(data, SSP_MSG2SSP_INST_CHANGE_DELAY, type,
+ (u8 *)&to_send, sizeof(to_send));
+ if (ret < 0) {
+ dev_err(&data->spi->dev, "Changing sensor delay failed\n");
+ return ret;
+ }
+
+ data->delay_buf[type] = delay;
+
+ return 0;
+}
+EXPORT_SYMBOL(ssp_change_delay);
+
+/**
+ * ssp_disable_sensor() - disables sensor
+ *
+ * @data: sensorhub structure
+ * @type: SSP sensor type
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_disable_sensor(struct ssp_data *data, enum ssp_sensor_type type)
+{
+ int ret;
+ __le32 command;
+
+ if (data->sensor_enable & BIT(type)) {
+ command = cpu_to_le32(data->delay_buf[type]);
+
+ ret = ssp_send_instruction(data,
+ SSP_MSG2SSP_INST_BYPASS_SENSOR_RM,
+ type, (u8 *)&command,
+ sizeof(command));
+ if (ret < 0) {
+ dev_err(&data->spi->dev, "Remove sensor fail\n");
+ return ret;
+ }
+
+ data->sensor_enable &= ~BIT(type);
+ }
+
+ data->check_status[type] = SSP_ADD_SENSOR_STATE;
+
+ if (atomic_dec_and_test(&data->enable_refcount))
+ ssp_disable_wdt_timer(data);
+
+ return 0;
+}
+EXPORT_SYMBOL(ssp_disable_sensor);
+
+static irqreturn_t ssp_irq_thread_fn(int irq, void *dev_id)
+{
+ struct ssp_data *data = dev_id;
+
+ /*
+ * This wrapper is done to preserve error path for ssp_irq_msg, also
+ * it is defined in different file.
+ */
+ ssp_irq_msg(data);
+
+ return IRQ_HANDLED;
+}
+
+static int ssp_initialize_mcu(struct ssp_data *data)
+{
+ int ret;
+
+ ssp_clean_pending_list(data);
+
+ ret = ssp_get_chipid(data);
+ if (ret != SSP_DEVICE_ID) {
+ dev_err(&data->spi->dev, "%s - MCU %s ret = %d\n", __func__,
+ ret < 0 ? "is not working" : "identification failed",
+ ret);
+ return ret < 0 ? ret : -ENODEV;
+ }
+
+ dev_info(&data->spi->dev, "MCU device ID = %d\n", ret);
+
+ /*
+ * needs clarification, for now do not want to export all transfer
+ * methods to sensors' drivers
+ */
+ ret = ssp_set_magnetic_matrix(data);
+ if (ret < 0) {
+ dev_err(&data->spi->dev,
+ "%s - ssp_set_magnetic_matrix failed\n", __func__);
+ return ret;
+ }
+
+ data->available_sensors = ssp_get_sensor_scanning_info(data);
+ if (data->available_sensors == 0) {
+ dev_err(&data->spi->dev,
+ "%s - ssp_get_sensor_scanning_info failed\n", __func__);
+ return -EIO;
+ }
+
+ data->cur_firm_rev = ssp_get_firmware_rev(data);
+ dev_info(&data->spi->dev, "MCU Firm Rev : New = %8u\n",
+ data->cur_firm_rev);
+
+ return ssp_command(data, SSP_MSG2SSP_AP_MCU_DUMP_CHECK, 0);
+}
+
+/*
+ * sensorhub can request its reinitialization as some brutal and rare error
+ * handling. It can be requested from the MCU.
+ */
+static void ssp_refresh_task(struct work_struct *work)
+{
+ struct ssp_data *data = container_of((struct delayed_work *)work,
+ struct ssp_data, work_refresh);
+
+ dev_info(&data->spi->dev, "refreshing\n");
+
+ data->reset_cnt++;
+
+ if (ssp_initialize_mcu(data) >= 0) {
+ ssp_sync_available_sensors(data);
+ if (data->last_ap_state != 0)
+ ssp_command(data, data->last_ap_state, 0);
+
+ if (data->last_resume_state != 0)
+ ssp_command(data, data->last_resume_state, 0);
+
+ data->timeout_cnt = 0;
+ data->com_fail_cnt = 0;
+ }
+}
+
+int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay)
+{
+ cancel_delayed_work_sync(&data->work_refresh);
+
+ return queue_delayed_work(system_power_efficient_wq,
+ &data->work_refresh,
+ msecs_to_jiffies(delay));
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ssp_of_match[] = {
+ {
+ .compatible = "samsung,sensorhub-rinato",
+ .data = &ssp_rinato_info,
+ }, {
+ .compatible = "samsung,sensorhub-thermostat",
+ .data = &ssp_thermostat_info,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, ssp_of_match);
+
+static struct ssp_data *ssp_parse_dt(struct device *dev)
+{
+ int ret;
+ struct ssp_data *data;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
+ if (data->mcu_ap_gpio < 0)
+ goto err_free_pd;
+
+ data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
+ if (data->ap_mcu_gpio < 0)
+ goto err_free_pd;
+
+ data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
+ if (data->mcu_reset_gpio < 0)
+ goto err_free_pd;
+
+ ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
+ "ap-mcu-gpios");
+ if (ret)
+ goto err_free_pd;
+
+ ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
+ if (ret)
+ goto err_ap_mcu;
+
+ match = of_match_node(ssp_of_match, node);
+ if (!match)
+ goto err_mcu_reset_gpio;
+
+ data->sensorhub_info = (struct ssp_sensorhub_info *)match->data;
+
+ dev_set_drvdata(dev, data);
+
+ return data;
+
+err_mcu_reset_gpio:
+ devm_gpio_free(dev, data->mcu_reset_gpio);
+err_ap_mcu:
+ devm_gpio_free(dev, data->ap_mcu_gpio);
+err_free_pd:
+ devm_kfree(dev, data);
+ return NULL;
+}
+#else
+static struct ssp_data *ssp_parse_dt(struct device *pdev)
+{
+ return NULL;
+}
+#endif
+
+/**
+ * ssp_register_consumer() - registers iio consumer in ssp framework
+ *
+ * @indio_dev: consumer iio device
+ * @type: ssp sensor type
+ */
+void ssp_register_consumer(struct iio_dev *indio_dev, enum ssp_sensor_type type)
+{
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ data->sensor_devs[type] = indio_dev;
+}
+EXPORT_SYMBOL(ssp_register_consumer);
+
+static int ssp_probe(struct spi_device *spi)
+{
+ int ret, i;
+ struct ssp_data *data;
+
+ data = ssp_parse_dt(&spi->dev);
+ if (!data) {
+ dev_err(&spi->dev, "Failed to find platform data\n");
+ return -ENODEV;
+ }
+
+ ret = mfd_add_devices(&spi->dev, -1, sensorhub_sensor_devs,
+ ARRAY_SIZE(sensorhub_sensor_devs), NULL, 0, NULL);
+ if (ret < 0) {
+ dev_err(&spi->dev, "mfd add devices fail\n");
+ return ret;
+ }
+
+ spi->mode = SPI_MODE_1;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to setup spi\n");
+ return ret;
+ }
+
+ data->fw_dl_state = SSP_FW_DL_STATE_NONE;
+ data->spi = spi;
+ spi_set_drvdata(spi, data);
+
+ mutex_init(&data->comm_lock);
+
+ for (i = 0; i < SSP_SENSOR_MAX; ++i) {
+ data->delay_buf[i] = SSP_DEFAULT_POLLING_DELAY;
+ data->batch_latency_buf[i] = 0;
+ data->batch_opt_buf[i] = 0;
+ data->check_status[i] = SSP_INITIALIZATION_STATE;
+ }
+
+ data->delay_buf[SSP_BIO_HRM_LIB] = 100;
+
+ data->time_syncing = true;
+
+ mutex_init(&data->pending_lock);
+ INIT_LIST_HEAD(&data->pending_list);
+
+ atomic_set(&data->enable_refcount, 0);
+
+ INIT_WORK(&data->work_wdt, ssp_wdt_work_func);
+ INIT_DELAYED_WORK(&data->work_refresh, ssp_refresh_task);
+
+ setup_timer(&data->wdt_timer, ssp_wdt_timer_func, (unsigned long)data);
+
+ ret = request_threaded_irq(data->spi->irq, NULL,
+ ssp_irq_thread_fn,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "SSP_Int", data);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Irq request fail\n");
+ goto err_setup_irq;
+ }
+
+ /* Let's start with enabled one so irq balance could be ok */
+ data->shut_down = false;
+
+ /* just to avoid unbalanced irq set wake up */
+ enable_irq_wake(data->spi->irq);
+
+ data->fw_dl_state = ssp_check_fwbl(data);
+ if (data->fw_dl_state == SSP_FW_DL_STATE_NONE) {
+ ret = ssp_initialize_mcu(data);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Initialize_mcu failed\n");
+ goto err_read_reg;
+ }
+ } else {
+ dev_err(&spi->dev, "Firmware version not supported\n");
+ ret = -EPERM;
+ goto err_read_reg;
+ }
+
+ return 0;
+
+err_read_reg:
+ free_irq(data->spi->irq, data);
+err_setup_irq:
+ mutex_destroy(&data->pending_lock);
+ mutex_destroy(&data->comm_lock);
+
+ dev_err(&spi->dev, "Probe failed!\n");
+
+ return ret;
+}
+
+static int ssp_remove(struct spi_device *spi)
+{
+ struct ssp_data *data = spi_get_drvdata(spi);
+
+ if (ssp_command(data, SSP_MSG2SSP_AP_STATUS_SHUTDOWN, 0) < 0)
+ dev_err(&data->spi->dev,
+ "SSP_MSG2SSP_AP_STATUS_SHUTDOWN failed\n");
+
+ ssp_enable_mcu(data, false);
+ ssp_disable_wdt_timer(data);
+
+ ssp_clean_pending_list(data);
+
+ free_irq(data->spi->irq, data);
+
+ del_timer_sync(&data->wdt_timer);
+ cancel_work_sync(&data->work_wdt);
+
+ mutex_destroy(&data->comm_lock);
+ mutex_destroy(&data->pending_lock);
+
+ mfd_remove_devices(&spi->dev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ssp_suspend(struct device *dev)
+{
+ int ret;
+ struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
+
+ data->last_resume_state = SSP_MSG2SSP_AP_STATUS_SUSPEND;
+
+ if (atomic_read(&data->enable_refcount) > 0)
+ ssp_disable_wdt_timer(data);
+
+ ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_SUSPEND, 0);
+ if (ret < 0) {
+ dev_err(&data->spi->dev,
+ "%s SSP_MSG2SSP_AP_STATUS_SUSPEND failed\n", __func__);
+
+ ssp_enable_wdt_timer(data);
+ return ret;
+ }
+
+ data->time_syncing = false;
+ disable_irq(data->spi->irq);
+
+ return 0;
+}
+
+static int ssp_resume(struct device *dev)
+{
+ int ret;
+ struct ssp_data *data = spi_get_drvdata(to_spi_device(dev));
+
+ enable_irq(data->spi->irq);
+
+ if (atomic_read(&data->enable_refcount) > 0)
+ ssp_enable_wdt_timer(data);
+
+ ret = ssp_command(data, SSP_MSG2SSP_AP_STATUS_RESUME, 0);
+ if (ret < 0) {
+ dev_err(&data->spi->dev,
+ "%s SSP_MSG2SSP_AP_STATUS_RESUME failed\n", __func__);
+ ssp_disable_wdt_timer(data);
+ return ret;
+ }
+
+ /* timesyncing is set by MCU */
+ data->last_resume_state = SSP_MSG2SSP_AP_STATUS_RESUME;
+
+ return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ssp_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ssp_suspend, ssp_resume)
+};
+
+static struct spi_driver ssp_driver = {
+ .probe = ssp_probe,
+ .remove = ssp_remove,
+ .driver = {
+ .pm = &ssp_pm_ops,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(ssp_of_match),
+ .name = "sensorhub"
+ },
+};
+
+module_spi_driver(ssp_driver);
+
+MODULE_DESCRIPTION("ssp sensorhub driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c
new file mode 100644
index 0000000..a3ae165
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_iio.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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/iio/common/ssp_sensors.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include "ssp_iio_sensor.h"
+
+/**
+ * ssp_common_buffer_postenable() - generic postenable callback for ssp buffer
+ *
+ * @indio_dev: iio device
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_common_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ssp_sensor_data *spd = iio_priv(indio_dev);
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ /* the allocation is made in post because scan size is known in this
+ * moment
+ * */
+ spd->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL | GFP_DMA);
+ if (!spd->buffer)
+ return -ENOMEM;
+
+ return ssp_enable_sensor(data, spd->type,
+ ssp_get_sensor_delay(data, spd->type));
+}
+EXPORT_SYMBOL(ssp_common_buffer_postenable);
+
+/**
+ * ssp_common_buffer_postdisable() - generic postdisable callback for ssp buffer
+ *
+ * @indio_dev: iio device
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_common_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct ssp_sensor_data *spd = iio_priv(indio_dev);
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ ret = ssp_disable_sensor(data, spd->type);
+ if (ret < 0)
+ return ret;
+
+ kfree(spd->buffer);
+
+ return ret;
+}
+EXPORT_SYMBOL(ssp_common_buffer_postdisable);
+
+/**
+ * ssp_common_process_data() - Common process data callback for ssp sensors
+ *
+ * @indio_dev: iio device
+ * @buf: source buffer
+ * @len: sensor data length
+ * @timestamp: system timestamp
+ *
+ * Returns 0 or negative value in case of error
+ */
+int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
+ unsigned int len, int64_t timestamp)
+{
+ __le32 time;
+ int64_t calculated_time;
+ struct ssp_sensor_data *spd = iio_priv(indio_dev);
+
+ if (indio_dev->scan_bytes == 0)
+ return 0;
+
+ /*
+ * it always sends full set of samples, remember about available masks
+ */
+ memcpy(spd->buffer, buf, len);
+
+ if (indio_dev->scan_timestamp) {
+ memcpy(&time, &((char *)buf)[len], SSP_TIME_SIZE);
+ calculated_time =
+ timestamp + (int64_t)le32_to_cpu(time) * 1000000;
+ }
+
+ return iio_push_to_buffers_with_timestamp(indio_dev, spd->buffer,
+ calculated_time);
+}
+EXPORT_SYMBOL(ssp_common_process_data);
+
+MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
+MODULE_DESCRIPTION("Samsung sensorhub commons");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
new file mode 100644
index 0000000..541c659
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_iio_sensor.h
@@ -0,0 +1,71 @@
+#ifndef __SSP_IIO_SENSOR_H__
+#define __SSP_IIO_SENSOR_H__
+
+#define SSP_CHANNEL_AG(_type, _mod, _index) \
+{ \
+ .type = _type,\
+ .modified = 1,\
+ .channel2 = _mod,\
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .scan_index = _index,\
+ .scan_type = {\
+ .sign = 's',\
+ .realbits = 16,\
+ .storagebits = 16,\
+ .shift = 0,\
+ .endianness = IIO_LE,\
+ },\
+}
+
+/* It is defined here as it is a mixed timestamp */
+#define SSP_CHAN_TIMESTAMP(_si) { \
+ .type = IIO_TIMESTAMP, \
+ .channel = -1, \
+ .scan_index = _si, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 64, \
+ .storagebits = 64, \
+ }, \
+}
+
+#define SSP_MS_PER_S 1000
+#define SSP_INVERTED_SCALING_FACTOR 1000000U
+
+#define SSP_FACTOR_WITH_MS \
+ (SSP_INVERTED_SCALING_FACTOR * SSP_MS_PER_S)
+
+int ssp_common_buffer_postenable(struct iio_dev *indio_dev);
+
+int ssp_common_buffer_postdisable(struct iio_dev *indio_dev);
+
+int ssp_common_process_data(struct iio_dev *indio_dev, void *buf,
+ unsigned int len, int64_t timestamp);
+
+/* Converts time in ms to frequency */
+static inline void ssp_convert_to_freq(u32 time, int *integer_part,
+ int *fractional)
+{
+ if (time == 0) {
+ *fractional = 0;
+ *integer_part = 0;
+ return;
+ }
+
+ *integer_part = SSP_FACTOR_WITH_MS / time;
+ *fractional = *integer_part % SSP_INVERTED_SCALING_FACTOR;
+ *integer_part = *integer_part / SSP_INVERTED_SCALING_FACTOR;
+}
+
+/* Converts frequency to time in ms */
+static inline int ssp_convert_to_time(int integer_part, int fractional)
+{
+ u64 value;
+
+ value = (u64)integer_part * SSP_INVERTED_SCALING_FACTOR + fractional;
+ if (value == 0)
+ return 0;
+
+ return div64_u64((u64)SSP_FACTOR_WITH_MS, value);
+}
+#endif /* __SSP_IIO_SENSOR_H__ */
diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c
new file mode 100644
index 0000000..704284a
--- /dev/null
+++ b/drivers/iio/common/ssp_sensors/ssp_spi.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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 "ssp.h"
+
+#define SSP_DEV (&data->spi->dev)
+#define SSP_GET_MESSAGE_TYPE(data) (data & (3 << SSP_RW))
+
+/*
+ * SSP -> AP Instruction
+ * They tell what packet type can be expected. In the future there will
+ * be less of them. BYPASS means common sensor packets with accel, gyro,
+ * hrm etc. data. LIBRARY and META are mock-up's for now.
+ */
+#define SSP_MSG2AP_INST_BYPASS_DATA 0x37
+#define SSP_MSG2AP_INST_LIBRARY_DATA 0x01
+#define SSP_MSG2AP_INST_DEBUG_DATA 0x03
+#define SSP_MSG2AP_INST_BIG_DATA 0x04
+#define SSP_MSG2AP_INST_META_DATA 0x05
+#define SSP_MSG2AP_INST_TIME_SYNC 0x06
+#define SSP_MSG2AP_INST_RESET 0x07
+
+#define SSP_UNIMPLEMENTED -1
+
+struct ssp_msg_header {
+ u8 cmd;
+ __le16 length;
+ __le16 options;
+ __le32 data;
+} __attribute__((__packed__));
+
+struct ssp_msg {
+ u16 length;
+ u16 options;
+ struct list_head list;
+ struct completion *done;
+ struct ssp_msg_header *h;
+ char *buffer;
+};
+
+static const int ssp_offset_map[SSP_SENSOR_MAX] = {
+ [SSP_ACCELEROMETER_SENSOR] = SSP_ACCELEROMETER_SIZE +
+ SSP_TIME_SIZE,
+ [SSP_GYROSCOPE_SENSOR] = SSP_GYROSCOPE_SIZE +
+ SSP_TIME_SIZE,
+ [SSP_GEOMAGNETIC_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_GEOMAGNETIC_RAW] = SSP_UNIMPLEMENTED,
+ [SSP_GEOMAGNETIC_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_PRESSURE_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_GESTURE_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_PROXIMITY_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_TEMPERATURE_HUMIDITY_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_LIGHT_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_PROXIMITY_RAW] = SSP_UNIMPLEMENTED,
+ [SSP_ORIENTATION_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_STEP_DETECTOR] = SSP_UNIMPLEMENTED,
+ [SSP_SIG_MOTION_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_GYRO_UNCALIB_SENSOR] = SSP_UNIMPLEMENTED,
+ [SSP_GAME_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
+ [SSP_ROTATION_VECTOR] = SSP_UNIMPLEMENTED,
+ [SSP_STEP_COUNTER] = SSP_UNIMPLEMENTED,
+ [SSP_BIO_HRM_RAW] = SSP_BIO_HRM_RAW_SIZE +
+ SSP_TIME_SIZE,
+ [SSP_BIO_HRM_RAW_FAC] = SSP_BIO_HRM_RAW_FAC_SIZE +
+ SSP_TIME_SIZE,
+ [SSP_BIO_HRM_LIB] = SSP_BIO_HRM_LIB_SIZE +
+ SSP_TIME_SIZE,
+};
+
+#define SSP_HEADER_SIZE (sizeof(struct ssp_msg_header))
+#define SSP_HEADER_SIZE_ALIGNED (ALIGN(SSP_HEADER_SIZE, 4))
+
+static struct ssp_msg *ssp_create_msg(u8 cmd, u16 len, u16 opt, u32 data)
+{
+ struct ssp_msg_header h;
+ struct ssp_msg *msg;
+
+ msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+ if (!msg)
+ return NULL;
+
+ h.cmd = cmd;
+ h.length = cpu_to_le16(len);
+ h.options = cpu_to_le16(opt);
+ h.data = cpu_to_le32(data);
+
+ msg->buffer = kzalloc(SSP_HEADER_SIZE_ALIGNED + len,
+ GFP_KERNEL | GFP_DMA);
+ if (!msg->buffer) {
+ kfree(msg);
+ return NULL;
+ }
+
+ msg->length = len;
+ msg->options = opt;
+
+ memcpy(msg->buffer, &h, SSP_HEADER_SIZE);
+
+ return msg;
+}
+
+/*
+ * It is a bit heavy to do it this way but often the function is used to compose
+ * the message from smaller chunks which are placed on the stack. Often the
+ * chunks are small so memcpy should be optimalized.
+ */
+static inline void ssp_fill_buffer(struct ssp_msg *m, unsigned int offset,
+ const void *src, unsigned int len)
+{
+ memcpy(&m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], src, len);
+}
+
+static inline void ssp_get_buffer(struct ssp_msg *m, unsigned int offset,
+ void *dest, unsigned int len)
+{
+ memcpy(dest, &m->buffer[SSP_HEADER_SIZE_ALIGNED + offset], len);
+}
+
+#define SSP_GET_BUFFER_AT_INDEX(m, index) \
+ (m->buffer[SSP_HEADER_SIZE_ALIGNED + index])
+#define SSP_SET_BUFFER_AT_INDEX(m, index, val) \
+ (m->buffer[SSP_HEADER_SIZE_ALIGNED + index] = val)
+
+static void ssp_clean_msg(struct ssp_msg *m)
+{
+ kfree(m->buffer);
+ kfree(m);
+}
+
+static int ssp_print_mcu_debug(char *data_frame, int *data_index,
+ int received_len)
+{
+ int length = data_frame[(*data_index)++];
+
+ if (length > received_len - *data_index || length <= 0) {
+ ssp_dbg("[SSP]: MSG From MCU-invalid debug length(%d/%d)\n",
+ length, received_len);
+ return length ? length : -EPROTO;
+ }
+
+ ssp_dbg("[SSP]: MSG From MCU - %s\n", &data_frame[*data_index]);
+
+ *data_index += length;
+
+ return 0;
+}
+
+/*
+ * It was designed that way - additional lines to some kind of handshake,
+ * please do not ask why - only the firmware guy can know it.
+ */
+static int ssp_check_lines(struct ssp_data *data, bool state)
+{
+ int delay_cnt = 0;
+
+ gpio_set_value_cansleep(data->ap_mcu_gpio, state);
+
+ while (gpio_get_value_cansleep(data->mcu_ap_gpio) != state) {
+ usleep_range(3000, 3500);
+
+ if (data->shut_down || delay_cnt++ > 500) {
+ dev_err(SSP_DEV, "%s:timeout, hw ack wait fail %d\n",
+ __func__, state);
+
+ if (!state)
+ gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
+
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static int ssp_do_transfer(struct ssp_data *data, struct ssp_msg *msg,
+ struct completion *done, int timeout)
+{
+ int status;
+ /*
+ * check if this is a short one way message or the whole transfer has
+ * second part after an interrupt
+ */
+ const bool use_no_irq = msg->length == 0;
+
+ if (data->shut_down)
+ return -EPERM;
+
+ msg->done = done;
+
+ mutex_lock(&data->comm_lock);
+
+ status = ssp_check_lines(data, false);
+ if (status < 0)
+ goto _error_locked;
+
+ status = spi_write(data->spi, msg->buffer, SSP_HEADER_SIZE);
+ if (status < 0) {
+ gpio_set_value_cansleep(data->ap_mcu_gpio, 1);
+ dev_err(SSP_DEV, "%s spi_write fail\n", __func__);
+ goto _error_locked;
+ }
+
+ if (!use_no_irq) {
+ mutex_lock(&data->pending_lock);
+ list_add_tail(&msg->list, &data->pending_list);
+ mutex_unlock(&data->pending_lock);
+ }
+
+ status = ssp_check_lines(data, true);
+ if (status < 0) {
+ if (!use_no_irq) {
+ mutex_lock(&data->pending_lock);
+ list_del(&msg->list);
+ mutex_unlock(&data->pending_lock);
+ }
+ goto _error_locked;
+ }
+
+ mutex_unlock(&data->comm_lock);
+
+ if (!use_no_irq && done)
+ if (wait_for_completion_timeout(done,
+ msecs_to_jiffies(timeout)) ==
+ 0) {
+ mutex_lock(&data->pending_lock);
+ list_del(&msg->list);
+ mutex_unlock(&data->pending_lock);
+
+ data->timeout_cnt++;
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+
+_error_locked:
+ mutex_unlock(&data->comm_lock);
+ data->timeout_cnt++;
+ return status;
+}
+
+static inline int ssp_spi_sync_command(struct ssp_data *data,
+ struct ssp_msg *msg)
+{
+ return ssp_do_transfer(data, msg, NULL, 0);
+}
+
+static int ssp_spi_sync(struct ssp_data *data, struct ssp_msg *msg,
+ int timeout)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ if (WARN_ON(!msg->length))
+ return -EPERM;
+
+ return ssp_do_transfer(data, msg, &done, timeout);
+}
+
+static int ssp_handle_big_data(struct ssp_data *data, char *dataframe, int *idx)
+{
+ /* mock-up, it will be changed with adding another sensor types */
+ *idx += 8;
+ return 0;
+}
+
+static int ssp_parse_dataframe(struct ssp_data *data, char *dataframe, int len)
+{
+ int idx, sd;
+ struct timespec ts;
+ struct ssp_sensor_data *spd;
+ struct iio_dev **indio_devs = data->sensor_devs;
+
+ getnstimeofday(&ts);
+
+ for (idx = 0; idx < len;) {
+ switch (dataframe[idx++]) {
+ case SSP_MSG2AP_INST_BYPASS_DATA:
+ sd = dataframe[idx++];
+ if (sd < 0 || sd >= SSP_SENSOR_MAX) {
+ dev_err(SSP_DEV,
+ "Mcu data frame1 error %d\n", sd);
+ return -EPROTO;
+ }
+
+ if (indio_devs[sd]) {
+ spd = iio_priv(indio_devs[sd]);
+ if (spd->process_data)
+ spd->process_data(indio_devs[sd],
+ &dataframe[idx],
+ data->timestamp);
+ } else {
+ dev_err(SSP_DEV, "no client for frame\n");
+ }
+
+ idx += ssp_offset_map[sd];
+ break;
+ case SSP_MSG2AP_INST_DEBUG_DATA:
+ sd = ssp_print_mcu_debug(dataframe, &idx, len);
+ if (sd) {
+ dev_err(SSP_DEV,
+ "Mcu data frame3 error %d\n", sd);
+ return sd;
+ }
+ break;
+ case SSP_MSG2AP_INST_LIBRARY_DATA:
+ idx += len;
+ break;
+ case SSP_MSG2AP_INST_BIG_DATA:
+ ssp_handle_big_data(data, dataframe, &idx);
+ break;
+ case SSP_MSG2AP_INST_TIME_SYNC:
+ data->time_syncing = true;
+ break;
+ case SSP_MSG2AP_INST_RESET:
+ ssp_queue_ssp_refresh_task(data, 0);
+ break;
+ }
+ }
+
+ if (data->time_syncing)
+ data->timestamp = ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+
+ return 0;
+}
+
+/* threaded irq */
+int ssp_irq_msg(struct ssp_data *data)
+{
+ bool found = false;
+ char *buffer;
+ u8 msg_type;
+ int ret;
+ u16 length, msg_options;
+ struct ssp_msg *msg, *n;
+
+ ret = spi_read(data->spi, data->header_buffer, SSP_HEADER_BUFFER_SIZE);
+ if (ret < 0) {
+ dev_err(SSP_DEV, "header read fail\n");
+ return ret;
+ }
+
+ length = le16_to_cpu(data->header_buffer[1]);
+ msg_options = le16_to_cpu(data->header_buffer[0]);
+
+ if (length == 0) {
+ dev_err(SSP_DEV, "length received from mcu is 0\n");
+ return -EINVAL;
+ }
+
+ msg_type = SSP_GET_MESSAGE_TYPE(msg_options);
+
+ switch (msg_type) {
+ case SSP_AP2HUB_READ:
+ case SSP_AP2HUB_WRITE:
+ /*
+ * this is a small list, a few elements - the packets can be
+ * received with no order
+ */
+ mutex_lock(&data->pending_lock);
+ list_for_each_entry_safe(msg, n, &data->pending_list, list) {
+ if (msg->options == msg_options) {
+ list_del(&msg->list);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * here can be implemented dead messages handling
+ * but the slave should not send such ones - it is to
+ * check but let's handle this
+ */
+ buffer = kmalloc(length, GFP_KERNEL | GFP_DMA);
+ if (!buffer) {
+ ret = -ENOMEM;
+ goto _unlock;
+ }
+
+ /* got dead packet so it is always an error */
+ ret = spi_read(data->spi, buffer, length);
+ if (ret >= 0)
+ ret = -EPROTO;
+
+ kfree(buffer);
+
+ dev_err(SSP_DEV, "No match error %x\n",
+ msg_options);
+
+ goto _unlock;
+ }
+
+ if (msg_type == SSP_AP2HUB_READ)
+ ret = spi_read(data->spi,
+ &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
+ msg->length);
+
+ if (msg_type == SSP_AP2HUB_WRITE) {
+ ret = spi_write(data->spi,
+ &msg->buffer[SSP_HEADER_SIZE_ALIGNED],
+ msg->length);
+ if (msg_options & SSP_AP2HUB_RETURN) {
+ msg->options =
+ SSP_AP2HUB_READ | SSP_AP2HUB_RETURN;
+ msg->length = 1;
+
+ list_add_tail(&msg->list, &data->pending_list);
+ goto _unlock;
+ }
+ }
+
+ if (msg->done)
+ if (!completion_done(msg->done))
+ complete(msg->done);
+_unlock:
+ mutex_unlock(&data->pending_lock);
+ break;
+ case SSP_HUB2AP_WRITE:
+ buffer = kzalloc(length, GFP_KERNEL | GFP_DMA);
+ if (!buffer)
+ return -ENOMEM;
+
+ ret = spi_read(data->spi, buffer, length);
+ if (ret < 0) {
+ dev_err(SSP_DEV, "spi read fail\n");
+ kfree(buffer);
+ break;
+ }
+
+ ret = ssp_parse_dataframe(data, buffer, length);
+
+ kfree(buffer);
+ break;
+
+ default:
+ dev_err(SSP_DEV, "unknown msg type\n");
+ return -EPROTO;
+ }
+
+ return ret;
+}
+
+void ssp_clean_pending_list(struct ssp_data *data)
+{
+ struct ssp_msg *msg, *n;
+
+ mutex_lock(&data->pending_lock);
+ list_for_each_entry_safe(msg, n, &data->pending_list, list) {
+ list_del(&msg->list);
+
+ if (msg->done)
+ if (!completion_done(msg->done))
+ complete(msg->done);
+ }
+ mutex_unlock(&data->pending_lock);
+}
+
+int ssp_command(struct ssp_data *data, char command, int arg)
+{
+ int ret;
+ struct ssp_msg *msg;
+
+ msg = ssp_create_msg(command, 0, SSP_AP2HUB_WRITE, arg);
+ if (!msg)
+ return -ENOMEM;
+
+ ssp_dbg("%s - command 0x%x %d\n", __func__, command, arg);
+
+ ret = ssp_spi_sync_command(data, msg);
+ ssp_clean_msg(msg);
+
+ return ret;
+}
+
+int ssp_send_instruction(struct ssp_data *data, u8 inst, u8 sensor_type,
+ u8 *send_buf, u8 length)
+{
+ int ret;
+ struct ssp_msg *msg;
+
+ if (data->fw_dl_state == SSP_FW_DL_STATE_DOWNLOADING) {
+ dev_err(SSP_DEV, "%s - Skip Inst! DL state = %d\n",
+ __func__, data->fw_dl_state);
+ return -EBUSY;
+ } else if (!(data->available_sensors & BIT(sensor_type)) &&
+ (inst <= SSP_MSG2SSP_INST_CHANGE_DELAY)) {
+ dev_err(SSP_DEV, "%s - Bypass Inst Skip! - %u\n",
+ __func__, sensor_type);
+ return -EIO; /* just fail */
+ }
+
+ msg = ssp_create_msg(inst, length + 2, SSP_AP2HUB_WRITE, 0);
+ if (!msg)
+ return -ENOMEM;
+
+ ssp_fill_buffer(msg, 0, &sensor_type, 1);
+ ssp_fill_buffer(msg, 1, send_buf, length);
+
+ ssp_dbg("%s - Inst = 0x%x, Sensor Type = 0x%x, data = %u\n",
+ __func__, inst, sensor_type, send_buf[1]);
+
+ ret = ssp_spi_sync(data, msg, 1000);
+ ssp_clean_msg(msg);
+
+ return ret;
+}
+
+int ssp_get_chipid(struct ssp_data *data)
+{
+ int ret;
+ char buffer;
+ struct ssp_msg *msg;
+
+ msg = ssp_create_msg(SSP_MSG2SSP_AP_WHOAMI, 1, SSP_AP2HUB_READ, 0);
+ if (!msg)
+ return -ENOMEM;
+
+ ret = ssp_spi_sync(data, msg, 1000);
+
+ buffer = SSP_GET_BUFFER_AT_INDEX(msg, 0);
+
+ ssp_clean_msg(msg);
+
+ return ret < 0 ? ret : buffer;
+}
+
+int ssp_set_magnetic_matrix(struct ssp_data *data)
+{
+ int ret;
+ struct ssp_msg *msg;
+
+ msg = ssp_create_msg(SSP_MSG2SSP_AP_SET_MAGNETIC_STATIC_MATRIX,
+ data->sensorhub_info->mag_length, SSP_AP2HUB_WRITE,
+ 0);
+ if (!msg)
+ return -ENOMEM;
+
+ ssp_fill_buffer(msg, 0, data->sensorhub_info->mag_table,
+ data->sensorhub_info->mag_length);
+
+ ret = ssp_spi_sync(data, msg, 1000);
+ ssp_clean_msg(msg);
+
+ return ret;
+}
+
+unsigned int ssp_get_sensor_scanning_info(struct ssp_data *data)
+{
+ int ret;
+ __le32 result;
+ u32 cpu_result = 0;
+
+ struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_SENSOR_SCANNING, 4,
+ SSP_AP2HUB_READ, 0);
+ if (!msg)
+ return 0;
+
+ ret = ssp_spi_sync(data, msg, 1000);
+ if (ret < 0) {
+ dev_err(SSP_DEV, "%s - spi read fail %d\n", __func__, ret);
+ goto _exit;
+ }
+
+ ssp_get_buffer(msg, 0, &result, 4);
+ cpu_result = le32_to_cpu(result);
+
+ dev_info(SSP_DEV, "%s state: 0x%08x\n", __func__, cpu_result);
+
+_exit:
+ ssp_clean_msg(msg);
+ return cpu_result;
+}
+
+unsigned int ssp_get_firmware_rev(struct ssp_data *data)
+{
+ int ret;
+ __le32 result;
+
+ struct ssp_msg *msg = ssp_create_msg(SSP_MSG2SSP_AP_FIRMWARE_REV, 4,
+ SSP_AP2HUB_READ, 0);
+ if (!msg)
+ return SSP_INVALID_REVISION;
+
+ ret = ssp_spi_sync(data, msg, 1000);
+ if (ret < 0) {
+ dev_err(SSP_DEV, "%s - transfer fail %d\n", __func__, ret);
+ ret = SSP_INVALID_REVISION;
+ goto _exit;
+ }
+
+ ssp_get_buffer(msg, 0, &result, 4);
+ ret = le32_to_cpu(result);
+
+_exit:
+ ssp_clean_msg(msg);
+ return ret;
+}
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 24cfe4e..edd13d2 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -44,18 +44,18 @@ st_sensors_write_data_with_mask_error:
return err;
}
-static int st_sensors_match_odr(struct st_sensors *sensor,
+static int st_sensors_match_odr(struct st_sensor_settings *sensor_settings,
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)
+ if (sensor_settings->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;
+ if (sensor_settings->odr.odr_avl[i].hz == odr) {
+ odr_out->hz = sensor_settings->odr.odr_avl[i].hz;
+ odr_out->value = sensor_settings->odr.odr_avl[i].value;
ret = 0;
break;
}
@@ -71,23 +71,26 @@ int st_sensors_set_odr(struct iio_dev *indio_dev, unsigned int odr)
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);
+ err = st_sensors_match_odr(sdata->sensor_settings, 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->sensor_settings->odr.addr ==
+ sdata->sensor_settings->pw.addr) &&
+ (sdata->sensor_settings->odr.mask ==
+ sdata->sensor_settings->pw.mask)) {
if (sdata->enabled == true) {
err = st_sensors_write_data_with_mask(indio_dev,
- sdata->sensor->odr.addr,
- sdata->sensor->odr.mask,
+ sdata->sensor_settings->odr.addr,
+ sdata->sensor_settings->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,
+ sdata->sensor_settings->odr.addr,
+ sdata->sensor_settings->odr.mask,
odr_out.value);
}
if (err >= 0)
@@ -98,16 +101,16 @@ st_sensors_match_odr_error:
}
EXPORT_SYMBOL(st_sensors_set_odr);
-static int st_sensors_match_fs(struct st_sensors *sensor,
+static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
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)
+ if (sensor_settings->fs.fs_avl[i].num == 0)
goto st_sensors_match_odr_error;
- if (sensor->fs.fs_avl[i].num == fs) {
+ if (sensor_settings->fs.fs_avl[i].num == fs) {
*index_fs_avl = i;
ret = 0;
break;
@@ -118,25 +121,24 @@ st_sensors_match_odr_error:
return ret;
}
-static int st_sensors_set_fullscale(struct iio_dev *indio_dev,
- unsigned int fs)
+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);
+ err = st_sensors_match_fs(sdata->sensor_settings, 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);
+ sdata->sensor_settings->fs.addr,
+ sdata->sensor_settings->fs.mask,
+ sdata->sensor_settings->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];
+ &sdata->sensor_settings->fs.fs_avl[i];
return err;
st_accel_set_fullscale_error:
@@ -153,10 +155,12 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
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,
+ tmp_value = sdata->sensor_settings->pw.value_on;
+ if ((sdata->sensor_settings->odr.addr ==
+ sdata->sensor_settings->pw.addr) &&
+ (sdata->sensor_settings->odr.mask ==
+ sdata->sensor_settings->pw.mask)) {
+ err = st_sensors_match_odr(sdata->sensor_settings,
sdata->odr, &odr_out);
if (err < 0)
goto set_enable_error;
@@ -164,8 +168,8 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
found = true;
}
err = st_sensors_write_data_with_mask(indio_dev,
- sdata->sensor->pw.addr,
- sdata->sensor->pw.mask, tmp_value);
+ sdata->sensor_settings->pw.addr,
+ sdata->sensor_settings->pw.mask, tmp_value);
if (err < 0)
goto set_enable_error;
@@ -175,9 +179,9 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable)
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);
+ sdata->sensor_settings->pw.addr,
+ sdata->sensor_settings->pw.mask,
+ sdata->sensor_settings->pw.value_off);
if (err < 0)
goto set_enable_error;
@@ -194,8 +198,9 @@ 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);
+ sdata->sensor_settings->enable_axis.addr,
+ sdata->sensor_settings->enable_axis.mask,
+ axis_enable);
}
EXPORT_SYMBOL(st_sensors_set_axis_enable);
@@ -236,13 +241,13 @@ void st_sensors_power_disable(struct iio_dev *indio_dev)
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_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) {
+ if (sdata->sensor_settings->drdy_irq.mask_int1 == 0) {
dev_err(&indio_dev->dev,
"DRDY on INT1 not available.\n");
return -EINVAL;
@@ -250,7 +255,7 @@ static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev,
sdata->drdy_int_pin = 1;
break;
case 2:
- if (sdata->sensor->drdy_irq.mask_int2 == 0) {
+ if (sdata->sensor_settings->drdy_irq.mask_int2 == 0) {
dev_err(&indio_dev->dev,
"DRDY on INT2 not available.\n");
return -EINVAL;
@@ -318,7 +323,7 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
if (sdata->current_fullscale) {
err = st_sensors_set_fullscale(indio_dev,
- sdata->current_fullscale->num);
+ sdata->current_fullscale->num);
if (err < 0)
return err;
} else
@@ -330,7 +335,8 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,
/* set BDU */
err = st_sensors_write_data_with_mask(indio_dev,
- sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);
+ sdata->sensor_settings->bdu.addr,
+ sdata->sensor_settings->bdu.mask, true);
if (err < 0)
return err;
@@ -346,26 +352,28 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)
u8 drdy_mask;
struct st_sensor_data *sdata = iio_priv(indio_dev);
- if (!sdata->sensor->drdy_irq.addr)
+ if (!sdata->sensor_settings->drdy_irq.addr)
return 0;
/* Enable/Disable the interrupt generator 1. */
- if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {
+ if (sdata->sensor_settings->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);
+ sdata->sensor_settings->drdy_irq.ig1.en_addr,
+ sdata->sensor_settings->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;
+ drdy_mask = sdata->sensor_settings->drdy_irq.mask_int1;
else
- drdy_mask = sdata->sensor->drdy_irq.mask_int2;
+ drdy_mask = sdata->sensor_settings->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);
+ sdata->sensor_settings->drdy_irq.addr,
+ drdy_mask, (int)enable);
st_accel_set_dataready_irq_error:
return err;
@@ -378,8 +386,8 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
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)) {
+ if ((sdata->sensor_settings->fs.fs_avl[i].gain == scale) &&
+ (sdata->sensor_settings->fs.fs_avl[i].gain != 0)) {
err = 0;
break;
}
@@ -388,7 +396,7 @@ int st_sensors_set_fullscale_by_gain(struct iio_dev *indio_dev, int scale)
goto st_sensors_match_scale_error;
err = st_sensors_set_fullscale(indio_dev,
- sdata->sensor->fs.fs_avl[i].num);
+ sdata->sensor_settings->fs.fs_avl[i].num);
st_sensors_match_scale_error:
return err;
@@ -439,7 +447,7 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,
if (err < 0)
goto out;
- msleep((sdata->sensor->bootime * 1000) / sdata->odr);
+ msleep((sdata->sensor_settings->bootime * 1000) / sdata->odr);
err = st_sensors_read_axis_data(indio_dev, ch, val);
if (err < 0)
goto out;
@@ -456,7 +464,8 @@ out:
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)
+ int num_sensors_list,
+ const struct st_sensor_settings *sensor_settings)
{
u8 wai;
int i, n, err;
@@ -470,23 +479,24 @@ int st_sensors_check_device_support(struct iio_dev *indio_dev,
}
for (i = 0; i < num_sensors_list; i++) {
- if (sensors[i].wai == wai)
+ if (sensor_settings[i].wai == wai)
break;
}
if (i == num_sensors_list)
goto device_not_supported;
- for (n = 0; n < ARRAY_SIZE(sensors[i].sensors_supported); n++) {
+ for (n = 0; n < ARRAY_SIZE(sensor_settings[i].sensors_supported); n++) {
if (strcmp(indio_dev->name,
- &sensors[i].sensors_supported[n][0]) == 0)
+ &sensor_settings[i].sensors_supported[n][0]) == 0)
break;
}
- if (n == ARRAY_SIZE(sensors[i].sensors_supported)) {
+ if (n == ARRAY_SIZE(sensor_settings[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];
+ sdata->sensor_settings =
+ (struct st_sensor_settings *)&sensor_settings[i];
return i;
@@ -508,11 +518,11 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *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)
+ if (sdata->sensor_settings->odr.odr_avl[i].hz == 0)
break;
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
- sdata->sensor->odr.odr_avl[i].hz);
+ sdata->sensor_settings->odr.odr_avl[i].hz);
}
mutex_unlock(&indio_dev->mlock);
buf[len - 1] = '\n';
@@ -530,11 +540,11 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *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)
+ if (sdata->sensor_settings->fs.fs_avl[i].num == 0)
break;
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
- sdata->sensor->fs.fs_avl[i].gain);
+ sdata->sensor_settings->fs.fs_avl[i].gain);
}
mutex_unlock(&indio_dev->mlock);
buf[len - 1] = '\n';
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
index bb6f308..98cfee29 100644
--- a/drivers/iio/common/st_sensors/st_sensors_i2c.c
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -72,6 +72,7 @@ void st_sensors_i2c_configure(struct iio_dev *indio_dev,
indio_dev->dev.parent = &client->dev;
indio_dev->name = client->name;
+ sdata->dev = &client->dev;
sdata->tf = &st_sensors_tf_i2c;
sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
}
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
index 251baf6..5b37737 100644
--- a/drivers/iio/common/st_sensors/st_sensors_spi.c
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -54,7 +54,7 @@ static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
if (err)
goto acc_spi_read_error;
- memcpy(data, tb->rx_buf, len*sizeof(u8));
+ memcpy(data, tb->rx_buf, len);
mutex_unlock(&tb->buf_lock);
return len;
@@ -111,6 +111,7 @@ void st_sensors_spi_configure(struct iio_dev *indio_dev,
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi->modalias;
+ sdata->dev = &spi->dev;
sdata->tf = &st_sensors_tf_spi;
sdata->get_irq_data_ready = st_sensors_spi_get_irq;
}
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 2236ea2..13471a7 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -143,11 +143,16 @@ config AD7303
ad7303.
config MAX517
- tristate "Maxim MAX517/518/519 DAC driver"
+ tristate "Maxim MAX517/518/519/520/521 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).
+ If you say yes here you get support for the following Maxim chips
+ (I2C 8-Bit DACs with rail-to-rail outputs):
+ MAX517 - Single channel, single reference
+ MAX518 - Dual channel, ref=Vdd
+ MAX519 - Dual channel, dual reference
+ MAX520 - Quad channel, quad reference
+ MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7
This driver can also be built as a module. If so, the module
will be called max517.
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index f57562a..15c73e2 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -322,7 +322,7 @@ static int ad5686_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
- st->reg = devm_regulator_get(&spi->dev, "vcc");
+ st->reg = devm_regulator_get_optional(&spi->dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
if (ret)
diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c
index 9a82a72..5507b39 100644
--- a/drivers/iio/dac/max517.c
+++ b/drivers/iio/dac/max517.c
@@ -39,11 +39,13 @@ enum max517_device_ids {
ID_MAX517,
ID_MAX518,
ID_MAX519,
+ ID_MAX520,
+ ID_MAX521,
};
struct max517_data {
struct i2c_client *client;
- unsigned short vref_mv[2];
+ unsigned short vref_mv[8];
};
/*
@@ -149,7 +151,13 @@ static const struct iio_info max517_info = {
static const struct iio_chan_spec max517_channels[] = {
MAX517_CHANNEL(0),
- MAX517_CHANNEL(1)
+ MAX517_CHANNEL(1),
+ MAX517_CHANNEL(2),
+ MAX517_CHANNEL(3),
+ MAX517_CHANNEL(4),
+ MAX517_CHANNEL(5),
+ MAX517_CHANNEL(6),
+ MAX517_CHANNEL(7),
};
static int max517_probe(struct i2c_client *client,
@@ -158,6 +166,7 @@ static int max517_probe(struct i2c_client *client,
struct max517_data *data;
struct iio_dev *indio_dev;
struct max517_platform_data *platform_data = client->dev.platform_data;
+ int chan;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -169,11 +178,21 @@ static int max517_probe(struct i2c_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
+ switch (id->driver_data) {
+ case ID_MAX521:
+ indio_dev->num_channels = 8;
+ break;
+ case ID_MAX520:
+ indio_dev->num_channels = 4;
+ break;
+ case ID_MAX519:
+ case ID_MAX518:
indio_dev->num_channels = 2;
+ break;
+ default: /* single channel for MAX517 */
+ indio_dev->num_channels = 1;
+ break;
+ }
indio_dev->channels = max517_channels;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &max517_info;
@@ -182,11 +201,11 @@ static int max517_probe(struct i2c_client *client,
* 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];
+ for (chan = 0; chan < indio_dev->num_channels; chan++) {
+ if (id->driver_data == ID_MAX518 || !platform_data)
+ data->vref_mv[chan] = 5000; /* mV */
+ else
+ data->vref_mv[chan] = platform_data->vref_mv[chan];
}
return iio_device_register(indio_dev);
@@ -202,6 +221,8 @@ static const struct i2c_device_id max517_id[] = {
{ "max517", ID_MAX517 },
{ "max518", ID_MAX518 },
{ "max519", ID_MAX519 },
+ { "max520", ID_MAX520 },
+ { "max521", ID_MAX521 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max517_id);
@@ -218,5 +239,5 @@ static struct i2c_driver max517_driver = {
module_i2c_driver(max517_driver);
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
-MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC");
+MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
index 7c5245d9..50ed8d1 100644
--- a/drivers/iio/frequency/ad9523.c
+++ b/drivers/iio/frequency/ad9523.c
@@ -445,7 +445,7 @@ static int ad9523_store_eeprom(struct iio_dev *indio_dev)
tmp = 4;
do {
- msleep(16);
+ msleep(20);
ret = ad9523_read(indio_dev,
AD9523_EEPROM_DATA_XFER_STATUS);
if (ret < 0)
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 63a25d9..10a0dfc 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -387,10 +387,8 @@ static struct adf4350_platform_data *adf4350_parse_dt(struct device *dev)
int ret;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(dev, "could not allocate memory for platform data\n");
+ if (!pdata)
return NULL;
- }
strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
@@ -613,9 +611,8 @@ static int adf4350_remove(struct spi_device *spi)
if (st->clk)
clk_disable_unprepare(st->clk);
- if (!IS_ERR(reg)) {
+ if (!IS_ERR(reg))
regulator_disable(reg);
- }
return 0;
}
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 36a3877..f46341b 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -16,6 +16,8 @@ itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o
+obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_gyro_sensor.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
diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c
index 1f967e0d..4415f55 100644
--- a/drivers/iio/gyro/bmg160.c
+++ b/drivers/iio/gyro/bmg160.c
@@ -67,6 +67,9 @@
#define BMG160_REG_INT_EN_0 0x15
#define BMG160_DATA_ENABLE_INT BIT(7)
+#define BMG160_REG_INT_EN_1 0x16
+#define BMG160_INT1_BIT_OD BIT(1)
+
#define BMG160_REG_XOUT_L 0x02
#define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2))
@@ -82,6 +85,9 @@
#define BMG160_REG_INT_STATUS_2 0x0B
#define BMG160_ANY_MOTION_MASK 0x07
+#define BMG160_ANY_MOTION_BIT_X BIT(0)
+#define BMG160_ANY_MOTION_BIT_Y BIT(1)
+#define BMG160_ANY_MOTION_BIT_Z BIT(2)
#define BMG160_REG_TEMP 0x08
#define BMG160_TEMP_CENTER_VAL 23
@@ -222,6 +228,19 @@ static int bmg160_chip_init(struct bmg160_data *data)
data->slope_thres = ret;
/* Set default interrupt mode */
+ ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_int_en_1\n");
+ return ret;
+ }
+ ret &= ~BMG160_INT1_BIT_OD;
+ ret = i2c_smbus_write_byte_data(data->client,
+ BMG160_REG_INT_EN_1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_en_1\n");
+ return ret;
+ }
+
ret = i2c_smbus_write_byte_data(data->client,
BMG160_REG_INT_RST_LATCH,
BMG160_INT_MODE_LATCH_INT |
@@ -237,7 +256,7 @@ static int bmg160_chip_init(struct bmg160_data *data)
static int bmg160_set_power_state(struct bmg160_data *data, bool on)
{
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
int ret;
if (on)
@@ -250,6 +269,9 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on)
if (ret < 0) {
dev_err(&data->client->dev,
"Failed: bmg160_set_power_state for %d\n", on);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
return ret;
}
#endif
@@ -705,6 +727,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev,
ret = bmg160_setup_any_motion_interrupt(data, state);
if (ret < 0) {
+ bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
@@ -743,7 +766,7 @@ static const struct attribute_group bmg160_attrs_group = {
static const struct iio_event_spec bmg160_event = {
.type = IIO_EV_TYPE_ROC,
- .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING,
+ .dir = IIO_EV_DIR_EITHER,
.mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
BIT(IIO_EV_INFO_ENABLE)
};
@@ -799,7 +822,7 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p)
int bit, ret, i = 0;
mutex_lock(&data->mutex);
- for_each_set_bit(bit, indio_dev->buffer->scan_mask,
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
indio_dev->masklength) {
ret = i2c_smbus_read_word_data(data->client,
BMG160_AXIS_TO_REG(bit));
@@ -871,6 +894,7 @@ static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig,
else
ret = bmg160_setup_new_data_interrupt(data, state);
if (ret < 0) {
+ bmg160_set_power_state(data, false);
mutex_unlock(&data->mutex);
return ret;
}
@@ -908,10 +932,24 @@ static irqreturn_t bmg160_event_handler(int irq, void *private)
else
dir = IIO_EV_DIR_FALLING;
- if (ret & BMG160_ANY_MOTION_MASK)
+ if (ret & BMG160_ANY_MOTION_BIT_X)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMG160_ANY_MOTION_BIT_Y)
+ iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_ROC,
+ dir),
+ data->timestamp);
+ if (ret & BMG160_ANY_MOTION_BIT_Z)
iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL,
0,
- IIO_MOD_X_OR_Y_OR_Z,
+ IIO_MOD_Z,
IIO_EV_TYPE_ROC,
dir),
data->timestamp);
@@ -963,16 +1001,12 @@ static int bmg160_gpio_probe(struct i2c_client *client,
dev = &client->dev;
/* data ready gpio interrupt pin */
- gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0);
+ gpio = devm_gpiod_get_index(dev, BMG160_GPIO_NAME, 0, GPIOD_IN);
if (IS_ERR(gpio)) {
dev_err(dev, "acpi gpio get index failed\n");
return PTR_ERR(gpio);
}
- ret = gpiod_direction_input(gpio);
- if (ret)
- return ret;
-
ret = gpiod_to_irq(gpio);
dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
@@ -1164,13 +1198,20 @@ static int bmg160_resume(struct device *dev)
}
#endif
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
static int bmg160_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
struct bmg160_data *data = iio_priv(indio_dev);
+ int ret;
- return bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+ ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "set mode failed\n");
+ return -EAGAIN;
+ }
+
+ return 0;
}
static int bmg160_runtime_resume(struct device *dev)
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index a3ea1e8..b5883b6 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -111,26 +111,20 @@ static int gyro_3d_read_raw(struct iio_dev *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);
+ report_id,
+ SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&gyro_state->common_attributes,
@@ -416,6 +410,7 @@ static struct platform_driver hid_gyro_3d_platform_driver = {
.id_table = hid_gyro_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_gyro_3d_probe,
.remove = hid_gyro_3d_remove,
diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c
index 6a8020d..f0fd940 100644
--- a/drivers/iio/gyro/itg3200_core.c
+++ b/drivers/iio/gyro/itg3200_core.c
@@ -223,6 +223,10 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
int ret;
u8 val;
+ ret = itg3200_reset(indio_dev);
+ if (ret)
+ goto err_ret;
+
ret = itg3200_read_reg_8(indio_dev, ITG3200_REG_ADDRESS, &val);
if (ret)
goto err_ret;
@@ -233,10 +237,6 @@ static int itg3200_initial_setup(struct iio_dev *indio_dev)
goto err_ret;
}
- ret = itg3200_reset(indio_dev);
- if (ret)
- goto err_ret;
-
ret = itg3200_enable_full_scale(indio_dev);
err_ret:
return ret;
@@ -351,6 +351,26 @@ static int itg3200_remove(struct i2c_client *client)
return 0;
}
+static int __maybe_unused itg3200_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct itg3200 *st = iio_priv(indio_dev);
+
+ dev_dbg(&st->i2c->dev, "suspend device");
+
+ return itg3200_write_reg_8(indio_dev, ITG3200_REG_POWER_MANAGEMENT,
+ ITG3200_SLEEP);
+}
+
+static int __maybe_unused itg3200_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ return itg3200_initial_setup(indio_dev);
+}
+
+static SIMPLE_DEV_PM_OPS(itg3200_pm_ops, itg3200_suspend, itg3200_resume);
+
static const struct i2c_device_id itg3200_id[] = {
{ "itg3200", 0 },
{ }
@@ -361,6 +381,7 @@ static struct i2c_driver itg3200_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "itg3200",
+ .pm = &itg3200_pm_ops,
},
.id_table = itg3200_id,
.probe = itg3200_probe,
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
new file mode 100644
index 0000000..0a8afdd
--- /dev/null
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, Samsung Electronics Co. Ltd. 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.
+ *
+ * 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/iio/common/ssp_sensors.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include "../common/ssp_sensors/ssp_iio_sensor.h"
+
+#define SSP_CHANNEL_COUNT 3
+
+#define SSP_GYROSCOPE_NAME "ssp-gyroscope"
+static const char ssp_gyro_name[] = SSP_GYROSCOPE_NAME;
+
+enum ssp_gyro_3d_channel {
+ SSP_CHANNEL_SCAN_INDEX_X,
+ SSP_CHANNEL_SCAN_INDEX_Y,
+ SSP_CHANNEL_SCAN_INDEX_Z,
+ SSP_CHANNEL_SCAN_INDEX_TIME,
+};
+
+static int ssp_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ u32 t;
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ t = ssp_get_sensor_delay(data, SSP_GYROSCOPE_SENSOR);
+ ssp_convert_to_freq(t, val, val2);
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ int ret;
+ struct ssp_data *data = dev_get_drvdata(indio_dev->dev.parent->parent);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = ssp_convert_to_time(val, val2);
+ ret = ssp_change_delay(data, SSP_GYROSCOPE_SENSOR, ret);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "gyro sensor enable fail\n");
+
+ return ret;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static struct iio_info ssp_gyro_iio_info = {
+ .read_raw = &ssp_gyro_read_raw,
+ .write_raw = &ssp_gyro_write_raw,
+};
+
+static const unsigned long ssp_gyro_scan_mask[] = { 0x07, 0, };
+
+static const struct iio_chan_spec ssp_gyro_channels[] = {
+ SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_X, SSP_CHANNEL_SCAN_INDEX_X),
+ SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Y, SSP_CHANNEL_SCAN_INDEX_Y),
+ SSP_CHANNEL_AG(IIO_ANGL_VEL, IIO_MOD_Z, SSP_CHANNEL_SCAN_INDEX_Z),
+ SSP_CHAN_TIMESTAMP(SSP_CHANNEL_SCAN_INDEX_TIME),
+};
+
+static int ssp_process_gyro_data(struct iio_dev *indio_dev, void *buf,
+ int64_t timestamp)
+{
+ return ssp_common_process_data(indio_dev, buf, SSP_GYROSCOPE_SIZE,
+ timestamp);
+}
+
+static const struct iio_buffer_setup_ops ssp_gyro_buffer_ops = {
+ .postenable = &ssp_common_buffer_postenable,
+ .postdisable = &ssp_common_buffer_postdisable,
+};
+
+static int ssp_gyro_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct ssp_sensor_data *spd;
+ struct iio_buffer *buffer;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ spd = iio_priv(indio_dev);
+
+ spd->process_data = ssp_process_gyro_data;
+ spd->type = SSP_GYROSCOPE_SENSOR;
+
+ indio_dev->name = ssp_gyro_name;
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &ssp_gyro_iio_info;
+ indio_dev->modes = INDIO_BUFFER_SOFTWARE;
+ indio_dev->channels = ssp_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels);
+ indio_dev->available_scan_masks = ssp_gyro_scan_mask;
+
+ buffer = devm_iio_kfifo_allocate(&pdev->dev);
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+
+ indio_dev->setup_ops = &ssp_gyro_buffer_ops;
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /* ssp registering should be done after all iio setup */
+ ssp_register_consumer(indio_dev, SSP_GYROSCOPE_SENSOR);
+
+ return 0;
+}
+
+static int ssp_gyro_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver ssp_gyro_driver = {
+ .driver = {
+ .name = SSP_GYROSCOPE_NAME,
+ },
+ .probe = ssp_gyro_probe,
+ .remove = ssp_gyro_remove,
+};
+
+module_platform_driver(ssp_gyro_driver);
+
+MODULE_AUTHOR("Karol Wrona <k.wrona@samsung.com>");
+MODULE_DESCRIPTION("Samsung sensorhub gyroscopes driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h
index c197360..5353d63 100644
--- a/drivers/iio/gyro/st_gyro.h
+++ b/drivers/iio/gyro/st_gyro.h
@@ -30,8 +30,7 @@ static const struct st_sensors_platform_data gyro_pdata = {
.drdy_int_pin = 2,
};
-int st_gyro_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *pdata);
+int st_gyro_common_probe(struct iio_dev *indio_dev);
void st_gyro_common_remove(struct iio_dev *indio_dev);
#ifdef CONFIG_IIO_BUFFER
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index f156fc6..21395f2 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -87,6 +87,31 @@
#define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08
#define ST_GYRO_2_MULTIREAD_BIT true
+/* CUSTOM VALUES FOR SENSOR 3 */
+#define ST_GYRO_3_WAI_EXP 0xd7
+#define ST_GYRO_3_ODR_ADDR 0x20
+#define ST_GYRO_3_ODR_MASK 0xc0
+#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00
+#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01
+#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02
+#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03
+#define ST_GYRO_3_PW_ADDR 0x20
+#define ST_GYRO_3_PW_MASK 0x08
+#define ST_GYRO_3_FS_ADDR 0x23
+#define ST_GYRO_3_FS_MASK 0x30
+#define ST_GYRO_3_FS_AVL_250_VAL 0x00
+#define ST_GYRO_3_FS_AVL_500_VAL 0x01
+#define ST_GYRO_3_FS_AVL_2000_VAL 0x02
+#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750)
+#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500)
+#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000)
+#define ST_GYRO_3_BDU_ADDR 0x23
+#define ST_GYRO_3_BDU_MASK 0x80
+#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22
+#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08
+#define ST_GYRO_3_MULTIREAD_BIT true
+
+
static const struct iio_chan_spec st_gyro_16bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -103,7 +128,7 @@ static const struct iio_chan_spec st_gyro_16bit_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3)
};
-static const struct st_sensors st_gyro_sensors[] = {
+static const struct st_sensor_settings st_gyro_sensors_settings[] = {
{
.wai = ST_GYRO_1_WAI_EXP,
.sensors_supported = {
@@ -225,6 +250,64 @@ static const struct st_sensors st_gyro_sensors[] = {
.multi_read_bit = ST_GYRO_2_MULTIREAD_BIT,
.bootime = 2,
},
+ {
+ .wai = ST_GYRO_3_WAI_EXP,
+ .sensors_supported = {
+ [0] = L3GD20_GYRO_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_gyro_16bit_channels,
+ .odr = {
+ .addr = ST_GYRO_3_ODR_ADDR,
+ .mask = ST_GYRO_3_ODR_MASK,
+ .odr_avl = {
+ { 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, },
+ { 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, },
+ { 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, },
+ { 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_GYRO_3_PW_ADDR,
+ .mask = ST_GYRO_3_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_GYRO_3_FS_ADDR,
+ .mask = ST_GYRO_3_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_GYRO_FS_AVL_250DPS,
+ .value = ST_GYRO_3_FS_AVL_250_VAL,
+ .gain = ST_GYRO_3_FS_AVL_250_GAIN,
+ },
+ [1] = {
+ .num = ST_GYRO_FS_AVL_500DPS,
+ .value = ST_GYRO_3_FS_AVL_500_VAL,
+ .gain = ST_GYRO_3_FS_AVL_500_GAIN,
+ },
+ [2] = {
+ .num = ST_GYRO_FS_AVL_2000DPS,
+ .value = ST_GYRO_3_FS_AVL_2000_VAL,
+ .gain = ST_GYRO_3_FS_AVL_2000_GAIN,
+ },
+ },
+ },
+ .bdu = {
+ .addr = ST_GYRO_3_BDU_ADDR,
+ .mask = ST_GYRO_3_BDU_MASK,
+ },
+ .drdy_irq = {
+ .addr = ST_GYRO_3_DRDY_IRQ_ADDR,
+ .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK,
+ },
+ .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT,
+ .bootime = 2,
+ },
};
static int st_gyro_read_raw(struct iio_dev *indio_dev,
@@ -309,8 +392,7 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {
#define ST_GYRO_TRIGGER_OPS NULL
#endif
-int st_gyro_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *pdata)
+int st_gyro_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *gdata = iio_priv(indio_dev);
int irq = gdata->get_irq_data_ready(indio_dev);
@@ -322,20 +404,22 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,
st_sensors_power_enable(indio_dev);
err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors);
+ ARRAY_SIZE(st_gyro_sensors_settings),
+ st_gyro_sensors_settings);
if (err < 0)
return err;
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
- gdata->multiread_bit = gdata->sensor->multi_read_bit;
- indio_dev->channels = gdata->sensor->ch;
+ gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
+ indio_dev->channels = gdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
gdata->current_fullscale = (struct st_sensor_fullscale_avl *)
- &gdata->sensor->fs.fs_avl[0];
- gdata->odr = gdata->sensor->odr.odr_avl[0].hz;
+ &gdata->sensor_settings->fs.fs_avl[0];
+ gdata->odr = gdata->sensor_settings->odr.odr_avl[0].hz;
- err = st_sensors_init_sensor(indio_dev, pdata);
+ err = st_sensors_init_sensor(indio_dev,
+ (struct st_sensors_platform_data *)&gyro_pdata);
if (err < 0)
return err;
diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c
index 8fa0ad2..64480b1 100644
--- a/drivers/iio/gyro/st_gyro_i2c.c
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -67,13 +67,11 @@ static int st_gyro_i2c_probe(struct i2c_client *client,
return -ENOMEM;
gdata = iio_priv(indio_dev);
- gdata->dev = &client->dev;
st_sensors_of_i2c_probe(client, st_gyro_of_match);
st_sensors_i2c_configure(indio_dev, client, gdata);
- err = st_gyro_common_probe(indio_dev,
- (struct st_sensors_platform_data *)&gyro_pdata);
+ err = st_gyro_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
index b4ad3be..e59bead 100644
--- a/drivers/iio/gyro/st_gyro_spi.c
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -29,12 +29,10 @@ static int st_gyro_spi_probe(struct spi_device *spi)
return -ENOMEM;
gdata = iio_priv(indio_dev);
- gdata->dev = &spi->dev;
st_sensors_spi_configure(indio_dev, spi, gdata);
- err = st_gyro_common_probe(indio_dev,
- (struct st_sensors_platform_data *)&gyro_pdata);
+ err = st_gyro_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index e116bd8..4813b79 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -22,4 +22,14 @@ config SI7005
To compile this driver as a module, choose M here: the module
will be called si7005.
+config SI7020
+ tristate "Si7013/20/21 Relative Humidity and Temperature Sensors"
+ depends on I2C
+ help
+ Say yes here to build support for the Silicon Labs Si7013/20/21
+ Relative Humidity and Temperature Sensors.
+
+ To compile this driver as a module, choose M here: the module
+ will be called si7020.
+
endmenu
diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile
index e3f3d94..86e2d26 100644
--- a/drivers/iio/humidity/Makefile
+++ b/drivers/iio/humidity/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_DHT11) += dht11.o
obj-$(CONFIG_SI7005) += si7005.o
+obj-$(CONFIG_SI7020) += si7020.o
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index 623c145..7d79a1a 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -29,6 +29,7 @@
#include <linux/wait.h>
#include <linux/bitops.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
@@ -39,8 +40,12 @@
#define DHT11_DATA_VALID_TIME 2000000000 /* 2s in ns */
-#define DHT11_EDGES_PREAMBLE 4
+#define DHT11_EDGES_PREAMBLE 2
#define DHT11_BITS_PER_READ 40
+/*
+ * Note that when reading the sensor actually 84 edges are detected, but
+ * since the last edge is not significant, we only store 83:
+ */
#define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1)
/* Data transmission timing (nano seconds) */
@@ -57,6 +62,7 @@ struct dht11 {
int irq;
struct completion completion;
+ struct mutex lock;
s64 timestamp;
int temperature;
@@ -88,7 +94,7 @@ static int dht11_decode(struct dht11 *dht11, int offset)
unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum;
/* Calculate timestamp resolution */
- for (i = 0; i < dht11->num_edges; ++i) {
+ for (i = 1; i < dht11->num_edges; ++i) {
t = dht11->edges[i].ts - dht11->edges[i-1].ts;
if (t > 0 && t < timeres)
timeres = t;
@@ -138,6 +144,27 @@ static int dht11_decode(struct dht11 *dht11, int offset)
return 0;
}
+/*
+ * IRQ handler called on GPIO edges
+ */
+static irqreturn_t dht11_handle_irq(int irq, void *data)
+{
+ struct iio_dev *iio = data;
+ struct dht11 *dht11 = iio_priv(iio);
+
+ /* TODO: Consider making the handler safe for IRQ sharing */
+ if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
+ dht11->edges[dht11->num_edges].ts = iio_get_time_ns();
+ dht11->edges[dht11->num_edges++].value =
+ gpio_get_value(dht11->gpio);
+
+ if (dht11->num_edges >= DHT11_EDGES_PER_READ)
+ complete(&dht11->completion);
+ }
+
+ return IRQ_HANDLED;
+}
+
static int dht11_read_raw(struct iio_dev *iio_dev,
const struct iio_chan_spec *chan,
int *val, int *val2, long m)
@@ -145,6 +172,7 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
struct dht11 *dht11 = iio_priv(iio_dev);
int ret;
+ mutex_lock(&dht11->lock);
if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) {
reinit_completion(&dht11->completion);
@@ -157,8 +185,17 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
if (ret)
goto err;
+ ret = request_irq(dht11->irq, dht11_handle_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ iio_dev->name, iio_dev);
+ if (ret)
+ goto err;
+
ret = wait_for_completion_killable_timeout(&dht11->completion,
HZ);
+
+ free_irq(dht11->irq, iio_dev);
+
if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) {
dev_err(&iio_dev->dev,
"Only %d signal edges detected\n",
@@ -185,6 +222,7 @@ static int dht11_read_raw(struct iio_dev *iio_dev,
ret = -EINVAL;
err:
dht11->num_edges = -1;
+ mutex_unlock(&dht11->lock);
return ret;
}
@@ -193,27 +231,6 @@ static const struct iio_info dht11_iio_info = {
.read_raw = dht11_read_raw,
};
-/*
- * IRQ handler called on GPIO edges
-*/
-static irqreturn_t dht11_handle_irq(int irq, void *data)
-{
- struct iio_dev *iio = data;
- struct dht11 *dht11 = iio_priv(iio);
-
- /* TODO: Consider making the handler safe for IRQ sharing */
- if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
- dht11->edges[dht11->num_edges].ts = iio_get_time_ns();
- dht11->edges[dht11->num_edges++].value =
- gpio_get_value(dht11->gpio);
-
- if (dht11->num_edges >= DHT11_EDGES_PER_READ)
- complete(&dht11->completion);
- }
-
- return IRQ_HANDLED;
-}
-
static const struct iio_chan_spec dht11_chan_spec[] = {
{ .type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), },
@@ -256,11 +273,6 @@ static int dht11_probe(struct platform_device *pdev)
dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio);
return -EINVAL;
}
- ret = devm_request_irq(dev, dht11->irq, dht11_handle_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- pdev->name, iio);
- if (ret)
- return ret;
dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1;
dht11->num_edges = -1;
@@ -268,6 +280,7 @@ static int dht11_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, iio);
init_completion(&dht11->completion);
+ mutex_init(&dht11->lock);
iio->name = pdev->name;
iio->dev.parent = &pdev->dev;
iio->info = &dht11_iio_info;
diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
new file mode 100644
index 0000000..fa3b809
--- /dev/null
+++ b/drivers/iio/humidity/si7020.c
@@ -0,0 +1,161 @@
+/*
+ * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
+ * Copyright (c) 2013,2014 Uplogix, Inc.
+ * David Barksdale <dbarksdale@uplogix.com>
+ *
+ * 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 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.
+ */
+
+/*
+ * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors
+ * are i2c devices which have an identical programming interface for
+ * measuring relative humidity and temperature. The Si7013 has an additional
+ * temperature input which this driver does not support.
+ *
+ * Data Sheets:
+ * Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf
+ * Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf
+ * Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Measure Relative Humidity, Hold Master Mode */
+#define SI7020CMD_RH_HOLD 0xE5
+/* Measure Temperature, Hold Master Mode */
+#define SI7020CMD_TEMP_HOLD 0xE3
+/* Software Reset */
+#define SI7020CMD_RESET 0xFE
+
+static int si7020_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct i2c_client **client = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_word_data(*client,
+ chan->type == IIO_TEMP ?
+ SI7020CMD_TEMP_HOLD :
+ SI7020CMD_RH_HOLD);
+ if (ret < 0)
+ return ret;
+ *val = ret >> 2;
+ if (chan->type == IIO_HUMIDITYRELATIVE)
+ *val &= GENMASK(11, 0);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP)
+ *val = 175720; /* = 175.72 * 1000 */
+ else
+ *val = 125 * 1000;
+ *val2 = 65536 >> 2;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_OFFSET:
+ /*
+ * Since iio_convert_raw_to_processed_unlocked assumes offset
+ * is an integer we have to round these values and lose
+ * accuracy.
+ * Relative humidity will be 0.0032959% too high and
+ * temperature will be 0.00277344 degrees too high.
+ * This is no big deal because it's within the accuracy of the
+ * sensor.
+ */
+ if (chan->type == IIO_TEMP)
+ *val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */
+ else
+ *val = -786; /* = -6 * (65536 >> 2) / 125 */
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec si7020_channels[] = {
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ }
+};
+
+static const struct iio_info si7020_info = {
+ .read_raw = si7020_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int si7020_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct i2c_client **data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENODEV;
+
+ /* Reset device, loads default settings. */
+ ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
+ if (ret < 0)
+ return ret;
+ /* Wait the maximum power-up time after software reset. */
+ msleep(15);
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ *data = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &si7020_info;
+ indio_dev->channels = si7020_channels;
+ indio_dev->num_channels = ARRAY_SIZE(si7020_channels);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id si7020_id[] = {
+ { "si7020", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si7020_id);
+
+static struct i2c_driver si7020_driver = {
+ .driver.name = "si7020",
+ .probe = si7020_probe,
+ .id_table = si7020_id,
+};
+
+module_i2c_driver(si7020_driver);
+MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors");
+MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index 5f0ea77..3598835 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -48,6 +48,8 @@ unsigned int iio_buffer_poll(struct file *filp,
ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
size_t n, loff_t *f_ps);
+int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev);
+void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr (&iio_buffer_poll)
#define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)
@@ -60,6 +62,13 @@ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);
#define iio_buffer_poll_addr NULL
#define iio_buffer_read_first_n_outer_addr NULL
+static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {}
+
static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {}
static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 2b0e451..5e610f7 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -25,6 +25,17 @@ config ADIS16480
Say yes here to build support for Analog Devices ADIS16375, ADIS16480,
ADIS16485, ADIS16488 inertial sensors.
+config KMX61
+ tristate "Kionix KMX61 6-axis accelerometer and magnetometer"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here if you want to build a driver for Kionix KMX61 6-axis
+ accelerometer and magnetometer.
+ To compile this driver as module, choose M here: the module will
+ be called kmx61.
+
source "drivers/iio/imu/inv_mpu6050/Kconfig"
endmenu
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 114d2c1..e1e6e3d 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -14,3 +14,5 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += inv_mpu6050/
+
+obj-$(CONFIG_KMX61) += kmx61.o
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c
index b70873d..fa795dc 100644
--- a/drivers/iio/imu/adis16400_core.c
+++ b/drivers/iio/imu/adis16400_core.c
@@ -26,6 +26,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/debugfs.h>
+#include <linux/bitops.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -414,7 +415,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&indio_dev->mlock);
if (ret)
return ret;
- val16 = ((val16 & 0xFFF) << 4) >> 4;
+ val16 = sign_extend32(val16, 11);
*val = val16;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index e0017c2..f53e9a8 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -60,7 +60,7 @@ int adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev)
iio_trigger_set_drvdata(adis->trig, adis);
ret = iio_trigger_register(adis->trig);
- indio_dev->trig = adis->trig;
+ indio_dev->trig = iio_trigger_get(adis->trig);
if (ret)
goto error_free_irq;
diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig
index 2d0608b..48fbc0b 100644
--- a/drivers/iio/imu/inv_mpu6050/Kconfig
+++ b/drivers/iio/imu/inv_mpu6050/Kconfig
@@ -7,6 +7,7 @@ config INV_MPU6050_IIO
depends on I2C && SYSFS
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+ select I2C_MUX
help
This driver supports the Invensense MPU6050 devices.
This driver can also support MPU6500 in MPU6050 compatibility mode
diff --git a/drivers/iio/imu/inv_mpu6050/Makefile b/drivers/iio/imu/inv_mpu6050/Makefile
index 3a677c7..f566f6a 100644
--- a/drivers/iio/imu/inv_mpu6050/Makefile
+++ b/drivers/iio/imu/inv_mpu6050/Makefile
@@ -3,4 +3,4 @@
#
obj-$(CONFIG_INV_MPU6050_IIO) += inv-mpu6050.o
-inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o
+inv-mpu6050-objs := inv_mpu_core.o inv_mpu_ring.o inv_mpu_trigger.o inv_mpu_acpi.o
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
new file mode 100644
index 0000000..1c982a5
--- /dev/null
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -0,0 +1,211 @@
+/*
+ * inv_mpu_acpi: ACPI processing for creating client devices
+ * Copyright (c) 2015, 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.
+ */
+
+#ifdef CONFIG_ACPI
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/dmi.h>
+#include <linux/acpi.h>
+#include "inv_mpu_iio.h"
+
+enum inv_mpu_product_name {
+ INV_MPU_NOT_MATCHED,
+ INV_MPU_ASUS_T100TA,
+};
+
+static enum inv_mpu_product_name matched_product_name;
+
+static int __init asus_t100_matched(const struct dmi_system_id *d)
+{
+ matched_product_name = INV_MPU_ASUS_T100TA;
+
+ return 0;
+}
+
+static const struct dmi_system_id inv_mpu_dev_list[] = {
+ {
+ .callback = asus_t100_matched,
+ .ident = "Asus Transformer Book T100",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
+ },
+ },
+ /* Add more matching tables here..*/
+ {}
+};
+
+static int asus_acpi_get_sensor_info(struct acpi_device *adev,
+ struct i2c_client *client,
+ struct i2c_board_info *info)
+{
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ int i;
+ acpi_status status;
+ union acpi_object *cpm;
+
+ status = acpi_evaluate_object(adev->handle, "CNF0", NULL, &buffer);
+ if (ACPI_FAILURE(status))
+ return -ENODEV;
+
+ cpm = buffer.pointer;
+ for (i = 0; i < cpm->package.count; ++i) {
+ union acpi_object *elem;
+ int j;
+
+ elem = &(cpm->package.elements[i]);
+ for (j = 0; j < elem->package.count; ++j) {
+ union acpi_object *sub_elem;
+
+ sub_elem = &(elem->package.elements[j]);
+ if (sub_elem->type == ACPI_TYPE_STRING)
+ strlcpy(info->type, sub_elem->string.pointer,
+ sizeof(info->type));
+ else if (sub_elem->type == ACPI_TYPE_INTEGER) {
+ if (sub_elem->integer.value != client->addr) {
+ info->addr = sub_elem->integer.value;
+ break; /* Not a MPU6500 primary */
+ }
+ }
+ }
+ }
+
+ kfree(buffer.pointer);
+
+ return cpm->package.count;
+}
+
+static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
+{
+ u32 *addr = data;
+
+ if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
+ struct acpi_resource_i2c_serialbus *sb;
+
+ sb = &ares->data.i2c_serial_bus;
+ if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
+ if (*addr)
+ *addr |= (sb->slave_address << 16);
+ else
+ *addr = sb->slave_address;
+ }
+ }
+
+ /* Tell the ACPI core that we already copied this address */
+ return 1;
+}
+
+static int inv_mpu_process_acpi_config(struct i2c_client *client,
+ unsigned short *primary_addr,
+ unsigned short *secondary_addr)
+{
+ const struct acpi_device_id *id;
+ struct acpi_device *adev;
+ u32 i2c_addr = 0;
+ LIST_HEAD(resources);
+ int ret;
+
+ id = acpi_match_device(client->dev.driver->acpi_match_table,
+ &client->dev);
+ if (!id)
+ return -ENODEV;
+
+ adev = ACPI_COMPANION(&client->dev);
+ if (!adev)
+ return -ENODEV;
+
+ ret = acpi_dev_get_resources(adev, &resources,
+ acpi_i2c_check_resource, &i2c_addr);
+ if (ret < 0)
+ return ret;
+
+ acpi_dev_free_resource_list(&resources);
+ *primary_addr = i2c_addr & 0x0000ffff;
+ *secondary_addr = (i2c_addr & 0xffff0000) >> 16;
+
+ return 0;
+}
+
+int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
+{
+
+ st->mux_client = NULL;
+ if (ACPI_HANDLE(&st->client->dev)) {
+ struct i2c_board_info info;
+ struct acpi_device *adev;
+ int ret = -1;
+
+ adev = ACPI_COMPANION(&st->client->dev);
+ memset(&info, 0, sizeof(info));
+
+ dmi_check_system(inv_mpu_dev_list);
+ switch (matched_product_name) {
+ case INV_MPU_ASUS_T100TA:
+ ret = asus_acpi_get_sensor_info(adev, st->client,
+ &info);
+ break;
+ /* Add more matched product processing here */
+ default:
+ break;
+ }
+
+ if (ret < 0) {
+ /* No matching DMI, so create device on INV6XX type */
+ unsigned short primary, secondary;
+
+ ret = inv_mpu_process_acpi_config(st->client, &primary,
+ &secondary);
+ if (!ret && secondary) {
+ char *name;
+
+ info.addr = secondary;
+ strlcpy(info.type, dev_name(&adev->dev),
+ sizeof(info.type));
+ name = strchr(info.type, ':');
+ if (name)
+ *name = '\0';
+ strlcat(info.type, "-client",
+ sizeof(info.type));
+ } else
+ return 0; /* no secondary addr, which is OK */
+ }
+ st->mux_client = i2c_new_device(st->mux_adapter, &info);
+ if (!st->mux_client)
+ return -ENODEV;
+
+ }
+
+ return 0;
+}
+
+void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
+{
+ if (st->mux_client)
+ i2c_unregister_device(st->mux_client);
+}
+#else
+
+#include "inv_mpu_iio.h"
+
+int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st)
+{
+ return 0;
+}
+
+void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st)
+{
+}
+#endif
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index b75519d..17d4bb1 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -23,6 +23,8 @@
#include <linux/kfifo.h>
#include <linux/spinlock.h>
#include <linux/iio/iio.h>
+#include <linux/i2c-mux.h>
+#include <linux/acpi.h>
#include "inv_mpu_iio.h"
/*
@@ -52,6 +54,7 @@ static const struct inv_mpu6050_reg_map reg_set_6050 = {
.int_enable = INV_MPU6050_REG_INT_ENABLE,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
+ .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@@ -77,6 +80,83 @@ int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 d)
return i2c_smbus_write_i2c_block_data(st->client, reg, 1, &d);
}
+/*
+ * The i2c read/write needs to happen in unlocked mode. As the parent
+ * adapter is common. If we use locked versions, it will fail as
+ * the mux adapter will lock the parent i2c adapter, while calling
+ * select/deselect functions.
+ */
+static int inv_mpu6050_write_reg_unlocked(struct inv_mpu6050_state *st,
+ u8 reg, u8 d)
+{
+ int ret;
+ u8 buf[2];
+ struct i2c_msg msg[1] = {
+ {
+ .addr = st->client->addr,
+ .flags = 0,
+ .len = sizeof(buf),
+ .buf = buf,
+ }
+ };
+
+ buf[0] = reg;
+ buf[1] = d;
+ ret = __i2c_transfer(st->client->adapter, msg, 1);
+ if (ret != 1)
+ return ret;
+
+ return 0;
+}
+
+static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
+ u32 chan_id)
+{
+ struct iio_dev *indio_dev = mux_priv;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ /* Use the same mutex which was used everywhere to protect power-op */
+ mutex_lock(&indio_dev->mlock);
+ if (!st->powerup_count) {
+ ret = inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
+ 0);
+ if (ret)
+ goto write_error;
+
+ msleep(INV_MPU6050_REG_UP_TIME);
+ }
+ if (!ret) {
+ st->powerup_count++;
+ ret = inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
+ st->client->irq |
+ INV_MPU6050_BIT_BYPASS_EN);
+ }
+write_error:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
+ void *mux_priv, u32 chan_id)
+{
+ struct iio_dev *indio_dev = mux_priv;
+ struct inv_mpu6050_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&indio_dev->mlock);
+ /* It doesn't really mattter, if any of the calls fails */
+ inv_mpu6050_write_reg_unlocked(st, st->reg->int_pin_cfg,
+ st->client->irq);
+ st->powerup_count--;
+ if (!st->powerup_count)
+ inv_mpu6050_write_reg_unlocked(st, st->reg->pwr_mgmt_1,
+ INV_MPU6050_BIT_SLEEP);
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
{
u8 d, mgmt_1;
@@ -133,13 +213,22 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on)
{
- int result;
+ int result = 0;
+
+ if (power_on) {
+ /* Already under indio-dev->mlock mutex */
+ if (!st->powerup_count)
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
+ 0);
+ if (!result)
+ st->powerup_count++;
+ } else {
+ st->powerup_count--;
+ if (!st->powerup_count)
+ result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
+ INV_MPU6050_BIT_SLEEP);
+ }
- if (power_on)
- result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1, 0);
- else
- result = inv_mpu6050_write_reg(st, st->reg->pwr_mgmt_1,
- INV_MPU6050_BIT_SLEEP);
if (result)
return result;
@@ -321,42 +410,46 @@ error_read_raw:
}
}
-static int inv_mpu6050_write_fsr(struct inv_mpu6050_state *st, int fsr)
+static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fsr < 0 || fsr > INV_MPU6050_MAX_GYRO_FS_PARAM)
- return -EINVAL;
- if (fsr == st->chip_config.fsr)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) {
+ if (gyro_scale_6050[i] == val) {
+ d = (i << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->gyro_config, d);
+ if (result)
+ return result;
- d = (fsr << INV_MPU6050_GYRO_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->gyro_config, d);
- if (result)
- return result;
- st->chip_config.fsr = fsr;
+ st->chip_config.fsr = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
-static int inv_mpu6050_write_accel_fs(struct inv_mpu6050_state *st, int fs)
+static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val)
{
- int result;
+ int result, i;
u8 d;
- if (fs < 0 || fs > INV_MPU6050_MAX_ACCL_FS_PARAM)
- return -EINVAL;
- if (fs == st->chip_config.accl_fs)
- return 0;
+ for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) {
+ if (accel_scale[i] == val) {
+ d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
+ result = inv_mpu6050_write_reg(st,
+ st->reg->accl_config, d);
+ if (result)
+ return result;
- d = (fs << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT);
- result = inv_mpu6050_write_reg(st, st->reg->accl_config, d);
- if (result)
- return result;
- st->chip_config.accl_fs = fs;
+ st->chip_config.accl_fs = i;
+ return 0;
+ }
+ }
- return 0;
+ return -EINVAL;
}
static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
@@ -382,10 +475,10 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_ANGL_VEL:
- result = inv_mpu6050_write_fsr(st, val);
+ result = inv_mpu6050_write_gyro_scale(st, val2);
break;
case IIO_ACCEL:
- result = inv_mpu6050_write_accel_fs(st, val);
+ result = inv_mpu6050_write_accel_scale(st, val2);
break;
default:
result = -EINVAL;
@@ -673,6 +766,7 @@ static int inv_mpu_probe(struct i2c_client *client,
st = iio_priv(indio_dev);
st->client = client;
+ st->powerup_count = 0;
pdata = dev_get_platdata(&client->dev);
if (pdata)
st->plat_data = *pdata;
@@ -690,7 +784,11 @@ static int inv_mpu_probe(struct i2c_client *client,
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
- indio_dev->name = id->name;
+ /* id will be NULL when enumerated via ACPI */
+ if (id)
+ indio_dev->name = (char *)id->name;
+ else
+ indio_dev->name = (char *)dev_name(&client->dev);
indio_dev->channels = inv_mpu_channels;
indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
@@ -720,8 +818,27 @@ static int inv_mpu_probe(struct i2c_client *client,
goto out_remove_trigger;
}
+ st->mux_adapter = i2c_add_mux_adapter(client->adapter,
+ &client->dev,
+ indio_dev,
+ 0, 0, 0,
+ inv_mpu6050_select_bypass,
+ inv_mpu6050_deselect_bypass);
+ if (!st->mux_adapter) {
+ result = -ENODEV;
+ goto out_unreg_device;
+ }
+
+ result = inv_mpu_acpi_create_mux_client(st);
+ if (result)
+ goto out_del_mux;
+
return 0;
+out_del_mux:
+ i2c_del_mux_adapter(st->mux_adapter);
+out_unreg_device:
+ iio_device_unregister(indio_dev);
out_remove_trigger:
inv_mpu6050_remove_trigger(st);
out_unreg_ring:
@@ -734,6 +851,8 @@ static int inv_mpu_remove(struct i2c_client *client)
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct inv_mpu6050_state *st = iio_priv(indio_dev);
+ inv_mpu_acpi_delete_mux_client(st);
+ i2c_del_mux_adapter(st->mux_adapter);
iio_device_unregister(indio_dev);
inv_mpu6050_remove_trigger(st);
iio_triggered_buffer_cleanup(indio_dev);
@@ -772,6 +891,13 @@ static const struct i2c_device_id inv_mpu_id[] = {
MODULE_DEVICE_TABLE(i2c, inv_mpu_id);
+static const struct acpi_device_id inv_acpi_match[] = {
+ {"INVN6500", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(acpi, inv_acpi_match);
+
static struct i2c_driver inv_mpu_driver = {
.probe = inv_mpu_probe,
.remove = inv_mpu_remove,
@@ -780,6 +906,7 @@ static struct i2c_driver inv_mpu_driver = {
.owner = THIS_MODULE,
.name = "inv-mpu6050",
.pm = INV_MPU6050_PMOPS,
+ .acpi_match_table = ACPI_PTR(inv_acpi_match),
},
};
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index e779931..db0a4a2 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -54,6 +54,7 @@ struct inv_mpu6050_reg_map {
u8 int_enable;
u8 pwr_mgmt_1;
u8 pwr_mgmt_2;
+ u8 int_pin_cfg;
};
/*device enum */
@@ -119,6 +120,9 @@ struct inv_mpu6050_state {
enum inv_devices chip_type;
spinlock_t time_stamp_lock;
struct i2c_client *client;
+ struct i2c_adapter *mux_adapter;
+ struct i2c_client *mux_client;
+ unsigned int powerup_count;
struct inv_mpu6050_platform_data plat_data;
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
};
@@ -179,6 +183,9 @@ struct inv_mpu6050_state {
/* 6 + 6 round up and plus 8 */
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
+#define INV_MPU6050_REG_INT_PIN_CFG 0x37
+#define INV_MPU6050_BIT_BYPASS_EN 0x2
+
/* init parameters */
#define INV_MPU6050_INIT_FIFO_RATE 50
#define INV_MPU6050_TIME_STAMP_TOR 5
@@ -245,3 +252,5 @@ int inv_reset_fifo(struct iio_dev *indio_dev);
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
int inv_mpu6050_write_reg(struct inv_mpu6050_state *st, int reg, u8 val);
int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, bool power_on);
+int inv_mpu_acpi_create_mux_client(struct inv_mpu6050_state *st);
+void inv_mpu_acpi_delete_mux_client(struct inv_mpu6050_state *st);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index 0cd306a..ba27e27 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -24,6 +24,16 @@
#include <linux/poll.h>
#include "inv_mpu_iio.h"
+static void inv_clear_kfifo(struct inv_mpu6050_state *st)
+{
+ unsigned long flags;
+
+ /* take the spin lock sem to avoid interrupt kick in */
+ spin_lock_irqsave(&st->time_stamp_lock, flags);
+ kfifo_reset(&st->timestamps);
+ spin_unlock_irqrestore(&st->time_stamp_lock, flags);
+}
+
int inv_reset_fifo(struct iio_dev *indio_dev)
{
int result;
@@ -50,6 +60,10 @@ int inv_reset_fifo(struct iio_dev *indio_dev)
INV_MPU6050_BIT_FIFO_RST);
if (result)
goto reset_fifo_fail;
+
+ /* clear timestamps fifo */
+ inv_clear_kfifo(st);
+
/* enable interrupt */
if (st->chip_config.accl_fifo_enable ||
st->chip_config.gyro_fifo_enable) {
@@ -83,16 +97,6 @@ reset_fifo_fail:
return result;
}
-static void inv_clear_kfifo(struct inv_mpu6050_state *st)
-{
- unsigned long flags;
-
- /* take the spin lock sem to avoid interrupt kick in */
- spin_lock_irqsave(&st->time_stamp_lock, flags);
- kfifo_reset(&st->timestamps);
- spin_unlock_irqrestore(&st->time_stamp_lock, flags);
-}
-
/**
* inv_mpu6050_irq_handler() - Cache a timestamp at each data ready interrupt.
*/
@@ -184,7 +188,6 @@ end_session:
flush_fifo:
/* Flush HW and SW FIFOs. */
inv_reset_fifo(indio_dev);
- inv_clear_kfifo(st);
mutex_unlock(&indio_dev->mlock);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
index 926fcce..844610c 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -116,40 +116,35 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
int ret;
struct inv_mpu6050_state *st = iio_priv(indio_dev);
- st->trig = iio_trigger_alloc("%s-dev%d",
- indio_dev->name,
- indio_dev->id);
- if (st->trig == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
- ret = request_irq(st->client->irq, &iio_trigger_generic_data_rdy_poll,
- IRQF_TRIGGER_RISING,
- "inv_mpu",
- st->trig);
+ st->trig = devm_iio_trigger_alloc(&indio_dev->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!st->trig)
+ return -ENOMEM;
+
+ ret = devm_request_irq(&indio_dev->dev, st->client->irq,
+ &iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_RISING,
+ "inv_mpu",
+ st->trig);
if (ret)
- goto error_free_trig;
+ return ret;
+
st->trig->dev.parent = &st->client->dev;
st->trig->ops = &inv_mpu_trigger_ops;
iio_trigger_set_drvdata(st->trig, indio_dev);
+
ret = iio_trigger_register(st->trig);
if (ret)
- goto error_free_irq;
+ return ret;
+
indio_dev->trig = iio_trigger_get(st->trig);
return 0;
-
-error_free_irq:
- free_irq(st->client->irq, st->trig);
-error_free_trig:
- iio_trigger_free(st->trig);
-error_ret:
- return ret;
}
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st)
{
iio_trigger_unregister(st->trig);
- free_irq(st->client->irq, st->trig);
- iio_trigger_free(st->trig);
}
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
new file mode 100644
index 0000000..462a010
--- /dev/null
+++ b/drivers/iio/imu/kmx61.c
@@ -0,0 +1,1579 @@
+/*
+ * KMX61 - Kionix 6-axis Accelerometer/Magnetometer
+ *
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * 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.
+ *
+ * IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F).
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define KMX61_DRV_NAME "kmx61"
+#define KMX61_GPIO_NAME "kmx61_int"
+#define KMX61_IRQ_NAME "kmx61_event"
+
+#define KMX61_REG_WHO_AM_I 0x00
+#define KMX61_REG_INS1 0x01
+#define KMX61_REG_INS2 0x02
+
+/*
+ * three 16-bit accelerometer output registers for X/Y/Z axis
+ * we use only XOUT_L as a base register, all other addresses
+ * can be obtained by applying an offset and are provided here
+ * only for clarity.
+ */
+#define KMX61_ACC_XOUT_L 0x0A
+#define KMX61_ACC_XOUT_H 0x0B
+#define KMX61_ACC_YOUT_L 0x0C
+#define KMX61_ACC_YOUT_H 0x0D
+#define KMX61_ACC_ZOUT_L 0x0E
+#define KMX61_ACC_ZOUT_H 0x0F
+
+/*
+ * one 16-bit temperature output register
+ */
+#define KMX61_TEMP_L 0x10
+#define KMX61_TEMP_H 0x11
+
+/*
+ * three 16-bit magnetometer output registers for X/Y/Z axis
+ */
+#define KMX61_MAG_XOUT_L 0x12
+#define KMX61_MAG_XOUT_H 0x13
+#define KMX61_MAG_YOUT_L 0x14
+#define KMX61_MAG_YOUT_H 0x15
+#define KMX61_MAG_ZOUT_L 0x16
+#define KMX61_MAG_ZOUT_H 0x17
+
+#define KMX61_REG_INL 0x28
+#define KMX61_REG_STBY 0x29
+#define KMX61_REG_CTRL1 0x2A
+#define KMX61_REG_CTRL2 0x2B
+#define KMX61_REG_ODCNTL 0x2C
+#define KMX61_REG_INC1 0x2D
+
+#define KMX61_REG_WUF_THRESH 0x3D
+#define KMX61_REG_WUF_TIMER 0x3E
+
+#define KMX61_ACC_STBY_BIT BIT(0)
+#define KMX61_MAG_STBY_BIT BIT(1)
+#define KMX61_ACT_STBY_BIT BIT(7)
+
+#define KMX61_ALL_STBY (KMX61_ACC_STBY_BIT | KMX61_MAG_STBY_BIT)
+
+#define KMX61_REG_INS1_BIT_WUFS BIT(1)
+
+#define KMX61_REG_INS2_BIT_ZP BIT(0)
+#define KMX61_REG_INS2_BIT_ZN BIT(1)
+#define KMX61_REG_INS2_BIT_YP BIT(2)
+#define KMX61_REG_INS2_BIT_YN BIT(3)
+#define KMX61_REG_INS2_BIT_XP BIT(4)
+#define KMX61_REG_INS2_BIT_XN BIT(5)
+
+#define KMX61_REG_CTRL1_GSEL_MASK 0x03
+
+#define KMX61_REG_CTRL1_BIT_RES BIT(4)
+#define KMX61_REG_CTRL1_BIT_DRDYE BIT(5)
+#define KMX61_REG_CTRL1_BIT_WUFE BIT(6)
+#define KMX61_REG_CTRL1_BIT_BTSE BIT(7)
+
+#define KMX61_REG_INC1_BIT_WUFS BIT(0)
+#define KMX61_REG_INC1_BIT_DRDYM BIT(1)
+#define KMX61_REG_INC1_BIT_DRDYA BIT(2)
+#define KMX61_REG_INC1_BIT_IEN BIT(5)
+
+#define KMX61_ACC_ODR_SHIFT 0
+#define KMX61_MAG_ODR_SHIFT 4
+#define KMX61_ACC_ODR_MASK 0x0F
+#define KMX61_MAG_ODR_MASK 0xF0
+
+#define KMX61_OWUF_MASK 0x7
+
+#define KMX61_DEFAULT_WAKE_THRESH 1
+#define KMX61_DEFAULT_WAKE_DURATION 1
+
+#define KMX61_SLEEP_DELAY_MS 2000
+
+#define KMX61_CHIP_ID 0x12
+
+/* KMX61 devices */
+#define KMX61_ACC 0x01
+#define KMX61_MAG 0x02
+
+struct kmx61_data {
+ struct i2c_client *client;
+
+ /* serialize access to non-atomic ops, e.g set_mode */
+ struct mutex lock;
+
+ /* standby state */
+ bool acc_stby;
+ bool mag_stby;
+
+ /* power state */
+ bool acc_ps;
+ bool mag_ps;
+
+ /* config bits */
+ u8 range;
+ u8 odr_bits;
+ u8 wake_thresh;
+ u8 wake_duration;
+
+ /* accelerometer specific data */
+ struct iio_dev *acc_indio_dev;
+ struct iio_trigger *acc_dready_trig;
+ struct iio_trigger *motion_trig;
+ bool acc_dready_trig_on;
+ bool motion_trig_on;
+ bool ev_enable_state;
+
+ /* magnetometer specific data */
+ struct iio_dev *mag_indio_dev;
+ struct iio_trigger *mag_dready_trig;
+ bool mag_dready_trig_on;
+};
+
+enum kmx61_range {
+ KMX61_RANGE_2G,
+ KMX61_RANGE_4G,
+ KMX61_RANGE_8G,
+};
+
+enum kmx61_axis {
+ KMX61_AXIS_X,
+ KMX61_AXIS_Y,
+ KMX61_AXIS_Z,
+};
+
+static const u16 kmx61_uscale_table[] = {9582, 19163, 38326};
+
+static const struct {
+ int val;
+ int val2;
+} kmx61_samp_freq_table[] = { {12, 500000},
+ {25, 0},
+ {50, 0},
+ {100, 0},
+ {200, 0},
+ {400, 0},
+ {800, 0},
+ {1600, 0},
+ {0, 781000},
+ {1, 563000},
+ {3, 125000},
+ {6, 250000} };
+
+static const struct {
+ int val;
+ int val2;
+ int odr_bits;
+} kmx61_wake_up_odr_table[] = { {0, 781000, 0x00},
+ {1, 563000, 0x01},
+ {3, 125000, 0x02},
+ {6, 250000, 0x03},
+ {12, 500000, 0x04},
+ {25, 0, 0x05},
+ {50, 0, 0x06},
+ {100, 0, 0x06},
+ {200, 0, 0x06},
+ {400, 0, 0x06},
+ {800, 0, 0x06},
+ {1600, 0, 0x06} };
+
+static IIO_CONST_ATTR(accel_scale_available, "0.009582 0.019163 0.038326");
+static IIO_CONST_ATTR(magn_scale_available, "0.001465");
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "0.781000 1.563000 3.125000 6.250000 12.500000 25 50 100 200 400 800");
+
+static struct attribute *kmx61_acc_attributes[] = {
+ &iio_const_attr_accel_scale_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute *kmx61_mag_attributes[] = {
+ &iio_const_attr_magn_scale_available.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group kmx61_acc_attribute_group = {
+ .attrs = kmx61_acc_attributes,
+};
+
+static const struct attribute_group kmx61_mag_attribute_group = {
+ .attrs = kmx61_mag_attributes,
+};
+
+static const struct iio_event_spec kmx61_event = {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD),
+};
+
+#define KMX61_ACC_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) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .address = KMX61_ACC, \
+ .scan_index = KMX61_AXIS_ ## _axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_LE, \
+ }, \
+ .event_spec = &kmx61_event, \
+ .num_event_specs = 1 \
+}
+
+#define KMX61_MAG_CHAN(_axis) { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _axis, \
+ .address = KMX61_MAG, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = KMX61_AXIS_ ## _axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 14, \
+ .storagebits = 16, \
+ .shift = 2, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
+static const struct iio_chan_spec kmx61_acc_channels[] = {
+ KMX61_ACC_CHAN(X),
+ KMX61_ACC_CHAN(Y),
+ KMX61_ACC_CHAN(Z),
+};
+
+static const struct iio_chan_spec kmx61_mag_channels[] = {
+ KMX61_MAG_CHAN(X),
+ KMX61_MAG_CHAN(Y),
+ KMX61_MAG_CHAN(Z),
+};
+
+static void kmx61_set_data(struct iio_dev *indio_dev, struct kmx61_data *data)
+{
+ struct kmx61_data **priv = iio_priv(indio_dev);
+
+ *priv = data;
+}
+
+static struct kmx61_data *kmx61_get_data(struct iio_dev *indio_dev)
+{
+ return *(struct kmx61_data **)iio_priv(indio_dev);
+}
+
+static int kmx61_convert_freq_to_bit(int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kmx61_samp_freq_table); i++)
+ if (val == kmx61_samp_freq_table[i].val &&
+ val2 == kmx61_samp_freq_table[i].val2)
+ return i;
+ return -EINVAL;
+}
+
+static int kmx61_convert_wake_up_odr_to_bit(int val, int val2)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(kmx61_wake_up_odr_table); ++i)
+ if (kmx61_wake_up_odr_table[i].val == val &&
+ kmx61_wake_up_odr_table[i].val2 == val2)
+ return kmx61_wake_up_odr_table[i].odr_bits;
+ return -EINVAL;
+}
+
+/**
+ * kmx61_set_mode() - set KMX61 device operating mode
+ * @data - kmx61 device private data pointer
+ * @mode - bitmask, indicating operating mode for @device
+ * @device - bitmask, indicating device for which @mode needs to be set
+ * @update - update stby bits stored in device's private @data
+ *
+ * For each sensor (accelerometer/magnetometer) there are two operating modes
+ * STANDBY and OPERATION. Neither accel nor magn can be disabled independently
+ * if they are both enabled. Internal sensors state is saved in acc_stby and
+ * mag_stby members of driver's private @data.
+ */
+static int kmx61_set_mode(struct kmx61_data *data, u8 mode, u8 device,
+ bool update)
+{
+ int ret;
+ int acc_stby = -1, mag_stby = -1;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_stby\n");
+ return ret;
+ }
+ if (device & KMX61_ACC) {
+ if (mode & KMX61_ACC_STBY_BIT) {
+ ret |= KMX61_ACC_STBY_BIT;
+ acc_stby = 1;
+ } else {
+ ret &= ~KMX61_ACC_STBY_BIT;
+ acc_stby = 0;
+ }
+ }
+
+ if (device & KMX61_MAG) {
+ if (mode & KMX61_MAG_STBY_BIT) {
+ ret |= KMX61_MAG_STBY_BIT;
+ mag_stby = 1;
+ } else {
+ ret &= ~KMX61_MAG_STBY_BIT;
+ mag_stby = 0;
+ }
+ }
+
+ if (mode & KMX61_ACT_STBY_BIT)
+ ret |= KMX61_ACT_STBY_BIT;
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_STBY, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_stby\n");
+ return ret;
+ }
+
+ if (acc_stby != -1 && update)
+ data->acc_stby = acc_stby;
+ if (mag_stby != -1 && update)
+ data->mag_stby = mag_stby;
+
+ return 0;
+}
+
+static int kmx61_get_mode(struct kmx61_data *data, u8 *mode, u8 device)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_STBY);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_stby\n");
+ return ret;
+ }
+ *mode = 0;
+
+ if (device & KMX61_ACC) {
+ if (ret & KMX61_ACC_STBY_BIT)
+ *mode |= KMX61_ACC_STBY_BIT;
+ else
+ *mode &= ~KMX61_ACC_STBY_BIT;
+ }
+
+ if (device & KMX61_MAG) {
+ if (ret & KMX61_MAG_STBY_BIT)
+ *mode |= KMX61_MAG_STBY_BIT;
+ else
+ *mode &= ~KMX61_MAG_STBY_BIT;
+ }
+
+ return 0;
+}
+
+static int kmx61_set_wake_up_odr(struct kmx61_data *data, int val, int val2)
+{
+ int ret, odr_bits;
+
+ odr_bits = kmx61_convert_wake_up_odr_to_bit(val, val2);
+ if (odr_bits < 0)
+ return odr_bits;
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL2,
+ odr_bits);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error writing reg_ctrl2\n");
+ return ret;
+}
+
+static int kmx61_set_odr(struct kmx61_data *data, int val, int val2, u8 device)
+{
+ int ret;
+ u8 mode;
+ int lodr_bits, odr_bits;
+
+ ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+ if (ret < 0)
+ return ret;
+
+ lodr_bits = kmx61_convert_freq_to_bit(val, val2);
+ if (lodr_bits < 0)
+ return lodr_bits;
+
+ /* To change ODR, accel and magn must be in STDBY */
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG,
+ true);
+ if (ret < 0)
+ return ret;
+
+ odr_bits = 0;
+ if (device & KMX61_ACC)
+ odr_bits |= lodr_bits << KMX61_ACC_ODR_SHIFT;
+ if (device & KMX61_MAG)
+ odr_bits |= lodr_bits << KMX61_MAG_ODR_SHIFT;
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_ODCNTL,
+ odr_bits);
+ if (ret < 0)
+ return ret;
+
+ data->odr_bits = odr_bits;
+
+ if (device & KMX61_ACC) {
+ ret = kmx61_set_wake_up_odr(data, val, val2);
+ if (ret)
+ return ret;
+ }
+
+ return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+static int kmx61_get_odr(struct kmx61_data *data, int *val, int *val2,
+ u8 device)
+{
+ u8 lodr_bits;
+
+ if (device & KMX61_ACC)
+ lodr_bits = (data->odr_bits >> KMX61_ACC_ODR_SHIFT) &
+ KMX61_ACC_ODR_MASK;
+ else if (device & KMX61_MAG)
+ lodr_bits = (data->odr_bits >> KMX61_MAG_ODR_SHIFT) &
+ KMX61_MAG_ODR_MASK;
+ else
+ return -EINVAL;
+
+ if (lodr_bits >= ARRAY_SIZE(kmx61_samp_freq_table))
+ return -EINVAL;
+
+ *val = kmx61_samp_freq_table[lodr_bits].val;
+ *val2 = kmx61_samp_freq_table[lodr_bits].val2;
+
+ return 0;
+}
+
+static int kmx61_set_range(struct kmx61_data *data, u8 range)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+ return ret;
+ }
+
+ ret &= ~KMX61_REG_CTRL1_GSEL_MASK;
+ ret |= range & KMX61_REG_CTRL1_GSEL_MASK;
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+ return ret;
+ }
+
+ data->range = range;
+
+ return 0;
+}
+
+static int kmx61_set_scale(struct kmx61_data *data, u16 uscale)
+{
+ int ret, i;
+ u8 mode;
+
+ for (i = 0; i < ARRAY_SIZE(kmx61_uscale_table); i++) {
+ if (kmx61_uscale_table[i] == uscale) {
+ ret = kmx61_get_mode(data, &mode,
+ KMX61_ACC | KMX61_MAG);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY,
+ KMX61_ACC | KMX61_MAG, true);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_set_range(data, i);
+ if (ret < 0)
+ return ret;
+
+ return kmx61_set_mode(data, mode,
+ KMX61_ACC | KMX61_MAG, true);
+ }
+ }
+ return -EINVAL;
+}
+
+static int kmx61_chip_init(struct kmx61_data *data)
+{
+ int ret, val, val2;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_WHO_AM_I);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading who_am_i\n");
+ return ret;
+ }
+
+ if (ret != KMX61_CHIP_ID) {
+ dev_err(&data->client->dev,
+ "Wrong chip id, got %x expected %x\n",
+ ret, KMX61_CHIP_ID);
+ return -EINVAL;
+ }
+
+ /* set accel 12bit, 4g range */
+ ret = kmx61_set_range(data, KMX61_RANGE_4G);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_ODCNTL);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_odcntl\n");
+ return ret;
+ }
+ data->odr_bits = ret;
+
+ /*
+ * set output data rate for wake up (motion detection) function
+ * to match data rate for accelerometer sampling
+ */
+ ret = kmx61_get_odr(data, &val, &val2, KMX61_ACC);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_set_wake_up_odr(data, val, val2);
+ if (ret < 0)
+ return ret;
+
+ /* set acc/magn to OPERATION mode */
+ ret = kmx61_set_mode(data, 0, KMX61_ACC | KMX61_MAG, true);
+ if (ret < 0)
+ return ret;
+
+ data->wake_thresh = KMX61_DEFAULT_WAKE_THRESH;
+ data->wake_duration = KMX61_DEFAULT_WAKE_DURATION;
+
+ return 0;
+}
+
+static int kmx61_setup_new_data_interrupt(struct kmx61_data *data,
+ bool status, u8 device)
+{
+ u8 mode;
+ int ret;
+
+ ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+ return ret;
+ }
+
+ if (status) {
+ ret |= KMX61_REG_INC1_BIT_IEN;
+ if (device & KMX61_ACC)
+ ret |= KMX61_REG_INC1_BIT_DRDYA;
+ if (device & KMX61_MAG)
+ ret |= KMX61_REG_INC1_BIT_DRDYM;
+ } else {
+ ret &= ~KMX61_REG_INC1_BIT_IEN;
+ if (device & KMX61_ACC)
+ ret &= ~KMX61_REG_INC1_BIT_DRDYA;
+ if (device & KMX61_MAG)
+ ret &= ~KMX61_REG_INC1_BIT_DRDYM;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+ return ret;
+ }
+
+ if (status)
+ ret |= KMX61_REG_CTRL1_BIT_DRDYE;
+ else
+ ret &= ~KMX61_REG_CTRL1_BIT_DRDYE;
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+ return ret;
+ }
+
+ return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+static int kmx61_chip_update_thresholds(struct kmx61_data *data)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ KMX61_REG_WUF_TIMER,
+ data->wake_duration);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Errow writing reg_wuf_timer\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ KMX61_REG_WUF_THRESH,
+ data->wake_thresh);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error writing reg_wuf_thresh\n");
+
+ return ret;
+}
+
+static int kmx61_setup_any_motion_interrupt(struct kmx61_data *data,
+ bool status)
+{
+ u8 mode;
+ int ret;
+
+ ret = kmx61_get_mode(data, &mode, KMX61_ACC | KMX61_MAG);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+ if (ret < 0)
+ return ret;
+
+ ret = kmx61_chip_update_thresholds(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INC1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_inc1\n");
+ return ret;
+ }
+ if (status)
+ ret |= (KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS);
+ else
+ ret &= ~(KMX61_REG_INC1_BIT_IEN | KMX61_REG_INC1_BIT_WUFS);
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_INC1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_inc1\n");
+ return ret;
+ }
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+ return ret;
+ }
+
+ if (status)
+ ret |= KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE;
+ else
+ ret &= ~(KMX61_REG_CTRL1_BIT_WUFE | KMX61_REG_CTRL1_BIT_BTSE);
+
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+ return ret;
+ }
+ mode |= KMX61_ACT_STBY_BIT;
+ return kmx61_set_mode(data, mode, KMX61_ACC | KMX61_MAG, true);
+}
+
+/**
+ * kmx61_set_power_state() - set power state for kmx61 @device
+ * @data - kmx61 device private pointer
+ * @on - power state to be set for @device
+ * @device - bitmask indicating device for which @on state needs to be set
+ *
+ * Notice that when ACC power state needs to be set to ON and MAG is in
+ * OPERATION then we know that kmx61_runtime_resume was already called
+ * so we must set ACC OPERATION mode here. The same happens when MAG power
+ * state needs to be set to ON and ACC is in OPERATION.
+ */
+static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device)
+{
+#ifdef CONFIG_PM
+ int ret;
+
+ if (device & KMX61_ACC) {
+ if (on && !data->acc_ps && !data->mag_stby) {
+ ret = kmx61_set_mode(data, 0, KMX61_ACC, true);
+ if (ret < 0)
+ return ret;
+ }
+ data->acc_ps = on;
+ }
+ if (device & KMX61_MAG) {
+ if (on && !data->mag_ps && !data->acc_stby) {
+ ret = kmx61_set_mode(data, 0, KMX61_MAG, true);
+ if (ret < 0)
+ return ret;
+ }
+ data->mag_ps = on;
+ }
+
+ if (on) {
+ ret = pm_runtime_get_sync(&data->client->dev);
+ } else {
+ pm_runtime_mark_last_busy(&data->client->dev);
+ ret = pm_runtime_put_autosuspend(&data->client->dev);
+ }
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed: kmx61_set_power_state for %d, ret %d\n",
+ on, ret);
+ if (on)
+ pm_runtime_put_noidle(&data->client->dev);
+
+ return ret;
+ }
+#endif
+ return 0;
+}
+
+static int kmx61_read_measurement(struct kmx61_data *data, u8 base, u8 offset)
+{
+ int ret;
+ u8 reg = base + offset * 2;
+
+ ret = i2c_smbus_read_word_data(data->client, reg);
+ if (ret < 0)
+ dev_err(&data->client->dev, "failed to read reg at %x\n", reg);
+
+ return ret;
+}
+
+static int kmx61_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ u8 base_reg;
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ base_reg = KMX61_ACC_XOUT_L;
+ break;
+ case IIO_MAGN:
+ base_reg = KMX61_MAG_XOUT_L;
+ break;
+ default:
+ return -EINVAL;
+ }
+ mutex_lock(&data->lock);
+
+ ret = kmx61_set_power_state(data, true, chan->address);
+ if (ret) {
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+
+ ret = kmx61_read_measurement(data, base_reg, chan->scan_index);
+ if (ret < 0) {
+ kmx61_set_power_state(data, false, chan->address);
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ *val = sign_extend32(ret >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+ ret = kmx61_set_power_state(data, false, chan->address);
+
+ mutex_unlock(&data->lock);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ *val = 0;
+ *val2 = kmx61_uscale_table[data->range];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_MAGN:
+ /* 14 bits res, 1465 microGauss per magn count */
+ *val = 0;
+ *val2 = 1465;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = kmx61_get_odr(data, val, val2, chan->address);
+ mutex_unlock(&data->lock);
+ if (ret)
+ return -EINVAL;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static int kmx61_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ int ret;
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (chan->type != IIO_ACCEL && chan->type != IIO_MAGN)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ ret = kmx61_set_odr(data, val, val2, chan->address);
+ mutex_unlock(&data->lock);
+ return ret;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ if (val != 0)
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ ret = kmx61_set_scale(data, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int kmx61_read_event(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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+ *val2 = 0;
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ *val = data->wake_thresh;
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ *val = data->wake_duration;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int kmx61_write_event(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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+ if (data->ev_enable_state)
+ return -EBUSY;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ data->wake_thresh = val;
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ data->wake_duration = val;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int kmx61_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 kmx61_data *data = kmx61_get_data(indio_dev);
+
+ return data->ev_enable_state;
+}
+
+static int kmx61_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 kmx61_data *data = kmx61_get_data(indio_dev);
+ int ret = 0;
+
+ if (state && data->ev_enable_state)
+ return 0;
+
+ mutex_lock(&data->lock);
+
+ if (!state && data->motion_trig_on) {
+ data->ev_enable_state = false;
+ goto err_unlock;
+ }
+
+ ret = kmx61_set_power_state(data, state, KMX61_ACC);
+ if (ret < 0)
+ goto err_unlock;
+
+ ret = kmx61_setup_any_motion_interrupt(data, state);
+ if (ret < 0) {
+ kmx61_set_power_state(data, false, KMX61_ACC);
+ goto err_unlock;
+ }
+
+ data->ev_enable_state = state;
+
+err_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int kmx61_acc_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+ if (data->acc_dready_trig != trig && data->motion_trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int kmx61_mag_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+ if (data->mag_dready_trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct iio_info kmx61_acc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = kmx61_read_raw,
+ .write_raw = kmx61_write_raw,
+ .attrs = &kmx61_acc_attribute_group,
+ .read_event_value = kmx61_read_event,
+ .write_event_value = kmx61_write_event,
+ .read_event_config = kmx61_read_event_config,
+ .write_event_config = kmx61_write_event_config,
+ .validate_trigger = kmx61_acc_validate_trigger,
+};
+
+static const struct iio_info kmx61_mag_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = kmx61_read_raw,
+ .write_raw = kmx61_write_raw,
+ .attrs = &kmx61_mag_attribute_group,
+ .validate_trigger = kmx61_mag_validate_trigger,
+};
+
+
+static int kmx61_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ int ret = 0;
+ u8 device;
+
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+
+ mutex_lock(&data->lock);
+
+ if (!state && data->ev_enable_state && data->motion_trig_on) {
+ data->motion_trig_on = false;
+ goto err_unlock;
+ }
+
+ if (data->acc_dready_trig == trig || data->motion_trig == trig)
+ device = KMX61_ACC;
+ else
+ device = KMX61_MAG;
+
+ ret = kmx61_set_power_state(data, state, device);
+ if (ret < 0)
+ goto err_unlock;
+
+ if (data->acc_dready_trig == trig || data->mag_dready_trig == trig)
+ ret = kmx61_setup_new_data_interrupt(data, state, device);
+ else
+ ret = kmx61_setup_any_motion_interrupt(data, state);
+ if (ret < 0) {
+ kmx61_set_power_state(data, false, device);
+ goto err_unlock;
+ }
+
+ if (data->acc_dready_trig == trig)
+ data->acc_dready_trig_on = state;
+ else if (data->mag_dready_trig == trig)
+ data->mag_dready_trig_on = state;
+ else
+ data->motion_trig_on = state;
+err_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int kmx61_trig_try_reenable(struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_inl\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops kmx61_trigger_ops = {
+ .set_trigger_state = kmx61_data_rdy_trigger_set_state,
+ .try_reenable = kmx61_trig_try_reenable,
+ .owner = THIS_MODULE,
+};
+
+static irqreturn_t kmx61_event_handler(int irq, void *private)
+{
+ struct kmx61_data *data = private;
+ struct iio_dev *indio_dev = data->acc_indio_dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS1);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ins1\n");
+ goto ack_intr;
+ }
+
+ if (ret & KMX61_REG_INS1_BIT_WUFS) {
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INS2);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_ins2\n");
+ goto ack_intr;
+ }
+
+ if (ret & KMX61_REG_INS2_BIT_XN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ 0);
+
+ if (ret & KMX61_REG_INS2_BIT_XP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ 0);
+
+ if (ret & KMX61_REG_INS2_BIT_YN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ 0);
+
+ if (ret & KMX61_REG_INS2_BIT_YP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ 0);
+
+ if (ret & KMX61_REG_INS2_BIT_ZN)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_FALLING),
+ 0);
+
+ if (ret & KMX61_REG_INS2_BIT_ZP)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ 0);
+ }
+
+ack_intr:
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_CTRL1);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error reading reg_ctrl1\n");
+
+ ret |= KMX61_REG_CTRL1_BIT_RES;
+ ret = i2c_smbus_write_byte_data(data->client, KMX61_REG_CTRL1, ret);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error writing reg_ctrl1\n");
+
+ ret = i2c_smbus_read_byte_data(data->client, KMX61_REG_INL);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error reading reg_inl\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t kmx61_data_rdy_trig_poll(int irq, void *private)
+{
+ struct kmx61_data *data = private;
+
+ if (data->acc_dready_trig_on)
+ iio_trigger_poll(data->acc_dready_trig);
+ if (data->mag_dready_trig_on)
+ iio_trigger_poll(data->mag_dready_trig);
+
+ if (data->motion_trig_on)
+ iio_trigger_poll(data->motion_trig);
+
+ if (data->ev_enable_state)
+ return IRQ_WAKE_THREAD;
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t kmx61_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct kmx61_data *data = kmx61_get_data(indio_dev);
+ int bit, ret, i = 0;
+ u8 base;
+ s16 buffer[8];
+
+ if (indio_dev == data->acc_indio_dev)
+ base = KMX61_ACC_XOUT_L;
+ else
+ base = KMX61_MAG_XOUT_L;
+
+ mutex_lock(&data->lock);
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = kmx61_read_measurement(data, base, bit);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto err;
+ }
+ buffer[i++] = ret;
+ }
+ mutex_unlock(&data->lock);
+
+ iio_push_to_buffers(indio_dev, buffer);
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const char *kmx61_match_acpi_device(struct device *dev)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+ if (!id)
+ return NULL;
+ return dev_name(dev);
+}
+
+static int kmx61_gpio_probe(struct i2c_client *client, struct kmx61_data *data)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* data ready gpio interrupt pin */
+ gpio = devm_gpiod_get_index(dev, KMX61_GPIO_NAME, 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "acpi gpio get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+ return ret;
+}
+
+static struct iio_dev *kmx61_indiodev_setup(struct kmx61_data *data,
+ const struct iio_info *info,
+ const struct iio_chan_spec *chan,
+ int num_channels,
+ const char *name)
+{
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&data->client->dev, sizeof(data));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+
+ kmx61_set_data(indio_dev, data);
+
+ indio_dev->dev.parent = &data->client->dev;
+ indio_dev->channels = chan;
+ indio_dev->num_channels = num_channels;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = info;
+
+ return indio_dev;
+}
+
+static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data,
+ struct iio_dev *indio_dev,
+ const char *tag)
+{
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&data->client->dev,
+ "%s-%s-dev%d",
+ indio_dev->name,
+ tag,
+ indio_dev->id);
+ if (!trig)
+ return ERR_PTR(-ENOMEM);
+
+ trig->dev.parent = &data->client->dev;
+ trig->ops = &kmx61_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return trig;
+}
+
+static int kmx61_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct kmx61_data *data;
+ const char *name = NULL;
+
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ data->client = client;
+
+ mutex_init(&data->lock);
+
+ if (id)
+ name = id->name;
+ else if (ACPI_HANDLE(&client->dev))
+ name = kmx61_match_acpi_device(&client->dev);
+ else
+ return -ENODEV;
+
+ data->acc_indio_dev =
+ kmx61_indiodev_setup(data, &kmx61_acc_info,
+ kmx61_acc_channels,
+ ARRAY_SIZE(kmx61_acc_channels),
+ name);
+ if (IS_ERR(data->acc_indio_dev))
+ return PTR_ERR(data->acc_indio_dev);
+
+ data->mag_indio_dev =
+ kmx61_indiodev_setup(data, &kmx61_mag_info,
+ kmx61_mag_channels,
+ ARRAY_SIZE(kmx61_mag_channels),
+ name);
+ if (IS_ERR(data->mag_indio_dev))
+ return PTR_ERR(data->mag_indio_dev);
+
+ ret = kmx61_chip_init(data);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq < 0)
+ client->irq = kmx61_gpio_probe(client, data);
+
+ if (client->irq >= 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ kmx61_data_rdy_trig_poll,
+ kmx61_event_handler,
+ IRQF_TRIGGER_RISING,
+ KMX61_IRQ_NAME,
+ data);
+ if (ret)
+ goto err_chip_uninit;
+
+ data->acc_dready_trig =
+ kmx61_trigger_setup(data, data->acc_indio_dev,
+ "dready");
+ if (IS_ERR(data->acc_dready_trig)) {
+ ret = PTR_ERR(data->acc_dready_trig);
+ goto err_chip_uninit;
+ }
+
+ data->mag_dready_trig =
+ kmx61_trigger_setup(data, data->mag_indio_dev,
+ "dready");
+ if (IS_ERR(data->mag_dready_trig)) {
+ ret = PTR_ERR(data->mag_dready_trig);
+ goto err_trigger_unregister_acc_dready;
+ }
+
+ data->motion_trig =
+ kmx61_trigger_setup(data, data->acc_indio_dev,
+ "any-motion");
+ if (IS_ERR(data->motion_trig)) {
+ ret = PTR_ERR(data->motion_trig);
+ goto err_trigger_unregister_mag_dready;
+ }
+
+ ret = iio_triggered_buffer_setup(data->acc_indio_dev,
+ &iio_pollfunc_store_time,
+ kmx61_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed to setup acc triggered buffer\n");
+ goto err_trigger_unregister_motion;
+ }
+
+ ret = iio_triggered_buffer_setup(data->mag_indio_dev,
+ &iio_pollfunc_store_time,
+ kmx61_trigger_handler,
+ NULL);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Failed to setup mag triggered buffer\n");
+ goto err_buffer_cleanup_acc;
+ }
+ }
+
+ ret = iio_device_register(data->acc_indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register acc iio device\n");
+ goto err_buffer_cleanup_mag;
+ }
+
+ ret = iio_device_register(data->mag_indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register mag iio device\n");
+ goto err_iio_unregister_acc;
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto err_iio_unregister_mag;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, KMX61_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ return 0;
+
+err_iio_unregister_mag:
+ iio_device_unregister(data->mag_indio_dev);
+err_iio_unregister_acc:
+ iio_device_unregister(data->acc_indio_dev);
+err_buffer_cleanup_mag:
+ if (client->irq >= 0)
+ iio_triggered_buffer_cleanup(data->mag_indio_dev);
+err_buffer_cleanup_acc:
+ if (client->irq >= 0)
+ iio_triggered_buffer_cleanup(data->acc_indio_dev);
+err_trigger_unregister_motion:
+ iio_trigger_unregister(data->motion_trig);
+err_trigger_unregister_mag_dready:
+ iio_trigger_unregister(data->mag_dready_trig);
+err_trigger_unregister_acc_dready:
+ iio_trigger_unregister(data->acc_dready_trig);
+err_chip_uninit:
+ kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+ return ret;
+}
+
+static int kmx61_remove(struct i2c_client *client)
+{
+ struct kmx61_data *data = i2c_get_clientdata(client);
+
+ pm_runtime_disable(&client->dev);
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_put_noidle(&client->dev);
+
+ iio_device_unregister(data->acc_indio_dev);
+ iio_device_unregister(data->mag_indio_dev);
+
+ if (client->irq >= 0) {
+ iio_triggered_buffer_cleanup(data->acc_indio_dev);
+ iio_triggered_buffer_cleanup(data->mag_indio_dev);
+ iio_trigger_unregister(data->acc_dready_trig);
+ iio_trigger_unregister(data->mag_dready_trig);
+ iio_trigger_unregister(data->motion_trig);
+ }
+
+ mutex_lock(&data->lock);
+ kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+ mutex_unlock(&data->lock);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int kmx61_suspend(struct device *dev)
+{
+ int ret;
+ struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->lock);
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG,
+ false);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int kmx61_resume(struct device *dev)
+{
+ u8 stby = 0;
+ struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ if (data->acc_stby)
+ stby |= KMX61_ACC_STBY_BIT;
+ if (data->mag_stby)
+ stby |= KMX61_MAG_STBY_BIT;
+
+ return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
+}
+#endif
+
+#ifdef CONFIG_PM
+static int kmx61_runtime_suspend(struct device *dev)
+{
+ struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = kmx61_set_mode(data, KMX61_ALL_STBY, KMX61_ACC | KMX61_MAG, true);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int kmx61_runtime_resume(struct device *dev)
+{
+ struct kmx61_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ u8 stby = 0;
+
+ if (!data->acc_ps)
+ stby |= KMX61_ACC_STBY_BIT;
+ if (!data->mag_ps)
+ stby |= KMX61_MAG_STBY_BIT;
+
+ return kmx61_set_mode(data, stby, KMX61_ACC | KMX61_MAG, true);
+}
+#endif
+
+static const struct dev_pm_ops kmx61_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(kmx61_suspend, kmx61_resume)
+ SET_RUNTIME_PM_OPS(kmx61_runtime_suspend, kmx61_runtime_resume, NULL)
+};
+
+static const struct acpi_device_id kmx61_acpi_match[] = {
+ {"KMX61021", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(acpi, kmx61_acpi_match);
+
+static const struct i2c_device_id kmx61_id[] = {
+ {"kmx611021", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, kmx61_id);
+
+static struct i2c_driver kmx61_driver = {
+ .driver = {
+ .name = KMX61_DRV_NAME,
+ .acpi_match_table = ACPI_PTR(kmx61_acpi_match),
+ .pm = &kmx61_pm_ops,
+ },
+ .probe = kmx61_probe,
+ .remove = kmx61_remove,
+ .id_table = kmx61_id,
+};
+
+module_i2c_driver(kmx61_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("KMX61 accelerometer/magnetometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index f971f79..df919f4 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -37,11 +37,57 @@ static bool iio_buffer_is_active(struct iio_buffer *buf)
return !list_empty(&buf->buffer_list);
}
-static bool iio_buffer_data_available(struct iio_buffer *buf)
+static size_t iio_buffer_data_available(struct iio_buffer *buf)
{
return buf->access->data_available(buf);
}
+static int iio_buffer_flush_hwfifo(struct iio_dev *indio_dev,
+ struct iio_buffer *buf, size_t required)
+{
+ if (!indio_dev->info->hwfifo_flush_to_buffer)
+ return -ENODEV;
+
+ return indio_dev->info->hwfifo_flush_to_buffer(indio_dev, required);
+}
+
+static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf,
+ size_t to_wait, int to_flush)
+{
+ size_t avail;
+ int flushed = 0;
+
+ /* wakeup if the device was unregistered */
+ if (!indio_dev->info)
+ return true;
+
+ /* drain the buffer if it was disabled */
+ if (!iio_buffer_is_active(buf)) {
+ to_wait = min_t(size_t, to_wait, 1);
+ to_flush = 0;
+ }
+
+ avail = iio_buffer_data_available(buf);
+
+ if (avail >= to_wait) {
+ /* force a flush for non-blocking reads */
+ if (!to_wait && !avail && to_flush)
+ iio_buffer_flush_hwfifo(indio_dev, buf, to_flush);
+ return true;
+ }
+
+ if (to_flush)
+ flushed = iio_buffer_flush_hwfifo(indio_dev, buf,
+ to_wait - avail);
+ if (flushed <= 0)
+ return false;
+
+ if (avail + flushed >= to_wait)
+ return true;
+
+ return false;
+}
+
/**
* iio_buffer_read_first_n_outer() - chrdev read for buffer access
*
@@ -53,6 +99,9 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
{
struct iio_dev *indio_dev = filp->private_data;
struct iio_buffer *rb = indio_dev->buffer;
+ size_t datum_size;
+ size_t to_wait = 0;
+ size_t to_read;
int ret;
if (!indio_dev->info)
@@ -61,19 +110,28 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,
if (!rb || !rb->access->read_first_n)
return -EINVAL;
+ datum_size = rb->bytes_per_datum;
+
+ /*
+ * If datum_size is 0 there will never be anything to read from the
+ * buffer, so signal end of file now.
+ */
+ if (!datum_size)
+ return 0;
+
+ to_read = min_t(size_t, n / datum_size, rb->watermark);
+
+ if (!(filp->f_flags & O_NONBLOCK))
+ to_wait = to_read;
+
do {
- if (!iio_buffer_data_available(rb)) {
- if (filp->f_flags & O_NONBLOCK)
- return -EAGAIN;
+ ret = wait_event_interruptible(rb->pollq,
+ iio_buffer_ready(indio_dev, rb, to_wait, to_read));
+ if (ret)
+ return ret;
- ret = wait_event_interruptible(rb->pollq,
- iio_buffer_data_available(rb) ||
- indio_dev->info == NULL);
- if (ret)
- return ret;
- if (indio_dev->info == NULL)
- return -ENODEV;
- }
+ if (!indio_dev->info)
+ return -ENODEV;
ret = rb->access->read_first_n(rb, n, buf);
if (ret == 0 && (filp->f_flags & O_NONBLOCK))
@@ -96,9 +154,8 @@ unsigned int iio_buffer_poll(struct file *filp,
return -ENODEV;
poll_wait(filp, &rb->pollq, wait);
- if (iio_buffer_data_available(rb))
+ if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0))
return POLLIN | POLLRDNORM;
- /* need a way of knowing if there may be enough data... */
return 0;
}
@@ -123,6 +180,7 @@ void iio_buffer_init(struct iio_buffer *buffer)
INIT_LIST_HEAD(&buffer->buffer_list);
init_waitqueue_head(&buffer->pollq);
kref_init(&buffer->ref);
+ buffer->watermark = 1;
}
EXPORT_SYMBOL(iio_buffer_init);
@@ -178,6 +236,80 @@ static ssize_t iio_scan_el_show(struct device *dev,
return sprintf(buf, "%d\n", ret);
}
+/* Note NULL used as error indicator as it doesn't make sense. */
+static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
+ unsigned int masklength,
+ const unsigned long *mask)
+{
+ if (bitmap_empty(mask, masklength))
+ return NULL;
+ while (*av_masks) {
+ if (bitmap_subset(mask, av_masks, masklength))
+ return av_masks;
+ av_masks += BITS_TO_LONGS(masklength);
+ }
+ return NULL;
+}
+
+static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ if (!indio_dev->setup_ops->validate_scan_mask)
+ return true;
+
+ return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
+}
+
+/**
+ * iio_scan_mask_set() - set particular bit in the scan mask
+ * @indio_dev: the iio device
+ * @buffer: the buffer whose scan mask we are interested in
+ * @bit: the bit to be set.
+ *
+ * Note that at this point we have no way of knowing what other
+ * buffers might request, hence this code only verifies that the
+ * individual buffers request is plausible.
+ */
+static int iio_scan_mask_set(struct iio_dev *indio_dev,
+ struct iio_buffer *buffer, int bit)
+{
+ const unsigned long *mask;
+ unsigned long *trialmask;
+
+ trialmask = kmalloc(sizeof(*trialmask)*
+ BITS_TO_LONGS(indio_dev->masklength),
+ GFP_KERNEL);
+
+ if (trialmask == NULL)
+ return -ENOMEM;
+ if (!indio_dev->masklength) {
+ WARN_ON("Trying to set scanmask prior to registering buffer\n");
+ goto err_invalid_mask;
+ }
+ bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
+ set_bit(bit, trialmask);
+
+ if (!iio_validate_scan_mask(indio_dev, trialmask))
+ goto err_invalid_mask;
+
+ if (indio_dev->available_scan_masks) {
+ mask = iio_scan_mask_match(indio_dev->available_scan_masks,
+ indio_dev->masklength,
+ trialmask);
+ if (!mask)
+ goto err_invalid_mask;
+ }
+ bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
+
+ kfree(trialmask);
+
+ return 0;
+
+err_invalid_mask:
+ kfree(trialmask);
+ return -EINVAL;
+}
+
static int iio_scan_mask_clear(struct iio_buffer *buffer, int bit)
{
clear_bit(bit, buffer->scan_mask);
@@ -309,115 +441,19 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,
return ret;
}
-static const char * const iio_scan_elements_group_name = "scan_elements";
-
-int iio_buffer_register(struct iio_dev *indio_dev,
- const struct iio_chan_spec *channels,
- int num_channels)
-{
- struct iio_dev_attr *p;
- struct attribute **attr;
- struct iio_buffer *buffer = indio_dev->buffer;
- int ret, i, attrn, attrcount, attrcount_orig = 0;
-
- if (buffer->attrs)
- indio_dev->groups[indio_dev->groupcounter++] = buffer->attrs;
-
- if (buffer->scan_el_attrs != NULL) {
- attr = buffer->scan_el_attrs->attrs;
- while (*attr++ != NULL)
- attrcount_orig++;
- }
- attrcount = attrcount_orig;
- INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
- if (channels) {
- /* new magic */
- for (i = 0; i < num_channels; i++) {
- if (channels[i].scan_index < 0)
- continue;
-
- /* Establish necessary mask length */
- if (channels[i].scan_index >
- (int)indio_dev->masklength - 1)
- indio_dev->masklength
- = channels[i].scan_index + 1;
-
- ret = iio_buffer_add_channel_sysfs(indio_dev,
- &channels[i]);
- if (ret < 0)
- goto error_cleanup_dynamic;
- attrcount += ret;
- if (channels[i].type == IIO_TIMESTAMP)
- indio_dev->scan_index_timestamp =
- channels[i].scan_index;
- }
- if (indio_dev->masklength && buffer->scan_mask == NULL) {
- buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
- sizeof(*buffer->scan_mask),
- GFP_KERNEL);
- if (buffer->scan_mask == NULL) {
- ret = -ENOMEM;
- goto error_cleanup_dynamic;
- }
- }
- }
-
- buffer->scan_el_group.name = iio_scan_elements_group_name;
-
- buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
- sizeof(buffer->scan_el_group.attrs[0]),
- GFP_KERNEL);
- if (buffer->scan_el_group.attrs == NULL) {
- ret = -ENOMEM;
- goto error_free_scan_mask;
- }
- if (buffer->scan_el_attrs)
- memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
- sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
- attrn = attrcount_orig;
-
- list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
- buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
- indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
-
- return 0;
-
-error_free_scan_mask:
- kfree(buffer->scan_mask);
-error_cleanup_dynamic:
- iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
-
- return ret;
-}
-EXPORT_SYMBOL(iio_buffer_register);
-
-void iio_buffer_unregister(struct iio_dev *indio_dev)
-{
- kfree(indio_dev->buffer->scan_mask);
- kfree(indio_dev->buffer->scan_el_group.attrs);
- iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
-}
-EXPORT_SYMBOL(iio_buffer_unregister);
-
-ssize_t iio_buffer_read_length(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t iio_buffer_read_length(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
- if (buffer->access->get_length)
- return sprintf(buf, "%d\n",
- buffer->access->get_length(buffer));
-
- return 0;
+ return sprintf(buf, "%d\n", buffer->length);
}
-EXPORT_SYMBOL(iio_buffer_read_length);
-ssize_t iio_buffer_write_length(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+static ssize_t iio_buffer_write_length(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_buffer *buffer = indio_dev->buffer;
@@ -428,47 +464,33 @@ ssize_t iio_buffer_write_length(struct device *dev,
if (ret)
return ret;
- if (buffer->access->get_length)
- if (val == buffer->access->get_length(buffer))
- return len;
+ if (val == buffer->length)
+ return len;
mutex_lock(&indio_dev->mlock);
if (iio_buffer_is_active(indio_dev->buffer)) {
ret = -EBUSY;
} else {
- if (buffer->access->set_length)
- buffer->access->set_length(buffer, val);
+ buffer->access->set_length(buffer, val);
ret = 0;
}
+ if (ret)
+ goto out;
+ if (buffer->length && buffer->length < buffer->watermark)
+ buffer->watermark = buffer->length;
+out:
mutex_unlock(&indio_dev->mlock);
return ret ? ret : len;
}
-EXPORT_SYMBOL(iio_buffer_write_length);
-ssize_t iio_buffer_show_enable(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t iio_buffer_show_enable(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));
}
-EXPORT_SYMBOL(iio_buffer_show_enable);
-
-/* Note NULL used as error indicator as it doesn't make sense. */
-static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,
- unsigned int masklength,
- const unsigned long *mask)
-{
- if (bitmap_empty(mask, masklength))
- return NULL;
- while (*av_masks) {
- if (bitmap_subset(mask, av_masks, masklength))
- return av_masks;
- av_masks += BITS_TO_LONGS(masklength);
- }
- return NULL;
-}
static int iio_compute_scan_bytes(struct iio_dev *indio_dev,
const unsigned long *mask, bool timestamp)
@@ -513,6 +535,7 @@ static void iio_buffer_activate(struct iio_dev *indio_dev,
static void iio_buffer_deactivate(struct iio_buffer *buffer)
{
list_del_init(&buffer->buffer_list);
+ wake_up_interruptible(&buffer->pollq);
iio_buffer_put(buffer);
}
@@ -670,17 +693,16 @@ static int __iio_update_buffers(struct iio_dev *indio_dev,
}
}
/* Definitely possible for devices to support both of these. */
- if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
- if (!indio_dev->trig) {
- printk(KERN_INFO "Buffer not started: no trigger\n");
- ret = -EINVAL;
- /* Can only occur on first buffer */
- goto error_run_postdisable;
- }
+ if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) {
indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;
} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {
indio_dev->currentmode = INDIO_BUFFER_HARDWARE;
+ } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) {
+ indio_dev->currentmode = INDIO_BUFFER_SOFTWARE;
} else { /* Should never be reached */
+ /* Can only occur on first buffer */
+ if (indio_dev->modes & INDIO_BUFFER_TRIGGERED)
+ pr_info("Buffer not started: no trigger\n");
ret = -EINVAL;
goto error_run_postdisable;
}
@@ -755,10 +777,10 @@ out_unlock:
}
EXPORT_SYMBOL_GPL(iio_update_buffers);
-ssize_t iio_buffer_store_enable(struct device *dev,
- struct device_attribute *attr,
- const char *buf,
- size_t len)
+static ssize_t iio_buffer_store_enable(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
{
int ret;
bool requested_state;
@@ -790,83 +812,204 @@ done:
mutex_unlock(&indio_dev->mlock);
return (ret < 0) ? ret : len;
}
-EXPORT_SYMBOL(iio_buffer_store_enable);
-/**
- * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
- * @indio_dev: the iio device
- * @mask: scan mask to be checked
- *
- * Return true if exactly one bit is set in the scan mask, false otherwise. It
- * can be used for devices where only one channel can be active for sampling at
- * a time.
- */
-bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
- const unsigned long *mask)
+static const char * const iio_scan_elements_group_name = "scan_elements";
+
+static ssize_t iio_buffer_show_watermark(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- return bitmap_weight(mask, indio_dev->masklength) == 1;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+
+ return sprintf(buf, "%u\n", buffer->watermark);
}
-EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
-static bool iio_validate_scan_mask(struct iio_dev *indio_dev,
- const unsigned long *mask)
+static ssize_t iio_buffer_store_watermark(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
{
- if (!indio_dev->setup_ops->validate_scan_mask)
- return true;
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_buffer *buffer = indio_dev->buffer;
+ unsigned int val;
+ int ret;
- return indio_dev->setup_ops->validate_scan_mask(indio_dev, mask);
+ ret = kstrtouint(buf, 10, &val);
+ if (ret)
+ return ret;
+ if (!val)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+
+ if (val > buffer->length) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (iio_buffer_is_active(indio_dev->buffer)) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ buffer->watermark = val;
+
+ if (indio_dev->info->hwfifo_set_watermark)
+ indio_dev->info->hwfifo_set_watermark(indio_dev, val);
+out:
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret ? ret : len;
}
-/**
- * iio_scan_mask_set() - set particular bit in the scan mask
- * @indio_dev: the iio device
- * @buffer: the buffer whose scan mask we are interested in
- * @bit: the bit to be set.
- *
- * Note that at this point we have no way of knowing what other
- * buffers might request, hence this code only verifies that the
- * individual buffers request is plausible.
- */
-int iio_scan_mask_set(struct iio_dev *indio_dev,
- struct iio_buffer *buffer, int bit)
+static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length,
+ iio_buffer_write_length);
+static struct device_attribute dev_attr_length_ro = __ATTR(length,
+ S_IRUGO, iio_buffer_read_length, NULL);
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR,
+ iio_buffer_show_enable, iio_buffer_store_enable);
+static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR,
+ iio_buffer_show_watermark, iio_buffer_store_watermark);
+
+static struct attribute *iio_buffer_attrs[] = {
+ &dev_attr_length.attr,
+ &dev_attr_enable.attr,
+ &dev_attr_watermark.attr,
+};
+
+int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev)
{
- const unsigned long *mask;
- unsigned long *trialmask;
+ struct iio_dev_attr *p;
+ struct attribute **attr;
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int ret, i, attrn, attrcount, attrcount_orig = 0;
+ const struct iio_chan_spec *channels;
- trialmask = kmalloc(sizeof(*trialmask)*
- BITS_TO_LONGS(indio_dev->masklength),
- GFP_KERNEL);
+ if (!buffer)
+ return 0;
- if (trialmask == NULL)
+ attrcount = 0;
+ if (buffer->attrs) {
+ while (buffer->attrs[attrcount] != NULL)
+ attrcount++;
+ }
+
+ attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1,
+ sizeof(struct attribute *), GFP_KERNEL);
+ if (!attr)
return -ENOMEM;
- if (!indio_dev->masklength) {
- WARN_ON("Trying to set scanmask prior to registering buffer\n");
- goto err_invalid_mask;
+
+ memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs));
+ if (!buffer->access->set_length)
+ attr[0] = &dev_attr_length_ro.attr;
+
+ if (buffer->attrs)
+ memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs,
+ sizeof(struct attribute *) * attrcount);
+
+ attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL;
+
+ buffer->buffer_group.name = "buffer";
+ buffer->buffer_group.attrs = attr;
+
+ indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group;
+
+ if (buffer->scan_el_attrs != NULL) {
+ attr = buffer->scan_el_attrs->attrs;
+ while (*attr++ != NULL)
+ attrcount_orig++;
}
- bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength);
- set_bit(bit, trialmask);
+ attrcount = attrcount_orig;
+ INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list);
+ channels = indio_dev->channels;
+ if (channels) {
+ /* new magic */
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ if (channels[i].scan_index < 0)
+ continue;
- if (!iio_validate_scan_mask(indio_dev, trialmask))
- goto err_invalid_mask;
+ /* Establish necessary mask length */
+ if (channels[i].scan_index >
+ (int)indio_dev->masklength - 1)
+ indio_dev->masklength
+ = channels[i].scan_index + 1;
- if (indio_dev->available_scan_masks) {
- mask = iio_scan_mask_match(indio_dev->available_scan_masks,
- indio_dev->masklength,
- trialmask);
- if (!mask)
- goto err_invalid_mask;
+ ret = iio_buffer_add_channel_sysfs(indio_dev,
+ &channels[i]);
+ if (ret < 0)
+ goto error_cleanup_dynamic;
+ attrcount += ret;
+ if (channels[i].type == IIO_TIMESTAMP)
+ indio_dev->scan_index_timestamp =
+ channels[i].scan_index;
+ }
+ if (indio_dev->masklength && buffer->scan_mask == NULL) {
+ buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+ sizeof(*buffer->scan_mask),
+ GFP_KERNEL);
+ if (buffer->scan_mask == NULL) {
+ ret = -ENOMEM;
+ goto error_cleanup_dynamic;
+ }
+ }
}
- bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
- kfree(trialmask);
+ buffer->scan_el_group.name = iio_scan_elements_group_name;
+
+ buffer->scan_el_group.attrs = kcalloc(attrcount + 1,
+ sizeof(buffer->scan_el_group.attrs[0]),
+ GFP_KERNEL);
+ if (buffer->scan_el_group.attrs == NULL) {
+ ret = -ENOMEM;
+ goto error_free_scan_mask;
+ }
+ if (buffer->scan_el_attrs)
+ memcpy(buffer->scan_el_group.attrs, buffer->scan_el_attrs,
+ sizeof(buffer->scan_el_group.attrs[0])*attrcount_orig);
+ attrn = attrcount_orig;
+
+ list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l)
+ buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr;
+ indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group;
return 0;
-err_invalid_mask:
- kfree(trialmask);
- return -EINVAL;
+error_free_scan_mask:
+ kfree(buffer->scan_mask);
+error_cleanup_dynamic:
+ iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
+ kfree(indio_dev->buffer->buffer_group.attrs);
+
+ return ret;
+}
+
+void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev)
+{
+ if (!indio_dev->buffer)
+ return;
+
+ kfree(indio_dev->buffer->scan_mask);
+ kfree(indio_dev->buffer->buffer_group.attrs);
+ kfree(indio_dev->buffer->scan_el_group.attrs);
+ iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
+}
+
+/**
+ * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected
+ * @indio_dev: the iio device
+ * @mask: scan mask to be checked
+ *
+ * Return true if exactly one bit is set in the scan mask, false otherwise. It
+ * can be used for devices where only one channel can be active for sampling at
+ * a time.
+ */
+bool iio_validate_scan_mask_onehot(struct iio_dev *indio_dev,
+ const unsigned long *mask)
+{
+ return bitmap_weight(mask, indio_dev->masklength) == 1;
}
-EXPORT_SYMBOL_GPL(iio_scan_mask_set);
+EXPORT_SYMBOL_GPL(iio_validate_scan_mask_onehot);
int iio_scan_mask_query(struct iio_dev *indio_dev,
struct iio_buffer *buffer, int bit)
@@ -913,8 +1056,18 @@ static const void *iio_demux(struct iio_buffer *buffer,
static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)
{
const void *dataout = iio_demux(buffer, data);
+ int ret;
+
+ ret = buffer->access->store_to(buffer, dataout);
+ if (ret)
+ return ret;
- return buffer->access->store_to(buffer, dataout);
+ /*
+ * We can't just test for watermark to decide if we wake the poll queue
+ * because read may request less samples than the watermark.
+ */
+ wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM);
+ return 0;
}
static void iio_buffer_demux_free(struct iio_buffer *buffer)
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index af3e76d..4df97f6 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -70,6 +70,11 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_CCT] = "cct",
[IIO_PRESSURE] = "pressure",
[IIO_HUMIDITYRELATIVE] = "humidityrelative",
+ [IIO_ACTIVITY] = "activity",
+ [IIO_STEPS] = "steps",
+ [IIO_ENERGY] = "energy",
+ [IIO_DISTANCE] = "distance",
+ [IIO_VELOCITY] = "velocity",
};
static const char * const iio_modifier_names[] = {
@@ -91,6 +96,11 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_NORTH_TRUE] = "from_north_true",
[IIO_MOD_NORTH_MAGN_TILT_COMP] = "from_north_magnetic_tilt_comp",
[IIO_MOD_NORTH_TRUE_TILT_COMP] = "from_north_true_tilt_comp",
+ [IIO_MOD_RUNNING] = "running",
+ [IIO_MOD_JOGGING] = "jogging",
+ [IIO_MOD_WALKING] = "walking",
+ [IIO_MOD_STILL] = "still",
+ [IIO_MOD_ROOT_SUM_SQUARED_X_Y_Z] = "sqrt(x^2+y^2+z^2)",
};
/* relies on pairs of these shared then separate */
@@ -113,6 +123,11 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
[IIO_CHAN_INFO_INT_TIME] = "integration_time",
+ [IIO_CHAN_INFO_ENABLE] = "en",
+ [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight",
+ [IIO_CHAN_INFO_CALIBWEIGHT] = "calibweight",
+ [IIO_CHAN_INFO_DEBOUNCE_COUNT] = "debounce_count",
+ [IIO_CHAN_INFO_DEBOUNCE_TIME] = "debounce_time",
};
/**
@@ -832,8 +847,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
* @attr_list: List of IIO device attributes
*
* This function frees the memory allocated for each of the IIO device
- * attributes in the list. Note: if you want to reuse the list after calling
- * this function you have to reinitialize it using INIT_LIST_HEAD().
+ * attributes in the list.
*/
void iio_free_chan_devattr_list(struct list_head *attr_list)
{
@@ -841,6 +855,7 @@ void iio_free_chan_devattr_list(struct list_head *attr_list)
list_for_each_entry_safe(p, n, attr_list, l) {
kfree(p->dev_attr.attr.name);
+ list_del(&p->l);
kfree(p);
}
}
@@ -921,6 +936,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)
iio_free_chan_devattr_list(&indio_dev->channel_attr_list);
kfree(indio_dev->chan_attr_group.attrs);
+ indio_dev->chan_attr_group.attrs = NULL;
}
static void iio_dev_release(struct device *device)
@@ -1035,7 +1051,6 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)
if (!ptr)
return NULL;
- /* use raw alloc_dr for kmalloc caller tracing */
iio_dev = iio_device_alloc(sizeof_priv);
if (iio_dev) {
*ptr = iio_dev;
@@ -1127,6 +1142,29 @@ static const struct file_operations iio_buffer_fileops = {
.compat_ioctl = iio_ioctl,
};
+static int iio_check_unique_scan_index(struct iio_dev *indio_dev)
+{
+ int i, j;
+ const struct iio_chan_spec *channels = indio_dev->channels;
+
+ if (!(indio_dev->modes & INDIO_ALL_BUFFER_MODES))
+ return 0;
+
+ for (i = 0; i < indio_dev->num_channels - 1; i++) {
+ if (channels[i].scan_index < 0)
+ continue;
+ for (j = i + 1; j < indio_dev->num_channels; j++)
+ if (channels[i].scan_index == channels[j].scan_index) {
+ dev_err(&indio_dev->dev,
+ "Duplicate scan index %d\n",
+ channels[i].scan_index);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
static const struct iio_buffer_setup_ops noop_ring_setup_ops;
/**
@@ -1141,6 +1179,10 @@ int iio_device_register(struct iio_dev *indio_dev)
if (!indio_dev->dev.of_node && indio_dev->dev.parent)
indio_dev->dev.of_node = indio_dev->dev.parent->of_node;
+ ret = iio_check_unique_scan_index(indio_dev);
+ if (ret < 0)
+ return ret;
+
/* configure elements for the chrdev */
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
@@ -1150,11 +1192,19 @@ int iio_device_register(struct iio_dev *indio_dev)
"Failed to register debugfs interfaces\n");
return ret;
}
+
+ ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to create buffer sysfs interfaces\n");
+ goto error_unreg_debugfs;
+ }
+
ret = iio_device_register_sysfs(indio_dev);
if (ret) {
dev_err(indio_dev->dev.parent,
"Failed to register sysfs interfaces\n");
- goto error_unreg_debugfs;
+ goto error_buffer_free_sysfs;
}
ret = iio_device_register_eventset(indio_dev);
if (ret) {
@@ -1187,6 +1237,8 @@ error_unreg_eventset:
iio_device_unregister_eventset(indio_dev);
error_free_sysfs:
iio_device_unregister_sysfs(indio_dev);
+error_buffer_free_sysfs:
+ iio_buffer_free_sysfs_and_mask(indio_dev);
error_unreg_debugfs:
iio_device_unregister_debugfs(indio_dev);
return ret;
@@ -1215,6 +1267,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
iio_buffer_wakeup_poll(indio_dev);
mutex_unlock(&indio_dev->info_exist_lock);
+
+ iio_buffer_free_sysfs_and_mask(indio_dev);
}
EXPORT_SYMBOL(iio_device_unregister);
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 0c1e37e..a99692b 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -197,6 +197,7 @@ static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_ROC] = "roc",
[IIO_EV_TYPE_THRESH_ADAPTIVE] = "thresh_adaptive",
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
+ [IIO_EV_TYPE_CHANGE] = "change",
};
static const char * const iio_ev_dir_text[] = {
@@ -327,9 +328,15 @@ static int iio_device_add_event(struct iio_dev *indio_dev,
for_each_set_bit(i, mask, sizeof(*mask)*8) {
if (i >= ARRAY_SIZE(iio_ev_info_text))
return -EINVAL;
- postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
- iio_ev_type_text[type], iio_ev_dir_text[dir],
- iio_ev_info_text[i]);
+ if (dir != IIO_EV_DIR_NONE)
+ postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",
+ iio_ev_type_text[type],
+ iio_ev_dir_text[dir],
+ iio_ev_info_text[i]);
+ else
+ postfix = kasprintf(GFP_KERNEL, "%s_%s",
+ iio_ev_type_text[type],
+ iio_ev_info_text[i]);
if (postfix == NULL)
return -ENOMEM;
@@ -404,7 +411,7 @@ static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev)
{
int j, ret, attrcount = 0;
- /* Dynically created from the channels array */
+ /* Dynamically created from the channels array */
for (j = 0; j < indio_dev->num_channels; j++) {
ret = iio_device_add_event_sysfs(indio_dev,
&indio_dev->channels[j]);
@@ -493,6 +500,7 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)
error_free_setup_event_lines:
iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);
kfree(indio_dev->event_interface);
+ indio_dev->event_interface = NULL;
return ret;
}
diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c
index d6f54930..15a5341 100644
--- a/drivers/iio/industrialio-triggered-buffer.c
+++ b/drivers/iio/industrialio-triggered-buffer.c
@@ -32,7 +32,7 @@ static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
*
* This function combines some common tasks which will normally be performed
* when setting up a triggered buffer. It will allocate the buffer and the
- * pollfunc, as well as register the buffer with the IIO core.
+ * pollfunc.
*
* Before calling this function the indio_dev structure should already be
* completely initialized, but not yet registered. In practice this means that
@@ -49,7 +49,7 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
struct iio_buffer *buffer;
int ret;
- buffer = iio_kfifo_allocate(indio_dev);
+ buffer = iio_kfifo_allocate();
if (!buffer) {
ret = -ENOMEM;
goto error_ret;
@@ -78,16 +78,8 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,
/* Flag that polled ring buffering is possible */
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
- ret = iio_buffer_register(indio_dev,
- indio_dev->channels,
- indio_dev->num_channels);
- if (ret)
- goto error_dealloc_pollfunc;
-
return 0;
-error_dealloc_pollfunc:
- iio_dealloc_pollfunc(indio_dev->pollfunc);
error_kfifo_free:
iio_kfifo_free(indio_dev->buffer);
error_ret:
@@ -101,7 +93,6 @@ EXPORT_SYMBOL(iio_triggered_buffer_setup);
*/
void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
{
- iio_buffer_unregister(indio_dev);
iio_dealloc_pollfunc(indio_dev->pollfunc);
iio_kfifo_free(indio_dev->buffer);
}
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index f084610..c8bad3c 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -100,6 +100,31 @@ static int iio_dev_node_match(struct device *dev, void *data)
return dev->of_node == data && dev->type == &iio_device_type;
}
+/**
+ * __of_iio_simple_xlate - translate iiospec to the IIO channel index
+ * @indio_dev: pointer to the iio_dev structure
+ * @iiospec: IIO specifier as found in the device tree
+ *
+ * This is simple translation function, suitable for the most 1:1 mapped
+ * channels in IIO chips. This function performs only one sanity check:
+ * whether IIO index is less than num_channels (that is specified in the
+ * iio_dev).
+ */
+static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ if (!iiospec->args_count)
+ return 0;
+
+ if (iiospec->args[0] >= indio_dev->num_channels) {
+ dev_err(&indio_dev->dev, "invalid channel index %u\n",
+ iiospec->args[0]);
+ return -EINVAL;
+ }
+
+ return iiospec->args[0];
+}
+
static int __of_iio_channel_get(struct iio_channel *channel,
struct device_node *np, int index)
{
@@ -122,18 +147,19 @@ static int __of_iio_channel_get(struct iio_channel *channel,
indio_dev = dev_to_iio_dev(idev);
channel->indio_dev = indio_dev;
- index = iiospec.args_count ? iiospec.args[0] : 0;
- if (index >= indio_dev->num_channels) {
- err = -EINVAL;
+ if (indio_dev->info->of_xlate)
+ index = indio_dev->info->of_xlate(indio_dev, &iiospec);
+ else
+ index = __of_iio_simple_xlate(indio_dev, &iiospec);
+ if (index < 0)
goto err_put;
- }
channel->channel = &indio_dev->channels[index];
return 0;
err_put:
iio_device_put(indio_dev);
- return err;
+ return index;
}
static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
@@ -426,6 +452,9 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
if (val2 == NULL)
val2 = &unused;
+ if(!iio_channel_has_info(chan->channel, info))
+ return -EINVAL;
+
if (chan->indio_dev->info->read_raw_multi) {
ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev,
chan->channel, INDIO_MAX_RAW_ELEMENTS,
@@ -608,3 +637,28 @@ err_unlock:
return ret;
}
EXPORT_SYMBOL_GPL(iio_get_channel_type);
+
+static int iio_channel_write(struct iio_channel *chan, int val, int val2,
+ enum iio_chan_info_enum info)
+{
+ return chan->indio_dev->info->write_raw(chan->indio_dev,
+ chan->channel, val, val2, info);
+}
+
+int iio_write_channel_raw(struct iio_channel *chan, int val)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_RAW);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_raw);
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 7134e8a..847ca56 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -47,30 +47,6 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
return ret;
}
-static int iio_get_length_kfifo(struct iio_buffer *r)
-{
- return r->length;
-}
-
-static IIO_BUFFER_ENABLE_ATTR;
-static IIO_BUFFER_LENGTH_ATTR;
-
-static struct attribute *iio_kfifo_attributes[] = {
- &dev_attr_length.attr,
- &dev_attr_enable.attr,
- NULL,
-};
-
-static struct attribute_group iio_kfifo_attribute_group = {
- .attrs = iio_kfifo_attributes,
- .name = "buffer",
-};
-
-static int iio_get_bytes_per_datum_kfifo(struct iio_buffer *r)
-{
- return r->bytes_per_datum;
-}
-
static int iio_mark_update_needed_kfifo(struct iio_buffer *r)
{
struct iio_kfifo *kf = iio_to_kfifo(r);
@@ -107,9 +83,6 @@ static int iio_store_to_kfifo(struct iio_buffer *r,
ret = kfifo_in(&kf->kf, data, 1);
if (ret != 1)
return -EBUSY;
-
- wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM);
-
return 0;
}
@@ -133,16 +106,16 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
return copied;
}
-static bool iio_kfifo_buf_data_available(struct iio_buffer *r)
+static size_t iio_kfifo_buf_data_available(struct iio_buffer *r)
{
struct iio_kfifo *kf = iio_to_kfifo(r);
- bool empty;
+ size_t samples;
mutex_lock(&kf->user_lock);
- empty = kfifo_is_empty(&kf->kf);
+ samples = kfifo_len(&kf->kf);
mutex_unlock(&kf->user_lock);
- return !empty;
+ return samples;
}
static void iio_kfifo_buffer_release(struct iio_buffer *buffer)
@@ -159,26 +132,25 @@ static const struct iio_buffer_access_funcs kfifo_access_funcs = {
.read_first_n = &iio_read_first_n_kfifo,
.data_available = iio_kfifo_buf_data_available,
.request_update = &iio_request_update_kfifo,
- .get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo,
.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,
- .get_length = &iio_get_length_kfifo,
.set_length = &iio_set_length_kfifo,
.release = &iio_kfifo_buffer_release,
};
-struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
+struct iio_buffer *iio_kfifo_allocate(void)
{
struct iio_kfifo *kf;
- kf = kzalloc(sizeof *kf, GFP_KERNEL);
+ kf = kzalloc(sizeof(*kf), GFP_KERNEL);
if (!kf)
return NULL;
+
kf->update_needed = true;
iio_buffer_init(&kf->buffer);
- kf->buffer.attrs = &iio_kfifo_attribute_group;
kf->buffer.access = &kfifo_access_funcs;
kf->buffer.length = 2;
mutex_init(&kf->user_lock);
+
return &kf->buffer;
}
EXPORT_SYMBOL(iio_kfifo_allocate);
@@ -189,4 +161,58 @@ void iio_kfifo_free(struct iio_buffer *r)
}
EXPORT_SYMBOL(iio_kfifo_free);
+static void devm_iio_kfifo_release(struct device *dev, void *res)
+{
+ iio_kfifo_free(*(struct iio_buffer **)res);
+}
+
+static int devm_iio_kfifo_match(struct device *dev, void *res, void *data)
+{
+ struct iio_buffer **r = res;
+
+ if (WARN_ON(!r || !*r))
+ return 0;
+
+ return *r == data;
+}
+
+/**
+ * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate()
+ * @dev: Device to allocate kfifo buffer for
+ *
+ * RETURNS:
+ * Pointer to allocated iio_buffer on success, NULL on failure.
+ */
+struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev)
+{
+ struct iio_buffer **ptr, *r;
+
+ ptr = devres_alloc(devm_iio_kfifo_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return NULL;
+
+ r = iio_kfifo_allocate();
+ if (r) {
+ *ptr = r;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return r;
+}
+EXPORT_SYMBOL(devm_iio_kfifo_allocate);
+
+/**
+ * devm_iio_fifo_free - Resource-managed iio_kfifo_free()
+ * @dev: Device the buffer belongs to
+ * @r: The buffer associated with the device
+ */
+void devm_iio_kfifo_free(struct device *dev, struct iio_buffer *r)
+{
+ WARN_ON(devres_release(dev, devm_iio_kfifo_release,
+ devm_iio_kfifo_match, r));
+}
+EXPORT_SYMBOL(devm_iio_kfifo_free);
+
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 5bea821..01a1a16 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -48,6 +48,27 @@ config CM32181
To compile this driver as a module, choose M here:
the module will be called cm32181.
+config CM3232
+ depends on I2C
+ tristate "CM3232 ambient light sensor"
+ help
+ Say Y here if you use cm3232.
+ This option enables ambient light sensor using
+ Capella Microsystems cm3232 device driver.
+
+ To compile this driver as a module, choose M here:
+ the module will be called cm3232.
+
+config CM3323
+ depends on I2C
+ tristate "Capella CM3323 color light sensor"
+ help
+ Say Y here if you want to build a driver for Capela CM3323
+ color sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called cm3323.
+
config CM36651
depends on I2C
tristate "CM36651 driver"
@@ -62,6 +83,7 @@ config CM36651
config GP2AP020A00F
tristate "Sharp GP2AP020A00F Proximity/ALS sensor"
depends on I2C
+ select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
select IRQ_WORK
@@ -95,6 +117,9 @@ config HID_SENSOR_ALS
Say yes here to build support for the HID SENSOR
Ambient light sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called hid-sensor-als.
+
config HID_SENSOR_PROX
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -109,6 +134,17 @@ config HID_SENSOR_PROX
To compile this driver as a module, choose M here: the
module will be called hid-sensor-prox.
+config JSA1212
+ tristate "JSA1212 ALS and proximity sensor driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here if you want to build a IIO driver for JSA1212
+ proximity & ALS sensor device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called jsa1212.
+
config SENSORS_LM3533
tristate "LM3533 ambient light sensor"
depends on MFD_LM3533
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 47877a3..ad7c30f 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -7,11 +7,14 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_AL3320A) += al3320a.o
obj-$(CONFIG_APDS9300) += apds9300.o
obj-$(CONFIG_CM32181) += cm32181.o
+obj-$(CONFIG_CM3232) += cm3232.o
+obj-$(CONFIG_CM3323) += cm3323.o
obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
obj-$(CONFIG_ISL29125) += isl29125.o
+obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index ad36b29..5d12ae54 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -169,7 +169,7 @@ static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val)
* @cm32181: pointer of struct cm32181.
*
* Convert sensor raw data to lux. It depends on integration
- * time and claibscale variable.
+ * time and calibscale variable.
*
* Return: Positive value is lux, otherwise is error code.
*/
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
new file mode 100644
index 0000000..39c8d99
--- /dev/null
+++ b/drivers/iio/light/cm3232.c
@@ -0,0 +1,439 @@
+/*
+ * CM3232 Ambient Light Sensor
+ *
+ * Copyright (C) 2014-2015 Capella Microsystems Inc.
+ * Author: Kevin Tsai <ktsai@capellamicro.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.
+ *
+ * IIO driver for CM3232 (7-bit I2C slave address 0x10).
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+
+/* Registers Address */
+#define CM3232_REG_ADDR_CMD 0x00
+#define CM3232_REG_ADDR_ALS 0x50
+#define CM3232_REG_ADDR_ID 0x53
+
+#define CM3232_CMD_ALS_DISABLE BIT(0)
+
+#define CM3232_CMD_ALS_IT_SHIFT 2
+#define CM3232_CMD_ALS_IT_MASK (BIT(2) | BIT(3) | BIT(4))
+#define CM3232_CMD_ALS_IT_DEFAULT (0x01 << CM3232_CMD_ALS_IT_SHIFT)
+
+#define CM3232_CMD_ALS_RESET BIT(6)
+
+#define CM3232_CMD_DEFAULT CM3232_CMD_ALS_IT_DEFAULT
+
+#define CM3232_HW_ID 0x32
+#define CM3232_CALIBSCALE_DEFAULT 100000
+#define CM3232_CALIBSCALE_RESOLUTION 100000
+#define CM3232_MLUX_PER_LUX 1000
+
+#define CM3232_MLUX_PER_BIT_DEFAULT 64
+#define CM3232_MLUX_PER_BIT_BASE_IT 100000
+
+static const struct {
+ int val;
+ int val2;
+ u8 it;
+} cm3232_als_it_scales[] = {
+ {0, 100000, 0}, /* 0.100000 */
+ {0, 200000, 1}, /* 0.200000 */
+ {0, 400000, 2}, /* 0.400000 */
+ {0, 800000, 3}, /* 0.800000 */
+ {1, 600000, 4}, /* 1.600000 */
+ {3, 200000, 5}, /* 3.200000 */
+};
+
+struct cm3232_als_info {
+ u8 regs_cmd_default;
+ u8 hw_id;
+ int calibscale;
+ int mlux_per_bit;
+ int mlux_per_bit_base_it;
+};
+
+static struct cm3232_als_info cm3232_als_info_default = {
+ .regs_cmd_default = CM3232_CMD_DEFAULT,
+ .hw_id = CM3232_HW_ID,
+ .calibscale = CM3232_CALIBSCALE_DEFAULT,
+ .mlux_per_bit = CM3232_MLUX_PER_BIT_DEFAULT,
+ .mlux_per_bit_base_it = CM3232_MLUX_PER_BIT_BASE_IT,
+};
+
+struct cm3232_chip {
+ struct i2c_client *client;
+ struct cm3232_als_info *als_info;
+ u8 regs_cmd;
+ u16 regs_als;
+};
+
+/**
+ * cm3232_reg_init() - Initialize CM3232
+ * @chip: pointer of struct cm3232_chip.
+ *
+ * Check and initialize CM3232 ambient light sensor.
+ *
+ * Return: 0 for success; otherwise for error code.
+ */
+static int cm3232_reg_init(struct cm3232_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ s32 ret;
+
+ chip->als_info = &cm3232_als_info_default;
+
+ /* Identify device */
+ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ID);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Error reading addr_id\n");
+ return ret;
+ }
+
+ if ((ret & 0xFF) != chip->als_info->hw_id)
+ return -ENODEV;
+
+ /* Disable and reset device */
+ chip->regs_cmd = CM3232_CMD_ALS_DISABLE | CM3232_CMD_ALS_RESET;
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+ if (ret < 0) {
+ dev_err(&chip->client->dev, "Error writing reg_cmd\n");
+ return ret;
+ }
+
+ /* Register default value */
+ chip->regs_cmd = chip->als_info->regs_cmd_default;
+
+ /* Configure register */
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+ if (ret < 0)
+ dev_err(&chip->client->dev, "Error writing reg_cmd\n");
+
+ return 0;
+}
+
+/**
+ * cm3232_read_als_it() - Get sensor integration time
+ * @chip: pointer of struct cm3232_chip
+ * @val: pointer of int to load the integration (sec).
+ * @val2: pointer of int to load the integration time (microsecond).
+ *
+ * Report the current integration time.
+ *
+ * Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL.
+ */
+static int cm3232_read_als_it(struct cm3232_chip *chip, int *val, int *val2)
+{
+ u16 als_it;
+ int i;
+
+ als_it = chip->regs_cmd;
+ als_it &= CM3232_CMD_ALS_IT_MASK;
+ als_it >>= CM3232_CMD_ALS_IT_SHIFT;
+ for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
+ if (als_it == cm3232_als_it_scales[i].it) {
+ *val = cm3232_als_it_scales[i].val;
+ *val2 = cm3232_als_it_scales[i].val2;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * cm3232_write_als_it() - Write sensor integration time
+ * @chip: pointer of struct cm3232_chip.
+ * @val: integration time in second.
+ * @val2: integration time in microsecond.
+ *
+ * Convert integration time to sensor value.
+ *
+ * Return: i2c_smbus_write_byte_data command return value.
+ */
+static int cm3232_write_als_it(struct cm3232_chip *chip, int val, int val2)
+{
+ struct i2c_client *client = chip->client;
+ u16 als_it, cmd;
+ int i;
+ s32 ret;
+
+ for (i = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++) {
+ if (val == cm3232_als_it_scales[i].val &&
+ val2 == cm3232_als_it_scales[i].val2) {
+
+ als_it = cm3232_als_it_scales[i].it;
+ als_it <<= CM3232_CMD_ALS_IT_SHIFT;
+
+ cmd = chip->regs_cmd & ~CM3232_CMD_ALS_IT_MASK;
+ cmd |= als_it;
+ ret = i2c_smbus_write_byte_data(client,
+ CM3232_REG_ADDR_CMD,
+ cmd);
+ if (ret < 0)
+ return ret;
+ chip->regs_cmd = cmd;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * cm3232_get_lux() - report current lux value
+ * @chip: pointer of struct cm3232_chip.
+ *
+ * Convert sensor data to lux. It depends on integration
+ * time and calibscale variable.
+ *
+ * Return: Zero or positive value is lux, otherwise error code.
+ */
+static int cm3232_get_lux(struct cm3232_chip *chip)
+{
+ struct i2c_client *client = chip->client;
+ struct cm3232_als_info *als_info = chip->als_info;
+ int ret;
+ int val, val2;
+ int als_it;
+ u64 lux;
+
+ /* Calculate mlux per bit based on als_it */
+ ret = cm3232_read_als_it(chip, &val, &val2);
+ if (ret < 0)
+ return -EINVAL;
+ als_it = val * 1000000 + val2;
+ lux = (__force u64)als_info->mlux_per_bit;
+ lux *= als_info->mlux_per_bit_base_it;
+ lux = div_u64(lux, als_it);
+
+ ret = i2c_smbus_read_word_data(client, CM3232_REG_ADDR_ALS);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading reg_addr_als\n");
+ return ret;
+ }
+
+ chip->regs_als = (u16)ret;
+ lux *= chip->regs_als;
+ lux *= als_info->calibscale;
+ lux = div_u64(lux, CM3232_CALIBSCALE_RESOLUTION);
+ lux = div_u64(lux, CM3232_MLUX_PER_LUX);
+
+ if (lux > 0xFFFF)
+ lux = 0xFFFF;
+
+ return (int)lux;
+}
+
+static int cm3232_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct cm3232_als_info *als_info = chip->als_info;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = cm3232_get_lux(chip);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ *val = als_info->calibscale;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ return cm3232_read_als_it(chip, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+static int cm3232_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct cm3232_als_info *als_info = chip->als_info;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ als_info->calibscale = val;
+ return 0;
+ case IIO_CHAN_INFO_INT_TIME:
+ return cm3232_write_als_it(chip, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * cm3232_get_it_available() - Get available ALS IT value
+ * @dev: pointer of struct device.
+ * @attr: pointer of struct device_attribute.
+ * @buf: pointer of return string buffer.
+ *
+ * Display the available integration time in second.
+ *
+ * Return: string length.
+ */
+static ssize_t cm3232_get_it_available(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int i, len;
+
+ for (i = 0, len = 0; i < ARRAY_SIZE(cm3232_als_it_scales); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%u.%06u ",
+ cm3232_als_it_scales[i].val,
+ cm3232_als_it_scales[i].val2);
+ return len + scnprintf(buf + len, PAGE_SIZE - len, "\n");
+}
+
+static const struct iio_chan_spec cm3232_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ }
+};
+
+static IIO_DEVICE_ATTR(in_illuminance_integration_time_available,
+ S_IRUGO, cm3232_get_it_available, NULL, 0);
+
+static struct attribute *cm3232_attributes[] = {
+ &iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group cm3232_attribute_group = {
+ .attrs = cm3232_attributes
+};
+
+static const struct iio_info cm3232_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &cm3232_read_raw,
+ .write_raw = &cm3232_write_raw,
+ .attrs = &cm3232_attribute_group,
+};
+
+static int cm3232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cm3232_chip *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);
+ i2c_set_clientdata(client, indio_dev);
+ chip->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = cm3232_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cm3232_channels);
+ indio_dev->info = &cm3232_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = cm3232_reg_init(chip);
+ if (ret) {
+ dev_err(&client->dev,
+ "%s: register init failed\n",
+ __func__);
+ return ret;
+ }
+
+ return iio_device_register(indio_dev);
+}
+
+static int cm3232_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ CM3232_CMD_ALS_DISABLE);
+
+ iio_device_unregister(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id cm3232_id[] = {
+ {"cm3232", 0},
+ {}
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int cm3232_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ chip->regs_cmd |= CM3232_CMD_ALS_DISABLE;
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd);
+
+ return ret;
+}
+
+static int cm3232_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct cm3232_chip *chip = iio_priv(indio_dev);
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ chip->regs_cmd &= ~CM3232_CMD_ALS_DISABLE;
+ ret = i2c_smbus_write_byte_data(client, CM3232_REG_ADDR_CMD,
+ chip->regs_cmd | CM3232_CMD_ALS_RESET);
+
+ return ret;
+}
+
+static const struct dev_pm_ops cm3232_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(cm3232_suspend, cm3232_resume)};
+#endif
+
+MODULE_DEVICE_TABLE(i2c, cm3232_id);
+
+static const struct of_device_id cm3232_of_match[] = {
+ {.compatible = "capella,cm3232"},
+ {}
+};
+
+static struct i2c_driver cm3232_driver = {
+ .driver = {
+ .name = "cm3232",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(cm3232_of_match),
+#ifdef CONFIG_PM_SLEEP
+ .pm = &cm3232_pm_ops,
+#endif
+ },
+ .id_table = cm3232_id,
+ .probe = cm3232_probe,
+ .remove = cm3232_remove,
+};
+
+module_i2c_driver(cm3232_driver);
+
+MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>");
+MODULE_DESCRIPTION("CM3232 ambient light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
new file mode 100644
index 0000000..869033e
--- /dev/null
+++ b/drivers/iio/light/cm3323.c
@@ -0,0 +1,286 @@
+/*
+ * CM3323 - Capella Color Light Sensor
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * 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.
+ *
+ * IIO driver for CM3323 (7-bit I2C slave address 0x10)
+ *
+ * TODO: calibscale to correct the lens factor
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define CM3323_DRV_NAME "cm3323"
+
+#define CM3323_CMD_CONF 0x00
+#define CM3323_CMD_RED_DATA 0x08
+#define CM3323_CMD_GREEN_DATA 0x09
+#define CM3323_CMD_BLUE_DATA 0x0A
+#define CM3323_CMD_CLEAR_DATA 0x0B
+
+#define CM3323_CONF_SD_BIT BIT(0) /* sensor disable */
+#define CM3323_CONF_AF_BIT BIT(1) /* auto/manual force mode */
+#define CM3323_CONF_IT_MASK (BIT(4) | BIT(5) | BIT(6))
+#define CM3323_CONF_IT_SHIFT 4
+
+#define CM3323_INT_TIME_AVAILABLE "0.04 0.08 0.16 0.32 0.64 1.28"
+
+static const struct {
+ int val;
+ int val2;
+} cm3323_int_time[] = {
+ {0, 40000}, /* 40 ms */
+ {0, 80000}, /* 80 ms */
+ {0, 160000}, /* 160 ms */
+ {0, 320000}, /* 320 ms */
+ {0, 640000}, /* 640 ms */
+ {1, 280000}, /* 1280 ms */
+};
+
+struct cm3323_data {
+ struct i2c_client *client;
+ u16 reg_conf;
+ struct mutex mutex;
+};
+
+#define CM3323_COLOR_CHANNEL(_color, _addr) { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), \
+ .channel2 = IIO_MOD_LIGHT_##_color, \
+ .address = _addr, \
+}
+
+static const struct iio_chan_spec cm3323_channels[] = {
+ CM3323_COLOR_CHANNEL(RED, CM3323_CMD_RED_DATA),
+ CM3323_COLOR_CHANNEL(GREEN, CM3323_CMD_GREEN_DATA),
+ CM3323_COLOR_CHANNEL(BLUE, CM3323_CMD_BLUE_DATA),
+ CM3323_COLOR_CHANNEL(CLEAR, CM3323_CMD_CLEAR_DATA),
+};
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL(CM3323_INT_TIME_AVAILABLE);
+
+static struct attribute *cm3323_attributes[] = {
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group cm3323_attribute_group = {
+ .attrs = cm3323_attributes,
+};
+
+static int cm3323_init(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct cm3323_data *data = iio_priv(indio_dev);
+
+ ret = i2c_smbus_read_word_data(data->client, CM3323_CMD_CONF);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg_conf\n");
+ return ret;
+ }
+
+ /* enable sensor and set auto force mode */
+ ret &= ~(CM3323_CONF_SD_BIT | CM3323_CONF_AF_BIT);
+
+ ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF, ret);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error writing reg_conf\n");
+ return ret;
+ }
+
+ data->reg_conf = ret;
+
+ return 0;
+}
+
+static void cm3323_disable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct cm3323_data *data = iio_priv(indio_dev);
+
+ ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
+ CM3323_CONF_SD_BIT);
+ if (ret < 0)
+ dev_err(&data->client->dev, "Error writing reg_conf\n");
+}
+
+static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
+{
+ int i, ret;
+ u16 reg_conf;
+
+ for (i = 0; i < ARRAY_SIZE(cm3323_int_time); i++) {
+ if (val == cm3323_int_time[i].val &&
+ val2 == cm3323_int_time[i].val2) {
+ reg_conf = data->reg_conf;
+ reg_conf |= i << CM3323_CONF_IT_SHIFT;
+
+ ret = i2c_smbus_write_word_data(data->client,
+ CM3323_CMD_CONF,
+ reg_conf);
+ if (ret < 0)
+ return ret;
+
+ data->reg_conf = reg_conf;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int cm3323_get_it_bits(struct cm3323_data *data)
+{
+ int bits;
+
+ bits = (data->reg_conf & CM3323_CONF_IT_MASK) >>
+ CM3323_CONF_IT_SHIFT;
+
+ if (bits >= ARRAY_SIZE(cm3323_int_time))
+ return -EINVAL;
+ return bits;
+}
+
+static int cm3323_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int i, ret;
+ struct cm3323_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->mutex);
+ ret = i2c_smbus_read_word_data(data->client, chan->address);
+ if (ret < 0) {
+ mutex_unlock(&data->mutex);
+ return ret;
+ }
+ *val = ret;
+ mutex_unlock(&data->mutex);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ mutex_lock(&data->mutex);
+ i = cm3323_get_it_bits(data);
+ if (i < 0) {
+ mutex_unlock(&data->mutex);
+ return -EINVAL;
+ }
+
+ *val = cm3323_int_time[i].val;
+ *val2 = cm3323_int_time[i].val2;
+ mutex_unlock(&data->mutex);
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cm3323_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct cm3323_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ mutex_lock(&data->mutex);
+ ret = cm3323_set_it_bits(data, val, val2);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info cm3323_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = cm3323_read_raw,
+ .write_raw = cm3323_write_raw,
+ .attrs = &cm3323_attribute_group,
+};
+
+static int cm3323_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct cm3323_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;
+
+ mutex_init(&data->mutex);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &cm3323_info;
+ indio_dev->name = CM3323_DRV_NAME;
+ indio_dev->channels = cm3323_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cm3323_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = cm3323_init(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "cm3323 chip init failed\n");
+ return ret;
+ }
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to register iio dev\n");
+ goto err_init;
+ }
+ return 0;
+err_init:
+ cm3323_disable(indio_dev);
+ return ret;
+}
+
+static int cm3323_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ cm3323_disable(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id cm3323_id[] = {
+ {"cm3323", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cm3323_id);
+
+static struct i2c_driver cm3323_driver = {
+ .driver = {
+ .name = CM3323_DRV_NAME,
+ },
+ .probe = cm3323_probe,
+ .remove = cm3323_remove,
+ .id_table = cm3323_id,
+};
+
+module_i2c_driver(cm3323_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Capella CM3323 Color Light Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 221ed16..32b6449 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -46,6 +46,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
#include <linux/iio/iio.h>
@@ -966,7 +967,6 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
struct iio_dev *indio_dev = pf->indio_dev;
struct gp2ap020a00f_data *priv = iio_priv(indio_dev);
size_t d_size = 0;
- __le32 light_lux;
int i, out_val, ret;
for_each_set_bit(i, indio_dev->active_scan_mask,
@@ -981,8 +981,8 @@ static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data)
i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) {
out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]);
gp2ap020a00f_output_to_lux(priv, &out_val);
- light_lux = cpu_to_le32(out_val);
- memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4);
+
+ put_unaligned_le32(out_val, &priv->buffer[d_size]);
d_size += 4;
} else {
d_size += 2;
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index a5283d7..1609ecd 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -80,7 +80,6 @@ static int als_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
@@ -97,19 +96,13 @@ static int als_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
- poll_value = hid_sensor_read_poll_value(
- &als_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&als_state->common_attributes,
true);
- msleep_interruptible(poll_value * 2);
-
*val = sensor_hub_input_attr_get_raw_value(
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
- report_id);
+ report_id,
+ SENSOR_HUB_SYNC);
hid_sensor_power_state(&als_state->common_attributes,
false);
} else {
@@ -381,6 +374,7 @@ static struct platform_driver hid_als_platform_driver = {
.id_table = hid_als_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_als_probe,
.remove = hid_als_remove,
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index f5a5146..91ecc46 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -75,7 +75,6 @@ static int prox_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
@@ -92,20 +91,13 @@ static int prox_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
- poll_value = hid_sensor_read_poll_value(
- &prox_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&prox_state->common_attributes,
true);
-
- msleep_interruptible(poll_value * 2);
-
*val = sensor_hub_input_attr_get_raw_value(
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
- report_id);
+ report_id,
+ SENSOR_HUB_SYNC);
hid_sensor_power_state(&prox_state->common_attributes,
false);
} else {
@@ -373,6 +365,7 @@ static struct platform_driver hid_prox_platform_driver = {
.id_table = hid_prox_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_prox_probe,
.remove = hid_prox_remove,
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
new file mode 100644
index 0000000..3a3af89
--- /dev/null
+++ b/drivers/iio/light/jsa1212.c
@@ -0,0 +1,471 @@
+/*
+ * JSA1212 Ambient Light & Proximity Sensor Driver
+ *
+ * Copyright (c) 2014, 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.
+ *
+ * JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD)
+ *
+ * TODO: Interrupt support, thresholds, range support.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* JSA1212 reg address */
+#define JSA1212_CONF_REG 0x01
+#define JSA1212_INT_REG 0x02
+#define JSA1212_PXS_LT_REG 0x03
+#define JSA1212_PXS_HT_REG 0x04
+#define JSA1212_ALS_TH1_REG 0x05
+#define JSA1212_ALS_TH2_REG 0x06
+#define JSA1212_ALS_TH3_REG 0x07
+#define JSA1212_PXS_DATA_REG 0x08
+#define JSA1212_ALS_DT1_REG 0x09
+#define JSA1212_ALS_DT2_REG 0x0A
+#define JSA1212_ALS_RNG_REG 0x0B
+#define JSA1212_MAX_REG 0x0C
+
+/* JSA1212 reg masks */
+#define JSA1212_CONF_MASK 0xFF
+#define JSA1212_INT_MASK 0xFF
+#define JSA1212_PXS_LT_MASK 0xFF
+#define JSA1212_PXS_HT_MASK 0xFF
+#define JSA1212_ALS_TH1_MASK 0xFF
+#define JSA1212_ALS_TH2_LT_MASK 0x0F
+#define JSA1212_ALS_TH2_HT_MASK 0xF0
+#define JSA1212_ALS_TH3_MASK 0xFF
+#define JSA1212_PXS_DATA_MASK 0xFF
+#define JSA1212_ALS_DATA_MASK 0x0FFF
+#define JSA1212_ALS_DT1_MASK 0xFF
+#define JSA1212_ALS_DT2_MASK 0x0F
+#define JSA1212_ALS_RNG_MASK 0x07
+
+/* JSA1212 CONF REG bits */
+#define JSA1212_CONF_PXS_MASK 0x80
+#define JSA1212_CONF_PXS_ENABLE 0x80
+#define JSA1212_CONF_PXS_DISABLE 0x00
+#define JSA1212_CONF_ALS_MASK 0x04
+#define JSA1212_CONF_ALS_ENABLE 0x04
+#define JSA1212_CONF_ALS_DISABLE 0x00
+#define JSA1212_CONF_IRDR_MASK 0x08
+/* Proxmity sensing IRDR current sink settings */
+#define JSA1212_CONF_IRDR_200MA 0x08
+#define JSA1212_CONF_IRDR_100MA 0x00
+#define JSA1212_CONF_PXS_SLP_MASK 0x70
+#define JSA1212_CONF_PXS_SLP_0MS 0x70
+#define JSA1212_CONF_PXS_SLP_12MS 0x60
+#define JSA1212_CONF_PXS_SLP_50MS 0x50
+#define JSA1212_CONF_PXS_SLP_75MS 0x40
+#define JSA1212_CONF_PXS_SLP_100MS 0x30
+#define JSA1212_CONF_PXS_SLP_200MS 0x20
+#define JSA1212_CONF_PXS_SLP_400MS 0x10
+#define JSA1212_CONF_PXS_SLP_800MS 0x00
+
+/* JSA1212 INT REG bits */
+#define JSA1212_INT_CTRL_MASK 0x01
+#define JSA1212_INT_CTRL_EITHER 0x00
+#define JSA1212_INT_CTRL_BOTH 0x01
+#define JSA1212_INT_ALS_PRST_MASK 0x06
+#define JSA1212_INT_ALS_PRST_1CONV 0x00
+#define JSA1212_INT_ALS_PRST_4CONV 0x02
+#define JSA1212_INT_ALS_PRST_8CONV 0x04
+#define JSA1212_INT_ALS_PRST_16CONV 0x06
+#define JSA1212_INT_ALS_FLAG_MASK 0x08
+#define JSA1212_INT_ALS_FLAG_CLR 0x00
+#define JSA1212_INT_PXS_PRST_MASK 0x60
+#define JSA1212_INT_PXS_PRST_1CONV 0x00
+#define JSA1212_INT_PXS_PRST_4CONV 0x20
+#define JSA1212_INT_PXS_PRST_8CONV 0x40
+#define JSA1212_INT_PXS_PRST_16CONV 0x60
+#define JSA1212_INT_PXS_FLAG_MASK 0x80
+#define JSA1212_INT_PXS_FLAG_CLR 0x00
+
+/* JSA1212 ALS RNG REG bits */
+#define JSA1212_ALS_RNG_0_2048 0x00
+#define JSA1212_ALS_RNG_0_1024 0x01
+#define JSA1212_ALS_RNG_0_512 0x02
+#define JSA1212_ALS_RNG_0_256 0x03
+#define JSA1212_ALS_RNG_0_128 0x04
+
+/* JSA1212 INT threshold range */
+#define JSA1212_ALS_TH_MIN 0x0000
+#define JSA1212_ALS_TH_MAX 0x0FFF
+#define JSA1212_PXS_TH_MIN 0x00
+#define JSA1212_PXS_TH_MAX 0xFF
+
+#define JSA1212_ALS_DELAY_MS 200
+#define JSA1212_PXS_DELAY_MS 100
+
+#define JSA1212_DRIVER_NAME "jsa1212"
+#define JSA1212_REGMAP_NAME "jsa1212_regmap"
+
+enum jsa1212_op_mode {
+ JSA1212_OPMODE_ALS_EN,
+ JSA1212_OPMODE_PXS_EN,
+};
+
+struct jsa1212_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ u8 als_rng_idx;
+ bool als_en; /* ALS enable status */
+ bool pxs_en; /* proximity enable status */
+ struct regmap *regmap;
+};
+
+/* ALS range idx to val mapping */
+static const int jsa1212_als_range_val[] = {2048, 1024, 512, 256, 128,
+ 128, 128, 128};
+
+/* Enables or disables ALS function based on status */
+static int jsa1212_als_enable(struct jsa1212_data *data, u8 status)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_ALS_MASK,
+ status);
+ if (ret < 0)
+ return ret;
+
+ data->als_en = !!status;
+
+ return 0;
+}
+
+/* Enables or disables PXS function based on status */
+static int jsa1212_pxs_enable(struct jsa1212_data *data, u8 status)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_PXS_MASK,
+ status);
+ if (ret < 0)
+ return ret;
+
+ data->pxs_en = !!status;
+
+ return 0;
+}
+
+static int jsa1212_read_als_data(struct jsa1212_data *data,
+ unsigned int *val)
+{
+ int ret;
+ __le16 als_data;
+
+ ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Delay for data output */
+ msleep(JSA1212_ALS_DELAY_MS);
+
+ /* Read 12 bit data */
+ ret = regmap_bulk_read(data->regmap, JSA1212_ALS_DT1_REG, &als_data, 2);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "als data read err\n");
+ goto als_data_read_err;
+ }
+
+ *val = le16_to_cpu(als_data);
+
+als_data_read_err:
+ return jsa1212_als_enable(data, JSA1212_CONF_ALS_DISABLE);
+}
+
+static int jsa1212_read_pxs_data(struct jsa1212_data *data,
+ unsigned int *val)
+{
+ int ret;
+ unsigned int pxs_data;
+
+ ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* Delay for data output */
+ msleep(JSA1212_PXS_DELAY_MS);
+
+ /* Read out all data */
+ ret = regmap_read(data->regmap, JSA1212_PXS_DATA_REG, &pxs_data);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "pxs data read err\n");
+ goto pxs_data_read_err;
+ }
+
+ *val = pxs_data & JSA1212_PXS_DATA_MASK;
+
+pxs_data_read_err:
+ return jsa1212_pxs_enable(data, JSA1212_CONF_PXS_DISABLE);
+}
+
+static int jsa1212_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct jsa1212_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = jsa1212_read_als_data(data, val);
+ break;
+ case IIO_PROXIMITY:
+ ret = jsa1212_read_pxs_data(data, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->lock);
+ return ret < 0 ? ret : IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ *val = jsa1212_als_range_val[data->als_rng_idx];
+ *val2 = BIT(12); /* Max 12 bit value */
+ return IIO_VAL_FRACTIONAL;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec jsa1212_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_PROXIMITY,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ }
+};
+
+static const struct iio_info jsa1212_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &jsa1212_read_raw,
+};
+
+static int jsa1212_chip_init(struct jsa1212_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, JSA1212_CONF_REG,
+ (JSA1212_CONF_PXS_SLP_50MS |
+ JSA1212_CONF_IRDR_200MA));
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, JSA1212_INT_REG,
+ JSA1212_INT_ALS_PRST_4CONV);
+ if (ret < 0)
+ return ret;
+
+ data->als_rng_idx = JSA1212_ALS_RNG_0_2048;
+
+ return 0;
+}
+
+static bool jsa1212_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case JSA1212_PXS_DATA_REG:
+ case JSA1212_ALS_DT1_REG:
+ case JSA1212_ALS_DT2_REG:
+ case JSA1212_INT_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config jsa1212_regmap_config = {
+ .name = JSA1212_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = JSA1212_MAX_REG,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = jsa1212_is_volatile_reg,
+};
+
+static int jsa1212_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct jsa1212_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &jsa1212_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+
+ mutex_init(&data->lock);
+
+ ret = jsa1212_chip_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = jsa1212_channels;
+ indio_dev->num_channels = ARRAY_SIZE(jsa1212_channels);
+ indio_dev->name = JSA1212_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ indio_dev->info = &jsa1212_info;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ dev_err(&client->dev, "%s: register device failed\n", __func__);
+
+ return ret;
+}
+
+ /* power off the device */
+static int jsa1212_power_off(struct jsa1212_data *data)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ ret = regmap_update_bits(data->regmap, JSA1212_CONF_REG,
+ JSA1212_CONF_ALS_MASK |
+ JSA1212_CONF_PXS_MASK,
+ JSA1212_CONF_ALS_DISABLE |
+ JSA1212_CONF_PXS_DISABLE);
+
+ if (ret < 0)
+ dev_err(&data->client->dev, "power off cmd failed\n");
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int jsa1212_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct jsa1212_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ return jsa1212_power_off(data);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int jsa1212_suspend(struct device *dev)
+{
+ struct jsa1212_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return jsa1212_power_off(data);
+}
+
+static int jsa1212_resume(struct device *dev)
+{
+ int ret = 0;
+ struct jsa1212_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ mutex_lock(&data->lock);
+
+ if (data->als_en) {
+ ret = jsa1212_als_enable(data, JSA1212_CONF_ALS_ENABLE);
+ if (ret < 0) {
+ dev_err(dev, "als resume failed\n");
+ goto unlock_and_ret;
+ }
+ }
+
+ if (data->pxs_en) {
+ ret = jsa1212_pxs_enable(data, JSA1212_CONF_PXS_ENABLE);
+ if (ret < 0)
+ dev_err(dev, "pxs resume failed\n");
+ }
+
+unlock_and_ret:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static SIMPLE_DEV_PM_OPS(jsa1212_pm_ops, jsa1212_suspend, jsa1212_resume);
+
+#define JSA1212_PM_OPS (&jsa1212_pm_ops)
+#else
+#define JSA1212_PM_OPS NULL
+#endif
+
+static const struct acpi_device_id jsa1212_acpi_match[] = {
+ {"JSA1212", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, jsa1212_acpi_match);
+
+static const struct i2c_device_id jsa1212_id[] = {
+ { JSA1212_DRIVER_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, jsa1212_id);
+
+static struct i2c_driver jsa1212_driver = {
+ .driver = {
+ .name = JSA1212_DRIVER_NAME,
+ .pm = JSA1212_PM_OPS,
+ .owner = THIS_MODULE,
+ .acpi_match_table = ACPI_PTR(jsa1212_acpi_match),
+ },
+ .probe = jsa1212_probe,
+ .remove = jsa1212_remove,
+ .id_table = jsa1212_id,
+};
+module_i2c_driver(jsa1212_driver);
+
+MODULE_AUTHOR("Sathya Kuppuswamy <sathyanarayanan.kuppuswamy@linux.intel.com>");
+MODULE_DESCRIPTION("JSA1212 proximity/ambient light sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index ae3c71b..076bc46 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -657,7 +657,7 @@ static ALS_HYSTERESIS_ATTR_RO(3);
#define ILLUMINANCE_ATTR_RO(_name) \
DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO, show_##_name, NULL)
#define ILLUMINANCE_ATTR_RW(_name) \
- DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR , \
+ DEVICE_ATTR(in_illuminance0_##_name, S_IRUGO | S_IWUSR, \
show_##_name, store_##_name)
/*
* ALS Zone threshold-event enable
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 62b7072..78b8783 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -333,6 +333,13 @@ static int ltr501_init(struct ltr501_data *data)
data->ps_contr);
}
+static int ltr501_powerdown(struct ltr501_data *data)
+{
+ return ltr501_write_contr(data->client,
+ data->als_contr & ~LTR501_CONTR_ACTIVE,
+ data->ps_contr & ~LTR501_CONTR_ACTIVE);
+}
+
static int ltr501_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -370,7 +377,7 @@ static int ltr501_probe(struct i2c_client *client,
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ltr501_trigger_handler, NULL);
if (ret)
- return ret;
+ goto powerdown_on_error;
ret = iio_device_register(indio_dev);
if (ret)
@@ -380,16 +387,11 @@ static int ltr501_probe(struct i2c_client *client,
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
+powerdown_on_error:
+ ltr501_powerdown(data);
return ret;
}
-static int ltr501_powerdown(struct ltr501_data *data)
-{
- return ltr501_write_contr(data->client,
- data->als_contr & ~LTR501_CONTR_ACTIVE,
- data->ps_contr & ~LTR501_CONTR_ACTIVE);
-}
-
static int ltr501_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index a9e449b..71c2bde 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -149,8 +149,8 @@ static int tcs3414_read_raw(struct iio_dev *indio_dev,
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
- *val = tcs3414_scales[i][0];
+ i = (data->gain & TCS3414_GAIN_MASK) >> TCS3414_GAIN_SHIFT;
+ *val = tcs3414_scales[i][0];
*val2 = tcs3414_scales[i][1];
return IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_INT_TIME:
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index b2dba9e..a5d6de7 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -6,13 +6,12 @@
menu "Magnetometer sensors"
config AK8975
- tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
+ tristate "Asahi Kasei AK 3-Axis Magnetometer"
depends on I2C
depends on GPIOLIB
help
- Say yes here to build support for Asahi Kasei AK8975 3-Axis
- Magnetometer. This driver can also support AK8963, if i2c
- device name is identified as ak8963.
+ Say yes here to build support for Asahi Kasei AK8975, AK8963,
+ AK09911 or AK09912 3-Axis Magnetometer.
To compile this driver as a module, choose M here: the module
will be called ak8975.
@@ -20,12 +19,10 @@ config AK8975
config AK09911
tristate "Asahi Kasei AK09911 3-axis Compass"
depends on I2C
+ depends on GPIOLIB
+ select AK8975
help
- Say yes here to build support for Asahi Kasei AK09911 3-Axis
- Magnetometer.
-
- To compile this driver as a module, choose M here: the module
- will be called ak09911.
+ Deprecated: AK09911 is now supported by AK8975 driver.
config MAG3110
tristate "Freescale MAG3110 3-Axis Magnetometer"
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index b91315e..0f5d3c9 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -3,7 +3,6 @@
#
# When adding new entries keep the list in alphabetical order
-obj-$(CONFIG_AK09911) += ak09911.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_MAG3110) += mag3110.o
obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
diff --git a/drivers/iio/magnetometer/ak09911.c b/drivers/iio/magnetometer/ak09911.c
deleted file mode 100644
index b2bc942..0000000
--- a/drivers/iio/magnetometer/ak09911.c
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * AK09911 3-axis compass driver
- * Copyright (c) 2014, 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.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/i2c.h>
-#include <linux/acpi.h>
-#include <linux/iio/iio.h>
-
-#define AK09911_REG_WIA1 0x00
-#define AK09911_REG_WIA2 0x01
-#define AK09911_WIA1_VALUE 0x48
-#define AK09911_WIA2_VALUE 0x05
-
-#define AK09911_REG_ST1 0x10
-#define AK09911_REG_HXL 0x11
-#define AK09911_REG_HXH 0x12
-#define AK09911_REG_HYL 0x13
-#define AK09911_REG_HYH 0x14
-#define AK09911_REG_HZL 0x15
-#define AK09911_REG_HZH 0x16
-
-#define AK09911_REG_ASAX 0x60
-#define AK09911_REG_ASAY 0x61
-#define AK09911_REG_ASAZ 0x62
-
-#define AK09911_REG_CNTL1 0x30
-#define AK09911_REG_CNTL2 0x31
-#define AK09911_REG_CNTL3 0x32
-
-#define AK09911_MODE_SNG_MEASURE 0x01
-#define AK09911_MODE_SELF_TEST 0x10
-#define AK09911_MODE_FUSE_ACCESS 0x1F
-#define AK09911_MODE_POWERDOWN 0x00
-#define AK09911_RESET_DATA 0x01
-
-#define AK09911_REG_CNTL1 0x30
-#define AK09911_REG_CNTL2 0x31
-#define AK09911_REG_CNTL3 0x32
-
-#define AK09911_RAW_TO_GAUSS(asa) ((((asa) + 128) * 6000) / 256)
-
-#define AK09911_MAX_CONVERSION_TIMEOUT_MS 500
-#define AK09911_CONVERSION_DONE_POLL_TIME_MS 10
-
-struct ak09911_data {
- struct i2c_client *client;
- struct mutex lock;
- u8 asa[3];
- long raw_to_gauss[3];
-};
-
-static const int ak09911_index_to_reg[] = {
- AK09911_REG_HXL, AK09911_REG_HYL, AK09911_REG_HZL,
-};
-
-static int ak09911_set_mode(struct i2c_client *client, u8 mode)
-{
- int ret;
-
- switch (mode) {
- case AK09911_MODE_SNG_MEASURE:
- case AK09911_MODE_SELF_TEST:
- case AK09911_MODE_FUSE_ACCESS:
- case AK09911_MODE_POWERDOWN:
- ret = i2c_smbus_write_byte_data(client,
- AK09911_REG_CNTL2, mode);
- if (ret < 0) {
- dev_err(&client->dev, "set_mode error\n");
- return ret;
- }
- /* After mode change wait atleast 100us */
- usleep_range(100, 500);
- break;
- default:
- dev_err(&client->dev,
- "%s: Unknown mode(%d).", __func__, mode);
- return -EINVAL;
- }
-
- return ret;
-}
-
-/* Get Sensitivity Adjustment value */
-static int ak09911_get_asa(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct ak09911_data *data = iio_priv(indio_dev);
- int ret;
-
- ret = ak09911_set_mode(client, AK09911_MODE_FUSE_ACCESS);
- if (ret < 0)
- return ret;
-
- /* Get asa data and store in the device data. */
- ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_ASAX,
- 3, data->asa);
- if (ret < 0) {
- dev_err(&client->dev, "Not able to read asa data\n");
- return ret;
- }
-
- ret = ak09911_set_mode(client, AK09911_MODE_POWERDOWN);
- if (ret < 0)
- return ret;
-
- data->raw_to_gauss[0] = AK09911_RAW_TO_GAUSS(data->asa[0]);
- data->raw_to_gauss[1] = AK09911_RAW_TO_GAUSS(data->asa[1]);
- data->raw_to_gauss[2] = AK09911_RAW_TO_GAUSS(data->asa[2]);
-
- return 0;
-}
-
-static int ak09911_verify_chip_id(struct i2c_client *client)
-{
- u8 wia_val[2];
- int ret;
-
- ret = i2c_smbus_read_i2c_block_data(client, AK09911_REG_WIA1,
- 2, wia_val);
- if (ret < 0) {
- dev_err(&client->dev, "Error reading WIA\n");
- return ret;
- }
-
- dev_dbg(&client->dev, "WIA %02x %02x\n", wia_val[0], wia_val[1]);
-
- if (wia_val[0] != AK09911_WIA1_VALUE ||
- wia_val[1] != AK09911_WIA2_VALUE) {
- dev_err(&client->dev, "Device ak09911 not found\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static int wait_conversion_complete_polled(struct ak09911_data *data)
-{
- struct i2c_client *client = data->client;
- u8 read_status;
- u32 timeout_ms = AK09911_MAX_CONVERSION_TIMEOUT_MS;
- int ret;
-
- /* Wait for the conversion to complete. */
- while (timeout_ms) {
- msleep_interruptible(AK09911_CONVERSION_DONE_POLL_TIME_MS);
- ret = i2c_smbus_read_byte_data(client, AK09911_REG_ST1);
- if (ret < 0) {
- dev_err(&client->dev, "Error in reading ST1\n");
- return ret;
- }
- read_status = ret & 0x01;
- if (read_status)
- break;
- timeout_ms -= AK09911_CONVERSION_DONE_POLL_TIME_MS;
- }
- if (!timeout_ms) {
- dev_err(&client->dev, "Conversion timeout happened\n");
- return -EIO;
- }
-
- return read_status;
-}
-
-static int ak09911_read_axis(struct iio_dev *indio_dev, int index, int *val)
-{
- struct ak09911_data *data = iio_priv(indio_dev);
- struct i2c_client *client = data->client;
- int ret;
-
- mutex_lock(&data->lock);
-
- ret = ak09911_set_mode(client, AK09911_MODE_SNG_MEASURE);
- if (ret < 0)
- goto fn_exit;
-
- ret = wait_conversion_complete_polled(data);
- if (ret < 0)
- goto fn_exit;
-
- /* Read data */
- ret = i2c_smbus_read_word_data(client, ak09911_index_to_reg[index]);
- if (ret < 0) {
- dev_err(&client->dev, "Read axis data fails\n");
- goto fn_exit;
- }
-
- mutex_unlock(&data->lock);
-
- /* Clamp to valid range. */
- *val = sign_extend32(clamp_t(s16, ret, -8192, 8191), 13);
-
- return IIO_VAL_INT;
-
-fn_exit:
- mutex_unlock(&data->lock);
-
- return ret;
-}
-
-static int ak09911_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2,
- long mask)
-{
- struct ak09911_data *data = iio_priv(indio_dev);
-
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- return ak09911_read_axis(indio_dev, chan->address, val);
- case IIO_CHAN_INFO_SCALE:
- *val = 0;
- *val2 = data->raw_to_gauss[chan->address];
- return IIO_VAL_INT_PLUS_MICRO;
- }
-
- return -EINVAL;
-}
-
-#define AK09911_CHANNEL(axis, index) \
- { \
- .type = IIO_MAGN, \
- .modified = 1, \
- .channel2 = IIO_MOD_##axis, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
- .address = index, \
- }
-
-static const struct iio_chan_spec ak09911_channels[] = {
- AK09911_CHANNEL(X, 0), AK09911_CHANNEL(Y, 1), AK09911_CHANNEL(Z, 2),
-};
-
-static const struct iio_info ak09911_info = {
- .read_raw = &ak09911_read_raw,
- .driver_module = THIS_MODULE,
-};
-
-static const struct acpi_device_id ak_acpi_match[] = {
- {"AK009911", 0},
- { },
-};
-MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
-
-static int ak09911_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct iio_dev *indio_dev;
- struct ak09911_data *data;
- const char *name;
- int ret;
-
- ret = ak09911_verify_chip_id(client);
- if (ret) {
- dev_err(&client->dev, "AK00911 not detected\n");
- return -ENODEV;
- }
-
- 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;
- mutex_init(&data->lock);
-
- ret = ak09911_get_asa(client);
- if (ret)
- return ret;
-
- if (id)
- name = id->name;
- else if (ACPI_HANDLE(&client->dev))
- name = dev_name(&client->dev);
- else
- return -ENODEV;
-
- dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
-
- indio_dev->dev.parent = &client->dev;
- indio_dev->channels = ak09911_channels;
- indio_dev->num_channels = ARRAY_SIZE(ak09911_channels);
- indio_dev->info = &ak09911_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->name = name;
-
- return devm_iio_device_register(&client->dev, indio_dev);
-}
-
-static const struct i2c_device_id ak09911_id[] = {
- {"ak09911", 0},
- {}
-};
-
-MODULE_DEVICE_TABLE(i2c, ak09911_id);
-
-static struct i2c_driver ak09911_driver = {
- .driver = {
- .name = "ak09911",
- .acpi_match_table = ACPI_PTR(ak_acpi_match),
- },
- .probe = ak09911_probe,
- .id_table = ak09911_id,
-};
-module_i2c_driver(ak09911_driver);
-
-MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("AK09911 Compass driver");
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index bf5ef07..b13936d 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -64,10 +64,10 @@
#define AK8975_REG_CNTL 0x0A
#define AK8975_REG_CNTL_MODE_SHIFT 0
#define AK8975_REG_CNTL_MODE_MASK (0xF << AK8975_REG_CNTL_MODE_SHIFT)
-#define AK8975_REG_CNTL_MODE_POWER_DOWN 0
-#define AK8975_REG_CNTL_MODE_ONCE 1
-#define AK8975_REG_CNTL_MODE_SELF_TEST 8
-#define AK8975_REG_CNTL_MODE_FUSE_ROM 0xF
+#define AK8975_REG_CNTL_MODE_POWER_DOWN 0x00
+#define AK8975_REG_CNTL_MODE_ONCE 0x01
+#define AK8975_REG_CNTL_MODE_SELF_TEST 0x08
+#define AK8975_REG_CNTL_MODE_FUSE_ROM 0x0F
#define AK8975_REG_RSVC 0x0B
#define AK8975_REG_ASTC 0x0C
@@ -81,18 +81,278 @@
#define AK8975_MAX_REGS AK8975_REG_ASAZ
/*
+ * AK09912 Register definitions
+ */
+#define AK09912_REG_WIA1 0x00
+#define AK09912_REG_WIA2 0x01
+#define AK09912_DEVICE_ID 0x04
+#define AK09911_DEVICE_ID 0x05
+
+#define AK09911_REG_INFO1 0x02
+#define AK09911_REG_INFO2 0x03
+
+#define AK09912_REG_ST1 0x10
+
+#define AK09912_REG_ST1_DRDY_SHIFT 0
+#define AK09912_REG_ST1_DRDY_MASK (1 << AK09912_REG_ST1_DRDY_SHIFT)
+
+#define AK09912_REG_HXL 0x11
+#define AK09912_REG_HXH 0x12
+#define AK09912_REG_HYL 0x13
+#define AK09912_REG_HYH 0x14
+#define AK09912_REG_HZL 0x15
+#define AK09912_REG_HZH 0x16
+#define AK09912_REG_TMPS 0x17
+
+#define AK09912_REG_ST2 0x18
+#define AK09912_REG_ST2_HOFL_SHIFT 3
+#define AK09912_REG_ST2_HOFL_MASK (1 << AK09912_REG_ST2_HOFL_SHIFT)
+
+#define AK09912_REG_CNTL1 0x30
+
+#define AK09912_REG_CNTL2 0x31
+#define AK09912_REG_CNTL_MODE_POWER_DOWN 0x00
+#define AK09912_REG_CNTL_MODE_ONCE 0x01
+#define AK09912_REG_CNTL_MODE_SELF_TEST 0x10
+#define AK09912_REG_CNTL_MODE_FUSE_ROM 0x1F
+#define AK09912_REG_CNTL2_MODE_SHIFT 0
+#define AK09912_REG_CNTL2_MODE_MASK (0x1F << AK09912_REG_CNTL2_MODE_SHIFT)
+
+#define AK09912_REG_CNTL3 0x32
+
+#define AK09912_REG_TS1 0x33
+#define AK09912_REG_TS2 0x34
+#define AK09912_REG_TS3 0x35
+#define AK09912_REG_I2CDIS 0x36
+#define AK09912_REG_TS4 0x37
+
+#define AK09912_REG_ASAX 0x60
+#define AK09912_REG_ASAY 0x61
+#define AK09912_REG_ASAZ 0x62
+
+#define AK09912_MAX_REGS AK09912_REG_ASAZ
+
+/*
* Miscellaneous values.
*/
#define AK8975_MAX_CONVERSION_TIMEOUT 500
#define AK8975_CONVERSION_DONE_POLL_TIME 10
#define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000)
-#define RAW_TO_GAUSS_8975(asa) ((((asa) + 128) * 3000) / 256)
-#define RAW_TO_GAUSS_8963(asa) ((((asa) + 128) * 6000) / 256)
+
+/*
+ * Precalculate scale factor (in Gauss units) for each axis and
+ * store in the device data.
+ *
+ * This scale factor is axis-dependent, and is derived from 3 calibration
+ * factors ASA(x), ASA(y), and ASA(z).
+ *
+ * These ASA values are read from the sensor device at start of day, and
+ * cached in the device context struct.
+ *
+ * Adjusting the flux value with the sensitivity adjustment value should be
+ * done via the following formula:
+ *
+ * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
+ * where H is the raw value, ASA is the sensitivity adjustment, and Hadj
+ * is the resultant adjusted value.
+ *
+ * We reduce the formula to:
+ *
+ * Hadj = H * (ASA + 128) / 256
+ *
+ * H is in the range of -4096 to 4095. The magnetometer has a range of
+ * +-1229uT. To go from the raw value to uT is:
+ *
+ * HuT = H * 1229/4096, or roughly, 3/10.
+ *
+ * Since 1uT = 0.01 gauss, our final scale factor becomes:
+ *
+ * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
+ * Hadj = H * ((ASA + 128) * 0.003) / 256
+ *
+ * Since ASA doesn't change, we cache the resultant scale factor into the
+ * device context in ak8975_setup().
+ *
+ * Given we use IIO_VAL_INT_PLUS_MICRO bit when displaying the scale, we
+ * multiply the stored scale value by 1e6.
+ */
+static long ak8975_raw_to_gauss(u16 data)
+{
+ return (((long)data + 128) * 3000) / 256;
+}
+
+/*
+ * For AK8963 and AK09911, same calculation, but the device is less sensitive:
+ *
+ * H is in the range of +-8190. The magnetometer has a range of
+ * +-4912uT. To go from the raw value to uT is:
+ *
+ * HuT = H * 4912/8190, or roughly, 6/10, instead of 3/10.
+ */
+
+static long ak8963_09911_raw_to_gauss(u16 data)
+{
+ return (((long)data + 128) * 6000) / 256;
+}
+
+/*
+ * For AK09912, same calculation, except the device is more sensitive:
+ *
+ * H is in the range of -32752 to 32752. The magnetometer has a range of
+ * +-4912uT. To go from the raw value to uT is:
+ *
+ * HuT = H * 4912/32752, or roughly, 3/20, instead of 3/10.
+ */
+static long ak09912_raw_to_gauss(u16 data)
+{
+ return (((long)data + 128) * 1500) / 256;
+}
/* Compatible Asahi Kasei Compass parts */
enum asahi_compass_chipset {
AK8975,
AK8963,
+ AK09911,
+ AK09912,
+ AK_MAX_TYPE
+};
+
+enum ak_ctrl_reg_addr {
+ ST1,
+ ST2,
+ CNTL,
+ ASA_BASE,
+ MAX_REGS,
+ REGS_END,
+};
+
+enum ak_ctrl_reg_mask {
+ ST1_DRDY,
+ ST2_HOFL,
+ ST2_DERR,
+ CNTL_MODE,
+ MASK_END,
+};
+
+enum ak_ctrl_mode {
+ POWER_DOWN,
+ MODE_ONCE,
+ SELF_TEST,
+ FUSE_ROM,
+ MODE_END,
+};
+
+struct ak_def {
+ enum asahi_compass_chipset type;
+ long (*raw_to_gauss)(u16 data);
+ u16 range;
+ u8 ctrl_regs[REGS_END];
+ u8 ctrl_masks[MASK_END];
+ u8 ctrl_modes[MODE_END];
+ u8 data_regs[3];
+};
+
+static struct ak_def ak_def_array[AK_MAX_TYPE] = {
+ {
+ .type = AK8975,
+ .raw_to_gauss = ak8975_raw_to_gauss,
+ .range = 4096,
+ .ctrl_regs = {
+ AK8975_REG_ST1,
+ AK8975_REG_ST2,
+ AK8975_REG_CNTL,
+ AK8975_REG_ASAX,
+ AK8975_MAX_REGS},
+ .ctrl_masks = {
+ AK8975_REG_ST1_DRDY_MASK,
+ AK8975_REG_ST2_HOFL_MASK,
+ AK8975_REG_ST2_DERR_MASK,
+ AK8975_REG_CNTL_MODE_MASK},
+ .ctrl_modes = {
+ AK8975_REG_CNTL_MODE_POWER_DOWN,
+ AK8975_REG_CNTL_MODE_ONCE,
+ AK8975_REG_CNTL_MODE_SELF_TEST,
+ AK8975_REG_CNTL_MODE_FUSE_ROM},
+ .data_regs = {
+ AK8975_REG_HXL,
+ AK8975_REG_HYL,
+ AK8975_REG_HZL},
+ },
+ {
+ .type = AK8963,
+ .raw_to_gauss = ak8963_09911_raw_to_gauss,
+ .range = 8190,
+ .ctrl_regs = {
+ AK8975_REG_ST1,
+ AK8975_REG_ST2,
+ AK8975_REG_CNTL,
+ AK8975_REG_ASAX,
+ AK8975_MAX_REGS},
+ .ctrl_masks = {
+ AK8975_REG_ST1_DRDY_MASK,
+ AK8975_REG_ST2_HOFL_MASK,
+ 0,
+ AK8975_REG_CNTL_MODE_MASK},
+ .ctrl_modes = {
+ AK8975_REG_CNTL_MODE_POWER_DOWN,
+ AK8975_REG_CNTL_MODE_ONCE,
+ AK8975_REG_CNTL_MODE_SELF_TEST,
+ AK8975_REG_CNTL_MODE_FUSE_ROM},
+ .data_regs = {
+ AK8975_REG_HXL,
+ AK8975_REG_HYL,
+ AK8975_REG_HZL},
+ },
+ {
+ .type = AK09911,
+ .raw_to_gauss = ak8963_09911_raw_to_gauss,
+ .range = 8192,
+ .ctrl_regs = {
+ AK09912_REG_ST1,
+ AK09912_REG_ST2,
+ AK09912_REG_CNTL2,
+ AK09912_REG_ASAX,
+ AK09912_MAX_REGS},
+ .ctrl_masks = {
+ AK09912_REG_ST1_DRDY_MASK,
+ AK09912_REG_ST2_HOFL_MASK,
+ 0,
+ AK09912_REG_CNTL2_MODE_MASK},
+ .ctrl_modes = {
+ AK09912_REG_CNTL_MODE_POWER_DOWN,
+ AK09912_REG_CNTL_MODE_ONCE,
+ AK09912_REG_CNTL_MODE_SELF_TEST,
+ AK09912_REG_CNTL_MODE_FUSE_ROM},
+ .data_regs = {
+ AK09912_REG_HXL,
+ AK09912_REG_HYL,
+ AK09912_REG_HZL},
+ },
+ {
+ .type = AK09912,
+ .raw_to_gauss = ak09912_raw_to_gauss,
+ .range = 32752,
+ .ctrl_regs = {
+ AK09912_REG_ST1,
+ AK09912_REG_ST2,
+ AK09912_REG_CNTL2,
+ AK09912_REG_ASAX,
+ AK09912_MAX_REGS},
+ .ctrl_masks = {
+ AK09912_REG_ST1_DRDY_MASK,
+ AK09912_REG_ST2_HOFL_MASK,
+ 0,
+ AK09912_REG_CNTL2_MODE_MASK},
+ .ctrl_modes = {
+ AK09912_REG_CNTL_MODE_POWER_DOWN,
+ AK09912_REG_CNTL_MODE_ONCE,
+ AK09912_REG_CNTL_MODE_SELF_TEST,
+ AK09912_REG_CNTL_MODE_FUSE_ROM},
+ .data_regs = {
+ AK09912_REG_HXL,
+ AK09912_REG_HYL,
+ AK09912_REG_HZL},
+ }
};
/*
@@ -100,40 +360,82 @@ enum asahi_compass_chipset {
*/
struct ak8975_data {
struct i2c_client *client;
+ struct ak_def *def;
struct attribute_group attrs;
struct mutex lock;
u8 asa[3];
long raw_to_gauss[3];
- u8 reg_cache[AK8975_MAX_REGS];
int eoc_gpio;
int eoc_irq;
wait_queue_head_t data_ready_queue;
unsigned long flags;
- enum asahi_compass_chipset chipset;
+ u8 cntl_cache;
};
-static const int ak8975_index_to_reg[] = {
- AK8975_REG_HXL, AK8975_REG_HYL, AK8975_REG_HZL,
-};
+/*
+ * Return 0 if the i2c device is the one we expect.
+ * return a negative error number otherwise
+ */
+static int ak8975_who_i_am(struct i2c_client *client,
+ enum asahi_compass_chipset type)
+{
+ u8 wia_val[2];
+ int ret;
+
+ /*
+ * Signature for each device:
+ * Device | WIA1 | WIA2
+ * AK09912 | DEVICE_ID | AK09912_DEVICE_ID
+ * AK09911 | DEVICE_ID | AK09911_DEVICE_ID
+ * AK8975 | DEVICE_ID | NA
+ * AK8963 | DEVICE_ID | NA
+ */
+ ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1,
+ 2, wia_val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading WIA\n");
+ return ret;
+ }
+
+ if (wia_val[0] != AK8975_DEVICE_ID)
+ return -ENODEV;
+
+ switch (type) {
+ case AK8975:
+ case AK8963:
+ return 0;
+ case AK09911:
+ if (wia_val[1] == AK09911_DEVICE_ID)
+ return 0;
+ break;
+ case AK09912:
+ if (wia_val[1] == AK09912_DEVICE_ID)
+ return 0;
+ break;
+ default:
+ dev_err(&client->dev, "Type %d unknown\n", type);
+ }
+ return -ENODEV;
+}
/*
- * Helper function to write to the I2C device's registers.
+ * Helper function to write to CNTL register.
*/
-static int ak8975_write_data(struct i2c_client *client,
- u8 reg, u8 val, u8 mask, u8 shift)
+static int ak8975_set_mode(struct ak8975_data *data, enum ak_ctrl_mode mode)
{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct ak8975_data *data = iio_priv(indio_dev);
u8 regval;
int ret;
- regval = (data->reg_cache[reg] & ~mask) | (val << shift);
- ret = i2c_smbus_write_byte_data(client, reg, regval);
+ regval = (data->cntl_cache & ~data->def->ctrl_masks[CNTL_MODE]) |
+ data->def->ctrl_modes[mode];
+ ret = i2c_smbus_write_byte_data(data->client,
+ data->def->ctrl_regs[CNTL], regval);
if (ret < 0) {
- dev_err(&client->dev, "Write to device fails status %x\n", ret);
return ret;
}
- data->reg_cache[reg] = regval;
+ data->cntl_cache = regval;
+ /* After mode change wait atleast 100us */
+ usleep_range(100, 500);
return 0;
}
@@ -166,8 +468,8 @@ static int ak8975_setup_irq(struct ak8975_data *data)
irq = gpio_to_irq(data->eoc_gpio);
rc = devm_request_irq(&client->dev, irq, ak8975_irq_handler,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT,
- dev_name(&client->dev), data);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&client->dev), data);
if (rc < 0) {
dev_err(&client->dev,
"irq %d request failed, (gpio %d): %d\n",
@@ -191,34 +493,18 @@ static int ak8975_setup(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct ak8975_data *data = iio_priv(indio_dev);
- u8 device_id;
int ret;
- /* Confirm that the device we're talking to is really an AK8975. */
- ret = i2c_smbus_read_byte_data(client, AK8975_REG_WIA);
- if (ret < 0) {
- dev_err(&client->dev, "Error reading WIA\n");
- return ret;
- }
- device_id = ret;
- if (device_id != AK8975_DEVICE_ID) {
- dev_err(&client->dev, "Device ak8975 not found\n");
- return -ENODEV;
- }
-
/* Write the fused rom access mode. */
- ret = ak8975_write_data(client,
- AK8975_REG_CNTL,
- AK8975_REG_CNTL_MODE_FUSE_ROM,
- AK8975_REG_CNTL_MODE_MASK,
- AK8975_REG_CNTL_MODE_SHIFT);
+ ret = ak8975_set_mode(data, FUSE_ROM);
if (ret < 0) {
dev_err(&client->dev, "Error in setting fuse access mode\n");
return ret;
}
/* Get asa data and store in the device data. */
- ret = i2c_smbus_read_i2c_block_data(client, AK8975_REG_ASAX,
+ ret = i2c_smbus_read_i2c_block_data(client,
+ data->def->ctrl_regs[ASA_BASE],
3, data->asa);
if (ret < 0) {
dev_err(&client->dev, "Not able to read asa data\n");
@@ -226,13 +512,13 @@ static int ak8975_setup(struct i2c_client *client)
}
/* After reading fuse ROM data set power-down mode */
- ret = ak8975_write_data(client,
- AK8975_REG_CNTL,
- AK8975_REG_CNTL_MODE_POWER_DOWN,
- AK8975_REG_CNTL_MODE_MASK,
- AK8975_REG_CNTL_MODE_SHIFT);
+ ret = ak8975_set_mode(data, POWER_DOWN);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error in setting power-down mode\n");
+ return ret;
+ }
- if (data->eoc_gpio > 0 || client->irq) {
+ if (data->eoc_gpio > 0 || client->irq > 0) {
ret = ak8975_setup_irq(data);
if (ret < 0) {
dev_err(&client->dev,
@@ -241,61 +527,9 @@ static int ak8975_setup(struct i2c_client *client)
}
}
- if (ret < 0) {
- dev_err(&client->dev, "Error in setting power-down mode\n");
- return ret;
- }
-
-/*
- * Precalculate scale factor (in Gauss units) for each axis and
- * store in the device data.
- *
- * This scale factor is axis-dependent, and is derived from 3 calibration
- * factors ASA(x), ASA(y), and ASA(z).
- *
- * These ASA values are read from the sensor device at start of day, and
- * cached in the device context struct.
- *
- * Adjusting the flux value with the sensitivity adjustment value should be
- * done via the following formula:
- *
- * Hadj = H * ( ( ( (ASA-128)*0.5 ) / 128 ) + 1 )
- *
- * where H is the raw value, ASA is the sensitivity adjustment, and Hadj
- * is the resultant adjusted value.
- *
- * We reduce the formula to:
- *
- * Hadj = H * (ASA + 128) / 256
- *
- * H is in the range of -4096 to 4095. The magnetometer has a range of
- * +-1229uT. To go from the raw value to uT is:
- *
- * HuT = H * 1229/4096, or roughly, 3/10.
- *
- * Since 1uT = 0.01 gauss, our final scale factor becomes:
- *
- * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100
- * Hadj = H * ((ASA + 128) * 0.003) / 256
- *
- * Since ASA doesn't change, we cache the resultant scale factor into the
- * device context in ak8975_setup().
- */
- if (data->chipset == AK8963) {
- /*
- * H range is +-8190 and magnetometer range is +-4912.
- * So HuT using the above explanation for 8975,
- * 4912/8190 = ~ 6/10.
- * So the Hadj should use 6/10 instead of 3/10.
- */
- data->raw_to_gauss[0] = RAW_TO_GAUSS_8963(data->asa[0]);
- data->raw_to_gauss[1] = RAW_TO_GAUSS_8963(data->asa[1]);
- data->raw_to_gauss[2] = RAW_TO_GAUSS_8963(data->asa[2]);
- } else {
- data->raw_to_gauss[0] = RAW_TO_GAUSS_8975(data->asa[0]);
- data->raw_to_gauss[1] = RAW_TO_GAUSS_8975(data->asa[1]);
- data->raw_to_gauss[2] = RAW_TO_GAUSS_8975(data->asa[2]);
- }
+ data->raw_to_gauss[0] = data->def->raw_to_gauss(data->asa[0]);
+ data->raw_to_gauss[1] = data->def->raw_to_gauss(data->asa[1]);
+ data->raw_to_gauss[2] = data->def->raw_to_gauss(data->asa[2]);
return 0;
}
@@ -318,7 +552,7 @@ static int wait_conversion_complete_gpio(struct ak8975_data *data)
return -EINVAL;
}
- ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
+ ret = i2c_smbus_read_byte_data(client, data->def->ctrl_regs[ST1]);
if (ret < 0)
dev_err(&client->dev, "Error in reading ST1\n");
@@ -335,7 +569,8 @@ static int wait_conversion_complete_polled(struct ak8975_data *data)
/* Wait for the conversion to complete. */
while (timeout_ms) {
msleep(AK8975_CONVERSION_DONE_POLL_TIME);
- ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST1);
+ ret = i2c_smbus_read_byte_data(client,
+ data->def->ctrl_regs[ST1]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST1\n");
return ret;
@@ -378,11 +613,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_lock(&data->lock);
/* Set up the device for taking a sample. */
- ret = ak8975_write_data(client,
- AK8975_REG_CNTL,
- AK8975_REG_CNTL_MODE_ONCE,
- AK8975_REG_CNTL_MODE_MASK,
- AK8975_REG_CNTL_MODE_SHIFT);
+ ret = ak8975_set_mode(data, MODE_ONCE);
if (ret < 0) {
dev_err(&client->dev, "Error in setting operating mode\n");
goto exit;
@@ -399,14 +630,15 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
goto exit;
/* This will be executed only for non-interrupt based waiting case */
- if (ret & AK8975_REG_ST1_DRDY_MASK) {
- ret = i2c_smbus_read_byte_data(client, AK8975_REG_ST2);
+ if (ret & data->def->ctrl_masks[ST1_DRDY]) {
+ ret = i2c_smbus_read_byte_data(client,
+ data->def->ctrl_regs[ST2]);
if (ret < 0) {
dev_err(&client->dev, "Error in reading ST2\n");
goto exit;
}
- if (ret & (AK8975_REG_ST2_DERR_MASK |
- AK8975_REG_ST2_HOFL_MASK)) {
+ if (ret & (data->def->ctrl_masks[ST2_DERR] |
+ data->def->ctrl_masks[ST2_HOFL])) {
dev_err(&client->dev, "ST2 status error 0x%x\n", ret);
ret = -EINVAL;
goto exit;
@@ -415,7 +647,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
/* Read the flux value from the appropriate register
(the register is specified in the iio device attributes). */
- ret = i2c_smbus_read_word_data(client, ak8975_index_to_reg[index]);
+ ret = i2c_smbus_read_word_data(client, data->def->data_regs[index]);
if (ret < 0) {
dev_err(&client->dev, "Read axis data fails\n");
goto exit;
@@ -424,7 +656,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
mutex_unlock(&data->lock);
/* Clamp to valid range. */
- *val = clamp_t(s16, ret, -4096, 4095);
+ *val = clamp_t(s16, ret, -data->def->range, data->def->range);
return IIO_VAL_INT;
exit:
@@ -473,6 +705,8 @@ static const struct acpi_device_id ak_acpi_match[] = {
{"AK8975", AK8975},
{"AK8963", AK8963},
{"INVN6500", AK8963},
+ {"AK09911", AK09911},
+ {"AK09912", AK09912},
{ },
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
@@ -498,6 +732,7 @@ static int ak8975_probe(struct i2c_client *client,
int eoc_gpio;
int err;
const char *name = NULL;
+ enum asahi_compass_chipset chipset;
/* Grab and set up the supplied GPIO. */
if (client->dev.platform_data)
@@ -537,42 +772,50 @@ static int ak8975_probe(struct i2c_client *client,
/* id will be NULL when enumerated via ACPI */
if (id) {
- data->chipset =
- (enum asahi_compass_chipset)(id->driver_data);
+ chipset = (enum asahi_compass_chipset)(id->driver_data);
name = id->name;
} else if (ACPI_HANDLE(&client->dev))
- name = ak8975_match_acpi_device(&client->dev, &data->chipset);
+ name = ak8975_match_acpi_device(&client->dev, &chipset);
else
return -ENOSYS;
+ if (chipset >= AK_MAX_TYPE) {
+ dev_err(&client->dev, "AKM device type unsupported: %d\n",
+ chipset);
+ return -ENODEV;
+ }
+
+ data->def = &ak_def_array[chipset];
+ err = ak8975_who_i_am(client, data->def->type);
+ if (err < 0) {
+ dev_err(&client->dev, "Unexpected device\n");
+ return err;
+ }
dev_dbg(&client->dev, "Asahi compass chip %s\n", name);
/* Perform some basic start-of-day setup of the device. */
err = ak8975_setup(client);
if (err < 0) {
- dev_err(&client->dev, "AK8975 initialization fails\n");
+ dev_err(&client->dev, "%s initialization fails\n", name);
return err;
}
- data->client = client;
mutex_init(&data->lock);
- data->eoc_gpio = eoc_gpio;
indio_dev->dev.parent = &client->dev;
indio_dev->channels = ak8975_channels;
indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);
indio_dev->info = &ak8975_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = name;
- err = devm_iio_device_register(&client->dev, indio_dev);
- if (err < 0)
- return err;
-
- return 0;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id ak8975_id[] = {
{"ak8975", AK8975},
{"ak8963", AK8963},
+ {"AK8963", AK8963},
+ {"ak09911", AK09911},
+ {"ak09912", AK09912},
{}
};
@@ -581,14 +824,20 @@ MODULE_DEVICE_TABLE(i2c, ak8975_id);
static const struct of_device_id ak8975_of_match[] = {
{ .compatible = "asahi-kasei,ak8975", },
{ .compatible = "ak8975", },
- { }
+ { .compatible = "asahi-kasei,ak8963", },
+ { .compatible = "ak8963", },
+ { .compatible = "asahi-kasei,ak09911", },
+ { .compatible = "ak09911", },
+ { .compatible = "asahi-kasei,ak09912", },
+ { .compatible = "ak09912", },
+ {}
};
MODULE_DEVICE_TABLE(of, ak8975_of_match);
static struct i2c_driver ak8975_driver = {
.driver = {
.name = "ak8975",
- .of_match_table = ak8975_of_match,
+ .of_match_table = of_match_ptr(ak8975_of_match),
.acpi_match_table = ACPI_PTR(ak_acpi_match),
},
.probe = ak8975_probe,
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index 6294575..4f9c0be 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -157,20 +157,12 @@ static int magn_3d_read_raw(struct iio_dev *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(
- &magn_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&magn_state->common_attributes, true);
- msleep_interruptible(poll_value * 2);
-
report_id =
magn_state->magn[chan->address].report_id;
address = magn_3d_addresses[chan->address];
@@ -178,7 +170,8 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
magn_state->common_attributes.hsdev,
HID_USAGE_SENSOR_COMPASS_3D, address,
- report_id);
+ report_id,
+ SENSOR_HUB_SYNC);
else {
*val = 0;
hid_sensor_power_state(&magn_state->common_attributes,
@@ -530,6 +523,7 @@ static struct platform_driver hid_magn_3d_platform_driver = {
.id_table = hid_magn_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_magn_3d_probe,
.remove = hid_magn_3d_remove,
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
index e3106b4..261d517 100644
--- a/drivers/iio/magnetometer/mag3110.c
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -321,6 +321,12 @@ static const struct iio_info mag3110_info = {
static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0};
+static int mag3110_standby(struct mag3110_data *data)
+{
+ return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
+ data->ctrl_reg1 & ~MAG3110_CTRL_AC);
+}
+
static int mag3110_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -360,12 +366,12 @@ static int mag3110_probe(struct i2c_client *client,
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
MAG3110_CTRL_AUTO_MRST_EN);
if (ret < 0)
- return ret;
+ goto standby_on_error;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
mag3110_trigger_handler, NULL);
if (ret < 0)
- return ret;
+ goto standby_on_error;
ret = iio_device_register(indio_dev);
if (ret < 0)
@@ -374,15 +380,11 @@ static int mag3110_probe(struct i2c_client *client,
buffer_cleanup:
iio_triggered_buffer_cleanup(indio_dev);
+standby_on_error:
+ mag3110_standby(iio_priv(indio_dev));
return ret;
}
-static int mag3110_standby(struct mag3110_data *data)
-{
- return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
- data->ctrl_reg1 & ~MAG3110_CTRL_AC);
-}
-
static int mag3110_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
index 694e33e..7e81d00 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -18,8 +18,7 @@
#define LSM303DLM_MAGN_DEV_NAME "lsm303dlm_magn"
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
-int st_magn_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *pdata);
+int st_magn_common_probe(struct iio_dev *indio_dev);
void st_magn_common_remove(struct iio_dev *indio_dev);
#ifdef CONFIG_IIO_BUFFER
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 68cae86..8ade473 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -149,7 +149,7 @@ static const struct iio_chan_spec st_magn_2_16bit_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3)
};
-static const struct st_sensors st_magn_sensors[] = {
+static const struct st_sensor_settings st_magn_sensors_settings[] = {
{
.wai = ST_MAGN_1_WAI_EXP,
.sensors_supported = {
@@ -361,8 +361,7 @@ static const struct iio_info magn_info = {
.write_raw = &st_magn_write_raw,
};
-int st_magn_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *pdata)
+int st_magn_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *mdata = iio_priv(indio_dev);
int irq = mdata->get_irq_data_ready(indio_dev);
@@ -374,20 +373,21 @@ int st_magn_common_probe(struct iio_dev *indio_dev,
st_sensors_power_enable(indio_dev);
err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_magn_sensors), st_magn_sensors);
+ ARRAY_SIZE(st_magn_sensors_settings),
+ st_magn_sensors_settings);
if (err < 0)
return err;
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
- mdata->multiread_bit = mdata->sensor->multi_read_bit;
- indio_dev->channels = mdata->sensor->ch;
+ mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
+ indio_dev->channels = mdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
mdata->current_fullscale = (struct st_sensor_fullscale_avl *)
- &mdata->sensor->fs.fs_avl[0];
- mdata->odr = mdata->sensor->odr.odr_avl[0].hz;
+ &mdata->sensor_settings->fs.fs_avl[0];
+ mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz;
- err = st_sensors_init_sensor(indio_dev, pdata);
+ err = st_sensors_init_sensor(indio_dev, NULL);
if (err < 0)
return err;
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index 6892500..92e5c15 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -51,12 +51,11 @@ static int st_magn_i2c_probe(struct i2c_client *client,
return -ENOMEM;
mdata = iio_priv(indio_dev);
- mdata->dev = &client->dev;
st_sensors_of_i2c_probe(client, st_magn_of_match);
st_sensors_i2c_configure(indio_dev, client, mdata);
- err = st_magn_common_probe(indio_dev, NULL);
+ err = st_magn_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index a6143ea..7adacf1 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -29,11 +29,10 @@ static int st_magn_spi_probe(struct spi_device *spi)
return -ENOMEM;
mdata = iio_priv(indio_dev);
- mdata->dev = &spi->dev;
st_sensors_spi_configure(indio_dev, spi, mdata);
- err = st_magn_common_probe(indio_dev, NULL);
+ err = st_magn_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index 1ff181b..5930fa3 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -111,20 +111,12 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- poll_value = hid_sensor_read_poll_value(
- &incl_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
-
hid_sensor_power_state(&incl_state->common_attributes, true);
- msleep_interruptible(poll_value * 2);
-
report_id =
incl_state->incl[chan->scan_index].report_id;
address = incl_3d_addresses[chan->scan_index];
@@ -132,7 +124,8 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
*val = sensor_hub_input_attr_get_raw_value(
incl_state->common_attributes.hsdev,
HID_USAGE_SENSOR_INCLINOMETER_3D, address,
- report_id);
+ report_id,
+ SENSOR_HUB_SYNC);
else {
hid_sensor_power_state(&incl_state->common_attributes,
false);
@@ -437,6 +430,7 @@ static struct platform_driver hid_incl_3d_platform_driver = {
.id_table = hid_incl_3d_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_incl_3d_probe,
.remove = hid_incl_3d_remove,
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 15afbc9..fa62950 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -5,6 +5,17 @@
menu "Pressure sensors"
+config BMP280
+ tristate "Bosch Sensortec BMP280 pressure sensor driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to build support for Bosch Sensortec BMP280
+ pressure and temperature sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called bmp280.
+
config HID_SENSOR_PRESS
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -41,6 +52,33 @@ config MPL3115
To compile this driver as a module, choose M here: the module
will be called mpl3115.
+config MS5611
+ tristate "Measurement Specialities MS5611 pressure sensor driver"
+ help
+ Say Y here to build support for the Measurement Specialities
+ MS5611 pressure and temperature sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_core.
+
+config MS5611_I2C
+ tristate "support I2C bus connection"
+ depends on I2C && MS5611
+ help
+ Say Y here to build I2C bus support for MS5611.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_i2c.
+
+config MS5611_SPI
+ tristate "support SPI bus connection"
+ depends on SPI_MASTER && MS5611
+ help
+ Say Y here to build SPI bus support for MS5611.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_spi.
+
config IIO_ST_PRESS
tristate "STMicroelectronics pressure sensor Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index 90a37e8..a4f98f8 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -3,9 +3,13 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_BMP280) += bmp280.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_MPL115) += mpl115.o
obj-$(CONFIG_MPL3115) += mpl3115.o
+obj-$(CONFIG_MS5611) += ms5611_core.o
+obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
+obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o
obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
st_pressure-y := st_pressure_core.o
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
diff --git a/drivers/iio/pressure/bmp280.c b/drivers/iio/pressure/bmp280.c
new file mode 100644
index 0000000..7c623e2
--- /dev/null
+++ b/drivers/iio/pressure/bmp280.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Driver for Bosch Sensortec BMP280 digital pressure sensor.
+ *
+ * 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.
+ *
+ */
+
+#define pr_fmt(fmt) "bmp280: " fmt
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/acpi.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define BMP280_REG_TEMP_XLSB 0xFC
+#define BMP280_REG_TEMP_LSB 0xFB
+#define BMP280_REG_TEMP_MSB 0xFA
+#define BMP280_REG_PRESS_XLSB 0xF9
+#define BMP280_REG_PRESS_LSB 0xF8
+#define BMP280_REG_PRESS_MSB 0xF7
+
+#define BMP280_REG_CONFIG 0xF5
+#define BMP280_REG_CTRL_MEAS 0xF4
+#define BMP280_REG_STATUS 0xF3
+#define BMP280_REG_RESET 0xE0
+#define BMP280_REG_ID 0xD0
+
+#define BMP280_REG_COMP_TEMP_START 0x88
+#define BMP280_COMP_TEMP_REG_COUNT 6
+
+#define BMP280_REG_COMP_PRESS_START 0x8E
+#define BMP280_COMP_PRESS_REG_COUNT 18
+
+#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2))
+#define BMP280_FILTER_OFF 0
+#define BMP280_FILTER_2X BIT(2)
+#define BMP280_FILTER_4X BIT(3)
+#define BMP280_FILTER_8X (BIT(3) | BIT(2))
+#define BMP280_FILTER_16X BIT(4)
+
+#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5))
+#define BMP280_OSRS_TEMP_SKIP 0
+#define BMP280_OSRS_TEMP_1X BIT(5)
+#define BMP280_OSRS_TEMP_2X BIT(6)
+#define BMP280_OSRS_TEMP_4X (BIT(6) | BIT(5))
+#define BMP280_OSRS_TEMP_8X BIT(7)
+#define BMP280_OSRS_TEMP_16X (BIT(7) | BIT(5))
+
+#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2))
+#define BMP280_OSRS_PRESS_SKIP 0
+#define BMP280_OSRS_PRESS_1X BIT(2)
+#define BMP280_OSRS_PRESS_2X BIT(3)
+#define BMP280_OSRS_PRESS_4X (BIT(3) | BIT(2))
+#define BMP280_OSRS_PRESS_8X BIT(4)
+#define BMP280_OSRS_PRESS_16X (BIT(4) | BIT(2))
+
+#define BMP280_MODE_MASK (BIT(1) | BIT(0))
+#define BMP280_MODE_SLEEP 0
+#define BMP280_MODE_FORCED BIT(0)
+#define BMP280_MODE_NORMAL (BIT(1) | BIT(0))
+
+#define BMP280_CHIP_ID 0x58
+#define BMP280_SOFT_RESET_VAL 0xB6
+
+struct bmp280_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct regmap *regmap;
+
+ /*
+ * Carryover value from temperature conversion, used in pressure
+ * calculation.
+ */
+ s32 t_fine;
+};
+
+/*
+ * These enums are used for indexing into the array of compensation
+ * parameters.
+ */
+enum { T1, T2, T3 };
+enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 };
+
+static const struct iio_chan_spec bmp280_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_CONFIG:
+ case BMP280_REG_CTRL_MEAS:
+ case BMP280_REG_RESET:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_TEMP_XLSB:
+ case BMP280_REG_TEMP_LSB:
+ case BMP280_REG_TEMP_MSB:
+ case BMP280_REG_PRESS_XLSB:
+ case BMP280_REG_PRESS_LSB:
+ case BMP280_REG_PRESS_MSB:
+ case BMP280_REG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config bmp280_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP280_REG_TEMP_XLSB,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp280_is_writeable_reg,
+ .volatile_reg = bmp280_is_volatile_reg,
+};
+
+/*
+ * Returns temperature in DegC, resolution is 0.01 DegC. Output value of
+ * "5123" equals 51.23 DegC. t_fine carries fine temperature as global
+ * value.
+ *
+ * Taken from datasheet, Section 3.11.3, "Compensation formula".
+ */
+static s32 bmp280_compensate_temp(struct bmp280_data *data,
+ s32 adc_temp)
+{
+ int ret;
+ s32 var1, var2;
+ __le16 buf[BMP280_COMP_TEMP_REG_COUNT / 2];
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
+ buf, BMP280_COMP_TEMP_REG_COUNT);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to read temperature calibration parameters\n");
+ return ret;
+ }
+
+ /*
+ * The double casts are necessary because le16_to_cpu returns an
+ * unsigned 16-bit value. Casting that value directly to a
+ * signed 32-bit will not do proper sign extension.
+ *
+ * Conversely, T1 and P1 are unsigned values, so they can be
+ * cast straight to the larger type.
+ */
+ var1 = (((adc_temp >> 3) - ((s32)le16_to_cpu(buf[T1]) << 1)) *
+ ((s32)(s16)le16_to_cpu(buf[T2]))) >> 11;
+ var2 = (((((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1]))) *
+ ((adc_temp >> 4) - ((s32)le16_to_cpu(buf[T1])))) >> 12) *
+ ((s32)(s16)le16_to_cpu(buf[T3]))) >> 14;
+
+ return (data->t_fine * 5 + 128) >> 8;
+}
+
+/*
+ * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
+ * integer bits and 8 fractional bits). Output value of "24674867"
+ * represents 24674867/256 = 96386.2 Pa = 963.862 hPa
+ *
+ * Taken from datasheet, Section 3.11.3, "Compensation formula".
+ */
+static u32 bmp280_compensate_press(struct bmp280_data *data,
+ s32 adc_press)
+{
+ int ret;
+ s64 var1, var2, p;
+ __le16 buf[BMP280_COMP_PRESS_REG_COUNT / 2];
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
+ buf, BMP280_COMP_PRESS_REG_COUNT);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to read pressure calibration parameters\n");
+ return ret;
+ }
+
+ var1 = ((s64)data->t_fine) - 128000;
+ var2 = var1 * var1 * (s64)(s16)le16_to_cpu(buf[P6]);
+ var2 += (var1 * (s64)(s16)le16_to_cpu(buf[P5])) << 17;
+ var2 += ((s64)(s16)le16_to_cpu(buf[P4])) << 35;
+ var1 = ((var1 * var1 * (s64)(s16)le16_to_cpu(buf[P3])) >> 8) +
+ ((var1 * (s64)(s16)le16_to_cpu(buf[P2])) << 12);
+ var1 = ((((s64)1) << 47) + var1) * ((s64)le16_to_cpu(buf[P1])) >> 33;
+
+ if (var1 == 0)
+ return 0;
+
+ p = ((((s64)1048576 - adc_press) << 31) - var2) * 3125;
+ p = div64_s64(p, var1);
+ var1 = (((s64)(s16)le16_to_cpu(buf[P9])) * (p >> 13) * (p >> 13)) >> 25;
+ var2 = (((s64)(s16)le16_to_cpu(buf[P8])) * p) >> 19;
+ p = ((p + var1 + var2) >> 8) + (((s64)(s16)le16_to_cpu(buf[P7])) << 4);
+
+ return (u32)p;
+}
+
+static int bmp280_read_temp(struct bmp280_data *data,
+ int *val)
+{
+ int ret;
+ __be32 tmp = 0;
+ s32 adc_temp, comp_temp;
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
+ (u8 *) &tmp, 3);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ adc_temp = be32_to_cpu(tmp) >> 12;
+ comp_temp = bmp280_compensate_temp(data, adc_temp);
+
+ /*
+ * val might be NULL if we're called by the read_press routine,
+ * who only cares about the carry over t_fine value.
+ */
+ if (val) {
+ *val = comp_temp * 10;
+ return IIO_VAL_INT;
+ }
+
+ return 0;
+}
+
+static int bmp280_read_press(struct bmp280_data *data,
+ int *val, int *val2)
+{
+ int ret;
+ __be32 tmp = 0;
+ s32 adc_press;
+ u32 comp_press;
+
+ /* Read and compensate temperature so we get a reading of t_fine. */
+ ret = bmp280_read_temp(data, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
+ (u8 *) &tmp, 3);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ adc_press = be32_to_cpu(tmp) >> 12;
+ comp_press = bmp280_compensate_press(data, adc_press);
+
+ *val = comp_press;
+ *val2 = 256000;
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static int bmp280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ struct bmp280_data *data = iio_priv(indio_dev);
+
+ mutex_lock(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ ret = bmp280_read_press(data, val, val2);
+ break;
+ case IIO_TEMP:
+ ret = bmp280_read_temp(data, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static const struct iio_info bmp280_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &bmp280_read_raw,
+};
+
+static int bmp280_chip_init(struct bmp280_data *data)
+{
+ int ret;
+
+ ret = regmap_update_bits(data->regmap, BMP280_REG_CTRL_MEAS,
+ BMP280_OSRS_TEMP_MASK |
+ BMP280_OSRS_PRESS_MASK |
+ BMP280_MODE_MASK,
+ BMP280_OSRS_TEMP_2X |
+ BMP280_OSRS_PRESS_16X |
+ BMP280_MODE_NORMAL);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to write ctrl_meas register\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMP280_REG_CONFIG,
+ BMP280_FILTER_MASK,
+ BMP280_FILTER_4X);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "failed to write config register\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int bmp280_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct bmp280_data *data;
+ unsigned int chip_id;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ mutex_init(&data->lock);
+ data->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = bmp280_channels;
+ indio_dev->num_channels = ARRAY_SIZE(bmp280_channels);
+ indio_dev->info = &bmp280_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data->regmap = devm_regmap_init_i2c(client, &bmp280_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ ret = regmap_read(data->regmap, BMP280_REG_ID, &chip_id);
+ if (ret < 0)
+ return ret;
+ if (chip_id != BMP280_CHIP_ID) {
+ dev_err(&client->dev, "bad chip id. expected %x got %x\n",
+ BMP280_CHIP_ID, chip_id);
+ return -EINVAL;
+ }
+
+ ret = bmp280_chip_init(data);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct acpi_device_id bmp280_acpi_match[] = {
+ {"BMP0280", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, bmp280_acpi_match);
+
+static const struct i2c_device_id bmp280_id[] = {
+ {"bmp280", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, bmp280_id);
+
+static struct i2c_driver bmp280_driver = {
+ .driver = {
+ .name = "bmp280",
+ .acpi_match_table = ACPI_PTR(bmp280_acpi_match),
+ },
+ .probe = bmp280_probe,
+ .id_table = bmp280_id,
+};
+module_i2c_driver(bmp280_driver);
+
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP280 pressure and temperature sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 7649286..7bb8d4c 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -79,7 +79,6 @@ static int press_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
- s32 poll_value;
*val = 0;
*val2 = 0;
@@ -96,19 +95,13 @@ static int press_read_raw(struct iio_dev *indio_dev,
break;
}
if (report_id >= 0) {
- poll_value = hid_sensor_read_poll_value(
- &press_state->common_attributes);
- if (poll_value < 0)
- return -EINVAL;
hid_sensor_power_state(&press_state->common_attributes,
true);
-
- msleep_interruptible(poll_value * 2);
-
*val = sensor_hub_input_attr_get_raw_value(
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
- report_id);
+ report_id,
+ SENSOR_HUB_SYNC);
hid_sensor_power_state(&press_state->common_attributes,
false);
} else {
@@ -382,6 +375,7 @@ static struct platform_driver hid_press_platform_driver = {
.id_table = hid_press_ids,
.driver = {
.name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
},
.probe = hid_press_probe,
.remove = hid_press_remove,
diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
new file mode 100644
index 0000000..099c6cd
--- /dev/null
+++ b/drivers/iio/pressure/ms5611.h
@@ -0,0 +1,44 @@
+/*
+ * MS5611 pressure and temperature sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.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.
+ *
+ */
+
+#ifndef _MS5611_H
+#define _MS5611_H
+
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+
+#define MS5611_RESET 0x1e
+#define MS5611_READ_ADC 0x00
+#define MS5611_READ_PROM_WORD 0xA0
+#define MS5611_START_TEMP_CONV 0x58
+#define MS5611_START_PRESSURE_CONV 0x48
+
+#define MS5611_CONV_TIME_MIN 9040
+#define MS5611_CONV_TIME_MAX 10000
+
+#define MS5611_PROM_WORDS_NB 8
+
+struct ms5611_state {
+ void *client;
+ struct mutex lock;
+
+ int (*reset)(struct device *dev);
+ int (*read_prom_word)(struct device *dev, int index, u16 *word);
+ int (*read_adc_temp_and_pressure)(struct device *dev,
+ s32 *temp, s32 *pressure);
+
+ u16 prom[MS5611_PROM_WORDS_NB];
+};
+
+int ms5611_probe(struct iio_dev *indio_dev, struct device *dev);
+
+#endif /* _MS5611_H */
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
new file mode 100644
index 0000000..e42c853
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -0,0 +1,215 @@
+/*
+ * MS5611 pressure and temperature sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.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.
+ *
+ * Data sheet:
+ * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/delay.h>
+
+#include "ms5611.h"
+
+static bool ms5611_prom_is_valid(u16 *prom, size_t len)
+{
+ int i, j;
+ uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
+
+ prom[7] &= 0xFF00;
+
+ for (i = 0; i < len * 2; i++) {
+ if (i % 2 == 1)
+ crc ^= prom[i >> 1] & 0x00FF;
+ else
+ crc ^= prom[i >> 1] >> 8;
+
+ for (j = 0; j < 8; j++) {
+ if (crc & 0x8000)
+ crc = (crc << 1) ^ 0x3000;
+ else
+ crc <<= 1;
+ }
+ }
+
+ crc = (crc >> 12) & 0x000F;
+
+ return crc_orig != 0x0000 && crc == crc_orig;
+}
+
+static int ms5611_read_prom(struct iio_dev *indio_dev)
+{
+ int ret, i;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
+ ret = st->read_prom_word(&indio_dev->dev, i, &st->prom[i]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to read prom at %d\n", i);
+ return ret;
+ }
+ }
+
+ if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
+ dev_err(&indio_dev->dev, "PROM integrity check failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
+ s32 *temp, s32 *pressure)
+{
+ int ret;
+ s32 t, p;
+ s64 off, sens, dt;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ ret = st->read_adc_temp_and_pressure(&indio_dev->dev, &t, &p);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to read temperature and pressure\n");
+ return ret;
+ }
+
+ dt = t - (st->prom[5] << 8);
+ off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
+ sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
+
+ t = 2000 + ((st->prom[6] * dt) >> 23);
+ if (t < 2000) {
+ s64 off2, sens2, t2;
+
+ t2 = (dt * dt) >> 31;
+ off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
+ sens2 = off2 >> 1;
+
+ if (t < -1500) {
+ s64 tmp = (t + 1500) * (t + 1500);
+
+ off2 += 7 * tmp;
+ sens2 += (11 * tmp) >> 1;
+ }
+
+ t -= t2;
+ off -= off2;
+ sens -= sens2;
+ }
+
+ *temp = t;
+ *pressure = (((p * sens) >> 21) - off) >> 15;
+
+ return 0;
+}
+
+static int ms5611_reset(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ ret = st->reset(&indio_dev->dev);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "failed to reset device\n");
+ return ret;
+ }
+
+ usleep_range(3000, 4000);
+
+ return 0;
+}
+
+static int ms5611_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ s32 temp, pressure;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&st->lock);
+ ret = ms5611_read_temp_and_pressure(indio_dev,
+ &temp, &pressure);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = temp * 10;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = pressure / 1000;
+ *val2 = (pressure % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec ms5611_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE)
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE)
+ }
+};
+
+static const struct iio_info ms5611_info = {
+ .read_raw = &ms5611_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ms5611_init(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ ret = ms5611_reset(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return ms5611_read_prom(indio_dev);
+}
+
+int ms5611_probe(struct iio_dev *indio_dev, struct device *dev)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ mutex_init(&st->lock);
+ indio_dev->dev.parent = dev;
+ indio_dev->name = dev->driver->name;
+ indio_dev->info = &ms5611_info;
+ indio_dev->channels = ms5611_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = ms5611_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL(ms5611_probe);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
new file mode 100644
index 0000000..748fd9a
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_i2c.c
@@ -0,0 +1,128 @@
+/*
+ * MS5611 pressure and temperature sensor driver (I2C bus)
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.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.
+ *
+ * 7-bit I2C slave addresses:
+ *
+ * 0x77 (CSB pin low)
+ * 0x76 (CSB pin high)
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include "ms5611.h"
+
+static int ms5611_i2c_reset(struct device *dev)
+{
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return i2c_smbus_write_byte(st->client, MS5611_RESET);
+}
+
+static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ ret = i2c_smbus_read_word_swapped(st->client,
+ MS5611_READ_PROM_WORD + (index << 1));
+ if (ret < 0)
+ return ret;
+
+ *word = ret;
+
+ return 0;
+}
+
+static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val)
+{
+ int ret;
+ u8 buf[3];
+
+ ret = i2c_smbus_read_i2c_block_data(st->client, MS5611_READ_ADC,
+ 3, buf);
+ if (ret < 0)
+ return ret;
+
+ *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
+static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev,
+ s32 *temp, s32 *pressure)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ ret = i2c_smbus_write_byte(st->client, MS5611_START_TEMP_CONV);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
+
+ ret = ms5611_i2c_read_adc(st, temp);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte(st->client, MS5611_START_PRESSURE_CONV);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
+
+ return ms5611_i2c_read_adc(st, pressure);
+}
+
+static int ms5611_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct ms5611_state *st;
+ struct iio_dev *indio_dev;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->reset = ms5611_i2c_reset;
+ st->read_prom_word = ms5611_i2c_read_prom_word;
+ st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
+ st->client = client;
+
+ return ms5611_probe(indio_dev, &client->dev);
+}
+
+static const struct i2c_device_id ms5611_id[] = {
+ { "ms5611", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ms5611_id);
+
+static struct i2c_driver ms5611_driver = {
+ .driver = {
+ .name = "ms5611",
+ .owner = THIS_MODULE,
+ },
+ .id_table = ms5611_id,
+ .probe = ms5611_i2c_probe,
+};
+module_i2c_driver(ms5611_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
new file mode 100644
index 0000000..976726f
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -0,0 +1,127 @@
+/*
+ * MS5611 pressure and temperature sensor driver (SPI bus)
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.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/module.h>
+#include <linux/spi/spi.h>
+
+#include "ms5611.h"
+
+static int ms5611_spi_reset(struct device *dev)
+{
+ u8 cmd = MS5611_RESET;
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ return spi_write_then_read(st->client, &cmd, 1, NULL, 0);
+}
+
+static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1));
+ if (ret < 0)
+ return ret;
+
+ *word = ret;
+
+ return 0;
+}
+
+static int ms5611_spi_read_adc(struct device *dev, s32 *val)
+{
+ int ret;
+ u8 buf[3] = { MS5611_READ_ADC };
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ ret = spi_write_then_read(st->client, buf, 1, buf, 3);
+ if (ret < 0)
+ return ret;
+
+ *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
+
+ return 0;
+}
+
+static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
+ s32 *temp, s32 *pressure)
+{
+ u8 cmd;
+ int ret;
+ struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+
+ cmd = MS5611_START_TEMP_CONV;
+ ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
+
+ ret = ms5611_spi_read_adc(dev, temp);
+ if (ret < 0)
+ return ret;
+
+ cmd = MS5611_START_PRESSURE_CONV;
+ ret = spi_write_then_read(st->client, &cmd, 1, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(MS5611_CONV_TIME_MIN, MS5611_CONV_TIME_MAX);
+
+ return ms5611_spi_read_adc(dev, pressure);
+}
+
+static int ms5611_spi_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ms5611_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ spi->mode = SPI_MODE_0;
+ spi->max_speed_hz = 20000000;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ st = iio_priv(indio_dev);
+ st->reset = ms5611_spi_reset;
+ st->read_prom_word = ms5611_spi_read_prom_word;
+ st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
+ st->client = spi;
+
+ return ms5611_probe(indio_dev, &spi->dev);
+}
+
+static const struct spi_device_id ms5611_id[] = {
+ { "ms5611", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ms5611_id);
+
+static struct spi_driver ms5611_driver = {
+ .driver = {
+ .name = "ms5611",
+ .owner = THIS_MODULE,
+ },
+ .id_table = ms5611_id,
+ .probe = ms5611_spi_probe,
+};
+module_spi_driver(ms5611_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h
index 242943c..f5f4149 100644
--- a/drivers/iio/pressure/st_pressure.h
+++ b/drivers/iio/pressure/st_pressure.h
@@ -26,8 +26,7 @@ static const struct st_sensors_platform_data default_press_pdata = {
.drdy_int_pin = 1,
};
-int st_press_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *pdata);
+int st_press_common_probe(struct iio_dev *indio_dev);
void st_press_common_remove(struct iio_dev *indio_dev);
#ifdef CONFIG_IIO_BUFFER
diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c
index b37b1c9..2ff53f2 100644
--- a/drivers/iio/pressure/st_pressure_buffer.c
+++ b/drivers/iio/pressure/st_pressure_buffer.c
@@ -38,10 +38,10 @@ static int st_press_buffer_preenable(struct iio_dev *indio_dev)
static int st_press_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *pdata = iio_priv(indio_dev);
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
- pdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (pdata->buffer_data == NULL) {
+ press_data->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (press_data->buffer_data == NULL) {
err = -ENOMEM;
goto allocate_memory_error;
}
@@ -53,7 +53,7 @@ static int st_press_buffer_postenable(struct iio_dev *indio_dev)
return err;
st_press_buffer_postenable_error:
- kfree(pdata->buffer_data);
+ kfree(press_data->buffer_data);
allocate_memory_error:
return err;
}
@@ -61,7 +61,7 @@ allocate_memory_error:
static int st_press_buffer_predisable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *pdata = iio_priv(indio_dev);
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
err = iio_triggered_buffer_predisable(indio_dev);
if (err < 0)
@@ -70,7 +70,7 @@ static int st_press_buffer_predisable(struct iio_dev *indio_dev)
err = st_sensors_set_enable(indio_dev, false);
st_press_buffer_predisable_error:
- kfree(pdata->buffer_data);
+ kfree(press_data->buffer_data);
return err;
}
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 473d914..97baf40d 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -175,7 +175,7 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(1)
};
-static const struct st_sensors st_press_sensors[] = {
+static const struct st_sensor_settings st_press_sensors_settings[] = {
{
.wai = ST_PRESS_LPS331AP_WAI_EXP,
.sensors_supported = {
@@ -333,7 +333,7 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
int err;
- struct st_sensor_data *pdata = iio_priv(indio_dev);
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
@@ -347,10 +347,10 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
switch (ch->type) {
case IIO_PRESSURE:
- *val2 = pdata->current_fullscale->gain;
+ *val2 = press_data->current_fullscale->gain;
break;
case IIO_TEMP:
- *val2 = pdata->current_fullscale->gain2;
+ *val2 = press_data->current_fullscale->gain2;
break;
default:
err = -EINVAL;
@@ -371,7 +371,7 @@ static int st_press_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_FRACTIONAL;
case IIO_CHAN_INFO_SAMP_FREQ:
- *val = pdata->odr;
+ *val = press_data->odr;
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -409,11 +409,10 @@ static const struct iio_trigger_ops st_press_trigger_ops = {
#define ST_PRESS_TRIGGER_OPS NULL
#endif
-int st_press_common_probe(struct iio_dev *indio_dev,
- struct st_sensors_platform_data *plat_data)
+int st_press_common_probe(struct iio_dev *indio_dev)
{
- struct st_sensor_data *pdata = iio_priv(indio_dev);
- int irq = pdata->get_irq_data_ready(indio_dev);
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
+ int irq = press_data->get_irq_data_ready(indio_dev);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -422,28 +421,30 @@ int st_press_common_probe(struct iio_dev *indio_dev,
st_sensors_power_enable(indio_dev);
err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_press_sensors),
- st_press_sensors);
+ ARRAY_SIZE(st_press_sensors_settings),
+ st_press_sensors_settings);
if (err < 0)
return err;
- pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
- pdata->multiread_bit = pdata->sensor->multi_read_bit;
- indio_dev->channels = pdata->sensor->ch;
- indio_dev->num_channels = pdata->sensor->num_ch;
+ press_data->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS;
+ press_data->multiread_bit = press_data->sensor_settings->multi_read_bit;
+ indio_dev->channels = press_data->sensor_settings->ch;
+ indio_dev->num_channels = press_data->sensor_settings->num_ch;
- if (pdata->sensor->fs.addr != 0)
- pdata->current_fullscale = (struct st_sensor_fullscale_avl *)
- &pdata->sensor->fs.fs_avl[0];
+ if (press_data->sensor_settings->fs.addr != 0)
+ press_data->current_fullscale =
+ (struct st_sensor_fullscale_avl *)
+ &press_data->sensor_settings->fs.fs_avl[0];
- pdata->odr = pdata->sensor->odr.odr_avl[0].hz;
+ press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
/* Some devices don't support a data ready pin. */
- if (!plat_data && pdata->sensor->drdy_irq.addr)
- plat_data =
+ if (!press_data->dev->platform_data &&
+ press_data->sensor_settings->drdy_irq.addr)
+ press_data->dev->platform_data =
(struct st_sensors_platform_data *)&default_press_pdata;
- err = st_sensors_init_sensor(indio_dev, plat_data);
+ err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data);
if (err < 0)
return err;
@@ -479,12 +480,12 @@ EXPORT_SYMBOL(st_press_common_probe);
void st_press_common_remove(struct iio_dev *indio_dev)
{
- struct st_sensor_data *pdata = iio_priv(indio_dev);
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev);
- if (pdata->get_irq_data_ready(indio_dev) > 0)
+ if (press_data->get_irq_data_ready(indio_dev) > 0)
st_sensors_deallocate_trigger(indio_dev);
st_press_deallocate_ring(indio_dev);
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
index acaf165..137788b 100644
--- a/drivers/iio/pressure/st_pressure_i2c.c
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -43,20 +43,19 @@ static int st_press_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
- struct st_sensor_data *pdata;
+ struct st_sensor_data *press_data;
int err;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*pdata));
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data));
if (!indio_dev)
return -ENOMEM;
- pdata = iio_priv(indio_dev);
- pdata->dev = &client->dev;
+ press_data = iio_priv(indio_dev);
st_sensors_of_i2c_probe(client, st_press_of_match);
- st_sensors_i2c_configure(indio_dev, client, pdata);
+ st_sensors_i2c_configure(indio_dev, client, press_data);
- err = st_press_common_probe(indio_dev, client->dev.platform_data);
+ err = st_press_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
index f45d430..1ffa6d4 100644
--- a/drivers/iio/pressure/st_pressure_spi.c
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -21,19 +21,18 @@
static int st_press_spi_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
- struct st_sensor_data *pdata;
+ struct st_sensor_data *press_data;
int err;
- indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*pdata));
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*press_data));
if (indio_dev == NULL)
return -ENOMEM;
- pdata = iio_priv(indio_dev);
- pdata->dev = &spi->dev;
+ press_data = iio_priv(indio_dev);
- st_sensors_spi_configure(indio_dev, spi, pdata);
+ st_sensors_spi_configure(indio_dev, spi, press_data);
- err = st_press_common_probe(indio_dev, spi->dev.platform_data);
+ err = st_press_common_probe(indio_dev);
if (err < 0)
return err;
diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 0c8cdf5..41a8d8f 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -17,3 +17,20 @@ config AS3935
module will be called as3935
endmenu
+
+menu "Proximity sensors"
+
+config SX9500
+ tristate "SX9500 Semtech proximity sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say Y here to build a driver for Semtech's SX9500 capacitive
+ proximity/button sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sx9500.
+
+endmenu
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index 743adee..9818dc5 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -4,3 +4,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AS3935) += as3935.o
+obj-$(CONFIG_SX9500) += sx9500.o
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index 8349cc0..bc0d68e 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -95,7 +95,7 @@ static int as3935_read(struct as3935_state *st, unsigned int reg, int *val)
*val = ret;
return 0;
-};
+}
static int as3935_write(struct as3935_state *st,
unsigned int reg,
@@ -107,7 +107,7 @@ static int as3935_write(struct as3935_state *st,
buf[1] = val;
return spi_write(st->spi, buf, 2);
-};
+}
static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
struct device_attribute *attr,
@@ -122,7 +122,7 @@ static ssize_t as3935_sensor_sensitivity_show(struct device *dev,
val = (val & AS3935_AFE_MASK) >> 1;
return sprintf(buf, "%d\n", val);
-};
+}
static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
struct device_attribute *attr,
@@ -142,7 +142,7 @@ static ssize_t as3935_sensor_sensitivity_store(struct device *dev,
as3935_write(st, AS3935_AFE_GAIN, val << 1);
return len;
-};
+}
static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR,
as3935_sensor_sensitivity_show, as3935_sensor_sensitivity_store, 0);
@@ -214,7 +214,7 @@ err_read:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
-};
+}
static const struct iio_trigger_ops iio_interrupt_trigger_ops = {
.owner = THIS_MODULE,
@@ -238,7 +238,7 @@ static void as3935_event_work(struct work_struct *work)
dev_warn(&st->spi->dev, "noise level is too high");
break;
}
-};
+}
static irqreturn_t as3935_interrupt_handler(int irq, void *private)
{
@@ -273,9 +273,9 @@ static void calibrate_as3935(struct as3935_state *st)
}
#ifdef CONFIG_PM_SLEEP
-static int as3935_suspend(struct spi_device *spi, pm_message_t msg)
+static int as3935_suspend(struct device *dev)
{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@@ -293,9 +293,9 @@ err_suspend:
return ret;
}
-static int as3935_resume(struct spi_device *spi)
+static int as3935_resume(struct device *dev)
{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct as3935_state *st = iio_priv(indio_dev);
int val, ret;
@@ -311,9 +311,12 @@ err_resume:
return ret;
}
+
+static SIMPLE_DEV_PM_OPS(as3935_pm_ops, as3935_suspend, as3935_resume);
+#define AS3935_PM_OPS (&as3935_pm_ops)
+
#else
-#define as3935_suspend NULL
-#define as3935_resume NULL
+#define AS3935_PM_OPS NULL
#endif
static int as3935_probe(struct spi_device *spi)
@@ -417,7 +420,7 @@ unregister_trigger:
iio_trigger_unregister(st->trig);
return ret;
-};
+}
static int as3935_remove(struct spi_device *spi)
{
@@ -429,7 +432,7 @@ static int as3935_remove(struct spi_device *spi)
iio_trigger_unregister(st->trig);
return 0;
-};
+}
static const struct spi_device_id as3935_id[] = {
{"as3935", 0},
@@ -441,12 +444,11 @@ static struct spi_driver as3935_driver = {
.driver = {
.name = "as3935",
.owner = THIS_MODULE,
+ .pm = AS3935_PM_OPS,
},
.probe = as3935_probe,
.remove = as3935_remove,
.id_table = as3935_id,
- .suspend = as3935_suspend,
- .resume = as3935_resume,
};
module_spi_driver(as3935_driver);
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
new file mode 100644
index 0000000..fa40f6d
--- /dev/null
+++ b/drivers/iio/proximity/sx9500.c
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Driver for Semtech's SX9500 capacitive proximity/button solution.
+ * Datasheet available at
+ * <http://www.semtech.com/images/datasheet/sx9500.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/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/acpi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define SX9500_DRIVER_NAME "sx9500"
+#define SX9500_IRQ_NAME "sx9500_event"
+#define SX9500_GPIO_NAME "sx9500_gpio"
+
+/* Register definitions. */
+#define SX9500_REG_IRQ_SRC 0x00
+#define SX9500_REG_STAT 0x01
+#define SX9500_REG_IRQ_MSK 0x03
+
+#define SX9500_REG_PROX_CTRL0 0x06
+#define SX9500_REG_PROX_CTRL1 0x07
+#define SX9500_REG_PROX_CTRL2 0x08
+#define SX9500_REG_PROX_CTRL3 0x09
+#define SX9500_REG_PROX_CTRL4 0x0a
+#define SX9500_REG_PROX_CTRL5 0x0b
+#define SX9500_REG_PROX_CTRL6 0x0c
+#define SX9500_REG_PROX_CTRL7 0x0d
+#define SX9500_REG_PROX_CTRL8 0x0e
+
+#define SX9500_REG_SENSOR_SEL 0x20
+#define SX9500_REG_USE_MSB 0x21
+#define SX9500_REG_USE_LSB 0x22
+#define SX9500_REG_AVG_MSB 0x23
+#define SX9500_REG_AVG_LSB 0x24
+#define SX9500_REG_DIFF_MSB 0x25
+#define SX9500_REG_DIFF_LSB 0x26
+#define SX9500_REG_OFFSET_MSB 0x27
+#define SX9500_REG_OFFSET_LSB 0x28
+
+#define SX9500_REG_RESET 0x7f
+
+/* Write this to REG_RESET to do a soft reset. */
+#define SX9500_SOFT_RESET 0xde
+
+#define SX9500_SCAN_PERIOD_MASK GENMASK(6, 4)
+#define SX9500_SCAN_PERIOD_SHIFT 4
+
+/*
+ * These serve for identifying IRQ source in the IRQ_SRC register, and
+ * also for masking the IRQs in the IRQ_MSK register.
+ */
+#define SX9500_CLOSE_IRQ BIT(6)
+#define SX9500_FAR_IRQ BIT(5)
+#define SX9500_CONVDONE_IRQ BIT(3)
+
+#define SX9500_PROXSTAT_SHIFT 4
+
+#define SX9500_NUM_CHANNELS 4
+
+struct sx9500_data {
+ struct mutex mutex;
+ struct i2c_client *client;
+ struct iio_trigger *trig;
+ struct regmap *regmap;
+ /*
+ * Last reading of the proximity status for each channel. We
+ * only send an event to user space when this changes.
+ */
+ bool prox_stat[SX9500_NUM_CHANNELS];
+ bool event_enabled[SX9500_NUM_CHANNELS];
+ bool trigger_enabled;
+ u16 *buffer;
+};
+
+static const struct iio_event_spec sx9500_events[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+#define SX9500_CHANNEL(idx) \
+ { \
+ .type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .indexed = 1, \
+ .channel = idx, \
+ .event_spec = sx9500_events, \
+ .num_event_specs = ARRAY_SIZE(sx9500_events), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .shift = 0, \
+ }, \
+ }
+
+static const struct iio_chan_spec sx9500_channels[] = {
+ SX9500_CHANNEL(0),
+ SX9500_CHANNEL(1),
+ SX9500_CHANNEL(2),
+ SX9500_CHANNEL(3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct {
+ int val;
+ int val2;
+} sx9500_samp_freq_table[] = {
+ {33, 333333},
+ {16, 666666},
+ {11, 111111},
+ {8, 333333},
+ {6, 666666},
+ {5, 0},
+ {3, 333333},
+ {2, 500000},
+};
+
+static const struct regmap_range sx9500_writable_reg_ranges[] = {
+ regmap_reg_range(SX9500_REG_IRQ_MSK, SX9500_REG_IRQ_MSK),
+ regmap_reg_range(SX9500_REG_PROX_CTRL0, SX9500_REG_PROX_CTRL8),
+ regmap_reg_range(SX9500_REG_SENSOR_SEL, SX9500_REG_SENSOR_SEL),
+ regmap_reg_range(SX9500_REG_OFFSET_MSB, SX9500_REG_OFFSET_LSB),
+ regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
+};
+
+static const struct regmap_access_table sx9500_writeable_regs = {
+ .yes_ranges = sx9500_writable_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9500_writable_reg_ranges),
+};
+
+/*
+ * All allocated registers are readable, so we just list unallocated
+ * ones.
+ */
+static const struct regmap_range sx9500_non_readable_reg_ranges[] = {
+ regmap_reg_range(SX9500_REG_STAT + 1, SX9500_REG_STAT + 1),
+ regmap_reg_range(SX9500_REG_IRQ_MSK + 1, SX9500_REG_PROX_CTRL0 - 1),
+ regmap_reg_range(SX9500_REG_PROX_CTRL8 + 1, SX9500_REG_SENSOR_SEL - 1),
+ regmap_reg_range(SX9500_REG_OFFSET_LSB + 1, SX9500_REG_RESET - 1),
+};
+
+static const struct regmap_access_table sx9500_readable_regs = {
+ .no_ranges = sx9500_non_readable_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(sx9500_non_readable_reg_ranges),
+};
+
+static const struct regmap_range sx9500_volatile_reg_ranges[] = {
+ regmap_reg_range(SX9500_REG_IRQ_SRC, SX9500_REG_STAT),
+ regmap_reg_range(SX9500_REG_USE_MSB, SX9500_REG_OFFSET_LSB),
+ regmap_reg_range(SX9500_REG_RESET, SX9500_REG_RESET),
+};
+
+static const struct regmap_access_table sx9500_volatile_regs = {
+ .yes_ranges = sx9500_volatile_reg_ranges,
+ .n_yes_ranges = ARRAY_SIZE(sx9500_volatile_reg_ranges),
+};
+
+static const struct regmap_config sx9500_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = SX9500_REG_RESET,
+ .cache_type = REGCACHE_RBTREE,
+
+ .wr_table = &sx9500_writeable_regs,
+ .rd_table = &sx9500_readable_regs,
+ .volatile_table = &sx9500_volatile_regs,
+};
+
+static int sx9500_read_proximity(struct sx9500_data *data,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ int ret;
+ __be16 regval;
+
+ ret = regmap_write(data->regmap, SX9500_REG_SENSOR_SEL, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, SX9500_REG_USE_MSB, &regval, 2);
+ if (ret < 0)
+ return ret;
+
+ *val = 32767 - (s16)be16_to_cpu(regval);
+
+ return IIO_VAL_INT;
+}
+
+static int sx9500_read_samp_freq(struct sx9500_data *data,
+ int *val, int *val2)
+{
+ int ret;
+ unsigned int regval;
+
+ mutex_lock(&data->mutex);
+ ret = regmap_read(data->regmap, SX9500_REG_PROX_CTRL0, &regval);
+ mutex_unlock(&data->mutex);
+
+ if (ret < 0)
+ return ret;
+
+ regval = (regval & SX9500_SCAN_PERIOD_MASK) >> SX9500_SCAN_PERIOD_SHIFT;
+ *val = sx9500_samp_freq_table[regval].val;
+ *val2 = sx9500_samp_freq_table[regval].val2;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int sx9500_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+ mutex_lock(&data->mutex);
+ ret = sx9500_read_proximity(data, chan, val);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9500_read_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sx9500_set_samp_freq(struct sx9500_data *data,
+ int val, int val2)
+{
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(sx9500_samp_freq_table); i++)
+ if (val == sx9500_samp_freq_table[i].val &&
+ val2 == sx9500_samp_freq_table[i].val2)
+ break;
+
+ if (i == ARRAY_SIZE(sx9500_samp_freq_table))
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, SX9500_REG_PROX_CTRL0,
+ SX9500_SCAN_PERIOD_MASK,
+ i << SX9500_SCAN_PERIOD_SHIFT);
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9500_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return sx9500_set_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t sx9500_irq_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sx9500_data *data = iio_priv(indio_dev);
+
+ if (data->trigger_enabled)
+ iio_trigger_poll(data->trig);
+
+ /*
+ * Even if no event is enabled, we need to wake the thread to
+ * clear the interrupt state by reading SX9500_REG_IRQ_SRC. It
+ * is not possible to do that here because regmap_read takes a
+ * mutex.
+ */
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+ unsigned int val, chan;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ goto out;
+ }
+
+ if (!(val & (SX9500_CLOSE_IRQ | SX9500_FAR_IRQ)))
+ goto out;
+
+ ret = regmap_read(data->regmap, SX9500_REG_STAT, &val);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "i2c transfer error in irq\n");
+ goto out;
+ }
+
+ val >>= SX9500_PROXSTAT_SHIFT;
+ for (chan = 0; chan < SX9500_NUM_CHANNELS; chan++) {
+ int dir;
+ u64 ev;
+ bool new_prox = val & BIT(chan);
+
+ if (!data->event_enabled[chan])
+ continue;
+ if (new_prox == data->prox_stat[chan])
+ /* No change on this channel. */
+ continue;
+
+ dir = new_prox ? IIO_EV_DIR_FALLING :
+ IIO_EV_DIR_RISING;
+ ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
+ chan,
+ IIO_EV_TYPE_THRESH,
+ dir);
+ iio_push_event(indio_dev, ev, iio_get_time_ns());
+ data->prox_stat[chan] = new_prox;
+ }
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int sx9500_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 sx9500_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
+ dir != IIO_EV_DIR_EITHER)
+ return -EINVAL;
+
+ return data->event_enabled[chan->channel];
+}
+
+static int sx9500_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 sx9500_data *data = iio_priv(indio_dev);
+ int ret, i;
+ bool any_active = false;
+ unsigned int irqmask;
+
+ if (chan->type != IIO_PROXIMITY || type != IIO_EV_TYPE_THRESH ||
+ dir != IIO_EV_DIR_EITHER)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+
+ data->event_enabled[chan->channel] = state;
+
+ for (i = 0; i < SX9500_NUM_CHANNELS; i++)
+ if (data->event_enabled[i]) {
+ any_active = true;
+ break;
+ }
+
+ irqmask = SX9500_CLOSE_IRQ | SX9500_FAR_IRQ;
+ if (any_active)
+ ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
+ irqmask, irqmask);
+ else
+ ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
+ irqmask, 0);
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int sx9500_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+
+ mutex_lock(&data->mutex);
+ kfree(data->buffer);
+ data->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ mutex_unlock(&data->mutex);
+
+ if (data->buffer == NULL)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "2.500000 3.333333 5 6.666666 8.333333 11.111111 16.666666 33.333333");
+
+static struct attribute *sx9500_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group sx9500_attribute_group = {
+ .attrs = sx9500_attributes,
+};
+
+static const struct iio_info sx9500_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &sx9500_attribute_group,
+ .read_raw = &sx9500_read_raw,
+ .write_raw = &sx9500_write_raw,
+ .read_event_config = &sx9500_read_event_config,
+ .write_event_config = &sx9500_write_event_config,
+ .update_scan_mode = &sx9500_update_scan_mode,
+};
+
+static int sx9500_set_trigger_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->mutex);
+
+ ret = regmap_update_bits(data->regmap, SX9500_REG_IRQ_MSK,
+ SX9500_CONVDONE_IRQ,
+ state ? SX9500_CONVDONE_IRQ : 0);
+ if (ret == 0)
+ data->trigger_enabled = state;
+
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct iio_trigger_ops sx9500_trigger_ops = {
+ .set_trigger_state = sx9500_set_trigger_state,
+ .owner = THIS_MODULE,
+};
+
+static irqreturn_t sx9500_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int val, bit, ret, i = 0;
+
+ mutex_lock(&data->mutex);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = sx9500_read_proximity(data, &indio_dev->channels[bit],
+ &val);
+ if (ret < 0)
+ goto out;
+
+ data->buffer[i++] = val;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns());
+
+out:
+ mutex_unlock(&data->mutex);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+struct sx9500_reg_default {
+ u8 reg;
+ u8 def;
+};
+
+static const struct sx9500_reg_default sx9500_default_regs[] = {
+ {
+ .reg = SX9500_REG_PROX_CTRL1,
+ /* Shield enabled, small range. */
+ .def = 0x43,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL2,
+ /* x8 gain, 167kHz frequency, finest resolution. */
+ .def = 0x77,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL3,
+ /* Doze enabled, 2x scan period doze, no raw filter. */
+ .def = 0x40,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL4,
+ /* Average threshold. */
+ .def = 0x30,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL5,
+ /*
+ * Debouncer off, lowest average negative filter,
+ * highest average postive filter.
+ */
+ .def = 0x0f,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL6,
+ /* Proximity detection threshold: 280 */
+ .def = 0x0e,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL7,
+ /*
+ * No automatic compensation, compensate each pin
+ * independently, proximity hysteresis: 32, close
+ * debouncer off, far debouncer off.
+ */
+ .def = 0x00,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL8,
+ /* No stuck timeout, no periodic compensation. */
+ .def = 0x00,
+ },
+ {
+ .reg = SX9500_REG_PROX_CTRL0,
+ /* Scan period: 30ms, all sensors enabled. */
+ .def = 0x0f,
+ },
+};
+
+static int sx9500_init_device(struct iio_dev *indio_dev)
+{
+ struct sx9500_data *data = iio_priv(indio_dev);
+ int ret, i;
+ unsigned int val;
+
+ ret = regmap_write(data->regmap, SX9500_REG_IRQ_MSK, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(data->regmap, SX9500_REG_RESET,
+ SX9500_SOFT_RESET);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(data->regmap, SX9500_REG_IRQ_SRC, &val);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(sx9500_default_regs); i++) {
+ ret = regmap_write(data->regmap,
+ sx9500_default_regs[i].reg,
+ sx9500_default_regs[i].def);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int sx9500_gpio_probe(struct i2c_client *client,
+ struct sx9500_data *data)
+{
+ struct device *dev;
+ struct gpio_desc *gpio;
+ int ret;
+
+ if (!client)
+ return -EINVAL;
+
+ dev = &client->dev;
+
+ /* data ready gpio interrupt pin */
+ gpio = devm_gpiod_get_index(dev, SX9500_GPIO_NAME, 0, GPIOD_IN);
+ if (IS_ERR(gpio)) {
+ dev_err(dev, "acpi gpio get index failed\n");
+ return PTR_ERR(gpio);
+ }
+
+ ret = gpiod_to_irq(gpio);
+
+ dev_dbg(dev, "GPIO resource, no:%d irq:%d\n", desc_to_gpio(gpio), ret);
+
+ return ret;
+}
+
+static int sx9500_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct sx9500_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ mutex_init(&data->mutex);
+ data->trigger_enabled = false;
+
+ data->regmap = devm_regmap_init_i2c(client, &sx9500_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ sx9500_init_device(indio_dev);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = SX9500_DRIVER_NAME;
+ indio_dev->channels = sx9500_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sx9500_channels);
+ indio_dev->info = &sx9500_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ i2c_set_clientdata(client, indio_dev);
+
+ if (client->irq <= 0)
+ client->irq = sx9500_gpio_probe(client, data);
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ sx9500_irq_handler, sx9500_irq_thread_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ SX9500_IRQ_NAME, indio_dev);
+ if (ret < 0)
+ return ret;
+
+ data->trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d", indio_dev->name, indio_dev->id);
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->dev.parent = &client->dev;
+ data->trig->ops = &sx9500_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+
+ ret = iio_trigger_register(data->trig);
+ if (ret)
+ return ret;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ sx9500_trigger_handler, NULL);
+ if (ret < 0)
+ goto out_trigger_unregister;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto out_buffer_cleanup;
+
+ return 0;
+
+out_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+out_trigger_unregister:
+ if (client->irq > 0)
+ iio_trigger_unregister(data->trig);
+
+ return ret;
+}
+
+static int sx9500_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct sx9500_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (client->irq > 0)
+ iio_trigger_unregister(data->trig);
+ kfree(data->buffer);
+
+ return 0;
+}
+
+static const struct acpi_device_id sx9500_acpi_match[] = {
+ {"SSX9500", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
+
+static const struct i2c_device_id sx9500_id[] = {
+ {"sx9500", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, sx9500_id);
+
+static struct i2c_driver sx9500_driver = {
+ .driver = {
+ .name = SX9500_DRIVER_NAME,
+ .acpi_match_table = ACPI_PTR(sx9500_acpi_match),
+ },
+ .probe = sx9500_probe,
+ .remove = sx9500_remove,
+ .id_table = sx9500_id,
+};
+module_i2c_driver(sx9500_driver);
+
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_DESCRIPTION("Driver for Semtech SX9500 proximity sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index c8b6ac8..a112fc9 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -2,6 +2,7 @@
* mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ * Copyright (c) 2015 Essensium NV
*
* 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
@@ -20,11 +21,35 @@
#include <linux/iio/iio.h>
-#define MLX90614_OP_RAM 0x00
+#define MLX90614_OP_RAM 0x00
+#define MLX90614_OP_EEPROM 0x20
+#define MLX90614_OP_SLEEP 0xff
/* RAM offsets with 16-bit data, MSB first */
-#define MLX90614_TA 0x06 /* ambient temperature */
-#define MLX90614_TOBJ1 0x07 /* object temperature */
+#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */
+#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */
+#define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */
+#define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */
+#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */
+
+/* EEPROM offsets with 16-bit data, MSB first */
+#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */
+#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */
+
+/* Control bits in configuration register */
+#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */
+#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT)
+#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */
+#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT)
+#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */
+#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT)
+#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */
+#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT)
+
+/* Timings (in ms) */
+#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */
+#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */
+#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */
struct mlx90614_data {
struct i2c_client *client;
@@ -35,26 +60,34 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev,
int *val2, long mask)
{
struct mlx90614_data *data = iio_priv(indio_dev);
+ u8 cmd;
s32 ret;
switch (mask) {
case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */
switch (channel->channel2) {
case IIO_MOD_TEMP_AMBIENT:
- ret = i2c_smbus_read_word_data(data->client,
- MLX90614_OP_RAM | MLX90614_TA);
- if (ret < 0)
- return ret;
+ cmd = MLX90614_TA;
break;
case IIO_MOD_TEMP_OBJECT:
- ret = i2c_smbus_read_word_data(data->client,
- MLX90614_OP_RAM | MLX90614_TOBJ1);
- if (ret < 0)
- return ret;
+ switch (channel->channel) {
+ case 0:
+ cmd = MLX90614_TOBJ1;
+ break;
+ case 1:
+ cmd = MLX90614_TOBJ2;
+ break;
+ default:
+ return -EINVAL;
+ }
break;
default:
return -EINVAL;
}
+
+ ret = i2c_smbus_read_word_data(data->client, cmd);
+ if (ret < 0)
+ return ret;
*val = ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_OFFSET:
@@ -86,6 +119,16 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
+ {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .modified = 1,
+ .channel = 1,
+ .channel2 = IIO_MOD_TEMP_OBJECT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
};
static const struct iio_info mlx90614_info = {
@@ -93,11 +136,25 @@ static const struct iio_info mlx90614_info = {
.driver_module = THIS_MODULE,
};
+/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */
+static int mlx90614_probe_num_ir_sensors(struct i2c_client *client)
+{
+ s32 ret;
+
+ ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG);
+
+ if (ret < 0)
+ return ret;
+
+ return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0;
+}
+
static int mlx90614_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct mlx90614_data *data;
+ int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -ENODEV;
@@ -115,8 +172,21 @@ static int mlx90614_probe(struct i2c_client *client,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mlx90614_info;
- indio_dev->channels = mlx90614_channels;
- indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels);
+ ret = mlx90614_probe_num_ir_sensors(client);
+ switch (ret) {
+ case 0:
+ dev_dbg(&client->dev, "Found single sensor");
+ indio_dev->channels = mlx90614_channels;
+ indio_dev->num_channels = 2;
+ break;
+ case 1:
+ dev_dbg(&client->dev, "Found dual sensor");
+ indio_dev->channels = mlx90614_channels;
+ indio_dev->num_channels = 3;
+ break;
+ default:
+ return ret;
+ }
return iio_device_register(indio_dev);
}
@@ -146,5 +216,6 @@ static struct i2c_driver mlx90614_driver = {
module_i2c_driver(mlx90614_driver);
MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_AUTHOR("Vianney le Clément de Saint-Marcq <vianney.leclement@essensium.com>");
MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c
index 254c7e9..3dfab2b 100644
--- a/drivers/iio/trigger/iio-trig-sysfs.c
+++ b/drivers/iio/trigger/iio-trig-sysfs.c
@@ -135,6 +135,7 @@ static int iio_sysfs_trigger_probe(int id)
struct iio_sysfs_trig *t;
int ret;
bool foundit = false;
+
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {
@@ -185,6 +186,7 @@ static int iio_sysfs_trigger_remove(int id)
{
bool foundit = false;
struct iio_sysfs_trig *t;
+
mutex_lock(&iio_sysfs_trig_list_mut);
list_for_each_entry(t, &iio_sysfs_trig_list, l)
if (id == t->id) {
OpenPOWER on IntegriCloud