diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-08-23 23:50:16 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-08-23 23:50:16 +0200 |
commit | d5bd1eba19230347acfc4960581a15bfb459a615 (patch) | |
tree | 8fc05e23b81792591d99550a93f463790e897e42 /drivers | |
parent | staging: most: aim-cdev: destroy ida struct in case of exception (diff) | |
parent | iio: accel: kxsd9: Add device tree bindings (diff) | |
download | linux-d5bd1eba19230347acfc4960581a15bfb459a615.tar.xz linux-d5bd1eba19230347acfc4960581a15bfb459a615.zip |
Merge tag 'iio-for-4.9a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into work-testing
Jonathan writes:
First round of new features, device support and cleanups for IIO in the 4.9 cycle.
Device support
* ak8974
- New driver and bindings for this 2009 vintage magnetometer (it was very
popular back then!)
* atlas-ph-sensor
- ORP sensor support(I had to look up what one of these was)
* cio-dac
- New driver for Measurement Computing DAC boards
* dmard06
- New driver for Domintech DMARDO6 accelerometer. Also vendor prefix.
* dmard09
- New driver for Domintech DMARD09 accelerometer.
* maxim-thermocouple
- max6675 and max31855 new driver
* mt6577 auxdac
- new driver for this Mediatek chip mt2701, mt6577 and mt8173 have this
hardware.
* ti-adc161s626
- new driver for this TI single channel differential ADC.
* vcnl4000
- support vcnl4010 and vcnl4020 which are compatible for all features
currently supported by this driver.
New features
* Core
- Allow retrieving of underlying iio_dev from a callback buffer handle.
This is needed to allow client drivers to perform operations such as
configuring the trigger used.
* hid-sensors
- asynchronous resume support to avoid really long resume times.
* kxcjk-1013
- add the mysterious KIOX000A ACPI id seen in the wild.
* Tools
- lsiio now enumerates processed as well as raw channels.
Cleanup
* ad7298
- use iio_device_claim_direct_mode and friends to simplify locking around
mode switching and drop some boilerplate.
* ad7793
- use iio_device_claim_direct_mode and friends to simplify locking around
mode switching and drop some boilerplate.
* ade7854
- checkpatch fixups (alignment of parameters)
* atlas-ph-sensor
- use iio_device_claim_direct_mode and friends to simplify locking around
mode switching and drop some boilerplate.
- Switch to REGCACHE_NONE as there are no useful register to cache.
* bma180
- use iio_device_claim_direct_mode and friends to simplify locking around
mode switching and drop some boilerplate.
* hdc100x
- Add mention of the HDC1000 and HDC1008 to the Kconfig help text.
* isl29018
- Add driver specific prefixes to defines and function names.
- Remove excessive logging.
- Drop newlines which add nothing to readability.
- General tidying up of comments.
- Drop I2C_CLASS_HWMON as irrelevant to driver.
* isl29028
- Add driver specific prefixes to defines, enums and function names.
- Drop comma's from available attribute output as not ABI compliant.
- Drop I2C_CLASS_HWMON as irrelevant to driver.
* kxsd9
- devicetree bindings.
* mag3110
- This one wasn't locking to protect against mode switches during
raw_reads. Use the iio_claim_direct_mode function to fix this buglet.
* maxim-theromcouple
- Fix missing selects for triggered buffer support in Kconfig.
* nau7802
- Use complete instead of complete_all as only one completion at a time.
* sx9500
- Use complete instead of complete_all as only one completion at a time.
* us5182d
- Add a missing error code asignment instead of checking the result of
an already checked statement.
* vcnl4000
- Use BIT macro where appropriate.
- Refactor return codes in read_raw callback.
- Add some missing locking for concurrent accesses to the device.
Diffstat (limited to 'drivers')
35 files changed, 2628 insertions, 251 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 89d78208de3f..5630f2316c58 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -50,6 +50,27 @@ config BMC150_ACCEL_SPI tristate select REGMAP_SPI +config DMARD06 + tristate "Domintech DMARD06 Digital Accelerometer Driver" + depends on OF || COMPILE_TEST + depends on I2C + help + Say yes here to build support for the Domintech low-g tri-axial + digital accelerometers: DMARD05, DMARD06, DMARD07. + + To compile this driver as a module, choose M here: the + module will be called dmard06. + +config DMARD09 + tristate "Domintech DMARD09 3-axis Accelerometer Driver" + depends on I2C + help + Say yes here to get support for the Domintech DMARD09 3-axis + accelerometer. + + Choosing M will build the driver as a module. If so, the module + will be called dmard09. + config HID_SENSOR_ACCEL_3D depends on HID_SENSOR_HUB select IIO_BUFFER diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 6cedbecca2ee..e974841ec9cf 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -8,6 +8,8 @@ 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 +obj-$(CONFIG_DMARD06) += dmard06.o +obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index e3f88ba5faf3..0890934ef66f 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + mutex_lock(&data->mutex); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&data->mutex); - return -EBUSY; - } ret = bma180_get_data_reg(data, chan->scan_index); mutex_unlock(&data->mutex); + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; *val = sign_extend32(ret >> chan->scan_type.shift, diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c new file mode 100644 index 000000000000..656ca8e1927f --- /dev/null +++ b/drivers/iio/accel/dmard06.c @@ -0,0 +1,241 @@ +/* + * IIO driver for Domintech DMARD06 accelerometer + * + * Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> + +#define DMARD06_DRV_NAME "dmard06" + +/* Device data registers */ +#define DMARD06_CHIP_ID_REG 0x0f +#define DMARD06_TOUT_REG 0x40 +#define DMARD06_XOUT_REG 0x41 +#define DMARD06_YOUT_REG 0x42 +#define DMARD06_ZOUT_REG 0x43 +#define DMARD06_CTRL1_REG 0x44 + +/* Device ID value */ +#define DMARD05_CHIP_ID 0x05 +#define DMARD06_CHIP_ID 0x06 +#define DMARD07_CHIP_ID 0x07 + +/* Device values */ +#define DMARD05_AXIS_SCALE_VAL 15625 +#define DMARD06_AXIS_SCALE_VAL 31250 +#define DMARD06_TEMP_CENTER_VAL 25 +#define DMARD06_SIGN_BIT 7 + +/* Device power modes */ +#define DMARD06_MODE_NORMAL 0x27 +#define DMARD06_MODE_POWERDOWN 0x00 + +/* Device channels */ +#define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \ + .type = IIO_ACCEL, \ + .address = _reg, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .modified = 1, \ +} + +#define DMARD06_TEMP_CHANNEL(_reg) { \ + .type = IIO_TEMP, \ + .address = _reg, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +} + +struct dmard06_data { + struct i2c_client *client; + u8 chip_id; +}; + +static const struct iio_chan_spec dmard06_channels[] = { + DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG), + DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG), + DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG), + DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG), +}; + +static int dmard06_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_byte_data(dmard06->client, + chan->address); + if (ret < 0) { + dev_err(&dmard06->client->dev, + "Error reading data: %d\n", ret); + return ret; + } + + *val = sign_extend32(ret, DMARD06_SIGN_BIT); + + if (dmard06->chip_id == DMARD06_CHIP_ID) + *val = *val >> 1; + + switch (chan->type) { + case IIO_ACCEL: + return IIO_VAL_INT; + case IIO_TEMP: + if (dmard06->chip_id != DMARD06_CHIP_ID) + *val = *val / 2; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + *val = DMARD06_TEMP_CENTER_VAL; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_ACCEL: + *val = 0; + if (dmard06->chip_id == DMARD06_CHIP_ID) + *val2 = DMARD06_AXIS_SCALE_VAL; + else + *val2 = DMARD05_AXIS_SCALE_VAL; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static const struct iio_info dmard06_info = { + .driver_module = THIS_MODULE, + .read_raw = dmard06_read_raw, +}; + +static int dmard06_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct dmard06_data *dmard06; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "I2C check functionality failed\n"); + return -ENXIO; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06)); + if (!indio_dev) { + dev_err(&client->dev, "Failed to allocate iio device\n"); + return -ENOMEM; + } + + dmard06 = iio_priv(indio_dev); + dmard06->client = client; + + ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG); + if (ret < 0) { + dev_err(&client->dev, "Error reading chip id: %d\n", ret); + return ret; + } + + if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID && + ret != DMARD07_CHIP_ID) { + dev_err(&client->dev, "Invalid chip id: %02d\n", ret); + return -ENODEV; + } + + dmard06->chip_id = ret; + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = DMARD06_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dmard06_channels; + indio_dev->num_channels = ARRAY_SIZE(dmard06_channels); + indio_dev->info = &dmard06_info; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +#ifdef CONFIG_PM_SLEEP +static int dmard06_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, + DMARD06_MODE_POWERDOWN); + if (ret < 0) + return ret; + + return 0; +} + +static int dmard06_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct dmard06_data *dmard06 = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG, + DMARD06_MODE_NORMAL); + if (ret < 0) + return ret; + + return 0; +} + +static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume); +#define DMARD06_PM_OPS (&dmard06_pm_ops) +#else +#define DMARD06_PM_OPS NULL +#endif + +static const struct i2c_device_id dmard06_id[] = { + { "dmard05", 0 }, + { "dmard06", 0 }, + { "dmard07", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, dmard06_id); + +static const struct of_device_id dmard06_of_match[] = { + { .compatible = "domintech,dmard05" }, + { .compatible = "domintech,dmard06" }, + { .compatible = "domintech,dmard07" }, + { } +}; +MODULE_DEVICE_TABLE(of, dmard06_of_match); + +static struct i2c_driver dmard06_driver = { + .probe = dmard06_probe, + .id_table = dmard06_id, + .driver = { + .name = DMARD06_DRV_NAME, + .of_match_table = of_match_ptr(dmard06_of_match), + .pm = DMARD06_PM_OPS, + }, +}; +module_i2c_driver(dmard06_driver); + +MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>"); +MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c new file mode 100644 index 000000000000..d3a28f96565c --- /dev/null +++ b/drivers/iio/accel/dmard09.c @@ -0,0 +1,157 @@ +/* + * IIO driver for the 3-axis accelerometer Domintech DMARD09. + * + * Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include <asm/unaligned.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> + +#define DMARD09_DRV_NAME "dmard09" + +#define DMARD09_REG_CHIPID 0x18 +#define DMARD09_REG_STAT 0x0A +#define DMARD09_REG_X 0x0C +#define DMARD09_REG_Y 0x0E +#define DMARD09_REG_Z 0x10 +#define DMARD09_CHIPID 0x95 + +#define DMARD09_BUF_LEN 8 +#define DMARD09_AXIS_X 0 +#define DMARD09_AXIS_Y 1 +#define DMARD09_AXIS_Z 2 +#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2) +#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2) +#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2) + +struct dmard09_data { + struct i2c_client *client; +}; + +#define DMARD09_CHANNEL(_axis, offset) { \ + .type = IIO_ACCEL, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .modified = 1, \ + .address = offset, \ + .channel2 = IIO_MOD_##_axis, \ +} + +static const struct iio_chan_spec dmard09_channels[] = { + DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET), + DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET), + DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET), +}; + +static int dmard09_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct dmard09_data *data = iio_priv(indio_dev); + u8 buf[DMARD09_BUF_LEN]; + int ret; + s16 accel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + /* + * Read from the DMAR09_REG_STAT register, since the chip + * caches reads from the individual X, Y, Z registers. + */ + ret = i2c_smbus_read_i2c_block_data(data->client, + DMARD09_REG_STAT, + DMARD09_BUF_LEN, buf); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg %d\n", + DMARD09_REG_STAT); + return ret; + } + + accel = get_unaligned_le16(&buf[chan->address]); + + /* Remove lower 3 bits and sign extend */ + accel <<= 4; + accel >>= 7; + + *val = accel; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info dmard09_info = { + .driver_module = THIS_MODULE, + .read_raw = dmard09_read_raw, +}; + +static int dmard09_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct iio_dev *indio_dev; + struct dmard09_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; + + ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID); + if (ret < 0) { + dev_err(&client->dev, "Error reading chip id %d\n", ret); + return ret; + } + + if (ret != DMARD09_CHIPID) { + dev_err(&client->dev, "Invalid chip id %d\n", ret); + return -ENODEV; + } + + i2c_set_clientdata(client, indio_dev); + indio_dev->dev.parent = &client->dev; + indio_dev->name = DMARD09_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = dmard09_channels; + indio_dev->num_channels = ARRAY_SIZE(dmard09_channels); + indio_dev->info = &dmard09_info; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id dmard09_id[] = { + { "dmard09", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, dmard09_id); + +static struct i2c_driver dmard09_driver = { + .driver = { + .name = DMARD09_DRV_NAME + }, + .probe = dmard09_probe, + .id_table = dmard09_id, +}; + +module_i2c_driver(dmard09_driver); + +MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>"); +MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 765a72362dc6..3f968c46e667 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = { {"KXCJ1013", KXCJK1013}, {"KXCJ1008", KXCJ91008}, {"KXCJ9000", KXCJ91008}, + {"KIOX000A", KXCJ91008}, {"KXTJ1009", KXTJ21009}, {"SMO8500", KXCJ91008}, { }, diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 1de31bdd4ce4..e4022fd89393 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -317,6 +317,19 @@ config MCP3422 This driver can also be built as a module. If so, the module will be called mcp3422. +config MEDIATEK_MT6577_AUXADC + tristate "MediaTek AUXADC driver" + depends on ARCH_MEDIATEK || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to enable support for MediaTek mt65xx AUXADC. + + The driver supports immediate mode operation to read from one of sixteen + channels (external or internal). + + This driver can also be built as a module. If so, the module will be + called mt6577_auxadc. + config MEN_Z188_ADC tristate "MEN 16z188 ADC IP Core support" depends on MCB @@ -426,6 +439,18 @@ config TI_ADC128S052 This driver can also be built as a module. If so, the module will be called ti-adc128s052. +config TI_ADC161S626 + tristate "Texas Instruments ADC161S626 1-channel differential ADC" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for Texas Instruments ADC141S626, + and ADC161S626 chips. + + This driver can also be built as a module. If so, the module will be + called ti-adc161s626. + config TI_ADS1015 tristate "Texas Instruments ADS1015 ADC" depends on I2C && !SENSORS_ADS1015 diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 0ba0d500eedb..33254eb96bec 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_MAX1027) += max1027.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o obj-$(CONFIG_NAU7802) += nau7802.o @@ -41,6 +42,7 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o +obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 10ec8fce395f..e399bf04c73a 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev, switch (m) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); - if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) { - ret = -EBUSY; - } else { - if (chan->address == AD7298_CH_TEMP) - ret = ad7298_scan_temp(st, val); - else - ret = ad7298_scan_direct(st, chan->address); - } - mutex_unlock(&indio_dev->mlock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + if (chan->address == AD7298_CH_TEMP) + ret = ad7298_scan_temp(st, val); + else + ret = ad7298_scan_direct(st, chan->address); + + iio_device_release_direct_mode(indio_dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 847789bae821..e6706a09e100 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev, int ret, i; unsigned int tmp; - mutex_lock(&indio_dev->mlock); - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&indio_dev->mlock); - return -EBUSY; - } + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev, ret = -EINVAL; } - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); return ret; } diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c new file mode 100644 index 000000000000..2d104c828041 --- /dev/null +++ b/drivers/iio/adc/mt6577_auxadc.c @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2016 MediaTek Inc. + * Author: Zhiyong Tao <zhiyong.tao@mediatek.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/iopoll.h> +#include <linux/io.h> +#include <linux/iio/iio.h> + +/* Register definitions */ +#define MT6577_AUXADC_CON0 0x00 +#define MT6577_AUXADC_CON1 0x04 +#define MT6577_AUXADC_CON2 0x10 +#define MT6577_AUXADC_STA BIT(0) + +#define MT6577_AUXADC_DAT0 0x14 +#define MT6577_AUXADC_RDY0 BIT(12) + +#define MT6577_AUXADC_MISC 0x94 +#define MT6577_AUXADC_PDN_EN BIT(14) + +#define MT6577_AUXADC_DAT_MASK 0xfff +#define MT6577_AUXADC_SLEEP_US 1000 +#define MT6577_AUXADC_TIMEOUT_US 10000 +#define MT6577_AUXADC_POWER_READY_MS 1 +#define MT6577_AUXADC_SAMPLE_READY_US 25 + +struct mt6577_auxadc_device { + void __iomem *reg_base; + struct clk *adc_clk; + struct mutex lock; +}; + +#define MT6577_AUXADC_CHANNEL(idx) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \ +} + +static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = { + MT6577_AUXADC_CHANNEL(0), + MT6577_AUXADC_CHANNEL(1), + MT6577_AUXADC_CHANNEL(2), + MT6577_AUXADC_CHANNEL(3), + MT6577_AUXADC_CHANNEL(4), + MT6577_AUXADC_CHANNEL(5), + MT6577_AUXADC_CHANNEL(6), + MT6577_AUXADC_CHANNEL(7), + MT6577_AUXADC_CHANNEL(8), + MT6577_AUXADC_CHANNEL(9), + MT6577_AUXADC_CHANNEL(10), + MT6577_AUXADC_CHANNEL(11), + MT6577_AUXADC_CHANNEL(12), + MT6577_AUXADC_CHANNEL(13), + MT6577_AUXADC_CHANNEL(14), + MT6577_AUXADC_CHANNEL(15), +}; + +static inline void mt6577_auxadc_mod_reg(void __iomem *reg, + u32 or_mask, u32 and_mask) +{ + u32 val; + + val = readl(reg); + val |= or_mask; + val &= ~and_mask; + writel(val, reg); +} + +static int mt6577_auxadc_read(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan) +{ + u32 val; + void __iomem *reg_channel; + int ret; + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 + + chan->channel * 0x04; + + mutex_lock(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 0, 1 << chan->channel); + + /* read channel and make sure old ready bit == 0 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] ready bit clear time out\n", + chan->channel); + goto err_timeout; + } + + /* set bit to trigger sample */ + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1, + 1 << chan->channel, 0); + + /* we must delay here for hardware sample channel data */ + udelay(MT6577_AUXADC_SAMPLE_READY_US); + + /* check MTK_AUXADC_CON2 if auxadc is idle */ + ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val, + ((val & MT6577_AUXADC_STA) == 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for auxadc idle time out\n"); + goto err_timeout; + } + + /* read channel and make sure ready bit == 1 */ + ret = readl_poll_timeout(reg_channel, val, + ((val & MT6577_AUXADC_RDY0) != 0), + MT6577_AUXADC_SLEEP_US, + MT6577_AUXADC_TIMEOUT_US); + if (ret < 0) { + dev_err(indio_dev->dev.parent, + "wait for channel[%d] data ready time out\n", + chan->channel); + goto err_timeout; + } + + /* read data */ + val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK; + + mutex_unlock(&adc_dev->lock); + + return val; + +err_timeout: + + mutex_unlock(&adc_dev->lock); + + return -ETIMEDOUT; +} + +static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_PROCESSED: + *val = mt6577_auxadc_read(indio_dev, chan); + if (*val < 0) { + dev_err(indio_dev->dev.parent, + "failed to sample data on channel[%d]\n", + chan->channel); + return *val; + } + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static const struct iio_info mt6577_auxadc_info = { + .driver_module = THIS_MODULE, + .read_raw = &mt6577_auxadc_read_raw, +}; + +static int mt6577_auxadc_probe(struct platform_device *pdev) +{ + struct mt6577_auxadc_device *adc_dev; + unsigned long adc_clk_rate; + struct resource *res; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev)); + if (!indio_dev) + return -ENOMEM; + + adc_dev = iio_priv(indio_dev); + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = dev_name(&pdev->dev); + indio_dev->info = &mt6577_auxadc_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mt6577_auxadc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(adc_dev->reg_base)) { + dev_err(&pdev->dev, "failed to get auxadc base address\n"); + return PTR_ERR(adc_dev->reg_base); + } + + adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main"); + if (IS_ERR(adc_dev->adc_clk)) { + dev_err(&pdev->dev, "failed to get auxadc clock\n"); + return PTR_ERR(adc_dev->adc_clk); + } + + ret = clk_prepare_enable(adc_dev->adc_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable auxadc clock\n"); + return ret; + } + + adc_clk_rate = clk_get_rate(adc_dev->adc_clk); + if (!adc_clk_rate) { + ret = -EINVAL; + dev_err(&pdev->dev, "null clock rate\n"); + goto err_disable_clk; + } + + mutex_init(&adc_dev->lock); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + MT6577_AUXADC_PDN_EN, 0); + mdelay(MT6577_AUXADC_POWER_READY_MS); + + platform_set_drvdata(pdev, indio_dev); + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "failed to register iio device\n"); + goto err_power_off; + } + + return 0; + +err_power_off: + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); +err_disable_clk: + clk_disable_unprepare(adc_dev->adc_clk); + return ret; +} + +static int mt6577_auxadc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC, + 0, MT6577_AUXADC_PDN_EN); + + clk_disable_unprepare(adc_dev->adc_clk); + + return 0; +} + +static const struct of_device_id mt6577_auxadc_of_match[] = { + { .compatible = "mediatek,mt2701-auxadc", }, + { .compatible = "mediatek,mt8173-auxadc", }, + { } +}; +MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match); + +static struct platform_driver mt6577_auxadc_driver = { + .driver = { + .name = "mt6577-auxadc", + .of_match_table = mt6577_auxadc_of_match, + }, + .probe = mt6577_auxadc_probe, + .remove = mt6577_auxadc_remove, +}; +module_platform_driver(mt6577_auxadc_driver); + +MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>"); +MODULE_DESCRIPTION("MTK AUXADC Device Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index db9b829ccf0d..08f446695f97 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private) if (st->conversion_count < NAU7802_MIN_CONVERSIONS) st->conversion_count++; if (st->conversion_count >= NAU7802_MIN_CONVERSIONS) - complete_all(&st->value_ok); + complete(&st->value_ok); return IRQ_HANDLED; } diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c new file mode 100644 index 000000000000..f94b69f9c288 --- /dev/null +++ b/drivers/iio/adc/ti-adc161s626.c @@ -0,0 +1,248 @@ +/* + * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC + * + * ADC Devices Supported: + * adc141s626 - 14-bit ADC + * adc161s626 - 16-bit ADC + * + * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define TI_ADC_DRV_NAME "ti-adc161s626" + +enum { + TI_ADC141S626, + TI_ADC161S626, +}; + +static const struct iio_chan_spec ti_adc141s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static const struct iio_chan_spec ti_adc161s626_channels[] = { + { + .type = IIO_VOLTAGE, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +struct ti_adc_data { + struct iio_dev *indio_dev; + struct spi_device *spi; + u8 read_size; + u8 shift; + + u8 buffer[16] ____cacheline_aligned; +}; + +static int ti_adc_read_measurement(struct ti_adc_data *data, + struct iio_chan_spec const *chan, int *val) +{ + int ret; + + switch (data->read_size) { + case 2: { + __be16 buf; + + ret = spi_read(data->spi, (void *) &buf, 2); + if (ret) + return ret; + + *val = be16_to_cpu(buf); + break; + } + case 3: { + __be32 buf; + + ret = spi_read(data->spi, (void *) &buf, 3); + if (ret) + return ret; + + *val = be32_to_cpu(buf) >> 8; + break; + } + default: + return -EINVAL; + } + + *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t ti_adc_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + ret = ti_adc_read_measurement(data, &indio_dev->channels[0], + (int *) &data->buffer); + if (!ret) + iio_push_to_buffers_with_timestamp(indio_dev, + data->buffer, + iio_get_time_ns(indio_dev)); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ti_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ti_adc_data *data = iio_priv(indio_dev); + int ret; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ti_adc_read_measurement(data, chan, val); + iio_device_release_direct_mode(indio_dev); + + if (!ret) + return IIO_VAL_INT; + + return 0; +} + +static const struct iio_info ti_adc_info = { + .driver_module = THIS_MODULE, + .read_raw = ti_adc_read_raw, +}; + +static int ti_adc_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ti_adc_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &ti_adc_info; + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = TI_ADC_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + spi_set_drvdata(spi, indio_dev); + + data = iio_priv(indio_dev); + data->spi = spi; + + switch (spi_get_device_id(spi)->driver_data) { + case TI_ADC141S626: + indio_dev->channels = ti_adc141s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels); + data->shift = 0; + data->read_size = 2; + break; + case TI_ADC161S626: + indio_dev->channels = ti_adc161s626_channels; + indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels); + data->shift = 6; + data->read_size = 3; + break; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int ti_adc_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 0; +} + +static const struct of_device_id ti_adc_dt_ids[] = { + { .compatible = "ti,adc141s626", }, + { .compatible = "ti,adc161s626", }, + {} +}; +MODULE_DEVICE_TABLE(of, ti_adc_dt_ids); + +static const struct spi_device_id ti_adc_id[] = { + {"adc141s626", TI_ADC141S626}, + {"adc161s626", TI_ADC161S626}, + {}, +}; +MODULE_DEVICE_TABLE(spi, ti_adc_id); + +static struct spi_driver ti_adc_driver = { + .driver = { + .name = TI_ADC_DRV_NAME, + .of_match_table = of_match_ptr(ti_adc_dt_ids), + }, + .probe = ti_adc_probe, + .remove = ti_adc_remove, + .id_table = ti_adc_id, +}; +module_spi_driver(ti_adc_driver); + +MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); +MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c index 323079c3ccce..b8f550e47d3d 100644 --- a/drivers/iio/buffer/industrialio-buffer-cb.c +++ b/drivers/iio/buffer/industrialio-buffer-cb.c @@ -18,6 +18,7 @@ struct iio_cb_buffer { int (*cb)(const void *data, void *private); void *private; struct iio_channel *channels; + struct iio_dev *indio_dev; }; static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer) @@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, { int ret; struct iio_cb_buffer *cb_buff; - struct iio_dev *indio_dev; struct iio_channel *chan; cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); @@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, goto error_free_cb_buff; } - indio_dev = cb_buff->channels[0].indio_dev; + cb_buff->indio_dev = cb_buff->channels[0].indio_dev; cb_buff->buffer.scan_mask - = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long), - GFP_KERNEL); + = kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength), + sizeof(long), GFP_KERNEL); if (cb_buff->buffer.scan_mask == NULL) { ret = -ENOMEM; goto error_release_channels; } chan = &cb_buff->channels[0]; while (chan->indio_dev) { - if (chan->indio_dev != indio_dev) { + if (chan->indio_dev != cb_buff->indio_dev) { ret = -EINVAL; goto error_free_scan_mask; } @@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff) { - return iio_update_buffers(cb_buff->channels[0].indio_dev, - &cb_buff->buffer, + return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer, NULL); } EXPORT_SYMBOL_GPL(iio_channel_start_all_cb); void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff) { - iio_update_buffers(cb_buff->channels[0].indio_dev, - NULL, - &cb_buff->buffer); + iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer); } EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb); @@ -133,6 +130,13 @@ struct iio_channel } EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels); +struct iio_dev +*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer) +{ + return cb_buffer->indio_dev; +} +EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev); + MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); MODULE_DESCRIPTION("Industrial I/O callback buffer"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 4bcc025e8c8a..cea7f9857a1f 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR Atlas Scientific OEM SM sensors: * pH SM sensor * EC SM sensor + * ORP 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 ae038a59d256..84fbff32b96d 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -66,12 +66,17 @@ #define ATLAS_REG_TDS_DATA 0x1c #define ATLAS_REG_PSS_DATA 0x20 +#define ATLAS_REG_ORP_CALIB_STATUS 0x0d +#define ATLAS_REG_ORP_DATA 0x0e + #define ATLAS_PH_INT_TIME_IN_US 450000 #define ATLAS_EC_INT_TIME_IN_US 650000 +#define ATLAS_ORP_INT_TIME_IN_US 450000 enum { ATLAS_PH_SM, ATLAS_EC_SM, + ATLAS_ORP_SM, }; struct atlas_data { @@ -84,26 +89,10 @@ struct atlas_data { __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 = { - .yes_ranges = atlas_volatile_ranges, - .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges), -}; - static const struct regmap_config atlas_regmap_config = { .name = ATLAS_REGMAP_NAME, - .reg_bits = 8, .val_bits = 8, - - .volatile_table = &atlas_volatile_table, - .max_register = ATLAS_REG_PSS_DATA + 4, - .cache_type = REGCACHE_RBTREE, }; static const struct iio_chan_spec atlas_ph_channels[] = { @@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = { }, }; +static const struct iio_chan_spec atlas_orp_channels[] = { + { + .type = IIO_VOLTAGE, + .address = ATLAS_REG_ORP_DATA, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 32, + .storagebits = 32, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + static int atlas_check_ph_calibration(struct atlas_data *data) { struct device *dev = &data->client->dev; @@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data) return 0; } +static int atlas_check_orp_calibration(struct atlas_data *data) +{ + struct device *dev = &data->client->dev; + int ret; + unsigned int val; + + ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val); + if (ret) + return ret; + + if (!val) + dev_warn(dev, "device has not been calibrated\n"); + + return 0; +}; + struct atlas_device { const struct iio_chan_spec *channels; int num_channels; @@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = { .calibration = &atlas_check_ec_calibration, .delay = ATLAS_EC_INT_TIME_IN_US, }, - + [ATLAS_ORP_SM] = { + .channels = atlas_orp_channels, + .num_channels = 2, + .data_reg = ATLAS_REG_ORP_DATA, + .calibration = &atlas_check_orp_calibration, + .delay = ATLAS_ORP_INT_TIME_IN_US, + }, }; static int atlas_set_powermode(struct atlas_data *data, int on) @@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev, case IIO_PH: case IIO_CONCENTRATION: case IIO_ELECTRICALCONDUCTIVITY: - mutex_lock(&indio_dev->mlock); + case IIO_VOLTAGE: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - if (iio_buffer_enabled(indio_dev)) - ret = -EBUSY; - else - ret = atlas_read_measurement(data, - chan->address, ®); + ret = atlas_read_measurement(data, chan->address, ®); - mutex_unlock(&indio_dev->mlock); + iio_device_release_direct_mode(indio_dev); break; default: ret = -EINVAL; @@ -440,6 +467,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev, *val = 0; /* 0.000000001 */ *val2 = 1000; return IIO_VAL_INT_PLUS_NANO; + case IIO_VOLTAGE: + *val = 1; /* 0.1 */ + *val2 = 10; + break; default: return -EINVAL; } @@ -475,6 +506,7 @@ 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}, + { "atlas-orp-sm", ATLAS_ORP_SM}, {} }; MODULE_DEVICE_TABLE(i2c, atlas_id); @@ -482,6 +514,7 @@ 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, }, + { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, }, { } }; MODULE_DEVICE_TABLE(of, atlas_dt_ids); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 5b41f9d0d4f3..5264ed6e03e5 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) #endif } +static void hid_sensor_set_power_work(struct work_struct *work) +{ + struct hid_sensor_common *attrb = container_of(work, + struct hid_sensor_common, + work); + _hid_sensor_power_state(attrb, true); +} + static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, bool state) { @@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, void hid_sensor_remove_trigger(struct hid_sensor_common *attrb) { + cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); iio_trigger_free(attrb->trigger); } @@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, goto error_unreg_trigger; iio_device_set_drvdata(indio_dev, attrb); + + INIT_WORK(&attrb->work, hid_sensor_set_power_work); + pm_suspend_ignore_children(&attrb->pdev->dev, true); pm_runtime_enable(&attrb->pdev->dev); /* Default to 3 seconds, but can be changed from sysfs */ @@ -202,7 +214,15 @@ static int hid_sensor_resume(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); + schedule_work(&attrb->work); + return 0; +} +static int hid_sensor_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev); return _hid_sensor_power_state(attrb, true); } @@ -211,7 +231,7 @@ static int hid_sensor_resume(struct device *dev) const struct dev_pm_ops hid_sensor_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume) SET_RUNTIME_PM_OPS(hid_sensor_suspend, - hid_sensor_resume, NULL) + hid_sensor_runtime_resume, NULL) }; EXPORT_SYMBOL(hid_sensor_pm_ops); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index ca814479fadf..b9f044249194 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -181,6 +181,15 @@ config AD7303 To compile this driver as module choose M here: the module will be called ad7303. +config CIO_DAC + tristate "Measurement Computing CIO-DAC IIO driver" + depends on X86 && ISA_BUS_API + help + Say yes here to build support for the Measurement Computing CIO-DAC + analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The + base port addresses for the devices may be configured via the base + array module parameter. + config LPC18XX_DAC tristate "NXP LPC18xx DAC driver" depends on ARCH_LPC18XX || COMPILE_TEST diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 8b78d5ca9b11..b1a120612462 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_AD5764) += ad5764.o obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD7303) += ad7303.o +obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o obj-$(CONFIG_M62332) += m62332.o obj-$(CONFIG_MAX517) += max517.o diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c new file mode 100644 index 000000000000..5a743e2a779d --- /dev/null +++ b/drivers/iio/dac/cio-dac.c @@ -0,0 +1,144 @@ +/* + * IIO driver for the Measurement Computing CIO-DAC + * Copyright (C) 2016 William Breathitt Gray + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * This driver supports the following Measurement Computing devices: CIO-DAC16, + * CIO-DAC06, and PC104-DAC06. + */ +#include <linux/bitops.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/isa.h> +#include <linux/module.h> +#include <linux/moduleparam.h> + +#define CIO_DAC_NUM_CHAN 16 + +#define CIO_DAC_CHAN(chan) { \ + .type = IIO_VOLTAGE, \ + .channel = chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .indexed = 1, \ + .output = 1 \ +} + +#define CIO_DAC_EXTENT 32 + +static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)]; +static unsigned int num_cio_dac; +module_param_array(base, uint, &num_cio_dac, 0); +MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses"); + +/** + * struct cio_dac_iio - IIO device private data structure + * @chan_out_states: channels' output states + * @base: base port address of the IIO device + */ +struct cio_dac_iio { + int chan_out_states[CIO_DAC_NUM_CHAN]; + unsigned int base; +}; + +static int cio_dac_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, int *val2, long mask) +{ + struct cio_dac_iio *const priv = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + *val = priv->chan_out_states[chan->channel]; + + return IIO_VAL_INT; +} + +static int cio_dac_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, int val2, long mask) +{ + struct cio_dac_iio *const priv = iio_priv(indio_dev); + const unsigned int chan_addr_offset = 2 * chan->channel; + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + /* DAC can only accept up to a 16-bit value */ + if ((unsigned int)val > 65535) + return -EINVAL; + + priv->chan_out_states[chan->channel] = val; + outw(val, priv->base + chan_addr_offset); + + return 0; +} + +static const struct iio_info cio_dac_info = { + .driver_module = THIS_MODULE, + .read_raw = cio_dac_read_raw, + .write_raw = cio_dac_write_raw +}; + +static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = { + CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3), + CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7), + CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11), + CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15) +}; + +static int cio_dac_probe(struct device *dev, unsigned int id) +{ + struct iio_dev *indio_dev; + struct cio_dac_iio *priv; + unsigned int i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT, + dev_name(dev))) { + dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n", + base[id], base[id] + CIO_DAC_EXTENT); + return -EBUSY; + } + + indio_dev->info = &cio_dac_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = cio_dac_channels; + indio_dev->num_channels = CIO_DAC_NUM_CHAN; + indio_dev->name = dev_name(dev); + + priv = iio_priv(indio_dev); + priv->base = base[id]; + + /* initialize DAC outputs to 0V */ + for (i = 0; i < 32; i += 2) + outw(0, base[id] + i); + + return devm_iio_device_register(dev, indio_dev); +} + +static struct isa_driver cio_dac_driver = { + .probe = cio_dac_probe, + .driver = { + .name = "cio-dac" + } +}; + +module_isa_driver(cio_dac_driver, num_cio_dac); + +MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>"); +MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 738a86d9e4a9..f155386286bf 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -26,11 +26,11 @@ config HDC100X tristate "TI HDC100x relative humidity and temperature sensor" depends on I2C help - Say yes here to build support for the TI HDC100x series of - relative humidity and temperature sensors. + Say yes here to build support for the Texas Instruments + HDC1000 and HDC1008 relative humidity and temperature sensors. - To compile this driver as a module, choose M here: the module - will be called hdc100x. + To compile this driver as a module, choose M here: the module + will be called hdc100x. config HTU21 tristate "Measurement Specialties HTU21 humidity & temperature sensor" diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 7c566f516572..28217478d828 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -333,11 +333,11 @@ config US5182D will be called us5182d. config VCNL4000 - tristate "VCNL4000 combined ALS and proximity sensor" + tristate "VCNL4000/4010/4020 combined ALS and proximity sensor" depends on I2C help - Say Y here if you want to build a driver for the Vishay VCNL4000 - combined ambient light and proximity sensor. + Say Y here if you want to build a driver for the Vishay VCNL4000, + VCNL4010, VCNL4020 combined ambient light and proximity sensor. To compile this driver as a module, choose M here: the module will be called vcnl4000. diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 20c40f780964..18cf2e29e4d5 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -894,7 +894,7 @@ static int us5182d_probe(struct i2c_client *client, goto out_err; if (data->default_continuous) { - pm_runtime_set_active(&client->dev); + ret = pm_runtime_set_active(&client->dev); if (ret < 0) goto out_err; } diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index c9d85bbc9230..360b6e98137a 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1,6 +1,6 @@ /* - * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and - * proximity sensor + * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient + * light and proximity sensor * * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> * @@ -13,6 +13,8 @@ * TODO: * allow to adjust IR current * proximity threshold and event handling + * periodic ALS/proximity measurement (VCNL4010/20) + * interrupts (VCNL4010/20) */ #include <linux/module.h> @@ -24,6 +26,8 @@ #include <linux/iio/sysfs.h> #define VCNL4000_DRV_NAME "vcnl4000" +#define VCNL4000_ID 0x01 +#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */ #define VCNL4000_COMMAND 0x80 /* Command register */ #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ @@ -37,13 +41,14 @@ #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ /* Bit masks for COMMAND register */ -#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */ -#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */ -#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */ -#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */ +#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ +#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ +#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ +#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ struct vcnl4000_data { struct i2c_client *client; + struct mutex lock; }; static const struct i2c_device_id vcnl4000_id[] = { @@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, __be16 buf; int ret; + mutex_lock(&data->lock); + ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, req_mask); if (ret < 0) - return ret; + goto fail; /* wait for data to become ready */ while (tries--) { ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); if (ret < 0) - return ret; + goto fail; if (ret & rdy_mask) break; msleep(20); /* measurement takes up to 100 ms */ @@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, if (tries < 0) { dev_err(&data->client->dev, "vcnl4000_measure() failed, data not ready\n"); - return -EIO; + ret = -EIO; + goto fail; } ret = i2c_smbus_read_i2c_block_data(data->client, data_reg, sizeof(buf), (u8 *) &buf); if (ret < 0) - return ret; + goto fail; + mutex_unlock(&data->lock); *val = be16_to_cpu(buf); return 0; + +fail: + mutex_unlock(&data->lock); + return ret; } static const struct iio_chan_spec vcnl4000_channels[] = { @@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) { - int ret = -EINVAL; + int ret; struct vcnl4000_data *data = iio_priv(indio_dev); switch (mask) { @@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev, VCNL4000_AL_RESULT_HI, val); if (ret < 0) return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; case IIO_PROXIMITY: ret = vcnl4000_measure(data, VCNL4000_PS_OD, VCNL4000_PS_RDY, VCNL4000_PS_RESULT_HI, val); if (ret < 0) return ret; - ret = IIO_VAL_INT; - break; + return IIO_VAL_INT; default: - break; + return -EINVAL; } - break; case IIO_CHAN_INFO_SCALE: - if (chan->type == IIO_LIGHT) { - *val = 0; - *val2 = 250000; - ret = IIO_VAL_INT_PLUS_MICRO; - } - break; + if (chan->type != IIO_LIGHT) + return -EINVAL; + + *val = 0; + *val2 = 250000; + return IIO_VAL_INT_PLUS_MICRO; default: - break; + return -EINVAL; } - - return ret; } static const struct iio_info vcnl4000_info = { @@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client, { struct vcnl4000_data *data; struct iio_dev *indio_dev; - int ret; + int ret, prod_id; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); if (!indio_dev) @@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client, data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); data->client = client; + mutex_init(&data->lock); ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); if (ret < 0) return ret; - dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n", - ret >> 4, ret & 0xf); + prod_id = ret >> 4; + if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID) + return -ENODEV; + + dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", + (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000", + ret & 0xf); indio_dev->dev.parent = &client->dev; indio_dev->info = &vcnl4000_info; diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 1f842abcb4a4..421ad90a5fbe 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -5,8 +5,22 @@ menu "Magnetometer sensors" +config AK8974 + tristate "Asahi Kasei AK8974 3-Axis Magnetometer" + depends on I2C + depends on OF + select REGMAP_I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Asahi Kasei AK8974 or + AMI305 I2C-based 3-axis magnetometer chips. + + To compile this driver as a module, choose M here: the module + will be called ak8974. + config AK8975 - tristate "Asahi Kasei AK 3-Axis Magnetometer" + tristate "Asahi Kasei AK8975 3-Axis Magnetometer" depends on I2C depends on GPIOLIB || COMPILE_TEST select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index 92a745c9a6e8..b86d6cb7f285 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -3,6 +3,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AK8974) += ak8974.o obj-$(CONFIG_AK8975) += ak8975.o obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c new file mode 100644 index 000000000000..e70e4e22b72c --- /dev/null +++ b/drivers/iio/magnetometer/ak8974.c @@ -0,0 +1,863 @@ +/* + * Driver for the Asahi Kasei EMD Corporation AK8974 + * and Aichi Steel AMI305 magnetometer chips. + * Based on a patch from Samu Onkalo and the AK8975 IIO driver. + * + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (c) 2010 NVIDIA Corporation. + * Copyright (C) 2016 Linaro Ltd. + * + * Author: Samu Onkalo <samu.p.onkalo@nokia.com> + * Author: Linus Walleij <linus.walleij@linaro.org> + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> /* For irq_get_irq_data() */ +#include <linux/completion.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/pm_runtime.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +/* + * 16-bit registers are little-endian. LSB is at the address defined below + * and MSB is at the next higher address. + */ + +/* These registers are common for AK8974 and AMI305 */ +#define AK8974_SELFTEST 0x0C +#define AK8974_SELFTEST_IDLE 0x55 +#define AK8974_SELFTEST_OK 0xAA + +#define AK8974_INFO 0x0D + +#define AK8974_WHOAMI 0x0F +#define AK8974_WHOAMI_VALUE_AMI305 0x47 +#define AK8974_WHOAMI_VALUE_AK8974 0x48 + +#define AK8974_DATA_X 0x10 +#define AK8974_DATA_Y 0x12 +#define AK8974_DATA_Z 0x14 +#define AK8974_INT_SRC 0x16 +#define AK8974_STATUS 0x18 +#define AK8974_INT_CLEAR 0x1A +#define AK8974_CTRL1 0x1B +#define AK8974_CTRL2 0x1C +#define AK8974_CTRL3 0x1D +#define AK8974_INT_CTRL 0x1E +#define AK8974_INT_THRES 0x26 /* Absolute any axis value threshold */ +#define AK8974_PRESET 0x30 + +/* AK8974-specific offsets */ +#define AK8974_OFFSET_X 0x20 +#define AK8974_OFFSET_Y 0x22 +#define AK8974_OFFSET_Z 0x24 +/* AMI305-specific offsets */ +#define AMI305_OFFSET_X 0x6C +#define AMI305_OFFSET_Y 0x72 +#define AMI305_OFFSET_Z 0x78 + +/* Different temperature registers */ +#define AK8974_TEMP 0x31 +#define AMI305_TEMP 0x60 + +#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */ +#define AK8974_INT_Y_HIGH BIT(6) +#define AK8974_INT_Z_HIGH BIT(5) +#define AK8974_INT_X_LOW BIT(4) /* Axis below -threshold */ +#define AK8974_INT_Y_LOW BIT(3) +#define AK8974_INT_Z_LOW BIT(2) +#define AK8974_INT_RANGE BIT(1) /* Range overflow (any axis) */ + +#define AK8974_STATUS_DRDY BIT(6) /* Data ready */ +#define AK8974_STATUS_OVERRUN BIT(5) /* Data overrun */ +#define AK8974_STATUS_INT BIT(4) /* Interrupt occurred */ + +#define AK8974_CTRL1_POWER BIT(7) /* 0 = standby; 1 = active */ +#define AK8974_CTRL1_RATE BIT(4) /* 0 = 10 Hz; 1 = 20 Hz */ +#define AK8974_CTRL1_FORCE_EN BIT(1) /* 0 = normal; 1 = force */ +#define AK8974_CTRL1_MODE2 BIT(0) /* 0 */ + +#define AK8974_CTRL2_INT_EN BIT(4) /* 1 = enable interrupts */ +#define AK8974_CTRL2_DRDY_EN BIT(3) /* 1 = enable data ready signal */ +#define AK8974_CTRL2_DRDY_POL BIT(2) /* 1 = data ready active high */ +#define AK8974_CTRL2_RESDEF (AK8974_CTRL2_DRDY_POL) + +#define AK8974_CTRL3_RESET BIT(7) /* Software reset */ +#define AK8974_CTRL3_FORCE BIT(6) /* Start forced measurement */ +#define AK8974_CTRL3_SELFTEST BIT(4) /* Set selftest register */ +#define AK8974_CTRL3_RESDEF 0x00 + +#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */ +#define AK8974_INT_CTRL_YEN BIT(6) +#define AK8974_INT_CTRL_ZEN BIT(5) +#define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5)) +#define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */ +#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */ +#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL) + +/* The AMI305 has elaborate FW version and serial number registers */ +#define AMI305_VER 0xE8 +#define AMI305_SN 0xEA + +#define AK8974_MAX_RANGE 2048 + +#define AK8974_POWERON_DELAY 50 +#define AK8974_ACTIVATE_DELAY 1 +#define AK8974_SELFTEST_DELAY 1 +/* + * Set the autosuspend to two orders of magnitude larger than the poweron + * delay to make sane reasonable power tradeoff savings (5 seconds in + * this case). + */ +#define AK8974_AUTOSUSPEND_DELAY 5000 + +#define AK8974_MEASTIME 3 + +#define AK8974_PWR_ON 1 +#define AK8974_PWR_OFF 0 + +/** + * struct ak8974 - state container for the AK8974 driver + * @i2c: parent I2C client + * @orientation: mounting matrix, flipped axis etc + * @map: regmap to access the AK8974 registers over I2C + * @regs: the avdd and dvdd power regulators + * @name: the name of the part + * @variant: the whoami ID value (for selecting code paths) + * @lock: locks the magnetometer for exclusive use during a measurement + * @drdy_irq: uses the DRDY IRQ line + * @drdy_complete: completion for DRDY + * @drdy_active_low: the DRDY IRQ is active low + */ +struct ak8974 { + struct i2c_client *i2c; + struct iio_mount_matrix orientation; + struct regmap *map; + struct regulator_bulk_data regs[2]; + const char *name; + u8 variant; + struct mutex lock; + bool drdy_irq; + struct completion drdy_complete; + bool drdy_active_low; +}; + +static const char ak8974_reg_avdd[] = "avdd"; +static const char ak8974_reg_dvdd[] = "dvdd"; + +static int ak8974_set_power(struct ak8974 *ak8974, bool mode) +{ + int ret; + u8 val; + + val = mode ? AK8974_CTRL1_POWER : 0; + val |= AK8974_CTRL1_FORCE_EN; + ret = regmap_write(ak8974->map, AK8974_CTRL1, val); + if (ret < 0) + return ret; + + if (mode) + msleep(AK8974_ACTIVATE_DELAY); + + return 0; +} + +static int ak8974_reset(struct ak8974 *ak8974) +{ + int ret; + + /* Power on to get register access. Sets CTRL1 reg to reset state */ + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_INT_CTRL, + AK8974_INT_CTRL_RESDEF); + if (ret) + return ret; + + /* After reset, power off is default state */ + return ak8974_set_power(ak8974, AK8974_PWR_OFF); +} + +static int ak8974_configure(struct ak8974 *ak8974) +{ + int ret; + + ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN | + AK8974_CTRL2_INT_EN); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_CTRL3, 0); + if (ret) + return ret; + ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL); + if (ret) + return ret; + + return regmap_write(ak8974->map, AK8974_PRESET, 0); +} + +static int ak8974_trigmeas(struct ak8974 *ak8974) +{ + unsigned int clear; + u8 mask; + u8 val; + int ret; + + /* Clear any previous measurement overflow status */ + ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear); + if (ret) + return ret; + + /* If we have a DRDY IRQ line, use it */ + if (ak8974->drdy_irq) { + mask = AK8974_CTRL2_INT_EN | + AK8974_CTRL2_DRDY_EN | + AK8974_CTRL2_DRDY_POL; + val = AK8974_CTRL2_DRDY_EN; + + if (!ak8974->drdy_active_low) + val |= AK8974_CTRL2_DRDY_POL; + + init_completion(&ak8974->drdy_complete); + ret = regmap_update_bits(ak8974->map, AK8974_CTRL2, + mask, val); + if (ret) + return ret; + } + + /* Force a measurement */ + return regmap_update_bits(ak8974->map, + AK8974_CTRL3, + AK8974_CTRL3_FORCE, + AK8974_CTRL3_FORCE); +} + +static int ak8974_await_drdy(struct ak8974 *ak8974) +{ + int timeout = 2; + unsigned int val; + int ret; + + if (ak8974->drdy_irq) { + ret = wait_for_completion_timeout(&ak8974->drdy_complete, + 1 + msecs_to_jiffies(1000)); + if (!ret) { + dev_err(&ak8974->i2c->dev, + "timeout waiting for DRDY IRQ\n"); + return -ETIMEDOUT; + } + return 0; + } + + /* Default delay-based poll loop */ + do { + msleep(AK8974_MEASTIME); + ret = regmap_read(ak8974->map, AK8974_STATUS, &val); + if (ret < 0) + return ret; + if (val & AK8974_STATUS_DRDY) + return 0; + } while (--timeout); + if (!timeout) { + dev_err(&ak8974->i2c->dev, + "timeout waiting for DRDY\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ak8974_getresult(struct ak8974 *ak8974, s16 *result) +{ + unsigned int src; + int ret; + + ret = ak8974_await_drdy(ak8974); + if (ret) + return ret; + ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src); + if (ret < 0) + return ret; + + /* Out of range overflow! Strong magnet close? */ + if (src & AK8974_INT_RANGE) { + dev_err(&ak8974->i2c->dev, + "range overflow in sensor\n"); + return -ERANGE; + } + + ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6); + if (ret) + return ret; + + return ret; +} + +static irqreturn_t ak8974_drdy_irq(int irq, void *d) +{ + struct ak8974 *ak8974 = d; + + if (!ak8974->drdy_irq) + return IRQ_NONE; + + /* TODO: timestamp here to get good measurement stamps */ + return IRQ_WAKE_THREAD; +} + +static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d) +{ + struct ak8974 *ak8974 = d; + unsigned int val; + int ret; + + /* Check if this was a DRDY from us */ + ret = regmap_read(ak8974->map, AK8974_STATUS, &val); + if (ret < 0) { + dev_err(&ak8974->i2c->dev, "error reading DRDY status\n"); + return IRQ_HANDLED; + } + if (val & AK8974_STATUS_DRDY) { + /* Yes this was our IRQ */ + complete(&ak8974->drdy_complete); + return IRQ_HANDLED; + } + + /* We may be on a shared IRQ, let the next client check */ + return IRQ_NONE; +} + +static int ak8974_selftest(struct ak8974 *ak8974) +{ + struct device *dev = &ak8974->i2c->dev; + unsigned int val; + int ret; + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_IDLE) { + dev_err(dev, "selftest not idle before test\n"); + return -EIO; + } + + /* Trigger self-test */ + ret = regmap_update_bits(ak8974->map, + AK8974_CTRL3, + AK8974_CTRL3_SELFTEST, + AK8974_CTRL3_SELFTEST); + if (ret) { + dev_err(dev, "could not write CTRL3\n"); + return ret; + } + + msleep(AK8974_SELFTEST_DELAY); + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_OK) { + dev_err(dev, "selftest result NOT OK (%02x)\n", val); + return -EIO; + } + + ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val); + if (ret) + return ret; + if (val != AK8974_SELFTEST_IDLE) { + dev_err(dev, "selftest not idle after test (%02x)\n", val); + return -EIO; + } + dev_dbg(dev, "passed self-test\n"); + + return 0; +} + +static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val) +{ + int ret; + u16 bulk; + + ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2); + if (ret) + return ret; + *val = le16_to_cpu(bulk); + + return 0; +} + +static int ak8974_detect(struct ak8974 *ak8974) +{ + unsigned int whoami; + const char *name; + int ret; + unsigned int fw; + u16 sn; + + ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami); + if (ret) + return ret; + + switch (whoami) { + case AK8974_WHOAMI_VALUE_AMI305: + name = "ami305"; + ret = regmap_read(ak8974->map, AMI305_VER, &fw); + if (ret) + return ret; + fw &= 0x7f; /* only bits 0 thru 6 valid */ + ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn); + if (ret) + return ret; + dev_info(&ak8974->i2c->dev, + "detected %s, FW ver %02x, S/N: %04x\n", + name, fw, sn); + break; + case AK8974_WHOAMI_VALUE_AK8974: + name = "ak8974"; + dev_info(&ak8974->i2c->dev, "detected AK8974\n"); + break; + default: + dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ", + whoami); + return -ENODEV; + } + + ak8974->name = name; + ak8974->variant = whoami; + + return 0; +} + +static int ak8974_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, + long mask) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + s16 hw_values[3]; + int ret = -EINVAL; + + pm_runtime_get_sync(&ak8974->i2c->dev); + mutex_lock(&ak8974->lock); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->address > 2) { + dev_err(&ak8974->i2c->dev, "faulty channel address\n"); + ret = -EIO; + goto out_unlock; + } + ret = ak8974_trigmeas(ak8974); + if (ret) + goto out_unlock; + ret = ak8974_getresult(ak8974, hw_values); + if (ret) + goto out_unlock; + + /* + * We read all axes and discard all but one, for optimized + * reading, use the triggered buffer. + */ + *val = le16_to_cpu(hw_values[chan->address]); + + ret = IIO_VAL_INT; + } + + out_unlock: + mutex_unlock(&ak8974->lock); + pm_runtime_mark_last_busy(&ak8974->i2c->dev); + pm_runtime_put_autosuspend(&ak8974->i2c->dev); + + return ret; +} + +static void ak8974_fill_buffer(struct iio_dev *indio_dev) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + int ret; + s16 hw_values[8]; /* Three axes + 64bit padding */ + + pm_runtime_get_sync(&ak8974->i2c->dev); + mutex_lock(&ak8974->lock); + + ret = ak8974_trigmeas(ak8974); + if (ret) { + dev_err(&ak8974->i2c->dev, "error triggering measure\n"); + goto out_unlock; + } + ret = ak8974_getresult(ak8974, hw_values); + if (ret) { + dev_err(&ak8974->i2c->dev, "error getting measures\n"); + goto out_unlock; + } + + iio_push_to_buffers_with_timestamp(indio_dev, hw_values, + iio_get_time_ns(indio_dev)); + + out_unlock: + mutex_unlock(&ak8974->lock); + pm_runtime_mark_last_busy(&ak8974->i2c->dev); + pm_runtime_put_autosuspend(&ak8974->i2c->dev); +} + +static irqreturn_t ak8974_handle_trigger(int irq, void *p) +{ + const struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + + ak8974_fill_buffer(indio_dev); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static const struct iio_mount_matrix * +ak8974_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ak8974 *ak8974 = iio_priv(indio_dev); + + return &ak8974->orientation; +} + +static const struct iio_chan_spec_ext_info ak8974_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix), + { }, +}; + +#define AK8974_AXIS_CHANNEL(axis, index) \ + { \ + .type = IIO_MAGN, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .ext_info = ak8974_ext_info, \ + .address = index, \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE \ + }, \ + } + +static const struct iio_chan_spec ak8974_channels[] = { + AK8974_AXIS_CHANNEL(X, 0), + AK8974_AXIS_CHANNEL(Y, 1), + AK8974_AXIS_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const unsigned long ak8974_scan_masks[] = { 0x7, 0 }; + +static const struct iio_info ak8974_info = { + .read_raw = &ak8974_read_raw, + .driver_module = THIS_MODULE, +}; + +static bool ak8974_writeable_reg(struct device *dev, unsigned int reg) +{ + struct i2c_client *i2c = to_i2c_client(dev); + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct ak8974 *ak8974 = iio_priv(indio_dev); + + switch (reg) { + case AK8974_CTRL1: + case AK8974_CTRL2: + case AK8974_CTRL3: + case AK8974_INT_CTRL: + case AK8974_INT_THRES: + case AK8974_INT_THRES + 1: + case AK8974_PRESET: + case AK8974_PRESET + 1: + return true; + case AK8974_OFFSET_X: + case AK8974_OFFSET_X + 1: + case AK8974_OFFSET_Y: + case AK8974_OFFSET_Y + 1: + case AK8974_OFFSET_Z: + case AK8974_OFFSET_Z + 1: + if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974) + return true; + return false; + case AMI305_OFFSET_X: + case AMI305_OFFSET_X + 1: + case AMI305_OFFSET_Y: + case AMI305_OFFSET_Y + 1: + case AMI305_OFFSET_Z: + case AMI305_OFFSET_Z + 1: + if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305) + return true; + return false; + default: + return false; + } +} + +static const struct regmap_config ak8974_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0xff, + .writeable_reg = ak8974_writeable_reg, +}; + +static int ak8974_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct ak8974 *ak8974; + unsigned long irq_trig; + int irq = i2c->irq; + int ret; + + /* Register with IIO */ + indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974)); + if (indio_dev == NULL) + return -ENOMEM; + + ak8974 = iio_priv(indio_dev); + i2c_set_clientdata(i2c, indio_dev); + ak8974->i2c = i2c; + mutex_init(&ak8974->lock); + + ret = of_iio_read_mount_matrix(&i2c->dev, + "mount-matrix", + &ak8974->orientation); + if (ret) + return ret; + + ak8974->regs[0].supply = ak8974_reg_avdd; + ak8974->regs[1].supply = ak8974_reg_dvdd; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(ak8974->regs), + ak8974->regs); + if (ret < 0) { + dev_err(&i2c->dev, "cannot get regulators\n"); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + if (ret < 0) { + dev_err(&i2c->dev, "cannot enable regulators\n"); + return ret; + } + + /* Take runtime PM online */ + pm_runtime_get_noresume(&i2c->dev); + pm_runtime_set_active(&i2c->dev); + pm_runtime_enable(&i2c->dev); + + ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config); + if (IS_ERR(ak8974->map)) { + dev_err(&i2c->dev, "failed to allocate register map\n"); + return PTR_ERR(ak8974->map); + } + + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) { + dev_err(&i2c->dev, "could not power on\n"); + goto power_off; + } + + ret = ak8974_detect(ak8974); + if (ret) { + dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n"); + goto power_off; + } + + ret = ak8974_selftest(ak8974); + if (ret) + dev_err(&i2c->dev, "selftest failed (continuing anyway)\n"); + + ret = ak8974_reset(ak8974); + if (ret) { + dev_err(&i2c->dev, "AK8974 reset failed\n"); + goto power_off; + } + + pm_runtime_set_autosuspend_delay(&i2c->dev, + AK8974_AUTOSUSPEND_DELAY); + pm_runtime_use_autosuspend(&i2c->dev); + pm_runtime_put(&i2c->dev); + + indio_dev->dev.parent = &i2c->dev; + indio_dev->channels = ak8974_channels; + indio_dev->num_channels = ARRAY_SIZE(ak8974_channels); + indio_dev->info = &ak8974_info; + indio_dev->available_scan_masks = ak8974_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = ak8974->name; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + ak8974_handle_trigger, + NULL); + if (ret) { + dev_err(&i2c->dev, "triggered buffer setup failed\n"); + goto disable_pm; + } + + /* If we have a valid DRDY IRQ, make use of it */ + if (irq > 0) { + irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq)); + if (irq_trig == IRQF_TRIGGER_RISING) { + dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n"); + } else if (irq_trig == IRQF_TRIGGER_FALLING) { + ak8974->drdy_active_low = true; + dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n"); + } else { + irq_trig = IRQF_TRIGGER_RISING; + } + irq_trig |= IRQF_ONESHOT; + irq_trig |= IRQF_SHARED; + + ret = devm_request_threaded_irq(&i2c->dev, + irq, + ak8974_drdy_irq, + ak8974_drdy_irq_thread, + irq_trig, + ak8974->name, + ak8974); + if (ret) { + dev_err(&i2c->dev, "unable to request DRDY IRQ " + "- proceeding without IRQ\n"); + goto no_irq; + } + ak8974->drdy_irq = true; + } + +no_irq: + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&i2c->dev, "device register failed\n"); + goto cleanup_buffer; + } + + return 0; + +cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +disable_pm: + pm_runtime_put_noidle(&i2c->dev); + pm_runtime_disable(&i2c->dev); + ak8974_set_power(ak8974, AK8974_PWR_OFF); +power_off: + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return ret; +} + +static int __exit ak8974_remove(struct i2c_client *i2c) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(i2c); + struct ak8974 *ak8974 = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + pm_runtime_get_sync(&i2c->dev); + pm_runtime_put_noidle(&i2c->dev); + pm_runtime_disable(&i2c->dev); + ak8974_set_power(ak8974, AK8974_PWR_OFF); + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return 0; +} + +#ifdef CONFIG_PM +static int ak8974_runtime_suspend(struct device *dev) +{ + struct ak8974 *ak8974 = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + + ak8974_set_power(ak8974, AK8974_PWR_OFF); + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return 0; +} + +static int ak8974_runtime_resume(struct device *dev) +{ + struct ak8974 *ak8974 = + iio_priv(i2c_get_clientdata(to_i2c_client(dev))); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + if (ret) + return ret; + msleep(AK8974_POWERON_DELAY); + ret = ak8974_set_power(ak8974, AK8974_PWR_ON); + if (ret) + goto out_regulator_disable; + + ret = ak8974_configure(ak8974); + if (ret) + goto out_disable_power; + + return 0; + +out_disable_power: + ak8974_set_power(ak8974, AK8974_PWR_OFF); +out_regulator_disable: + regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs); + + return ret; +} +#endif /* CONFIG_PM */ + +static const struct dev_pm_ops ak8974_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(ak8974_runtime_suspend, + ak8974_runtime_resume, NULL) +}; + +static const struct i2c_device_id ak8974_id[] = { + {"ami305", 0 }, + {"ak8974", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ak8974_id); + +static const struct of_device_id ak8974_of_match[] = { + { .compatible = "asahi-kasei,ak8974", }, + {} +}; +MODULE_DEVICE_TABLE(of, ak8974_of_match); + +static struct i2c_driver ak8974_driver = { + .driver = { + .name = "ak8974", + .owner = THIS_MODULE, + .pm = &ak8974_dev_pm_ops, + .of_match_table = of_match_ptr(ak8974_of_match), + }, + .probe = ak8974_probe, + .remove = __exit_p(ak8974_remove), + .id_table = ak8974_id, +}; +module_i2c_driver(ak8974_driver); + +MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver"); +MODULE_AUTHOR("Samu Onkalo"); +MODULE_AUTHOR("Linus Walleij"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f2be4a049056..f2b3bd7bf862 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; switch (chan->type) { case IIO_MAGN: /* in 0.1 uT / LSB */ ret = mag3110_read(data, buffer); if (ret < 0) - return ret; + goto release; *val = sign_extend32( be16_to_cpu(buffer[chan->scan_index]), 15); - return IIO_VAL_INT; + ret = IIO_VAL_INT; + break; case IIO_TEMP: /* in 1 C / LSB */ mutex_lock(&data->lock); ret = mag3110_request(data); if (ret < 0) { mutex_unlock(&data->lock); - return ret; + goto release; } ret = i2c_smbus_read_byte_data(data->client, MAG3110_DIE_TEMP); mutex_unlock(&data->lock); if (ret < 0) - return ret; + goto release; *val = sign_extend32(ret, 7); - return IIO_VAL_INT; + ret = IIO_VAL_INT; + break; default: - return -EINVAL; + ret = -EINVAL; } +release: + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_MAGN: diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 1d74b3aafeed..6f84f53dfe54 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private) sx9500_push_events(indio_dev); if (val & SX9500_CONVDONE_IRQ) - complete_all(&data->completion); + complete(&data->completion); out: mutex_unlock(&data->mutex); diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index c4664e5de791..5ea77a7e261d 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -3,6 +3,22 @@ # menu "Temperature sensors" +config MAXIM_THERMOCOUPLE + tristate "Maxim thermocouple sensors" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + If you say yes here you get support for the Maxim series of + thermocouple sensors connected via SPI. + + Supported sensors: + * MAX6675 + * MAX31855 + + This driver can also be built as a module. If so, the module will + be called maxim_thermocouple. + config MLX90614 tristate "MLX90614 contact-less infrared sensor" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 02bc79d49b24..78c3de0dc3f0 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -2,6 +2,7 @@ # Makefile for industrial I/O temperature drivers # +obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TSYS01) += tsys01.o diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c new file mode 100644 index 000000000000..030827ec8f3a --- /dev/null +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -0,0 +1,281 @@ +/* + * maxim_thermocouple.c - Support for Maxim thermocouple chips + * + * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> + +#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple" + +enum { + MAX6675, + MAX31855, +}; + +const struct iio_chan_spec max6675_channels[] = { + { /* thermocouple temperature */ + .type = IIO_TEMP, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 13, + .storagebits = 16, + .shift = 3, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +const struct iio_chan_spec max31855_channels[] = { + { /* thermocouple temperature */ + .type = IIO_TEMP, + .address = 2, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 0, + .scan_type = { + .sign = 's', + .realbits = 14, + .storagebits = 16, + .shift = 2, + .endianness = IIO_BE, + }, + }, + { /* cold junction temperature */ + .type = IIO_TEMP, + .address = 0, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .modified = 1, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + .scan_index = 1, + .scan_type = { + .sign = 's', + .realbits = 12, + .storagebits = 16, + .shift = 4, + .endianness = IIO_BE, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const unsigned long max31855_scan_masks[] = {0x3, 0}; + +struct maxim_thermocouple_chip { + const struct iio_chan_spec *channels; + const unsigned long *scan_masks; + u8 num_channels; + u8 read_size; + + /* bit-check for valid input */ + u32 status_bit; +}; + +const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = { + [MAX6675] = { + .channels = max6675_channels, + .num_channels = ARRAY_SIZE(max6675_channels), + .read_size = 2, + .status_bit = BIT(2), + }, + [MAX31855] = { + .channels = max31855_channels, + .num_channels = ARRAY_SIZE(max31855_channels), + .read_size = 4, + .scan_masks = max31855_scan_masks, + .status_bit = BIT(16), + }, +}; + +struct maxim_thermocouple_data { + struct spi_device *spi; + const struct maxim_thermocouple_chip *chip; + + u8 buffer[16] ____cacheline_aligned; +}; + +static int maxim_thermocouple_read(struct maxim_thermocouple_data *data, + struct iio_chan_spec const *chan, int *val) +{ + unsigned int storage_bytes = data->chip->read_size; + unsigned int shift = chan->scan_type.shift + (chan->address * 8); + unsigned int buf; + int ret; + + ret = spi_read(data->spi, (void *) &buf, storage_bytes); + if (ret) + return ret; + + switch (storage_bytes) { + case 2: + *val = be16_to_cpu(buf); + break; + case 4: + *val = be32_to_cpu(buf); + break; + } + + /* check to be sure this is a valid reading */ + if (*val & data->chip->status_bit) + return -EINVAL; + + *val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1); + + return 0; +} + +static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct maxim_thermocouple_data *data = iio_priv(indio_dev); + int ret; + + ret = spi_read(data->spi, data->buffer, data->chip->read_size); + if (!ret) { + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_get_time_ns(indio_dev)); + } + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct maxim_thermocouple_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = maxim_thermocouple_read(data, chan, val); + iio_device_release_direct_mode(indio_dev); + + if (!ret) + return IIO_VAL_INT; + + break; + case IIO_CHAN_INFO_SCALE: + switch (chan->channel2) { + case IIO_MOD_TEMP_AMBIENT: + *val = 62; + *val2 = 500000; /* 1000 * 0.0625 */ + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + *val = 250; /* 1000 * 0.25 */ + ret = IIO_VAL_INT; + }; + break; + } + + return ret; +} + +static const struct iio_info maxim_thermocouple_info = { + .driver_module = THIS_MODULE, + .read_raw = maxim_thermocouple_read_raw, +}; + +static int maxim_thermocouple_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct iio_dev *indio_dev; + struct maxim_thermocouple_data *data; + const struct maxim_thermocouple_chip *chip = + &maxim_thermocouple_chips[id->driver_data]; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + indio_dev->info = &maxim_thermocouple_info; + indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME; + indio_dev->channels = chip->channels; + indio_dev->available_scan_masks = chip->scan_masks; + indio_dev->num_channels = chip->num_channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + data = iio_priv(indio_dev); + data->spi = spi; + data->chip = chip; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + maxim_thermocouple_trigger_handler, NULL); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_unreg_buffer; + + return 0; + +error_unreg_buffer: + iio_triggered_buffer_cleanup(indio_dev); + + return ret; +} + +static int maxim_thermocouple_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 0; +} + +static const struct spi_device_id maxim_thermocouple_id[] = { + {"max6675", MAX6675}, + {"max31855", MAX31855}, + {}, +}; +MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id); + +static struct spi_driver maxim_thermocouple_driver = { + .driver = { + .name = MAXIM_THERMOCOUPLE_DRV_NAME, + }, + .probe = maxim_thermocouple_probe, + .remove = maxim_thermocouple_remove, + .id_table = maxim_thermocouple_id, +}; +module_spi_driver(maxim_thermocouple_driver); + +MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); +MODULE_DESCRIPTION("Maxim thermocouple sensors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c index 76d9f74e7dcb..f47a17d7f6b3 100644 --- a/drivers/staging/iio/light/isl29018.c +++ b/drivers/staging/iio/light/isl29018.c @@ -32,25 +32,25 @@ #include <linux/iio/sysfs.h> #include <linux/acpi.h> -#define CONVERSION_TIME_MS 100 +#define ISL29018_CONV_TIME_MS 100 #define ISL29018_REG_ADD_COMMAND1 0x00 -#define COMMMAND1_OPMODE_SHIFT 5 -#define COMMMAND1_OPMODE_MASK (7 << COMMMAND1_OPMODE_SHIFT) -#define COMMMAND1_OPMODE_POWER_DOWN 0 -#define COMMMAND1_OPMODE_ALS_ONCE 1 -#define COMMMAND1_OPMODE_IR_ONCE 2 -#define COMMMAND1_OPMODE_PROX_ONCE 3 +#define ISL29018_CMD1_OPMODE_SHIFT 5 +#define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT) +#define ISL29018_CMD1_OPMODE_POWER_DOWN 0 +#define ISL29018_CMD1_OPMODE_ALS_ONCE 1 +#define ISL29018_CMD1_OPMODE_IR_ONCE 2 +#define ISL29018_CMD1_OPMODE_PROX_ONCE 3 -#define ISL29018_REG_ADD_COMMANDII 0x01 -#define COMMANDII_RESOLUTION_SHIFT 2 -#define COMMANDII_RESOLUTION_MASK (0x3 << COMMANDII_RESOLUTION_SHIFT) +#define ISL29018_REG_ADD_COMMAND2 0x01 +#define ISL29018_CMD2_RESOLUTION_SHIFT 2 +#define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT) -#define COMMANDII_RANGE_SHIFT 0 -#define COMMANDII_RANGE_MASK (0x3 << COMMANDII_RANGE_SHIFT) +#define ISL29018_CMD2_RANGE_SHIFT 0 +#define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT) -#define COMMANDII_SCHEME_SHIFT 7 -#define COMMANDII_SCHEME_MASK (0x1 << COMMANDII_SCHEME_SHIFT) +#define ISL29018_CMD2_SCHEME_SHIFT 7 +#define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT) #define ISL29018_REG_ADD_DATA_LSB 0x02 #define ISL29018_REG_ADD_DATA_MSB 0x03 @@ -127,13 +127,13 @@ static int isl29018_set_integration_time(struct isl29018_chip *chip, if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type])) return -EINVAL; - ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, - COMMANDII_RESOLUTION_MASK, - i << COMMANDII_RESOLUTION_SHIFT); + ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, + ISL29018_CMD2_RESOLUTION_MASK, + i << ISL29018_CMD2_RESOLUTION_SHIFT); if (ret < 0) return ret; - /* keep the same range when integration time changes */ + /* Keep the same range when integration time changes */ int_time = chip->int_time; for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) { if (chip->scale.scale == isl29018_scales[int_time][i].scale && @@ -163,9 +163,9 @@ static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale) if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time])) return -EINVAL; - ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, - COMMANDII_RANGE_MASK, - i << COMMANDII_RANGE_SHIFT); + ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, + ISL29018_CMD2_RANGE_MASK, + i << ISL29018_CMD2_RANGE_SHIFT); if (ret < 0) return ret; @@ -183,13 +183,13 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode) /* Set mode */ status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, - mode << COMMMAND1_OPMODE_SHIFT); + mode << ISL29018_CMD1_OPMODE_SHIFT); if (status) { dev_err(dev, "Error in setting operating mode err %d\n", status); return status; } - msleep(CONVERSION_TIME_MS); + msleep(ISL29018_CONV_TIME_MS); status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb); if (status < 0) { dev_err(dev, @@ -213,8 +213,8 @@ static int isl29018_read_lux(struct isl29018_chip *chip, int *lux) int lux_data; unsigned int data_x_range; - lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE); - + lux_data = isl29018_read_sensor_input(chip, + ISL29018_CMD1_OPMODE_ALS_ONCE); if (lux_data < 0) return lux_data; @@ -230,8 +230,8 @@ static int isl29018_read_ir(struct isl29018_chip *chip, int *ir) { int ir_data; - ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE); - + ir_data = isl29018_read_sensor_input(chip, + ISL29018_CMD1_OPMODE_IR_ONCE); if (ir_data < 0) return ir_data; @@ -249,16 +249,16 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme, struct device *dev = regmap_get_device(chip->regmap); /* Do proximity sensing with required scheme */ - status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMANDII, - COMMANDII_SCHEME_MASK, - scheme << COMMANDII_SCHEME_SHIFT); + status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2, + ISL29018_CMD2_SCHEME_MASK, + scheme << ISL29018_CMD2_SCHEME_SHIFT); if (status) { dev_err(dev, "Error in setting operating mode\n"); return status; } prox_data = isl29018_read_sensor_input(chip, - COMMMAND1_OPMODE_PROX_ONCE); + ISL29018_CMD1_OPMODE_PROX_ONCE); if (prox_data < 0) return prox_data; @@ -267,8 +267,8 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme, return 0; } - ir_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_IR_ONCE); - + ir_data = isl29018_read_sensor_input(chip, + ISL29018_CMD1_OPMODE_IR_ONCE); if (ir_data < 0) return ir_data; @@ -280,7 +280,7 @@ static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme, return 0; } -static ssize_t show_scale_available(struct device *dev, +static ssize_t isl29018_show_scale_available(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -297,7 +297,7 @@ static ssize_t show_scale_available(struct device *dev, return len; } -static ssize_t show_int_time_available(struct device *dev, +static ssize_t isl29018_show_int_time_available(struct device *dev, struct device_attribute *attr, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -313,8 +313,7 @@ static ssize_t show_int_time_available(struct device *dev, return len; } -/* proximity scheme */ -static ssize_t show_prox_infrared_suppression(struct device *dev, +static ssize_t isl29018_show_prox_infrared_suppression(struct device *dev, struct device_attribute *attr, char *buf) { @@ -322,13 +321,13 @@ static ssize_t show_prox_infrared_suppression(struct device *dev, struct isl29018_chip *chip = iio_priv(indio_dev); /* - * return the "proximity scheme" i.e. if the chip does on chip + * Return the "proximity scheme" i.e. if the chip does on chip * infrared suppression (1 means perform on chip suppression) */ return sprintf(buf, "%d\n", chip->prox_scheme); } -static ssize_t store_prox_infrared_suppression(struct device *dev, +static ssize_t isl29018_store_prox_infrared_suppression(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -338,13 +337,11 @@ static ssize_t store_prox_infrared_suppression(struct device *dev, if (kstrtoint(buf, 10, &val)) return -EINVAL; - if (!(val == 0 || val == 1)) { - dev_err(dev, "The mode is not supported\n"); + if (!(val == 0 || val == 1)) return -EINVAL; - } /* - * get the "proximity scheme" i.e. if the chip does on chip + * Get the "proximity scheme" i.e. if the chip does on chip * infrared suppression (1 means perform on chip suppression) */ mutex_lock(&chip->lock); @@ -354,7 +351,6 @@ static ssize_t store_prox_infrared_suppression(struct device *dev, return count; } -/* Channel IO */ static int isl29018_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, @@ -491,13 +487,13 @@ static const struct iio_chan_spec isl29023_channels[] = { }; static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, S_IRUGO, - show_int_time_available, NULL, 0); + isl29018_show_int_time_available, NULL, 0); static IIO_DEVICE_ATTR(in_illuminance_scale_available, S_IRUGO, - show_scale_available, NULL, 0); + isl29018_show_scale_available, NULL, 0); static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_suppression, S_IRUGO | S_IWUSR, - show_prox_infrared_suppression, - store_prox_infrared_suppression, 0); + isl29018_show_prox_infrared_suppression, + isl29018_store_prox_infrared_suppression, 0); #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) @@ -541,7 +537,7 @@ static int isl29035_detect(struct isl29018_chip *chip) if (id != ISL29035_DEVICE_ID) return -ENODEV; - /* clear out brownout bit */ + /* Clear brownout bit */ return regmap_update_bits(chip->regmap, ISL29035_REG_DEVICE_ID, ISL29035_BOUT_MASK, 0); } @@ -574,7 +570,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip) * conversions, clear the test registers, and then rewrite all * registers to the desired values. * ... - * FOR ISL29011, ISL29018, ISL29021, ISL29023 + * For ISL29011, ISL29018, ISL29021, ISL29023 * 1. Write 0x00 to register 0x08 (TEST) * 2. Write 0x00 to register 0x00 (CMD1) * 3. Rewrite all registers to the desired values @@ -603,7 +599,7 @@ static int isl29018_chip_init(struct isl29018_chip *chip) usleep_range(1000, 2000); /* per data sheet, page 10 */ - /* set defaults */ + /* Set defaults */ status = isl29018_set_scale(chip, chip->scale.scale, chip->scale.uscale); if (status < 0) { @@ -635,7 +631,7 @@ static const struct iio_info isl29023_info = { .write_raw = isl29018_write_raw, }; -static bool is_volatile_reg(struct device *dev, unsigned int reg) +static bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case ISL29018_REG_ADD_DATA_LSB: @@ -649,37 +645,32 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) } } -/* - * isl29018_regmap_config: regmap configuration. - * Use RBTREE mechanism for caching. - */ static const struct regmap_config isl29018_regmap_config = { .reg_bits = 8, .val_bits = 8, - .volatile_reg = is_volatile_reg, + .volatile_reg = isl29018_is_volatile_reg, .max_register = ISL29018_REG_TEST, .num_reg_defaults_raw = ISL29018_REG_TEST + 1, .cache_type = REGCACHE_RBTREE, }; -/* isl29035_regmap_config: regmap configuration for ISL29035 */ static const struct regmap_config isl29035_regmap_config = { .reg_bits = 8, .val_bits = 8, - .volatile_reg = is_volatile_reg, + .volatile_reg = isl29018_is_volatile_reg, .max_register = ISL29035_REG_DEVICE_ID, .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1, .cache_type = REGCACHE_RBTREE, }; -struct chip_info { +struct isl29018_chip_info { const struct iio_chan_spec *channels; int num_channels; const struct iio_info *indio_info; const struct regmap_config *regmap_cfg; }; -static const struct chip_info chip_info_tbl[] = { +static const struct isl29018_chip_info isl29018_chip_info_tbl[] = { [isl29018] = { .channels = isl29018_channels, .num_channels = ARRAY_SIZE(isl29018_channels), @@ -724,10 +715,8 @@ static int isl29018_probe(struct i2c_client *client, int dev_id = 0; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (!indio_dev) { - dev_err(&client->dev, "iio allocation fails\n"); + if (!indio_dev) return -ENOMEM; - } chip = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); @@ -750,7 +739,7 @@ static int isl29018_probe(struct i2c_client *client, chip->suspended = false; chip->regmap = devm_regmap_init_i2c(client, - chip_info_tbl[dev_id].regmap_cfg); + isl29018_chip_info_tbl[dev_id].regmap_cfg); if (IS_ERR(chip->regmap)) { err = PTR_ERR(chip->regmap); dev_err(&client->dev, "regmap initialization fails: %d\n", err); @@ -761,19 +750,13 @@ static int isl29018_probe(struct i2c_client *client, if (err) return err; - indio_dev->info = chip_info_tbl[dev_id].indio_info; - indio_dev->channels = chip_info_tbl[dev_id].channels; - indio_dev->num_channels = chip_info_tbl[dev_id].num_channels; + indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info; + indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels; + indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels; indio_dev->name = name; indio_dev->dev.parent = &client->dev; indio_dev->modes = INDIO_DIRECT_MODE; - err = devm_iio_device_register(&client->dev, indio_dev); - if (err) { - dev_err(&client->dev, "iio registration fails\n"); - return err; - } - - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM_SLEEP @@ -840,7 +823,6 @@ static const struct of_device_id isl29018_of_match[] = { MODULE_DEVICE_TABLE(of, isl29018_of_match); static struct i2c_driver isl29018_driver = { - .class = I2C_CLASS_HWMON, .driver = { .name = "isl29018", .acpi_match_table = ACPI_PTR(isl29018_acpi_match), diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 2e3b1d64e32a..aa413e5878b9 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -27,29 +27,27 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#define CONVERSION_TIME_MS 100 +#define ISL29028_CONV_TIME_MS 100 #define ISL29028_REG_CONFIGURE 0x01 -#define CONFIGURE_ALS_IR_MODE_ALS 0 -#define CONFIGURE_ALS_IR_MODE_IR BIT(0) -#define CONFIGURE_ALS_IR_MODE_MASK BIT(0) +#define ISL29028_CONF_ALS_IR_MODE_ALS 0 +#define ISL29028_CONF_ALS_IR_MODE_IR BIT(0) +#define ISL29028_CONF_ALS_IR_MODE_MASK BIT(0) -#define CONFIGURE_ALS_RANGE_LOW_LUX 0 -#define CONFIGURE_ALS_RANGE_HIGH_LUX BIT(1) -#define CONFIGURE_ALS_RANGE_MASK BIT(1) +#define ISL29028_CONF_ALS_RANGE_LOW_LUX 0 +#define ISL29028_CONF_ALS_RANGE_HIGH_LUX BIT(1) +#define ISL29028_CONF_ALS_RANGE_MASK BIT(1) -#define CONFIGURE_ALS_DIS 0 -#define CONFIGURE_ALS_EN BIT(2) -#define CONFIGURE_ALS_EN_MASK BIT(2) +#define ISL29028_CONF_ALS_DIS 0 +#define ISL29028_CONF_ALS_EN BIT(2) +#define ISL29028_CONF_ALS_EN_MASK BIT(2) -#define CONFIGURE_PROX_DRIVE BIT(3) +#define ISL29028_CONF_PROX_SLP_SH 4 +#define ISL29028_CONF_PROX_SLP_MASK (7 << ISL29028_CONF_PROX_SLP_SH) -#define CONFIGURE_PROX_SLP_SH 4 -#define CONFIGURE_PROX_SLP_MASK (7 << CONFIGURE_PROX_SLP_SH) - -#define CONFIGURE_PROX_EN BIT(7) -#define CONFIGURE_PROX_EN_MASK BIT(7) +#define ISL29028_CONF_PROX_EN BIT(7) +#define ISL29028_CONF_PROX_EN_MASK BIT(7) #define ISL29028_REG_INTERRUPT 0x02 @@ -62,10 +60,10 @@ #define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1) -enum als_ir_mode { - MODE_NONE = 0, - MODE_ALS, - MODE_IR +enum isl29028_als_ir_mode { + ISL29028_MODE_NONE = 0, + ISL29028_MODE_ALS, + ISL29028_MODE_IR, }; struct isl29028_chip { @@ -76,7 +74,7 @@ struct isl29028_chip { bool enable_prox; int lux_scale; - int als_ir_mode; + enum isl29028_als_ir_mode als_ir_mode; }; static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, @@ -91,7 +89,8 @@ static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, break; } return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_PROX_SLP_MASK, sel << CONFIGURE_PROX_SLP_SH); + ISL29028_CONF_PROX_SLP_MASK, + sel << ISL29028_CONF_PROX_SLP_SH); } static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) @@ -100,9 +99,9 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) int val = 0; if (enable) - val = CONFIGURE_PROX_EN; + val = ISL29028_CONF_PROX_EN; ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_PROX_EN_MASK, val); + ISL29028_CONF_PROX_EN_MASK, val); if (ret < 0) return ret; @@ -113,40 +112,40 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) { - int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX : - CONFIGURE_ALS_RANGE_LOW_LUX; + int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX : + ISL29028_CONF_ALS_RANGE_LOW_LUX; return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_RANGE_MASK, val); + ISL29028_CONF_ALS_RANGE_MASK, val); } static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, - enum als_ir_mode mode) + enum isl29028_als_ir_mode mode) { int ret = 0; switch (mode) { - case MODE_ALS: + case ISL29028_MODE_ALS: ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_IR_MODE_MASK, - CONFIGURE_ALS_IR_MODE_ALS); + ISL29028_CONF_ALS_IR_MODE_MASK, + ISL29028_CONF_ALS_IR_MODE_ALS); if (ret < 0) return ret; ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_RANGE_MASK, - CONFIGURE_ALS_RANGE_HIGH_LUX); + ISL29028_CONF_ALS_RANGE_MASK, + ISL29028_CONF_ALS_RANGE_HIGH_LUX); break; - case MODE_IR: + case ISL29028_MODE_IR: ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_IR_MODE_MASK, - CONFIGURE_ALS_IR_MODE_IR); + ISL29028_CONF_ALS_IR_MODE_MASK, + ISL29028_CONF_ALS_IR_MODE_IR); break; - case MODE_NONE: + case ISL29028_MODE_NONE: return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS); + ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS); } if (ret < 0) @@ -154,12 +153,13 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, /* Enable the ALS/IR */ ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN); + ISL29028_CONF_ALS_EN_MASK, + ISL29028_CONF_ALS_EN); if (ret < 0) return ret; /* Need to wait for conversion time if ALS/IR mode enabled */ - mdelay(CONVERSION_TIME_MS); + mdelay(ISL29028_CONV_TIME_MS); return 0; } @@ -223,14 +223,14 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) int ret; int als_ir_data; - if (chip->als_ir_mode != MODE_ALS) { - ret = isl29028_set_als_ir_mode(chip, MODE_ALS); + if (chip->als_ir_mode != ISL29028_MODE_ALS) { + ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS); if (ret < 0) { dev_err(dev, "Error in enabling ALS mode err %d\n", ret); return ret; } - chip->als_ir_mode = MODE_ALS; + chip->als_ir_mode = ISL29028_MODE_ALS; } ret = isl29028_read_als_ir(chip, &als_ir_data); @@ -256,14 +256,14 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) struct device *dev = regmap_get_device(chip->regmap); int ret; - if (chip->als_ir_mode != MODE_IR) { - ret = isl29028_set_als_ir_mode(chip, MODE_IR); + if (chip->als_ir_mode != ISL29028_MODE_IR) { + ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR); if (ret < 0) { dev_err(dev, "Error in enabling IR mode err %d\n", ret); return ret; } - chip->als_ir_mode = MODE_IR; + chip->als_ir_mode = ISL29028_MODE_IR; } return isl29028_read_als_ir(chip, ir_data); } @@ -383,8 +383,8 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, } static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, - "1, 3, 5, 10, 13, 20, 83, 100"); -static IIO_CONST_ATTR(in_illuminance_scale_available, "125, 2000"); + "1 3 5 10 13 20 83 100"); +static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000"); #define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) @@ -428,7 +428,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip) chip->enable_prox = false; chip->prox_sampling = 20; chip->lux_scale = 2000; - chip->als_ir_mode = MODE_NONE; + chip->als_ir_mode = ISL29028_MODE_NONE; ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); if (ret < 0) { @@ -462,7 +462,7 @@ static int isl29028_chip_init(struct isl29028_chip *chip) return ret; } -static bool is_volatile_reg(struct device *dev, unsigned int reg) +static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case ISL29028_REG_INTERRUPT: @@ -478,7 +478,7 @@ static bool is_volatile_reg(struct device *dev, unsigned int reg) static const struct regmap_config isl29028_regmap_config = { .reg_bits = 8, .val_bits = 8, - .volatile_reg = is_volatile_reg, + .volatile_reg = isl29028_is_volatile_reg, .max_register = ISL29028_NUM_REGS - 1, .num_reg_defaults_raw = ISL29028_NUM_REGS, .cache_type = REGCACHE_RBTREE, @@ -546,7 +546,6 @@ static const struct of_device_id isl29028_of_match[] = { MODULE_DEVICE_TABLE(of, isl29028_of_match); static struct i2c_driver isl29028_driver = { - .class = I2C_CLASS_HWMON, .driver = { .name = "isl29028", .of_match_table = isl29028_of_match, diff --git a/drivers/staging/iio/meter/ade7854.c b/drivers/staging/iio/meter/ade7854.c index 75e8685e6df2..24edbc39ab4e 100644 --- a/drivers/staging/iio/meter/ade7854.c +++ b/drivers/staging/iio/meter/ade7854.c @@ -23,8 +23,8 @@ #include "ade7854.h" static ssize_t ade7854_read_8bit(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { int ret; u8 val = 0; @@ -40,8 +40,8 @@ static ssize_t ade7854_read_8bit(struct device *dev, } static ssize_t ade7854_read_16bit(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { int ret; u16 val = 0; @@ -57,8 +57,8 @@ static ssize_t ade7854_read_16bit(struct device *dev, } static ssize_t ade7854_read_24bit(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { int ret; u32 val; @@ -74,8 +74,8 @@ static ssize_t ade7854_read_24bit(struct device *dev, } static ssize_t ade7854_read_32bit(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { int ret; u32 val = 0; @@ -91,9 +91,9 @@ static ssize_t ade7854_read_32bit(struct device *dev, } static ssize_t ade7854_write_8bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) + struct device_attribute *attr, + const char *buf, + size_t len) { struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -112,9 +112,9 @@ error_ret: } static ssize_t ade7854_write_16bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) + struct device_attribute *attr, + const char *buf, + size_t len) { struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -133,9 +133,9 @@ error_ret: } static ssize_t ade7854_write_24bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) + struct device_attribute *attr, + const char *buf, + size_t len) { struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct iio_dev *indio_dev = dev_to_iio_dev(dev); @@ -154,9 +154,9 @@ error_ret: } static ssize_t ade7854_write_32bit(struct device *dev, - struct device_attribute *attr, - const char *buf, - size_t len) + struct device_attribute *attr, + const char *buf, + size_t len) { struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); struct iio_dev *indio_dev = dev_to_iio_dev(dev); |