summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-iio-cros-ec18
-rw-r--r--Documentation/devicetree/bindings/i2c/trivial-devices.txt1
-rw-r--r--Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt46
-rw-r--r--Documentation/devicetree/bindings/iio/light/tsl2583.txt26
-rw-r--r--Documentation/devicetree/bindings/iio/st-sensors.txt1
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/iio/accel/Kconfig3
-rw-r--r--drivers/iio/accel/st_accel.h1
-rw-r--r--drivers/iio/accel/st_accel_core.c73
-rw-r--r--drivers/iio/accel/st_accel_i2c.c5
-rw-r--r--drivers/iio/accel/st_accel_spi.c1
-rw-r--r--drivers/iio/adc/at91_adc.c28
-rw-r--r--drivers/iio/adc/ti_am335x_adc.c148
-rw-r--r--drivers/iio/common/Kconfig1
-rw-r--r--drivers/iio/common/Makefile1
-rw-r--r--drivers/iio/common/cros_ec_sensors/Kconfig22
-rw-r--r--drivers/iio/common/cros_ec_sensors/Makefile6
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c322
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c450
-rw-r--r--drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h175
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c5
-rw-r--r--drivers/iio/gyro/Kconfig17
-rw-r--r--drivers/iio/gyro/Makefile5
-rw-r--r--drivers/iio/gyro/mpu3050-core.c1307
-rw-r--r--drivers/iio/gyro/mpu3050-i2c.c124
-rw-r--r--drivers/iio/gyro/mpu3050.h96
-rw-r--r--drivers/iio/humidity/hts221_buffer.c1
-rw-r--r--drivers/iio/humidity/si7020.c11
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c2
-rw-r--r--drivers/mfd/ti_am335x_tscadc.c1
-rw-r--r--drivers/platform/chrome/cros_ec_dev.c159
-rw-r--r--drivers/staging/iio/adc/ad7192.c43
-rw-r--r--drivers/staging/iio/adc/ad7780.c22
-rw-r--r--drivers/staging/iio/cdc/ad7746.c39
-rw-r--r--drivers/staging/iio/frequency/ad9832.c56
-rw-r--r--drivers/staging/iio/frequency/ad9832.h6
-rw-r--r--drivers/staging/iio/frequency/ad9834.c19
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c21
-rw-r--r--drivers/staging/iio/light/tsl2583.c611
-rw-r--r--include/linux/mfd/cros_ec.h10
-rw-r--r--include/linux/mfd/cros_ec_commands.h183
-rw-r--r--include/linux/mfd/ti_am335x_tscadc.h8
42 files changed, 3646 insertions, 435 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec
new file mode 100644
index 000000000000..297b9720f024
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec
@@ -0,0 +1,18 @@
+What: /sys/bus/iio/devices/iio:deviceX/calibrate
+Date: July 2015
+KernelVersion: 4.7
+Contact: linux-iio@vger.kernel.org
+Description:
+ Writing '1' will perform a FOC (Fast Online Calibration). The
+ corresponding calibration offsets can be read from *_calibbias
+ entries.
+
+What: /sys/bus/iio/devices/iio:deviceX/location
+Date: July 2015
+KernelVersion: 4.7
+Contact: linux-iio@vger.kernel.org
+Description:
+ This attribute returns a string with the physical location where
+ the motion sensor is placed. For example, in a laptop a motion
+ sensor can be located on the base or on the lid. Current valid
+ values are 'base' and 'lid'.
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index 6e4ba816094b..03349ad5abfa 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -152,6 +152,7 @@ ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
sgx,vz89x SGX Sensortech VZ89X Sensors
sii,s35390a 2-wire CMOS real-time clock
+silabs,si7020 Relative Humidity and Temperature Sensors
skyworks,sky81452 Skyworks SKY81452: Six-Channel White LED Driver with Touch Panel Bias Supply
st,24c256 i2c serial eeprom (24cxx)
st,m41t00 Serial real-time clock (RTC)
diff --git a/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
new file mode 100644
index 000000000000..b0d3b59966bc
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/gyroscope/invensense,mpu3050.txt
@@ -0,0 +1,46 @@
+Invensense MPU-3050 Gyroscope device tree bindings
+
+Required properties:
+ - compatible : should be "invensense,mpu3050"
+ - reg : the I2C address of the sensor
+
+Optional properties:
+ - interrupt-parent : should be the phandle for the interrupt controller
+ - interrupts : interrupt mapping for the trigger interrupt from the
+ internal oscillator. The following IRQ modes are supported:
+ IRQ_TYPE_EDGE_RISING, IRQ_TYPE_EDGE_FALLING, IRQ_TYPE_LEVEL_HIGH and
+ IRQ_TYPE_LEVEL_LOW. The driver should detect and configure the hardware
+ for the desired interrupt type.
+ - vdd-supply : supply regulator for the main power voltage.
+ - vlogic-supply : supply regulator for the signal voltage.
+ - mount-matrix : see iio/mount-matrix.txt
+
+Optional subnodes:
+ - The MPU-3050 will pass through and forward the I2C signals from the
+ incoming I2C bus, alternatively drive traffic to a slave device (usually
+ an accelerometer) on its own initiative. Therefore is supports a subnode
+ i2c gate node. For details see: i2c/i2c-gate.txt
+
+Example:
+
+mpu3050@68 {
+ compatible = "invensense,mpu3050";
+ reg = <0x68>;
+ interrupt-parent = <&foo>;
+ interrupts = <12 IRQ_TYPE_EDGE_FALLING>;
+ vdd-supply = <&bar>;
+ vlogic-supply = <&baz>;
+
+ /* External I2C interface */
+ i2c-gate {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ fnord@18 {
+ compatible = "fnord";
+ reg = <0x18>;
+ interrupt-parent = <&foo>;
+ interrupts = <13 IRQ_TYPE_EDGE_FALLING>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/iio/light/tsl2583.txt b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
new file mode 100644
index 000000000000..8e2066c83f70
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/light/tsl2583.txt
@@ -0,0 +1,26 @@
+* TAOS TSL 2580/2581/2583 ALS sensor
+
+Required properties:
+
+ - compatible: Should be one of
+ "amstaos,tsl2580"
+ "amstaos,tsl2581"
+ "amstaos,tsl2583"
+ - reg: the I2C address of the device
+
+Optional properties:
+
+ - interrupt-parent: should be the phandle for the interrupt controller
+ - interrupts: the sole interrupt generated by the device
+
+ Refer to interrupt-controller/interrupts.txt for generic interrupt client
+ node bindings.
+
+ - vcc-supply: phandle to the regulator that provides power to the sensor.
+
+Example:
+
+tsl2581@29 {
+ compatible = "amstaos,tsl2581";
+ reg = <0x29>;
+};
diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt
index e41fe340162b..c040c9ad1889 100644
--- a/Documentation/devicetree/bindings/iio/st-sensors.txt
+++ b/Documentation/devicetree/bindings/iio/st-sensors.txt
@@ -42,6 +42,7 @@ Accelerometers:
- st,lsm303agr-accel
- st,lis2dh12-accel
- st,h3lis331dl-accel
+- st,lng2dm-accel
Gyroscopes:
- st,l3g4200d-gyro
diff --git a/MAINTAINERS b/MAINTAINERS
index eced366e69b0..ec1ee3ef112e 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -6498,6 +6498,13 @@ S: Maintained
F: arch/x86/include/asm/pmc_core.h
F: drivers/platform/x86/intel_pmc_core*
+INVENSENSE MPU-3050 GYROSCOPE DRIVER
+M: Linus Walleij <linus.walleij@linaro.org>
+L: linux-iio@vger.kernel.org
+S: Maintained
+F: drivers/iio/gyro/mpu3050*
+F: Documentation/devicetree/bindings/iio/gyroscope/inv,mpu3050.txt
+
IOC3 ETHERNET DRIVER
M: Ralf Baechle <ralf@linux-mips.org>
L: linux-mips@linux-mips.org
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index c6cc2c0909a1..c68bdb649005 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -127,7 +127,8 @@ config IIO_ST_ACCEL_3AXIS
help
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
- LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL.
+ LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
+ LNG2DM
This driver can also be built as a module. If so, these modules
will be created:
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index f8dfdb690563..7c231687109a 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -30,6 +30,7 @@
#define LSM303AGR_ACCEL_DEV_NAME "lsm303agr_accel"
#define LIS2DH12_ACCEL_DEV_NAME "lis2dh12_accel"
#define LIS3L02DQ_ACCEL_DEV_NAME "lis3l02dq"
+#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
/**
* struct st_sensors_platform_data - default accel platform data
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index da3fb069ec5c..b242457d0c80 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -231,6 +231,12 @@
#define ST_ACCEL_7_DRDY_IRQ_INT1_MASK 0x04
#define ST_ACCEL_7_MULTIREAD_BIT false
+/* CUSTOM VALUES FOR SENSOR 8 */
+#define ST_ACCEL_8_FS_AVL_2_GAIN IIO_G_TO_M_S_2(15600)
+#define ST_ACCEL_8_FS_AVL_4_GAIN IIO_G_TO_M_S_2(31200)
+#define ST_ACCEL_8_FS_AVL_8_GAIN IIO_G_TO_M_S_2(62500)
+#define ST_ACCEL_8_FS_AVL_16_GAIN IIO_G_TO_M_S_2(187500)
+
static const struct iio_chan_spec st_accel_8bit_channels[] = {
ST_SENSORS_LSM_CHANNELS(IIO_ACCEL,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
@@ -726,6 +732,73 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
.multi_read_bit = ST_ACCEL_7_MULTIREAD_BIT,
.bootime = 2,
},
+ {
+ .wai = ST_ACCEL_1_WAI_EXP,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LNG2DM_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_8bit_channels,
+ .odr = {
+ .addr = ST_ACCEL_1_ODR_ADDR,
+ .mask = ST_ACCEL_1_ODR_MASK,
+ .odr_avl = {
+ { 1, ST_ACCEL_1_ODR_AVL_1HZ_VAL, },
+ { 10, ST_ACCEL_1_ODR_AVL_10HZ_VAL, },
+ { 25, ST_ACCEL_1_ODR_AVL_25HZ_VAL, },
+ { 50, ST_ACCEL_1_ODR_AVL_50HZ_VAL, },
+ { 100, ST_ACCEL_1_ODR_AVL_100HZ_VAL, },
+ { 200, ST_ACCEL_1_ODR_AVL_200HZ_VAL, },
+ { 400, ST_ACCEL_1_ODR_AVL_400HZ_VAL, },
+ { 1600, ST_ACCEL_1_ODR_AVL_1600HZ_VAL, },
+ },
+ },
+ .pw = {
+ .addr = ST_ACCEL_1_ODR_ADDR,
+ .mask = ST_ACCEL_1_ODR_MASK,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = ST_ACCEL_1_FS_ADDR,
+ .mask = ST_ACCEL_1_FS_MASK,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = ST_ACCEL_1_FS_AVL_2_VAL,
+ .gain = ST_ACCEL_8_FS_AVL_2_GAIN,
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_4G,
+ .value = ST_ACCEL_1_FS_AVL_4_VAL,
+ .gain = ST_ACCEL_8_FS_AVL_4_GAIN,
+ },
+ [2] = {
+ .num = ST_ACCEL_FS_AVL_8G,
+ .value = ST_ACCEL_1_FS_AVL_8_VAL,
+ .gain = ST_ACCEL_8_FS_AVL_8_GAIN,
+ },
+ [3] = {
+ .num = ST_ACCEL_FS_AVL_16G,
+ .value = ST_ACCEL_1_FS_AVL_16_VAL,
+ .gain = ST_ACCEL_8_FS_AVL_16_GAIN,
+ },
+ },
+ },
+ .drdy_irq = {
+ .addr = ST_ACCEL_1_DRDY_IRQ_ADDR,
+ .mask_int1 = ST_ACCEL_1_DRDY_IRQ_INT1_MASK,
+ .mask_int2 = ST_ACCEL_1_DRDY_IRQ_INT2_MASK,
+ .addr_ihl = ST_ACCEL_1_IHL_IRQ_ADDR,
+ .mask_ihl = ST_ACCEL_1_IHL_IRQ_MASK,
+ .addr_stat_drdy = ST_SENSORS_DEFAULT_STAT_ADDR,
+ },
+ .multi_read_bit = ST_ACCEL_1_MULTIREAD_BIT,
+ .bootime = 2,
+ },
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index e9d427a5df7c..c0f8867aa1ea 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -84,6 +84,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis3l02dq",
.data = LIS3L02DQ_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lng2dm-accel",
+ .data = LNG2DM_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{ LIS3L02DQ_ACCEL_DEV_NAME },
+ { LNG2DM_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index efd43941d45d..c25ac50d4600 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -60,6 +60,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LSM303AGR_ACCEL_DEV_NAME },
{ LIS2DH12_ACCEL_DEV_NAME },
{ LIS3L02DQ_ACCEL_DEV_NAME },
+ { LNG2DM_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index bbdac07f4aaa..34b928cefeed 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -30,6 +30,7 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/pinctrl/consumer.h>
/* Registers */
#define AT91_ADC_CR 0x00 /* Control Register */
@@ -1347,6 +1348,32 @@ static int at91_adc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int at91_adc_suspend(struct device *dev)
+{
+ struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct at91_adc_state *st = iio_priv(idev);
+
+ pinctrl_pm_select_sleep_state(dev);
+ clk_disable_unprepare(st->clk);
+
+ return 0;
+}
+
+static int at91_adc_resume(struct device *dev)
+{
+ struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct at91_adc_state *st = iio_priv(idev);
+
+ clk_prepare_enable(st->clk);
+ pinctrl_pm_select_default_state(dev);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend, at91_adc_resume);
+
static struct at91_adc_caps at91sam9260_caps = {
.calc_startup_ticks = calc_startup_ticks_9260,
.num_channels = 4,
@@ -1441,6 +1468,7 @@ static struct platform_driver at91_adc_driver = {
.driver = {
.name = DRIVER_NAME,
.of_match_table = of_match_ptr(at91_adc_dt_ids),
+ .pm = &at91_adc_pm_ops,
},
};
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index c3cfacca2541..ad9dec30bb30 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -30,10 +30,28 @@
#include <linux/iio/buffer.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+
+#define DMA_BUFFER_SIZE SZ_2K
+
+struct tiadc_dma {
+ struct dma_slave_config conf;
+ struct dma_chan *chan;
+ dma_addr_t addr;
+ dma_cookie_t cookie;
+ u8 *buf;
+ int current_period;
+ int period_size;
+ u8 fifo_thresh;
+};
+
struct tiadc_device {
struct ti_tscadc_dev *mfd_tscadc;
+ struct tiadc_dma dma;
struct mutex fifo1_lock; /* to protect fifo access */
int channels;
+ int total_ch_enabled;
u8 channel_line[8];
u8 channel_step[8];
int buffer_en_ch_steps;
@@ -198,6 +216,67 @@ static irqreturn_t tiadc_worker_h(int irq, void *private)
return IRQ_HANDLED;
}
+static void tiadc_dma_rx_complete(void *param)
+{
+ struct iio_dev *indio_dev = param;
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ u8 *data;
+ int i;
+
+ data = dma->buf + dma->current_period * dma->period_size;
+ dma->current_period = 1 - dma->current_period; /* swap the buffer ID */
+
+ for (i = 0; i < dma->period_size; i += indio_dev->scan_bytes) {
+ iio_push_to_buffers(indio_dev, data);
+ data += indio_dev->scan_bytes;
+ }
+}
+
+static int tiadc_start_dma(struct iio_dev *indio_dev)
+{
+ struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
+ struct dma_async_tx_descriptor *desc;
+
+ dma->current_period = 0; /* We start to fill period 0 */
+ /*
+ * Make the fifo thresh as the multiple of total number of
+ * channels enabled, so make sure that cyclic DMA period
+ * length is also a multiple of total number of channels
+ * enabled. This ensures that no invalid data is reported
+ * to the stack via iio_push_to_buffers().
+ */
+ dma->fifo_thresh = rounddown(FIFO1_THRESHOLD + 1,
+ adc_dev->total_ch_enabled) - 1;
+ /* Make sure that period length is multiple of fifo thresh level */
+ dma->period_size = rounddown(DMA_BUFFER_SIZE / 2,
+ (dma->fifo_thresh + 1) * sizeof(u16));
+
+ dma->conf.src_maxburst = dma->fifo_thresh + 1;
+ dmaengine_slave_config(dma->chan, &dma->conf);
+
+ desc = dmaengine_prep_dma_cyclic(dma->chan, dma->addr,
+ dma->period_size * 2,
+ dma->period_size, DMA_DEV_TO_MEM,
+ DMA_PREP_INTERRUPT);
+ if (!desc)
+ return -EBUSY;
+
+ desc->callback = tiadc_dma_rx_complete;
+ desc->callback_param = indio_dev;
+
+ dma->cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(dma->chan);
+
+ tiadc_writel(adc_dev, REG_FIFO1THR, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMA1REQ, dma->fifo_thresh);
+ tiadc_writel(adc_dev, REG_DMAENABLE_SET, DMA_FIFO1);
+
+ return 0;
+}
+
static int tiadc_buffer_preenable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
@@ -218,20 +297,30 @@ 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 tiadc_dma *dma = &adc_dev->dma;
+ unsigned int irq_enable;
unsigned int enb = 0;
u8 bit;
tiadc_step_config(indio_dev);
- for_each_set_bit(bit, indio_dev->active_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->total_ch_enabled++;
+ }
adc_dev->buffer_en_ch_steps = enb;
+ if (dma->chan)
+ tiadc_start_dma(indio_dev);
+
am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb);
tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES
| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW);
- tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES
- | IRQENB_FIFO1OVRRUN);
+
+ irq_enable = IRQENB_FIFO1OVRRUN;
+ if (!dma->chan)
+ irq_enable |= IRQENB_FIFO1THRES;
+ tiadc_writel(adc_dev, REG_IRQENABLE, irq_enable);
return 0;
}
@@ -239,12 +328,18 @@ static int tiadc_buffer_postenable(struct iio_dev *indio_dev)
static int tiadc_buffer_predisable(struct iio_dev *indio_dev)
{
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
int fifo1count, i, read;
tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES |
IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW));
am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps);
adc_dev->buffer_en_ch_steps = 0;
+ adc_dev->total_ch_enabled = 0;
+ if (dma->chan) {
+ tiadc_writel(adc_dev, REG_DMAENABLE_CLEAR, 0x2);
+ dmaengine_terminate_async(dma->chan);
+ }
/* Flush FIFO of leftover data in the time it takes to disable adc */
fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT);
@@ -430,6 +525,41 @@ static const struct iio_info tiadc_info = {
.driver_module = THIS_MODULE,
};
+static int tiadc_request_dma(struct platform_device *pdev,
+ struct tiadc_device *adc_dev)
+{
+ struct tiadc_dma *dma = &adc_dev->dma;
+ dma_cap_mask_t mask;
+
+ /* Default slave configuration parameters */
+ dma->conf.direction = DMA_DEV_TO_MEM;
+ dma->conf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ dma->conf.src_addr = adc_dev->mfd_tscadc->tscadc_phys_base + REG_FIFO1;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_CYCLIC, mask);
+
+ /* Get a channel for RX */
+ dma->chan = dma_request_chan(adc_dev->mfd_tscadc->dev, "fifo1");
+ if (IS_ERR(dma->chan)) {
+ int ret = PTR_ERR(dma->chan);
+
+ dma->chan = NULL;
+ return ret;
+ }
+
+ /* RX buffer */
+ dma->buf = dma_alloc_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ &dma->addr, GFP_KERNEL);
+ if (!dma->buf)
+ goto err;
+
+ return 0;
+err:
+ dma_release_channel(dma->chan);
+ return -ENOMEM;
+}
+
static int tiadc_parse_dt(struct platform_device *pdev,
struct tiadc_device *adc_dev)
{
@@ -512,8 +642,14 @@ static int tiadc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, indio_dev);
+ err = tiadc_request_dma(pdev, adc_dev);
+ if (err && err == -EPROBE_DEFER)
+ goto err_dma;
+
return 0;
+err_dma:
+ iio_device_unregister(indio_dev);
err_buffer_unregister:
tiadc_iio_buffered_hardware_remove(indio_dev);
err_free_channels:
@@ -525,8 +661,14 @@ static int tiadc_remove(struct platform_device *pdev)
{
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
+ struct tiadc_dma *dma = &adc_dev->dma;
u32 step_en;
+ if (dma->chan) {
+ dma_free_coherent(dma->chan->device->dev, DMA_BUFFER_SIZE,
+ dma->buf, dma->addr);
+ dma_release_channel(dma->chan);
+ }
iio_device_unregister(indio_dev);
tiadc_iio_buffered_hardware_remove(indio_dev);
tiadc_channels_remove(indio_dev);
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index 26a6026de614..e108996a9627 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -2,6 +2,7 @@
# IIO common modules
#
+source "drivers/iio/common/cros_ec_sensors/Kconfig"
source "drivers/iio/common/hid-sensors/Kconfig"
source "drivers/iio/common/ms_sensors/Kconfig"
source "drivers/iio/common/ssp_sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
index 585da6a1b188..6fa760e1bdd5 100644
--- a/drivers/iio/common/Makefile
+++ b/drivers/iio/common/Makefile
@@ -7,6 +7,7 @@
#
# When adding new entries keep the list in alphabetical order
+obj-y += cros_ec_sensors/
obj-y += hid-sensors/
obj-y += ms_sensors/
obj-y += ssp_sensors/
diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
new file mode 100644
index 000000000000..135f6825903f
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -0,0 +1,22 @@
+#
+# Chrome OS Embedded Controller managed sensors library
+#
+config IIO_CROS_EC_SENSORS_CORE
+ tristate "ChromeOS EC Sensors Core"
+ depends on SYSFS && MFD_CROS_EC
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Base module for the ChromeOS EC Sensors module.
+ Contains core functions used by other IIO CrosEC sensor
+ drivers.
+ Define common attributes and sysfs interrupt handler.
+
+config IIO_CROS_EC_SENSORS
+ tristate "ChromeOS EC Contiguous Sensors"
+ depends on IIO_CROS_EC_SENSORS_CORE
+ help
+ Module to handle 3d contiguous sensors like
+ Accelerometers, Gyroscope and Magnetometer that are
+ presented by the ChromeOS EC Sensor hub.
+ Creates an IIO device for each functions.
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
new file mode 100644
index 000000000000..ec716ff2a775
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for sensors seen through the ChromeOS EC sensor hub.
+#
+
+obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
+obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
new file mode 100644
index 000000000000..d6c372bb433b
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -0,0 +1,322 @@
+/*
+ * cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about sensors data. Data access is presented through iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "cros_ec_sensors_core.h"
+
+#define CROS_EC_SENSORS_MAX_CHANNELS 4
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_sensors_state {
+ /* Shared by all sensors */
+ struct cros_ec_sensors_core_state core;
+
+ struct iio_chan_spec channels[CROS_EC_SENSORS_MAX_CHANNELS];
+};
+
+static int cros_ec_sensors_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+ s16 data = 0;
+ s64 val64;
+ int i;
+ int ret;
+ int idx = chan->scan_index;
+
+ mutex_lock(&st->core.cmd_lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = st->core.read_ec_sensors_data(indio_dev, 1 << idx, &data);
+ if (ret < 0)
+ break;
+
+ *val = data;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+ st->core.param.sensor_offset.flags = 0;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret < 0)
+ break;
+
+ /* Save values */
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ st->core.calib[i] =
+ st->core.resp->sensor_offset.offset[i];
+
+ *val = st->core.calib[idx];
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+ st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret < 0)
+ break;
+
+ val64 = st->core.resp->sensor_range.ret;
+ switch (st->core.type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ /*
+ * EC returns data in g, iio exepects m/s^2.
+ * Do not use IIO_G_TO_M_S_2 to avoid precision loss.
+ */
+ *val = div_s64(val64 * 980665, 10);
+ *val2 = 10000 << (CROS_EC_SENSOR_BITS - 1);
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ case MOTIONSENSE_TYPE_GYRO:
+ /*
+ * EC returns data in dps, iio expects rad/s.
+ * Do not use IIO_DEGREE_TO_RAD to avoid precision
+ * loss. Round to the nearest integer.
+ */
+ *val = div_s64(val64 * 314159 + 9000000ULL, 1000);
+ *val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ /*
+ * EC returns data in 16LSB / uT,
+ * iio expects Gauss
+ */
+ *val = val64;
+ *val2 = 100 << (CROS_EC_SENSOR_BITS - 1);
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+ default:
+ ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
+ mask);
+ break;
+ }
+ mutex_unlock(&st->core.cmd_lock);
+
+ return ret;
+}
+
+static int cros_ec_sensors_write(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct cros_ec_sensors_state *st = iio_priv(indio_dev);
+ int i;
+ int ret;
+ int idx = chan->scan_index;
+
+ mutex_lock(&st->core.cmd_lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ st->core.calib[idx] = val;
+
+ /* Send to EC for each axis, even if not complete */
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
+ st->core.param.sensor_offset.flags =
+ MOTION_SENSE_SET_OFFSET;
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ st->core.param.sensor_offset.offset[i] =
+ st->core.calib[i];
+ st->core.param.sensor_offset.temp =
+ EC_MOTION_SENSE_INVALID_CALIB_TEMP;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->core.type == MOTIONSENSE_TYPE_MAG) {
+ ret = -EINVAL;
+ break;
+ }
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+ st->core.param.sensor_range.data = val;
+
+ /* Always roundup, so caller gets at least what it asks for. */
+ st->core.param.sensor_range.roundup = 1;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ break;
+ default:
+ ret = cros_ec_sensors_core_write(
+ &st->core, chan, val, val2, mask);
+ break;
+ }
+
+ mutex_unlock(&st->core.cmd_lock);
+
+ return ret;
+}
+
+static const struct iio_info ec_sensors_info = {
+ .read_raw = &cros_ec_sensors_read,
+ .write_raw = &cros_ec_sensors_write,
+ .driver_module = THIS_MODULE,
+};
+
+static int cros_ec_sensors_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+ struct cros_ec_device *ec_device;
+ struct iio_dev *indio_dev;
+ struct cros_ec_sensors_state *state;
+ struct iio_chan_spec *channel;
+ int ret, i;
+
+ if (!ec_dev || !ec_dev->ec_dev) {
+ dev_warn(&pdev->dev, "No CROS EC device found.\n");
+ return -EINVAL;
+ }
+ ec_device = ec_dev->ec_dev;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &ec_sensors_info;
+ state = iio_priv(indio_dev);
+ for (channel = state->channels, i = CROS_EC_SENSOR_X;
+ i < CROS_EC_SENSOR_MAX_AXIS; i++, channel++) {
+ /* Common part */
+ channel->info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS);
+ channel->info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_FREQUENCY) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
+ channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
+ channel->scan_index = i;
+ channel->ext_info = cros_ec_sensors_ext_info;
+ channel->modified = 1;
+ channel->channel2 = IIO_MOD_X + i;
+ channel->scan_type.sign = 's';
+
+ /* Sensor specific */
+ switch (state->core.type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ channel->type = IIO_ACCEL;
+ break;
+ case MOTIONSENSE_TYPE_GYRO:
+ channel->type = IIO_ANGL_VEL;
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ channel->type = IIO_MAGN;
+ break;
+ default:
+ dev_err(&pdev->dev, "Unknown motion sensor\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Timestamp */
+ channel->type = IIO_TIMESTAMP;
+ channel->channel = -1;
+ channel->scan_index = CROS_EC_SENSOR_MAX_AXIS;
+ channel->scan_type.sign = 's';
+ channel->scan_type.realbits = 64;
+ channel->scan_type.storagebits = 64;
+
+ indio_dev->channels = state->channels;
+ indio_dev->num_channels = CROS_EC_SENSORS_MAX_CHANNELS;
+
+ /* There is only enough room for accel and gyro in the io space */
+ if ((state->core.ec->cmd_readmem != NULL) &&
+ (state->core.type != MOTIONSENSE_TYPE_MAG))
+ state->core.read_ec_sensors_data = cros_ec_sensors_read_lpc;
+ else
+ state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ cros_ec_sensors_capture, NULL);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_uninit_buffer;
+
+ return 0;
+
+error_uninit_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int cros_ec_sensors_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static const struct platform_device_id cros_ec_sensors_ids[] = {
+ {
+ .name = "cros-ec-accel",
+ },
+ {
+ .name = "cros-ec-gyro",
+ },
+ {
+ .name = "cros-ec-mag",
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_sensors_ids);
+
+static struct platform_driver cros_ec_sensors_platform_driver = {
+ .driver = {
+ .name = "cros-ec-sensors",
+ },
+ .probe = cros_ec_sensors_probe,
+ .remove = cros_ec_sensors_remove,
+ .id_table = cros_ec_sensors_ids,
+};
+module_platform_driver(cros_ec_sensors_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC 3-axis sensors driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
new file mode 100644
index 000000000000..a3be7991355e
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -0,0 +1,450 @@
+/*
+ * cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/mfd/cros_ec.h>
+#include <linux/mfd/cros_ec_commands.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/platform_device.h>
+
+#include "cros_ec_sensors_core.h"
+
+static char *cros_ec_loc[] = {
+ [MOTIONSENSE_LOC_BASE] = "base",
+ [MOTIONSENSE_LOC_LID] = "lid",
+ [MOTIONSENSE_LOC_MAX] = "unknown",
+};
+
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+ struct iio_dev *indio_dev,
+ bool physical_device)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
+ struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
+ struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ state->ec = ec->ec_dev;
+ state->msg = devm_kzalloc(&pdev->dev,
+ max((u16)sizeof(struct ec_params_motion_sense),
+ state->ec->max_response), GFP_KERNEL);
+ if (!state->msg)
+ return -ENOMEM;
+
+ state->resp = (struct ec_response_motion_sense *)state->msg->data;
+
+ mutex_init(&state->cmd_lock);
+
+ /* Set up the host command structure. */
+ state->msg->version = 2;
+ state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ state->msg->outsize = sizeof(struct ec_params_motion_sense);
+
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = pdev->name;
+
+ if (physical_device) {
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ state->param.cmd = MOTIONSENSE_CMD_INFO;
+ state->param.info.sensor_num = sensor_platform->sensor_num;
+ if (cros_ec_motion_send_host_cmd(state, 0)) {
+ dev_warn(dev, "Can not access sensor info\n");
+ return -EIO;
+ }
+ state->type = state->resp->info.type;
+ state->loc = state->resp->info.location;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_init);
+
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *state,
+ u16 opt_length)
+{
+ int ret;
+
+ if (opt_length)
+ state->msg->insize = min(opt_length, state->ec->max_response);
+ else
+ state->msg->insize = state->ec->max_response;
+
+ memcpy(state->msg->data, &state->param, sizeof(state->param));
+
+ ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
+ if (ret < 0)
+ return -EIO;
+
+ if (ret &&
+ state->resp != (struct ec_response_motion_sense *)state->msg->data)
+ memcpy(state->resp, state->msg->data, ret);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_motion_send_host_cmd);
+
+static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ int ret, i;
+ bool calibrate;
+
+ ret = strtobool(buf, &calibrate);
+ if (ret < 0)
+ return ret;
+ if (!calibrate)
+ return -EINVAL;
+
+ mutex_lock(&st->cmd_lock);
+ st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB;
+ ret = cros_ec_motion_send_host_cmd(st, 0);
+ if (ret != 0) {
+ dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n");
+ } else {
+ /* Save values */
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ st->calib[i] = st->resp->perform_calib.offset[i];
+ }
+ mutex_unlock(&st->cmd_lock);
+
+ return ret ? ret : len;
+}
+
+static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", cros_ec_loc[st->loc]);
+}
+
+const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[] = {
+ {
+ .name = "calibrate",
+ .shared = IIO_SHARED_BY_ALL,
+ .write = cros_ec_sensors_calibrate
+ },
+ {
+ .name = "location",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = cros_ec_sensors_loc
+ },
+ { },
+};
+EXPORT_SYMBOL_GPL(cros_ec_sensors_ext_info);
+
+/**
+ * cros_ec_sensors_idx_to_reg - convert index into offset in shared memory
+ * @st: pointer to state information for device
+ * @idx: sensor index (should be element of enum sensor_index)
+ *
+ * Return: address to read at
+ */
+static unsigned int cros_ec_sensors_idx_to_reg(
+ struct cros_ec_sensors_core_state *st,
+ unsigned int idx)
+{
+ /*
+ * When using LPC interface, only space for 2 Accel and one Gyro.
+ * First halfword of MOTIONSENSE_TYPE_ACCEL is used by angle.
+ */
+ if (st->type == MOTIONSENSE_TYPE_ACCEL)
+ return EC_MEMMAP_ACC_DATA + sizeof(u16) *
+ (1 + idx + st->param.info.sensor_num *
+ CROS_EC_SENSOR_MAX_AXIS);
+
+ return EC_MEMMAP_GYRO_DATA + sizeof(u16) * idx;
+}
+
+static int cros_ec_sensors_cmd_read_u8(struct cros_ec_device *ec,
+ unsigned int offset, u8 *dest)
+{
+ return ec->cmd_readmem(ec, offset, 1, dest);
+}
+
+static int cros_ec_sensors_cmd_read_u16(struct cros_ec_device *ec,
+ unsigned int offset, u16 *dest)
+{
+ __le16 tmp;
+ int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
+
+ if (ret >= 0)
+ *dest = le16_to_cpu(tmp);
+
+ return ret;
+}
+
+/**
+ * cros_ec_sensors_read_until_not_busy() - read until is not busy
+ *
+ * @st: pointer to state information for device
+ *
+ * Read from EC status byte until it reads not busy.
+ * Return: 8-bit status if ok, -errno on failure.
+ */
+static int cros_ec_sensors_read_until_not_busy(
+ struct cros_ec_sensors_core_state *st)
+{
+ struct cros_ec_device *ec = st->ec;
+ u8 status;
+ int ret, attempts = 0;
+
+ ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
+ if (ret < 0)
+ return ret;
+
+ while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
+ /* Give up after enough attempts, return error. */
+ if (attempts++ >= 50)
+ return -EIO;
+
+ /* Small delay every so often. */
+ if (attempts % 5 == 0)
+ msleep(25);
+
+ ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
+ &status);
+ if (ret < 0)
+ return ret;
+ }
+
+ return status;
+}
+
+/**
+ * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory
+ * @indio_dev: pointer to IIO device
+ * @scan_mask: bitmap of the sensor indices to scan
+ * @data: location to store data
+ *
+ * This is the unsafe function for reading the EC data. It does not guarantee
+ * that the EC will not modify the data as it is being read in.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+static int cros_ec_sensors_read_data_unsafe(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ struct cros_ec_device *ec = st->ec;
+ unsigned int i;
+ int ret;
+
+ /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
+ for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+ ret = cros_ec_sensors_cmd_read_u16(ec,
+ cros_ec_sensors_idx_to_reg(st, i),
+ data);
+ if (ret < 0)
+ return ret;
+
+ data++;
+ }
+
+ return 0;
+}
+
+int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ struct cros_ec_device *ec = st->ec;
+ u8 samp_id = 0xff, status = 0;
+ int ret, attempts = 0;
+
+ /*
+ * Continually read all data from EC until the status byte after
+ * all reads reflects that the EC is not busy and the sample id
+ * matches the sample id from before all reads. This guarantees
+ * that data read in was not modified by the EC while reading.
+ */
+ while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
+ EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
+ /* If we have tried to read too many times, return error. */
+ if (attempts++ >= 5)
+ return -EIO;
+
+ /* Read status byte until EC is not busy. */
+ status = cros_ec_sensors_read_until_not_busy(st);
+ if (status < 0)
+ return status;
+
+ /*
+ * Store the current sample id so that we can compare to the
+ * sample id after reading the data.
+ */
+ samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
+
+ /* Read all EC data, format it, and store it into data. */
+ ret = cros_ec_sensors_read_data_unsafe(indio_dev, scan_mask,
+ data);
+ if (ret < 0)
+ return ret;
+
+ /* Read status byte. */
+ ret = cros_ec_sensors_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS,
+ &status);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_lpc);
+
+int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned int i;
+
+ /* Read all sensor data through a command. */
+ st->param.cmd = MOTIONSENSE_CMD_DATA;
+ ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->data));
+ if (ret != 0) {
+ dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
+ return ret;
+ }
+
+ for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+ *data = st->resp->data.data[i];
+ data++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_read_cmd);
+
+irqreturn_t cros_ec_sensors_capture(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->cmd_lock);
+
+ /* Clear capture data. */
+ memset(st->samples, 0, indio_dev->scan_bytes);
+
+ /* Read data based on which channels are enabled in scan mask. */
+ ret = st->read_ec_sensors_data(indio_dev,
+ *(indio_dev->active_scan_mask),
+ (s16 *)st->samples);
+ if (ret < 0)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, st->samples,
+ iio_get_time_ns(indio_dev));
+
+done:
+ /*
+ * Tell the core we are done with this trigger and ready for the
+ * next one.
+ */
+ iio_trigger_notify_done(indio_dev->trig);
+
+ mutex_unlock(&st->cmd_lock);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_capture);
+
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret = IIO_VAL_INT;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+ st->param.ec_rate.data =
+ EC_MOTION_SENSE_NO_VALUE;
+
+ if (cros_ec_motion_send_host_cmd(st, 0))
+ ret = -EIO;
+ else
+ *val = st->resp->ec_rate.ret;
+ break;
+ case IIO_CHAN_INFO_FREQUENCY:
+ st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+ st->param.sensor_odr.data =
+ EC_MOTION_SENSE_NO_VALUE;
+
+ if (cros_ec_motion_send_host_cmd(st, 0))
+ ret = -EIO;
+ else
+ *val = st->resp->sensor_odr.ret;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
+
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_FREQUENCY:
+ st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
+ st->param.sensor_odr.data = val;
+
+ /* Always roundup, so caller gets at least what it asks for. */
+ st->param.sensor_odr.roundup = 1;
+
+ if (cros_ec_motion_send_host_cmd(st, 0))
+ ret = -EIO;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
+ st->param.ec_rate.data = val;
+
+ if (cros_ec_motion_send_host_cmd(st, 0))
+ ret = -EIO;
+ else
+ st->curr_sampl_freq = val;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_write);
+
+MODULE_DESCRIPTION("ChromeOS EC sensor hub core functions");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
new file mode 100644
index 000000000000..8bc2ca3c2e2e
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.h
@@ -0,0 +1,175 @@
+/*
+ * ChromeOS EC sensor hub
+ *
+ * Copyright (C) 2016 Google, Inc
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 __CROS_EC_SENSORS_CORE_H
+#define __CROS_EC_SENSORS_CORE_H
+
+#include <linux/irqreturn.h>
+
+enum {
+ CROS_EC_SENSOR_X,
+ CROS_EC_SENSOR_Y,
+ CROS_EC_SENSOR_Z,
+ CROS_EC_SENSOR_MAX_AXIS,
+};
+
+/* EC returns sensor values using signed 16 bit registers */
+#define CROS_EC_SENSOR_BITS 16
+
+/*
+ * 4 16 bit channels are allowed.
+ * Good enough for current sensors, they use up to 3 16 bit vectors.
+ */
+#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2)
+
+/* Minimum sampling period to use when device is suspending */
+#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */
+
+/**
+ * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver
+ * @ec: cros EC device structure
+ * @cmd_lock: lock used to prevent simultaneous access to the
+ * commands.
+ * @msg: cros EC command structure
+ * @param: motion sensor parameters structure
+ * @resp: motion sensor response structure
+ * @type: type of motion sensor
+ * @loc: location where the motion sensor is placed
+ * @calib: calibration parameters. Note that trigger
+ * captured data will always provide the calibrated
+ * data
+ * @samples: static array to hold data from a single capture.
+ * For each channel we need 2 bytes, except for
+ * the timestamp. The timestamp is always last and
+ * is always 8-byte aligned.
+ * @read_ec_sensors_data: function used for accessing sensors values
+ * @cuur_sampl_freq: current sampling period
+ */
+struct cros_ec_sensors_core_state {
+ struct cros_ec_device *ec;
+ struct mutex cmd_lock;
+
+ struct cros_ec_command *msg;
+ struct ec_params_motion_sense param;
+ struct ec_response_motion_sense *resp;
+
+ enum motionsensor_type type;
+ enum motionsensor_location loc;
+
+ s16 calib[CROS_EC_SENSOR_MAX_AXIS];
+
+ u8 samples[CROS_EC_SAMPLE_SIZE];
+
+ int (*read_ec_sensors_data)(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data);
+
+ int curr_sampl_freq;
+};
+
+/**
+ * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory
+ * @indio_dev: pointer to IIO device
+ * @scan_mask: bitmap of the sensor indices to scan
+ * @data: location to store data
+ *
+ * This is the safe function for reading the EC data. It guarantees that the
+ * data sampled was not modified by the EC while being read.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask,
+ s16 *data);
+
+/**
+ * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol
+ * @indio_dev: pointer to IIO device
+ * @scan_mask: bitmap of the sensor indices to scan
+ * @data: location to store data
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask,
+ s16 *data);
+
+/**
+ * cros_ec_sensors_core_init() - basic initialization of the core structure
+ * @pdev: platform device created for the sensors
+ * @indio_dev: iio device structure of the device
+ * @physical_device: true if the device refers to a physical device
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_sensors_core_init(struct platform_device *pdev,
+ struct iio_dev *indio_dev, bool physical_device);
+
+/**
+ * cros_ec_sensors_capture() - the trigger handler function
+ * @irq: the interrupt number.
+ * @p: a pointer to the poll function.
+ *
+ * On a trigger event occurring, if the pollfunc is attached then this
+ * handler is called as a threaded interrupt (and hence may sleep). It
+ * is responsible for grabbing data from the device and pushing it into
+ * the associated buffer.
+ *
+ * Return: IRQ_HANDLED
+ */
+irqreturn_t cros_ec_sensors_capture(int irq, void *p);
+
+/**
+ * cros_ec_motion_send_host_cmd() - send motion sense host command
+ * @st: pointer to state information for device
+ * @opt_length: optional length to reduce the response size, useful on the data
+ * path. Otherwise, the maximal allowed response size is used
+ *
+ * When called, the sub-command is assumed to be set in param->cmd.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
+int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st,
+ u16 opt_length);
+
+/**
+ * cros_ec_sensors_core_read() - function to request a value from the sensor
+ * @st: pointer to state information for device
+ * @chan: channel specification structure table
+ * @val: will contain one element making up the returned value
+ * @val2: will contain another element making up the returned value
+ * @mask: specifies which values to be requested
+ *
+ * Return: the type of value returned by the device
+ */
+int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+/**
+ * cros_ec_sensors_core_write() - function to write a value to the sensor
+ * @st: pointer to state information for device
+ * @chan: channel specification structure table
+ * @val: first part of value to write
+ * @val2: second part of value to write
+ * @mask: specifies which values to write
+ *
+ * Return: the type of value returned by the device
+ */
+int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask);
+
+/* List of extended channel specification for all sensors */
+extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[];
+
+#endif /* __CROS_EC_SENSORS_CORE_H */
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index dc33c1dd5191..4509f8475c54 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -201,7 +201,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st,
int ret;
if (val1 < 0 || val2 < 0)
- ret = -EINVAL;
+ return -EINVAL;
value = val1 * pow_10(6) + val2;
if (value) {
@@ -250,6 +250,9 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st,
s32 value;
int ret;
+ if (val1 < 0 || val2 < 0)
+ return -EINVAL;
+
value = convert_to_vtf_format(st->sensitivity.size,
st->sensitivity.unit_expo,
val1, val2);
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 205a84420ae9..107b5efd4178 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -84,6 +84,23 @@ config HID_SENSOR_GYRO_3D
Say yes here to build support for the HID SENSOR
Gyroscope 3D.
+config MPU3050
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP
+
+config MPU3050_I2C
+ tristate "Invensense MPU3050 devices on I2C"
+ depends on !(INPUT_MPU3050=y || INPUT_MPU3050=m)
+ select MPU3050
+ select REGMAP_I2C
+ select I2C_MUX
+ help
+ This driver supports the Invensense MPU3050 gyroscope over I2C.
+ This driver can be built as a module. The module will be called
+ inv-mpu3050-i2c.
+
config IIO_ST_GYRO_3AXIS
tristate "STMicroelectronics gyroscopes 3-Axis Driver"
depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index f866a4be0667..f0e149a606b0 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -14,6 +14,11 @@ obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
+# Currently this is rolled into one module, split it if
+# we ever create a separate SPI interface for MPU-3050
+obj-$(CONFIG_MPU3050) += mpu3050.o
+mpu3050-objs := mpu3050-core.o mpu3050-i2c.o
+
itg3200-y := itg3200_core.o
itg3200-$(CONFIG_IIO_BUFFER) += itg3200_buffer.o
obj-$(CONFIG_ITG3200) += itg3200.o
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
new file mode 100644
index 000000000000..ed681c70a7b4
--- /dev/null
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -0,0 +1,1307 @@
+/*
+ * MPU3050 gyroscope driver
+ *
+ * Copyright (C) 2016 Linaro Ltd.
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Based on the input subsystem driver, Copyright (C) 2011 Wistron Co.Ltd
+ * Joseph Lai <joseph_lai@wistron.com> and trimmed down by
+ * Alan Cox <alan@linux.intel.com> in turn based on bma023.c.
+ * Device behaviour based on a misc driver posted by Nathan Royer in 2011.
+ *
+ * TODO: add support for setting up the low pass 3dB frequency.
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/err.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>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/random.h>
+#include <linux/slab.h>
+
+#include "mpu3050.h"
+
+#define MPU3050_CHIP_ID 0x69
+
+/*
+ * Register map: anything suffixed *_H is a big-endian high byte and always
+ * followed by the corresponding low byte (*_L) even though these are not
+ * explicitly included in the register definitions.
+ */
+#define MPU3050_CHIP_ID_REG 0x00
+#define MPU3050_PRODUCT_ID_REG 0x01
+#define MPU3050_XG_OFFS_TC 0x05
+#define MPU3050_YG_OFFS_TC 0x08
+#define MPU3050_ZG_OFFS_TC 0x0B
+#define MPU3050_X_OFFS_USR_H 0x0C
+#define MPU3050_Y_OFFS_USR_H 0x0E
+#define MPU3050_Z_OFFS_USR_H 0x10
+#define MPU3050_FIFO_EN 0x12
+#define MPU3050_AUX_VDDIO 0x13
+#define MPU3050_SLV_ADDR 0x14
+#define MPU3050_SMPLRT_DIV 0x15
+#define MPU3050_DLPF_FS_SYNC 0x16
+#define MPU3050_INT_CFG 0x17
+#define MPU3050_AUX_ADDR 0x18
+#define MPU3050_INT_STATUS 0x1A
+#define MPU3050_TEMP_H 0x1B
+#define MPU3050_XOUT_H 0x1D
+#define MPU3050_YOUT_H 0x1F
+#define MPU3050_ZOUT_H 0x21
+#define MPU3050_DMP_CFG1 0x35
+#define MPU3050_DMP_CFG2 0x36
+#define MPU3050_BANK_SEL 0x37
+#define MPU3050_MEM_START_ADDR 0x38
+#define MPU3050_MEM_R_W 0x39
+#define MPU3050_FIFO_COUNT_H 0x3A
+#define MPU3050_FIFO_R 0x3C
+#define MPU3050_USR_CTRL 0x3D
+#define MPU3050_PWR_MGM 0x3E
+
+/* MPU memory bank read options */
+#define MPU3050_MEM_PRFTCH BIT(5)
+#define MPU3050_MEM_USER_BANK BIT(4)
+/* Bits 8-11 select memory bank */
+#define MPU3050_MEM_RAM_BANK_0 0
+#define MPU3050_MEM_RAM_BANK_1 1
+#define MPU3050_MEM_RAM_BANK_2 2
+#define MPU3050_MEM_RAM_BANK_3 3
+#define MPU3050_MEM_OTP_BANK_0 4
+
+#define MPU3050_AXIS_REGS(axis) (MPU3050_XOUT_H + (axis * 2))
+
+/* Register bits */
+
+/* FIFO Enable */
+#define MPU3050_FIFO_EN_FOOTER BIT(0)
+#define MPU3050_FIFO_EN_AUX_ZOUT BIT(1)
+#define MPU3050_FIFO_EN_AUX_YOUT BIT(2)
+#define MPU3050_FIFO_EN_AUX_XOUT BIT(3)
+#define MPU3050_FIFO_EN_GYRO_ZOUT BIT(4)
+#define MPU3050_FIFO_EN_GYRO_YOUT BIT(5)
+#define MPU3050_FIFO_EN_GYRO_XOUT BIT(6)
+#define MPU3050_FIFO_EN_TEMP_OUT BIT(7)
+
+/*
+ * Digital Low Pass filter (DLPF)
+ * Full Scale (FS)
+ * and Synchronization
+ */
+#define MPU3050_EXT_SYNC_NONE 0x00
+#define MPU3050_EXT_SYNC_TEMP 0x20
+#define MPU3050_EXT_SYNC_GYROX 0x40
+#define MPU3050_EXT_SYNC_GYROY 0x60
+#define MPU3050_EXT_SYNC_GYROZ 0x80
+#define MPU3050_EXT_SYNC_ACCELX 0xA0
+#define MPU3050_EXT_SYNC_ACCELY 0xC0
+#define MPU3050_EXT_SYNC_ACCELZ 0xE0
+#define MPU3050_EXT_SYNC_MASK 0xE0
+#define MPU3050_EXT_SYNC_SHIFT 5
+
+#define MPU3050_FS_250DPS 0x00
+#define MPU3050_FS_500DPS 0x08
+#define MPU3050_FS_1000DPS 0x10
+#define MPU3050_FS_2000DPS 0x18
+#define MPU3050_FS_MASK 0x18
+#define MPU3050_FS_SHIFT 3
+
+#define MPU3050_DLPF_CFG_256HZ_NOLPF2 0x00
+#define MPU3050_DLPF_CFG_188HZ 0x01
+#define MPU3050_DLPF_CFG_98HZ 0x02
+#define MPU3050_DLPF_CFG_42HZ 0x03
+#define MPU3050_DLPF_CFG_20HZ 0x04
+#define MPU3050_DLPF_CFG_10HZ 0x05
+#define MPU3050_DLPF_CFG_5HZ 0x06
+#define MPU3050_DLPF_CFG_2100HZ_NOLPF 0x07
+#define MPU3050_DLPF_CFG_MASK 0x07
+#define MPU3050_DLPF_CFG_SHIFT 0
+
+/* Interrupt config */
+#define MPU3050_INT_RAW_RDY_EN BIT(0)
+#define MPU3050_INT_DMP_DONE_EN BIT(1)
+#define MPU3050_INT_MPU_RDY_EN BIT(2)
+#define MPU3050_INT_ANYRD_2CLEAR BIT(4)
+#define MPU3050_INT_LATCH_EN BIT(5)
+#define MPU3050_INT_OPEN BIT(6)
+#define MPU3050_INT_ACTL BIT(7)
+/* Interrupt status */
+#define MPU3050_INT_STATUS_RAW_RDY BIT(0)
+#define MPU3050_INT_STATUS_DMP_DONE BIT(1)
+#define MPU3050_INT_STATUS_MPU_RDY BIT(2)
+#define MPU3050_INT_STATUS_FIFO_OVFLW BIT(7)
+/* USR_CTRL */
+#define MPU3050_USR_CTRL_FIFO_EN BIT(6)
+#define MPU3050_USR_CTRL_AUX_IF_EN BIT(5)
+#define MPU3050_USR_CTRL_AUX_IF_RST BIT(3)
+#define MPU3050_USR_CTRL_FIFO_RST BIT(1)
+#define MPU3050_USR_CTRL_GYRO_RST BIT(0)
+/* PWR_MGM */
+#define MPU3050_PWR_MGM_PLL_X 0x01
+#define MPU3050_PWR_MGM_PLL_Y 0x02
+#define MPU3050_PWR_MGM_PLL_Z 0x03
+#define MPU3050_PWR_MGM_CLKSEL_MASK 0x07
+#define MPU3050_PWR_MGM_STBY_ZG BIT(3)
+#define MPU3050_PWR_MGM_STBY_YG BIT(4)
+#define MPU3050_PWR_MGM_STBY_XG BIT(5)
+#define MPU3050_PWR_MGM_SLEEP BIT(6)
+#define MPU3050_PWR_MGM_RESET BIT(7)
+#define MPU3050_PWR_MGM_MASK 0xff
+
+/*
+ * Fullscale precision is (for finest precision) +/- 250 deg/s, so the full
+ * scale is actually 500 deg/s. All 16 bits are then used to cover this scale,
+ * in two's complement.
+ */
+static unsigned int mpu3050_fs_precision[] = {
+ IIO_DEGREE_TO_RAD(250),
+ IIO_DEGREE_TO_RAD(500),
+ IIO_DEGREE_TO_RAD(1000),
+ IIO_DEGREE_TO_RAD(2000)
+};
+
+/*
+ * Regulator names
+ */
+static const char mpu3050_reg_vdd[] = "vdd";
+static const char mpu3050_reg_vlogic[] = "vlogic";
+
+static unsigned int mpu3050_get_freq(struct mpu3050 *mpu3050)
+{
+ unsigned int freq;
+
+ if (mpu3050->lpf == MPU3050_DLPF_CFG_256HZ_NOLPF2)
+ freq = 8000;
+ else
+ freq = 1000;
+ freq /= (mpu3050->divisor + 1);
+
+ return freq;
+}
+
+static int mpu3050_start_sampling(struct mpu3050 *mpu3050)
+{
+ __be16 raw_val[3];
+ int ret;
+ int i;
+
+ /* Reset */
+ ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_RESET, MPU3050_PWR_MGM_RESET);
+ if (ret)
+ return ret;
+
+ /* Turn on the Z-axis PLL */
+ ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_CLKSEL_MASK,
+ MPU3050_PWR_MGM_PLL_Z);
+ if (ret)
+ return ret;
+
+ /* Write calibration offset registers */
+ for (i = 0; i < 3; i++)
+ raw_val[i] = cpu_to_be16(mpu3050->calibration[i]);
+
+ ret = regmap_bulk_write(mpu3050->map, MPU3050_X_OFFS_USR_H, raw_val,
+ sizeof(raw_val));
+ if (ret)
+ return ret;
+
+ /* Set low pass filter (sample rate), sync and full scale */
+ ret = regmap_write(mpu3050->map, MPU3050_DLPF_FS_SYNC,
+ MPU3050_EXT_SYNC_NONE << MPU3050_EXT_SYNC_SHIFT |
+ mpu3050->fullscale << MPU3050_FS_SHIFT |
+ mpu3050->lpf << MPU3050_DLPF_CFG_SHIFT);
+ if (ret)
+ return ret;
+
+ /* Set up sampling frequency */
+ ret = regmap_write(mpu3050->map, MPU3050_SMPLRT_DIV, mpu3050->divisor);
+ if (ret)
+ return ret;
+
+ /*
+ * Max 50 ms start-up time after setting DLPF_FS_SYNC
+ * according to the data sheet, then wait for the next sample
+ * at this frequency T = 1000/f ms.
+ */
+ msleep(50 + 1000 / mpu3050_get_freq(mpu3050));
+
+ return 0;
+}
+
+static int mpu3050_set_8khz_samplerate(struct mpu3050 *mpu3050)
+{
+ int ret;
+ u8 divisor;
+ enum mpu3050_lpf lpf;
+
+ lpf = mpu3050->lpf;
+ divisor = mpu3050->divisor;
+
+ mpu3050->lpf = LPF_256_HZ_NOLPF; /* 8 kHz base frequency */
+ mpu3050->divisor = 0; /* Divide by 1 */
+ ret = mpu3050_start_sampling(mpu3050);
+
+ mpu3050->lpf = lpf;
+ mpu3050->divisor = divisor;
+
+ return ret;
+}
+
+static int mpu3050_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ int ret;
+ __be16 raw_val;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /* The temperature scaling is (x+23000)/280 Celsius */
+ *val = 23000;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_CALIBBIAS:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = mpu3050->calibration[chan->scan_index-1];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = mpu3050_get_freq(mpu3050);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /* Millidegrees, see about temperature scaling above */
+ *val = 1000;
+ *val2 = 280;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_ANGL_VEL:
+ /*
+ * Convert to the corresponding full scale in
+ * radians. All 16 bits are used with sign to
+ * span the available scale: to account for the one
+ * missing value if we multiply by 1/S16_MAX, instead
+ * multiply with 2/U16_MAX.
+ */
+ *val = mpu3050_fs_precision[mpu3050->fullscale] * 2;
+ *val2 = U16_MAX;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ /* Resume device */
+ pm_runtime_get_sync(mpu3050->dev);
+ mutex_lock(&mpu3050->lock);
+
+ ret = mpu3050_set_8khz_samplerate(mpu3050);
+ if (ret)
+ goto out_read_raw_unlock;
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H,
+ &raw_val, sizeof(raw_val));
+ if (ret) {
+ dev_err(mpu3050->dev,
+ "error reading temperature\n");
+ goto out_read_raw_unlock;
+ }
+
+ *val = be16_to_cpu(raw_val);
+ ret = IIO_VAL_INT;
+
+ goto out_read_raw_unlock;
+ case IIO_ANGL_VEL:
+ ret = regmap_bulk_read(mpu3050->map,
+ MPU3050_AXIS_REGS(chan->scan_index-1),
+ &raw_val,
+ sizeof(raw_val));
+ if (ret) {
+ dev_err(mpu3050->dev,
+ "error reading axis data\n");
+ goto out_read_raw_unlock;
+ }
+
+ *val = be16_to_cpu(raw_val);
+ ret = IIO_VAL_INT;
+
+ goto out_read_raw_unlock;
+ default:
+ ret = -EINVAL;
+ goto out_read_raw_unlock;
+ }
+ default:
+ break;
+ }
+
+ return -EINVAL;
+
+out_read_raw_unlock:
+ mutex_unlock(&mpu3050->lock);
+ pm_runtime_mark_last_busy(mpu3050->dev);
+ pm_runtime_put_autosuspend(mpu3050->dev);
+
+ return ret;
+}
+
+static int mpu3050_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ /*
+ * Couldn't figure out a way to precalculate these at compile time.
+ */
+ unsigned int fs250 =
+ DIV_ROUND_CLOSEST(mpu3050_fs_precision[0] * 1000000 * 2,
+ U16_MAX);
+ unsigned int fs500 =
+ DIV_ROUND_CLOSEST(mpu3050_fs_precision[1] * 1000000 * 2,
+ U16_MAX);
+ unsigned int fs1000 =
+ DIV_ROUND_CLOSEST(mpu3050_fs_precision[2] * 1000000 * 2,
+ U16_MAX);
+ unsigned int fs2000 =
+ DIV_ROUND_CLOSEST(mpu3050_fs_precision[3] * 1000000 * 2,
+ U16_MAX);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ mpu3050->calibration[chan->scan_index-1] = val;
+ return 0;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ /*
+ * The max samplerate is 8000 Hz, the minimum
+ * 1000 / 256 ~= 4 Hz
+ */
+ if (val < 4 || val > 8000)
+ return -EINVAL;
+
+ /*
+ * Above 1000 Hz we must turn off the digital low pass filter
+ * so we get a base frequency of 8kHz to the divider
+ */
+ if (val > 1000) {
+ mpu3050->lpf = LPF_256_HZ_NOLPF;
+ mpu3050->divisor = DIV_ROUND_CLOSEST(8000, val) - 1;
+ return 0;
+ }
+
+ mpu3050->lpf = LPF_188_HZ;
+ mpu3050->divisor = DIV_ROUND_CLOSEST(1000, val) - 1;
+ return 0;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ /*
+ * We support +/-250, +/-500, +/-1000 and +/2000 deg/s
+ * which means we need to round to the closest radians
+ * which will be roughly +/-4.3, +/-8.7, +/-17.5, +/-35
+ * rad/s. The scale is then for the 16 bits used to cover
+ * it 2/(2^16) of that.
+ */
+
+ /* Just too large, set the max range */
+ if (val != 0) {
+ mpu3050->fullscale = FS_2000_DPS;
+ return 0;
+ }
+
+ /*
+ * Now we're dealing with fractions below zero in millirad/s
+ * do some integer interpolation and match with the closest
+ * fullscale in the table.
+ */
+ if (val2 <= fs250 ||
+ val2 < ((fs500 + fs250) / 2))
+ mpu3050->fullscale = FS_250_DPS;
+ else if (val2 <= fs500 ||
+ val2 < ((fs1000 + fs500) / 2))
+ mpu3050->fullscale = FS_500_DPS;
+ else if (val2 <= fs1000 ||
+ val2 < ((fs2000 + fs1000) / 2))
+ mpu3050->fullscale = FS_1000_DPS;
+ else
+ /* Catch-all */
+ mpu3050->fullscale = FS_2000_DPS;
+ return 0;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t mpu3050_trigger_handler(int irq, void *p)
+{
+ const struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ int ret;
+ /*
+ * Temperature 1*16 bits
+ * Three axes 3*16 bits
+ * Timestamp 64 bits (4*16 bits)
+ * Sum total 8*16 bits
+ */
+ __be16 hw_values[8];
+ s64 timestamp;
+ unsigned int datums_from_fifo = 0;
+
+ /*
+ * If we're using the hardware trigger, get the precise timestamp from
+ * the top half of the threaded IRQ handler. Otherwise get the
+ * timestamp here so it will be close in time to the actual values
+ * read from the registers.
+ */
+ if (iio_trigger_using_own(indio_dev))
+ timestamp = mpu3050->hw_timestamp;
+ else
+ timestamp = iio_get_time_ns(indio_dev);
+
+ mutex_lock(&mpu3050->lock);
+
+ /* Using the hardware IRQ trigger? Check the buffer then. */
+ if (mpu3050->hw_irq_trigger) {
+ __be16 raw_fifocnt;
+ u16 fifocnt;
+ /* X, Y, Z + temperature */
+ unsigned int bytes_per_datum = 8;
+ bool fifo_overflow = false;
+
+ ret = regmap_bulk_read(mpu3050->map,
+ MPU3050_FIFO_COUNT_H,
+ &raw_fifocnt,
+ sizeof(raw_fifocnt));
+ if (ret)
+ goto out_trigger_unlock;
+ fifocnt = be16_to_cpu(raw_fifocnt);
+
+ if (fifocnt == 512) {
+ dev_info(mpu3050->dev,
+ "FIFO overflow! Emptying and resetting FIFO\n");
+ fifo_overflow = true;
+ /* Reset and enable the FIFO */
+ ret = regmap_update_bits(mpu3050->map,
+ MPU3050_USR_CTRL,
+ MPU3050_USR_CTRL_FIFO_EN |
+ MPU3050_USR_CTRL_FIFO_RST,
+ MPU3050_USR_CTRL_FIFO_EN |
+ MPU3050_USR_CTRL_FIFO_RST);
+ if (ret) {
+ dev_info(mpu3050->dev, "error resetting FIFO\n");
+ goto out_trigger_unlock;
+ }
+ mpu3050->pending_fifo_footer = false;
+ }
+
+ if (fifocnt)
+ dev_dbg(mpu3050->dev,
+ "%d bytes in the FIFO\n",
+ fifocnt);
+
+ while (!fifo_overflow && fifocnt > bytes_per_datum) {
+ unsigned int toread;
+ unsigned int offset;
+ __be16 fifo_values[5];
+
+ /*
+ * If there is a FIFO footer in the pipe, first clear
+ * that out. This follows the complex algorithm in the
+ * datasheet that states that you may never leave the
+ * FIFO empty after the first reading: you have to
+ * always leave two footer bytes in it. The footer is
+ * in practice just two zero bytes.
+ */
+ if (mpu3050->pending_fifo_footer) {
+ toread = bytes_per_datum + 2;
+ offset = 0;
+ } else {
+ toread = bytes_per_datum;
+ offset = 1;
+ /* Put in some dummy value */
+ fifo_values[0] = 0xAAAA;
+ }
+
+ ret = regmap_bulk_read(mpu3050->map,
+ MPU3050_FIFO_R,
+ &fifo_values[offset],
+ toread);
+
+ dev_dbg(mpu3050->dev,
+ "%04x %04x %04x %04x %04x\n",
+ fifo_values[0],
+ fifo_values[1],
+ fifo_values[2],
+ fifo_values[3],
+ fifo_values[4]);
+
+ /* Index past the footer (fifo_values[0]) and push */
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ &fifo_values[1],
+ timestamp);
+
+ fifocnt -= toread;
+ datums_from_fifo++;
+ mpu3050->pending_fifo_footer = true;
+
+ /*
+ * If we're emptying the FIFO, just make sure to
+ * check if something new appeared.
+ */
+ if (fifocnt < bytes_per_datum) {
+ ret = regmap_bulk_read(mpu3050->map,
+ MPU3050_FIFO_COUNT_H,
+ &raw_fifocnt,
+ sizeof(raw_fifocnt));
+ if (ret)
+ goto out_trigger_unlock;
+ fifocnt = be16_to_cpu(raw_fifocnt);
+ }
+
+ if (fifocnt < bytes_per_datum)
+ dev_dbg(mpu3050->dev,
+ "%d bytes left in the FIFO\n",
+ fifocnt);
+
+ /*
+ * At this point, the timestamp that triggered the
+ * hardware interrupt is no longer valid for what
+ * we are reading (the interrupt likely fired for
+ * the value on the top of the FIFO), so set the
+ * timestamp to zero and let userspace deal with it.
+ */
+ timestamp = 0;
+ }
+ }
+
+ /*
+ * If we picked some datums from the FIFO that's enough, else
+ * fall through and just read from the current value registers.
+ * This happens in two cases:
+ *
+ * - We are using some other trigger (external, like an HRTimer)
+ * than the sensor's own sample generator. In this case the
+ * sensor is just set to the max sampling frequency and we give
+ * the trigger a copy of the latest value every time we get here.
+ *
+ * - The hardware trigger is active but unused and we actually use
+ * another trigger which calls here with a frequency higher
+ * than what the device provides data. We will then just read
+ * duplicate values directly from the hardware registers.
+ */
+ if (datums_from_fifo) {
+ dev_dbg(mpu3050->dev,
+ "read %d datums from the FIFO\n",
+ datums_from_fifo);
+ goto out_trigger_unlock;
+ }
+
+ ret = regmap_bulk_read(mpu3050->map, MPU3050_TEMP_H, &hw_values,
+ sizeof(hw_values));
+ if (ret) {
+ dev_err(mpu3050->dev,
+ "error reading axis data\n");
+ goto out_trigger_unlock;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, hw_values, timestamp);
+
+out_trigger_unlock:
+ mutex_unlock(&mpu3050->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mpu3050_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(mpu3050->dev);
+
+ /* Unless we have OUR trigger active, run at full speed */
+ if (!mpu3050->hw_irq_trigger)
+ return mpu3050_set_8khz_samplerate(mpu3050);
+
+ return 0;
+}
+
+static int mpu3050_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ pm_runtime_mark_last_busy(mpu3050->dev);
+ pm_runtime_put_autosuspend(mpu3050->dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops mpu3050_buffer_setup_ops = {
+ .preenable = mpu3050_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = mpu3050_buffer_postdisable,
+};
+
+static const struct iio_mount_matrix *
+mpu3050_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ return &mpu3050->orientation;
+}
+
+static const struct iio_chan_spec_ext_info mpu3050_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mpu3050_get_mount_matrix),
+ { },
+};
+
+#define MPU3050_AXIS_CHANNEL(axis, index) \
+ { \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_CALIBBIAS), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .ext_info = mpu3050_ext_info, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec mpu3050_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ MPU3050_AXIS_CHANNEL(X, 1),
+ MPU3050_AXIS_CHANNEL(Y, 2),
+ MPU3050_AXIS_CHANNEL(Z, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+/* Four channels apart from timestamp, scan mask = 0x0f */
+static const unsigned long mpu3050_scan_masks[] = { 0xf, 0 };
+
+/*
+ * These are just the hardcoded factors resulting from the more elaborate
+ * calculations done with fractions in the scale raw get/set functions.
+ */
+static IIO_CONST_ATTR(anglevel_scale_available,
+ "0.000122070 "
+ "0.000274658 "
+ "0.000518798 "
+ "0.001068115");
+
+static struct attribute *mpu3050_attributes[] = {
+ &iio_const_attr_anglevel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group mpu3050_attribute_group = {
+ .attrs = mpu3050_attributes,
+};
+
+static const struct iio_info mpu3050_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mpu3050_read_raw,
+ .write_raw = mpu3050_write_raw,
+ .attrs = &mpu3050_attribute_group,
+ .driver_module = THIS_MODULE,
+};
+
+/**
+ * mpu3050_read_mem() - read MPU-3050 internal memory
+ * @mpu3050: device to read from
+ * @bank: target bank
+ * @addr: target address
+ * @len: number of bytes
+ * @buf: the buffer to store the read bytes in
+ */
+static int mpu3050_read_mem(struct mpu3050 *mpu3050,
+ u8 bank,
+ u8 addr,
+ u8 len,
+ u8 *buf)
+{
+ int ret;
+
+ ret = regmap_write(mpu3050->map,
+ MPU3050_BANK_SEL,
+ bank);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(mpu3050->map,
+ MPU3050_MEM_START_ADDR,
+ addr);
+ if (ret)
+ return ret;
+
+ return regmap_bulk_read(mpu3050->map,
+ MPU3050_MEM_R_W,
+ buf,
+ len);
+}
+
+static int mpu3050_hw_init(struct mpu3050 *mpu3050)
+{
+ int ret;
+ u8 otp[8];
+
+ /* Reset */
+ ret = regmap_update_bits(mpu3050->map,
+ MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_RESET,
+ MPU3050_PWR_MGM_RESET);
+ if (ret)
+ return ret;
+
+ /* Turn on the PLL */
+ ret = regmap_update_bits(mpu3050->map,
+ MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_CLKSEL_MASK,
+ MPU3050_PWR_MGM_PLL_Z);
+ if (ret)
+ return ret;
+
+ /* Disable IRQs */
+ ret = regmap_write(mpu3050->map,
+ MPU3050_INT_CFG,
+ 0);
+ if (ret)
+ return ret;
+
+ /* Read out the 8 bytes of OTP (one-time-programmable) memory */
+ ret = mpu3050_read_mem(mpu3050,
+ (MPU3050_MEM_PRFTCH |
+ MPU3050_MEM_USER_BANK |
+ MPU3050_MEM_OTP_BANK_0),
+ 0,
+ sizeof(otp),
+ otp);
+ if (ret)
+ return ret;
+
+ /* This is device-unique data so it goes into the entropy pool */
+ add_device_randomness(otp, sizeof(otp));
+
+ dev_info(mpu3050->dev,
+ "die ID: %04X, wafer ID: %02X, A lot ID: %04X, "
+ "W lot ID: %03X, WP ID: %01X, rev ID: %02X\n",
+ /* Die ID, bits 0-12 */
+ (otp[1] << 8 | otp[0]) & 0x1fff,
+ /* Wafer ID, bits 13-17 */
+ ((otp[2] << 8 | otp[1]) & 0x03e0) >> 5,
+ /* A lot ID, bits 18-33 */
+ ((otp[4] << 16 | otp[3] << 8 | otp[2]) & 0x3fffc) >> 2,
+ /* W lot ID, bits 34-45 */
+ ((otp[5] << 8 | otp[4]) & 0x3ffc) >> 2,
+ /* WP ID, bits 47-49 */
+ ((otp[6] << 8 | otp[5]) & 0x0380) >> 7,
+ /* rev ID, bits 50-55 */
+ otp[6] >> 2);
+
+ return 0;
+}
+
+static int mpu3050_power_up(struct mpu3050 *mpu3050)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(mpu3050->regs), mpu3050->regs);
+ if (ret) {
+ dev_err(mpu3050->dev, "cannot enable regulators\n");
+ return ret;
+ }
+ /*
+ * 20-100 ms start-up time for register read/write according to
+ * the datasheet, be on the safe side and wait 200 ms.
+ */
+ msleep(200);
+
+ /* Take device out of sleep mode */
+ ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_SLEEP, 0);
+ if (ret) {
+ dev_err(mpu3050->dev, "error setting power mode\n");
+ return ret;
+ }
+ msleep(10);
+
+ return 0;
+}
+
+static int mpu3050_power_down(struct mpu3050 *mpu3050)
+{
+ int ret;
+
+ /*
+ * Put MPU-3050 into sleep mode before cutting regulators.
+ * This is important, because we may not be the sole user
+ * of the regulator so the power may stay on after this, and
+ * then we would be wasting power unless we go to sleep mode
+ * first.
+ */
+ ret = regmap_update_bits(mpu3050->map, MPU3050_PWR_MGM,
+ MPU3050_PWR_MGM_SLEEP, MPU3050_PWR_MGM_SLEEP);
+ if (ret)
+ dev_err(mpu3050->dev, "error putting to sleep\n");
+
+ ret = regulator_bulk_disable(ARRAY_SIZE(mpu3050->regs), mpu3050->regs);
+ if (ret)
+ dev_err(mpu3050->dev, "error disabling regulators\n");
+
+ return 0;
+}
+
+static irqreturn_t mpu3050_irq_handler(int irq, void *p)
+{
+ struct iio_trigger *trig = p;
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ if (!mpu3050->hw_irq_trigger)
+ return IRQ_NONE;
+
+ /* Get the time stamp as close in time as possible */
+ mpu3050->hw_timestamp = iio_get_time_ns(indio_dev);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t mpu3050_irq_thread(int irq, void *p)
+{
+ struct iio_trigger *trig = p;
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ /* ACK IRQ and check if it was from us */
+ ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+ if (ret) {
+ dev_err(mpu3050->dev, "error reading IRQ status\n");
+ return IRQ_HANDLED;
+ }
+ if (!(val & MPU3050_INT_STATUS_RAW_RDY))
+ return IRQ_NONE;
+
+ iio_trigger_poll_chained(p);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * mpu3050_drdy_trigger_set_state() - set data ready interrupt state
+ * @trig: trigger instance
+ * @enable: true if trigger should be enabled, false to disable
+ */
+static int mpu3050_drdy_trigger_set_state(struct iio_trigger *trig,
+ bool enable)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ unsigned int val;
+ int ret;
+
+ /* Disabling trigger: disable interrupt and return */
+ if (!enable) {
+ /* Disable all interrupts */
+ ret = regmap_write(mpu3050->map,
+ MPU3050_INT_CFG,
+ 0);
+ if (ret)
+ dev_err(mpu3050->dev, "error disabling IRQ\n");
+
+ /* Clear IRQ flag */
+ ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+ if (ret)
+ dev_err(mpu3050->dev, "error clearing IRQ status\n");
+
+ /* Disable all things in the FIFO and reset it */
+ ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN, 0);
+ if (ret)
+ dev_err(mpu3050->dev, "error disabling FIFO\n");
+
+ ret = regmap_write(mpu3050->map, MPU3050_USR_CTRL,
+ MPU3050_USR_CTRL_FIFO_RST);
+ if (ret)
+ dev_err(mpu3050->dev, "error resetting FIFO\n");
+
+ pm_runtime_mark_last_busy(mpu3050->dev);
+ pm_runtime_put_autosuspend(mpu3050->dev);
+ mpu3050->hw_irq_trigger = false;
+
+ return 0;
+ } else {
+ /* Else we're enabling the trigger from this point */
+ pm_runtime_get_sync(mpu3050->dev);
+ mpu3050->hw_irq_trigger = true;
+
+ /* Disable all things in the FIFO */
+ ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN, 0);
+ if (ret)
+ return ret;
+
+ /* Reset and enable the FIFO */
+ ret = regmap_update_bits(mpu3050->map, MPU3050_USR_CTRL,
+ MPU3050_USR_CTRL_FIFO_EN |
+ MPU3050_USR_CTRL_FIFO_RST,
+ MPU3050_USR_CTRL_FIFO_EN |
+ MPU3050_USR_CTRL_FIFO_RST);
+ if (ret)
+ return ret;
+
+ mpu3050->pending_fifo_footer = false;
+
+ /* Turn on the FIFO for temp+X+Y+Z */
+ ret = regmap_write(mpu3050->map, MPU3050_FIFO_EN,
+ MPU3050_FIFO_EN_TEMP_OUT |
+ MPU3050_FIFO_EN_GYRO_XOUT |
+ MPU3050_FIFO_EN_GYRO_YOUT |
+ MPU3050_FIFO_EN_GYRO_ZOUT |
+ MPU3050_FIFO_EN_FOOTER);
+ if (ret)
+ return ret;
+
+ /* Configure the sample engine */
+ ret = mpu3050_start_sampling(mpu3050);
+ if (ret)
+ return ret;
+
+ /* Clear IRQ flag */
+ ret = regmap_read(mpu3050->map, MPU3050_INT_STATUS, &val);
+ if (ret)
+ dev_err(mpu3050->dev, "error clearing IRQ status\n");
+
+ /* Give us interrupts whenever there is new data ready */
+ val = MPU3050_INT_RAW_RDY_EN;
+
+ if (mpu3050->irq_actl)
+ val |= MPU3050_INT_ACTL;
+ if (mpu3050->irq_latch)
+ val |= MPU3050_INT_LATCH_EN;
+ if (mpu3050->irq_opendrain)
+ val |= MPU3050_INT_OPEN;
+
+ ret = regmap_write(mpu3050->map, MPU3050_INT_CFG, val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mpu3050_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = mpu3050_drdy_trigger_set_state,
+};
+
+static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq)
+{
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+ unsigned long irq_trig;
+ int ret;
+
+ mpu3050->trig = devm_iio_trigger_alloc(&indio_dev->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!mpu3050->trig)
+ return -ENOMEM;
+
+ /* Check if IRQ is open drain */
+ if (of_property_read_bool(mpu3050->dev->of_node, "drive-open-drain"))
+ mpu3050->irq_opendrain = true;
+
+ irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+ /*
+ * Configure the interrupt generator hardware to supply whatever
+ * the interrupt is configured for, edges low/high level low/high,
+ * we can provide it all.
+ */
+ switch (irq_trig) {
+ case IRQF_TRIGGER_RISING:
+ dev_info(&indio_dev->dev,
+ "pulse interrupts on the rising edge\n");
+ if (mpu3050->irq_opendrain) {
+ dev_info(&indio_dev->dev,
+ "rising edge incompatible with open drain\n");
+ mpu3050->irq_opendrain = false;
+ }
+ break;
+ case IRQF_TRIGGER_FALLING:
+ mpu3050->irq_actl = true;
+ dev_info(&indio_dev->dev,
+ "pulse interrupts on the falling edge\n");
+ break;
+ case IRQF_TRIGGER_HIGH:
+ mpu3050->irq_latch = true;
+ dev_info(&indio_dev->dev,
+ "interrupts active high level\n");
+ if (mpu3050->irq_opendrain) {
+ dev_info(&indio_dev->dev,
+ "active high incompatible with open drain\n");
+ mpu3050->irq_opendrain = false;
+ }
+ /*
+ * With level IRQs, we mask the IRQ until it is processed,
+ * but with edge IRQs (pulses) we can queue several interrupts
+ * in the top half.
+ */
+ irq_trig |= IRQF_ONESHOT;
+ break;
+ case IRQF_TRIGGER_LOW:
+ mpu3050->irq_latch = true;
+ mpu3050->irq_actl = true;
+ irq_trig |= IRQF_ONESHOT;
+ dev_info(&indio_dev->dev,
+ "interrupts active low level\n");
+ break;
+ default:
+ /* This is the most preferred mode, if possible */
+ dev_err(&indio_dev->dev,
+ "unsupported IRQ trigger specified (%lx), enforce "
+ "rising edge\n", irq_trig);
+ irq_trig = IRQF_TRIGGER_RISING;
+ break;
+ }
+
+ /* An open drain line can be shared with several devices */
+ if (mpu3050->irq_opendrain)
+ irq_trig |= IRQF_SHARED;
+
+ ret = request_threaded_irq(irq,
+ mpu3050_irq_handler,
+ mpu3050_irq_thread,
+ irq_trig,
+ mpu3050->trig->name,
+ mpu3050->trig);
+ if (ret) {
+ dev_err(mpu3050->dev,
+ "can't get IRQ %d, error %d\n", irq, ret);
+ return ret;
+ }
+
+ mpu3050->irq = irq;
+ mpu3050->trig->dev.parent = mpu3050->dev;
+ mpu3050->trig->ops = &mpu3050_trigger_ops;
+ iio_trigger_set_drvdata(mpu3050->trig, indio_dev);
+
+ ret = iio_trigger_register(mpu3050->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(mpu3050->trig);
+
+ return 0;
+}
+
+int mpu3050_common_probe(struct device *dev,
+ struct regmap *map,
+ int irq,
+ const char *name)
+{
+ struct iio_dev *indio_dev;
+ struct mpu3050 *mpu3050;
+ unsigned int val;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*mpu3050));
+ if (!indio_dev)
+ return -ENOMEM;
+ mpu3050 = iio_priv(indio_dev);
+
+ mpu3050->dev = dev;
+ mpu3050->map = map;
+ mutex_init(&mpu3050->lock);
+ /* Default fullscale: 2000 degrees per second */
+ mpu3050->fullscale = FS_2000_DPS;
+ /* 1 kHz, divide by 100, default frequency = 10 Hz */
+ mpu3050->lpf = MPU3050_DLPF_CFG_188HZ;
+ mpu3050->divisor = 99;
+
+ /* Read the mounting matrix, if present */
+ ret = of_iio_read_mount_matrix(dev, "mount-matrix",
+ &mpu3050->orientation);
+ if (ret)
+ return ret;
+
+ /* Fetch and turn on regulators */
+ mpu3050->regs[0].supply = mpu3050_reg_vdd;
+ mpu3050->regs[1].supply = mpu3050_reg_vlogic;
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(mpu3050->regs),
+ mpu3050->regs);
+ if (ret) {
+ dev_err(dev, "Cannot get regulators\n");
+ return ret;
+ }
+
+ ret = mpu3050_power_up(mpu3050);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(map, MPU3050_CHIP_ID_REG, &val);
+ if (ret) {
+ dev_err(dev, "could not read device ID\n");
+ ret = -ENODEV;
+
+ goto err_power_down;
+ }
+
+ if (val != MPU3050_CHIP_ID) {
+ dev_err(dev, "unsupported chip id %02x\n", (u8)val);
+ ret = -ENODEV;
+ goto err_power_down;
+ }
+
+ ret = regmap_read(map, MPU3050_PRODUCT_ID_REG, &val);
+ if (ret) {
+ dev_err(dev, "could not read device ID\n");
+ ret = -ENODEV;
+
+ goto err_power_down;
+ }
+ dev_info(dev, "found MPU-3050 part no: %d, version: %d\n",
+ ((val >> 4) & 0xf), (val & 0xf));
+
+ ret = mpu3050_hw_init(mpu3050);
+ if (ret)
+ goto err_power_down;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->channels = mpu3050_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mpu3050_channels);
+ indio_dev->info = &mpu3050_info;
+ indio_dev->available_scan_masks = mpu3050_scan_masks;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = name;
+
+ ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+ mpu3050_trigger_handler,
+ &mpu3050_buffer_setup_ops);
+ if (ret) {
+ dev_err(dev, "triggered buffer setup failed\n");
+ goto err_power_down;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "device register failed\n");
+ goto err_cleanup_buffer;
+ }
+
+ dev_set_drvdata(dev, indio_dev);
+
+ /* Check if we have an assigned IRQ to use as trigger */
+ if (irq) {
+ ret = mpu3050_trigger_probe(indio_dev, irq);
+ if (ret)
+ dev_err(dev, "failed to register trigger\n");
+ }
+
+ /* Enable runtime PM */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Set autosuspend to two orders of magnitude larger than the
+ * start-up time. 100ms start-up time means 10000ms autosuspend,
+ * i.e. 10 seconds.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 10000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ return 0;
+
+err_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_power_down:
+ mpu3050_power_down(mpu3050);
+
+ return ret;
+}
+EXPORT_SYMBOL(mpu3050_common_probe);
+
+int mpu3050_common_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (mpu3050->irq)
+ free_irq(mpu3050->irq, mpu3050);
+ iio_device_unregister(indio_dev);
+ mpu3050_power_down(mpu3050);
+
+ return 0;
+}
+EXPORT_SYMBOL(mpu3050_common_remove);
+
+#ifdef CONFIG_PM
+static int mpu3050_runtime_suspend(struct device *dev)
+{
+ return mpu3050_power_down(iio_priv(dev_get_drvdata(dev)));
+}
+
+static int mpu3050_runtime_resume(struct device *dev)
+{
+ return mpu3050_power_up(iio_priv(dev_get_drvdata(dev)));
+}
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops mpu3050_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(mpu3050_runtime_suspend,
+ mpu3050_runtime_resume, NULL)
+};
+EXPORT_SYMBOL(mpu3050_dev_pm_ops);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("MPU3050 gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/gyro/mpu3050-i2c.c b/drivers/iio/gyro/mpu3050-i2c.c
new file mode 100644
index 000000000000..06007200bf49
--- /dev/null
+++ b/drivers/iio/gyro/mpu3050-i2c.c
@@ -0,0 +1,124 @@
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mpu3050.h"
+
+static const struct regmap_config mpu3050_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int mpu3050_i2c_bypass_select(struct i2c_mux_core *mux, u32 chan_id)
+{
+ struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
+
+ /* Just power up the device, that is all that is needed */
+ pm_runtime_get_sync(mpu3050->dev);
+ return 0;
+}
+
+static int mpu3050_i2c_bypass_deselect(struct i2c_mux_core *mux, u32 chan_id)
+{
+ struct mpu3050 *mpu3050 = i2c_mux_priv(mux);
+
+ pm_runtime_mark_last_busy(mpu3050->dev);
+ pm_runtime_put_autosuspend(mpu3050->dev);
+ return 0;
+}
+
+static int mpu3050_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ const char *name;
+ struct mpu3050 *mpu3050;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EOPNOTSUPP;
+
+ if (id)
+ name = id->name;
+ else
+ return -ENODEV;
+
+ regmap = devm_regmap_init_i2c(client, &mpu3050_i2c_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ ret = mpu3050_common_probe(&client->dev, regmap, client->irq, name);
+ if (ret)
+ return ret;
+
+ /* The main driver is up, now register the I2C mux */
+ mpu3050 = iio_priv(dev_get_drvdata(&client->dev));
+ mpu3050->i2cmux = i2c_mux_alloc(client->adapter, &client->dev,
+ 1, 0, I2C_MUX_LOCKED | I2C_MUX_GATE,
+ mpu3050_i2c_bypass_select,
+ mpu3050_i2c_bypass_deselect);
+ /* Just fail the mux, there is no point in killing the driver */
+ if (!mpu3050->i2cmux)
+ dev_err(&client->dev, "failed to allocate I2C mux\n");
+ else {
+ mpu3050->i2cmux->priv = mpu3050;
+ ret = i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0);
+ if (ret)
+ dev_err(&client->dev, "failed to add I2C mux\n");
+ }
+
+ return 0;
+}
+
+static int mpu3050_i2c_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
+ struct mpu3050 *mpu3050 = iio_priv(indio_dev);
+
+ if (mpu3050->i2cmux)
+ i2c_mux_del_adapters(mpu3050->i2cmux);
+
+ return mpu3050_common_remove(&client->dev);
+}
+
+/*
+ * device id table is used to identify what device can be
+ * supported by this driver
+ */
+static const struct i2c_device_id mpu3050_i2c_id[] = {
+ { "mpu3050" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mpu3050_i2c_id);
+
+static const struct of_device_id mpu3050_i2c_of_match[] = {
+ { .compatible = "invensense,mpu3050", .data = "mpu3050" },
+ /* Deprecated vendor ID from the Input driver */
+ { .compatible = "invn,mpu3050", .data = "mpu3050" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mpu3050_i2c_of_match);
+
+static struct i2c_driver mpu3050_i2c_driver = {
+ .probe = mpu3050_i2c_probe,
+ .remove = mpu3050_i2c_remove,
+ .id_table = mpu3050_i2c_id,
+ .driver = {
+ .of_match_table = mpu3050_i2c_of_match,
+ .name = "mpu3050-i2c",
+ .pm = &mpu3050_dev_pm_ops,
+ },
+};
+module_i2c_driver(mpu3050_i2c_driver);
+
+MODULE_AUTHOR("Linus Walleij");
+MODULE_DESCRIPTION("Invensense MPU3050 gyroscope driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/gyro/mpu3050.h b/drivers/iio/gyro/mpu3050.h
new file mode 100644
index 000000000000..bef87a714dc5
--- /dev/null
+++ b/drivers/iio/gyro/mpu3050.h
@@ -0,0 +1,96 @@
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/i2c.h>
+
+/**
+ * enum mpu3050_fullscale - indicates the full range of the sensor in deg/sec
+ */
+enum mpu3050_fullscale {
+ FS_250_DPS = 0,
+ FS_500_DPS,
+ FS_1000_DPS,
+ FS_2000_DPS,
+};
+
+/**
+ * enum mpu3050_lpf - indicates the low pass filter width
+ */
+enum mpu3050_lpf {
+ /* This implicity sets sample frequency to 8 kHz */
+ LPF_256_HZ_NOLPF = 0,
+ /* All others sets the sample frequency to 1 kHz */
+ LPF_188_HZ,
+ LPF_98_HZ,
+ LPF_42_HZ,
+ LPF_20_HZ,
+ LPF_10_HZ,
+ LPF_5_HZ,
+ LPF_2100_HZ_NOLPF,
+};
+
+enum mpu3050_axis {
+ AXIS_X = 0,
+ AXIS_Y,
+ AXIS_Z,
+ AXIS_MAX,
+};
+
+/**
+ * struct mpu3050 - instance state container for the device
+ * @dev: parent device for this instance
+ * @orientation: mounting matrix, flipped axis etc
+ * @map: regmap to reach the registers
+ * @lock: serialization lock to marshal all requests
+ * @irq: the IRQ used for this device
+ * @regs: the regulators to power this device
+ * @fullscale: the current fullscale setting for the device
+ * @lpf: digital low pass filter setting for the device
+ * @divisor: base frequency divider: divides 8 or 1 kHz
+ * @calibration: the three signed 16-bit calibration settings that
+ * get written into the offset registers for each axis to compensate
+ * for DC offsets
+ * @trig: trigger for the MPU-3050 interrupt, if present
+ * @hw_irq_trigger: hardware interrupt trigger is in use
+ * @irq_actl: interrupt is active low
+ * @irq_latch: latched IRQ, this means that it is a level IRQ
+ * @irq_opendrain: the interrupt line shall be configured open drain
+ * @pending_fifo_footer: tells us if there is a pending footer in the FIFO
+ * that we have to read out first when handling the FIFO
+ * @hw_timestamp: latest hardware timestamp from the trigger IRQ, when in
+ * use
+ * @i2cmux: an I2C mux reflecting the fact that this sensor is a hub with
+ * a pass-through I2C interface coming out of it: this device needs to be
+ * powered up in order to reach devices on the other side of this mux
+ */
+struct mpu3050 {
+ struct device *dev;
+ struct iio_mount_matrix orientation;
+ struct regmap *map;
+ struct mutex lock;
+ int irq;
+ struct regulator_bulk_data regs[2];
+ enum mpu3050_fullscale fullscale;
+ enum mpu3050_lpf lpf;
+ u8 divisor;
+ s16 calibration[3];
+ struct iio_trigger *trig;
+ bool hw_irq_trigger;
+ bool irq_actl;
+ bool irq_latch;
+ bool irq_opendrain;
+ bool pending_fifo_footer;
+ s64 hw_timestamp;
+ struct i2c_mux_core *i2cmux;
+};
+
+/* Probe called from different transports */
+int mpu3050_common_probe(struct device *dev,
+ struct regmap *map,
+ int irq,
+ const char *name);
+int mpu3050_common_remove(struct device *dev);
+
+/* PM ops */
+extern const struct dev_pm_ops mpu3050_dev_pm_ops;
diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
index 76b2e8130b6f..72ddcdac21a2 100644
--- a/drivers/iio/humidity/hts221_buffer.c
+++ b/drivers/iio/humidity/hts221_buffer.c
@@ -15,7 +15,6 @@
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
-#include <linux/interrupt.h>
#include <linux/iio/events.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
index ffc2ccf6374e..345a7656c5ef 100644
--- a/drivers/iio/humidity/si7020.c
+++ b/drivers/iio/humidity/si7020.c
@@ -154,8 +154,17 @@ static const struct i2c_device_id si7020_id[] = {
};
MODULE_DEVICE_TABLE(i2c, si7020_id);
+static const struct of_device_id si7020_dt_ids[] = {
+ { .compatible = "silabs,si7020" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, si7020_dt_ids);
+
static struct i2c_driver si7020_driver = {
- .driver.name = "si7020",
+ .driver = {
+ .name = "si7020",
+ .of_match_table = of_match_ptr(si7020_dt_ids),
+ },
.probe = si7020_probe,
.id_table = si7020_id,
};
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
index 066161a4bccd..f962f31a5eb2 100644
--- a/drivers/iio/temperature/maxim_thermocouple.c
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -136,6 +136,8 @@ static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
ret = spi_read(data->spi, (void *)&buf32, storage_bytes);
*val = be32_to_cpu(buf32);
break;
+ default:
+ ret = -EINVAL;
}
if (ret)
diff --git a/drivers/mfd/ti_am335x_tscadc.c b/drivers/mfd/ti_am335x_tscadc.c
index c8f027b4ea4c..0f3fab47fe48 100644
--- a/drivers/mfd/ti_am335x_tscadc.c
+++ b/drivers/mfd/ti_am335x_tscadc.c
@@ -183,6 +183,7 @@ static int ti_tscadc_probe(struct platform_device *pdev)
tscadc->irq = err;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ tscadc->tscadc_phys_base = res->start;
tscadc->tscadc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(tscadc->tscadc_base))
return PTR_ERR(tscadc->tscadc_base);
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c
index 8abd80dbcbed..47268ecedc4d 100644
--- a/drivers/platform/chrome/cros_ec_dev.c
+++ b/drivers/platform/chrome/cros_ec_dev.c
@@ -18,6 +18,7 @@
*/
#include <linux/fs.h>
+#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -87,6 +88,41 @@ exit:
return ret;
}
+static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
+{
+ struct cros_ec_command *msg;
+ int ret;
+
+ if (ec->features[0] == -1U && ec->features[1] == -1U) {
+ /* features bitmap not read yet */
+
+ msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
+ if (!msg)
+ return -ENOMEM;
+
+ msg->version = 0;
+ msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
+ msg->insize = sizeof(ec->features);
+ msg->outsize = 0;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
+ ret, msg->result);
+ memset(ec->features, 0, sizeof(ec->features));
+ }
+
+ memcpy(ec->features, msg->data, sizeof(ec->features));
+
+ dev_dbg(ec->dev, "EC features %08x %08x\n",
+ ec->features[0], ec->features[1]);
+
+ kfree(msg);
+ }
+
+ return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
+}
+
/* Device file ops */
static int ec_device_open(struct inode *inode, struct file *filp)
{
@@ -230,6 +266,123 @@ static void __remove(struct device *dev)
kfree(ec);
}
+static void cros_ec_sensors_register(struct cros_ec_dev *ec)
+{
+ /*
+ * Issue a command to get the number of sensor reported.
+ * Build an array of sensors driver and register them all.
+ */
+ int ret, i, id, sensor_num;
+ struct mfd_cell *sensor_cells;
+ struct cros_ec_sensor_platform *sensor_platforms;
+ int sensor_type[MOTIONSENSE_TYPE_MAX];
+ struct ec_params_motion_sense *params;
+ struct ec_response_motion_sense *resp;
+ struct cros_ec_command *msg;
+
+ msg = kzalloc(sizeof(struct cros_ec_command) +
+ max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
+ if (msg == NULL)
+ return;
+
+ msg->version = 2;
+ msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
+ msg->outsize = sizeof(*params);
+ msg->insize = sizeof(*resp);
+
+ params = (struct ec_params_motion_sense *)msg->data;
+ params->cmd = MOTIONSENSE_CMD_DUMP;
+
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
+ ret, msg->result);
+ goto error;
+ }
+
+ resp = (struct ec_response_motion_sense *)msg->data;
+ sensor_num = resp->dump.sensor_count;
+ /* Allocate 2 extra sensors in case lid angle or FIFO are needed */
+ sensor_cells = kzalloc(sizeof(struct mfd_cell) * (sensor_num + 2),
+ GFP_KERNEL);
+ if (sensor_cells == NULL)
+ goto error;
+
+ sensor_platforms = kzalloc(sizeof(struct cros_ec_sensor_platform) *
+ (sensor_num + 1), GFP_KERNEL);
+ if (sensor_platforms == NULL)
+ goto error_platforms;
+
+ memset(sensor_type, 0, sizeof(sensor_type));
+ id = 0;
+ for (i = 0; i < sensor_num; i++) {
+ params->cmd = MOTIONSENSE_CMD_INFO;
+ params->info.sensor_num = i;
+ ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
+ if (ret < 0 || msg->result != EC_RES_SUCCESS) {
+ dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
+ i, ret, msg->result);
+ continue;
+ }
+ switch (resp->info.type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ sensor_cells[id].name = "cros-ec-accel";
+ break;
+ case MOTIONSENSE_TYPE_GYRO:
+ sensor_cells[id].name = "cros-ec-gyro";
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ sensor_cells[id].name = "cros-ec-mag";
+ break;
+ case MOTIONSENSE_TYPE_PROX:
+ sensor_cells[id].name = "cros-ec-prox";
+ break;
+ case MOTIONSENSE_TYPE_LIGHT:
+ sensor_cells[id].name = "cros-ec-light";
+ break;
+ case MOTIONSENSE_TYPE_ACTIVITY:
+ sensor_cells[id].name = "cros-ec-activity";
+ break;
+ default:
+ dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
+ continue;
+ }
+ sensor_platforms[id].sensor_num = i;
+ sensor_cells[id].id = sensor_type[resp->info.type];
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+
+ sensor_type[resp->info.type]++;
+ id++;
+ }
+ if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2) {
+ sensor_platforms[id].sensor_num = sensor_num;
+
+ sensor_cells[id].name = "cros-ec-angle";
+ sensor_cells[id].id = 0;
+ sensor_cells[id].platform_data = &sensor_platforms[id];
+ sensor_cells[id].pdata_size =
+ sizeof(struct cros_ec_sensor_platform);
+ id++;
+ }
+ if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
+ sensor_cells[id].name = "cros-ec-ring";
+ id++;
+ }
+
+ ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
+ NULL, 0, NULL);
+ if (ret)
+ dev_err(ec->dev, "failed to add EC sensors\n");
+
+ kfree(sensor_platforms);
+error_platforms:
+ kfree(sensor_cells);
+error:
+ kfree(msg);
+}
+
static int ec_device_probe(struct platform_device *pdev)
{
int retval = -ENOMEM;
@@ -245,6 +398,8 @@ static int ec_device_probe(struct platform_device *pdev)
ec->ec_dev = dev_get_drvdata(dev->parent);
ec->dev = dev;
ec->cmd_offset = ec_platform->cmd_offset;
+ ec->features[0] = -1U; /* Not cached yet */
+ ec->features[1] = -1U; /* Not cached yet */
device_initialize(&ec->class_dev);
cdev_init(&ec->cdev, &fops);
@@ -282,6 +437,10 @@ static int ec_device_probe(struct platform_device *pdev)
goto dev_reg_failed;
}
+ /* check whether this EC is a sensor hub. */
+ if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
+ cros_ec_sensors_register(ec);
+
return 0;
dev_reg_failed:
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index bfa12ceb1e1f..1fb68c01abd5 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -152,7 +152,8 @@
*/
struct ad7192_state {
- struct regulator *reg;
+ struct regulator *avdd;
+ struct regulator *dvdd;
u16 int_vref_mv;
u32 mclk;
u32 f_order;
@@ -633,15 +634,30 @@ static int ad7192_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
- st->reg = devm_regulator_get(&spi->dev, "vcc");
- if (!IS_ERR(st->reg)) {
- ret = regulator_enable(st->reg);
- if (ret)
- return ret;
+ st->avdd = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->avdd))
+ return PTR_ERR(st->avdd);
- voltage_uv = regulator_get_voltage(st->reg);
+ ret = regulator_enable(st->avdd);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
+ }
+
+ st->dvdd = devm_regulator_get(&spi->dev, "dvdd");
+ if (IS_ERR(st->dvdd)) {
+ ret = PTR_ERR(st->dvdd);
+ goto error_disable_avdd;
}
+ ret = regulator_enable(st->dvdd);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified DVdd supply\n");
+ goto error_disable_avdd;
+ }
+
+ voltage_uv = regulator_get_voltage(st->avdd);
+
if (pdata->vref_mv)
st->int_vref_mv = pdata->vref_mv;
else if (voltage_uv)
@@ -675,7 +691,7 @@ static int ad7192_probe(struct spi_device *spi)
ret = ad_sd_setup_buffer_and_trigger(indio_dev);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad7192_setup(st, pdata);
if (ret)
@@ -688,9 +704,10 @@ static int ad7192_probe(struct spi_device *spi)
error_remove_trigger:
ad_sd_cleanup_buffer_and_trigger(indio_dev);
-error_disable_reg:
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+error_disable_dvdd:
+ regulator_disable(st->dvdd);
+error_disable_avdd:
+ regulator_disable(st->avdd);
return ret;
}
@@ -703,8 +720,8 @@ static int ad7192_remove(struct spi_device *spi)
iio_device_unregister(indio_dev);
ad_sd_cleanup_buffer_and_trigger(indio_dev);
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->dvdd);
+ regulator_disable(st->avdd);
return 0;
}
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
index c9a0c2aa602f..e14960038d3e 100644
--- a/drivers/staging/iio/adc/ad7780.c
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -173,14 +173,16 @@ static int ad7780_probe(struct spi_device *spi)
ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
- st->reg = devm_regulator_get(&spi->dev, "vcc");
- if (!IS_ERR(st->reg)) {
- ret = regulator_enable(st->reg);
- if (ret)
- return ret;
-
- voltage_uv = regulator_get_voltage(st->reg);
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
}
+ voltage_uv = regulator_get_voltage(st->reg);
st->chip_info =
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
@@ -222,8 +224,7 @@ static int ad7780_probe(struct spi_device *spi)
error_cleanup_buffer_and_trigger:
ad_sd_cleanup_buffer_and_trigger(indio_dev);
error_disable_reg:
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
return ret;
}
@@ -236,8 +237,7 @@ static int ad7780_remove(struct spi_device *spi)
iio_device_unregister(indio_dev);
ad_sd_cleanup_buffer_and_trigger(indio_dev);
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
return 0;
}
diff --git a/drivers/staging/iio/cdc/ad7746.c b/drivers/staging/iio/cdc/ad7746.c
index f41251ceeacd..81f8b9ee1120 100644
--- a/drivers/staging/iio/cdc/ad7746.c
+++ b/drivers/staging/iio/cdc/ad7746.c
@@ -70,8 +70,10 @@
#define AD7746_EXCSETUP_EXCLVL(x) (((x) & 0x3) << 0)
/* Config Register Bit Designations (AD7746_REG_CFG) */
-#define AD7746_CONF_VTFS(x) ((x) << 6)
-#define AD7746_CONF_CAPFS(x) ((x) << 3)
+#define AD7746_CONF_VTFS_SHIFT 6
+#define AD7746_CONF_CAPFS_SHIFT 3
+#define AD7746_CONF_VTFS_MASK GENMASK(7, 6)
+#define AD7746_CONF_CAPFS_MASK GENMASK(5, 3)
#define AD7746_CONF_MODE_IDLE (0 << 0)
#define AD7746_CONF_MODE_CONT_CONV (1 << 0)
#define AD7746_CONF_MODE_SINGLE_CONV (2 << 0)
@@ -217,15 +219,16 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan)
{
struct ad7746_chip_info *chip = iio_priv(indio_dev);
- int ret, delay;
+ int ret, delay, idx;
u8 vt_setup, cap_setup;
switch (chan->type) {
case IIO_CAPACITANCE:
cap_setup = (chan->address & 0xFF) | AD7746_CAPSETUP_CAPEN;
vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
- delay = ad7746_cap_filter_rate_table[(chip->config >> 3) &
- 0x7][1];
+ idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
+ AD7746_CONF_CAPFS_SHIFT;
+ delay = ad7746_cap_filter_rate_table[idx][1];
if (chip->capdac_set != chan->channel) {
ret = i2c_smbus_write_byte_data(chip->client,
@@ -246,8 +249,9 @@ static int ad7746_select_channel(struct iio_dev *indio_dev,
case IIO_TEMP:
vt_setup = (chan->address & 0xFF) | AD7746_VTSETUP_VTEN;
cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
- delay = ad7746_cap_filter_rate_table[(chip->config >> 6) &
- 0x3][1];
+ idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
+ AD7746_CONF_VTFS_SHIFT;
+ delay = ad7746_cap_filter_rate_table[idx][1];
break;
default:
return -EINVAL;
@@ -369,8 +373,8 @@ static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
- chip->config &= ~AD7746_CONF_CAPFS(0x7);
- chip->config |= AD7746_CONF_CAPFS(i);
+ chip->config &= ~AD7746_CONF_CAPFS_MASK;
+ chip->config |= i << AD7746_CONF_CAPFS_SHIFT;
return 0;
}
@@ -387,8 +391,8 @@ static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
- chip->config &= ~AD7746_CONF_VTFS(0x3);
- chip->config |= AD7746_CONF_VTFS(i);
+ chip->config &= ~AD7746_CONF_VTFS_MASK;
+ chip->config |= i << AD7746_CONF_VTFS_SHIFT;
return 0;
}
@@ -527,7 +531,7 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct ad7746_chip_info *chip = iio_priv(indio_dev);
- int ret, delay;
+ int ret, delay, idx;
u8 regval, reg;
mutex_lock(&indio_dev->mlock);
@@ -635,13 +639,16 @@ static int ad7746_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_CAPACITANCE:
- *val = ad7746_cap_filter_rate_table[
- (chip->config >> 3) & 0x7][0];
+ idx = (chip->config & AD7746_CONF_CAPFS_MASK) >>
+ AD7746_CONF_CAPFS_SHIFT;
+ *val = ad7746_cap_filter_rate_table[idx][0];
ret = IIO_VAL_INT;
break;
case IIO_VOLTAGE:
- *val = ad7746_vt_filter_rate_table[
- (chip->config >> 6) & 0x3][0];
+ idx = (chip->config & AD7746_CONF_VTFS_MASK) >>
+ AD7746_CONF_VTFS_SHIFT;
+ *val = ad7746_vt_filter_rate_table[idx][0];
+ ret = IIO_VAL_INT;
break;
default:
ret = -EINVAL;
diff --git a/drivers/staging/iio/frequency/ad9832.c b/drivers/staging/iio/frequency/ad9832.c
index 358400b22d33..639047fade30 100644
--- a/drivers/staging/iio/frequency/ad9832.c
+++ b/drivers/staging/iio/frequency/ad9832.c
@@ -204,7 +204,6 @@ static int ad9832_probe(struct spi_device *spi)
struct ad9832_platform_data *pdata = dev_get_platdata(&spi->dev);
struct iio_dev *indio_dev;
struct ad9832_state *st;
- struct regulator *reg;
int ret;
if (!pdata) {
@@ -212,21 +211,35 @@ static int ad9832_probe(struct spi_device *spi)
return -ENODEV;
}
- reg = devm_regulator_get(&spi->dev, "vcc");
- if (!IS_ERR(reg)) {
- ret = regulator_enable(reg);
- if (ret)
- return ret;
+ st->avdd = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->avdd))
+ return PTR_ERR(st->avdd);
+
+ ret = regulator_enable(st->avdd);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
+ return ret;
+ }
+
+ st->dvdd = devm_regulator_get(&spi->dev, "dvdd");
+ if (IS_ERR(st->dvdd)) {
+ ret = PTR_ERR(st->dvdd);
+ goto error_disable_avdd;
+ }
+
+ ret = regulator_enable(st->dvdd);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified DVDD supply\n");
+ goto error_disable_avdd;
}
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev) {
ret = -ENOMEM;
- goto error_disable_reg;
+ goto error_disable_dvdd;
}
spi_set_drvdata(spi, indio_dev);
st = iio_priv(indio_dev);
- st->reg = reg;
st->mclk = pdata->mclk;
st->spi = spi;
@@ -277,42 +290,43 @@ static int ad9832_probe(struct spi_device *spi)
ret = spi_sync(st->spi, &st->msg);
if (ret) {
dev_err(&spi->dev, "device init failed\n");
- goto error_disable_reg;
+ goto error_disable_dvdd;
}
ret = ad9832_write_frequency(st, AD9832_FREQ0HM, pdata->freq0);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad9832_write_frequency(st, AD9832_FREQ1HM, pdata->freq1);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad9832_write_phase(st, AD9832_PHASE0H, pdata->phase0);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad9832_write_phase(st, AD9832_PHASE1H, pdata->phase1);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad9832_write_phase(st, AD9832_PHASE2H, pdata->phase2);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = ad9832_write_phase(st, AD9832_PHASE3H, pdata->phase3);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
ret = iio_device_register(indio_dev);
if (ret)
- goto error_disable_reg;
+ goto error_disable_dvdd;
return 0;
-error_disable_reg:
- if (!IS_ERR(reg))
- regulator_disable(reg);
+error_disable_dvdd:
+ regulator_disable(st->dvdd);
+error_disable_avdd:
+ regulator_disable(st->avdd);
return ret;
}
@@ -323,8 +337,8 @@ static int ad9832_remove(struct spi_device *spi)
struct ad9832_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->dvdd);
+ regulator_disable(st->avdd);
return 0;
}
diff --git a/drivers/staging/iio/frequency/ad9832.h b/drivers/staging/iio/frequency/ad9832.h
index d32323b46be6..1b08b04482a4 100644
--- a/drivers/staging/iio/frequency/ad9832.h
+++ b/drivers/staging/iio/frequency/ad9832.h
@@ -58,7 +58,8 @@
/**
* struct ad9832_state - driver instance specific data
* @spi: spi_device
- * @reg: supply regulator
+ * @avdd: supply regulator for the analog section
+ * @dvdd: supply regulator for the digital section
* @mclk: external master clock
* @ctrl_fp: cached frequency/phase control word
* @ctrl_ss: cached sync/selsrc control word
@@ -76,7 +77,8 @@
struct ad9832_state {
struct spi_device *spi;
- struct regulator *reg;
+ struct regulator *avdd;
+ struct regulator *dvdd;
unsigned long mclk;
unsigned short ctrl_fp;
unsigned short ctrl_ss;
diff --git a/drivers/staging/iio/frequency/ad9834.c b/drivers/staging/iio/frequency/ad9834.c
index 6366216e4f37..19216af1dfc9 100644
--- a/drivers/staging/iio/frequency/ad9834.c
+++ b/drivers/staging/iio/frequency/ad9834.c
@@ -329,11 +329,14 @@ static int ad9834_probe(struct spi_device *spi)
return -ENODEV;
}
- reg = devm_regulator_get(&spi->dev, "vcc");
- if (!IS_ERR(reg)) {
- ret = regulator_enable(reg);
- if (ret)
- return ret;
+ reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+
+ ret = regulator_enable(reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVDD supply\n");
+ return ret;
}
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
@@ -416,8 +419,7 @@ static int ad9834_probe(struct spi_device *spi)
return 0;
error_disable_reg:
- if (!IS_ERR(reg))
- regulator_disable(reg);
+ regulator_disable(reg);
return ret;
}
@@ -428,8 +430,7 @@ static int ad9834_remove(struct spi_device *spi)
struct ad9834_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
return 0;
}
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index 3892a7470410..944789843938 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -726,13 +726,16 @@ static int ad5933_probe(struct i2c_client *client,
if (!pdata)
pdata = &ad5933_default_pdata;
- st->reg = devm_regulator_get(&client->dev, "vcc");
- if (!IS_ERR(st->reg)) {
- ret = regulator_enable(st->reg);
- if (ret)
- return ret;
- voltage_uv = regulator_get_voltage(st->reg);
+ st->reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&client->dev, "Failed to enable specified VDD supply\n");
+ return ret;
}
+ voltage_uv = regulator_get_voltage(st->reg);
if (voltage_uv)
st->vref_mv = voltage_uv / 1000;
@@ -775,8 +778,7 @@ static int ad5933_probe(struct i2c_client *client,
error_unreg_ring:
iio_kfifo_free(indio_dev->buffer);
error_disable_reg:
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
return ret;
}
@@ -788,8 +790,7 @@ static int ad5933_remove(struct i2c_client *client)
iio_device_unregister(indio_dev);
iio_kfifo_free(indio_dev->buffer);
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
return 0;
}
diff --git a/drivers/staging/iio/light/tsl2583.c b/drivers/staging/iio/light/tsl2583.c
index 08f1583ee34e..7eab17f4557e 100644
--- a/drivers/staging/iio/light/tsl2583.c
+++ b/drivers/staging/iio/light/tsl2583.c
@@ -29,8 +29,7 @@
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
-
-#define TSL258X_MAX_DEVICE_REGS 32
+#include <linux/iio/sysfs.h>
/* Triton register offsets */
#define TSL258X_REG_MAX 8
@@ -65,6 +64,9 @@
/* Lux calculation constants */
#define TSL258X_LUX_CALC_OVER_FLOW 65535
+#define TSL2583_CHIP_ID 0x90
+#define TSL2583_CHIP_ID_MASK 0xf0
+
enum {
TSL258X_CHIP_UNKNOWN = 0,
TSL258X_CHIP_WORKING = 1,
@@ -124,14 +126,15 @@ static struct taos_lux taos_device_lux[11] = {
struct gainadj {
s16 ch0;
s16 ch1;
+ s16 mean;
};
/* Index = (0 - 3) Used to validate the gain selection index */
static const struct gainadj gainadj[] = {
- { 1, 1 },
- { 8, 8 },
- { 16, 16 },
- { 107, 115 }
+ { 1, 1, 1 },
+ { 8, 8, 8 },
+ { 16, 16, 16 },
+ { 107, 115, 111 }
};
/*
@@ -140,42 +143,23 @@ static const struct gainadj gainadj[] = {
*/
static void taos_defaults(struct tsl2583_chip *chip)
{
- /* Operational parameters */
+ /*
+ * The integration time must be a multiple of 50ms and within the
+ * range [50, 600] ms.
+ */
chip->taos_settings.als_time = 100;
- /* must be a multiple of 50mS */
+
+ /*
+ * This is an index into the gainadj table. Assume clear glass as the
+ * default.
+ */
chip->taos_settings.als_gain = 0;
- /* this is actually an index into the gain table */
- /* assume clear glass as default */
- chip->taos_settings.als_gain_trim = 1000;
- /* default gain trim to account for aperture effects */
- chip->taos_settings.als_cal_target = 130;
- /* Known external ALS reading used for calibration */
-}
-/*
- * Read a number of bytes starting at register (reg) location.
- * Return 0, or i2c_smbus_write_byte ERROR code.
- */
-static int
-taos_i2c_read(struct i2c_client *client, u8 reg, u8 *val, unsigned int len)
-{
- int i, ret;
+ /* Default gain trim to account for aperture effects */
+ chip->taos_settings.als_gain_trim = 1000;
- for (i = 0; i < len; i++) {
- /* select register to write */
- ret = i2c_smbus_write_byte(client, (TSL258X_CMD_REG | reg));
- if (ret < 0) {
- dev_err(&client->dev,
- "taos_i2c_read failed to write register %x\n",
- reg);
- return ret;
- }
- /* read the data */
- *val = i2c_smbus_read_byte(client);
- val++;
- reg++;
- }
- return 0;
+ /* Known external ALS reading used for calibration */
+ chip->taos_settings.als_cal_target = 130;
}
/*
@@ -204,40 +188,37 @@ static int taos_get_lux(struct iio_dev *indio_dev)
u32 ch0lux = 0;
u32 ch1lux = 0;
- if (mutex_trylock(&chip->als_mutex) == 0) {
- dev_info(&chip->client->dev, "taos_get_lux device is busy\n");
- return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
- }
-
if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
/* device is not enabled */
dev_err(&chip->client->dev, "taos_get_lux device is not enabled\n");
ret = -EBUSY;
- goto out_unlock;
+ goto done;
}
- ret = taos_i2c_read(chip->client, (TSL258X_CMD_REG), &buf[0], 1);
+ ret = i2c_smbus_read_byte_data(chip->client, TSL258X_CMD_REG);
if (ret < 0) {
dev_err(&chip->client->dev, "taos_get_lux failed to read CMD_REG\n");
- goto out_unlock;
+ goto done;
}
+
/* is data new & valid */
- if (!(buf[0] & TSL258X_STA_ADC_INTR)) {
+ if (!(ret & TSL258X_STA_ADC_INTR)) {
dev_err(&chip->client->dev, "taos_get_lux data not valid\n");
ret = chip->als_cur_info.lux; /* return LAST VALUE */
- goto out_unlock;
+ goto done;
}
for (i = 0; i < 4; i++) {
int reg = TSL258X_CMD_REG | (TSL258X_ALS_CHAN0LO + i);
- ret = taos_i2c_read(chip->client, reg, &buf[i], 1);
+ ret = i2c_smbus_read_byte_data(chip->client, reg);
if (ret < 0) {
dev_err(&chip->client->dev,
"taos_get_lux failed to read register %x\n",
reg);
- goto out_unlock;
+ goto done;
}
+ buf[i] = ret;
}
/*
@@ -252,7 +233,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
dev_err(&chip->client->dev,
"taos_i2c_write_command failed in taos_get_lux, err = %d\n",
ret);
- goto out_unlock; /* have no data, so return failure */
+ goto done; /* have no data, so return failure */
}
/* extract ALS/lux data */
@@ -269,7 +250,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
/* have no data, so return LAST VALUE */
ret = 0;
chip->als_cur_info.lux = 0;
- goto out_unlock;
+ goto done;
}
/* calculate ratio */
ratio = (ch1 << 15) / ch0;
@@ -295,7 +276,7 @@ static int taos_get_lux(struct iio_dev *indio_dev)
dev_dbg(&chip->client->dev, "No Data - Return last value\n");
ret = 0;
chip->als_cur_info.lux = 0;
- goto out_unlock;
+ goto done;
}
/* adjust for active time scale */
@@ -327,8 +308,7 @@ return_max:
chip->als_cur_info.lux = lux;
ret = lux;
-out_unlock:
- mutex_unlock(&chip->als_mutex);
+done:
return ret;
}
@@ -340,39 +320,25 @@ out_unlock:
static int taos_als_calibrate(struct iio_dev *indio_dev)
{
struct tsl2583_chip *chip = iio_priv(indio_dev);
- u8 reg_val;
unsigned int gain_trim_val;
int ret;
int lux_val;
- ret = i2c_smbus_write_byte(chip->client,
- (TSL258X_CMD_REG | TSL258X_CNTRL));
+ ret = i2c_smbus_read_byte_data(chip->client,
+ TSL258X_CMD_REG | TSL258X_CNTRL);
if (ret < 0) {
dev_err(&chip->client->dev,
- "taos_als_calibrate failed to reach the CNTRL register, ret=%d\n",
- ret);
+ "%s failed to read from the CNTRL register\n",
+ __func__);
return ret;
}
- reg_val = i2c_smbus_read_byte(chip->client);
- if ((reg_val & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
+ if ((ret & (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON))
!= (TSL258X_CNTL_ADC_ENBL | TSL258X_CNTL_PWR_ON)) {
dev_err(&chip->client->dev,
"taos_als_calibrate failed: device not powered on with ADC enabled\n");
- return -1;
- }
-
- ret = i2c_smbus_write_byte(chip->client,
- (TSL258X_CMD_REG | TSL258X_CNTRL));
- if (ret < 0) {
- dev_err(&chip->client->dev,
- "taos_als_calibrate failed to reach the STATUS register, ret=%d\n",
- ret);
- return ret;
- }
- reg_val = i2c_smbus_read_byte(chip->client);
-
- if ((reg_val & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
+ return -EINVAL;
+ } else if ((ret & TSL258X_STA_ADC_VALID) != TSL258X_STA_ADC_VALID) {
dev_err(&chip->client->dev,
"taos_als_calibrate failed: STATUS - ADC not valid.\n");
return -ENODATA;
@@ -433,17 +399,8 @@ static int taos_chip_on(struct iio_dev *indio_dev)
chip->als_saturation = als_count * 922; /* 90% of full scale */
chip->als_time_scale = (als_time + 25) / 50;
- /*
- * TSL258x Specific power-on / adc enable sequence
- * Power on the device 1st.
- */
- utmp = TSL258X_CNTL_PWR_ON;
- ret = i2c_smbus_write_byte_data(chip->client,
- TSL258X_CMD_REG | TSL258X_CNTRL, utmp);
- if (ret < 0) {
- dev_err(&chip->client->dev, "taos_chip_on failed on CNTRL reg.\n");
- return ret;
- }
+ /* Power on the device; ADC off. */
+ chip->taos_config[TSL258X_CNTRL] = TSL258X_CNTL_PWR_ON;
/*
* Use the following shadow copy for our delay before enabling ADC.
@@ -491,219 +448,60 @@ static int taos_chip_off(struct iio_dev *indio_dev)
/* Sysfs Interface Functions */
-static ssize_t taos_power_state_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t in_illuminance_input_target_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int ret;
- return sprintf(buf, "%d\n", chip->taos_chip_status);
-}
-
-static ssize_t taos_power_state_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- int value;
-
- if (kstrtoint(buf, 0, &value))
- return -EINVAL;
-
- if (!value)
- taos_chip_off(indio_dev);
- else
- taos_chip_on(indio_dev);
-
- return len;
-}
-
-static ssize_t taos_gain_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
- char gain[4] = {0};
-
- switch (chip->taos_settings.als_gain) {
- case 0:
- strcpy(gain, "001");
- break;
- case 1:
- strcpy(gain, "008");
- break;
- case 2:
- strcpy(gain, "016");
- break;
- case 3:
- strcpy(gain, "111");
- break;
- }
-
- return sprintf(buf, "%s\n", gain);
-}
-
-static ssize_t taos_gain_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
- int value;
-
- if (kstrtoint(buf, 0, &value))
- return -EINVAL;
-
- switch (value) {
- case 1:
- chip->taos_settings.als_gain = 0;
- break;
- case 8:
- chip->taos_settings.als_gain = 1;
- break;
- case 16:
- chip->taos_settings.als_gain = 2;
- break;
- case 111:
- chip->taos_settings.als_gain = 3;
- break;
- default:
- dev_err(dev, "Invalid Gain Index (must be 1,8,16,111)\n");
- return -1;
- }
-
- return len;
-}
-
-static ssize_t taos_gain_available_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%s\n", "1 8 16 111");
-}
-
-static ssize_t taos_als_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
-
- return sprintf(buf, "%d\n", chip->taos_settings.als_time);
-}
-
-static ssize_t taos_als_time_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
- int value;
-
- if (kstrtoint(buf, 0, &value))
- return -EINVAL;
-
- if ((value < 50) || (value > 650))
- return -EINVAL;
-
- if (value % 50)
- return -EINVAL;
-
- chip->taos_settings.als_time = value;
-
- return len;
-}
-
-static ssize_t taos_als_time_available_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%s\n",
- "50 100 150 200 250 300 350 400 450 500 550 600 650");
-}
-
-static ssize_t taos_als_trim_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
+ mutex_lock(&chip->als_mutex);
+ ret = sprintf(buf, "%d\n", chip->taos_settings.als_cal_target);
+ mutex_unlock(&chip->als_mutex);
- return sprintf(buf, "%d\n", chip->taos_settings.als_gain_trim);
+ return ret;
}
-static ssize_t taos_als_trim_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
+static ssize_t in_illuminance_input_target_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
int value;
- if (kstrtoint(buf, 0, &value))
+ if (kstrtoint(buf, 0, &value) || !value)
return -EINVAL;
- if (value)
- chip->taos_settings.als_gain_trim = value;
+ mutex_lock(&chip->als_mutex);
+ chip->taos_settings.als_cal_target = value;
+ mutex_unlock(&chip->als_mutex);
return len;
}
-static ssize_t taos_als_cal_target_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t in_illuminance_calibrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
-
- return sprintf(buf, "%d\n", chip->taos_settings.als_cal_target);
-}
-
-static ssize_t taos_als_cal_target_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
- struct tsl2583_chip *chip = iio_priv(indio_dev);
- int value;
-
- if (kstrtoint(buf, 0, &value))
- return -EINVAL;
-
- if (value)
- chip->taos_settings.als_cal_target = value;
-
- return len;
-}
-
-static ssize_t taos_lux_show(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- int ret;
-
- ret = taos_get_lux(dev_to_iio_dev(dev));
- if (ret < 0)
- return ret;
-
- return sprintf(buf, "%d\n", ret);
-}
-
-static ssize_t taos_do_calibrate(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
-{
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
int value;
- if (kstrtoint(buf, 0, &value))
+ if (kstrtoint(buf, 0, &value) || value != 1)
return -EINVAL;
- if (value == 1)
- taos_als_calibrate(indio_dev);
+ mutex_lock(&chip->als_mutex);
+ taos_als_calibrate(indio_dev);
+ mutex_unlock(&chip->als_mutex);
return len;
}
-static ssize_t taos_luxtable_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t in_illuminance_lux_table_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
int i;
int offset = 0;
@@ -727,14 +525,16 @@ static ssize_t taos_luxtable_show(struct device *dev,
return offset;
}
-static ssize_t taos_luxtable_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t len)
+static ssize_t in_illuminance_lux_table_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct tsl2583_chip *chip = iio_priv(indio_dev);
int value[ARRAY_SIZE(taos_device_lux) * 3 + 1];
- int n;
+ int n, ret = -EINVAL;
+
+ mutex_lock(&chip->als_mutex);
get_options(buf, ARRAY_SIZE(value), value);
@@ -746,60 +546,38 @@ static ssize_t taos_luxtable_store(struct device *dev,
n = value[0];
if ((n % 3) || n < 6 || n > ((ARRAY_SIZE(taos_device_lux) - 1) * 3)) {
dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
- return -EINVAL;
+ goto done;
}
if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
- return -EINVAL;
+ goto done;
}
- if (chip->taos_chip_status == TSL258X_CHIP_WORKING)
- taos_chip_off(indio_dev);
-
/* Zero out the table */
memset(taos_device_lux, 0, sizeof(taos_device_lux));
memcpy(taos_device_lux, &value[1], (value[0] * 4));
- taos_chip_on(indio_dev);
-
- return len;
-}
+ ret = len;
-static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
- taos_power_state_show, taos_power_state_store);
-
-static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
- taos_gain_show, taos_gain_store);
-static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
- taos_gain_available_show, NULL);
-
-static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
- taos_als_time_show, taos_als_time_store);
-static DEVICE_ATTR(illuminance0_integration_time_available, S_IRUGO,
- taos_als_time_available_show, NULL);
-
-static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
- taos_als_trim_show, taos_als_trim_store);
+done:
+ mutex_unlock(&chip->als_mutex);
-static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
- taos_als_cal_target_show, taos_als_cal_target_store);
+ return ret;
+}
-static DEVICE_ATTR(illuminance0_input, S_IRUGO, taos_lux_show, NULL);
-static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL, taos_do_calibrate);
-static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
- taos_luxtable_show, taos_luxtable_store);
+static IIO_CONST_ATTR(in_illuminance_calibscale_available, "1 8 16 111");
+static IIO_CONST_ATTR(in_illuminance_integration_time_available,
+ "0.000050 0.000100 0.000150 0.000200 0.000250 0.000300 0.000350 0.000400 0.000450 0.000500 0.000550 0.000600 0.000650");
+static IIO_DEVICE_ATTR_RW(in_illuminance_input_target, 0);
+static IIO_DEVICE_ATTR_WO(in_illuminance_calibrate, 0);
+static IIO_DEVICE_ATTR_RW(in_illuminance_lux_table, 0);
static struct attribute *sysfs_attrs_ctrl[] = {
- &dev_attr_power_state.attr,
- &dev_attr_illuminance0_calibscale.attr, /* Gain */
- &dev_attr_illuminance0_calibscale_available.attr,
- &dev_attr_illuminance0_integration_time.attr, /* I time*/
- &dev_attr_illuminance0_integration_time_available.attr,
- &dev_attr_illuminance0_calibbias.attr, /* trim */
- &dev_attr_illuminance0_input_target.attr,
- &dev_attr_illuminance0_input.attr,
- &dev_attr_illuminance0_calibrate.attr,
- &dev_attr_illuminance0_lux_table.attr,
+ &iio_const_attr_in_illuminance_calibscale_available.dev_attr.attr,
+ &iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_input_target.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_calibrate.dev_attr.attr,
+ &iio_dev_attr_in_illuminance_lux_table.dev_attr.attr,
NULL
};
@@ -807,15 +585,161 @@ static const struct attribute_group tsl2583_attribute_group = {
.attrs = sysfs_attrs_ctrl,
};
-/* Use the default register values to identify the Taos device */
-static int taos_tsl258x_device(unsigned char *bufp)
+static const struct iio_chan_spec tsl2583_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_IR,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ .type = IIO_LIGHT,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ },
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+};
+
+static int tsl2583_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->als_mutex);
+
+ if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
+ ret = -EBUSY;
+ goto read_done;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_LIGHT) {
+ ret = taos_get_lux(indio_dev);
+ if (ret < 0)
+ goto read_done;
+
+ /*
+ * From page 20 of the TSL2581, TSL2583 data
+ * sheet (TAOS134 − MARCH 2011):
+ *
+ * One of the photodiodes (channel 0) is
+ * sensitive to both visible and infrared light,
+ * while the second photodiode (channel 1) is
+ * sensitive primarily to infrared light.
+ */
+ if (chan->channel2 == IIO_MOD_LIGHT_BOTH)
+ *val = chip->als_cur_info.als_ch0;
+ else
+ *val = chip->als_cur_info.als_ch1;
+
+ ret = IIO_VAL_INT;
+ }
+ break;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type == IIO_LIGHT) {
+ ret = taos_get_lux(indio_dev);
+ if (ret < 0)
+ goto read_done;
+
+ *val = ret;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (chan->type == IIO_LIGHT) {
+ *val = chip->taos_settings.als_gain_trim;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT) {
+ *val = gainadj[chip->taos_settings.als_gain].mean;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT) {
+ *val = 0;
+ *val2 = chip->taos_settings.als_time;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ default:
+ break;
+ }
+
+read_done:
+ mutex_unlock(&chip->als_mutex);
+
+ return ret;
+}
+
+static int tsl2583_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
- return ((bufp[TSL258X_CHIPID] & 0xf0) == 0x90);
+ struct tsl2583_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->als_mutex);
+
+ if (chip->taos_chip_status != TSL258X_CHIP_WORKING) {
+ ret = -EBUSY;
+ goto write_done;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (chan->type == IIO_LIGHT) {
+ chip->taos_settings.als_gain_trim = val;
+ ret = 0;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gainadj); i++) {
+ if (gainadj[i].mean == val) {
+ chip->taos_settings.als_gain = i;
+ ret = 0;
+ break;
+ }
+ }
+ }
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT && !val && val2 >= 50 &&
+ val2 <= 650 && !(val2 % 50)) {
+ chip->taos_settings.als_time = val2;
+ ret = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+write_done:
+ mutex_unlock(&chip->als_mutex);
+
+ return ret;
}
static const struct iio_info tsl2583_info = {
.attrs = &tsl2583_attribute_group,
.driver_module = THIS_MODULE,
+ .read_raw = tsl2583_read_raw,
+ .write_raw = tsl2583_write_raw,
};
/*
@@ -825,8 +749,7 @@ static const struct iio_info tsl2583_info = {
static int taos_probe(struct i2c_client *clientp,
const struct i2c_device_id *idp)
{
- int i, ret;
- unsigned char buf[TSL258X_MAX_DEVICE_REGS];
+ int ret;
struct tsl2583_chip *chip;
struct iio_dev *indio_dev;
@@ -847,28 +770,17 @@ static int taos_probe(struct i2c_client *clientp,
chip->taos_chip_status = TSL258X_CHIP_UNKNOWN;
memcpy(chip->taos_config, taos_config, sizeof(chip->taos_config));
- for (i = 0; i < TSL258X_MAX_DEVICE_REGS; i++) {
- ret = i2c_smbus_write_byte(clientp,
- (TSL258X_CMD_REG | (TSL258X_CNTRL + i)));
- if (ret < 0) {
- dev_err(&clientp->dev,
- "i2c_smbus_write_byte to cmd reg failed in taos_probe(), err = %d\n",
- ret);
- return ret;
- }
- ret = i2c_smbus_read_byte(clientp);
- if (ret < 0) {
- dev_err(&clientp->dev,
- "i2c_smbus_read_byte from reg failed in taos_probe(), err = %d\n",
- ret);
- return ret;
- }
- buf[i] = ret;
+ ret = i2c_smbus_read_byte_data(clientp,
+ TSL258X_CMD_REG | TSL258X_CHIPID);
+ if (ret < 0) {
+ dev_err(&clientp->dev,
+ "%s failed to read the chip ID register\n", __func__);
+ return ret;
}
- if (!taos_tsl258x_device(buf)) {
- dev_info(&clientp->dev,
- "i2c device found but does not match expected id in taos_probe()\n");
+ if ((ret & TSL2583_CHIP_ID_MASK) != TSL2583_CHIP_ID) {
+ dev_info(&clientp->dev, "%s received an unknown chip ID %x\n",
+ __func__, ret);
return -EINVAL;
}
@@ -881,6 +793,8 @@ static int taos_probe(struct i2c_client *clientp,
}
indio_dev->info = &tsl2583_info;
+ indio_dev->channels = tsl2583_channels;
+ indio_dev->num_channels = ARRAY_SIZE(tsl2583_channels);
indio_dev->dev.parent = &clientp->dev;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->name = chip->client->name;
@@ -894,7 +808,9 @@ static int taos_probe(struct i2c_client *clientp,
taos_defaults(chip);
/* Make sure the chip is on */
- taos_chip_on(indio_dev);
+ ret = taos_chip_on(indio_dev);
+ if (ret < 0)
+ return ret;
dev_info(&clientp->dev, "Light sensor found.\n");
return 0;
@@ -947,11 +863,24 @@ static struct i2c_device_id taos_idtable[] = {
};
MODULE_DEVICE_TABLE(i2c, taos_idtable);
+#ifdef CONFIG_OF
+static const struct of_device_id taos2583_of_match[] = {
+ { .compatible = "amstaos,tsl2580", },
+ { .compatible = "amstaos,tsl2581", },
+ { .compatible = "amstaos,tsl2583", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, taos2583_of_match);
+#else
+#define taos2583_of_match NULL
+#endif
+
/* Driver definition */
static struct i2c_driver taos_driver = {
.driver = {
.name = "tsl2583",
.pm = TAOS_PM_OPS,
+ .of_match_table = taos2583_of_match,
},
.id_table = taos_idtable,
.probe = taos_probe,
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index 76f7ef4d3a0d..f62043a75f43 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -148,6 +148,15 @@ struct cros_ec_device {
int event_size;
};
+/**
+ * struct cros_ec_sensor_platform - ChromeOS EC sensor platform information
+ *
+ * @sensor_num: Id of the sensor, as reported by the EC.
+ */
+struct cros_ec_sensor_platform {
+ u8 sensor_num;
+};
+
/* struct cros_ec_platform - ChromeOS EC platform information
*
* @ec_name: name of EC device (e.g. 'cros-ec', 'cros-pd', ...)
@@ -175,6 +184,7 @@ struct cros_ec_dev {
struct cros_ec_device *ec_dev;
struct device *dev;
u16 cmd_offset;
+ u32 features[2];
};
/**
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 76728ff37d01..1683003603f3 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -713,6 +713,90 @@ struct ec_response_get_set_value {
/* More than one command can use these structs to get/set paramters. */
#define EC_CMD_GSV_PAUSE_IN_S5 0x0c
+/*****************************************************************************/
+/* List the features supported by the firmware */
+#define EC_CMD_GET_FEATURES 0x0d
+
+/* Supported features */
+enum ec_feature_code {
+ /*
+ * This image contains a limited set of features. Another image
+ * in RW partition may support more features.
+ */
+ EC_FEATURE_LIMITED = 0,
+ /*
+ * Commands for probing/reading/writing/erasing the flash in the
+ * EC are present.
+ */
+ EC_FEATURE_FLASH = 1,
+ /*
+ * Can control the fan speed directly.
+ */
+ EC_FEATURE_PWM_FAN = 2,
+ /*
+ * Can control the intensity of the keyboard backlight.
+ */
+ EC_FEATURE_PWM_KEYB = 3,
+ /*
+ * Support Google lightbar, introduced on Pixel.
+ */
+ EC_FEATURE_LIGHTBAR = 4,
+ /* Control of LEDs */
+ EC_FEATURE_LED = 5,
+ /* Exposes an interface to control gyro and sensors.
+ * The host goes through the EC to access these sensors.
+ * In addition, the EC may provide composite sensors, like lid angle.
+ */
+ EC_FEATURE_MOTION_SENSE = 6,
+ /* The keyboard is controlled by the EC */
+ EC_FEATURE_KEYB = 7,
+ /* The AP can use part of the EC flash as persistent storage. */
+ EC_FEATURE_PSTORE = 8,
+ /* The EC monitors BIOS port 80h, and can return POST codes. */
+ EC_FEATURE_PORT80 = 9,
+ /*
+ * Thermal management: include TMP specific commands.
+ * Higher level than direct fan control.
+ */
+ EC_FEATURE_THERMAL = 10,
+ /* Can switch the screen backlight on/off */
+ EC_FEATURE_BKLIGHT_SWITCH = 11,
+ /* Can switch the wifi module on/off */
+ EC_FEATURE_WIFI_SWITCH = 12,
+ /* Monitor host events, through for example SMI or SCI */
+ EC_FEATURE_HOST_EVENTS = 13,
+ /* The EC exposes GPIO commands to control/monitor connected devices. */
+ EC_FEATURE_GPIO = 14,
+ /* The EC can send i2c messages to downstream devices. */
+ EC_FEATURE_I2C = 15,
+ /* Command to control charger are included */
+ EC_FEATURE_CHARGER = 16,
+ /* Simple battery support. */
+ EC_FEATURE_BATTERY = 17,
+ /*
+ * Support Smart battery protocol
+ * (Common Smart Battery System Interface Specification)
+ */
+ EC_FEATURE_SMART_BATTERY = 18,
+ /* EC can dectect when the host hangs. */
+ EC_FEATURE_HANG_DETECT = 19,
+ /* Report power information, for pit only */
+ EC_FEATURE_PMU = 20,
+ /* Another Cros EC device is present downstream of this one */
+ EC_FEATURE_SUB_MCU = 21,
+ /* Support USB Power delivery (PD) commands */
+ EC_FEATURE_USB_PD = 22,
+ /* Control USB multiplexer, for audio through USB port for instance. */
+ EC_FEATURE_USB_MUX = 23,
+ /* Motion Sensor code has an internal software FIFO */
+ EC_FEATURE_MOTION_SENSE_FIFO = 24,
+};
+
+#define EC_FEATURE_MASK_0(event_code) (1UL << (event_code % 32))
+#define EC_FEATURE_MASK_1(event_code) (1UL << (event_code - 32))
+struct ec_response_get_features {
+ uint32_t flags[2];
+} __packed;
/*****************************************************************************/
/* Flash commands */
@@ -1315,6 +1399,24 @@ enum motionsense_command {
*/
MOTIONSENSE_CMD_KB_WAKE_ANGLE = 5,
+ /*
+ * Returns a single sensor data.
+ */
+ MOTIONSENSE_CMD_DATA = 6,
+
+ /*
+ * Perform low level calibration.. On sensors that support it, ask to
+ * do offset calibration.
+ */
+ MOTIONSENSE_CMD_PERFORM_CALIB = 10,
+
+ /*
+ * Sensor Offset command is a setter/getter command for the offset used
+ * for calibration. The offsets can be calculated by the host, or via
+ * PERFORM_CALIB command.
+ */
+ MOTIONSENSE_CMD_SENSOR_OFFSET = 11,
+
/* Number of motionsense sub-commands. */
MOTIONSENSE_NUM_CMDS
};
@@ -1335,12 +1437,18 @@ enum motionsensor_id {
enum motionsensor_type {
MOTIONSENSE_TYPE_ACCEL = 0,
MOTIONSENSE_TYPE_GYRO = 1,
+ MOTIONSENSE_TYPE_MAG = 2,
+ MOTIONSENSE_TYPE_PROX = 3,
+ MOTIONSENSE_TYPE_LIGHT = 4,
+ MOTIONSENSE_TYPE_ACTIVITY = 5,
+ MOTIONSENSE_TYPE_MAX
};
/* List of motion sensor locations. */
enum motionsensor_location {
MOTIONSENSE_LOC_BASE = 0,
MOTIONSENSE_LOC_LID = 1,
+ MOTIONSENSE_LOC_MAX,
};
/* List of motion sensor chips. */
@@ -1361,6 +1469,31 @@ enum motionsensor_chip {
*/
#define EC_MOTION_SENSE_NO_VALUE -1
+#define EC_MOTION_SENSE_INVALID_CALIB_TEMP 0x8000
+
+/* Set Calibration information */
+#define MOTION_SENSE_SET_OFFSET 1
+
+struct ec_response_motion_sensor_data {
+ /* Flags for each sensor. */
+ uint8_t flags;
+ /* Sensor number the data comes from */
+ uint8_t sensor_num;
+ /* Each sensor is up to 3-axis. */
+ union {
+ int16_t data[3];
+ struct {
+ uint16_t rsvd;
+ uint32_t timestamp;
+ } __packed;
+ struct {
+ uint8_t activity; /* motionsensor_activity */
+ uint8_t state;
+ int16_t add_info[2];
+ };
+ };
+} __packed;
+
struct ec_params_motion_sense {
uint8_t cmd;
union {
@@ -1378,9 +1511,37 @@ struct ec_params_motion_sense {
int16_t data;
} ec_rate, kb_wake_angle;
+ /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
+ struct {
+ uint8_t sensor_num;
+
+ /*
+ * bit 0: If set (MOTION_SENSE_SET_OFFSET), set
+ * the calibration information in the EC.
+ * If unset, just retrieve calibration information.
+ */
+ uint16_t flags;
+
+ /*
+ * Temperature at calibration, in units of 0.01 C
+ * 0x8000: invalid / unknown.
+ * 0x0: 0C
+ * 0x7fff: +327.67C
+ */
+ int16_t temp;
+
+ /*
+ * Offset for calibration.
+ * Unit:
+ * Accelerometer: 1/1024 g
+ * Gyro: 1/1024 deg/s
+ * Compass: 1/16 uT
+ */
+ int16_t offset[3];
+ } __packed sensor_offset;
+
/* Used for MOTIONSENSE_CMD_INFO. */
struct {
- /* Should be element of enum motionsensor_id. */
uint8_t sensor_num;
} info;
@@ -1410,11 +1571,14 @@ struct ec_response_motion_sense {
/* Flags representing the motion sensor module. */
uint8_t module_flags;
- /* Flags for each sensor in enum motionsensor_id. */
- uint8_t sensor_flags[EC_MOTION_SENSOR_COUNT];
+ /* Number of sensors managed directly by the EC. */
+ uint8_t sensor_count;
- /* Array of all sensor data. Each sensor is 3-axis. */
- int16_t data[3*EC_MOTION_SENSOR_COUNT];
+ /*
+ * Sensor data is truncated if response_max is too small
+ * for holding all the data.
+ */
+ struct ec_response_motion_sensor_data sensor[0];
} dump;
/* Used for MOTIONSENSE_CMD_INFO. */
@@ -1429,6 +1593,9 @@ struct ec_response_motion_sense {
uint8_t chip;
} info;
+ /* Used for MOTIONSENSE_CMD_DATA */
+ struct ec_response_motion_sensor_data data;
+
/*
* Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR,
* MOTIONSENSE_CMD_SENSOR_RANGE, and
@@ -1438,6 +1605,12 @@ struct ec_response_motion_sense {
/* Current value of the parameter queried. */
int32_t ret;
} ec_rate, sensor_odr, sensor_range, kb_wake_angle;
+
+ /* Used for MOTIONSENSE_CMD_SENSOR_OFFSET */
+ struct {
+ int16_t temp;
+ int16_t offset[3];
+ } sensor_offset, perform_calib;
};
} __packed;
diff --git a/include/linux/mfd/ti_am335x_tscadc.h b/include/linux/mfd/ti_am335x_tscadc.h
index 7f55b8b41032..b9a53e013bff 100644
--- a/include/linux/mfd/ti_am335x_tscadc.h
+++ b/include/linux/mfd/ti_am335x_tscadc.h
@@ -23,6 +23,8 @@
#define REG_IRQENABLE 0x02C
#define REG_IRQCLR 0x030
#define REG_IRQWAKEUP 0x034
+#define REG_DMAENABLE_SET 0x038
+#define REG_DMAENABLE_CLEAR 0x03c
#define REG_CTRL 0x040
#define REG_ADCFSM 0x044
#define REG_CLKDIV 0x04C
@@ -36,6 +38,7 @@
#define REG_FIFO0THR 0xE8
#define REG_FIFO1CNT 0xF0
#define REG_FIFO1THR 0xF4
+#define REG_DMA1REQ 0xF8
#define REG_FIFO0 0x100
#define REG_FIFO1 0x200
@@ -126,6 +129,10 @@
#define FIFOREAD_DATA_MASK (0xfff << 0)
#define FIFOREAD_CHNLID_MASK (0xf << 16)
+/* DMA ENABLE/CLEAR Register */
+#define DMA_FIFO0 BIT(0)
+#define DMA_FIFO1 BIT(1)
+
/* Sequencer Status */
#define SEQ_STATUS BIT(5)
#define CHARGE_STEP 0x11
@@ -155,6 +162,7 @@ struct ti_tscadc_dev {
struct device *dev;
struct regmap *regmap;
void __iomem *tscadc_base;
+ phys_addr_t tscadc_phys_base;
int irq;
int used_cells; /* 1-2 */
int tsc_wires;