From a8175ba33542d625430b66a805ef315ea3b4e755 Mon Sep 17 00:00:00 2001 From: Crestez Dan Leonard Date: Fri, 29 Apr 2016 22:05:32 +0300 Subject: [PATCH 01/61] iio: ak8975: Support adapters limited to BYTE_DATA The device has simple 8-bit registers but the driver incorrectly uses block or word reads without checking functionality bits. Fix by using i2c_smbus_read_i2c_block_data_or_emulated instead of i2c_smbus_read_i2c_block_data or i2c_smbus_read_word_data. This will check functionality bits and use the fastest available transfer method. Signed-off-by: Crestez Dan Leonard Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8975.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index dbf066129a04..c24b8a509ed8 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -430,8 +430,8 @@ static int ak8975_who_i_am(struct i2c_client *client, * AK8975 | DEVICE_ID | NA * AK8963 | DEVICE_ID | NA */ - ret = i2c_smbus_read_i2c_block_data(client, AK09912_REG_WIA1, - 2, wia_val); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, AK09912_REG_WIA1, 2, wia_val); if (ret < 0) { dev_err(&client->dev, "Error reading WIA\n"); return ret; @@ -543,9 +543,9 @@ static int ak8975_setup(struct i2c_client *client) } /* Get asa data and store in the device data. */ - ret = i2c_smbus_read_i2c_block_data(client, - data->def->ctrl_regs[ASA_BASE], - 3, data->asa); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, data->def->ctrl_regs[ASA_BASE], + 3, data->asa); if (ret < 0) { dev_err(&client->dev, "Not able to read asa data\n"); return ret; @@ -686,6 +686,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) struct ak8975_data *data = iio_priv(indio_dev); const struct i2c_client *client = data->client; const struct ak_def *def = data->def; + u16 buff; int ret; mutex_lock(&data->lock); @@ -694,14 +695,17 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val) if (ret) goto exit; - ret = i2c_smbus_read_word_data(client, def->data_regs[index]); + ret = i2c_smbus_read_i2c_block_data_or_emulated( + client, def->data_regs[index], + sizeof(buff), (u8*)&buff); if (ret < 0) goto exit; mutex_unlock(&data->lock); - /* Clamp to valid range. */ - *val = clamp_t(s16, ret, -def->range, def->range); + /* Swap bytes and convert to valid range. */ + buff = le16_to_cpu(buff); + *val = clamp_t(s16, buff, -def->range, def->range); return IIO_VAL_INT; exit: From 32133be6768257726b57094f673415418cb3dc48 Mon Sep 17 00:00:00 2001 From: Constantin Musca Date: Tue, 3 May 2016 15:05:45 +0300 Subject: [PATCH 02/61] iio: accel: Add support for Freescale MMA7660FC Minimal implementation of an IIO driver for the Freescale MMA7660FC 3-axis accelerometer. Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MMA7660FC.pdf Includes: - ACPI support; - read_raw for x,y,z axes; - reading and setting the scale (range) parameter. - power management Signed-off-by: Constantin Musca Reviewed-by: Martin Klepplinger Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 10 ++ drivers/iio/accel/Makefile | 2 + drivers/iio/accel/mma7660.c | 277 ++++++++++++++++++++++++++++++++++++ 3 files changed, 289 insertions(+) create mode 100644 drivers/iio/accel/mma7660.c diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index e4a758cd7d35..1df6361a57fe 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -136,6 +136,16 @@ config MMA7455_SPI To compile this driver as a module, choose M here: the module will be called mma7455_spi. +config MMA7660 + tristate "Freescale MMA7660FC 3-Axis Accelerometer Driver" + depends on I2C + help + Say yes here to get support for the Freescale MMA7660FC 3-Axis + accelerometer. + + Choosing M will build the driver as a module. If so, the module + will be called mma7660. + config MMA8452 tristate "Freescale MMA8452Q and similar Accelerometers Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 71b6794de885..ba1165f54ec7 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -15,6 +15,8 @@ obj-$(CONFIG_MMA7455) += mma7455_core.o obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o +obj-$(CONFIG_MMA7660) += mma7660.o + obj-$(CONFIG_MMA8452) += mma8452.o obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c new file mode 100644 index 000000000000..0acdee516973 --- /dev/null +++ b/drivers/iio/accel/mma7660.c @@ -0,0 +1,277 @@ +/** + * Freescale MMA7660FC 3-Axis Accelerometer + * + * Copyright (c) 2016, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c. + */ + +#include +#include +#include +#include +#include + +#define MMA7660_DRIVER_NAME "mma7660" + +#define MMA7660_REG_XOUT 0x00 +#define MMA7660_REG_YOUT 0x01 +#define MMA7660_REG_ZOUT 0x02 +#define MMA7660_REG_OUT_BIT_ALERT BIT(6) + +#define MMA7660_REG_MODE 0x07 +#define MMA7660_REG_MODE_BIT_MODE BIT(0) +#define MMA7660_REG_MODE_BIT_TON BIT(2) + +#define MMA7660_I2C_READ_RETRIES 5 + +/* + * The accelerometer has one measurement range: + * + * -1.5g - +1.5g (6-bit, signed) + * + * scale = (1.5 + 1.5) * 9.81 / (2^6 - 1) = 0.467142857 + */ + +#define MMA7660_SCALE_AVAIL "0.467142857" + +const int mma7660_nscale = 467142857; + +#define MMA7660_CHANNEL(reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec mma7660_channels[] = { + MMA7660_CHANNEL(MMA7660_REG_XOUT, X), + MMA7660_CHANNEL(MMA7660_REG_YOUT, Y), + MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z), +}; + +enum mma7660_mode { + MMA7660_MODE_STANDBY, + MMA7660_MODE_ACTIVE +}; + +struct mma7660_data { + struct i2c_client *client; + struct mutex lock; + enum mma7660_mode mode; +}; + +static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL); + +static struct attribute *mma7660_attributes[] = { + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group mma7660_attribute_group = { + .attrs = mma7660_attributes +}; + +static int mma7660_set_mode(struct mma7660_data *data, + enum mma7660_mode mode) +{ + int ret; + struct i2c_client *client = data->client; + + if (mode == data->mode) + return 0; + + ret = i2c_smbus_read_byte_data(client, MMA7660_REG_MODE); + if (ret < 0) { + dev_err(&client->dev, "failed to read sensor mode\n"); + return ret; + } + + if (mode == MMA7660_MODE_ACTIVE) { + ret &= ~MMA7660_REG_MODE_BIT_TON; + ret |= MMA7660_REG_MODE_BIT_MODE; + } else { + ret &= ~MMA7660_REG_MODE_BIT_TON; + ret &= ~MMA7660_REG_MODE_BIT_MODE; + } + + ret = i2c_smbus_write_byte_data(client, MMA7660_REG_MODE, ret); + if (ret < 0) { + dev_err(&client->dev, "failed to change sensor mode\n"); + return ret; + } + + data->mode = mode; + + return ret; +} + +static int mma7660_read_accel(struct mma7660_data *data, u8 address) +{ + int ret, retries = MMA7660_I2C_READ_RETRIES; + struct i2c_client *client = data->client; + + /* + * Read data. If the Alert bit is set, the register was read at + * the same time as the device was attempting to update the content. + * The solution is to read the register again. Do this only + * MMA7660_I2C_READ_RETRIES times to avoid spending too much time + * in the kernel. + */ + do { + ret = i2c_smbus_read_byte_data(client, address); + if (ret < 0) { + dev_err(&client->dev, "register read failed\n"); + return ret; + } + } while (retries-- > 0 && ret & MMA7660_REG_OUT_BIT_ALERT); + + if (ret & MMA7660_REG_OUT_BIT_ALERT) { + dev_err(&client->dev, "all register read retries failed\n"); + return -ETIMEDOUT; + } + + return ret; +} + +static int mma7660_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mma7660_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret = mma7660_read_accel(data, chan->address); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = mma7660_nscale; + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } + + return -EINVAL; +} + +static const struct iio_info mma7660_info = { + .driver_module = THIS_MODULE, + .read_raw = mma7660_read_raw, + .attrs = &mma7660_attribute_group, +}; + +static int mma7660_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct mma7660_data *data; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&client->dev, "iio allocation failed!\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + data->client = client; + i2c_set_clientdata(client, indio_dev); + mutex_init(&data->lock); + data->mode = MMA7660_MODE_STANDBY; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &mma7660_info; + indio_dev->name = MMA7660_DRIVER_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mma7660_channels; + indio_dev->num_channels = ARRAY_SIZE(mma7660_channels); + + ret = mma7660_set_mode(data, MMA7660_MODE_ACTIVE); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&client->dev, "device_register failed\n"); + mma7660_set_mode(data, MMA7660_MODE_STANDBY); + } + + return ret; +} + +static int mma7660_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + + return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); +} + +#ifdef CONFIG_PM_SLEEP +static int mma7660_suspend(struct device *dev) +{ + struct mma7660_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mma7660_set_mode(data, MMA7660_MODE_STANDBY); +} + +static int mma7660_resume(struct device *dev) +{ + struct mma7660_data *data; + + data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); +} + +static SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, mma7660_resume); + +#define MMA7660_PM_OPS (&mma7660_pm_ops) +#else +#define MMA7660_PM_OPS NULL +#endif + +static const struct i2c_device_id mma7660_i2c_id[] = { + {"mma7660", 0}, + {} +}; + +static const struct acpi_device_id mma7660_acpi_id[] = { + {"MMA7660", 0}, + {} +}; + +MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); + +static struct i2c_driver mma7660_driver = { + .driver = { + .name = "mma7660", + .pm = MMA7660_PM_OPS, + .acpi_match_table = ACPI_PTR(mma7660_acpi_id), + }, + .probe = mma7660_probe, + .remove = mma7660_remove, + .id_table = mma7660_i2c_id, +}; + +module_i2c_driver(mma7660_driver); + +MODULE_AUTHOR("Constantin Musca "); +MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver"); +MODULE_LICENSE("GPL v2"); From a91e0b6de468cc4413d8ca260ed4ed0366dfb5b7 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 23 Apr 2016 19:57:57 +0000 Subject: [PATCH 03/61] iio: mxs-lradc: simplify TS registration This patch simplifies the TS registration of mxs-lradc by using devm_input_allocate_device. Signed-off-by: Stefan Wahren Reviewed-by: Marek Vasut Acked-by: Dmitry Torokhov Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mxs-lradc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c index ad26da1edbee..3d1d3706bf9b 100644 --- a/drivers/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -1120,18 +1120,16 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc) { struct input_dev *input; struct device *dev = lradc->dev; - int ret; if (!lradc->use_touchscreen) return 0; - input = input_allocate_device(); + input = devm_input_allocate_device(dev); if (!input) return -ENOMEM; input->name = DRIVER_NAME; input->id.bustype = BUS_HOST; - input->dev.parent = dev; input->open = mxs_lradc_ts_open; input->close = mxs_lradc_ts_close; @@ -1146,11 +1144,8 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc) lradc->ts_input = input; input_set_drvdata(input, lradc); - ret = input_register_device(input); - if (ret) - input_free_device(lradc->ts_input); - return ret; + return input_register_device(input); } static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc) @@ -1159,7 +1154,6 @@ static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc) return; mxs_lradc_disable_ts(lradc); - input_unregister_device(lradc->ts_input); } /* From 962ed43a3eac49da65ad90fd1036e74faa5b5066 Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 23 Apr 2016 19:57:58 +0000 Subject: [PATCH 04/61] iio: mxs-lradc: remove mxs_lradc_ts_unregister After using devm_input_allocate_device for registration the function mxs_lradc_ts_unregister isn't necessary anymore since mxs_lradc_ts_close already does the job. Signed-off-by: Stefan Wahren Suggested-by: Dmitry Torokhov Acked-by: Dmitry Torokhov Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mxs-lradc.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c index 3d1d3706bf9b..90242bac31db 100644 --- a/drivers/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -1148,14 +1148,6 @@ static int mxs_lradc_ts_register(struct mxs_lradc *lradc) return input_register_device(input); } -static void mxs_lradc_ts_unregister(struct mxs_lradc *lradc) -{ - if (!lradc->use_touchscreen) - return; - - mxs_lradc_disable_ts(lradc); -} - /* * IRQ Handling */ @@ -1715,13 +1707,11 @@ static int mxs_lradc_probe(struct platform_device *pdev) ret = iio_device_register(iio); if (ret) { dev_err(dev, "Failed to register IIO device\n"); - goto err_ts; + return ret; } return 0; -err_ts: - mxs_lradc_ts_unregister(lradc); err_ts_register: mxs_lradc_hw_stop(lradc); err_dev: @@ -1739,7 +1729,6 @@ static int mxs_lradc_remove(struct platform_device *pdev) struct mxs_lradc *lradc = iio_priv(iio); iio_device_unregister(iio); - mxs_lradc_ts_unregister(lradc); mxs_lradc_hw_stop(lradc); mxs_lradc_trigger_remove(iio); iio_triggered_buffer_cleanup(iio); From 331dd5be97a9a241329a99cbe19e217273ae541b Mon Sep 17 00:00:00 2001 From: Stefan Wahren Date: Sat, 23 Apr 2016 19:57:59 +0000 Subject: [PATCH 05/61] iio: mxs-lradc: disable only masked channels in mxs_lradc_hw_stop Disabling of the touchscreen IRQs should be done in mxs_lradc_disable_ts. So disable only the masked virtual channels in mxs_lradc_hw_stop and finally remove the unused function mxs_lradc_irq_en_mask. Signed-off-by: Stefan Wahren Reviewed-by: Marek Vasut Tested-by: Marek Vasut Signed-off-by: Jonathan Cameron --- drivers/iio/adc/mxs-lradc.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/drivers/iio/adc/mxs-lradc.c b/drivers/iio/adc/mxs-lradc.c index 90242bac31db..b84d37c80a94 100644 --- a/drivers/iio/adc/mxs-lradc.c +++ b/drivers/iio/adc/mxs-lradc.c @@ -373,13 +373,6 @@ static u32 mxs_lradc_plate_mask(struct mxs_lradc *lradc) return LRADC_CTRL0_MX28_PLATE_MASK; } -static u32 mxs_lradc_irq_en_mask(struct mxs_lradc *lradc) -{ - if (lradc->soc == IMX23_LRADC) - return LRADC_CTRL1_MX23_LRADC_IRQ_EN_MASK; - return LRADC_CTRL1_MX28_LRADC_IRQ_EN_MASK; -} - static u32 mxs_lradc_irq_mask(struct mxs_lradc *lradc) { if (lradc->soc == IMX23_LRADC) @@ -1496,7 +1489,9 @@ static void mxs_lradc_hw_stop(struct mxs_lradc *lradc) { int i; - mxs_lradc_reg_clear(lradc, mxs_lradc_irq_en_mask(lradc), LRADC_CTRL1); + mxs_lradc_reg_clear(lradc, + lradc->buffer_vchans << LRADC_CTRL1_LRADC_IRQ_EN_OFFSET, + LRADC_CTRL1); for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++) mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(i)); From 216912e3f1aa6c7216ba7913c8694c41c885fa04 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Fri, 29 Apr 2016 14:42:35 +0300 Subject: [PATCH 06/61] imu: bmi160: Add avail frequency and scale attributes Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi160/bmi160_core.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 0bf92b06d7d8..914e2e7971be 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "bmi160.h" @@ -466,10 +467,36 @@ static int bmi160_write_raw(struct iio_dev *indio_dev, return 0; } +static +IIO_CONST_ATTR(in_accel_sampling_frequency_available, + "0.78125 1.5625 3.125 6.25 12.5 25 50 100 200 400 800 1600"); +static +IIO_CONST_ATTR(in_anglvel_sampling_frequency_available, + "25 50 100 200 400 800 1600 3200"); +static +IIO_CONST_ATTR(in_accel_scale_available, + "0.000598 0.001197 0.002394 0.004788"); +static +IIO_CONST_ATTR(in_anglvel_scale_available, + "0.001065 0.000532 0.000266 0.000133 0.000066"); + +static struct attribute *bmi160_attrs[] = { + &iio_const_attr_in_accel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_anglvel_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + &iio_const_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bmi160_attrs_group = { + .attrs = bmi160_attrs, +}; + static const struct iio_info bmi160_info = { .driver_module = THIS_MODULE, .read_raw = bmi160_read_raw, .write_raw = bmi160_write_raw, + .attrs = &bmi160_attrs_group, }; static const char *bmi160_match_acpi_device(struct device *dev) From f59e6b5ae0a9dd94ac9a3d5d95f8650649343dc3 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:51 -0500 Subject: [PATCH 07/61] iio: health/afe440x: Fix kernel-doc format Fix kernel-doc formatting for structs, and while we are making little fixes, clarify the module description and update the copywrite. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 18 +++++++++--------- drivers/iio/health/afe4404.c | 16 ++++++++-------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 88e43f87b926..209411332665 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -1,7 +1,7 @@ /* * AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * * This program is free software; you can redistribute it and/or modify @@ -103,13 +103,13 @@ #define AFE4403_TIAGAIN_RES_NONE 0x7 /** - * struct afe4403_data - * @dev - Device structure - * @spi - SPI device handle - * @regmap - Register map of the device - * @regulator - Pointer to the regulator for the IC - * @trig - IIO trigger for this device - * @irq - ADC_RDY line interrupt number + * struct afe4403_data - AFE4403 device instance data + * @dev: Device structure + * @spi: SPI device handle + * @regmap: Register map of the device + * @regulator: Pointer to the regulator for the IC + * @trig: IIO trigger for this device + * @irq: ADC_RDY line interrupt number */ struct afe4403_data { struct device *dev; @@ -704,5 +704,5 @@ static struct spi_driver afe4403_spi_driver = { module_spi_driver(afe4403_spi_driver); MODULE_AUTHOR("Andrew F. Davis "); -MODULE_DESCRIPTION("TI AFE4403 Heart Rate and Pulse Oximeter"); +MODULE_DESCRIPTION("TI AFE4403 Heart Rate Monitor and Pulse Oximeter AFE"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 5096a4643784..7127d038b512 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -1,7 +1,7 @@ /* * AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters * - * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis * * This program is free software; you can redistribute it and/or modify @@ -106,12 +106,12 @@ #define AFE4404_TIA_GAIN_RES_2_M 0x7 /** - * struct afe4404_data - * @dev - Device structure - * @regmap - Register map of the device - * @regulator - Pointer to the regulator for the IC - * @trig - IIO trigger for this device - * @irq - ADC_RDY line interrupt number + * struct afe4404_data - AFE4404 device instance data + * @dev: Device structure + * @regmap: Register map of the device + * @regulator: Pointer to the regulator for the IC + * @trig: IIO trigger for this device + * @irq: ADC_RDY line interrupt number */ struct afe4404_data { struct device *dev; @@ -675,5 +675,5 @@ static struct i2c_driver afe4404_i2c_driver = { module_i2c_driver(afe4404_i2c_driver); MODULE_AUTHOR("Andrew F. Davis "); -MODULE_DESCRIPTION("TI AFE4404 Heart Rate and Pulse Oximeter"); +MODULE_DESCRIPTION("TI AFE4404 Heart Rate Monitor and Pulse Oximeter AFE"); MODULE_LICENSE("GPL v2"); From daffd7a75c7e878845ddae8e16d9dc76eea7e2d2 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:52 -0500 Subject: [PATCH 08/61] iio: health/afe440x: Remove of_match_ptr and ifdefs The drivers DT tables are not built-in when OF is not enabled, this does not save us enough to justify ugly ifdefs. Clean this up. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 4 +--- drivers/iio/health/afe4404.c | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 209411332665..e3700cfec23d 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -498,13 +498,11 @@ static const struct regmap_config afe4403_regmap_config = { .volatile_table = &afe4403_volatile_table, }; -#ifdef CONFIG_OF static const struct of_device_id afe4403_of_match[] = { { .compatible = "ti,afe4403", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, afe4403_of_match); -#endif static int __maybe_unused afe4403_suspend(struct device *dev) { @@ -694,7 +692,7 @@ MODULE_DEVICE_TABLE(spi, afe4403_ids); static struct spi_driver afe4403_spi_driver = { .driver = { .name = AFE4403_DRIVER_NAME, - .of_match_table = of_match_ptr(afe4403_of_match), + .of_match_table = afe4403_of_match, .pm = &afe4403_pm_ops, }, .probe = afe4403_probe, diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 7127d038b512..783b9b437716 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -469,13 +469,11 @@ static const struct regmap_config afe4404_regmap_config = { .volatile_table = &afe4404_volatile_table, }; -#ifdef CONFIG_OF static const struct of_device_id afe4404_of_match[] = { { .compatible = "ti,afe4404", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, afe4404_of_match); -#endif static int __maybe_unused afe4404_suspend(struct device *dev) { @@ -665,7 +663,7 @@ MODULE_DEVICE_TABLE(i2c, afe4404_ids); static struct i2c_driver afe4404_i2c_driver = { .driver = { .name = AFE4404_DRIVER_NAME, - .of_match_table = of_match_ptr(afe4404_of_match), + .of_match_table = afe4404_of_match, .pm = &afe4404_pm_ops, }, .probe = afe4404_probe, From e85fa0338b4c0399d7c42837f45f9afb8ede7541 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:53 -0500 Subject: [PATCH 09/61] iio: health/afe440x: Remove unneeded initializers The drivers set some register values during initialization that can be set at runtime, these defaults were used in testing but are not necessary, remove these. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 7 +------ drivers/iio/health/afe4404.c | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index e3700cfec23d..54847853eb7a 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -472,12 +472,7 @@ static const struct iio_trigger_ops afe4403_trigger_ops = { static const struct reg_sequence afe4403_reg_sequences[] = { AFE4403_TIMING_PAIRS, - { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN | 0x000007}, - { AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES_1_M }, - { AFE440X_LEDCNTRL, (0x14 << AFE440X_LEDCNTRL_LED1_SHIFT) | - (0x14 << AFE440X_LEDCNTRL_LED2_SHIFT) }, - { AFE440X_CONTROL2, AFE440X_CONTROL2_TX_REF_050 << - AFE440X_CONTROL2_TX_REF_SHIFT }, + { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, }; static const struct regmap_range afe4403_yes_ranges[] = { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 783b9b437716..2d4c522f81e1 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -443,10 +443,6 @@ static const struct iio_trigger_ops afe4404_trigger_ops = { static const struct reg_sequence afe4404_reg_sequences[] = { AFE4404_TIMING_PAIRS, { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, - { AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES_50_K }, - { AFE440X_LEDCNTRL, (0xf << AFE4404_LEDCNTRL_ILED1_SHIFT) | - (0x3 << AFE4404_LEDCNTRL_ILED2_SHIFT) | - (0x3 << AFE4404_LEDCNTRL_ILED3_SHIFT) }, { AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE }, }; From 81f517270da632c0ddf4c511a086d5aa7e5606ee Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:54 -0500 Subject: [PATCH 10/61] iio: health/afe440x: Always use separate gain values Locking the two gain stages to the same setting adds no value for us, so initialize them as unlocked and remove the sysfs for unlocking them. This also allows us to greatly simplify showing and setting the gain registers. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-health-afe440x | 9 --- drivers/iio/health/afe4403.c | 60 ++++++------------- drivers/iio/health/afe4404.c | 60 ++++++------------- drivers/iio/health/afe440x.h | 15 +---- 4 files changed, 37 insertions(+), 107 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index 3740f253d406..b19053a86054 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -8,15 +8,6 @@ Description: Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for Rf2 and Cf2 values. -What: /sys/bus/iio/devices/iio:deviceX/tia_separate_en -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Enable or disable separate settings for the TransImpedance - Amplifier above, when disabled both values are set by the - first channel. - What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw Date: December 2015 diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 54847853eb7a..bcff5280c5c9 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -180,9 +180,9 @@ static ssize_t afe440x_show_register(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct afe4403_data *afe = iio_priv(indio_dev); struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); - unsigned int reg_val, type; + unsigned int reg_val; int vals[2]; - int ret, val_len; + int ret; ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); if (ret) @@ -191,27 +191,13 @@ static ssize_t afe440x_show_register(struct device *dev, reg_val &= afe440x_attr->mask; reg_val >>= afe440x_attr->shift; - switch (afe440x_attr->type) { - case SIMPLE: - type = IIO_VAL_INT; - val_len = 1; - vals[0] = reg_val; - break; - case RESISTANCE: - case CAPACITANCE: - type = IIO_VAL_INT_PLUS_MICRO; - val_len = 2; - if (reg_val < afe440x_attr->table_size) { - vals[0] = afe440x_attr->val_table[reg_val].integer; - vals[1] = afe440x_attr->val_table[reg_val].fract; - break; - } + if (reg_val >= afe440x_attr->table_size) return -EINVAL; - default: - return -EINVAL; - } - return iio_format_value(buf, type, val_len, vals); + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); } static ssize_t afe440x_store_register(struct device *dev, @@ -227,22 +213,12 @@ static ssize_t afe440x_store_register(struct device *dev, if (ret) return ret; - switch (afe440x_attr->type) { - case SIMPLE: - val = integer; - break; - case RESISTANCE: - case CAPACITANCE: - for (val = 0; val < afe440x_attr->table_size; val++) - if (afe440x_attr->val_table[val].integer == integer && - afe440x_attr->val_table[val].fract == fract) - break; - if (val == afe440x_attr->table_size) - return -EINVAL; - break; - default: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) return -EINVAL; - } ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, afe440x_attr->mask, @@ -253,16 +229,13 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_separate_en, AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); +static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, afe4403_res_table); +static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); -static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); - -static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, RESISTANCE, afe4403_res_table, ARRAY_SIZE(afe4403_res_table)); -static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, CAPACITANCE, afe4403_cap_table, ARRAY_SIZE(afe4403_cap_table)); +static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, afe4403_res_table); +static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, afe4403_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_separate_en.dev_attr.attr, &afe440x_attr_tia_resistance1.dev_attr.attr, &afe440x_attr_tia_capacitance1.dev_attr.attr, &afe440x_attr_tia_resistance2.dev_attr.attr, @@ -473,6 +446,7 @@ static const struct iio_trigger_ops afe4403_trigger_ops = { static const struct reg_sequence afe4403_reg_sequences[] = { AFE4403_TIMING_PAIRS, { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, + { AFE4403_TIAGAIN, AFE440X_TIAGAIN_ENSEPGAIN }, }; static const struct regmap_range afe4403_yes_ranges[] = { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2d4c522f81e1..b9c1666c963b 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -193,9 +193,9 @@ static ssize_t afe440x_show_register(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct afe4404_data *afe = iio_priv(indio_dev); struct afe440x_attr *afe440x_attr = to_afe440x_attr(attr); - unsigned int reg_val, type; + unsigned int reg_val; int vals[2]; - int ret, val_len; + int ret; ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); if (ret) @@ -204,27 +204,13 @@ static ssize_t afe440x_show_register(struct device *dev, reg_val &= afe440x_attr->mask; reg_val >>= afe440x_attr->shift; - switch (afe440x_attr->type) { - case SIMPLE: - type = IIO_VAL_INT; - val_len = 1; - vals[0] = reg_val; - break; - case RESISTANCE: - case CAPACITANCE: - type = IIO_VAL_INT_PLUS_MICRO; - val_len = 2; - if (reg_val < afe440x_attr->table_size) { - vals[0] = afe440x_attr->val_table[reg_val].integer; - vals[1] = afe440x_attr->val_table[reg_val].fract; - break; - } + if (reg_val >= afe440x_attr->table_size) return -EINVAL; - default: - return -EINVAL; - } - return iio_format_value(buf, type, val_len, vals); + vals[0] = afe440x_attr->val_table[reg_val].integer; + vals[1] = afe440x_attr->val_table[reg_val].fract; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals); } static ssize_t afe440x_store_register(struct device *dev, @@ -240,22 +226,12 @@ static ssize_t afe440x_store_register(struct device *dev, if (ret) return ret; - switch (afe440x_attr->type) { - case SIMPLE: - val = integer; - break; - case RESISTANCE: - case CAPACITANCE: - for (val = 0; val < afe440x_attr->table_size; val++) - if (afe440x_attr->val_table[val].integer == integer && - afe440x_attr->val_table[val].fract == fract) - break; - if (val == afe440x_attr->table_size) - return -EINVAL; - break; - default: + for (val = 0; val < afe440x_attr->table_size; val++) + if (afe440x_attr->val_table[val].integer == integer && + afe440x_attr->val_table[val].fract == fract) + break; + if (val == afe440x_attr->table_size) return -EINVAL; - } ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, afe440x_attr->mask, @@ -266,16 +242,13 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_separate_en, AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN, SIMPLE, NULL, 0); +static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, afe4404_res_table); +static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); -static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); - -static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, RESISTANCE, afe4404_res_table, ARRAY_SIZE(afe4404_res_table)); -static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, CAPACITANCE, afe4404_cap_table, ARRAY_SIZE(afe4404_cap_table)); +static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, afe4404_res_table); +static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, afe4404_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_separate_en.dev_attr.attr, &afe440x_attr_tia_resistance1.dev_attr.attr, &afe440x_attr_tia_capacitance1.dev_attr.attr, &afe440x_attr_tia_resistance2.dev_attr.attr, @@ -443,6 +416,7 @@ static const struct iio_trigger_ops afe4404_trigger_ops = { static const struct reg_sequence afe4404_reg_sequences[] = { AFE4404_TIMING_PAIRS, { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, + { AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN }, { AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE }, }; diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index c671ab78a23a..544bbabd6321 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -71,8 +71,7 @@ #define AFE440X_CONTROL1_TIMEREN BIT(8) /* TIAGAIN register fields */ -#define AFE440X_TIAGAIN_ENSEPGAIN_MASK BIT(15) -#define AFE440X_TIAGAIN_ENSEPGAIN_SHIFT 15 +#define AFE440X_TIAGAIN_ENSEPGAIN BIT(15) /* CONTROL2 register fields */ #define AFE440X_CONTROL2_PDN_AFE BIT(0) @@ -133,12 +132,6 @@ struct afe440x_reg_info { .output = true, \ } -enum afe440x_reg_type { - SIMPLE, - RESISTANCE, - CAPACITANCE, -}; - struct afe440x_val_table { int integer; int fract; @@ -167,7 +160,6 @@ struct afe440x_attr { unsigned int reg; unsigned int shift; unsigned int mask; - enum afe440x_reg_type type; const struct afe440x_val_table *val_table; unsigned int table_size; }; @@ -175,7 +167,7 @@ struct afe440x_attr { #define to_afe440x_attr(_dev_attr) \ container_of(_dev_attr, struct afe440x_attr, dev_attr) -#define AFE440X_ATTR(_name, _reg, _field, _type, _table, _size) \ +#define AFE440X_ATTR(_name, _reg, _field, _table) \ struct afe440x_attr afe440x_attr_##_name = { \ .dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \ afe440x_show_register, \ @@ -183,9 +175,8 @@ struct afe440x_attr { .reg = _reg, \ .shift = _field ## _SHIFT, \ .mask = _field ## _MASK, \ - .type = _type, \ .val_table = _table, \ - .table_size = _size, \ + .table_size = ARRAY_SIZE(_table), \ } #endif /* _AFE440X_H */ From 9d3d9a57e4242b155b965f58639a230b212a0ea5 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:55 -0500 Subject: [PATCH 11/61] iio: health/afe440x: Fix scan_index assignment The LED channels are not scannable and so scan_index should be negative, fix this here. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe440x.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index 544bbabd6321..583d071af11d 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -125,7 +125,7 @@ struct afe440x_reg_info { .type = IIO_CURRENT, \ .channel = _index, \ .address = _index, \ - .scan_index = _index, \ + .scan_index = -1, \ .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ From 2e0df3a583af9297259ff5512c1a843220492770 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:56 -0500 Subject: [PATCH 12/61] iio: health/afe440x: Remove unneeded offset handling No channel in the afe4403 driver has IIO_CHAN_INFO_OFFSET set so remove the handlers for this. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index bcff5280c5c9..cac60909a585 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -293,14 +293,6 @@ static int afe4403_read_raw(struct iio_dev *indio_dev, if (ret) return ret; return IIO_VAL_INT; - case IIO_CHAN_INFO_OFFSET: - ret = regmap_read(afe->regmap, reg_info.offreg, - val); - if (ret) - return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; - return IIO_VAL_INT; } break; case IIO_CURRENT: @@ -333,15 +325,6 @@ static int afe4403_write_raw(struct iio_dev *indio_dev, const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; switch (chan->type) { - case IIO_INTENSITY: - switch (mask) { - case IIO_CHAN_INFO_OFFSET: - return regmap_update_bits(afe->regmap, - reg_info.offreg, - reg_info.mask, - (val << reg_info.shift)); - } - break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: From 606c7e6c1b02054febd78aac1d3cff448c7bd052 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:57 -0500 Subject: [PATCH 13/61] iio: health/afe4404: Remove LED3 input channel Input channel LED3 is only an alias for stage ALED2, this virtual channel does nothing for us, remove this channel. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4404.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index b9c1666c963b..2edb7d75ef38 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -126,7 +126,6 @@ enum afe4404_chan_id { ALED1, LED2, ALED2, - LED3, LED1_ALED1, LED2_ALED2, ILED1, @@ -139,7 +138,6 @@ static const struct afe440x_reg_info afe4404_reg_info[] = { [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), - [LED3] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), @@ -153,7 +151,6 @@ static const struct iio_chan_spec afe4404_channels[] = { AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)), AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)), AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED3, "led3", BIT(IIO_CHAN_INFO_OFFSET)), AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), /* LED current */ From 24b9dea764bdf0de8434fb4567e7f62038ba869e Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:58 -0500 Subject: [PATCH 14/61] iio: health/afe440x: Remove channel names These AFEs have 4 ADC mesuring stages (called LED2, ALED2, LED1, and ALED1 in the datasheet), we map these as channels, these stages can serve different purposes depending on the application. For instance the AFE4404 has an additional LED (LED3), this LED can be timed to be active during stage 2 (or anystage, but the datasheet describes this case and the name of the stage reflects this use). This ability is used further in upcoming parts that tie the front-end gain and the LED timings together. For these reasons we remove explicit naming the channels. Without channel names it is best that the index numbers are in order to match the stage number, reorder the channel numbers. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-health-afe440x | 39 +++++++++---------- drivers/iio/health/afe4403.c | 28 ++++++------- drivers/iio/health/afe4404.c | 30 +++++++------- drivers/iio/health/afe440x.h | 8 ++-- 4 files changed, 51 insertions(+), 54 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index b19053a86054..a06707320d84 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -8,38 +8,35 @@ Description: Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for Rf2 and Cf2 values. -What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_raw - /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY_ambient_raw -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_raw +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: Get measured values from the ADC for these stages. Y is the - specific LED number. The values are expressed in 24-bit twos - complement. + specific stage number corresponding to datasheet stage names + as follows: + 1 -> LED2 + 2 -> ALED2/LED3 + 3 -> LED1 + 4 -> ALED1/LED4 + Note that channels 5 and 6 represent LED2-ALED2 and LED1-ALED1 + respectively which simply helper channels containing the + calculated difference in the value of stage 1 - 2 and 3 - 4. + The values are expressed in 24-bit twos complement. -What: /sys/bus/iio/devices/iio:deviceX/in_intensity_ledY-ledY_ambient_raw -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Get differential values from the ADC for these stages. Y is the - specific LED number. The values are expressed in 24-bit twos - complement for the specified LEDs. - -What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_offset - /sys/bus/iio/devices/iio:deviceX/out_current_ledY_ambient_offset -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_offset +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: Get and set the offset cancellation DAC setting for these stages. The values are expressed in 5-bit sign-magnitude. -What: /sys/bus/iio/devices/iio:deviceX/out_current_ledY_raw -Date: December 2015 +What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw +Date: May 2016 KernelVersion: Contact: Andrew F. Davis Description: - Get and set the LED current for the specified LED. Y is the - specific LED number. + Get and set the LED current for the specified LED active during + this stage. Y is the specific stage number. diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index cac60909a585..4a580646f10e 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -121,38 +121,38 @@ struct afe4403_data { }; enum afe4403_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED1_ALED1, LED2_ALED2, + LED1_ALED1, ILED1, ILED2, }; static const struct afe440x_reg_info afe4403_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL), [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1), [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2), }; static const struct iio_chan_spec afe4403_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", 0), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2, "led2", 0), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", 0), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, 0), + AFE440X_INTENSITY_CHAN(ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1, 0), + AFE440X_INTENSITY_CHAN(ALED1, 0), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), + AFE440X_CURRENT_CHAN(ILED1), + AFE440X_CURRENT_CHAN(ILED2), }; static const struct afe440x_val_table afe4403_res_table[] = { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 2edb7d75ef38..7806a452edf8 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -122,24 +122,24 @@ struct afe4404_data { }; enum afe4404_chan_id { + LED2 = 1, + ALED2, LED1, ALED1, - LED2, - ALED2, - LED1_ALED1, LED2_ALED2, + LED1_ALED1, ILED1, ILED2, ILED3, }; static const struct afe440x_reg_info afe4404_reg_info[] = { - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), + [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), + [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), + [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2), [ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3), @@ -147,16 +147,16 @@ static const struct afe440x_reg_info afe4404_reg_info[] = { static const struct iio_chan_spec afe4404_channels[] = { /* ADC values */ - AFE440X_INTENSITY_CHAN(LED1, "led1", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED1, "led1_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED2, "led2", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(ALED2, "led2_ambient", BIT(IIO_CHAN_INFO_OFFSET)), - AFE440X_INTENSITY_CHAN(LED1_ALED1, "led1-led1_ambient", 0), - AFE440X_INTENSITY_CHAN(LED2_ALED2, "led2-led2_ambient", 0), + AFE440X_INTENSITY_CHAN(LED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED2, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(ALED1, BIT(IIO_CHAN_INFO_OFFSET)), + AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), + AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1, "led1"), - AFE440X_CURRENT_CHAN(ILED2, "led2"), - AFE440X_CURRENT_CHAN(ILED3, "led3"), + AFE440X_CURRENT_CHAN(ILED1), + AFE440X_CURRENT_CHAN(ILED2), + AFE440X_CURRENT_CHAN(ILED3), }; static const struct afe440x_val_table afe4404_res_table[] = { diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index 583d071af11d..713972fd4601 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -103,7 +103,7 @@ struct afe440x_reg_info { .mask = _sm ## _MASK, \ } -#define AFE440X_INTENSITY_CHAN(_index, _name, _mask) \ +#define AFE440X_INTENSITY_CHAN(_index, _mask) \ { \ .type = IIO_INTENSITY, \ .channel = _index, \ @@ -115,20 +115,20 @@ struct afe440x_reg_info { .storagebits = 32, \ .endianness = IIO_CPU, \ }, \ - .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ _mask, \ + .indexed = true, \ } -#define AFE440X_CURRENT_CHAN(_index, _name) \ +#define AFE440X_CURRENT_CHAN(_index) \ { \ .type = IIO_CURRENT, \ .channel = _index, \ .address = _index, \ .scan_index = -1, \ - .extend_name = _name, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ + .indexed = true, \ .output = true, \ } From b36e8257641a043764c62240316610c81e36376c Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:36:59 -0500 Subject: [PATCH 15/61] iio: health/afe440x: Use regmap fields These drivers can use regmap fields to access fields in registers, this allows us to remove some macros/defines and simplify code, do this here. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 118 +++++++++++++------------- drivers/iio/health/afe4404.c | 156 ++++++++++++++++++----------------- drivers/iio/health/afe440x.h | 25 +----- 3 files changed, 144 insertions(+), 155 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 4a580646f10e..19501550c6ff 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -39,32 +39,6 @@ #define AFE4403_TIAGAIN 0x20 #define AFE4403_TIA_AMB_GAIN 0x21 -/* AFE4403 GAIN register fields */ -#define AFE4403_TIAGAIN_RES_MASK GENMASK(2, 0) -#define AFE4403_TIAGAIN_RES_SHIFT 0 -#define AFE4403_TIAGAIN_CAP_MASK GENMASK(7, 3) -#define AFE4403_TIAGAIN_CAP_SHIFT 3 - -/* AFE4403 LEDCNTRL register fields */ -#define AFE440X_LEDCNTRL_LED1_MASK GENMASK(15, 8) -#define AFE440X_LEDCNTRL_LED1_SHIFT 8 -#define AFE440X_LEDCNTRL_LED2_MASK GENMASK(7, 0) -#define AFE440X_LEDCNTRL_LED2_SHIFT 0 -#define AFE440X_LEDCNTRL_LED_RANGE_MASK GENMASK(17, 16) -#define AFE440X_LEDCNTRL_LED_RANGE_SHIFT 16 - -/* AFE4403 CONTROL2 register fields */ -#define AFE440X_CONTROL2_PWR_DWN_TX BIT(2) -#define AFE440X_CONTROL2_EN_SLOW_DIAG BIT(8) -#define AFE440X_CONTROL2_DIAG_OUT_TRI BIT(10) -#define AFE440X_CONTROL2_TX_BRDG_MOD BIT(11) -#define AFE440X_CONTROL2_TX_REF_MASK GENMASK(18, 17) -#define AFE440X_CONTROL2_TX_REF_SHIFT 17 - -/* AFE4404 NULL fields */ -#define NULL_MASK 0 -#define NULL_SHIFT 0 - /* AFE4403 LEDCNTRL values */ #define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1 #define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2 @@ -102,11 +76,35 @@ #define AFE4403_TIAGAIN_RES_1_M 0x6 #define AFE4403_TIAGAIN_RES_NONE 0x7 +enum afe4403_fields { + /* Gains */ + F_RF_LED1, F_CF_LED1, + F_RF_LED, F_CF_LED, + + /* LED Current */ + F_ILED1, F_ILED2, + + /* sentinel */ + F_MAX_FIELDS +}; + +static const struct reg_field afe4403_reg_fields[] = { + /* Gains */ + [F_RF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 0, 2), + [F_CF_LED1] = REG_FIELD(AFE4403_TIAGAIN, 3, 7), + [F_RF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 0, 2), + [F_CF_LED] = REG_FIELD(AFE4403_TIA_AMB_GAIN, 3, 7), + /* LED Current */ + [F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 7), + [F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 8, 15), +}; + /** * struct afe4403_data - AFE4403 device instance data * @dev: Device structure * @spi: SPI device handle * @regmap: Register map of the device + * @fields: Register fields of the device * @regulator: Pointer to the regulator for the IC * @trig: IIO trigger for this device * @irq: ADC_RDY line interrupt number @@ -115,6 +113,7 @@ struct afe4403_data { struct device *dev; struct spi_device *spi; struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; struct regulator *regulator; struct iio_trigger *trig; int irq; @@ -131,15 +130,18 @@ enum afe4403_chan_id { ILED2, }; -static const struct afe440x_reg_info afe4403_reg_info[] = { - [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, 0, NULL), - [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, 0, NULL), - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, 0, NULL), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, 0, NULL), - [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), - [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED1), - [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE440X_LEDCNTRL_LED2), +static const unsigned int afe4403_channel_values[] = { + [LED2] = AFE440X_LED2VAL, + [ALED2] = AFE440X_ALED2VAL, + [LED1] = AFE440X_LED1VAL, + [ALED1] = AFE440X_ALED1VAL, + [LED2_ALED2] = AFE440X_LED2_ALED2VAL, + [LED1_ALED1] = AFE440X_LED1_ALED1VAL, +}; + +static const unsigned int afe4403_channel_leds[] = { + [ILED1] = F_ILED1, + [ILED2] = F_ILED2, }; static const struct iio_chan_spec afe4403_channels[] = { @@ -184,13 +186,10 @@ static ssize_t afe440x_show_register(struct device *dev, int vals[2]; int ret; - ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val); if (ret) return ret; - reg_val &= afe440x_attr->mask; - reg_val >>= afe440x_attr->shift; - if (reg_val >= afe440x_attr->table_size) return -EINVAL; @@ -220,20 +219,18 @@ static ssize_t afe440x_store_register(struct device *dev, if (val == afe440x_attr->table_size) return -EINVAL; - ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, - afe440x_attr->mask, - (val << afe440x_attr->shift)); + ret = regmap_field_write(afe->fields[afe440x_attr->field], val); if (ret) return ret; return count; } -static AFE440X_ATTR(tia_resistance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_RES, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance1, AFE4403_TIAGAIN, AFE4403_TIAGAIN_CAP, afe4403_cap_table); +static AFE440X_ATTR(tia_resistance1, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(tia_capacitance1, F_CF_LED1, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance2, AFE4403_TIA_AMB_GAIN, AFE4403_TIAGAIN_RES, afe4403_cap_table); +static AFE440X_ATTR(tia_resistance2, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(tia_capacitance2, F_CF_LED, afe4403_cap_table); static struct attribute *afe440x_attributes[] = { &afe440x_attr_tia_resistance1.dev_attr.attr, @@ -282,14 +279,15 @@ static int afe4403_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct afe4403_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + unsigned int reg = afe4403_channel_values[chan->address]; + unsigned int field = afe4403_channel_leds[chan->address]; int ret; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = afe4403_read(afe, reg_info.reg, val); + ret = afe4403_read(afe, reg, val); if (ret) return ret; return IIO_VAL_INT; @@ -298,11 +296,9 @@ static int afe4403_read_raw(struct iio_dev *indio_dev, case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_field_read(afe->fields[field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; @@ -322,16 +318,13 @@ static int afe4403_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct afe4403_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4403_reg_info[chan->address]; + unsigned int field = afe4403_channel_leds[chan->address]; switch (chan->type) { case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - return regmap_update_bits(afe->regmap, - reg_info.reg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[field], val); } break; default: @@ -366,7 +359,7 @@ static irqreturn_t afe4403_trigger_handler(int irq, void *private) for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { ret = spi_write_then_read(afe->spi, - &afe4403_reg_info[bit].reg, 1, + &afe4403_channel_values[bit], 1, rx, 3); if (ret) goto err; @@ -503,7 +496,7 @@ static int afe4403_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct afe4403_data *afe; - int ret; + int i, ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*afe)); if (!indio_dev) @@ -522,6 +515,15 @@ static int afe4403_probe(struct spi_device *spi) return PTR_ERR(afe->regmap); } + for (i = 0; i < F_MAX_FIELDS; i++) { + afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap, + afe4403_reg_fields[i]); + if (IS_ERR(afe->fields[i])) { + dev_err(afe->dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(afe->fields[i]); + } + } + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); if (IS_ERR(afe->regulator)) { dev_err(afe->dev, "Unable to get regulator\n"); diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 7806a452edf8..0d1af4a4a903 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -48,43 +48,9 @@ #define AFE4404_AVG_LED2_ALED2VAL 0x3f #define AFE4404_AVG_LED1_ALED1VAL 0x40 -/* AFE4404 GAIN register fields */ -#define AFE4404_TIA_GAIN_RES_MASK GENMASK(2, 0) -#define AFE4404_TIA_GAIN_RES_SHIFT 0 -#define AFE4404_TIA_GAIN_CAP_MASK GENMASK(5, 3) -#define AFE4404_TIA_GAIN_CAP_SHIFT 3 - -/* AFE4404 LEDCNTRL register fields */ -#define AFE4404_LEDCNTRL_ILED1_MASK GENMASK(5, 0) -#define AFE4404_LEDCNTRL_ILED1_SHIFT 0 -#define AFE4404_LEDCNTRL_ILED2_MASK GENMASK(11, 6) -#define AFE4404_LEDCNTRL_ILED2_SHIFT 6 -#define AFE4404_LEDCNTRL_ILED3_MASK GENMASK(17, 12) -#define AFE4404_LEDCNTRL_ILED3_SHIFT 12 - -/* AFE4404 CONTROL2 register fields */ -#define AFE440X_CONTROL2_ILED_2X_MASK BIT(17) -#define AFE440X_CONTROL2_ILED_2X_SHIFT 17 - /* AFE4404 CONTROL3 register fields */ #define AFE440X_CONTROL3_OSC_ENABLE BIT(9) -/* AFE4404 OFFDAC register current fields */ -#define AFE4404_OFFDAC_CURR_LED1_MASK GENMASK(9, 5) -#define AFE4404_OFFDAC_CURR_LED1_SHIFT 5 -#define AFE4404_OFFDAC_CURR_LED2_MASK GENMASK(19, 15) -#define AFE4404_OFFDAC_CURR_LED2_SHIFT 15 -#define AFE4404_OFFDAC_CURR_LED3_MASK GENMASK(4, 0) -#define AFE4404_OFFDAC_CURR_LED3_SHIFT 0 -#define AFE4404_OFFDAC_CURR_ALED1_MASK GENMASK(14, 10) -#define AFE4404_OFFDAC_CURR_ALED1_SHIFT 10 -#define AFE4404_OFFDAC_CURR_ALED2_MASK GENMASK(4, 0) -#define AFE4404_OFFDAC_CURR_ALED2_SHIFT 0 - -/* AFE4404 NULL fields */ -#define NULL_MASK 0 -#define NULL_SHIFT 0 - /* AFE4404 TIA_GAIN_CAP values */ #define AFE4404_TIA_GAIN_CAP_5_P 0x0 #define AFE4404_TIA_GAIN_CAP_2_5_P 0x1 @@ -105,10 +71,43 @@ #define AFE4404_TIA_GAIN_RES_1_M 0x6 #define AFE4404_TIA_GAIN_RES_2_M 0x7 +enum afe4404_fields { + /* Gains */ + F_TIA_GAIN_SEP, F_TIA_CF_SEP, + F_TIA_GAIN, TIA_CF, + + /* LED Current */ + F_ILED1, F_ILED2, F_ILED3, + + /* Offset DAC */ + F_OFFDAC_AMB2, F_OFFDAC_LED1, F_OFFDAC_AMB1, F_OFFDAC_LED2, + + /* sentinel */ + F_MAX_FIELDS +}; + +static const struct reg_field afe4404_reg_fields[] = { + /* Gains */ + [F_TIA_GAIN_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 0, 2), + [F_TIA_CF_SEP] = REG_FIELD(AFE4404_TIA_GAIN_SEP, 3, 5), + [F_TIA_GAIN] = REG_FIELD(AFE4404_TIA_GAIN, 0, 2), + [TIA_CF] = REG_FIELD(AFE4404_TIA_GAIN, 3, 5), + /* LED Current */ + [F_ILED1] = REG_FIELD(AFE440X_LEDCNTRL, 0, 5), + [F_ILED2] = REG_FIELD(AFE440X_LEDCNTRL, 6, 11), + [F_ILED3] = REG_FIELD(AFE440X_LEDCNTRL, 12, 17), + /* Offset DAC */ + [F_OFFDAC_AMB2] = REG_FIELD(AFE4404_OFFDAC, 0, 4), + [F_OFFDAC_LED1] = REG_FIELD(AFE4404_OFFDAC, 5, 9), + [F_OFFDAC_AMB1] = REG_FIELD(AFE4404_OFFDAC, 10, 14), + [F_OFFDAC_LED2] = REG_FIELD(AFE4404_OFFDAC, 15, 19), +}; + /** * struct afe4404_data - AFE4404 device instance data * @dev: Device structure * @regmap: Register map of the device + * @fields: Register fields of the device * @regulator: Pointer to the regulator for the IC * @trig: IIO trigger for this device * @irq: ADC_RDY line interrupt number @@ -116,6 +115,7 @@ struct afe4404_data { struct device *dev; struct regmap *regmap; + struct regmap_field *fields[F_MAX_FIELDS]; struct regulator *regulator; struct iio_trigger *trig; int irq; @@ -133,16 +133,26 @@ enum afe4404_chan_id { ILED3, }; -static const struct afe440x_reg_info afe4404_reg_info[] = { - [LED2] = AFE440X_REG_INFO(AFE440X_LED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED2), - [ALED2] = AFE440X_REG_INFO(AFE440X_ALED2VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED2), - [LED1] = AFE440X_REG_INFO(AFE440X_LED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_LED1), - [ALED1] = AFE440X_REG_INFO(AFE440X_ALED1VAL, AFE4404_OFFDAC, AFE4404_OFFDAC_CURR_ALED1), - [LED2_ALED2] = AFE440X_REG_INFO(AFE440X_LED2_ALED2VAL, 0, NULL), - [LED1_ALED1] = AFE440X_REG_INFO(AFE440X_LED1_ALED1VAL, 0, NULL), - [ILED1] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED1), - [ILED2] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED2), - [ILED3] = AFE440X_REG_INFO(AFE440X_LEDCNTRL, 0, AFE4404_LEDCNTRL_ILED3), +static const unsigned int afe4404_channel_values[] = { + [LED2] = AFE440X_LED2VAL, + [ALED2] = AFE440X_ALED2VAL, + [LED1] = AFE440X_LED1VAL, + [ALED1] = AFE440X_ALED1VAL, + [LED2_ALED2] = AFE440X_LED2_ALED2VAL, + [LED1_ALED1] = AFE440X_LED1_ALED1VAL, +}; + +static const unsigned int afe4404_channel_leds[] = { + [ILED1] = F_ILED1, + [ILED2] = F_ILED2, + [ILED3] = F_ILED3, +}; + +static const unsigned int afe4404_channel_offdacs[] = { + [LED2] = F_OFFDAC_LED2, + [ALED2] = F_OFFDAC_AMB2, + [LED1] = F_OFFDAC_LED1, + [ALED1] = F_OFFDAC_AMB1, }; static const struct iio_chan_spec afe4404_channels[] = { @@ -194,13 +204,10 @@ static ssize_t afe440x_show_register(struct device *dev, int vals[2]; int ret; - ret = regmap_read(afe->regmap, afe440x_attr->reg, ®_val); + ret = regmap_field_read(afe->fields[afe440x_attr->field], ®_val); if (ret) return ret; - reg_val &= afe440x_attr->mask; - reg_val >>= afe440x_attr->shift; - if (reg_val >= afe440x_attr->table_size) return -EINVAL; @@ -230,20 +237,18 @@ static ssize_t afe440x_store_register(struct device *dev, if (val == afe440x_attr->table_size) return -EINVAL; - ret = regmap_update_bits(afe->regmap, afe440x_attr->reg, - afe440x_attr->mask, - (val << afe440x_attr->shift)); + ret = regmap_field_write(afe->fields[afe440x_attr->field], val); if (ret) return ret; return count; } -static AFE440X_ATTR(tia_resistance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_RES, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance1, AFE4404_TIA_GAIN, AFE4404_TIA_GAIN_CAP, afe4404_cap_table); +static AFE440X_ATTR(tia_resistance1, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(tia_capacitance1, TIA_CF, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_RES, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance2, AFE4404_TIA_GAIN_SEP, AFE4404_TIA_GAIN_CAP, afe4404_cap_table); +static AFE440X_ATTR(tia_resistance2, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(tia_capacitance2, F_TIA_CF_SEP, afe4404_cap_table); static struct attribute *afe440x_attributes[] = { &afe440x_attr_tia_resistance1.dev_attr.attr, @@ -264,35 +269,32 @@ static int afe4404_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct afe4404_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + unsigned int value_reg = afe4404_channel_values[chan->address]; + unsigned int led_field = afe4404_channel_leds[chan->address]; + unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; int ret; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_read(afe->regmap, value_reg, val); if (ret) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - ret = regmap_read(afe->regmap, reg_info.offreg, - val); + ret = regmap_field_read(afe->fields[offdac_field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; } break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - ret = regmap_read(afe->regmap, reg_info.reg, val); + ret = regmap_field_read(afe->fields[led_field], val); if (ret) return ret; - *val &= reg_info.mask; - *val >>= reg_info.shift; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: *val = 0; @@ -312,25 +314,20 @@ static int afe4404_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct afe4404_data *afe = iio_priv(indio_dev); - const struct afe440x_reg_info reg_info = afe4404_reg_info[chan->address]; + unsigned int led_field = afe4404_channel_leds[chan->address]; + unsigned int offdac_field = afe4404_channel_offdacs[chan->address]; switch (chan->type) { case IIO_INTENSITY: switch (mask) { case IIO_CHAN_INFO_OFFSET: - return regmap_update_bits(afe->regmap, - reg_info.offreg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[offdac_field], val); } break; case IIO_CURRENT: switch (mask) { case IIO_CHAN_INFO_RAW: - return regmap_update_bits(afe->regmap, - reg_info.reg, - reg_info.mask, - (val << reg_info.shift)); + return regmap_field_write(afe->fields[led_field], val); } break; default: @@ -357,7 +354,7 @@ static irqreturn_t afe4404_trigger_handler(int irq, void *private) for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { - ret = regmap_read(afe->regmap, afe4404_reg_info[bit].reg, + ret = regmap_read(afe->regmap, afe4404_channel_values[bit], &buffer[i++]); if (ret) goto err; @@ -490,7 +487,7 @@ static int afe4404_probe(struct i2c_client *client, { struct iio_dev *indio_dev; struct afe4404_data *afe; - int ret; + int i, ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*afe)); if (!indio_dev) @@ -508,6 +505,15 @@ static int afe4404_probe(struct i2c_client *client, return PTR_ERR(afe->regmap); } + for (i = 0; i < F_MAX_FIELDS; i++) { + afe->fields[i] = devm_regmap_field_alloc(afe->dev, afe->regmap, + afe4404_reg_fields[i]); + if (IS_ERR(afe->fields[i])) { + dev_err(afe->dev, "Unable to allocate regmap fields\n"); + return PTR_ERR(afe->fields[i]); + } + } + afe->regulator = devm_regulator_get(afe->dev, "tx_sup"); if (IS_ERR(afe->regulator)) { dev_err(afe->dev, "Unable to get regulator\n"); diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h index 713972fd4601..1a0f247043ca 100644 --- a/drivers/iio/health/afe440x.h +++ b/drivers/iio/health/afe440x.h @@ -88,21 +88,6 @@ #define AFE440X_CONTROL0_WRITE 0x0 #define AFE440X_CONTROL0_READ 0x1 -struct afe440x_reg_info { - unsigned int reg; - unsigned int offreg; - unsigned int shift; - unsigned int mask; -}; - -#define AFE440X_REG_INFO(_reg, _offreg, _sm) \ - { \ - .reg = _reg, \ - .offreg = _offreg, \ - .shift = _sm ## _SHIFT, \ - .mask = _sm ## _MASK, \ - } - #define AFE440X_INTENSITY_CHAN(_index, _mask) \ { \ .type = IIO_INTENSITY, \ @@ -157,9 +142,7 @@ static DEVICE_ATTR_RO(_name) struct afe440x_attr { struct device_attribute dev_attr; - unsigned int reg; - unsigned int shift; - unsigned int mask; + unsigned int field; const struct afe440x_val_table *val_table; unsigned int table_size; }; @@ -167,14 +150,12 @@ struct afe440x_attr { #define to_afe440x_attr(_dev_attr) \ container_of(_dev_attr, struct afe440x_attr, dev_attr) -#define AFE440X_ATTR(_name, _reg, _field, _table) \ +#define AFE440X_ATTR(_name, _field, _table) \ struct afe440x_attr afe440x_attr_##_name = { \ .dev_attr = __ATTR(_name, (S_IRUGO | S_IWUSR), \ afe440x_show_register, \ afe440x_store_register), \ - .reg = _reg, \ - .shift = _field ## _SHIFT, \ - .mask = _field ## _MASK, \ + .field = _field, \ .val_table = _table, \ .table_size = ARRAY_SIZE(_table), \ } From 1276187c5261217aa9cc23ec153e0e903181c16b Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:37:00 -0500 Subject: [PATCH 16/61] iio: health/afe440x: Make gain settings a modifier for the stages Currently the TIA gain settings are exported to userspace as sysfs entries that do not clearly represent their internal relation to the sampling stages. The gain settings are enabled on a per-stage basis, this can be seen in figure 24 of the current AFE4404 datasheet. These gain settings should therefore be tied to the channels that are read during these stages. Make this change here. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-health-afe440x | 19 +++++------ drivers/iio/health/afe4403.c | 34 ++++++++++++------- drivers/iio/health/afe4404.c | 34 ++++++++++++------- 3 files changed, 53 insertions(+), 34 deletions(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x index a06707320d84..6adba9058b22 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x +++ b/Documentation/ABI/testing/sysfs-bus-iio-health-afe440x @@ -1,13 +1,3 @@ -What: /sys/bus/iio/devices/iio:deviceX/tia_resistanceY - /sys/bus/iio/devices/iio:deviceX/tia_capacitanceY -Date: December 2015 -KernelVersion: -Contact: Andrew F. Davis -Description: - Get and set the resistance and the capacitance settings for the - Transimpedance Amplifier. Y is 1 for Rf1 and Cf1, Y is 2 for - Rf2 and Cf2 values. - What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_raw Date: May 2016 KernelVersion: @@ -33,6 +23,15 @@ Description: Get and set the offset cancellation DAC setting for these stages. The values are expressed in 5-bit sign-magnitude. +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_resistance +What: /sys/bus/iio/devices/iio:deviceX/in_intensityY_capacitance +Date: May 2016 +KernelVersion: +Contact: Andrew F. Davis +Description: + Get and set the resistance and the capacitance settings for the + Transimpedance Amplifier during the associated stage. + What: /sys/bus/iio/devices/iio:deviceX/out_currentY_raw Date: May 2016 KernelVersion: diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 19501550c6ff..610631b036ec 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -161,7 +161,7 @@ static const struct afe440x_val_table afe4403_res_table[] = { { 500000 }, { 250000 }, { 100000 }, { 50000 }, { 25000 }, { 10000 }, { 1000000 }, { 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4403_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4403_res_table); static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 5000 }, { 0, 10000 }, { 0, 20000 }, { 0, 25000 }, @@ -173,7 +173,7 @@ static const struct afe440x_val_table afe4403_cap_table[] = { { 0, 205000 }, { 0, 210000 }, { 0, 220000 }, { 0, 225000 }, { 0, 230000 }, { 0, 235000 }, { 0, 245000 }, { 0, 250000 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4403_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4403_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -226,19 +226,29 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_resistance1, F_RF_LED1, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance1, F_CF_LED1, afe4403_cap_table); +static AFE440X_ATTR(in_intensity1_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_CF_LED, afe4403_cap_table); -static AFE440X_ATTR(tia_resistance2, F_RF_LED, afe4403_res_table); -static AFE440X_ATTR(tia_capacitance2, F_CF_LED, afe4403_cap_table); +static AFE440X_ATTR(in_intensity2_resistance, F_RF_LED, afe4403_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_CF_LED, afe4403_cap_table); + +static AFE440X_ATTR(in_intensity3_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, F_CF_LED1, afe4403_cap_table); + +static AFE440X_ATTR(in_intensity4_resistance, F_RF_LED1, afe4403_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, F_CF_LED1, afe4403_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 0d1af4a4a903..69116cdd9d92 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -179,7 +179,7 @@ static const struct afe440x_val_table afe4404_res_table[] = { { .integer = 1000000, .fract = 0 }, { .integer = 2000000, .fract = 0 }, }; -AFE440X_TABLE_ATTR(tia_resistance_available, afe4404_res_table); +AFE440X_TABLE_ATTR(in_intensity_resistance_available, afe4404_res_table); static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 5000 }, @@ -191,7 +191,7 @@ static const struct afe440x_val_table afe4404_cap_table[] = { { .integer = 0, .fract = 25000 }, { .integer = 0, .fract = 22500 }, }; -AFE440X_TABLE_ATTR(tia_capacitance_available, afe4404_cap_table); +AFE440X_TABLE_ATTR(in_intensity_capacitance_available, afe4404_cap_table); static ssize_t afe440x_show_register(struct device *dev, struct device_attribute *attr, @@ -244,19 +244,29 @@ static ssize_t afe440x_store_register(struct device *dev, return count; } -static AFE440X_ATTR(tia_resistance1, F_TIA_GAIN, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance1, TIA_CF, afe4404_cap_table); +static AFE440X_ATTR(in_intensity1_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity1_capacitance, F_TIA_CF_SEP, afe4404_cap_table); -static AFE440X_ATTR(tia_resistance2, F_TIA_GAIN_SEP, afe4404_res_table); -static AFE440X_ATTR(tia_capacitance2, F_TIA_CF_SEP, afe4404_cap_table); +static AFE440X_ATTR(in_intensity2_resistance, F_TIA_GAIN_SEP, afe4404_res_table); +static AFE440X_ATTR(in_intensity2_capacitance, F_TIA_CF_SEP, afe4404_cap_table); + +static AFE440X_ATTR(in_intensity3_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity3_capacitance, TIA_CF, afe4404_cap_table); + +static AFE440X_ATTR(in_intensity4_resistance, F_TIA_GAIN, afe4404_res_table); +static AFE440X_ATTR(in_intensity4_capacitance, TIA_CF, afe4404_cap_table); static struct attribute *afe440x_attributes[] = { - &afe440x_attr_tia_resistance1.dev_attr.attr, - &afe440x_attr_tia_capacitance1.dev_attr.attr, - &afe440x_attr_tia_resistance2.dev_attr.attr, - &afe440x_attr_tia_capacitance2.dev_attr.attr, - &dev_attr_tia_resistance_available.attr, - &dev_attr_tia_capacitance_available.attr, + &dev_attr_in_intensity_resistance_available.attr, + &dev_attr_in_intensity_capacitance_available.attr, + &afe440x_attr_in_intensity1_resistance.dev_attr.attr, + &afe440x_attr_in_intensity1_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity2_resistance.dev_attr.attr, + &afe440x_attr_in_intensity2_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity3_resistance.dev_attr.attr, + &afe440x_attr_in_intensity3_capacitance.dev_attr.attr, + &afe440x_attr_in_intensity4_resistance.dev_attr.attr, + &afe440x_attr_in_intensity4_capacitance.dev_attr.attr, NULL }; From 3ff34ee2ad2aa16fdde728bd9048098f71391087 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:37:01 -0500 Subject: [PATCH 17/61] iio: health/afe440x: Match LED currents to stages The current channel number for the LEDs should match the stage number that they are active during, fix this here. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 10 ++++------ drivers/iio/health/afe4404.c | 15 ++++++--------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 610631b036ec..059d52153e78 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -126,8 +126,6 @@ enum afe4403_chan_id { ALED1, LED2_ALED2, LED1_ALED1, - ILED1, - ILED2, }; static const unsigned int afe4403_channel_values[] = { @@ -140,8 +138,8 @@ static const unsigned int afe4403_channel_values[] = { }; static const unsigned int afe4403_channel_leds[] = { - [ILED1] = F_ILED1, - [ILED2] = F_ILED2, + [LED2] = F_ILED2, + [LED1] = F_ILED1, }; static const struct iio_chan_spec afe4403_channels[] = { @@ -153,8 +151,8 @@ static const struct iio_chan_spec afe4403_channels[] = { AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1), - AFE440X_CURRENT_CHAN(ILED2), + AFE440X_CURRENT_CHAN(LED2), + AFE440X_CURRENT_CHAN(LED1), }; static const struct afe440x_val_table afe4403_res_table[] = { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 69116cdd9d92..aa8770b291c8 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -128,9 +128,6 @@ enum afe4404_chan_id { ALED1, LED2_ALED2, LED1_ALED1, - ILED1, - ILED2, - ILED3, }; static const unsigned int afe4404_channel_values[] = { @@ -143,9 +140,9 @@ static const unsigned int afe4404_channel_values[] = { }; static const unsigned int afe4404_channel_leds[] = { - [ILED1] = F_ILED1, - [ILED2] = F_ILED2, - [ILED3] = F_ILED3, + [LED2] = F_ILED2, + [ALED2] = F_ILED3, + [LED1] = F_ILED1, }; static const unsigned int afe4404_channel_offdacs[] = { @@ -164,9 +161,9 @@ static const struct iio_chan_spec afe4404_channels[] = { AFE440X_INTENSITY_CHAN(LED2_ALED2, 0), AFE440X_INTENSITY_CHAN(LED1_ALED1, 0), /* LED current */ - AFE440X_CURRENT_CHAN(ILED1), - AFE440X_CURRENT_CHAN(ILED2), - AFE440X_CURRENT_CHAN(ILED3), + AFE440X_CURRENT_CHAN(LED2), + AFE440X_CURRENT_CHAN(ALED2), + AFE440X_CURRENT_CHAN(LED1), }; static const struct afe440x_val_table afe4404_res_table[] = { From e462350a95ee6d1ea53bc518f2bc5232b451e2e2 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:37:02 -0500 Subject: [PATCH 18/61] iio: health/afe440x: Remove unused definitions These definitions are not currently used and if the functionality they represent is needed the values should be added back to a table for easy userspace use. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 37 ------------------------------------ drivers/iio/health/afe4404.c | 20 ------------------- 2 files changed, 57 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 059d52153e78..9a081465c42f 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -39,43 +39,6 @@ #define AFE4403_TIAGAIN 0x20 #define AFE4403_TIA_AMB_GAIN 0x21 -/* AFE4403 LEDCNTRL values */ -#define AFE440X_LEDCNTRL_RANGE_TX_HALF 0x1 -#define AFE440X_LEDCNTRL_RANGE_TX_FULL 0x2 -#define AFE440X_LEDCNTRL_RANGE_TX_OFF 0x3 - -/* AFE4403 CONTROL2 values */ -#define AFE440X_CONTROL2_TX_REF_025 0x0 -#define AFE440X_CONTROL2_TX_REF_050 0x1 -#define AFE440X_CONTROL2_TX_REF_100 0x2 -#define AFE440X_CONTROL2_TX_REF_075 0x3 - -/* AFE4403 CONTROL3 values */ -#define AFE440X_CONTROL3_CLK_DIV_2 0x0 -#define AFE440X_CONTROL3_CLK_DIV_4 0x2 -#define AFE440X_CONTROL3_CLK_DIV_6 0x3 -#define AFE440X_CONTROL3_CLK_DIV_8 0x4 -#define AFE440X_CONTROL3_CLK_DIV_12 0x5 -#define AFE440X_CONTROL3_CLK_DIV_1 0x7 - -/* AFE4403 TIAGAIN_CAP values */ -#define AFE4403_TIAGAIN_CAP_5_P 0x0 -#define AFE4403_TIAGAIN_CAP_10_P 0x1 -#define AFE4403_TIAGAIN_CAP_20_P 0x2 -#define AFE4403_TIAGAIN_CAP_30_P 0x3 -#define AFE4403_TIAGAIN_CAP_55_P 0x8 -#define AFE4403_TIAGAIN_CAP_155_P 0x10 - -/* AFE4403 TIAGAIN_RES values */ -#define AFE4403_TIAGAIN_RES_500_K 0x0 -#define AFE4403_TIAGAIN_RES_250_K 0x1 -#define AFE4403_TIAGAIN_RES_100_K 0x2 -#define AFE4403_TIAGAIN_RES_50_K 0x3 -#define AFE4403_TIAGAIN_RES_25_K 0x4 -#define AFE4403_TIAGAIN_RES_10_K 0x5 -#define AFE4403_TIAGAIN_RES_1_M 0x6 -#define AFE4403_TIAGAIN_RES_NONE 0x7 - enum afe4403_fields { /* Gains */ F_RF_LED1, F_CF_LED1, diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index aa8770b291c8..3a8131de2626 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -51,26 +51,6 @@ /* AFE4404 CONTROL3 register fields */ #define AFE440X_CONTROL3_OSC_ENABLE BIT(9) -/* AFE4404 TIA_GAIN_CAP values */ -#define AFE4404_TIA_GAIN_CAP_5_P 0x0 -#define AFE4404_TIA_GAIN_CAP_2_5_P 0x1 -#define AFE4404_TIA_GAIN_CAP_10_P 0x2 -#define AFE4404_TIA_GAIN_CAP_7_5_P 0x3 -#define AFE4404_TIA_GAIN_CAP_20_P 0x4 -#define AFE4404_TIA_GAIN_CAP_17_5_P 0x5 -#define AFE4404_TIA_GAIN_CAP_25_P 0x6 -#define AFE4404_TIA_GAIN_CAP_22_5_P 0x7 - -/* AFE4404 TIA_GAIN_RES values */ -#define AFE4404_TIA_GAIN_RES_500_K 0x0 -#define AFE4404_TIA_GAIN_RES_250_K 0x1 -#define AFE4404_TIA_GAIN_RES_100_K 0x2 -#define AFE4404_TIA_GAIN_RES_50_K 0x3 -#define AFE4404_TIA_GAIN_RES_25_K 0x4 -#define AFE4404_TIA_GAIN_RES_10_K 0x5 -#define AFE4404_TIA_GAIN_RES_1_M 0x6 -#define AFE4404_TIA_GAIN_RES_2_M 0x7 - enum afe4404_fields { /* Gains */ F_TIA_GAIN_SEP, F_TIA_CF_SEP, From 0825cce21f764c04a96b719af2b491011bc6c523 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sun, 1 May 2016 15:37:03 -0500 Subject: [PATCH 19/61] iio: health/afe4404: ENSEPGAIN is part of CONTROL2 register Rename this definition, no functional changes. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4404.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 3a8131de2626..45266404f7e3 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -48,8 +48,8 @@ #define AFE4404_AVG_LED2_ALED2VAL 0x3f #define AFE4404_AVG_LED1_ALED1VAL 0x40 -/* AFE4404 CONTROL3 register fields */ -#define AFE440X_CONTROL3_OSC_ENABLE BIT(9) +/* AFE4404 CONTROL2 register fields */ +#define AFE440X_CONTROL2_OSC_ENABLE BIT(9) enum afe4404_fields { /* Gains */ @@ -398,7 +398,7 @@ static const struct reg_sequence afe4404_reg_sequences[] = { AFE4404_TIMING_PAIRS, { AFE440X_CONTROL1, AFE440X_CONTROL1_TIMEREN }, { AFE4404_TIA_GAIN_SEP, AFE440X_TIAGAIN_ENSEPGAIN }, - { AFE440X_CONTROL2, AFE440X_CONTROL3_OSC_ENABLE }, + { AFE440X_CONTROL2, AFE440X_CONTROL2_OSC_ENABLE }, }; static const struct regmap_range afe4404_yes_ranges[] = { From 0f3a8c3f34f728e7c96651bb7271e1c388c9aac2 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 25 Apr 2016 16:15:51 +0300 Subject: [PATCH 20/61] iio: Add support for creating IIO devices via configfs This is similar with support for creating triggers via configfs. Devices will be hosted under: * /config/iio/devices We allow users to register "device types" under: * /config/iio/devices// Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 8 ++ drivers/iio/Makefile | 1 + drivers/iio/industrialio-sw-device.c | 182 +++++++++++++++++++++++++++ include/linux/iio/sw_device.h | 70 +++++++++++ 4 files changed, 261 insertions(+) create mode 100644 drivers/iio/industrialio-sw-device.c create mode 100644 include/linux/iio/sw_device.h diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 505e921f0b19..6743b18194fb 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -46,6 +46,14 @@ config IIO_CONSUMERS_PER_TRIGGER This value controls the maximum number of consumers that a given trigger may handle. Default is 2. +config IIO_SW_DEVICE + tristate "Enable software IIO device support" + select IIO_CONFIGFS + help + Provides IIO core support for software devices. A software + device can be created via configfs or directly by a driver + using the API provided. + config IIO_SW_TRIGGER tristate "Enable software triggers support" select IIO_CONFIGFS diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 20f649073462..87e4c4369e2f 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -8,6 +8,7 @@ industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o +obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o obj-$(CONFIG_IIO_SW_TRIGGER) += industrialio-sw-trigger.o obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o diff --git a/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c new file mode 100644 index 000000000000..81b49cfca452 --- /dev/null +++ b/drivers/iio/industrialio-sw-device.c @@ -0,0 +1,182 @@ +/* + * The Industrial I/O core, software IIO devices functions + * + * Copyright (c) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +static struct config_group *iio_devices_group; +static struct config_item_type iio_device_type_group_type; + +static struct config_item_type iio_devices_group_type = { + .ct_owner = THIS_MODULE, +}; + +static LIST_HEAD(iio_device_types_list); +static DEFINE_MUTEX(iio_device_types_lock); + +static +struct iio_sw_device_type *__iio_find_sw_device_type(const char *name, + unsigned len) +{ + struct iio_sw_device_type *d = NULL, *iter; + + list_for_each_entry(iter, &iio_device_types_list, list) + if (!strcmp(iter->name, name)) { + d = iter; + break; + } + + return d; +} + +int iio_register_sw_device_type(struct iio_sw_device_type *d) +{ + struct iio_sw_device_type *iter; + int ret = 0; + + mutex_lock(&iio_device_types_lock); + iter = __iio_find_sw_device_type(d->name, strlen(d->name)); + if (iter) + ret = -EBUSY; + else + list_add_tail(&d->list, &iio_device_types_list); + mutex_unlock(&iio_device_types_lock); + + if (ret) + return ret; + + d->group = configfs_register_default_group(iio_devices_group, d->name, + &iio_device_type_group_type); + if (IS_ERR(d->group)) + ret = PTR_ERR(d->group); + + return ret; +} +EXPORT_SYMBOL(iio_register_sw_device_type); + +void iio_unregister_sw_device_type(struct iio_sw_device_type *dt) +{ + struct iio_sw_device_type *iter; + + mutex_lock(&iio_device_types_lock); + iter = __iio_find_sw_device_type(dt->name, strlen(dt->name)); + if (iter) + list_del(&dt->list); + mutex_unlock(&iio_device_types_lock); + + configfs_unregister_default_group(dt->group); +} +EXPORT_SYMBOL(iio_unregister_sw_device_type); + +static +struct iio_sw_device_type *iio_get_sw_device_type(const char *name) +{ + struct iio_sw_device_type *dt; + + mutex_lock(&iio_device_types_lock); + dt = __iio_find_sw_device_type(name, strlen(name)); + if (dt && !try_module_get(dt->owner)) + dt = NULL; + mutex_unlock(&iio_device_types_lock); + + return dt; +} + +struct iio_sw_device *iio_sw_device_create(const char *type, const char *name) +{ + struct iio_sw_device *d; + struct iio_sw_device_type *dt; + + dt = iio_get_sw_device_type(type); + if (!dt) { + pr_err("Invalid device type: %s\n", type); + return ERR_PTR(-EINVAL); + } + d = dt->ops->probe(name); + if (IS_ERR(d)) + goto out_module_put; + + d->device_type = dt; + + return d; +out_module_put: + module_put(dt->owner); + return d; +} +EXPORT_SYMBOL(iio_sw_device_create); + +void iio_sw_device_destroy(struct iio_sw_device *d) +{ + struct iio_sw_device_type *dt = d->device_type; + + dt->ops->remove(d); + module_put(dt->owner); +} +EXPORT_SYMBOL(iio_sw_device_destroy); + +static struct config_group *device_make_group(struct config_group *group, + const char *name) +{ + struct iio_sw_device *d; + + d = iio_sw_device_create(group->cg_item.ci_name, name); + if (IS_ERR(d)) + return ERR_CAST(d); + + config_item_set_name(&d->group.cg_item, "%s", name); + + return &d->group; +} + +static void device_drop_group(struct config_group *group, + struct config_item *item) +{ + struct iio_sw_device *d = to_iio_sw_device(item); + + iio_sw_device_destroy(d); + config_item_put(item); +} + +static struct configfs_group_operations device_ops = { + .make_group = &device_make_group, + .drop_item = &device_drop_group, +}; + +static struct config_item_type iio_device_type_group_type = { + .ct_group_ops = &device_ops, + .ct_owner = THIS_MODULE, +}; + +static int __init iio_sw_device_init(void) +{ + iio_devices_group = + configfs_register_default_group(&iio_configfs_subsys.su_group, + "devices", + &iio_devices_group_type); + return PTR_ERR_OR_ZERO(iio_devices_group); +} +module_init(iio_sw_device_init); + +static void __exit iio_sw_device_exit(void) +{ + configfs_unregister_default_group(iio_devices_group); +} +module_exit(iio_sw_device_exit); + +MODULE_AUTHOR("Daniel Baluta "); +MODULE_DESCRIPTION("Industrial I/O software devices support"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/iio/sw_device.h b/include/linux/iio/sw_device.h new file mode 100644 index 000000000000..23ca41515527 --- /dev/null +++ b/include/linux/iio/sw_device.h @@ -0,0 +1,70 @@ +/* + * Industrial I/O software device interface + * + * Copyright (c) 2016 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#ifndef __IIO_SW_DEVICE +#define __IIO_SW_DEVICE + +#include +#include +#include +#include + +#define module_iio_sw_device_driver(__iio_sw_device_type) \ + module_driver(__iio_sw_device_type, iio_register_sw_device_type, \ + iio_unregister_sw_device_type) + +struct iio_sw_device_ops; + +struct iio_sw_device_type { + const char *name; + struct module *owner; + const struct iio_sw_device_ops *ops; + struct list_head list; + struct config_group *group; +}; + +struct iio_sw_device { + struct iio_dev *device; + struct iio_sw_device_type *device_type; + struct config_group group; +}; + +struct iio_sw_device_ops { + struct iio_sw_device* (*probe)(const char *); + int (*remove)(struct iio_sw_device *); +}; + +static inline +struct iio_sw_device *to_iio_sw_device(struct config_item *item) +{ + return container_of(to_config_group(item), struct iio_sw_device, + group); +} + +int iio_register_sw_device_type(struct iio_sw_device_type *dt); +void iio_unregister_sw_device_type(struct iio_sw_device_type *dt); + +struct iio_sw_device *iio_sw_device_create(const char *, const char *); +void iio_sw_device_destroy(struct iio_sw_device *); + +int iio_sw_device_type_configfs_register(struct iio_sw_device_type *dt); +void iio_sw_device_type_configfs_unregister(struct iio_sw_device_type *dt); + +static inline +void iio_swd_group_init_type_name(struct iio_sw_device *d, + const char *name, + struct config_item_type *type) +{ +#ifdef CONFIG_CONFIGFS_FS + config_group_init_type_name(&d->group, name, type); +#endif +} + +#endif /* __IIO_SW_DEVICE */ From 3d85fb6f81046b51e4428e14fb9643ea75648630 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 25 Apr 2016 16:15:52 +0300 Subject: [PATCH 21/61] iio: dummy: Convert IIO dummy to configfs We register a new device type named "dummy", this will create a configfs entry under: * /config/iio/devices/dummy. Creating dummy devices is now as simple as: $ mkdir /config/iio/devices/dummy/my_dummy_device Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/dummy/Kconfig | 1 + drivers/iio/dummy/iio_simple_dummy.c | 102 ++++++++++----------------- 2 files changed, 38 insertions(+), 65 deletions(-) diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig index 71805ced1aae..aa5824d96a43 100644 --- a/drivers/iio/dummy/Kconfig +++ b/drivers/iio/dummy/Kconfig @@ -10,6 +10,7 @@ config IIO_DUMMY_EVGEN config IIO_SIMPLE_DUMMY tristate "An example driver with no hardware requirements" + depends on IIO_SW_DEVICE help Driver intended mainly as documentation for how to write a driver. May also be useful for testing userspace code diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c index 43fe4ba7d0dc..ad3410e528b6 100644 --- a/drivers/iio/dummy/iio_simple_dummy.c +++ b/drivers/iio/dummy/iio_simple_dummy.c @@ -17,26 +17,18 @@ #include #include #include +#include #include #include #include #include +#include #include "iio_simple_dummy.h" -/* - * A few elements needed to fake a bus for this driver - * Note instances parameter controls how many of these - * dummy devices are registered. - */ -static unsigned instances = 1; -module_param(instances, uint, 0); - -/* Pointer array used to fake bus elements */ -static struct iio_dev **iio_dummy_devs; - -/* Fake a name for the part number, usually obtained from the id table */ -static const char *iio_dummy_part_number = "iio_dummy_part_no"; +static struct config_item_type iio_dummy_type = { + .ct_owner = THIS_MODULE, +}; /** * struct iio_dummy_accel_calibscale - realworld to register mapping @@ -572,12 +564,18 @@ static int iio_dummy_init_device(struct iio_dev *indio_dev) * const struct i2c_device_id *id) * SPI: iio_dummy_probe(struct spi_device *spi) */ -static int iio_dummy_probe(int index) +static struct iio_sw_device *iio_dummy_probe(const char *name) { int ret; struct iio_dev *indio_dev; struct iio_dummy_state *st; + struct iio_sw_device *swd; + swd = kzalloc(sizeof(*swd), GFP_KERNEL); + if (!swd) { + ret = -ENOMEM; + goto error_kzalloc; + } /* * Allocate an IIO device. * @@ -608,7 +606,7 @@ static int iio_dummy_probe(int index) * i2c_set_clientdata(client, indio_dev); * spi_set_drvdata(spi, indio_dev); */ - iio_dummy_devs[index] = indio_dev; + swd->device = indio_dev; /* * Set the device name. @@ -619,7 +617,7 @@ static int iio_dummy_probe(int index) * indio_dev->name = id->name; * indio_dev->name = spi_get_device_id(spi)->name; */ - indio_dev->name = iio_dummy_part_number; + indio_dev->name = kstrdup(name, GFP_KERNEL); /* Provide description of available channels */ indio_dev->channels = iio_dummy_channels; @@ -646,7 +644,9 @@ static int iio_dummy_probe(int index) if (ret < 0) goto error_unconfigure_buffer; - return 0; + iio_swd_group_init_type_name(swd, name, &iio_dummy_type); + + return swd; error_unconfigure_buffer: iio_simple_dummy_unconfigure_buffer(indio_dev); error_unregister_events: @@ -654,16 +654,18 @@ error_unregister_events: error_free_device: iio_device_free(indio_dev); error_ret: - return ret; + kfree(swd); +error_kzalloc: + return ERR_PTR(ret); } /** * iio_dummy_remove() - device instance removal function - * @index: device index. + * @swd: pointer to software IIO device abstraction * * Parameters follow those of iio_dummy_probe for buses. */ -static void iio_dummy_remove(int index) +static int iio_dummy_remove(struct iio_sw_device *swd) { /* * Get a pointer to the device instance iio_dev structure @@ -671,7 +673,7 @@ static void iio_dummy_remove(int index) * struct iio_dev *indio_dev = i2c_get_clientdata(client); * struct iio_dev *indio_dev = spi_get_drvdata(spi); */ - struct iio_dev *indio_dev = iio_dummy_devs[index]; + struct iio_dev *indio_dev = swd->device; /* Unregister the device */ iio_device_unregister(indio_dev); @@ -684,11 +686,13 @@ static void iio_dummy_remove(int index) iio_simple_dummy_events_unregister(indio_dev); /* Free all structures */ + kfree(indio_dev->name); iio_device_free(indio_dev); -} + return 0; +} /** - * iio_dummy_init() - device driver registration + * module_iio_sw_device_driver() - device driver registration * * Varies depending on bus type of the device. As there is no device * here, call probe directly. For information on device registration @@ -697,50 +701,18 @@ static void iio_dummy_remove(int index) * spi: * Documentation/spi/spi-summary */ -static __init int iio_dummy_init(void) -{ - int i, ret; +static const struct iio_sw_device_ops iio_dummy_device_ops = { + .probe = iio_dummy_probe, + .remove = iio_dummy_remove, +}; - if (instances > 10) { - instances = 1; - return -EINVAL; - } +static struct iio_sw_device_type iio_dummy_device = { + .name = "dummy", + .owner = THIS_MODULE, + .ops = &iio_dummy_device_ops, +}; - /* Fake a bus */ - iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs), - GFP_KERNEL); - /* Here we have no actual device so call probe */ - for (i = 0; i < instances; i++) { - ret = iio_dummy_probe(i); - if (ret < 0) - goto error_remove_devs; - } - return 0; - -error_remove_devs: - while (i--) - iio_dummy_remove(i); - - kfree(iio_dummy_devs); - return ret; -} -module_init(iio_dummy_init); - -/** - * iio_dummy_exit() - device driver removal - * - * Varies depending on bus type of the device. - * As there is no device here, call remove directly. - */ -static __exit void iio_dummy_exit(void) -{ - int i; - - for (i = 0; i < instances; i++) - iio_dummy_remove(i); - kfree(iio_dummy_devs); -} -module_exit(iio_dummy_exit); +module_iio_sw_device_driver(iio_dummy_device); MODULE_AUTHOR("Jonathan Cameron "); MODULE_DESCRIPTION("IIO dummy driver"); From 6994aea7842d824a8cc38c950ddcab3f8a75c278 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 25 Apr 2016 16:15:53 +0300 Subject: [PATCH 22/61] Documentation: iio: Add IIO software devices docs Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/configfs-iio | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Documentation/ABI/testing/configfs-iio b/Documentation/ABI/testing/configfs-iio index 2483756fccf5..aebda53ec0f7 100644 --- a/Documentation/ABI/testing/configfs-iio +++ b/Documentation/ABI/testing/configfs-iio @@ -19,3 +19,16 @@ KernelVersion: 4.4 Description: High resolution timers directory. Creating a directory here will result in creating a hrtimer trigger in the IIO subsystem. + +What: /config/iio/devices +Date: April 2016 +KernelVersion: 4.7 +Description: + Industrial IO software devices directory. + +What: /config/iio/devices/dummy +Date: April 2016 +KernelVersion: 4.7 +Description: + Dummy IIO devices directory. Creating a directory here will result + in creating a dummy IIO device in the IIO subystem. From ed859fc17d67f4c0ade6f5a58365e621f88de3cf Mon Sep 17 00:00:00 2001 From: Martin Kepplinger Date: Mon, 25 Apr 2016 14:08:25 +0200 Subject: [PATCH 23/61] iio: mma8452: add support for oversampling ratio This adds the following sysfs files according to the iio ABI: -rw-r--r-- 4096 in_accel_oversampling_ratio -r--r--r-- 4096 in_accel_oversampling_ratio_available Internally, the device knows about 4 different power modes that differ in oversampling ratio (and power consumption). We just show the user what oversampling ratio(s) is/are available, depending on the current frequency. The referenced table in the datasheets makes it easier to understand. Signed-off-by: Martin Kepplinger Signed-off-by: Christoph Muellner Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 187 +++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 26 deletions(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index e225d3c53bd5..458c82715427 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -76,6 +76,8 @@ #define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ #define MMA8452_CTRL_REG2 0x2b #define MMA8452_CTRL_REG2_RST BIT(6) +#define MMA8452_CTRL_REG2_MODS_SHIFT 3 +#define MMA8452_CTRL_REG2_MODS_MASK 0x1b #define MMA8452_CTRL_REG4 0x2d #define MMA8452_CTRL_REG5 0x2e #define MMA8452_OFF_X 0x2f @@ -257,20 +259,17 @@ static const int mma8452_samp_freq[8][2] = { {6, 250000}, {1, 560000} }; -/* Datasheet table 35 (step time vs sample frequency) */ -static const int mma8452_transient_time_step_us[8] = { - 1250, - 2500, - 5000, - 10000, - 20000, - 20000, - 20000, - 20000 +/* Datasheet table: step time "Relationship with the ODR" (sample frequency) */ +static const int mma8452_transient_time_step_us[4][8] = { + { 1250, 2500, 5000, 10000, 20000, 20000, 20000, 20000 }, /* normal */ + { 1250, 2500, 5000, 10000, 20000, 80000, 80000, 80000 }, /* l p l n */ + { 1250, 2500, 2500, 2500, 2500, 2500, 2500, 2500 }, /* high res*/ + { 1250, 2500, 5000, 10000, 20000, 80000, 160000, 160000 } /* l p */ }; -/* Datasheet table 18 (normal mode) */ -static const int mma8452_hp_filter_cutoff[8][4][2] = { +/* Datasheet table "High-Pass Filter Cutoff Options" */ +static const int mma8452_hp_filter_cutoff[4][8][4][2] = { + { /* normal */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 800 Hz sample */ { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, /* 400 Hz sample */ { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, /* 200 Hz sample */ @@ -279,8 +278,61 @@ static const int mma8452_hp_filter_cutoff[8][4][2] = { { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 12.5 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, /* 6.25 Hz sample */ { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} } /* 1.56 Hz sample */ + }, + { /* low noise low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} }, + { {0, 500000}, {0, 250000}, {0, 125000}, {0, 063000} } + }, + { /* high resolution */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {16, 0}, {8, 0}, {4, 0}, {2, 0} } + }, + { /* low power */ + { {16, 0}, {8, 0}, {4, 0}, {2, 0} }, + { {8, 0}, {4, 0}, {2, 0}, {1, 0} }, + { {4, 0}, {2, 0}, {1, 0}, {0, 500000} }, + { {2, 0}, {1, 0}, {0, 500000}, {0, 250000} }, + { {1, 0}, {0, 500000}, {0, 250000}, {0, 125000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} }, + { {0, 250000}, {0, 125000}, {0, 063000}, {0, 031000} } + } }; +/* Datasheet table "MODS Oversampling modes averaging values at each ODR" */ +static const u16 mma8452_os_ratio[4][8] = { + /* 800 Hz, 400 Hz, ... , 1.56 Hz */ + { 2, 4, 4, 4, 4, 16, 32, 128 }, /* normal */ + { 2, 4, 4, 4, 4, 4, 8, 32 }, /* low power low noise */ + { 2, 4, 8, 16, 32, 128, 256, 1024 }, /* high resolution */ + { 2, 2, 2, 2, 2, 2, 4, 16 } /* low power */ +}; + +static int mma8452_get_power_mode(struct mma8452_data *data) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + return ((reg & MMA8452_CTRL_REG2_MODS_MASK) >> + MMA8452_CTRL_REG2_MODS_SHIFT); +} + static ssize_t mma8452_show_samp_freq_avail(struct device *dev, struct device_attribute *attr, char *buf) @@ -306,10 +358,39 @@ static ssize_t mma8452_show_hp_cutoff_avail(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct mma8452_data *data = iio_priv(indio_dev); - int i = mma8452_get_odr_index(data); + int i, j; - return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0])); + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + + return mma8452_show_int_plus_micros(buf, mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0])); +} + +static ssize_t mma8452_show_os_ratio_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct mma8452_data *data = iio_priv(indio_dev); + int i = mma8452_get_odr_index(data); + int j; + u16 val = 0; + size_t len = 0; + + for (j = 0; j < ARRAY_SIZE(mma8452_os_ratio); j++) { + if (val == mma8452_os_ratio[j][i]) + continue; + + val = mma8452_os_ratio[j][i]; + + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", val); + } + buf[len - 1] = '\n'; + + return len; } static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); @@ -317,6 +398,8 @@ static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, mma8452_show_scale_avail, NULL, 0); static IIO_DEVICE_ATTR(in_accel_filter_high_pass_3db_frequency_available, S_IRUGO, mma8452_show_hp_cutoff_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_accel_oversampling_ratio_available, S_IRUGO, + mma8452_show_os_ratio_avail, NULL, 0); static int mma8452_get_samp_freq_index(struct mma8452_data *data, int val, int val2) @@ -335,24 +418,33 @@ static int mma8452_get_scale_index(struct mma8452_data *data, int val, int val2) static int mma8452_get_hp_filter_index(struct mma8452_data *data, int val, int val2) { - int i = mma8452_get_odr_index(data); + int i, j; - return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[i], - ARRAY_SIZE(mma8452_hp_filter_cutoff[0]), val, val2); + i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + + return mma8452_get_int_plus_micros_index(mma8452_hp_filter_cutoff[j][i], + ARRAY_SIZE(mma8452_hp_filter_cutoff[0][0]), val, val2); } static int mma8452_read_hp_filter(struct mma8452_data *data, int *hz, int *uHz) { - int i, ret; + int j, i, ret; ret = i2c_smbus_read_byte_data(data->client, MMA8452_HP_FILTER_CUTOFF); if (ret < 0) return ret; i = mma8452_get_odr_index(data); + j = mma8452_get_power_mode(data); + if (j < 0) + return j; + ret &= MMA8452_HP_FILTER_CUTOFF_SEL_MASK; - *hz = mma8452_hp_filter_cutoff[i][ret][0]; - *uHz = mma8452_hp_filter_cutoff[i][ret][1]; + *hz = mma8452_hp_filter_cutoff[j][i][ret][0]; + *uHz = mma8452_hp_filter_cutoff[j][i][ret][1]; return 0; } @@ -414,6 +506,15 @@ static int mma8452_read_raw(struct iio_dev *indio_dev, } return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + + i = mma8452_get_odr_index(data); + + *val = mma8452_os_ratio[ret][i]; + return IIO_VAL_INT; } return -EINVAL; @@ -480,6 +581,21 @@ fail: return ret; } +static int mma8452_set_power_mode(struct mma8452_data *data, u8 mode) +{ + int reg; + + reg = i2c_smbus_read_byte_data(data->client, + MMA8452_CTRL_REG2); + if (reg < 0) + return reg; + + reg &= ~MMA8452_CTRL_REG2_MODS_MASK; + reg |= mode << MMA8452_CTRL_REG2_MODS_SHIFT; + + return mma8452_change_config(data, MMA8452_CTRL_REG2, reg); +} + /* returns >0 if in freefall mode, 0 if not or <0 if an error occurred */ static int mma8452_freefall_mode_enabled(struct mma8452_data *data) { @@ -597,6 +713,14 @@ static int mma8452_write_raw(struct iio_dev *indio_dev, return mma8452_change_config(data, MMA8452_DATA_CFG, data->data_cfg); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret = mma8452_get_odr_index(data); + + for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) { + if (mma8452_os_ratio[i][ret] == val) + return mma8452_set_power_mode(data, i); + } + default: return -EINVAL; } @@ -610,7 +734,7 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, int *val, int *val2) { struct mma8452_data *data = iio_priv(indio_dev); - int ret, us; + int ret, us, power_mode; switch (info) { case IIO_EV_INFO_VALUE: @@ -629,7 +753,11 @@ static int mma8452_read_thresh(struct iio_dev *indio_dev, if (ret < 0) return ret; - us = ret * mma8452_transient_time_step_us[ + power_mode = mma8452_get_power_mode(data); + if (power_mode < 0) + return power_mode; + + us = ret * mma8452_transient_time_step_us[power_mode][ mma8452_get_odr_index(data)]; *val = us / USEC_PER_SEC; *val2 = us % USEC_PER_SEC; @@ -677,8 +805,12 @@ static int mma8452_write_thresh(struct iio_dev *indio_dev, val); case IIO_EV_INFO_PERIOD: + ret = mma8452_get_power_mode(data); + if (ret < 0) + return ret; + steps = (val * USEC_PER_SEC + val2) / - mma8452_transient_time_step_us[ + mma8452_transient_time_step_us[ret][ mma8452_get_odr_index(data)]; if (steps < 0 || steps > 0xff) @@ -978,7 +1110,8 @@ static struct attribute_group mma8452_event_attribute_group = { BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY), \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -998,7 +1131,8 @@ static struct attribute_group mma8452_event_attribute_group = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ - BIT(IIO_CHAN_INFO_SCALE), \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = idx, \ .scan_type = { \ .sign = 's', \ @@ -1171,6 +1305,7 @@ static struct attribute *mma8452_attributes[] = { &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_accel_scale_available.dev_attr.attr, &iio_dev_attr_in_accel_filter_high_pass_3db_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_oversampling_ratio_available.dev_attr.attr, NULL }; From 2763ac94f3e4d3711863729f4c11500d245f68cc Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Wed, 11 May 2016 21:47:48 -0700 Subject: [PATCH 24/61] iio: potentiometer: tpl0102: remove unneeded i2c check functionality test Actually I2C_FUNC_SMBUS_WORD_DATA isn't need for this device, and regmap handles all single byte reads transparently. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/tpl0102.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index 5c304d42d713..7b6b54531ea2 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -116,10 +116,6 @@ static int tpl0102_probe(struct i2c_client *client, struct tpl0102_data *data; struct iio_dev *indio_dev; - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_WORD_DATA)) - return -EOPNOTSUPP; - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; From bf2a5600a3ebc9dc5f085b47791009e25ade0157 Mon Sep 17 00:00:00 2001 From: Tiberiu Breana Date: Thu, 5 May 2016 18:48:55 +0300 Subject: [PATCH 25/61] iio: accel: Add support for Bosch BMA220 This commit adds basic support for the Bosch Sensortec BMA220 digital triaxial acceleration sensor. The device datasheet can be found here: http://www.mouser.com/pdfdocs/BSTBMA220DS00308.PDF Includes: - raw readings - ACPI detection - power management Signed-off-by: Tiberiu Breana Signed-off-by: Jonathan Cameron --- drivers/iio/accel/Kconfig | 10 ++ drivers/iio/accel/Makefile | 1 + drivers/iio/accel/bma220_spi.c | 277 +++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 drivers/iio/accel/bma220_spi.c diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 1df6361a57fe..31325870a9ef 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -17,6 +17,16 @@ config BMA180 To compile this driver as a module, choose M here: the module will be called bma180. +config BMA220 + tristate "Bosch BMA220 3-Axis Accelerometer Driver" + depends on SPI + help + Say yes here to add support for the Bosch BMA220 triaxial + acceleration sensor. + + To compile this driver as a module, choose M here: the + module will be called bma220_spi. + config BMC150_ACCEL tristate "Bosch BMC150 Accelerometer Driver" select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index ba1165f54ec7..6cedbecca2ee 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_BMA180) += bma180.o +obj-$(CONFIG_BMA220) += bma220_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c new file mode 100644 index 000000000000..7343575e84ba --- /dev/null +++ b/drivers/iio/accel/bma220_spi.c @@ -0,0 +1,277 @@ +/** + * BMA220 Digital triaxial acceleration sensor driver + * + * Copyright (c) 2016, Intel Corporation. + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + */ + +#include +#include +#include +#include +#include +#include + +#define BMA220_REG_ID 0x00 +#define BMA220_REG_ACCEL_X 0x02 +#define BMA220_REG_ACCEL_Y 0x03 +#define BMA220_REG_ACCEL_Z 0x04 +#define BMA220_REG_RANGE 0x11 +#define BMA220_REG_SUSPEND 0x18 + +#define BMA220_CHIP_ID 0xDD +#define BMA220_READ_MASK 0x80 +#define BMA220_RANGE_MASK 0x03 +#define BMA220_DATA_SHIFT 2 +#define BMA220_SUSPEND_SLEEP 0xFF +#define BMA220_SUSPEND_WAKE 0x00 + +#define BMA220_DEVICE_NAME "bma220" +#define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983" + +#define BMA220_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE); + +static struct attribute *bma220_attributes[] = { + &iio_const_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group bma220_attribute_group = { + .attrs = bma220_attributes, +}; + +static const int bma220_scale_table[][4] = { + {0, 623000}, {1, 248000}, {2, 491000}, {4, 983000} +}; + +struct bma220_data { + struct spi_device *spi_device; + struct mutex lock; + u8 tx_buf[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec bma220_channels[] = { + BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X), + BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y), + BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z), +}; + +static inline int bma220_read_reg(struct spi_device *spi, u8 reg) +{ + return spi_w8r8(spi, reg | BMA220_READ_MASK); +} + +static int bma220_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + u8 range_idx; + struct bma220_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = bma220_read_reg(data->spi_device, chan->address); + if (ret < 0) + return -EINVAL; + *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); + if (ret < 0) + return ret; + range_idx = ret & BMA220_RANGE_MASK; + *val = bma220_scale_table[range_idx][0]; + *val2 = bma220_scale_table[range_idx][1]; + return IIO_VAL_INT_PLUS_MICRO; + } + + return -EINVAL; +} + +static int bma220_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int i; + int ret; + int index = -1; + struct bma220_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++) + if (val == bma220_scale_table[i][0] && + val2 == bma220_scale_table[i][1]) { + index = i; + break; + } + if (index < 0) + return -EINVAL; + + mutex_lock(&data->lock); + data->tx_buf[0] = BMA220_REG_RANGE; + data->tx_buf[1] = index; + ret = spi_write(data->spi_device, data->tx_buf, + sizeof(data->tx_buf)); + if (ret < 0) + dev_err(&data->spi_device->dev, + "failed to set measurement range\n"); + mutex_unlock(&data->lock); + + return 0; + } + + return -EINVAL; +} + +static const struct iio_info bma220_info = { + .driver_module = THIS_MODULE, + .read_raw = bma220_read_raw, + .write_raw = bma220_write_raw, + .attrs = &bma220_attribute_group, +}; + +static int bma220_init(struct spi_device *spi) +{ + int ret; + + ret = bma220_read_reg(spi, BMA220_REG_ID); + if (ret != BMA220_CHIP_ID) + return -ENODEV; + + /* Make sure the chip is powered on */ + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret < 0) + return ret; + else if (ret == BMA220_SUSPEND_WAKE) + return bma220_read_reg(spi, BMA220_REG_SUSPEND); + + return 0; +} + +static int bma220_deinit(struct spi_device *spi) +{ + int ret; + + /* Make sure the chip is powered off */ + ret = bma220_read_reg(spi, BMA220_REG_SUSPEND); + if (ret < 0) + return ret; + else if (ret == BMA220_SUSPEND_SLEEP) + return bma220_read_reg(spi, BMA220_REG_SUSPEND); + + return 0; +} + +static int bma220_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct bma220_data *data; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) { + dev_err(&spi->dev, "iio allocation failed!\n"); + return -ENOMEM; + } + + data = iio_priv(indio_dev); + data->spi_device = spi; + spi_set_drvdata(spi, indio_dev); + mutex_init(&data->lock); + + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &bma220_info; + indio_dev->name = BMA220_DEVICE_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = bma220_channels; + indio_dev->num_channels = ARRAY_SIZE(bma220_channels); + + ret = bma220_init(data->spi_device); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&spi->dev, "iio_device_register failed\n"); + return bma220_deinit(spi); + } + + return ret; +} + +static int bma220_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + iio_device_unregister(indio_dev); + + return bma220_deinit(spi); +} + +#ifdef CONFIG_PM_SLEEP +static int bma220_suspend(struct device *dev) +{ + struct bma220_data *data = + iio_priv(spi_get_drvdata(to_spi_device(dev))); + + /* The chip can be suspended/woken up by a simple register read. */ + return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); +} + +static int bma220_resume(struct device *dev) +{ + struct bma220_data *data = + iio_priv(spi_get_drvdata(to_spi_device(dev))); + + return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND); +} + +static SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume); + +#define BMA220_PM_OPS (&bma220_pm_ops) +#else +#define BMA220_PM_OPS NULL +#endif + +static const struct spi_device_id bma220_spi_id[] = { + {"bma220", 0}, + {} +}; + +static const struct acpi_device_id bma220_acpi_id[] = { + {"BMA0220", 0}, + {} +}; + +MODULE_DEVICE_TABLE(spi, bma220_spi_id); + +static struct spi_driver bma220_driver = { + .driver = { + .name = "bma220_spi", + .pm = BMA220_PM_OPS, + .acpi_match_table = ACPI_PTR(bma220_acpi_id), + }, + .probe = bma220_probe, + .remove = bma220_remove, + .id_table = bma220_spi_id, +}; + +module_spi_driver(bma220_driver); + +MODULE_AUTHOR("Tiberiu Breana "); +MODULE_DESCRIPTION("BMA220 acceleration sensor driver"); +MODULE_LICENSE("GPL v2"); From 9d75db36df146d212ade86a1aa69b718ebf31ac8 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Tue, 26 Apr 2016 15:39:58 +0300 Subject: [PATCH 26/61] iio: magn: Add support for BMM150 magnetometer BMM150 is register compatible with magnetometer part of BMC156. Datasheet is at: http://www.mouser.com/ds/2/783/BST-BMM150-DS001-01-786480.pdf Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/bmc150_magn_i2c.c | 3 +++ drivers/iio/magnetometer/bmc150_magn_spi.c | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c index eddc7f0d0096..ee05722587aa 100644 --- a/drivers/iio/magnetometer/bmc150_magn_i2c.c +++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c @@ -2,6 +2,7 @@ * 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips: * - BMC150 * - BMC156 + * - BMM150 * * Copyright (c) 2016, Intel Corporation. * @@ -49,6 +50,7 @@ static int bmc150_magn_i2c_remove(struct i2c_client *client) static const struct acpi_device_id bmc150_magn_acpi_match[] = { {"BMC150B", 0}, {"BMC156B", 0}, + {"BMM150B", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); @@ -56,6 +58,7 @@ MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); static const struct i2c_device_id bmc150_magn_i2c_id[] = { {"bmc150_magn", 0}, {"bmc156_magn", 0}, + {"bmm150_magn", 0}, {} }; MODULE_DEVICE_TABLE(i2c, bmc150_magn_i2c_id); diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c index c4c738a07695..7d4152d4d01e 100644 --- a/drivers/iio/magnetometer/bmc150_magn_spi.c +++ b/drivers/iio/magnetometer/bmc150_magn_spi.c @@ -2,6 +2,7 @@ * 3-axis magnetometer driver support following SPI Bosch-Sensortec chips: * - BMC150 * - BMC156 + * - BMM150 * * Copyright (c) 2016, Intel Corporation. * @@ -41,6 +42,7 @@ static int bmc150_magn_spi_remove(struct spi_device *spi) static const struct spi_device_id bmc150_magn_spi_id[] = { {"bmc150_magn", 0}, {"bmc156_magn", 0}, + {"bmm150_magn", 0}, {} }; MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); @@ -48,6 +50,7 @@ MODULE_DEVICE_TABLE(spi, bmc150_magn_spi_id); static const struct acpi_device_id bmc150_magn_acpi_match[] = { {"BMC150B", 0}, {"BMC156B", 0}, + {"BMM150B", 0}, {}, }; MODULE_DEVICE_TABLE(acpi, bmc150_magn_acpi_match); From ba35f111aa6f386df33f950aeaea53a2bf040cc2 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sun, 15 May 2016 22:18:46 -0700 Subject: [PATCH 27/61] iio: adc: ti-ads1015: add support for ADS1115 part TI ADS1115 is a 16-bit resolution ADC that is register map compatible with the ADS1015 device. Signed-off-by: Matt Ranostay Acked-by: Daniel Baluta Acked-by: Crt Mori Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 124 ++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 73cbf0b54e54..a83542319d28 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -55,6 +55,11 @@ #define ADS1015_DEFAULT_DATA_RATE 4 #define ADS1015_DEFAULT_CHAN 0 +enum { + ADS1015, + ADS1115, +}; + enum ads1015_channels { ADS1015_AIN0_AIN1 = 0, ADS1015_AIN0_AIN3, @@ -71,6 +76,10 @@ static const unsigned int ads1015_data_rate[] = { 128, 250, 490, 920, 1600, 2400, 3300, 3300 }; +static const unsigned int ads1115_data_rate[] = { + 8, 16, 32, 64, 128, 250, 475, 860 +}; + static const struct { int scale; int uscale; @@ -123,6 +132,42 @@ static const struct { }, \ } +#define ADS1115_V_CHAN(_chan, _addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define ADS1115_V_DIFF_CHAN(_chan, _chan2, _addr) { \ + .type = IIO_VOLTAGE, \ + .differential = 1, \ + .indexed = 1, \ + .address = _addr, \ + .channel = _chan, \ + .channel2 = _chan2, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = _addr, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + struct ads1015_data { struct regmap *regmap; /* @@ -131,6 +176,8 @@ struct ads1015_data { */ struct mutex lock; struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; + + unsigned int *data_rate; }; static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg) @@ -157,6 +204,18 @@ static const struct iio_chan_spec ads1015_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), }; +static const struct iio_chan_spec ads1115_channels[] = { + ADS1115_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1), + ADS1115_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3), + ADS1115_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3), + ADS1115_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3), + ADS1115_V_CHAN(0, ADS1015_AIN0), + ADS1115_V_CHAN(1, ADS1015_AIN1), + ADS1115_V_CHAN(2, ADS1015_AIN2), + ADS1115_V_CHAN(3, ADS1015_AIN3), + IIO_CHAN_SOFT_TIMESTAMP(ADS1015_TIMESTAMP), +}; + static int ads1015_set_power_state(struct ads1015_data *data, bool on) { int ret; @@ -196,7 +255,7 @@ int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val) return ret; if (change) { - conv_time = DIV_ROUND_UP(USEC_PER_SEC, ads1015_data_rate[dr]); + conv_time = DIV_ROUND_UP(USEC_PER_SEC, data->data_rate[dr]); usleep_range(conv_time, conv_time + 1); } @@ -263,7 +322,7 @@ static int ads1015_set_data_rate(struct ads1015_data *data, int chan, int rate) int i, ret, rindex = -1; for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++) - if (ads1015_data_rate[i] == rate) { + if (data->data_rate[i] == rate) { rindex = i; break; } @@ -291,7 +350,9 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, mutex_lock(&indio_dev->mlock); mutex_lock(&data->lock); switch (mask) { - case IIO_CHAN_INFO_RAW: + case IIO_CHAN_INFO_RAW: { + int shift = chan->scan_type.shift; + if (iio_buffer_enabled(indio_dev)) { ret = -EBUSY; break; @@ -307,8 +368,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, break; } - /* 12 bit res, D0 is bit 4 in conversion register */ - *val = sign_extend32(*val >> 4, 11); + *val = sign_extend32(*val >> shift, 15 - shift); ret = ads1015_set_power_state(data, false); if (ret < 0) @@ -316,6 +376,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; + } case IIO_CHAN_INFO_SCALE: idx = data->channel_data[chan->address].pga; *val = ads1015_scale[idx].scale; @@ -324,7 +385,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_SAMP_FREQ: idx = data->channel_data[chan->address].data_rate; - *val = ads1015_data_rate[idx]; + *val = data->data_rate[idx]; ret = IIO_VAL_INT; break; default: @@ -380,12 +441,15 @@ static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = { }; static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125"); -static IIO_CONST_ATTR(sampling_frequency_available, - "128 250 490 920 1600 2400 3300"); + +static IIO_CONST_ATTR_NAMED(ads1015_sampling_frequency_available, + sampling_frequency_available, "128 250 490 920 1600 2400 3300"); +static IIO_CONST_ATTR_NAMED(ads1115_sampling_frequency_available, + sampling_frequency_available, "8 16 32 64 128 250 475 860"); static struct attribute *ads1015_attributes[] = { &iio_const_attr_scale_available.dev_attr.attr, - &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_const_attr_ads1015_sampling_frequency_available.dev_attr.attr, NULL, }; @@ -393,11 +457,28 @@ static const struct attribute_group ads1015_attribute_group = { .attrs = ads1015_attributes, }; -static const struct iio_info ads1015_info = { +static struct attribute *ads1115_attributes[] = { + &iio_const_attr_scale_available.dev_attr.attr, + &iio_const_attr_ads1115_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group ads1115_attribute_group = { + .attrs = ads1115_attributes, +}; + +static struct iio_info ads1015_info = { .driver_module = THIS_MODULE, .read_raw = ads1015_read_raw, .write_raw = ads1015_write_raw, - .attrs = &ads1015_attribute_group, + .attrs = &ads1015_attribute_group, +}; + +static struct iio_info ads1115_info = { + .driver_module = THIS_MODULE, + .read_raw = ads1015_read_raw, + .write_raw = ads1015_write_raw, + .attrs = &ads1115_attribute_group, }; #ifdef CONFIG_OF @@ -500,12 +581,24 @@ static int ads1015_probe(struct i2c_client *client, mutex_init(&data->lock); indio_dev->dev.parent = &client->dev; - indio_dev->info = &ads1015_info; indio_dev->name = ADS1015_DRV_NAME; - indio_dev->channels = ads1015_channels; - indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); indio_dev->modes = INDIO_DIRECT_MODE; + switch (id->driver_data) { + case ADS1015: + indio_dev->channels = ads1015_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1015_channels); + indio_dev->info = &ads1015_info; + data->data_rate = (unsigned int *) &ads1015_data_rate; + break; + case ADS1115: + indio_dev->channels = ads1115_channels; + indio_dev->num_channels = ARRAY_SIZE(ads1115_channels); + indio_dev->info = &ads1115_info; + data->data_rate = (unsigned int *) &ads1115_data_rate; + break; + } + /* we need to keep this ABI the same as used by hwmon ADS1015 driver */ ads1015_get_channels_config(client); @@ -590,7 +683,8 @@ static const struct dev_pm_ops ads1015_pm_ops = { }; static const struct i2c_device_id ads1015_id[] = { - {"ads1015", 0}, + {"ads1015", ADS1015}, + {"ads1115", ADS1115}, {} }; MODULE_DEVICE_TABLE(i2c, ads1015_id); From 194dc4c714132a63a7a731fe4debeccbdfab13e1 Mon Sep 17 00:00:00 2001 From: Tiberiu Breana Date: Mon, 16 May 2016 14:58:23 +0300 Subject: [PATCH 28/61] iio: accel: Add triggered buffer support for BMA220 Signed-off-by: Tiberiu Breana Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma220_spi.c | 65 ++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 7343575e84ba..1098d10df8e8 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -11,9 +11,12 @@ #include #include #include +#include #include #include #include +#include +#include #define BMA220_REG_ID 0x00 #define BMA220_REG_ACCEL_X 0x02 @@ -39,8 +42,22 @@ .channel2 = IIO_MOD_##axis, \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 6, \ + .storagebits = 8, \ + .shift = BMA220_DATA_SHIFT, \ + .endianness = IIO_CPU, \ + }, \ } +enum bma220_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE); static struct attribute *bma220_attributes[] = { @@ -59,6 +76,7 @@ static const int bma220_scale_table[][4] = { struct bma220_data { struct spi_device *spi_device; struct mutex lock; + s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */ u8 tx_buf[2] ____cacheline_aligned; }; @@ -66,6 +84,7 @@ static const struct iio_chan_spec bma220_channels[] = { BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X), BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y), BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z), + IIO_CHAN_SOFT_TIMESTAMP(3), }; static inline int bma220_read_reg(struct spi_device *spi, u8 reg) @@ -73,6 +92,35 @@ static inline int bma220_read_reg(struct spi_device *spi, u8 reg) return spi_w8r8(spi, reg | BMA220_READ_MASK); } +static const unsigned long bma220_accel_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + +static irqreturn_t bma220_trigger_handler(int irq, void *p) +{ + int ret; + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct bma220_data *data = iio_priv(indio_dev); + struct spi_device *spi = data->spi_device; + + mutex_lock(&data->lock); + data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; + ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, + ARRAY_SIZE(bma220_channels) - 1); + if (ret < 0) + goto err; + + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + pf->timestamp); +err: + mutex_unlock(&data->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int bma220_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -199,18 +247,30 @@ static int bma220_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = bma220_channels; indio_dev->num_channels = ARRAY_SIZE(bma220_channels); + indio_dev->available_scan_masks = bma220_accel_scan_masks; ret = bma220_init(data->spi_device); if (ret < 0) return ret; + ret = iio_triggered_buffer_setup(indio_dev, NULL, + bma220_trigger_handler, NULL); + if (ret < 0) { + dev_err(&spi->dev, "iio triggered buffer setup failed\n"); + goto err_suspend; + } + ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(&spi->dev, "iio_device_register failed\n"); - return bma220_deinit(spi); + iio_triggered_buffer_cleanup(indio_dev); + goto err_suspend; } - return ret; + return 0; + +err_suspend: + return bma220_deinit(spi); } static int bma220_remove(struct spi_device *spi) @@ -218,6 +278,7 @@ static int bma220_remove(struct spi_device *spi) struct iio_dev *indio_dev = spi_get_drvdata(spi); iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); return bma220_deinit(spi); } From cdd469ad9e008a58ed465efa09f8594f1387e8ce Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 17 May 2016 12:25:37 -0400 Subject: [PATCH 29/61] iio: Export I2C module alias information The I2C drivers have an i2c_device_id array but that information isn't exported to the modules using the MODULE_DEVICE_TABLE() macro. So the modules autoloading won't work if the I2C device is registered using OF or legacy board files due missing alias information in the modules. The issue was found using Kieran Bingham's coccinelle semantic patch: https://lkml.org/lkml/2016/5/10/520 Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/am2315.c | 1 + drivers/iio/humidity/htu21.c | 1 + drivers/iio/pressure/hp206c.c | 1 + drivers/iio/pressure/ms5637.c | 1 + drivers/iio/temperature/tsys02d.c | 1 + 5 files changed, 5 insertions(+) diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 3be6d209a159..8de39bd349f9 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -278,6 +278,7 @@ static const struct i2c_device_id am2315_i2c_id[] = { {"am2315", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); static const struct acpi_device_id am2315_acpi_id[] = { {"AOS2315", 0}, diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c index 11cbc38b450f..0fbbd8c40894 100644 --- a/drivers/iio/humidity/htu21.c +++ b/drivers/iio/humidity/htu21.c @@ -236,6 +236,7 @@ static const struct i2c_device_id htu21_id[] = { {"ms8607-humidity", MS8607}, {} }; +MODULE_DEVICE_TABLE(i2c, htu21_id); static struct i2c_driver htu21_driver = { .probe = htu21_probe, diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c index 90f2b6e4a920..12f769e86355 100644 --- a/drivers/iio/pressure/hp206c.c +++ b/drivers/iio/pressure/hp206c.c @@ -401,6 +401,7 @@ static const struct i2c_device_id hp206c_id[] = { {"hp206c"}, {} }; +MODULE_DEVICE_TABLE(i2c, hp206c_id); #ifdef CONFIG_ACPI static const struct acpi_device_id hp206c_acpi_match[] = { diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c index e68052c118e6..8fb6f7ab97e4 100644 --- a/drivers/iio/pressure/ms5637.c +++ b/drivers/iio/pressure/ms5637.c @@ -173,6 +173,7 @@ static const struct i2c_device_id ms5637_id[] = { {"ms8607-temppressure", 1}, {} }; +MODULE_DEVICE_TABLE(i2c, ms5637_id); static struct i2c_driver ms5637_driver = { .probe = ms5637_probe, diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c index ab6fe8f6f2d1..c0a19a000387 100644 --- a/drivers/iio/temperature/tsys02d.c +++ b/drivers/iio/temperature/tsys02d.c @@ -174,6 +174,7 @@ static const struct i2c_device_id tsys02d_id[] = { {"tsys02d", 0}, {} }; +MODULE_DEVICE_TABLE(i2c, tsys02d_id); static struct i2c_driver tsys02d_driver = { .probe = tsys02d_probe, From 9a47894fbeda2ab92c6ec57ee359adeaf283b962 Mon Sep 17 00:00:00 2001 From: Cristina Moraru Date: Thu, 19 May 2016 08:55:46 +0300 Subject: [PATCH 30/61] iio: max5487: Add support for Maxim digital potentiometers Add implementation for Maxim MAX5487, MAX5488, MAX5489 digital potentiometers. Datasheet: http://datasheets.maximintegrated.com/en/ds/MAX5487-MAX5489.pdf Signed-off-by: Cristina Moraru CC: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/Kconfig | 11 ++ drivers/iio/potentiometer/Makefile | 1 + drivers/iio/potentiometer/max5487.c | 161 ++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+) create mode 100644 drivers/iio/potentiometer/max5487.c diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 6acb23810bb4..0941c8d490a0 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -15,6 +15,17 @@ config DS1803 To compile this driver as a module, choose M here: the module will be called ds1803. +config MAX5487 + tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver" + depends on SPI + help + Say yes here to build support for the Maxim + MAX5487, MAX5488, MAX5489 digital potentiometer + chips. + + To compile this driver as a module, choose M here: the + module will be called max5487. + config MCP4131 tristate "Microchip MCP413X/414X/415X/416X/423X/424X/425X/426X Digital Potentiometer driver" depends on SPI diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 6007faa2fb02..8adb58f38c0b 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_DS1803) += ds1803.o +obj-$(CONFIG_MAX5487) += max5487.o obj-$(CONFIG_MCP4131) += mcp4131.o obj-$(CONFIG_MCP4531) += mcp4531.o obj-$(CONFIG_TPL0102) += tpl0102.o diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c new file mode 100644 index 000000000000..6c50939a2e83 --- /dev/null +++ b/drivers/iio/potentiometer/max5487.c @@ -0,0 +1,161 @@ +/* + * max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers + * + * Copyright (C) 2016 Cristina-Gabriela Moraru + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include + +#include +#include + +#define MAX5487_WRITE_WIPER_A (0x01 << 8) +#define MAX5487_WRITE_WIPER_B (0x02 << 8) + +/* copy both wiper regs to NV regs */ +#define MAX5487_COPY_AB_TO_NV (0x23 << 8) +/* copy both NV regs to wiper regs */ +#define MAX5487_COPY_NV_TO_AB (0x33 << 8) + +#define MAX5487_MAX_POS 255 + +struct max5487_data { + struct spi_device *spi; + int kohms; +}; + +#define MAX5487_CHANNEL(ch, addr) { \ + .type = IIO_RESISTANCE, \ + .indexed = 1, \ + .output = 1, \ + .channel = ch, \ + .address = addr, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ +} + +static const struct iio_chan_spec max5487_channels[] = { + MAX5487_CHANNEL(0, MAX5487_WRITE_WIPER_A), + MAX5487_CHANNEL(1, MAX5487_WRITE_WIPER_B), +}; + +static int max5487_write_cmd(struct spi_device *spi, u16 cmd) +{ + return spi_write(spi, (const void *) &cmd, sizeof(u16)); +} + +static int max5487_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct max5487_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_SCALE) + return -EINVAL; + + *val = 1000 * data->kohms; + *val2 = MAX5487_MAX_POS; + + return IIO_VAL_FRACTIONAL; +} + +static int max5487_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct max5487_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val < 0 || val > MAX5487_MAX_POS) + return -EINVAL; + + return max5487_write_cmd(data->spi, chan->address | val); +} + +static const struct iio_info max5487_info = { + .read_raw = max5487_read_raw, + .write_raw = max5487_write_raw, + .driver_module = THIS_MODULE, +}; + +static int max5487_spi_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct max5487_data *data; + const struct spi_device_id *id = spi_get_device_id(spi); + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + dev_set_drvdata(&spi->dev, indio_dev); + data = iio_priv(indio_dev); + + data->spi = spi; + data->kohms = id->driver_data; + + indio_dev->info = &max5487_info; + indio_dev->name = id->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = max5487_channels; + indio_dev->num_channels = ARRAY_SIZE(max5487_channels); + + /* restore both wiper regs from NV regs */ + ret = max5487_write_cmd(data->spi, MAX5487_COPY_NV_TO_AB); + if (ret < 0) + return ret; + + return iio_device_register(indio_dev); +} + +static int max5487_spi_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + + iio_device_unregister(indio_dev); + + /* save both wiper regs to NV regs */ + return max5487_write_cmd(spi, MAX5487_COPY_AB_TO_NV); +} + +static const struct spi_device_id max5487_id[] = { + { "MAX5487", 10 }, + { "MAX5488", 50 }, + { "MAX5489", 100 }, + { } +}; +MODULE_DEVICE_TABLE(spi, max5487_id); + +static const struct acpi_device_id max5487_acpi_match[] = { + { "MAX5487", 10 }, + { "MAX5488", 50 }, + { "MAX5489", 100 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, max5487_acpi_match); + +static struct spi_driver max5487_driver = { + .driver = { + .name = "max5487", + .owner = THIS_MODULE, + .acpi_match_table = ACPI_PTR(max5487_acpi_match), + }, + .id_table = max5487_id, + .probe = max5487_spi_probe, + .remove = max5487_spi_remove +}; +module_spi_driver(max5487_driver); + +MODULE_AUTHOR("Cristina-Gabriela Moraru "); +MODULE_DESCRIPTION("max5487 SPI driver"); +MODULE_LICENSE("GPL v2"); From 44072b2c8da876e19c862ef609c14c8c4aecb48f Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Wed, 18 May 2016 17:16:18 +0200 Subject: [PATCH 31/61] iio: adc: nau7802: Expose possible gains in sysfs The Nuvoton NAU7802 ADC is able to adjust its gain but prior knowledge of its possible values was required to adjust it. Users had to guess the possible gain values based on the ADC datasheet or on this driver's code. This exposes the possible values in the in_voltage_scale_available file of each nau7802 ADC device. The gain is set for the whole ADC and is therefore not configurable by channel. Thus, there exists only one in_voltage_scale_available file for each nau7802 ADC device even if it has two separate channels. Signed-off-by: Quentin Schulz Acked-by: Alexandre Belloni Signed-off-by: Jonathan Cameron --- drivers/iio/adc/nau7802.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index e525aa6475c4..57365c504093 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -79,10 +79,29 @@ static const struct iio_chan_spec nau7802_chan_array[] = { static const u16 nau7802_sample_freq_avail[] = {10, 20, 40, 80, 10, 10, 10, 320}; +static ssize_t nau7802_show_scales(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nau7802_state *st = iio_priv(dev_to_iio_dev(dev)); + int i, len = 0; + + for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%09d ", + st->scale_avail[i]); + + buf[len-1] = '\n'; + + return len; +} + static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("10 40 80 320"); +static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, nau7802_show_scales, + NULL, 0); + static struct attribute *nau7802_attributes[] = { &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, NULL }; From b7a96bb96fa0dfa3974c1f7830aa698959bff155 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Sun, 15 May 2016 11:37:27 -0700 Subject: [PATCH 32/61] iio: light: jsa1212: remove unneeded i2c check functionality test This driver does not call i2c_smbus_read|write_byte_data(), so remove the corresponding functionality test. It uses regmap to handle byte transfers transparently. Signed-off-by: Alison Schofield Reviewed-by:Kuppuswamy Sathyanarayanan Reviewed-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/light/jsa1212.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c index 99a62816c3b4..e8a8931b4f50 100644 --- a/drivers/iio/light/jsa1212.c +++ b/drivers/iio/light/jsa1212.c @@ -325,9 +325,6 @@ static int jsa1212_probe(struct i2c_client *client, struct regmap *regmap; int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -EOPNOTSUPP; - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; From 14beaa8f5ab11b881c5e822e2474f5278d0946d5 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Wed, 4 May 2016 22:57:30 -0700 Subject: [PATCH 33/61] iio: pressure: bmp280: add humidity support Enable humidity support for the BME280 part Signed-off-by: Matt Ranostay Acked-by: Vlad Dogaru Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/Kconfig | 3 +- drivers/iio/pressure/bmp280.c | 200 +++++++++++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 3 deletions(-) diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index cda9f128f3a4..9125a9382b3e 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -12,7 +12,8 @@ config BMP280 select REGMAP_I2C help Say yes here to build support for Bosch Sensortec BMP180 and BMP280 - pressure and temperature sensors. + pressure and temperature sensors. Also supports the BE280 with + an additional humidty sensor channel. To compile this driver as a module, choose M here: the module will be called bmp280. diff --git a/drivers/iio/pressure/bmp280.c b/drivers/iio/pressure/bmp280.c index 2f1498e12bb2..1876b50cc84a 100644 --- a/drivers/iio/pressure/bmp280.c +++ b/drivers/iio/pressure/bmp280.c @@ -10,6 +10,7 @@ * Datasheet: * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf + * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf */ #define pr_fmt(fmt) "bmp280: " fmt @@ -23,6 +24,8 @@ #include /* BMP280 specific registers */ +#define BMP280_REG_HUMIDITY_LSB 0xFE +#define BMP280_REG_HUMIDITY_MSB 0xFD #define BMP280_REG_TEMP_XLSB 0xFC #define BMP280_REG_TEMP_LSB 0xFB #define BMP280_REG_TEMP_MSB 0xFA @@ -31,7 +34,17 @@ #define BMP280_REG_PRESS_MSB 0xF7 #define BMP280_REG_CONFIG 0xF5 +#define BMP280_REG_CTRL_MEAS 0xF4 #define BMP280_REG_STATUS 0xF3 +#define BMP280_REG_CTRL_HUMIDITY 0xF2 + +/* Due to non linear mapping, and data sizes we can't do a bulk read */ +#define BMP280_REG_COMP_H1 0xA1 +#define BMP280_REG_COMP_H2 0xE1 +#define BMP280_REG_COMP_H3 0xE3 +#define BMP280_REG_COMP_H4 0xE4 +#define BMP280_REG_COMP_H5 0xE5 +#define BMP280_REG_COMP_H6 0xE7 #define BMP280_REG_COMP_TEMP_START 0x88 #define BMP280_COMP_TEMP_REG_COUNT 6 @@ -46,6 +59,15 @@ #define BMP280_FILTER_8X (BIT(3) | BIT(2)) #define BMP280_FILTER_16X BIT(4) +#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0)) +#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0) +#define BMP280_OSRS_HUMIDITY_SKIP 0 +#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1) +#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2) +#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3) +#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4) +#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5) + #define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5)) #define BMP280_OSRS_TEMP_SKIP 0 #define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5) @@ -92,6 +114,7 @@ #define BMP180_CHIP_ID 0x55 #define BMP280_CHIP_ID 0x58 +#define BME280_CHIP_ID 0x60 #define BMP280_SOFT_RESET_VAL 0xB6 struct bmp280_data { @@ -103,6 +126,7 @@ struct bmp280_data { /* log of base 2 of oversampling rate */ u8 oversampling_press; u8 oversampling_temp; + u8 oversampling_humid; /* * Carryover value from temperature conversion, used in pressure @@ -120,9 +144,13 @@ struct bmp280_chip_info { const int *oversampling_press_avail; int num_oversampling_press_avail; + const int *oversampling_humid_avail; + int num_oversampling_humid_avail; + int (*chip_config)(struct bmp280_data *); int (*read_temp)(struct bmp280_data *, int *); int (*read_press)(struct bmp280_data *, int *, int *); + int (*read_humid)(struct bmp280_data *, int *, int *); }; /* @@ -143,12 +171,18 @@ static const struct iio_chan_spec bmp280_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + }, }; static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case BMP280_REG_CONFIG: + case BMP280_REG_CTRL_HUMIDITY: case BMP280_REG_CTRL_MEAS: case BMP280_REG_RESET: return true; @@ -160,6 +194,8 @@ static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg) static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { + case BMP280_REG_HUMIDITY_LSB: + case BMP280_REG_HUMIDITY_MSB: case BMP280_REG_TEMP_XLSB: case BMP280_REG_TEMP_LSB: case BMP280_REG_TEMP_MSB: @@ -177,13 +213,77 @@ static const struct regmap_config bmp280_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = BMP280_REG_TEMP_XLSB, + .max_register = BMP280_REG_HUMIDITY_LSB, .cache_type = REGCACHE_RBTREE, .writeable_reg = bmp280_is_writeable_reg, .volatile_reg = bmp280_is_volatile_reg, }; +/* + * Returns humidity in percent, resolution is 0.01 percent. Output value of + * "47445" represents 47445/1024 = 46.333 %RH. + * + * Taken from BME280 datasheet, Section 4.2.3, "Compensation formula". + */ + +static u32 bmp280_compensate_humidity(struct bmp280_data *data, + s32 adc_humidity) +{ + struct device *dev = &data->client->dev; + unsigned int H1, H3, tmp; + int H2, H4, H5, H6, ret, var; + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &H1); + if (ret < 0) { + dev_err(dev, "failed to read H1 comp value\n"); + return ret; + } + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H2 comp value\n"); + return ret; + } + H2 = sign_extend32(le16_to_cpu(tmp), 15); + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &H3); + if (ret < 0) { + dev_err(dev, "failed to read H3 comp value\n"); + return ret; + } + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H4 comp value\n"); + return ret; + } + H4 = sign_extend32(((be16_to_cpu(tmp) >> 4) & 0xff0) | + (be16_to_cpu(tmp) & 0xf), 11); + + ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &tmp, 2); + if (ret < 0) { + dev_err(dev, "failed to read H5 comp value\n"); + return ret; + } + H5 = sign_extend32(((le16_to_cpu(tmp) >> 4) & 0xfff), 11); + + ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp); + if (ret < 0) { + dev_err(dev, "failed to read H6 comp value\n"); + return ret; + } + H6 = sign_extend32(tmp, 7); + + var = ((s32)data->t_fine) - 76800; + var = ((((adc_humidity << 14) - (H4 << 20) - (H5 * var)) + 16384) >> 15) + * (((((((var * H6) >> 10) * (((var * H3) >> 11) + 32768)) >> 10) + + 2097152) * H2 + 8192) >> 14); + var -= ((((var >> 15) * (var >> 15)) >> 7) * H1) >> 4; + + return var >> 12; +}; + /* * Returns temperature in DegC, resolution is 0.01 DegC. Output value of * "5123" equals 51.23 DegC. t_fine carries fine temperature as global @@ -324,6 +424,34 @@ static int bmp280_read_press(struct bmp280_data *data, return IIO_VAL_FRACTIONAL; } +static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2) +{ + int ret; + __be16 tmp = 0; + s32 adc_humidity; + u32 comp_humidity; + + /* Read and compensate temperature so we get a reading of t_fine. */ + ret = bmp280_read_temp(data, NULL); + if (ret < 0) + return ret; + + ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, + (u8 *) &tmp, 2); + if (ret < 0) { + dev_err(&data->client->dev, "failed to read humidity\n"); + return ret; + } + + adc_humidity = be16_to_cpu(tmp); + comp_humidity = bmp280_compensate_humidity(data, adc_humidity); + + *val = comp_humidity; + *val2 = 1024; + + return IIO_VAL_FRACTIONAL; +} + static int bmp280_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -336,6 +464,9 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_PROCESSED: switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + ret = data->chip_info->read_humid(data, val, val2); + break; case IIO_PRESSURE: ret = data->chip_info->read_press(data, val, val2); break; @@ -349,6 +480,10 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, break; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + *val = 1 << data->oversampling_humid; + ret = IIO_VAL_INT; + break; case IIO_PRESSURE: *val = 1 << data->oversampling_press; ret = IIO_VAL_INT; @@ -372,6 +507,23 @@ static int bmp280_read_raw(struct iio_dev *indio_dev, return ret; } +static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data, + int val) +{ + int i; + const int *avail = data->chip_info->oversampling_humid_avail; + const int n = data->chip_info->num_oversampling_humid_avail; + + for (i = 0; i < n; i++) { + if (avail[i] == val) { + data->oversampling_humid = ilog2(val); + + return data->chip_info->chip_config(data); + } + } + return -EINVAL; +} + static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data, int val) { @@ -417,6 +569,9 @@ static int bmp280_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_OVERSAMPLING_RATIO: mutex_lock(&data->lock); switch (chan->type) { + case IIO_HUMIDITYRELATIVE: + ret = bmp280_write_oversampling_ratio_humid(data, val); + break; case IIO_PRESSURE: ret = bmp280_write_oversampling_ratio_press(data, val); break; @@ -535,6 +690,37 @@ static const struct bmp280_chip_info bmp280_chip_info = { .read_press = bmp280_read_press, }; +static int bme280_chip_config(struct bmp280_data *data) +{ + int ret = bmp280_chip_config(data); + u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1); + + if (ret < 0) + return ret; + + return regmap_update_bits(data->regmap, BMP280_REG_CTRL_HUMIDITY, + BMP280_OSRS_HUMIDITY_MASK, osrs); +} + +static const struct bmp280_chip_info bme280_chip_info = { + .regmap_config = &bmp280_regmap_config, + + .oversampling_temp_avail = bmp280_oversampling_avail, + .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .oversampling_press_avail = bmp280_oversampling_avail, + .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .oversampling_humid_avail = bmp280_oversampling_avail, + .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail), + + .chip_config = bme280_chip_config, + .read_temp = bmp280_read_temp, + .read_press = bmp280_read_press, + .read_humid = bmp280_read_humid, +}; + + static bool bmp180_is_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -849,21 +1035,29 @@ static int bmp280_probe(struct i2c_client *client, indio_dev->dev.parent = &client->dev; indio_dev->name = id->name; indio_dev->channels = bmp280_channels; - indio_dev->num_channels = ARRAY_SIZE(bmp280_channels); indio_dev->info = &bmp280_info; indio_dev->modes = INDIO_DIRECT_MODE; switch (id->driver_data) { case BMP180_CHIP_ID: + indio_dev->num_channels = 2; data->chip_info = &bmp180_chip_info; data->oversampling_press = ilog2(8); data->oversampling_temp = ilog2(1); break; case BMP280_CHIP_ID: + indio_dev->num_channels = 2; data->chip_info = &bmp280_chip_info; data->oversampling_press = ilog2(16); data->oversampling_temp = ilog2(2); break; + case BME280_CHIP_ID: + indio_dev->num_channels = 3; + data->chip_info = &bme280_chip_info; + data->oversampling_press = ilog2(16); + data->oversampling_humid = ilog2(16); + data->oversampling_temp = ilog2(2); + break; default: return -EINVAL; } @@ -895,6 +1089,7 @@ static const struct acpi_device_id bmp280_acpi_match[] = { {"BMP0280", BMP280_CHIP_ID }, {"BMP0180", BMP180_CHIP_ID }, {"BMP0085", BMP180_CHIP_ID }, + {"BME0280", BME280_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(acpi, bmp280_acpi_match); @@ -903,6 +1098,7 @@ static const struct i2c_device_id bmp280_id[] = { {"bmp280", BMP280_CHIP_ID }, {"bmp180", BMP180_CHIP_ID }, {"bmp085", BMP180_CHIP_ID }, + {"bme280", BME280_CHIP_ID }, { }, }; MODULE_DEVICE_TABLE(i2c, bmp280_id); From 866b148a73e72af919ff836f3dfb5a6022823623 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 22 May 2016 11:05:57 +0200 Subject: [PATCH 34/61] MAINTAINERS: Add file patterns for iio device tree bindings Submitters of device tree binding documentation may forget to CC the subsystem maintainer if this is missing. Signed-off-by: Geert Uytterhoeven Cc: Jonathan Cameron Cc: Hartmut Knaack Cc: Lars-Peter Clausen Cc: Peter Meerwald-Stadler Cc: linux-iio@vger.kernel.org Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1ace393e963c..cb1eaddfdf53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5541,6 +5541,7 @@ R: Lars-Peter Clausen R: Peter Meerwald-Stadler L: linux-iio@vger.kernel.org S: Maintained +F: Documentation/devicetree/bindings/iio/ F: drivers/iio/ F: drivers/staging/iio/ F: include/linux/iio/ From 5291582dc6a4a9c1167ee86748bce5444633d7cc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 22 May 2016 11:06:22 +0200 Subject: [PATCH 35/61] MAINTAINERS: Add file patterns for staging iio device tree bindings Submitters of device tree binding documentation may forget to CC the subsystem maintainer if this is missing. Signed-off-by: Geert Uytterhoeven Cc: Jonathan Cameron Cc: linux-iio@vger.kernel.org Signed-off-by: Jonathan Cameron --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index cb1eaddfdf53..0c484c472eec 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10561,6 +10561,7 @@ STAGING - INDUSTRIAL IO M: Jonathan Cameron L: linux-iio@vger.kernel.org S: Odd Fixes +F: Documentation/devicetree/bindings/staging/iio/ F: drivers/staging/iio/ STAGING - LIRC (LINUX INFRARED REMOTE CONTROL) DRIVERS From 8ac8aa61f87eda944cf29229c9c20cba9e83f1ea Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 17 May 2016 15:02:03 -0700 Subject: [PATCH 36/61] iio: adc: ti-ads1015: add datasheet names Add datasheet names for ADC channels to allow iio consumers access. Signed-off-by: Matt Ranostay Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index a83542319d28..8be192a84893 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -110,6 +110,7 @@ static const struct { .shift = 4, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan, \ } #define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) { \ @@ -130,6 +131,7 @@ static const struct { .shift = 4, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ } #define ADS1115_V_CHAN(_chan, _addr) { \ @@ -147,6 +149,7 @@ static const struct { .storagebits = 16, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan, \ } #define ADS1115_V_DIFF_CHAN(_chan, _chan2, _addr) { \ @@ -166,6 +169,7 @@ static const struct { .storagebits = 16, \ .endianness = IIO_CPU, \ }, \ + .datasheet_name = "AIN"#_chan"-AIN"#_chan2, \ } struct ads1015_data { From ef2d71d6b7fbbb57e332883d8fad39f2adb9199e Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 14 May 2016 18:42:08 +0100 Subject: [PATCH 37/61] iio: triggers: Make trigger ops structure explicitly non optional. This structure has not been optional for a long time (if ever) but the code implies that it is. As we then use it later in a fashion that would crash if it was in fact NULL, it's inconsistent so fix it up by removing unnecessary checks. Reported-by: Dan Carpenter Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index ae2806aafb72..672911293987 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -68,6 +68,10 @@ int iio_trigger_register(struct iio_trigger *trig_info) { int ret; + /* trig_info->ops is required for the module member */ + if (!trig_info->ops) + return -EINVAL; + trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); if (trig_info->id < 0) return trig_info->id; @@ -164,8 +168,7 @@ EXPORT_SYMBOL(iio_trigger_poll_chained); void iio_trigger_notify_done(struct iio_trigger *trig) { - if (atomic_dec_and_test(&trig->use_count) && trig->ops && - trig->ops->try_reenable) + if (atomic_dec_and_test(&trig->use_count) && trig->ops->try_reenable) if (trig->ops->try_reenable(trig)) /* Missed an interrupt so launch new poll now */ iio_trigger_poll(trig); @@ -219,7 +222,7 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig, return ret; } - if (trig->ops && trig->ops->set_trigger_state && notinuse) { + if (trig->ops->set_trigger_state && notinuse) { ret = trig->ops->set_trigger_state(trig, true); if (ret < 0) module_put(pf->indio_dev->info->driver_module); @@ -236,7 +239,7 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig, = (bitmap_weight(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1); - if (trig->ops && trig->ops->set_trigger_state && no_other_users) { + if (trig->ops->set_trigger_state && no_other_users) { ret = trig->ops->set_trigger_state(trig, false); if (ret) return ret; @@ -358,7 +361,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, return ret; } - if (trig && trig->ops && trig->ops->validate_device) { + if (trig && trig->ops->validate_device) { ret = trig->ops->validate_device(trig, indio_dev); if (ret) return ret; From e039e2f5b4dab9a90bb5441a154c01a051b1abfa Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Tue, 19 Apr 2016 11:18:32 +0200 Subject: [PATCH 38/61] iio:st_pressure:initial lps22hb sensor support Initial support for ST LPS22HB pressure sensor. Datasheet: http://www2.st.com/resource/en/datasheet/lps22hb.pdf Features: * pressure data and timestamping channels * sampling frequency selection * interrupt based trigger * over I2C or SPI Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/st-sensors.txt | 1 + drivers/iio/pressure/Kconfig | 2 +- drivers/iio/pressure/st_pressure.h | 1 + drivers/iio/pressure/st_pressure_core.c | 93 ++++++++++++++++++- drivers/iio/pressure/st_pressure_i2c.c | 4 + drivers/iio/pressure/st_pressure_spi.c | 1 + 6 files changed, 97 insertions(+), 5 deletions(-) diff --git a/Documentation/devicetree/bindings/iio/st-sensors.txt b/Documentation/devicetree/bindings/iio/st-sensors.txt index 5844cf72862d..e41fe340162b 100644 --- a/Documentation/devicetree/bindings/iio/st-sensors.txt +++ b/Documentation/devicetree/bindings/iio/st-sensors.txt @@ -64,3 +64,4 @@ Pressure sensors: - st,lps001wp-press - st,lps25h-press - st,lps331ap-press +- st,lps22hb-press diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index 9125a9382b3e..8d654f671b6b 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -131,7 +131,7 @@ config IIO_ST_PRESS select IIO_TRIGGERED_BUFFER if (IIO_BUFFER) help Say yes here to build support for STMicroelectronics pressure - sensors: LPS001WP, LPS25H, LPS331AP. + sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB. This driver can also be built as a module. If so, these modules will be created: diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index f5f41490060b..903a21e46874 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -17,6 +17,7 @@ #define LPS001WP_PRESS_DEV_NAME "lps001wp" #define LPS25H_PRESS_DEV_NAME "lps25h" #define LPS331AP_PRESS_DEV_NAME "lps331ap" +#define LPS22HB_PRESS_DEV_NAME "lps22hb" /** * struct st_sensors_platform_data - default press platform data diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 9e9b72a8f18f..c0ff3bf071d8 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -113,6 +113,26 @@ #define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28 #define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b +/* CUSTOM VALUES FOR LPS22HB SENSOR */ +#define ST_PRESS_LPS22HB_WAI_EXP 0xb1 +#define ST_PRESS_LPS22HB_ODR_ADDR 0x10 +#define ST_PRESS_LPS22HB_ODR_MASK 0x70 +#define ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL 0x01 +#define ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL 0x02 +#define ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL 0x03 +#define ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL 0x04 +#define ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL 0x05 +#define ST_PRESS_LPS22HB_PW_ADDR 0x10 +#define ST_PRESS_LPS22HB_PW_MASK 0x70 +#define ST_PRESS_LPS22HB_BDU_ADDR 0x10 +#define ST_PRESS_LPS22HB_BDU_MASK 0x02 +#define ST_PRESS_LPS22HB_DRDY_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK 0x04 +#define ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK 0x08 +#define ST_PRESS_LPS22HB_IHL_IRQ_ADDR 0x12 +#define ST_PRESS_LPS22HB_IHL_IRQ_MASK 0x80 +#define ST_PRESS_LPS22HB_MULTIREAD_BIT true + static const struct iio_chan_spec st_press_1_channels[] = { { .type = IIO_PRESSURE, @@ -183,6 +203,27 @@ static const struct iio_chan_spec st_press_lps001wp_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1) }; +static const struct iio_chan_spec st_press_lps22hb_channels[] = { + { + .type = IIO_PRESSURE, + .channel2 = IIO_NO_MOD, + .address = ST_PRESS_1_OUT_XL_ADDR, + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 24, + .endianness = IIO_LE, + }, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .modified = 0, + }, + IIO_CHAN_SOFT_TIMESTAMP(1) +}; + static const struct st_sensor_settings st_press_sensors_settings[] = { { .wai = ST_PRESS_LPS331AP_WAI_EXP, @@ -326,6 +367,51 @@ static const struct st_sensor_settings st_press_sensors_settings[] = { .multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT, .bootime = 2, }, + { + .wai = ST_PRESS_LPS22HB_WAI_EXP, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LPS22HB_PRESS_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_press_lps22hb_channels, + .num_ch = ARRAY_SIZE(st_press_lps22hb_channels), + .odr = { + .addr = ST_PRESS_LPS22HB_ODR_ADDR, + .mask = ST_PRESS_LPS22HB_ODR_MASK, + .odr_avl = { + { 1, ST_PRESS_LPS22HB_ODR_AVL_1HZ_VAL, }, + { 10, ST_PRESS_LPS22HB_ODR_AVL_10HZ_VAL, }, + { 25, ST_PRESS_LPS22HB_ODR_AVL_25HZ_VAL, }, + { 50, ST_PRESS_LPS22HB_ODR_AVL_50HZ_VAL, }, + { 75, ST_PRESS_LPS22HB_ODR_AVL_75HZ_VAL, }, + }, + }, + .pw = { + .addr = ST_PRESS_LPS22HB_PW_ADDR, + .mask = ST_PRESS_LPS22HB_PW_MASK, + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .fs = { + .fs_avl = { + [0] = { + .num = ST_PRESS_FS_AVL_1260MB, + .gain = ST_PRESS_KPASCAL_NANO_SCALE, + }, + }, + }, + .bdu = { + .addr = ST_PRESS_LPS22HB_BDU_ADDR, + .mask = ST_PRESS_LPS22HB_BDU_MASK, + }, + .drdy_irq = { + .addr = ST_PRESS_LPS22HB_DRDY_IRQ_ADDR, + .mask_int1 = ST_PRESS_LPS22HB_DRDY_IRQ_INT1_MASK, + .mask_int2 = ST_PRESS_LPS22HB_DRDY_IRQ_INT2_MASK, + .addr_ihl = ST_PRESS_LPS22HB_IHL_IRQ_ADDR, + .mask_ihl = ST_PRESS_LPS22HB_IHL_IRQ_MASK, + }, + .multi_read_bit = ST_PRESS_LPS22HB_MULTIREAD_BIT, + }, }; static int st_press_write_raw(struct iio_dev *indio_dev, @@ -454,10 +540,9 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->channels = press_data->sensor_settings->ch; indio_dev->num_channels = press_data->sensor_settings->num_ch; - if (press_data->sensor_settings->fs.addr != 0) - press_data->current_fullscale = - (struct st_sensor_fullscale_avl *) - &press_data->sensor_settings->fs.fs_avl[0]; + press_data->current_fullscale = + (struct st_sensor_fullscale_avl *) + &press_data->sensor_settings->fs.fs_avl[0]; press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz; diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 8fcf9766eaec..ed18701c68c9 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -32,6 +32,10 @@ static const struct of_device_id st_press_of_match[] = { .compatible = "st,lps331ap-press", .data = LPS331AP_PRESS_DEV_NAME, }, + { + .compatible = "st,lps22hb-press", + .data = LPS22HB_PRESS_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_press_of_match); diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 40c0692ff1de..550508025af1 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -50,6 +50,7 @@ static const struct spi_device_id st_press_id_table[] = { { LPS001WP_PRESS_DEV_NAME }, { LPS25H_PRESS_DEV_NAME }, { LPS331AP_PRESS_DEV_NAME }, + { LPS22HB_PRESS_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_press_id_table); From dfe3ab1af0765eb800da5ce4cb4c685783096d9c Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Tue, 19 Apr 2016 11:18:38 +0200 Subject: [PATCH 39/61] iio:st_sensors: unexport st_sensors_get_buffer_element Remove st_sensors_get_buffer_element symbol export since not explicitly used outside of st_sensors driver. Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_buffer.c | 3 +-- include/linux/iio/common/st_sensors.h | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index c55898543a47..4ccc438afb5f 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -22,7 +22,7 @@ #include -int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) +static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) { int i, len; int total = 0; @@ -49,7 +49,6 @@ int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf) return total; } -EXPORT_SYMBOL(st_sensors_get_buffer_element); irqreturn_t st_sensors_trigger_handler(int irq, void *p) { diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index d029ffac0d69..78a193494370 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -251,8 +251,6 @@ struct st_sensor_data { #ifdef CONFIG_IIO_BUFFER irqreturn_t st_sensors_trigger_handler(int irq, void *p); - -int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf); #endif #ifdef CONFIG_IIO_TRIGGER From 169a88c1ee19b0734c9703d51a6d8ebe538f5bc3 Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Tue, 19 Apr 2016 11:18:39 +0200 Subject: [PATCH 40/61] iio:st_sensors: emulate SMBus block read if needed Use SMBus "block read" protocol only when supported by adapter. Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_i2c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c index 98cfee296d46..b43aa36031f8 100644 --- a/drivers/iio/common/st_sensors/st_sensors_i2c.c +++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c @@ -48,8 +48,8 @@ static int st_sensors_i2c_read_multiple_byte( if (multiread_bit) reg_addr |= ST_SENSORS_I2C_MULTIREAD; - return i2c_smbus_read_i2c_block_data(to_i2c_client(dev), - reg_addr, len, data); + return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev), + reg_addr, len, data); } static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb, From 14f295c846063c4f1812b09427195cee522aa006 Mon Sep 17 00:00:00 2001 From: Gregor Boirie Date: Tue, 19 Apr 2016 11:18:40 +0200 Subject: [PATCH 41/61] iio:st_sensors: fix power regulator usage Ensure failure to enable power regulators is properly handled. Signed-off-by: Gregor Boirie Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_core.c | 12 +++++--- .../iio/common/st_sensors/st_sensors_core.c | 29 +++++++++++++++---- drivers/iio/gyro/st_gyro_core.c | 12 +++++--- drivers/iio/magnetometer/st_magn_core.c | 12 +++++--- drivers/iio/pressure/st_pressure_core.c | 12 +++++--- include/linux/iio/common/st_sensors.h | 2 +- 6 files changed, 57 insertions(+), 22 deletions(-) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index dc73f2d85e6d..b8d3c3c91b4d 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -757,13 +757,15 @@ int st_accel_common_probe(struct iio_dev *indio_dev) indio_dev->info = &accel_info; mutex_init(&adata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_accel_sensors_settings), st_accel_sensors_settings); if (err < 0) - return err; + goto st_accel_power_off; adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; adata->multiread_bit = adata->sensor_settings->multi_read_bit; @@ -780,11 +782,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, adata->dev->platform_data); if (err < 0) - return err; + goto st_accel_power_off; err = st_accel_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_accel_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -807,6 +809,8 @@ st_accel_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_accel_probe_trigger_error: st_accel_deallocate_ring(indio_dev); +st_accel_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index dffe00692169..00078b5fe33c 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -228,7 +228,7 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); -void st_sensors_power_enable(struct iio_dev *indio_dev) +int st_sensors_power_enable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); int err; @@ -237,18 +237,37 @@ void st_sensors_power_enable(struct iio_dev *indio_dev) pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd"); if (!IS_ERR(pdata->vdd)) { err = regulator_enable(pdata->vdd); - if (err != 0) + if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd supply\n"); + return err; + } + } else { + err = PTR_ERR(pdata->vdd); + if (err != -ENODEV) + return err; } pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio"); if (!IS_ERR(pdata->vdd_io)) { err = regulator_enable(pdata->vdd_io); - if (err != 0) + if (err != 0) { dev_warn(&indio_dev->dev, "Failed to enable specified Vdd_IO supply\n"); + goto st_sensors_disable_vdd; + } + } else { + err = PTR_ERR(pdata->vdd_io); + if (err != -ENODEV) + goto st_sensors_disable_vdd; } + + return 0; + +st_sensors_disable_vdd: + if (!IS_ERR_OR_NULL(pdata->vdd)) + regulator_disable(pdata->vdd); + return err; } EXPORT_SYMBOL(st_sensors_power_enable); @@ -256,10 +275,10 @@ void st_sensors_power_disable(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); - if (!IS_ERR(pdata->vdd)) + if (!IS_ERR_OR_NULL(pdata->vdd)) regulator_disable(pdata->vdd); - if (!IS_ERR(pdata->vdd_io)) + if (!IS_ERR_OR_NULL(pdata->vdd_io)) regulator_disable(pdata->vdd_io); } EXPORT_SYMBOL(st_sensors_power_disable); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index 52a3c87c375c..ae1377da9a0b 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -425,13 +425,15 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->info = &gyro_info; mutex_init(&gdata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_gyro_sensors_settings), st_gyro_sensors_settings); if (err < 0) - return err; + goto st_gyro_power_off; gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; gdata->multiread_bit = gdata->sensor_settings->multi_read_bit; @@ -445,11 +447,11 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, (struct st_sensors_platform_data *)&gyro_pdata); if (err < 0) - return err; + goto st_gyro_power_off; err = st_gyro_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -472,6 +474,8 @@ st_gyro_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_gyro_probe_trigger_error: st_gyro_deallocate_ring(indio_dev); +st_gyro_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 62036d2a9956..7c94adc3ff1d 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -588,13 +588,15 @@ int st_magn_common_probe(struct iio_dev *indio_dev) indio_dev->info = &magn_info; mutex_init(&mdata->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_magn_sensors_settings), st_magn_sensors_settings); if (err < 0) - return err; + goto st_magn_power_off; mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS; mdata->multiread_bit = mdata->sensor_settings->multi_read_bit; @@ -607,11 +609,11 @@ int st_magn_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, NULL); if (err < 0) - return err; + goto st_magn_power_off; err = st_magn_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -634,6 +636,8 @@ st_magn_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_magn_probe_trigger_error: st_magn_deallocate_ring(indio_dev); +st_magn_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index c0ff3bf071d8..4d317af55405 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -527,13 +527,15 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->info = &press_info; mutex_init(&press_data->tb.buf_lock); - st_sensors_power_enable(indio_dev); + err = st_sensors_power_enable(indio_dev); + if (err) + return err; err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_press_sensors_settings), st_press_sensors_settings); if (err < 0) - return err; + goto st_press_power_off; press_data->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS; press_data->multiread_bit = press_data->sensor_settings->multi_read_bit; @@ -554,11 +556,11 @@ int st_press_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, press_data->dev->platform_data); if (err < 0) - return err; + goto st_press_power_off; err = st_press_allocate_ring(indio_dev); if (err < 0) - return err; + goto st_press_power_off; if (irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -581,6 +583,8 @@ st_press_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_press_probe_trigger_error: st_press_deallocate_ring(indio_dev); +st_press_power_off: + st_sensors_power_disable(indio_dev); return err; } diff --git a/include/linux/iio/common/st_sensors.h b/include/linux/iio/common/st_sensors.h index 78a193494370..91d5f684a53a 100644 --- a/include/linux/iio/common/st_sensors.h +++ b/include/linux/iio/common/st_sensors.h @@ -278,7 +278,7 @@ int st_sensors_set_enable(struct iio_dev *indio_dev, bool enable); int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable); -void st_sensors_power_enable(struct iio_dev *indio_dev); +int st_sensors_power_enable(struct iio_dev *indio_dev); void st_sensors_power_disable(struct iio_dev *indio_dev); From c70df20e31595cf696c8b4f50fc7d82b69f3dd5c Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 24 May 2016 12:16:23 -0700 Subject: [PATCH 42/61] iio: adc: ad7266: claim direct mode during sensor read Driver was checking for direct mode but not locking it down. Use iio_device_claim_direct_mode() to guarantee device stays in direct mode. Signed-off-by: Alison Schofield Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 21e19b60e2b9..01240aeeeab0 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -154,12 +154,11 @@ static int ad7266_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - - ret = ad7266_read_single(st, val, chan->address); + ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; + ret = ad7266_read_single(st, val, chan->address); + iio_device_release_direct_mode(indio_dev); *val = (*val >> 2) & 0xfff; if (chan->scan_type.sign == 's') From a52f238e7c808d9fd1f9dfc675f583b77156cdeb Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 24 May 2016 12:18:06 -0700 Subject: [PATCH 43/61] iio: adc: ad7476: use iio helper function to guarantee direct mode Replace the code that guarantees the device stays in direct mode with iio_device_claim_direct_mode() which does same. Signed-off-by: Alison Schofield Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index be85c2a0ad97..810c9a9fa62f 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -106,12 +106,11 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7476_scan_direct(st); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7476_scan_direct(st); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; From 6fea8a426b67455a58b59964cee905754ab77497 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 24 May 2016 12:18:43 -0700 Subject: [PATCH 44/61] iio: adc: ad7887: use iio helper function to guarantee direct mode Replace the code that guarantees the device stays in direct mode with iio_device_claim_direct_mode() which does same. Signed-off-by: Alison Schofield Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7887.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 2d3c397e66ad..ee2ccc19fab6 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -156,12 +156,11 @@ static int ad7887_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7887_scan_direct(st, chan->address); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7887_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; From 9f57e068e01c14f34f69244e1f4a6c567d93cdfe Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 24 May 2016 12:19:49 -0700 Subject: [PATCH 45/61] iio: adc: ad7923: use iio helper function to guarantee direct mode Replace the code that guarantees the device stays in direct mode with iio_device_claim_direct_mode() which does same. Signed-off-by: Alison Schofield Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7923.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 45e29ccd824f..ff444c19d749 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -233,12 +233,11 @@ static int ad7923_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad7923_scan_direct(st, chan->address); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad7923_scan_direct(st, chan->address); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; From 1bb86ecb6c03e156cdbd89978320c839e9f954b4 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 24 May 2016 12:20:24 -0700 Subject: [PATCH 46/61] iio: adc: ad799x: use iio helper function to guarantee direct mode Replace the code that guarantees the device stays in direct mode with iio_device_claim_direct_mode() which does same. Signed-off-by: Alison Schofield Acked-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad799x.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index a3f5254f4e51..ec0200dd52cb 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -282,12 +282,11 @@ static int ad799x_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = ad799x_scan_direct(st, chan->scan_index); - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = ad799x_scan_direct(st, chan->scan_index); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; @@ -395,11 +394,9 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, struct ad799x_state *st = iio_priv(indio_dev); int ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - ret = -EBUSY; - goto done; - } + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; if (state) st->config |= BIT(chan->scan_index) << AD799X_CHANNEL_SHIFT; @@ -412,10 +409,7 @@ static int ad799x_write_event_config(struct iio_dev *indio_dev, st->config &= ~AD7998_ALERT_EN; ret = ad799x_write_config(st, st->config); - -done: - mutex_unlock(&indio_dev->mlock); - + iio_device_release_direct_mode(indio_dev); return ret; } From 0c4b650029ec6d8b13b527c5b775549c2a06c898 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Wed, 25 May 2016 14:31:13 +0100 Subject: [PATCH 47/61] tools: iio: Add ability to install/uninstall Add options to the Makefile for install/uninstall similar to other tools. Signed-off-by: Peter Robinson Signed-off-by: Jonathan Cameron --- tools/iio/Makefile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/iio/Makefile b/tools/iio/Makefile index 3a7a54f59713..5c32e908f576 100644 --- a/tools/iio/Makefile +++ b/tools/iio/Makefile @@ -1,6 +1,10 @@ CC = $(CROSS_COMPILE)gcc CFLAGS += -Wall -g -D_GNU_SOURCE +BINDIR=usr/bin +INSTALL_PROGRAM=install -m 755 -p +DEL_FILE=rm -f + all: iio_event_monitor lsiio generic_buffer iio_event_monitor: iio_event_monitor.o iio_utils.o @@ -11,6 +15,17 @@ generic_buffer: generic_buffer.o iio_utils.o %.o: %.c iio_utils.h +install: + - mkdir -p $(INSTALL_ROOT)/$(BINDIR) + - $(INSTALL_PROGRAM) "iio_event_monitor" "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor" + - $(INSTALL_PROGRAM) "lsiio" "$(INSTALL_ROOT)/$(BINDIR)/lsiio" + - $(INSTALL_PROGRAM) "generic_buffer" "$(INSTALL_ROOT)/$(BINDIR)/generic_buffer" + +uninstall: + $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor" + $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/lsiio" + $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/generic_buffer" + .PHONY: clean clean: rm -f *.o iio_event_monitor lsiio generic_buffer From 5d48d6b0203de854587e8338376661e2315b2571 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Tue, 24 May 2016 18:03:55 +0300 Subject: [PATCH 48/61] tools: iio: Rename generic_buffer to iio_generic_buffer This makes it clear that generic_buffer is an IIO tool and also complies with filename conventions in tools/iio. Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- tools/iio/Makefile | 10 +++++----- tools/iio/{generic_buffer.c => iio_generic_buffer.c} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename tools/iio/{generic_buffer.c => iio_generic_buffer.c} (100%) diff --git a/tools/iio/Makefile b/tools/iio/Makefile index 5c32e908f576..5446d625e17d 100644 --- a/tools/iio/Makefile +++ b/tools/iio/Makefile @@ -5,13 +5,13 @@ BINDIR=usr/bin INSTALL_PROGRAM=install -m 755 -p DEL_FILE=rm -f -all: iio_event_monitor lsiio generic_buffer +all: iio_event_monitor lsiio iio_generic_buffer iio_event_monitor: iio_event_monitor.o iio_utils.o lsiio: lsiio.o iio_utils.o -generic_buffer: generic_buffer.o iio_utils.o +iio_generic_buffer: iio_generic_buffer.o iio_utils.o %.o: %.c iio_utils.h @@ -19,13 +19,13 @@ install: - mkdir -p $(INSTALL_ROOT)/$(BINDIR) - $(INSTALL_PROGRAM) "iio_event_monitor" "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor" - $(INSTALL_PROGRAM) "lsiio" "$(INSTALL_ROOT)/$(BINDIR)/lsiio" - - $(INSTALL_PROGRAM) "generic_buffer" "$(INSTALL_ROOT)/$(BINDIR)/generic_buffer" + - $(INSTALL_PROGRAM) "iio_generic_buffer" "$(INSTALL_ROOT)/$(BINDIR)/iio_generic_buffer" uninstall: $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/iio_event_monitor" $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/lsiio" - $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/generic_buffer" + $(DEL_FILE) "$(INSTALL_ROOT)/$(BINDIR)/iio_generic_buffer" .PHONY: clean clean: - rm -f *.o iio_event_monitor lsiio generic_buffer + rm -f *.o iio_event_monitor lsiio iio_generic_buffer diff --git a/tools/iio/generic_buffer.c b/tools/iio/iio_generic_buffer.c similarity index 100% rename from tools/iio/generic_buffer.c rename to tools/iio/iio_generic_buffer.c From 7103b99b031cb0ff6979331757bfc4893f37ae9e Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 24 May 2016 21:29:18 -0700 Subject: [PATCH 49/61] iio: chemical: atlas-ph-sensor: reorg driver to allow multiple chips Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-ph-sensor.c | 147 +++++++++++++++---------- 1 file changed, 91 insertions(+), 56 deletions(-) diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 62b37cd8fb56..e85477cb16fd 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -43,20 +44,25 @@ #define ATLAS_REG_PWR_CONTROL 0x06 -#define ATLAS_REG_CALIB_STATUS 0x0d -#define ATLAS_REG_CALIB_STATUS_MASK 0x07 -#define ATLAS_REG_CALIB_STATUS_LOW BIT(0) -#define ATLAS_REG_CALIB_STATUS_MID BIT(1) -#define ATLAS_REG_CALIB_STATUS_HIGH BIT(2) +#define ATLAS_REG_PH_CALIB_STATUS 0x0d +#define ATLAS_REG_PH_CALIB_STATUS_MASK 0x07 +#define ATLAS_REG_PH_CALIB_STATUS_LOW BIT(0) +#define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) +#define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) -#define ATLAS_REG_TEMP_DATA 0x0e +#define ATLAS_REG_PH_TEMP_DATA 0x0e #define ATLAS_REG_PH_DATA 0x16 #define ATLAS_PH_INT_TIME_IN_US 450000 +enum { + ATLAS_PH_SM, +}; + struct atlas_data { struct i2c_client *client; struct iio_trigger *trig; + struct atlas_device *chip; struct regmap *regmap; struct irq_work work; @@ -84,9 +90,10 @@ static const struct regmap_config atlas_regmap_config = { .cache_type = REGCACHE_RBTREE, }; -static const struct iio_chan_spec atlas_channels[] = { +static const struct iio_chan_spec atlas_ph_channels[] = { { .type = IIO_PH, + .address = ATLAS_REG_PH_DATA, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .scan_index = 0, @@ -100,7 +107,7 @@ static const struct iio_chan_spec atlas_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(1), { .type = IIO_TEMP, - .address = ATLAS_REG_TEMP_DATA, + .address = ATLAS_REG_PH_TEMP_DATA, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), .output = 1, @@ -108,6 +115,52 @@ static const struct iio_chan_spec atlas_channels[] = { }, }; +static int atlas_check_ph_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_PH_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_MID)) + dev_warn(dev, "device missing mid point calibration\n"); + + if (!(val & ATLAS_REG_PH_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + + return 0; +} + +struct atlas_device { + const struct iio_chan_spec *channels; + int num_channels; + int data_reg; + + int (*calibration)(struct atlas_data *data); + int delay; +}; + +static struct atlas_device atlas_devices[] = { + [ATLAS_PH_SM] = { + .channels = atlas_ph_channels, + .num_channels = 3, + .data_reg = ATLAS_REG_PH_DATA, + .calibration = &atlas_check_ph_calibration, + .delay = ATLAS_PH_INT_TIME_IN_US, + }, +}; + static int atlas_set_powermode(struct atlas_data *data, int on) { return regmap_write(data->regmap, ATLAS_REG_PWR_CONTROL, on); @@ -178,8 +231,9 @@ static irqreturn_t atlas_trigger_handler(int irq, void *private) struct atlas_data *data = iio_priv(indio_dev); int ret; - ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, - (u8 *) &data->buffer, sizeof(data->buffer[0])); + ret = regmap_bulk_read(data->regmap, data->chip->data_reg, + (u8 *) &data->buffer, + sizeof(__be32) * (data->chip->num_channels - 2)); if (!ret) iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, @@ -200,7 +254,7 @@ static irqreturn_t atlas_interrupt_handler(int irq, void *private) return IRQ_HANDLED; } -static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) +static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) { struct device *dev = &data->client->dev; int suspended = pm_runtime_suspended(dev); @@ -213,11 +267,9 @@ static int atlas_read_ph_measurement(struct atlas_data *data, __be32 *val) } if (suspended) - usleep_range(ATLAS_PH_INT_TIME_IN_US, - ATLAS_PH_INT_TIME_IN_US + 100000); + usleep_range(data->chip->delay, data->chip->delay + 100000); - ret = regmap_bulk_read(data->regmap, ATLAS_REG_PH_DATA, - (u8 *) val, sizeof(*val)); + ret = regmap_bulk_read(data->regmap, reg, (u8 *) val, sizeof(*val)); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -247,7 +299,8 @@ static int atlas_read_raw(struct iio_dev *indio_dev, if (iio_buffer_enabled(indio_dev)) ret = -EBUSY; else - ret = atlas_read_ph_measurement(data, ®); + ret = atlas_read_measurement(data, + chan->address, ®); mutex_unlock(&indio_dev->mlock); break; @@ -303,37 +356,24 @@ static const struct iio_info atlas_info = { .write_raw = atlas_write_raw, }; -static int atlas_check_calibration(struct atlas_data *data) -{ - struct device *dev = &data->client->dev; - int ret; - unsigned int val; - - ret = regmap_read(data->regmap, ATLAS_REG_CALIB_STATUS, &val); - if (ret) - return ret; - - if (!(val & ATLAS_REG_CALIB_STATUS_MASK)) { - dev_warn(dev, "device has not been calibrated\n"); - return 0; - } - - if (!(val & ATLAS_REG_CALIB_STATUS_LOW)) - dev_warn(dev, "device missing low point calibration\n"); - - if (!(val & ATLAS_REG_CALIB_STATUS_MID)) - dev_warn(dev, "device missing mid point calibration\n"); - - if (!(val & ATLAS_REG_CALIB_STATUS_HIGH)) - dev_warn(dev, "device missing high point calibration\n"); - - return 0; +static const struct i2c_device_id atlas_id[] = { + { "atlas-ph-sm", ATLAS_PH_SM}, + {} }; +MODULE_DEVICE_TABLE(i2c, atlas_id); + +static const struct of_device_id atlas_dt_ids[] = { + { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, + { } +}; +MODULE_DEVICE_TABLE(of, atlas_dt_ids); static int atlas_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct atlas_data *data; + struct atlas_device *chip; + const struct of_device_id *of_id; struct iio_trigger *trig; struct iio_dev *indio_dev; int ret; @@ -342,10 +382,16 @@ static int atlas_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; + of_id = of_match_device(atlas_dt_ids, &client->dev); + if (!of_id) + chip = &atlas_devices[id->driver_data]; + else + chip = &atlas_devices[(unsigned long)of_id->data]; + indio_dev->info = &atlas_info; indio_dev->name = ATLAS_DRV_NAME; - indio_dev->channels = atlas_channels; - indio_dev->num_channels = ARRAY_SIZE(atlas_channels); + indio_dev->channels = chip->channels; + indio_dev->num_channels = chip->num_channels; indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; indio_dev->dev.parent = &client->dev; @@ -358,6 +404,7 @@ static int atlas_probe(struct i2c_client *client, data = iio_priv(indio_dev); data->client = client; data->trig = trig; + data->chip = chip; trig->dev.parent = indio_dev->dev.parent; trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -379,7 +426,7 @@ static int atlas_probe(struct i2c_client *client, return -EINVAL; } - ret = atlas_check_calibration(data); + ret = chip->calibration(data); if (ret) return ret; @@ -480,18 +527,6 @@ static const struct dev_pm_ops atlas_pm_ops = { atlas_runtime_resume, NULL) }; -static const struct i2c_device_id atlas_id[] = { - { "atlas-ph-sm", 0 }, - {} -}; -MODULE_DEVICE_TABLE(i2c, atlas_id); - -static const struct of_device_id atlas_dt_ids[] = { - { .compatible = "atlas,ph-sm" }, - { } -}; -MODULE_DEVICE_TABLE(of, atlas_dt_ids); - static struct i2c_driver atlas_driver = { .driver = { .name = ATLAS_DRV_NAME, From 4b9d2090a444d44f16788b9ad60180011d133f97 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 24 May 2016 21:29:19 -0700 Subject: [PATCH 50/61] iio: electricalconductivity: add IIO_ELECTRICALCONDUCTIVITY type Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 7 +++++++ drivers/iio/industrialio-core.c | 1 + include/uapi/linux/iio/types.h | 1 + 3 files changed, 9 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index df44998e7506..e7f590c6ef8e 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1565,3 +1565,10 @@ Description: * X is in the plane of the propellers, perpendicular to Y axis, and positive towards the starboard side of the UAV ; * Z is perpendicular to propellers plane and positive upwards. + +What: /sys/bus/iio/devices/iio:deviceX/in_electricalconductivity_raw +KernelVersion: 4.8 +Contact: linux-iio@vger.kernel.org +Description: + Raw (unscaled no offset etc.) electric conductivity reading that + can be processed to siemens per meter. diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index e6319a9346b2..2a85bd888619 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -80,6 +80,7 @@ static const char * const iio_chan_type_name_spec[] = { [IIO_RESISTANCE] = "resistance", [IIO_PH] = "ph", [IIO_UVINDEX] = "uvindex", + [IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity", }; static const char * const iio_modifier_names[] = { diff --git a/include/uapi/linux/iio/types.h b/include/uapi/linux/iio/types.h index b0916fc72cce..22e5e589a274 100644 --- a/include/uapi/linux/iio/types.h +++ b/include/uapi/linux/iio/types.h @@ -39,6 +39,7 @@ enum iio_chan_type { IIO_RESISTANCE, IIO_PH, IIO_UVINDEX, + IIO_ELECTRICALCONDUCTIVITY, }; enum iio_modifier { From e8dd92bfbff2516f3e76bf08f38131c522454edd Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Tue, 24 May 2016 21:29:20 -0700 Subject: [PATCH 51/61] iio: chemical: atlas-ph-sensor: add EC feature Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- .../bindings/iio/chemical/atlas,ec-sm.txt | 22 ++++ drivers/iio/chemical/Kconfig | 8 +- drivers/iio/chemical/atlas-ph-sensor.c | 122 +++++++++++++++++- 3 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt diff --git a/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt new file mode 100644 index 000000000000..2962bd9a2b3d --- /dev/null +++ b/Documentation/devicetree/bindings/iio/chemical/atlas,ec-sm.txt @@ -0,0 +1,22 @@ +* Atlas Scientific EC-SM OEM sensor + +http://www.atlas-scientific.com/_files/_datasheets/_oem/EC_oem_datasheet.pdf + +Required properties: + + - compatible: must be "atlas,ec-sm" + - reg: the I2C address of the sensor + - 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. + +Example: + +atlas@64 { + compatible = "atlas,ec-sm"; + reg = <0x64>; + interrupt-parent = <&gpio1>; + interrupts = <16 2>; +}; diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index f73290f84c90..4bcc025e8c8a 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -5,15 +5,17 @@ menu "Chemical Sensors" config ATLAS_PH_SENSOR - tristate "Atlas Scientific OEM pH-SM sensor" + tristate "Atlas Scientific OEM SM sensors" depends on I2C select REGMAP_I2C select IIO_BUFFER select IIO_TRIGGERED_BUFFER select IRQ_WORK help - Say Y here to build I2C interface support for the Atlas - Scientific OEM pH-SM sensor. + Say Y here to build I2C interface support for the following + Atlas Scientific OEM SM sensors: + * pH SM sensor + * EC SM sensor To compile this driver as module, choose M here: the module will be called atlas-ph-sensor. diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index e85477cb16fd..02e85db5d31f 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -50,13 +50,28 @@ #define ATLAS_REG_PH_CALIB_STATUS_MID BIT(1) #define ATLAS_REG_PH_CALIB_STATUS_HIGH BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_MASK 0x0f +#define ATLAS_REG_EC_CALIB_STATUS_DRY BIT(0) +#define ATLAS_REG_EC_CALIB_STATUS_SINGLE BIT(1) +#define ATLAS_REG_EC_CALIB_STATUS_LOW BIT(2) +#define ATLAS_REG_EC_CALIB_STATUS_HIGH BIT(3) + #define ATLAS_REG_PH_TEMP_DATA 0x0e #define ATLAS_REG_PH_DATA 0x16 +#define ATLAS_REG_EC_PROBE 0x08 +#define ATLAS_REG_EC_TEMP_DATA 0x10 +#define ATLAS_REG_EC_DATA 0x18 +#define ATLAS_REG_TDS_DATA 0x1c +#define ATLAS_REG_PSS_DATA 0x20 + #define ATLAS_PH_INT_TIME_IN_US 450000 +#define ATLAS_EC_INT_TIME_IN_US 650000 enum { ATLAS_PH_SM, + ATLAS_EC_SM, }; struct atlas_data { @@ -66,12 +81,13 @@ struct atlas_data { struct regmap *regmap; struct irq_work work; - __be32 buffer[4]; /* 32-bit pH data + 32-bit pad + 64-bit timestamp */ + __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ }; static const struct regmap_range atlas_volatile_ranges[] = { regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL), regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4), + regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4), }; static const struct regmap_access_table atlas_volatile_table = { @@ -86,7 +102,7 @@ static const struct regmap_config atlas_regmap_config = { .val_bits = 8, .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PH_DATA + 4, + .max_register = ATLAS_REG_PSS_DATA + 4, .cache_type = REGCACHE_RBTREE, }; @@ -115,6 +131,50 @@ static const struct iio_chan_spec atlas_ph_channels[] = { }, }; +#define ATLAS_EC_CHANNEL(_idx, _addr) \ + {\ + .type = IIO_CONCENTRATION, \ + .indexed = 1, \ + .channel = _idx, \ + .address = _addr, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = _idx + 1, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 32, \ + .storagebits = 32, \ + .endianness = IIO_BE, \ + }, \ + } + +static const struct iio_chan_spec atlas_ec_channels[] = { + { + .type = IIO_ELECTRICALCONDUCTIVITY, + .address = ATLAS_REG_EC_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + ATLAS_EC_CHANNEL(0, ATLAS_REG_TDS_DATA), + ATLAS_EC_CHANNEL(1, ATLAS_REG_PSS_DATA), + IIO_CHAN_SOFT_TIMESTAMP(3), + { + .type = IIO_TEMP, + .address = ATLAS_REG_EC_TEMP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .output = 1, + .scan_index = -1 + }, +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -142,6 +202,44 @@ static int atlas_check_ph_calibration(struct atlas_data *data) return 0; } +static int atlas_check_ec_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_bulk_read(data->regmap, ATLAS_REG_EC_PROBE, &val, 2); + if (ret) + return ret; + + dev_info(dev, "probe set to K = %d.%.2d", be16_to_cpu(val) / 100, + be16_to_cpu(val) % 100); + + ret = regmap_read(data->regmap, ATLAS_REG_EC_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_MASK)) { + dev_warn(dev, "device has not been calibrated\n"); + return 0; + } + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_DRY)) + dev_warn(dev, "device missing dry point calibration\n"); + + if (val & ATLAS_REG_EC_CALIB_STATUS_SINGLE) { + dev_warn(dev, "device using single point calibration\n"); + } else { + if (!(val & ATLAS_REG_EC_CALIB_STATUS_LOW)) + dev_warn(dev, "device missing low point calibration\n"); + + if (!(val & ATLAS_REG_EC_CALIB_STATUS_HIGH)) + dev_warn(dev, "device missing high point calibration\n"); + } + + return 0; +} + struct atlas_device { const struct iio_chan_spec *channels; int num_channels; @@ -159,6 +257,14 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_ph_calibration, .delay = ATLAS_PH_INT_TIME_IN_US, }, + [ATLAS_EC_SM] = { + .channels = atlas_ec_channels, + .num_channels = 5, + .data_reg = ATLAS_REG_EC_DATA, + .calibration = &atlas_check_ec_calibration, + .delay = ATLAS_EC_INT_TIME_IN_US, + }, + }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -294,6 +400,8 @@ static int atlas_read_raw(struct iio_dev *indio_dev, (u8 *) ®, sizeof(reg)); break; case IIO_PH: + case IIO_CONCENTRATION: + case IIO_ELECTRICALCONDUCTIVITY: mutex_lock(&indio_dev->mlock); if (iio_buffer_enabled(indio_dev)) @@ -324,6 +432,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, *val = 1; /* 0.001 */ *val2 = 1000; break; + case IIO_ELECTRICALCONDUCTIVITY: + *val = 1; /* 0.00001 */ + *val = 100000; + break; + case IIO_CONCENTRATION: + *val = 0; /* 0.000000001 */ + *val2 = 1000; + return IIO_VAL_INT_PLUS_NANO; default: return -EINVAL; } @@ -358,12 +474,14 @@ static const struct iio_info atlas_info = { static const struct i2c_device_id atlas_id[] = { { "atlas-ph-sm", ATLAS_PH_SM}, + { "atlas-ec-sm", ATLAS_EC_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); static const struct of_device_id atlas_dt_ids[] = { { .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, }, + { .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); From 0aea7ac8548a9cda7a5003b39fe9b6031f0c19e0 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Tue, 24 May 2016 15:42:28 +0300 Subject: [PATCH 52/61] iio: magnetometer: bmc150: Document Bosch supported chips bmc150 driver supports also BMC156 and BMM150 chips. Signed-off-by: Daniel Baluta Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/Kconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 84e6559ccc65..1f842abcb4a4 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -44,6 +44,7 @@ config BMC150_MAGN_I2C This driver is only implementing magnetometer part, which has its own address and register map. + This driver also supports I2C Bosch BMC156 and BMM150 chips. To compile this driver as a module, choose M here: the module will be called bmc150_magn_i2c. @@ -60,6 +61,7 @@ config BMC150_MAGN_SPI This driver is only implementing magnetometer part, which has its own address and register map. + This driver also supports SPI Bosch BMC156 and BMM150 chips. To compile this driver as a module, choose M here: the module will be called bmc150_magn_spi. From 90e9a9500f16e60bf96c95cd6b74f84d56e12ef0 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Mon, 23 May 2016 23:27:30 -0700 Subject: [PATCH 53/61] iio: proximity: as3935: remove redundant MODULE_ALIAS MODULE_ALIAS isn't needed since the module name is the same as the alias defined. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index f4d29d5dbd5f..c12fde23c7ef 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -461,4 +461,3 @@ module_spi_driver(as3935_driver); MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("AS3935 lightning sensor"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:as3935"); From 4f20d5927b8e900ff1c3ee590b700ca71cd3f3ca Mon Sep 17 00:00:00 2001 From: Crestez Dan Leonard Date: Mon, 23 May 2016 21:39:56 +0300 Subject: [PATCH 54/61] iio: iio_generic_buffer: Cleanup when receiving signals This will clean (disable buffer/trigger/channels) when doing something like a CTRL-C. Otherwise restarting generic_buffer requires a manual echo 0 > buffer/enable This also cleanup up all the code freeing string buffers at the end of main. We initialize all pointers to NULL so that cleanup can all be done under a single error label. Signed-off-by: Crestez Dan Leonard Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 172 +++++++++++++++++++++------------ 1 file changed, 108 insertions(+), 64 deletions(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 2429c78de940..972f40008a49 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -32,6 +32,8 @@ #include #include #include +#include +#include #include "iio_utils.h" /** @@ -254,6 +256,65 @@ void print_usage(void) " -w Set delay between reads in us (event-less mode)\n"); } +enum autochan autochannels = AUTOCHANNELS_DISABLED; +char *dev_dir_name = NULL; +char *buf_dir_name = NULL; +bool current_trigger_set = false; + +void cleanup(void) +{ + int ret; + + /* Disable trigger */ + if (dev_dir_name && current_trigger_set) { + /* Disconnect the trigger - just write a dummy name. */ + ret = write_sysfs_string("trigger/current_trigger", + dev_dir_name, "NULL"); + if (ret < 0) + fprintf(stderr, "Failed to disable trigger: %s\n", + strerror(-ret)); + current_trigger_set = false; + } + + /* Disable buffer */ + if (buf_dir_name) { + ret = write_sysfs_int("enable", buf_dir_name, 0); + if (ret < 0) + fprintf(stderr, "Failed to disable buffer: %s\n", + strerror(-ret)); + } + + /* Disable channels if auto-enabled */ + if (dev_dir_name && autochannels == AUTOCHANNELS_ACTIVE) { + ret = enable_disable_all_channels(dev_dir_name, 0); + if (ret) + fprintf(stderr, "Failed to disable all channels\n"); + autochannels = AUTOCHANNELS_DISABLED; + } +} + +void sig_handler(int signum) +{ + fprintf(stderr, "Caught signal %d\n", signum); + cleanup(); + exit(-signum); +} + +void register_cleanup(void) +{ + struct sigaction sa = { .sa_handler = sig_handler }; + const int signums[] = { SIGINT, SIGTERM, SIGABRT }; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(signums); ++i) { + ret = sigaction(signums[i], &sa, NULL); + if (ret) { + perror("Failed to register signal handler"); + exit(-1); + } + } +} + int main(int argc, char **argv) { unsigned long num_loops = 2; @@ -261,25 +322,24 @@ int main(int argc, char **argv) unsigned long buf_len = 128; int ret, c, i, j, toread; - int fp; + int fp = -1; - int num_channels; + int num_channels = 0; char *trigger_name = NULL, *device_name = NULL; - char *dev_dir_name, *buf_dir_name; - int datardytrigger = 1; - char *data; + char *data = NULL; ssize_t read_size; int dev_num, trig_num; - char *buffer_access; + char *buffer_access = NULL; int scan_size; int noevents = 0; int notrigger = 0; - enum autochan autochannels = AUTOCHANNELS_DISABLED; char *dummy; struct iio_channel_info *channels; + register_cleanup(); + while ((c = getopt(argc, argv, "ac:egl:n:t:w:")) != -1) { switch (c) { case 'a': @@ -288,8 +348,10 @@ int main(int argc, char **argv) case 'c': errno = 0; num_loops = strtoul(optarg, &dummy, 10); - if (errno) - return -errno; + if (errno) { + ret = -errno; + goto error; + } break; case 'e': @@ -301,26 +363,30 @@ int main(int argc, char **argv) case 'l': errno = 0; buf_len = strtoul(optarg, &dummy, 10); - if (errno) - return -errno; + if (errno) { + ret = -errno; + goto error; + } break; case 'n': device_name = optarg; break; case 't': - trigger_name = optarg; - datardytrigger = 0; + trigger_name = strdup(optarg); break; case 'w': errno = 0; timedelay = strtoul(optarg, &dummy, 10); - if (errno) - return -errno; + if (errno) { + ret = -errno; + goto error; + } break; case '?': print_usage(); - return -1; + ret = -1; + goto error; } } @@ -334,14 +400,17 @@ int main(int argc, char **argv) dev_num = find_type_by_name(device_name, "iio:device"); if (dev_num < 0) { fprintf(stderr, "Failed to find the %s\n", device_name); - return dev_num; + ret = dev_num; + goto error; } printf("iio device number being used is %d\n", dev_num); ret = asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); - if (ret < 0) - return -ENOMEM; + if (ret < 0) { + ret = -ENOMEM; + goto error; + } if (!notrigger) { if (!trigger_name) { @@ -354,7 +423,7 @@ int main(int argc, char **argv) "%s-dev%d", device_name, dev_num); if (ret < 0) { ret = -ENOMEM; - goto error_free_dev_dir_name; + goto error; } } @@ -367,7 +436,7 @@ int main(int argc, char **argv) "%s-trigger", device_name); if (ret < 0) { ret = -ENOMEM; - goto error_free_dev_dir_name; + goto error; } } @@ -376,7 +445,7 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to find the trigger %s\n", trigger_name); ret = trig_num; - goto error_free_triggername; + goto error; } printf("iio trigger number being used is %d\n", trig_num); @@ -392,7 +461,7 @@ int main(int argc, char **argv) if (ret) { fprintf(stderr, "Problem reading scan element information\n" "diag %s\n", dev_dir_name); - goto error_free_triggername; + goto error; } if (num_channels && autochannels == AUTOCHANNELS_ENABLED) { fprintf(stderr, "Auto-channels selected but some channels " @@ -407,7 +476,7 @@ int main(int argc, char **argv) ret = enable_disable_all_channels(dev_dir_name, 1); if (ret) { fprintf(stderr, "Failed to enable all channels\n"); - goto error_free_triggername; + goto error; } /* This flags that we need to disable the channels again */ @@ -419,12 +488,12 @@ int main(int argc, char **argv) fprintf(stderr, "Problem reading scan element " "information\n" "diag %s\n", dev_dir_name); - goto error_disable_channels; + goto error; } if (!num_channels) { fprintf(stderr, "Still no channels after " "auto-enabling, giving up\n"); - goto error_disable_channels; + goto error; } } @@ -436,7 +505,7 @@ int main(int argc, char **argv) "/*_en or pass -a to autoenable channels and " "try again.\n", dev_dir_name); ret = -ENOENT; - goto error_free_triggername; + goto error; } /* @@ -448,7 +517,7 @@ int main(int argc, char **argv) "%siio:device%d/buffer", iio_dir, dev_num); if (ret < 0) { ret = -ENOMEM; - goto error_free_channels; + goto error; } if (!notrigger) { @@ -463,34 +532,34 @@ int main(int argc, char **argv) if (ret < 0) { fprintf(stderr, "Failed to write current_trigger file\n"); - goto error_free_buf_dir_name; + goto error; } } /* Setup ring buffer parameters */ ret = write_sysfs_int("length", buf_dir_name, buf_len); if (ret < 0) - goto error_free_buf_dir_name; + goto error; /* Enable the buffer */ ret = write_sysfs_int("enable", buf_dir_name, 1); if (ret < 0) { fprintf(stderr, "Failed to enable buffer: %s\n", strerror(-ret)); - goto error_free_buf_dir_name; + goto error; } scan_size = size_from_channelarray(channels, num_channels); data = malloc(scan_size * buf_len); if (!data) { ret = -ENOMEM; - goto error_free_buf_dir_name; + goto error; } ret = asprintf(&buffer_access, "/dev/iio:device%d", dev_num); if (ret < 0) { ret = -ENOMEM; - goto error_free_data; + goto error; } /* Attempt to open non blocking the access dev */ @@ -498,7 +567,7 @@ int main(int argc, char **argv) if (fp == -1) { /* TODO: If it isn't there make the node */ ret = -errno; fprintf(stderr, "Failed to open %s\n", buffer_access); - goto error_free_buffer_access; + goto error; } for (j = 0; j < num_loops; j++) { @@ -511,7 +580,7 @@ int main(int argc, char **argv) ret = poll(&pfd, 1, -1); if (ret < 0) { ret = -errno; - goto error_close_buffer_access; + goto error; } else if (ret == 0) { continue; } @@ -536,45 +605,20 @@ int main(int argc, char **argv) num_channels); } - /* Stop the buffer */ - ret = write_sysfs_int("enable", buf_dir_name, 0); - if (ret < 0) - goto error_close_buffer_access; +error: + cleanup(); - if (!notrigger) - /* Disconnect the trigger - just write a dummy name. */ - ret = write_sysfs_string("trigger/current_trigger", - dev_dir_name, "NULL"); - if (ret < 0) - fprintf(stderr, "Failed to write to %s\n", - dev_dir_name); - -error_close_buffer_access: - if (close(fp) == -1) + if (fp >= 0 && close(fp) == -1) perror("Failed to close buffer"); - -error_free_buffer_access: free(buffer_access); -error_free_data: free(data); -error_free_buf_dir_name: free(buf_dir_name); -error_free_channels: for (i = num_channels - 1; i >= 0; i--) { free(channels[i].name); free(channels[i].generic_name); } free(channels); -error_free_triggername: - if (datardytrigger) - free(trigger_name); -error_disable_channels: - if (autochannels == AUTOCHANNELS_ACTIVE) { - ret = enable_disable_all_channels(dev_dir_name, 0); - if (ret) - fprintf(stderr, "Failed to disable all channels\n"); - } -error_free_dev_dir_name: + free(trigger_name); free(dev_dir_name); return ret; From de397db8ab9e292ed3b5be42d0892a0ec717330d Mon Sep 17 00:00:00 2001 From: Crestez Dan Leonard Date: Mon, 23 May 2016 21:39:57 +0300 Subject: [PATCH 55/61] iio: iio_generic_buffer: Add --device-num option This makes it possible to distinguish between iio devices with the same name. Signed-off-by: Crestez Dan Leonard Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 69 +++++++++++++++++++++++++--------- 1 file changed, 51 insertions(+), 18 deletions(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 972f40008a49..3f16e9f08d20 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -251,7 +251,9 @@ void print_usage(void) " -e Disable wait for event (new data)\n" " -g Use trigger-less mode\n" " -l Set buffer length to n samples\n" - " -n Set device name (mandatory)\n" + " --device-name -n \n" + " --device-num -N \n" + " Set device by name or number (mandatory)\n" " -t Set trigger name\n" " -w Set delay between reads in us (event-less mode)\n"); } @@ -315,6 +317,12 @@ void register_cleanup(void) } } +static const struct option longopts[] = { + { "device-name", 1, 0, 'n' }, + { "device-num", 1, 0, 'N' }, + { }, +}; + int main(int argc, char **argv) { unsigned long num_loops = 2; @@ -329,7 +337,7 @@ int main(int argc, char **argv) char *data = NULL; ssize_t read_size; - int dev_num, trig_num; + int dev_num = -1, trig_num; char *buffer_access = NULL; int scan_size; int noevents = 0; @@ -340,7 +348,7 @@ int main(int argc, char **argv) register_cleanup(); - while ((c = getopt(argc, argv, "ac:egl:n:t:w:")) != -1) { + while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:w:", longopts, NULL)) != -1) { switch (c) { case 'a': autochannels = AUTOCHANNELS_ENABLED; @@ -370,7 +378,15 @@ int main(int argc, char **argv) break; case 'n': - device_name = optarg; + device_name = strdup(optarg); + break; + case 'N': + errno = 0; + dev_num = strtoul(optarg, &dummy, 10); + if (errno) { + ret = -errno; + goto error; + } break; case 't': trigger_name = strdup(optarg); @@ -390,26 +406,42 @@ int main(int argc, char **argv) } } - if (!device_name) { - fprintf(stderr, "Device name not set\n"); - print_usage(); - return -1; - } - /* Find the device requested */ - dev_num = find_type_by_name(device_name, "iio:device"); - if (dev_num < 0) { - fprintf(stderr, "Failed to find the %s\n", device_name); - ret = dev_num; + if (dev_num < 0 && !device_name) { + fprintf(stderr, "Device not set\n"); + print_usage(); + ret = -1; goto error; + } else if (dev_num >= 0 && device_name) { + fprintf(stderr, "Only one of --device-num or --device-name needs to be set\n"); + print_usage(); + ret = -1; + goto error; + } else if (dev_num < 0) { + dev_num = find_type_by_name(device_name, "iio:device"); + if (dev_num < 0) { + fprintf(stderr, "Failed to find the %s\n", device_name); + ret = dev_num; + goto error; + } } - printf("iio device number being used is %d\n", dev_num); ret = asprintf(&dev_dir_name, "%siio:device%d", iio_dir, dev_num); - if (ret < 0) { - ret = -ENOMEM; - goto error; + if (ret < 0) + return -ENOMEM; + /* Fetch device_name if specified by number */ + if (!device_name) { + device_name = malloc(IIO_MAX_NAME_LENGTH); + if (!device_name) { + ret = -ENOMEM; + goto error; + } + ret = read_sysfs_string("name", dev_dir_name, device_name); + if (ret < 0) { + fprintf(stderr, "Failed to read name of device %d\n", dev_num); + goto error; + } } if (!notrigger) { @@ -619,6 +651,7 @@ error: } free(channels); free(trigger_name); + free(device_name); free(dev_dir_name); return ret; From 7c7e9dad7017ff5b5f0524ea6d85dcda3c62431e Mon Sep 17 00:00:00 2001 From: Crestez Dan Leonard Date: Mon, 23 May 2016 21:39:58 +0300 Subject: [PATCH 56/61] iio: iio_generic_buffer: Add --trigger-num option Signed-off-by: Crestez Dan Leonard Signed-off-by: Jonathan Cameron --- tools/iio/iio_generic_buffer.c | 34 +++++++++++++++++++++++++++++----- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/tools/iio/iio_generic_buffer.c b/tools/iio/iio_generic_buffer.c index 3f16e9f08d20..e8c30521e99c 100644 --- a/tools/iio/iio_generic_buffer.c +++ b/tools/iio/iio_generic_buffer.c @@ -254,7 +254,9 @@ void print_usage(void) " --device-name -n \n" " --device-num -N \n" " Set device by name or number (mandatory)\n" - " -t Set trigger name\n" + " --trigger-name -t \n" + " --trigger-num -T \n" + " Set trigger by name or number\n" " -w Set delay between reads in us (event-less mode)\n"); } @@ -320,6 +322,8 @@ void register_cleanup(void) static const struct option longopts[] = { { "device-name", 1, 0, 'n' }, { "device-num", 1, 0, 'N' }, + { "trigger-name", 1, 0, 't' }, + { "trigger-num", 1, 0, 'T' }, { }, }; @@ -348,7 +352,7 @@ int main(int argc, char **argv) register_cleanup(); - while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:w:", longopts, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "ac:egl:n:N:t:T:w:", longopts, NULL)) != -1) { switch (c) { case 'a': autochannels = AUTOCHANNELS_ENABLED; @@ -391,6 +395,12 @@ int main(int argc, char **argv) case 't': trigger_name = strdup(optarg); break; + case 'T': + errno = 0; + trig_num = strtoul(optarg, &dummy, 10); + if (errno) + return -errno; + break; case 'w': errno = 0; timedelay = strtoul(optarg, &dummy, 10); @@ -444,7 +454,23 @@ int main(int argc, char **argv) } } - if (!notrigger) { + if (notrigger) { + printf("trigger-less mode selected\n"); + } if (trig_num > 0) { + char *trig_dev_name; + ret = asprintf(&trig_dev_name, "%strigger%d", iio_dir, trig_num); + if (ret < 0) { + return -ENOMEM; + } + trigger_name = malloc(IIO_MAX_NAME_LENGTH); + ret = read_sysfs_string("name", trig_dev_name, trigger_name); + free(trig_dev_name); + if (ret < 0) { + fprintf(stderr, "Failed to read trigger%d name from\n", trig_num); + return ret; + } + printf("iio trigger number being used is %d\n", trig_num); + } else { if (!trigger_name) { /* * Build the trigger name. If it is device associated @@ -481,8 +507,6 @@ int main(int argc, char **argv) } printf("iio trigger number being used is %d\n", trig_num); - } else { - printf("trigger-less mode selected\n"); } /* From afa814841c812cd1a36dbbb124542a487c2df5ee Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Wed, 1 Jun 2016 21:28:14 -0700 Subject: [PATCH 57/61] iio: adc: ad7793: claim direct mode when writing frequency Driver was checking for direct mode and trying to lock it, but left a gap where mode could change before the desired operation. Use iio_device_claim_direct_mode() to guarantee device stays in direct mode. Refactor function to clarify look-up followed by lock sequence. Signed-off-by: Alison Schofield Cc: Daniel Baluta Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7793.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 7b07bb651671..a43722fbf03a 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -369,13 +369,6 @@ static ssize_t ad7793_write_frequency(struct device *dev, long lval; int i, ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - mutex_unlock(&indio_dev->mlock); - ret = kstrtol(buf, 10, &lval); if (ret) return ret; @@ -383,20 +376,21 @@ static ssize_t ad7793_write_frequency(struct device *dev, if (lval == 0) return -EINVAL; - ret = -EINVAL; - for (i = 0; i < 16; i++) - if (lval == st->chip_info->sample_freq_avail[i]) { - mutex_lock(&indio_dev->mlock); - st->mode &= ~AD7793_MODE_RATE(-1); - st->mode |= AD7793_MODE_RATE(i); - ad_sd_write_reg(&st->sd, AD7793_REG_MODE, - sizeof(st->mode), st->mode); - mutex_unlock(&indio_dev->mlock); - ret = 0; - } + if (lval == st->chip_info->sample_freq_avail[i]) + break; + if (i == 16) + return -EINVAL; - return ret ? ret : len; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + st->mode &= ~AD7793_MODE_RATE(-1); + st->mode |= AD7793_MODE_RATE(i); + ad_sd_write_reg(&st->sd, AD7793_REG_MODE, sizeof(st->mode), st->mode); + iio_device_release_direct_mode(indio_dev); + + return len; } static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, From d7203ad864db0b34f0e106ec0659890c4e58c143 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Tue, 31 May 2016 21:35:49 -0700 Subject: [PATCH 58/61] iio: adc: ad7791: claim direct mode when writing frequency Driver was checking for direct mode and trying to lock it, but left a gap where mode could change before the desired operation. Use iio_device_claim_direct_mode() to guarantee device stays in direct mode. Refactor function to clarify look-up followed by lock sequence. Signed-off-by: Alison Schofield Cc: Daniel Baluta Acked-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7791.c | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index cf172d58cd44..1dfe6410c64c 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -272,30 +272,22 @@ static ssize_t ad7791_write_frequency(struct device *dev, struct ad7791_state *st = iio_priv(indio_dev); int i, ret; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } - mutex_unlock(&indio_dev->mlock); - - ret = -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) { - if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) { - - mutex_lock(&indio_dev->mlock); - st->filter &= ~AD7791_FILTER_RATE_MASK; - st->filter |= i; - ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, - sizeof(st->filter), st->filter); - mutex_unlock(&indio_dev->mlock); - ret = 0; + for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) + if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) break; - } - } + if (i == ARRAY_SIZE(ad7791_sample_freq_avail)) + return -EINVAL; - return ret ? ret : len; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + st->filter &= ~AD7791_FILTER_RATE_MASK; + st->filter |= i; + ad_sd_write_reg(&st->sd, AD7791_REG_FILTER, sizeof(st->filter), + st->filter); + iio_device_release_direct_mode(indio_dev); + + return len; } static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, From fea89e2dfceaf78d132b12a4aab3db3c04fb5639 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 31 May 2016 12:00:12 -0500 Subject: [PATCH 59/61] iio: adc: ti_am335x_adc: use variable names for sizeof() operator Fix the code formatting to use the kernel preferred style of using the actual variables to determize the size using the sizeof() operator. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti_am335x_adc.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index c1e05532d437..9f406d0af273 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -326,8 +326,7 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels) int i; indio_dev->num_channels = channels; - chan_array = kcalloc(channels, - sizeof(struct iio_chan_spec), GFP_KERNEL); + chan_array = kcalloc(channels, sizeof(*chan_array), GFP_KERNEL); if (chan_array == NULL) return -ENOMEM; @@ -467,8 +466,7 @@ static int tiadc_probe(struct platform_device *pdev) return -EINVAL; } - indio_dev = devm_iio_device_alloc(&pdev->dev, - sizeof(struct tiadc_device)); + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*indio_dev)); if (indio_dev == NULL) { dev_err(&pdev->dev, "failed to allocate iio device\n"); return -ENOMEM; From 27aa832d1882da4b6595abe99e287f46b2d54f45 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Tue, 31 May 2016 12:00:07 -0500 Subject: [PATCH 60/61] iio: adc: ti_am335x_adc: use SIMPLE_DEV_PM_OPS helper macro Replace ifdefs with SIMPLE_DEV_PM_OPS helper macro. Signed-off-by: Andrew F. Davis Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti_am335x_adc.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index 9f406d0af273..8a368756881b 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -529,8 +529,7 @@ static int tiadc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int tiadc_suspend(struct device *dev) +static int __maybe_unused tiadc_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -548,7 +547,7 @@ static int tiadc_suspend(struct device *dev) return 0; } -static int tiadc_resume(struct device *dev) +static int __maybe_unused tiadc_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct tiadc_device *adc_dev = iio_priv(indio_dev); @@ -565,14 +564,7 @@ static int tiadc_resume(struct device *dev) return 0; } -static const struct dev_pm_ops tiadc_pm_ops = { - .suspend = tiadc_suspend, - .resume = tiadc_resume, -}; -#define TIADC_PM_OPS (&tiadc_pm_ops) -#else -#define TIADC_PM_OPS NULL -#endif +static SIMPLE_DEV_PM_OPS(tiadc_pm_ops, tiadc_suspend, tiadc_resume); static const struct of_device_id ti_adc_dt_ids[] = { { .compatible = "ti,am3359-adc", }, @@ -583,7 +575,7 @@ MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); static struct platform_driver tiadc_driver = { .driver = { .name = "TI-am335x-adc", - .pm = TIADC_PM_OPS, + .pm = &tiadc_pm_ops, .of_match_table = ti_adc_dt_ids, }, .probe = tiadc_probe, From bc2e1126eccb47517b9d1c685020c38600f99a3d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 6 Mar 2016 20:02:56 +0000 Subject: [PATCH 61/61] iio:trigger: Experimental kthread tight loop trigger (thread only) This patch is in response to that of Gregor Boirie who proposed using a tight kthread within a device driver (be it with the support factored out into a helper library) in order to basically spin as fast as possible. It is meant as a talking point rather than a formal proposal of the code (though we are heading towards that I think). Also gives people some working code to mess around with. I proposed that this could be done with a trigger with a few constraints and this is the proof (be it ugly) of that. There are some constraints though, some of which we would want to relax if this were to move forward. * Will only run the thread part of the registered pollfunc. This is to avoid the overhead of jumping in and out of interrupt context. Is the overhead significant? Not certain but feels like it should be! * This limitation precludes any device that 'must' do some work in interrupt context. However, that is true of few if any drivers and I suspect that any that do will be restricted to using triggers they provide themselves. Usually we have a top half mainly to grab a timestamp as soon after the dataready type signal as possible. Signed-off-by: Jonathan Cameron Acked-by: Daniel Baluta --- drivers/iio/trigger/Kconfig | 12 +++ drivers/iio/trigger/Makefile | 1 + drivers/iio/trigger/iio-trig-loop.c | 143 ++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 drivers/iio/trigger/iio-trig-loop.c diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig index 519e6772f6f5..809b2e7d58fa 100644 --- a/drivers/iio/trigger/Kconfig +++ b/drivers/iio/trigger/Kconfig @@ -24,6 +24,18 @@ config IIO_INTERRUPT_TRIGGER To compile this driver as a module, choose M here: the module will be called iio-trig-interrupt. +config IIO_TIGHTLOOP_TRIGGER + tristate "A kthread based hammering loop trigger" + depends on IIO_SW_TRIGGER + help + An experimental trigger, used to allow sensors to be sampled as fast + as possible under the limitations of whatever else is going on. + Uses a tight loop in a kthread. Will only work with lower half only + trigger consumers. + + To compile this driver as a module, choose M here: the + module will be called iio-trig-loop. + config IIO_SYSFS_TRIGGER tristate "SYSFS trigger" depends on SYSFS diff --git a/drivers/iio/trigger/Makefile b/drivers/iio/trigger/Makefile index fe06eb564367..aab4dc23303d 100644 --- a/drivers/iio/trigger/Makefile +++ b/drivers/iio/trigger/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_IIO_HRTIMER_TRIGGER) += iio-trig-hrtimer.o obj-$(CONFIG_IIO_INTERRUPT_TRIGGER) += iio-trig-interrupt.o obj-$(CONFIG_IIO_SYSFS_TRIGGER) += iio-trig-sysfs.o +obj-$(CONFIG_IIO_TIGHTLOOP_TRIGGER) += iio-trig-loop.o diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c new file mode 100644 index 000000000000..dc6be28f96fe --- /dev/null +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -0,0 +1,143 @@ +/* + * Copyright 2016 Jonathan Cameron + * + * Licensed under the GPL-2. + * + * Based on a mashup of the hrtimer trigger and continuous sampling proposal of + * Gregor Boirie + * + * Note this is still rather experimental and may eat babies. + * + * Todo + * * Protect against connection of devices that 'need' the top half + * handler. + * * Work out how to run top half handlers in this context if it is + * safe to do so (timestamp grabbing for example) + * + * Tested against a max1363. Used about 33% cpu for the thread and 20% + * for generic_buffer piping to /dev/null. Watermark set at 64 on a 128 + * element kfifo buffer. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct iio_loop_info { + struct iio_sw_trigger swt; + struct task_struct *task; +}; + +static struct config_item_type iio_loop_type = { + .ct_owner = THIS_MODULE, +}; + +static int iio_loop_thread(void *data) +{ + struct iio_trigger *trig = data; + + set_freezable(); + + do { + iio_trigger_poll_chained(trig); + } while (likely(!kthread_freezable_should_stop(NULL))); + + return 0; +} + +static int iio_loop_trigger_set_state(struct iio_trigger *trig, bool state) +{ + struct iio_loop_info *loop_trig = iio_trigger_get_drvdata(trig); + + if (state) { + loop_trig->task = kthread_run(iio_loop_thread, + trig, trig->name); + if (unlikely(IS_ERR(loop_trig->task))) { + dev_err(&trig->dev, + "failed to create trigger loop thread\n"); + return PTR_ERR(loop_trig->task); + } + } else { + kthread_stop(loop_trig->task); + } + + return 0; +} + +static const struct iio_trigger_ops iio_loop_trigger_ops = { + .set_trigger_state = iio_loop_trigger_set_state, + .owner = THIS_MODULE, +}; + +static struct iio_sw_trigger *iio_trig_loop_probe(const char *name) +{ + struct iio_loop_info *trig_info; + int ret; + + trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); + if (!trig_info) + return ERR_PTR(-ENOMEM); + + trig_info->swt.trigger = iio_trigger_alloc("%s", name); + if (!trig_info->swt.trigger) { + ret = -ENOMEM; + goto err_free_trig_info; + } + + iio_trigger_set_drvdata(trig_info->swt.trigger, trig_info); + trig_info->swt.trigger->ops = &iio_loop_trigger_ops; + + ret = iio_trigger_register(trig_info->swt.trigger); + if (ret) + goto err_free_trigger; + + iio_swt_group_init_type_name(&trig_info->swt, name, &iio_loop_type); + + return &trig_info->swt; + +err_free_trigger: + iio_trigger_free(trig_info->swt.trigger); +err_free_trig_info: + kfree(trig_info); + + return ERR_PTR(ret); +} + +static int iio_trig_loop_remove(struct iio_sw_trigger *swt) +{ + struct iio_loop_info *trig_info; + + trig_info = iio_trigger_get_drvdata(swt->trigger); + + iio_trigger_unregister(swt->trigger); + iio_trigger_free(swt->trigger); + kfree(trig_info); + + return 0; +} + +static const struct iio_sw_trigger_ops iio_trig_loop_ops = { + .probe = iio_trig_loop_probe, + .remove = iio_trig_loop_remove, +}; + +static struct iio_sw_trigger_type iio_trig_loop = { + .name = "loop", + .owner = THIS_MODULE, + .ops = &iio_trig_loop_ops, +}; + +module_iio_sw_trigger_driver(iio_trig_loop); + +MODULE_AUTHOR("Jonathan Cameron "); +MODULE_DESCRIPTION("Loop based trigger for the iio subsystem"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:iio-trig-loop");