From 3acddf74f807778f0593ab4aaf26189c93e55ff1 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 18 Mar 2015 10:52:06 +0100 Subject: [PATCH 01/14] iio: st-sensors: add support for lis3lv02d accelerometer This adds support for the LIS3LV02 accelerometer found in the ST Microelectronics Nomadik board series. Cc: devicetree@vger.kernel.org Cc: Denis CIOCCA Acked-by: Lee Jones Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/st-sensors.txt | 1 + drivers/iio/accel/st_accel.h | 1 + drivers/iio/accel/st_accel_core.c | 81 +++++++++++++++++++ drivers/iio/accel/st_accel_i2c.c | 4 + 4 files changed, 87 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index a7a0a15913ad..d2aaca974531 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -23,6 +23,7 @@ standard bindings from pinctrl/pinctrl-bindings.txt. Valid compatible strings: Accelerometers: +- st,lis3lv02dl-accel - st,lsm303dlh-accel - st,lsm303dlhc-accel - st,lis3dh-accel diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index fa9646034305..7ee9724b1428 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -14,6 +14,7 @@ #include #include +#define LIS3LV02DL_ACCEL_DEV_NAME "lis3lv02dl_accel" #define LSM303DLHC_ACCEL_DEV_NAME "lsm303dlhc_accel" #define LIS3DH_ACCEL_DEV_NAME "lis3dh" #define LSM330D_ACCEL_DEV_NAME "lsm330d_accel" diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 53f32629283a..58d1d13d552a 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -129,6 +129,30 @@ #define ST_ACCEL_3_IG1_EN_MASK 0x08 #define ST_ACCEL_3_MULTIREAD_BIT false +/* CUSTOM VALUES FOR SENSOR 4 */ +#define ST_ACCEL_4_WAI_EXP 0x3a +#define ST_ACCEL_4_ODR_ADDR 0x20 +#define ST_ACCEL_4_ODR_MASK 0x30 /* DF1 and DF0 */ +#define ST_ACCEL_4_ODR_AVL_40HZ_VAL 0x00 +#define ST_ACCEL_4_ODR_AVL_160HZ_VAL 0x01 +#define ST_ACCEL_4_ODR_AVL_640HZ_VAL 0x02 +#define ST_ACCEL_4_ODR_AVL_2560HZ_VAL 0x03 +#define ST_ACCEL_4_PW_ADDR 0x20 +#define ST_ACCEL_4_PW_MASK 0xc0 +#define ST_ACCEL_4_FS_ADDR 0x21 +#define ST_ACCEL_4_FS_MASK 0x80 +#define ST_ACCEL_4_FS_AVL_2_VAL 0X00 +#define ST_ACCEL_4_FS_AVL_6_VAL 0X01 +#define ST_ACCEL_4_FS_AVL_2_GAIN IIO_G_TO_M_S_2(1024) +#define ST_ACCEL_4_FS_AVL_6_GAIN IIO_G_TO_M_S_2(340) +#define ST_ACCEL_4_BDU_ADDR 0x21 +#define ST_ACCEL_4_BDU_MASK 0x40 +#define ST_ACCEL_4_DRDY_IRQ_ADDR 0x21 +#define ST_ACCEL_4_DRDY_IRQ_INT1_MASK 0x04 +#define ST_ACCEL_4_IG1_EN_ADDR 0x21 +#define ST_ACCEL_4_IG1_EN_MASK 0x08 +#define ST_ACCEL_4_MULTIREAD_BIT true + static const struct iio_chan_spec st_accel_12bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -373,6 +397,63 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = ST_ACCEL_3_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_ACCEL_4_WAI_EXP, + .sensors_supported = { + [0] = LIS3LV02DL_ACCEL_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_12bit_channels, + .odr = { + .addr = ST_ACCEL_4_ODR_ADDR, + .mask = ST_ACCEL_4_ODR_MASK, + .odr_avl = { + { 40, ST_ACCEL_4_ODR_AVL_40HZ_VAL }, + { 160, ST_ACCEL_4_ODR_AVL_160HZ_VAL, }, + { 640, ST_ACCEL_4_ODR_AVL_640HZ_VAL, }, + { 2560, ST_ACCEL_4_ODR_AVL_2560HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_ACCEL_4_PW_ADDR, + .mask = ST_ACCEL_4_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_ACCEL_4_FS_ADDR, + .mask = ST_ACCEL_4_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = ST_ACCEL_4_FS_AVL_2_VAL, + .gain = ST_ACCEL_4_FS_AVL_2_GAIN, + }, + [1] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = ST_ACCEL_4_FS_AVL_6_VAL, + .gain = ST_ACCEL_4_FS_AVL_6_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_ACCEL_4_BDU_ADDR, + .mask = ST_ACCEL_4_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_ACCEL_4_DRDY_IRQ_ADDR, + .mask_int1 = ST_ACCEL_4_DRDY_IRQ_INT1_MASK, + .ig1 = { + .en_addr = ST_ACCEL_4_IG1_EN_ADDR, + .en_mask = ST_ACCEL_4_IG1_EN_MASK, + }, + }, + .multi_read_bit = ST_ACCEL_4_MULTIREAD_BIT, + .bootime = 2, /* guess */ + }, }; static int st_accel_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index c7246bdd30b9..6b720c190b2d 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -20,6 +20,10 @@ #ifdef CONFIG_OF static const struct of_device_id st_accel_of_match[] = { + { + .compatible = "st,lis3lv02dl-accel", + .data = LIS3LV02DL_ACCEL_DEV_NAME, + }, { .compatible = "st,lsm303dlh-accel", .data = LSM303DLH_ACCEL_DEV_NAME, From 7cf78db585b13d5f43e5108cca6cdc5d9682793a Mon Sep 17 00:00:00 2001 From: Darshana Padmadas Date: Mon, 16 Mar 2015 17:18:20 +0530 Subject: [PATCH 02/14] iio: Add ABI documentation for illuminance raw and scale values in light This patchset adds ABI documentation for the following attributes: in_illuminance_scale, used atleast once in al3320a staging/iio/light/ in_illuminance_calibscale, used atleast once in cm32181 in_illuminance_input, used in cm3232 at least once in_illuminance_raw used atleast once in al3320a in_illuminance_clear_raw and in_illuminance_ir_raw exposed by gp2ap020a00f with modifiers IIO_MOD_LIGHT_CLEAR and IIO_MOD_LIGHT_IR respectively. Signed-off-by: Darshana Padmadas Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 6be17c2c5c65..7438e4efdf4c 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -297,6 +297,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_scale What: /sys/bus/iio/devices/iio:deviceX/in_pressure_scale What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_scale What: /sys/bus/iio/devices/iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_scale +What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_scale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -337,6 +338,7 @@ what /sys/bus/iio/devices/iio:deviceX/in_illuminance0_calibscale what /sys/bus/iio/devices/iio:deviceX/in_proximity0_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_calibscale What: /sys/bus/iio/devices/iio:deviceX/in_pressure_calibscale +What: /sys/bus/iio/devices/iio:deviceX/in_illuminance_calibscale KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: @@ -1167,9 +1169,13 @@ Description: values should behave in the same way as a distance, i.e. lower values indicate something is closer to the sensor. +What: /sys/.../iio:deviceX/in_illuminance_input +What: /sys/.../iio:deviceX/in_illuminance_raw What: /sys/.../iio:deviceX/in_illuminanceY_input What: /sys/.../iio:deviceX/in_illuminanceY_raw What: /sys/.../iio:deviceX/in_illuminanceY_mean_raw +What: /sys/.../iio:deviceX/in_illuminance_ir_raw +What: /sys/.../iio:deviceX/in_illuminance_clear_raw KernelVersion: 3.4 Contact: linux-iio@vger.kernel.org Description: From 7253606d383954c5b0470b5f27fbcedeec2e6d72 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 16 Mar 2015 20:17:15 +0100 Subject: [PATCH 03/14] iio: constify of_device_id array of_device_id is always used as const. (See driver.of_match_table and open firmware functions) Signed-off-by: Fabian Frederick Signed-off-by: Jonathan Cameron --- drivers/iio/common/ssp_sensors/ssp_dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c index 55a90082a29b..9a40097e7cf8 100644 --- a/drivers/iio/common/ssp_sensors/ssp_dev.c +++ b/drivers/iio/common/ssp_sensors/ssp_dev.c @@ -437,7 +437,7 @@ int ssp_queue_ssp_refresh_task(struct ssp_data *data, unsigned int delay) } #ifdef CONFIG_OF -static struct of_device_id ssp_of_match[] = { +static const struct of_device_id ssp_of_match[] = { { .compatible = "samsung,sensorhub-rinato", .data = &ssp_rinato_info, From a878a1a61a1f0e4b23602ddd87b1408a7a748d0e Mon Sep 17 00:00:00 2001 From: Antonio Fiol Date: Sat, 28 Mar 2015 09:07:14 +0100 Subject: [PATCH 04/14] iio: max517: Add support for MAX520 and MAX521 chips. MAX520 and MAX521 are protocol-compatible with the already supported chips, just have more channels. Signed-off-by: Antonio Fiol Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 11 ++++++--- drivers/iio/dac/max517.c | 45 +++++++++++++++++++++++++--------- include/linux/iio/dac/max517.h | 2 +- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 2236ea22f98a..13471a76e5bf 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -143,11 +143,16 @@ config AD7303 ad7303. config MAX517 - tristate "Maxim MAX517/518/519 DAC driver" + tristate "Maxim MAX517/518/519/520/521 DAC driver" depends on I2C help - If you say yes here you get support for the Maxim chips MAX517, - MAX518 and MAX519 (I2C 8-Bit DACs with rail-to-rail outputs). + If you say yes here you get support for the following Maxim chips + (I2C 8-Bit DACs with rail-to-rail outputs): + MAX517 - Single channel, single reference + MAX518 - Dual channel, ref=Vdd + MAX519 - Dual channel, dual reference + MAX520 - Quad channel, quad reference + MAX521 - Octal channel, independent ref for ch0-3, shared ref for ch4-7 This driver can also be built as a module. If so, the module will be called max517. diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 9a82a7255ebb..5507b3970b4b 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -39,11 +39,13 @@ enum max517_device_ids { ID_MAX517, ID_MAX518, ID_MAX519, + ID_MAX520, + ID_MAX521, }; struct max517_data { struct i2c_client *client; - unsigned short vref_mv[2]; + unsigned short vref_mv[8]; }; /* @@ -149,7 +151,13 @@ static const struct iio_info max517_info = { static const struct iio_chan_spec max517_channels[] = { MAX517_CHANNEL(0), - MAX517_CHANNEL(1) + MAX517_CHANNEL(1), + MAX517_CHANNEL(2), + MAX517_CHANNEL(3), + MAX517_CHANNEL(4), + MAX517_CHANNEL(5), + MAX517_CHANNEL(6), + MAX517_CHANNEL(7), }; static int max517_probe(struct i2c_client *client, @@ -158,6 +166,7 @@ static int max517_probe(struct i2c_client *client, struct max517_data *data; struct iio_dev *indio_dev; struct max517_platform_data *platform_data = client->dev.platform_data; + int chan; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -169,11 +178,21 @@ static int max517_probe(struct i2c_client *client, /* establish that the iio_dev is a child of the i2c device */ indio_dev->dev.parent = &client->dev; - /* reduced channel set for MAX517 */ - if (id->driver_data == ID_MAX517) - indio_dev->num_channels = 1; - else + switch (id->driver_data) { + case ID_MAX521: + indio_dev->num_channels = 8; + break; + case ID_MAX520: + indio_dev->num_channels = 4; + break; + case ID_MAX519: + case ID_MAX518: indio_dev->num_channels = 2; + break; + default: /* single channel for MAX517 */ + indio_dev->num_channels = 1; + break; + } indio_dev->channels = max517_channels; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &max517_info; @@ -182,11 +201,11 @@ static int max517_probe(struct i2c_client *client, * Reference voltage on MAX518 and default is 5V, else take vref_mv * from platform_data */ - if (id->driver_data == ID_MAX518 || !platform_data) { - data->vref_mv[0] = data->vref_mv[1] = 5000; /* mV */ - } else { - data->vref_mv[0] = platform_data->vref_mv[0]; - data->vref_mv[1] = platform_data->vref_mv[1]; + for (chan = 0; chan < indio_dev->num_channels; chan++) { + if (id->driver_data == ID_MAX518 || !platform_data) + data->vref_mv[chan] = 5000; /* mV */ + else + data->vref_mv[chan] = platform_data->vref_mv[chan]; } return iio_device_register(indio_dev); @@ -202,6 +221,8 @@ static const struct i2c_device_id max517_id[] = { { "max517", ID_MAX517 }, { "max518", ID_MAX518 }, { "max519", ID_MAX519 }, + { "max520", ID_MAX520 }, + { "max521", ID_MAX521 }, { } }; MODULE_DEVICE_TABLE(i2c, max517_id); @@ -218,5 +239,5 @@ static struct i2c_driver max517_driver = { module_i2c_driver(max517_driver); MODULE_AUTHOR("Roland Stigge "); -MODULE_DESCRIPTION("MAX517/MAX518/MAX519 8-bit DAC"); +MODULE_DESCRIPTION("MAX517/518/519/520/521 8-bit DAC"); MODULE_LICENSE("GPL"); diff --git a/include/linux/iio/dac/max517.h b/include/linux/iio/dac/max517.h index f6d1d252f08d..7668716cd73c 100644 --- a/include/linux/iio/dac/max517.h +++ b/include/linux/iio/dac/max517.h @@ -9,7 +9,7 @@ #define IIO_DAC_MAX517_H_ struct max517_platform_data { - u16 vref_mv[2]; + u16 vref_mv[8]; }; #endif /* IIO_DAC_MAX517_H_ */ From 8ea06893e66d03dc567845d65b42812efd71b9f3 Mon Sep 17 00:00:00 2001 From: Haneen Mohammed Date: Sat, 28 Mar 2015 03:02:39 +0300 Subject: [PATCH 05/14] iio: Add ABI documentation for in_rot offset value This patch adds ABI documentation entries for in_rot_offset. At least one user for these is present that is the HID Sensors Driver. Signed-off-by: Haneen Mohammed Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 7438e4efdf4c..580b3f85437b 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -254,6 +254,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_pressureY_offset What: /sys/bus/iio/devices/iio:deviceX/in_pressure_offset What: /sys/bus/iio/devices/iio:deviceX/in_humidityrelative_offset What: /sys/bus/iio/devices/iio:deviceX/in_magn_offset +What: /sys/bus/iio/devices/iio:deviceX/in_rot_offset KernelVersion: 2.6.35 Contact: linux-iio@vger.kernel.org Description: From d9d7b990473889a17b0f54e860a65b141733799c Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Fri, 27 Mar 2015 13:53:00 +0200 Subject: [PATCH 06/14] tools: iio: generic_buffer: Fix generic scale extraction When using generic_buffer to read data, the scale is not properly detected for scale shared by type. This is caused by a problem with the generation of generic name out of the full name. E.g.: for current->name in_accel_z, the extracted generic name is "in" (when it should be "in_accel"). This is used in generic_buffer to generate scale and offset paths (in_accel_scale). Consider the in_ or out_ prefix when extracting the generic name from the full name. Signed-off-by: Irina Tirdea Reviewed-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- tools/iio/iio_utils.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 2680a2e0f113..6f6452167b67 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c @@ -20,6 +20,11 @@ const char *iio_dir = "/sys/bus/iio/devices/"; +static char * const iio_direction[] = { + "in", + "out", +}; + /** * iioutils_break_up_name() - extract generic name from full channel name * @full_name: the full channel name @@ -30,10 +35,19 @@ int iioutils_break_up_name(const char *full_name, { char *current; char *w, *r; - char *working; + char *working, *prefix = ""; + int i; - current = strdup(full_name); + for (i = 0; i < sizeof(iio_direction) / sizeof(iio_direction[0]); i++) + if (!strncmp(full_name, iio_direction[i], + strlen(iio_direction[i]))) { + prefix = iio_direction[i]; + break; + } + + current = strdup(full_name + strlen(prefix) + 1); working = strtok(current, "_\0"); + w = working; r = working; @@ -45,7 +59,7 @@ int iioutils_break_up_name(const char *full_name, r++; } *w = '\0'; - *generic_name = strdup(working); + asprintf(generic_name, "%s_%s", prefix, working); free(current); return 0; From 9444a300c2be3ce6266462e3171ceb6636cc62e8 Mon Sep 17 00:00:00 2001 From: Robert Dolca Date: Mon, 23 Mar 2015 15:40:25 +0200 Subject: [PATCH 07/14] IIO: Add support for L3GD20H gyroscope It can be used exactly like L3GD20 but it has a different WhoAmI register value. Signed-off-by: Robert Dolca Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/gyro/st_gyro_core.c | 83 +++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index f07a2336f7dc..21395f26d227 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -87,6 +87,31 @@ #define ST_GYRO_2_DRDY_IRQ_INT2_MASK 0x08 #define ST_GYRO_2_MULTIREAD_BIT true +/* CUSTOM VALUES FOR SENSOR 3 */ +#define ST_GYRO_3_WAI_EXP 0xd7 +#define ST_GYRO_3_ODR_ADDR 0x20 +#define ST_GYRO_3_ODR_MASK 0xc0 +#define ST_GYRO_3_ODR_AVL_95HZ_VAL 0x00 +#define ST_GYRO_3_ODR_AVL_190HZ_VAL 0x01 +#define ST_GYRO_3_ODR_AVL_380HZ_VAL 0x02 +#define ST_GYRO_3_ODR_AVL_760HZ_VAL 0x03 +#define ST_GYRO_3_PW_ADDR 0x20 +#define ST_GYRO_3_PW_MASK 0x08 +#define ST_GYRO_3_FS_ADDR 0x23 +#define ST_GYRO_3_FS_MASK 0x30 +#define ST_GYRO_3_FS_AVL_250_VAL 0x00 +#define ST_GYRO_3_FS_AVL_500_VAL 0x01 +#define ST_GYRO_3_FS_AVL_2000_VAL 0x02 +#define ST_GYRO_3_FS_AVL_250_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_GYRO_3_FS_AVL_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_GYRO_3_FS_AVL_2000_GAIN IIO_DEGREE_TO_RAD(70000) +#define ST_GYRO_3_BDU_ADDR 0x23 +#define ST_GYRO_3_BDU_MASK 0x80 +#define ST_GYRO_3_DRDY_IRQ_ADDR 0x22 +#define ST_GYRO_3_DRDY_IRQ_INT2_MASK 0x08 +#define ST_GYRO_3_MULTIREAD_BIT true + + static const struct iio_chan_spec st_gyro_16bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), @@ -225,6 +250,64 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { .multi_read_bit = ST_GYRO_2_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_GYRO_3_WAI_EXP, + .sensors_supported = { + [0] = L3GD20_GYRO_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_gyro_16bit_channels, + .odr = { + .addr = ST_GYRO_3_ODR_ADDR, + .mask = ST_GYRO_3_ODR_MASK, + .odr_avl = { + { 95, ST_GYRO_3_ODR_AVL_95HZ_VAL, }, + { 190, ST_GYRO_3_ODR_AVL_190HZ_VAL, }, + { 380, ST_GYRO_3_ODR_AVL_380HZ_VAL, }, + { 760, ST_GYRO_3_ODR_AVL_760HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_GYRO_3_PW_ADDR, + .mask = ST_GYRO_3_PW_MASK, + .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = ST_GYRO_3_FS_ADDR, + .mask = ST_GYRO_3_FS_MASK, + .fs_avl = { + [0] = { + .num = ST_GYRO_FS_AVL_250DPS, + .value = ST_GYRO_3_FS_AVL_250_VAL, + .gain = ST_GYRO_3_FS_AVL_250_GAIN, + }, + [1] = { + .num = ST_GYRO_FS_AVL_500DPS, + .value = ST_GYRO_3_FS_AVL_500_VAL, + .gain = ST_GYRO_3_FS_AVL_500_GAIN, + }, + [2] = { + .num = ST_GYRO_FS_AVL_2000DPS, + .value = ST_GYRO_3_FS_AVL_2000_VAL, + .gain = ST_GYRO_3_FS_AVL_2000_GAIN, + }, + }, + }, + .bdu = { + .addr = ST_GYRO_3_BDU_ADDR, + .mask = ST_GYRO_3_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_GYRO_3_DRDY_IRQ_ADDR, + .mask_int2 = ST_GYRO_3_DRDY_IRQ_INT2_MASK, + }, + .multi_read_bit = ST_GYRO_3_MULTIREAD_BIT, + .bootime = 2, + }, }; static int st_gyro_read_raw(struct iio_dev *indio_dev, From 37d3455672732b29a477732a94abfe95e199f0ce Mon Sep 17 00:00:00 2001 From: Josselin Costanzi Date: Sun, 22 Mar 2015 20:33:38 +0200 Subject: [PATCH 08/14] iio: add watermark logic to iio read and poll Currently the IIO buffer blocking read only wait until at least one data element is available. This patch makes the reader sleep until enough data is collected before returning to userspace. This should limit the read() calls count when trying to get data in batches. Co-author: Yannick Bedhomme Signed-off-by: Josselin Costanzi [rebased and remove buffer timeout] Signed-off-by: Octavian Purdila Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 15 +++ drivers/iio/industrialio-buffer.c | 120 ++++++++++++++++++++--- drivers/iio/kfifo_buf.c | 11 +-- drivers/staging/iio/accel/sca3000_ring.c | 4 +- include/linux/iio/buffer.h | 8 +- 5 files changed, 131 insertions(+), 27 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 580b3f85437b..0b6f0abf3370 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1280,3 +1280,18 @@ Contact: linux-iio@vger.kernel.org Description: Specifies number of seconds in which we compute the steps that occur in order to decide if the consumer is making steps. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/watermark +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + A single positive integer specifying the maximum number of scan + elements to wait for. + Poll will block until the watermark is reached. + Blocking read will wait until the minimum between the requested + read amount or the low water mark is available. + Non-blocking read will retrieve the available samples from the + buffer even if there are less samples then watermark level. This + allows the application to block on poll with a timeout and read + the available samples after the timeout expires and thus have a + maximum delay guarantee. diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index c2d5440aa226..a24b2e005eb3 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -37,11 +37,28 @@ static bool iio_buffer_is_active(struct iio_buffer *buf) return !list_empty(&buf->buffer_list); } -static bool iio_buffer_data_available(struct iio_buffer *buf) +static size_t iio_buffer_data_available(struct iio_buffer *buf) { return buf->access->data_available(buf); } +static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, + size_t to_wait) +{ + /* wakeup if the device was unregistered */ + if (!indio_dev->info) + return true; + + /* drain the buffer if it was disabled */ + if (!iio_buffer_is_active(buf)) + to_wait = min_t(size_t, to_wait, 1); + + if (iio_buffer_data_available(buf) >= to_wait) + return true; + + return false; +} + /** * iio_buffer_read_first_n_outer() - chrdev read for buffer access * @@ -53,6 +70,8 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, { struct iio_dev *indio_dev = filp->private_data; struct iio_buffer *rb = indio_dev->buffer; + size_t datum_size; + size_t to_wait = 0; int ret; if (!indio_dev->info) @@ -61,19 +80,26 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, if (!rb || !rb->access->read_first_n) return -EINVAL; + datum_size = rb->bytes_per_datum; + + /* + * If datum_size is 0 there will never be anything to read from the + * buffer, so signal end of file now. + */ + if (!datum_size) + return 0; + + if (!(filp->f_flags & O_NONBLOCK)) + to_wait = min_t(size_t, n / datum_size, rb->watermark); + do { - if (!iio_buffer_data_available(rb)) { - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; + ret = wait_event_interruptible(rb->pollq, + iio_buffer_ready(indio_dev, rb, to_wait)); + if (ret) + return ret; - ret = wait_event_interruptible(rb->pollq, - iio_buffer_data_available(rb) || - indio_dev->info == NULL); - if (ret) - return ret; - if (indio_dev->info == NULL) - return -ENODEV; - } + if (!indio_dev->info) + return -ENODEV; ret = rb->access->read_first_n(rb, n, buf); if (ret == 0 && (filp->f_flags & O_NONBLOCK)) @@ -96,9 +122,8 @@ unsigned int iio_buffer_poll(struct file *filp, return -ENODEV; poll_wait(filp, &rb->pollq, wait); - if (iio_buffer_data_available(rb)) + if (iio_buffer_ready(indio_dev, rb, rb->watermark)) return POLLIN | POLLRDNORM; - /* need a way of knowing if there may be enough data... */ return 0; } @@ -123,6 +148,7 @@ void iio_buffer_init(struct iio_buffer *buffer) INIT_LIST_HEAD(&buffer->buffer_list); init_waitqueue_head(&buffer->pollq); kref_init(&buffer->ref); + buffer->watermark = 1; } EXPORT_SYMBOL(iio_buffer_init); @@ -416,6 +442,11 @@ static ssize_t iio_buffer_write_length(struct device *dev, buffer->access->set_length(buffer, val); ret = 0; } + if (ret) + goto out; + if (buffer->length && buffer->length < buffer->watermark) + buffer->watermark = buffer->length; +out: mutex_unlock(&indio_dev->mlock); return ret ? ret : len; @@ -472,6 +503,7 @@ static void iio_buffer_activate(struct iio_dev *indio_dev, static void iio_buffer_deactivate(struct iio_buffer *buffer) { list_del_init(&buffer->buffer_list); + wake_up_interruptible(&buffer->pollq); iio_buffer_put(buffer); } @@ -754,16 +786,64 @@ static ssize_t iio_buffer_store_enable(struct device *dev, static const char * const iio_scan_elements_group_name = "scan_elements"; +static ssize_t iio_buffer_show_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = indio_dev->buffer; + + return sprintf(buf, "%u\n", buffer->watermark); +} + +static ssize_t iio_buffer_store_watermark(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = indio_dev->buffer; + unsigned int val; + int ret; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + if (!val) + return -EINVAL; + + mutex_lock(&indio_dev->mlock); + + if (val > buffer->length) { + ret = -EINVAL; + goto out; + } + + if (iio_buffer_is_active(indio_dev->buffer)) { + ret = -EBUSY; + goto out; + } + + buffer->watermark = val; +out: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, iio_buffer_write_length); static struct device_attribute dev_attr_length_ro = __ATTR(length, S_IRUGO, iio_buffer_read_length, NULL); static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, iio_buffer_show_enable, iio_buffer_store_enable); +static DEVICE_ATTR(watermark, S_IRUGO | S_IWUSR, + iio_buffer_show_watermark, iio_buffer_store_watermark); static struct attribute *iio_buffer_attrs[] = { &dev_attr_length.attr, &dev_attr_enable.attr, + &dev_attr_watermark.attr, }; int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) @@ -944,8 +1024,18 @@ static const void *iio_demux(struct iio_buffer *buffer, static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data) { const void *dataout = iio_demux(buffer, data); + int ret; + + ret = buffer->access->store_to(buffer, dataout); + if (ret) + return ret; - return buffer->access->store_to(buffer, dataout); + /* + * We can't just test for watermark to decide if we wake the poll queue + * because read may request less samples than the watermark. + */ + wake_up_interruptible_poll(&buffer->pollq, POLLIN | POLLRDNORM); + return 0; } static void iio_buffer_demux_free(struct iio_buffer *buffer) diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c index b2beea01c49b..847ca561afe0 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -83,9 +83,6 @@ static int iio_store_to_kfifo(struct iio_buffer *r, ret = kfifo_in(&kf->kf, data, 1); if (ret != 1) return -EBUSY; - - wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM); - return 0; } @@ -109,16 +106,16 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r, return copied; } -static bool iio_kfifo_buf_data_available(struct iio_buffer *r) +static size_t iio_kfifo_buf_data_available(struct iio_buffer *r) { struct iio_kfifo *kf = iio_to_kfifo(r); - bool empty; + size_t samples; mutex_lock(&kf->user_lock); - empty = kfifo_is_empty(&kf->kf); + samples = kfifo_len(&kf->kf); mutex_unlock(&kf->user_lock); - return !empty; + return samples; } static void iio_kfifo_buffer_release(struct iio_buffer *buffer) diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c index f76a26885808..8589eade1057 100644 --- a/drivers/staging/iio/accel/sca3000_ring.c +++ b/drivers/staging/iio/accel/sca3000_ring.c @@ -129,9 +129,9 @@ static int sca3000_read_first_n_hw_rb(struct iio_buffer *r, return ret ? ret : num_read; } -static bool sca3000_ring_buf_data_available(struct iio_buffer *r) +static size_t sca3000_ring_buf_data_available(struct iio_buffer *r) { - return r->stufftoread; + return r->stufftoread ? r->watermark : 0; } /** diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index b65850a41127..eb8622b78ec9 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -21,8 +21,8 @@ struct iio_buffer; * struct iio_buffer_access_funcs - access functions for buffers. * @store_to: actually store stuff to the buffer * @read_first_n: try to get a specified number of bytes (must exist) - * @data_available: indicates whether data for reading from the buffer is - * available. + * @data_available: indicates how much data is available for reading from + * the buffer. * @request_update: if a parameter change has been marked, update underlying * storage. * @set_bytes_per_datum:set number of bytes per datum @@ -43,7 +43,7 @@ struct iio_buffer_access_funcs { int (*read_first_n)(struct iio_buffer *buffer, size_t n, char __user *buf); - bool (*data_available)(struct iio_buffer *buffer); + size_t (*data_available)(struct iio_buffer *buffer); int (*request_update)(struct iio_buffer *buffer); @@ -72,6 +72,7 @@ struct iio_buffer_access_funcs { * @demux_bounce: [INTERN] buffer for doing gather from incoming scan. * @buffer_list: [INTERN] entry in the devices list of current buffers. * @ref: [INTERN] reference count of the buffer. + * @watermark: [INTERN] number of datums to wait for poll/read. */ struct iio_buffer { int length; @@ -90,6 +91,7 @@ struct iio_buffer { void *demux_bounce; struct list_head buffer_list; struct kref ref; + unsigned int watermark; }; /** From f4f4673b7535eff4ee1a8cfb1685fa1e1a0cb79d Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 22 Mar 2015 20:33:39 +0200 Subject: [PATCH 09/14] iio: add support for hardware fifo Some devices have hardware buffers that can store a number of samples for later consumption. Hardware usually provides interrupts to notify the processor when the FIFO is full or when it has reached a certain watermark level. This helps with reducing the number of interrupts to the host processor and thus it helps decreasing the power consumption. This patch enables usage of hardware FIFOs for IIO devices in conjunction with software device buffers. When the hardware FIFO is enabled the samples are stored in the hardware FIFO. The samples are later flushed to the device software buffer when the number of entries in the hardware FIFO reaches the hardware watermark or when a flush operation is triggered by the user when doing a non-blocking read on an empty software device buffer. In order to implement hardware FIFO support the device drivers must implement the following new operations: setting and getting the hardware FIFO watermark level, flushing the hardware FIFO to the software device buffer. The device must also expose information about the hardware FIFO such it's minimum and maximum watermark and if necessary a list of supported watermark values. Finally, the device driver must activate the hardware FIFO when the device buffer is enabled, if the current device settings allows it. The software device buffer watermark is passed by the IIO core to the device driver as a hint for the hardware FIFO watermark. The device driver can adjust this value to allow for hardware limitations (such as capping it to the maximum hardware watermark or adjust it to a value that is supported by the hardware). It can also disable the hardware watermark (and implicitly the hardware FIFO) it this value is below the minimum hardware watermark. Since a driver may support hardware FIFO only when not in triggered buffer mode (due to different semantics of hardware FIFO sampling and triggered sampling) this patch changes the IIO core code to allow falling back to non-triggered buffered mode if no trigger is enabled. Signed-off-by: Octavian Purdila Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 69 +++++++++++++++++++++++++ drivers/iio/industrialio-buffer.c | 58 ++++++++++++++++----- include/linux/iio/iio.h | 13 +++++ 3 files changed, 127 insertions(+), 13 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 0b6f0abf3370..6bf79072179f 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1295,3 +1295,72 @@ Description: allows the application to block on poll with a timeout and read the available samples after the timeout expires and thus have a maximum delay guarantee. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_enabled +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + A read-only boolean value that indicates if the hardware fifo is + currently enabled or disabled. If the device does not have a + hardware fifo this entry is not present. + The hardware fifo is enabled when the buffer is enabled if the + current hardware fifo watermark level is set and other current + device settings allows it (e.g. if a trigger is set that samples + data differently that the hardware fifo does then hardware fifo + will not enabled). + If the hardware fifo is enabled and the level of the hardware + fifo reaches the hardware fifo watermark level the device will + flush its hardware fifo to the device buffer. Doing a non + blocking read on the device when no samples are present in the + device buffer will also force a flush. + When the hardware fifo is enabled there is no need to use a + trigger to use buffer mode since the watermark settings + guarantees that the hardware fifo is flushed to the device + buffer. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + Read-only entry that contains a single integer specifying the + current watermark level for the hardware fifo. If the device + does not have a hardware fifo this entry is not present. + The watermark level for the hardware fifo is set by the driver + based on the value set by the user in buffer/watermark but + taking into account hardware limitations (e.g. most hardware + buffers are limited to 32-64 samples, some hardware buffers + watermarks are fixed or have minimum levels). A value of 0 + means that the hardware watermark is unset. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_min +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + A single positive integer specifying the minimum watermark level + for the hardware fifo of this device. If the device does not + have a hardware fifo this entry is not present. + If the user sets buffer/watermark to a value less than this one, + then the hardware watermark will remain unset. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_max +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + A single positive integer specifying the maximum watermark level + for the hardware fifo of this device. If the device does not + have a hardware fifo this entry is not present. + If the user sets buffer/watermark to a value greater than this + one, then the hardware watermark will be capped at this value. + +What: /sys/bus/iio/devices/iio:deviceX/buffer/hwfifo_watermark_available +KernelVersion: 4.2 +Contact: linux-iio@vger.kernel.org +Description: + A list of positive integers specifying the available watermark + levels for the hardware fifo. This entry is optional and if it + is not present it means that all the values between + hwfifo_watermark_min and hwfifo_watermark_max are supported. + If the user sets buffer/watermark to a value greater than + hwfifo_watermak_min but not equal to any of the values in this + list, the driver will chose an appropriate value for the + hardware fifo watermark level. diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index a24b2e005eb3..df919f44d513 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -42,18 +42,47 @@ static size_t iio_buffer_data_available(struct iio_buffer *buf) return buf->access->data_available(buf); } +static int iio_buffer_flush_hwfifo(struct iio_dev *indio_dev, + struct iio_buffer *buf, size_t required) +{ + if (!indio_dev->info->hwfifo_flush_to_buffer) + return -ENODEV; + + return indio_dev->info->hwfifo_flush_to_buffer(indio_dev, required); +} + static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, - size_t to_wait) + size_t to_wait, int to_flush) { + size_t avail; + int flushed = 0; + /* wakeup if the device was unregistered */ if (!indio_dev->info) return true; /* drain the buffer if it was disabled */ - if (!iio_buffer_is_active(buf)) + if (!iio_buffer_is_active(buf)) { to_wait = min_t(size_t, to_wait, 1); + to_flush = 0; + } + + avail = iio_buffer_data_available(buf); - if (iio_buffer_data_available(buf) >= to_wait) + if (avail >= to_wait) { + /* force a flush for non-blocking reads */ + if (!to_wait && !avail && to_flush) + iio_buffer_flush_hwfifo(indio_dev, buf, to_flush); + return true; + } + + if (to_flush) + flushed = iio_buffer_flush_hwfifo(indio_dev, buf, + to_wait - avail); + if (flushed <= 0) + return false; + + if (avail + flushed >= to_wait) return true; return false; @@ -72,6 +101,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, struct iio_buffer *rb = indio_dev->buffer; size_t datum_size; size_t to_wait = 0; + size_t to_read; int ret; if (!indio_dev->info) @@ -89,12 +119,14 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf, if (!datum_size) return 0; + to_read = min_t(size_t, n / datum_size, rb->watermark); + if (!(filp->f_flags & O_NONBLOCK)) - to_wait = min_t(size_t, n / datum_size, rb->watermark); + to_wait = to_read; do { ret = wait_event_interruptible(rb->pollq, - iio_buffer_ready(indio_dev, rb, to_wait)); + iio_buffer_ready(indio_dev, rb, to_wait, to_read)); if (ret) return ret; @@ -122,7 +154,7 @@ unsigned int iio_buffer_poll(struct file *filp, return -ENODEV; poll_wait(filp, &rb->pollq, wait); - if (iio_buffer_ready(indio_dev, rb, rb->watermark)) + if (iio_buffer_ready(indio_dev, rb, rb->watermark, 0)) return POLLIN | POLLRDNORM; return 0; } @@ -661,19 +693,16 @@ static int __iio_update_buffers(struct iio_dev *indio_dev, } } /* Definitely possible for devices to support both of these. */ - if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) { - if (!indio_dev->trig) { - printk(KERN_INFO "Buffer not started: no trigger\n"); - ret = -EINVAL; - /* Can only occur on first buffer */ - goto error_run_postdisable; - } + if ((indio_dev->modes & INDIO_BUFFER_TRIGGERED) && indio_dev->trig) { indio_dev->currentmode = INDIO_BUFFER_TRIGGERED; } else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) { indio_dev->currentmode = INDIO_BUFFER_HARDWARE; } else if (indio_dev->modes & INDIO_BUFFER_SOFTWARE) { indio_dev->currentmode = INDIO_BUFFER_SOFTWARE; } else { /* Should never be reached */ + /* Can only occur on first buffer */ + if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) + pr_info("Buffer not started: no trigger\n"); ret = -EINVAL; goto error_run_postdisable; } @@ -825,6 +854,9 @@ static ssize_t iio_buffer_store_watermark(struct device *dev, } buffer->watermark = val; + + if (indio_dev->info->hwfifo_set_watermark) + indio_dev->info->hwfifo_set_watermark(indio_dev, val); out: mutex_unlock(&indio_dev->mlock); diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 80d855061064..d86b753e9b30 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -338,6 +338,16 @@ struct iio_dev; * provide a custom of_xlate function that reads the * *args* and returns the appropriate index in registered * IIO channels array. + * @hwfifo_set_watermark: function pointer to set the current hardware + * fifo watermark level; see hwfifo_* entries in + * Documentation/ABI/testing/sysfs-bus-iio for details on + * how the hardware fifo operates + * @hwfifo_flush_to_buffer: function pointer to flush the samples stored + * in the hardware fifo to the device buffer. The driver + * should not flush more than count samples. The function + * must return the number of samples flushed, 0 if no + * samples were flushed or a negative integer if no samples + * were flushed and there was an error. **/ struct iio_info { struct module *driver_module; @@ -399,6 +409,9 @@ struct iio_info { unsigned *readval); int (*of_xlate)(struct iio_dev *indio_dev, const struct of_phandle_args *iiospec); + int (*hwfifo_set_watermark)(struct iio_dev *indio_dev, unsigned val); + int (*hwfifo_flush_to_buffer)(struct iio_dev *indio_dev, + unsigned count); }; /** From 3bbec9773389112330954a6a64422eaa78d546c1 Mon Sep 17 00:00:00 2001 From: Octavian Purdila Date: Sun, 22 Mar 2015 20:33:40 +0200 Subject: [PATCH 10/14] iio: bmc150_accel: add support for hardware fifo We only advertise hardware fifo support if the I2C bus supports full I2C or smbus I2C block data reads since it is mandatory to read the full frame in one read (otherwise the rest of the frame is discarded). The hardware fifo is enabled only when triggers are not active because: (a) when using the any-motion trigger the user expects to see samples based on ROC events, but the fifo stores samples based on the sample frequency (b) the data-ready trigger is waking the CPU for for every sample, so using the hardware fifo does not have any benefit Signed-off-by: Octavian Purdila Reviewed-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bmc150-accel.c | 408 +++++++++++++++++++++++++++++-- 1 file changed, 392 insertions(+), 16 deletions(-) diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index f1b80c4c0b35..4026122a7592 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -70,7 +70,9 @@ #define BMC150_ACCEL_INT_MAP_0_BIT_SLOPE BIT(2) #define BMC150_ACCEL_REG_INT_MAP_1 0x1A -#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_DATA BIT(0) +#define BMC150_ACCEL_INT_MAP_1_BIT_FWM BIT(1) +#define BMC150_ACCEL_INT_MAP_1_BIT_FFULL BIT(2) #define BMC150_ACCEL_REG_INT_RST_LATCH 0x21 #define BMC150_ACCEL_INT_MODE_LATCH_RESET 0x80 @@ -83,7 +85,9 @@ #define BMC150_ACCEL_INT_EN_BIT_SLP_Z BIT(2) #define BMC150_ACCEL_REG_INT_EN_1 0x17 -#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_DATA_EN BIT(4) +#define BMC150_ACCEL_INT_EN_BIT_FFULL_EN BIT(5) +#define BMC150_ACCEL_INT_EN_BIT_FWM_EN BIT(6) #define BMC150_ACCEL_REG_INT_OUT_CTRL 0x20 #define BMC150_ACCEL_INT_OUT_CTRL_INT1_LVL BIT(0) @@ -122,6 +126,12 @@ #define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2)) #define BMC150_AUTO_SUSPEND_DELAY_MS 2000 +#define BMC150_ACCEL_REG_FIFO_STATUS 0x0E +#define BMC150_ACCEL_REG_FIFO_CONFIG0 0x30 +#define BMC150_ACCEL_REG_FIFO_CONFIG1 0x3E +#define BMC150_ACCEL_REG_FIFO_DATA 0x3F +#define BMC150_ACCEL_FIFO_LENGTH 32 + enum bmc150_accel_axis { AXIS_X, AXIS_Y, @@ -179,13 +189,14 @@ struct bmc150_accel_data { atomic_t active_intr; struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; struct mutex mutex; + u8 fifo_mode, watermark; s16 buffer[8]; u8 bw_bits; u32 slope_dur; u32 slope_thres; u32 range; int ev_enable_state; - int64_t timestamp; + int64_t timestamp, old_timestamp; const struct bmc150_accel_chip_info *chip_info; }; @@ -470,6 +481,12 @@ static const struct bmc150_accel_interrupt_info { BMC150_ACCEL_INT_EN_BIT_SLP_Y | BMC150_ACCEL_INT_EN_BIT_SLP_Z }, + { /* fifo watermark interrupt */ + .map_reg = BMC150_ACCEL_REG_INT_MAP_1, + .map_bitmask = BMC150_ACCEL_INT_MAP_1_BIT_FWM, + .en_reg = BMC150_ACCEL_REG_INT_EN_1, + .en_bitmask = BMC150_ACCEL_INT_EN_BIT_FWM_EN, + }, }; static void bmc150_accel_interrupts_setup(struct iio_dev *indio_dev, @@ -823,6 +840,214 @@ static int bmc150_accel_validate_trigger(struct iio_dev *indio_dev, return -EINVAL; } +static ssize_t bmc150_accel_get_fifo_watermark(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmc150_accel_data *data = iio_priv(indio_dev); + int wm; + + mutex_lock(&data->mutex); + wm = data->watermark; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", wm); +} + +static ssize_t bmc150_accel_get_fifo_state(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct bmc150_accel_data *data = iio_priv(indio_dev); + bool state; + + mutex_lock(&data->mutex); + state = data->fifo_mode; + mutex_unlock(&data->mutex); + + return sprintf(buf, "%d\n", state); +} + +static IIO_CONST_ATTR(hwfifo_watermark_min, "1"); +static IIO_CONST_ATTR(hwfifo_watermark_max, + __stringify(BMC150_ACCEL_FIFO_LENGTH)); +static IIO_DEVICE_ATTR(hwfifo_enabled, S_IRUGO, + bmc150_accel_get_fifo_state, NULL, 0); +static IIO_DEVICE_ATTR(hwfifo_watermark, S_IRUGO, + bmc150_accel_get_fifo_watermark, NULL, 0); + +static const struct attribute *bmc150_accel_fifo_attributes[] = { + &iio_const_attr_hwfifo_watermark_min.dev_attr.attr, + &iio_const_attr_hwfifo_watermark_max.dev_attr.attr, + &iio_dev_attr_hwfifo_watermark.dev_attr.attr, + &iio_dev_attr_hwfifo_enabled.dev_attr.attr, + NULL, +}; + +static int bmc150_accel_set_watermark(struct iio_dev *indio_dev, unsigned val) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + + if (val > BMC150_ACCEL_FIFO_LENGTH) + val = BMC150_ACCEL_FIFO_LENGTH; + + mutex_lock(&data->mutex); + data->watermark = val; + mutex_unlock(&data->mutex); + + return 0; +} + +/* + * We must read at least one full frame in one burst, otherwise the rest of the + * frame data is discarded. + */ +static int bmc150_accel_fifo_transfer(const struct i2c_client *client, + char *buffer, int samples) +{ + int sample_length = 3 * 2; + u8 reg_fifo_data = BMC150_ACCEL_REG_FIFO_DATA; + int ret = -EIO; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .buf = ®_fifo_data, + .len = sizeof(reg_fifo_data), + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .buf = (u8 *)buffer, + .len = samples * sample_length, + } + }; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret != 2) + ret = -EIO; + else + ret = 0; + } else { + int i, step = I2C_SMBUS_BLOCK_MAX / sample_length; + + for (i = 0; i < samples * sample_length; i += step) { + ret = i2c_smbus_read_i2c_block_data(client, + reg_fifo_data, step, + &buffer[i]); + if (ret != step) { + ret = -EIO; + break; + } + + ret = 0; + } + } + + if (ret) + dev_err(&client->dev, "Error transferring data from fifo\n"); + + return ret; +} + +static int __bmc150_accel_fifo_flush(struct iio_dev *indio_dev, + unsigned samples, bool irq) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret, i; + u8 count; + u16 buffer[BMC150_ACCEL_FIFO_LENGTH * 3]; + int64_t tstamp; + uint64_t sample_period; + ret = i2c_smbus_read_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_STATUS); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_fifo_status\n"); + return ret; + } + + count = ret & 0x7F; + + if (!count) + return 0; + + /* + * If we getting called from IRQ handler we know the stored timestamp is + * fairly accurate for the last stored sample. Otherwise, if we are + * called as a result of a read operation from userspace and hence + * before the watermark interrupt was triggered, take a timestamp + * now. We can fall anywhere in between two samples so the error in this + * case is at most one sample period. + */ + if (!irq) { + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(); + } + + /* + * Approximate timestamps for each of the sample based on the sampling + * frequency, timestamp for last sample and number of samples. + * + * Note that we can't use the current bandwidth settings to compute the + * sample period because the sample rate varies with the device + * (e.g. between 31.70ms to 32.20ms for a bandwidth of 15.63HZ). That + * small variation adds when we store a large number of samples and + * creates significant jitter between the last and first samples in + * different batches (e.g. 32ms vs 21ms). + * + * To avoid this issue we compute the actual sample period ourselves + * based on the timestamp delta between the last two flush operations. + */ + sample_period = (data->timestamp - data->old_timestamp); + do_div(sample_period, count); + tstamp = data->timestamp - (count - 1) * sample_period; + + if (samples && count > samples) + count = samples; + + ret = bmc150_accel_fifo_transfer(data->client, (u8 *)buffer, count); + if (ret) + return ret; + + /* + * Ideally we want the IIO core to handle the demux when running in fifo + * mode but not when running in triggered buffer mode. Unfortunately + * this does not seem to be possible, so stick with driver demux for + * now. + */ + for (i = 0; i < count; i++) { + u16 sample[8]; + int j, bit; + + j = 0; + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) + memcpy(&sample[j++], &buffer[i * 3 + bit], 2); + + iio_push_to_buffers_with_timestamp(indio_dev, sample, tstamp); + + tstamp += sample_period; + } + + return count; +} + +static int bmc150_accel_fifo_flush(struct iio_dev *indio_dev, unsigned samples) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; + + mutex_lock(&data->mutex); + ret = __bmc150_accel_fifo_flush(indio_dev, samples, false); + mutex_unlock(&data->mutex); + + return ret; +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL( "7.810000 15.630000 31.250000 62.500000 125 250 500 1000"); @@ -962,6 +1187,20 @@ static const struct iio_info bmc150_accel_info = { .driver_module = THIS_MODULE, }; +static const struct iio_info bmc150_accel_info_fifo = { + .attrs = &bmc150_accel_attrs_group, + .read_raw = bmc150_accel_read_raw, + .write_raw = bmc150_accel_write_raw, + .read_event_value = bmc150_accel_read_event, + .write_event_value = bmc150_accel_write_event, + .write_event_config = bmc150_accel_write_event_config, + .read_event_config = bmc150_accel_read_event_config, + .validate_trigger = bmc150_accel_validate_trigger, + .hwfifo_set_watermark = bmc150_accel_set_watermark, + .hwfifo_flush_to_buffer = bmc150_accel_fifo_flush, + .driver_module = THIS_MODULE, +}; + static irqreturn_t bmc150_accel_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -1057,18 +1296,17 @@ static const struct iio_trigger_ops bmc150_accel_trigger_ops = { .owner = THIS_MODULE, }; -static irqreturn_t bmc150_accel_event_handler(int irq, void *private) +static int bmc150_accel_handle_roc_event(struct iio_dev *indio_dev) { - struct iio_dev *indio_dev = private; struct bmc150_accel_data *data = iio_priv(indio_dev); - int ret; int dir; + int ret; ret = i2c_smbus_read_byte_data(data->client, BMC150_ACCEL_REG_INT_STATUS_2); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_status_2\n"); - goto ack_intr_status; + return ret; } if (ret & BMC150_ACCEL_ANY_MOTION_BIT_SIGN) @@ -1097,35 +1335,73 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private) IIO_EV_TYPE_ROC, dir), data->timestamp); -ack_intr_status: - if (!data->triggers[BMC150_ACCEL_TRIGGER_DATA_READY].enabled) + return ret; +} + +static irqreturn_t bmc150_accel_irq_thread_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct bmc150_accel_data *data = iio_priv(indio_dev); + bool ack = false; + int ret; + + mutex_lock(&data->mutex); + + if (data->fifo_mode) { + ret = __bmc150_accel_fifo_flush(indio_dev, + BMC150_ACCEL_FIFO_LENGTH, true); + if (ret > 0) + ack = true; + } + + if (data->ev_enable_state) { + ret = bmc150_accel_handle_roc_event(indio_dev); + if (ret > 0) + ack = true; + } + + if (ack) { ret = i2c_smbus_write_byte_data(data->client, BMC150_ACCEL_REG_INT_RST_LATCH, BMC150_ACCEL_INT_MODE_LATCH_INT | BMC150_ACCEL_INT_MODE_LATCH_RESET); + if (ret) + dev_err(&data->client->dev, "Error writing reg_int_rst_latch\n"); + ret = IRQ_HANDLED; + } else { + ret = IRQ_NONE; + } - return IRQ_HANDLED; + mutex_unlock(&data->mutex); + + return ret; } -static irqreturn_t bmc150_accel_data_rdy_trig_poll(int irq, void *private) +static irqreturn_t bmc150_accel_irq_handler(int irq, void *private) { struct iio_dev *indio_dev = private; struct bmc150_accel_data *data = iio_priv(indio_dev); + bool ack = false; int i; + data->old_timestamp = data->timestamp; data->timestamp = iio_get_time_ns(); for (i = 0; i < BMC150_ACCEL_TRIGGERS; i++) { if (data->triggers[i].enabled) { iio_trigger_poll(data->triggers[i].indio_trig); + ack = true; break; } } - if (data->ev_enable_state) + if (data->ev_enable_state || data->fifo_mode) return IRQ_WAKE_THREAD; - else + + if (ack) return IRQ_HANDLED; + + return IRQ_NONE; } static const char *bmc150_accel_match_acpi_device(struct device *dev, int *data) @@ -1232,6 +1508,94 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, return ret; } +#define BMC150_ACCEL_FIFO_MODE_STREAM 0x80 +#define BMC150_ACCEL_FIFO_MODE_FIFO 0x40 +#define BMC150_ACCEL_FIFO_MODE_BYPASS 0x00 + +static int bmc150_accel_fifo_set_mode(struct bmc150_accel_data *data) +{ + u8 reg = BMC150_ACCEL_REG_FIFO_CONFIG1; + int ret; + + ret = i2c_smbus_write_byte_data(data->client, reg, data->fifo_mode); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_fifo_config1\n"); + return ret; + } + + if (!data->fifo_mode) + return 0; + + ret = i2c_smbus_write_byte_data(data->client, + BMC150_ACCEL_REG_FIFO_CONFIG0, + data->watermark); + if (ret < 0) + dev_err(&data->client->dev, "Error writing reg_fifo_config0\n"); + + return ret; +} + +static int bmc150_accel_buffer_postenable(struct iio_dev *indio_dev) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret = 0; + + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) + return iio_triggered_buffer_postenable(indio_dev); + + mutex_lock(&data->mutex); + + if (!data->watermark) + goto out; + + ret = bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, + true); + if (ret) + goto out; + + data->fifo_mode = BMC150_ACCEL_FIFO_MODE_FIFO; + + ret = bmc150_accel_fifo_set_mode(data); + if (ret) { + data->fifo_mode = 0; + bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, + false); + } + +out: + mutex_unlock(&data->mutex); + + return ret; +} + +static int bmc150_accel_buffer_predisable(struct iio_dev *indio_dev) +{ + struct bmc150_accel_data *data = iio_priv(indio_dev); + + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) + return iio_triggered_buffer_predisable(indio_dev); + + mutex_lock(&data->mutex); + + if (!data->fifo_mode) + goto out; + + bmc150_accel_set_interrupt(data, BMC150_ACCEL_INT_WATERMARK, false); + __bmc150_accel_fifo_flush(indio_dev, BMC150_ACCEL_FIFO_LENGTH, false); + data->fifo_mode = 0; + bmc150_accel_fifo_set_mode(data); + +out: + mutex_unlock(&data->mutex); + + return 0; +} + +static const struct iio_buffer_setup_ops bmc150_accel_buffer_ops = { + .postenable = bmc150_accel_buffer_postenable, + .predisable = bmc150_accel_buffer_predisable, +}; + static int bmc150_accel_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1278,8 +1642,8 @@ static int bmc150_accel_probe(struct i2c_client *client, if (client->irq >= 0) { ret = devm_request_threaded_irq( &client->dev, client->irq, - bmc150_accel_data_rdy_trig_poll, - bmc150_accel_event_handler, + bmc150_accel_irq_handler, + bmc150_accel_irq_thread_handler, IRQF_TRIGGER_RISING, BMC150_ACCEL_IRQ_NAME, indio_dev); @@ -1309,12 +1673,20 @@ static int bmc150_accel_probe(struct i2c_client *client, ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, bmc150_accel_trigger_handler, - NULL); + &bmc150_accel_buffer_ops); if (ret < 0) { dev_err(&client->dev, "Failed: iio triggered buffer setup\n"); goto err_trigger_unregister; } + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) || + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + indio_dev->modes |= INDIO_BUFFER_SOFTWARE; + indio_dev->info = &bmc150_accel_info_fifo; + indio_dev->buffer->attrs = bmc150_accel_fifo_attributes; + } } ret = iio_device_register(indio_dev); @@ -1386,6 +1758,7 @@ static int bmc150_accel_resume(struct device *dev) mutex_lock(&data->mutex); if (atomic_read(&data->active_intr)) bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); + bmc150_accel_fifo_set_mode(data); mutex_unlock(&data->mutex); return 0; @@ -1417,6 +1790,9 @@ static int bmc150_accel_runtime_resume(struct device *dev) dev_dbg(&data->client->dev, __func__); ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_NORMAL, 0); + if (ret < 0) + return ret; + ret = bmc150_accel_fifo_set_mode(data); if (ret < 0) return ret; From 209c00691938c4b3d58142b05ca6f50b8971e521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vianney=20le=20Cl=C3=A9ment=20de=20Saint-Marcq?= Date: Tue, 24 Mar 2015 16:54:14 +0100 Subject: [PATCH 11/14] iio: mlx90614: Add symbols for accessible registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add symbols for all accessible RAM and EEPROM registers, as well as the sleep command and timings defined in the datasheet. Signed-off-by: Vianney le Clément de Saint-Marcq Cc: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/mlx90614.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index e2c6f1a0d27f..a2e3aa6aa39d 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -20,11 +20,35 @@ #include -#define MLX90614_OP_RAM 0x00 +#define MLX90614_OP_RAM 0x00 +#define MLX90614_OP_EEPROM 0x20 +#define MLX90614_OP_SLEEP 0xff /* RAM offsets with 16-bit data, MSB first */ +#define MLX90614_RAW1 (MLX90614_OP_RAM | 0x04) /* raw data IR channel 1 */ +#define MLX90614_RAW2 (MLX90614_OP_RAM | 0x05) /* raw data IR channel 2 */ #define MLX90614_TA (MLX90614_OP_RAM | 0x06) /* ambient temperature */ #define MLX90614_TOBJ1 (MLX90614_OP_RAM | 0x07) /* object 1 temperature */ +#define MLX90614_TOBJ2 (MLX90614_OP_RAM | 0x08) /* object 2 temperature */ + +/* EEPROM offsets with 16-bit data, MSB first */ +#define MLX90614_EMISSIVITY (MLX90614_OP_EEPROM | 0x04) /* emissivity correction coefficient */ +#define MLX90614_CONFIG (MLX90614_OP_EEPROM | 0x05) /* configuration register */ + +/* Control bits in configuration register */ +#define MLX90614_CONFIG_IIR_SHIFT 0 /* IIR coefficient */ +#define MLX90614_CONFIG_IIR_MASK (0x7 << MLX90614_CONFIG_IIR_SHIFT) +#define MLX90614_CONFIG_DUAL_SHIFT 6 /* single (0) or dual (1) IR sensor */ +#define MLX90614_CONFIG_DUAL_MASK (1 << MLX90614_CONFIG_DUAL_SHIFT) +#define MLX90614_CONFIG_FIR_SHIFT 8 /* FIR coefficient */ +#define MLX90614_CONFIG_FIR_MASK (0x7 << MLX90614_CONFIG_FIR_SHIFT) +#define MLX90614_CONFIG_GAIN_SHIFT 11 /* gain */ +#define MLX90614_CONFIG_GAIN_MASK (0x7 << MLX90614_CONFIG_GAIN_SHIFT) + +/* Timings (in ms) */ +#define MLX90614_TIMING_EEPROM 20 /* time for EEPROM write/erase to complete */ +#define MLX90614_TIMING_WAKEUP 34 /* time to hold SDA low for wake-up */ +#define MLX90614_TIMING_STARTUP 250 /* time before first data after wake-up */ struct mlx90614_data { struct i2c_client *client; From bad4d1a074c7ba29636b951520574e18aec4c7fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vianney=20le=20Cl=C3=A9ment=20de=20Saint-Marcq?= Date: Tue, 24 Mar 2015 16:54:15 +0100 Subject: [PATCH 12/14] iio: mlx90614: Support devices with dual IR sensor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The model is detected by reading the EEPROM configuration during probing. Signed-off-by: Vianney le Clément de Saint-Marcq Cc: Arnout Vandecappelle (Essensium/Mind) Signed-off-by: Jonathan Cameron --- drivers/iio/temperature/mlx90614.c | 67 +++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 10 deletions(-) diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index a2e3aa6aa39d..a112fc9abf43 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -2,6 +2,7 @@ * mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor * * Copyright (c) 2014 Peter Meerwald + * Copyright (c) 2015 Essensium NV * * This file is subject to the terms and conditions of version 2 of * the GNU General Public License. See the file COPYING in the main @@ -59,26 +60,34 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, int *val2, long mask) { struct mlx90614_data *data = iio_priv(indio_dev); + u8 cmd; s32 ret; switch (mask) { case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */ switch (channel->channel2) { case IIO_MOD_TEMP_AMBIENT: - ret = i2c_smbus_read_word_data(data->client, - MLX90614_TA); - if (ret < 0) - return ret; + cmd = MLX90614_TA; break; case IIO_MOD_TEMP_OBJECT: - ret = i2c_smbus_read_word_data(data->client, - MLX90614_TOBJ1); - if (ret < 0) - return ret; + switch (channel->channel) { + case 0: + cmd = MLX90614_TOBJ1; + break; + case 1: + cmd = MLX90614_TOBJ2; + break; + default: + return -EINVAL; + } break; default: return -EINVAL; } + + ret = i2c_smbus_read_word_data(data->client, cmd); + if (ret < 0) + return ret; *val = ret; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: @@ -110,6 +119,16 @@ static const struct iio_chan_spec mlx90614_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), }, + { + .type = IIO_TEMP, + .indexed = 1, + .modified = 1, + .channel = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + }, }; static const struct iio_info mlx90614_info = { @@ -117,11 +136,25 @@ static const struct iio_info mlx90614_info = { .driver_module = THIS_MODULE, }; +/* Return 0 for single sensor, 1 for dual sensor, <0 on error. */ +static int mlx90614_probe_num_ir_sensors(struct i2c_client *client) +{ + s32 ret; + + ret = i2c_smbus_read_word_data(client, MLX90614_CONFIG); + + if (ret < 0) + return ret; + + return (ret & MLX90614_CONFIG_DUAL_MASK) ? 1 : 0; +} + static int mlx90614_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct iio_dev *indio_dev; struct mlx90614_data *data; + int ret; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -139,8 +172,21 @@ static int mlx90614_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mlx90614_info; - indio_dev->channels = mlx90614_channels; - indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels); + ret = mlx90614_probe_num_ir_sensors(client); + switch (ret) { + case 0: + dev_dbg(&client->dev, "Found single sensor"); + indio_dev->channels = mlx90614_channels; + indio_dev->num_channels = 2; + break; + case 1: + dev_dbg(&client->dev, "Found dual sensor"); + indio_dev->channels = mlx90614_channels; + indio_dev->num_channels = 3; + break; + default: + return ret; + } return iio_device_register(indio_dev); } @@ -170,5 +216,6 @@ static struct i2c_driver mlx90614_driver = { module_i2c_driver(mlx90614_driver); MODULE_AUTHOR("Peter Meerwald "); +MODULE_AUTHOR("Vianney le Clément de Saint-Marcq "); MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver"); MODULE_LICENSE("GPL"); From 5798cd23f87a93a7f187c0314804b9a736a5319a Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Sun, 29 Mar 2015 01:59:49 +0100 Subject: [PATCH 13/14] Documentation/ABI:iio:fix typo Signed-off-by: Martin Kepplinger Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 6bf79072179f..103dd9b94192 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -800,7 +800,7 @@ Description: What: /sys/.../events/in_accel_x_thresh_rising_period What: /sys/.../events/in_accel_x_thresh_falling_period -hat: /sys/.../events/in_accel_x_roc_rising_period +What: /sys/.../events/in_accel_x_roc_rising_period What: /sys/.../events/in_accel_x_roc_falling_period What: /sys/.../events/in_accel_y_thresh_rising_period What: /sys/.../events/in_accel_y_thresh_falling_period From e0631a31b9ed501415e552a783b1f1e3988c7c46 Mon Sep 17 00:00:00 2001 From: Irina Tirdea Date: Mon, 30 Mar 2015 14:22:13 +0300 Subject: [PATCH 14/14] iio: Documentation: fix kernel version for 4.0 new ABI Kernel version for new ABI in 4.0 has been documented as 3.20, since the changes have been merged before the kernel version number change. Change kernel version from 3.20 to 4.0. Signed-off-by: Irina Tirdea Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 103dd9b94192..3befcb19f414 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -351,7 +351,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Gender of the user (e.g.: male, female) used by some pedometers @@ -362,7 +362,7 @@ What: /sys/bus/iio/devices/iio:deviceX/in_activity_calibgender_available What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibgender_available What: /sys/bus/iio/devices/iio:deviceX/in_distance_calibgender_available What: /sys/bus/iio/devices/iio:deviceX/in_velocity_calibgender_available -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Lists all available gender values (e.g.: male, female). @@ -379,7 +379,7 @@ Description: type. What: /sys/bus/iio/devices/iio:deviceX/in_energy_calibweight -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Weight of the user (in kg). It is needed by some pedometers @@ -947,7 +947,7 @@ Description: this type. What: /sys/.../events/in_steps_change_en -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Event generated when channel passes a threshold on the absolute @@ -956,7 +956,7 @@ Description: in_steps_change_value. What: /sys/.../events/in_steps_change_value -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Specifies the value of change threshold that the @@ -1131,7 +1131,7 @@ Description: What: /sys/.../iio:deviceX/in_energy_input What: /sys/.../iio:deviceX/in_energy_raw -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: This attribute is used to read the energy value reported by the @@ -1140,7 +1140,7 @@ Description: What: /sys/.../iio:deviceX/in_distance_input What: /sys/.../iio:deviceX/in_distance_raw -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: This attribute is used to read the distance covered by the user @@ -1205,7 +1205,7 @@ Description: seconds. What: /sys/.../iio:deviceX/in_velocity_sqrt(x^2+y^2+z^2)_integration_time -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Number of seconds in which to compute speed. @@ -1267,7 +1267,7 @@ Description: Units after application of scale are m/s. What: /sys/.../iio:deviceX/in_steps_debounce_count -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Specifies the number of steps that must occur within @@ -1275,7 +1275,7 @@ Description: consumer is making steps. What: /sys/.../iio:deviceX/in_steps_debounce_time -KernelVersion: 3.20 +KernelVersion: 4.0 Contact: linux-iio@vger.kernel.org Description: Specifies number of seconds in which we compute the steps