diff options
Diffstat (limited to 'drivers/iio')
54 files changed, 3603 insertions, 305 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 5dd0e120a50..345395e9dc6 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -4,6 +4,7 @@ menuconfig IIO tristate "Industrial I/O support" + select ANON_INODES help The industrial I/O subsystem provides a unified framework for drivers for many different types of embedded sensors using a @@ -74,6 +75,7 @@ if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" endif #IIO_TRIGGER source "drivers/iio/pressure/Kconfig" +source "drivers/iio/proximity/Kconfig" source "drivers/iio/temperature/Kconfig" endif # IIO diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 887d39090d7..698afc2d17c 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -24,5 +24,6 @@ obj-y += light/ obj-y += magnetometer/ obj-y += orientation/ obj-y += pressure/ +obj-y += proximity/ obj-y += temperature/ obj-y += trigger/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index e23e5085065..1e120fa1e15 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -65,4 +65,16 @@ config KXSD9 Say yes here to build support for the Kionix KXSD9 accelerometer. Currently this only supports the device via an SPI interface. +config MMA8452 + tristate "Freescale MMA8452Q Accelerometer Driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for the Freescale MMA8452Q 3-axis + accelerometer. + + To compile this driver as a module, choose M here: the module + will be called mma8452. + endmenu diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index c48d15f2561..dc0e379c259 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_BMA180) += bma180.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXSD9) += kxsd9.o +obj-$(CONFIG_MMA8452) += mma8452.o obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o st_accel-y := st_accel_core.o diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 3dcdbad6545..69abf9163df 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -42,6 +43,10 @@ struct accel_3d_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; u32 accel_val[ACCEL_3D_CHANNEL_MAX]; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { @@ -56,6 +61,7 @@ static const struct iio_chan_spec accel_3d_channels[] = { .type = IIO_ACCEL, .modified = 1, .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -65,6 +71,7 @@ static const struct iio_chan_spec accel_3d_channels[] = { .type = IIO_ACCEL, .modified = 1, .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -74,6 +81,7 @@ static const struct iio_chan_spec accel_3d_channels[] = { .type = IIO_ACCEL, .modified = 1, .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -104,31 +112,42 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; switch (mask) { case 0: + poll_value = hid_sensor_read_poll_value( + &accel_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&accel_state->common_attributes, true); + msleep_interruptible(poll_value * 2); report_id = accel_state->accel[chan->scan_index].report_id; address = accel_3d_addresses[chan->scan_index]; if (report_id >= 0) *val = sensor_hub_input_attr_get_raw_value( - accel_state->common_attributes.hsdev, - HID_USAGE_SENSOR_ACCEL_3D, address, - report_id); + accel_state->common_attributes.hsdev, + HID_USAGE_SENSOR_ACCEL_3D, address, + report_id); else { *val = 0; + hid_sensor_power_state(&accel_state->common_attributes, + false); return -EINVAL; } + hid_sensor_power_state(&accel_state->common_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units; - ret_type = IIO_VAL_INT; + *val = accel_state->scale_pre_decml; + *val2 = accel_state->scale_post_decml; + ret_type = accel_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo); + *val = accel_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -197,9 +216,8 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct accel_3d_state *accel_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n", - accel_state->common_attributes.data_ready); - if (accel_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "accel_3d_proc_event\n"); + if (atomic_read(&accel_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, accel_state->accel_val, sizeof(accel_state->accel_val)); @@ -262,6 +280,11 @@ static int accel_3d_parse_report(struct platform_device *pdev, st->accel[1].index, st->accel[1].report_id, st->accel[2].index, st->accel[2].report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_ACCEL_3D, + &st->accel[CHANNEL_SCAN_INDEX_X], + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -333,7 +356,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - accel_state->common_attributes.data_ready = false; + atomic_set(&accel_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &accel_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c new file mode 100644 index 00000000000..17aeea17056 --- /dev/null +++ b/drivers/iio/accel/mma8452.c @@ -0,0 +1,439 @@ +/* + * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer + * + * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * 7-bit I2C slave address 0x1c/0x1d (pin selectable) + * + * TODO: interrupt, thresholding, orientation / freefall events, autosleep + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/delay.h> + +#define MMA8452_STATUS 0x00 +#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */ +#define MMA8452_OUT_Y 0x03 +#define MMA8452_OUT_Z 0x05 +#define MMA8452_WHO_AM_I 0x0d +#define MMA8452_DATA_CFG 0x0e +#define MMA8452_OFF_X 0x2f +#define MMA8452_OFF_Y 0x30 +#define MMA8452_OFF_Z 0x31 +#define MMA8452_CTRL_REG1 0x2a +#define MMA8452_CTRL_REG2 0x2b + +#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) + +#define MMA8452_CTRL_DR_MASK (BIT(5) | BIT(4) | BIT(3)) +#define MMA8452_CTRL_DR_SHIFT 3 +#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */ +#define MMA8452_CTRL_ACTIVE BIT(0) + +#define MMA8452_DATA_CFG_FS_MASK (BIT(1) | BIT(0)) +#define MMA8452_DATA_CFG_FS_2G 0 +#define MMA8452_DATA_CFG_FS_4G 1 +#define MMA8452_DATA_CFG_FS_8G 2 + +#define MMA8452_DEVICE_ID 0x2a + +struct mma8452_data { + struct i2c_client *client; + struct mutex lock; + u8 ctrl_reg1; + u8 data_cfg; +}; + +static int mma8452_drdy(struct mma8452_data *data) +{ + int tries = 150; + + while (tries-- > 0) { + int ret = i2c_smbus_read_byte_data(data->client, + MMA8452_STATUS); + if (ret < 0) + return ret; + if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY) + return 0; + msleep(20); + } + + dev_err(&data->client->dev, "data not ready\n"); + return -EIO; +} + +static int mma8452_read(struct mma8452_data *data, __be16 buf[3]) +{ + int ret = mma8452_drdy(data); + if (ret < 0) + return ret; + return i2c_smbus_read_i2c_block_data(data->client, + MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *) buf); +} + +static ssize_t mma8452_show_int_plus_micros(char *buf, + const int (*vals)[2], int n) +{ + size_t len = 0; + + while (n-- > 0) + len += scnprintf(buf + len, PAGE_SIZE - len, + "%d.%06d ", vals[n][0], vals[n][1]); + + /* replace trailing space by newline */ + buf[len - 1] = '\n'; + + return len; +} + +static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n, + int val, int val2) +{ + while (n-- > 0) + if (val == vals[n][0] && val2 == vals[n][1]) + return n; + + return -EINVAL; +} + +static const int mma8452_samp_freq[8][2] = { + {800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000}, + {6, 250000}, {1, 560000} +}; + +static const int mma8452_scales[3][2] = { + {0, 977}, {0, 1953}, {0, 3906} +}; + +static ssize_t mma8452_show_samp_freq_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return mma8452_show_int_plus_micros(buf, mma8452_samp_freq, + ARRAY_SIZE(mma8452_samp_freq)); +} + +static ssize_t mma8452_show_scale_avail(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return mma8452_show_int_plus_micros(buf, mma8452_scales, + ARRAY_SIZE(mma8452_scales)); +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail); +static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO, + mma8452_show_scale_avail, NULL, 0); + +static int mma8452_get_samp_freq_index(struct mma8452_data *data, + int val, int val2) +{ + return mma8452_get_int_plus_micros_index(mma8452_samp_freq, + ARRAY_SIZE(mma8452_samp_freq), val, val2); +} + +static int mma8452_get_scale_index(struct mma8452_data *data, + int val, int val2) +{ + return mma8452_get_int_plus_micros_index(mma8452_scales, + ARRAY_SIZE(mma8452_scales), val, val2); +} + +static int mma8452_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mma8452_data *data = iio_priv(indio_dev); + __be16 buffer[3]; + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + mutex_lock(&data->lock); + ret = mma8452_read(data, buffer); + mutex_unlock(&data->lock); + if (ret < 0) + return ret; + *val = sign_extend32( + be16_to_cpu(buffer[chan->scan_index]) >> 4, 11); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK; + *val = mma8452_scales[i][0]; + *val2 = mma8452_scales[i][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >> + MMA8452_CTRL_DR_SHIFT; + *val = mma8452_samp_freq[i][0]; + *val2 = mma8452_samp_freq[i][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = i2c_smbus_read_byte_data(data->client, MMA8452_OFF_X + + chan->scan_index); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 7); + return IIO_VAL_INT; + } + return -EINVAL; +} + +static int mma8452_standby(struct mma8452_data *data) +{ + return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1, + data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE); +} + +static int mma8452_active(struct mma8452_data *data) +{ + return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1, + data->ctrl_reg1); +} + +static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val) +{ + int ret; + + mutex_lock(&data->lock); + + /* config can only be changed when in standby */ + ret = mma8452_standby(data); + if (ret < 0) + goto fail; + + ret = i2c_smbus_write_byte_data(data->client, reg, val); + if (ret < 0) + goto fail; + + ret = mma8452_active(data); + if (ret < 0) + goto fail; + + ret = 0; +fail: + mutex_unlock(&data->lock); + return ret; +} + +static int mma8452_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct mma8452_data *data = iio_priv(indio_dev); + int i; + + if (iio_buffer_enabled(indio_dev)) + return -EBUSY; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + i = mma8452_get_samp_freq_index(data, val, val2); + if (i < 0) + return -EINVAL; + + data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK; + data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT; + return mma8452_change_config(data, MMA8452_CTRL_REG1, + data->ctrl_reg1); + case IIO_CHAN_INFO_SCALE: + i = mma8452_get_scale_index(data, val, val2); + if (i < 0) + return -EINVAL; + data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK; + data->data_cfg |= i; + return mma8452_change_config(data, MMA8452_DATA_CFG, + data->data_cfg); + case IIO_CHAN_INFO_CALIBBIAS: + if (val < -128 || val > 127) + return -EINVAL; + return mma8452_change_config(data, MMA8452_OFF_X + + chan->scan_index, val); + default: + return -EINVAL; + } +} + +static irqreturn_t mma8452_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct mma8452_data *data = iio_priv(indio_dev); + u8 buffer[16]; /* 3 16-bit channels + padding + ts */ + int ret; + + ret = mma8452_read(data, (__be16 *) buffer); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, + iio_get_time_ns()); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +#define MMA8452_CHANNEL(axis, idx) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ +} + +static const struct iio_chan_spec mma8452_channels[] = { + MMA8452_CHANNEL(X, 0), + MMA8452_CHANNEL(Y, 1), + MMA8452_CHANNEL(Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static struct attribute *mma8452_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group mma8452_group = { + .attrs = mma8452_attributes, +}; + +static const struct iio_info mma8452_info = { + .attrs = &mma8452_group, + .read_raw = &mma8452_read_raw, + .write_raw = &mma8452_write_raw, + .driver_module = THIS_MODULE, +}; + +static const unsigned long mma8452_scan_masks[] = {0x7, 0}; + +static int mma8452_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mma8452_data *data; + struct iio_dev *indio_dev; + int ret; + + ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I); + if (ret < 0) + return ret; + if (ret != MMA8452_DEVICE_ID) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + + i2c_set_clientdata(client, indio_dev); + indio_dev->info = &mma8452_info; + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mma8452_channels; + indio_dev->num_channels = ARRAY_SIZE(mma8452_channels); + indio_dev->available_scan_masks = mma8452_scan_masks; + + data->ctrl_reg1 = MMA8452_CTRL_ACTIVE | + (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT); + ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1, + data->ctrl_reg1); + if (ret < 0) + return ret; + + data->data_cfg = MMA8452_DATA_CFG_FS_2G; + ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG, + data->data_cfg); + if (ret < 0) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + mma8452_trigger_handler, NULL); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto buffer_cleanup; + return 0; + +buffer_cleanup: + iio_triggered_buffer_cleanup(indio_dev); + return ret; +} + +static int mma8452_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + mma8452_standby(iio_priv(indio_dev)); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mma8452_suspend(struct device *dev) +{ + return mma8452_standby(iio_priv(i2c_get_clientdata( + to_i2c_client(dev)))); +} + +static int mma8452_resume(struct device *dev) +{ + return mma8452_active(iio_priv(i2c_get_clientdata( + to_i2c_client(dev)))); +} + +static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume); +#define MMA8452_PM_OPS (&mma8452_pm_ops) +#else +#define MMA8452_PM_OPS NULL +#endif + +static const struct i2c_device_id mma8452_id[] = { + { "mma8452", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mma8452_id); + +static struct i2c_driver mma8452_driver = { + .driver = { + .name = "mma8452", + .pm = MMA8452_PM_OPS, + }, + .probe = mma8452_probe, + .remove = mma8452_remove, + .id_table = mma8452_id, +}; +module_i2c_driver(mma8452_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Freescale MMA8452 accelerometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 38caedc76b9..a2abf7c2ce3 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -459,6 +459,8 @@ int st_accel_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &accel_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_accel_sensors), st_accel_sensors); if (err < 0) @@ -496,6 +498,9 @@ int st_accel_common_probe(struct iio_dev *indio_dev, if (err) goto st_accel_device_register_error; + dev_info(&indio_dev->dev, "registered accelerometer %s\n", + indio_dev->name); + return 0; st_accel_device_register_error: @@ -512,6 +517,8 @@ void st_accel_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (adata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index d86196cfe4b..a80d23628f1 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -96,9 +96,21 @@ config AD7923 To compile this driver as a module, choose M here: the module will be called ad7923. +config AD799X + tristate "Analog Devices AD799x ADC driver" + depends on I2C + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Analog Devices: + ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, ad7998 + i2c analog to digital converters (ADC). Provides direct access + via sysfs. + config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 + depends on INPUT select IIO_BUFFER select IIO_TRIGGERED_BUFFER select SYSFS @@ -106,15 +118,15 @@ config AT91_ADC Say yes here to build support for Atmel AT91 ADC. config EXYNOS_ADC - bool "Exynos ADC driver support" - depends on OF + tristate "Exynos ADC driver support" + depends on ARCH_EXYNOS || (OF && COMPILE_TEST) help Core support for the ADC block found in the Samsung EXYNOS series of SoCs for drivers such as the touchscreen and hwmon to use to share this resource. config LP8788_ADC - bool "LP8788 ADC driver" + tristate "LP8788 ADC driver" depends on MFD_LP8788 help Say yes here to build support for TI LP8788 ADC. @@ -146,11 +158,12 @@ config MCP320X called mcp320x. config MCP3422 - tristate "Microchip Technology MCP3422/3/4 driver" + tristate "Microchip Technology MCP3422/3/4/6/7/8 driver" depends on I2C help - Say yes here to build support for Microchip Technology's MCP3422, - MCP3423 or MCP3424 analog to digital converters. + Say yes here to build support for Microchip Technology's + MCP3422, MCP3423, MCP3424, MCP3426, MCP3427 or MCP3428 + analog to digital converters. This driver can also be built as a module. If so, the module will be called mcp3422. diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index ab346d88c68..9d60f2deaaa 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_AD7476) += ad7476.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o +obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AT91_ADC) += at91_adc.o obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c new file mode 100644 index 00000000000..39b4cb48d73 --- /dev/null +++ b/drivers/iio/adc/ad799x.c @@ -0,0 +1,791 @@ +/* + * iio/adc/ad799x.c + * Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc. + * + * based on iio/adc/max1363 + * Copyright (C) 2008-2010 Jonathan Cameron + * + * based on linux/drivers/i2c/chips/max123x + * Copyright (C) 2002-2004 Stefan Eletzhofer + * + * based on linux/drivers/acron/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * 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. + * + * ad799x.c + * + * Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997, + * ad7998 and similar chips. + * + */ + +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/i2c.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define AD799X_CHANNEL_SHIFT 4 +#define AD799X_STORAGEBITS 16 +/* + * AD7991, AD7995 and AD7999 defines + */ + +#define AD7991_REF_SEL 0x08 +#define AD7991_FLTR 0x04 +#define AD7991_BIT_TRIAL_DELAY 0x02 +#define AD7991_SAMPLE_DELAY 0x01 + +/* + * AD7992, AD7993, AD7994, AD7997 and AD7998 defines + */ + +#define AD7998_FLTR 0x08 +#define AD7998_ALERT_EN 0x04 +#define AD7998_BUSY_ALERT 0x02 +#define AD7998_BUSY_ALERT_POL 0x01 + +#define AD7998_CONV_RES_REG 0x0 +#define AD7998_ALERT_STAT_REG 0x1 +#define AD7998_CONF_REG 0x2 +#define AD7998_CYCLE_TMR_REG 0x3 + +#define AD7998_DATALOW_REG(x) ((x) * 3 + 0x4) +#define AD7998_DATAHIGH_REG(x) ((x) * 3 + 0x5) +#define AD7998_HYST_REG(x) ((x) * 3 + 0x6) + +#define AD7998_CYC_MASK 0x7 +#define AD7998_CYC_DIS 0x0 +#define AD7998_CYC_TCONF_32 0x1 +#define AD7998_CYC_TCONF_64 0x2 +#define AD7998_CYC_TCONF_128 0x3 +#define AD7998_CYC_TCONF_256 0x4 +#define AD7998_CYC_TCONF_512 0x5 +#define AD7998_CYC_TCONF_1024 0x6 +#define AD7998_CYC_TCONF_2048 0x7 + +#define AD7998_ALERT_STAT_CLEAR 0xFF + +/* + * AD7997 and AD7997 defines + */ + +#define AD7997_8_READ_SINGLE 0x80 +#define AD7997_8_READ_SEQUENCE 0x70 +/* TODO: move this into a common header */ +#define RES_MASK(bits) ((1 << (bits)) - 1) + +enum { + ad7991, + ad7995, + ad7999, + ad7992, + ad7993, + ad7994, + ad7997, + ad7998 +}; + +/** + * struct ad799x_chip_info - chip specific information + * @channel: channel specification + * @num_channels: number of channels + * @monitor_mode: whether the chip supports monitor interrupts + * @default_config: device default configuration + * @event_attrs: pointer to the monitor event attribute group + */ +struct ad799x_chip_info { + struct iio_chan_spec channel[9]; + int num_channels; + u16 default_config; + const struct iio_info *info; +}; + +struct ad799x_state { + struct i2c_client *client; + const struct ad799x_chip_info *chip_info; + struct regulator *reg; + struct regulator *vref; + unsigned id; + u16 config; + + u8 *rx_buf; + unsigned int transfer_size; +}; + +/** + * ad799x_trigger_handler() bh of trigger launched polling to ring buffer + * + * Currently there is no option in this driver to disable the saving of + * timestamps within the ring. + **/ +static irqreturn_t ad799x_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad799x_state *st = iio_priv(indio_dev); + int b_sent; + u8 cmd; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | + (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = (*indio_dev->active_scan_mask << AD799X_CHANNEL_SHIFT) | + AD7998_CONV_RES_REG; + break; + case ad7997: + case ad7998: + cmd = AD7997_8_READ_SEQUENCE | AD7998_CONV_RES_REG; + break; + default: + cmd = 0; + } + + b_sent = i2c_smbus_read_i2c_block_data(st->client, + cmd, st->transfer_size, st->rx_buf); + if (b_sent < 0) + goto out; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns()); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/* + * ad799x register access by I2C + */ +static int ad799x_i2c_read16(struct ad799x_state *st, u8 reg, u16 *data) +{ + struct i2c_client *client = st->client; + int ret = 0; + + ret = i2c_smbus_read_word_swapped(client, reg); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + + *data = (u16)ret; + + return 0; +} + +static int ad799x_i2c_read8(struct ad799x_state *st, u8 reg, u8 *data) +{ + struct i2c_client *client = st->client; + int ret = 0; + + ret = i2c_smbus_read_byte_data(client, reg); + if (ret < 0) { + dev_err(&client->dev, "I2C read error\n"); + return ret; + } + + *data = (u8)ret; + + return 0; +} + +static int ad799x_i2c_write16(struct ad799x_state *st, u8 reg, u16 data) +{ + struct i2c_client *client = st->client; + int ret = 0; + + ret = i2c_smbus_write_word_swapped(client, reg, data); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad799x_i2c_write8(struct ad799x_state *st, u8 reg, u8 data) +{ + struct i2c_client *client = st->client; + int ret = 0; + + ret = i2c_smbus_write_byte_data(client, reg, data); + if (ret < 0) + dev_err(&client->dev, "I2C write error\n"); + + return ret; +} + +static int ad7997_8_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad799x_state *st = iio_priv(indio_dev); + + kfree(st->rx_buf); + st->rx_buf = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); + if (!st->rx_buf) + return -ENOMEM; + + st->transfer_size = bitmap_weight(scan_mask, indio_dev->masklength) * 2; + + switch (st->id) { + case ad7997: + case ad7998: + return ad799x_i2c_write16(st, AD7998_CONF_REG, + st->config | (*scan_mask << AD799X_CHANNEL_SHIFT)); + default: + break; + } + + return 0; +} + +static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch) +{ + u16 rxbuf; + u8 cmd; + int ret; + + switch (st->id) { + case ad7991: + case ad7995: + case ad7999: + cmd = st->config | ((1 << ch) << AD799X_CHANNEL_SHIFT); + break; + case ad7992: + case ad7993: + case ad7994: + cmd = (1 << ch) << AD799X_CHANNEL_SHIFT; + break; + case ad7997: + case ad7998: + cmd = (ch << AD799X_CHANNEL_SHIFT) | AD7997_8_READ_SINGLE; + break; + default: + return -EINVAL; + } + + ret = ad799x_i2c_read16(st, cmd, &rxbuf); + if (ret < 0) + return ret; + + return rxbuf; +} + +static int ad799x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&indio_dev->mlock); + if (iio_buffer_enabled(indio_dev)) + ret = -EBUSY; + else + ret = ad799x_scan_direct(st, chan->scan_index); + mutex_unlock(&indio_dev->mlock); + + if (ret < 0) + return ret; + *val = (ret >> chan->scan_type.shift) & + RES_MASK(chan->scan_type.realbits); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = regulator_get_voltage(st->vref); + if (ret < 0) + return ret; + *val = ret / 1000; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} +static const unsigned int ad7998_frequencies[] = { + [AD7998_CYC_DIS] = 0, + [AD7998_CYC_TCONF_32] = 15625, + [AD7998_CYC_TCONF_64] = 7812, + [AD7998_CYC_TCONF_128] = 3906, + [AD7998_CYC_TCONF_512] = 976, + [AD7998_CYC_TCONF_1024] = 488, + [AD7998_CYC_TCONF_2048] = 244, +}; +static ssize_t ad799x_read_frequency(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + int ret; + u8 val; + ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &val); + if (ret) + return ret; + + val &= AD7998_CYC_MASK; + + return sprintf(buf, "%u\n", ad7998_frequencies[val]); +} + +static ssize_t ad799x_write_frequency(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ad799x_state *st = iio_priv(indio_dev); + + long val; + int ret, i; + u8 t; + + ret = kstrtol(buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&indio_dev->mlock); + ret = ad799x_i2c_read8(st, AD7998_CYCLE_TMR_REG, &t); + if (ret) + goto error_ret_mutex; + /* Wipe the bits clean */ + t &= ~AD7998_CYC_MASK; + + for (i = 0; i < ARRAY_SIZE(ad7998_frequencies); i++) + if (val == ad7998_frequencies[i]) + break; + if (i == ARRAY_SIZE(ad7998_frequencies)) { + ret = -EINVAL; + goto error_ret_mutex; + } + t |= i; + ret = ad799x_i2c_write8(st, AD7998_CYCLE_TMR_REG, t); + +error_ret_mutex: + mutex_unlock(&indio_dev->mlock); + + return ret ? ret : len; +} + +static int ad799x_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + return 1; +} + +static unsigned int ad799x_threshold_reg(const struct iio_chan_spec *chan, + enum iio_event_direction dir, + enum iio_event_info info) +{ + switch (info) { + case IIO_EV_INFO_VALUE: + if (dir == IIO_EV_DIR_FALLING) + return AD7998_DATALOW_REG(chan->channel); + else + return AD7998_DATAHIGH_REG(chan->channel); + case IIO_EV_INFO_HYSTERESIS: + return AD7998_HYST_REG(chan->channel); + default: + return -EINVAL; + } + + return 0; +} + +static int ad799x_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + + mutex_lock(&indio_dev->mlock); + ret = ad799x_i2c_write16(st, ad799x_threshold_reg(chan, dir, info), + val); + mutex_unlock(&indio_dev->mlock); + + return ret; +} + +static int ad799x_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + int ret; + struct ad799x_state *st = iio_priv(indio_dev); + u16 valin; + + mutex_lock(&indio_dev->mlock); + ret = ad799x_i2c_read16(st, ad799x_threshold_reg(chan, dir, info), + &valin); + mutex_unlock(&indio_dev->mlock); + if (ret < 0) + return ret; + *val = valin; + + return IIO_VAL_INT; +} + +static irqreturn_t ad799x_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ad799x_state *st = iio_priv(private); + u8 status; + int i, ret; + + ret = ad799x_i2c_read8(st, AD7998_ALERT_STAT_REG, &status); + if (ret) + goto done; + + if (!status) + goto done; + + ad799x_i2c_write8(st, AD7998_ALERT_STAT_REG, AD7998_ALERT_STAT_CLEAR); + + for (i = 0; i < 8; i++) { + if (status & (1 << i)) + iio_push_event(indio_dev, + i & 0x1 ? + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING) : + IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE, + (i >> 1), + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_FALLING), + iio_get_time_ns()); + } + +done: + return IRQ_HANDLED; +} + +static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, + ad799x_read_frequency, + ad799x_write_frequency); +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0"); + +static struct attribute *ad799x_event_attributes[] = { + &iio_dev_attr_sampling_frequency.dev_attr.attr, + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL, +}; + +static struct attribute_group ad799x_event_attrs_group = { + .attrs = ad799x_event_attributes, + .name = "events", +}; + +static const struct iio_info ad7991_info = { + .read_raw = &ad799x_read_raw, + .driver_module = THIS_MODULE, +}; + +static const struct iio_info ad7993_4_7_8_info = { + .read_raw = &ad799x_read_raw, + .event_attrs = &ad799x_event_attrs_group, + .read_event_config = &ad799x_read_event_config, + .read_event_value = &ad799x_read_event_value, + .write_event_value = &ad799x_write_event_value, + .driver_module = THIS_MODULE, + .update_scan_mode = ad7997_8_update_scan_mode, +}; + +static const struct iio_event_spec ad799x_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_HYSTERESIS), + }, +}; + +#define _AD799X_CHANNEL(_index, _realbits, _ev_spec, _num_ev_spec) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_index), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_index = (_index), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (_realbits), \ + .storagebits = 16, \ + .shift = 12 - (_realbits), \ + .endianness = IIO_BE, \ + }, \ + .event_spec = _ev_spec, \ + .num_event_specs = _num_ev_spec, \ +} + +#define AD799X_CHANNEL(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, NULL, 0) + +#define AD799X_CHANNEL_WITH_EVENTS(_index, _realbits) \ + _AD799X_CHANNEL(_index, _realbits, ad799x_events, \ + ARRAY_SIZE(ad799x_events)) + +static const struct ad799x_chip_info ad799x_chip_info_tbl[] = { + [ad7991] = { + .channel = { + AD799X_CHANNEL(0, 12), + AD799X_CHANNEL(1, 12), + AD799X_CHANNEL(2, 12), + AD799X_CHANNEL(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .num_channels = 5, + .info = &ad7991_info, + }, + [ad7995] = { + .channel = { + AD799X_CHANNEL(0, 10), + AD799X_CHANNEL(1, 10), + AD799X_CHANNEL(2, 10), + AD799X_CHANNEL(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .num_channels = 5, + .info = &ad7991_info, + }, + [ad7999] = { + .channel = { + AD799X_CHANNEL(0, 8), + AD799X_CHANNEL(1, 8), + AD799X_CHANNEL(2, 8), + AD799X_CHANNEL(3, 8), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .num_channels = 5, + .info = &ad7991_info, + }, + [ad7992] = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + IIO_CHAN_SOFT_TIMESTAMP(3), + }, + .num_channels = 3, + .default_config = AD7998_ALERT_EN, + .info = &ad7993_4_7_8_info, + }, + [ad7993] = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .num_channels = 5, + .default_config = AD7998_ALERT_EN, + .info = &ad7993_4_7_8_info, + }, + [ad7994] = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + IIO_CHAN_SOFT_TIMESTAMP(4), + }, + .num_channels = 5, + .default_config = AD7998_ALERT_EN, + .info = &ad7993_4_7_8_info, + }, + [ad7997] = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 10), + AD799X_CHANNEL_WITH_EVENTS(1, 10), + AD799X_CHANNEL_WITH_EVENTS(2, 10), + AD799X_CHANNEL_WITH_EVENTS(3, 10), + AD799X_CHANNEL(4, 10), + AD799X_CHANNEL(5, 10), + AD799X_CHANNEL(6, 10), + AD799X_CHANNEL(7, 10), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .num_channels = 9, + .default_config = AD7998_ALERT_EN, + .info = &ad7993_4_7_8_info, + }, + [ad7998] = { + .channel = { + AD799X_CHANNEL_WITH_EVENTS(0, 12), + AD799X_CHANNEL_WITH_EVENTS(1, 12), + AD799X_CHANNEL_WITH_EVENTS(2, 12), + AD799X_CHANNEL_WITH_EVENTS(3, 12), + AD799X_CHANNEL(4, 12), + AD799X_CHANNEL(5, 12), + AD799X_CHANNEL(6, 12), + AD799X_CHANNEL(7, 12), + IIO_CHAN_SOFT_TIMESTAMP(8), + }, + .num_channels = 9, + .default_config = AD7998_ALERT_EN, + .info = &ad7993_4_7_8_info, + }, +}; + +static int ad799x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + struct ad799x_state *st; + struct iio_dev *indio_dev; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + /* this is only used for device removal purposes */ + i2c_set_clientdata(client, indio_dev); + + st->id = id->driver_data; + st->chip_info = &ad799x_chip_info_tbl[st->id]; + st->config = st->chip_info->default_config; + + /* TODO: Add pdata options for filtering and bit delay */ + + st->reg = devm_regulator_get(&client->dev, "vcc"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + ret = regulator_enable(st->reg); + if (ret) + return ret; + st->vref = devm_regulator_get(&client->dev, "vref"); + if (IS_ERR(st->vref)) { + ret = PTR_ERR(st->vref); + goto error_disable_reg; + } + ret = regulator_enable(st->vref); + if (ret) + goto error_disable_reg; + + st->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->info = st->chip_info->info; + + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = st->chip_info->channel; + indio_dev->num_channels = st->chip_info->num_channels; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &ad799x_trigger_handler, NULL); + if (ret) + goto error_disable_vref; + + if (client->irq > 0) { + ret = devm_request_threaded_irq(&client->dev, + client->irq, + NULL, + ad799x_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + client->name, + indio_dev); + if (ret) + goto error_cleanup_ring; + } + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_ring; + + return 0; + +error_cleanup_ring: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_vref: + regulator_disable(st->vref); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int ad799x_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct ad799x_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->vref); + regulator_disable(st->reg); + kfree(st->rx_buf); + + return 0; +} + +static const struct i2c_device_id ad799x_id[] = { + { "ad7991", ad7991 }, + { "ad7995", ad7995 }, + { "ad7999", ad7999 }, + { "ad7992", ad7992 }, + { "ad7993", ad7993 }, + { "ad7994", ad7994 }, + { "ad7997", ad7997 }, + { "ad7998", ad7998 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad799x_id); + +static struct i2c_driver ad799x_driver = { + .driver = { + .name = "ad799x", + }, + .probe = ad799x_probe, + .remove = ad799x_remove, + .id_table = ad799x_id, +}; +module_i2c_driver(ad799x_driver); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("Analog Devices AD799x ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 5b1aa027c03..3b5bacd4d8d 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -31,7 +31,108 @@ #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> -#include <mach/at91_adc.h> +/* Registers */ +#define AT91_ADC_CR 0x00 /* Control Register */ +#define AT91_ADC_SWRST (1 << 0) /* Software Reset */ +#define AT91_ADC_START (1 << 1) /* Start Conversion */ + +#define AT91_ADC_MR 0x04 /* Mode Register */ +#define AT91_ADC_TSAMOD (3 << 0) /* ADC mode */ +#define AT91_ADC_TSAMOD_ADC_ONLY_MODE (0 << 0) /* ADC Mode */ +#define AT91_ADC_TSAMOD_TS_ONLY_MODE (1 << 0) /* Touch Screen Only Mode */ +#define AT91_ADC_TRGEN (1 << 0) /* Trigger Enable */ +#define AT91_ADC_TRGSEL (7 << 1) /* Trigger Selection */ +#define AT91_ADC_TRGSEL_TC0 (0 << 1) +#define AT91_ADC_TRGSEL_TC1 (1 << 1) +#define AT91_ADC_TRGSEL_TC2 (2 << 1) +#define AT91_ADC_TRGSEL_EXTERNAL (6 << 1) +#define AT91_ADC_LOWRES (1 << 4) /* Low Resolution */ +#define AT91_ADC_SLEEP (1 << 5) /* Sleep Mode */ +#define AT91_ADC_PENDET (1 << 6) /* Pen contact detection enable */ +#define AT91_ADC_PRESCAL_9260 (0x3f << 8) /* Prescalar Rate Selection */ +#define AT91_ADC_PRESCAL_9G45 (0xff << 8) +#define AT91_ADC_PRESCAL_(x) ((x) << 8) +#define AT91_ADC_STARTUP_9260 (0x1f << 16) /* Startup Up Time */ +#define AT91_ADC_STARTUP_9G45 (0x7f << 16) +#define AT91_ADC_STARTUP_9X5 (0xf << 16) +#define AT91_ADC_STARTUP_(x) ((x) << 16) +#define AT91_ADC_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_SHTIM_(x) ((x) << 24) +#define AT91_ADC_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_PENDBC_(x) ((x) << 28) + +#define AT91_ADC_TSR 0x0C +#define AT91_ADC_TSR_SHTIM (0xf << 24) /* Sample & Hold Time */ +#define AT91_ADC_TSR_SHTIM_(x) ((x) << 24) + +#define AT91_ADC_CHER 0x10 /* Channel Enable Register */ +#define AT91_ADC_CHDR 0x14 /* Channel Disable Register */ +#define AT91_ADC_CHSR 0x18 /* Channel Status Register */ +#define AT91_ADC_CH(n) (1 << (n)) /* Channel Number */ + +#define AT91_ADC_SR 0x1C /* Status Register */ +#define AT91_ADC_EOC(n) (1 << (n)) /* End of Conversion on Channel N */ +#define AT91_ADC_OVRE(n) (1 << ((n) + 8))/* Overrun Error on Channel N */ +#define AT91_ADC_DRDY (1 << 16) /* Data Ready */ +#define AT91_ADC_GOVRE (1 << 17) /* General Overrun Error */ +#define AT91_ADC_ENDRX (1 << 18) /* End of RX Buffer */ +#define AT91_ADC_RXFUFF (1 << 19) /* RX Buffer Full */ + +#define AT91_ADC_SR_9X5 0x30 /* Status Register for 9x5 */ +#define AT91_ADC_SR_DRDY_9X5 (1 << 24) /* Data Ready */ + +#define AT91_ADC_LCDR 0x20 /* Last Converted Data Register */ +#define AT91_ADC_LDATA (0x3ff) + +#define AT91_ADC_IER 0x24 /* Interrupt Enable Register */ +#define AT91_ADC_IDR 0x28 /* Interrupt Disable Register */ +#define AT91_ADC_IMR 0x2C /* Interrupt Mask Register */ +#define AT91RL_ADC_IER_PEN (1 << 20) +#define AT91RL_ADC_IER_NOPEN (1 << 21) +#define AT91_ADC_IER_PEN (1 << 29) +#define AT91_ADC_IER_NOPEN (1 << 30) +#define AT91_ADC_IER_XRDY (1 << 20) +#define AT91_ADC_IER_YRDY (1 << 21) +#define AT91_ADC_IER_PRDY (1 << 22) +#define AT91_ADC_ISR_PENS (1 << 31) + +#define AT91_ADC_CHR(n) (0x30 + ((n) * 4)) /* Channel Data Register N */ +#define AT91_ADC_DATA (0x3ff) + +#define AT91_ADC_CDR0_9X5 (0x50) /* Channel Data Register 0 for 9X5 */ + +#define AT91_ADC_ACR 0x94 /* Analog Control Register */ +#define AT91_ADC_ACR_PENDETSENS (0x3 << 0) /* pull-up resistor */ + +#define AT91_ADC_TSMR 0xB0 +#define AT91_ADC_TSMR_TSMODE (3 << 0) /* Touch Screen Mode */ +#define AT91_ADC_TSMR_TSMODE_NONE (0 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_NO_PRESS (1 << 0) +#define AT91_ADC_TSMR_TSMODE_4WIRE_PRESS (2 << 0) +#define AT91_ADC_TSMR_TSMODE_5WIRE (3 << 0) +#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */ +#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4) +#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */ +#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */ +#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28) +#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */ +#define AT91_ADC_TSMR_PENDET_DIS (0 << 24) /* Pen contact detection disable */ +#define AT91_ADC_TSMR_PENDET_ENA (1 << 24) /* Pen contact detection enable */ + +#define AT91_ADC_TSXPOSR 0xB4 +#define AT91_ADC_TSYPOSR 0xB8 +#define AT91_ADC_TSPRESSR 0xBC + +#define AT91_ADC_TRGR_9260 AT91_ADC_MR +#define AT91_ADC_TRGR_9G45 0x08 +#define AT91_ADC_TRGR_9X5 0xC0 + +/* Trigger Register bit field */ +#define AT91_ADC_TRGR_TRGPER (0xffff << 16) +#define AT91_ADC_TRGR_TRGPER_(x) ((x) << 16) +#define AT91_ADC_TRGR_TRGMOD (0x7 << 0) +#define AT91_ADC_TRGR_NONE (0 << 0) +#define AT91_ADC_TRGR_MOD_PERIOD_TRIG (5 << 0) #define AT91_ADC_CHAN(st, ch) \ (st->registers->channel_base + (ch * 4)) @@ -46,6 +147,29 @@ #define TOUCH_SAMPLE_PERIOD_US 2000 /* 2ms */ #define TOUCH_PEN_DETECT_DEBOUNCE_US 200 +#define MAX_RLPOS_BITS 10 +#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */ +#define TOUCH_SHTIM 0xa + +/** + * struct at91_adc_reg_desc - Various informations relative to registers + * @channel_base: Base offset for the channel data registers + * @drdy_mask: Mask of the DRDY field in the relevant registers + (Interruptions registers mostly) + * @status_register: Offset of the Interrupt Status Register + * @trigger_register: Offset of the Trigger setup register + * @mr_prescal_mask: Mask of the PRESCAL field in the adc MR register + * @mr_startup_mask: Mask of the STARTUP field in the adc MR register + */ +struct at91_adc_reg_desc { + u8 channel_base; + u32 drdy_mask; + u8 status_register; + u8 trigger_register; + u32 mr_prescal_mask; + u32 mr_startup_mask; +}; + struct at91_adc_caps { bool has_ts; /* Support touch screen */ bool has_tsmr; /* only at91sam9x5, sama5d3 have TSMR reg */ @@ -64,12 +188,6 @@ struct at91_adc_caps { struct at91_adc_reg_desc registers; }; -enum atmel_adc_ts_type { - ATMEL_ADC_TOUCHSCREEN_NONE = 0, - ATMEL_ADC_TOUCHSCREEN_4WIRE = 4, - ATMEL_ADC_TOUCHSCREEN_5WIRE = 5, -}; - struct at91_adc_state { struct clk *adc_clk; u16 *buffer; @@ -114,6 +232,11 @@ struct at91_adc_state { u16 ts_sample_period_val; u32 ts_pressure_threshold; + u16 ts_pendbc; + + bool ts_bufferedmeasure; + u32 ts_prev_absx; + u32 ts_prev_absy; }; static irqreturn_t at91_adc_trigger_handler(int irq, void *p) @@ -220,7 +343,72 @@ static int at91_ts_sample(struct at91_adc_state *st) return 0; } -static irqreturn_t at91_adc_interrupt(int irq, void *private) +static irqreturn_t at91_adc_rl_interrupt(int irq, void *private) +{ + struct iio_dev *idev = private; + struct at91_adc_state *st = iio_priv(idev); + u32 status = at91_adc_readl(st, st->registers->status_register); + unsigned int reg; + + status &= at91_adc_readl(st, AT91_ADC_IMR); + if (status & st->registers->drdy_mask) + handle_adc_eoc_trigger(irq, idev); + + if (status & AT91RL_ADC_IER_PEN) { + /* Disabling pen debounce is required to get a NOPEN irq */ + reg = at91_adc_readl(st, AT91_ADC_MR); + reg &= ~AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + /* Set up period trigger for sampling */ + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_MOD_PERIOD_TRIG | + AT91_ADC_TRGR_TRGPER_(st->ts_sample_period_val)); + } else if (status & AT91RL_ADC_IER_NOPEN) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + at91_adc_writel(st, st->registers->trigger_register, + AT91_ADC_TRGR_NONE); + + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_NOPEN + | AT91_ADC_EOC(3)); + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); + st->ts_bufferedmeasure = false; + input_report_key(st->ts_input, BTN_TOUCH, 0); + input_sync(st->ts_input); + } else if (status & AT91_ADC_EOC(3)) { + /* Conversion finished */ + if (st->ts_bufferedmeasure) { + /* + * Last measurement is always discarded, since it can + * be erroneous. + * Always report previous measurement + */ + input_report_abs(st->ts_input, ABS_X, st->ts_prev_absx); + input_report_abs(st->ts_input, ABS_Y, st->ts_prev_absy); + input_report_key(st->ts_input, BTN_TOUCH, 1); + input_sync(st->ts_input); + } else + st->ts_bufferedmeasure = true; + + /* Now make new measurement */ + st->ts_prev_absx = at91_adc_readl(st, AT91_ADC_CHAN(st, 3)) + << MAX_RLPOS_BITS; + st->ts_prev_absx /= at91_adc_readl(st, AT91_ADC_CHAN(st, 2)); + + st->ts_prev_absy = at91_adc_readl(st, AT91_ADC_CHAN(st, 1)) + << MAX_RLPOS_BITS; + st->ts_prev_absy /= at91_adc_readl(st, AT91_ADC_CHAN(st, 0)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t at91_adc_9x5_interrupt(int irq, void *private) { struct iio_dev *idev = private; struct at91_adc_state *st = iio_priv(idev); @@ -653,6 +841,8 @@ static int at91_adc_probe_dt_ts(struct device_node *node, return -EINVAL; } + if (!st->caps->has_tsmr) + return 0; prop = 0; of_property_read_u32(node, "atmel,adc-ts-pressure-threshold", &prop); st->ts_pressure_threshold = prop; @@ -765,14 +955,18 @@ static int at91_adc_probe_pdata(struct at91_adc_state *st, if (!pdata) return -EINVAL; + st->caps = (struct at91_adc_caps *) + platform_get_device_id(pdev)->driver_data; + st->use_external = pdata->use_external_triggers; st->vref_mv = pdata->vref; st->channels_mask = pdata->channels_used; - st->num_channels = pdata->num_channels; + st->num_channels = st->caps->num_channels; st->startup_time = pdata->startup_time; st->trigger_number = pdata->trigger_number; st->trigger_list = pdata->trigger_list; - st->registers = pdata->registers; + st->registers = &st->caps->registers; + st->touchscreen_type = pdata->touchscreen_type; return 0; } @@ -787,7 +981,10 @@ static int atmel_ts_open(struct input_dev *dev) { struct at91_adc_state *st = input_get_drvdata(dev); - at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IER, AT91RL_ADC_IER_PEN); return 0; } @@ -795,45 +992,61 @@ static void atmel_ts_close(struct input_dev *dev) { struct at91_adc_state *st = input_get_drvdata(dev); - at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + if (st->caps->has_tsmr) + at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); + else + at91_adc_writel(st, AT91_ADC_IDR, AT91RL_ADC_IER_PEN); } static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) { - u32 reg = 0, pendbc; + u32 reg = 0; int i = 0; - if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) - reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; - else - reg = AT91_ADC_TSMR_TSMODE_5WIRE; - /* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid * pen detect noise. * The formula is : Pen Detect Debounce Time = (2 ^ pendbc) / ADCClock */ - pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / 1000, 1); + st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / + 1000, 1); - while (pendbc >> ++i) + while (st->ts_pendbc >> ++i) ; /* Empty! Find the shift offset */ - if (abs(pendbc - (1 << i)) < abs(pendbc - (1 << (i - 1)))) - pendbc = i; + if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1)))) + st->ts_pendbc = i; else - pendbc = i - 1; + st->ts_pendbc = i - 1; - if (st->caps->has_tsmr) { - reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) - & AT91_ADC_TSMR_TSAV; - reg |= AT91_ADC_TSMR_PENDBC_(pendbc) & AT91_ADC_TSMR_PENDBC; - reg |= AT91_ADC_TSMR_NOTSDMA; - reg |= AT91_ADC_TSMR_PENDET_ENA; - reg |= 0x03 << 8; /* TSFREQ, need bigger than TSAV */ - - at91_adc_writel(st, AT91_ADC_TSMR, reg); - } else { - /* TODO: for 9g45 which has no TSMR */ + if (!st->caps->has_tsmr) { + reg = at91_adc_readl(st, AT91_ADC_MR); + reg |= AT91_ADC_TSAMOD_TS_ONLY_MODE | AT91_ADC_PENDET; + + reg |= AT91_ADC_PENDBC_(st->ts_pendbc) & AT91_ADC_PENDBC; + at91_adc_writel(st, AT91_ADC_MR, reg); + + reg = AT91_ADC_TSR_SHTIM_(TOUCH_SHTIM) & AT91_ADC_TSR_SHTIM; + at91_adc_writel(st, AT91_ADC_TSR, reg); + + st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US_RL * + adc_clk_khz / 1000) - 1, 1); + + return 0; } + if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) + reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS; + else + reg = AT91_ADC_TSMR_TSMODE_5WIRE; + + reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average) + & AT91_ADC_TSMR_TSAV; + reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC; + reg |= AT91_ADC_TSMR_NOTSDMA; + reg |= AT91_ADC_TSMR_PENDET_ENA; + reg |= 0x03 << 8; /* TSFREQ, needs to be bigger than TSAV */ + + at91_adc_writel(st, AT91_ADC_TSMR, reg); + /* Change adc internal resistor value for better pen detection, * default value is 100 kOhm. * 0 = 200 kOhm, 1 = 150 kOhm, 2 = 100 kOhm, 3 = 50 kOhm @@ -842,7 +1055,7 @@ static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz) at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity & AT91_ADC_ACR_PENDETSENS); - /* Sample Peroid Time = (TRGPER + 1) / ADCClock */ + /* Sample Period Time = (TRGPER + 1) / ADCClock */ st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US * adc_clk_khz / 1000) - 1, 1); @@ -871,17 +1084,37 @@ static int at91_ts_register(struct at91_adc_state *st, __set_bit(EV_ABS, input->evbit); __set_bit(EV_KEY, input->evbit); __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, 0, 0); - input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, 0, 0); - input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + if (st->caps->has_tsmr) { + input_set_abs_params(input, ABS_X, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_POS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, 0xffffff, 0, 0); + } else { + if (st->touchscreen_type != ATMEL_ADC_TOUCHSCREEN_4WIRE) { + dev_err(&pdev->dev, + "This touchscreen controller only support 4 wires\n"); + ret = -EINVAL; + goto err; + } + + input_set_abs_params(input, ABS_X, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + input_set_abs_params(input, ABS_Y, 0, (1 << MAX_RLPOS_BITS) - 1, + 0, 0); + } st->ts_input = input; input_set_drvdata(input, st); ret = input_register_device(input); if (ret) - input_free_device(st->ts_input); + goto err; + + return ret; +err: + input_free_device(st->ts_input); return ret; } @@ -940,11 +1173,13 @@ static int at91_adc_probe(struct platform_device *pdev) */ at91_adc_writel(st, AT91_ADC_CR, AT91_ADC_SWRST); at91_adc_writel(st, AT91_ADC_IDR, 0xFFFFFFFF); - ret = request_irq(st->irq, - at91_adc_interrupt, - 0, - pdev->dev.driver->name, - idev); + + if (st->caps->has_tsmr) + ret = request_irq(st->irq, at91_adc_9x5_interrupt, 0, + pdev->dev.driver->name, idev); + else + ret = request_irq(st->irq, at91_adc_rl_interrupt, 0, + pdev->dev.driver->name, idev); if (ret) { dev_err(&pdev->dev, "Failed to allocate IRQ.\n"); return ret; @@ -1004,8 +1239,11 @@ static int at91_adc_probe(struct platform_device *pdev) * the best converted final value between two channels selection * The formula thus is : Sample and Hold Time = (shtim + 1) / ADCClock */ - shtim = round_up((st->sample_hold_time * adc_clk_khz / - 1000) - 1, 1); + if (st->sample_hold_time > 0) + shtim = round_up((st->sample_hold_time * adc_clk_khz / 1000) + - 1, 1); + else + shtim = 0; reg = AT91_ADC_PRESCAL_(prsc) & st->registers->mr_prescal_mask; reg |= AT91_ADC_STARTUP_(ticks) & st->registers->mr_startup_mask; @@ -1045,12 +1283,6 @@ static int at91_adc_probe(struct platform_device *pdev) goto error_disable_adc_clk; } } else { - if (!st->caps->has_tsmr) { - dev_err(&pdev->dev, "We don't support non-TSMR adc\n"); - ret = -ENODEV; - goto error_disable_adc_clk; - } - ret = at91_ts_register(st, pdev); if (ret) goto error_disable_adc_clk; @@ -1101,7 +1333,6 @@ static int at91_adc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static struct at91_adc_caps at91sam9260_caps = { .calc_startup_ticks = calc_startup_ticks_9260, .num_channels = 4, @@ -1115,6 +1346,20 @@ static struct at91_adc_caps at91sam9260_caps = { }, }; +static struct at91_adc_caps at91sam9rl_caps = { + .has_ts = true, + .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ + .num_channels = 6, + .registers = { + .channel_base = AT91_ADC_CHR(0), + .drdy_mask = AT91_ADC_DRDY, + .status_register = AT91_ADC_SR, + .trigger_register = AT91_ADC_TRGR_9G45, + .mr_prescal_mask = AT91_ADC_PRESCAL_9260, + .mr_startup_mask = AT91_ADC_STARTUP_9G45, + }, +}; + static struct at91_adc_caps at91sam9g45_caps = { .has_ts = true, .calc_startup_ticks = calc_startup_ticks_9260, /* same as 9260 */ @@ -1149,16 +1394,36 @@ static struct at91_adc_caps at91sam9x5_caps = { static const struct of_device_id at91_adc_dt_ids[] = { { .compatible = "atmel,at91sam9260-adc", .data = &at91sam9260_caps }, + { .compatible = "atmel,at91sam9rl-adc", .data = &at91sam9rl_caps }, { .compatible = "atmel,at91sam9g45-adc", .data = &at91sam9g45_caps }, { .compatible = "atmel,at91sam9x5-adc", .data = &at91sam9x5_caps }, {}, }; MODULE_DEVICE_TABLE(of, at91_adc_dt_ids); -#endif + +static const struct platform_device_id at91_adc_ids[] = { + { + .name = "at91sam9260-adc", + .driver_data = (unsigned long)&at91sam9260_caps, + }, { + .name = "at91sam9rl-adc", + .driver_data = (unsigned long)&at91sam9rl_caps, + }, { + .name = "at91sam9g45-adc", + .driver_data = (unsigned long)&at91sam9g45_caps, + }, { + .name = "at91sam9x5-adc", + .driver_data = (unsigned long)&at91sam9x5_caps, + }, { + /* terminator */ + } +}; +MODULE_DEVICE_TABLE(platform, at91_adc_ids); static struct platform_driver at91_adc_driver = { .probe = at91_adc_probe, .remove = at91_adc_remove, + .id_table = at91_adc_ids, .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(at91_adc_dt_ids), diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index d25b262193a..010578f1d76 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -82,7 +82,7 @@ enum adc_version { #define ADC_CON_EN_START (1u << 0) #define ADC_DATX_MASK 0xFFF -#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(1000)) +#define EXYNOS_ADC_TIMEOUT (msecs_to_jiffies(100)) struct exynos_adc { void __iomem *regs; @@ -112,6 +112,30 @@ static inline unsigned int exynos_adc_get_version(struct platform_device *pdev) return (unsigned int)match->data; } +static void exynos_adc_hw_init(struct exynos_adc *info) +{ + u32 con1, con2; + + if (info->version == ADC_V2) { + con1 = ADC_V2_CON1_SOFT_RESET; + writel(con1, ADC_V2_CON1(info->regs)); + + con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | + ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); + writel(con2, ADC_V2_CON2(info->regs)); + + /* Enable interrupts */ + writel(1, ADC_V2_INT_EN(info->regs)); + } else { + /* set default prescaler values and Enable prescaler */ + con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; + + /* Enable 12-bit ADC resolution */ + con1 |= ADC_V1_CON_RES; + writel(con1, ADC_V1_CON(info->regs)); + } +} + static int exynos_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, @@ -121,11 +145,13 @@ static int exynos_read_raw(struct iio_dev *indio_dev, struct exynos_adc *info = iio_priv(indio_dev); unsigned long timeout; u32 con1, con2; + int ret; if (mask != IIO_CHAN_INFO_RAW) return -EINVAL; mutex_lock(&indio_dev->mlock); + reinit_completion(&info->completion); /* Select the channel to be used and Trigger conversion */ if (info->version == ADC_V2) { @@ -145,16 +171,21 @@ static int exynos_read_raw(struct iio_dev *indio_dev, ADC_V1_CON(info->regs)); } - timeout = wait_for_completion_interruptible_timeout + timeout = wait_for_completion_timeout (&info->completion, EXYNOS_ADC_TIMEOUT); - *val = info->value; + if (timeout == 0) { + dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); + exynos_adc_hw_init(info); + ret = -ETIMEDOUT; + } else { + *val = info->value; + *val2 = 0; + ret = IIO_VAL_INT; + } mutex_unlock(&indio_dev->mlock); - if (timeout == 0) - return -ETIMEDOUT; - - return IIO_VAL_INT; + return ret; } static irqreturn_t exynos_adc_isr(int irq, void *dev_id) @@ -226,30 +257,6 @@ static int exynos_adc_remove_devices(struct device *dev, void *c) return 0; } -static void exynos_adc_hw_init(struct exynos_adc *info) -{ - u32 con1, con2; - - if (info->version == ADC_V2) { - con1 = ADC_V2_CON1_SOFT_RESET; - writel(con1, ADC_V2_CON1(info->regs)); - - con2 = ADC_V2_CON2_OSEL | ADC_V2_CON2_ESEL | - ADC_V2_CON2_HIGHF | ADC_V2_CON2_C_TIME(0); - writel(con2, ADC_V2_CON2(info->regs)); - - /* Enable interrupts */ - writel(1, ADC_V2_INT_EN(info->regs)); - } else { - /* set default prescaler values and Enable prescaler */ - con1 = ADC_V1_CON_PRSCLV(49) | ADC_V1_CON_PRSCEN; - - /* Enable 12-bit ADC resolution */ - con1 |= ADC_V1_CON_RES; - writel(con1, ADC_V1_CON(info->regs)); - } -} - static int exynos_adc_probe(struct platform_device *pdev) { struct exynos_adc *info = NULL; @@ -290,32 +297,30 @@ static int exynos_adc_probe(struct platform_device *pdev) init_completion(&info->completion); - ret = request_irq(info->irq, exynos_adc_isr, - 0, dev_name(&pdev->dev), info); - if (ret < 0) { - dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", - info->irq); - return ret; - } - - writel(1, info->enable_reg); - info->clk = devm_clk_get(&pdev->dev, "adc"); if (IS_ERR(info->clk)) { dev_err(&pdev->dev, "failed getting clock, err = %ld\n", PTR_ERR(info->clk)); - ret = PTR_ERR(info->clk); - goto err_irq; + return PTR_ERR(info->clk); } info->vdd = devm_regulator_get(&pdev->dev, "vdd"); if (IS_ERR(info->vdd)) { dev_err(&pdev->dev, "failed getting regulator, err = %ld\n", PTR_ERR(info->vdd)); - ret = PTR_ERR(info->vdd); - goto err_irq; + return PTR_ERR(info->vdd); } + ret = regulator_enable(info->vdd); + if (ret) + return ret; + + ret = clk_prepare_enable(info->clk); + if (ret) + goto err_disable_reg; + + writel(1, info->enable_reg); + info->version = exynos_adc_get_version(pdev); platform_set_drvdata(pdev, indio_dev); @@ -332,19 +337,21 @@ static int exynos_adc_probe(struct platform_device *pdev) else indio_dev->num_channels = MAX_ADC_V2_CHANNELS; + ret = request_irq(info->irq, exynos_adc_isr, + 0, dev_name(&pdev->dev), info); + if (ret < 0) { + dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", + info->irq); + goto err_disable_clk; + } + ret = iio_device_register(indio_dev); if (ret) goto err_irq; - ret = regulator_enable(info->vdd); - if (ret) - goto err_iio_dev; - - clk_prepare_enable(info->clk); - exynos_adc_hw_init(info); - ret = of_platform_populate(np, exynos_adc_match, NULL, &pdev->dev); + ret = of_platform_populate(np, exynos_adc_match, NULL, &indio_dev->dev); if (ret < 0) { dev_err(&pdev->dev, "failed adding child nodes\n"); goto err_of_populate; @@ -353,14 +360,16 @@ static int exynos_adc_probe(struct platform_device *pdev) return 0; err_of_populate: - device_for_each_child(&pdev->dev, NULL, + device_for_each_child(&indio_dev->dev, NULL, exynos_adc_remove_devices); - regulator_disable(info->vdd); - clk_disable_unprepare(info->clk); -err_iio_dev: iio_device_unregister(indio_dev); err_irq: free_irq(info->irq, info); +err_disable_clk: + writel(0, info->enable_reg); + clk_disable_unprepare(info->clk); +err_disable_reg: + regulator_disable(info->vdd); return ret; } @@ -369,13 +378,13 @@ static int exynos_adc_remove(struct platform_device *pdev) struct iio_dev *indio_dev = platform_get_drvdata(pdev); struct exynos_adc *info = iio_priv(indio_dev); - device_for_each_child(&pdev->dev, NULL, + device_for_each_child(&indio_dev->dev, NULL, exynos_adc_remove_devices); - regulator_disable(info->vdd); - clk_disable_unprepare(info->clk); - writel(0, info->enable_reg); iio_device_unregister(indio_dev); free_irq(info->irq, info); + writel(0, info->enable_reg); + clk_disable_unprepare(info->clk); + regulator_disable(info->vdd); return 0; } @@ -397,8 +406,8 @@ static int exynos_adc_suspend(struct device *dev) writel(con, ADC_V1_CON(info->regs)); } - clk_disable_unprepare(info->clk); writel(0, info->enable_reg); + clk_disable_unprepare(info->clk); regulator_disable(info->vdd); return 0; @@ -414,9 +423,11 @@ static int exynos_adc_resume(struct device *dev) if (ret) return ret; - writel(1, info->enable_reg); - clk_prepare_enable(info->clk); + ret = clk_prepare_enable(info->clk); + if (ret) + return ret; + writel(1, info->enable_reg); exynos_adc_hw_init(info); return 0; diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 9cf3229a727..1b3b74be5c2 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1252,8 +1252,8 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), }, [max11605] = { .bits = 8, @@ -1262,8 +1262,8 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), + .channels = max1038_channels, + .num_channels = ARRAY_SIZE(max1038_channels), }, [max11606] = { .bits = 10, @@ -1312,8 +1312,8 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), }, [max11611] = { .bits = 10, @@ -1322,8 +1322,8 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = { .num_modes = ARRAY_SIZE(max1238_mode_list), .default_mode = s0to11, .info = &max1238_info, - .channels = max1238_channels, - .num_channels = ARRAY_SIZE(max1238_channels), + .channels = max1138_channels, + .num_channels = ARRAY_SIZE(max1138_channels), }, [max11612] = { .bits = 12, diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c index 47dcb34ff44..51672256072 100644 --- a/drivers/iio/adc/mcp3422.c +++ b/drivers/iio/adc/mcp3422.c @@ -1,10 +1,11 @@ /* - * mcp3422.c - driver for the Microchip mcp3422/3/4 chip family + * mcp3422.c - driver for the Microchip mcp3422/3/4/6/7/8 chip family * * Copyright (C) 2013, Angelo Compagnucci * Author: Angelo Compagnucci <angelo.compagnucci@gmail.com> * * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/22088b.pdf + * http://ww1.microchip.com/downloads/en/DeviceDoc/22226a.pdf * * This driver exports the value of analog input voltage to sysfs, the * voltage unit is nV. @@ -96,6 +97,7 @@ static const int mcp3422_sign_extend[4] = { /* Client data (each client gets its own) */ struct mcp3422 { struct i2c_client *i2c; + u8 id; u8 config; u8 pga[4]; struct mutex lock; @@ -238,6 +240,8 @@ static int mcp3422_write_raw(struct iio_dev *iio, temp = MCP3422_SRATE_15; break; case 3: + if (adc->id > 4) + return -EINVAL; temp = MCP3422_SRATE_3; break; default: @@ -271,6 +275,17 @@ static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev, } } +static ssize_t mcp3422_show_samp_freqs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); + + if (adc->id > 4) + return sprintf(buf, "240 60 15\n"); + + return sprintf(buf, "240 60 15 3\n"); +} + static ssize_t mcp3422_show_scales(struct device *dev, struct device_attribute *attr, char *buf) { @@ -284,12 +299,13 @@ static ssize_t mcp3422_show_scales(struct device *dev, mcp3422_scales[sample_rate][3]); } -static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("240 60 15 3"); +static IIO_DEVICE_ATTR(sampling_frequency_available, S_IRUGO, + mcp3422_show_samp_freqs, NULL, 0); static IIO_DEVICE_ATTR(in_voltage_scale_available, S_IRUGO, mcp3422_show_scales, NULL, 0); static struct attribute *mcp3422_attributes[] = { - &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, &iio_dev_attr_in_voltage_scale_available.dev_attr.attr, NULL, }; @@ -335,6 +351,7 @@ static int mcp3422_probe(struct i2c_client *client, adc = iio_priv(indio_dev); adc->i2c = client; + adc->id = (u8)(id->driver_data); mutex_init(&adc->lock); @@ -343,13 +360,16 @@ static int mcp3422_probe(struct i2c_client *client, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &mcp3422_info; - switch ((unsigned int)(id->driver_data)) { + switch (adc->id) { case 2: case 3: + case 6: + case 7: indio_dev->channels = mcp3422_channels; indio_dev->num_channels = ARRAY_SIZE(mcp3422_channels); break; case 4: + case 8: indio_dev->channels = mcp3424_channels; indio_dev->num_channels = ARRAY_SIZE(mcp3424_channels); break; @@ -375,6 +395,9 @@ static const struct i2c_device_id mcp3422_id[] = { { "mcp3422", 2 }, { "mcp3423", 3 }, { "mcp3424", 4 }, + { "mcp3426", 6 }, + { "mcp3427", 7 }, + { "mcp3428", 8 }, { } }; MODULE_DEVICE_TABLE(i2c, mcp3422_id); @@ -399,5 +422,5 @@ static struct i2c_driver mcp3422_driver = { module_i2c_driver(mcp3422_driver); MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>"); -MODULE_DESCRIPTION("Microchip mcp3422/3/4 driver"); +MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 75b54730a96..403dd3d8986 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -26,6 +26,40 @@ #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> +struct { + u32 usage_id; + int unit; /* 0 for default others from HID sensor spec */ + int scale_val0; /* scale, whole number */ + int scale_val1; /* scale, fraction in micros */ +} static unit_conversion[] = { + {HID_USAGE_SENSOR_ACCEL_3D, 0, 9, 806650}, + {HID_USAGE_SENSOR_ACCEL_3D, + HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD, 1, 0}, + {HID_USAGE_SENSOR_ACCEL_3D, + HID_USAGE_SENSOR_UNITS_G, 9, 806650}, + + {HID_USAGE_SENSOR_GYRO_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_GYRO_3D, + HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND, 1, 0}, + {HID_USAGE_SENSOR_GYRO_3D, + HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND, 0, 17453}, + + {HID_USAGE_SENSOR_COMPASS_3D, 0, 0, 1000}, + {HID_USAGE_SENSOR_COMPASS_3D, HID_USAGE_SENSOR_UNITS_GAUSS, 1, 0}, + + {HID_USAGE_SENSOR_INCLINOMETER_3D, 0, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, + HID_USAGE_SENSOR_UNITS_DEGREES, 0, 17453}, + {HID_USAGE_SENSOR_INCLINOMETER_3D, + HID_USAGE_SENSOR_UNITS_RADIANS, 1, 0}, + + {HID_USAGE_SENSOR_ALS, 0, 1, 0}, + {HID_USAGE_SENSOR_ALS, HID_USAGE_SENSOR_UNITS_LUX, 1, 0}, + + {HID_USAGE_SENSOR_PRESSURE, 0, 100000, 0}, + {HID_USAGE_SENSOR_PRESSURE, HID_USAGE_SENSOR_UNITS_PASCAL, 1, 0}, +}; + static int pow_10(unsigned power) { int i; @@ -113,6 +147,26 @@ static u32 convert_to_vtf_format(int size, int exp, int val1, int val2) return value; } +s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) +{ + s32 value = 0; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->poll.report_id, + st->poll.index, &value); + + if (ret < 0 || value < 0) { + return -EINVAL; + } else { + if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND) + value = value * 1000; + } + + return value; +} +EXPORT_SYMBOL(hid_sensor_read_poll_value); + int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, int *val1, int *val2) { @@ -209,15 +263,108 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); -int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, +/* + * This fuction applies the unit exponent to the scale. + * For example: + * 9.806650 ->exp:2-> val0[980]val1[665000] + * 9.000806 ->exp:2-> val0[900]val1[80600] + * 0.174535 ->exp:2-> val0[17]val1[453500] + * 1.001745 ->exp:0-> val0[1]val1[1745] + * 1.001745 ->exp:2-> val0[100]val1[174500] + * 1.001745 ->exp:4-> val0[10017]val1[450000] + * 9.806650 ->exp:-2-> val0[0]val1[98066] + */ +static void adjust_exponent_micro(int *val0, int *val1, int scale0, + int scale1, int exp) +{ + int i; + int x; + int res; + int rem; + + if (exp > 0) { + *val0 = scale0 * pow_10(exp); + res = 0; + if (exp > 6) { + *val1 = 0; + return; + } + for (i = 0; i < exp; ++i) { + x = scale1 / pow_10(5 - i); + res += (pow_10(exp - 1 - i) * x); + scale1 = scale1 % pow_10(5 - i); + } + *val0 += res; + *val1 = scale1 * pow_10(exp); + } else if (exp < 0) { + exp = abs(exp); + if (exp > 6) { + *val0 = *val1 = 0; + return; + } + *val0 = scale0 / pow_10(exp); + rem = scale0 % pow_10(exp); + res = 0; + for (i = 0; i < (6 - exp); ++i) { + x = scale1 / pow_10(5 - i); + res += (pow_10(5 - exp - i) * x); + scale1 = scale1 % pow_10(5 - i); + } + *val1 = rem * pow_10(6 - exp) + res; + } else { + *val0 = scale0; + *val1 = scale1; + } +} + +int hid_sensor_format_scale(u32 usage_id, + struct hid_sensor_hub_attribute_info *attr_info, + int *val0, int *val1) +{ + int i; + int exp; + + *val0 = 1; + *val1 = 0; + + for (i = 0; i < ARRAY_SIZE(unit_conversion); ++i) { + if (unit_conversion[i].usage_id == usage_id && + unit_conversion[i].unit == attr_info->units) { + exp = hid_sensor_convert_exponent( + attr_info->unit_expo); + adjust_exponent_micro(val0, val1, + unit_conversion[i].scale_val0, + unit_conversion[i].scale_val1, exp); + break; + } + } + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_format_scale); + +int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, u32 usage_id, struct hid_sensor_common *st) { - sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, HID_USAGE_SENSOR_PROP_REPORT_INTERVAL, &st->poll); + /* Default unit of measure is milliseconds */ + if (st->poll.units == 0) + st->poll.units = HID_USAGE_SENSOR_UNITS_MILLISECOND; + return 0; + +} + +int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, + u32 usage_id, + struct hid_sensor_common *st) +{ + + + hid_sensor_get_reporting_interval(hsdev, usage_id, st); sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index dbefbdaf7cd..73282cee0c8 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -28,16 +28,17 @@ #include <linux/iio/sysfs.h> #include "hid-sensor-trigger.h" -static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, - bool state) +int hid_sensor_power_state(struct hid_sensor_common *st, bool state) { - struct hid_sensor_common *st = iio_trigger_get_drvdata(trig); int state_val; int report_val; if (state) { if (sensor_hub_device_open(st->hsdev)) return -EIO; + + atomic_inc(&st->data_ready); + state_val = hid_sensor_get_usage_index(st->hsdev, st->power_state.report_id, st->power_state.index, @@ -47,6 +48,8 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM); } else { + if (!atomic_dec_and_test(&st->data_ready)) + return 0; sensor_hub_device_close(st->hsdev); state_val = hid_sensor_get_usage_index(st->hsdev, st->power_state.report_id, @@ -57,7 +60,6 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, st->report_state.index, HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM); } - st->data_ready = state; if (state_val >= 0) { state_val += st->power_state.logical_minimum; @@ -75,6 +77,13 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, return 0; } +EXPORT_SYMBOL(hid_sensor_power_state); + +static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig, + bool state) +{ + return hid_sensor_power_state(iio_trigger_get_drvdata(trig), state); +} void hid_sensor_remove_trigger(struct hid_sensor_common *attrb) { diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index ca02f7811aa..0f8e78c249d 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -22,5 +22,6 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, struct hid_sensor_common *attrb); void hid_sensor_remove_trigger(struct hid_sensor_common *attrb); +int hid_sensor_power_state(struct hid_sensor_common *st, bool state); #endif diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 7ba1ef27021..e8b932fed70 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/delay.h> #include <linux/iio/iio.h> +#include <linux/regulator/consumer.h> #include <asm/unaligned.h> #include <linux/iio/common/st_sensors.h> @@ -198,6 +199,42 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable) } EXPORT_SYMBOL(st_sensors_set_axis_enable); +void st_sensors_power_enable(struct iio_dev *indio_dev) +{ + struct st_sensor_data *pdata = iio_priv(indio_dev); + int err; + + /* Regulators not mandatory, but if requested we should enable them. */ + pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd"); + if (!IS_ERR(pdata->vdd)) { + err = regulator_enable(pdata->vdd); + if (err != 0) + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd supply\n"); + } + + pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio"); + if (!IS_ERR(pdata->vdd_io)) { + err = regulator_enable(pdata->vdd_io); + if (err != 0) + dev_warn(&indio_dev->dev, + "Failed to enable specified Vdd_IO supply\n"); + } +} +EXPORT_SYMBOL(st_sensors_power_enable); + +void st_sensors_power_disable(struct iio_dev *indio_dev) +{ + struct st_sensor_data *pdata = iio_priv(indio_dev); + + if (!IS_ERR(pdata->vdd)) + regulator_disable(pdata->vdd); + + if (!IS_ERR(pdata->vdd_io)) + regulator_disable(pdata->vdd_io); +} +EXPORT_SYMBOL(st_sensors_power_disable); + static int st_sensors_set_drdy_int_pin(struct iio_dev *indio_dev, struct st_sensors_platform_data *pdata) { diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index 59d6bc3e04d..40f4e4935d0 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -42,6 +43,10 @@ struct gyro_3d_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX]; u32 gyro_val[GYRO_3D_CHANNEL_MAX]; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { @@ -56,6 +61,7 @@ static const struct iio_chan_spec gyro_3d_channels[] = { .type = IIO_ANGL_VEL, .modified = 1, .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -65,6 +71,7 @@ static const struct iio_chan_spec gyro_3d_channels[] = { .type = IIO_ANGL_VEL, .modified = 1, .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -74,6 +81,7 @@ static const struct iio_chan_spec gyro_3d_channels[] = { .type = IIO_ANGL_VEL, .modified = 1, .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -104,31 +112,42 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; switch (mask) { case 0: + poll_value = hid_sensor_read_poll_value( + &gyro_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&gyro_state->common_attributes, true); + msleep_interruptible(poll_value * 2); report_id = gyro_state->gyro[chan->scan_index].report_id; address = gyro_3d_addresses[chan->scan_index]; if (report_id >= 0) *val = sensor_hub_input_attr_get_raw_value( - gyro_state->common_attributes.hsdev, - HID_USAGE_SENSOR_GYRO_3D, address, - report_id); + gyro_state->common_attributes.hsdev, + HID_USAGE_SENSOR_GYRO_3D, address, + report_id); else { *val = 0; + hid_sensor_power_state(&gyro_state->common_attributes, + false); return -EINVAL; } + hid_sensor_power_state(&gyro_state->common_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units; - ret_type = IIO_VAL_INT; + *val = gyro_state->scale_pre_decml; + *val2 = gyro_state->scale_post_decml; + ret_type = gyro_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo); + *val = gyro_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -197,9 +216,8 @@ static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct gyro_3d_state *gyro_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n", - gyro_state->common_attributes.data_ready); - if (gyro_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "gyro_3d_proc_event\n"); + if (atomic_read(&gyro_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, gyro_state->gyro_val, sizeof(gyro_state->gyro_val)); @@ -262,6 +280,11 @@ static int gyro_3d_parse_report(struct platform_device *pdev, st->gyro[1].index, st->gyro[1].report_id, st->gyro[2].index, st->gyro[2].report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_GYRO_3D, + &st->gyro[CHANNEL_SCAN_INDEX_X], + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -330,7 +353,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - gyro_state->common_attributes.data_ready = false; + atomic_set(&gyro_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &gyro_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index 4d3f3b92b36..8295e318399 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -110,8 +110,6 @@ static int itg3200_read_raw(struct iio_dev *indio_dev, default: return -EINVAL; } - - return ret; } static ssize_t itg3200_read_frequency(struct device *dev, diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index a8e174a47bc..ed74a906998 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -311,6 +311,8 @@ int st_gyro_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &gyro_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_gyro_sensors), st_gyro_sensors); if (err < 0) @@ -344,6 +346,9 @@ int st_gyro_common_probe(struct iio_dev *indio_dev, if (err) goto st_gyro_device_register_error; + dev_info(&indio_dev->dev, "registered gyroscope %s\n", + indio_dev->name); + return 0; st_gyro_device_register_error: @@ -360,6 +365,8 @@ void st_gyro_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (gdata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index f6db6af36ba..5f0ea77fe71 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -35,7 +35,7 @@ int __iio_add_chan_devattr(const char *postfix, struct list_head *attr_list); void iio_free_chan_devattr_list(struct list_head *attr_list); -ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2); +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); /* Event interface flags */ #define IIO_BUSY_BIT_POS 1 diff --git a/drivers/iio/imu/inv_mpu6050/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig index 361b2328453..2d0608ba88d 100644 --- a/drivers/iio/imu/inv_mpu6050/Kconfig +++ b/drivers/iio/imu/inv_mpu6050/Kconfig @@ -9,6 +9,8 @@ config INV_MPU6050_IIO select IIO_TRIGGERED_BUFFER help This driver supports the Invensense MPU6050 devices. + This driver can also support MPU6500 in MPU6050 compatibility mode + and also in MPU6500 mode with some limitations. It is a gyroscope/accelerometer combo device. This driver can be built as a module. The module will be called inv-mpu6050. diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index cb9f96b446a..0c6517c94a9 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -660,6 +660,7 @@ static int inv_mpu_probe(struct i2c_client *client, { struct inv_mpu6050_state *st; struct iio_dev *indio_dev; + struct inv_mpu6050_platform_data *pdata; int result; if (!i2c_check_functionality(client->adapter, @@ -672,8 +673,10 @@ static int inv_mpu_probe(struct i2c_client *client, st = iio_priv(indio_dev); st->client = client; - st->plat_data = *(struct inv_mpu6050_platform_data - *)dev_get_platdata(&client->dev); + pdata = (struct inv_mpu6050_platform_data + *)dev_get_platdata(&client->dev); + if (pdata) + st->plat_data = *pdata; /* power is turned on inside check chip type*/ result = inv_check_and_setup_chip(st, id); if (result) @@ -764,6 +767,7 @@ static SIMPLE_DEV_PM_OPS(inv_mpu_pmops, inv_mpu_suspend, inv_mpu_resume); */ static const struct i2c_device_id inv_mpu_id[] = { {"mpu6050", INV_MPU6050}, + {"mpu6500", INV_MPU6500}, {} }; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 0ab382be1e6..e7799315d4d 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -59,6 +59,7 @@ struct inv_mpu6050_reg_map { /*device enum */ enum inv_devices { INV_MPU6050, + INV_MPU6500, INV_NUM_PARTS }; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e108f2a9d82..36b1ae92e23 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -150,7 +150,16 @@ static ssize_t iio_show_fixed_type(struct device *dev, type = IIO_BE; #endif } - return sprintf(buf, "%s:%c%d/%d>>%u\n", + if (this_attr->c->scan_type.repeat > 1) + return sprintf(buf, "%s:%c%d/%dX%d>>%u\n", + iio_endian_prefix[type], + this_attr->c->scan_type.sign, + this_attr->c->scan_type.realbits, + this_attr->c->scan_type.storagebits, + this_attr->c->scan_type.repeat, + this_attr->c->scan_type.shift); + else + return sprintf(buf, "%s:%c%d/%d>>%u\n", iio_endian_prefix[type], this_attr->c->scan_type.sign, this_attr->c->scan_type.realbits, @@ -165,7 +174,8 @@ static ssize_t iio_scan_el_show(struct device *dev, int ret; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - ret = test_bit(to_iio_dev_attr(attr)->address, + /* Ensure ret is 0 or 1. */ + ret = !!test_bit(to_iio_dev_attr(attr)->address, indio_dev->buffer->scan_mask); return sprintf(buf, "%d\n", ret); @@ -474,14 +484,22 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, for_each_set_bit(i, mask, indio_dev->masklength) { ch = iio_find_channel_from_si(indio_dev, i); - length = ch->scan_type.storagebits / 8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; bytes = ALIGN(bytes, length); bytes += length; } if (timestamp) { ch = iio_find_channel_from_si(indio_dev, indio_dev->scan_index_timestamp); - length = ch->scan_type.storagebits / 8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; bytes = ALIGN(bytes, length); bytes += length; } @@ -862,7 +880,8 @@ int iio_scan_mask_query(struct iio_dev *indio_dev, if (!buffer->scan_mask) return 0; - return test_bit(bit, buffer->scan_mask); + /* Ensure return value is 0 or 1. */ + return !!test_bit(bit, buffer->scan_mask); }; EXPORT_SYMBOL_GPL(iio_scan_mask_query); @@ -957,7 +976,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, indio_dev->masklength, in_ind + 1); ch = iio_find_channel_from_si(indio_dev, in_ind); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; /* Make sure we are aligned */ in_loc += length; if (in_loc % length) @@ -969,7 +992,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, goto error_clear_mux_table; } ch = iio_find_channel_from_si(indio_dev, in_ind); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; if (out_loc % length) out_loc += length - out_loc % length; if (in_loc % length) @@ -990,7 +1017,11 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, } ch = iio_find_channel_from_si(indio_dev, indio_dev->scan_index_timestamp); - length = ch->scan_type.storagebits/8; + if (ch->scan_type.repeat > 1) + length = ch->scan_type.storagebits / 8 * + ch->scan_type.repeat; + else + length = ch->scan_type.storagebits / 8; if (out_loc % length) out_loc += length - out_loc % length; if (in_loc % length) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index ede16aec20f..4b1f375c565 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -84,6 +84,9 @@ static const char * const iio_modifier_names[] = { [IIO_MOD_LIGHT_RED] = "red", [IIO_MOD_LIGHT_GREEN] = "green", [IIO_MOD_LIGHT_BLUE] = "blue", + [IIO_MOD_QUATERNION] = "quaternion", + [IIO_MOD_TEMP_AMBIENT] = "ambient", + [IIO_MOD_TEMP_OBJECT] = "object", }; /* relies on pairs of these shared then separate */ @@ -340,7 +343,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev, else if (i >= e->num_items) return -EINVAL; - return sprintf(buf, "%s\n", e->items[i]); + return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]); } EXPORT_SYMBOL_GPL(iio_enum_read); @@ -373,41 +376,53 @@ EXPORT_SYMBOL_GPL(iio_enum_write); * @buf: The buffer to which the formated value gets written * @type: One of the IIO_VAL_... constants. This decides how the val and val2 * parameters are formatted. - * @val: First part of the value, exact meaning depends on the type parameter. - * @val2: Second part of the value, exact meaning depends on the type parameter. + * @vals: pointer to the values, exact meaning depends on the type parameter. */ -ssize_t iio_format_value(char *buf, unsigned int type, int val, int val2) +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) { unsigned long long tmp; bool scale_db = false; switch (type) { case IIO_VAL_INT: - return sprintf(buf, "%d\n", val); + return sprintf(buf, "%d\n", vals[0]); case IIO_VAL_INT_PLUS_MICRO_DB: scale_db = true; case IIO_VAL_INT_PLUS_MICRO: - if (val2 < 0) - return sprintf(buf, "-%ld.%06u%s\n", abs(val), -val2, + if (vals[1] < 0) + return sprintf(buf, "-%ld.%06u%s\n", abs(vals[0]), + -vals[1], scale_db ? " dB" : ""); else - return sprintf(buf, "%d.%06u%s\n", val, val2, + return sprintf(buf, "%d.%06u%s\n", vals[0], vals[1], scale_db ? " dB" : ""); case IIO_VAL_INT_PLUS_NANO: - if (val2 < 0) - return sprintf(buf, "-%ld.%09u\n", abs(val), -val2); + if (vals[1] < 0) + return sprintf(buf, "-%ld.%09u\n", abs(vals[0]), + -vals[1]); else - return sprintf(buf, "%d.%09u\n", val, val2); + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL: - tmp = div_s64((s64)val * 1000000000LL, val2); - val2 = do_div(tmp, 1000000000LL); - val = tmp; - return sprintf(buf, "%d.%09u\n", val, val2); + tmp = div_s64((s64)vals[0] * 1000000000LL, vals[1]); + vals[1] = do_div(tmp, 1000000000LL); + vals[0] = tmp; + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); case IIO_VAL_FRACTIONAL_LOG2: - tmp = (s64)val * 1000000000LL >> val2; - val2 = do_div(tmp, 1000000000LL); - val = tmp; - return sprintf(buf, "%d.%09u\n", val, val2); + tmp = (s64)vals[0] * 1000000000LL >> vals[1]; + vals[1] = do_div(tmp, 1000000000LL); + vals[0] = tmp; + return sprintf(buf, "%d.%09u\n", vals[0], vals[1]); + case IIO_VAL_INT_MULTIPLE: + { + int i; + int len = 0; + + for (i = 0; i < size; ++i) + len += snprintf(&buf[len], PAGE_SIZE - len, "%d ", + vals[i]); + len += snprintf(&buf[len], PAGE_SIZE - len, "\n"); + return len; + } default: return 0; } @@ -419,14 +434,23 @@ static ssize_t iio_read_channel_info(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val, val2; - int ret = indio_dev->info->read_raw(indio_dev, this_attr->c, - &val, &val2, this_attr->address); + int vals[INDIO_MAX_RAW_ELEMENTS]; + int ret; + int val_len = 2; + + if (indio_dev->info->read_raw_multi) + ret = indio_dev->info->read_raw_multi(indio_dev, this_attr->c, + INDIO_MAX_RAW_ELEMENTS, + vals, &val_len, + this_attr->address); + else + ret = indio_dev->info->read_raw(indio_dev, this_attr->c, + &vals[0], &vals[1], this_attr->address); if (ret < 0) return ret; - return iio_format_value(buf, ret, val, val2); + return iio_format_value(buf, ret, val_len, vals); } /** @@ -716,6 +740,8 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, int i, ret, attrcount = 0; for_each_set_bit(i, infomask, sizeof(infomask)*8) { + if (i >= ARRAY_SIZE(iio_chan_info_postfix)) + return -EINVAL; ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], chan, &iio_read_channel_info, @@ -820,7 +846,7 @@ static ssize_t iio_show_dev_name(struct device *dev, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - return sprintf(buf, "%s\n", indio_dev->name); + return snprintf(buf, PAGE_SIZE, "%s\n", indio_dev->name); } static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index ea6e06b9c7d..258a973a1fb 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -270,7 +270,7 @@ static ssize_t iio_ev_value_show(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); - int val, val2; + int val, val2, val_arr[2]; int ret; ret = indio_dev->info->read_event_value(indio_dev, @@ -279,7 +279,9 @@ static ssize_t iio_ev_value_show(struct device *dev, &val, &val2); if (ret < 0) return ret; - return iio_format_value(buf, ret, val, val2); + val_arr[0] = val; + val_arr[1] = val2; + return iio_format_value(buf, ret, 2, val_arr); } static ssize_t iio_ev_value_store(struct device *dev, @@ -321,7 +323,9 @@ static int iio_device_add_event(struct iio_dev *indio_dev, char *postfix; int ret; - for_each_set_bit(i, mask, sizeof(*mask)) { + for_each_set_bit(i, mask, sizeof(*mask)*8) { + if (i >= ARRAY_SIZE(iio_ev_info_text)) + return -EINVAL; postfix = kasprintf(GFP_KERNEL, "%s_%s_%s", iio_ev_type_text[type], iio_ev_dir_text[dir], iio_ev_info_text[i]); diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0cf5f8e06cf..d833d55052e 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -417,12 +417,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum info) { int unused; + int vals[INDIO_MAX_RAW_ELEMENTS]; + int ret; + int val_len = 2; if (val2 == NULL) val2 = &unused; - return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel, - val, val2, info); + if (chan->indio_dev->info->read_raw_multi) { + ret = chan->indio_dev->info->read_raw_multi(chan->indio_dev, + chan->channel, INDIO_MAX_RAW_ELEMENTS, + vals, &val_len, info); + *val = vals[0]; + *val2 = vals[1]; + } else + ret = chan->indio_dev->info->read_raw(chan->indio_dev, + chan->channel, val, val2, info); + + return ret; } int iio_read_channel_raw(struct iio_channel *chan, int *val) @@ -443,6 +455,24 @@ err_unlock: } EXPORT_SYMBOL_GPL(iio_read_channel_raw); +int iio_read_channel_average_raw(struct iio_channel *chan, int *val) +{ + int ret; + + mutex_lock(&chan->indio_dev->info_exist_lock); + if (chan->indio_dev->info == NULL) { + ret = -ENODEV; + goto err_unlock; + } + + ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); +err_unlock: + mutex_unlock(&chan->indio_dev->info_exist_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); + static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, int raw, int *processed, unsigned int scale) { diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c index 47a6dbac2d0..d976e6ce60d 100644 --- a/drivers/iio/light/cm32181.c +++ b/drivers/iio/light/cm32181.c @@ -221,6 +221,7 @@ static int cm32181_read_raw(struct iio_dev *indio_dev, *val = cm32181->calibscale; return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: + *val = 0; ret = cm32181_read_als_it(cm32181, val2); return ret; } diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c index a45e07492db..39fc67e8213 100644 --- a/drivers/iio/light/cm36651.c +++ b/drivers/iio/light/cm36651.c @@ -652,7 +652,19 @@ static int cm36651_probe(struct i2c_client *client, cm36651->client = client; cm36651->ps_client = i2c_new_dummy(client->adapter, CM36651_I2C_ADDR_PS); + if (!cm36651->ps_client) { + dev_err(&client->dev, "%s: new i2c device failed\n", __func__); + ret = -ENODEV; + goto error_disable_reg; + } + cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA); + if (!cm36651->ara_client) { + dev_err(&client->dev, "%s: new i2c device failed\n", __func__); + ret = -ENODEV; + goto error_i2c_unregister_ps; + } + mutex_init(&cm36651->lock); indio_dev->dev.parent = &client->dev; indio_dev->channels = cm36651_channels; @@ -664,7 +676,7 @@ static int cm36651_probe(struct i2c_client *client, ret = cm36651_setup_reg(cm36651); if (ret) { dev_err(&client->dev, "%s: register setup failed\n", __func__); - goto error_disable_reg; + goto error_i2c_unregister_ara; } ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler, @@ -672,7 +684,7 @@ static int cm36651_probe(struct i2c_client *client, "cm36651", indio_dev); if (ret) { dev_err(&client->dev, "%s: request irq failed\n", __func__); - goto error_disable_reg; + goto error_i2c_unregister_ara; } ret = iio_device_register(indio_dev); @@ -685,6 +697,10 @@ static int cm36651_probe(struct i2c_client *client, error_free_irq: free_irq(client->irq, indio_dev); +error_i2c_unregister_ara: + i2c_unregister_device(cm36651->ara_client); +error_i2c_unregister_ps: + i2c_unregister_device(cm36651->ps_client); error_disable_reg: regulator_disable(cm36651->vled_reg); return ret; @@ -698,6 +714,8 @@ static int cm36651_remove(struct i2c_client *client) iio_device_unregister(indio_dev); regulator_disable(cm36651->vled_reg); free_irq(client->irq, indio_dev); + i2c_unregister_device(cm36651->ps_client); + i2c_unregister_device(cm36651->ara_client); return 0; } diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index 5ea4a03c7e7..04bdb85d2d9 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -5,13 +5,13 @@ * IIO features supported by the driver: * * Read-only raw channels: - * - illiminance_clear [lux] - * - illiminance_ir + * - illuminance_clear [lux] + * - illuminance_ir * - proximity * * Triggered buffer: - * - illiminance_clear - * - illiminance_ir + * - illuminance_clear + * - illuminance_ir * - proximity * * Events: diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 621541fb10a..f34c94380b4 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -37,6 +38,10 @@ struct als_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info als_illum; u32 illum; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; /* Channel definitions */ @@ -45,6 +50,7 @@ static const struct iio_chan_spec als_channels[] = { .type = IIO_INTENSITY, .modified = 1, .channel2 = IIO_MOD_LIGHT_BOTH, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -75,6 +81,7 @@ static int als_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; @@ -90,24 +97,35 @@ static int als_read_raw(struct iio_dev *indio_dev, report_id = -1; break; } - if (report_id >= 0) + if (report_id >= 0) { + poll_value = hid_sensor_read_poll_value( + &als_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&als_state->common_attributes, + true); + msleep_interruptible(poll_value * 2); + *val = sensor_hub_input_attr_get_raw_value( - als_state->common_attributes.hsdev, - HID_USAGE_SENSOR_ALS, address, - report_id); - else { + als_state->common_attributes.hsdev, + HID_USAGE_SENSOR_ALS, address, + report_id); + hid_sensor_power_state(&als_state->common_attributes, + false); + } else { *val = 0; return -EINVAL; } ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = als_state->als_illum.units; - ret_type = IIO_VAL_INT; + *val = als_state->scale_pre_decml; + *val2 = als_state->scale_post_decml; + ret_type = als_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - als_state->als_illum.unit_expo); + *val = als_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -176,9 +194,8 @@ static int als_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct als_state *als_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n", - als_state->common_attributes.data_ready); - if (als_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "als_proc_event\n"); + if (atomic_read(&als_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &als_state->illum, sizeof(als_state->illum)); @@ -229,6 +246,11 @@ static int als_parse_report(struct platform_device *pdev, dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index, st->als_illum.report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_ALS, + &st->als_illum, + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -296,7 +318,7 @@ static int hid_als_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - als_state->common_attributes.data_ready = false; + atomic_set(&als_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &als_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 1894ab196f9..d203ef4d892 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -75,6 +76,7 @@ static int prox_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; @@ -90,12 +92,24 @@ static int prox_read_raw(struct iio_dev *indio_dev, report_id = -1; break; } - if (report_id >= 0) + if (report_id >= 0) { + poll_value = hid_sensor_read_poll_value( + &prox_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&prox_state->common_attributes, + true); + + msleep_interruptible(poll_value * 2); + *val = sensor_hub_input_attr_get_raw_value( prox_state->common_attributes.hsdev, HID_USAGE_SENSOR_PROX, address, report_id); - else { + hid_sensor_power_state(&prox_state->common_attributes, + false); + } else { *val = 0; return -EINVAL; } @@ -176,9 +190,8 @@ static int prox_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct prox_state *prox_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "prox_proc_event [%d]\n", - prox_state->common_attributes.data_ready); - if (prox_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "prox_proc_event\n"); + if (atomic_read(&prox_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &prox_state->human_presence, sizeof(prox_state->human_presence)); @@ -297,7 +310,7 @@ static int hid_prox_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - prox_state->common_attributes.data_ready = false; + atomic_set(&prox_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &prox_state->common_attributes); if (ret) { diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index d86d226dcd6..05a364c543f 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -11,7 +11,8 @@ config AK8975 depends on GPIOLIB help Say yes here to build support for Asahi Kasei AK8975 3-Axis - Magnetometer. + Magnetometer. This driver can also support AK8963, if i2c + device name is identified as ak8963. To compile this driver as a module, choose M here: the module will be called ak8975. diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 74866d1efd1..09ea5c481f4 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -31,6 +31,7 @@ #include <linux/bitops.h> #include <linux/gpio.h> #include <linux/of_gpio.h> +#include <linux/acpi.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -85,7 +86,14 @@ #define AK8975_MAX_CONVERSION_TIMEOUT 500 #define AK8975_CONVERSION_DONE_POLL_TIME 10 #define AK8975_DATA_READY_TIMEOUT ((100*HZ)/1000) -#define RAW_TO_GAUSS(asa) ((((asa) + 128) * 3000) / 256) +#define RAW_TO_GAUSS_8975(asa) ((((asa) + 128) * 3000) / 256) +#define RAW_TO_GAUSS_8963(asa) ((((asa) + 128) * 6000) / 256) + +/* Compatible Asahi Kasei Compass parts */ +enum asahi_compass_chipset { + AK8975, + AK8963, +}; /* * Per-instance context data for the device. @@ -101,6 +109,7 @@ struct ak8975_data { int eoc_irq; wait_queue_head_t data_ready_queue; unsigned long flags; + enum asahi_compass_chipset chipset; }; static const int ak8975_index_to_reg[] = { @@ -272,9 +281,21 @@ static int ak8975_setup(struct i2c_client *client) * Since ASA doesn't change, we cache the resultant scale factor into the * device context in ak8975_setup(). */ - data->raw_to_gauss[0] = RAW_TO_GAUSS(data->asa[0]); - data->raw_to_gauss[1] = RAW_TO_GAUSS(data->asa[1]); - data->raw_to_gauss[2] = RAW_TO_GAUSS(data->asa[2]); + if (data->chipset == AK8963) { + /* + * H range is +-8190 and magnetometer range is +-4912. + * So HuT using the above explanation for 8975, + * 4912/8190 = ~ 6/10. + * So the Hadj should use 6/10 instead of 3/10. + */ + data->raw_to_gauss[0] = RAW_TO_GAUSS_8963(data->asa[0]); + data->raw_to_gauss[1] = RAW_TO_GAUSS_8963(data->asa[1]); + data->raw_to_gauss[2] = RAW_TO_GAUSS_8963(data->asa[2]); + } else { + data->raw_to_gauss[0] = RAW_TO_GAUSS_8975(data->asa[0]); + data->raw_to_gauss[1] = RAW_TO_GAUSS_8975(data->asa[1]); + data->raw_to_gauss[2] = RAW_TO_GAUSS_8975(data->asa[2]); + } return 0; } @@ -455,6 +476,27 @@ static const struct iio_info ak8975_info = { .driver_module = THIS_MODULE, }; +static const struct acpi_device_id ak_acpi_match[] = { + {"AK8975", AK8975}, + {"AK8963", AK8963}, + {"INVN6500", AK8963}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, ak_acpi_match); + +static char *ak8975_match_acpi_device(struct device *dev, + enum asahi_compass_chipset *chipset) +{ + const struct acpi_device_id *id; + + id = acpi_match_device(dev->driver->acpi_match_table, dev); + if (!id) + return NULL; + *chipset = (int)id->driver_data; + + return (char *)dev_name(dev); +} + static int ak8975_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -462,6 +504,7 @@ static int ak8975_probe(struct i2c_client *client, struct iio_dev *indio_dev; int eoc_gpio; int err; + char *name = NULL; /* Grab and set up the supplied GPIO. */ if (client->dev.platform_data) @@ -499,6 +542,19 @@ static int ak8975_probe(struct i2c_client *client, data->eoc_gpio = eoc_gpio; data->eoc_irq = 0; + /* id will be NULL when enumerated via ACPI */ + if (id) { + data->chipset = + (enum asahi_compass_chipset)(id->driver_data); + name = (char *) id->name; + } else if (ACPI_HANDLE(&client->dev)) + name = ak8975_match_acpi_device(&client->dev, &data->chipset); + else { + err = -ENOSYS; + goto exit_free_iio; + } + dev_dbg(&client->dev, "Asahi compass chip %s\n", name); + /* Perform some basic start-of-day setup of the device. */ err = ak8975_setup(client); if (err < 0) { @@ -513,9 +569,8 @@ static int ak8975_probe(struct i2c_client *client, indio_dev->channels = ak8975_channels; indio_dev->num_channels = ARRAY_SIZE(ak8975_channels); indio_dev->info = &ak8975_info; - indio_dev->name = id->name; indio_dev->modes = INDIO_DIRECT_MODE; - + indio_dev->name = name; err = iio_device_register(indio_dev); if (err < 0) goto exit_free_iio; @@ -552,7 +607,8 @@ static int ak8975_remove(struct i2c_client *client) } static const struct i2c_device_id ak8975_id[] = { - {"ak8975", 0}, + {"ak8975", AK8975}, + {"ak8963", AK8963}, {} }; @@ -569,6 +625,7 @@ static struct i2c_driver ak8975_driver = { .driver = { .name = "ak8975", .of_match_table = ak8975_of_match, + .acpi_match_table = ACPI_PTR(ak_acpi_match), }, .probe = ak8975_probe, .remove = ak8975_remove, diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index 6d162b7e7af..41cf29e2a37 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -42,6 +43,10 @@ struct magn_3d_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX]; u32 magn_val[MAGN_3D_CHANNEL_MAX]; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = { @@ -56,6 +61,7 @@ static const struct iio_chan_spec magn_3d_channels[] = { .type = IIO_MAGN, .modified = 1, .channel2 = IIO_MOD_X, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -65,6 +71,7 @@ static const struct iio_chan_spec magn_3d_channels[] = { .type = IIO_MAGN, .modified = 1, .channel2 = IIO_MOD_Y, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -74,6 +81,7 @@ static const struct iio_chan_spec magn_3d_channels[] = { .type = IIO_MAGN, .modified = 1, .channel2 = IIO_MOD_Z, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | @@ -104,11 +112,20 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; switch (mask) { case 0: + poll_value = hid_sensor_read_poll_value( + &magn_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&magn_state->common_attributes, true); + msleep_interruptible(poll_value * 2); + report_id = magn_state->magn[chan->scan_index].report_id; address = magn_3d_addresses[chan->scan_index]; @@ -119,17 +136,20 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev, report_id); else { *val = 0; + hid_sensor_power_state(&magn_state->common_attributes, + false); return -EINVAL; } + hid_sensor_power_state(&magn_state->common_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units; - ret_type = IIO_VAL_INT; + *val = magn_state->scale_pre_decml; + *val2 = magn_state->scale_post_decml; + ret_type = magn_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - magn_state->magn[CHANNEL_SCAN_INDEX_X].unit_expo); + *val = magn_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -198,9 +218,8 @@ static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct magn_3d_state *magn_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n", - magn_state->common_attributes.data_ready); - if (magn_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "magn_3d_proc_event\n"); + if (atomic_read(&magn_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, magn_state->magn_val, sizeof(magn_state->magn_val)); @@ -263,6 +282,11 @@ static int magn_3d_parse_report(struct platform_device *pdev, st->magn[1].index, st->magn[1].report_id, st->magn[2].index, st->magn[2].report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_COMPASS_3D, + &st->magn[CHANNEL_SCAN_INDEX_X], + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -334,7 +358,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - magn_state->common_attributes.data_ready = false; + atomic_set(&magn_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &magn_state->common_attributes); if (ret < 0) { diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index 8b77782474d..e3106b43ef4 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -199,6 +199,13 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, *val = mag3110_samp_freq[i][0]; *val2 = mag3110_samp_freq[i][1]; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_CALIBBIAS: + ret = i2c_smbus_read_word_swapped(data->client, + MAG3110_OFF_X + 2 * chan->scan_index); + if (ret < 0) + return ret; + *val = sign_extend32(ret >> 1, 14); + return IIO_VAL_INT; } return -EINVAL; } @@ -223,6 +230,11 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, data->ctrl_reg1); + case IIO_CHAN_INFO_CALIBBIAS: + if (val < -10000 || val > 10000) + return -EINVAL; + return i2c_smbus_write_word_swapped(data->client, + MAG3110_OFF_X + 2 * chan->scan_index, val << 1); default: return -EINVAL; } @@ -260,7 +272,8 @@ done: .type = IIO_MAGN, \ .modified = 1, \ .channel2 = IIO_MOD_##axis, \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_CALIBBIAS), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_SCALE), \ .scan_index = idx, \ @@ -338,14 +351,14 @@ static int mag3110_probe(struct i2c_client *client, indio_dev->num_channels = ARRAY_SIZE(mag3110_channels); indio_dev->available_scan_masks = mag3110_scan_masks; - data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT; + data->ctrl_reg1 = MAG3110_CTRL_DR_DEFAULT << MAG3110_CTRL_DR_SHIFT; ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG1, data->ctrl_reg1); if (ret < 0) return ret; ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2, - MAG3110_CTRL_AUTO_MRST_EN | MAG3110_CTRL_RAW); + MAG3110_CTRL_AUTO_MRST_EN); if (ret < 0) return ret; diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 52bbcfa1e07..240a21dd0c6 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -355,6 +355,8 @@ int st_magn_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &magn_info; + st_sensors_power_enable(indio_dev); + err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_magn_sensors), st_magn_sensors); if (err < 0) @@ -387,6 +389,9 @@ int st_magn_common_probe(struct iio_dev *indio_dev, if (err) goto st_magn_device_register_error; + dev_info(&indio_dev->dev, "registered magnetometer %s\n", + indio_dev->name); + return 0; st_magn_device_register_error: @@ -403,6 +408,8 @@ void st_magn_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); + st_sensors_power_disable(indio_dev); + iio_device_unregister(indio_dev); if (mdata->get_irq_data_ready(indio_dev) > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig index 58c62c837e1..e3aa1e58d92 100644 --- a/drivers/iio/orientation/Kconfig +++ b/drivers/iio/orientation/Kconfig @@ -16,4 +16,16 @@ config HID_SENSOR_INCLINOMETER_3D Say yes here to build support for the HID SENSOR Inclinometer 3D. +config HID_SENSOR_DEVICE_ROTATION + depends on HID_SENSOR_HUB + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select HID_SENSOR_IIO_COMMON + select HID_SENSOR_IIO_TRIGGER + tristate "HID Device Rotation" + help + Say yes here to build support for the HID SENSOR + device rotation. The output of a device rotation sensor + is presented using quaternion format. + endmenu diff --git a/drivers/iio/orientation/Makefile b/drivers/iio/orientation/Makefile index 2c97572ee91..4734dabbde1 100644 --- a/drivers/iio/orientation/Makefile +++ b/drivers/iio/orientation/Makefile @@ -4,3 +4,4 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_HID_SENSOR_INCLINOMETER_3D) += hid-sensor-incl-3d.o +obj-$(CONFIG_HID_SENSOR_DEVICE_ROTATION) += hid-sensor-rotation.o diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 070feab08fa..2478f6c2ef2 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -22,6 +22,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -42,6 +43,10 @@ struct incl_3d_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info incl[INCLI_3D_CHANNEL_MAX]; u32 incl_val[INCLI_3D_CHANNEL_MAX]; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = { @@ -106,11 +111,20 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, int report_id = -1; u32 address; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; switch (mask) { case IIO_CHAN_INFO_RAW: + poll_value = hid_sensor_read_poll_value( + &incl_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + + hid_sensor_power_state(&incl_state->common_attributes, true); + msleep_interruptible(poll_value * 2); + report_id = incl_state->incl[chan->scan_index].report_id; address = incl_3d_addresses[chan->scan_index]; @@ -120,17 +134,20 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev, HID_USAGE_SENSOR_INCLINOMETER_3D, address, report_id); else { + hid_sensor_power_state(&incl_state->common_attributes, + false); return -EINVAL; } + hid_sensor_power_state(&incl_state->common_attributes, false); ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = incl_state->incl[CHANNEL_SCAN_INDEX_X].units; - ret_type = IIO_VAL_INT; + *val = incl_state->scale_pre_decml; + *val2 = incl_state->scale_post_decml; + ret_type = incl_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - incl_state->incl[CHANNEL_SCAN_INDEX_X].unit_expo); + *val = incl_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -196,9 +213,8 @@ static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct incl_3d_state *incl_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "incl_3d_proc_event [%d]\n", - incl_state->common_attributes.data_ready); - if (incl_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "incl_3d_proc_event\n"); + if (atomic_read(&incl_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, (u8 *)incl_state->incl_val, sizeof(incl_state->incl_val)); @@ -279,6 +295,11 @@ static int incl_3d_parse_report(struct platform_device *pdev, st->incl[1].index, st->incl[1].report_id, st->incl[2].index, st->incl[2].report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_INCLINOMETER_3D, + &st->incl[CHANNEL_SCAN_INDEX_X], + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -349,7 +370,7 @@ static int hid_incl_3d_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - incl_state->common_attributes.data_ready = false; + atomic_set(&incl_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &incl_state->common_attributes); if (ret) { diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c new file mode 100644 index 00000000000..dccf848e8b0 --- /dev/null +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -0,0 +1,346 @@ +/* + * HID Sensors Driver + * Copyright (c) 2014, Intel Corporation. + * + * 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 <linux/device.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/hid-sensor-hub.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include "../common/hid-sensors/hid-sensor-trigger.h" + +struct dev_rot_state { + struct hid_sensor_hub_callbacks callbacks; + struct hid_sensor_common common_attributes; + struct hid_sensor_hub_attribute_info quaternion; + u32 sampled_vals[4]; +}; + +/* Channel definitions */ +static const struct iio_chan_spec dev_rot_channels[] = { + { + .type = IIO_ROT, + .modified = 1, + .channel2 = IIO_MOD_QUATERNION, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | + BIT(IIO_CHAN_INFO_HYSTERESIS) + } +}; + +/* Adjust channel real bits based on report descriptor */ +static void dev_rot_adjust_channel_bit_mask(struct iio_chan_spec *chan, + int size) +{ + chan->scan_type.sign = 's'; + /* Real storage bits will change based on the report desc. */ + chan->scan_type.realbits = size * 8; + /* Maximum size of a sample to capture is u32 */ + chan->scan_type.storagebits = sizeof(u32) * 8; + chan->scan_type.repeat = 4; +} + +/* Channel read_raw handler */ +static int dev_rot_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int size, int *vals, int *val_len, + long mask) +{ + struct dev_rot_state *rot_state = iio_priv(indio_dev); + int ret_type; + int i; + + vals[0] = 0; + vals[1] = 0; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (size >= 4) { + for (i = 0; i < 4; ++i) + vals[i] = rot_state->sampled_vals[i]; + ret_type = IIO_VAL_INT_MULTIPLE; + *val_len = 4; + } else + ret_type = -EINVAL; + break; + case IIO_CHAN_INFO_SAMP_FREQ: + ret_type = hid_sensor_read_samp_freq_value( + &rot_state->common_attributes, &vals[0], &vals[1]); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret_type = hid_sensor_read_raw_hyst_value( + &rot_state->common_attributes, &vals[0], &vals[1]); + break; + default: + ret_type = -EINVAL; + break; + } + + return ret_type; +} + +/* Channel write_raw handler */ +static int dev_rot_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct dev_rot_state *rot_state = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = hid_sensor_write_samp_freq_value( + &rot_state->common_attributes, val, val2); + break; + case IIO_CHAN_INFO_HYSTERESIS: + ret = hid_sensor_write_raw_hyst_value( + &rot_state->common_attributes, val, val2); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static const struct iio_info dev_rot_info = { + .driver_module = THIS_MODULE, + .read_raw_multi = &dev_rot_read_raw, + .write_raw = &dev_rot_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +{ + dev_dbg(&indio_dev->dev, "hid_sensor_push_data >>\n"); + iio_push_to_buffers(indio_dev, (u8 *)data); + dev_dbg(&indio_dev->dev, "hid_sensor_push_data <<\n"); + +} + +/* Callback handler to send event after all samples are received and captured */ +static int dev_rot_proc_event(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + dev_dbg(&indio_dev->dev, "dev_rot_proc_event\n"); + if (atomic_read(&rot_state->common_attributes.data_ready)) + hid_sensor_push_data(indio_dev, + (u8 *)rot_state->sampled_vals, + sizeof(rot_state->sampled_vals)); + + return 0; +} + +/* Capture samples in local storage */ +static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, + unsigned usage_id, + size_t raw_len, char *raw_data, + void *priv) +{ + struct iio_dev *indio_dev = platform_get_drvdata(priv); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) { + memcpy(rot_state->sampled_vals, raw_data, + sizeof(rot_state->sampled_vals)); + dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len, + sizeof(rot_state->sampled_vals)); + } + + return 0; +} + +/* Parse report which is specific to an usage id*/ +static int dev_rot_parse_report(struct platform_device *pdev, + struct hid_sensor_hub_device *hsdev, + struct iio_chan_spec *channels, + unsigned usage_id, + struct dev_rot_state *st) +{ + int ret; + + ret = sensor_hub_input_get_attribute_info(hsdev, + HID_INPUT_REPORT, + usage_id, + HID_USAGE_SENSOR_ORIENT_QUATERNION, + &st->quaternion); + if (ret) + return ret; + + dev_rot_adjust_channel_bit_mask(&channels[0], + st->quaternion.size / 4); + + dev_dbg(&pdev->dev, "dev_rot %x:%x\n", st->quaternion.index, + st->quaternion.report_id); + + dev_dbg(&pdev->dev, "dev_rot: attrib size %d\n", + st->quaternion.size); + + /* Set Sensitivity field ids, when there is no individual modifier */ + if (st->common_attributes.sensitivity.index < 0) { + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + HID_USAGE_SENSOR_DATA_ORIENTATION, + &st->common_attributes.sensitivity); + dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", + st->common_attributes.sensitivity.index, + st->common_attributes.sensitivity.report_id); + } + + return 0; +} + +/* Function to initialize the processing for usage id */ +static int hid_dev_rot_probe(struct platform_device *pdev) +{ + int ret; + static char *name = "dev_rotation"; + struct iio_dev *indio_dev; + struct dev_rot_state *rot_state; + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_chan_spec *channels; + + indio_dev = devm_iio_device_alloc(&pdev->dev, + sizeof(struct dev_rot_state)); + if (indio_dev == NULL) + return -ENOMEM; + + platform_set_drvdata(pdev, indio_dev); + + rot_state = iio_priv(indio_dev); + rot_state->common_attributes.hsdev = hsdev; + rot_state->common_attributes.pdev = pdev; + + ret = hid_sensor_parse_common_attributes(hsdev, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, + &rot_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "failed to setup common attributes\n"); + return ret; + } + + channels = devm_kmemdup(&pdev->dev, dev_rot_channels, + sizeof(dev_rot_channels), GFP_KERNEL); + if (!channels) { + dev_err(&pdev->dev, "failed to duplicate channels\n"); + return -ENOMEM; + } + + ret = dev_rot_parse_report(pdev, hsdev, channels, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, rot_state); + if (ret) { + dev_err(&pdev->dev, "failed to setup attributes\n"); + return ret; + } + + indio_dev->channels = channels; + indio_dev->num_channels = ARRAY_SIZE(dev_rot_channels); + indio_dev->dev.parent = &pdev->dev; + indio_dev->info = &dev_rot_info; + indio_dev->name = name; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, + NULL, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); + return ret; + } + atomic_set(&rot_state->common_attributes.data_ready, 0); + ret = hid_sensor_setup_trigger(indio_dev, name, + &rot_state->common_attributes); + if (ret) { + dev_err(&pdev->dev, "trigger setup failed\n"); + goto error_unreg_buffer_funcs; + } + + ret = iio_device_register(indio_dev); + if (ret) { + dev_err(&pdev->dev, "device register failed\n"); + goto error_remove_trigger; + } + + rot_state->callbacks.send_event = dev_rot_proc_event; + rot_state->callbacks.capture_sample = dev_rot_capture_sample; + rot_state->callbacks.pdev = pdev; + ret = sensor_hub_register_callback(hsdev, + HID_USAGE_SENSOR_DEVICE_ORIENTATION, + &rot_state->callbacks); + if (ret) { + dev_err(&pdev->dev, "callback reg failed\n"); + goto error_iio_unreg; + } + + return 0; + +error_iio_unreg: + iio_device_unregister(indio_dev); +error_remove_trigger: + hid_sensor_remove_trigger(&rot_state->common_attributes); +error_unreg_buffer_funcs: + iio_triggered_buffer_cleanup(indio_dev); + return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_dev_rot_remove(struct platform_device *pdev) +{ + struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data; + struct iio_dev *indio_dev = platform_get_drvdata(pdev); + struct dev_rot_state *rot_state = iio_priv(indio_dev); + + sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_DEVICE_ORIENTATION); + iio_device_unregister(indio_dev); + hid_sensor_remove_trigger(&rot_state->common_attributes); + iio_triggered_buffer_cleanup(indio_dev); + + return 0; +} + +static struct platform_device_id hid_dev_rot_ids[] = { + { + /* Format: HID-SENSOR-usage_id_in_hex_lowercase */ + .name = "HID-SENSOR-20008a", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_dev_rot_ids); + +static struct platform_driver hid_dev_rot_platform_driver = { + .id_table = hid_dev_rot_ids, + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = hid_dev_rot_probe, + .remove = hid_dev_rot_remove, +}; +module_platform_driver(hid_dev_rot_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Device Rotation"); +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig index d88ff17fedb..ffac8ac1efc 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -19,6 +19,16 @@ config HID_SENSOR_PRESS To compile this driver as a module, choose M here: the module will be called hid-sensor-press. +config MPL115 + tristate "Freescale MPL115A2 pressure sensor driver" + depends on I2C + help + Say yes here to build support for the Freescale MPL115A2 + pressure sensor connected via I2C. + + To compile this driver as a module, choose M here: the module + will be called mpl115. + config MPL3115 tristate "Freescale MPL3115A2 pressure sensor driver" depends on I2C diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index 4a57bf65b04..c53d2500737 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -4,6 +4,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o +obj-$(CONFIG_MPL115) += mpl115.o obj-$(CONFIG_MPL3115) += mpl3115.o obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o st_pressure-y := st_pressure_core.o diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index e0e6409aa94..1cd190c7378 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -21,6 +21,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> +#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -36,6 +37,10 @@ struct press_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info press_attr; u32 press_data; + int scale_pre_decml; + int scale_post_decml; + int scale_precision; + int value_offset; }; /* Channel definitions */ @@ -75,6 +80,7 @@ static int press_read_raw(struct iio_dev *indio_dev, u32 address; int ret; int ret_type; + s32 poll_value; *val = 0; *val2 = 0; @@ -90,24 +96,35 @@ static int press_read_raw(struct iio_dev *indio_dev, report_id = -1; break; } - if (report_id >= 0) + if (report_id >= 0) { + poll_value = hid_sensor_read_poll_value( + &press_state->common_attributes); + if (poll_value < 0) + return -EINVAL; + hid_sensor_power_state(&press_state->common_attributes, + true); + + msleep_interruptible(poll_value * 2); + *val = sensor_hub_input_attr_get_raw_value( press_state->common_attributes.hsdev, HID_USAGE_SENSOR_PRESSURE, address, report_id); - else { + hid_sensor_power_state(&press_state->common_attributes, + false); + } else { *val = 0; return -EINVAL; } ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SCALE: - *val = press_state->press_attr.units; - ret_type = IIO_VAL_INT; + *val = press_state->scale_pre_decml; + *val2 = press_state->scale_post_decml; + ret_type = press_state->scale_precision; break; case IIO_CHAN_INFO_OFFSET: - *val = hid_sensor_convert_exponent( - press_state->press_attr.unit_expo); + *val = press_state->value_offset; ret_type = IIO_VAL_INT; break; case IIO_CHAN_INFO_SAMP_FREQ: @@ -176,9 +193,8 @@ static int press_proc_event(struct hid_sensor_hub_device *hsdev, struct iio_dev *indio_dev = platform_get_drvdata(priv); struct press_state *press_state = iio_priv(indio_dev); - dev_dbg(&indio_dev->dev, "press_proc_event [%d]\n", - press_state->common_attributes.data_ready); - if (press_state->common_attributes.data_ready) + dev_dbg(&indio_dev->dev, "press_proc_event\n"); + if (atomic_read(&press_state->common_attributes.data_ready)) hid_sensor_push_data(indio_dev, &press_state->press_data, sizeof(press_state->press_data)); @@ -229,6 +245,11 @@ static int press_parse_report(struct platform_device *pdev, dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index, st->press_attr.report_id); + st->scale_precision = hid_sensor_format_scale( + HID_USAGE_SENSOR_PRESSURE, + &st->press_attr, + &st->scale_pre_decml, &st->scale_post_decml); + /* Set Sensitivity field ids, when there is no individual modifier */ if (st->common_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, @@ -298,7 +319,7 @@ static int hid_press_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to initialize trigger buffer\n"); goto error_free_dev_mem; } - press_state->common_attributes.data_ready = false; + atomic_set(&press_state->common_attributes.data_ready, 0); ret = hid_sensor_setup_trigger(indio_dev, name, &press_state->common_attributes); if (ret) { diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c new file mode 100644 index 00000000000..f5ecd6e19f5 --- /dev/null +++ b/drivers/iio/pressure/mpl115.c @@ -0,0 +1,211 @@ +/* + * mpl115.c - Support for Freescale MPL115A2 pressure/temperature sensor + * + * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * (7-bit I2C slave address 0x60) + * + * TODO: shutdown pin + * + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/iio/iio.h> +#include <linux/delay.h> + +#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */ +#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */ +#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */ +#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */ +#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */ +#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */ +#define MPL115_CONVERT 0x12 /* convert temperature and pressure */ + +struct mpl115_data { + struct i2c_client *client; + struct mutex lock; + s16 a0; + s16 b1, b2; + s16 c12; +}; + +static int mpl115_request(struct mpl115_data *data) +{ + int ret = i2c_smbus_write_byte_data(data->client, MPL115_CONVERT, 0); + if (ret < 0) + return ret; + + usleep_range(3000, 4000); + + return 0; +} + +static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2) +{ + int ret; + u16 padc, tadc; + int a1, y1, pcomp; + unsigned kpa; + + mutex_lock(&data->lock); + ret = mpl115_request(data); + if (ret < 0) + goto done; + + ret = i2c_smbus_read_word_swapped(data->client, MPL115_PADC); + if (ret < 0) + goto done; + padc = ret >> 6; + + ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); + if (ret < 0) + goto done; + tadc = ret >> 6; + + /* see Freescale AN3785 */ + a1 = data->b1 + ((data->c12 * tadc) >> 11); + y1 = (data->a0 << 10) + a1 * padc; + + /* compensated pressure with 4 fractional bits */ + pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9; + + kpa = pcomp * (115 - 50) / 1023 + (50 << 4); + *val = kpa >> 4; + *val2 = (kpa & 15) * (1000000 >> 4); +done: + mutex_unlock(&data->lock); + return ret; +} + +static int mpl115_read_temp(struct mpl115_data *data) +{ + int ret; + + mutex_lock(&data->lock); + ret = mpl115_request(data); + if (ret < 0) + goto done; + ret = i2c_smbus_read_word_swapped(data->client, MPL115_TADC); +done: + mutex_unlock(&data->lock); + return ret; +} + +static int mpl115_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct mpl115_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + ret = mpl115_comp_pressure(data, val, val2); + if (ret < 0) + return ret; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + /* temperature -5.35 C / LSB, 472 LSB is 25 C */ + ret = mpl115_read_temp(data); + if (ret < 0) + return ret; + *val = ret >> 6; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = 605; + *val2 = 750000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + *val = -186; + *val2 = 915888; + return IIO_VAL_INT_PLUS_MICRO; + } + return -EINVAL; +} + +static const struct iio_chan_spec mpl115_channels[] = { + { + .type = IIO_PRESSURE, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_info mpl115_info = { + .read_raw = &mpl115_read_raw, + .driver_module = THIS_MODULE, +}; + +static int mpl115_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mpl115_data *data; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + + i2c_set_clientdata(client, indio_dev); + indio_dev->info = &mpl115_info; + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = mpl115_channels; + indio_dev->num_channels = ARRAY_SIZE(mpl115_channels); + + ret = i2c_smbus_read_word_swapped(data->client, MPL115_A0); + if (ret < 0) + return ret; + data->a0 = ret; + ret = i2c_smbus_read_word_swapped(data->client, MPL115_B1); + if (ret < 0) + return ret; + data->b1 = ret; + ret = i2c_smbus_read_word_swapped(data->client, MPL115_B2); + if (ret < 0) + return ret; + data->b2 = ret; + ret = i2c_smbus_read_word_swapped(data->client, MPL115_C12); + if (ret < 0) + return ret; + data->c12 = ret; + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id mpl115_id[] = { + { "mpl115", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mpl115_id); + +static struct i2c_driver mpl115_driver = { + .driver = { + .name = "mpl115", + }, + .probe = mpl115_probe, + .id_table = mpl115_id, +}; +module_i2c_driver(mpl115_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 7418768ed49..cd7e01f3a93 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -23,7 +23,6 @@ #include <linux/iio/sysfs.h> #include <linux/iio/trigger.h> #include <linux/iio/buffer.h> -#include <linux/regulator/consumer.h> #include <asm/unaligned.h> #include <linux/iio/common/st_sensors.h> @@ -387,40 +386,6 @@ static const struct iio_trigger_ops st_press_trigger_ops = { #define ST_PRESS_TRIGGER_OPS NULL #endif -static void st_press_power_enable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - int err; - - /* Regulators not mandatory, but if requested we should enable them. */ - pdata->vdd = devm_regulator_get_optional(&indio_dev->dev, "vdd"); - if (!IS_ERR(pdata->vdd)) { - err = regulator_enable(pdata->vdd); - if (err != 0) - dev_warn(&indio_dev->dev, - "Failed to enable specified Vdd supply\n"); - } - - pdata->vdd_io = devm_regulator_get_optional(&indio_dev->dev, "vddio"); - if (!IS_ERR(pdata->vdd_io)) { - err = regulator_enable(pdata->vdd_io); - if (err != 0) - dev_warn(&indio_dev->dev, - "Failed to enable specified Vdd_IO supply\n"); - } -} - -static void st_press_power_disable(struct iio_dev *indio_dev) -{ - struct st_sensor_data *pdata = iio_priv(indio_dev); - - if (!IS_ERR(pdata->vdd)) - regulator_disable(pdata->vdd); - - if (!IS_ERR(pdata->vdd_io)) - regulator_disable(pdata->vdd_io); -} - int st_press_common_probe(struct iio_dev *indio_dev, struct st_sensors_platform_data *plat_data) { @@ -431,7 +396,7 @@ int st_press_common_probe(struct iio_dev *indio_dev, indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &press_info; - st_press_power_enable(indio_dev); + st_sensors_power_enable(indio_dev); err = st_sensors_check_device_support(indio_dev, ARRAY_SIZE(st_press_sensors), @@ -474,6 +439,9 @@ int st_press_common_probe(struct iio_dev *indio_dev, if (err) goto st_press_device_register_error; + dev_info(&indio_dev->dev, "registered pressure sensor %s\n", + indio_dev->name); + return err; st_press_device_register_error: @@ -490,7 +458,7 @@ void st_press_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *pdata = iio_priv(indio_dev); - st_press_power_disable(indio_dev); + st_sensors_power_disable(indio_dev); iio_device_unregister(indio_dev); if (pdata->get_irq_data_ready(indio_dev) > 0) diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig new file mode 100644 index 00000000000..0c8cdf58f6a --- /dev/null +++ b/drivers/iio/proximity/Kconfig @@ -0,0 +1,19 @@ +# +# Proximity sensors +# + +menu "Lightning sensors" + +config AS3935 + tristate "AS3935 Franklin lightning sensor" + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + depends on SPI + help + Say Y here to build SPI interface support for the Austrian + Microsystems AS3935 lightning detection sensor. + + To compile this driver as a module, choose M here: the + module will be called as3935 + +endmenu diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile new file mode 100644 index 00000000000..743adee1c8b --- /dev/null +++ b/drivers/iio/proximity/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for IIO proximity sensors +# + +# When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AS3935) += as3935.o diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c new file mode 100644 index 00000000000..bf677bfe8eb --- /dev/null +++ b/drivers/iio/proximity/as3935.c @@ -0,0 +1,456 @@ +/* + * as3935.c - Support for AS3935 Franklin lightning sensor + * + * Copyright (C) 2014 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/interrupt.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/buffer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/of_gpio.h> + + +#define AS3935_AFE_GAIN 0x00 +#define AS3935_AFE_MASK 0x3F +#define AS3935_AFE_GAIN_MAX 0x1F +#define AS3935_AFE_PWR_BIT BIT(0) + +#define AS3935_INT 0x03 +#define AS3935_INT_MASK 0x07 +#define AS3935_EVENT_INT BIT(3) +#define AS3935_NOISE_INT BIT(1) + +#define AS3935_DATA 0x07 +#define AS3935_DATA_MASK 0x3F + +#define AS3935_TUNE_CAP 0x08 +#define AS3935_CALIBRATE 0x3D + +#define AS3935_WRITE_DATA BIT(15) +#define AS3935_READ_DATA BIT(14) +#define AS3935_ADDRESS(x) ((x) << 8) + +#define MAX_PF_CAP 120 +#define TUNE_CAP_DIV 8 + +struct as3935_state { + struct spi_device *spi; + struct iio_trigger *trig; + struct mutex lock; + struct delayed_work work; + + u32 tune_cap; + u8 buf[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec as3935_channels[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = + BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_PROCESSED), + .scan_index = 0, + .scan_type = { + .sign = 'u', + .realbits = 6, + .storagebits = 8, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(1), +}; + +static int as3935_read(struct as3935_state *st, unsigned int reg, int *val) +{ + u8 cmd; + int ret; + + cmd = (AS3935_READ_DATA | AS3935_ADDRESS(reg)) >> 8; + ret = spi_w8r8(st->spi, cmd); + if (ret < 0) + return ret; + *val = ret; + + return 0; +}; + +static int as3935_write(struct as3935_state *st, + unsigned int reg, + unsigned int val) +{ + u8 *buf = st->buf; + + buf[0] = (AS3935_WRITE_DATA | AS3935_ADDRESS(reg)) >> 8; + buf[1] = val; + + return spi_write(st->spi, buf, 2); +}; + +static ssize_t as3935_sensor_sensitivity_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct as3935_state *st = iio_priv(dev_to_iio_dev(dev)); + int val, ret; + + ret = as3935_read(st, AS3935_AFE_GAIN, &val); + if (ret) + return ret; + val = (val & AS3935_AFE_MASK) >> 1; + + return sprintf(buf, "%d\n", val); +}; + +static ssize_t as3935_sensor_sensitivity_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct as3935_state *st = iio_priv(dev_to_iio_dev(dev)); + unsigned long val; + int ret; + + ret = kstrtoul((const char *) buf, 10, &val); + if (ret) + return -EINVAL; + + if (val > AS3935_AFE_GAIN_MAX) + return -EINVAL; + + as3935_write(st, AS3935_AFE_GAIN, val << 1); + + return len; +}; + +static IIO_DEVICE_ATTR(sensor_sensitivity, S_IRUGO | S_IWUSR, + as3935_sensor_sensitivity_show, as3935_sensor_sensitivity_store, 0); + + +static struct attribute *as3935_attributes[] = { + &iio_dev_attr_sensor_sensitivity.dev_attr.attr, + NULL, +}; + +static struct attribute_group as3935_attribute_group = { + .attrs = as3935_attributes, +}; + +static int as3935_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + struct as3935_state *st = iio_priv(indio_dev); + int ret; + + + switch (m) { + case IIO_CHAN_INFO_PROCESSED: + case IIO_CHAN_INFO_RAW: + *val2 = 0; + ret = as3935_read(st, AS3935_DATA, val); + if (ret) + return ret; + + if (m == IIO_CHAN_INFO_RAW) + return IIO_VAL_INT; + + /* storm out of range */ + if (*val == AS3935_DATA_MASK) + return -EINVAL; + *val *= 1000; + break; + default: + return -EINVAL; + } + + return IIO_VAL_INT; +} + +static const struct iio_info as3935_info = { + .driver_module = THIS_MODULE, + .attrs = &as3935_attribute_group, + .read_raw = &as3935_read_raw, +}; + +static irqreturn_t as3935_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct as3935_state *st = iio_priv(indio_dev); + int val, ret; + + ret = as3935_read(st, AS3935_DATA, &val); + if (ret) + goto err_read; + val &= AS3935_DATA_MASK; + val *= 1000; + + iio_push_to_buffers_with_timestamp(indio_dev, &val, pf->timestamp); +err_read: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +}; + +static const struct iio_trigger_ops iio_interrupt_trigger_ops = { + .owner = THIS_MODULE, +}; + +static void as3935_event_work(struct work_struct *work) +{ + struct as3935_state *st; + int val; + + st = container_of(work, struct as3935_state, work.work); + + as3935_read(st, AS3935_INT, &val); + val &= AS3935_INT_MASK; + + switch (val) { + case AS3935_EVENT_INT: + iio_trigger_poll(st->trig, iio_get_time_ns()); + break; + case AS3935_NOISE_INT: + dev_warn(&st->spi->dev, "noise level is too high"); + break; + } +}; + +static irqreturn_t as3935_interrupt_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct as3935_state *st = iio_priv(indio_dev); + + /* + * Delay work for >2 milliseconds after an interrupt to allow + * estimated distance to recalculated. + */ + + schedule_delayed_work(&st->work, msecs_to_jiffies(3)); + + return IRQ_HANDLED; +} + +static void calibrate_as3935(struct as3935_state *st) +{ + mutex_lock(&st->lock); + + /* mask disturber interrupt bit */ + as3935_write(st, AS3935_INT, BIT(5)); + + as3935_write(st, AS3935_CALIBRATE, 0x96); + as3935_write(st, AS3935_TUNE_CAP, + BIT(5) | (st->tune_cap / TUNE_CAP_DIV)); + + mdelay(2); + as3935_write(st, AS3935_TUNE_CAP, (st->tune_cap / TUNE_CAP_DIV)); + + mutex_unlock(&st->lock); +} + +#ifdef CONFIG_PM_SLEEP +static int as3935_suspend(struct spi_device *spi, pm_message_t msg) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct as3935_state *st = iio_priv(indio_dev); + int val, ret; + + mutex_lock(&st->lock); + ret = as3935_read(st, AS3935_AFE_GAIN, &val); + if (ret) + goto err_suspend; + val |= AS3935_AFE_PWR_BIT; + + ret = as3935_write(st, AS3935_AFE_GAIN, val); + +err_suspend: + mutex_unlock(&st->lock); + + return ret; +} + +static int as3935_resume(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct as3935_state *st = iio_priv(indio_dev); + int val, ret; + + mutex_lock(&st->lock); + ret = as3935_read(st, AS3935_AFE_GAIN, &val); + if (ret) + goto err_resume; + val &= ~AS3935_AFE_PWR_BIT; + ret = as3935_write(st, AS3935_AFE_GAIN, val); + +err_resume: + mutex_unlock(&st->lock); + + return ret; +} +#else +#define as3935_suspend NULL +#define as3935_resume NULL +#endif + +static int as3935_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct iio_trigger *trig; + struct as3935_state *st; + struct device_node *np = spi->dev.of_node; + int ret; + + /* Be sure lightning event interrupt is specified */ + if (!spi->irq) { + dev_err(&spi->dev, "unable to get event interrupt\n"); + return -EINVAL; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->spi = spi; + st->tune_cap = 0; + + spi_set_drvdata(spi, indio_dev); + mutex_init(&st->lock); + INIT_DELAYED_WORK(&st->work, as3935_event_work); + + ret = of_property_read_u32(np, + "ams,tuning-capacitor-pf", &st->tune_cap); + if (ret) { + st->tune_cap = 0; + dev_warn(&spi->dev, + "no tuning-capacitor-pf set, defaulting to %d", + st->tune_cap); + } + + if (st->tune_cap > MAX_PF_CAP) { + dev_err(&spi->dev, + "wrong tuning-capacitor-pf setting of %d\n", + st->tune_cap); + return -EINVAL; + } + + indio_dev->dev.parent = &spi->dev; + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->channels = as3935_channels; + indio_dev->num_channels = ARRAY_SIZE(as3935_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &as3935_info; + + trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + + if (!trig) + return -ENOMEM; + + st->trig = trig; + trig->dev.parent = indio_dev->dev.parent; + iio_trigger_set_drvdata(trig, indio_dev); + trig->ops = &iio_interrupt_trigger_ops; + + ret = iio_trigger_register(trig); + if (ret) { + dev_err(&spi->dev, "failed to register trigger\n"); + return ret; + } + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &as3935_trigger_handler, NULL); + + if (ret) { + dev_err(&spi->dev, "cannot setup iio trigger\n"); + goto unregister_trigger; + } + + calibrate_as3935(st); + + ret = devm_request_irq(&spi->dev, spi->irq, + &as3935_interrupt_handler, + IRQF_TRIGGER_RISING, + dev_name(&spi->dev), + indio_dev); + + if (ret) { + dev_err(&spi->dev, "unable to request irq\n"); + goto unregister_buffer; + } + + ret = iio_device_register(indio_dev); + if (ret < 0) { + dev_err(&spi->dev, "unable to register device\n"); + goto unregister_buffer; + } + return 0; + +unregister_buffer: + iio_triggered_buffer_cleanup(indio_dev); + +unregister_trigger: + iio_trigger_unregister(st->trig); + + return ret; +}; + +static int as3935_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct as3935_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + iio_trigger_unregister(st->trig); + + return 0; +}; + +static const struct spi_device_id as3935_id[] = { + {"as3935", 0}, + {}, +}; +MODULE_DEVICE_TABLE(spi, as3935_id); + +static struct spi_driver as3935_driver = { + .driver = { + .name = "as3935", + .owner = THIS_MODULE, + }, + .probe = as3935_probe, + .remove = as3935_remove, + .id_table = as3935_id, + .suspend = as3935_suspend, + .resume = as3935_resume, +}; +module_spi_driver(as3935_driver); + +MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); +MODULE_DESCRIPTION("AS3935 lightning sensor"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:as3935"); diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 372f8fb3085..21feaa4661b 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -3,6 +3,16 @@ # menu "Temperature sensors" +config MLX90614 + tristate "MLX90614 contact-less infrared sensor" + depends on I2C + help + If you say yes here you get support for the Melexis + MLX90614 contact-less infrared sensor connected with I2C. + + This driver can also be built as a module. If so, the module will + be called mlx90614. + config TMP006 tristate "TMP006 infrared thermopile sensor" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 24d7b602db3..40710a81158 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -2,4 +2,5 @@ # Makefile for industrial I/O temperature drivers # +obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_TMP006) += tmp006.o diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c new file mode 100644 index 00000000000..c8b6ac8b2d6 --- /dev/null +++ b/drivers/iio/temperature/mlx90614.c @@ -0,0 +1,150 @@ +/* + * mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor + * + * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Driver for the Melexis MLX90614 I2C 16-bit IR thermopile sensor + * + * (7-bit I2C slave address 0x5a, 100KHz bus speed only!) + * + * TODO: sleep mode, configuration EEPROM + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#include <linux/iio/iio.h> + +#define MLX90614_OP_RAM 0x00 + +/* RAM offsets with 16-bit data, MSB first */ +#define MLX90614_TA 0x06 /* ambient temperature */ +#define MLX90614_TOBJ1 0x07 /* object temperature */ + +struct mlx90614_data { + struct i2c_client *client; +}; + +static int mlx90614_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mlx90614_data *data = iio_priv(indio_dev); + s32 ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: /* 0.02K / LSB */ + switch (channel->channel2) { + case IIO_MOD_TEMP_AMBIENT: + ret = i2c_smbus_read_word_data(data->client, + MLX90614_OP_RAM | MLX90614_TA); + if (ret < 0) + return ret; + break; + case IIO_MOD_TEMP_OBJECT: + ret = i2c_smbus_read_word_data(data->client, + MLX90614_OP_RAM | MLX90614_TOBJ1); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + *val = ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = 13657; + *val2 = 500000; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + *val = 20; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec mlx90614_channels[] = { + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + }, + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_info mlx90614_info = { + .read_raw = mlx90614_read_raw, + .driver_module = THIS_MODULE, +}; + +static int mlx90614_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct mlx90614_data *data; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mlx90614_info; + + indio_dev->channels = mlx90614_channels; + indio_dev->num_channels = ARRAY_SIZE(mlx90614_channels); + + return iio_device_register(indio_dev); +} + +static int mlx90614_remove(struct i2c_client *client) +{ + iio_device_unregister(i2c_get_clientdata(client)); + + return 0; +} + +static const struct i2c_device_id mlx90614_id[] = { + { "mlx90614", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mlx90614_id); + +static struct i2c_driver mlx90614_driver = { + .driver = { + .name = "mlx90614", + .owner = THIS_MODULE, + }, + .probe = mlx90614_probe, + .remove = mlx90614_remove, + .id_table = mlx90614_id, +}; +module_i2c_driver(mlx90614_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Melexis MLX90614 contactless IR temperature sensor driver"); +MODULE_LICENSE("GPL"); |