diff options
Diffstat (limited to 'drivers/iio')
-rw-r--r-- | drivers/iio/accel/bmc150-accel.c | 40 | ||||
-rw-r--r-- | drivers/iio/accel/kxcjk-1013.c | 4 | ||||
-rw-r--r-- | drivers/iio/adc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/adc/axp288_adc.c | 261 | ||||
-rw-r--r-- | drivers/iio/adc/men_z188_adc.c | 1 | ||||
-rw-r--r-- | drivers/iio/gyro/bmg160.c | 53 | ||||
-rw-r--r-- | drivers/iio/light/tsl4531.c | 7 | ||||
-rw-r--r-- | drivers/iio/proximity/as3935.c | 2 |
9 files changed, 362 insertions, 15 deletions
diff --git a/drivers/iio/accel/bmc150-accel.c b/drivers/iio/accel/bmc150-accel.c index 22c096c..513bd6d 100644 --- a/drivers/iio/accel/bmc150-accel.c +++ b/drivers/iio/accel/bmc150-accel.c @@ -44,6 +44,9 @@ #define BMC150_ACCEL_REG_INT_STATUS_2 0x0B #define BMC150_ACCEL_ANY_MOTION_MASK 0x07 +#define BMC150_ACCEL_ANY_MOTION_BIT_X BIT(0) +#define BMC150_ACCEL_ANY_MOTION_BIT_Y BIT(1) +#define BMC150_ACCEL_ANY_MOTION_BIT_Z BIT(2) #define BMC150_ACCEL_ANY_MOTION_BIT_SIGN BIT(3) #define BMC150_ACCEL_REG_PMU_LPW 0x11 @@ -92,9 +95,9 @@ #define BMC150_ACCEL_SLOPE_THRES_MASK 0xFF /* Slope duration in terms of number of samples */ -#define BMC150_ACCEL_DEF_SLOPE_DURATION 2 +#define BMC150_ACCEL_DEF_SLOPE_DURATION 1 /* in terms of multiples of g's/LSB, based on range */ -#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 5 +#define BMC150_ACCEL_DEF_SLOPE_THRESHOLD 1 #define BMC150_ACCEL_REG_XOUT_L 0x02 @@ -536,6 +539,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmc150_accel_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } @@ -811,6 +817,7 @@ static int bmc150_accel_write_event_config(struct iio_dev *indio_dev, ret = bmc150_accel_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -846,7 +853,7 @@ static const struct attribute_group bmc150_accel_attrs_group = { static const struct iio_event_spec bmc150_accel_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) @@ -1054,6 +1061,7 @@ static int bmc150_accel_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmc150_accel_setup_new_data_interrupt(data, state); if (ret < 0) { + bmc150_accel_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -1092,12 +1100,26 @@ static irqreturn_t bmc150_accel_event_handler(int irq, void *private) else dir = IIO_EV_DIR_RISING; - if (ret & BMC150_ACCEL_ANY_MOTION_MASK) + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_X) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Y) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_Y, IIO_EV_TYPE_ROC, - IIO_EV_DIR_EITHER), + dir), + data->timestamp); + if (ret & BMC150_ACCEL_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ACCEL, + 0, + IIO_MOD_Z, + IIO_EV_TYPE_ROC, + dir), data->timestamp); ack_intr_status: if (!data->dready_trigger_on) @@ -1354,10 +1376,14 @@ static int bmc150_accel_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmc150_accel_data *data = iio_priv(indio_dev); + int ret; dev_dbg(&data->client->dev, __func__); + ret = bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + if (ret < 0) + return -EAGAIN; - return bmc150_accel_set_mode(data, BMC150_ACCEL_SLEEP_MODE_SUSPEND, 0); + return 0; } static int bmc150_accel_runtime_resume(struct device *dev) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 98909a9..320aa72 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -269,6 +269,8 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) return ret; } + ret &= ~(KXCJK1013_REG_CTRL1_BIT_GSEL0 | + KXCJK1013_REG_CTRL1_BIT_GSEL1); ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); @@ -894,7 +896,7 @@ static const struct attribute_group kxcjk1013_attrs_group = { static const struct iio_event_spec kxcjk1013_event = { .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_PERIOD) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 88bdc8f..bc4e787 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -127,6 +127,14 @@ config AT91_ADC help Say yes here to build support for Atmel AT91 ADC. +config AXP288_ADC + tristate "X-Powers AXP288 ADC driver" + depends on MFD_AXP20X + help + Say yes here to have support for X-Powers power management IC (PMIC) ADC + device. Depending on platform configuration, this general purpose ADC can + be used for sampling sensors such as thermal resistors. + config EXYNOS_ADC tristate "Exynos ADC driver support" depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index cb88a6a..f30093f 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o +obj-$(CONFIG_AXP288_ADC) += axp288_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_MAX1027) += max1027.o diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 0000000..08bcfb0 --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c @@ -0,0 +1,261 @@ +/* + * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver + * + * Copyright (C) 2014 Intel Corporation + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/regmap.h> +#include <linux/mfd/axp20x.h> +#include <linux/platform_device.h> + +#include <linux/iio/iio.h> +#include <linux/iio/machine.h> +#include <linux/iio/driver.h> + +#define AXP288_ADC_EN_MASK 0xF1 +#define AXP288_ADC_TS_PIN_GPADC 0xF2 +#define AXP288_ADC_TS_PIN_ON 0xF3 + +enum axp288_adc_id { + AXP288_ADC_TS, + AXP288_ADC_PMIC, + AXP288_ADC_GP, + AXP288_ADC_BATT_CHRG_I, + AXP288_ADC_BATT_DISCHRG_I, + AXP288_ADC_BATT_V, + AXP288_ADC_NR_CHAN, +}; + +struct axp288_adc_info { + int irq; + struct regmap *regmap; +}; + +static const struct iio_chan_spec const axp288_adc_channels[] = { + { + .indexed = 1, + .type = IIO_TEMP, + .channel = 0, + .address = AXP288_TS_ADC_H, + .datasheet_name = "TS_PIN", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 1, + .address = AXP288_PMIC_ADC_H, + .datasheet_name = "PMIC_TEMP", + }, { + .indexed = 1, + .type = IIO_TEMP, + .channel = 2, + .address = AXP288_GP_ADC_H, + .datasheet_name = "GPADC", + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 3, + .address = AXP20X_BATT_CHRG_I_H, + .datasheet_name = "BATT_CHG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_CURRENT, + .channel = 4, + .address = AXP20X_BATT_DISCHRG_I_H, + .datasheet_name = "BATT_DISCHRG_I", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .indexed = 1, + .type = IIO_VOLTAGE, + .channel = 5, + .address = AXP20X_BATT_V_H, + .datasheet_name = "BATT_V", + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, +}; + +#define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ + _consumer_channel) \ + { \ + .adc_channel_label = _adc_channel_label, \ + .consumer_dev_name = _consumer_dev_name, \ + .consumer_channel = _consumer_channel, \ + } + +/* for consumer drivers */ +static struct iio_map axp288_adc_default_maps[] = { + AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), + AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), + AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), + AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), + AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), + AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), + {}, +}; + +static int axp288_adc_read_channel(int *val, unsigned long address, + struct regmap *regmap) +{ + u8 buf[2]; + + if (regmap_bulk_read(regmap, address, buf, 2)) + return -EIO; + *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); + + return IIO_VAL_INT; +} + +static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, + unsigned long address) +{ + /* channels other than GPADC do not need to switch TS pin */ + if (address != AXP288_GP_ADC_H) + return 0; + + return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); +} + +static int axp288_adc_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret; + struct axp288_adc_info *info = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, + chan->address)) { + dev_err(&indio_dev->dev, "GPADC mode\n"); + ret = -EINVAL; + break; + } + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, + chan->address)) + dev_err(&indio_dev->dev, "TS pin restore\n"); + break; + case IIO_CHAN_INFO_PROCESSED: + ret = axp288_adc_read_channel(val, chan->address, info->regmap); + break; + default: + ret = -EINVAL; + } + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int axp288_adc_set_state(struct regmap *regmap) +{ + /* ADC should be always enabled for internal FG to function */ + if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) + return -EIO; + + return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); +} + +static const struct iio_info axp288_adc_iio_info = { + .read_raw = &axp288_adc_read_raw, + .driver_module = THIS_MODULE, +}; + +static int axp288_adc_probe(struct platform_device *pdev) +{ + int ret; + struct axp288_adc_info *info; + struct iio_dev *indio_dev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); + if (!indio_dev) + return -ENOMEM; + + info = iio_priv(indio_dev); + info->irq = platform_get_irq(pdev, 0); + if (info->irq < 0) { + dev_err(&pdev->dev, "no irq resource?\n"); + return info->irq; + } + platform_set_drvdata(pdev, indio_dev); + info->regmap = axp20x->regmap; + /* + * Set ADC to enabled state at all time, including system suspend. + * otherwise internal fuel gauge functionality may be affected. + */ + ret = axp288_adc_set_state(axp20x->regmap); + if (ret) { + dev_err(&pdev->dev, "unable to enable ADC device\n"); + return ret; + } + + indio_dev->dev.parent = &pdev->dev; + indio_dev->name = pdev->name; + indio_dev->channels = axp288_adc_channels; + indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); + indio_dev->info = &axp288_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register iio device\n"); + goto err_array_unregister; + } + return 0; + +err_array_unregister: + iio_map_array_unregister(indio_dev); + + return ret; +} + +static int axp288_adc_remove(struct platform_device *pdev) +{ + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + + iio_device_unregister(indio_dev); + iio_map_array_unregister(indio_dev); + + return 0; +} + +static struct platform_device_id axp288_adc_id_table[] = { + { .name = "axp288_adc" }, + {}, +}; + +static struct platform_driver axp288_adc_driver = { + .probe = axp288_adc_probe, + .remove = axp288_adc_remove, + .id_table = axp288_adc_id_table, + .driver = { + .name = "axp288_adc", + }, +}; + +MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); + +module_platform_driver(axp288_adc_driver); + +MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); +MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c index b58d630..d095efe 100644 --- a/drivers/iio/adc/men_z188_adc.c +++ b/drivers/iio/adc/men_z188_adc.c @@ -152,6 +152,7 @@ static void men_z188_remove(struct mcb_device *dev) static const struct mcb_device_id men_z188_ids[] = { { .device = 0xbc }, + { } }; MODULE_DEVICE_TABLE(mcb, men_z188_ids); diff --git a/drivers/iio/gyro/bmg160.c b/drivers/iio/gyro/bmg160.c index 1f967e0d..d2fa526 100644 --- a/drivers/iio/gyro/bmg160.c +++ b/drivers/iio/gyro/bmg160.c @@ -67,6 +67,9 @@ #define BMG160_REG_INT_EN_0 0x15 #define BMG160_DATA_ENABLE_INT BIT(7) +#define BMG160_REG_INT_EN_1 0x16 +#define BMG160_INT1_BIT_OD BIT(1) + #define BMG160_REG_XOUT_L 0x02 #define BMG160_AXIS_TO_REG(axis) (BMG160_REG_XOUT_L + (axis * 2)) @@ -82,6 +85,9 @@ #define BMG160_REG_INT_STATUS_2 0x0B #define BMG160_ANY_MOTION_MASK 0x07 +#define BMG160_ANY_MOTION_BIT_X BIT(0) +#define BMG160_ANY_MOTION_BIT_Y BIT(1) +#define BMG160_ANY_MOTION_BIT_Z BIT(2) #define BMG160_REG_TEMP 0x08 #define BMG160_TEMP_CENTER_VAL 23 @@ -222,6 +228,19 @@ static int bmg160_chip_init(struct bmg160_data *data) data->slope_thres = ret; /* Set default interrupt mode */ + ret = i2c_smbus_read_byte_data(data->client, BMG160_REG_INT_EN_1); + if (ret < 0) { + dev_err(&data->client->dev, "Error reading reg_int_en_1\n"); + return ret; + } + ret &= ~BMG160_INT1_BIT_OD; + ret = i2c_smbus_write_byte_data(data->client, + BMG160_REG_INT_EN_1, ret); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_int_en_1\n"); + return ret; + } + ret = i2c_smbus_write_byte_data(data->client, BMG160_REG_INT_RST_LATCH, BMG160_INT_MODE_LATCH_INT | @@ -250,6 +269,9 @@ static int bmg160_set_power_state(struct bmg160_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: bmg160_set_power_state for %d\n", on); + if (on) + pm_runtime_put_noidle(&data->client->dev); + return ret; } #endif @@ -705,6 +727,7 @@ static int bmg160_write_event_config(struct iio_dev *indio_dev, ret = bmg160_setup_any_motion_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -743,7 +766,7 @@ static const struct attribute_group bmg160_attrs_group = { static const struct iio_event_spec bmg160_event = { .type = IIO_EV_TYPE_ROC, - .dir = IIO_EV_DIR_RISING | IIO_EV_DIR_FALLING, + .dir = IIO_EV_DIR_EITHER, .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE) }; @@ -871,6 +894,7 @@ static int bmg160_data_rdy_trigger_set_state(struct iio_trigger *trig, else ret = bmg160_setup_new_data_interrupt(data, state); if (ret < 0) { + bmg160_set_power_state(data, false); mutex_unlock(&data->mutex); return ret; } @@ -908,10 +932,24 @@ static irqreturn_t bmg160_event_handler(int irq, void *private) else dir = IIO_EV_DIR_FALLING; - if (ret & BMG160_ANY_MOTION_MASK) + if (ret & BMG160_ANY_MOTION_BIT_X) iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, 0, - IIO_MOD_X_OR_Y_OR_Z, + IIO_MOD_X, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Y) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Y, + IIO_EV_TYPE_ROC, + dir), + data->timestamp); + if (ret & BMG160_ANY_MOTION_BIT_Z) + iio_push_event(indio_dev, IIO_MOD_EVENT_CODE(IIO_ANGL_VEL, + 0, + IIO_MOD_Z, IIO_EV_TYPE_ROC, dir), data->timestamp); @@ -1169,8 +1207,15 @@ static int bmg160_runtime_suspend(struct device *dev) { struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); struct bmg160_data *data = iio_priv(indio_dev); + int ret; + + ret = bmg160_set_mode(data, BMG160_MODE_SUSPEND); + if (ret < 0) { + dev_err(&data->client->dev, "set mode failed\n"); + return -EAGAIN; + } - return bmg160_set_mode(data, BMG160_MODE_SUSPEND); + return 0; } static int bmg160_runtime_resume(struct device *dev) diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c index a15006e..0763b86 100644 --- a/drivers/iio/light/tsl4531.c +++ b/drivers/iio/light/tsl4531.c @@ -230,9 +230,12 @@ static int tsl4531_resume(struct device *dev) return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL, TSL4531_MODE_NORMAL); } -#endif static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume); +#define TSL4531_PM_OPS (&tsl4531_pm_ops) +#else +#define TSL4531_PM_OPS NULL +#endif static const struct i2c_device_id tsl4531_id[] = { { "tsl4531", 0 }, @@ -243,7 +246,7 @@ MODULE_DEVICE_TABLE(i2c, tsl4531_id); static struct i2c_driver tsl4531_driver = { .driver = { .name = TSL4531_DRV_NAME, - .pm = &tsl4531_pm_ops, + .pm = TSL4531_PM_OPS, .owner = THIS_MODULE, }, .probe = tsl4531_probe, diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 5e780ef..8349cc0 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -330,7 +330,7 @@ static int as3935_probe(struct spi_device *spi) return -EINVAL; } - indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(st)); + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) return -ENOMEM; |