diff options
Diffstat (limited to 'drivers/iio')
137 files changed, 16713 insertions, 1741 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 90cf0cda50c..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 @@ -65,13 +66,16 @@ source "drivers/iio/common/Kconfig"  source "drivers/iio/dac/Kconfig"  source "drivers/iio/frequency/Kconfig"  source "drivers/iio/gyro/Kconfig" +source "drivers/iio/humidity/Kconfig"  source "drivers/iio/imu/Kconfig"  source "drivers/iio/light/Kconfig"  source "drivers/iio/magnetometer/Kconfig" +source "drivers/iio/orientation/Kconfig"  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 bcf7e9e3b05..698afc2d17c 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -18,9 +18,12 @@ obj-y += common/  obj-y += dac/  obj-y += gyro/  obj-y += frequency/ +obj-y += humidity/  obj-y += imu/  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/bma180.c b/drivers/iio/accel/bma180.c index 81e3dc26099..a077cc86421 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -68,13 +68,13 @@  /* Defaults values */  #define BMA180_DEF_PMODE	0  #define BMA180_DEF_BW		20 -#define BMA180_DEF_SCALE	250 +#define BMA180_DEF_SCALE	2452  /* Available values for sysfs */  #define BMA180_FLP_FREQ_AVAILABLE \  	"10 20 40 75 150 300"  #define BMA180_SCALE_AVAILABLE \ -	"0.000130 0.000190 0.000250 0.000380 0.000500 0.000990 0.001980" +	"0.001275 0.001863 0.002452 0.003727 0.004903 0.009709 0.019417"  struct bma180_data {  	struct i2c_client *client; @@ -94,7 +94,7 @@ enum bma180_axis {  };  static int bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ -static int scale_table[] = { 130, 190, 250, 380, 500, 990, 1980 }; +static int scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 };  static int bma180_get_acc_reg(struct bma180_data *data, enum bma180_axis axis)  { @@ -376,6 +376,8 @@ static int bma180_write_raw(struct iio_dev *indio_dev,  		mutex_unlock(&data->mutex);  		return ret;  	case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: +		if (val2) +			return -EINVAL;  		mutex_lock(&data->mutex);  		ret = bma180_set_bw(data, val);  		mutex_unlock(&data->mutex); @@ -447,23 +449,28 @@ static const struct iio_chan_spec_ext_info bma180_ext_info[] = {  	{ },  }; -#define BMA180_CHANNEL(_index) {					\ +#define BMA180_CHANNEL(_axis) {					\  	.type = IIO_ACCEL,						\ -	.indexed = 1,							\ -	.channel = (_index),						\ -	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\ +	.modified = 1,							\ +	.channel2 = IIO_MOD_##_axis,					\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |		\  		BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),	\ -	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\ -	.scan_index = (_index),						\ -	.scan_type = IIO_ST('s', 14, 16, 2),				\ +	.scan_index = AXIS_##_axis,					\ +	.scan_type = {							\ +		.sign = 's',						\ +		.realbits = 14,						\ +		.storagebits = 16,					\ +		.shift = 2,						\ +	},								\  	.ext_info = bma180_ext_info,					\  }  static const struct iio_chan_spec bma180_channels[] = { -	BMA180_CHANNEL(AXIS_X), -	BMA180_CHANNEL(AXIS_Y), -	BMA180_CHANNEL(AXIS_Z), -	IIO_CHAN_SOFT_TIMESTAMP(4), +	BMA180_CHANNEL(X), +	BMA180_CHANNEL(Y), +	BMA180_CHANNEL(Z), +	IIO_CHAN_SOFT_TIMESTAMP(3),  };  static irqreturn_t bma180_trigger_handler(int irq, void *p) @@ -471,13 +478,10 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct bma180_data *data = iio_priv(indio_dev); +	int64_t time_ns = iio_get_time_ns();  	int bit, ret, i = 0;  	mutex_lock(&data->mutex); -	if (indio_dev->scan_timestamp) { -		ret = indio_dev->scan_bytes / sizeof(s64) - 1; -		((s64 *)data->buff)[ret] = iio_get_time_ns(); -	}  	for_each_set_bit(bit, indio_dev->buffer->scan_mask,  			 indio_dev->masklength) { @@ -490,7 +494,7 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p)  	}  	mutex_unlock(&data->mutex); -	iio_push_to_buffers(indio_dev, (u8 *)data->buff); +	iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns);  err:  	iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 46d22f3fb1a..54e464e4bb7 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) | @@ -102,44 +110,52 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,  	struct accel_3d_state *accel_state = iio_priv(indio_dev);  	int report_id = -1;  	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: -		ret = hid_sensor_read_samp_freq_value( +		ret_type = hid_sensor_read_samp_freq_value(  			&accel_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	case IIO_CHAN_INFO_HYSTERESIS: -		ret = hid_sensor_read_raw_hyst_value( +		ret_type = hid_sensor_read_raw_hyst_value(  			&accel_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	default:  		ret_type = -EINVAL; @@ -182,10 +198,11 @@ static const struct iio_info accel_3d_info = {  };  /* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +	int len)  {  	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); -	iio_push_to_buffers(indio_dev, (u8 *)data); +	iio_push_to_buffers(indio_dev, data);  }  /* Callback handler to send event after all samples are received and captured */ @@ -196,11 +213,10 @@ 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, -				(u8 *)accel_state->accel_val, +				accel_state->accel_val,  				sizeof(accel_state->accel_val));  	return 0; @@ -261,6 +277,23 @@ 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, +			HID_FEATURE_REPORT, usage_id, +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | +			HID_USAGE_SENSOR_DATA_ACCELERATION, +			&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 ret;  } @@ -320,7 +353,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) { @@ -349,7 +382,7 @@ static int hid_accel_3d_probe(struct platform_device *pdev)  error_iio_unreg:  	iio_device_unregister(indio_dev);  error_remove_trigger: -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&accel_state->common_attributes);  error_unreg_buffer_funcs:  	iio_triggered_buffer_cleanup(indio_dev);  error_free_dev_mem: @@ -362,10 +395,11 @@ static int hid_accel_3d_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 accel_3d_state *accel_state = iio_priv(indio_dev);  	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);  	iio_device_unregister(indio_dev); -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&accel_state->common_attributes);  	iio_triggered_buffer_cleanup(indio_dev);  	kfree(indio_dev->channels); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 709c13259f1..98ba761cbb9 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -112,9 +112,10 @@ static int kxsd9_read(struct iio_dev *indio_dev, u8 address)  	mutex_lock(&st->buf_lock);  	st->tx[0] = KXSD9_READ(address);  	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers)); -	if (ret) -		return ret; -	return (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0); +	if (!ret) +		ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0); +	mutex_unlock(&st->buf_lock); +	return ret;  }  static IIO_CONST_ATTR(accel_scale_available, @@ -222,7 +223,6 @@ static int kxsd9_probe(struct spi_device *spi)  {  	struct iio_dev *indio_dev;  	struct kxsd9_state *st; -	int ret;  	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));  	if (!indio_dev) @@ -244,11 +244,7 @@ static int kxsd9_probe(struct spi_device *spi)  	spi_setup(spi);  	kxsd9_power_up(st); -	ret = iio_device_register(indio_dev); -	if (ret) -		return ret; - -	return 0; +	return iio_device_register(indio_dev);  }  static int kxsd9_remove(struct spi_device *spi) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c new file mode 100644 index 00000000000..2a5fa9a436e --- /dev/null +++ b/drivers/iio/accel/mma8452.c @@ -0,0 +1,445 @@ +/* + * 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} +}; + +/*  + * Hardware has fullscale of -2G, -4G, -8G corresponding to raw value -2048 + * The userspace interface uses m/s^2 and we declare micro units + * So scale factor is given by: + * 	g * N * 1000000 / 2048 for N = 2, 4, 8 and g=9.80665 + */ +static const int mma8452_scales[3][2] = { +	{0, 9577}, {0, 19154}, {0, 38307} +}; + +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_buffer.c b/drivers/iio/accel/st_accel_buffer.c index d9b350756f9..a1e642ee13d 100644 --- a/drivers/iio/accel/st_accel_buffer.c +++ b/drivers/iio/accel/st_accel_buffer.c @@ -32,16 +32,7 @@ int st_accel_trig_set_state(struct iio_trigger *trig, bool state)  static int st_accel_buffer_preenable(struct iio_dev *indio_dev)  { -	int err; - -	err = st_sensors_set_enable(indio_dev, true); -	if (err < 0) -		goto st_accel_set_enable_error; - -	err = iio_sw_buffer_preenable(indio_dev); - -st_accel_set_enable_error: -	return err; +	return st_sensors_set_enable(indio_dev, true);  }  static int st_accel_buffer_postenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 1458343f6f3..a2abf7c2ce3 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -452,16 +452,19 @@ static const struct iio_trigger_ops st_accel_trigger_ops = {  int st_accel_common_probe(struct iio_dev *indio_dev,  				struct st_sensors_platform_data *plat_data)  { -	int err;  	struct st_sensor_data *adata = iio_priv(indio_dev); +	int irq = adata->get_irq_data_ready(indio_dev); +	int err;  	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) -		goto st_accel_common_probe_error; +		return err;  	adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;  	adata->multiread_bit = adata->sensor->multi_read_bit; @@ -478,13 +481,13 @@ int st_accel_common_probe(struct iio_dev *indio_dev,  	err = st_sensors_init_sensor(indio_dev, plat_data);  	if (err < 0) -		goto st_accel_common_probe_error; +		return err; -	if (adata->get_irq_data_ready(indio_dev) > 0) { -		err = st_accel_allocate_ring(indio_dev); -		if (err < 0) -			goto st_accel_common_probe_error; +	err = st_accel_allocate_ring(indio_dev); +	if (err < 0) +		return err; +	if (irq > 0) {  		err = st_sensors_allocate_trigger(indio_dev,  						 ST_ACCEL_TRIGGER_OPS);  		if (err < 0) @@ -495,15 +498,17 @@ int st_accel_common_probe(struct iio_dev *indio_dev,  	if (err)  		goto st_accel_device_register_error; -	return err; +	dev_info(&indio_dev->dev, "registered accelerometer %s\n", +		 indio_dev->name); + +	return 0;  st_accel_device_register_error: -	if (adata->get_irq_data_ready(indio_dev) > 0) +	if (irq > 0)  		st_sensors_deallocate_trigger(indio_dev);  st_accel_probe_trigger_error: -	if (adata->get_irq_data_ready(indio_dev) > 0) -		st_accel_deallocate_ring(indio_dev); -st_accel_common_probe_error: +	st_accel_deallocate_ring(indio_dev); +  	return err;  }  EXPORT_SYMBOL(st_accel_common_probe); @@ -512,11 +517,13 @@ 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) { +	if (adata->get_irq_data_ready(indio_dev) > 0)  		st_sensors_deallocate_trigger(indio_dev); -		st_accel_deallocate_ring(indio_dev); -	} + +	st_accel_deallocate_ring(indio_dev);  }  EXPORT_SYMBOL(st_accel_common_remove); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 09371cbc9dc..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. @@ -145,6 +157,27 @@ config MCP320X  	  This driver can also be built as a module. If so, the module will be  	  called mcp320x. +config MCP3422 +	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, 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. + +config MEN_Z188_ADC +	tristate "MEN 16z188 ADC IP Core support" +	depends on MCB +	help +	  Say yes here to enable support for the MEN 16z188 ADC IP-Core on a MCB +	  carrier. + +	  This driver can also be built as a module. If so, the module will be +	  called men_z188_adc. +  config NAU7802  	tristate "Nuvoton NAU7802 ADC driver"  	depends on I2C @@ -167,10 +200,22 @@ config TI_ADC081C  config TI_AM335X_ADC  	tristate "TI's AM335X ADC driver"  	depends on MFD_TI_AM335X_TSCADC +	select IIO_BUFFER +	select IIO_KFIFO_BUF  	help  	  Say yes here to build support for Texas Instruments ADC  	  driver which is also a MFD client. +config TWL4030_MADC +	tristate "TWL4030 MADC (Monitoring A/D Converter)" +	depends on TWL4030_CORE +	help +	This driver provides support for Triton TWL4030-MADC. The +	driver supports both RT and SW conversion methods. + +	This driver can also be built as a module. If so, the module will be +	called twl4030-madc. +  config TWL6030_GPADC  	tristate "TWL6030 GPADC (General Purpose A/D Converter) Support"  	depends on TWL4030_CORE @@ -185,6 +230,16 @@ config TWL6030_GPADC  	  This driver can also be built as a module. If so, the module will be  	  called twl6030-gpadc. +config VF610_ADC +	tristate "Freescale vf610 ADC driver" +	depends on OF +	help +	  Say yes here to support for Vybrid board analog-to-digital converter. +	  Since the IP is used for i.MX6SLX, the driver also support i.MX6SLX. + +	  This driver can also be built as a module. If so, the module will be +	  called vf610_adc. +  config VIPERBOARD_ADC  	tristate "Viperboard ADC support"  	depends on MFD_VIPERBOARD && USB @@ -192,4 +247,17 @@ config VIPERBOARD_ADC  	  Say yes here to access the ADC part of the Nano River  	  Technologies Viperboard. +config XILINX_XADC +	tristate "Xilinx XADC driver" +	depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST +	depends on HAS_IOMEM +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	  Say yes here to have support for the Xilinx XADC. The driver does support +	  both the ZYNQ interface to the XADC as well as the AXI-XADC interface. + +	  The driver can also be build as a module. If so, the module will be called +	  xilinx-xadc. +  endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 33656ef7d1f..9d60f2deaaa 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,13 +11,20 @@ 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  obj-$(CONFIG_MAX1363) += max1363.o  obj-$(CONFIG_MCP320X) += mcp320x.o +obj-$(CONFIG_MCP3422) += mcp3422.o +obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o  obj-$(CONFIG_NAU7802) += nau7802.o  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o  obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o  obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o +obj-$(CONFIG_VF610_ADC) += vf610_adc.o  obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o +xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o +obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 371731df163..70f78c3062a 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -27,7 +27,7 @@  struct ad7266_state {  	struct spi_device	*spi;  	struct regulator	*reg; -	unsigned long		vref_uv; +	unsigned long		vref_mv;  	struct spi_transfer	single_xfer[3];  	struct spi_message	single_msg; @@ -43,35 +43,28 @@ struct ad7266_state {  	 * The buffer needs to be large enough to hold two samples (4 bytes) and  	 * the naturally aligned timestamp (8 bytes).  	 */ -	uint8_t data[ALIGN(4, sizeof(s64)) + sizeof(s64)] ____cacheline_aligned; +	struct { +		__be16 sample[2]; +		s64 timestamp; +	} data ____cacheline_aligned;  };  static int ad7266_wakeup(struct ad7266_state *st)  {  	/* Any read with >= 2 bytes will wake the device */ -	return spi_read(st->spi, st->data, 2); +	return spi_read(st->spi, &st->data.sample[0], 2);  }  static int ad7266_powerdown(struct ad7266_state *st)  {  	/* Any read with < 2 bytes will powerdown the device */ -	return spi_read(st->spi, st->data, 1); +	return spi_read(st->spi, &st->data.sample[0], 1);  }  static int ad7266_preenable(struct iio_dev *indio_dev)  {  	struct ad7266_state *st = iio_priv(indio_dev); -	int ret; - -	ret = ad7266_wakeup(st); -	if (ret) -		return ret; - -	ret = iio_sw_buffer_preenable(indio_dev); -	if (ret) -		ad7266_powerdown(st); - -	return ret; +	return ad7266_wakeup(st);  }  static int ad7266_postdisable(struct iio_dev *indio_dev) @@ -94,11 +87,10 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)  	struct ad7266_state *st = iio_priv(indio_dev);  	int ret; -	ret = spi_read(st->spi, st->data, 4); +	ret = spi_read(st->spi, st->data.sample, 4);  	if (ret == 0) { -		if (indio_dev->scan_timestamp) -			((s64 *)st->data)[1] = pf->timestamp; -		iio_push_to_buffers(indio_dev, (u8 *)st->data); +		iio_push_to_buffers_with_timestamp(indio_dev, &st->data, +			    pf->timestamp);  	}  	iio_trigger_notify_done(indio_dev->trig); @@ -148,7 +140,7 @@ static int ad7266_read_single(struct ad7266_state *st, int *val,  	ad7266_select_input(st, address);  	ret = spi_sync(st->spi, &st->single_msg); -	*val = be16_to_cpu(st->data[address % 2]); +	*val = be16_to_cpu(st->data.sample[address % 2]);  	return ret;  } @@ -157,7 +149,7 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,  	struct iio_chan_spec const *chan, int *val, int *val2, long m)  {  	struct ad7266_state *st = iio_priv(indio_dev); -	unsigned long scale_uv; +	unsigned long scale_mv;  	int ret;  	switch (m) { @@ -175,16 +167,15 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (st->vref_uv * 100); +		scale_mv = st->vref_mv;  		if (st->mode == AD7266_MODE_DIFF) -			scale_uv *= 2; +			scale_mv *= 2;  		if (st->range == AD7266_RANGE_2VREF) -			scale_uv *= 2; +			scale_mv *= 2; -		scale_uv >>= chan->scan_type.realbits; -		*val =  scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = scale_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	case IIO_CHAN_INFO_OFFSET:  		if (st->range == AD7266_RANGE_2VREF &&  			st->mode != AD7266_MODE_DIFF) @@ -293,7 +284,7 @@ static const struct iio_info ad7266_info = {  	.driver_module = THIS_MODULE,  }; -static unsigned long ad7266_available_scan_masks[] = { +static const unsigned long ad7266_available_scan_masks[] = {  	0x003,  	0x00c,  	0x030, @@ -303,14 +294,14 @@ static unsigned long ad7266_available_scan_masks[] = {  	0x000,  }; -static unsigned long ad7266_available_scan_masks_diff[] = { +static const unsigned long ad7266_available_scan_masks_diff[] = {  	0x003,  	0x00c,  	0x030,  	0x000,  }; -static unsigned long ad7266_available_scan_masks_fixed[] = { +static const unsigned long ad7266_available_scan_masks_fixed[] = {  	0x003,  	0x000,  }; @@ -318,7 +309,7 @@ static unsigned long ad7266_available_scan_masks_fixed[] = {  struct ad7266_chan_info {  	const struct iio_chan_spec *channels;  	unsigned int num_channels; -	unsigned long *scan_masks; +	const unsigned long *scan_masks;  };  #define AD7266_CHAN_INFO_INDEX(_differential, _signed, _fixed) \ @@ -415,10 +406,10 @@ static int ad7266_probe(struct spi_device *spi)  		if (ret < 0)  			goto error_disable_reg; -		st->vref_uv = ret; +		st->vref_mv = ret / 1000;  	} else {  		/* Use internal reference */ -		st->vref_uv = 2500000; +		st->vref_mv = 2500;  	}  	if (pdata) { @@ -454,15 +445,15 @@ static int ad7266_probe(struct spi_device *spi)  	ad7266_init_channels(indio_dev);  	/* wakeup */ -	st->single_xfer[0].rx_buf = &st->data; +	st->single_xfer[0].rx_buf = &st->data.sample[0];  	st->single_xfer[0].len = 2;  	st->single_xfer[0].cs_change = 1;  	/* conversion */ -	st->single_xfer[1].rx_buf = &st->data; +	st->single_xfer[1].rx_buf = st->data.sample;  	st->single_xfer[1].len = 4;  	st->single_xfer[1].cs_change = 1;  	/* powerdown */ -	st->single_xfer[2].tx_buf = &st->data; +	st->single_xfer[2].tx_buf = &st->data.sample[0];  	st->single_xfer[2].len = 1;  	spi_message_init(&st->single_msg); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 85d1481c312..2a3b65c74af 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -159,20 +159,14 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct ad7298_state *st = iio_priv(indio_dev); -	s64 time_ns = 0;  	int b_sent;  	b_sent = spi_sync(st->spi, &st->ring_msg);  	if (b_sent)  		goto done; -	if (indio_dev->scan_timestamp) { -		time_ns = iio_get_time_ns(); -		memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64), -			&time_ns, sizeof(time_ns)); -	} - -	iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf); +	iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, +		iio_get_time_ns());  done:  	iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 6d2b1d8d1a1..d141d452c3d 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -64,19 +64,14 @@ static irqreturn_t ad7476_trigger_handler(int irq, void  *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct ad7476_state *st = iio_priv(indio_dev); -	s64 time_ns;  	int b_sent;  	b_sent = spi_sync(st->spi, &st->msg);  	if (b_sent < 0)  		goto done; -	time_ns = iio_get_time_ns(); - -	if (indio_dev->scan_timestamp) -		((s64 *)st->data)[1] = time_ns; - -	iio_push_to_buffers(indio_dev, st->data); +	iio_push_to_buffers_with_timestamp(indio_dev, st->data, +		iio_get_time_ns());  done:  	iio_trigger_notify_done(indio_dev->trig); @@ -132,10 +127,9 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,  		} else {  			scale_uv = st->chip_info->int_vref_uv;  		} -		scale_uv >>= chan->scan_type.realbits; -		*val =  scale_uv / 1000; -		*val2 = (scale_uv % 1000) * 1000; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = scale_uv / 1000; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index c20203577d2..c19f8fd1b4b 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -202,7 +202,6 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,  {  	struct ad7791_state *st = iio_priv(indio_dev);  	bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR); -	unsigned long long scale_pv;  	switch (info) {  	case IIO_CHAN_INFO_RAW: @@ -220,23 +219,26 @@ static int ad7791_read_raw(struct iio_dev *indio_dev,  	case IIO_CHAN_INFO_SCALE:  		/* The monitor channel uses an internal reference. */  		if (chan->address == AD7791_CH_AVDD_MONITOR) { -			scale_pv = 5850000000000ULL; +			/* +			 * The signal is attenuated by a factor of 5 and +			 * compared against a 1.17V internal reference. +			 */ +			*val = 1170 * 5;  		} else {  			int voltage_uv;  			voltage_uv = regulator_get_voltage(st->reg);  			if (voltage_uv < 0)  				return voltage_uv; -			scale_pv = (unsigned long long)voltage_uv * 1000000; + +			*val = voltage_uv / 1000;  		}  		if (unipolar) -			scale_pv >>= chan->scan_type.realbits; +			*val2 = chan->scan_type.realbits;  		else -			scale_pv >>= chan->scan_type.realbits - 1; -		*val2 = do_div(scale_pv, 1000000000); -		*val = scale_pv; +			*val2 = chan->scan_type.realbits - 1; -		return IIO_VAL_INT_PLUS_NANO; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL; diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 9dd077b7875..749a6cadab8 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -78,11 +78,6 @@ enum ad7887_supported_device_ids {  static int ad7887_ring_preenable(struct iio_dev *indio_dev)  {  	struct ad7887_state *st = iio_priv(indio_dev); -	int ret; - -	ret = iio_sw_buffer_preenable(indio_dev); -	if (ret < 0) -		return ret;  	/* We know this is a single long so can 'cheat' */  	switch (*indio_dev->active_scan_mask) { @@ -121,20 +116,14 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct ad7887_state *st = iio_priv(indio_dev); -	s64 time_ns;  	int b_sent;  	b_sent = spi_sync(st->spi, st->ring_msg);  	if (b_sent)  		goto done; -	time_ns = iio_get_time_ns(); - -	if (indio_dev->scan_timestamp) -		memcpy(st->data + indio_dev->scan_bytes - sizeof(s64), -		       &time_ns, sizeof(time_ns)); - -	iio_push_to_buffers(indio_dev, st->data); +	iio_push_to_buffers_with_timestamp(indio_dev, st->data, +		iio_get_time_ns());  done:  	iio_trigger_notify_done(indio_dev->trig); @@ -211,7 +200,13 @@ static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {  			.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),  			.address = 1,  			.scan_index = 1, -			.scan_type = IIO_ST('u', 12, 16, 0), +			.scan_type = { +				.sign = 'u', +				.realbits = 12, +				.storagebits = 16, +				.shift = 0, +				.endianness = IIO_BE, +			},  		},  		.channel[1] = {  			.type = IIO_VOLTAGE, @@ -221,7 +216,13 @@ static const struct ad7887_chip_info ad7887_chip_info_tbl[] = {  			.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),  			.address = 0,  			.scan_index = 0, -			.scan_type = IIO_ST('u', 12, 16, 0), +			.scan_type = { +				.sign = 'u', +				.realbits = 12, +				.storagebits = 16, +				.shift = 0, +				.endianness = IIO_BE, +			},  		},  		.channel[2] = IIO_CHAN_SOFT_TIMESTAMP(2),  		.int_vref_mv = 2500, diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index 4108dbb28c3..28732c28e81 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -174,20 +174,14 @@ static irqreturn_t ad7923_trigger_handler(int irq, void *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct ad7923_state *st = iio_priv(indio_dev); -	s64 time_ns = 0;  	int b_sent;  	b_sent = spi_sync(st->spi, &st->ring_msg);  	if (b_sent)  		goto done; -	if (indio_dev->scan_timestamp) { -		time_ns = iio_get_time_ns(); -		memcpy((u8 *)st->rx_buf + indio_dev->scan_bytes - sizeof(s64), -			&time_ns, sizeof(time_ns)); -	} - -	iio_push_to_buffers(indio_dev, (u8 *)st->rx_buf); +	iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, +		iio_get_time_ns());  done:  	iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c new file mode 100644 index 00000000000..6eba301ee03 --- /dev/null +++ b/drivers/iio/adc/ad799x.c @@ -0,0 +1,795 @@ +/* + * 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); + +	if (val < 0 || val > RES_MASK(chan->scan_type.realbits)) +		return -EINVAL; + +	mutex_lock(&indio_dev->mlock); +	ret = ad799x_i2c_write16(st, ad799x_threshold_reg(chan, dir, info), +		val << chan->scan_type.shift); +	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 >> chan->scan_type.shift) & +		RES_MASK(chan->scan_type.realbits); + +	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/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index f0d6335ae08..9a4e0e32a77 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -188,7 +188,7 @@ static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,  	spi_bus_lock(sigma_delta->spi->master);  	sigma_delta->bus_locked = true; -	INIT_COMPLETION(sigma_delta->completion); +	reinit_completion(&sigma_delta->completion);  	ret = ad_sigma_delta_set_mode(sigma_delta, mode);  	if (ret < 0) @@ -259,7 +259,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,  	spi_bus_lock(sigma_delta->spi->master);  	sigma_delta->bus_locked = true; -	INIT_COMPLETION(sigma_delta->completion); +	reinit_completion(&sigma_delta->completion);  	ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); @@ -343,7 +343,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)  {  	struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); -	INIT_COMPLETION(sigma_delta->completion); +	reinit_completion(&sigma_delta->completion);  	wait_for_completion_timeout(&sigma_delta->completion, HZ);  	if (!sigma_delta->irq_dis) { @@ -368,10 +368,6 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)  	memset(data, 0x00, 16); -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (indio_dev->scan_timestamp) -		((s64 *)data)[1] = pf->timestamp; -  	reg_size = indio_dev->channels[0].scan_type.realbits +  			indio_dev->channels[0].scan_type.shift;  	reg_size = DIV_ROUND_UP(reg_size, 8); @@ -391,7 +387,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)  		break;  	} -	iio_push_to_buffers(indio_dev, (uint8_t *)data); +	iio_push_to_buffers_with_timestamp(indio_dev, data, pf->timestamp);  	iio_trigger_notify_done(indio_dev->trig);  	sigma_delta->irq_dis = false; @@ -401,7 +397,6 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)  }  static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = { -	.preenable = &iio_sw_buffer_preenable,  	.postenable = &ad_sd_buffer_postenable,  	.predisable = &iio_triggered_buffer_predisable,  	.postdisable = &ad_sd_buffer_postdisable, diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 0f16b553e06..2b6a9ce9927 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -11,6 +11,7 @@  #include <linux/clk.h>  #include <linux/err.h>  #include <linux/io.h> +#include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/jiffies.h>  #include <linux/kernel.h> @@ -30,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)) @@ -39,7 +141,50 @@  #define at91_adc_writel(st, reg, val) \  	(writel_relaxed(val, st->reg_base + reg)) +#define DRIVER_NAME		"at91_adc" +#define MAX_POS_BITS		12 + +#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 */ +	/* +	 * Numbers of sampling data will be averaged. Can be 0~3. +	 * Hardware can average (2 ^ ts_filter_average) sample data. +	 */ +	u8	ts_filter_average; +	/* Pen Detection input pull-up resistor, can be 0~3 */ +	u8	ts_pen_detect_sensitivity; + +	/* startup time calculate function */ +	u32 (*calc_startup_ticks)(u8 startup_time, u32 adc_clk_khz); + +	u8	num_channels;  	struct at91_adc_reg_desc registers;  }; @@ -67,6 +212,31 @@ struct at91_adc_state {  	bool			low_res;	/* the resolution corresponds to the lowest one */  	wait_queue_head_t	wq_data_avail;  	struct at91_adc_caps	*caps; + +	/* +	 * Following ADC channels are shared by touchscreen: +	 * +	 * CH0 -- Touch screen XP/UL +	 * CH1 -- Touch screen XM/UR +	 * CH2 -- Touch screen YP/LL +	 * CH3 -- Touch screen YM/Sense +	 * CH4 -- Touch screen LR(5-wire only) +	 * +	 * The bitfields below represents the reserved channel in the +	 * touchscreen mode. +	 */ +#define CHAN_MASK_TOUCHSCREEN_4WIRE	(0xf << 0) +#define CHAN_MASK_TOUCHSCREEN_5WIRE	(0x1f << 0) +	enum atmel_adc_ts_type	touchscreen_type; +	struct input_dev	*ts_input; + +	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) @@ -83,13 +253,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)  		j++;  	} -	if (idev->scan_timestamp) { -		s64 *timestamp = (s64 *)((u8 *)st->buffer + -					ALIGN(j, sizeof(s64))); -		*timestamp = pf->timestamp; -	} - -	iio_push_to_buffers(idev, (u8 *)st->buffer); +	iio_push_to_buffers_with_timestamp(idev, st->buffer, pf->timestamp);  	iio_trigger_notify_done(idev->trig); @@ -101,14 +265,10 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)  	return IRQ_HANDLED;  } -static irqreturn_t at91_adc_eoc_trigger(int irq, void *private) +/* Handler for classic adc channel eoc trigger */ +void handle_adc_eoc_trigger(int irq, struct iio_dev *idev)  { -	struct iio_dev *idev = private;  	struct at91_adc_state *st = iio_priv(idev); -	u32 status = at91_adc_readl(st, st->registers->status_register); - -	if (!(status & st->registers->drdy_mask)) -		return IRQ_HANDLED;  	if (iio_buffer_enabled(idev)) {  		disable_irq_nosync(irq); @@ -118,6 +278,180 @@ static irqreturn_t at91_adc_eoc_trigger(int irq, void *private)  		st->done = true;  		wake_up_interruptible(&st->wq_data_avail);  	} +} + +static int at91_ts_sample(struct at91_adc_state *st) +{ +	unsigned int xscale, yscale, reg, z1, z2; +	unsigned int x, y, pres, xpos, ypos; +	unsigned int rxp = 1; +	unsigned int factor = 1000; +	struct iio_dev *idev = iio_priv_to_dev(st); + +	unsigned int xyz_mask_bits = st->res; +	unsigned int xyz_mask = (1 << xyz_mask_bits) - 1; + +	/* calculate position */ +	/* x position = (x / xscale) * max, max = 2^MAX_POS_BITS - 1 */ +	reg = at91_adc_readl(st, AT91_ADC_TSXPOSR); +	xpos = reg & xyz_mask; +	x = (xpos << MAX_POS_BITS) - xpos; +	xscale = (reg >> 16) & xyz_mask; +	if (xscale == 0) { +		dev_err(&idev->dev, "Error: xscale == 0!\n"); +		return -1; +	} +	x /= xscale; + +	/* y position = (y / yscale) * max, max = 2^MAX_POS_BITS - 1 */ +	reg = at91_adc_readl(st, AT91_ADC_TSYPOSR); +	ypos = reg & xyz_mask; +	y = (ypos << MAX_POS_BITS) - ypos; +	yscale = (reg >> 16) & xyz_mask; +	if (yscale == 0) { +		dev_err(&idev->dev, "Error: yscale == 0!\n"); +		return -1; +	} +	y /= yscale; + +	/* calculate the pressure */ +	reg = at91_adc_readl(st, AT91_ADC_TSPRESSR); +	z1 = reg & xyz_mask; +	z2 = (reg >> 16) & xyz_mask; + +	if (z1 != 0) +		pres = rxp * (x * factor / 1024) * (z2 * factor / z1 - factor) +			/ factor; +	else +		pres = st->ts_pressure_threshold;	/* no pen contacted */ + +	dev_dbg(&idev->dev, "xpos = %d, xscale = %d, ypos = %d, yscale = %d, z1 = %d, z2 = %d, press = %d\n", +				xpos, xscale, ypos, yscale, z1, z2, pres); + +	if (pres < st->ts_pressure_threshold) { +		dev_dbg(&idev->dev, "x = %d, y = %d, pressure = %d\n", +					x, y, pres / factor); +		input_report_abs(st->ts_input, ABS_X, x); +		input_report_abs(st->ts_input, ABS_Y, y); +		input_report_abs(st->ts_input, ABS_PRESSURE, pres); +		input_report_key(st->ts_input, BTN_TOUCH, 1); +		input_sync(st->ts_input); +	} else { +		dev_dbg(&idev->dev, "pressure too low: not reporting\n"); +	} + +	return 0; +} + +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); +	u32 status = at91_adc_readl(st, st->registers->status_register); +	const uint32_t ts_data_irq_mask = +		AT91_ADC_IER_XRDY | +		AT91_ADC_IER_YRDY | +		AT91_ADC_IER_PRDY; + +	if (status & st->registers->drdy_mask) +		handle_adc_eoc_trigger(irq, idev); + +	if (status & AT91_ADC_IER_PEN) { +		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_PEN); +		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_NOPEN | +			ts_data_irq_mask); +		/* 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 & AT91_ADC_IER_NOPEN) { +		at91_adc_writel(st, st->registers->trigger_register, 0); +		at91_adc_writel(st, AT91_ADC_IDR, AT91_ADC_IER_NOPEN | +			ts_data_irq_mask); +		at91_adc_writel(st, AT91_ADC_IER, AT91_ADC_IER_PEN); + +		input_report_key(st->ts_input, BTN_TOUCH, 0); +		input_sync(st->ts_input); +	} else if ((status & ts_data_irq_mask) == ts_data_irq_mask) { +		/* Now all touchscreen data is ready */ + +		if (status & AT91_ADC_ISR_PENS) { +			/* validate data by pen contact */ +			at91_ts_sample(st); +		} else { +			/* triggered by event that is no pen contact, just read +			 * them to clean the interrupt and discard all. +			 */ +			at91_adc_readl(st, AT91_ADC_TSXPOSR); +			at91_adc_readl(st, AT91_ADC_TSYPOSR); +			at91_adc_readl(st, AT91_ADC_TSPRESSR); +		} +	}  	return IRQ_HANDLED;  } @@ -127,6 +461,16 @@ static int at91_adc_channel_init(struct iio_dev *idev)  	struct at91_adc_state *st = iio_priv(idev);  	struct iio_chan_spec *chan_array, *timestamp;  	int bit, idx = 0; +	unsigned long rsvd_mask = 0; + +	/* If touchscreen is enable, then reserve the adc channels */ +	if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE) +		rsvd_mask = CHAN_MASK_TOUCHSCREEN_4WIRE; +	else if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_5WIRE) +		rsvd_mask = CHAN_MASK_TOUCHSCREEN_5WIRE; + +	/* set up the channel mask to reserve touchscreen channels */ +	st->channels_mask &= ~rsvd_mask;  	idev->num_channels = bitmap_weight(&st->channels_mask,  					   st->num_channels) + 1; @@ -166,12 +510,11 @@ static int at91_adc_channel_init(struct iio_dev *idev)  	return idev->num_channels;  } -static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev, +static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev,  					     struct at91_adc_trigger *triggers,  					     const char *trigger_name)  {  	struct at91_adc_state *st = iio_priv(idev); -	u8 value = 0;  	int i;  	for (i = 0; i < st->trigger_number; i++) { @@ -184,15 +527,16 @@ static u8 at91_adc_get_trigger_value_by_name(struct iio_dev *idev,  			return -ENOMEM;  		if (strcmp(trigger_name, name) == 0) { -			value = triggers[i].value;  			kfree(name); -			break; +			if (triggers[i].value == 0) +				return -EINVAL; +			return triggers[i].value;  		}  		kfree(name);  	} -	return value; +	return -EINVAL;  }  static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state) @@ -202,14 +546,14 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)  	struct iio_buffer *buffer = idev->buffer;  	struct at91_adc_reg_desc *reg = st->registers;  	u32 status = at91_adc_readl(st, reg->trigger_register); -	u8 value; +	int value;  	u8 bit;  	value = at91_adc_get_trigger_value_by_name(idev,  						   st->trigger_list,  						   idev->trig->name); -	if (value == 0) -		return -EINVAL; +	if (value < 0) +		return value;  	if (state) {  		st->buffer = kmalloc(idev->scan_bytes, GFP_KERNEL); @@ -279,7 +623,7 @@ static int at91_adc_trigger_init(struct iio_dev *idev)  	int i, ret;  	st->trig = devm_kzalloc(&idev->dev, -				st->trigger_number * sizeof(st->trig), +				st->trigger_number * sizeof(*st->trig),  				GFP_KERNEL);  	if (st->trig == NULL) { @@ -372,9 +716,9 @@ static int at91_adc_read_raw(struct iio_dev *idev,  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		*val = (st->vref_mv * 1000) >> chan->scan_type.realbits; -		*val2 = 0; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = st->vref_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	default:  		break;  	} @@ -434,8 +778,82 @@ ret:  	return ret;  } +static u32 calc_startup_ticks_9260(u8 startup_time, u32 adc_clk_khz) +{ +	/* +	 * Number of ticks needed to cover the startup time of the ADC +	 * as defined in the electrical characteristics of the board, +	 * divided by 8. The formula thus is : +	 *   Startup Time = (ticks + 1) * 8 / ADC Clock +	 */ +	return round_up((startup_time * adc_clk_khz / 1000) - 1, 8) / 8; +} + +static u32 calc_startup_ticks_9x5(u8 startup_time, u32 adc_clk_khz) +{ +	/* +	 * For sama5d3x and at91sam9x5, the formula changes to: +	 * Startup Time = <lookup_table_value> / ADC Clock +	 */ +	const int startup_lookup[] = { +		0  , 8  , 16 , 24 , +		64 , 80 , 96 , 112, +		512, 576, 640, 704, +		768, 832, 896, 960 +		}; +	int i, size = ARRAY_SIZE(startup_lookup); +	unsigned int ticks; + +	ticks = startup_time * adc_clk_khz / 1000; +	for (i = 0; i < size; i++) +		if (ticks < startup_lookup[i]) +			break; + +	ticks = i; +	if (ticks == size) +		/* Reach the end of lookup table */ +		ticks = size - 1; + +	return ticks; +} +  static const struct of_device_id at91_adc_dt_ids[]; +static int at91_adc_probe_dt_ts(struct device_node *node, +	struct at91_adc_state *st, struct device *dev) +{ +	int ret; +	u32 prop; + +	ret = of_property_read_u32(node, "atmel,adc-ts-wires", &prop); +	if (ret) { +		dev_info(dev, "ADC Touch screen is disabled.\n"); +		return 0; +	} + +	switch (prop) { +	case 4: +	case 5: +		st->touchscreen_type = prop; +		break; +	default: +		dev_err(dev, "Unsupported number of touchscreen wires (%d). Should be 4 or 5.\n", prop); +		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; +	if (st->ts_pressure_threshold) { +		return 0; +	} else { +		dev_err(dev, "Invalid pressure threshold for the touchscreen\n"); +		return -EINVAL; +	} +} +  static int at91_adc_probe_dt(struct at91_adc_state *st,  			     struct platform_device *pdev)  { @@ -460,13 +878,6 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,  	}  	st->channels_mask = prop; -	if (of_property_read_u32(node, "atmel,adc-num-channels", &prop)) { -		dev_err(&idev->dev, "Missing adc-num-channels property in the DT.\n"); -		ret = -EINVAL; -		goto error_ret; -	} -	st->num_channels = prop; -  	st->sleep_mode = of_property_read_bool(node, "atmel,adc-sleep-mode");  	if (of_property_read_u32(node, "atmel,adc-startup-time", &prop)) { @@ -492,6 +903,7 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,  		goto error_ret;  	st->registers = &st->caps->registers; +	st->num_channels = st->caps->num_channels;  	st->trigger_number = of_get_child_count(node);  	st->trigger_list = devm_kzalloc(&idev->dev, st->trigger_number *  					sizeof(struct at91_adc_trigger), @@ -523,6 +935,12 @@ static int at91_adc_probe_dt(struct at91_adc_state *st,  		i++;  	} +	/* Check if touchscreen is supported. */ +	if (st->caps->has_ts) +		return at91_adc_probe_dt_ts(node, st, &idev->dev); +	else +		dev_info(&idev->dev, "not support touchscreen in the adc compatible string.\n"); +  	return 0;  error_ret: @@ -537,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;  } @@ -554,6 +976,153 @@ static const struct iio_info at91_adc_info = {  	.read_raw = &at91_adc_read_raw,  }; +/* Touchscreen related functions */ +static int atmel_ts_open(struct input_dev *dev) +{ +	struct at91_adc_state *st = input_get_drvdata(dev); + +	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; +} + +static void atmel_ts_close(struct input_dev *dev) +{ +	struct at91_adc_state *st = input_get_drvdata(dev); + +	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; +	int i = 0; + +	/* 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 +	 */ +	st->ts_pendbc = round_up(TOUCH_PEN_DETECT_DEBOUNCE_US * adc_clk_khz / +				 1000, 1); + +	while (st->ts_pendbc >> ++i) +		;	/* Empty! Find the shift offset */ +	if (abs(st->ts_pendbc - (1 << i)) < abs(st->ts_pendbc - (1 << (i - 1)))) +		st->ts_pendbc = i; +	else +		st->ts_pendbc = i - 1; + +	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 +	 * option only available on ES2 and higher +	 */ +	at91_adc_writel(st, AT91_ADC_ACR, st->caps->ts_pen_detect_sensitivity +			& AT91_ADC_ACR_PENDETSENS); + +	/* Sample Period Time = (TRGPER + 1) / ADCClock */ +	st->ts_sample_period_val = round_up((TOUCH_SAMPLE_PERIOD_US * +			adc_clk_khz / 1000) - 1, 1); + +	return 0; +} + +static int at91_ts_register(struct at91_adc_state *st, +		struct platform_device *pdev) +{ +	struct input_dev *input; +	struct iio_dev *idev = iio_priv_to_dev(st); +	int ret; + +	input = input_allocate_device(); +	if (!input) { +		dev_err(&idev->dev, "Failed to allocate TS device!\n"); +		return -ENOMEM; +	} + +	input->name = DRIVER_NAME; +	input->id.bustype = BUS_HOST; +	input->dev.parent = &pdev->dev; +	input->open = atmel_ts_open; +	input->close = atmel_ts_close; + +	__set_bit(EV_ABS, input->evbit); +	__set_bit(EV_KEY, input->evbit); +	__set_bit(BTN_TOUCH, input->keybit); +	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) +		goto err; + +	return ret; + +err: +	input_free_device(st->ts_input); +	return ret; +} + +static void at91_ts_unregister(struct at91_adc_state *st) +{ +	input_unregister_device(st->ts_input); +} +  static int at91_adc_probe(struct platform_device *pdev)  {  	unsigned int prsc, mstrclk, ticks, adc_clk, adc_clk_khz, shtim; @@ -604,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_eoc_trigger, -			  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; @@ -650,6 +1221,10 @@ static int at91_adc_probe(struct platform_device *pdev)  	mstrclk = clk_get_rate(st->clk);  	adc_clk = clk_get_rate(st->adc_clk);  	adc_clk_khz = adc_clk / 1000; + +	dev_dbg(&pdev->dev, "Master clock is set as: %d Hz, adc_clk should set as: %d Hz\n", +		mstrclk, adc_clk); +  	prsc = (mstrclk / (2 * adc_clk)) - 1;  	if (!st->startup_time) { @@ -657,21 +1232,18 @@ static int at91_adc_probe(struct platform_device *pdev)  		ret = -EINVAL;  		goto error_disable_adc_clk;  	} +	ticks = (*st->caps->calc_startup_ticks)(st->startup_time, adc_clk_khz);  	/* -	 * Number of ticks needed to cover the startup time of the ADC as -	 * defined in the electrical characteristics of the board, divided by 8. -	 * The formula thus is : Startup Time = (ticks + 1) * 8 / ADC Clock -	 */ -	ticks = round_up((st->startup_time * adc_clk_khz / -			  1000) - 1, 8) / 8; -	/*  	 * a minimal Sample and Hold Time is necessary for the ADC to guarantee  	 * 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; @@ -692,30 +1264,47 @@ static int at91_adc_probe(struct platform_device *pdev)  	init_waitqueue_head(&st->wq_data_avail);  	mutex_init(&st->lock); -	ret = at91_adc_buffer_init(idev); -	if (ret < 0) { -		dev_err(&pdev->dev, "Couldn't initialize the buffer.\n"); -		goto error_disable_adc_clk; -	} +	/* +	 * Since touch screen will set trigger register as period trigger. So +	 * when touch screen is enabled, then we have to disable hardware +	 * trigger for classic adc. +	 */ +	if (!st->touchscreen_type) { +		ret = at91_adc_buffer_init(idev); +		if (ret < 0) { +			dev_err(&pdev->dev, "Couldn't initialize the buffer.\n"); +			goto error_disable_adc_clk; +		} -	ret = at91_adc_trigger_init(idev); -	if (ret < 0) { -		dev_err(&pdev->dev, "Couldn't setup the triggers.\n"); -		goto error_unregister_buffer; +		ret = at91_adc_trigger_init(idev); +		if (ret < 0) { +			dev_err(&pdev->dev, "Couldn't setup the triggers.\n"); +			at91_adc_buffer_remove(idev); +			goto error_disable_adc_clk; +		} +	} else { +		ret = at91_ts_register(st, pdev); +		if (ret) +			goto error_disable_adc_clk; + +		at91_ts_hw_init(st, adc_clk_khz);  	}  	ret = iio_device_register(idev);  	if (ret < 0) {  		dev_err(&pdev->dev, "Couldn't register the device.\n"); -		goto error_remove_triggers; +		goto error_iio_device_register;  	}  	return 0; -error_remove_triggers: -	at91_adc_trigger_remove(idev); -error_unregister_buffer: -	at91_adc_buffer_remove(idev); +error_iio_device_register: +	if (!st->touchscreen_type) { +		at91_adc_trigger_remove(idev); +		at91_adc_buffer_remove(idev); +	} else { +		at91_ts_unregister(st); +	}  error_disable_adc_clk:  	clk_disable_unprepare(st->adc_clk);  error_disable_clk: @@ -731,8 +1320,12 @@ static int at91_adc_remove(struct platform_device *pdev)  	struct at91_adc_state *st = iio_priv(idev);  	iio_device_unregister(idev); -	at91_adc_trigger_remove(idev); -	at91_adc_buffer_remove(idev); +	if (!st->touchscreen_type) { +		at91_adc_trigger_remove(idev); +		at91_adc_buffer_remove(idev); +	} else { +		at91_ts_unregister(st); +	}  	clk_disable_unprepare(st->adc_clk);  	clk_disable_unprepare(st->clk);  	free_irq(st->irq, idev); @@ -740,8 +1333,9 @@ 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,  	.registers = {  		.channel_base = AT91_ADC_CHR(0),  		.drdy_mask = AT91_ADC_DRDY, @@ -752,7 +1346,24 @@ 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 */ +	.num_channels = 8,  	.registers = {  		.channel_base = AT91_ADC_CHR(0),  		.drdy_mask = AT91_ADC_DRDY, @@ -764,6 +1375,12 @@ static struct at91_adc_caps at91sam9g45_caps = {  };  static struct at91_adc_caps at91sam9x5_caps = { +	.has_ts = true, +	.has_tsmr = true, +	.ts_filter_average = 3, +	.ts_pen_detect_sensitivity = 2, +	.calc_startup_ticks = calc_startup_ticks_9x5, +	.num_channels = 12,  	.registers = {  		.channel_base = AT91_ADC_CDR0_9X5,  		.drdy_mask = AT91_ADC_SR_DRDY_9X5, @@ -777,18 +1394,38 @@ 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 = "at91_adc", +		   .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 4fb35d1d749..1b3b74be5c2 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -8,17 +8,11 @@    * based on linux/drivers/acron/char/pcf8583.c    * Copyright (C) 2000 Russell King    * +  * Driver for max1363 and similar chips. +  *    * 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. -  * -  * max1363.c -  * -  * Partial support for max1363 and similar chips. -  * -  * Not currently implemented. -  * -  * - Control of internal reference.    */  #include <linux/interrupt.h> @@ -165,6 +159,8 @@ struct max1363_chip_info {   * @thresh_low:		low threshold values   * @vref:		Reference voltage regulator   * @vref_uv:		Actual (external or internal) reference voltage + * @send:		function used to send data to the chip + * @recv:		function used to receive data from the chip   */  struct max1363_state {  	struct i2c_client		*client; @@ -186,6 +182,10 @@ struct max1363_state {  	s16				thresh_low[8];  	struct regulator		*vref;  	u32				vref_uv; +	int				(*send)(const struct i2c_client *client, +						const char *buf, int count); +	int				(*recv)(const struct i2c_client *client, +						char *buf, int count);  };  #define MAX1363_MODE_SINGLE(_num, _mask) {				\ @@ -311,13 +311,37 @@ static const struct max1363_mode  	return NULL;  } -static int max1363_write_basic_config(struct i2c_client *client, -				      unsigned char d1, -				      unsigned char d2) +static int max1363_smbus_send(const struct i2c_client *client, const char *buf, +		int count)  { -	u8 tx_buf[2] = {d1, d2}; +	int i, err; + +	for (i = err = 0; err == 0 && i < count; ++i) +		err = i2c_smbus_write_byte(client, buf[i]); -	return i2c_master_send(client, tx_buf, 2); +	return err ? err : count; +} + +static int max1363_smbus_recv(const struct i2c_client *client, char *buf, +		int count) +{ +	int i, ret; + +	for (i = 0; i < count; ++i) { +		ret = i2c_smbus_read_byte(client); +		if (ret < 0) +			return ret; +		buf[i] = ret; +	} + +	return count; +} + +static int max1363_write_basic_config(struct max1363_state *st) +{ +	u8 tx_buf[2] = { st->setupbyte, st->configbyte }; + +	return st->send(st->client, tx_buf, 2);  }  static int max1363_set_scan_mode(struct max1363_state *st) @@ -327,9 +351,7 @@ static int max1363_set_scan_mode(struct max1363_state *st)  			    | MAX1363_SE_DE_MASK);  	st->configbyte |= st->current_mode->conf; -	return max1363_write_basic_config(st->client, -					  st->setupbyte, -					  st->configbyte); +	return max1363_write_basic_config(st);  }  static int max1363_read_single_chan(struct iio_dev *indio_dev, @@ -366,7 +388,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,  	}  	if (st->chip_info->bits != 8) {  		/* Get reading */ -		data = i2c_master_recv(client, rxbuf, 2); +		data = st->recv(client, rxbuf, 2);  		if (data < 0) {  			ret = data;  			goto error_ret; @@ -375,7 +397,7 @@ static int max1363_read_single_chan(struct iio_dev *indio_dev,  		  ((1 << st->chip_info->bits) - 1);  	} else {  		/* Get reading */ -		data = i2c_master_recv(client, rxbuf, 1); +		data = st->recv(client, rxbuf, 1);  		if (data < 0) {  			ret = data;  			goto error_ret; @@ -397,7 +419,6 @@ static int max1363_read_raw(struct iio_dev *indio_dev,  {  	struct max1363_state *st = iio_priv(indio_dev);  	int ret; -	unsigned long scale_uv;  	switch (m) {  	case IIO_CHAN_INFO_RAW: @@ -406,10 +427,9 @@ static int max1363_read_raw(struct iio_dev *indio_dev,  			return ret;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = st->vref_uv >> st->chip_info->bits; -		*val = scale_uv / 1000; -		*val2 = (scale_uv % 1000) * 1000; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = st->vref_uv / 1000; +		*val2 = st->chip_info->bits; +		return IIO_VAL_FRACTIONAL_LOG2;  	default:  		return -EINVAL;  	} @@ -424,11 +444,21 @@ static const enum max1363_modes max1363_mode_list[] = {  	d0m1to2m3, d1m0to3m2,  }; -#define MAX1363_EV_M						\ -	(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING)	\ -	 | IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)) +static const struct iio_event_spec max1363_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), +	}, +}; -#define MAX1363_CHAN_U(num, addr, si, bits, evmask)			\ +#define MAX1363_CHAN_U(num, addr, si, bits, ev_spec, num_ev_spec)	\  	{								\  		.type = IIO_VOLTAGE,					\  		.indexed = 1,						\ @@ -444,11 +474,12 @@ static const enum max1363_modes max1363_mode_list[] = {  			.endianness = IIO_BE,				\  		},							\  		.scan_index = si,					\ -		.event_mask = evmask,					\ +		.event_spec = ev_spec,					\ +		.num_event_specs = num_ev_spec,				\  	}  /* bipolar channel */ -#define MAX1363_CHAN_B(num, num2, addr, si, bits, evmask)		\ +#define MAX1363_CHAN_B(num, num2, addr, si, bits, ev_spec, num_ev_spec)	\  	{								\  		.type = IIO_VOLTAGE,					\  		.differential = 1,					\ @@ -466,28 +497,32 @@ static const enum max1363_modes max1363_mode_list[] = {  			.endianness = IIO_BE,				\  		},							\  		.scan_index = si,					\ -		.event_mask = evmask,					\ +		.event_spec = ev_spec,					\ +		.num_event_specs = num_ev_spec,				\  	} -#define MAX1363_4X_CHANS(bits, em) {			\ -	MAX1363_CHAN_U(0, _s0, 0, bits, em),		\ -	MAX1363_CHAN_U(1, _s1, 1, bits, em),		\ -	MAX1363_CHAN_U(2, _s2, 2, bits, em),		\ -	MAX1363_CHAN_U(3, _s3, 3, bits, em),		\ -	MAX1363_CHAN_B(0, 1, d0m1, 4, bits, em),	\ -	MAX1363_CHAN_B(2, 3, d2m3, 5, bits, em),	\ -	MAX1363_CHAN_B(1, 0, d1m0, 6, bits, em),	\ -	MAX1363_CHAN_B(3, 2, d3m2, 7, bits, em),	\ -	IIO_CHAN_SOFT_TIMESTAMP(8)			\ +#define MAX1363_4X_CHANS(bits, ev_spec, num_ev_spec) {			\ +	MAX1363_CHAN_U(0, _s0, 0, bits, ev_spec, num_ev_spec),		\ +	MAX1363_CHAN_U(1, _s1, 1, bits, ev_spec, num_ev_spec),		\ +	MAX1363_CHAN_U(2, _s2, 2, bits, ev_spec, num_ev_spec),		\ +	MAX1363_CHAN_U(3, _s3, 3, bits, ev_spec, num_ev_spec),		\ +	MAX1363_CHAN_B(0, 1, d0m1, 4, bits, ev_spec, num_ev_spec),	\ +	MAX1363_CHAN_B(2, 3, d2m3, 5, bits, ev_spec, num_ev_spec),	\ +	MAX1363_CHAN_B(1, 0, d1m0, 6, bits, ev_spec, num_ev_spec),	\ +	MAX1363_CHAN_B(3, 2, d3m2, 7, bits, ev_spec, num_ev_spec),	\ +	IIO_CHAN_SOFT_TIMESTAMP(8)					\  	} -static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0); -static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0); -static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0); +static const struct iio_chan_spec max1036_channels[] = +	MAX1363_4X_CHANS(8, NULL, 0); +static const struct iio_chan_spec max1136_channels[] = +	MAX1363_4X_CHANS(10, NULL, 0); +static const struct iio_chan_spec max1236_channels[] = +	MAX1363_4X_CHANS(12, NULL, 0);  static const struct iio_chan_spec max1361_channels[] = -	MAX1363_4X_CHANS(10, MAX1363_EV_M); +	MAX1363_4X_CHANS(10, max1363_events, ARRAY_SIZE(max1363_events));  static const struct iio_chan_spec max1363_channels[] = -	MAX1363_4X_CHANS(12, MAX1363_EV_M); +	MAX1363_4X_CHANS(12, max1363_events, ARRAY_SIZE(max1363_events));  /* Applies to max1236, max1237 */  static const enum max1363_modes max1236_mode_list[] = { @@ -511,32 +546,32 @@ static const enum max1363_modes max1238_mode_list[] = {  	d6m7to8m9, d6m7to10m11, d7m6to9m8, d7m6to11m10,  }; -#define MAX1363_12X_CHANS(bits) {			\ -	MAX1363_CHAN_U(0, _s0, 0, bits, 0),		\ -	MAX1363_CHAN_U(1, _s1, 1, bits, 0),		\ -	MAX1363_CHAN_U(2, _s2, 2, bits, 0),		\ -	MAX1363_CHAN_U(3, _s3, 3, bits, 0),		\ -	MAX1363_CHAN_U(4, _s4, 4, bits, 0),		\ -	MAX1363_CHAN_U(5, _s5, 5, bits, 0),		\ -	MAX1363_CHAN_U(6, _s6, 6, bits, 0),		\ -	MAX1363_CHAN_U(7, _s7, 7, bits, 0),		\ -	MAX1363_CHAN_U(8, _s8, 8, bits, 0),		\ -	MAX1363_CHAN_U(9, _s9, 9, bits, 0),		\ -	MAX1363_CHAN_U(10, _s10, 10, bits, 0),		\ -	MAX1363_CHAN_U(11, _s11, 11, bits, 0),		\ -	MAX1363_CHAN_B(0, 1, d0m1, 12, bits, 0),	\ -	MAX1363_CHAN_B(2, 3, d2m3, 13, bits, 0),	\ -	MAX1363_CHAN_B(4, 5, d4m5, 14, bits, 0),	\ -	MAX1363_CHAN_B(6, 7, d6m7, 15, bits, 0),	\ -	MAX1363_CHAN_B(8, 9, d8m9, 16, bits, 0),	\ -	MAX1363_CHAN_B(10, 11, d10m11, 17, bits, 0),	\ -	MAX1363_CHAN_B(1, 0, d1m0, 18, bits, 0),	\ -	MAX1363_CHAN_B(3, 2, d3m2, 19, bits, 0),	\ -	MAX1363_CHAN_B(5, 4, d5m4, 20, bits, 0),	\ -	MAX1363_CHAN_B(7, 6, d7m6, 21, bits, 0),	\ -	MAX1363_CHAN_B(9, 8, d9m8, 22, bits, 0),	\ -	MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0),	\ -	IIO_CHAN_SOFT_TIMESTAMP(24)			\ +#define MAX1363_12X_CHANS(bits) {				\ +	MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0),		\ +	MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0),		\ +	MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0),		\ +	MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0),		\ +	MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0),		\ +	MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0),		\ +	MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0),		\ +	MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0),		\ +	MAX1363_CHAN_U(8, _s8, 8, bits, NULL, 0),		\ +	MAX1363_CHAN_U(9, _s9, 9, bits, NULL, 0),		\ +	MAX1363_CHAN_U(10, _s10, 10, bits, NULL, 0),		\ +	MAX1363_CHAN_U(11, _s11, 11, bits, NULL, 0),		\ +	MAX1363_CHAN_B(0, 1, d0m1, 12, bits, NULL, 0),		\ +	MAX1363_CHAN_B(2, 3, d2m3, 13, bits, NULL, 0),		\ +	MAX1363_CHAN_B(4, 5, d4m5, 14, bits, NULL, 0),		\ +	MAX1363_CHAN_B(6, 7, d6m7, 15, bits, NULL, 0),		\ +	MAX1363_CHAN_B(8, 9, d8m9, 16, bits, NULL, 0),		\ +	MAX1363_CHAN_B(10, 11, d10m11, 17, bits, NULL, 0),	\ +	MAX1363_CHAN_B(1, 0, d1m0, 18, bits, NULL, 0),		\ +	MAX1363_CHAN_B(3, 2, d3m2, 19, bits, NULL, 0),		\ +	MAX1363_CHAN_B(5, 4, d5m4, 20, bits, NULL, 0),		\ +	MAX1363_CHAN_B(7, 6, d7m6, 21, bits, NULL, 0),		\ +	MAX1363_CHAN_B(9, 8, d9m8, 22, bits, NULL, 0),		\ +	MAX1363_CHAN_B(11, 10, d11m10, 23, bits, NULL, 0),	\ +	IIO_CHAN_SOFT_TIMESTAMP(24)				\  	}  static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);  static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10); @@ -561,22 +596,22 @@ static const enum max1363_modes max11608_mode_list[] = {  };  #define MAX1363_8X_CHANS(bits) {			\ -	MAX1363_CHAN_U(0, _s0, 0, bits, 0),		\ -	MAX1363_CHAN_U(1, _s1, 1, bits, 0),		\ -	MAX1363_CHAN_U(2, _s2, 2, bits, 0),		\ -	MAX1363_CHAN_U(3, _s3, 3, bits, 0),		\ -	MAX1363_CHAN_U(4, _s4, 4, bits, 0),		\ -	MAX1363_CHAN_U(5, _s5, 5, bits, 0),		\ -	MAX1363_CHAN_U(6, _s6, 6, bits, 0),		\ -	MAX1363_CHAN_U(7, _s7, 7, bits, 0),		\ -	MAX1363_CHAN_B(0, 1, d0m1, 8, bits, 0),	\ -	MAX1363_CHAN_B(2, 3, d2m3, 9, bits, 0),	\ -	MAX1363_CHAN_B(4, 5, d4m5, 10, bits, 0),	\ -	MAX1363_CHAN_B(6, 7, d6m7, 11, bits, 0),	\ -	MAX1363_CHAN_B(1, 0, d1m0, 12, bits, 0),	\ -	MAX1363_CHAN_B(3, 2, d3m2, 13, bits, 0),	\ -	MAX1363_CHAN_B(5, 4, d5m4, 14, bits, 0),	\ -	MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0),	\ +	MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0),	\ +	MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0),	\ +	MAX1363_CHAN_U(2, _s2, 2, bits, NULL, 0),	\ +	MAX1363_CHAN_U(3, _s3, 3, bits, NULL, 0),	\ +	MAX1363_CHAN_U(4, _s4, 4, bits, NULL, 0),	\ +	MAX1363_CHAN_U(5, _s5, 5, bits, NULL, 0),	\ +	MAX1363_CHAN_U(6, _s6, 6, bits, NULL, 0),	\ +	MAX1363_CHAN_U(7, _s7, 7, bits, NULL, 0),	\ +	MAX1363_CHAN_B(0, 1, d0m1, 8, bits, NULL, 0),	\ +	MAX1363_CHAN_B(2, 3, d2m3, 9, bits, NULL, 0),	\ +	MAX1363_CHAN_B(4, 5, d4m5, 10, bits, NULL, 0),	\ +	MAX1363_CHAN_B(6, 7, d6m7, 11, bits, NULL, 0),	\ +	MAX1363_CHAN_B(1, 0, d1m0, 12, bits, NULL, 0),	\ +	MAX1363_CHAN_B(3, 2, d3m2, 13, bits, NULL, 0),	\ +	MAX1363_CHAN_B(5, 4, d5m4, 14, bits, NULL, 0),	\ +	MAX1363_CHAN_B(7, 6, d7m6, 15, bits, NULL, 0),	\  	IIO_CHAN_SOFT_TIMESTAMP(16)			\  }  static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8); @@ -588,10 +623,10 @@ static const enum max1363_modes max11644_mode_list[] = {  };  #define MAX1363_2X_CHANS(bits) {			\ -	MAX1363_CHAN_U(0, _s0, 0, bits, 0),		\ -	MAX1363_CHAN_U(1, _s1, 1, bits, 0),		\ -	MAX1363_CHAN_B(0, 1, d0m1, 2, bits, 0),	\ -	MAX1363_CHAN_B(1, 0, d1m0, 3, bits, 0),	\ +	MAX1363_CHAN_U(0, _s0, 0, bits, NULL, 0),	\ +	MAX1363_CHAN_U(1, _s1, 1, bits, NULL, 0),	\ +	MAX1363_CHAN_B(0, 1, d0m1, 2, bits, NULL, 0),	\ +	MAX1363_CHAN_B(1, 0, d1m0, 3, bits, NULL, 0),	\  	IIO_CHAN_SOFT_TIMESTAMP(4)			\  	} @@ -686,20 +721,22 @@ static IIO_CONST_ATTR(sampling_frequency_available,  		"133000 665000 33300 16600 8300 4200 2000 1000");  static int max1363_read_thresh(struct iio_dev *indio_dev, -			       u64 event_code, -			       int *val) +	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)  {  	struct max1363_state *st = iio_priv(indio_dev); -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) -		*val = st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; +	if (dir == IIO_EV_DIR_FALLING) +		*val = st->thresh_low[chan->channel];  	else -		*val = st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)]; -	return 0; +		*val = st->thresh_high[chan->channel]; +	return IIO_VAL_INT;  }  static int max1363_write_thresh(struct iio_dev *indio_dev, -				u64 event_code, -				int val) +	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)  {  	struct max1363_state *st = iio_priv(indio_dev);  	/* make it handle signed correctly as well */ @@ -714,13 +751,15 @@ static int max1363_write_thresh(struct iio_dev *indio_dev,  		break;  	} -	switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { +	switch (dir) {  	case IIO_EV_DIR_FALLING: -		st->thresh_low[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; +		st->thresh_low[chan->channel] = val;  		break;  	case IIO_EV_DIR_RISING: -		st->thresh_high[IIO_EVENT_CODE_EXTRACT_CHAN(event_code)] = val; +		st->thresh_high[chan->channel] = val;  		break; +	default: +		return -EINVAL;  	}  	return 0; @@ -755,24 +794,25 @@ static irqreturn_t max1363_event_handler(int irq, void *private)  	u8 tx[2] = { st->setupbyte,  		     MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0 }; -	i2c_master_recv(st->client, &rx, 1); +	st->recv(st->client, &rx, 1);  	mask = rx;  	for_each_set_bit(loc, &mask, 8)  		iio_push_event(indio_dev, max1363_event_codes[loc], timestamp); -	i2c_master_send(st->client, tx, 2); +	st->send(st->client, tx, 2);  	return IRQ_HANDLED;  }  static int max1363_read_event_config(struct iio_dev *indio_dev, -				     u64 event_code) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir)  {  	struct max1363_state *st = iio_priv(indio_dev);  	int val; -	int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); +	int number = chan->channel;  	mutex_lock(&indio_dev->mlock); -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) +	if (dir == IIO_EV_DIR_FALLING)  		val = (1 << number) & st->mask_low;  	else  		val = (1 << number) & st->mask_high; @@ -794,9 +834,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)  		st->setupbyte &= ~MAX1363_SETUP_MONITOR_SETUP;  		st->configbyte &= ~MAX1363_SCAN_MASK;  		st->monitor_on = false; -		return max1363_write_basic_config(st->client, -						st->setupbyte, -						st->configbyte); +		return max1363_write_basic_config(st);  	}  	/* Ensure we are in the relevant mode */ @@ -858,7 +896,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)  		} -	ret = i2c_master_send(st->client, tx_buf, len); +	ret = st->send(st->client, tx_buf, len);  	if (ret < 0)  		goto error_ret;  	if (ret != len) { @@ -875,7 +913,7 @@ static int max1363_monitor_mode_update(struct max1363_state *st, int enabled)  	 */  	tx_buf[0] = st->setupbyte;  	tx_buf[1] = MAX1363_MON_INT_ENABLE | (st->monitor_speed << 1) | 0xF0; -	ret = i2c_master_send(st->client, tx_buf, 2); +	ret = st->send(st->client, tx_buf, 2);  	if (ret < 0)  		goto error_ret;  	if (ret != 2) { @@ -917,17 +955,17 @@ error_ret:  }  static int max1363_write_event_config(struct iio_dev *indio_dev, -				      u64 event_code, -				      int state) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state)  {  	int ret = 0;  	struct max1363_state *st = iio_priv(indio_dev);  	u16 unifiedmask; -	int number = IIO_EVENT_CODE_EXTRACT_CHAN(event_code); +	int number = chan->channel;  	mutex_lock(&indio_dev->mlock);  	unifiedmask = st->mask_low | st->mask_high; -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_FALLING) { +	if (dir == IIO_EV_DIR_FALLING) {  		if (state == 0)  			st->mask_low &= ~(1 << number); @@ -1209,13 +1247,13 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {  	},  	[max11604] = {  		.bits = 8, -		.int_vref_mv = 4098, +		.int_vref_mv = 4096,  		.mode_list = max1238_mode_list,  		.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, @@ -1224,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, @@ -1269,13 +1307,13 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {  	},  	[max11610] = {  		.bits = 10, -		.int_vref_mv = 4098, +		.int_vref_mv = 4096,  		.mode_list = max1238_mode_list,  		.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, @@ -1284,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, @@ -1329,7 +1367,7 @@ static const struct max1363_chip_info max1363_chip_info_tbl[] = {  	},  	[max11616] = {  		.bits = 12, -		.int_vref_mv = 4098, +		.int_vref_mv = 4096,  		.mode_list = max1238_mode_list,  		.num_modes = ARRAY_SIZE(max1238_mode_list),  		.default_mode = s0to11, @@ -1436,7 +1474,6 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p)  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct max1363_state *st = iio_priv(indio_dev); -	s64 time_ns;  	__u8 *rxbuf;  	int b_sent;  	size_t d_size; @@ -1464,17 +1501,13 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p)  	if (rxbuf == NULL)  		goto done;  	if (st->chip_info->bits != 8) -		b_sent = i2c_master_recv(st->client, rxbuf, numvals*2); +		b_sent = st->recv(st->client, rxbuf, numvals * 2);  	else -		b_sent = i2c_master_recv(st->client, rxbuf, numvals); +		b_sent = st->recv(st->client, rxbuf, numvals);  	if (b_sent < 0)  		goto done_free; -	time_ns = iio_get_time_ns(); - -	if (indio_dev->scan_timestamp) -		memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns)); -	iio_push_to_buffers(indio_dev, rxbuf); +	iio_push_to_buffers_with_timestamp(indio_dev, rxbuf, iio_get_time_ns());  done_free:  	kfree(rxbuf); @@ -1484,12 +1517,6 @@ done:  	return IRQ_HANDLED;  } -static const struct iio_buffer_setup_ops max1363_buffered_setup_ops = { -	.postenable = &iio_triggered_buffer_postenable, -	.preenable = &iio_sw_buffer_preenable, -	.predisable = &iio_triggered_buffer_predisable, -}; -  static int max1363_probe(struct i2c_client *client,  			 const struct i2c_device_id *id)  { @@ -1527,7 +1554,7 @@ static int max1363_probe(struct i2c_client *client,  	st->client = client;  	st->vref_uv = st->chip_info->int_vref_mv * 1000; -	vref = devm_regulator_get(&client->dev, "vref"); +	vref = devm_regulator_get_optional(&client->dev, "vref");  	if (!IS_ERR(vref)) {  		int vref_uv; @@ -1543,6 +1570,18 @@ static int max1363_probe(struct i2c_client *client,  		st->vref_uv = vref_uv;  	} +	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { +		st->send = i2c_master_send; +		st->recv = i2c_master_recv; +	} else if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE) +			&& st->chip_info->bits == 8) { +		st->send = max1363_smbus_send; +		st->recv = max1363_smbus_recv; +	} else { +		ret = -EOPNOTSUPP; +		goto error_disable_reg; +	} +  	ret = max1363_alloc_scan_masks(indio_dev);  	if (ret)  		goto error_disable_reg; @@ -1559,7 +1598,7 @@ static int max1363_probe(struct i2c_client *client,  		goto error_disable_reg;  	ret = iio_triggered_buffer_setup(indio_dev, NULL, -		&max1363_trigger_handler, &max1363_buffered_setup_ops); +		&max1363_trigger_handler, NULL);  	if (ret)  		goto error_disable_reg; diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c new file mode 100644 index 00000000000..51672256072 --- /dev/null +++ b/drivers/iio/adc/mcp3422.c @@ -0,0 +1,426 @@ +/* + * 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. + * + * 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. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/sysfs.h> +#include <linux/of.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* Masks */ +#define MCP3422_CHANNEL_MASK	0x60 +#define MCP3422_PGA_MASK	0x03 +#define MCP3422_SRATE_MASK	0x0C +#define MCP3422_SRATE_240	0x0 +#define MCP3422_SRATE_60	0x1 +#define MCP3422_SRATE_15	0x2 +#define MCP3422_SRATE_3	0x3 +#define MCP3422_PGA_1	0 +#define MCP3422_PGA_2	1 +#define MCP3422_PGA_4	2 +#define MCP3422_PGA_8	3 +#define MCP3422_CONT_SAMPLING	0x10 + +#define MCP3422_CHANNEL(config)	(((config) & MCP3422_CHANNEL_MASK) >> 5) +#define MCP3422_PGA(config)	((config) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE(config)	(((config) & MCP3422_SRATE_MASK) >> 2) + +#define MCP3422_CHANNEL_VALUE(value) (((value) << 5) & MCP3422_CHANNEL_MASK) +#define MCP3422_PGA_VALUE(value) ((value) & MCP3422_PGA_MASK) +#define MCP3422_SAMPLE_RATE_VALUE(value) ((value << 2) & MCP3422_SRATE_MASK) + +#define MCP3422_CHAN(_index) \ +	{ \ +		.type = IIO_VOLTAGE, \ +		.indexed = 1, \ +		.channel = _index, \ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ +				| BIT(IIO_CHAN_INFO_SCALE), \ +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +	} + +/* LSB is in nV to eliminate floating point */ +static const u32 rates_to_lsb[] = {1000000, 250000, 62500, 15625}; + +/* + *  scales calculated as: + *  rates_to_lsb[sample_rate] / (1 << pga); + *  pga is 1 for 0, 2 + */ + +static const int mcp3422_scales[4][4] = { +	{ 1000000, 250000, 62500, 15625 }, +	{ 500000 , 125000, 31250, 7812 }, +	{ 250000 , 62500 , 15625, 3906 }, +	{ 125000 , 31250 , 7812 , 1953 } }; + +/* Constant msleep times for data acquisitions */ +static const int mcp3422_read_times[4] = { +	[MCP3422_SRATE_240] = 1000 / 240, +	[MCP3422_SRATE_60] = 1000 / 60, +	[MCP3422_SRATE_15] = 1000 / 15, +	[MCP3422_SRATE_3] = 1000 / 3 }; + +/* sample rates to integer conversion table */ +static const int mcp3422_sample_rates[4] = { +	[MCP3422_SRATE_240] = 240, +	[MCP3422_SRATE_60] = 60, +	[MCP3422_SRATE_15] = 15, +	[MCP3422_SRATE_3] = 3 }; + +/* sample rates to sign extension table */ +static const int mcp3422_sign_extend[4] = { +	[MCP3422_SRATE_240] = 11, +	[MCP3422_SRATE_60] = 13, +	[MCP3422_SRATE_15] = 15, +	[MCP3422_SRATE_3] = 17 }; + +/* Client data (each client gets its own) */ +struct mcp3422 { +	struct i2c_client *i2c; +	u8 id; +	u8 config; +	u8 pga[4]; +	struct mutex lock; +}; + +static int mcp3422_update_config(struct mcp3422 *adc, u8 newconfig) +{ +	int ret; + +	mutex_lock(&adc->lock); + +	ret = i2c_master_send(adc->i2c, &newconfig, 1); +	if (ret > 0) { +		adc->config = newconfig; +		ret = 0; +	} + +	mutex_unlock(&adc->lock); + +	return ret; +} + +static int mcp3422_read(struct mcp3422 *adc, int *value, u8 *config) +{ +	int ret = 0; +	u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); +	u8 buf[4] = {0, 0, 0, 0}; +	u32 temp; + +	if (sample_rate == MCP3422_SRATE_3) { +		ret = i2c_master_recv(adc->i2c, buf, 4); +		temp = buf[0] << 16 | buf[1] << 8 | buf[2]; +		*config = buf[3]; +	} else { +		ret = i2c_master_recv(adc->i2c, buf, 3); +		temp = buf[0] << 8 | buf[1]; +		*config = buf[2]; +	} + +	*value = sign_extend32(temp, mcp3422_sign_extend[sample_rate]); + +	return ret; +} + +static int mcp3422_read_channel(struct mcp3422 *adc, +				struct iio_chan_spec const *channel, int *value) +{ +	int ret; +	u8 config; +	u8 req_channel = channel->channel; + +	if (req_channel != MCP3422_CHANNEL(adc->config)) { +		config = adc->config; +		config &= ~MCP3422_CHANNEL_MASK; +		config |= MCP3422_CHANNEL_VALUE(req_channel); +		config &= ~MCP3422_PGA_MASK; +		config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); +		ret = mcp3422_update_config(adc, config); +		if (ret < 0) +			return ret; +		msleep(mcp3422_read_times[MCP3422_SAMPLE_RATE(adc->config)]); +	} + +	return mcp3422_read(adc, value, &config); +} + +static int mcp3422_read_raw(struct iio_dev *iio, +			struct iio_chan_spec const *channel, int *val1, +			int *val2, long mask) +{ +	struct mcp3422 *adc = iio_priv(iio); +	int err; + +	u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); +	u8 pga		 = MCP3422_PGA(adc->config); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		err = mcp3422_read_channel(adc, channel, val1); +		if (err < 0) +			return -EINVAL; +		return IIO_VAL_INT; + +	case IIO_CHAN_INFO_SCALE: + +		*val1 = 0; +		*val2 = mcp3422_scales[sample_rate][pga]; +		return IIO_VAL_INT_PLUS_NANO; + +	case IIO_CHAN_INFO_SAMP_FREQ: +		*val1 = mcp3422_sample_rates[MCP3422_SAMPLE_RATE(adc->config)]; +		return IIO_VAL_INT; + +	default: +		break; +	} + +	return -EINVAL; +} + +static int mcp3422_write_raw(struct iio_dev *iio, +			struct iio_chan_spec const *channel, int val1, +			int val2, long mask) +{ +	struct mcp3422 *adc = iio_priv(iio); +	u8 temp; +	u8 config = adc->config; +	u8 req_channel = channel->channel; +	u8 sample_rate = MCP3422_SAMPLE_RATE(config); +	u8 i; + +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		if (val1 != 0) +			return -EINVAL; + +		for (i = 0; i < ARRAY_SIZE(mcp3422_scales[0]); i++) { +			if (val2 == mcp3422_scales[sample_rate][i]) { +				adc->pga[req_channel] = i; + +				config &= ~MCP3422_CHANNEL_MASK; +				config |= MCP3422_CHANNEL_VALUE(req_channel); +				config &= ~MCP3422_PGA_MASK; +				config |= MCP3422_PGA_VALUE(adc->pga[req_channel]); + +				return mcp3422_update_config(adc, config); +			} +		} +		return -EINVAL; + +	case IIO_CHAN_INFO_SAMP_FREQ: +		switch (val1) { +		case 240: +			temp = MCP3422_SRATE_240; +			break; +		case 60: +			temp = MCP3422_SRATE_60; +			break; +		case 15: +			temp = MCP3422_SRATE_15; +			break; +		case 3: +			if (adc->id > 4) +				return -EINVAL; +			temp = MCP3422_SRATE_3; +			break; +		default: +			return -EINVAL; +		} + +		config &= ~MCP3422_CHANNEL_MASK; +		config |= MCP3422_CHANNEL_VALUE(req_channel); +		config &= ~MCP3422_SRATE_MASK; +		config |= MCP3422_SAMPLE_RATE_VALUE(temp); + +		return mcp3422_update_config(adc, config); + +	default: +		break; +	} + +	return -EINVAL; +} + +static int mcp3422_write_raw_get_fmt(struct iio_dev *indio_dev, +		struct iio_chan_spec const *chan, long mask) +{ +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		return IIO_VAL_INT_PLUS_NANO; +	case IIO_CHAN_INFO_SAMP_FREQ: +		return IIO_VAL_INT_PLUS_MICRO; +	default: +		return -EINVAL; +	} +} + +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) +{ +	struct mcp3422 *adc = iio_priv(dev_to_iio_dev(dev)); +	u8 sample_rate = MCP3422_SAMPLE_RATE(adc->config); + +	return sprintf(buf, "0.%09u 0.%09u 0.%09u 0.%09u\n", +		mcp3422_scales[sample_rate][0], +		mcp3422_scales[sample_rate][1], +		mcp3422_scales[sample_rate][2], +		mcp3422_scales[sample_rate][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_dev_attr_sampling_frequency_available.dev_attr.attr, +	&iio_dev_attr_in_voltage_scale_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group mcp3422_attribute_group = { +	.attrs = mcp3422_attributes, +}; + +static const struct iio_chan_spec mcp3422_channels[] = { +	MCP3422_CHAN(0), +	MCP3422_CHAN(1), +}; + +static const struct iio_chan_spec mcp3424_channels[] = { +	MCP3422_CHAN(0), +	MCP3422_CHAN(1), +	MCP3422_CHAN(2), +	MCP3422_CHAN(3), +}; + +static const struct iio_info mcp3422_info = { +	.read_raw = mcp3422_read_raw, +	.write_raw = mcp3422_write_raw, +	.write_raw_get_fmt = mcp3422_write_raw_get_fmt, +	.attrs = &mcp3422_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int mcp3422_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct iio_dev *indio_dev; +	struct mcp3422 *adc; +	int err; +	u8 config; + +	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) +		return -ENODEV; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adc)); +	if (!indio_dev) +		return -ENOMEM; + +	adc = iio_priv(indio_dev); +	adc->i2c = client; +	adc->id = (u8)(id->driver_data); + +	mutex_init(&adc->lock); + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = dev_name(&client->dev); +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->info = &mcp3422_info; + +	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; +	} + +	/* meaningful default configuration */ +	config = (MCP3422_CONT_SAMPLING +		| MCP3422_CHANNEL_VALUE(1) +		| MCP3422_PGA_VALUE(MCP3422_PGA_1) +		| MCP3422_SAMPLE_RATE_VALUE(MCP3422_SRATE_240)); +	mcp3422_update_config(adc, config); + +	err = devm_iio_device_register(&client->dev, indio_dev); +	if (err < 0) +		return err; + +	i2c_set_clientdata(client, indio_dev); + +	return 0; +} + +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); + +#ifdef CONFIG_OF +static const struct of_device_id mcp3422_of_match[] = { +	{ .compatible = "mcp3422" }, +	{ } +}; +MODULE_DEVICE_TABLE(of, mcp3422_of_match); +#endif + +static struct i2c_driver mcp3422_driver = { +	.driver = { +		.name = "mcp3422", +		.owner = THIS_MODULE, +		.of_match_table = of_match_ptr(mcp3422_of_match), +	}, +	.probe = mcp3422_probe, +	.id_table = mcp3422_id, +}; +module_i2c_driver(mcp3422_driver); + +MODULE_AUTHOR("Angelo Compagnucci <angelo.compagnucci@gmail.com>"); +MODULE_DESCRIPTION("Microchip mcp3422/3/4/6/7/8 driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c new file mode 100644 index 00000000000..b58d6302521 --- /dev/null +++ b/drivers/iio/adc/men_z188_adc.c @@ -0,0 +1,172 @@ +/* + * MEN 16z188 Analog to Digial Converter + * + * Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de) + * Author: Johannes Thumshirn <johannes.thumshirn@men.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; version 2 of the License. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mcb.h> +#include <linux/io.h> +#include <linux/iio/iio.h> + +#define Z188_ADC_MAX_CHAN	8 +#define Z188_ADC_GAIN		0x0700000 +#define Z188_MODE_VOLTAGE	BIT(27) +#define Z188_CFG_AUTO		0x1 +#define Z188_CTRL_REG		0x40 + +#define ADC_DATA(x) (((x) >> 2) & 0x7ffffc) +#define ADC_OVR(x) ((x) & 0x1) + +struct z188_adc { +	struct resource *mem; +	void __iomem *base; +}; + +#define Z188_ADC_CHANNEL(idx) {					\ +		.type = IIO_VOLTAGE,				\ +		.indexed = 1,					\ +		.channel = (idx),				\ +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),   \ +} + +static const struct iio_chan_spec z188_adc_iio_channels[] = { +	Z188_ADC_CHANNEL(0), +	Z188_ADC_CHANNEL(1), +	Z188_ADC_CHANNEL(2), +	Z188_ADC_CHANNEL(3), +	Z188_ADC_CHANNEL(4), +	Z188_ADC_CHANNEL(5), +	Z188_ADC_CHANNEL(6), +	Z188_ADC_CHANNEL(7), +}; + +static int z188_iio_read_raw(struct iio_dev *iio_dev, +			struct iio_chan_spec const *chan, +			int *val, +			int *val2, +			long info) +{ +	struct z188_adc *adc = iio_priv(iio_dev); +	int ret; +	u16 tmp; + +	switch (info) { +	case IIO_CHAN_INFO_RAW: +		tmp = readw(adc->base + chan->channel * 4); + +		if (ADC_OVR(tmp)) { +			dev_info(&iio_dev->dev, +				"Oversampling error on ADC channel %d\n", +				chan->channel); +			return -EIO; +		} +		*val = ADC_DATA(tmp); +		ret = IIO_VAL_INT; +		break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +static struct iio_info z188_adc_info = { +	.read_raw = &z188_iio_read_raw, +	.driver_module = THIS_MODULE, +}; + +static void men_z188_config_channels(void __iomem *addr) +{ +	int i; +	u32 cfg; +	u32 ctl; + +	ctl = readl(addr + Z188_CTRL_REG); +	ctl |= Z188_CFG_AUTO; +	writel(ctl, addr + Z188_CTRL_REG); + +	for (i = 0; i < Z188_ADC_MAX_CHAN; i++) { +		cfg = readl(addr + i); +		cfg &= ~Z188_ADC_GAIN; +		cfg |= Z188_MODE_VOLTAGE; +		writel(cfg, addr + i); +	} +} + +static int men_z188_probe(struct mcb_device *dev, +			const struct mcb_device_id *id) +{ +	struct z188_adc *adc; +	struct iio_dev *indio_dev; +	struct resource *mem; + +	indio_dev = devm_iio_device_alloc(&dev->dev, sizeof(struct z188_adc)); +	if (!indio_dev) +		return -ENOMEM; + +	adc = iio_priv(indio_dev); +	indio_dev->name = "z188-adc"; +	indio_dev->dev.parent = &dev->dev; +	indio_dev->info = &z188_adc_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = z188_adc_iio_channels; +	indio_dev->num_channels = ARRAY_SIZE(z188_adc_iio_channels); + +	mem = mcb_request_mem(dev, "z188-adc"); +	if (IS_ERR(mem)) +		return PTR_ERR(mem); + +	adc->base = ioremap(mem->start, resource_size(mem)); +	if (adc->base == NULL) +		goto err; + +	men_z188_config_channels(adc->base); + +	adc->mem = mem; +	mcb_set_drvdata(dev, indio_dev); + +	return iio_device_register(indio_dev); + +err: +	mcb_release_mem(mem); +	return -ENXIO; +} + +static void men_z188_remove(struct mcb_device *dev) +{ +	struct iio_dev *indio_dev  = mcb_get_drvdata(dev); +	struct z188_adc *adc = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	iounmap(adc->base); +	mcb_release_mem(adc->mem); +} + +static const struct mcb_device_id men_z188_ids[] = { +	{ .device = 0xbc }, +}; +MODULE_DEVICE_TABLE(mcb, men_z188_ids); + +static struct mcb_driver men_z188_driver = { +	.driver = { +		.name = "z188-adc", +		.owner = THIS_MODULE, +	}, +	.probe = men_z188_probe, +	.remove = men_z188_remove, +	.id_table = men_z188_ids, +}; +module_mcb_driver(men_z188_driver); + +MODULE_AUTHOR("Johannes Thumshirn <johannes.thumshirn@men.de>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("IIO ADC driver for MEN 16z188 ADC Core"); +MODULE_ALIAS("mcb:16z188"); diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index bdf03468f3b..e525aa6475c 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -12,6 +12,7 @@  #include <linux/module.h>  #include <linux/wait.h>  #include <linux/log2.h> +#include <linux/of.h>  #include <linux/iio/iio.h>  #include <linux/iio/sysfs.h> @@ -189,7 +190,7 @@ static int nau7802_read_irq(struct iio_dev *indio_dev,  	struct nau7802_state *st = iio_priv(indio_dev);  	int ret; -	INIT_COMPLETION(st->value_ok); +	reinit_completion(&st->value_ok);  	enable_irq(st->client->irq);  	nau7802_sync(st); @@ -569,7 +570,7 @@ static struct i2c_driver nau7802_driver = {  	.id_table = nau7802_i2c_id,  	.driver = {  		   .name = "nau7802", -		   .of_match_table = of_match_ptr(nau7802_dt_ids), +		   .of_match_table = nau7802_dt_ids,  	},  }; diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index ee5f72bffe5..b3a82b4d1a7 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -9,6 +9,7 @@  #include <linux/err.h>  #include <linux/i2c.h>  #include <linux/module.h> +#include <linux/of.h>  #include <linux/iio/iio.h>  #include <linux/regulator/consumer.h> diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index a952538a1a8..d5dc4c6ce86 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -13,7 +13,6 @@   * GNU General Public License for more details.   */ -#include <linux/init.h>  #include <linux/kernel.h>  #include <linux/err.h>  #include <linux/module.h> @@ -28,12 +27,16 @@  #include <linux/iio/driver.h>  #include <linux/mfd/ti_am335x_tscadc.h> +#include <linux/iio/buffer.h> +#include <linux/iio/kfifo_buf.h>  struct tiadc_device {  	struct ti_tscadc_dev *mfd_tscadc;  	int channels;  	u8 channel_line[8];  	u8 channel_step[8]; +	int buffer_en_ch_steps; +	u16 data[8];  };  static unsigned int tiadc_readl(struct tiadc_device *adc, unsigned int reg) @@ -56,8 +59,32 @@ static u32 get_adc_step_mask(struct tiadc_device *adc_dev)  	return step_en;  } -static void tiadc_step_config(struct tiadc_device *adc_dev) +static u32 get_adc_chan_step_mask(struct tiadc_device *adc_dev, +		struct iio_chan_spec const *chan)  { +	int i; + +	for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { +		if (chan->channel == adc_dev->channel_line[i]) { +			u32 step; + +			step = adc_dev->channel_step[i]; +			/* +1 for the charger */ +			return 1 << (step + 1); +		} +	} +	WARN_ON(1); +	return 0; +} + +static u32 get_adc_step_bit(struct tiadc_device *adc_dev, int chan) +{ +	return 1 << adc_dev->channel_step[chan]; +} + +static void tiadc_step_config(struct iio_dev *indio_dev) +{ +	struct tiadc_device *adc_dev = iio_priv(indio_dev);  	unsigned int stepconfig;  	int i, steps; @@ -72,7 +99,11 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)  	 */  	steps = TOTAL_STEPS - adc_dev->channels; -	stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1; +	if (iio_buffer_enabled(indio_dev)) +		stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1 +					| STEPCONFIG_MODE_SWCNT; +	else +		stepconfig = STEPCONFIG_AVG_16 | STEPCONFIG_FIFO1;  	for (i = 0; i < adc_dev->channels; i++) {  		int chan; @@ -85,9 +116,179 @@ static void tiadc_step_config(struct tiadc_device *adc_dev)  		adc_dev->channel_step[i] = steps;  		steps++;  	} +} + +static irqreturn_t tiadc_irq_h(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct tiadc_device *adc_dev = iio_priv(indio_dev); +	unsigned int status, config; +	status = tiadc_readl(adc_dev, REG_IRQSTATUS); + +	/* +	 * ADC and touchscreen share the IRQ line. +	 * FIFO0 interrupts are used by TSC. Handle FIFO1 IRQs here only +	 */ +	if (status & IRQENB_FIFO1OVRRUN) { +		/* FIFO Overrun. Clear flag. Disable/Enable ADC to recover */ +		config = tiadc_readl(adc_dev, REG_CTRL); +		config &= ~(CNTRLREG_TSCSSENB); +		tiadc_writel(adc_dev, REG_CTRL, config); +		tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1OVRRUN +				| IRQENB_FIFO1UNDRFLW | IRQENB_FIFO1THRES); +		tiadc_writel(adc_dev, REG_CTRL, (config | CNTRLREG_TSCSSENB)); +		return IRQ_HANDLED; +	} else if (status & IRQENB_FIFO1THRES) { +		/* Disable irq and wake worker thread */ +		tiadc_writel(adc_dev, REG_IRQCLR, IRQENB_FIFO1THRES); +		return IRQ_WAKE_THREAD; +	} + +	return IRQ_NONE; +} + +static irqreturn_t tiadc_worker_h(int irq, void *private) +{ +	struct iio_dev *indio_dev = private; +	struct tiadc_device *adc_dev = iio_priv(indio_dev); +	int i, k, fifo1count, read; +	u16 *data = adc_dev->data; + +	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +	for (k = 0; k < fifo1count; k = k + i) { +		for (i = 0; i < (indio_dev->scan_bytes)/2; i++) { +			read = tiadc_readl(adc_dev, REG_FIFO1); +			data[i] = read & FIFOREAD_DATA_MASK; +		} +		iio_push_to_buffers(indio_dev, (u8 *) data); +	} +	tiadc_writel(adc_dev, REG_IRQSTATUS, IRQENB_FIFO1THRES); +	tiadc_writel(adc_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + +	return IRQ_HANDLED; +} + +static int tiadc_buffer_preenable(struct iio_dev *indio_dev) +{ +	struct tiadc_device *adc_dev = iio_priv(indio_dev); +	int i, fifo1count, read; + +	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES | +				IRQENB_FIFO1OVRRUN | +				IRQENB_FIFO1UNDRFLW)); + +	/* Flush FIFO. Needed in corner cases in simultaneous tsc/adc use */ +	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +	for (i = 0; i < fifo1count; i++) +		read = tiadc_readl(adc_dev, REG_FIFO1); + +	return 0;  } +static int tiadc_buffer_postenable(struct iio_dev *indio_dev) +{ +	struct tiadc_device *adc_dev = iio_priv(indio_dev); +	struct iio_buffer *buffer = indio_dev->buffer; +	unsigned int enb = 0; +	u8 bit; + +	tiadc_step_config(indio_dev); +	for_each_set_bit(bit, buffer->scan_mask, adc_dev->channels) +		enb |= (get_adc_step_bit(adc_dev, bit) << 1); +	adc_dev->buffer_en_ch_steps = enb; + +	am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, enb); + +	tiadc_writel(adc_dev,  REG_IRQSTATUS, IRQENB_FIFO1THRES +				| IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW); +	tiadc_writel(adc_dev,  REG_IRQENABLE, IRQENB_FIFO1THRES +				| IRQENB_FIFO1OVRRUN); + +	return 0; +} + +static int tiadc_buffer_predisable(struct iio_dev *indio_dev) +{ +	struct tiadc_device *adc_dev = iio_priv(indio_dev); +	int fifo1count, i, read; + +	tiadc_writel(adc_dev, REG_IRQCLR, (IRQENB_FIFO1THRES | +				IRQENB_FIFO1OVRRUN | IRQENB_FIFO1UNDRFLW)); +	am335x_tsc_se_clr(adc_dev->mfd_tscadc, adc_dev->buffer_en_ch_steps); +	adc_dev->buffer_en_ch_steps = 0; + +	/* Flush FIFO of leftover data in the time it takes to disable adc */ +	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +	for (i = 0; i < fifo1count; i++) +		read = tiadc_readl(adc_dev, REG_FIFO1); + +	return 0; +} + +static int tiadc_buffer_postdisable(struct iio_dev *indio_dev) +{ +	tiadc_step_config(indio_dev); + +	return 0; +} + +static const struct iio_buffer_setup_ops tiadc_buffer_setup_ops = { +	.preenable = &tiadc_buffer_preenable, +	.postenable = &tiadc_buffer_postenable, +	.predisable = &tiadc_buffer_predisable, +	.postdisable = &tiadc_buffer_postdisable, +}; + +static int tiadc_iio_buffered_hardware_setup(struct iio_dev *indio_dev, +	irqreturn_t (*pollfunc_bh)(int irq, void *p), +	irqreturn_t (*pollfunc_th)(int irq, void *p), +	int irq, +	unsigned long flags, +	const struct iio_buffer_setup_ops *setup_ops) +{ +	struct iio_buffer *buffer; +	int ret; + +	buffer = iio_kfifo_allocate(indio_dev); +	if (!buffer) +		return -ENOMEM; + +	iio_device_attach_buffer(indio_dev, buffer); + +	ret = request_threaded_irq(irq,	pollfunc_th, pollfunc_bh, +				flags, indio_dev->name, indio_dev); +	if (ret) +		goto error_kfifo_free; + +	indio_dev->setup_ops = setup_ops; +	indio_dev->modes |= INDIO_BUFFER_HARDWARE; + +	ret = iio_buffer_register(indio_dev, +				  indio_dev->channels, +				  indio_dev->num_channels); +	if (ret) +		goto error_free_irq; + +	return 0; + +error_free_irq: +	free_irq(irq, indio_dev); +error_kfifo_free: +	iio_kfifo_free(indio_dev->buffer); +	return ret; +} + +static void tiadc_iio_buffered_hardware_remove(struct iio_dev *indio_dev) +{ +	struct tiadc_device *adc_dev = iio_priv(indio_dev); + +	free_irq(adc_dev->mfd_tscadc->irq, indio_dev); +	iio_kfifo_free(indio_dev->buffer); +	iio_buffer_unregister(indio_dev); +} + +  static const char * const chan_name_ain[] = {  	"AIN0",  	"AIN1", @@ -120,9 +321,10 @@ static int tiadc_channel_init(struct iio_dev *indio_dev, int channels)  		chan->channel = adc_dev->channel_line[i];  		chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);  		chan->datasheet_name = chan_name_ain[chan->channel]; +		chan->scan_index = i;  		chan->scan_type.sign = 'u';  		chan->scan_type.realbits = 12; -		chan->scan_type.storagebits = 32; +		chan->scan_type.storagebits = 16;  	}  	indio_dev->channels = chan_array; @@ -142,42 +344,45 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,  	struct tiadc_device *adc_dev = iio_priv(indio_dev);  	int i, map_val;  	unsigned int fifo1count, read, stepid; -	u32 step = UINT_MAX;  	bool found = false;  	u32 step_en; -	unsigned long timeout = jiffies + usecs_to_jiffies -				(IDLE_TIMEOUT * adc_dev->channels); -	step_en = get_adc_step_mask(adc_dev); -	am335x_tsc_se_set(adc_dev->mfd_tscadc, step_en); +	unsigned long timeout; -	/* Wait for ADC sequencer to complete sampling */ -	while (tiadc_readl(adc_dev, REG_ADCFSM) & SEQ_STATUS) { -		if (time_after(jiffies, timeout)) -			return -EAGAIN; -		} -	map_val = chan->channel + TOTAL_CHANNELS; +	if (iio_buffer_enabled(indio_dev)) +		return -EBUSY; -	/* -	 * When the sub-system is first enabled, -	 * the sequencer will always start with the -	 * lowest step (1) and continue until step (16). -	 * For ex: If we have enabled 4 ADC channels and -	 * currently use only 1 out of them, the -	 * sequencer still configures all the 4 steps, -	 * leading to 3 unwanted data. -	 * Hence we need to flush out this data. -	 */ +	step_en = get_adc_chan_step_mask(adc_dev, chan); +	if (!step_en) +		return -EINVAL; -	for (i = 0; i < ARRAY_SIZE(adc_dev->channel_step); i++) { -		if (chan->channel == adc_dev->channel_line[i]) { -			step = adc_dev->channel_step[i]; +	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +	while (fifo1count--) +		tiadc_readl(adc_dev, REG_FIFO1); + +	am335x_tsc_se_set_once(adc_dev->mfd_tscadc, step_en); + +	timeout = jiffies + usecs_to_jiffies +				(IDLE_TIMEOUT * adc_dev->channels); +	/* Wait for Fifo threshold interrupt */ +	while (1) { +		fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +		if (fifo1count)  			break; + +		if (time_after(jiffies, timeout)) { +			am335x_tsc_se_adc_done(adc_dev->mfd_tscadc); +			return -EAGAIN;  		}  	} -	if (WARN_ON_ONCE(step == UINT_MAX)) -		return -EINVAL; +	map_val = adc_dev->channel_step[chan->scan_index]; -	fifo1count = tiadc_readl(adc_dev, REG_FIFO1CNT); +	/* +	 * We check the complete FIFO. We programmed just one entry but in case +	 * something went wrong we left empty handed (-EAGAIN previously) and +	 * then the value apeared somehow in the FIFO we would have two entries. +	 * Therefore we read every item and keep only the latest version of the +	 * requested channel. +	 */  	for (i = 0; i < fifo1count; i++) {  		read = tiadc_readl(adc_dev, REG_FIFO1);  		stepid = read & FIFOREAD_CHNLID_MASK; @@ -186,9 +391,10 @@ static int tiadc_read_raw(struct iio_dev *indio_dev,  		if (stepid == map_val) {  			read = read & FIFOREAD_DATA_MASK;  			found = true; -			*val = read; +			*val = (u16) read;  		}  	} +	am335x_tsc_se_adc_done(adc_dev->mfd_tscadc);  	if (found == false)  		return -EBUSY; @@ -237,20 +443,33 @@ static int tiadc_probe(struct platform_device *pdev)  	indio_dev->modes = INDIO_DIRECT_MODE;  	indio_dev->info = &tiadc_info; -	tiadc_step_config(adc_dev); +	tiadc_step_config(indio_dev); +	tiadc_writel(adc_dev, REG_FIFO1THR, FIFO1_THRESHOLD);  	err = tiadc_channel_init(indio_dev, adc_dev->channels);  	if (err < 0)  		return err; -	err = iio_device_register(indio_dev); +	err = tiadc_iio_buffered_hardware_setup(indio_dev, +		&tiadc_worker_h, +		&tiadc_irq_h, +		adc_dev->mfd_tscadc->irq, +		IRQF_SHARED, +		&tiadc_buffer_setup_ops); +  	if (err)  		goto err_free_channels; +	err = iio_device_register(indio_dev); +	if (err) +		goto err_buffer_unregister; +  	platform_set_drvdata(pdev, indio_dev);  	return 0; +err_buffer_unregister: +	tiadc_iio_buffered_hardware_remove(indio_dev);  err_free_channels:  	tiadc_channels_remove(indio_dev);  	return err; @@ -263,6 +482,7 @@ static int tiadc_remove(struct platform_device *pdev)  	u32 step_en;  	iio_device_unregister(indio_dev); +	tiadc_iio_buffered_hardware_remove(indio_dev);  	tiadc_channels_remove(indio_dev);  	step_en = get_adc_step_mask(adc_dev); @@ -301,8 +521,9 @@ static int tiadc_resume(struct device *dev)  	restore &= ~(CNTRLREG_POWERDOWN);  	tiadc_writel(adc_dev, REG_CTRL, restore); -	tiadc_step_config(adc_dev); - +	tiadc_step_config(indio_dev); +	am335x_tsc_se_set_cache(adc_dev->mfd_tscadc, +			adc_dev->buffer_en_ch_steps);  	return 0;  } @@ -326,7 +547,7 @@ static struct platform_driver tiadc_driver = {  		.name   = "TI-am335x-adc",  		.owner	= THIS_MODULE,  		.pm	= TIADC_PM_OPS, -		.of_match_table = of_match_ptr(ti_adc_dt_ids), +		.of_match_table = ti_adc_dt_ids,  	},  	.probe	= tiadc_probe,  	.remove	= tiadc_remove, diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c new file mode 100644 index 00000000000..eb86786e698 --- /dev/null +++ b/drivers/iio/adc/twl4030-madc.c @@ -0,0 +1,896 @@ +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + * + * Amit Kucheria <amit.kucheria@canonical.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/i2c/twl.h> +#include <linux/i2c/twl4030-madc.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> + +/** + * struct twl4030_madc_data - a container for madc info + * @dev:		Pointer to device structure for madc + * @lock:		Mutex protecting this data structure + * @requests:		Array of request struct corresponding to SW1, SW2 and RT + * @use_second_irq:	IRQ selection (main or co-processor) + * @imr:		Interrupt mask register of MADC + * @isr:		Interrupt status register of MADC + */ +struct twl4030_madc_data { +	struct device *dev; +	struct mutex lock;	/* mutex protecting this data structure */ +	struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; +	bool use_second_irq; +	u8 imr; +	u8 isr; +}; + +static int twl4030_madc_read(struct iio_dev *iio_dev, +			     const struct iio_chan_spec *chan, +			     int *val, int *val2, long mask) +{ +	struct twl4030_madc_data *madc = iio_priv(iio_dev); +	struct twl4030_madc_request req; +	int ret; + +	req.method = madc->use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1; + +	req.channels = BIT(chan->channel); +	req.active = false; +	req.func_cb = NULL; +	req.type = TWL4030_MADC_WAIT; +	req.raw = !(mask == IIO_CHAN_INFO_PROCESSED); +	req.do_avg = (mask == IIO_CHAN_INFO_AVERAGE_RAW); + +	ret = twl4030_madc_conversion(&req); +	if (ret < 0) +		return ret; + +	*val = req.rbuf[chan->channel]; + +	return IIO_VAL_INT; +} + +static const struct iio_info twl4030_madc_iio_info = { +	.read_raw = &twl4030_madc_read, +	.driver_module = THIS_MODULE, +}; + +#define TWL4030_ADC_CHANNEL(_channel, _type, _name) {	\ +	.type = _type,					\ +	.channel = _channel,				\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  \ +			      BIT(IIO_CHAN_INFO_AVERAGE_RAW) | \ +			      BIT(IIO_CHAN_INFO_PROCESSED), \ +	.datasheet_name = _name,			\ +	.indexed = 1,					\ +} + +static const struct iio_chan_spec twl4030_madc_iio_channels[] = { +	TWL4030_ADC_CHANNEL(0, IIO_VOLTAGE, "ADCIN0"), +	TWL4030_ADC_CHANNEL(1, IIO_TEMP, "ADCIN1"), +	TWL4030_ADC_CHANNEL(2, IIO_VOLTAGE, "ADCIN2"), +	TWL4030_ADC_CHANNEL(3, IIO_VOLTAGE, "ADCIN3"), +	TWL4030_ADC_CHANNEL(4, IIO_VOLTAGE, "ADCIN4"), +	TWL4030_ADC_CHANNEL(5, IIO_VOLTAGE, "ADCIN5"), +	TWL4030_ADC_CHANNEL(6, IIO_VOLTAGE, "ADCIN6"), +	TWL4030_ADC_CHANNEL(7, IIO_VOLTAGE, "ADCIN7"), +	TWL4030_ADC_CHANNEL(8, IIO_VOLTAGE, "ADCIN8"), +	TWL4030_ADC_CHANNEL(9, IIO_VOLTAGE, "ADCIN9"), +	TWL4030_ADC_CHANNEL(10, IIO_CURRENT, "ADCIN10"), +	TWL4030_ADC_CHANNEL(11, IIO_VOLTAGE, "ADCIN11"), +	TWL4030_ADC_CHANNEL(12, IIO_VOLTAGE, "ADCIN12"), +	TWL4030_ADC_CHANNEL(13, IIO_VOLTAGE, "ADCIN13"), +	TWL4030_ADC_CHANNEL(14, IIO_VOLTAGE, "ADCIN14"), +	TWL4030_ADC_CHANNEL(15, IIO_VOLTAGE, "ADCIN15"), +}; + +static struct twl4030_madc_data *twl4030_madc; + +struct twl4030_prescale_divider_ratios { +	s16 numerator; +	s16 denominator; +}; + +static const struct twl4030_prescale_divider_ratios +twl4030_divider_ratios[16] = { +	{1, 1},		/* CHANNEL 0 No Prescaler */ +	{1, 1},		/* CHANNEL 1 No Prescaler */ +	{6, 10},	/* CHANNEL 2 */ +	{6, 10},	/* CHANNEL 3 */ +	{6, 10},	/* CHANNEL 4 */ +	{6, 10},	/* CHANNEL 5 */ +	{6, 10},	/* CHANNEL 6 */ +	{6, 10},	/* CHANNEL 7 */ +	{3, 14},	/* CHANNEL 8 */ +	{1, 3},		/* CHANNEL 9 */ +	{1, 1},		/* CHANNEL 10 No Prescaler */ +	{15, 100},	/* CHANNEL 11 */ +	{1, 4},		/* CHANNEL 12 */ +	{1, 1},		/* CHANNEL 13 Reserved channels */ +	{1, 1},		/* CHANNEL 14 Reseved channels */ +	{5, 11},	/* CHANNEL 15 */ +}; + + +/* Conversion table from -3 to 55 degrees Celcius */ +static int twl4030_therm_tbl[] = { +	30800,	29500,	28300,	27100, +	26000,	24900,	23900,	22900,	22000,	21100,	20300,	19400,	18700, +	17900,	17200,	16500,	15900,	15300,	14700,	14100,	13600,	13100, +	12600,	12100,	11600,	11200,	10800,	10400,	10000,	9630,	9280, +	8950,	8620,	8310,	8020,	7730,	7460,	7200,	6950,	6710, +	6470,	6250,	6040,	5830,	5640,	5450,	5260,	5090,	4920, +	4760,	4600,	4450,	4310,	4170,	4040,	3910,	3790,	3670, +	3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { +	[TWL4030_MADC_RT] = { +			     .sel = TWL4030_MADC_RTSELECT_LSB, +			     .avg = TWL4030_MADC_RTAVERAGE_LSB, +			     .rbase = TWL4030_MADC_RTCH0_LSB, +			     }, +	[TWL4030_MADC_SW1] = { +			      .sel = TWL4030_MADC_SW1SELECT_LSB, +			      .avg = TWL4030_MADC_SW1AVERAGE_LSB, +			      .rbase = TWL4030_MADC_GPCH0_LSB, +			      .ctrl = TWL4030_MADC_CTRL_SW1, +			      }, +	[TWL4030_MADC_SW2] = { +			      .sel = TWL4030_MADC_SW2SELECT_LSB, +			      .avg = TWL4030_MADC_SW2AVERAGE_LSB, +			      .rbase = TWL4030_MADC_GPCH0_LSB, +			      .ctrl = TWL4030_MADC_CTRL_SW2, +			      }, +}; + +/** + * twl4030_madc_channel_raw_read() - Function to read a particular channel value + * @madc:	pointer to struct twl4030_madc_data + * @reg:	lsb of ADC Channel + * + * Return: 0 on success, an error code otherwise. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ +	u16 val; +	int ret; +	/* +	 * For each ADC channel, we have MSB and LSB register pair. MSB address +	 * is always LSB address+1. reg parameter is the address of LSB register +	 */ +	ret = twl_i2c_read_u16(TWL4030_MODULE_MADC, &val, reg); +	if (ret) { +		dev_err(madc->dev, "unable to read register 0x%X\n", reg); +		return ret; +	} + +	return (int)(val >> 6); +} + +/* + * Return battery temperature in degrees Celsius + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ +	u8 val; +	int temp, curr, volt, res, ret; + +	volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; +	/* Getting and calculating the supply current in micro amperes */ +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, +		REG_BCICTL2); +	if (ret < 0) +		return ret; + +	curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; +	/* Getting and calculating the thermistor resistance in ohms */ +	res = volt * 1000 / curr; +	/* calculating temperature */ +	for (temp = 58; temp >= 0; temp--) { +		int actual = twl4030_therm_tbl[temp]; +		if ((actual - res) >= 0) +			break; +	} + +	return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ +	int ret; +	u8 val; + +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, +		TWL4030_BCI_BCICTL1); +	if (ret) +		return ret; +	if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ +		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; +	else /* slope of 0.88 mV/mA */ +		return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} + +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel's value is read + * @buf - The channel values are stored here. if read fails error + * @raw - Return raw values without conversion + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, +				      u8 reg_base, unsigned +				      long channels, int *buf, +				      bool raw) +{ +	int count = 0; +	int i; +	u8 reg; + +	for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { +		reg = reg_base + (2 * i); +		buf[i] = twl4030_madc_channel_raw_read(madc, reg); +		if (buf[i] < 0) { +			dev_err(madc->dev, "Unable to read register 0x%X\n", +				reg); +			return buf[i]; +		} +		if (raw) { +			count++; +			continue; +		} +		switch (i) { +		case 10: +			buf[i] = twl4030battery_current(buf[i]); +			if (buf[i] < 0) { +				dev_err(madc->dev, "err reading current\n"); +				return buf[i]; +			} else { +				count++; +				buf[i] = buf[i] - 750; +			} +			break; +		case 1: +			buf[i] = twl4030battery_temperature(buf[i]); +			if (buf[i] < 0) { +				dev_err(madc->dev, "err reading temperature\n"); +				return buf[i]; +			} else { +				buf[i] -= 3; +				count++; +			} +			break; +		default: +			count++; +			/* Analog Input (V) = conv_result * step_size / R +			 * conv_result = decimal value of 10-bit conversion +			 *		 result +			 * step size = 1.5 / (2 ^ 10 -1) +			 * R = Prescaler ratio for input channels. +			 * Result given in mV hence multiplied by 1000. +			 */ +			buf[i] = (buf[i] * 3 * 1000 * +				 twl4030_divider_ratios[i].denominator) +				/ (2 * 1023 * +				twl4030_divider_ratios[i].numerator); +		} +	} + +	return count; +} + +/* + * Enables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be enabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) +{ +	u8 val; +	int ret; + +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); +	if (ret) { +		dev_err(madc->dev, "unable to read imr register 0x%X\n", +			madc->imr); +		return ret; +	} + +	val &= ~(1 << id); +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); +	if (ret) { +		dev_err(madc->dev, +			"unable to write imr register 0x%X\n", madc->imr); +		return ret; +	} + +	return 0; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ +	u8 val; +	int ret; + +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); +	if (ret) { +		dev_err(madc->dev, "unable to read imr register 0x%X\n", +			madc->imr); +		return ret; +	} +	val |= (1 << id); +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); +	if (ret) { +		dev_err(madc->dev, +			"unable to write imr register 0x%X\n", madc->imr); +		return ret; +	} + +	return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ +	struct twl4030_madc_data *madc = _madc; +	const struct twl4030_madc_conversion_method *method; +	u8 isr_val, imr_val; +	int i, len, ret; +	struct twl4030_madc_request *r; + +	mutex_lock(&madc->lock); +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); +	if (ret) { +		dev_err(madc->dev, "unable to read isr register 0x%X\n", +			madc->isr); +		goto err_i2c; +	} +	ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); +	if (ret) { +		dev_err(madc->dev, "unable to read imr register 0x%X\n", +			madc->imr); +		goto err_i2c; +	} +	isr_val &= ~imr_val; +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { +		if (!(isr_val & (1 << i))) +			continue; +		ret = twl4030_madc_disable_irq(madc, i); +		if (ret < 0) +			dev_dbg(madc->dev, "Disable interrupt failed %d\n", i); +		madc->requests[i].result_pending = 1; +	} +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { +		r = &madc->requests[i]; +		/* No pending results for this method, move to next one */ +		if (!r->result_pending) +			continue; +		method = &twl4030_conversion_methods[r->method]; +		/* Read results */ +		len = twl4030_madc_read_channels(madc, method->rbase, +						 r->channels, r->rbuf, r->raw); +		/* Return results to caller */ +		if (r->func_cb != NULL) { +			r->func_cb(len, r->channels, r->rbuf); +			r->func_cb = NULL; +		} +		/* Free request */ +		r->result_pending = 0; +		r->active = 0; +	} +	mutex_unlock(&madc->lock); + +	return IRQ_HANDLED; + +err_i2c: +	/* +	 * In case of error check whichever request is active +	 * and service the same. +	 */ +	for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { +		r = &madc->requests[i]; +		if (r->active == 0) +			continue; +		method = &twl4030_conversion_methods[r->method]; +		/* Read results */ +		len = twl4030_madc_read_channels(madc, method->rbase, +						 r->channels, r->rbuf, r->raw); +		/* Return results to caller */ +		if (r->func_cb != NULL) { +			r->func_cb(len, r->channels, r->rbuf); +			r->func_cb = NULL; +		} +		/* Free request */ +		r->result_pending = 0; +		r->active = 0; +	} +	mutex_unlock(&madc->lock); + +	return IRQ_HANDLED; +} + +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, +				struct twl4030_madc_request *req) +{ +	struct twl4030_madc_request *p; +	int ret; + +	p = &madc->requests[req->method]; +	memcpy(p, req, sizeof(*req)); +	ret = twl4030_madc_enable_irq(madc, req->method); +	if (ret < 0) { +		dev_err(madc->dev, "enable irq failed!!\n"); +		return ret; +	} + +	return 0; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, +					 int conv_method) +{ +	const struct twl4030_madc_conversion_method *method; +	int ret = 0; + +	if (conv_method != TWL4030_MADC_SW1 && conv_method != TWL4030_MADC_SW2) +		return -ENOTSUPP; + +	method = &twl4030_conversion_methods[conv_method]; +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, TWL4030_MADC_SW_START, +			       method->ctrl); +	if (ret) { +		dev_err(madc->dev, "unable to write ctrl register 0x%X\n", +			method->ctrl); +		return ret; +	} + +	return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, +					      unsigned int timeout_ms, +					      u8 status_reg) +{ +	unsigned long timeout; +	int ret; + +	timeout = jiffies + msecs_to_jiffies(timeout_ms); +	do { +		u8 reg; + +		ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); +		if (ret) { +			dev_err(madc->dev, +				"unable to read status register 0x%X\n", +				status_reg); +			return ret; +		} +		if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) +			return 0; +		usleep_range(500, 2000); +	} while (!time_after(jiffies, timeout)); +	dev_err(madc->dev, "conversion timeout!\n"); + +	return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ +	const struct twl4030_madc_conversion_method *method; +	int ret; + +	if (!req || !twl4030_madc) +		return -EINVAL; + +	mutex_lock(&twl4030_madc->lock); +	if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { +		ret = -EINVAL; +		goto out; +	} +	/* Do we have a conversion request ongoing */ +	if (twl4030_madc->requests[req->method].active) { +		ret = -EBUSY; +		goto out; +	} +	method = &twl4030_conversion_methods[req->method]; +	/* Select channels to be converted */ +	ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, method->sel); +	if (ret) { +		dev_err(twl4030_madc->dev, +			"unable to write sel register 0x%X\n", method->sel); +		goto out; +	} +	/* Select averaging for all channels if do_avg is set */ +	if (req->do_avg) { +		ret = twl_i2c_write_u16(TWL4030_MODULE_MADC, req->channels, +				       method->avg); +		if (ret) { +			dev_err(twl4030_madc->dev, +				"unable to write avg register 0x%X\n", +				method->avg); +			goto out; +		} +	} +	if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { +		ret = twl4030_madc_set_irq(twl4030_madc, req); +		if (ret < 0) +			goto out; +		ret = twl4030_madc_start_conversion(twl4030_madc, req->method); +		if (ret < 0) +			goto out; +		twl4030_madc->requests[req->method].active = 1; +		ret = 0; +		goto out; +	} +	/* With RT method we should not be here anymore */ +	if (req->method == TWL4030_MADC_RT) { +		ret = -EINVAL; +		goto out; +	} +	ret = twl4030_madc_start_conversion(twl4030_madc, req->method); +	if (ret < 0) +		goto out; +	twl4030_madc->requests[req->method].active = 1; +	/* Wait until conversion is ready (ctrl register returns EOC) */ +	ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); +	if (ret) { +		twl4030_madc->requests[req->method].active = 0; +		goto out; +	} +	ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, +					 req->channels, req->rbuf, req->raw); +	twl4030_madc->requests[req->method].active = 0; + +out: +	mutex_unlock(&twl4030_madc->lock); + +	return ret; +} +EXPORT_SYMBOL_GPL(twl4030_madc_conversion); + +int twl4030_get_madc_conversion(int channel_no) +{ +	struct twl4030_madc_request req; +	int temp = 0; +	int ret; + +	req.channels = (1 << channel_no); +	req.method = TWL4030_MADC_SW2; +	req.active = 0; +	req.raw = 0; +	req.func_cb = NULL; +	ret = twl4030_madc_conversion(&req); +	if (ret < 0) +		return ret; +	if (req.rbuf[channel_no] > 0) +		temp = req.rbuf[channel_no]; + +	return temp; +} +EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); + +/** + * twl4030_madc_set_current_generator() - setup bias current + * + * @madc:	pointer to twl4030_madc_data struct + * @chan:	can be one of the two values: + *		TWL4030_BCI_ITHEN + *		Enables bias current for main battery type reading + *		TWL4030_BCI_TYPEN + *		Enables bias current for main battery temperature sensing + * @on:		enable or disable chan. + * + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, +					      int chan, int on) +{ +	int ret; +	int regmask; +	u8 regval; + +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, +			      ®val, TWL4030_BCI_BCICTL1); +	if (ret) { +		dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", +			TWL4030_BCI_BCICTL1); +		return ret; +	} + +	regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; +	if (on) +		regval |= regmask; +	else +		regval &= ~regmask; + +	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, +			       regval, TWL4030_BCI_BCICTL1); +	if (ret) { +		dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", +			TWL4030_BCI_BCICTL1); +		return ret; +	} + +	return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software power on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ +	u8 regval; +	int ret; + +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, +			      ®val, TWL4030_MADC_CTRL1); +	if (ret) { +		dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", +			TWL4030_MADC_CTRL1); +		return ret; +	} +	if (on) +		regval |= TWL4030_MADC_MADCON; +	else +		regval &= ~TWL4030_MADC_MADCON; +	ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); +	if (ret) { +		dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", +			TWL4030_MADC_CTRL1); +		return ret; +	} + +	return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int twl4030_madc_probe(struct platform_device *pdev) +{ +	struct twl4030_madc_data *madc; +	struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); +	struct device_node *np = pdev->dev.of_node; +	int irq, ret; +	u8 regval; +	struct iio_dev *iio_dev = NULL; + +	if (!pdata && !np) { +		dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); +		return -EINVAL; +	} + +	iio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*madc)); +	if (!iio_dev) { +		dev_err(&pdev->dev, "failed allocating iio device\n"); +		return -ENOMEM; +	} + +	madc = iio_priv(iio_dev); +	madc->dev = &pdev->dev; + +	iio_dev->name = dev_name(&pdev->dev); +	iio_dev->dev.parent = &pdev->dev; +	iio_dev->dev.of_node = pdev->dev.of_node; +	iio_dev->info = &twl4030_madc_iio_info; +	iio_dev->modes = INDIO_DIRECT_MODE; +	iio_dev->channels = twl4030_madc_iio_channels; +	iio_dev->num_channels = ARRAY_SIZE(twl4030_madc_iio_channels); + +	/* +	 * Phoenix provides 2 interrupt lines. The first one is connected to +	 * the OMAP. The other one can be connected to the other processor such +	 * as modem. Hence two separate ISR and IMR registers. +	 */ +	if (pdata) +		madc->use_second_irq = (pdata->irq_line != 1); +	else +		madc->use_second_irq = of_property_read_bool(np, +				       "ti,system-uses-second-madc-irq"); + +	madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : +					   TWL4030_MADC_IMR1; +	madc->isr = madc->use_second_irq ? TWL4030_MADC_ISR2 : +					   TWL4030_MADC_ISR1; + +	ret = twl4030_madc_set_power(madc, 1); +	if (ret < 0) +		return ret; +	ret = twl4030_madc_set_current_generator(madc, 0, 1); +	if (ret < 0) +		goto err_current_generator; + +	ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, +			      ®val, TWL4030_BCI_BCICTL1); +	if (ret) { +		dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", +			TWL4030_BCI_BCICTL1); +		goto err_i2c; +	} +	regval |= TWL4030_BCI_MESBAT; +	ret = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, +			       regval, TWL4030_BCI_BCICTL1); +	if (ret) { +		dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", +			TWL4030_BCI_BCICTL1); +		goto err_i2c; +	} + +	/* Check that MADC clock is on */ +	ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); +	if (ret) { +		dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", +				TWL4030_REG_GPBR1); +		goto err_i2c; +	} + +	/* If MADC clk is not on, turn it on */ +	if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { +		dev_info(&pdev->dev, "clk disabled, enabling\n"); +		regval |= TWL4030_GPBR1_MADC_HFCLK_EN; +		ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, +				       TWL4030_REG_GPBR1); +		if (ret) { +			dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", +					TWL4030_REG_GPBR1); +			goto err_i2c; +		} +	} + +	platform_set_drvdata(pdev, iio_dev); +	mutex_init(&madc->lock); + +	irq = platform_get_irq(pdev, 0); +	ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, +				   twl4030_madc_threaded_irq_handler, +				   IRQF_TRIGGER_RISING, "twl4030_madc", madc); +	if (ret) { +		dev_err(&pdev->dev, "could not request irq\n"); +		goto err_i2c; +	} +	twl4030_madc = madc; + +	ret = iio_device_register(iio_dev); +	if (ret) { +		dev_err(&pdev->dev, "could not register iio device\n"); +		goto err_i2c; +	} + +	return 0; + +err_i2c: +	twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: +	twl4030_madc_set_power(madc, 0); +	return ret; +} + +static int twl4030_madc_remove(struct platform_device *pdev) +{ +	struct iio_dev *iio_dev = platform_get_drvdata(pdev); +	struct twl4030_madc_data *madc = iio_priv(iio_dev); + +	iio_device_unregister(iio_dev); + +	twl4030_madc_set_current_generator(madc, 0, 0); +	twl4030_madc_set_power(madc, 0); + +	return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id twl_madc_of_match[] = { +	{ .compatible = "ti,twl4030-madc", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, twl_madc_of_match); +#endif + +static struct platform_driver twl4030_madc_driver = { +	.probe = twl4030_madc_probe, +	.remove = twl4030_madc_remove, +	.driver = { +		   .name = "twl4030_madc", +		   .owner = THIS_MODULE, +		   .of_match_table = of_match_ptr(twl_madc_of_match), +	}, +}; + +module_platform_driver(twl4030_madc_driver); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 0ea96c058c0..15282f148b3 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -28,7 +28,6 @@   * 02110-1301 USA   *   */ -#include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/kernel.h>  #include <linux/module.h> @@ -887,7 +886,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)  	int irq;  	int ret; -	match = of_match_device(of_match_ptr(of_twl6030_match_tbl), dev); +	match = of_match_device(of_twl6030_match_tbl, dev);  	if (!match)  		return -EINVAL; @@ -948,9 +947,7 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)  	indio_dev->channels = pdata->iio_channels;  	indio_dev->num_channels = pdata->nchannels; -	ret = iio_device_register(indio_dev); - -	return ret; +	return iio_device_register(indio_dev);  }  static int twl6030_gpadc_remove(struct platform_device *pdev) @@ -971,7 +968,7 @@ static int twl6030_gpadc_suspend(struct device *pdev)  	ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, TWL6030_GPADCR,  				TWL6030_REG_TOGGLE1);  	if (ret) -		dev_err(pdev, "error reseting GPADC (%d)!\n", ret); +		dev_err(pdev, "error resetting GPADC (%d)!\n", ret);  	return 0;  }; diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c new file mode 100644 index 00000000000..44799eb5930 --- /dev/null +++ b/drivers/iio/adc/vf610_adc.c @@ -0,0 +1,711 @@ +/* + * Freescale Vybrid vf610 ADC driver + * + * Copyright 2013 Freescale Semiconductor, Inc. + * + * 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. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/regulator/consumer.h> +#include <linux/of_platform.h> +#include <linux/err.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/driver.h> + +/* This will be the driver name the kernel reports */ +#define DRIVER_NAME "vf610-adc" + +/* Vybrid/IMX ADC registers */ +#define VF610_REG_ADC_HC0		0x00 +#define VF610_REG_ADC_HC1		0x04 +#define VF610_REG_ADC_HS		0x08 +#define VF610_REG_ADC_R0		0x0c +#define VF610_REG_ADC_R1		0x10 +#define VF610_REG_ADC_CFG		0x14 +#define VF610_REG_ADC_GC		0x18 +#define VF610_REG_ADC_GS		0x1c +#define VF610_REG_ADC_CV		0x20 +#define VF610_REG_ADC_OFS		0x24 +#define VF610_REG_ADC_CAL		0x28 +#define VF610_REG_ADC_PCTL		0x30 + +/* Configuration register field define */ +#define VF610_ADC_MODE_BIT8		0x00 +#define VF610_ADC_MODE_BIT10		0x04 +#define VF610_ADC_MODE_BIT12		0x08 +#define VF610_ADC_MODE_MASK		0x0c +#define VF610_ADC_BUSCLK2_SEL		0x01 +#define VF610_ADC_ALTCLK_SEL		0x02 +#define VF610_ADC_ADACK_SEL		0x03 +#define VF610_ADC_ADCCLK_MASK		0x03 +#define VF610_ADC_CLK_DIV2		0x20 +#define VF610_ADC_CLK_DIV4		0x40 +#define VF610_ADC_CLK_DIV8		0x60 +#define VF610_ADC_CLK_MASK		0x60 +#define VF610_ADC_ADLSMP_LONG		0x10 +#define VF610_ADC_ADSTS_MASK		0x300 +#define VF610_ADC_ADLPC_EN		0x80 +#define VF610_ADC_ADHSC_EN		0x400 +#define VF610_ADC_REFSEL_VALT		0x100 +#define VF610_ADC_REFSEL_VBG		0x1000 +#define VF610_ADC_ADTRG_HARD		0x2000 +#define VF610_ADC_AVGS_8		0x4000 +#define VF610_ADC_AVGS_16		0x8000 +#define VF610_ADC_AVGS_32		0xC000 +#define VF610_ADC_AVGS_MASK		0xC000 +#define VF610_ADC_OVWREN		0x10000 + +/* General control register field define */ +#define VF610_ADC_ADACKEN		0x1 +#define VF610_ADC_DMAEN			0x2 +#define VF610_ADC_ACREN			0x4 +#define VF610_ADC_ACFGT			0x8 +#define VF610_ADC_ACFE			0x10 +#define VF610_ADC_AVGEN			0x20 +#define VF610_ADC_ADCON			0x40 +#define VF610_ADC_CAL			0x80 + +/* Other field define */ +#define VF610_ADC_ADCHC(x)		((x) & 0xF) +#define VF610_ADC_AIEN			(0x1 << 7) +#define VF610_ADC_CONV_DISABLE		0x1F +#define VF610_ADC_HS_COCO0		0x1 +#define VF610_ADC_CALF			0x2 +#define VF610_ADC_TIMEOUT		msecs_to_jiffies(100) + +enum clk_sel { +	VF610_ADCIOC_BUSCLK_SET, +	VF610_ADCIOC_ALTCLK_SET, +	VF610_ADCIOC_ADACK_SET, +}; + +enum vol_ref { +	VF610_ADCIOC_VR_VREF_SET, +	VF610_ADCIOC_VR_VALT_SET, +	VF610_ADCIOC_VR_VBG_SET, +}; + +enum average_sel { +	VF610_ADC_SAMPLE_1, +	VF610_ADC_SAMPLE_4, +	VF610_ADC_SAMPLE_8, +	VF610_ADC_SAMPLE_16, +	VF610_ADC_SAMPLE_32, +}; + +struct vf610_adc_feature { +	enum clk_sel	clk_sel; +	enum vol_ref	vol_ref; + +	int	clk_div; +	int     sample_rate; +	int	res_mode; + +	bool	lpm; +	bool	calibration; +	bool	ovwren; +}; + +struct vf610_adc { +	struct device *dev; +	void __iomem *regs; +	struct clk *clk; + +	u32 vref_uv; +	u32 value; +	struct regulator *vref; +	struct vf610_adc_feature adc_feature; + +	struct completion completion; +}; + +#define VF610_ADC_CHAN(_idx, _chan_type) {			\ +	.type = (_chan_type),					\ +	.indexed = 1,						\ +	.channel = (_idx),					\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\ +				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\ +} + +static const struct iio_chan_spec vf610_adc_iio_channels[] = { +	VF610_ADC_CHAN(0, IIO_VOLTAGE), +	VF610_ADC_CHAN(1, IIO_VOLTAGE), +	VF610_ADC_CHAN(2, IIO_VOLTAGE), +	VF610_ADC_CHAN(3, IIO_VOLTAGE), +	VF610_ADC_CHAN(4, IIO_VOLTAGE), +	VF610_ADC_CHAN(5, IIO_VOLTAGE), +	VF610_ADC_CHAN(6, IIO_VOLTAGE), +	VF610_ADC_CHAN(7, IIO_VOLTAGE), +	VF610_ADC_CHAN(8, IIO_VOLTAGE), +	VF610_ADC_CHAN(9, IIO_VOLTAGE), +	VF610_ADC_CHAN(10, IIO_VOLTAGE), +	VF610_ADC_CHAN(11, IIO_VOLTAGE), +	VF610_ADC_CHAN(12, IIO_VOLTAGE), +	VF610_ADC_CHAN(13, IIO_VOLTAGE), +	VF610_ADC_CHAN(14, IIO_VOLTAGE), +	VF610_ADC_CHAN(15, IIO_VOLTAGE), +	/* sentinel */ +}; + +/* + * ADC sample frequency, unit is ADCK cycles. + * ADC clk source is ipg clock, which is the same as bus clock. + * + * ADC conversion time = SFCAdder + AverageNum x (BCT + LSTAdder) + * SFCAdder: fixed to 6 ADCK cycles + * AverageNum: 1, 4, 8, 16, 32 samples for hardware average. + * BCT (Base Conversion Time): fixed to 25 ADCK cycles for 12 bit mode + * LSTAdder(Long Sample Time): fixed to 3 ADCK cycles + * + * By default, enable 12 bit resolution mode, clock source + * set to ipg clock, So get below frequency group: + */ +static const u32 vf610_sample_freq_avail[5] = +{1941176, 559332, 286957, 145374, 73171}; + +static inline void vf610_adc_cfg_init(struct vf610_adc *info) +{ +	/* set default Configuration for ADC controller */ +	info->adc_feature.clk_sel = VF610_ADCIOC_BUSCLK_SET; +	info->adc_feature.vol_ref = VF610_ADCIOC_VR_VREF_SET; + +	info->adc_feature.calibration = true; +	info->adc_feature.ovwren = true; + +	info->adc_feature.clk_div = 1; +	info->adc_feature.res_mode = 12; +	info->adc_feature.sample_rate = 1; +	info->adc_feature.lpm = true; +} + +static void vf610_adc_cfg_post_set(struct vf610_adc *info) +{ +	struct vf610_adc_feature *adc_feature = &info->adc_feature; +	int cfg_data = 0; +	int gc_data = 0; + +	switch (adc_feature->clk_sel) { +	case VF610_ADCIOC_ALTCLK_SET: +		cfg_data |= VF610_ADC_ALTCLK_SEL; +		break; +	case VF610_ADCIOC_ADACK_SET: +		cfg_data |= VF610_ADC_ADACK_SEL; +		break; +	default: +		break; +	} + +	/* low power set for calibration */ +	cfg_data |= VF610_ADC_ADLPC_EN; + +	/* enable high speed for calibration */ +	cfg_data |= VF610_ADC_ADHSC_EN; + +	/* voltage reference */ +	switch (adc_feature->vol_ref) { +	case VF610_ADCIOC_VR_VREF_SET: +		break; +	case VF610_ADCIOC_VR_VALT_SET: +		cfg_data |= VF610_ADC_REFSEL_VALT; +		break; +	case VF610_ADCIOC_VR_VBG_SET: +		cfg_data |= VF610_ADC_REFSEL_VBG; +		break; +	default: +		dev_err(info->dev, "error voltage reference\n"); +	} + +	/* data overwrite enable */ +	if (adc_feature->ovwren) +		cfg_data |= VF610_ADC_OVWREN; + +	writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +	writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_calibration(struct vf610_adc *info) +{ +	int adc_gc, hc_cfg; +	int timeout; + +	if (!info->adc_feature.calibration) +		return; + +	/* enable calibration interrupt */ +	hc_cfg = VF610_ADC_AIEN | VF610_ADC_CONV_DISABLE; +	writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + +	adc_gc = readl(info->regs + VF610_REG_ADC_GC); +	writel(adc_gc | VF610_ADC_CAL, info->regs + VF610_REG_ADC_GC); + +	timeout = wait_for_completion_timeout +			(&info->completion, VF610_ADC_TIMEOUT); +	if (timeout == 0) +		dev_err(info->dev, "Timeout for adc calibration\n"); + +	adc_gc = readl(info->regs + VF610_REG_ADC_GS); +	if (adc_gc & VF610_ADC_CALF) +		dev_err(info->dev, "ADC calibration failed\n"); + +	info->adc_feature.calibration = false; +} + +static void vf610_adc_cfg_set(struct vf610_adc *info) +{ +	struct vf610_adc_feature *adc_feature = &(info->adc_feature); +	int cfg_data; + +	cfg_data = readl(info->regs + VF610_REG_ADC_CFG); + +	/* low power configuration */ +	cfg_data &= ~VF610_ADC_ADLPC_EN; +	if (adc_feature->lpm) +		cfg_data |= VF610_ADC_ADLPC_EN; + +	/* disable high speed */ +	cfg_data &= ~VF610_ADC_ADHSC_EN; + +	writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +} + +static void vf610_adc_sample_set(struct vf610_adc *info) +{ +	struct vf610_adc_feature *adc_feature = &(info->adc_feature); +	int cfg_data, gc_data; + +	cfg_data = readl(info->regs + VF610_REG_ADC_CFG); +	gc_data = readl(info->regs + VF610_REG_ADC_GC); + +	/* resolution mode */ +	cfg_data &= ~VF610_ADC_MODE_MASK; +	switch (adc_feature->res_mode) { +	case 8: +		cfg_data |= VF610_ADC_MODE_BIT8; +		break; +	case 10: +		cfg_data |= VF610_ADC_MODE_BIT10; +		break; +	case 12: +		cfg_data |= VF610_ADC_MODE_BIT12; +		break; +	default: +		dev_err(info->dev, "error resolution mode\n"); +		break; +	} + +	/* clock select and clock divider */ +	cfg_data &= ~(VF610_ADC_CLK_MASK | VF610_ADC_ADCCLK_MASK); +	switch (adc_feature->clk_div) { +	case 1: +		break; +	case 2: +		cfg_data |= VF610_ADC_CLK_DIV2; +		break; +	case 4: +		cfg_data |= VF610_ADC_CLK_DIV4; +		break; +	case 8: +		cfg_data |= VF610_ADC_CLK_DIV8; +		break; +	case 16: +		switch (adc_feature->clk_sel) { +		case VF610_ADCIOC_BUSCLK_SET: +			cfg_data |= VF610_ADC_BUSCLK2_SEL | VF610_ADC_CLK_DIV8; +			break; +		default: +			dev_err(info->dev, "error clk divider\n"); +			break; +		} +		break; +	} + +	/* Use the short sample mode */ +	cfg_data &= ~(VF610_ADC_ADLSMP_LONG | VF610_ADC_ADSTS_MASK); + +	/* update hardware average selection */ +	cfg_data &= ~VF610_ADC_AVGS_MASK; +	gc_data &= ~VF610_ADC_AVGEN; +	switch (adc_feature->sample_rate) { +	case VF610_ADC_SAMPLE_1: +		break; +	case VF610_ADC_SAMPLE_4: +		gc_data |= VF610_ADC_AVGEN; +		break; +	case VF610_ADC_SAMPLE_8: +		gc_data |= VF610_ADC_AVGEN; +		cfg_data |= VF610_ADC_AVGS_8; +		break; +	case VF610_ADC_SAMPLE_16: +		gc_data |= VF610_ADC_AVGEN; +		cfg_data |= VF610_ADC_AVGS_16; +		break; +	case VF610_ADC_SAMPLE_32: +		gc_data |= VF610_ADC_AVGEN; +		cfg_data |= VF610_ADC_AVGS_32; +		break; +	default: +		dev_err(info->dev, +			"error hardware sample average select\n"); +	} + +	writel(cfg_data, info->regs + VF610_REG_ADC_CFG); +	writel(gc_data, info->regs + VF610_REG_ADC_GC); +} + +static void vf610_adc_hw_init(struct vf610_adc *info) +{ +	/* CFG: Feature set */ +	vf610_adc_cfg_post_set(info); +	vf610_adc_sample_set(info); + +	/* adc calibration */ +	vf610_adc_calibration(info); + +	/* CFG: power and speed set */ +	vf610_adc_cfg_set(info); +} + +static int vf610_adc_read_data(struct vf610_adc *info) +{ +	int result; + +	result = readl(info->regs + VF610_REG_ADC_R0); + +	switch (info->adc_feature.res_mode) { +	case 8: +		result &= 0xFF; +		break; +	case 10: +		result &= 0x3FF; +		break; +	case 12: +		result &= 0xFFF; +		break; +	default: +		break; +	} + +	return result; +} + +static irqreturn_t vf610_adc_isr(int irq, void *dev_id) +{ +	struct vf610_adc *info = (struct vf610_adc *)dev_id; +	int coco; + +	coco = readl(info->regs + VF610_REG_ADC_HS); +	if (coco & VF610_ADC_HS_COCO0) { +		info->value = vf610_adc_read_data(info); +		complete(&info->completion); +	} + +	return IRQ_HANDLED; +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1941176, 559332, 286957, 145374, 73171"); + +static struct attribute *vf610_attributes[] = { +	&iio_const_attr_sampling_frequency_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group vf610_attribute_group = { +	.attrs = vf610_attributes, +}; + +static int vf610_read_raw(struct iio_dev *indio_dev, +			struct iio_chan_spec const *chan, +			int *val, +			int *val2, +			long mask) +{ +	struct vf610_adc *info = iio_priv(indio_dev); +	unsigned int hc_cfg; +	long ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		mutex_lock(&indio_dev->mlock); +		reinit_completion(&info->completion); + +		hc_cfg = VF610_ADC_ADCHC(chan->channel); +		hc_cfg |= VF610_ADC_AIEN; +		writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); +		ret = wait_for_completion_interruptible_timeout +				(&info->completion, VF610_ADC_TIMEOUT); +		if (ret == 0) { +			mutex_unlock(&indio_dev->mlock); +			return -ETIMEDOUT; +		} +		if (ret < 0) { +			mutex_unlock(&indio_dev->mlock); +			return ret; +		} + +		*val = info->value; +		mutex_unlock(&indio_dev->mlock); +		return IIO_VAL_INT; + +	case IIO_CHAN_INFO_SCALE: +		*val = info->vref_uv / 1000; +		*val2 = info->adc_feature.res_mode; +		return IIO_VAL_FRACTIONAL_LOG2; + +	case IIO_CHAN_INFO_SAMP_FREQ: +		*val = vf610_sample_freq_avail[info->adc_feature.sample_rate]; +		*val2 = 0; +		return IIO_VAL_INT; + +	default: +		break; +	} + +	return -EINVAL; +} + +static int vf610_write_raw(struct iio_dev *indio_dev, +			struct iio_chan_spec const *chan, +			int val, +			int val2, +			long mask) +{ +	struct vf610_adc *info = iio_priv(indio_dev); +	int i; + +	switch (mask) { +		case IIO_CHAN_INFO_SAMP_FREQ: +			for (i = 0; +				i < ARRAY_SIZE(vf610_sample_freq_avail); +				i++) +				if (val == vf610_sample_freq_avail[i]) { +					info->adc_feature.sample_rate = i; +					vf610_adc_sample_set(info); +					return 0; +				} +			break; + +		default: +			break; +	} + +	return -EINVAL; +} + +static int vf610_adc_reg_access(struct iio_dev *indio_dev, +			unsigned reg, unsigned writeval, +			unsigned *readval) +{ +	struct vf610_adc *info = iio_priv(indio_dev); + +	if ((readval == NULL) || +		(!(reg % 4) || (reg > VF610_REG_ADC_PCTL))) +		return -EINVAL; + +	*readval = readl(info->regs + reg); + +	return 0; +} + +static const struct iio_info vf610_adc_iio_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &vf610_read_raw, +	.write_raw = &vf610_write_raw, +	.debugfs_reg_access = &vf610_adc_reg_access, +	.attrs = &vf610_attribute_group, +}; + +static const struct of_device_id vf610_adc_match[] = { +	{ .compatible = "fsl,vf610-adc", }, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, vf610_adc_match); + +static int vf610_adc_probe(struct platform_device *pdev) +{ +	struct vf610_adc *info; +	struct iio_dev *indio_dev; +	struct resource *mem; +	int irq; +	int ret; + +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct vf610_adc)); +	if (!indio_dev) { +		dev_err(&pdev->dev, "Failed allocating iio device\n"); +		return -ENOMEM; +	} + +	info = iio_priv(indio_dev); +	info->dev = &pdev->dev; + +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	info->regs = devm_ioremap_resource(&pdev->dev, mem); +	if (IS_ERR(info->regs)) +		return PTR_ERR(info->regs); + +	irq = platform_get_irq(pdev, 0); +	if (irq <= 0) { +		dev_err(&pdev->dev, "no irq resource?\n"); +		return -EINVAL; +	} + +	ret = devm_request_irq(info->dev, irq, +				vf610_adc_isr, 0, +				dev_name(&pdev->dev), info); +	if (ret < 0) { +		dev_err(&pdev->dev, "failed requesting irq, irq = %d\n", irq); +		return ret; +	} + +	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); +		return ret; +	} + +	info->vref = devm_regulator_get(&pdev->dev, "vref"); +	if (IS_ERR(info->vref)) +		return PTR_ERR(info->vref); + +	ret = regulator_enable(info->vref); +	if (ret) +		return ret; + +	info->vref_uv = regulator_get_voltage(info->vref); + +	platform_set_drvdata(pdev, indio_dev); + +	init_completion(&info->completion); + +	indio_dev->name = dev_name(&pdev->dev); +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->dev.of_node = pdev->dev.of_node; +	indio_dev->info = &vf610_adc_iio_info; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = vf610_adc_iio_channels; +	indio_dev->num_channels = ARRAY_SIZE(vf610_adc_iio_channels); + +	ret = clk_prepare_enable(info->clk); +	if (ret) { +		dev_err(&pdev->dev, +			"Could not prepare or enable the clock.\n"); +		goto error_adc_clk_enable; +	} + +	vf610_adc_cfg_init(info); +	vf610_adc_hw_init(info); + +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&pdev->dev, "Couldn't register the device.\n"); +		goto error_iio_device_register; +	} + +	return 0; + + +error_iio_device_register: +	clk_disable_unprepare(info->clk); +error_adc_clk_enable: +	regulator_disable(info->vref); + +	return ret; +} + +static int vf610_adc_remove(struct platform_device *pdev) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct vf610_adc *info = iio_priv(indio_dev); + +	iio_device_unregister(indio_dev); +	regulator_disable(info->vref); +	clk_disable_unprepare(info->clk); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int vf610_adc_suspend(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct vf610_adc *info = iio_priv(indio_dev); +	int hc_cfg; + +	/* ADC controller enters to stop mode */ +	hc_cfg = readl(info->regs + VF610_REG_ADC_HC0); +	hc_cfg |= VF610_ADC_CONV_DISABLE; +	writel(hc_cfg, info->regs + VF610_REG_ADC_HC0); + +	clk_disable_unprepare(info->clk); +	regulator_disable(info->vref); + +	return 0; +} + +static int vf610_adc_resume(struct device *dev) +{ +	struct iio_dev *indio_dev = dev_get_drvdata(dev); +	struct vf610_adc *info = iio_priv(indio_dev); +	int ret; + +	ret = regulator_enable(info->vref); +	if (ret) +		return ret; + +	ret = clk_prepare_enable(info->clk); +	if (ret) +		return ret; + +	vf610_adc_hw_init(info); + +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(vf610_adc_pm_ops, +			vf610_adc_suspend, +			vf610_adc_resume); + +static struct platform_driver vf610_adc_driver = { +	.probe          = vf610_adc_probe, +	.remove         = vf610_adc_remove, +	.driver         = { +		.name   = DRIVER_NAME, +		.owner  = THIS_MODULE, +		.of_match_table = vf610_adc_match, +		.pm     = &vf610_adc_pm_ops, +	}, +}; + +module_platform_driver(vf610_adc_driver); + +MODULE_AUTHOR("Fugang Duan <B38611@freescale.com>"); +MODULE_DESCRIPTION("Freescale VF610 ADC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c index 09727a71e9f..9acf6b6d705 100644 --- a/drivers/iio/adc/viperboard_adc.c +++ b/drivers/iio/adc/viperboard_adc.c @@ -42,12 +42,6 @@ struct vprbrd_adc {  	.indexed = 1,					\  	.channel = _index,				\  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\ -	.scan_index = _index,				\ -	.scan_type = {					\ -		.sign = 'u',				\ -		.realbits = 8,				\ -		.storagebits = 8,			\ -	},						\  }  static struct iio_chan_spec const vprbrd_adc_iio_channels[] = { @@ -73,7 +67,7 @@ static int vprbrd_iio_read_raw(struct iio_dev *iio_dev,  		mutex_lock(&vb->lock);  		admsg->cmd = VPRBRD_ADC_CMD_GET; -		admsg->chan = chan->scan_index; +		admsg->chan = chan->channel;  		admsg->val = 0x00;  		ret = usb_control_msg(vb->usb_dev, @@ -139,23 +133,12 @@ static int vprbrd_adc_probe(struct platform_device *pdev)  	indio_dev->channels = vprbrd_adc_iio_channels;  	indio_dev->num_channels = ARRAY_SIZE(vprbrd_adc_iio_channels); -	ret = iio_device_register(indio_dev); +	ret = devm_iio_device_register(&pdev->dev, indio_dev);  	if (ret) {  		dev_err(&pdev->dev, "could not register iio (adc)");  		return ret;  	} -	platform_set_drvdata(pdev, indio_dev); - -	return 0; -} - -static int vprbrd_adc_remove(struct platform_device *pdev) -{ -	struct iio_dev *indio_dev = platform_get_drvdata(pdev); - -	iio_device_unregister(indio_dev); -  	return 0;  } @@ -165,7 +148,6 @@ static struct platform_driver vprbrd_adc_driver = {  		.owner	= THIS_MODULE,  	},  	.probe		= vprbrd_adc_probe, -	.remove		= vprbrd_adc_remove,  };  module_platform_driver(vprbrd_adc_driver); diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c new file mode 100644 index 00000000000..ab52be29141 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -0,0 +1,1333 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013-2014 Analog Devices Inc. + *  Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + * Documentation for the parts can be found at: + *  - XADC hardmacro: Xilinx UG480 + *  - ZYNQ XADC interface: Xilinx UG585 + *  - AXI XADC interface: Xilinx PG019 + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/events.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/triggered_buffer.h> + +#include "xilinx-xadc.h" + +static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; + +/* ZYNQ register definitions */ +#define XADC_ZYNQ_REG_CFG	0x00 +#define XADC_ZYNQ_REG_INTSTS	0x04 +#define XADC_ZYNQ_REG_INTMSK	0x08 +#define XADC_ZYNQ_REG_STATUS	0x0c +#define XADC_ZYNQ_REG_CFIFO	0x10 +#define XADC_ZYNQ_REG_DFIFO	0x14 +#define XADC_ZYNQ_REG_CTL		0x18 + +#define XADC_ZYNQ_CFG_ENABLE		BIT(31) +#define XADC_ZYNQ_CFG_CFIFOTH_MASK	(0xf << 20) +#define XADC_ZYNQ_CFG_CFIFOTH_OFFSET	20 +#define XADC_ZYNQ_CFG_DFIFOTH_MASK	(0xf << 16) +#define XADC_ZYNQ_CFG_DFIFOTH_OFFSET	16 +#define XADC_ZYNQ_CFG_WEDGE		BIT(13) +#define XADC_ZYNQ_CFG_REDGE		BIT(12) +#define XADC_ZYNQ_CFG_TCKRATE_MASK	(0x3 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV2	(0x0 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV4	(0x1 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV8	(0x2 << 8) +#define XADC_ZYNQ_CFG_TCKRATE_DIV16	(0x3 << 8) +#define XADC_ZYNQ_CFG_IGAP_MASK		0x1f +#define XADC_ZYNQ_CFG_IGAP(x)		(x) + +#define XADC_ZYNQ_INT_CFIFO_LTH		BIT(9) +#define XADC_ZYNQ_INT_DFIFO_GTH		BIT(8) +#define XADC_ZYNQ_INT_ALARM_MASK	0xff +#define XADC_ZYNQ_INT_ALARM_OFFSET	0 + +#define XADC_ZYNQ_STATUS_CFIFO_LVL_MASK	(0xf << 16) +#define XADC_ZYNQ_STATUS_CFIFO_LVL_OFFSET	16 +#define XADC_ZYNQ_STATUS_DFIFO_LVL_MASK	(0xf << 12) +#define XADC_ZYNQ_STATUS_DFIFO_LVL_OFFSET	12 +#define XADC_ZYNQ_STATUS_CFIFOF		BIT(11) +#define XADC_ZYNQ_STATUS_CFIFOE		BIT(10) +#define XADC_ZYNQ_STATUS_DFIFOF		BIT(9) +#define XADC_ZYNQ_STATUS_DFIFOE		BIT(8) +#define XADC_ZYNQ_STATUS_OT		BIT(7) +#define XADC_ZYNQ_STATUS_ALM(x)		BIT(x) + +#define XADC_ZYNQ_CTL_RESET		BIT(4) + +#define XADC_ZYNQ_CMD_NOP		0x00 +#define XADC_ZYNQ_CMD_READ		0x01 +#define XADC_ZYNQ_CMD_WRITE		0x02 + +#define XADC_ZYNQ_CMD(cmd, addr, data) (((cmd) << 26) | ((addr) << 16) | (data)) + +/* AXI register definitions */ +#define XADC_AXI_REG_RESET		0x00 +#define XADC_AXI_REG_STATUS		0x04 +#define XADC_AXI_REG_ALARM_STATUS	0x08 +#define XADC_AXI_REG_CONVST		0x0c +#define XADC_AXI_REG_XADC_RESET		0x10 +#define XADC_AXI_REG_GIER		0x5c +#define XADC_AXI_REG_IPISR		0x60 +#define XADC_AXI_REG_IPIER		0x68 +#define XADC_AXI_ADC_REG_OFFSET		0x200 + +#define XADC_AXI_RESET_MAGIC		0xa +#define XADC_AXI_GIER_ENABLE		BIT(31) + +#define XADC_AXI_INT_EOS		BIT(4) +#define XADC_AXI_INT_ALARM_MASK		0x3c0f + +#define XADC_FLAGS_BUFFERED BIT(0) + +static void xadc_write_reg(struct xadc *xadc, unsigned int reg, +	uint32_t val) +{ +	writel(val, xadc->base + reg); +} + +static void xadc_read_reg(struct xadc *xadc, unsigned int reg, +	uint32_t *val) +{ +	*val = readl(xadc->base + reg); +} + +/* + * The ZYNQ interface uses two asynchronous FIFOs for communication with the + * XADC. Reads and writes to the XADC register are performed by submitting a + * request to the command FIFO (CFIFO), once the request has been completed the + * result can be read from the data FIFO (DFIFO). The method currently used in + * this driver is to submit the request for a read/write operation, then go to + * sleep and wait for an interrupt that signals that a response is available in + * the data FIFO. + */ + +static void xadc_zynq_write_fifo(struct xadc *xadc, uint32_t *cmd, +	unsigned int n) +{ +	unsigned int i; + +	for (i = 0; i < n; i++) +		xadc_write_reg(xadc, XADC_ZYNQ_REG_CFIFO, cmd[i]); +} + +static void xadc_zynq_drain_fifo(struct xadc *xadc) +{ +	uint32_t status, tmp; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); + +	while (!(status & XADC_ZYNQ_STATUS_DFIFOE)) { +		xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); +		xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &status); +	} +} + +static void xadc_zynq_update_intmsk(struct xadc *xadc, unsigned int mask, +	unsigned int val) +{ +	xadc->zynq_intmask &= ~mask; +	xadc->zynq_intmask |= val; + +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, +		xadc->zynq_intmask | xadc->zynq_masked_alarm); +} + +static int xadc_zynq_write_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t val) +{ +	uint32_t cmd[1]; +	uint32_t tmp; +	int ret; + +	spin_lock_irq(&xadc->lock); +	xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, +			XADC_ZYNQ_INT_DFIFO_GTH); + +	reinit_completion(&xadc->completion); + +	cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_WRITE, reg, val); +	xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); +	xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); +	tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; +	tmp |= 0 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; +	xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + +	xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); +	spin_unlock_irq(&xadc->lock); + +	ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); +	if (ret == 0) +		ret = -EIO; +	else +		ret = 0; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &tmp); + +	return ret; +} + +static int xadc_zynq_read_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t *val) +{ +	uint32_t cmd[2]; +	uint32_t resp, tmp; +	int ret; + +	cmd[0] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_READ, reg, 0); +	cmd[1] = XADC_ZYNQ_CMD(XADC_ZYNQ_CMD_NOP, 0, 0); + +	spin_lock_irq(&xadc->lock); +	xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, +			XADC_ZYNQ_INT_DFIFO_GTH); +	xadc_zynq_drain_fifo(xadc); +	reinit_completion(&xadc->completion); + +	xadc_zynq_write_fifo(xadc, cmd, ARRAY_SIZE(cmd)); +	xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &tmp); +	tmp &= ~XADC_ZYNQ_CFG_DFIFOTH_MASK; +	tmp |= 1 << XADC_ZYNQ_CFG_DFIFOTH_OFFSET; +	xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, tmp); + +	xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, 0); +	spin_unlock_irq(&xadc->lock); +	ret = wait_for_completion_interruptible_timeout(&xadc->completion, HZ); +	if (ret == 0) +		ret = -EIO; +	if (ret < 0) +		return ret; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); +	xadc_read_reg(xadc, XADC_ZYNQ_REG_DFIFO, &resp); + +	*val = resp & 0xffff; + +	return 0; +} + +static unsigned int xadc_zynq_transform_alarm(unsigned int alarm) +{ +	return ((alarm & 0x80) >> 4) | +		((alarm & 0x78) << 1) | +		(alarm & 0x07); +} + +/* + * The ZYNQ threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active thresholds interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void xadc_zynq_unmask_worker(struct work_struct *work) +{ +	struct xadc *xadc = container_of(work, struct xadc, zynq_unmask_work.work); +	unsigned int misc_sts, unmask; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_STATUS, &misc_sts); + +	misc_sts &= XADC_ZYNQ_INT_ALARM_MASK; + +	spin_lock_irq(&xadc->lock); + +	/* Clear those bits which are not active anymore */ +	unmask = (xadc->zynq_masked_alarm ^ misc_sts) & xadc->zynq_masked_alarm; +	xadc->zynq_masked_alarm &= misc_sts; + +	/* Also clear those which are masked out anyway */ +	xadc->zynq_masked_alarm &= ~xadc->zynq_intmask; + +	/* Clear the interrupts before we unmask them */ +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, unmask); + +	xadc_zynq_update_intmsk(xadc, 0, 0); + +	spin_unlock_irq(&xadc->lock); + +	/* if still pending some alarm re-trigger the timer */ +	if (xadc->zynq_masked_alarm) { +		schedule_delayed_work(&xadc->zynq_unmask_work, +				msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); +	} +} + +static irqreturn_t xadc_zynq_threaded_interrupt_handler(int irq, void *devid) +{ +	struct iio_dev *indio_dev = devid; +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned int alarm; + +	spin_lock_irq(&xadc->lock); +	alarm = xadc->zynq_alarm; +	xadc->zynq_alarm = 0; +	spin_unlock_irq(&xadc->lock); + +	xadc_handle_events(indio_dev, xadc_zynq_transform_alarm(alarm)); + +	/* unmask the required interrupts in timer. */ +	schedule_delayed_work(&xadc->zynq_unmask_work, +			msecs_to_jiffies(XADC_ZYNQ_UNMASK_TIMEOUT)); + +	return IRQ_HANDLED; +} + +static irqreturn_t xadc_zynq_interrupt_handler(int irq, void *devid) +{ +	struct iio_dev *indio_dev = devid; +	struct xadc *xadc = iio_priv(indio_dev); +	irqreturn_t ret = IRQ_HANDLED; +	uint32_t status; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); + +	status &= ~(xadc->zynq_intmask | xadc->zynq_masked_alarm); + +	if (!status) +		return IRQ_NONE; + +	spin_lock(&xadc->lock); + +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status); + +	if (status & XADC_ZYNQ_INT_DFIFO_GTH) { +		xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_DFIFO_GTH, +			XADC_ZYNQ_INT_DFIFO_GTH); +		complete(&xadc->completion); +	} + +	status &= XADC_ZYNQ_INT_ALARM_MASK; +	if (status) { +		xadc->zynq_alarm |= status; +		xadc->zynq_masked_alarm |= status; +		/* +		 * mask the current event interrupt, +		 * unmask it when the interrupt is no more active. +		 */ +		xadc_zynq_update_intmsk(xadc, 0, 0); +		ret = IRQ_WAKE_THREAD; +	} +	spin_unlock(&xadc->lock); + +	return ret; +} + +#define XADC_ZYNQ_TCK_RATE_MAX 50000000 +#define XADC_ZYNQ_IGAP_DEFAULT 20 + +static int xadc_zynq_setup(struct platform_device *pdev, +	struct iio_dev *indio_dev, int irq) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned long pcap_rate; +	unsigned int tck_div; +	unsigned int div; +	unsigned int igap; +	unsigned int tck_rate; + +	/* TODO: Figure out how to make igap and tck_rate configurable */ +	igap = XADC_ZYNQ_IGAP_DEFAULT; +	tck_rate = XADC_ZYNQ_TCK_RATE_MAX; + +	xadc->zynq_intmask = ~0; + +	pcap_rate = clk_get_rate(xadc->clk); + +	if (tck_rate > XADC_ZYNQ_TCK_RATE_MAX) +		tck_rate = XADC_ZYNQ_TCK_RATE_MAX; +	if (tck_rate > pcap_rate / 2) { +		div = 2; +	} else { +		div = pcap_rate / tck_rate; +		if (pcap_rate / div > XADC_ZYNQ_TCK_RATE_MAX) +			div++; +	} + +	if (div <= 3) +		tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV2; +	else if (div <= 7) +		tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV4; +	else if (div <= 15) +		tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV8; +	else +		tck_div = XADC_ZYNQ_CFG_TCKRATE_DIV16; + +	xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, XADC_ZYNQ_CTL_RESET); +	xadc_write_reg(xadc, XADC_ZYNQ_REG_CTL, 0); +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, ~0); +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTMSK, xadc->zynq_intmask); +	xadc_write_reg(xadc, XADC_ZYNQ_REG_CFG, XADC_ZYNQ_CFG_ENABLE | +			XADC_ZYNQ_CFG_REDGE | XADC_ZYNQ_CFG_WEDGE | +			tck_div | XADC_ZYNQ_CFG_IGAP(igap)); + +	return 0; +} + +static unsigned long xadc_zynq_get_dclk_rate(struct xadc *xadc) +{ +	unsigned int div; +	uint32_t val; + +	xadc_read_reg(xadc, XADC_ZYNQ_REG_CFG, &val); + +	switch (val & XADC_ZYNQ_CFG_TCKRATE_MASK) { +	case XADC_ZYNQ_CFG_TCKRATE_DIV4: +		div = 4; +		break; +	case XADC_ZYNQ_CFG_TCKRATE_DIV8: +		div = 8; +		break; +	case XADC_ZYNQ_CFG_TCKRATE_DIV16: +		div = 16; +		break; +	default: +		div = 2; +		break; +	} + +	return clk_get_rate(xadc->clk) / div; +} + +static void xadc_zynq_update_alarm(struct xadc *xadc, unsigned int alarm) +{ +	unsigned long flags; +	uint32_t status; + +	/* Move OT to bit 7 */ +	alarm = ((alarm & 0x08) << 4) | ((alarm & 0xf0) >> 1) | (alarm & 0x07); + +	spin_lock_irqsave(&xadc->lock, flags); + +	/* Clear previous interrupts if any. */ +	xadc_read_reg(xadc, XADC_ZYNQ_REG_INTSTS, &status); +	xadc_write_reg(xadc, XADC_ZYNQ_REG_INTSTS, status & alarm); + +	xadc_zynq_update_intmsk(xadc, XADC_ZYNQ_INT_ALARM_MASK, +		~alarm & XADC_ZYNQ_INT_ALARM_MASK); + +	spin_unlock_irqrestore(&xadc->lock, flags); +} + +static const struct xadc_ops xadc_zynq_ops = { +	.read = xadc_zynq_read_adc_reg, +	.write = xadc_zynq_write_adc_reg, +	.setup = xadc_zynq_setup, +	.get_dclk_rate = xadc_zynq_get_dclk_rate, +	.interrupt_handler = xadc_zynq_interrupt_handler, +	.threaded_interrupt_handler = xadc_zynq_threaded_interrupt_handler, +	.update_alarm = xadc_zynq_update_alarm, +}; + +static int xadc_axi_read_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t *val) +{ +	uint32_t val32; + +	xadc_read_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, &val32); +	*val = val32 & 0xffff; + +	return 0; +} + +static int xadc_axi_write_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t val) +{ +	xadc_write_reg(xadc, XADC_AXI_ADC_REG_OFFSET + reg * 4, val); + +	return 0; +} + +static int xadc_axi_setup(struct platform_device *pdev, +	struct iio_dev *indio_dev, int irq) +{ +	struct xadc *xadc = iio_priv(indio_dev); + +	xadc_write_reg(xadc, XADC_AXI_REG_RESET, XADC_AXI_RESET_MAGIC); +	xadc_write_reg(xadc, XADC_AXI_REG_GIER, XADC_AXI_GIER_ENABLE); + +	return 0; +} + +static irqreturn_t xadc_axi_interrupt_handler(int irq, void *devid) +{ +	struct iio_dev *indio_dev = devid; +	struct xadc *xadc = iio_priv(indio_dev); +	uint32_t status, mask; +	unsigned int events; + +	xadc_read_reg(xadc, XADC_AXI_REG_IPISR, &status); +	xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &mask); +	status &= mask; + +	if (!status) +		return IRQ_NONE; + +	if ((status & XADC_AXI_INT_EOS) && xadc->trigger) +		iio_trigger_poll(xadc->trigger, 0); + +	if (status & XADC_AXI_INT_ALARM_MASK) { +		/* +		 * The order of the bits in the AXI-XADC status register does +		 * not match the order of the bits in the XADC alarm enable +		 * register. xadc_handle_events() expects the events to be in +		 * the same order as the XADC alarm enable register. +		 */ +		events = (status & 0x000e) >> 1; +		events |= (status & 0x0001) << 3; +		events |= (status & 0x3c00) >> 6; +		xadc_handle_events(indio_dev, events); +	} + +	xadc_write_reg(xadc, XADC_AXI_REG_IPISR, status); + +	return IRQ_HANDLED; +} + +static void xadc_axi_update_alarm(struct xadc *xadc, unsigned int alarm) +{ +	uint32_t val; +	unsigned long flags; + +	/* +	 * The order of the bits in the AXI-XADC status register does not match +	 * the order of the bits in the XADC alarm enable register. We get +	 * passed the alarm mask in the same order as in the XADC alarm enable +	 * register. +	 */ +	alarm = ((alarm & 0x07) << 1) | ((alarm & 0x08) >> 3) | +			((alarm & 0xf0) << 6); + +	spin_lock_irqsave(&xadc->lock, flags); +	xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); +	val &= ~XADC_AXI_INT_ALARM_MASK; +	val |= alarm; +	xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); +	spin_unlock_irqrestore(&xadc->lock, flags); +} + +static unsigned long xadc_axi_get_dclk(struct xadc *xadc) +{ +	return clk_get_rate(xadc->clk); +} + +static const struct xadc_ops xadc_axi_ops = { +	.read = xadc_axi_read_adc_reg, +	.write = xadc_axi_write_adc_reg, +	.setup = xadc_axi_setup, +	.get_dclk_rate = xadc_axi_get_dclk, +	.update_alarm = xadc_axi_update_alarm, +	.interrupt_handler = xadc_axi_interrupt_handler, +	.flags = XADC_FLAGS_BUFFERED, +}; + +static int _xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t mask, uint16_t val) +{ +	uint16_t tmp; +	int ret; + +	ret = _xadc_read_adc_reg(xadc, reg, &tmp); +	if (ret) +		return ret; + +	return _xadc_write_adc_reg(xadc, reg, (tmp & ~mask) | val); +} + +static int xadc_update_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t mask, uint16_t val) +{ +	int ret; + +	mutex_lock(&xadc->mutex); +	ret = _xadc_update_adc_reg(xadc, reg, mask, val); +	mutex_unlock(&xadc->mutex); + +	return ret; +} + +static unsigned long xadc_get_dclk_rate(struct xadc *xadc) +{ +	return xadc->ops->get_dclk_rate(xadc); +} + +static int xadc_update_scan_mode(struct iio_dev *indio_dev, +	const unsigned long *mask) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned int n; + +	n = bitmap_weight(mask, indio_dev->masklength); + +	kfree(xadc->data); +	xadc->data = kcalloc(n, sizeof(*xadc->data), GFP_KERNEL); +	if (!xadc->data) +		return -ENOMEM; + +	return 0; +} + +static unsigned int xadc_scan_index_to_channel(unsigned int scan_index) +{ +	switch (scan_index) { +	case 5: +		return XADC_REG_VCCPINT; +	case 6: +		return XADC_REG_VCCPAUX; +	case 7: +		return XADC_REG_VCCO_DDR; +	case 8: +		return XADC_REG_TEMP; +	case 9: +		return XADC_REG_VCCINT; +	case 10: +		return XADC_REG_VCCAUX; +	case 11: +		return XADC_REG_VPVN; +	case 12: +		return XADC_REG_VREFP; +	case 13: +		return XADC_REG_VREFN; +	case 14: +		return XADC_REG_VCCBRAM; +	default: +		return XADC_REG_VAUX(scan_index - 16); +	} +} + +static irqreturn_t xadc_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned int chan; +	int i, j; + +	if (!xadc->data) +		goto out; + +	j = 0; +	for_each_set_bit(i, indio_dev->active_scan_mask, +		indio_dev->masklength) { +		chan = xadc_scan_index_to_channel(i); +		xadc_read_adc_reg(xadc, chan, &xadc->data[j]); +		j++; +	} + +	iio_push_to_buffers(indio_dev, xadc->data); + +out: +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +static int xadc_trigger_set_state(struct iio_trigger *trigger, bool state) +{ +	struct xadc *xadc = iio_trigger_get_drvdata(trigger); +	unsigned long flags; +	unsigned int convst; +	unsigned int val; +	int ret = 0; + +	mutex_lock(&xadc->mutex); + +	if (state) { +		/* Only one of the two triggers can be active at the a time. */ +		if (xadc->trigger != NULL) { +			ret = -EBUSY; +			goto err_out; +		} else { +			xadc->trigger = trigger; +			if (trigger == xadc->convst_trigger) +				convst = XADC_CONF0_EC; +			else +				convst = 0; +		} +		ret = _xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF0_EC, +					convst); +		if (ret) +			goto err_out; +	} else { +		xadc->trigger = NULL; +	} + +	spin_lock_irqsave(&xadc->lock, flags); +	xadc_read_reg(xadc, XADC_AXI_REG_IPIER, &val); +	xadc_write_reg(xadc, XADC_AXI_REG_IPISR, val & XADC_AXI_INT_EOS); +	if (state) +		val |= XADC_AXI_INT_EOS; +	else +		val &= ~XADC_AXI_INT_EOS; +	xadc_write_reg(xadc, XADC_AXI_REG_IPIER, val); +	spin_unlock_irqrestore(&xadc->lock, flags); + +err_out: +	mutex_unlock(&xadc->mutex); + +	return ret; +} + +static const struct iio_trigger_ops xadc_trigger_ops = { +	.owner = THIS_MODULE, +	.set_trigger_state = &xadc_trigger_set_state, +}; + +static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, +	const char *name) +{ +	struct iio_trigger *trig; +	int ret; + +	trig = iio_trigger_alloc("%s%d-%s", indio_dev->name, +				indio_dev->id, name); +	if (trig == NULL) +		return ERR_PTR(-ENOMEM); + +	trig->dev.parent = indio_dev->dev.parent; +	trig->ops = &xadc_trigger_ops; +	iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); + +	ret = iio_trigger_register(trig); +	if (ret) +		goto error_free_trig; + +	return trig; + +error_free_trig: +	iio_trigger_free(trig); +	return ERR_PTR(ret); +} + +static int xadc_power_adc_b(struct xadc *xadc, unsigned int seq_mode) +{ +	uint16_t val; + +	switch (seq_mode) { +	case XADC_CONF1_SEQ_SIMULTANEOUS: +	case XADC_CONF1_SEQ_INDEPENDENT: +		val = XADC_CONF2_PD_ADC_B; +		break; +	default: +		val = 0; +		break; +	} + +	return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_PD_MASK, +		val); +} + +static int xadc_get_seq_mode(struct xadc *xadc, unsigned long scan_mode) +{ +	unsigned int aux_scan_mode = scan_mode >> 16; + +	if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_DUAL) +		return XADC_CONF1_SEQ_SIMULTANEOUS; + +	if ((aux_scan_mode & 0xff00) == 0 || +		(aux_scan_mode & 0x00ff) == 0) +		return XADC_CONF1_SEQ_CONTINUOUS; + +	return XADC_CONF1_SEQ_SIMULTANEOUS; +} + +static int xadc_postdisable(struct iio_dev *indio_dev) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned long scan_mask; +	int ret; +	int i; + +	scan_mask = 1; /* Run calibration as part of the sequence */ +	for (i = 0; i < indio_dev->num_channels; i++) +		scan_mask |= BIT(indio_dev->channels[i].scan_index); + +	/* Enable all channels and calibration */ +	ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); +	if (ret) +		return ret; + +	ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); +	if (ret) +		return ret; + +	ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, +		XADC_CONF1_SEQ_CONTINUOUS); +	if (ret) +		return ret; + +	return xadc_power_adc_b(xadc, XADC_CONF1_SEQ_CONTINUOUS); +} + +static int xadc_preenable(struct iio_dev *indio_dev) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned long scan_mask; +	int seq_mode; +	int ret; + +	ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, +		XADC_CONF1_SEQ_DEFAULT); +	if (ret) +		goto err; + +	scan_mask = *indio_dev->active_scan_mask; +	seq_mode = xadc_get_seq_mode(xadc, scan_mask); + +	ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(0), scan_mask & 0xffff); +	if (ret) +		goto err; + +	ret = xadc_write_adc_reg(xadc, XADC_REG_SEQ(1), scan_mask >> 16); +	if (ret) +		goto err; + +	ret = xadc_power_adc_b(xadc, seq_mode); +	if (ret) +		goto err; + +	ret = xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_SEQ_MASK, +		seq_mode); +	if (ret) +		goto err; + +	return 0; +err: +	xadc_postdisable(indio_dev); +	return ret; +} + +static struct iio_buffer_setup_ops xadc_buffer_ops = { +	.preenable = &xadc_preenable, +	.postenable = &iio_triggered_buffer_postenable, +	.predisable = &iio_triggered_buffer_predisable, +	.postdisable = &xadc_postdisable, +}; + +static int xadc_read_raw(struct iio_dev *indio_dev, +	struct iio_chan_spec const *chan, int *val, int *val2, long info) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned int div; +	uint16_t val16; +	int ret; + +	switch (info) { +	case IIO_CHAN_INFO_RAW: +		if (iio_buffer_enabled(indio_dev)) +			return -EBUSY; +		ret = xadc_read_adc_reg(xadc, chan->address, &val16); +		if (ret < 0) +			return ret; + +		val16 >>= 4; +		if (chan->scan_type.sign == 'u') +			*val = val16; +		else +			*val = sign_extend32(val16, 11); + +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_VOLTAGE: +			/* V = (val * 3.0) / 4096 */ +			switch (chan->address) { +			case XADC_REG_VCCINT: +			case XADC_REG_VCCAUX: +			case XADC_REG_VCCBRAM: +			case XADC_REG_VCCPINT: +			case XADC_REG_VCCPAUX: +			case XADC_REG_VCCO_DDR: +				*val = 3000; +				break; +			default: +				*val = 1000; +				break; +			} +			*val2 = 12; +			return IIO_VAL_FRACTIONAL_LOG2; +		case IIO_TEMP: +			/* Temp in C = (val * 503.975) / 4096 - 273.15 */ +			*val = 503975; +			*val2 = 12; +			return IIO_VAL_FRACTIONAL_LOG2; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_OFFSET: +		/* Only the temperature channel has an offset */ +		*val = -((273150 << 12) / 503975); +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret = xadc_read_adc_reg(xadc, XADC_REG_CONF2, &val16); +		if (ret) +			return ret; + +		div = (val16 & XADC_CONF2_DIV_MASK) >> XADC_CONF2_DIV_OFFSET; +		if (div < 2) +			div = 2; + +		*val = xadc_get_dclk_rate(xadc) / div / 26; + +		return IIO_VAL_INT; +	default: +		return -EINVAL; +	} +} + +static int xadc_write_raw(struct iio_dev *indio_dev, +	struct iio_chan_spec const *chan, int val, int val2, long info) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	unsigned long clk_rate = xadc_get_dclk_rate(xadc); +	unsigned int div; + +	if (info != IIO_CHAN_INFO_SAMP_FREQ) +		return -EINVAL; + +	if (val <= 0) +		return -EINVAL; + +	/* Max. 150 kSPS */ +	if (val > 150000) +		val = 150000; + +	val *= 26; + +	/* Min 1MHz */ +	if (val < 1000000) +		val = 1000000; + +	/* +	 * We want to round down, but only if we do not exceed the 150 kSPS +	 * limit. +	 */ +	div = clk_rate / val; +	if (clk_rate / div / 26 > 150000) +		div++; +	if (div < 2) +		div = 2; +	else if (div > 0xff) +		div = 0xff; + +	return xadc_update_adc_reg(xadc, XADC_REG_CONF2, XADC_CONF2_DIV_MASK, +		div << XADC_CONF2_DIV_OFFSET); +} + +static const struct iio_event_spec xadc_temp_events[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) | +				BIT(IIO_EV_INFO_VALUE) | +				BIT(IIO_EV_INFO_HYSTERESIS), +	}, +}; + +/* Separate values for upper and lower thresholds, but only a shared enabled */ +static const struct iio_event_spec xadc_voltage_events[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE), +	}, { +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_EITHER, +		.mask_separate = BIT(IIO_EV_INFO_ENABLE), +	}, +}; + +#define XADC_CHAN_TEMP(_chan, _scan_index, _addr) { \ +	.type = IIO_TEMP, \ +	.indexed = 1, \ +	.channel = (_chan), \ +	.address = (_addr), \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ +		BIT(IIO_CHAN_INFO_SCALE) | \ +		BIT(IIO_CHAN_INFO_OFFSET), \ +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +	.event_spec = xadc_temp_events, \ +	.num_event_specs = ARRAY_SIZE(xadc_temp_events), \ +	.scan_index = (_scan_index), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 12, \ +		.storagebits = 16, \ +		.shift = 4, \ +		.endianness = IIO_CPU, \ +	}, \ +} + +#define XADC_CHAN_VOLTAGE(_chan, _scan_index, _addr, _ext, _alarm) { \ +	.type = IIO_VOLTAGE, \ +	.indexed = 1, \ +	.channel = (_chan), \ +	.address = (_addr), \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ +		BIT(IIO_CHAN_INFO_SCALE), \ +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +	.event_spec = (_alarm) ? xadc_voltage_events : NULL, \ +	.num_event_specs = (_alarm) ? ARRAY_SIZE(xadc_voltage_events) : 0, \ +	.scan_index = (_scan_index), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 12, \ +		.storagebits = 16, \ +		.shift = 4, \ +		.endianness = IIO_CPU, \ +	}, \ +	.extend_name = _ext, \ +} + +static const struct iio_chan_spec xadc_channels[] = { +	XADC_CHAN_TEMP(0, 8, XADC_REG_TEMP), +	XADC_CHAN_VOLTAGE(0, 9, XADC_REG_VCCINT, "vccint", true), +	XADC_CHAN_VOLTAGE(1, 10, XADC_REG_VCCINT, "vccaux", true), +	XADC_CHAN_VOLTAGE(2, 14, XADC_REG_VCCBRAM, "vccbram", true), +	XADC_CHAN_VOLTAGE(3, 5, XADC_REG_VCCPINT, "vccpint", true), +	XADC_CHAN_VOLTAGE(4, 6, XADC_REG_VCCPAUX, "vccpaux", true), +	XADC_CHAN_VOLTAGE(5, 7, XADC_REG_VCCO_DDR, "vccoddr", true), +	XADC_CHAN_VOLTAGE(6, 12, XADC_REG_VREFP, "vrefp", false), +	XADC_CHAN_VOLTAGE(7, 13, XADC_REG_VREFN, "vrefn", false), +	XADC_CHAN_VOLTAGE(8, 11, XADC_REG_VPVN, NULL, false), +	XADC_CHAN_VOLTAGE(9, 16, XADC_REG_VAUX(0), NULL, false), +	XADC_CHAN_VOLTAGE(10, 17, XADC_REG_VAUX(1), NULL, false), +	XADC_CHAN_VOLTAGE(11, 18, XADC_REG_VAUX(2), NULL, false), +	XADC_CHAN_VOLTAGE(12, 19, XADC_REG_VAUX(3), NULL, false), +	XADC_CHAN_VOLTAGE(13, 20, XADC_REG_VAUX(4), NULL, false), +	XADC_CHAN_VOLTAGE(14, 21, XADC_REG_VAUX(5), NULL, false), +	XADC_CHAN_VOLTAGE(15, 22, XADC_REG_VAUX(6), NULL, false), +	XADC_CHAN_VOLTAGE(16, 23, XADC_REG_VAUX(7), NULL, false), +	XADC_CHAN_VOLTAGE(17, 24, XADC_REG_VAUX(8), NULL, false), +	XADC_CHAN_VOLTAGE(18, 25, XADC_REG_VAUX(9), NULL, false), +	XADC_CHAN_VOLTAGE(19, 26, XADC_REG_VAUX(10), NULL, false), +	XADC_CHAN_VOLTAGE(20, 27, XADC_REG_VAUX(11), NULL, false), +	XADC_CHAN_VOLTAGE(21, 28, XADC_REG_VAUX(12), NULL, false), +	XADC_CHAN_VOLTAGE(22, 29, XADC_REG_VAUX(13), NULL, false), +	XADC_CHAN_VOLTAGE(23, 30, XADC_REG_VAUX(14), NULL, false), +	XADC_CHAN_VOLTAGE(24, 31, XADC_REG_VAUX(15), NULL, false), +}; + +static const struct iio_info xadc_info = { +	.read_raw = &xadc_read_raw, +	.write_raw = &xadc_write_raw, +	.read_event_config = &xadc_read_event_config, +	.write_event_config = &xadc_write_event_config, +	.read_event_value = &xadc_read_event_value, +	.write_event_value = &xadc_write_event_value, +	.update_scan_mode = &xadc_update_scan_mode, +	.driver_module = THIS_MODULE, +}; + +static const struct of_device_id xadc_of_match_table[] = { +	{ .compatible = "xlnx,zynq-xadc-1.00.a", (void *)&xadc_zynq_ops }, +	{ .compatible = "xlnx,axi-xadc-1.00.a", (void *)&xadc_axi_ops }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, xadc_of_match_table); + +static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, +	unsigned int *conf) +{ +	struct xadc *xadc = iio_priv(indio_dev); +	struct iio_chan_spec *channels, *chan; +	struct device_node *chan_node, *child; +	unsigned int num_channels; +	const char *external_mux; +	u32 ext_mux_chan; +	int reg; +	int ret; + +	*conf = 0; + +	ret = of_property_read_string(np, "xlnx,external-mux", &external_mux); +	if (ret < 0 || strcasecmp(external_mux, "none") == 0) +		xadc->external_mux_mode = XADC_EXTERNAL_MUX_NONE; +	else if (strcasecmp(external_mux, "single") == 0) +		xadc->external_mux_mode = XADC_EXTERNAL_MUX_SINGLE; +	else if (strcasecmp(external_mux, "dual") == 0) +		xadc->external_mux_mode = XADC_EXTERNAL_MUX_DUAL; +	else +		return -EINVAL; + +	if (xadc->external_mux_mode != XADC_EXTERNAL_MUX_NONE) { +		ret = of_property_read_u32(np, "xlnx,external-mux-channel", +					&ext_mux_chan); +		if (ret < 0) +			return ret; + +		if (xadc->external_mux_mode == XADC_EXTERNAL_MUX_SINGLE) { +			if (ext_mux_chan == 0) +				ext_mux_chan = XADC_REG_VPVN; +			else if (ext_mux_chan <= 16) +				ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); +			else +				return -EINVAL; +		} else { +			if (ext_mux_chan > 0 && ext_mux_chan <= 8) +				ext_mux_chan = XADC_REG_VAUX(ext_mux_chan - 1); +			else +				return -EINVAL; +		} + +		*conf |= XADC_CONF0_MUX | XADC_CONF0_CHAN(ext_mux_chan); +	} + +	channels = kmemdup(xadc_channels, sizeof(xadc_channels), GFP_KERNEL); +	if (!channels) +		return -ENOMEM; + +	num_channels = 9; +	chan = &channels[9]; + +	chan_node = of_get_child_by_name(np, "xlnx,channels"); +	if (chan_node) { +		for_each_child_of_node(chan_node, child) { +			if (num_channels >= ARRAY_SIZE(xadc_channels)) { +				of_node_put(child); +				break; +			} + +			ret = of_property_read_u32(child, "reg", ®); +			if (ret || reg > 16) +				continue; + +			if (of_property_read_bool(child, "xlnx,bipolar")) +				chan->scan_type.sign = 's'; + +			if (reg == 0) { +				chan->scan_index = 11; +				chan->address = XADC_REG_VPVN; +			} else { +				chan->scan_index = 15 + reg; +				chan->scan_index = XADC_REG_VAUX(reg - 1); +			} +			num_channels++; +			chan++; +		} +	} +	of_node_put(chan_node); + +	indio_dev->num_channels = num_channels; +	indio_dev->channels = krealloc(channels, sizeof(*channels) * +					num_channels, GFP_KERNEL); +	/* If we can't resize the channels array, just use the original */ +	if (!indio_dev->channels) +		indio_dev->channels = channels; + +	return 0; +} + +static int xadc_probe(struct platform_device *pdev) +{ +	const struct of_device_id *id; +	struct iio_dev *indio_dev; +	unsigned int bipolar_mask; +	struct resource *mem; +	unsigned int conf0; +	struct xadc *xadc; +	int ret; +	int irq; +	int i; + +	if (!pdev->dev.of_node) +		return -ENODEV; + +	id = of_match_node(xadc_of_match_table, pdev->dev.of_node); +	if (!id) +		return -EINVAL; + +	irq = platform_get_irq(pdev, 0); +	if (irq <= 0) +		return -ENXIO; + +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*xadc)); +	if (!indio_dev) +		return -ENOMEM; + +	xadc = iio_priv(indio_dev); +	xadc->ops = id->data; +	init_completion(&xadc->completion); +	mutex_init(&xadc->mutex); +	spin_lock_init(&xadc->lock); +	INIT_DELAYED_WORK(&xadc->zynq_unmask_work, xadc_zynq_unmask_worker); + +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	xadc->base = devm_ioremap_resource(&pdev->dev, mem); +	if (IS_ERR(xadc->base)) +		return PTR_ERR(xadc->base); + +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->dev.of_node = pdev->dev.of_node; +	indio_dev->name = "xadc"; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->info = &xadc_info; + +	ret = xadc_parse_dt(indio_dev, pdev->dev.of_node, &conf0); +	if (ret) +		goto err_device_free; + +	if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { +		ret = iio_triggered_buffer_setup(indio_dev, +			&iio_pollfunc_store_time, &xadc_trigger_handler, +			&xadc_buffer_ops); +		if (ret) +			goto err_device_free; + +		xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); +		if (IS_ERR(xadc->convst_trigger)) +			goto err_triggered_buffer_cleanup; +		xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, +			"samplerate"); +		if (IS_ERR(xadc->samplerate_trigger)) +			goto err_free_convst_trigger; +	} + +	xadc->clk = devm_clk_get(&pdev->dev, NULL); +	if (IS_ERR(xadc->clk)) { +		ret = PTR_ERR(xadc->clk); +		goto err_free_samplerate_trigger; +	} +	clk_prepare_enable(xadc->clk); + +	ret = xadc->ops->setup(pdev, indio_dev, irq); +	if (ret) +		goto err_free_samplerate_trigger; + +	ret = request_threaded_irq(irq, xadc->ops->interrupt_handler, +				xadc->ops->threaded_interrupt_handler, +				0, dev_name(&pdev->dev), indio_dev); +	if (ret) +		goto err_clk_disable_unprepare; + +	for (i = 0; i < 16; i++) +		xadc_read_adc_reg(xadc, XADC_REG_THRESHOLD(i), +			&xadc->threshold[i]); + +	ret = xadc_write_adc_reg(xadc, XADC_REG_CONF0, conf0); +	if (ret) +		goto err_free_irq; + +	bipolar_mask = 0; +	for (i = 0; i < indio_dev->num_channels; i++) { +		if (indio_dev->channels[i].scan_type.sign == 's') +			bipolar_mask |= BIT(indio_dev->channels[i].scan_index); +	} + +	ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(0), bipolar_mask); +	if (ret) +		goto err_free_irq; +	ret = xadc_write_adc_reg(xadc, XADC_REG_INPUT_MODE(1), +		bipolar_mask >> 16); +	if (ret) +		goto err_free_irq; + +	/* Disable all alarms */ +	xadc_update_adc_reg(xadc, XADC_REG_CONF1, XADC_CONF1_ALARM_MASK, +		XADC_CONF1_ALARM_MASK); + +	/* Set thresholds to min/max */ +	for (i = 0; i < 16; i++) { +		/* +		 * Set max voltage threshold and both temperature thresholds to +		 * 0xffff, min voltage threshold to 0. +		 */ +		if (i % 8 < 4 || i == 7) +			xadc->threshold[i] = 0xffff; +		else +			xadc->threshold[i] = 0; +		xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i), +			xadc->threshold[i]); +	} + +	/* Go to non-buffered mode */ +	xadc_postdisable(indio_dev); + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto err_free_irq; + +	platform_set_drvdata(pdev, indio_dev); + +	return 0; + +err_free_irq: +	free_irq(irq, indio_dev); +err_free_samplerate_trigger: +	if (xadc->ops->flags & XADC_FLAGS_BUFFERED) +		iio_trigger_free(xadc->samplerate_trigger); +err_free_convst_trigger: +	if (xadc->ops->flags & XADC_FLAGS_BUFFERED) +		iio_trigger_free(xadc->convst_trigger); +err_triggered_buffer_cleanup: +	if (xadc->ops->flags & XADC_FLAGS_BUFFERED) +		iio_triggered_buffer_cleanup(indio_dev); +err_clk_disable_unprepare: +	clk_disable_unprepare(xadc->clk); +err_device_free: +	kfree(indio_dev->channels); + +	return ret; +} + +static int xadc_remove(struct platform_device *pdev) +{ +	struct iio_dev *indio_dev = platform_get_drvdata(pdev); +	struct xadc *xadc = iio_priv(indio_dev); +	int irq = platform_get_irq(pdev, 0); + +	iio_device_unregister(indio_dev); +	if (xadc->ops->flags & XADC_FLAGS_BUFFERED) { +		iio_trigger_free(xadc->samplerate_trigger); +		iio_trigger_free(xadc->convst_trigger); +		iio_triggered_buffer_cleanup(indio_dev); +	} +	free_irq(irq, indio_dev); +	clk_disable_unprepare(xadc->clk); +	cancel_delayed_work(&xadc->zynq_unmask_work); +	kfree(xadc->data); +	kfree(indio_dev->channels); + +	return 0; +} + +static struct platform_driver xadc_driver = { +	.probe = xadc_probe, +	.remove = xadc_remove, +	.driver = { +		.name = "xadc", +		.owner = THIS_MODULE, +		.of_match_table = xadc_of_match_table, +	}, +}; +module_platform_driver(xadc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Xilinx XADC IIO driver"); diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c new file mode 100644 index 00000000000..3e7f0d7a80c --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc-events.c @@ -0,0 +1,254 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + *  Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/kernel.h> + +#include "xilinx-xadc.h" + +static const struct iio_chan_spec *xadc_event_to_channel( +	struct iio_dev *indio_dev, unsigned int event) +{ +	switch (event) { +	case XADC_THRESHOLD_OT_MAX: +	case XADC_THRESHOLD_TEMP_MAX: +		return &indio_dev->channels[0]; +	case XADC_THRESHOLD_VCCINT_MAX: +	case XADC_THRESHOLD_VCCAUX_MAX: +		return &indio_dev->channels[event]; +	default: +		return &indio_dev->channels[event-1]; +	} +} + +static void xadc_handle_event(struct iio_dev *indio_dev, unsigned int event) +{ +	const struct iio_chan_spec *chan; +	unsigned int offset; + +	/* Temperature threshold error, we don't handle this yet */ +	if (event == 0) +		return; + +	if (event < 4) +		offset = event; +	else +		offset = event + 4; + +	chan = xadc_event_to_channel(indio_dev, event); + +	if (chan->type == IIO_TEMP) { +		/* +		 * The temperature channel only supports over-temperature +		 * events. +		 */ +		iio_push_event(indio_dev, +			IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, +				IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +			iio_get_time_ns()); +	} else { +		/* +		 * For other channels we don't know whether it is a upper or +		 * lower threshold event. Userspace will have to check the +		 * channel value if it wants to know. +		 */ +		iio_push_event(indio_dev, +			IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, +				IIO_EV_TYPE_THRESH, IIO_EV_DIR_EITHER), +			iio_get_time_ns()); +	} +} + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ +	unsigned int i; + +	for_each_set_bit(i, &events, 8) +		xadc_handle_event(indio_dev, i); +} + +static unsigned xadc_get_threshold_offset(const struct iio_chan_spec *chan, +	enum iio_event_direction dir) +{ +	unsigned int offset; + +	if (chan->type == IIO_TEMP) { +		offset = XADC_THRESHOLD_OT_MAX; +	} else { +		if (chan->channel < 2) +			offset = chan->channel + 1; +		else +			offset = chan->channel + 6; +	} + +	if (dir == IIO_EV_DIR_FALLING) +		offset += 4; + +	return offset; +} + +static unsigned int xadc_get_alarm_mask(const struct iio_chan_spec *chan) +{ +	if (chan->type == IIO_TEMP) { +		return XADC_ALARM_OT_MASK; +	} else { +		switch (chan->channel) { +		case 0: +			return XADC_ALARM_VCCINT_MASK; +		case 1: +			return XADC_ALARM_VCCAUX_MASK; +		case 2: +			return XADC_ALARM_VCCBRAM_MASK; +		case 3: +			return XADC_ALARM_VCCPINT_MASK; +		case 4: +			return XADC_ALARM_VCCPAUX_MASK; +		case 5: +			return XADC_ALARM_VCCODDR_MASK; +		default: +			/* We will never get here */ +			return 0; +		} +	} +} + +int xadc_read_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir) +{ +	struct xadc *xadc = iio_priv(indio_dev); + +	return (bool)(xadc->alarm_mask & xadc_get_alarm_mask(chan)); +} + +int xadc_write_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state) +{ +	unsigned int alarm = xadc_get_alarm_mask(chan); +	struct xadc *xadc = iio_priv(indio_dev); +	uint16_t cfg, old_cfg; +	int ret; + +	mutex_lock(&xadc->mutex); + +	if (state) +		xadc->alarm_mask |= alarm; +	else +		xadc->alarm_mask &= ~alarm; + +	xadc->ops->update_alarm(xadc, xadc->alarm_mask); + +	ret = _xadc_read_adc_reg(xadc, XADC_REG_CONF1, &cfg); +	if (ret) +		goto err_out; + +	old_cfg = cfg; +	cfg |= XADC_CONF1_ALARM_MASK; +	cfg &= ~((xadc->alarm_mask & 0xf0) << 4); /* bram, pint, paux, ddr */ +	cfg &= ~((xadc->alarm_mask & 0x08) >> 3); /* ot */ +	cfg &= ~((xadc->alarm_mask & 0x07) << 1); /* temp, vccint, vccaux */ +	if (old_cfg != cfg) +		ret = _xadc_write_adc_reg(xadc, XADC_REG_CONF1, cfg); + +err_out: +	mutex_unlock(&xadc->mutex); + +	return ret; +} + +/* Register value is msb aligned, the lower 4 bits are ignored */ +#define XADC_THRESHOLD_VALUE_SHIFT 4 + +int xadc_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) +{ +	unsigned int offset = xadc_get_threshold_offset(chan, dir); +	struct xadc *xadc = iio_priv(indio_dev); + +	switch (info) { +	case IIO_EV_INFO_VALUE: +		*val = xadc->threshold[offset]; +		break; +	case IIO_EV_INFO_HYSTERESIS: +		*val = xadc->temp_hysteresis; +		break; +	default: +		return -EINVAL; +	} + +	*val >>= XADC_THRESHOLD_VALUE_SHIFT; + +	return IIO_VAL_INT; +} + +int xadc_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) +{ +	unsigned int offset = xadc_get_threshold_offset(chan, dir); +	struct xadc *xadc = iio_priv(indio_dev); +	int ret = 0; + +	val <<= XADC_THRESHOLD_VALUE_SHIFT; + +	if (val < 0 || val > 0xffff) +		return -EINVAL; + +	mutex_lock(&xadc->mutex); + +	switch (info) { +	case IIO_EV_INFO_VALUE: +		xadc->threshold[offset] = val; +		break; +	case IIO_EV_INFO_HYSTERESIS: +		xadc->temp_hysteresis = val; +		break; +	default: +		mutex_unlock(&xadc->mutex); +		return -EINVAL; +	} + +	if (chan->type == IIO_TEMP) { +		/* +		 * According to the datasheet we need to set the lower 4 bits to +		 * 0x3, otherwise 125 degree celsius will be used as the +		 * threshold. +		 */ +		val |= 0x3; + +		/* +		 * Since we store the hysteresis as relative (to the threshold) +		 * value, but the hardware expects an absolute value we need to +		 * recalcualte this value whenever the hysteresis or the +		 * threshold changes. +		 */ +		if (xadc->threshold[offset] < xadc->temp_hysteresis) +			xadc->threshold[offset + 4] = 0; +		else +			xadc->threshold[offset + 4] = xadc->threshold[offset] - +					xadc->temp_hysteresis; +		ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset + 4), +			xadc->threshold[offset + 4]); +		if (ret) +			goto out_unlock; +	} + +	if (info == IIO_EV_INFO_VALUE) +		ret = _xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(offset), val); + +out_unlock: +	mutex_unlock(&xadc->mutex); + +	return ret; +} diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h new file mode 100644 index 00000000000..c7487e8d7f8 --- /dev/null +++ b/drivers/iio/adc/xilinx-xadc.h @@ -0,0 +1,209 @@ +/* + * Xilinx XADC driver + * + * Copyright 2013 Analog Devices Inc. + *  Author: Lars-Peter Clauen <lars@metafoo.de> + * + * Licensed under the GPL-2. + */ + +#ifndef __IIO_XILINX_XADC__ +#define __IIO_XILINX_XADC__ + +#include <linux/interrupt.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> + +struct iio_dev; +struct clk; +struct xadc_ops; +struct platform_device; + +void xadc_handle_events(struct iio_dev *indio_dev, unsigned long events); + +int xadc_read_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir); +int xadc_write_event_config(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state); +int xadc_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 xadc_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); + +enum xadc_external_mux_mode { +	XADC_EXTERNAL_MUX_NONE, +	XADC_EXTERNAL_MUX_SINGLE, +	XADC_EXTERNAL_MUX_DUAL, +}; + +struct xadc { +	void __iomem *base; +	struct clk *clk; + +	const struct xadc_ops *ops; + +	uint16_t threshold[16]; +	uint16_t temp_hysteresis; +	unsigned int alarm_mask; + +	uint16_t *data; + +	struct iio_trigger *trigger; +	struct iio_trigger *convst_trigger; +	struct iio_trigger *samplerate_trigger; + +	enum xadc_external_mux_mode external_mux_mode; + +	unsigned int zynq_alarm; +	unsigned int zynq_masked_alarm; +	unsigned int zynq_intmask; +	struct delayed_work zynq_unmask_work; + +	struct mutex mutex; +	spinlock_t lock; + +	struct completion completion; +}; + +struct xadc_ops { +	int (*read)(struct xadc *, unsigned int, uint16_t *); +	int (*write)(struct xadc *, unsigned int, uint16_t); +	int (*setup)(struct platform_device *pdev, struct iio_dev *indio_dev, +			int irq); +	void (*update_alarm)(struct xadc *, unsigned int); +	unsigned long (*get_dclk_rate)(struct xadc *); +	irqreturn_t (*interrupt_handler)(int, void *); +	irqreturn_t (*threaded_interrupt_handler)(int, void *); + +	unsigned int flags; +}; + +static inline int _xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t *val) +{ +	lockdep_assert_held(&xadc->mutex); +	return xadc->ops->read(xadc, reg, val); +} + +static inline int _xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t val) +{ +	lockdep_assert_held(&xadc->mutex); +	return xadc->ops->write(xadc, reg, val); +} + +static inline int xadc_read_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t *val) +{ +	int ret; + +	mutex_lock(&xadc->mutex); +	ret = _xadc_read_adc_reg(xadc, reg, val); +	mutex_unlock(&xadc->mutex); +	return ret; +} + +static inline int xadc_write_adc_reg(struct xadc *xadc, unsigned int reg, +	uint16_t val) +{ +	int ret; + +	mutex_lock(&xadc->mutex); +	ret = _xadc_write_adc_reg(xadc, reg, val); +	mutex_unlock(&xadc->mutex); +	return ret; +} + +/* XADC hardmacro register definitions */ +#define XADC_REG_TEMP		0x00 +#define XADC_REG_VCCINT		0x01 +#define XADC_REG_VCCAUX		0x02 +#define XADC_REG_VPVN		0x03 +#define XADC_REG_VREFP		0x04 +#define XADC_REG_VREFN		0x05 +#define XADC_REG_VCCBRAM	0x06 + +#define XADC_REG_VCCPINT	0x0d +#define XADC_REG_VCCPAUX	0x0e +#define XADC_REG_VCCO_DDR	0x0f +#define XADC_REG_VAUX(x)	(0x10 + (x)) + +#define XADC_REG_MAX_TEMP	0x20 +#define XADC_REG_MAX_VCCINT	0x21 +#define XADC_REG_MAX_VCCAUX	0x22 +#define XADC_REG_MAX_VCCBRAM	0x23 +#define XADC_REG_MIN_TEMP	0x24 +#define XADC_REG_MIN_VCCINT	0x25 +#define XADC_REG_MIN_VCCAUX	0x26 +#define XADC_REG_MIN_VCCBRAM	0x27 +#define XADC_REG_MAX_VCCPINT	0x28 +#define XADC_REG_MAX_VCCPAUX	0x29 +#define XADC_REG_MAX_VCCO_DDR	0x2a +#define XADC_REG_MIN_VCCPINT	0x2b +#define XADC_REG_MIN_VCCPAUX	0x2c +#define XADC_REG_MIN_VCCO_DDR	0x2d + +#define XADC_REG_CONF0		0x40 +#define XADC_REG_CONF1		0x41 +#define XADC_REG_CONF2		0x42 +#define XADC_REG_SEQ(x)		(0x48 + (x)) +#define XADC_REG_INPUT_MODE(x)	(0x4c + (x)) +#define XADC_REG_THRESHOLD(x)	(0x50 + (x)) + +#define XADC_REG_FLAG		0x3f + +#define XADC_CONF0_EC			BIT(9) +#define XADC_CONF0_ACQ			BIT(8) +#define XADC_CONF0_MUX			BIT(11) +#define XADC_CONF0_CHAN(x)		(x) + +#define XADC_CONF1_SEQ_MASK		(0xf << 12) +#define XADC_CONF1_SEQ_DEFAULT		(0 << 12) +#define XADC_CONF1_SEQ_SINGLE_PASS	(1 << 12) +#define XADC_CONF1_SEQ_CONTINUOUS	(2 << 12) +#define XADC_CONF1_SEQ_SINGLE_CHANNEL	(3 << 12) +#define XADC_CONF1_SEQ_SIMULTANEOUS	(4 << 12) +#define XADC_CONF1_SEQ_INDEPENDENT	(8 << 12) +#define XADC_CONF1_ALARM_MASK		0x0f0f + +#define XADC_CONF2_DIV_MASK	0xff00 +#define XADC_CONF2_DIV_OFFSET	8 + +#define XADC_CONF2_PD_MASK	(0x3 << 4) +#define XADC_CONF2_PD_NONE	(0x0 << 4) +#define XADC_CONF2_PD_ADC_B	(0x2 << 4) +#define XADC_CONF2_PD_BOTH	(0x3 << 4) + +#define XADC_ALARM_TEMP_MASK		BIT(0) +#define XADC_ALARM_VCCINT_MASK		BIT(1) +#define XADC_ALARM_VCCAUX_MASK		BIT(2) +#define XADC_ALARM_OT_MASK		BIT(3) +#define XADC_ALARM_VCCBRAM_MASK		BIT(4) +#define XADC_ALARM_VCCPINT_MASK		BIT(5) +#define XADC_ALARM_VCCPAUX_MASK		BIT(6) +#define XADC_ALARM_VCCODDR_MASK		BIT(7) + +#define XADC_THRESHOLD_TEMP_MAX		0x0 +#define XADC_THRESHOLD_VCCINT_MAX	0x1 +#define XADC_THRESHOLD_VCCAUX_MAX	0x2 +#define XADC_THRESHOLD_OT_MAX		0x3 +#define XADC_THRESHOLD_TEMP_MIN		0x4 +#define XADC_THRESHOLD_VCCINT_MIN	0x5 +#define XADC_THRESHOLD_VCCAUX_MIN	0x6 +#define XADC_THRESHOLD_OT_MIN		0x7 +#define XADC_THRESHOLD_VCCBRAM_MAX	0x8 +#define XADC_THRESHOLD_VCCPINT_MAX	0x9 +#define XADC_THRESHOLD_VCCPAUX_MAX	0xa +#define XADC_THRESHOLD_VCCODDR_MAX	0xb +#define XADC_THRESHOLD_VCCBRAM_MIN	0xc +#define XADC_THRESHOLD_VCCPINT_MIN	0xd +#define XADC_THRESHOLD_VCCPAUX_MIN	0xe +#define XADC_THRESHOLD_VCCODDR_MIN	0xf + +#endif diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c index d0a79a4bce1..ba6f6a91dff 100644 --- a/drivers/iio/amplifiers/ad8366.c +++ b/drivers/iio/amplifiers/ad8366.c @@ -185,10 +185,8 @@ static int ad8366_remove(struct spi_device *spi)  	iio_device_unregister(indio_dev); -	if (!IS_ERR(reg)) { +	if (!IS_ERR(reg))  		regulator_disable(reg); -		regulator_put(reg); -	}  	return 0;  } diff --git a/drivers/iio/buffer_cb.c b/drivers/iio/buffer_cb.c index 415f3c6efd7..eb46e728aa2 100644 --- a/drivers/iio/buffer_cb.c +++ b/drivers/iio/buffer_cb.c @@ -7,26 +7,36 @@  struct iio_cb_buffer {  	struct iio_buffer buffer; -	int (*cb)(u8 *data, void *private); +	int (*cb)(const void *data, void *private);  	void *private;  	struct iio_channel *channels;  }; -static int iio_buffer_cb_store_to(struct iio_buffer *buffer, u8 *data) +static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)  { -	struct iio_cb_buffer *cb_buff = container_of(buffer, -						     struct iio_cb_buffer, -						     buffer); +	return container_of(buffer, struct iio_cb_buffer, buffer); +} +static int iio_buffer_cb_store_to(struct iio_buffer *buffer, const void *data) +{ +	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);  	return cb_buff->cb(data, cb_buff->private);  } -static struct iio_buffer_access_funcs iio_cb_access = { +static void iio_buffer_cb_release(struct iio_buffer *buffer) +{ +	struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer); +	kfree(cb_buff->buffer.scan_mask); +	kfree(cb_buff); +} + +static const struct iio_buffer_access_funcs iio_cb_access = {  	.store_to = &iio_buffer_cb_store_to, +	.release = &iio_buffer_cb_release,  };  struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev, -					     int (*cb)(u8 *data, +					     int (*cb)(const void *data,  						       void *private),  					     void *private)  { @@ -36,10 +46,8 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,  	struct iio_channel *chan;  	cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL); -	if (cb_buff == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} +	if (cb_buff == NULL) +		return ERR_PTR(-ENOMEM);  	iio_buffer_init(&cb_buff->buffer); @@ -81,7 +89,6 @@ error_release_channels:  	iio_channel_release_all(cb_buff->channels);  error_free_cb_buff:  	kfree(cb_buff); -error_ret:  	return ERR_PTR(ret);  }  EXPORT_SYMBOL_GPL(iio_channel_get_all_cb); @@ -104,9 +111,8 @@ EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);  void iio_channel_release_all_cb(struct iio_cb_buffer *cb_buff)  { -	kfree(cb_buff->buffer.scan_mask);  	iio_channel_release_all(cb_buff->channels); -	kfree(cb_buff); +	iio_buffer_put(&cb_buff->buffer);  }  EXPORT_SYMBOL_GPL(iio_channel_release_all_cb); diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig index 1178121b55b..39188b72cd3 100644 --- a/drivers/iio/common/hid-sensors/Kconfig +++ b/drivers/iio/common/hid-sensors/Kconfig @@ -25,13 +25,4 @@ config HID_SENSOR_IIO_TRIGGER  	  If this driver is compiled as a module, it will be named  	  hid-sensor-trigger. -config HID_SENSOR_ENUM_BASE_QUIRKS -	bool "ENUM base quirks for HID Sensor IIO drivers" -	depends on HID_SENSOR_IIO_COMMON -	help -	  Say yes here to build support for sensor hub FW using -	  enumeration, which is using 1 as base instead of 0. -	  Since logical minimum is still set 0 instead of 1, -	  there is no easy way to differentiate. -  endmenu 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 87419c41b99..a3109a6f4d8 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -28,32 +28,70 @@  #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 = state ? 1 : 0; -	if (IS_ENABLED(CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS)) -		++state_val; -	st->data_ready = state; -	sensor_hub_set_feature(st->hsdev, st->power_state.report_id, +		state_val = hid_sensor_get_usage_index(st->hsdev, +			st->power_state.report_id, +			st->power_state.index, +			HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM); +		report_val = hid_sensor_get_usage_index(st->hsdev, +			st->report_state.report_id, +			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, +			st->power_state.index, +			HID_USAGE_SENSOR_PROP_POWER_STATE_D4_POWER_OFF_ENUM); +		report_val = hid_sensor_get_usage_index(st->hsdev, +			st->report_state.report_id, +			st->report_state.index, +			HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM); +	} + +	if (state_val >= 0) { +		state_val += st->power_state.logical_minimum; +		sensor_hub_set_feature(st->hsdev, st->power_state.report_id,  					st->power_state.index,  					(s32)state_val); +	} -	sensor_hub_set_feature(st->hsdev, st->report_state.report_id, +	if (report_val >= 0) { +		report_val += st->report_state.logical_minimum; +		sensor_hub_set_feature(st->hsdev, st->report_state.report_id,  					st->report_state.index, -					(s32)state_val); +					(s32)report_val); +	} +	sensor_hub_get_feature(st->hsdev, st->power_state.report_id, +					st->power_state.index, +					&state_val);  	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 iio_dev *indio_dev) +void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)  { -	iio_trigger_unregister(indio_dev->trig); -	iio_trigger_free(indio_dev->trig); -	indio_dev->trig = NULL; +	iio_trigger_unregister(attrb->trigger); +	iio_trigger_free(attrb->trigger);  }  EXPORT_SYMBOL(hid_sensor_remove_trigger); @@ -84,7 +122,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,  		dev_err(&indio_dev->dev, "Trigger Register Failed\n");  		goto error_free_trig;  	} -	indio_dev->trig = trig; +	indio_dev->trig = attrb->trigger = trig;  	return ret; diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index 9a8731478ed..0f8e78c249d 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -21,6 +21,7 @@  int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,  				struct hid_sensor_common *attrb); -void hid_sensor_remove_trigger(struct iio_dev *indio_dev); +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_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index 71a2c5f63b9..1665c8e4b62 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -113,11 +113,8 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p)  	if (len < 0)  		goto st_sensors_get_buffer_element_error; -	if (indio_dev->scan_timestamp) -		*(s64 *)((u8 *)sdata->buffer_data + -				ALIGN(len, sizeof(s64))) = pf->timestamp; - -	iio_push_to_buffers(indio_dev, sdata->buffer_data); +	iio_push_to_buffers_with_timestamp(indio_dev, sdata->buffer_data, +		pf->timestamp);  st_sensors_get_buffer_element_error:  	iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 965ee22d3ac..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,21 +199,53 @@ int st_sensors_set_axis_enable(struct iio_dev *indio_dev, u8 axis_enable)  }  EXPORT_SYMBOL(st_sensors_set_axis_enable); -int st_sensors_init_sensor(struct iio_dev *indio_dev, -					struct st_sensors_platform_data *pdata) +void st_sensors_power_enable(struct iio_dev *indio_dev)  { +	struct st_sensor_data *pdata = iio_priv(indio_dev);  	int err; -	struct st_sensor_data *sdata = iio_priv(indio_dev); -	mutex_init(&sdata->tb.buf_lock); +	/* 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) +{ +	struct st_sensor_data *sdata = iio_priv(indio_dev);  	switch (pdata->drdy_int_pin) {  	case 1:  		if (sdata->sensor->drdy_irq.mask_int1 == 0) {  			dev_err(&indio_dev->dev,  					"DRDY on INT1 not available.\n"); -			err = -EINVAL; -			goto init_error; +			return -EINVAL;  		}  		sdata->drdy_int_pin = 1;  		break; @@ -220,39 +253,53 @@ int st_sensors_init_sensor(struct iio_dev *indio_dev,  		if (sdata->sensor->drdy_irq.mask_int2 == 0) {  			dev_err(&indio_dev->dev,  					"DRDY on INT2 not available.\n"); -			err = -EINVAL; -			goto init_error; +			return -EINVAL;  		}  		sdata->drdy_int_pin = 2;  		break;  	default:  		dev_err(&indio_dev->dev, "DRDY on pdata not valid.\n"); -		err = -EINVAL; -		goto init_error; +		return -EINVAL;  	} +	return 0; +} + +int st_sensors_init_sensor(struct iio_dev *indio_dev, +					struct st_sensors_platform_data *pdata) +{ +	struct st_sensor_data *sdata = iio_priv(indio_dev); +	int err = 0; + +	mutex_init(&sdata->tb.buf_lock); + +	if (pdata) +		err = st_sensors_set_drdy_int_pin(indio_dev, pdata); +  	err = st_sensors_set_enable(indio_dev, false);  	if (err < 0) -		goto init_error; +		return err; -	err = st_sensors_set_fullscale(indio_dev, -						sdata->current_fullscale->num); -	if (err < 0) -		goto init_error; +	if (sdata->current_fullscale) { +		err = st_sensors_set_fullscale(indio_dev, +					       sdata->current_fullscale->num); +		if (err < 0) +			return err; +	} else +		dev_info(&indio_dev->dev, "Full-scale not possible\n");  	err = st_sensors_set_odr(indio_dev, sdata->odr);  	if (err < 0) -		goto init_error; +		return err;  	/* set BDU */  	err = st_sensors_write_data_with_mask(indio_dev,  			sdata->sensor->bdu.addr, sdata->sensor->bdu.mask, true);  	if (err < 0) -		goto init_error; +		return err;  	err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS); -init_error:  	return err;  }  EXPORT_SYMBOL(st_sensors_init_sensor); @@ -263,6 +310,9 @@ int st_sensors_set_dataready_irq(struct iio_dev *indio_dev, bool enable)  	u8 drdy_mask;  	struct st_sensor_data *sdata = iio_priv(indio_dev); +	if (!sdata->sensor->drdy_irq.addr) +		return 0; +  	/* Enable/Disable the interrupt generator 1. */  	if (sdata->sensor->drdy_irq.ig1.en_addr > 0) {  		err = st_sensors_write_data_with_mask(indio_dev, @@ -318,10 +368,8 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,  	unsigned int byte_for_channel = ch->scan_type.storagebits >> 3;  	outdata = kmalloc(byte_for_channel, GFP_KERNEL); -	if (!outdata) { -		err = -EINVAL; -		goto st_sensors_read_axis_data_error; -	} +	if (!outdata) +		return -ENOMEM;  	err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,  				ch->address, byte_for_channel, @@ -336,7 +384,7 @@ static int st_sensors_read_axis_data(struct iio_dev *indio_dev,  st_sensors_free_memory:  	kfree(outdata); -st_sensors_read_axis_data_error: +  	return err;  } @@ -349,28 +397,25 @@ int st_sensors_read_info_raw(struct iio_dev *indio_dev,  	mutex_lock(&indio_dev->mlock);  	if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {  		err = -EBUSY; -		goto read_error; +		goto out;  	} else {  		err = st_sensors_set_enable(indio_dev, true);  		if (err < 0) -			goto read_error; +			goto out;  		msleep((sdata->sensor->bootime * 1000) / sdata->odr);  		err = st_sensors_read_axis_data(indio_dev, ch, val);  		if (err < 0) -			goto read_error; +			goto out;  		*val = *val >> ch->scan_type.shift;  		err = st_sensors_set_enable(indio_dev, false);  	} +out:  	mutex_unlock(&indio_dev->mlock);  	return err; - -read_error: -	mutex_unlock(&indio_dev->mlock); -	return err;  }  EXPORT_SYMBOL(st_sensors_read_info_raw); diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 3c6a78a75b7..f378ca8033d 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -57,7 +57,7 @@ config AD5446  	  Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,  	  AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,  	  AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612, -	  AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs. +	  AD5620, AD5621, AD5622, AD5640, AD5641, AD5660, AD5662 DACs.  	  To compile this driver as a module, choose M here: the  	  module will be called ad5446. diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index a3a52be4852..f03b92fd380 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -239,10 +239,9 @@ static int ad5064_read_raw(struct iio_dev *indio_dev,  		if (scale_uv < 0)  			return scale_uv; -		scale_uv = (scale_uv * 100) >> chan->scan_type.realbits; -		*val =  scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = scale_uv / 1000; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	default:  		break;  	} @@ -285,8 +284,9 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {  		.name = "powerdown",  		.read = ad5064_read_dac_powerdown,  		.write = ad5064_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", false, &ad5064_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum),  	{ },  }; @@ -299,7 +299,12 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = {  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\  	BIT(IIO_CHAN_INFO_SCALE),					\  	.address = addr,					\ -	.scan_type = IIO_ST('u', (bits), 16, 20 - (bits)),	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (bits),				\ +		.storagebits = 16,				\ +		.shift = 20 - bits,				\ +	},							\  	.ext_info = ad5064_ext_info,				\  } diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index d2da71ece74..64634d7f578 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -107,7 +107,12 @@ enum ad5360_type {  		BIT(IIO_CHAN_INFO_OFFSET) |				\  		BIT(IIO_CHAN_INFO_CALIBSCALE) |			\  		BIT(IIO_CHAN_INFO_CALIBBIAS),			\ -	.scan_type = IIO_ST('u', (bits), 16, 16 - (bits))	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (bits),				\ +		.storagebits = 16,				\ +		.shift = 16 - (bits),				\ +	},							\  }  static const struct ad5360_chip_info ad5360_chip_info_tbl[] = { @@ -379,15 +384,14 @@ static int ad5360_read_raw(struct iio_dev *indio_dev,  		*val = ret >> chan->scan_type.shift;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		/* vout = 4 * vref * dac_code */ -		scale_uv = ad5360_get_channel_vref(st, chan->channel) * 4 * 100; +		scale_uv = ad5360_get_channel_vref(st, chan->channel);  		if (scale_uv < 0)  			return scale_uv; -		scale_uv >>= (chan->scan_type.realbits); -		*val =  scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; +		/* vout = 4 * vref * dac_code */ +		*val = scale_uv * 4 / 1000; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	case IIO_CHAN_INFO_CALIBBIAS:  		ret = ad5360_read(indio_dev, AD5360_READBACK_OFFSET,  			chan->address); diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 1c44ae3920e..9de4c4d3828 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -204,7 +204,6 @@ static int ad5380_read_raw(struct iio_dev *indio_dev,  	struct iio_chan_spec const *chan, int *val, int *val2, long info)  {  	struct ad5380_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	int ret;  	switch (info) { @@ -225,10 +224,9 @@ static int ad5380_read_raw(struct iio_dev *indio_dev,  		val -= (1 << chan->scan_type.realbits) / 2;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = ((2 * st->vref) >> chan->scan_type.realbits) * 100; -		*val =  scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = 2 * st->vref; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	default:  		break;  	} @@ -247,8 +245,10 @@ static struct iio_chan_spec_ext_info ad5380_ext_info[] = {  		.name = "powerdown",  		.read = ad5380_read_dac_powerdown,  		.write = ad5380_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", true, &ad5380_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, +		 &ad5380_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum),  	{ },  }; @@ -261,7 +261,12 @@ static struct iio_chan_spec_ext_info ad5380_ext_info[] = {  		BIT(IIO_CHAN_INFO_CALIBSCALE) |			\  		BIT(IIO_CHAN_INFO_CALIBBIAS),			\  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\ -	.scan_type = IIO_ST('u', (_bits), 16, 14 - (_bits)),	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (_bits),				\ +		.storagebits =  16,				\ +		.shift = 14 - (_bits),				\ +	},							\  	.ext_info = ad5380_ext_info,				\  } @@ -269,72 +274,72 @@ static const struct ad5380_chip_info ad5380_chip_info_tbl[] = {  	[ID_AD5380_3] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 40, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5380_5] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 40, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5381_3] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 16, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5381_5] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 16, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5382_3] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 32, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5382_5] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 32, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5383_3] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 32, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5383_5] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 32, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5390_3] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 16, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5390_5] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 16, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5391_3] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 16, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5391_5] = {  		.channel_template = AD5380_CHANNEL(12),  		.num_channels = 16, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  	[ID_AD5392_3] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 8, -		.int_vref = 1250000, +		.int_vref = 1250,  	},  	[ID_AD5392_5] = {  		.channel_template = AD5380_CHANNEL(14),  		.num_channels = 8, -		.int_vref = 2500000, +		.int_vref = 2500,  	},  }; @@ -393,7 +398,7 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,  		return ret;  	} -	if (st->chip_info->int_vref == 2500000) +	if (st->chip_info->int_vref == 2500)  		ctrl |= AD5380_CTRL_INT_VREF_2V5;  	st->vref_reg = devm_regulator_get(dev, "vref"); @@ -409,7 +414,7 @@ static int ad5380_probe(struct device *dev, struct regmap *regmap,  		if (ret < 0)  			goto error_disable_reg; -		st->vref = ret; +		st->vref = ret / 1000;  	} else {  		st->vref = st->chip_info->int_vref;  		ctrl |= AD5380_CTRL_INT_VREF_EN; diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c index 1f78b14abb7..787ef1d859c 100644 --- a/drivers/iio/dac/ad5421.c +++ b/drivers/iio/dac/ad5421.c @@ -75,11 +75,34 @@ struct ad5421_state {  	 * transfer buffers to live in their own cache lines.  	 */  	union { -		u32 d32; +		__be32 d32;  		u8 d8[4];  	} data[2] ____cacheline_aligned;  }; +static const struct iio_event_spec ad5421_current_event[] = { +	{ +		.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), +	}, +}; + +static const struct iio_event_spec ad5421_temp_event[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, +}; +  static const struct iio_chan_spec ad5421_channels[] = {  	{  		.type = IIO_CURRENT, @@ -91,14 +114,19 @@ static const struct iio_chan_spec ad5421_channels[] = {  			BIT(IIO_CHAN_INFO_CALIBBIAS),  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |  			BIT(IIO_CHAN_INFO_OFFSET), -		.scan_type = IIO_ST('u', 16, 16, 0), -		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | -			IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING), +		.scan_type = { +			.sign = 'u', +			.realbits = 16, +			.storagebits = 16, +		}, +		.event_spec = ad5421_current_event, +		.num_event_specs = ARRAY_SIZE(ad5421_current_event),  	},  	{  		.type = IIO_TEMP,  		.channel = -1, -		.event_mask = IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING), +		.event_spec = ad5421_temp_event, +		.num_event_specs = ARRAY_SIZE(ad5421_temp_event),  	},  }; @@ -281,18 +309,11 @@ static inline unsigned int ad5421_get_offset(struct ad5421_state *st)  	return (min * (1 << 16)) / (max - min);  } -static inline unsigned int ad5421_get_scale(struct ad5421_state *st) -{ -	unsigned int min, max; - -	ad5421_get_current_min_max(st, &min, &max); -	return ((max - min) * 1000) / (1 << 16); -} -  static int ad5421_read_raw(struct iio_dev *indio_dev,  	struct iio_chan_spec const *chan, int *val, int *val2, long m)  {  	struct ad5421_state *st = iio_priv(indio_dev); +	unsigned int min, max;  	int ret;  	if (chan->type != IIO_CURRENT) @@ -306,9 +327,10 @@ static int ad5421_read_raw(struct iio_dev *indio_dev,  		*val = ret;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		*val = 0; -		*val2 = ad5421_get_scale(st); -		return IIO_VAL_INT_PLUS_MICRO; +		ad5421_get_current_min_max(st, &min, &max); +		*val = max - min; +		*val2 = (1 << 16) * 1000; +		return IIO_VAL_FRACTIONAL;  	case IIO_CHAN_INFO_OFFSET:  		*val = ad5421_get_offset(st);  		return IIO_VAL_INT; @@ -359,15 +381,15 @@ static int ad5421_write_raw(struct iio_dev *indio_dev,  }  static int ad5421_write_event_config(struct iio_dev *indio_dev, -	u64 event_code, int state) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state)  {  	struct ad5421_state *st = iio_priv(indio_dev);  	unsigned int mask; -	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { +	switch (chan->type) {  	case IIO_CURRENT: -		if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == -			IIO_EV_DIR_RISING) +		if (dir == IIO_EV_DIR_RISING)  			mask = AD5421_FAULT_OVER_CURRENT;  		else  			mask = AD5421_FAULT_UNDER_CURRENT; @@ -390,15 +412,15 @@ static int ad5421_write_event_config(struct iio_dev *indio_dev,  }  static int ad5421_read_event_config(struct iio_dev *indio_dev, -	u64 event_code) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir)  {  	struct ad5421_state *st = iio_priv(indio_dev);  	unsigned int mask; -	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { +	switch (chan->type) {  	case IIO_CURRENT: -		if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == -			IIO_EV_DIR_RISING) +		if (dir == IIO_EV_DIR_RISING)  			mask = AD5421_FAULT_OVER_CURRENT;  		else  			mask = AD5421_FAULT_UNDER_CURRENT; @@ -413,12 +435,14 @@ static int ad5421_read_event_config(struct iio_dev *indio_dev,  	return (bool)(st->fault_mask & mask);  } -static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code, -	int *val) +static int ad5421_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; -	switch (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code)) { +	switch (chan->type) {  	case IIO_CURRENT:  		ret = ad5421_read(indio_dev, AD5421_REG_DAC_DATA);  		if (ret < 0) @@ -432,7 +456,7 @@ static int ad5421_read_event_value(struct iio_dev *indio_dev, u64 event_code,  		return -EINVAL;  	} -	return 0; +	return IIO_VAL_INT;  }  static const struct iio_info ad5421_info = { @@ -494,22 +518,7 @@ static int ad5421_probe(struct spi_device *spi)  			return ret;  	} -	ret = iio_device_register(indio_dev); -	if (ret) { -		dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); -		return ret; -	} - -	return 0; -} - -static int ad5421_remove(struct spi_device *spi) -{ -	struct iio_dev *indio_dev = spi_get_drvdata(spi); - -	iio_device_unregister(indio_dev); - -	return 0; +	return devm_iio_device_register(&spi->dev, indio_dev);  }  static struct spi_driver ad5421_driver = { @@ -518,7 +527,6 @@ static struct spi_driver ad5421_driver = {  		   .owner = THIS_MODULE,  	},  	.probe = ad5421_probe, -	.remove = ad5421_remove,  };  module_spi_driver(ad5421_driver); diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 96e9ed4c2d0..46bb62a5c1d 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -132,20 +132,26 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {  		.name = "powerdown",  		.read = ad5446_read_dac_powerdown,  		.write = ad5446_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", false, &ad5446_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum),  	{ },  }; -#define _AD5446_CHANNEL(bits, storage, shift, ext) { \ +#define _AD5446_CHANNEL(bits, storage, _shift, ext) { \  	.type = IIO_VOLTAGE, \  	.indexed = 1, \  	.output = 1, \  	.channel = 0, \  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ -	.scan_type = IIO_ST('u', (bits), (storage), (shift)), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = (bits), \ +		.storagebits = (storage), \ +		.shift = (_shift), \ +		}, \  	.ext_info = (ext), \  } @@ -162,18 +168,15 @@ static int ad5446_read_raw(struct iio_dev *indio_dev,  			   long m)  {  	struct ad5446_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	switch (m) {  	case IIO_CHAN_INFO_RAW:  		*val = st->cached_val;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; -		*val =  scale_uv / 1000; -		*val2 = (scale_uv % 1000) * 1000; -		return IIO_VAL_INT_PLUS_MICRO; - +		*val = st->vref_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } @@ -329,6 +332,7 @@ enum ad5446_supported_spi_device_ids {  	ID_AD5601,  	ID_AD5611,  	ID_AD5621, +	ID_AD5641,  	ID_AD5620_2500,  	ID_AD5620_1250,  	ID_AD5640_2500, @@ -391,6 +395,10 @@ static const struct ad5446_chip_info ad5446_spi_chip_info[] = {  		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),  		.write = ad5446_write,  	}, +	[ID_AD5641] = { +		.channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0), +		.write = ad5446_write, +	},  	[ID_AD5620_2500] = {  		.channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),  		.int_vref_mv = 2500, @@ -445,6 +453,7 @@ static const struct spi_device_id ad5446_spi_ids[] = {  	{"ad5601", ID_AD5601},  	{"ad5611", ID_AD5611},  	{"ad5621", ID_AD5621}, +	{"ad5641", ID_AD5641},  	{"ad5620-2500", ID_AD5620_2500}, /* AD5620/40/60: */  	{"ad5620-1250", ID_AD5620_1250}, /* part numbers may look differently */  	{"ad5640-2500", ID_AD5640_2500}, diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c index fff7d0762c0..64d7256cbb6 100644 --- a/drivers/iio/dac/ad5449.c +++ b/drivers/iio/dac/ad5449.c @@ -101,7 +101,6 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,  {  	struct ad5449 *st = iio_priv(indio_dev);  	int ret; -	struct spi_message msg;  	struct spi_transfer t[] = {  		{  			.tx_buf = &st->data[0], @@ -114,15 +113,11 @@ static int ad5449_read(struct iio_dev *indio_dev, unsigned int addr,  		},  	}; -	spi_message_init(&msg); -	spi_message_add_tail(&t[0], &msg); -	spi_message_add_tail(&t[1], &msg); -  	mutex_lock(&indio_dev->mlock);  	st->data[0] = cpu_to_be16(addr << 12);  	st->data[1] = cpu_to_be16(AD5449_CMD_NOOP); -	ret = spi_sync(st->spi, &msg); +	ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));  	if (ret < 0)  		goto out_unlock; @@ -209,7 +204,12 @@ static const struct iio_info ad5449_info = {  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |		\  		BIT(IIO_CHAN_INFO_SCALE),			\  	.address = (chan),					\ -	.scan_type = IIO_ST('u', (bits), 16, 12 - (bits)),	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (bits),				\ +		.storagebits = 16,				\ +		.shift = 12 - (bits),				\ +	},							\  }  #define DECLARE_AD5449_CHANNELS(name, bits) \ diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index caffb16bc05..1e6449346b5 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -47,14 +47,16 @@   * @vref_mv:		actual reference voltage used   * @pwr_down_mask	power down mask   * @pwr_down_mode	current power down mode + * @data:		transfer buffer   */ -  struct ad5504_state {  	struct spi_device		*spi;  	struct regulator		*reg;  	unsigned short			vref_mv;  	unsigned			pwr_down_mask;  	unsigned			pwr_down_mode; + +	__be16				data[2] ____cacheline_aligned;  };  /** @@ -66,31 +68,29 @@ enum ad5504_supported_device_ids {  	ID_AD5501,  }; -static int ad5504_spi_write(struct spi_device *spi, u8 addr, u16 val) +static int ad5504_spi_write(struct ad5504_state *st, u8 addr, u16 val)  { -	u16 tmp = cpu_to_be16(AD5504_CMD_WRITE | -			      AD5504_ADDR(addr) | +	st->data[0] = cpu_to_be16(AD5504_CMD_WRITE | AD5504_ADDR(addr) |  			      (val & AD5504_RES_MASK)); -	return spi_write(spi, (u8 *)&tmp, 2); +	return spi_write(st->spi, &st->data[0], 2);  } -static int ad5504_spi_read(struct spi_device *spi, u8 addr) +static int ad5504_spi_read(struct ad5504_state *st, u8 addr)  { -	u16 tmp = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); -	u16 val;  	int ret; -	struct spi_transfer	t = { -			.tx_buf		= &tmp, -			.rx_buf		= &val, -			.len		= 2, -		}; -	ret = spi_sync_transfer(spi, &t, 1); - +	struct spi_transfer t = { +	    .tx_buf = &st->data[0], +	    .rx_buf = &st->data[1], +	    .len = 2, +	}; + +	st->data[0] = cpu_to_be16(AD5504_CMD_READ | AD5504_ADDR(addr)); +	ret = spi_sync_transfer(st->spi, &t, 1);  	if (ret < 0)  		return ret; -	return be16_to_cpu(val) & AD5504_RES_MASK; +	return be16_to_cpu(st->data[1]) & AD5504_RES_MASK;  }  static int ad5504_read_raw(struct iio_dev *indio_dev, @@ -100,12 +100,11 @@ static int ad5504_read_raw(struct iio_dev *indio_dev,  			   long m)  {  	struct ad5504_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	int ret;  	switch (m) {  	case IIO_CHAN_INFO_RAW: -		ret = ad5504_spi_read(st->spi, chan->address); +		ret = ad5504_spi_read(st, chan->address);  		if (ret < 0)  			return ret; @@ -113,11 +112,9 @@ static int ad5504_read_raw(struct iio_dev *indio_dev,  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; -		*val =  scale_uv / 1000; -		*val2 = (scale_uv % 1000) * 1000; -		return IIO_VAL_INT_PLUS_MICRO; - +		*val = st->vref_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } @@ -136,7 +133,7 @@ static int ad5504_write_raw(struct iio_dev *indio_dev,  		if (val >= (1 << chan->scan_type.realbits) || val < 0)  			return -EINVAL; -		return ad5504_spi_write(st->spi, chan->address, val); +		return ad5504_spi_write(st, chan->address, val);  	default:  		ret = -EINVAL;  	} @@ -200,12 +197,12 @@ static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev,  	else  		st->pwr_down_mask &= ~(1 << chan->channel); -	ret = ad5504_spi_write(st->spi, AD5504_ADDR_CTRL, +	ret = ad5504_spi_write(st, AD5504_ADDR_CTRL,  				AD5504_DAC_PWRDWN_MODE(st->pwr_down_mode) |  				AD5504_DAC_PWR(st->pwr_down_mask));  	/* writes to the CTRL register must be followed by a NOOP */ -	ad5504_spi_write(st->spi, AD5504_ADDR_NOOP, 0); +	ad5504_spi_write(st, AD5504_ADDR_NOOP, 0);  	return ret ? ret : len;  } @@ -248,8 +245,10 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {  		.name = "powerdown",  		.read = ad5504_read_dac_powerdown,  		.write = ad5504_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", true, &ad5504_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, +		 &ad5504_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum),  	{ },  }; @@ -262,7 +261,11 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = {  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \  	.address = AD5504_ADDR_DAC(_chan), \ -	.scan_type = IIO_ST('u', 12, 16, 0), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 12, \ +		.storagebits = 16, \ +	}, \  	.ext_info = ad5504_ext_info, \  } diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 714af757cd5..e8199cce2ae 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -50,15 +50,12 @@ static int ad5624r_read_raw(struct iio_dev *indio_dev,  			   long m)  {  	struct ad5624r_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	switch (m) {  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (st->vref_mv * 1000) >> chan->scan_type.realbits; -		*val =  scale_uv / 1000; -		*val2 = (scale_uv % 1000) * 1000; -		return IIO_VAL_INT_PLUS_MICRO; - +		*val = st->vref_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } @@ -163,8 +160,10 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {  		.name = "powerdown",  		.read = ad5624r_read_dac_powerdown,  		.write = ad5624r_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", true, &ad5624r_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, +		 &ad5624r_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum),  	{ },  }; @@ -177,7 +176,12 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = {  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \  	.address = (_chan), \ -	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = (_bits), \ +		.storagebits = 16, \ +		.shift = 16 - (_bits), \ +	}, \  	.ext_info = ad5624r_ext_info, \  } diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 57825ead7db..17aca4d9bd0 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -78,7 +78,7 @@ struct ad5686_state {  	 */  	union { -		u32 d32; +		__be32 d32;  		u8 d8[4];  	} data[3] ____cacheline_aligned;  }; @@ -201,7 +201,6 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,  			   long m)  {  	struct ad5686_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	int ret;  	switch (m) { @@ -213,14 +212,10 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,  			return ret;  		*val = ret;  		return IIO_VAL_INT; -		break;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (st->vref_mv * 100000) -			>> (chan->scan_type.realbits); -		*val =  scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; - +		*val = st->vref_mv; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } @@ -265,13 +260,14 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {  		.name = "powerdown",  		.read = ad5686_read_dac_powerdown,  		.write = ad5686_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", false, &ad5686_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum),  	{ },  }; -#define AD5868_CHANNEL(chan, bits, shift) {			\ +#define AD5868_CHANNEL(chan, bits, _shift) {			\  		.type = IIO_VOLTAGE,				\  		.indexed = 1,					\  		.output = 1,					\ @@ -279,7 +275,12 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = {  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\  		.address = AD5686_ADDR_DAC(chan),		\ -		.scan_type = IIO_ST('u', bits, 16, shift),	\ +		.scan_type = {					\ +			.sign = 'u',				\ +			.realbits = (bits),			\ +			.storagebits = 16,			\ +			.shift = (_shift),			\ +		},						\  		.ext_info = ad5686_ext_info,			\  } diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 36a4361aece..a7c851f62d7 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -97,7 +97,7 @@ struct ad5755_state {  	 */  	union { -		u32 d32; +		__be32 d32;  		u8 d8[4];  	} data[2] ____cacheline_aligned;  }; @@ -253,15 +253,6 @@ static inline int ad5755_get_offset(struct ad5755_state *st,  	return (min * (1 << chan->scan_type.realbits)) / (max - min);  } -static inline int ad5755_get_scale(struct ad5755_state *st, -	struct iio_chan_spec const *chan) -{ -	int min, max; - -	ad5755_get_min_max(st, chan, &min, &max); -	return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits; -} -  static int ad5755_chan_reg_info(struct ad5755_state *st,  	struct iio_chan_spec const *chan, long info, bool write,  	unsigned int *reg, unsigned int *shift, unsigned int *offset) @@ -303,13 +294,15 @@ static int ad5755_read_raw(struct iio_dev *indio_dev,  {  	struct ad5755_state *st = iio_priv(indio_dev);  	unsigned int reg, shift, offset; +	int min, max;  	int ret;  	switch (info) {  	case IIO_CHAN_INFO_SCALE: -		*val = 0; -		*val2 = ad5755_get_scale(st, chan); -		return IIO_VAL_INT_PLUS_NANO; +		ad5755_get_min_max(st, chan, &min, &max); +		*val = max - min; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	case IIO_CHAN_INFO_OFFSET:  		*val = ad5755_get_offset(st, chan);  		return IIO_VAL_INT; @@ -386,6 +379,7 @@ static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {  		.name = "powerdown",  		.read = ad5755_read_powerdown,  		.write = ad5755_write_powerdown, +		.shared = IIO_SEPARATE,  	},  	{ },  }; @@ -398,7 +392,12 @@ static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {  		BIT(IIO_CHAN_INFO_OFFSET) |			\  		BIT(IIO_CHAN_INFO_CALIBSCALE) |			\  		BIT(IIO_CHAN_INFO_CALIBBIAS),			\ -	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)),	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (_bits),				\ +		.storagebits = 16,				\ +		.shift = 16 - (_bits),				\ +	},							\  	.ext_info = ad5755_ext_info,				\  } @@ -595,22 +594,7 @@ static int ad5755_probe(struct spi_device *spi)  	if (ret)  		return ret; -	ret = iio_device_register(indio_dev); -	if (ret) { -		dev_err(&spi->dev, "Failed to register iio device: %d\n", ret); -		return ret; -	} - -	return 0; -} - -static int ad5755_remove(struct spi_device *spi) -{ -	struct iio_dev *indio_dev = spi_get_drvdata(spi); - -	iio_device_unregister(indio_dev); - -	return 0; +	return devm_iio_device_register(&spi->dev, indio_dev);  }  static const struct spi_device_id ad5755_id[] = { @@ -629,7 +613,6 @@ static struct spi_driver ad5755_driver = {  		.owner = THIS_MODULE,  	},  	.probe = ad5755_probe, -	.remove = ad5755_remove,  	.id_table = ad5755_id,  };  module_spi_driver(ad5755_driver); diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c index df7e028d9db..d0d38165339 100644 --- a/drivers/iio/dac/ad5764.c +++ b/drivers/iio/dac/ad5764.c @@ -83,7 +83,12 @@ enum ad5764_type {  		BIT(IIO_CHAN_INFO_CALIBSCALE) |			\  		BIT(IIO_CHAN_INFO_CALIBBIAS),			\  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET),	\ -	.scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits))	\ +	.scan_type = {						\ +		.sign = 'u',					\ +		.realbits = (_bits),				\ +		.storagebits = 16,				\ +		.shift = 16 - (_bits),				\ +	},							\  }  #define DECLARE_AD5764_CHANNELS(_name, _bits) \ @@ -217,7 +222,6 @@ static int ad5764_read_raw(struct iio_dev *indio_dev,  	struct iio_chan_spec const *chan, int *val, int *val2, long info)  {  	struct ad5764_state *st = iio_priv(indio_dev); -	unsigned long scale_uv;  	unsigned int reg;  	int vref;  	int ret; @@ -245,15 +249,14 @@ static int ad5764_read_raw(struct iio_dev *indio_dev,  		*val = sign_extend32(*val, 5);  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		/* vout = 4 * vref + ((dac_code / 65535) - 0.5) */ +		/* vout = 4 * vref + ((dac_code / 65536) - 0.5) */  		vref = ad5764_get_channel_vref(st, chan->channel);  		if (vref < 0)  			return vref; -		scale_uv = (vref * 4 * 100) >> chan->scan_type.realbits; -		*val = scale_uv / 100000; -		*val2 = (scale_uv % 100000) * 10; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = vref * 4 / 1000; +		*val2 = chan->scan_type.realbits; +		return IIO_VAL_FRACTIONAL_LOG2;  	case IIO_CHAN_INFO_OFFSET:  		*val = -(1 << chan->scan_type.realbits) / 2;  		return IIO_VAL_INT; diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index ce745896330..ae49afe2b38 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -91,6 +91,11 @@ struct ad5791_state {  	unsigned			ctrl;  	unsigned			pwr_down_mode;  	bool				pwr_down; + +	union { +		__be32 d32; +		u8 d8[4]; +	} data[3] ____cacheline_aligned;  };  /** @@ -104,48 +109,39 @@ enum ad5791_supported_device_ids {  	ID_AD5791,  }; -static int ad5791_spi_write(struct spi_device *spi, u8 addr, u32 val) +static int ad5791_spi_write(struct ad5791_state *st, u8 addr, u32 val)  { -	union { -		u32 d32; -		u8 d8[4]; -	} data; - -	data.d32 = cpu_to_be32(AD5791_CMD_WRITE | +	st->data[0].d32 = cpu_to_be32(AD5791_CMD_WRITE |  			      AD5791_ADDR(addr) |  			      (val & AD5791_DAC_MASK)); -	return spi_write(spi, &data.d8[1], 3); +	return spi_write(st->spi, &st->data[0].d8[1], 3);  } -static int ad5791_spi_read(struct spi_device *spi, u8 addr, u32 *val) +static int ad5791_spi_read(struct ad5791_state *st, u8 addr, u32 *val)  { -	union { -		u32 d32; -		u8 d8[4]; -	} data[3];  	int ret;  	struct spi_transfer xfers[] = {  		{ -			.tx_buf = &data[0].d8[1], +			.tx_buf = &st->data[0].d8[1],  			.bits_per_word = 8,  			.len = 3,  			.cs_change = 1,  		}, { -			.tx_buf = &data[1].d8[1], -			.rx_buf = &data[2].d8[1], +			.tx_buf = &st->data[1].d8[1], +			.rx_buf = &st->data[2].d8[1],  			.bits_per_word = 8,  			.len = 3,  		},  	}; -	data[0].d32 = cpu_to_be32(AD5791_CMD_READ | +	st->data[0].d32 = cpu_to_be32(AD5791_CMD_READ |  			      AD5791_ADDR(addr)); -	data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); +	st->data[1].d32 = cpu_to_be32(AD5791_ADDR(AD5791_ADDR_NOOP)); -	ret = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); +	ret = spi_sync_transfer(st->spi, xfers, ARRAY_SIZE(xfers)); -	*val = be32_to_cpu(data[2].d32); +	*val = be32_to_cpu(st->data[2].d32);  	return ret;  } @@ -210,7 +206,7 @@ static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev,  	}  	st->pwr_down = pwr_down; -	ret = ad5791_spi_write(st->spi, AD5791_ADDR_CTRL, st->ctrl); +	ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl);  	return ret ? ret : len;  } @@ -263,16 +259,16 @@ static int ad5791_read_raw(struct iio_dev *indio_dev,  	switch (m) {  	case IIO_CHAN_INFO_RAW: -		ret = ad5791_spi_read(st->spi, chan->address, val); +		ret = ad5791_spi_read(st, chan->address, val);  		if (ret)  			return ret;  		*val &= AD5791_DAC_MASK;  		*val >>= chan->scan_type.shift;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		*val = 0; -		*val2 = (((u64)st->vref_mv) * 1000000ULL) >> chan->scan_type.realbits; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = st->vref_mv; +		*val2 = (1 << chan->scan_type.realbits) - 1; +		return IIO_VAL_FRACTIONAL;  	case IIO_CHAN_INFO_OFFSET:  		val64 = (((u64)st->vref_neg_mv) << chan->scan_type.realbits);  		do_div(val64, st->vref_mv); @@ -287,16 +283,17 @@ static int ad5791_read_raw(struct iio_dev *indio_dev,  static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {  	{  		.name = "powerdown", -		.shared = true, +		.shared = IIO_SHARED_BY_TYPE,  		.read = ad5791_read_dac_powerdown,  		.write = ad5791_write_dac_powerdown,  	}, -	IIO_ENUM("powerdown_mode", true, &ad5791_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, +		 &ad5791_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum),  	{ },  }; -#define AD5791_CHAN(bits, shift) {			\ +#define AD5791_CHAN(bits, _shift) {			\  	.type = IIO_VOLTAGE,				\  	.output = 1,					\  	.indexed = 1,					\ @@ -305,7 +302,12 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = {  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |	\  		BIT(IIO_CHAN_INFO_OFFSET),		\ -	.scan_type = IIO_ST('u', bits, 24, shift),	\ +	.scan_type = {					\ +		.sign = 'u',				\ +		.realbits = (bits),			\ +		.storagebits = 24,			\ +		.shift = (_shift),			\ +	},						\  	.ext_info = ad5791_ext_info,			\  } @@ -329,7 +331,7 @@ static int ad5791_write_raw(struct iio_dev *indio_dev,  		val &= AD5791_RES_MASK(chan->scan_type.realbits);  		val <<= chan->scan_type.shift; -		return ad5791_spi_write(st->spi, chan->address, val); +		return ad5791_spi_write(st, chan->address, val);  	default:  		return -EINVAL; @@ -392,7 +394,7 @@ static int ad5791_probe(struct spi_device *spi)  		dev_warn(&spi->dev, "reference voltage unspecified\n");  	} -	ret = ad5791_spi_write(spi, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET); +	ret = ad5791_spi_write(st, AD5791_ADDR_SW_CTRL, AD5791_SWCTRL_RESET);  	if (ret)  		goto error_disable_reg_neg; @@ -404,7 +406,7 @@ static int ad5791_probe(struct spi_device *spi)  		  | ((pdata && pdata->use_rbuf_gain2) ? 0 : AD5791_CTRL_RBUF) |  		  AD5791_CTRL_BIN2SC; -	ret = ad5791_spi_write(spi, AD5791_ADDR_CTRL, st->ctrl | +	ret = ad5791_spi_write(st, AD5791_ADDR_CTRL, st->ctrl |  		AD5791_CTRL_OPGND | AD5791_CTRL_DACTRI);  	if (ret)  		goto error_disable_reg_neg; diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index ed2d276477b..fa281003296 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -92,7 +92,7 @@ static ssize_t ad7303_write_dac_powerdown(struct iio_dev *indio_dev,  	ad7303_write(st, chan->channel, st->dac_cache[chan->channel]);  	mutex_unlock(&indio_dev->mlock); -	return ret ? ret : len; +	return len;  }  static int ad7303_get_vref(struct ad7303_state *st, @@ -169,6 +169,7 @@ static const struct iio_chan_spec_ext_info ad7303_ext_info[] = {  		.name = "powerdown",  		.read = ad7303_read_dac_powerdown,  		.write = ad7303_write_dac_powerdown, +		.shared = IIO_SEPARATE,  	},  	{ },  }; diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index 83adcbf1a20..9a82a7255eb 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -19,7 +19,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/jiffies.h>  #include <linux/i2c.h> @@ -82,15 +81,13 @@ static int max517_read_raw(struct iio_dev *indio_dev,  			   long m)  {  	struct max517_data *data = iio_priv(indio_dev); -	unsigned int scale_uv;  	switch (m) {  	case IIO_CHAN_INFO_SCALE:  		/* Corresponds to Vref / 2^(bits) */ -		scale_uv = (data->vref_mv[chan->channel] * 1000) >> 8; -		*val =  scale_uv / 1000000; -		*val2 = scale_uv % 1000000; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = data->vref_mv[chan->channel]; +		*val2 = 8; +		return IIO_VAL_FRACTIONAL_LOG2;  	default:  		break;  	} @@ -148,7 +145,6 @@ static const struct iio_info max517_info = {  	.channel = (chan),				\  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\  	BIT(IIO_CHAN_INFO_SCALE),			\ -	.scan_type = IIO_ST('u', 8, 8, 0),		\  }  static const struct iio_chan_spec max517_channels[] = { @@ -162,7 +158,6 @@ static int max517_probe(struct i2c_client *client,  	struct max517_data *data;  	struct iio_dev *indio_dev;  	struct max517_platform_data *platform_data = client->dev.platform_data; -	int err;  	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));  	if (!indio_dev) @@ -194,13 +189,7 @@ static int max517_probe(struct i2c_client *client,  		data->vref_mv[1] = platform_data->vref_mv[1];  	} -	err = iio_device_register(indio_dev); -	if (err) -		return err; - -	dev_info(&client->dev, "DAC registered\n"); - -	return 0; +	return iio_device_register(indio_dev);  }  static int max517_remove(struct i2c_client *client) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 1397b6e0e41..43d14588448 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -15,7 +15,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/i2c.h>  #include <linux/err.h>  #include <linux/delay.h> @@ -195,8 +194,9 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = {  		.name = "powerdown",  		.read = mcp4725_read_powerdown,  		.write = mcp4725_write_powerdown, +		.shared = IIO_SEPARATE,  	}, -	IIO_ENUM("powerdown_mode", false, &mcp4725_powerdown_mode_enum), +	IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp4725_powerdown_mode_enum),  	IIO_ENUM_AVAILABLE("powerdown_mode", &mcp4725_powerdown_mode_enum),  	{ },  }; @@ -208,7 +208,6 @@ static const struct iio_chan_spec mcp4725_channel = {  	.channel	= 0,  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),  	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), -	.scan_type	= IIO_ST('u', 12, 16, 0),  	.ext_info	= mcp4725_ext_info,  }; @@ -238,17 +237,15 @@ static int mcp4725_read_raw(struct iio_dev *indio_dev,  			   int *val, int *val2, long mask)  {  	struct mcp4725_data *data = iio_priv(indio_dev); -	unsigned long scale_uv;  	switch (mask) {  	case IIO_CHAN_INFO_RAW:  		*val = data->dac_value;  		return IIO_VAL_INT;  	case IIO_CHAN_INFO_SCALE: -		scale_uv = (data->vref_mv * 1000) >> 12; -		*val =  scale_uv / 1000000; -		*val2 = scale_uv % 1000000; -		return IIO_VAL_INT_PLUS_MICRO; +		*val = data->vref_mv; +		*val2 = 12; +		return IIO_VAL_FRACTIONAL_LOG2;  	}  	return -EINVAL;  } @@ -321,13 +318,7 @@ static int mcp4725_probe(struct i2c_client *client,  	data->powerdown_mode = pd ? pd-1 : 2; /* 500kohm_to_gnd */  	data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4); -	err = iio_device_register(indio_dev); -	if (err) -		return err; - -	dev_info(&client->dev, "MCP4725 DAC registered\n"); - -	return 0; +	return iio_device_register(indio_dev);  }  static int mcp4725_remove(struct i2c_client *client) diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index a7b30be86ae..63a25d9e120 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -351,6 +351,7 @@ static ssize_t adf4350_read(struct iio_dev *indio_dev,  	.read = adf4350_read, \  	.write = adf4350_write, \  	.private = _ident, \ +	.shared = IIO_SEPARATE, \  }  static const struct iio_chan_spec_ext_info adf4350_ext_info[] = { @@ -525,8 +526,10 @@ static int adf4350_probe(struct spi_device *spi)  	}  	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); -	if (indio_dev == NULL) -		return -ENOMEM; +	if (indio_dev == NULL) { +		ret =  -ENOMEM; +		goto error_disable_clk; +	}  	st = iio_priv(indio_dev); diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig index 41c64a43bca..ac2d69e34c8 100644 --- a/drivers/iio/gyro/Kconfig +++ b/drivers/iio/gyro/Kconfig @@ -70,7 +70,7 @@ config IIO_ST_GYRO_3AXIS  	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)  	help  	  Say yes here to build support for STMicroelectronics gyroscopes: -	  L3G4200D, LSM330DL, L3GD20, L3GD20H, LSM330DLC, L3G4IS, LSM330. +	  L3G4200D, LSM330DL, L3GD20, LSM330DLC, L3G4IS, LSM330.  	  This driver can also be built as a module. If so, these modules  	  will be created: diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c index e9ec022ae22..add50983726 100644 --- a/drivers/iio/gyro/adis16080.c +++ b/drivers/iio/gyro/adis16080.c @@ -51,7 +51,6 @@ static int adis16080_read_sample(struct iio_dev *indio_dev,  		u16 addr, int *val)  {  	struct adis16080_state *st = iio_priv(indio_dev); -	struct spi_message m;  	int ret;  	struct spi_transfer	t[] = {  		{ @@ -66,11 +65,7 @@ static int adis16080_read_sample(struct iio_dev *indio_dev,  	st->buf = cpu_to_be16(addr | ADIS16080_DIN_WRITE); -	spi_message_init(&m); -	spi_message_add_tail(&t[0], &m); -	spi_message_add_tail(&t[1], &m); - -	ret = spi_sync(st->us, &m); +	ret = spi_sync_transfer(st->us, t, ARRAY_SIZE(t));  	if (ret == 0)  		*val = sign_extend32(be16_to_cpu(st->buf), 11); diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c index ac66fc18404..8d08c7ed1ea 100644 --- a/drivers/iio/gyro/adis16130.c +++ b/drivers/iio/gyro/adis16130.c @@ -47,7 +47,6 @@ static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)  {  	int ret;  	struct adis16130_state *st = iio_priv(indio_dev); -	struct spi_message msg;  	struct spi_transfer xfer = {  		.tx_buf = st->buf,  		.rx_buf = st->buf, @@ -59,10 +58,7 @@ static int adis16130_spi_read(struct iio_dev *indio_dev, u8 reg_addr, u32 *val)  	st->buf[0] = ADIS16130_CON_RD | reg_addr;  	st->buf[1] = st->buf[2] = st->buf[3] = 0; -	spi_message_init(&msg); -	spi_message_add_tail(&xfer, &msg); -	ret = spi_sync(st->us, &msg); - +	ret = spi_sync_transfer(st->us, &xfer, 1);  	if (ret == 0)  		*val = (st->buf[1] << 16) | (st->buf[2] << 8) | st->buf[3];  	mutex_unlock(&st->buf_lock); @@ -103,7 +99,6 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,  		default:  			return -EINVAL;  		} -		break;  	case IIO_CHAN_INFO_OFFSET:  		switch (chan->type) {  		case IIO_ANGL_VEL: @@ -115,7 +110,6 @@ static int adis16130_read_raw(struct iio_dev *indio_dev,  		default:  			return -EINVAL;  		} -		break;  	}  	return -EINVAL; @@ -167,13 +161,7 @@ static int adis16130_probe(struct spi_device *spi)  	indio_dev->info = &adis16130_info;  	indio_dev->modes = INDIO_DIRECT_MODE; -	return iio_device_register(indio_dev); -} - -static int adis16130_remove(struct spi_device *spi) -{ -	iio_device_unregister(spi_get_drvdata(spi)); -	return 0; +	return devm_iio_device_register(&spi->dev, indio_dev);  }  static struct spi_driver adis16130_driver = { @@ -182,7 +170,6 @@ static struct spi_driver adis16130_driver = {  		.owner = THIS_MODULE,  	},  	.probe = adis16130_probe, -	.remove = adis16130_remove,  };  module_spi_driver(adis16130_driver); diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index 06541162fc0..22b6fb80fa1 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -239,7 +239,6 @@ static int adis16260_read_raw(struct iio_dev *indio_dev,  		default:  			return -EINVAL;  		} -		break;  	case IIO_CHAN_INFO_OFFSET:  		*val = 250000 / 1453; /* 25 C = 0x00 */  		return IIO_VAL_INT; diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c index 6dab2995f0f..eb0e08ec9e2 100644 --- a/drivers/iio/gyro/adxrs450.c +++ b/drivers/iio/gyro/adxrs450.c @@ -90,7 +90,6 @@ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,  				    u8 reg_address,  				    u16 *val)  { -	struct spi_message msg;  	struct adxrs450_state *st = iio_priv(indio_dev);  	u32 tx;  	int ret; @@ -114,10 +113,7 @@ static int adxrs450_spi_read_reg_16(struct iio_dev *indio_dev,  		tx |= ADXRS450_P;  	st->tx = cpu_to_be32(tx); -	spi_message_init(&msg); -	spi_message_add_tail(&xfers[0], &msg); -	spi_message_add_tail(&xfers[1], &msg); -	ret = spi_sync(st->us, &msg); +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));  	if (ret) {  		dev_err(&st->us->dev, "problem while reading 16 bit register 0x%02x\n",  				reg_address); @@ -169,7 +165,6 @@ static int adxrs450_spi_write_reg_16(struct iio_dev *indio_dev,   **/  static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)  { -	struct spi_message msg;  	struct adxrs450_state *st = iio_priv(indio_dev);  	int ret;  	struct spi_transfer xfers[] = { @@ -188,10 +183,7 @@ static int adxrs450_spi_sensor_data(struct iio_dev *indio_dev, s16 *val)  	mutex_lock(&st->buf_lock);  	st->tx = cpu_to_be32(ADXRS450_SENSOR_DATA); -	spi_message_init(&msg); -	spi_message_add_tail(&xfers[0], &msg); -	spi_message_add_tail(&xfers[1], &msg); -	ret = spi_sync(st->us, &msg); +	ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));  	if (ret) {  		dev_err(&st->us->dev, "Problem while reading sensor data\n");  		goto error_ret; @@ -354,7 +346,6 @@ static int adxrs450_read_raw(struct iio_dev *indio_dev,  		default:  			return -EINVAL;  		} -		break;  	case IIO_CHAN_INFO_QUADRATURE_CORRECTION_RAW:  		ret = adxrs450_spi_read_reg_16(indio_dev, ADXRS450_QUAD1, &t);  		if (ret) @@ -443,23 +434,14 @@ static int adxrs450_probe(struct spi_device *spi)  	indio_dev->num_channels = ARRAY_SIZE(adxrs450_channels);  	indio_dev->name = spi->dev.driver->name; -	ret = iio_device_register(indio_dev); +	ret = devm_iio_device_register(&spi->dev, indio_dev);  	if (ret)  		return ret;  	/* Get the device into a sane initial state */  	ret = adxrs450_initial_setup(indio_dev);  	if (ret) -		goto error_initial; -	return 0; -error_initial: -	iio_device_unregister(indio_dev); -	return ret; -} - -static int adxrs450_remove(struct spi_device *spi) -{ -	iio_device_unregister(spi_get_drvdata(spi)); +		return ret;  	return 0;  } @@ -477,7 +459,6 @@ static struct spi_driver adxrs450_driver = {  		.owner = THIS_MODULE,  	},  	.probe = adxrs450_probe, -	.remove = adxrs450_remove,  	.id_table	= adxrs450_id,  };  module_spi_driver(adxrs450_driver); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index c688d974d3e..fa034a3dad7 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) | @@ -102,44 +110,52 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,  	struct gyro_3d_state *gyro_state = iio_priv(indio_dev);  	int report_id = -1;  	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: -		ret = hid_sensor_read_samp_freq_value( +		ret_type = hid_sensor_read_samp_freq_value(  			&gyro_state->common_attributes, val, val2); -			ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	case IIO_CHAN_INFO_HYSTERESIS: -		ret = hid_sensor_read_raw_hyst_value( +		ret_type = hid_sensor_read_raw_hyst_value(  			&gyro_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	default:  		ret_type = -EINVAL; @@ -182,10 +198,11 @@ static const struct iio_info gyro_3d_info = {  };  /* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +	int len)  {  	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); -	iio_push_to_buffers(indio_dev, (u8 *)data); +	iio_push_to_buffers(indio_dev, data);  }  /* Callback handler to send event after all samples are received and captured */ @@ -196,11 +213,10 @@ 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, -				(u8 *)gyro_state->gyro_val, +				gyro_state->gyro_val,  				sizeof(gyro_state->gyro_val));  	return 0; @@ -261,6 +277,22 @@ 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, +			HID_FEATURE_REPORT, usage_id, +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | +			HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, +			&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 ret;  } @@ -318,7 +350,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) { @@ -347,7 +379,7 @@ static int hid_gyro_3d_probe(struct platform_device *pdev)  error_iio_unreg:  	iio_device_unregister(indio_dev);  error_remove_trigger: -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&gyro_state->common_attributes);  error_unreg_buffer_funcs:  	iio_triggered_buffer_cleanup(indio_dev);  error_free_dev_mem: @@ -360,10 +392,11 @@ static int hid_gyro_3d_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 gyro_3d_state *gyro_state = iio_priv(indio_dev);  	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);  	iio_device_unregister(indio_dev); -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&gyro_state->common_attributes);  	iio_triggered_buffer_cleanup(indio_dev);  	kfree(indio_dev->channels); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 6c43af9bb0a..e3b3c508407 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -55,11 +55,8 @@ static irqreturn_t itg3200_trigger_handler(int irq, void *p)  	if (ret < 0)  		goto error_ret; -	if (indio_dev->scan_timestamp) -		memcpy(buf + indio_dev->scan_bytes - sizeof(s64), -				&pf->timestamp, sizeof(pf->timestamp)); +	iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp); -	iio_push_to_buffers(indio_dev, (u8 *)buf);  	iio_trigger_notify_done(indio_dev->trig);  error_ret: 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.h b/drivers/iio/gyro/st_gyro.h index f8f2bf84a5a..c197360c450 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -19,7 +19,6 @@  #define LSM330DL_GYRO_DEV_NAME		"lsm330dl_gyro"  #define LSM330DLC_GYRO_DEV_NAME		"lsm330dlc_gyro"  #define L3GD20_GYRO_DEV_NAME		"l3gd20" -#define L3GD20H_GYRO_DEV_NAME		"l3gd20h"  #define L3G4IS_GYRO_DEV_NAME		"l3g4is_ui"  #define LSM330_GYRO_DEV_NAME		"lsm330_gyro" diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c index 69017c7ec30..d67b17b6a7a 100644 --- a/drivers/iio/gyro/st_gyro_buffer.c +++ b/drivers/iio/gyro/st_gyro_buffer.c @@ -32,16 +32,7 @@ int st_gyro_trig_set_state(struct iio_trigger *trig, bool state)  static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)  { -	int err; - -	err = st_sensors_set_enable(indio_dev, true); -	if (err < 0) -		goto st_gyro_set_enable_error; - -	err = iio_sw_buffer_preenable(indio_dev); - -st_gyro_set_enable_error: -	return err; +	return st_sensors_set_enable(indio_dev, true);  }  static int st_gyro_buffer_postenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index e13c2b0bf3d..ed74a906998 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -167,11 +167,10 @@ static const struct st_sensors st_gyro_sensors[] = {  		.wai = ST_GYRO_2_WAI_EXP,  		.sensors_supported = {  			[0] = L3GD20_GYRO_DEV_NAME, -			[1] = L3GD20H_GYRO_DEV_NAME, -			[2] = LSM330D_GYRO_DEV_NAME, -			[3] = LSM330DLC_GYRO_DEV_NAME, -			[4] = L3G4IS_GYRO_DEV_NAME, -			[5] = LSM330_GYRO_DEV_NAME, +			[1] = LSM330D_GYRO_DEV_NAME, +			[2] = LSM330DLC_GYRO_DEV_NAME, +			[3] = L3G4IS_GYRO_DEV_NAME, +			[4] = LSM330_GYRO_DEV_NAME,  		},  		.ch = (struct iio_chan_spec *)st_gyro_16bit_channels,  		.odr = { @@ -305,16 +304,19 @@ static const struct iio_trigger_ops st_gyro_trigger_ops = {  int st_gyro_common_probe(struct iio_dev *indio_dev,  					struct st_sensors_platform_data *pdata)  { -	int err;  	struct st_sensor_data *gdata = iio_priv(indio_dev); +	int irq = gdata->get_irq_data_ready(indio_dev); +	int err;  	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) -		goto st_gyro_common_probe_error; +		return err;  	gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;  	gdata->multiread_bit = gdata->sensor->multi_read_bit; @@ -327,13 +329,13 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,  	err = st_sensors_init_sensor(indio_dev, pdata);  	if (err < 0) -		goto st_gyro_common_probe_error; +		return err; -	if (gdata->get_irq_data_ready(indio_dev) > 0) { -		err = st_gyro_allocate_ring(indio_dev); -		if (err < 0) -			goto st_gyro_common_probe_error; +	err = st_gyro_allocate_ring(indio_dev); +	if (err < 0) +		return err; +	if (irq > 0) {  		err = st_sensors_allocate_trigger(indio_dev,  						  ST_GYRO_TRIGGER_OPS);  		if (err < 0) @@ -344,15 +346,17 @@ int st_gyro_common_probe(struct iio_dev *indio_dev,  	if (err)  		goto st_gyro_device_register_error; -	return err; +	dev_info(&indio_dev->dev, "registered gyroscope %s\n", +		 indio_dev->name); + +	return 0;  st_gyro_device_register_error: -	if (gdata->get_irq_data_ready(indio_dev) > 0) +	if (irq > 0)  		st_sensors_deallocate_trigger(indio_dev);  st_gyro_probe_trigger_error: -	if (gdata->get_irq_data_ready(indio_dev) > 0) -		st_gyro_deallocate_ring(indio_dev); -st_gyro_common_probe_error: +	st_gyro_deallocate_ring(indio_dev); +  	return err;  }  EXPORT_SYMBOL(st_gyro_common_probe); @@ -361,11 +365,13 @@ 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) { +	if (gdata->get_irq_data_ready(indio_dev) > 0)  		st_sensors_deallocate_trigger(indio_dev); -		st_gyro_deallocate_ring(indio_dev); -	} + +	st_gyro_deallocate_ring(indio_dev);  }  EXPORT_SYMBOL(st_gyro_common_remove); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 16b8b8d70bf..23c12f361b0 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -55,7 +55,6 @@ static const struct i2c_device_id st_gyro_id_table[] = {  	{ LSM330DL_GYRO_DEV_NAME },  	{ LSM330DLC_GYRO_DEV_NAME },  	{ L3GD20_GYRO_DEV_NAME }, -	{ L3GD20H_GYRO_DEV_NAME },  	{ L3G4IS_GYRO_DEV_NAME },  	{ LSM330_GYRO_DEV_NAME },  	{}, diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index 94763e25caf..b4ad3be2668 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -54,7 +54,6 @@ static const struct spi_device_id st_gyro_id_table[] = {  	{ LSM330DL_GYRO_DEV_NAME },  	{ LSM330DLC_GYRO_DEV_NAME },  	{ L3GD20_GYRO_DEV_NAME }, -	{ L3GD20H_GYRO_DEV_NAME },  	{ L3G4IS_GYRO_DEV_NAME },  	{ LSM330_GYRO_DEV_NAME },  	{}, diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig new file mode 100644 index 00000000000..e116bd8dd0e --- /dev/null +++ b/drivers/iio/humidity/Kconfig @@ -0,0 +1,25 @@ +# +# humidity sensor drivers +# +menu "Humidity sensors" + +config DHT11 +	tristate "DHT11 (and compatible sensors) driver" +	depends on GPIOLIB +	help +	  This driver supports reading data via a single interrupt +	  generating GPIO line. Currently tested are DHT11 and DHT22. +	  Other sensors should work as well as long as they speak the +	  same protocol. + +config SI7005 +	tristate "SI7005 relative humidity and temperature sensor" +	depends on I2C +	help +	  Say yes here to build support for the Silabs Si7005 relative +	  humidity and temperature sensor. + +	  To compile this driver as a module, choose M here: the module +	  will be called si7005. + +endmenu diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile new file mode 100644 index 00000000000..e3f3d942e64 --- /dev/null +++ b/drivers/iio/humidity/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for IIO humidity sensor drivers +# + +obj-$(CONFIG_DHT11) += dht11.o +obj-$(CONFIG_SI7005) += si7005.o diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c new file mode 100644 index 00000000000..d8771f546bf --- /dev/null +++ b/drivers/iio/humidity/dht11.c @@ -0,0 +1,294 @@ +/* + * DHT11/DHT22 bit banging GPIO driver + * + * Copyright (c) Harald Geyer <harald@ccbib.org> + * + * 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/err.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/sysfs.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/wait.h> +#include <linux/bitops.h> +#include <linux/completion.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/of_gpio.h> + +#include <linux/iio/iio.h> + +#define DRIVER_NAME	"dht11" + +#define DHT11_DATA_VALID_TIME	2000000000  /* 2s in ns */ + +#define DHT11_EDGES_PREAMBLE 4 +#define DHT11_BITS_PER_READ 40 +#define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1) + +/* Data transmission timing (nano seconds) */ +#define DHT11_START_TRANSMISSION	18  /* ms */ +#define DHT11_SENSOR_RESPONSE	80000 +#define DHT11_START_BIT		50000 +#define DHT11_DATA_BIT_LOW	27000 +#define DHT11_DATA_BIT_HIGH	70000 + +struct dht11 { +	struct device			*dev; + +	int				gpio; +	int				irq; + +	struct completion		completion; + +	s64				timestamp; +	int				temperature; +	int				humidity; + +	/* num_edges: -1 means "no transmission in progress" */ +	int				num_edges; +	struct {s64 ts; int value; }	edges[DHT11_EDGES_PER_READ]; +}; + +static unsigned char dht11_decode_byte(int *timing, int threshold) +{ +	unsigned char ret = 0; +	int i; + +	for (i = 0; i < 8; ++i) { +		ret <<= 1; +		if (timing[i] >= threshold) +			++ret; +	} + +	return ret; +} + +static int dht11_decode(struct dht11 *dht11, int offset) +{ +	int i, t, timing[DHT11_BITS_PER_READ], threshold, +		timeres = DHT11_SENSOR_RESPONSE; +	unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum; + +	/* Calculate timestamp resolution */ +	for (i = 0; i < dht11->num_edges; ++i) { +		t = dht11->edges[i].ts - dht11->edges[i-1].ts; +		if (t > 0 && t < timeres) +			timeres = t; +	} +	if (2*timeres > DHT11_DATA_BIT_HIGH) { +		pr_err("dht11: timeresolution %d too bad for decoding\n", +			timeres); +		return -EIO; +	} +	threshold = DHT11_DATA_BIT_HIGH / timeres; +	if (DHT11_DATA_BIT_LOW/timeres + 1 >= threshold) +		pr_err("dht11: WARNING: decoding ambiguous\n"); + +	/* scale down with timeres and check validity */ +	for (i = 0; i < DHT11_BITS_PER_READ; ++i) { +		t = dht11->edges[offset + 2*i + 2].ts - +			dht11->edges[offset + 2*i + 1].ts; +		if (!dht11->edges[offset + 2*i + 1].value) +			return -EIO;  /* lost synchronisation */ +		timing[i] = t / timeres; +	} + +	hum_int = dht11_decode_byte(timing, threshold); +	hum_dec = dht11_decode_byte(&timing[8], threshold); +	temp_int = dht11_decode_byte(&timing[16], threshold); +	temp_dec = dht11_decode_byte(&timing[24], threshold); +	checksum = dht11_decode_byte(&timing[32], threshold); + +	if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) +		return -EIO; + +	dht11->timestamp = iio_get_time_ns(); +	if (hum_int < 20) {  /* DHT22 */ +		dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * +					((temp_int & 0x80) ? -100 : 100); +		dht11->humidity = ((hum_int << 8) + hum_dec) * 100; +	} else if (temp_dec == 0 && hum_dec == 0) {  /* DHT11 */ +		dht11->temperature = temp_int * 1000; +		dht11->humidity = hum_int * 1000; +	} else { +		dev_err(dht11->dev, +			"Don't know how to decode data: %d %d %d %d\n", +			hum_int, hum_dec, temp_int, temp_dec); +		return -EIO; +	} + +	return 0; +} + +static int dht11_read_raw(struct iio_dev *iio_dev, +			const struct iio_chan_spec *chan, +			int *val, int *val2, long m) +{ +	struct dht11 *dht11 = iio_priv(iio_dev); +	int ret; + +	if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) { +		reinit_completion(&dht11->completion); + +		dht11->num_edges = 0; +		ret = gpio_direction_output(dht11->gpio, 0); +		if (ret) +			goto err; +		msleep(DHT11_START_TRANSMISSION); +		ret = gpio_direction_input(dht11->gpio); +		if (ret) +			goto err; + +		ret = wait_for_completion_killable_timeout(&dht11->completion, +								 HZ); +		if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) { +			dev_err(&iio_dev->dev, +					"Only %d signal edges detected\n", +					dht11->num_edges); +			ret = -ETIMEDOUT; +		} +		if (ret < 0) +			goto err; + +		ret = dht11_decode(dht11, +				dht11->num_edges == DHT11_EDGES_PER_READ ? +					DHT11_EDGES_PREAMBLE : +					DHT11_EDGES_PREAMBLE - 2); +		if (ret) +			goto err; +	} + +	ret = IIO_VAL_INT; +	if (chan->type == IIO_TEMP) +		*val = dht11->temperature; +	else if (chan->type == IIO_HUMIDITYRELATIVE) +		*val = dht11->humidity; +	else +		ret = -EINVAL; +err: +	dht11->num_edges = -1; +	return ret; +} + +static const struct iio_info dht11_iio_info = { +	.driver_module		= THIS_MODULE, +	.read_raw		= dht11_read_raw, +}; + +/* + * IRQ handler called on GPIO edges +*/ +static irqreturn_t dht11_handle_irq(int irq, void *data) +{ +	struct iio_dev *iio = data; +	struct dht11 *dht11 = iio_priv(iio); + +	/* TODO: Consider making the handler safe for IRQ sharing */ +	if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { +		dht11->edges[dht11->num_edges].ts = iio_get_time_ns(); +		dht11->edges[dht11->num_edges++].value = +						gpio_get_value(dht11->gpio); + +		if (dht11->num_edges >= DHT11_EDGES_PER_READ) +			complete(&dht11->completion); +	} + +	return IRQ_HANDLED; +} + +static const struct iio_chan_spec dht11_chan_spec[] = { +	{ .type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), }, +	{ .type = IIO_HUMIDITYRELATIVE, +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), } +}; + +static const struct of_device_id dht11_dt_ids[] = { +	{ .compatible = "dht11", }, +	{ } +}; +MODULE_DEVICE_TABLE(of, dht11_dt_ids); + +static int dht11_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct device_node *node = dev->of_node; +	struct dht11 *dht11; +	struct iio_dev *iio; +	int ret; + +	iio = devm_iio_device_alloc(dev, sizeof(*dht11)); +	if (!iio) { +		dev_err(dev, "Failed to allocate IIO device\n"); +		return -ENOMEM; +	} + +	dht11 = iio_priv(iio); +	dht11->dev = dev; + +	dht11->gpio = ret = of_get_gpio(node, 0); +	if (ret < 0) +		return ret; +	ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name); +	if (ret) +		return ret; + +	dht11->irq = gpio_to_irq(dht11->gpio); +	if (dht11->irq < 0) { +		dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio); +		return -EINVAL; +	} +	ret = devm_request_irq(dev, dht11->irq, dht11_handle_irq, +				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, +				pdev->name, iio); +	if (ret) +		return ret; + +	dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1; +	dht11->num_edges = -1; + +	platform_set_drvdata(pdev, iio); + +	init_completion(&dht11->completion); +	iio->name = pdev->name; +	iio->dev.parent = &pdev->dev; +	iio->info = &dht11_iio_info; +	iio->modes = INDIO_DIRECT_MODE; +	iio->channels = dht11_chan_spec; +	iio->num_channels = ARRAY_SIZE(dht11_chan_spec); + +	return devm_iio_device_register(dev, iio); +} + +static struct platform_driver dht11_driver = { +	.driver = { +		.name	= DRIVER_NAME, +		.owner	= THIS_MODULE, +		.of_match_table = dht11_dt_ids, +	}, +	.probe  = dht11_probe, +}; + +module_platform_driver(dht11_driver); + +MODULE_AUTHOR("Harald Geyer <harald@ccbib.org>"); +MODULE_DESCRIPTION("DHT11 humidity/temperature sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c new file mode 100644 index 00000000000..bdd586e6d95 --- /dev/null +++ b/drivers/iio/humidity/si7005.c @@ -0,0 +1,189 @@ +/* + * si7005.c - Support for Silabs Si7005 humidity and 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 0x40) + * + * TODO: heater, fast mode, processed mode (temp. / linearity compensation) + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/pm.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define SI7005_STATUS 0x00 +#define SI7005_DATA 0x01 /* 16-bit, MSB */ +#define SI7005_CONFIG 0x03 +#define SI7005_ID 0x11 + +#define SI7005_STATUS_NRDY BIT(0) +#define SI7005_CONFIG_TEMP BIT(4) +#define SI7005_CONFIG_START BIT(0) + +#define SI7005_ID_7005 0x50 +#define SI7005_ID_7015 0xf0 + +struct si7005_data { +	struct i2c_client *client; +	struct mutex lock; +	u8 config; +}; + +static int si7005_read_measurement(struct si7005_data *data, bool temp) +{ +	int tries = 50; +	int ret; + +	mutex_lock(&data->lock); + +	ret = i2c_smbus_write_byte_data(data->client, SI7005_CONFIG, +		data->config | SI7005_CONFIG_START | +		(temp ? SI7005_CONFIG_TEMP : 0)); +	if (ret < 0) +		goto done; + +	while (tries-- > 0) { +		msleep(20); +		ret = i2c_smbus_read_byte_data(data->client, SI7005_STATUS); +		if (ret < 0) +			goto done; +		if (!(ret & SI7005_STATUS_NRDY)) +			break; +	} +	if (tries < 0) { +		ret = -EIO; +		goto done; +	} + +	ret = i2c_smbus_read_word_swapped(data->client, SI7005_DATA); + +done: +	mutex_unlock(&data->lock); + +	return ret; +} + +static int si7005_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, int *val, +			    int *val2, long mask) +{ +	struct si7005_data *data = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		ret = si7005_read_measurement(data, chan->type == IIO_TEMP); +		if (ret < 0) +			return ret; +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		if (chan->type == IIO_TEMP) { +			*val = 7; +			*val2 = 812500; +		} else { +			*val = 3; +			*val2 = 906250; +		} +		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_OFFSET: +		if (chan->type == IIO_TEMP) +			*val = -50 * 32 * 4; +		else +			*val = -24 * 16 * 16; +		return IIO_VAL_INT; +	default: +		break; +	} + +	return -EINVAL; +} + +static const struct iio_chan_spec si7005_channels[] = { +	{ +		.type = IIO_HUMIDITYRELATIVE, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), +	}, +	{ +		.type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET), +	} +}; + +static const struct iio_info si7005_info = { +	.read_raw = si7005_read_raw, +	.driver_module = THIS_MODULE, +}; + +static int si7005_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct iio_dev *indio_dev; +	struct si7005_data *data; +	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); +	i2c_set_clientdata(client, indio_dev); +	data->client = client; +	mutex_init(&data->lock); + +	indio_dev->dev.parent = &client->dev; +	indio_dev->name = dev_name(&client->dev); +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->info = &si7005_info; + +	indio_dev->channels = si7005_channels; +	indio_dev->num_channels = ARRAY_SIZE(si7005_channels); + +	ret = i2c_smbus_read_byte_data(client, SI7005_ID); +	if (ret < 0) +		return ret; +	if (ret != SI7005_ID_7005 && ret != SI7005_ID_7015) +		return -ENODEV; + +	ret = i2c_smbus_read_byte_data(client, SI7005_CONFIG); +	if (ret < 0) +		return ret; +	data->config = ret; + +	return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id si7005_id[] = { +	{ "si7005", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, si7005_id); + +static struct i2c_driver si7005_driver = { +	.driver = { +		.name	= "si7005", +		.owner	= THIS_MODULE, +	}, +	.probe = si7005_probe, +	.id_table = si7005_id, +}; +module_i2c_driver(si7005_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Silabs Si7005 humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 9b32253b824..5f0ea77fe71 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -30,9 +30,12 @@ int __iio_add_chan_devattr(const char *postfix,  						const char *buf,  						size_t len),  			   u64 mask, -			   bool generic, +			   enum iio_shared_by shared_by,  			   struct device *dev,  			   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 size, int *vals);  /* Event interface flags */  #define IIO_BUSY_BIT_POS 1 @@ -50,6 +53,7 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,  #define iio_buffer_read_first_n_outer_addr (&iio_buffer_read_first_n_outer)  void iio_disable_all_buffers(struct iio_dev *indio_dev); +void iio_buffer_wakeup_poll(struct iio_dev *indio_dev);  #else @@ -57,11 +61,13 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev);  #define iio_buffer_read_first_n_outer_addr NULL  static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {} +static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {}  #endif  int iio_device_register_eventset(struct iio_dev *indio_dev);  void iio_device_unregister_eventset(struct iio_dev *indio_dev); +void iio_device_wakeup_eventset(struct iio_dev *indio_dev);  int iio_event_getfd(struct iio_dev *indio_dev);  #endif diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 663e88a1a3c..2b0e45133e9 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -25,6 +25,8 @@ config ADIS16480  	  Say yes here to build support for Analog Devices ADIS16375, ADIS16480,  	  ADIS16485, ADIS16488 inertial sensors. +source "drivers/iio/imu/inv_mpu6050/Kconfig" +  endmenu  config IIO_ADIS_LIB @@ -38,5 +40,3 @@ config IIO_ADIS_LIB_BUFFER  	help  	  A set of buffer helper functions for the Analog Devices ADIS* device  	  family. - -source "drivers/iio/imu/inv_mpu6050/Kconfig" diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h index 2f8f9d63238..0916bf6b6c3 100644 --- a/drivers/iio/imu/adis16400.h +++ b/drivers/iio/imu/adis16400.h @@ -189,6 +189,7 @@ enum {  	ADIS16300_SCAN_INCLI_X,  	ADIS16300_SCAN_INCLI_Y,  	ADIS16400_SCAN_ADC, +	ADIS16400_SCAN_TIMESTAMP,  };  #ifdef CONFIG_IIO_BUFFER diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu/adis16400_buffer.c index 054c01d6e73..f2cf829e5df 100644 --- a/drivers/iio/imu/adis16400_buffer.c +++ b/drivers/iio/imu/adis16400_buffer.c @@ -82,13 +82,8 @@ irqreturn_t adis16400_trigger_handler(int irq, void *p)  		spi_setup(st->adis.spi);  	} -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (indio_dev->scan_timestamp) { -		void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); -		*(s64 *)b = pf->timestamp; -	} - -	iio_push_to_buffers(indio_dev, adis->buffer); +	iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, +		pf->timestamp);  	iio_trigger_notify_done(indio_dev->trig); diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400_core.c index 3fb7757a102..433583b6f80 100644 --- a/drivers/iio/imu/adis16400_core.c +++ b/drivers/iio/imu/adis16400_core.c @@ -281,7 +281,7 @@ static ssize_t adis16400_write_frequency(struct device *dev,  	st->variant->set_freq(st, val);  	mutex_unlock(&indio_dev->mlock); -	return ret ? ret : len; +	return len;  }  /* Power down the device */ @@ -632,7 +632,7 @@ static const struct iio_chan_spec adis16400_channels[] = {  	ADIS16400_MAGN_CHAN(Z, ADIS16400_ZMAGN_OUT, 14),  	ADIS16400_TEMP_CHAN(ADIS16400_TEMP_OUT, 12),  	ADIS16400_AUX_ADC_CHAN(ADIS16400_AUX_ADC, 12), -	IIO_CHAN_SOFT_TIMESTAMP(12) +	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),  };  static const struct iio_chan_spec adis16448_channels[] = { @@ -651,10 +651,15 @@ static const struct iio_chan_spec adis16448_channels[] = {  		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),  		.address = ADIS16448_BARO_OUT,  		.scan_index = ADIS16400_SCAN_BARO, -		.scan_type = IIO_ST('s', 16, 16, 0), +		.scan_type = { +			.sign = 's', +			.realbits = 16, +			.storagebits = 16, +			.endianness = IIO_BE, +		},  	},  	ADIS16400_TEMP_CHAN(ADIS16448_TEMP_OUT, 12), -	IIO_CHAN_SOFT_TIMESTAMP(11) +	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),  };  static const struct iio_chan_spec adis16350_channels[] = { @@ -672,7 +677,7 @@ static const struct iio_chan_spec adis16350_channels[] = {  	ADIS16400_MOD_TEMP_CHAN(X, ADIS16350_XTEMP_OUT, 12),  	ADIS16400_MOD_TEMP_CHAN(Y, ADIS16350_YTEMP_OUT, 12),  	ADIS16400_MOD_TEMP_CHAN(Z, ADIS16350_ZTEMP_OUT, 12), -	IIO_CHAN_SOFT_TIMESTAMP(11) +	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),  };  static const struct iio_chan_spec adis16300_channels[] = { @@ -685,7 +690,7 @@ static const struct iio_chan_spec adis16300_channels[] = {  	ADIS16400_AUX_ADC_CHAN(ADIS16300_AUX_ADC, 12),  	ADIS16400_INCLI_CHAN(X, ADIS16300_PITCH_OUT, 13),  	ADIS16400_INCLI_CHAN(Y, ADIS16300_ROLL_OUT, 13), -	IIO_CHAN_SOFT_TIMESTAMP(14) +	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),  };  static const struct iio_chan_spec adis16334_channels[] = { @@ -696,7 +701,7 @@ static const struct iio_chan_spec adis16334_channels[] = {  	ADIS16400_ACCEL_CHAN(Y, ADIS16400_YACCL_OUT, 14),  	ADIS16400_ACCEL_CHAN(Z, ADIS16400_ZACCL_OUT, 14),  	ADIS16400_TEMP_CHAN(ADIS16350_XTEMP_OUT, 12), -	IIO_CHAN_SOFT_TIMESTAMP(8) +	IIO_CHAN_SOFT_TIMESTAMP(ADIS16400_SCAN_TIMESTAMP),  };  static struct attribute *adis16400_attributes[] = { diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index 99d8e0b0dd3..cb32b593f1c 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -102,13 +102,8 @@ static irqreturn_t adis_trigger_handler(int irq, void *p)  		mutex_unlock(&adis->txrx_lock);  	} -	/* Guaranteed to be aligned with 8 byte boundary */ -	if (indio_dev->scan_timestamp) { -		void *b = adis->buffer + indio_dev->scan_bytes - sizeof(s64); -		*(s64 *)b = pf->timestamp; -	} - -	iio_push_to_buffers(indio_dev, adis->buffer); +	iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, +		pf->timestamp);  	iio_trigger_notify_done(indio_dev->trig); 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 df7f1e1157a..0c6517c94a9 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -12,7 +12,6 @@  */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/i2c.h>  #include <linux/err.h> @@ -117,7 +116,7 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask)  		return result;  	if (en) { -		/* Wait for output stablize */ +		/* Wait for output stabilize */  		msleep(INV_MPU6050_TEMP_UP_TIME);  		if (INV_MPU6050_BIT_PWR_GYRO_STBY == mask) {  			/* switch internal clock to PLL */ @@ -661,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, @@ -673,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) @@ -765,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 f38395529a4..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  }; @@ -126,35 +127,35 @@ struct inv_mpu6050_state {  #define INV_MPU6050_REG_SAMPLE_RATE_DIV     0x19  #define INV_MPU6050_REG_CONFIG              0x1A  #define INV_MPU6050_REG_GYRO_CONFIG         0x1B -#define INV_MPU6050_REG_ACCEL_CONFIG	    0x1C +#define INV_MPU6050_REG_ACCEL_CONFIG        0x1C  #define INV_MPU6050_REG_FIFO_EN             0x23 -#define INV_MPU6050_BIT_ACCEL_OUT                   0x08 -#define INV_MPU6050_BITS_GYRO_OUT                   0x70 +#define INV_MPU6050_BIT_ACCEL_OUT           0x08 +#define INV_MPU6050_BITS_GYRO_OUT           0x70  #define INV_MPU6050_REG_INT_ENABLE          0x38 -#define INV_MPU6050_BIT_DATA_RDY_EN                 0x01 -#define INV_MPU6050_BIT_DMP_INT_EN                  0x02 +#define INV_MPU6050_BIT_DATA_RDY_EN         0x01 +#define INV_MPU6050_BIT_DMP_INT_EN          0x02  #define INV_MPU6050_REG_RAW_ACCEL           0x3B  #define INV_MPU6050_REG_TEMPERATURE         0x41  #define INV_MPU6050_REG_RAW_GYRO            0x43  #define INV_MPU6050_REG_USER_CTRL           0x6A -#define INV_MPU6050_BIT_FIFO_RST                    0x04 -#define INV_MPU6050_BIT_DMP_RST                     0x08 -#define INV_MPU6050_BIT_I2C_MST_EN                  0x20 -#define INV_MPU6050_BIT_FIFO_EN                     0x40 -#define INV_MPU6050_BIT_DMP_EN                      0x80 +#define INV_MPU6050_BIT_FIFO_RST            0x04 +#define INV_MPU6050_BIT_DMP_RST             0x08 +#define INV_MPU6050_BIT_I2C_MST_EN          0x20 +#define INV_MPU6050_BIT_FIFO_EN             0x40 +#define INV_MPU6050_BIT_DMP_EN              0x80  #define INV_MPU6050_REG_PWR_MGMT_1          0x6B -#define INV_MPU6050_BIT_H_RESET                     0x80 -#define INV_MPU6050_BIT_SLEEP                       0x40 -#define INV_MPU6050_BIT_CLK_MASK                    0x7 +#define INV_MPU6050_BIT_H_RESET             0x80 +#define INV_MPU6050_BIT_SLEEP               0x40 +#define INV_MPU6050_BIT_CLK_MASK            0x7  #define INV_MPU6050_REG_PWR_MGMT_2          0x6C -#define INV_MPU6050_BIT_PWR_ACCL_STBY               0x38 -#define INV_MPU6050_BIT_PWR_GYRO_STBY               0x07 +#define INV_MPU6050_BIT_PWR_ACCL_STBY       0x38 +#define INV_MPU6050_BIT_PWR_GYRO_STBY       0x07  #define INV_MPU6050_REG_FIFO_COUNT_H        0x72  #define INV_MPU6050_REG_FIFO_R_W            0x74 @@ -180,10 +181,10 @@ struct inv_mpu6050_state {  /* init parameters */  #define INV_MPU6050_INIT_FIFO_RATE           50 -#define INV_MPU6050_TIME_STAMP_TOR                        5 -#define INV_MPU6050_MAX_FIFO_RATE                         1000 -#define INV_MPU6050_MIN_FIFO_RATE                         4 -#define INV_MPU6050_ONE_K_HZ                              1000 +#define INV_MPU6050_TIME_STAMP_TOR           5 +#define INV_MPU6050_MAX_FIFO_RATE            1000 +#define INV_MPU6050_MIN_FIFO_RATE            4 +#define INV_MPU6050_ONE_K_HZ                 1000  /* scan element definition */  enum inv_mpu6050_scan { diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index 7da0832f187..0cd306a72a6 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -12,7 +12,6 @@  */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/slab.h>  #include <linux/i2c.h>  #include <linux/err.h> @@ -124,7 +123,6 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)  	u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];  	u16 fifo_count;  	s64 timestamp; -	u64 *tmp;  	mutex_lock(&indio_dev->mlock);  	if (!(st->chip_config.accl_fifo_enable | @@ -170,9 +168,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)  		if (0 == result)  			timestamp = 0; -		tmp = (u64 *)data; -		tmp[DIV_ROUND_UP(bytes_per_datum, 8)] = timestamp; -		result = iio_push_to_buffers(indio_dev, data); +		result = iio_push_to_buffers_with_timestamp(indio_dev, data, +			timestamp);  		if (result)  			goto flush_fifo;  		fifo_count -= bytes_per_datum; diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2710f7245c3..9f1a1400990 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -20,6 +20,7 @@  #include <linux/cdev.h>  #include <linux/slab.h>  #include <linux/poll.h> +#include <linux/sched.h>  #include <linux/iio/iio.h>  #include "iio_core.h" @@ -31,16 +32,17 @@ static const char * const iio_endian_prefix[] = {  	[IIO_LE] = "le",  }; -static bool iio_buffer_is_active(struct iio_dev *indio_dev, -				 struct iio_buffer *buf) +static bool iio_buffer_is_active(struct iio_buffer *buf)  { -	struct list_head *p; +	return !list_empty(&buf->buffer_list); +} -	list_for_each(p, &indio_dev->buffer_list) -		if (p == &buf->buffer_list) -			return true; +static bool iio_buffer_data_available(struct iio_buffer *buf) +{ +	if (buf->access->data_available) +		return buf->access->data_available(buf); -	return false; +	return buf->stufftoread;  }  /** @@ -54,10 +56,34 @@ ssize_t iio_buffer_read_first_n_outer(struct file *filp, char __user *buf,  {  	struct iio_dev *indio_dev = filp->private_data;  	struct iio_buffer *rb = indio_dev->buffer; +	int ret; + +	if (!indio_dev->info) +		return -ENODEV;  	if (!rb || !rb->access->read_first_n)  		return -EINVAL; -	return rb->access->read_first_n(rb, n, buf); + +	do { +		if (!iio_buffer_data_available(rb)) { +			if (filp->f_flags & O_NONBLOCK) +				return -EAGAIN; + +			ret = wait_event_interruptible(rb->pollq, +					iio_buffer_data_available(rb) || +					indio_dev->info == NULL); +			if (ret) +				return ret; +			if (indio_dev->info == NULL) +				return -ENODEV; +		} + +		ret = rb->access->read_first_n(rb, n, buf); +		if (ret == 0 && (filp->f_flags & O_NONBLOCK)) +			ret = -EAGAIN; +	 } while (ret == 0); + +	return ret;  }  /** @@ -69,17 +95,37 @@ unsigned int iio_buffer_poll(struct file *filp,  	struct iio_dev *indio_dev = filp->private_data;  	struct iio_buffer *rb = indio_dev->buffer; +	if (!indio_dev->info) +		return -ENODEV; +  	poll_wait(filp, &rb->pollq, wait); -	if (rb->stufftoread) +	if (iio_buffer_data_available(rb))  		return POLLIN | POLLRDNORM;  	/* need a way of knowing if there may be enough data... */  	return 0;  } +/** + * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue + * @indio_dev: The IIO device + * + * Wakes up the event waitqueue used for poll(). Should usually + * be called when the device is unregistered. + */ +void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) +{ +	if (!indio_dev->buffer) +		return; + +	wake_up(&indio_dev->buffer->pollq); +} +  void iio_buffer_init(struct iio_buffer *buffer)  {  	INIT_LIST_HEAD(&buffer->demux_list); +	INIT_LIST_HEAD(&buffer->buffer_list);  	init_waitqueue_head(&buffer->pollq); +	kref_init(&buffer->ref);  }  EXPORT_SYMBOL(iio_buffer_init); @@ -104,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, @@ -119,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); @@ -146,7 +202,7 @@ static ssize_t iio_scan_el_store(struct device *dev,  	if (ret < 0)  		return ret;  	mutex_lock(&indio_dev->mlock); -	if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { +	if (iio_buffer_is_active(indio_dev->buffer)) {  		ret = -EBUSY;  		goto error_ret;  	} @@ -192,7 +248,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev,  		return ret;  	mutex_lock(&indio_dev->mlock); -	if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { +	if (iio_buffer_is_active(indio_dev->buffer)) {  		ret = -EBUSY;  		goto error_ret;  	} @@ -214,11 +270,11 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,  				     &iio_show_scan_index,  				     NULL,  				     0, -				     0, +				     IIO_SEPARATE,  				     &indio_dev->dev,  				     &buffer->scan_el_dev_attr_list);  	if (ret) -		goto error_ret; +		return ret;  	attrcount++;  	ret = __iio_add_chan_devattr("type",  				     chan, @@ -229,7 +285,7 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,  				     &indio_dev->dev,  				     &buffer->scan_el_dev_attr_list);  	if (ret) -		goto error_ret; +		return ret;  	attrcount++;  	if (chan->type != IIO_TIMESTAMP)  		ret = __iio_add_chan_devattr("en", @@ -249,29 +305,13 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev,  					     0,  					     &indio_dev->dev,  					     &buffer->scan_el_dev_attr_list); +	if (ret) +		return ret;  	attrcount++;  	ret = attrcount; -error_ret:  	return ret;  } -static void iio_buffer_remove_and_free_scan_dev_attr(struct iio_dev *indio_dev, -						     struct iio_dev_attr *p) -{ -	kfree(p->dev_attr.attr.name); -	kfree(p); -} - -static void __iio_buffer_attr_cleanup(struct iio_dev *indio_dev) -{ -	struct iio_dev_attr *p, *n; -	struct iio_buffer *buffer = indio_dev->buffer; - -	list_for_each_entry_safe(p, n, -				 &buffer->scan_el_dev_attr_list, l) -		iio_buffer_remove_and_free_scan_dev_attr(indio_dev, p); -} -  static const char * const iio_scan_elements_group_name = "scan_elements";  int iio_buffer_register(struct iio_dev *indio_dev, @@ -348,7 +388,7 @@ int iio_buffer_register(struct iio_dev *indio_dev,  error_free_scan_mask:  	kfree(buffer->scan_mask);  error_cleanup_dynamic: -	__iio_buffer_attr_cleanup(indio_dev); +	iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);  	return ret;  } @@ -358,7 +398,7 @@ void iio_buffer_unregister(struct iio_dev *indio_dev)  {  	kfree(indio_dev->buffer->scan_mask);  	kfree(indio_dev->buffer->scan_el_group.attrs); -	__iio_buffer_attr_cleanup(indio_dev); +	iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);  }  EXPORT_SYMBOL(iio_buffer_unregister); @@ -396,7 +436,7 @@ ssize_t iio_buffer_write_length(struct device *dev,  			return len;  	mutex_lock(&indio_dev->mlock); -	if (iio_buffer_is_active(indio_dev, indio_dev->buffer)) { +	if (iio_buffer_is_active(indio_dev->buffer)) {  		ret = -EBUSY;  	} else {  		if (buffer->access->set_length) @@ -414,13 +454,11 @@ ssize_t iio_buffer_show_enable(struct device *dev,  			       char *buf)  {  	struct iio_dev *indio_dev = dev_to_iio_dev(dev); -	return sprintf(buf, "%d\n", -		       iio_buffer_is_active(indio_dev, -					    indio_dev->buffer)); +	return sprintf(buf, "%d\n", iio_buffer_is_active(indio_dev->buffer));  }  EXPORT_SYMBOL(iio_buffer_show_enable); -/* note NULL used as error indicator as it doesn't make sense. */ +/* Note NULL used as error indicator as it doesn't make sense. */  static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,  					  unsigned int masklength,  					  const unsigned long *mask) @@ -435,8 +473,8 @@ static const unsigned long *iio_scan_mask_match(const unsigned long *av_masks,  	return NULL;  } -static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask, -				  bool timestamp) +static int iio_compute_scan_bytes(struct iio_dev *indio_dev, +				const unsigned long *mask, bool timestamp)  {  	const struct iio_chan_spec *ch;  	unsigned bytes = 0; @@ -446,20 +484,41 @@ static int iio_compute_scan_bytes(struct iio_dev *indio_dev, const long *mask,  	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;  	}  	return bytes;  } +static void iio_buffer_activate(struct iio_dev *indio_dev, +	struct iio_buffer *buffer) +{ +	iio_buffer_get(buffer); +	list_add(&buffer->buffer_list, &indio_dev->buffer_list); +} + +static void iio_buffer_deactivate(struct iio_buffer *buffer) +{ +	list_del_init(&buffer->buffer_list); +	iio_buffer_put(buffer); +} +  void iio_disable_all_buffers(struct iio_dev *indio_dev)  {  	struct iio_buffer *buffer, *_buffer; @@ -472,14 +531,31 @@ void iio_disable_all_buffers(struct iio_dev *indio_dev)  	list_for_each_entry_safe(buffer, _buffer,  			&indio_dev->buffer_list, buffer_list) -		list_del_init(&buffer->buffer_list); +		iio_buffer_deactivate(buffer);  	indio_dev->currentmode = INDIO_DIRECT_MODE;  	if (indio_dev->setup_ops->postdisable)  		indio_dev->setup_ops->postdisable(indio_dev); + +	if (indio_dev->available_scan_masks == NULL) +		kfree(indio_dev->active_scan_mask);  } -int iio_update_buffers(struct iio_dev *indio_dev, +static void iio_buffer_update_bytes_per_datum(struct iio_dev *indio_dev, +	struct iio_buffer *buffer) +{ +	unsigned int bytes; + +	if (!buffer->access->set_bytes_per_datum) +		return; + +	bytes = iio_compute_scan_bytes(indio_dev, buffer->scan_mask, +		buffer->scan_timestamp); + +	buffer->access->set_bytes_per_datum(buffer, bytes); +} + +static int __iio_update_buffers(struct iio_dev *indio_dev,  		       struct iio_buffer *insert_buffer,  		       struct iio_buffer *remove_buffer)  { @@ -494,13 +570,13 @@ int iio_update_buffers(struct iio_dev *indio_dev,  		if (indio_dev->setup_ops->predisable) {  			ret = indio_dev->setup_ops->predisable(indio_dev);  			if (ret) -				goto error_ret; +				return ret;  		}  		indio_dev->currentmode = INDIO_DIRECT_MODE;  		if (indio_dev->setup_ops->postdisable) {  			ret = indio_dev->setup_ops->postdisable(indio_dev);  			if (ret) -				goto error_ret; +				return ret;  		}  	}  	/* Keep a copy of current setup to allow roll back */ @@ -509,9 +585,9 @@ int iio_update_buffers(struct iio_dev *indio_dev,  		indio_dev->active_scan_mask = NULL;  	if (remove_buffer) -		list_del(&remove_buffer->buffer_list); +		iio_buffer_deactivate(remove_buffer);  	if (insert_buffer) -		list_add(&insert_buffer->buffer_list, &indio_dev->buffer_list); +		iio_buffer_activate(indio_dev, insert_buffer);  	/* If no buffers in list, we are done */  	if (list_empty(&indio_dev->buffer_list)) { @@ -521,7 +597,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  		return 0;  	} -	/* What scan mask do we actually have ?*/ +	/* What scan mask do we actually have? */  	compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),  				sizeof(long), GFP_KERNEL);  	if (compound_mask == NULL) { @@ -546,7 +622,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  			 * Roll back.  			 * Note can only occur when adding a buffer.  			 */ -			list_del(&insert_buffer->buffer_list); +			iio_buffer_deactivate(insert_buffer);  			if (old_mask) {  				indio_dev->active_scan_mask = old_mask;  				success = -EINVAL; @@ -554,7 +630,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  			else {  				kfree(compound_mask);  				ret = -EINVAL; -				goto error_ret; +				return ret;  			}  		}  	} else { @@ -576,7 +652,8 @@ int iio_update_buffers(struct iio_dev *indio_dev,  		iio_compute_scan_bytes(indio_dev,  				       indio_dev->active_scan_mask,  				       indio_dev->scan_timestamp); -	list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) +	list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) { +		iio_buffer_update_bytes_per_datum(indio_dev, buffer);  		if (buffer->access->request_update) {  			ret = buffer->access->request_update(buffer);  			if (ret) { @@ -585,6 +662,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  				goto error_run_postdisable;  			}  		} +	}  	if (indio_dev->info->update_scan_mode) {  		ret = indio_dev->info  			->update_scan_mode(indio_dev, @@ -594,7 +672,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  			goto error_run_postdisable;  		}  	} -	/* Definitely possible for devices to support both of these.*/ +	/* Definitely possible for devices to support both of these. */  	if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {  		if (!indio_dev->trig) {  			printk(KERN_INFO "Buffer not started: no trigger\n"); @@ -605,7 +683,7 @@ int iio_update_buffers(struct iio_dev *indio_dev,  		indio_dev->currentmode = INDIO_BUFFER_TRIGGERED;  	} else if (indio_dev->modes & INDIO_BUFFER_HARDWARE) {  		indio_dev->currentmode = INDIO_BUFFER_HARDWARE; -	} else { /* should never be reached */ +	} else { /* Should never be reached */  		ret = -EINVAL;  		goto error_run_postdisable;  	} @@ -635,12 +713,46 @@ error_run_postdisable:  	if (indio_dev->setup_ops->postdisable)  		indio_dev->setup_ops->postdisable(indio_dev);  error_remove_inserted: -  	if (insert_buffer) -		list_del(&insert_buffer->buffer_list); +		iio_buffer_deactivate(insert_buffer);  	indio_dev->active_scan_mask = old_mask;  	kfree(compound_mask); -error_ret: +	return ret; +} + +int iio_update_buffers(struct iio_dev *indio_dev, +		       struct iio_buffer *insert_buffer, +		       struct iio_buffer *remove_buffer) +{ +	int ret; + +	if (insert_buffer == remove_buffer) +		return 0; + +	mutex_lock(&indio_dev->info_exist_lock); +	mutex_lock(&indio_dev->mlock); + +	if (insert_buffer && iio_buffer_is_active(insert_buffer)) +		insert_buffer = NULL; + +	if (remove_buffer && !iio_buffer_is_active(remove_buffer)) +		remove_buffer = NULL; + +	if (!insert_buffer && !remove_buffer) { +		ret = 0; +		goto out_unlock; +	} + +	if (indio_dev->info == NULL) { +		ret = -ENODEV; +		goto out_unlock; +	} + +	ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer); + +out_unlock: +	mutex_unlock(&indio_dev->mlock); +	mutex_unlock(&indio_dev->info_exist_lock);  	return ret;  } @@ -654,7 +766,6 @@ ssize_t iio_buffer_store_enable(struct device *dev,  	int ret;  	bool requested_state;  	struct iio_dev *indio_dev = dev_to_iio_dev(dev); -	struct iio_buffer *pbuf = indio_dev->buffer;  	bool inlist;  	ret = strtobool(buf, &requested_state); @@ -664,16 +775,16 @@ ssize_t iio_buffer_store_enable(struct device *dev,  	mutex_lock(&indio_dev->mlock);  	/* Find out if it is in the list */ -	inlist = iio_buffer_is_active(indio_dev, pbuf); +	inlist = iio_buffer_is_active(indio_dev->buffer);  	/* Already in desired state */  	if (inlist == requested_state)  		goto done;  	if (requested_state) -		ret = iio_update_buffers(indio_dev, +		ret = __iio_update_buffers(indio_dev,  					 indio_dev->buffer, NULL);  	else -		ret = iio_update_buffers(indio_dev, +		ret = __iio_update_buffers(indio_dev,  					 NULL, indio_dev->buffer);  	if (ret < 0) @@ -684,24 +795,6 @@ done:  }  EXPORT_SYMBOL(iio_buffer_store_enable); -int iio_sw_buffer_preenable(struct iio_dev *indio_dev) -{ -	struct iio_buffer *buffer; -	unsigned bytes; -	dev_dbg(&indio_dev->dev, "%s\n", __func__); - -	list_for_each_entry(buffer, &indio_dev->buffer_list, buffer_list) -		if (buffer->access->set_bytes_per_datum) { -			bytes = iio_compute_scan_bytes(indio_dev, -						       buffer->scan_mask, -						       buffer->scan_timestamp); - -			buffer->access->set_bytes_per_datum(buffer, bytes); -		} -	return 0; -} -EXPORT_SYMBOL(iio_sw_buffer_preenable); -  /**   * iio_validate_scan_mask_onehot() - Validates that exactly one channel is selected   * @indio_dev: the iio device @@ -729,6 +822,7 @@ static bool iio_validate_scan_mask(struct iio_dev *indio_dev,  /**   * iio_scan_mask_set() - set particular bit in the scan mask + * @indio_dev: the iio device   * @buffer: the buffer whose scan mask we are interested in   * @bit: the bit to be set.   * @@ -749,7 +843,7 @@ int iio_scan_mask_set(struct iio_dev *indio_dev,  	if (trialmask == NULL)  		return -ENOMEM;  	if (!indio_dev->masklength) { -		WARN_ON("trying to set scanmask prior to registering buffer\n"); +		WARN_ON("Trying to set scanmask prior to registering buffer\n");  		goto err_invalid_mask;  	}  	bitmap_copy(trialmask, buffer->scan_mask, indio_dev->masklength); @@ -786,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); @@ -804,8 +899,8 @@ struct iio_demux_table {  	struct list_head l;  }; -static unsigned char *iio_demux(struct iio_buffer *buffer, -				 unsigned char *datain) +static const void *iio_demux(struct iio_buffer *buffer, +				 const void *datain)  {  	struct iio_demux_table *t; @@ -818,9 +913,9 @@ static unsigned char *iio_demux(struct iio_buffer *buffer,  	return buffer->demux_bounce;  } -static int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data) +static int iio_push_to_buffer(struct iio_buffer *buffer, const void *data)  { -	unsigned char *dataout = iio_demux(buffer, data); +	const void *dataout = iio_demux(buffer, data);  	return buffer->access->store_to(buffer, dataout);  } @@ -835,7 +930,7 @@ static void iio_buffer_demux_free(struct iio_buffer *buffer)  } -int iio_push_to_buffers(struct iio_dev *indio_dev, unsigned char *data) +int iio_push_to_buffers(struct iio_dev *indio_dev, const void *data)  {  	int ret;  	struct iio_buffer *buf; @@ -871,7 +966,7 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev,  	/* Now we have the two masks, work from least sig and build up sizes */  	for_each_set_bit(out_ind, -			 indio_dev->active_scan_mask, +			 buffer->scan_mask,  			 indio_dev->masklength) {  		in_ind = find_next_bit(indio_dev->active_scan_mask,  				       indio_dev->masklength, @@ -881,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) @@ -893,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) @@ -914,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) @@ -958,3 +1065,45 @@ error_clear_mux_table:  	return ret;  }  EXPORT_SYMBOL_GPL(iio_update_demux); + +/** + * iio_buffer_release() - Free a buffer's resources + * @ref: Pointer to the kref embedded in the iio_buffer struct + * + * This function is called when the last reference to the buffer has been + * dropped. It will typically free all resources allocated by the buffer. Do not + * call this function manually, always use iio_buffer_put() when done using a + * buffer. + */ +static void iio_buffer_release(struct kref *ref) +{ +	struct iio_buffer *buffer = container_of(ref, struct iio_buffer, ref); + +	buffer->access->release(buffer); +} + +/** + * iio_buffer_get() - Grab a reference to the buffer + * @buffer: The buffer to grab a reference for, may be NULL + * + * Returns the pointer to the buffer that was passed into the function. + */ +struct iio_buffer *iio_buffer_get(struct iio_buffer *buffer) +{ +	if (buffer) +		kref_get(&buffer->ref); + +	return buffer; +} +EXPORT_SYMBOL_GPL(iio_buffer_get); + +/** + * iio_buffer_put() - Release the reference to the buffer + * @buffer: The buffer to release the reference for, may be NULL + */ +void iio_buffer_put(struct iio_buffer *buffer) +{ +	if (buffer) +		kref_put(&buffer->ref, iio_buffer_release); +} +EXPORT_SYMBOL_GPL(iio_buffer_put); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 8e84cd522e4..4b1f375c565 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -9,6 +9,8 @@   * Based on elements of hwmon and input subsystems.   */ +#define pr_fmt(fmt) "iio-core: " fmt +  #include <linux/kernel.h>  #include <linux/module.h>  #include <linux/idr.h> @@ -28,6 +30,7 @@  #include "iio_core_trigger.h"  #include <linux/iio/sysfs.h>  #include <linux/iio/events.h> +#include <linux/iio/buffer.h>  /* IDA to assign each registered device a unique id */  static DEFINE_IDA(iio_ida); @@ -66,6 +69,7 @@ static const char * const iio_chan_type_name_spec[] = {  	[IIO_ALTVOLTAGE] = "altvoltage",  	[IIO_CCT] = "cct",  	[IIO_PRESSURE] = "pressure", +	[IIO_HUMIDITYRELATIVE] = "humidityrelative",  };  static const char * const iio_modifier_names[] = { @@ -80,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 */ @@ -101,8 +108,14 @@ static const char * const iio_chan_info_postfix[] = {  	[IIO_CHAN_INFO_PHASE] = "phase",  	[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",  	[IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", +	[IIO_CHAN_INFO_INT_TIME] = "integration_time",  }; +/** + * iio_find_channel_from_si() - get channel from its scan index + * @indio_dev:		device + * @si:			scan index to match + */  const struct iio_chan_spec  *iio_find_channel_from_si(struct iio_dev *indio_dev, int si)  { @@ -130,16 +143,13 @@ static int __init iio_init(void)  	/* Register sysfs bus */  	ret  = bus_register(&iio_bus_type);  	if (ret < 0) { -		printk(KERN_ERR -		       "%s could not register bus type\n", -			__FILE__); +		pr_err("could not register bus type\n");  		goto error_nothing;  	}  	ret = alloc_chrdev_region(&iio_devt, 0, IIO_DEV_MAX, "iio");  	if (ret < 0) { -		printk(KERN_ERR "%s: failed to allocate char dev region\n", -		       __FILE__); +		pr_err("failed to allocate char dev region\n");  		goto error_unregister_bus_type;  	} @@ -333,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); @@ -361,53 +371,88 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,  }  EXPORT_SYMBOL_GPL(iio_enum_write); -static ssize_t iio_read_channel_info(struct device *dev, -				     struct device_attribute *attr, -				     char *buf) +/** + * iio_format_value() - Formats a IIO value into its string representation + * @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. + * @vals: pointer to the values, exact meaning depends on the type parameter. + */ +ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals)  { -	struct iio_dev *indio_dev = dev_to_iio_dev(dev); -	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);  	unsigned long long tmp; -	int val, val2;  	bool scale_db = false; -	int ret = indio_dev->info->read_raw(indio_dev, this_attr->c, -					    &val, &val2, this_attr->address); -	if (ret < 0) -		return ret; - -	switch (ret) { +	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;  	}  } +static ssize_t iio_read_channel_info(struct device *dev, +				     struct device_attribute *attr, +				     char *buf) +{ +	struct iio_dev *indio_dev = dev_to_iio_dev(dev); +	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); +	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_len, vals); +} +  /**   * iio_str_to_fixpoint() - Parse a fixed-point number from a string   * @str: The string to parse @@ -516,14 +561,15 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,  						struct device_attribute *attr,  						const char *buf,  						size_t len), -			   bool generic) +			   enum iio_shared_by shared_by)  { -	int ret; -	char *name_format, *full_postfix; +	int ret = 0; +	char *name = NULL; +	char *full_postfix;  	sysfs_attr_init(&dev_attr->attr);  	/* Build up postfix of <extend_name>_<modifier>_postfix */ -	if (chan->modified && !generic) { +	if (chan->modified && (shared_by == IIO_SEPARATE)) {  		if (chan->extend_name)  			full_postfix = kasprintf(GFP_KERNEL, "%s_%s_%s",  						 iio_modifier_names[chan @@ -536,7 +582,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,  								    ->channel2],  						 postfix);  	} else { -		if (chan->extend_name == NULL) +		if (chan->extend_name == NULL || shared_by != IIO_SEPARATE)  			full_postfix = kstrdup(postfix, GFP_KERNEL);  		else  			full_postfix = kasprintf(GFP_KERNEL, @@ -544,66 +590,79 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,  						 chan->extend_name,  						 postfix);  	} -	if (full_postfix == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} +	if (full_postfix == NULL) +		return -ENOMEM;  	if (chan->differential) { /* Differential can not have modifier */ -		if (generic) -			name_format -				= kasprintf(GFP_KERNEL, "%s_%s-%s_%s", +		switch (shared_by) { +		case IIO_SHARED_BY_ALL: +			name = kasprintf(GFP_KERNEL, "%s", full_postfix); +			break; +		case IIO_SHARED_BY_DIR: +			name = kasprintf(GFP_KERNEL, "%s_%s", +						iio_direction[chan->output], +						full_postfix); +			break; +		case IIO_SHARED_BY_TYPE: +			name = kasprintf(GFP_KERNEL, "%s_%s-%s_%s",  					    iio_direction[chan->output],  					    iio_chan_type_name_spec[chan->type],  					    iio_chan_type_name_spec[chan->type],  					    full_postfix); -		else if (chan->indexed) -			name_format -				= kasprintf(GFP_KERNEL, "%s_%s%d-%s%d_%s", +			break; +		case IIO_SEPARATE: +			if (!chan->indexed) { +				WARN_ON("Differential channels must be indexed\n"); +				ret = -EINVAL; +				goto error_free_full_postfix; +			} +			name = kasprintf(GFP_KERNEL, +					    "%s_%s%d-%s%d_%s",  					    iio_direction[chan->output],  					    iio_chan_type_name_spec[chan->type],  					    chan->channel,  					    iio_chan_type_name_spec[chan->type],  					    chan->channel2,  					    full_postfix); -		else { -			WARN_ON("Differential channels must be indexed\n"); -			ret = -EINVAL; -			goto error_free_full_postfix; +			break;  		}  	} else { /* Single ended */ -		if (generic) -			name_format -				= kasprintf(GFP_KERNEL, "%s_%s_%s", -					    iio_direction[chan->output], -					    iio_chan_type_name_spec[chan->type], -					    full_postfix); -		else if (chan->indexed) -			name_format -				= kasprintf(GFP_KERNEL, "%s_%s%d_%s", -					    iio_direction[chan->output], -					    iio_chan_type_name_spec[chan->type], -					    chan->channel, -					    full_postfix); -		else -			name_format -				= kasprintf(GFP_KERNEL, "%s_%s_%s", +		switch (shared_by) { +		case IIO_SHARED_BY_ALL: +			name = kasprintf(GFP_KERNEL, "%s", full_postfix); +			break; +		case IIO_SHARED_BY_DIR: +			name = kasprintf(GFP_KERNEL, "%s_%s", +						iio_direction[chan->output], +						full_postfix); +			break; +		case IIO_SHARED_BY_TYPE: +			name = kasprintf(GFP_KERNEL, "%s_%s_%s",  					    iio_direction[chan->output],  					    iio_chan_type_name_spec[chan->type],  					    full_postfix); +			break; + +		case IIO_SEPARATE: +			if (chan->indexed) +				name = kasprintf(GFP_KERNEL, "%s_%s%d_%s", +						    iio_direction[chan->output], +						    iio_chan_type_name_spec[chan->type], +						    chan->channel, +						    full_postfix); +			else +				name = kasprintf(GFP_KERNEL, "%s_%s_%s", +						    iio_direction[chan->output], +						    iio_chan_type_name_spec[chan->type], +						    full_postfix); +			break; +		}  	} -	if (name_format == NULL) { +	if (name == NULL) {  		ret = -ENOMEM;  		goto error_free_full_postfix;  	} -	dev_attr->attr.name = kasprintf(GFP_KERNEL, -					name_format, -					chan->channel, -					chan->channel2); -	if (dev_attr->attr.name == NULL) { -		ret = -ENOMEM; -		goto error_free_name_format; -	} +	dev_attr->attr.name = name;  	if (readfunc) {  		dev_attr->attr.mode |= S_IRUGO; @@ -614,16 +673,10 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,  		dev_attr->attr.mode |= S_IWUSR;  		dev_attr->store = writefunc;  	} -	kfree(name_format); -	kfree(full_postfix); - -	return 0; -error_free_name_format: -	kfree(name_format);  error_free_full_postfix:  	kfree(full_postfix); -error_ret: +  	return ret;  } @@ -642,21 +695,19 @@ int __iio_add_chan_devattr(const char *postfix,  						const char *buf,  						size_t len),  			   u64 mask, -			   bool generic, +			   enum iio_shared_by shared_by,  			   struct device *dev,  			   struct list_head *attr_list)  {  	int ret;  	struct iio_dev_attr *iio_attr, *t; -	iio_attr = kzalloc(sizeof *iio_attr, GFP_KERNEL); -	if (iio_attr == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} +	iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL); +	if (iio_attr == NULL) +		return -ENOMEM;  	ret = __iio_device_attr_init(&iio_attr->dev_attr,  				     postfix, chan, -				     readfunc, writefunc, generic); +				     readfunc, writefunc, shared_by);  	if (ret)  		goto error_iio_dev_attr_free;  	iio_attr->c = chan; @@ -664,7 +715,7 @@ int __iio_add_chan_devattr(const char *postfix,  	list_for_each_entry(t, attr_list, l)  		if (strcmp(t->dev_attr.attr.name,  			   iio_attr->dev_attr.attr.name) == 0) { -			if (!generic) +			if (shared_by == IIO_SEPARATE)  				dev_err(dev, "tried to double register : %s\n",  					t->dev_attr.attr.name);  			ret = -EBUSY; @@ -678,50 +729,73 @@ error_device_attr_deinit:  	__iio_device_attr_deinit(&iio_attr->dev_attr);  error_iio_dev_attr_free:  	kfree(iio_attr); -error_ret:  	return ret;  } -static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, -					struct iio_chan_spec const *chan) +static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, +					 struct iio_chan_spec const *chan, +					 enum iio_shared_by shared_by, +					 const long *infomask)  { -	int ret, attrcount = 0; -	int i; -	const struct iio_chan_spec_ext_info *ext_info; +	int i, ret, attrcount = 0; -	if (chan->channel < 0) -		return 0; -	for_each_set_bit(i, &chan->info_mask_separate, sizeof(long)*8) { -		ret = __iio_add_chan_devattr(iio_chan_info_postfix[i], -					     chan, -					     &iio_read_channel_info, -					     &iio_write_channel_info, -					     i, -					     0, -					     &indio_dev->dev, -					     &indio_dev->channel_attr_list); -		if (ret < 0) -			goto error_ret; -		attrcount++; -	} -	for_each_set_bit(i, &chan->info_mask_shared_by_type, sizeof(long)*8) { +	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,  					     &iio_write_channel_info,  					     i, -					     1, +					     shared_by,  					     &indio_dev->dev,  					     &indio_dev->channel_attr_list); -		if (ret == -EBUSY) { -			ret = 0; +		if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE))  			continue; -		} else if (ret < 0) { -			goto error_ret; -		} +		else if (ret < 0) +			return ret;  		attrcount++;  	} +	return attrcount; +} + +static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, +					struct iio_chan_spec const *chan) +{ +	int ret, attrcount = 0; +	const struct iio_chan_spec_ext_info *ext_info; + +	if (chan->channel < 0) +		return 0; +	ret = iio_device_add_info_mask_type(indio_dev, chan, +					    IIO_SEPARATE, +					    &chan->info_mask_separate); +	if (ret < 0) +		return ret; +	attrcount += ret; + +	ret = iio_device_add_info_mask_type(indio_dev, chan, +					    IIO_SHARED_BY_TYPE, +					    &chan->info_mask_shared_by_type); +	if (ret < 0) +		return ret; +	attrcount += ret; + +	ret = iio_device_add_info_mask_type(indio_dev, chan, +					    IIO_SHARED_BY_DIR, +					    &chan->info_mask_shared_by_dir); +	if (ret < 0) +		return ret; +	attrcount += ret; + +	ret = iio_device_add_info_mask_type(indio_dev, chan, +					    IIO_SHARED_BY_ALL, +					    &chan->info_mask_shared_by_all); +	if (ret < 0) +		return ret; +	attrcount += ret; +  	if (chan->ext_info) {  		unsigned int i = 0;  		for (ext_info = chan->ext_info; ext_info->name; ext_info++) { @@ -740,22 +814,31 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,  				continue;  			if (ret) -				goto error_ret; +				return ret;  			attrcount++;  		}  	} -	ret = attrcount; -error_ret: -	return ret; +	return attrcount;  } -static void iio_device_remove_and_free_read_attr(struct iio_dev *indio_dev, -						 struct iio_dev_attr *p) +/** + * iio_free_chan_devattr_list() - Free a list of IIO device attributes + * @attr_list: List of IIO device attributes + * + * This function frees the memory allocated for each of the IIO device + * attributes in the list. Note: if you want to reuse the list after calling + * this function you have to reinitialize it using INIT_LIST_HEAD(). + */ +void iio_free_chan_devattr_list(struct list_head *attr_list)  { -	kfree(p->dev_attr.attr.name); -	kfree(p); +	struct iio_dev_attr *p, *n; + +	list_for_each_entry_safe(p, n, attr_list, l) { +		kfree(p->dev_attr.attr.name); +		kfree(p); +	}  }  static ssize_t iio_show_dev_name(struct device *dev, @@ -763,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); @@ -771,7 +854,7 @@ static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL);  static int iio_device_register_sysfs(struct iio_dev *indio_dev)  {  	int i, ret = 0, attrcount, attrn, attrcount_orig = 0; -	struct iio_dev_attr *p, *n; +	struct iio_dev_attr *p;  	struct attribute **attr;  	/* First count elements in any existing group */ @@ -824,11 +907,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)  	return 0;  error_clear_attrs: -	list_for_each_entry_safe(p, n, -				 &indio_dev->channel_attr_list, l) { -		list_del(&p->l); -		iio_device_remove_and_free_read_attr(indio_dev, p); -	} +	iio_free_chan_devattr_list(&indio_dev->channel_attr_list);  	return ret;  } @@ -836,12 +915,7 @@ error_clear_attrs:  static void iio_device_unregister_sysfs(struct iio_dev *indio_dev)  { -	struct iio_dev_attr *p, *n; - -	list_for_each_entry_safe(p, n, &indio_dev->channel_attr_list, l) { -		list_del(&p->l); -		iio_device_remove_and_free_read_attr(indio_dev, p); -	} +	iio_free_chan_devattr_list(&indio_dev->channel_attr_list);  	kfree(indio_dev->chan_attr_group.attrs);  } @@ -852,7 +926,8 @@ static void iio_dev_release(struct device *device)  		iio_device_unregister_trigger_consumer(indio_dev);  	iio_device_unregister_eventset(indio_dev);  	iio_device_unregister_sysfs(indio_dev); -	iio_device_unregister_debugfs(indio_dev); + +	iio_buffer_put(indio_dev->buffer);  	ida_simple_remove(&iio_ida, indio_dev->id);  	kfree(indio_dev); @@ -863,6 +938,10 @@ struct device_type iio_device_type = {  	.release = iio_dev_release,  }; +/** + * iio_device_alloc() - allocate an iio_dev from a driver + * @sizeof_priv:	Space to allocate for private structure. + **/  struct iio_dev *iio_device_alloc(int sizeof_priv)  {  	struct iio_dev *dev; @@ -891,7 +970,7 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)  		dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL);  		if (dev->id < 0) {  			/* cannot use a dev_err as the name isn't available */ -			printk(KERN_ERR "Failed to get id\n"); +			pr_err("failed to get device id\n");  			kfree(dev);  			return NULL;  		} @@ -903,6 +982,10 @@ struct iio_dev *iio_device_alloc(int sizeof_priv)  }  EXPORT_SYMBOL(iio_device_alloc); +/** + * iio_device_free() - free an iio_dev from a driver + * @dev:		the iio_dev associated with the device + **/  void iio_device_free(struct iio_dev *dev)  {  	if (dev) @@ -925,6 +1008,20 @@ static int devm_iio_device_match(struct device *dev, void *res, void *data)  	return *r == data;  } +/** + * devm_iio_device_alloc - Resource-managed iio_device_alloc() + * @dev:		Device to allocate iio_dev for + * @sizeof_priv:	Space to allocate for private structure. + * + * Managed iio_device_alloc. iio_dev allocated with this function is + * automatically freed on driver detach. + * + * If an iio_dev allocated with this function needs to be freed separately, + * devm_iio_device_free() must be used. + * + * RETURNS: + * Pointer to allocated iio_dev on success, NULL on failure. + */  struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)  {  	struct iio_dev **ptr, *iio_dev; @@ -947,6 +1044,13 @@ struct iio_dev *devm_iio_device_alloc(struct device *dev, int sizeof_priv)  }  EXPORT_SYMBOL_GPL(devm_iio_device_alloc); +/** + * devm_iio_device_free - Resource-managed iio_device_free() + * @dev:		Device this iio_dev belongs to + * @iio_dev:		the iio_dev associated with the device + * + * Free iio_dev allocated with devm_iio_device_alloc(). + */  void devm_iio_device_free(struct device *dev, struct iio_dev *iio_dev)  {  	int rc; @@ -996,6 +1100,9 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)  	int __user *ip = (int __user *)arg;  	int fd; +	if (!indio_dev->info) +		return -ENODEV; +  	if (cmd == IIO_GET_EVENT_FD_IOCTL) {  		fd = iio_event_getfd(indio_dev);  		if (copy_to_user(ip, &fd, sizeof(fd))) @@ -1018,6 +1125,10 @@ static const struct file_operations iio_buffer_fileops = {  static const struct iio_buffer_setup_ops noop_ring_setup_ops; +/** + * iio_device_register() - register a device with the IIO subsystem + * @indio_dev:		Device structure filled by the device driver + **/  int iio_device_register(struct iio_dev *indio_dev)  {  	int ret; @@ -1033,7 +1144,7 @@ int iio_device_register(struct iio_dev *indio_dev)  	if (ret) {  		dev_err(indio_dev->dev.parent,  			"Failed to register debugfs interfaces\n"); -		goto error_ret; +		return ret;  	}  	ret = iio_device_register_sysfs(indio_dev);  	if (ret) { @@ -1074,11 +1185,14 @@ error_free_sysfs:  	iio_device_unregister_sysfs(indio_dev);  error_unreg_debugfs:  	iio_device_unregister_debugfs(indio_dev); -error_ret:  	return ret;  }  EXPORT_SYMBOL(iio_device_register); +/** + * iio_device_unregister() - unregister a device from the IIO subsystem + * @indio_dev:		Device structure representing the device. + **/  void iio_device_unregister(struct iio_dev *indio_dev)  {  	mutex_lock(&indio_dev->info_exist_lock); @@ -1087,13 +1201,77 @@ void iio_device_unregister(struct iio_dev *indio_dev)  	if (indio_dev->chrdev.dev)  		cdev_del(&indio_dev->chrdev); +	iio_device_unregister_debugfs(indio_dev);  	iio_disable_all_buffers(indio_dev);  	indio_dev->info = NULL; + +	iio_device_wakeup_eventset(indio_dev); +	iio_buffer_wakeup_poll(indio_dev); +  	mutex_unlock(&indio_dev->info_exist_lock);  }  EXPORT_SYMBOL(iio_device_unregister); + +static void devm_iio_device_unreg(struct device *dev, void *res) +{ +	iio_device_unregister(*(struct iio_dev **)res); +} + +/** + * devm_iio_device_register - Resource-managed iio_device_register() + * @dev:	Device to allocate iio_dev for + * @indio_dev:	Device structure filled by the device driver + * + * Managed iio_device_register.  The IIO device registered with this + * function is automatically unregistered on driver detach. This function + * calls iio_device_register() internally. Refer to that function for more + * information. + * + * If an iio_dev registered with this function needs to be unregistered + * separately, devm_iio_device_unregister() must be used. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev) +{ +	struct iio_dev **ptr; +	int ret; + +	ptr = devres_alloc(devm_iio_device_unreg, sizeof(*ptr), GFP_KERNEL); +	if (!ptr) +		return -ENOMEM; + +	*ptr = indio_dev; +	ret = iio_device_register(indio_dev); +	if (!ret) +		devres_add(dev, ptr); +	else +		devres_free(ptr); + +	return ret; +} +EXPORT_SYMBOL_GPL(devm_iio_device_register); + +/** + * devm_iio_device_unregister - Resource-managed iio_device_unregister() + * @dev:	Device this iio_dev belongs to + * @indio_dev:	the iio_dev associated with the device + * + * Unregister iio_dev registered with devm_iio_device_register(). + */ +void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev) +{ +	int rc; + +	rc = devres_release(dev, devm_iio_device_unreg, +			    devm_iio_device_match, indio_dev); +	WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_iio_device_unregister); +  subsys_initcall(iio_init);  module_exit(iio_exit); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 6be65ef5faa..bfbf4d419f4 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -40,27 +40,34 @@ struct iio_event_interface {  	struct list_head	dev_attr_list;  	unsigned long		flags;  	struct attribute_group	group; +	struct mutex		read_lock;  }; +/** + * iio_push_event() - try to add event to the list for userspace reading + * @indio_dev:		IIO device structure + * @ev_code:		What event + * @timestamp:		When the event occurred + * + * Note: The caller must make sure that this function is not running + * concurrently for the same indio_dev more than once. + **/  int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)  {  	struct iio_event_interface *ev_int = indio_dev->event_interface;  	struct iio_event_data ev; -	unsigned long flags;  	int copied;  	/* Does anyone care? */ -	spin_lock_irqsave(&ev_int->wait.lock, flags);  	if (test_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) {  		ev.id = ev_code;  		ev.timestamp = timestamp; -		copied = kfifo_put(&ev_int->det_events, &ev); +		copied = kfifo_put(&ev_int->det_events, ev);  		if (copied != 0) -			wake_up_locked_poll(&ev_int->wait, POLLIN); +			wake_up_poll(&ev_int->wait, POLLIN);  	} -	spin_unlock_irqrestore(&ev_int->wait.lock, flags);  	return 0;  } @@ -76,12 +83,13 @@ static unsigned int iio_event_poll(struct file *filep,  	struct iio_event_interface *ev_int = indio_dev->event_interface;  	unsigned int events = 0; +	if (!indio_dev->info) +		return -ENODEV; +  	poll_wait(filep, &ev_int->wait, wait); -	spin_lock_irq(&ev_int->wait.lock);  	if (!kfifo_is_empty(&ev_int->det_events))  		events = POLLIN | POLLRDNORM; -	spin_unlock_irq(&ev_int->wait.lock);  	return events;  } @@ -96,29 +104,46 @@ static ssize_t iio_event_chrdev_read(struct file *filep,  	unsigned int copied;  	int ret; +	if (!indio_dev->info) +		return -ENODEV; +  	if (count < sizeof(struct iio_event_data))  		return -EINVAL; -	spin_lock_irq(&ev_int->wait.lock); -	if (kfifo_is_empty(&ev_int->det_events)) { -		if (filep->f_flags & O_NONBLOCK) { -			ret = -EAGAIN; -			goto error_unlock; +	do { +		if (kfifo_is_empty(&ev_int->det_events)) { +			if (filep->f_flags & O_NONBLOCK) +				return -EAGAIN; + +			ret = wait_event_interruptible(ev_int->wait, +					!kfifo_is_empty(&ev_int->det_events) || +					indio_dev->info == NULL); +			if (ret) +				return ret; +			if (indio_dev->info == NULL) +				return -ENODEV;  		} -		/* Blocking on device; waiting for something to be there */ -		ret = wait_event_interruptible_locked_irq(ev_int->wait, -					!kfifo_is_empty(&ev_int->det_events)); + +		if (mutex_lock_interruptible(&ev_int->read_lock)) +			return -ERESTARTSYS; +		ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); +		mutex_unlock(&ev_int->read_lock); +  		if (ret) -			goto error_unlock; -		/* Single access device so no one else can get the data */ -	} +			return ret; -	ret = kfifo_to_user(&ev_int->det_events, buf, count, &copied); +		/* +		 * If we couldn't read anything from the fifo (a different +		 * thread might have been faster) we either return -EAGAIN if +		 * the file descriptor is non-blocking, otherwise we go back to +		 * sleep and wait for more data to arrive. +		 */ +		if (copied == 0 && (filep->f_flags & O_NONBLOCK)) +			return -EAGAIN; -error_unlock: -	spin_unlock_irq(&ev_int->wait.lock); +	} while (copied == 0); -	return ret ? ret : copied; +	return copied;  }  static int iio_event_chrdev_release(struct inode *inode, struct file *filep) @@ -126,15 +151,7 @@ static int iio_event_chrdev_release(struct inode *inode, struct file *filep)  	struct iio_dev *indio_dev = filep->private_data;  	struct iio_event_interface *ev_int = indio_dev->event_interface; -	spin_lock_irq(&ev_int->wait.lock); -	__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); -	/* -	 * In order to maintain a clean state for reopening, -	 * clear out any awaiting events. The mask will prevent -	 * any new __iio_push_event calls running. -	 */ -	kfifo_reset_out(&ev_int->det_events); -	spin_unlock_irq(&ev_int->wait.lock); +	clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);  	iio_device_put(indio_dev); @@ -157,22 +174,20 @@ int iio_event_getfd(struct iio_dev *indio_dev)  	if (ev_int == NULL)  		return -ENODEV; -	spin_lock_irq(&ev_int->wait.lock); -	if (__test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags)) { -		spin_unlock_irq(&ev_int->wait.lock); +	if (test_and_set_bit(IIO_BUSY_BIT_POS, &ev_int->flags))  		return -EBUSY; -	} -	spin_unlock_irq(&ev_int->wait.lock); +  	iio_device_get(indio_dev);  	fd = anon_inode_getfd("iio:event", &iio_event_chrdev_fileops, -				indio_dev, O_RDONLY); +				indio_dev, O_RDONLY | O_CLOEXEC);  	if (fd < 0) { -		spin_lock_irq(&ev_int->wait.lock); -		__clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags); -		spin_unlock_irq(&ev_int->wait.lock); +		clear_bit(IIO_BUSY_BIT_POS, &ev_int->flags);  		iio_device_put(indio_dev); +	} else { +		kfifo_reset_out(&ev_int->det_events);  	} +  	return fd;  } @@ -190,6 +205,27 @@ static const char * const iio_ev_dir_text[] = {  	[IIO_EV_DIR_FALLING] = "falling"  }; +static const char * const iio_ev_info_text[] = { +	[IIO_EV_INFO_ENABLE] = "en", +	[IIO_EV_INFO_VALUE] = "value", +	[IIO_EV_INFO_HYSTERESIS] = "hysteresis", +}; + +static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) +{ +	return attr->c->event_spec[attr->address & 0xffff].dir; +} + +static enum iio_event_type iio_ev_attr_type(struct iio_dev_attr *attr) +{ +	return attr->c->event_spec[attr->address & 0xffff].type; +} + +static enum iio_event_info iio_ev_attr_info(struct iio_dev_attr *attr) +{ +	return (attr->address >> 16) & 0xffff; +} +  static ssize_t iio_ev_state_store(struct device *dev,  				  struct device_attribute *attr,  				  const char *buf, @@ -205,8 +241,9 @@ static ssize_t iio_ev_state_store(struct device *dev,  		return ret;  	ret = indio_dev->info->write_event_config(indio_dev, -						  this_attr->address, -						  val); +		this_attr->c, iio_ev_attr_type(this_attr), +		iio_ev_attr_dir(this_attr), val); +  	return (ret < 0) ? ret : len;  } @@ -216,9 +253,11 @@ static ssize_t iio_ev_state_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 = indio_dev->info->read_event_config(indio_dev, -						     this_attr->address); +	int val; +	val = indio_dev->info->read_event_config(indio_dev, +		this_attr->c, iio_ev_attr_type(this_attr), +		iio_ev_attr_dir(this_attr));  	if (val < 0)  		return val;  	else @@ -231,14 +270,18 @@ 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, ret; +	int val, val2, val_arr[2]; +	int ret;  	ret = indio_dev->info->read_event_value(indio_dev, -						this_attr->address, &val); +		this_attr->c, iio_ev_attr_type(this_attr), +		iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), +		&val, &val2);  	if (ret < 0)  		return ret; - -	return sprintf(buf, "%d\n", val); +	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, @@ -248,106 +291,112 @@ static ssize_t iio_ev_value_store(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; +	int val, val2;  	int ret;  	if (!indio_dev->info->write_event_value)  		return -EINVAL; -	ret = kstrtoint(buf, 10, &val); +	ret = iio_str_to_fixpoint(buf, 100000, &val, &val2);  	if (ret)  		return ret; - -	ret = indio_dev->info->write_event_value(indio_dev, this_attr->address, -						 val); +	ret = indio_dev->info->write_event_value(indio_dev, +		this_attr->c, iio_ev_attr_type(this_attr), +		iio_ev_attr_dir(this_attr), iio_ev_attr_info(this_attr), +		val, val2);  	if (ret < 0)  		return ret;  	return len;  } -static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, -				      struct iio_chan_spec const *chan) +static int iio_device_add_event(struct iio_dev *indio_dev, +	const struct iio_chan_spec *chan, unsigned int spec_index, +	enum iio_event_type type, enum iio_event_direction dir, +	enum iio_shared_by shared_by, const unsigned long *mask)  { -	int ret = 0, i, attrcount = 0; -	u64 mask = 0; +	ssize_t (*show)(struct device *, struct device_attribute *, char *); +	ssize_t (*store)(struct device *, struct device_attribute *, +		const char *, size_t); +	unsigned int attrcount = 0; +	unsigned int i;  	char *postfix; -	if (!chan->event_mask) -		return 0; +	int ret; -	for_each_set_bit(i, &chan->event_mask, sizeof(chan->event_mask)*8) { -		postfix = kasprintf(GFP_KERNEL, "%s_%s_en", -				    iio_ev_type_text[i/IIO_EV_DIR_MAX], -				    iio_ev_dir_text[i%IIO_EV_DIR_MAX]); -		if (postfix == NULL) { -			ret = -ENOMEM; -			goto error_ret; -		} -		if (chan->modified) -			mask = IIO_MOD_EVENT_CODE(chan->type, 0, chan->channel2, -						  i/IIO_EV_DIR_MAX, -						  i%IIO_EV_DIR_MAX); -		else if (chan->differential) -			mask = IIO_EVENT_CODE(chan->type, -					      0, 0, -					      i%IIO_EV_DIR_MAX, -					      i/IIO_EV_DIR_MAX, -					      0, -					      chan->channel, -					      chan->channel2); -		else -			mask = IIO_UNMOD_EVENT_CODE(chan->type, -						    chan->channel, -						    i/IIO_EV_DIR_MAX, -						    i%IIO_EV_DIR_MAX); - -		ret = __iio_add_chan_devattr(postfix, -					     chan, -					     &iio_ev_state_show, -					     iio_ev_state_store, -					     mask, -					     0, -					     &indio_dev->dev, -					     &indio_dev->event_interface-> -					     dev_attr_list); -		kfree(postfix); -		if (ret) -			goto error_ret; -		attrcount++; -		postfix = kasprintf(GFP_KERNEL, "%s_%s_value", -				    iio_ev_type_text[i/IIO_EV_DIR_MAX], -				    iio_ev_dir_text[i%IIO_EV_DIR_MAX]); -		if (postfix == NULL) { -			ret = -ENOMEM; -			goto error_ret; +	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]); +		if (postfix == NULL) +			return -ENOMEM; + +		if (i == IIO_EV_INFO_ENABLE) { +			show = iio_ev_state_show; +			store = iio_ev_state_store; +		} else { +			show = iio_ev_value_show; +			store = iio_ev_value_store;  		} -		ret = __iio_add_chan_devattr(postfix, chan, -					     iio_ev_value_show, -					     iio_ev_value_store, -					     mask, -					     0, -					     &indio_dev->dev, -					     &indio_dev->event_interface-> -					     dev_attr_list); + +		ret = __iio_add_chan_devattr(postfix, chan, show, store, +			 (i << 16) | spec_index, shared_by, &indio_dev->dev, +			&indio_dev->event_interface->dev_attr_list);  		kfree(postfix); + +		if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) +			continue; +  		if (ret) -			goto error_ret; +			return ret; +  		attrcount++;  	} -	ret = attrcount; -error_ret: -	return ret; + +	return attrcount;  } -static inline void __iio_remove_event_config_attrs(struct iio_dev *indio_dev) +static int iio_device_add_event_sysfs(struct iio_dev *indio_dev, +	struct iio_chan_spec const *chan)  { -	struct iio_dev_attr *p, *n; -	list_for_each_entry_safe(p, n, -				 &indio_dev->event_interface-> -				 dev_attr_list, l) { -		kfree(p->dev_attr.attr.name); -		kfree(p); +	int ret = 0, i, attrcount = 0; +	enum iio_event_direction dir; +	enum iio_event_type type; + +	for (i = 0; i < chan->num_event_specs; i++) { +		type = chan->event_spec[i].type; +		dir = chan->event_spec[i].dir; + +		ret = iio_device_add_event(indio_dev, chan, i, type, dir, +			IIO_SEPARATE, &chan->event_spec[i].mask_separate); +		if (ret < 0) +			return ret; +		attrcount += ret; + +		ret = iio_device_add_event(indio_dev, chan, i, type, dir, +			IIO_SHARED_BY_TYPE, +			&chan->event_spec[i].mask_shared_by_type); +		if (ret < 0) +			return ret; +		attrcount += ret; + +		ret = iio_device_add_event(indio_dev, chan, i, type, dir, +			IIO_SHARED_BY_DIR, +			&chan->event_spec[i].mask_shared_by_dir); +		if (ret < 0) +			return ret; +		attrcount += ret; + +		ret = iio_device_add_event(indio_dev, chan, i, type, dir, +			IIO_SHARED_BY_ALL, +			&chan->event_spec[i].mask_shared_by_all); +		if (ret < 0) +			return ret; +		attrcount += ret;  	} +	ret = attrcount; +	return ret;  }  static inline int __iio_add_event_config_attrs(struct iio_dev *indio_dev) @@ -369,9 +418,10 @@ static bool iio_check_for_dynamic_events(struct iio_dev *indio_dev)  {  	int j; -	for (j = 0; j < indio_dev->num_channels; j++) -		if (indio_dev->channels[j].event_mask != 0) +	for (j = 0; j < indio_dev->num_channels; j++) { +		if (indio_dev->channels[j].num_event_specs != 0)  			return true; +	}  	return false;  } @@ -379,6 +429,7 @@ static void iio_setup_ev_int(struct iio_event_interface *ev_int)  {  	INIT_KFIFO(ev_int->det_events);  	init_waitqueue_head(&ev_int->wait); +	mutex_init(&ev_int->read_lock);  }  static const char *iio_event_group_name = "events"; @@ -394,10 +445,8 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)  	indio_dev->event_interface =  		kzalloc(sizeof(struct iio_event_interface), GFP_KERNEL); -	if (indio_dev->event_interface == NULL) { -		ret = -ENOMEM; -		goto error_ret; -	} +	if (indio_dev->event_interface == NULL) +		return -ENOMEM;  	INIT_LIST_HEAD(&indio_dev->event_interface->dev_attr_list); @@ -441,18 +490,30 @@ int iio_device_register_eventset(struct iio_dev *indio_dev)  	return 0;  error_free_setup_event_lines: -	__iio_remove_event_config_attrs(indio_dev); +	iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);  	kfree(indio_dev->event_interface); -error_ret: -  	return ret;  } +/** + * iio_device_wakeup_eventset - Wakes up the event waitqueue + * @indio_dev: The IIO device + * + * Wakes up the event waitqueue used for poll() and blocking read(). + * Should usually be called when the device is unregistered. + */ +void iio_device_wakeup_eventset(struct iio_dev *indio_dev) +{ +	if (indio_dev->event_interface == NULL) +		return; +	wake_up(&indio_dev->event_interface->wait); +} +  void iio_device_unregister_eventset(struct iio_dev *indio_dev)  {  	if (indio_dev->event_interface == NULL)  		return; -	__iio_remove_event_config_attrs(indio_dev); +	iio_free_chan_devattr_list(&indio_dev->event_interface->dev_attr_list);  	kfree(indio_dev->event_interface->group.attrs);  	kfree(indio_dev->event_interface);  } diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index bf5e70a32d3..3383b025f62 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -55,25 +55,16 @@ static struct attribute *iio_trig_dev_attrs[] = {  	&dev_attr_name.attr,  	NULL,  }; - -static struct attribute_group iio_trig_attr_group = { -	.attrs	= iio_trig_dev_attrs, -}; - -static const struct attribute_group *iio_trig_attr_groups[] = { -	&iio_trig_attr_group, -	NULL -}; +ATTRIBUTE_GROUPS(iio_trig_dev);  int iio_trigger_register(struct iio_trigger *trig_info)  {  	int ret;  	trig_info->id = ida_simple_get(&iio_trigger_ida, 0, 0, GFP_KERNEL); -	if (trig_info->id < 0) { -		ret = trig_info->id; -		goto error_ret; -	} +	if (trig_info->id < 0) +		return trig_info->id; +  	/* Set the name used for the sysfs directory etc */  	dev_set_name(&trig_info->dev, "trigger%ld",  		     (unsigned long) trig_info->id); @@ -91,7 +82,6 @@ int iio_trigger_register(struct iio_trigger *trig_info)  error_unregister_id:  	ida_simple_remove(&iio_trigger_ida, trig_info->id); -error_ret:  	return ret;  }  EXPORT_SYMBOL(iio_trigger_register); @@ -242,13 +232,12 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig,  	if (trig->ops && trig->ops->set_trigger_state && no_other_users) {  		ret = trig->ops->set_trigger_state(trig, false);  		if (ret) -			goto error_ret; +			return ret;  	}  	iio_trigger_put_irq(trig, pf->irq);  	free_irq(pf->irq, pf);  	module_put(pf->indio_dev->info->driver_module); -error_ret:  	return ret;  } @@ -318,7 +307,7 @@ static ssize_t iio_trigger_read_current(struct device *dev,   * iio_trigger_write_current() - trigger consumer sysfs set current trigger   *   * For trigger consumers the current_trigger interface allows the trigger - * used for this device to be specified at run time based on the triggers + * used for this device to be specified at run time based on the trigger's   * name.   **/  static ssize_t iio_trigger_write_current(struct device *dev, @@ -356,7 +345,7 @@ static ssize_t iio_trigger_write_current(struct device *dev,  	indio_dev->trig = trig; -	if (oldtrig && indio_dev->trig != oldtrig) +	if (oldtrig)  		iio_trigger_put(oldtrig);  	if (indio_dev->trig)  		iio_trigger_get(indio_dev->trig); @@ -403,7 +392,7 @@ static void iio_trig_release(struct device *device)  static struct device_type iio_trig_type = {  	.release = iio_trig_release, -	.groups = iio_trig_attr_groups, +	.groups = iio_trig_dev_groups,  };  static void iio_trig_subirqmask(struct irq_data *d) @@ -506,6 +495,23 @@ static int devm_iio_trigger_match(struct device *dev, void *res, void *data)  	return *r == data;  } +/** + * devm_iio_trigger_alloc - Resource-managed iio_trigger_alloc() + * @dev:		Device to allocate iio_trigger for + * @fmt:		trigger name format. If it includes format + *			specifiers, the additional arguments following + *			format are formatted and inserted in the resulting + *			string replacing their respective specifiers. + * + * Managed iio_trigger_alloc.  iio_trigger allocated with this function is + * automatically freed on driver detach. + * + * If an iio_trigger allocated with this function needs to be freed separately, + * devm_iio_trigger_free() must be used. + * + * RETURNS: + * Pointer to allocated iio_trigger on success, NULL on failure. + */  struct iio_trigger *devm_iio_trigger_alloc(struct device *dev,  						const char *fmt, ...)  { @@ -532,6 +538,13 @@ struct iio_trigger *devm_iio_trigger_alloc(struct device *dev,  }  EXPORT_SYMBOL_GPL(devm_iio_trigger_alloc); +/** + * devm_iio_trigger_free - Resource-managed iio_trigger_free() + * @dev:		Device this iio_dev belongs to + * @iio_trig:		the iio_trigger associated with the device + * + * Free iio_trigger allocated with devm_iio_trigger_alloc(). + */  void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig)  {  	int rc; diff --git a/drivers/iio/industrialio-triggered-buffer.c b/drivers/iio/industrialio-triggered-buffer.c index 46c619b0d8c..d6f54930b34 100644 --- a/drivers/iio/industrialio-triggered-buffer.c +++ b/drivers/iio/industrialio-triggered-buffer.c @@ -17,7 +17,6 @@  #include <linux/iio/trigger_consumer.h>  static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = { -	.preenable = &iio_sw_buffer_preenable,  	.postenable = &iio_triggered_buffer_postenable,  	.predisable = &iio_triggered_buffer_predisable,  }; @@ -47,14 +46,17 @@ int iio_triggered_buffer_setup(struct iio_dev *indio_dev,  	irqreturn_t (*pollfunc_th)(int irq, void *p),  	const struct iio_buffer_setup_ops *setup_ops)  { +	struct iio_buffer *buffer;  	int ret; -	indio_dev->buffer = iio_kfifo_allocate(indio_dev); -	if (!indio_dev->buffer) { +	buffer = iio_kfifo_allocate(indio_dev); +	if (!buffer) {  		ret = -ENOMEM;  		goto error_ret;  	} +	iio_device_attach_buffer(indio_dev, buffer); +  	indio_dev->pollfunc = iio_alloc_pollfunc(pollfunc_bh,  						 pollfunc_th,  						 IRQF_ONESHOT, diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 0cf5f8e06cf..c7497009d60 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -183,7 +183,7 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,  		else if (name && index >= 0) {  			pr_err("ERROR: could not get IIO channel %s:%s(%i)\n",  				np->full_name, name ? name : "", index); -			return chan; +			return NULL;  		}  		/* @@ -193,8 +193,9 @@ static struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,  		 */  		np = np->parent;  		if (np && !of_get_property(np, "io-channel-ranges", NULL)) -			break; +			return NULL;  	} +  	return chan;  } @@ -317,6 +318,7 @@ struct iio_channel *iio_channel_get(struct device *dev,  		if (channel != NULL)  			return channel;  	} +  	return iio_channel_get_sys(name, channel_name);  }  EXPORT_SYMBOL_GPL(iio_channel_get); @@ -417,12 +419,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 +457,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/kfifo_buf.c b/drivers/iio/kfifo_buf.c index a923c78d5cb..7134e8ada09 100644 --- a/drivers/iio/kfifo_buf.c +++ b/drivers/iio/kfifo_buf.c @@ -7,10 +7,12 @@  #include <linux/mutex.h>  #include <linux/iio/kfifo_buf.h>  #include <linux/sched.h> +#include <linux/poll.h>  struct iio_kfifo {  	struct iio_buffer buffer;  	struct kfifo kf; +	struct mutex user_lock;  	int update_needed;  }; @@ -31,13 +33,17 @@ static int iio_request_update_kfifo(struct iio_buffer *r)  	int ret = 0;  	struct iio_kfifo *buf = iio_to_kfifo(r); -	if (!buf->update_needed) -		goto error_ret; -	kfifo_free(&buf->kf); -	ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum, +	mutex_lock(&buf->user_lock); +	if (buf->update_needed) { +		kfifo_free(&buf->kf); +		ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,  				   buf->buffer.length); -	r->stufftoread = false; -error_ret: +		buf->update_needed = false; +	} else { +		kfifo_reset_out(&buf->kf); +	} +	mutex_unlock(&buf->user_lock); +  	return ret;  } @@ -94,15 +100,15 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length)  }  static int iio_store_to_kfifo(struct iio_buffer *r, -			      u8 *data) +			      const void *data)  {  	int ret;  	struct iio_kfifo *kf = iio_to_kfifo(r);  	ret = kfifo_in(&kf->kf, data, 1);  	if (ret != 1)  		return -EBUSY; -	r->stufftoread = true; -	wake_up_interruptible(&r->pollq); + +	wake_up_interruptible_poll(&r->pollq, POLLIN | POLLRDNORM);  	return 0;  } @@ -113,30 +119,51 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,  	int ret, copied;  	struct iio_kfifo *kf = iio_to_kfifo(r); -	if (n < r->bytes_per_datum || r->bytes_per_datum == 0) -		return -EINVAL; +	if (mutex_lock_interruptible(&kf->user_lock)) +		return -ERESTARTSYS; -	ret = kfifo_to_user(&kf->kf, buf, n, &copied); +	if (!kfifo_initialized(&kf->kf) || n < kfifo_esize(&kf->kf)) +		ret = -EINVAL; +	else +		ret = kfifo_to_user(&kf->kf, buf, n, &copied); +	mutex_unlock(&kf->user_lock);  	if (ret < 0)  		return ret; -	if (kfifo_is_empty(&kf->kf)) -		r->stufftoread = false; -	/* verify it is still empty to avoid race */ -	if (!kfifo_is_empty(&kf->kf)) -		r->stufftoread = true; -  	return copied;  } +static bool iio_kfifo_buf_data_available(struct iio_buffer *r) +{ +	struct iio_kfifo *kf = iio_to_kfifo(r); +	bool empty; + +	mutex_lock(&kf->user_lock); +	empty = kfifo_is_empty(&kf->kf); +	mutex_unlock(&kf->user_lock); + +	return !empty; +} + +static void iio_kfifo_buffer_release(struct iio_buffer *buffer) +{ +	struct iio_kfifo *kf = iio_to_kfifo(buffer); + +	mutex_destroy(&kf->user_lock); +	kfifo_free(&kf->kf); +	kfree(kf); +} +  static const struct iio_buffer_access_funcs kfifo_access_funcs = {  	.store_to = &iio_store_to_kfifo,  	.read_first_n = &iio_read_first_n_kfifo, +	.data_available = iio_kfifo_buf_data_available,  	.request_update = &iio_request_update_kfifo,  	.get_bytes_per_datum = &iio_get_bytes_per_datum_kfifo,  	.set_bytes_per_datum = &iio_set_bytes_per_datum_kfifo,  	.get_length = &iio_get_length_kfifo,  	.set_length = &iio_set_length_kfifo, +	.release = &iio_kfifo_buffer_release,  };  struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev) @@ -151,13 +178,14 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)  	kf->buffer.attrs = &iio_kfifo_attribute_group;  	kf->buffer.access = &kfifo_access_funcs;  	kf->buffer.length = 2; +	mutex_init(&kf->user_lock);  	return &kf->buffer;  }  EXPORT_SYMBOL(iio_kfifo_allocate);  void iio_kfifo_free(struct iio_buffer *r)  { -	kfree(iio_to_kfifo(r)); +	iio_buffer_put(r);  }  EXPORT_SYMBOL(iio_kfifo_free); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index bf9fa0d7aff..c89740d4748 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -27,6 +27,41 @@ config APDS9300  	 To compile this driver as a module, choose M here: the  	 module will be called apds9300. +config CM32181 +	depends on I2C +	tristate "CM32181 driver" +	help +	 Say Y here if you use cm32181. +	 This option enables ambient light sensor using +	 Capella cm32181 device driver. + +	 To compile this driver as a module, choose M here: +	 the module will be called cm32181. + +config CM36651 +	depends on I2C +	tristate "CM36651 driver" +	help +	 Say Y here if you use cm36651. +	 This option enables proximity & RGB sensor using +	 Capella cm36651 device driver. + +	 To compile this driver as a module, choose M here: +	 the module will be called cm36651. + +config GP2AP020A00F +	tristate "Sharp GP2AP020A00F Proximity/ALS sensor" +	depends on I2C +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	select IRQ_WORK +	help +	  Say Y here if you have a Sharp GP2AP020A00F proximity/ALS combo-chip +	  hooked to an I2C bus. + +	  To compile this driver as a module, choose M here: the +	  module will be called gp2ap020a00f. +  config HID_SENSOR_ALS  	depends on HID_SENSOR_HUB  	select IIO_BUFFER @@ -38,6 +73,20 @@ config HID_SENSOR_ALS  	  Say yes here to build support for the HID SENSOR  	  Ambient light sensor. +config HID_SENSOR_PROX +	depends on HID_SENSOR_HUB +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	select HID_SENSOR_IIO_COMMON +	select HID_SENSOR_IIO_TRIGGER +	tristate "HID PROX" +	help +	  Say yes here to build support for the HID SENSOR +	  Proximity sensor. + +	  To compile this driver as a module, choose M here: the +	  module will be called hid-sensor-prox. +  config SENSORS_LM3533  	tristate "LM3533 ambient light sensor"  	depends on MFD_LM3533 @@ -55,6 +104,30 @@ config SENSORS_LM3533  	  changes. The ALS-control output values can be set per zone for the  	  three current output channels. +config LTR501 +	tristate "LTR-501ALS-01 light sensor" +	depends on I2C +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	 If you say yes here you get support for the Lite-On LTR-501ALS-01 +	 ambient light and proximity sensor. + +	 This driver can also be built as a module.  If so, the module +         will be called ltr501. + +config TCS3472 +	tristate "TAOS TCS3472 color light-to-digital converter" +	depends on I2C +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	 If you say yes here you get support for the TAOS TCS3472 +	 family of color light-to-digital converters with IR filter. + +	 This driver can also be built as a module.  If so, the module +	 will be called tcs3472. +  config SENSORS_TSL2563  	tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"  	depends on I2C @@ -65,6 +138,16 @@ config SENSORS_TSL2563  	 This driver can also be built as a module.  If so, the module  	 will be called tsl2563. +config TSL4531 +	tristate "TAOS TSL4531 ambient light sensors" +	depends on I2C +	help +	 Say Y here if you want to build a driver for the TAOS TSL4531 family +	 of ambient light sensors with direct lux output. + +	 To compile this driver as a module, choose M here: the +	 module will be called tsl4531. +  config VCNL4000  	tristate "VCNL4000 combined ALS and proximity sensor"  	depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 354ee9ab237..3eb36e5151f 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -5,7 +5,14 @@  # When adding new entries keep the list in alphabetical order  obj-$(CONFIG_ADJD_S311)		+= adjd_s311.o  obj-$(CONFIG_APDS9300)		+= apds9300.o +obj-$(CONFIG_CM32181)		+= cm32181.o +obj-$(CONFIG_CM36651)		+= cm36651.o +obj-$(CONFIG_GP2AP020A00F)	+= gp2ap020a00f.o  obj-$(CONFIG_HID_SENSOR_ALS)	+= hid-sensor-als.o +obj-$(CONFIG_HID_SENSOR_PROX)	+= hid-sensor-prox.o  obj-$(CONFIG_SENSORS_LM3533)	+= lm3533-als.o +obj-$(CONFIG_LTR501)		+= ltr501.o  obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o +obj-$(CONFIG_TCS3472)		+= tcs3472.o +obj-$(CONFIG_TSL4531)		+= tsl4531.o  obj-$(CONFIG_VCNL4000)		+= vcnl4000.o diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c index 23cff798598..09ad5f1ce53 100644 --- a/drivers/iio/light/adjd_s311.c +++ b/drivers/iio/light/adjd_s311.c @@ -14,7 +14,6 @@   */  #include <linux/module.h> -#include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/i2c.h>  #include <linux/slab.h> @@ -114,50 +113,12 @@ static int adjd_s311_read_data(struct iio_dev *indio_dev, u8 reg, int *val)  	return 0;  } -static ssize_t adjd_s311_read_int_time(struct iio_dev *indio_dev, -	uintptr_t private, const struct iio_chan_spec *chan, char *buf) -{ -	struct adjd_s311_data *data = iio_priv(indio_dev); -	s32 ret; - -	ret = i2c_smbus_read_word_data(data->client, -		ADJD_S311_INT_REG(chan->address)); -	if (ret < 0) -		return ret; - -	return sprintf(buf, "%d\n", ret & ADJD_S311_INT_MASK); -} - -static ssize_t adjd_s311_write_int_time(struct iio_dev *indio_dev, -	 uintptr_t private, const struct iio_chan_spec *chan, const char *buf, -	 size_t len) -{ -	struct adjd_s311_data *data = iio_priv(indio_dev); -	unsigned long int_time; -	int ret; - -	ret = kstrtoul(buf, 10, &int_time); -	if (ret) -		return ret; - -	if (int_time > ADJD_S311_INT_MASK) -		return -EINVAL; - -	ret = i2c_smbus_write_word_data(data->client, -		ADJD_S311_INT_REG(chan->address), int_time); -	if (ret < 0) -		return ret; - -	return len; -} -  static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)  {  	struct iio_poll_func *pf = p;  	struct iio_dev *indio_dev = pf->indio_dev;  	struct adjd_s311_data *data = iio_priv(indio_dev);  	s64 time_ns = iio_get_time_ns(); -	int len = 0;  	int i, j = 0;  	int ret = adjd_s311_req_data(indio_dev); @@ -172,13 +133,9 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)  			goto done;  		data->buffer[j++] = ret & ADJD_S311_DATA_MASK; -		len += 2;  	} -	if (indio_dev->scan_timestamp) -		*(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64))) -			= time_ns; -	iio_push_to_buffers(indio_dev, (u8 *)data->buffer); +	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, time_ns);  done:  	iio_trigger_notify_done(indio_dev->trig); @@ -186,25 +143,21 @@ done:  	return IRQ_HANDLED;  } -static const struct iio_chan_spec_ext_info adjd_s311_ext_info[] = { -	{ -		.name = "integration_time", -		.read = adjd_s311_read_int_time, -		.write = adjd_s311_write_int_time, -	}, -	{ } -}; -  #define ADJD_S311_CHANNEL(_color, _scan_idx) { \  	.type = IIO_INTENSITY, \  	.modified = 1, \  	.address = (IDX_##_color), \  	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ -		BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ +		BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \ +		BIT(IIO_CHAN_INFO_INT_TIME), \  	.channel2 = (IIO_MOD_LIGHT_##_color), \  	.scan_index = (_scan_idx), \ -	.scan_type = IIO_ST('u', 10, 16, 0), \ -	.ext_info = adjd_s311_ext_info, \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 10, \ +		.storagebits = 16, \ +		.endianness = IIO_CPU, \ +	}, \  }  static const struct iio_chan_spec adjd_s311_channels[] = { @@ -236,6 +189,18 @@ static int adjd_s311_read_raw(struct iio_dev *indio_dev,  			return ret;  		*val = ret & ADJD_S311_CAP_MASK;  		return IIO_VAL_INT; +	case IIO_CHAN_INFO_INT_TIME: +		ret = i2c_smbus_read_word_data(data->client, +			ADJD_S311_INT_REG(chan->address)); +		if (ret < 0) +			return ret; +		*val = 0; +		/* +		 * not documented, based on measurement: +		 * 4095 LSBs correspond to roughly 4 ms +		 */ +		*val2 = ret & ADJD_S311_INT_MASK; +		return IIO_VAL_INT_PLUS_MICRO;  	}  	return -EINVAL;  } @@ -245,16 +210,20 @@ static int adjd_s311_write_raw(struct iio_dev *indio_dev,  			       int val, int val2, long mask)  {  	struct adjd_s311_data *data = iio_priv(indio_dev); -	int ret;  	switch (mask) {  	case IIO_CHAN_INFO_HARDWAREGAIN:  		if (val < 0 || val > ADJD_S311_CAP_MASK)  			return -EINVAL; -		ret = i2c_smbus_write_byte_data(data->client, +		return i2c_smbus_write_byte_data(data->client,  			ADJD_S311_CAP_REG(chan->address), val); -		return ret; +	case IIO_CHAN_INFO_INT_TIME: +		if (val != 0 || val2 < 0 || val2 > ADJD_S311_INT_MASK) +			return -EINVAL; + +		return i2c_smbus_write_word_data(data->client, +			ADJD_S311_INT_REG(chan->address), val2);  	}  	return -EINVAL;  } diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c index 66a58bda6dc..9ddde0ca9c3 100644 --- a/drivers/iio/light/apds9300.c +++ b/drivers/iio/light/apds9300.c @@ -273,12 +273,14 @@ static int apds9300_read_raw(struct iio_dev *indio_dev,  	return ret;  } -static int apds9300_read_thresh(struct iio_dev *indio_dev, u64 event_code, -		int *val) +static int apds9300_read_thresh(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)  {  	struct apds9300_data *data = iio_priv(indio_dev); -	switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { +	switch (dir) {  	case IIO_EV_DIR_RISING:  		*val = data->thresh_hi;  		break; @@ -289,17 +291,19 @@ static int apds9300_read_thresh(struct iio_dev *indio_dev, u64 event_code,  		return -EINVAL;  	} -	return 0; +	return IIO_VAL_INT;  } -static int apds9300_write_thresh(struct iio_dev *indio_dev, u64 event_code, -		int val) +static int apds9300_write_thresh(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)  {  	struct apds9300_data *data = iio_priv(indio_dev);  	int ret;  	mutex_lock(&data->mutex); -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) +	if (dir == IIO_EV_DIR_RISING)  		ret = apds9300_set_thresh_hi(data, val);  	else  		ret = apds9300_set_thresh_low(data, val); @@ -309,7 +313,9 @@ static int apds9300_write_thresh(struct iio_dev *indio_dev, u64 event_code,  }  static int apds9300_read_interrupt_config(struct iio_dev *indio_dev, -		u64 event_code) +		const struct iio_chan_spec *chan, +		enum iio_event_type type, +		enum iio_event_direction dir)  {  	struct apds9300_data *data = iio_priv(indio_dev); @@ -317,7 +323,8 @@ static int apds9300_read_interrupt_config(struct iio_dev *indio_dev,  }  static int apds9300_write_interrupt_config(struct iio_dev *indio_dev, -		u64 event_code, int state) +		const struct iio_chan_spec *chan, enum iio_event_type type, +		enum iio_event_direction dir, int state)  {  	struct apds9300_data *data = iio_priv(indio_dev);  	int ret; @@ -343,6 +350,20 @@ static const struct iio_info apds9300_info = {  	.write_event_config	= apds9300_write_interrupt_config,  }; +static const struct iio_event_spec apds9300_event_spec[] = { +	{ +		.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), +	}, +}; +  static const struct iio_chan_spec apds9300_channels[] = {  	{  		.type = IIO_LIGHT, @@ -355,10 +376,8 @@ static const struct iio_chan_spec apds9300_channels[] = {  		.channel2 = IIO_MOD_LIGHT_BOTH,  		.indexed = true,  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), -		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH, -					  IIO_EV_DIR_RISING) | -			       IIO_EV_BIT(IIO_EV_TYPE_THRESH, -					  IIO_EV_DIR_FALLING)), +		.event_spec = apds9300_event_spec, +		.num_event_specs = ARRAY_SIZE(apds9300_event_spec),  	}, {  		.type = IIO_INTENSITY,  		.channel = 1, diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c new file mode 100644 index 00000000000..d976e6ce60d --- /dev/null +++ b/drivers/iio/light/cm32181.c @@ -0,0 +1,380 @@ +/* + * Copyright (C) 2013 Capella Microsystems Inc. + * Author: Kevin Tsai <ktsai@capellamicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2, as published + * by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> +#include <linux/init.h> + +/* Registers Address */ +#define CM32181_REG_ADDR_CMD		0x00 +#define CM32181_REG_ADDR_ALS		0x04 +#define CM32181_REG_ADDR_STATUS		0x06 +#define CM32181_REG_ADDR_ID		0x07 + +/* Number of Configurable Registers */ +#define CM32181_CONF_REG_NUM		0x01 + +/* CMD register */ +#define CM32181_CMD_ALS_ENABLE		0x00 +#define CM32181_CMD_ALS_DISABLE		0x01 +#define CM32181_CMD_ALS_INT_EN		0x02 + +#define CM32181_CMD_ALS_IT_SHIFT	6 +#define CM32181_CMD_ALS_IT_MASK		(0x0F << CM32181_CMD_ALS_IT_SHIFT) +#define CM32181_CMD_ALS_IT_DEFAULT	(0x00 << CM32181_CMD_ALS_IT_SHIFT) + +#define CM32181_CMD_ALS_SM_SHIFT	11 +#define CM32181_CMD_ALS_SM_MASK		(0x03 << CM32181_CMD_ALS_SM_SHIFT) +#define CM32181_CMD_ALS_SM_DEFAULT	(0x01 << CM32181_CMD_ALS_SM_SHIFT) + +#define CM32181_MLUX_PER_BIT		5	/* ALS_SM=01 IT=800ms */ +#define CM32181_MLUX_PER_BIT_BASE_IT	800000	/* Based on IT=800ms */ +#define	CM32181_CALIBSCALE_DEFAULT	1000 +#define CM32181_CALIBSCALE_RESOLUTION	1000 +#define MLUX_PER_LUX			1000 + +static const u8 cm32181_reg[CM32181_CONF_REG_NUM] = { +	CM32181_REG_ADDR_CMD, +}; + +static const int als_it_bits[] = {12, 8, 0, 1, 2, 3}; +static const int als_it_value[] = {25000, 50000, 100000, 200000, 400000, +	800000}; + +struct cm32181_chip { +	struct i2c_client *client; +	struct mutex lock; +	u16 conf_regs[CM32181_CONF_REG_NUM]; +	int calibscale; +}; + +/** + * cm32181_reg_init() - Initialize CM32181 registers + * @cm32181:	pointer of struct cm32181. + * + * Initialize CM32181 ambient light sensor register to default values. + * + * Return: 0 for success; otherwise for error code. + */ +static int cm32181_reg_init(struct cm32181_chip *cm32181) +{ +	struct i2c_client *client = cm32181->client; +	int i; +	s32 ret; + +	ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ID); +	if (ret < 0) +		return ret; + +	/* check device ID */ +	if ((ret & 0xFF) != 0x81) +		return -ENODEV; + +	/* Default Values */ +	cm32181->conf_regs[CM32181_REG_ADDR_CMD] = CM32181_CMD_ALS_ENABLE | +			CM32181_CMD_ALS_IT_DEFAULT | CM32181_CMD_ALS_SM_DEFAULT; +	cm32181->calibscale = CM32181_CALIBSCALE_DEFAULT; + +	/* Initialize registers*/ +	for (i = 0; i < CM32181_CONF_REG_NUM; i++) { +		ret = i2c_smbus_write_word_data(client, cm32181_reg[i], +			cm32181->conf_regs[i]); +		if (ret < 0) +			return ret; +	} + +	return 0; +} + +/** + *  cm32181_read_als_it() - Get sensor integration time (ms) + *  @cm32181:	pointer of struct cm32181 + *  @val2:	pointer of int to load the als_it value. + * + *  Report the current integartion time by millisecond. + * + *  Return: IIO_VAL_INT_PLUS_MICRO for success, otherwise -EINVAL. + */ +static int cm32181_read_als_it(struct cm32181_chip *cm32181, int *val2) +{ +	u16 als_it; +	int i; + +	als_it = cm32181->conf_regs[CM32181_REG_ADDR_CMD]; +	als_it &= CM32181_CMD_ALS_IT_MASK; +	als_it >>= CM32181_CMD_ALS_IT_SHIFT; +	for (i = 0; i < ARRAY_SIZE(als_it_bits); i++) { +		if (als_it == als_it_bits[i]) { +			*val2 = als_it_value[i]; +			return IIO_VAL_INT_PLUS_MICRO; +		} +	} + +	return -EINVAL; +} + +/** + * cm32181_write_als_it() - Write sensor integration time + * @cm32181:	pointer of struct cm32181. + * @val:	integration time by millisecond. + * + * Convert integration time (ms) to sensor value. + * + * Return: i2c_smbus_write_word_data command return value. + */ +static int cm32181_write_als_it(struct cm32181_chip *cm32181, int val) +{ +	struct i2c_client *client = cm32181->client; +	u16 als_it; +	int ret, i, n; + +	n = ARRAY_SIZE(als_it_value); +	for (i = 0; i < n; i++) +		if (val <= als_it_value[i]) +			break; +	if (i >= n) +		i = n - 1; + +	als_it = als_it_bits[i]; +	als_it <<= CM32181_CMD_ALS_IT_SHIFT; + +	mutex_lock(&cm32181->lock); +	cm32181->conf_regs[CM32181_REG_ADDR_CMD] &= +		~CM32181_CMD_ALS_IT_MASK; +	cm32181->conf_regs[CM32181_REG_ADDR_CMD] |= +		als_it; +	ret = i2c_smbus_write_word_data(client, CM32181_REG_ADDR_CMD, +			cm32181->conf_regs[CM32181_REG_ADDR_CMD]); +	mutex_unlock(&cm32181->lock); + +	return ret; +} + +/** + * cm32181_get_lux() - report current lux value + * @cm32181:	pointer of struct cm32181. + * + * Convert sensor raw data to lux.  It depends on integration + * time and claibscale variable. + * + * Return: Positive value is lux, otherwise is error code. + */ +static int cm32181_get_lux(struct cm32181_chip *cm32181) +{ +	struct i2c_client *client = cm32181->client; +	int ret; +	int als_it; +	unsigned long lux; + +	ret = cm32181_read_als_it(cm32181, &als_it); +	if (ret < 0) +		return -EINVAL; + +	lux = CM32181_MLUX_PER_BIT; +	lux *= CM32181_MLUX_PER_BIT_BASE_IT; +	lux /= als_it; + +	ret = i2c_smbus_read_word_data(client, CM32181_REG_ADDR_ALS); +	if (ret < 0) +		return ret; + +	lux *= ret; +	lux *= cm32181->calibscale; +	lux /= CM32181_CALIBSCALE_RESOLUTION; +	lux /= MLUX_PER_LUX; + +	if (lux > 0xFFFF) +		lux = 0xFFFF; + +	return lux; +} + +static int cm32181_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, int *val2, long mask) +{ +	struct cm32181_chip *cm32181 = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_PROCESSED: +		ret = cm32181_get_lux(cm32181); +		if (ret < 0) +			return ret; +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBSCALE: +		*val = cm32181->calibscale; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_INT_TIME: +		*val = 0; +		ret = cm32181_read_als_it(cm32181, val2); +		return ret; +	} + +	return -EINVAL; +} + +static int cm32181_write_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int val, int val2, long mask) +{ +	struct cm32181_chip *cm32181 = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		cm32181->calibscale = val; +		return val; +	case IIO_CHAN_INFO_INT_TIME: +		ret = cm32181_write_als_it(cm32181, val2); +		return ret; +	} + +	return -EINVAL; +} + +/** + * cm32181_get_it_available() - Get available ALS IT value + * @dev:	pointer of struct device. + * @attr:	pointer of struct device_attribute. + * @buf:	pointer of return string buffer. + * + * Display the available integration time values by millisecond. + * + * Return: string length. + */ +static ssize_t cm32181_get_it_available(struct device *dev, +			struct device_attribute *attr, char *buf) +{ +	int i, n, len; + +	n = ARRAY_SIZE(als_it_value); +	for (i = 0, len = 0; i < n; i++) +		len += sprintf(buf + len, "0.%06u ", als_it_value[i]); +	return len + sprintf(buf + len, "\n"); +} + +static const struct iio_chan_spec cm32181_channels[] = { +	{ +		.type = IIO_LIGHT, +		.info_mask_separate = +			BIT(IIO_CHAN_INFO_PROCESSED) | +			BIT(IIO_CHAN_INFO_CALIBSCALE) | +			BIT(IIO_CHAN_INFO_INT_TIME), +	} +}; + +static IIO_DEVICE_ATTR(in_illuminance_integration_time_available, +			S_IRUGO, cm32181_get_it_available, NULL, 0); + +static struct attribute *cm32181_attributes[] = { +	&iio_dev_attr_in_illuminance_integration_time_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group cm32181_attribute_group = { +	.attrs = cm32181_attributes +}; + +static const struct iio_info cm32181_info = { +	.driver_module		= THIS_MODULE, +	.read_raw		= &cm32181_read_raw, +	.write_raw		= &cm32181_write_raw, +	.attrs			= &cm32181_attribute_group, +}; + +static int cm32181_probe(struct i2c_client *client, +			const struct i2c_device_id *id) +{ +	struct cm32181_chip *cm32181; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm32181)); +	if (!indio_dev) { +		dev_err(&client->dev, "devm_iio_device_alloc failed\n"); +		return -ENOMEM; +	} + +	cm32181 = iio_priv(indio_dev); +	i2c_set_clientdata(client, indio_dev); +	cm32181->client = client; + +	mutex_init(&cm32181->lock); +	indio_dev->dev.parent = &client->dev; +	indio_dev->channels = cm32181_channels; +	indio_dev->num_channels = ARRAY_SIZE(cm32181_channels); +	indio_dev->info = &cm32181_info; +	indio_dev->name = id->name; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = cm32181_reg_init(cm32181); +	if (ret) { +		dev_err(&client->dev, +			"%s: register init failed\n", +			__func__); +		return ret; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&client->dev, +			"%s: regist device failed\n", +			__func__); +		return ret; +	} + +	return 0; +} + +static int cm32181_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); + +	iio_device_unregister(indio_dev); +	return 0; +} + +static const struct i2c_device_id cm32181_id[] = { +	{ "cm32181", 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, cm32181_id); + +static const struct of_device_id cm32181_of_match[] = { +	{ .compatible = "capella,cm32181" }, +	{ } +}; + +static struct i2c_driver cm32181_driver = { +	.driver = { +		.name	= "cm32181", +		.of_match_table = of_match_ptr(cm32181_of_match), +		.owner	= THIS_MODULE, +	}, +	.id_table       = cm32181_id, +	.probe		= cm32181_probe, +	.remove		= cm32181_remove, +}; + +module_i2c_driver(cm32181_driver); + +MODULE_AUTHOR("Kevin Tsai <ktsai@capellamicro.com>"); +MODULE_DESCRIPTION("CM32181 ambient light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c new file mode 100644 index 00000000000..39fc67e8213 --- /dev/null +++ b/drivers/iio/light/cm36651.c @@ -0,0 +1,750 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Beomho Seo <beomho.seo@samsung.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General Public License version 2, as published + * by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/regulator/consumer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +/* Slave address 0x19 for PS of 7 bit addressing protocol for I2C */ +#define CM36651_I2C_ADDR_PS		0x19 +/* Alert Response Address */ +#define CM36651_ARA			0x0C + +/* Ambient light sensor */ +#define CM36651_CS_CONF1		0x00 +#define CM36651_CS_CONF2		0x01 +#define CM36651_ALS_WH_M		0x02 +#define CM36651_ALS_WH_L		0x03 +#define CM36651_ALS_WL_M		0x04 +#define CM36651_ALS_WL_L		0x05 +#define CM36651_CS_CONF3		0x06 +#define CM36651_CS_CONF_REG_NUM		0x02 + +/* Proximity sensor */ +#define CM36651_PS_CONF1		0x00 +#define CM36651_PS_THD			0x01 +#define CM36651_PS_CANC			0x02 +#define CM36651_PS_CONF2		0x03 +#define CM36651_PS_REG_NUM		0x04 + +/* CS_CONF1 command code */ +#define CM36651_ALS_ENABLE		0x00 +#define CM36651_ALS_DISABLE		0x01 +#define CM36651_ALS_INT_EN		0x02 +#define CM36651_ALS_THRES		0x04 + +/* CS_CONF2 command code */ +#define CM36651_CS_CONF2_DEFAULT_BIT	0x08 + +/* CS_CONF3 channel integration time */ +#define CM36651_CS_IT1			0x00 /* Integration time 80 msec */ +#define CM36651_CS_IT2			0x40 /* Integration time 160 msec */ +#define CM36651_CS_IT3			0x80 /* Integration time 320 msec */ +#define CM36651_CS_IT4			0xC0 /* Integration time 640 msec */ + +/* PS_CONF1 command code */ +#define CM36651_PS_ENABLE		0x00 +#define CM36651_PS_DISABLE		0x01 +#define CM36651_PS_INT_EN		0x02 +#define CM36651_PS_PERS2		0x04 +#define CM36651_PS_PERS3		0x08 +#define CM36651_PS_PERS4		0x0C + +/* PS_CONF1 command code: integration time */ +#define CM36651_PS_IT1			0x00 /* Integration time 0.32 msec */ +#define CM36651_PS_IT2			0x10 /* Integration time 0.42 msec */ +#define CM36651_PS_IT3			0x20 /* Integration time 0.52 msec */ +#define CM36651_PS_IT4			0x30 /* Integration time 0.64 msec */ + +/* PS_CONF1 command code: duty ratio */ +#define CM36651_PS_DR1			0x00 /* Duty ratio 1/80 */ +#define CM36651_PS_DR2			0x40 /* Duty ratio 1/160 */ +#define CM36651_PS_DR3			0x80 /* Duty ratio 1/320 */ +#define CM36651_PS_DR4			0xC0 /* Duty ratio 1/640 */ + +/* PS_THD command code */ +#define CM36651_PS_INITIAL_THD		0x05 + +/* PS_CANC command code */ +#define CM36651_PS_CANC_DEFAULT		0x00 + +/* PS_CONF2 command code */ +#define CM36651_PS_HYS1			0x00 +#define CM36651_PS_HYS2			0x01 +#define CM36651_PS_SMART_PERS_EN	0x02 +#define CM36651_PS_DIR_INT		0x04 +#define CM36651_PS_MS			0x10 + +#define CM36651_CS_COLOR_NUM		4 + +#define CM36651_CLOSE_PROXIMITY		0x32 +#define CM36651_FAR_PROXIMITY			0x33 + +#define CM36651_CS_INT_TIME_AVAIL	"0.08 0.16 0.32 0.64" +#define CM36651_PS_INT_TIME_AVAIL	"0.000320 0.000420 0.000520 0.000640" + +enum cm36651_operation_mode { +	CM36651_LIGHT_EN, +	CM36651_PROXIMITY_EN, +	CM36651_PROXIMITY_EV_EN, +}; + +enum cm36651_light_channel_idx { +	CM36651_LIGHT_CHANNEL_IDX_RED, +	CM36651_LIGHT_CHANNEL_IDX_GREEN, +	CM36651_LIGHT_CHANNEL_IDX_BLUE, +	CM36651_LIGHT_CHANNEL_IDX_CLEAR, +}; + +enum cm36651_command { +	CM36651_CMD_READ_RAW_LIGHT, +	CM36651_CMD_READ_RAW_PROXIMITY, +	CM36651_CMD_PROX_EV_EN, +	CM36651_CMD_PROX_EV_DIS, +}; + +static const u8 cm36651_cs_reg[CM36651_CS_CONF_REG_NUM] = { +	CM36651_CS_CONF1, +	CM36651_CS_CONF2, +}; + +static const u8 cm36651_ps_reg[CM36651_PS_REG_NUM] = { +	CM36651_PS_CONF1, +	CM36651_PS_THD, +	CM36651_PS_CANC, +	CM36651_PS_CONF2, +}; + +struct cm36651_data { +	const struct cm36651_platform_data *pdata; +	struct i2c_client *client; +	struct i2c_client *ps_client; +	struct i2c_client *ara_client; +	struct mutex lock; +	struct regulator *vled_reg; +	unsigned long flags; +	int cs_int_time[CM36651_CS_COLOR_NUM]; +	int ps_int_time; +	u8 cs_ctrl_regs[CM36651_CS_CONF_REG_NUM]; +	u8 ps_ctrl_regs[CM36651_PS_REG_NUM]; +	u16 color[CM36651_CS_COLOR_NUM]; +}; + +static int cm36651_setup_reg(struct cm36651_data *cm36651) +{ +	struct i2c_client *client = cm36651->client; +	struct i2c_client *ps_client = cm36651->ps_client; +	int i, ret; + +	/* CS initialization */ +	cm36651->cs_ctrl_regs[CM36651_CS_CONF1] = CM36651_ALS_ENABLE | +							     CM36651_ALS_THRES; +	cm36651->cs_ctrl_regs[CM36651_CS_CONF2] = CM36651_CS_CONF2_DEFAULT_BIT; + +	for (i = 0; i < CM36651_CS_CONF_REG_NUM; i++) { +		ret = i2c_smbus_write_byte_data(client, cm36651_cs_reg[i], +						     cm36651->cs_ctrl_regs[i]); +		if (ret < 0) +			return ret; +	} + +	/* PS initialization */ +	cm36651->ps_ctrl_regs[CM36651_PS_CONF1] = CM36651_PS_ENABLE | +								CM36651_PS_IT2; +	cm36651->ps_ctrl_regs[CM36651_PS_THD] = CM36651_PS_INITIAL_THD; +	cm36651->ps_ctrl_regs[CM36651_PS_CANC] = CM36651_PS_CANC_DEFAULT; +	cm36651->ps_ctrl_regs[CM36651_PS_CONF2] = CM36651_PS_HYS2 | +				CM36651_PS_DIR_INT | CM36651_PS_SMART_PERS_EN; + +	for (i = 0; i < CM36651_PS_REG_NUM; i++) { +		ret = i2c_smbus_write_byte_data(ps_client, cm36651_ps_reg[i], +						     cm36651->ps_ctrl_regs[i]); +		if (ret < 0) +			return ret; +	} + +	/* Set shutdown mode */ +	ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, +							  CM36651_ALS_DISABLE); +	if (ret < 0) +		return ret; + +	ret = i2c_smbus_write_byte_data(cm36651->ps_client, +					 CM36651_PS_CONF1, CM36651_PS_DISABLE); +	if (ret < 0) +		return ret; + +	return 0; +} + +static int cm36651_read_output(struct cm36651_data *cm36651, +				struct iio_chan_spec const *chan, int *val) +{ +	struct i2c_client *client = cm36651->client; +	int ret = -EINVAL; + +	switch (chan->type) { +	case IIO_LIGHT: +		*val = i2c_smbus_read_word_data(client, chan->address); +		if (*val < 0) +			return ret; + +		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, +							CM36651_ALS_DISABLE); +		if (ret < 0) +			return ret; + +		ret = IIO_VAL_INT; +		break; +	case IIO_PROXIMITY: +		*val = i2c_smbus_read_byte(cm36651->ps_client); +		if (*val < 0) +			return ret; + +		if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { +			ret = i2c_smbus_write_byte_data(cm36651->ps_client, +					CM36651_PS_CONF1, CM36651_PS_DISABLE); +			if (ret < 0) +				return ret; +		} + +		ret = IIO_VAL_INT; +		break; +	default: +		break; +	} + +	return ret; +} + +static irqreturn_t cm36651_irq_handler(int irq, void *data) +{ +	struct iio_dev *indio_dev = data; +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	struct i2c_client *client = cm36651->client; +	int ev_dir, ret; +	u64 ev_code; + +	/* +	 * The PS INT pin is an active low signal that PS INT move logic low +	 * when the object is detect. Once the MCU host received the PS INT +	 * "LOW" signal, the Host needs to read the data at Alert Response +	 * Address(ARA) to clear the PS INT signal. After clearing the PS +	 * INT pin, the PS INT signal toggles from low to high. +	 */ +	ret = i2c_smbus_read_byte(cm36651->ara_client); +	if (ret < 0) { +		dev_err(&client->dev, +				"%s: Data read failed: %d\n", __func__, ret); +		return IRQ_HANDLED; +	} +	switch (ret) { +	case CM36651_CLOSE_PROXIMITY: +		ev_dir = IIO_EV_DIR_RISING; +		break; +	case CM36651_FAR_PROXIMITY: +		ev_dir = IIO_EV_DIR_FALLING; +		break; +	default: +		dev_err(&client->dev, +			"%s: Data read wrong: %d\n", __func__, ret); +		return IRQ_HANDLED; +	} + +	ev_code = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, +				CM36651_CMD_READ_RAW_PROXIMITY, +				IIO_EV_TYPE_THRESH, ev_dir); + +	iio_push_event(indio_dev, ev_code, iio_get_time_ns()); + +	return IRQ_HANDLED; +} + +static int cm36651_set_operation_mode(struct cm36651_data *cm36651, int cmd) +{ +	struct i2c_client *client = cm36651->client; +	struct i2c_client *ps_client = cm36651->ps_client; +	int ret = -EINVAL; + +	switch (cmd) { +	case CM36651_CMD_READ_RAW_LIGHT: +		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF1, +				cm36651->cs_ctrl_regs[CM36651_CS_CONF1]); +		break; +	case CM36651_CMD_READ_RAW_PROXIMITY: +		if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) +			return CM36651_PROXIMITY_EV_EN; + +		ret = i2c_smbus_write_byte_data(ps_client, CM36651_PS_CONF1, +				cm36651->ps_ctrl_regs[CM36651_PS_CONF1]); +		break; +	case CM36651_CMD_PROX_EV_EN: +		if (test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { +			dev_err(&client->dev, +				"Already proximity event enable state\n"); +			return ret; +		} +		set_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); + +		ret = i2c_smbus_write_byte_data(ps_client, +			cm36651_ps_reg[CM36651_PS_CONF1], +			CM36651_PS_INT_EN | CM36651_PS_PERS2 | CM36651_PS_IT2); + +		if (ret < 0) { +			dev_err(&client->dev, "Proximity enable event failed\n"); +			return ret; +		} +		break; +	case CM36651_CMD_PROX_EV_DIS: +		if (!test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags)) { +			dev_err(&client->dev, +				"Already proximity event disable state\n"); +			return ret; +		} +		clear_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); +		ret = i2c_smbus_write_byte_data(ps_client, +					CM36651_PS_CONF1, CM36651_PS_DISABLE); +		break; +	} + +	if (ret < 0) +		dev_err(&client->dev, "Write register failed\n"); + +	return ret; +} + +static int cm36651_read_channel(struct cm36651_data *cm36651, +				struct iio_chan_spec const *chan, int *val) +{ +	struct i2c_client *client = cm36651->client; +	int cmd, ret; + +	if (chan->type == IIO_LIGHT) +		cmd = CM36651_CMD_READ_RAW_LIGHT; +	else if (chan->type == IIO_PROXIMITY) +		cmd = CM36651_CMD_READ_RAW_PROXIMITY; +	else +		return -EINVAL; + +	ret = cm36651_set_operation_mode(cm36651, cmd); +	if (ret < 0) { +		dev_err(&client->dev, "CM36651 set operation mode failed\n"); +		return ret; +	} +	/* Delay for work after enable operation */ +	msleep(50); +	ret = cm36651_read_output(cm36651, chan, val); +	if (ret < 0) { +		dev_err(&client->dev, "CM36651 read output failed\n"); +		return ret; +	} + +	return ret; +} + +static int cm36651_read_int_time(struct cm36651_data *cm36651, +				struct iio_chan_spec const *chan, int *val2) +{ +	switch (chan->type) { +	case IIO_LIGHT: +		if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT1) +			*val2 = 80000; +		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT2) +			*val2 = 160000; +		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT3) +			*val2 = 320000; +		else if (cm36651->cs_int_time[chan->address] == CM36651_CS_IT4) +			*val2 = 640000; +		else +			return -EINVAL; +		break; +	case IIO_PROXIMITY: +		if (cm36651->ps_int_time == CM36651_PS_IT1) +			*val2 = 320; +		else if (cm36651->ps_int_time == CM36651_PS_IT2) +			*val2 = 420; +		else if (cm36651->ps_int_time == CM36651_PS_IT3) +			*val2 = 520; +		else if (cm36651->ps_int_time == CM36651_PS_IT4) +			*val2 = 640; +		else +			return -EINVAL; +		break; +	default: +		return -EINVAL; +	} + +	return IIO_VAL_INT_PLUS_MICRO; +} + +static int cm36651_write_int_time(struct cm36651_data *cm36651, +				struct iio_chan_spec const *chan, int val) +{ +	struct i2c_client *client = cm36651->client; +	struct i2c_client *ps_client = cm36651->ps_client; +	int int_time, ret; + +	switch (chan->type) { +	case IIO_LIGHT: +		if (val == 80000) +			int_time = CM36651_CS_IT1; +		else if (val == 160000) +			int_time = CM36651_CS_IT2; +		else if (val == 320000) +			int_time = CM36651_CS_IT3; +		else if (val == 640000) +			int_time = CM36651_CS_IT4; +		else +			return -EINVAL; + +		ret = i2c_smbus_write_byte_data(client, CM36651_CS_CONF3, +					   int_time >> 2 * (chan->address)); +		if (ret < 0) { +			dev_err(&client->dev, "CS integration time write failed\n"); +			return ret; +		} +		cm36651->cs_int_time[chan->address] = int_time; +		break; +	case IIO_PROXIMITY: +		if (val == 320) +			int_time = CM36651_PS_IT1; +		else if (val == 420) +			int_time = CM36651_PS_IT2; +		else if (val == 520) +			int_time = CM36651_PS_IT3; +		else if (val == 640) +			int_time = CM36651_PS_IT4; +		else +			return -EINVAL; + +		ret = i2c_smbus_write_byte_data(ps_client, +						CM36651_PS_CONF1, int_time); +		if (ret < 0) { +			dev_err(&client->dev, "PS integration time write failed\n"); +			return ret; +		} +		cm36651->ps_int_time = int_time; +		break; +	default: +		return -EINVAL; +	} + +	return ret; +} + +static int cm36651_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, int *val2, long mask) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	int ret; + +	mutex_lock(&cm36651->lock); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		ret = cm36651_read_channel(cm36651, chan, val); +		break; +	case IIO_CHAN_INFO_INT_TIME: +		*val = 0; +		ret = cm36651_read_int_time(cm36651, chan, val2); +		break; +	default: +		ret = -EINVAL; +	} + +	mutex_unlock(&cm36651->lock); + +	return ret; +} + +static int cm36651_write_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int val, int val2, long mask) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	struct i2c_client *client = cm36651->client; +	int ret = -EINVAL; + +	if (mask == IIO_CHAN_INFO_INT_TIME) { +		ret = cm36651_write_int_time(cm36651, chan, val2); +		if (ret < 0) +			dev_err(&client->dev, "Integration time write failed\n"); +	} + +	return ret; +} + +static int cm36651_read_prox_thresh(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) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); + +	*val = cm36651->ps_ctrl_regs[CM36651_PS_THD]; + +	return 0; +} + +static int cm36651_write_prox_thresh(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) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	struct i2c_client *client = cm36651->client; +	int ret; + +	if (val < 3 || val > 255) +		return -EINVAL; + +	cm36651->ps_ctrl_regs[CM36651_PS_THD] = val; +	ret = i2c_smbus_write_byte_data(cm36651->ps_client, CM36651_PS_THD, +					cm36651->ps_ctrl_regs[CM36651_PS_THD]); + +	if (ret < 0) { +		dev_err(&client->dev, "PS threshold write failed: %d\n", ret); +		return ret; +	} + +	return 0; +} + +static int cm36651_write_prox_event_config(struct iio_dev *indio_dev, +					const struct iio_chan_spec *chan, +					enum iio_event_type type, +					enum iio_event_direction dir, +					int state) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	int cmd, ret = -EINVAL; + +	mutex_lock(&cm36651->lock); + +	cmd = state ? CM36651_CMD_PROX_EV_EN : CM36651_CMD_PROX_EV_DIS; +	ret = cm36651_set_operation_mode(cm36651, cmd); + +	mutex_unlock(&cm36651->lock); + +	return ret; +} + +static int cm36651_read_prox_event_config(struct iio_dev *indio_dev, +					const struct iio_chan_spec *chan, +					enum iio_event_type type, +					enum iio_event_direction dir) +{ +	struct cm36651_data *cm36651 = iio_priv(indio_dev); +	int event_en; + +	mutex_lock(&cm36651->lock); + +	event_en = test_bit(CM36651_PROXIMITY_EV_EN, &cm36651->flags); + +	mutex_unlock(&cm36651->lock); + +	return event_en; +} + +#define CM36651_LIGHT_CHANNEL(_color, _idx) {		\ +	.type = IIO_LIGHT,				\ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |	\ +			BIT(IIO_CHAN_INFO_INT_TIME),	\ +	.address = _idx,				\ +	.modified = 1,					\ +	.channel2 = IIO_MOD_LIGHT_##_color,		\ +}							\ + +static const struct iio_event_spec cm36651_event_spec[] = { +	{ +		.type = IIO_EV_TYPE_THRESH, +		.dir = IIO_EV_DIR_EITHER, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +				BIT(IIO_EV_INFO_ENABLE), +	} +}; + +static const struct iio_chan_spec cm36651_channels[] = { +	{ +		.type = IIO_PROXIMITY, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +				BIT(IIO_CHAN_INFO_INT_TIME), +		.event_spec = cm36651_event_spec, +		.num_event_specs = ARRAY_SIZE(cm36651_event_spec), +	}, +	CM36651_LIGHT_CHANNEL(RED, CM36651_LIGHT_CHANNEL_IDX_RED), +	CM36651_LIGHT_CHANNEL(GREEN, CM36651_LIGHT_CHANNEL_IDX_GREEN), +	CM36651_LIGHT_CHANNEL(BLUE, CM36651_LIGHT_CHANNEL_IDX_BLUE), +	CM36651_LIGHT_CHANNEL(CLEAR, CM36651_LIGHT_CHANNEL_IDX_CLEAR), +}; + +static IIO_CONST_ATTR(in_illuminance_integration_time_available, +					CM36651_CS_INT_TIME_AVAIL); +static IIO_CONST_ATTR(in_proximity_integration_time_available, +					CM36651_PS_INT_TIME_AVAIL); + +static struct attribute *cm36651_attributes[] = { +	&iio_const_attr_in_illuminance_integration_time_available.dev_attr.attr, +	&iio_const_attr_in_proximity_integration_time_available.dev_attr.attr, +	NULL, +}; + +static const struct attribute_group cm36651_attribute_group = { +	.attrs = cm36651_attributes +}; + +static const struct iio_info cm36651_info = { +	.driver_module		= THIS_MODULE, +	.read_raw		= &cm36651_read_raw, +	.write_raw		= &cm36651_write_raw, +	.read_event_value	= &cm36651_read_prox_thresh, +	.write_event_value	= &cm36651_write_prox_thresh, +	.read_event_config	= &cm36651_read_prox_event_config, +	.write_event_config	= &cm36651_write_prox_event_config, +	.attrs			= &cm36651_attribute_group, +}; + +static int cm36651_probe(struct i2c_client *client, +			     const struct i2c_device_id *id) +{ +	struct cm36651_data *cm36651; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*cm36651)); +	if (!indio_dev) +		return -ENOMEM; + +	cm36651 = iio_priv(indio_dev); + +	cm36651->vled_reg = devm_regulator_get(&client->dev, "vled"); +	if (IS_ERR(cm36651->vled_reg)) { +		dev_err(&client->dev, "get regulator vled failed\n"); +		return PTR_ERR(cm36651->vled_reg); +	} + +	ret = regulator_enable(cm36651->vled_reg); +	if (ret) { +		dev_err(&client->dev, "enable regulator vled failed\n"); +		return ret; +	} + +	i2c_set_clientdata(client, indio_dev); + +	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; +	indio_dev->num_channels = ARRAY_SIZE(cm36651_channels); +	indio_dev->info = &cm36651_info; +	indio_dev->name = id->name; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = cm36651_setup_reg(cm36651); +	if (ret) { +		dev_err(&client->dev, "%s: register setup failed\n", __func__); +		goto error_i2c_unregister_ara; +	} + +	ret = request_threaded_irq(client->irq, NULL, cm36651_irq_handler, +					IRQF_TRIGGER_FALLING | IRQF_ONESHOT, +							"cm36651", indio_dev); +	if (ret) { +		dev_err(&client->dev, "%s: request irq failed\n", __func__); +		goto error_i2c_unregister_ara; +	} + +	ret = iio_device_register(indio_dev); +	if (ret) { +		dev_err(&client->dev, "%s: regist device failed\n", __func__); +		goto error_free_irq; +	} + +	return 0; + +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; +} + +static int cm36651_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct cm36651_data *cm36651 = iio_priv(indio_dev); + +	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; +} + +static const struct i2c_device_id cm36651_id[] = { +	{ "cm36651", 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, cm36651_id); + +static const struct of_device_id cm36651_of_match[] = { +	{ .compatible = "capella,cm36651" }, +	{ } +}; + +static struct i2c_driver cm36651_driver = { +	.driver = { +		.name	= "cm36651", +		.of_match_table = cm36651_of_match, +		.owner	= THIS_MODULE, +	}, +	.probe		= cm36651_probe, +	.remove		= cm36651_remove, +	.id_table	= cm36651_id, +}; + +module_i2c_driver(cm36651_driver); + +MODULE_AUTHOR("Beomho Seo <beomho.seo@samsung.com>"); +MODULE_DESCRIPTION("CM36651 proximity/ambient light sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c new file mode 100644 index 00000000000..04bdb85d2d9 --- /dev/null +++ b/drivers/iio/light/gp2ap020a00f.c @@ -0,0 +1,1654 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * Author: Jacek Anaszewski <j.anaszewski@samsung.com> + * + * IIO features supported by the driver: + * + * Read-only raw channels: + *   - illuminance_clear [lux] + *   - illuminance_ir + *   - proximity + * + * Triggered buffer: + *   - illuminance_clear + *   - illuminance_ir + *   - proximity + * + * Events: + *   - illuminance_clear (rising and falling) + *   - proximity (rising and falling) + *     - both falling and rising thresholds for the proximity events + *       must be set to the values greater than 0. + * + * The driver supports triggered buffers for all the three + * channels as well as high and low threshold events for the + * illuminance_clear and proxmimity channels. Triggers + * can be enabled simultaneously with both illuminance_clear + * events. Proximity events cannot be enabled simultaneously + * with any triggers or illuminance events. Enabling/disabling + * one of the proximity events automatically enables/disables + * the other one. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + */ + +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irq_work.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/iio/buffer.h> +#include <linux/iio/events.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/triggered_buffer.h> + +#define GP2A_I2C_NAME "gp2ap020a00f" + +/* Registers */ +#define GP2AP020A00F_OP_REG	0x00 /* Basic operations */ +#define GP2AP020A00F_ALS_REG	0x01 /* ALS related settings */ +#define GP2AP020A00F_PS_REG	0x02 /* PS related settings */ +#define GP2AP020A00F_LED_REG	0x03 /* LED reg */ +#define GP2AP020A00F_TL_L_REG	0x04 /* ALS: Threshold low LSB */ +#define GP2AP020A00F_TL_H_REG	0x05 /* ALS: Threshold low MSB */ +#define GP2AP020A00F_TH_L_REG	0x06 /* ALS: Threshold high LSB */ +#define GP2AP020A00F_TH_H_REG	0x07 /* ALS: Threshold high MSB */ +#define GP2AP020A00F_PL_L_REG	0x08 /* PS: Threshold low LSB */ +#define GP2AP020A00F_PL_H_REG	0x09 /* PS: Threshold low MSB */ +#define GP2AP020A00F_PH_L_REG	0x0a /* PS: Threshold high LSB */ +#define GP2AP020A00F_PH_H_REG	0x0b /* PS: Threshold high MSB */ +#define GP2AP020A00F_D0_L_REG	0x0c /* ALS result: Clear/Illuminance LSB */ +#define GP2AP020A00F_D0_H_REG	0x0d /* ALS result: Clear/Illuminance MSB */ +#define GP2AP020A00F_D1_L_REG	0x0e /* ALS result: IR LSB */ +#define GP2AP020A00F_D1_H_REG	0x0f /* ALS result: IR LSB */ +#define GP2AP020A00F_D2_L_REG	0x10 /* PS result LSB */ +#define GP2AP020A00F_D2_H_REG	0x11 /* PS result MSB */ +#define GP2AP020A00F_NUM_REGS	0x12 /* Number of registers */ + +/* OP_REG bits */ +#define GP2AP020A00F_OP3_MASK		0x80 /* Software shutdown */ +#define GP2AP020A00F_OP3_SHUTDOWN	0x00 +#define GP2AP020A00F_OP3_OPERATION	0x80 +#define GP2AP020A00F_OP2_MASK		0x40 /* Auto shutdown/Continuous mode */ +#define GP2AP020A00F_OP2_AUTO_SHUTDOWN	0x00 +#define GP2AP020A00F_OP2_CONT_OPERATION	0x40 +#define GP2AP020A00F_OP_MASK		0x30 /* Operating mode selection  */ +#define GP2AP020A00F_OP_ALS_AND_PS	0x00 +#define GP2AP020A00F_OP_ALS		0x10 +#define GP2AP020A00F_OP_PS		0x20 +#define GP2AP020A00F_OP_DEBUG		0x30 +#define GP2AP020A00F_PROX_MASK		0x08 /* PS: detection/non-detection */ +#define GP2AP020A00F_PROX_NON_DETECT	0x00 +#define GP2AP020A00F_PROX_DETECT	0x08 +#define GP2AP020A00F_FLAG_P		0x04 /* PS: interrupt result  */ +#define GP2AP020A00F_FLAG_A		0x02 /* ALS: interrupt result  */ +#define GP2AP020A00F_TYPE_MASK		0x01 /* Output data type selection */ +#define GP2AP020A00F_TYPE_MANUAL_CALC	0x00 +#define GP2AP020A00F_TYPE_AUTO_CALC	0x01 + +/* ALS_REG bits */ +#define GP2AP020A00F_PRST_MASK		0xc0 /* Number of measurement cycles */ +#define GP2AP020A00F_PRST_ONCE		0x00 +#define GP2AP020A00F_PRST_4_CYCLES	0x40 +#define GP2AP020A00F_PRST_8_CYCLES	0x80 +#define GP2AP020A00F_PRST_16_CYCLES	0xc0 +#define GP2AP020A00F_RES_A_MASK		0x38 /* ALS: Resolution */ +#define GP2AP020A00F_RES_A_800ms	0x00 +#define GP2AP020A00F_RES_A_400ms	0x08 +#define GP2AP020A00F_RES_A_200ms	0x10 +#define GP2AP020A00F_RES_A_100ms	0x18 +#define GP2AP020A00F_RES_A_25ms		0x20 +#define GP2AP020A00F_RES_A_6_25ms	0x28 +#define GP2AP020A00F_RES_A_1_56ms	0x30 +#define GP2AP020A00F_RES_A_0_39ms	0x38 +#define GP2AP020A00F_RANGE_A_MASK	0x07 /* ALS: Max measurable range */ +#define GP2AP020A00F_RANGE_A_x1		0x00 +#define GP2AP020A00F_RANGE_A_x2		0x01 +#define GP2AP020A00F_RANGE_A_x4		0x02 +#define GP2AP020A00F_RANGE_A_x8		0x03 +#define GP2AP020A00F_RANGE_A_x16	0x04 +#define GP2AP020A00F_RANGE_A_x32	0x05 +#define GP2AP020A00F_RANGE_A_x64	0x06 +#define GP2AP020A00F_RANGE_A_x128	0x07 + +/* PS_REG bits */ +#define GP2AP020A00F_ALC_MASK		0x80 /* Auto light cancel */ +#define GP2AP020A00F_ALC_ON		0x80 +#define GP2AP020A00F_ALC_OFF		0x00 +#define GP2AP020A00F_INTTYPE_MASK	0x40 /* Interrupt type setting */ +#define GP2AP020A00F_INTTYPE_LEVEL	0x00 +#define GP2AP020A00F_INTTYPE_PULSE	0x40 +#define GP2AP020A00F_RES_P_MASK		0x38 /* PS: Resolution */ +#define GP2AP020A00F_RES_P_800ms_x2	0x00 +#define GP2AP020A00F_RES_P_400ms_x2	0x08 +#define GP2AP020A00F_RES_P_200ms_x2	0x10 +#define GP2AP020A00F_RES_P_100ms_x2	0x18 +#define GP2AP020A00F_RES_P_25ms_x2	0x20 +#define GP2AP020A00F_RES_P_6_25ms_x2	0x28 +#define GP2AP020A00F_RES_P_1_56ms_x2	0x30 +#define GP2AP020A00F_RES_P_0_39ms_x2	0x38 +#define GP2AP020A00F_RANGE_P_MASK	0x07 /* PS: Max measurable range */ +#define GP2AP020A00F_RANGE_P_x1		0x00 +#define GP2AP020A00F_RANGE_P_x2		0x01 +#define GP2AP020A00F_RANGE_P_x4		0x02 +#define GP2AP020A00F_RANGE_P_x8		0x03 +#define GP2AP020A00F_RANGE_P_x16	0x04 +#define GP2AP020A00F_RANGE_P_x32	0x05 +#define GP2AP020A00F_RANGE_P_x64	0x06 +#define GP2AP020A00F_RANGE_P_x128	0x07 + +/* LED reg bits */ +#define GP2AP020A00F_INTVAL_MASK	0xc0 /* Intermittent operating */ +#define GP2AP020A00F_INTVAL_0		0x00 +#define GP2AP020A00F_INTVAL_4		0x40 +#define GP2AP020A00F_INTVAL_8		0x80 +#define GP2AP020A00F_INTVAL_16		0xc0 +#define GP2AP020A00F_IS_MASK		0x30 /* ILED drive peak current */ +#define GP2AP020A00F_IS_13_8mA		0x00 +#define GP2AP020A00F_IS_27_5mA		0x10 +#define GP2AP020A00F_IS_55mA		0x20 +#define GP2AP020A00F_IS_110mA		0x30 +#define GP2AP020A00F_PIN_MASK		0x0c /* INT terminal setting */ +#define GP2AP020A00F_PIN_ALS_OR_PS	0x00 +#define GP2AP020A00F_PIN_ALS		0x04 +#define GP2AP020A00F_PIN_PS		0x08 +#define GP2AP020A00F_PIN_PS_DETECT	0x0c +#define GP2AP020A00F_FREQ_MASK		0x02 /* LED modulation frequency */ +#define GP2AP020A00F_FREQ_327_5kHz	0x00 +#define GP2AP020A00F_FREQ_81_8kHz	0x02 +#define GP2AP020A00F_RST		0x01 /* Software reset */ + +#define GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR	0 +#define GP2AP020A00F_SCAN_MODE_LIGHT_IR		1 +#define GP2AP020A00F_SCAN_MODE_PROXIMITY	2 +#define GP2AP020A00F_CHAN_TIMESTAMP		3 + +#define GP2AP020A00F_DATA_READY_TIMEOUT		msecs_to_jiffies(1000) +#define GP2AP020A00F_DATA_REG(chan)		(GP2AP020A00F_D0_L_REG + \ +							(chan) * 2) +#define GP2AP020A00F_THRESH_REG(th_val_id)	(GP2AP020A00F_TL_L_REG + \ +							(th_val_id) * 2) +#define GP2AP020A00F_THRESH_VAL_ID(reg_addr)	((reg_addr - 4) / 2) + +#define GP2AP020A00F_SUBTRACT_MODE	0 +#define GP2AP020A00F_ADD_MODE		1 + +#define GP2AP020A00F_MAX_CHANNELS	3 + +enum gp2ap020a00f_opmode { +	GP2AP020A00F_OPMODE_READ_RAW_CLEAR, +	GP2AP020A00F_OPMODE_READ_RAW_IR, +	GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY, +	GP2AP020A00F_OPMODE_ALS, +	GP2AP020A00F_OPMODE_PS, +	GP2AP020A00F_OPMODE_ALS_AND_PS, +	GP2AP020A00F_OPMODE_PROX_DETECT, +	GP2AP020A00F_OPMODE_SHUTDOWN, +	GP2AP020A00F_NUM_OPMODES, +}; + +enum gp2ap020a00f_cmd { +	GP2AP020A00F_CMD_READ_RAW_CLEAR, +	GP2AP020A00F_CMD_READ_RAW_IR, +	GP2AP020A00F_CMD_READ_RAW_PROXIMITY, +	GP2AP020A00F_CMD_TRIGGER_CLEAR_EN, +	GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS, +	GP2AP020A00F_CMD_TRIGGER_IR_EN, +	GP2AP020A00F_CMD_TRIGGER_IR_DIS, +	GP2AP020A00F_CMD_TRIGGER_PROX_EN, +	GP2AP020A00F_CMD_TRIGGER_PROX_DIS, +	GP2AP020A00F_CMD_ALS_HIGH_EV_EN, +	GP2AP020A00F_CMD_ALS_HIGH_EV_DIS, +	GP2AP020A00F_CMD_ALS_LOW_EV_EN, +	GP2AP020A00F_CMD_ALS_LOW_EV_DIS, +	GP2AP020A00F_CMD_PROX_HIGH_EV_EN, +	GP2AP020A00F_CMD_PROX_HIGH_EV_DIS, +	GP2AP020A00F_CMD_PROX_LOW_EV_EN, +	GP2AP020A00F_CMD_PROX_LOW_EV_DIS, +}; + +enum gp2ap020a00f_flags { +	GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, +	GP2AP020A00F_FLAG_ALS_IR_TRIGGER, +	GP2AP020A00F_FLAG_PROX_TRIGGER, +	GP2AP020A00F_FLAG_PROX_RISING_EV, +	GP2AP020A00F_FLAG_PROX_FALLING_EV, +	GP2AP020A00F_FLAG_ALS_RISING_EV, +	GP2AP020A00F_FLAG_ALS_FALLING_EV, +	GP2AP020A00F_FLAG_LUX_MODE_HI, +	GP2AP020A00F_FLAG_DATA_READY, +}; + +enum gp2ap020a00f_thresh_val_id { +	GP2AP020A00F_THRESH_TL, +	GP2AP020A00F_THRESH_TH, +	GP2AP020A00F_THRESH_PL, +	GP2AP020A00F_THRESH_PH, +}; + +struct gp2ap020a00f_data { +	const struct gp2ap020a00f_platform_data *pdata; +	struct i2c_client *client; +	struct mutex lock; +	char *buffer; +	struct regulator *vled_reg; +	unsigned long flags; +	enum gp2ap020a00f_opmode cur_opmode; +	struct iio_trigger *trig; +	struct regmap *regmap; +	unsigned int thresh_val[4]; +	u8 debug_reg_addr; +	struct irq_work work; +	wait_queue_head_t data_ready_queue; +}; + +static const u8 gp2ap020a00f_reg_init_tab[] = { +	[GP2AP020A00F_OP_REG] = GP2AP020A00F_OP3_SHUTDOWN, +	[GP2AP020A00F_ALS_REG] = GP2AP020A00F_RES_A_25ms | +				 GP2AP020A00F_RANGE_A_x8, +	[GP2AP020A00F_PS_REG] = GP2AP020A00F_ALC_ON | +				GP2AP020A00F_RES_P_1_56ms_x2 | +				GP2AP020A00F_RANGE_P_x4, +	[GP2AP020A00F_LED_REG] = GP2AP020A00F_INTVAL_0 | +				 GP2AP020A00F_IS_110mA | +				 GP2AP020A00F_FREQ_327_5kHz, +	[GP2AP020A00F_TL_L_REG] = 0, +	[GP2AP020A00F_TL_H_REG] = 0, +	[GP2AP020A00F_TH_L_REG] = 0, +	[GP2AP020A00F_TH_H_REG] = 0, +	[GP2AP020A00F_PL_L_REG] = 0, +	[GP2AP020A00F_PL_H_REG] = 0, +	[GP2AP020A00F_PH_L_REG] = 0, +	[GP2AP020A00F_PH_H_REG] = 0, +}; + +static bool gp2ap020a00f_is_volatile_reg(struct device *dev, unsigned int reg) +{ +	switch (reg) { +	case GP2AP020A00F_OP_REG: +	case GP2AP020A00F_D0_L_REG: +	case GP2AP020A00F_D0_H_REG: +	case GP2AP020A00F_D1_L_REG: +	case GP2AP020A00F_D1_H_REG: +	case GP2AP020A00F_D2_L_REG: +	case GP2AP020A00F_D2_H_REG: +		return true; +	default: +		return false; +	} +} + +static const struct regmap_config gp2ap020a00f_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, + +	.max_register = GP2AP020A00F_D2_H_REG, +	.cache_type = REGCACHE_RBTREE, + +	.volatile_reg = gp2ap020a00f_is_volatile_reg, +}; + +static const struct gp2ap020a00f_mutable_config_regs { +	u8 op_reg; +	u8 als_reg; +	u8 ps_reg; +	u8 led_reg; +} opmode_regs_settings[GP2AP020A00F_NUM_OPMODES] = { +	[GP2AP020A00F_OPMODE_READ_RAW_CLEAR] = { +		GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_AUTO_CALC, +		GP2AP020A00F_PRST_ONCE, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_ALS +	}, +	[GP2AP020A00F_OPMODE_READ_RAW_IR] = { +		GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_MANUAL_CALC, +		GP2AP020A00F_PRST_ONCE, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_ALS +	}, +	[GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY] = { +		GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_MANUAL_CALC, +		GP2AP020A00F_PRST_ONCE, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_PS +	}, +	[GP2AP020A00F_OPMODE_PROX_DETECT] = { +		GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_MANUAL_CALC, +		GP2AP020A00F_PRST_4_CYCLES, +		GP2AP020A00F_INTTYPE_PULSE, +		GP2AP020A00F_PIN_PS_DETECT +	}, +	[GP2AP020A00F_OPMODE_ALS] = { +		GP2AP020A00F_OP_ALS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_AUTO_CALC, +		GP2AP020A00F_PRST_ONCE, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_ALS +	}, +	[GP2AP020A00F_OPMODE_PS] = { +		GP2AP020A00F_OP_PS | GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_MANUAL_CALC, +		GP2AP020A00F_PRST_4_CYCLES, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_PS +	}, +	[GP2AP020A00F_OPMODE_ALS_AND_PS] = { +		GP2AP020A00F_OP_ALS_AND_PS +		| GP2AP020A00F_OP2_CONT_OPERATION +		| GP2AP020A00F_OP3_OPERATION +		| GP2AP020A00F_TYPE_AUTO_CALC, +		GP2AP020A00F_PRST_4_CYCLES, +		GP2AP020A00F_INTTYPE_LEVEL, +		GP2AP020A00F_PIN_ALS_OR_PS +	}, +	[GP2AP020A00F_OPMODE_SHUTDOWN] = { GP2AP020A00F_OP3_SHUTDOWN, }, +}; + +static int gp2ap020a00f_set_operation_mode(struct gp2ap020a00f_data *data, +					enum gp2ap020a00f_opmode op) +{ +	unsigned int op_reg_val; +	int err; + +	if (op != GP2AP020A00F_OPMODE_SHUTDOWN) { +		err = regmap_read(data->regmap, GP2AP020A00F_OP_REG, +					&op_reg_val); +		if (err < 0) +			return err; +		/* +		 * Shutdown the device if the operation being executed entails +		 * mode transition. +		 */ +		if ((opmode_regs_settings[op].op_reg & GP2AP020A00F_OP_MASK) != +		    (op_reg_val & GP2AP020A00F_OP_MASK)) { +			/* set shutdown mode */ +			err = regmap_update_bits(data->regmap, +				GP2AP020A00F_OP_REG, GP2AP020A00F_OP3_MASK, +				GP2AP020A00F_OP3_SHUTDOWN); +			if (err < 0) +				return err; +		} + +		err = regmap_update_bits(data->regmap, GP2AP020A00F_ALS_REG, +			GP2AP020A00F_PRST_MASK, opmode_regs_settings[op] +								.als_reg); +		if (err < 0) +			return err; + +		err = regmap_update_bits(data->regmap, GP2AP020A00F_PS_REG, +			GP2AP020A00F_INTTYPE_MASK, opmode_regs_settings[op] +								.ps_reg); +		if (err < 0) +			return err; + +		err = regmap_update_bits(data->regmap, GP2AP020A00F_LED_REG, +			GP2AP020A00F_PIN_MASK, opmode_regs_settings[op] +								.led_reg); +		if (err < 0) +			return err; +	} + +	/* Set OP_REG and apply operation mode (power on / off) */ +	err = regmap_update_bits(data->regmap, +				 GP2AP020A00F_OP_REG, +				 GP2AP020A00F_OP_MASK | GP2AP020A00F_OP2_MASK | +				 GP2AP020A00F_OP3_MASK | GP2AP020A00F_TYPE_MASK, +				 opmode_regs_settings[op].op_reg); +	if (err < 0) +		return err; + +	data->cur_opmode = op; + +	return 0; +} + +static bool gp2ap020a00f_als_enabled(struct gp2ap020a00f_data *data) +{ +	return test_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags) || +	       test_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags) || +	       test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags) || +	       test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); +} + +static bool gp2ap020a00f_prox_detect_enabled(struct gp2ap020a00f_data *data) +{ +	return test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags) || +	       test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); +} + +static int gp2ap020a00f_write_event_threshold(struct gp2ap020a00f_data *data, +				enum gp2ap020a00f_thresh_val_id th_val_id, +				bool enable) +{ +	__le16 thresh_buf = 0; +	unsigned int thresh_reg_val; + +	if (!enable) +		thresh_reg_val = 0; +	else if (test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags) && +		 th_val_id != GP2AP020A00F_THRESH_PL && +		 th_val_id != GP2AP020A00F_THRESH_PH) +		/* +		 * For the high lux mode ALS threshold has to be scaled down +		 * to allow for proper comparison with the output value. +		 */ +		thresh_reg_val = data->thresh_val[th_val_id] / 16; +	else +		thresh_reg_val = data->thresh_val[th_val_id] > 16000 ? +					16000 : +					data->thresh_val[th_val_id]; + +	thresh_buf = cpu_to_le16(thresh_reg_val); + +	return regmap_bulk_write(data->regmap, +				 GP2AP020A00F_THRESH_REG(th_val_id), +				 (u8 *)&thresh_buf, 2); +} + +static int gp2ap020a00f_alter_opmode(struct gp2ap020a00f_data *data, +			enum gp2ap020a00f_opmode diff_mode, int add_sub) +{ +	enum gp2ap020a00f_opmode new_mode; + +	if (diff_mode != GP2AP020A00F_OPMODE_ALS && +	    diff_mode != GP2AP020A00F_OPMODE_PS) +		return -EINVAL; + +	if (add_sub == GP2AP020A00F_ADD_MODE) { +		if (data->cur_opmode == GP2AP020A00F_OPMODE_SHUTDOWN) +			new_mode =  diff_mode; +		else +			new_mode = GP2AP020A00F_OPMODE_ALS_AND_PS; +	} else { +		if (data->cur_opmode == GP2AP020A00F_OPMODE_ALS_AND_PS) +			new_mode = (diff_mode == GP2AP020A00F_OPMODE_ALS) ? +					GP2AP020A00F_OPMODE_PS : +					GP2AP020A00F_OPMODE_ALS; +		else +			new_mode = GP2AP020A00F_OPMODE_SHUTDOWN; +	} + +	return gp2ap020a00f_set_operation_mode(data, new_mode); +} + +static int gp2ap020a00f_exec_cmd(struct gp2ap020a00f_data *data, +					enum gp2ap020a00f_cmd cmd) +{ +	int err = 0; + +	switch (cmd) { +	case GP2AP020A00F_CMD_READ_RAW_CLEAR: +		if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) +			return -EBUSY; +		err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_READ_RAW_CLEAR); +		break; +	case GP2AP020A00F_CMD_READ_RAW_IR: +		if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) +			return -EBUSY; +		err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_READ_RAW_IR); +		break; +	case GP2AP020A00F_CMD_READ_RAW_PROXIMITY: +		if (data->cur_opmode != GP2AP020A00F_OPMODE_SHUTDOWN) +			return -EBUSY; +		err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY); +		break; +	case GP2AP020A00F_CMD_TRIGGER_CLEAR_EN: +		if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) +			return -EBUSY; +		if (!gp2ap020a00f_als_enabled(data)) +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_ADD_MODE); +		set_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); +		break; +	case GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS: +		clear_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &data->flags); +		if (gp2ap020a00f_als_enabled(data)) +			break; +		err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_SUBTRACT_MODE); +		break; +	case GP2AP020A00F_CMD_TRIGGER_IR_EN: +		if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) +			return -EBUSY; +		if (!gp2ap020a00f_als_enabled(data)) +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_ADD_MODE); +		set_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); +		break; +	case GP2AP020A00F_CMD_TRIGGER_IR_DIS: +		clear_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &data->flags); +		if (gp2ap020a00f_als_enabled(data)) +			break; +		err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_SUBTRACT_MODE); +		break; +	case GP2AP020A00F_CMD_TRIGGER_PROX_EN: +		if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) +			return -EBUSY; +		err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_PS, +						GP2AP020A00F_ADD_MODE); +		set_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); +		break; +	case GP2AP020A00F_CMD_TRIGGER_PROX_DIS: +		clear_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &data->flags); +		err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_PS, +						GP2AP020A00F_SUBTRACT_MODE); +		break; +	case GP2AP020A00F_CMD_ALS_HIGH_EV_EN: +		if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) +			return 0; +		if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) +			return -EBUSY; +		if (!gp2ap020a00f_als_enabled(data)) { +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_ADD_MODE); +			if (err < 0) +				return err; +		} +		set_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TH, true); +		break; +	case GP2AP020A00F_CMD_ALS_HIGH_EV_DIS: +		if (!test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) +			return 0; +		clear_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags); +		if (!gp2ap020a00f_als_enabled(data)) { +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_SUBTRACT_MODE); +			if (err < 0) +				return err; +		} +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TH, false); +		break; +	case GP2AP020A00F_CMD_ALS_LOW_EV_EN: +		if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) +			return 0; +		if (data->cur_opmode == GP2AP020A00F_OPMODE_PROX_DETECT) +			return -EBUSY; +		if (!gp2ap020a00f_als_enabled(data)) { +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_ADD_MODE); +			if (err < 0) +				return err; +		} +		set_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TL, true); +		break; +	case GP2AP020A00F_CMD_ALS_LOW_EV_DIS: +		if (!test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) +			return 0; +		clear_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags); +		if (!gp2ap020a00f_als_enabled(data)) { +			err = gp2ap020a00f_alter_opmode(data, +						GP2AP020A00F_OPMODE_ALS, +						GP2AP020A00F_SUBTRACT_MODE); +			if (err < 0) +				return err; +		} +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TL, false); +		break; +	case GP2AP020A00F_CMD_PROX_HIGH_EV_EN: +		if (test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) +			return 0; +		if (gp2ap020a00f_als_enabled(data) || +		    data->cur_opmode == GP2AP020A00F_OPMODE_PS) +			return -EBUSY; +		if (!gp2ap020a00f_prox_detect_enabled(data)) { +			err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_PROX_DETECT); +			if (err < 0) +				return err; +		} +		set_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_PH, true); +		break; +	case GP2AP020A00F_CMD_PROX_HIGH_EV_DIS: +		if (!test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags)) +			return 0; +		clear_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, &data->flags); +		err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_SHUTDOWN); +		if (err < 0) +			return err; +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_PH, false); +		break; +	case GP2AP020A00F_CMD_PROX_LOW_EV_EN: +		if (test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) +			return 0; +		if (gp2ap020a00f_als_enabled(data) || +		    data->cur_opmode == GP2AP020A00F_OPMODE_PS) +			return -EBUSY; +		if (!gp2ap020a00f_prox_detect_enabled(data)) { +			err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_PROX_DETECT); +			if (err < 0) +				return err; +		} +		set_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_PL, true); +		break; +	case GP2AP020A00F_CMD_PROX_LOW_EV_DIS: +		if (!test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags)) +			return 0; +		clear_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, &data->flags); +		err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_SHUTDOWN); +		if (err < 0) +			return err; +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_PL, false); +		break; +	} + +	return err; +} + +static int wait_conversion_complete_irq(struct gp2ap020a00f_data *data) +{ +	int ret; + +	ret = wait_event_timeout(data->data_ready_queue, +				 test_bit(GP2AP020A00F_FLAG_DATA_READY, +					  &data->flags), +				 GP2AP020A00F_DATA_READY_TIMEOUT); +	clear_bit(GP2AP020A00F_FLAG_DATA_READY, &data->flags); + +	return ret > 0 ? 0 : -ETIME; +} + +static int gp2ap020a00f_read_output(struct gp2ap020a00f_data *data, +					unsigned int output_reg, int *val) +{ +	u8 reg_buf[2]; +	int err; + +	err = wait_conversion_complete_irq(data); +	if (err < 0) +		dev_dbg(&data->client->dev, "data ready timeout\n"); + +	err = regmap_bulk_read(data->regmap, output_reg, reg_buf, 2); +	if (err < 0) +		return err; + +	*val = le16_to_cpup((__le16 *)reg_buf); + +	return err; +} + +static bool gp2ap020a00f_adjust_lux_mode(struct gp2ap020a00f_data *data, +				 int output_val) +{ +	u8 new_range = 0xff; +	int err; + +	if (!test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags)) { +		if (output_val > 16000) { +			set_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags); +			new_range = GP2AP020A00F_RANGE_A_x128; +		} +	} else { +		if (output_val < 1000) { +			clear_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags); +			new_range = GP2AP020A00F_RANGE_A_x8; +		} +	} + +	if (new_range != 0xff) { +		/* Clear als threshold registers to avoid spurious +		 * events caused by lux mode transition. +		 */ +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TH, false); +		if (err < 0) { +			dev_err(&data->client->dev, +				"Clearing als threshold register failed.\n"); +			return false; +		} + +		err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TL, false); +		if (err < 0) { +			dev_err(&data->client->dev, +				"Clearing als threshold register failed.\n"); +			return false; +		} + +		/* Change lux mode */ +		err = regmap_update_bits(data->regmap, +			GP2AP020A00F_OP_REG, +			GP2AP020A00F_OP3_MASK, +			GP2AP020A00F_OP3_SHUTDOWN); + +		if (err < 0) { +			dev_err(&data->client->dev, +				"Shutting down the device failed.\n"); +			return false; +		} + +		err = regmap_update_bits(data->regmap, +			GP2AP020A00F_ALS_REG, +			GP2AP020A00F_RANGE_A_MASK, +			new_range); + +		if (err < 0) { +			dev_err(&data->client->dev, +				"Adjusting device lux mode failed.\n"); +			return false; +		} + +		err = regmap_update_bits(data->regmap, +			GP2AP020A00F_OP_REG, +			GP2AP020A00F_OP3_MASK, +			GP2AP020A00F_OP3_OPERATION); + +		if (err < 0) { +			dev_err(&data->client->dev, +				"Powering up the device failed.\n"); +			return false; +		} + +		/* Adjust als threshold register values to the new lux mode */ +		if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &data->flags)) { +			err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TH, true); +			if (err < 0) { +				dev_err(&data->client->dev, +				"Adjusting als threshold value failed.\n"); +				return false; +			} +		} + +		if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &data->flags)) { +			err =  gp2ap020a00f_write_event_threshold(data, +					GP2AP020A00F_THRESH_TL, true); +			if (err < 0) { +				dev_err(&data->client->dev, +				"Adjusting als threshold value failed.\n"); +				return false; +			} +		} + +		return true; +	} + +	return false; +} + +static void gp2ap020a00f_output_to_lux(struct gp2ap020a00f_data *data, +						int *output_val) +{ +	if (test_bit(GP2AP020A00F_FLAG_LUX_MODE_HI, &data->flags)) +		*output_val *= 16; +} + +static void gp2ap020a00f_iio_trigger_work(struct irq_work *work) +{ +	struct gp2ap020a00f_data *data = +		container_of(work, struct gp2ap020a00f_data, work); + +	iio_trigger_poll(data->trig, 0); +} + +static irqreturn_t gp2ap020a00f_prox_sensing_handler(int irq, void *data) +{ +	struct iio_dev *indio_dev = data; +	struct gp2ap020a00f_data *priv = iio_priv(indio_dev); +	unsigned int op_reg_val; +	int ret; + +	/* Read interrupt flags */ +	ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, &op_reg_val); +	if (ret < 0) +		return IRQ_HANDLED; + +	if (gp2ap020a00f_prox_detect_enabled(priv)) { +		if (op_reg_val & GP2AP020A00F_PROX_DETECT) { +			iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE( +				    IIO_PROXIMITY, +				    GP2AP020A00F_SCAN_MODE_PROXIMITY, +				    IIO_EV_TYPE_ROC, +				    IIO_EV_DIR_RISING), +			       iio_get_time_ns()); +		} else { +			iio_push_event(indio_dev, +			       IIO_UNMOD_EVENT_CODE( +				    IIO_PROXIMITY, +				    GP2AP020A00F_SCAN_MODE_PROXIMITY, +				    IIO_EV_TYPE_ROC, +				    IIO_EV_DIR_FALLING), +			       iio_get_time_ns()); +		} +	} + +	return IRQ_HANDLED; +} + +static irqreturn_t gp2ap020a00f_thresh_event_handler(int irq, void *data) +{ +	struct iio_dev *indio_dev = data; +	struct gp2ap020a00f_data *priv = iio_priv(indio_dev); +	u8 op_reg_flags, d0_reg_buf[2]; +	unsigned int output_val, op_reg_val; +	int thresh_val_id, ret; + +	/* Read interrupt flags */ +	ret = regmap_read(priv->regmap, GP2AP020A00F_OP_REG, +							&op_reg_val); +	if (ret < 0) +		goto done; + +	op_reg_flags = op_reg_val & (GP2AP020A00F_FLAG_A | GP2AP020A00F_FLAG_P +					| GP2AP020A00F_PROX_DETECT); + +	op_reg_val &= (~GP2AP020A00F_FLAG_A & ~GP2AP020A00F_FLAG_P +					& ~GP2AP020A00F_PROX_DETECT); + +	/* Clear interrupt flags (if not in INTTYPE_PULSE mode) */ +	if (priv->cur_opmode != GP2AP020A00F_OPMODE_PROX_DETECT) { +		ret = regmap_write(priv->regmap, GP2AP020A00F_OP_REG, +								op_reg_val); +		if (ret < 0) +			goto done; +	} + +	if (op_reg_flags & GP2AP020A00F_FLAG_A) { +		/* Check D0 register to assess if the lux mode +		 * transition is required. +		 */ +		ret = regmap_bulk_read(priv->regmap, GP2AP020A00F_D0_L_REG, +							d0_reg_buf, 2); +		if (ret < 0) +			goto done; + +		output_val = le16_to_cpup((__le16 *)d0_reg_buf); + +		if (gp2ap020a00f_adjust_lux_mode(priv, output_val)) +			goto done; + +		gp2ap020a00f_output_to_lux(priv, &output_val); + +		/* +		 * We need to check output value to distinguish +		 * between high and low ambient light threshold event. +		 */ +		if (test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, &priv->flags)) { +			thresh_val_id = +			    GP2AP020A00F_THRESH_VAL_ID(GP2AP020A00F_TH_L_REG); +			if (output_val > priv->thresh_val[thresh_val_id]) +				iio_push_event(indio_dev, +				       IIO_MOD_EVENT_CODE( +					    IIO_LIGHT, +					    GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, +					    IIO_MOD_LIGHT_CLEAR, +					    IIO_EV_TYPE_THRESH, +					    IIO_EV_DIR_RISING), +				       iio_get_time_ns()); +		} + +		if (test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, &priv->flags)) { +			thresh_val_id = +			    GP2AP020A00F_THRESH_VAL_ID(GP2AP020A00F_TL_L_REG); +			if (output_val < priv->thresh_val[thresh_val_id]) +				iio_push_event(indio_dev, +				       IIO_MOD_EVENT_CODE( +					    IIO_LIGHT, +					    GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, +					    IIO_MOD_LIGHT_CLEAR, +					    IIO_EV_TYPE_THRESH, +					    IIO_EV_DIR_FALLING), +				       iio_get_time_ns()); +		} +	} + +	if (priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_CLEAR || +	    priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_IR || +	    priv->cur_opmode == GP2AP020A00F_OPMODE_READ_RAW_PROXIMITY) { +		set_bit(GP2AP020A00F_FLAG_DATA_READY, &priv->flags); +		wake_up(&priv->data_ready_queue); +		goto done; +	} + +	if (test_bit(GP2AP020A00F_FLAG_ALS_CLEAR_TRIGGER, &priv->flags) || +	    test_bit(GP2AP020A00F_FLAG_ALS_IR_TRIGGER, &priv->flags) || +	    test_bit(GP2AP020A00F_FLAG_PROX_TRIGGER, &priv->flags)) +		/* This fires off the trigger. */ +		irq_work_queue(&priv->work); + +done: +	return IRQ_HANDLED; +} + +static irqreturn_t gp2ap020a00f_trigger_handler(int irq, void *data) +{ +	struct iio_poll_func *pf = data; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct gp2ap020a00f_data *priv = iio_priv(indio_dev); +	size_t d_size = 0; +	__le32 light_lux; +	int i, out_val, ret; + +	for_each_set_bit(i, indio_dev->active_scan_mask, +		indio_dev->masklength) { +		ret = regmap_bulk_read(priv->regmap, +				GP2AP020A00F_DATA_REG(i), +				&priv->buffer[d_size], 2); +		if (ret < 0) +			goto done; + +		if (i == GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR || +		    i == GP2AP020A00F_SCAN_MODE_LIGHT_IR) { +			out_val = le16_to_cpup((__le16 *)&priv->buffer[d_size]); +			gp2ap020a00f_output_to_lux(priv, &out_val); +			light_lux = cpu_to_le32(out_val); +			memcpy(&priv->buffer[d_size], (u8 *)&light_lux, 4); +			d_size += 4; +		} else { +			d_size += 2; +		} +	} + +	iio_push_to_buffers_with_timestamp(indio_dev, priv->buffer, +		pf->timestamp); +done: +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +static u8 gp2ap020a00f_get_thresh_reg(const struct iio_chan_spec *chan, +					     enum iio_event_direction event_dir) +{ +	switch (chan->type) { +	case IIO_PROXIMITY: +		if (event_dir == IIO_EV_DIR_RISING) +			return GP2AP020A00F_PH_L_REG; +		else +			return GP2AP020A00F_PL_L_REG; +	case IIO_LIGHT: +		if (event_dir == IIO_EV_DIR_RISING) +			return GP2AP020A00F_TH_L_REG; +		else +			return GP2AP020A00F_TL_L_REG; +	default: +		break; +	} + +	return -EINVAL; +} + +static int gp2ap020a00f_write_event_val(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) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	bool event_en = false; +	u8 thresh_val_id; +	u8 thresh_reg_l; +	int err = 0; + +	mutex_lock(&data->lock); + +	thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); +	thresh_val_id = GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l); + +	if (thresh_val_id > GP2AP020A00F_THRESH_PH) { +		err = -EINVAL; +		goto error_unlock; +	} + +	switch (thresh_reg_l) { +	case GP2AP020A00F_TH_L_REG: +		event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, +							&data->flags); +		break; +	case GP2AP020A00F_TL_L_REG: +		event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, +							&data->flags); +		break; +	case GP2AP020A00F_PH_L_REG: +		if (val == 0) { +			err = -EINVAL; +			goto error_unlock; +		} +		event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, +							&data->flags); +		break; +	case GP2AP020A00F_PL_L_REG: +		if (val == 0) { +			err = -EINVAL; +			goto error_unlock; +		} +		event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, +							&data->flags); +		break; +	} + +	data->thresh_val[thresh_val_id] = val; +	err =  gp2ap020a00f_write_event_threshold(data, thresh_val_id, +							event_en); +error_unlock: +	mutex_unlock(&data->lock); + +	return err; +} + +static int gp2ap020a00f_read_event_val(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) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	u8 thresh_reg_l; +	int err = IIO_VAL_INT; + +	mutex_lock(&data->lock); + +	thresh_reg_l = gp2ap020a00f_get_thresh_reg(chan, dir); + +	if (thresh_reg_l > GP2AP020A00F_PH_L_REG) { +		err = -EINVAL; +		goto error_unlock; +	} + +	*val = data->thresh_val[GP2AP020A00F_THRESH_VAL_ID(thresh_reg_l)]; + +error_unlock: +	mutex_unlock(&data->lock); + +	return err; +} + +static int gp2ap020a00f_write_prox_event_config(struct iio_dev *indio_dev, +						int state) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	enum gp2ap020a00f_cmd cmd_high_ev, cmd_low_ev; +	int err; + +	cmd_high_ev = state ? GP2AP020A00F_CMD_PROX_HIGH_EV_EN : +			      GP2AP020A00F_CMD_PROX_HIGH_EV_DIS; +	cmd_low_ev = state ? GP2AP020A00F_CMD_PROX_LOW_EV_EN : +			     GP2AP020A00F_CMD_PROX_LOW_EV_DIS; + +	/* +	 * In order to enable proximity detection feature in the device +	 * both high and low threshold registers have to be written +	 * with different values, greater than zero. +	 */ +	if (state) { +		if (data->thresh_val[GP2AP020A00F_THRESH_PL] == 0) +			return -EINVAL; + +		if (data->thresh_val[GP2AP020A00F_THRESH_PH] == 0) +			return -EINVAL; +	} + +	err = gp2ap020a00f_exec_cmd(data, cmd_high_ev); +	if (err < 0) +		return err; + +	err = gp2ap020a00f_exec_cmd(data, cmd_low_ev); +	if (err < 0) +		return err; + +	free_irq(data->client->irq, indio_dev); + +	if (state) +		err = request_threaded_irq(data->client->irq, NULL, +					   &gp2ap020a00f_prox_sensing_handler, +					   IRQF_TRIGGER_RISING | +					   IRQF_TRIGGER_FALLING | +					   IRQF_ONESHOT, +					   "gp2ap020a00f_prox_sensing", +					   indio_dev); +	else { +		err = request_threaded_irq(data->client->irq, NULL, +					   &gp2ap020a00f_thresh_event_handler, +					   IRQF_TRIGGER_FALLING | +					   IRQF_ONESHOT, +					   "gp2ap020a00f_thresh_event", +					   indio_dev); +	} + +	return err; +} + +static int gp2ap020a00f_write_event_config(struct iio_dev *indio_dev, +					   const struct iio_chan_spec *chan, +					   enum iio_event_type type, +					   enum iio_event_direction dir, +					   int state) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	enum gp2ap020a00f_cmd cmd; +	int err; + +	mutex_lock(&data->lock); + +	switch (chan->type) { +	case IIO_PROXIMITY: +		err = gp2ap020a00f_write_prox_event_config(indio_dev, state); +		break; +	case IIO_LIGHT: +		if (dir == IIO_EV_DIR_RISING) { +			cmd = state ? GP2AP020A00F_CMD_ALS_HIGH_EV_EN : +				      GP2AP020A00F_CMD_ALS_HIGH_EV_DIS; +			err = gp2ap020a00f_exec_cmd(data, cmd); +		} else { +			cmd = state ? GP2AP020A00F_CMD_ALS_LOW_EV_EN : +				      GP2AP020A00F_CMD_ALS_LOW_EV_DIS; +			err = gp2ap020a00f_exec_cmd(data, cmd); +		} +		break; +	default: +		err = -EINVAL; +	} + +	mutex_unlock(&data->lock); + +	return err; +} + +static int gp2ap020a00f_read_event_config(struct iio_dev *indio_dev, +					   const struct iio_chan_spec *chan, +					   enum iio_event_type type, +					   enum iio_event_direction dir) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	int event_en = 0; + +	mutex_lock(&data->lock); + +	switch (chan->type) { +	case IIO_PROXIMITY: +		if (dir == IIO_EV_DIR_RISING) +			event_en = test_bit(GP2AP020A00F_FLAG_PROX_RISING_EV, +								&data->flags); +		else +			event_en = test_bit(GP2AP020A00F_FLAG_PROX_FALLING_EV, +								&data->flags); +		break; +	case IIO_LIGHT: +		if (dir == IIO_EV_DIR_RISING) +			event_en = test_bit(GP2AP020A00F_FLAG_ALS_RISING_EV, +								&data->flags); +		else +			event_en = test_bit(GP2AP020A00F_FLAG_ALS_FALLING_EV, +								&data->flags); +		break; +	default: +		event_en = -EINVAL; +		break; +	} + +	mutex_unlock(&data->lock); + +	return event_en; +} + +static int gp2ap020a00f_read_channel(struct gp2ap020a00f_data *data, +				struct iio_chan_spec const *chan, int *val) +{ +	enum gp2ap020a00f_cmd cmd; +	int err; + +	switch (chan->scan_index) { +	case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: +		cmd = GP2AP020A00F_CMD_READ_RAW_CLEAR; +		break; +	case GP2AP020A00F_SCAN_MODE_LIGHT_IR: +		cmd = GP2AP020A00F_CMD_READ_RAW_IR; +		break; +	case GP2AP020A00F_SCAN_MODE_PROXIMITY: +		cmd = GP2AP020A00F_CMD_READ_RAW_PROXIMITY; +		break; +	default: +		return -EINVAL; +	} + +	err = gp2ap020a00f_exec_cmd(data, cmd); +	if (err < 0) { +		dev_err(&data->client->dev, +			"gp2ap020a00f_exec_cmd failed\n"); +		goto error_ret; +	} + +	err = gp2ap020a00f_read_output(data, chan->address, val); +	if (err < 0) +		dev_err(&data->client->dev, +			"gp2ap020a00f_read_output failed\n"); + +	err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_SHUTDOWN); +	if (err < 0) +		dev_err(&data->client->dev, +			"Failed to shut down the device.\n"); + +	if (cmd == GP2AP020A00F_CMD_READ_RAW_CLEAR || +	    cmd == GP2AP020A00F_CMD_READ_RAW_IR) +		gp2ap020a00f_output_to_lux(data, val); + +error_ret: +	return err; +} + +static int gp2ap020a00f_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, int *val2, +			   long mask) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	int err = -EINVAL; + +	mutex_lock(&data->lock); + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		if (iio_buffer_enabled(indio_dev)) { +			err = -EBUSY; +			goto error_unlock; +		} + +		err = gp2ap020a00f_read_channel(data, chan, val); +		break; +	} + +error_unlock: +	mutex_unlock(&data->lock); + +	return err < 0 ? err : IIO_VAL_INT; +} + +static const struct iio_event_spec gp2ap020a00f_event_spec_light[] = { +	{ +		.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), +	}, +}; + +static const struct iio_event_spec gp2ap020a00f_event_spec_prox[] = { +	{ +		.type = IIO_EV_TYPE_ROC, +		.dir = IIO_EV_DIR_RISING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, { +		.type = IIO_EV_TYPE_ROC, +		.dir = IIO_EV_DIR_FALLING, +		.mask_separate = BIT(IIO_EV_INFO_VALUE) | +			BIT(IIO_EV_INFO_ENABLE), +	}, +}; + +static const struct iio_chan_spec gp2ap020a00f_channels[] = { +	{ +		.type = IIO_LIGHT, +		.channel2 = IIO_MOD_LIGHT_CLEAR, +		.modified = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.scan_type = { +			.sign = 'u', +			.realbits = 24, +			.shift = 0, +			.storagebits = 32, +			.endianness = IIO_LE, +		}, +		.scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR, +		.address = GP2AP020A00F_D0_L_REG, +		.event_spec = gp2ap020a00f_event_spec_light, +		.num_event_specs = ARRAY_SIZE(gp2ap020a00f_event_spec_light), +	}, +	{ +		.type = IIO_LIGHT, +		.channel2 = IIO_MOD_LIGHT_IR, +		.modified = 1, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.scan_type = { +			.sign = 'u', +			.realbits = 24, +			.shift = 0, +			.storagebits = 32, +			.endianness = IIO_LE, +		}, +		.scan_index = GP2AP020A00F_SCAN_MODE_LIGHT_IR, +		.address = GP2AP020A00F_D1_L_REG, +	}, +	{ +		.type = IIO_PROXIMITY, +		.modified = 0, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.scan_type = { +			.sign = 'u', +			.realbits = 16, +			.shift = 0, +			.storagebits = 16, +			.endianness = IIO_LE, +		}, +		.scan_index = GP2AP020A00F_SCAN_MODE_PROXIMITY, +		.address = GP2AP020A00F_D2_L_REG, +		.event_spec = gp2ap020a00f_event_spec_prox, +		.num_event_specs = ARRAY_SIZE(gp2ap020a00f_event_spec_prox), +	}, +	IIO_CHAN_SOFT_TIMESTAMP(GP2AP020A00F_CHAN_TIMESTAMP), +}; + +static const struct iio_info gp2ap020a00f_info = { +	.read_raw = &gp2ap020a00f_read_raw, +	.read_event_value = &gp2ap020a00f_read_event_val, +	.read_event_config = &gp2ap020a00f_read_event_config, +	.write_event_value = &gp2ap020a00f_write_event_val, +	.write_event_config = &gp2ap020a00f_write_event_config, +	.driver_module = THIS_MODULE, +}; + +static int gp2ap020a00f_buffer_postenable(struct iio_dev *indio_dev) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	int i, err = 0; + +	mutex_lock(&data->lock); + +	/* +	 * Enable triggers according to the scan_mask. Enabling either +	 * LIGHT_CLEAR or LIGHT_IR scan mode results in enabling ALS +	 * module in the device, which generates samples in both D0 (clear) +	 * and D1 (ir) registers. As the two registers are bound to the +	 * two separate IIO channels they are treated in the driver logic +	 * as if they were controlled independently. +	 */ +	for_each_set_bit(i, indio_dev->active_scan_mask, +		indio_dev->masklength) { +		switch (i) { +		case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_CLEAR_EN); +			break; +		case GP2AP020A00F_SCAN_MODE_LIGHT_IR: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_IR_EN); +			break; +		case GP2AP020A00F_SCAN_MODE_PROXIMITY: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_PROX_EN); +			break; +		} +	} + +	if (err < 0) +		goto error_unlock; + +	data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); +	if (!data->buffer) { +		err = -ENOMEM; +		goto error_unlock; +	} + +	err = iio_triggered_buffer_postenable(indio_dev); + +error_unlock: +	mutex_unlock(&data->lock); + +	return err; +} + +static int gp2ap020a00f_buffer_predisable(struct iio_dev *indio_dev) +{ +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	int i, err; + +	mutex_lock(&data->lock); + +	err = iio_triggered_buffer_predisable(indio_dev); +	if (err < 0) +		goto error_unlock; + +	for_each_set_bit(i, indio_dev->active_scan_mask, +		indio_dev->masklength) { +		switch (i) { +		case GP2AP020A00F_SCAN_MODE_LIGHT_CLEAR: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_CLEAR_DIS); +			break; +		case GP2AP020A00F_SCAN_MODE_LIGHT_IR: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_IR_DIS); +			break; +		case GP2AP020A00F_SCAN_MODE_PROXIMITY: +			err = gp2ap020a00f_exec_cmd(data, +					GP2AP020A00F_CMD_TRIGGER_PROX_DIS); +			break; +		} +	} + +	if (err == 0) +		kfree(data->buffer); + +error_unlock: +	mutex_unlock(&data->lock); + +	return err; +} + +static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { +	.postenable = &gp2ap020a00f_buffer_postenable, +	.predisable = &gp2ap020a00f_buffer_predisable, +}; + +static const struct iio_trigger_ops gp2ap020a00f_trigger_ops = { +	.owner = THIS_MODULE, +}; + +static int gp2ap020a00f_probe(struct i2c_client *client, +				const struct i2c_device_id *id) +{ +	struct gp2ap020a00f_data *data; +	struct iio_dev *indio_dev; +	struct regmap *regmap; +	int err; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); +	if (!indio_dev) +		return -ENOMEM; + +	data = iio_priv(indio_dev); + +	data->vled_reg = devm_regulator_get(&client->dev, "vled"); +	if (IS_ERR(data->vled_reg)) +		return PTR_ERR(data->vled_reg); + +	err = regulator_enable(data->vled_reg); +	if (err) +		return err; + +	regmap = devm_regmap_init_i2c(client, &gp2ap020a00f_regmap_config); +	if (IS_ERR(regmap)) { +		dev_err(&client->dev, "Regmap initialization failed.\n"); +		err = PTR_ERR(regmap); +		goto error_regulator_disable; +	} + +	/* Initialize device registers */ +	err = regmap_bulk_write(regmap, GP2AP020A00F_OP_REG, +			gp2ap020a00f_reg_init_tab, +			ARRAY_SIZE(gp2ap020a00f_reg_init_tab)); + +	if (err < 0) { +		dev_err(&client->dev, "Device initialization failed.\n"); +		goto error_regulator_disable; +	} + +	i2c_set_clientdata(client, indio_dev); + +	data->client = client; +	data->cur_opmode = GP2AP020A00F_OPMODE_SHUTDOWN; +	data->regmap = regmap; +	init_waitqueue_head(&data->data_ready_queue); + +	mutex_init(&data->lock); +	indio_dev->dev.parent = &client->dev; +	indio_dev->channels = gp2ap020a00f_channels; +	indio_dev->num_channels = ARRAY_SIZE(gp2ap020a00f_channels); +	indio_dev->info = &gp2ap020a00f_info; +	indio_dev->name = id->name; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	/* Allocate buffer */ +	err = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, +		&gp2ap020a00f_trigger_handler, &gp2ap020a00f_buffer_setup_ops); +	if (err < 0) +		goto error_regulator_disable; + +	/* Allocate trigger */ +	data->trig = devm_iio_trigger_alloc(&client->dev, "%s-trigger", +							indio_dev->name); +	if (data->trig == NULL) { +		err = -ENOMEM; +		dev_err(&indio_dev->dev, "Failed to allocate iio trigger.\n"); +		goto error_uninit_buffer; +	} + +	/* This needs to be requested here for read_raw calls to work. */ +	err = request_threaded_irq(client->irq, NULL, +				   &gp2ap020a00f_thresh_event_handler, +				   IRQF_TRIGGER_FALLING | +				   IRQF_ONESHOT, +				   "gp2ap020a00f_als_event", +				   indio_dev); +	if (err < 0) { +		dev_err(&client->dev, "Irq request failed.\n"); +		goto error_uninit_buffer; +	} + +	data->trig->ops = &gp2ap020a00f_trigger_ops; +	data->trig->dev.parent = &data->client->dev; + +	init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); + +	err = iio_trigger_register(data->trig); +	if (err < 0) { +		dev_err(&client->dev, "Failed to register iio trigger.\n"); +		goto error_free_irq; +	} + +	err = iio_device_register(indio_dev); +	if (err < 0) +		goto error_trigger_unregister; + +	return 0; + +error_trigger_unregister: +	iio_trigger_unregister(data->trig); +error_free_irq: +	free_irq(client->irq, indio_dev); +error_uninit_buffer: +	iio_triggered_buffer_cleanup(indio_dev); +error_regulator_disable: +	regulator_disable(data->vled_reg); + +	return err; +} + +static int gp2ap020a00f_remove(struct i2c_client *client) +{ +	struct iio_dev *indio_dev = i2c_get_clientdata(client); +	struct gp2ap020a00f_data *data = iio_priv(indio_dev); +	int err; + +	err = gp2ap020a00f_set_operation_mode(data, +					GP2AP020A00F_OPMODE_SHUTDOWN); +	if (err < 0) +		dev_err(&indio_dev->dev, "Failed to power off the device.\n"); + +	iio_device_unregister(indio_dev); +	iio_trigger_unregister(data->trig); +	free_irq(client->irq, indio_dev); +	iio_triggered_buffer_cleanup(indio_dev); +	regulator_disable(data->vled_reg); + +	return 0; +} + +static const struct i2c_device_id gp2ap020a00f_id[] = { +	{ GP2A_I2C_NAME, 0 }, +	{ } +}; + +MODULE_DEVICE_TABLE(i2c, gp2ap020a00f_id); + +#ifdef CONFIG_OF +static const struct of_device_id gp2ap020a00f_of_match[] = { +	{ .compatible = "sharp,gp2ap020a00f" }, +	{ } +}; +#endif + +static struct i2c_driver gp2ap020a00f_driver = { +	.driver = { +		.name	= GP2A_I2C_NAME, +		.of_match_table = of_match_ptr(gp2ap020a00f_of_match), +		.owner	= THIS_MODULE, +	}, +	.probe		= gp2ap020a00f_probe, +	.remove		= gp2ap020a00f_remove, +	.id_table	= gp2ap020a00f_id, +}; + +module_i2c_driver(gp2ap020a00f_driver); + +MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>"); +MODULE_DESCRIPTION("Sharp GP2AP020A00F Proximity/ALS sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index e59d00c3139..96e71e103ea 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) | @@ -73,8 +79,8 @@ static int als_read_raw(struct iio_dev *indio_dev,  	struct als_state *als_state = iio_priv(indio_dev);  	int report_id = -1;  	u32 address; -	int ret;  	int ret_type; +	s32 poll_value;  	*val = 0;  	*val2 = 0; @@ -90,35 +96,44 @@ 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: -		ret = hid_sensor_read_samp_freq_value( +		ret_type = hid_sensor_read_samp_freq_value(  				&als_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	case IIO_CHAN_INFO_HYSTERESIS: -		ret = hid_sensor_read_raw_hyst_value( +		ret_type = hid_sensor_read_raw_hyst_value(  				&als_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	default:  		ret_type = -EINVAL; @@ -161,10 +176,11 @@ static const struct iio_info als_info = {  };  /* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +					int len)  {  	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); -	iio_push_to_buffers(indio_dev, (u8 *)data); +	iio_push_to_buffers(indio_dev, data);  }  /* Callback handler to send event after all samples are received and captured */ @@ -175,11 +191,10 @@ 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, -				(u8 *)&als_state->illum, +				&als_state->illum,  				sizeof(als_state->illum));  	return 0; @@ -228,6 +243,22 @@ 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, +			HID_FEATURE_REPORT, usage_id, +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | +			HID_USAGE_SENSOR_DATA_LIGHT, +			&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 ret;  } @@ -284,7 +315,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) { @@ -313,7 +344,7 @@ static int hid_als_probe(struct platform_device *pdev)  error_iio_unreg:  	iio_device_unregister(indio_dev);  error_remove_trigger: -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&als_state->common_attributes);  error_unreg_buffer_funcs:  	iio_triggered_buffer_cleanup(indio_dev);  error_free_dev_mem: @@ -326,10 +357,11 @@ static int hid_als_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 als_state *als_state = iio_priv(indio_dev);  	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);  	iio_device_unregister(indio_dev); -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&als_state->common_attributes);  	iio_triggered_buffer_cleanup(indio_dev);  	kfree(indio_dev->channels); diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c new file mode 100644 index 00000000000..412bae86d6a --- /dev/null +++ b/drivers/iio/light/hid-sensor-prox.c @@ -0,0 +1,385 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. + * + */ +#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/delay.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" + +#define CHANNEL_SCAN_INDEX_PRESENCE 0 + +struct prox_state { +	struct hid_sensor_hub_callbacks callbacks; +	struct hid_sensor_common common_attributes; +	struct hid_sensor_hub_attribute_info prox_attr; +	u32 human_presence; +}; + +/* Channel definitions */ +static const struct iio_chan_spec prox_channels[] = { +	{ +		.type = IIO_PROXIMITY, +		.modified = 1, +		.channel2 = IIO_NO_MOD, +		.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) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_PRESENCE, +	} +}; + +/* Adjust channel real bits based on report descriptor */ +static void prox_adjust_channel_bit_mask(struct iio_chan_spec *channels, +					int channel, int size) +{ +	channels[channel].scan_type.sign = 's'; +	/* Real storage bits will change based on the report desc. */ +	channels[channel].scan_type.realbits = size * 8; +	/* Maximum size of a sample to capture is u32 */ +	channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int prox_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct prox_state *prox_state = iio_priv(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: +		switch (chan->scan_index) { +		case  CHANNEL_SCAN_INDEX_PRESENCE: +			report_id = prox_state->prox_attr.report_id; +			address = +			HID_USAGE_SENSOR_HUMAN_PRESENCE; +			break; +		default: +			report_id = -1; +			break; +		} +		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); +			hid_sensor_power_state(&prox_state->common_attributes, +						false); +		} else { +			*val = 0; +			return -EINVAL; +		} +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SCALE: +		*val = prox_state->prox_attr.units; +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = hid_sensor_convert_exponent( +				prox_state->prox_attr.unit_expo); +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret_type = hid_sensor_read_samp_freq_value( +				&prox_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret_type = hid_sensor_read_raw_hyst_value( +				&prox_state->common_attributes, val, val2); +		break; +	default: +		ret_type = -EINVAL; +		break; +	} + +	return ret_type; +} + +/* Channel write_raw handler */ +static int prox_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct prox_state *prox_state = iio_priv(indio_dev); +	int ret = 0; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret = hid_sensor_write_samp_freq_value( +				&prox_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret = hid_sensor_write_raw_hyst_value( +				&prox_state->common_attributes, val, val2); +		break; +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static const struct iio_info prox_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &prox_read_raw, +	.write_raw = &prox_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +					int len) +{ +	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); +	iio_push_to_buffers(indio_dev, data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int prox_proc_event(struct hid_sensor_hub_device *hsdev, +				unsigned usage_id, +				void *priv) +{ +	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\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)); + +	return 0; +} + +/* Capture samples in local storage */ +static int prox_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 prox_state *prox_state = iio_priv(indio_dev); +	int ret = -EINVAL; + +	switch (usage_id) { +	case HID_USAGE_SENSOR_HUMAN_PRESENCE: +		prox_state->human_presence = *(u32 *)raw_data; +		ret = 0; +		break; +	default: +		break; +	} + +	return ret; +} + +/* Parse report which is specific to an usage id*/ +static int prox_parse_report(struct platform_device *pdev, +				struct hid_sensor_hub_device *hsdev, +				struct iio_chan_spec *channels, +				unsigned usage_id, +				struct prox_state *st) +{ +	int ret; + +	ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, +			usage_id, +			HID_USAGE_SENSOR_HUMAN_PRESENCE, +			&st->prox_attr); +	if (ret < 0) +		return ret; +	prox_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESENCE, +					st->prox_attr.size); + +	dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index, +			st->prox_attr.report_id); + +	/* 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_PRESENCE, +			&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 ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_prox_probe(struct platform_device *pdev) +{ +	int ret = 0; +	static const char *name = "prox"; +	struct iio_dev *indio_dev; +	struct prox_state *prox_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 prox_state)); +	if (!indio_dev) +		return -ENOMEM; +	platform_set_drvdata(pdev, indio_dev); + +	prox_state = iio_priv(indio_dev); +	prox_state->common_attributes.hsdev = hsdev; +	prox_state->common_attributes.pdev = pdev; + +	ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX, +					&prox_state->common_attributes); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup common attributes\n"); +		return ret; +	} + +	channels = kmemdup(prox_channels, sizeof(prox_channels), GFP_KERNEL); +	if (!channels) { +		dev_err(&pdev->dev, "failed to duplicate channels\n"); +		return -ENOMEM; +	} + +	ret = prox_parse_report(pdev, hsdev, channels, +				HID_USAGE_SENSOR_PROX, prox_state); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup attributes\n"); +		goto error_free_dev_mem; +	} + +	indio_dev->channels = channels; +	indio_dev->num_channels = +				ARRAY_SIZE(prox_channels); +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->info = &prox_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"); +		goto error_free_dev_mem; +	} +	atomic_set(&prox_state->common_attributes.data_ready, 0); +	ret = hid_sensor_setup_trigger(indio_dev, name, +				&prox_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; +	} + +	prox_state->callbacks.send_event = prox_proc_event; +	prox_state->callbacks.capture_sample = prox_capture_sample; +	prox_state->callbacks.pdev = pdev; +	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PROX, +					&prox_state->callbacks); +	if (ret < 0) { +		dev_err(&pdev->dev, "callback reg failed\n"); +		goto error_iio_unreg; +	} + +	return ret; + +error_iio_unreg: +	iio_device_unregister(indio_dev); +error_remove_trigger: +	hid_sensor_remove_trigger(&prox_state->common_attributes); +error_unreg_buffer_funcs: +	iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: +	kfree(indio_dev->channels); +	return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_prox_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 prox_state *prox_state = iio_priv(indio_dev); + +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PROX); +	iio_device_unregister(indio_dev); +	hid_sensor_remove_trigger(&prox_state->common_attributes); +	iio_triggered_buffer_cleanup(indio_dev); +	kfree(indio_dev->channels); + +	return 0; +} + +static struct platform_device_id hid_prox_ids[] = { +	{ +		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */ +		.name = "HID-SENSOR-200011", +	}, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_prox_ids); + +static struct platform_driver hid_prox_platform_driver = { +	.id_table = hid_prox_ids, +	.driver = { +		.name	= KBUILD_MODNAME, +		.owner	= THIS_MODULE, +	}, +	.probe		= hid_prox_probe, +	.remove		= hid_prox_remove, +}; +module_platform_driver(hid_prox_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Proximity"); +MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c new file mode 100644 index 00000000000..62b7072af4d --- /dev/null +++ b/drivers/iio/light/ltr501.c @@ -0,0 +1,445 @@ +/* + * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor + * + * 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 0x23 + * + * TODO: interrupt, threshold, measurement rate, IR LED characteristics + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.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> + +#define LTR501_DRV_NAME "ltr501" + +#define LTR501_ALS_CONTR 0x80 /* ALS operation mode, SW reset */ +#define LTR501_PS_CONTR 0x81 /* PS operation mode */ +#define LTR501_PART_ID 0x86 +#define LTR501_MANUFAC_ID 0x87 +#define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */ +#define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */ +#define LTR501_ALS_PS_STATUS 0x8c +#define LTR501_PS_DATA 0x8d /* 16-bit, little endian */ + +#define LTR501_ALS_CONTR_SW_RESET BIT(2) +#define LTR501_CONTR_PS_GAIN_MASK (BIT(3) | BIT(2)) +#define LTR501_CONTR_PS_GAIN_SHIFT 2 +#define LTR501_CONTR_ALS_GAIN_MASK BIT(3) +#define LTR501_CONTR_ACTIVE BIT(1) + +#define LTR501_STATUS_ALS_RDY BIT(2) +#define LTR501_STATUS_PS_RDY BIT(0) + +#define LTR501_PS_DATA_MASK 0x7ff + +struct ltr501_data { +	struct i2c_client *client; +	struct mutex lock_als, lock_ps; +	u8 als_contr, ps_contr; +}; + +static int ltr501_drdy(struct ltr501_data *data, u8 drdy_mask) +{ +	int tries = 100; +	int ret; + +	while (tries--) { +		ret = i2c_smbus_read_byte_data(data->client, +			LTR501_ALS_PS_STATUS); +		if (ret < 0) +			return ret; +		if ((ret & drdy_mask) == drdy_mask) +			return 0; +		msleep(25); +	} + +	dev_err(&data->client->dev, "ltr501_drdy() failed, data not ready\n"); +	return -EIO; +} + +static int ltr501_read_als(struct ltr501_data *data, __le16 buf[2]) +{ +	int ret = ltr501_drdy(data, LTR501_STATUS_ALS_RDY); +	if (ret < 0) +		return ret; +	/* always read both ALS channels in given order */ +	return i2c_smbus_read_i2c_block_data(data->client, +		LTR501_ALS_DATA1, 2 * sizeof(__le16), (u8 *) buf); +} + +static int ltr501_read_ps(struct ltr501_data *data) +{ +	int ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY); +	if (ret < 0) +		return ret; +	return i2c_smbus_read_word_data(data->client, LTR501_PS_DATA); +} + +#define LTR501_INTENSITY_CHANNEL(_idx, _addr, _mod, _shared) { \ +	.type = IIO_INTENSITY, \ +	.modified = 1, \ +	.address = (_addr), \ +	.channel2 = (_mod), \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +	.info_mask_shared_by_type = (_shared), \ +	.scan_index = (_idx), \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 16, \ +		.storagebits = 16, \ +		.endianness = IIO_CPU, \ +	} \ +} + +static const struct iio_chan_spec ltr501_channels[] = { +	LTR501_INTENSITY_CHANNEL(0, LTR501_ALS_DATA0, IIO_MOD_LIGHT_BOTH, 0), +	LTR501_INTENSITY_CHANNEL(1, LTR501_ALS_DATA1, IIO_MOD_LIGHT_IR, +		BIT(IIO_CHAN_INFO_SCALE)), +	{ +		.type = IIO_PROXIMITY, +		.address = LTR501_PS_DATA, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		.scan_index = 2, +		.scan_type = { +			.sign = 'u', +			.realbits = 11, +			.storagebits = 16, +			.endianness = IIO_CPU, +		}, +	}, +	IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const int ltr501_ps_gain[4][2] = { +	{1, 0}, {0, 250000}, {0, 125000}, {0, 62500} +}; + +static int ltr501_read_raw(struct iio_dev *indio_dev, +				struct iio_chan_spec const *chan, +				int *val, int *val2, long mask) +{ +	struct ltr501_data *data = iio_priv(indio_dev); +	__le16 buf[2]; +	int ret, i; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		if (iio_buffer_enabled(indio_dev)) +			return -EBUSY; + +		switch (chan->type) { +		case IIO_INTENSITY: +			mutex_lock(&data->lock_als); +			ret = ltr501_read_als(data, buf); +			mutex_unlock(&data->lock_als); +			if (ret < 0) +				return ret; +			*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ? +				buf[0] : buf[1]); +			return IIO_VAL_INT; +		case IIO_PROXIMITY: +			mutex_lock(&data->lock_ps); +			ret = ltr501_read_ps(data); +			mutex_unlock(&data->lock_ps); +			if (ret < 0) +				return ret; +			*val = ret & LTR501_PS_DATA_MASK; +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_INTENSITY: +			if (data->als_contr & LTR501_CONTR_ALS_GAIN_MASK) { +				*val = 0; +				*val2 = 5000; +				return IIO_VAL_INT_PLUS_MICRO; +			} else { +				*val = 1; +				*val2 = 0; +				return IIO_VAL_INT; +			} +		case IIO_PROXIMITY: +			i = (data->ps_contr & LTR501_CONTR_PS_GAIN_MASK) >> +				LTR501_CONTR_PS_GAIN_SHIFT; +			*val = ltr501_ps_gain[i][0]; +			*val2 = ltr501_ps_gain[i][1]; +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	} +	return -EINVAL; +} + +static int ltr501_get_ps_gain_index(int val, int val2) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(ltr501_ps_gain); i++) +		if (val == ltr501_ps_gain[i][0] && val2 == ltr501_ps_gain[i][1]) +			return i; + +	return -1; +} + +static int ltr501_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, int val2, long mask) +{ +	struct ltr501_data *data = iio_priv(indio_dev); +	int i; + +	if (iio_buffer_enabled(indio_dev)) +		return -EBUSY; + +	switch (mask) { +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_INTENSITY: +			if (val == 0 && val2 == 5000) +				data->als_contr |= LTR501_CONTR_ALS_GAIN_MASK; +			else if (val == 1 && val2 == 0) +				data->als_contr &= ~LTR501_CONTR_ALS_GAIN_MASK; +			else +				return -EINVAL; +			return i2c_smbus_write_byte_data(data->client, +				LTR501_ALS_CONTR, data->als_contr); +		case IIO_PROXIMITY: +			i = ltr501_get_ps_gain_index(val, val2); +			if (i < 0) +				return -EINVAL; +			data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK; +			data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT; +			return i2c_smbus_write_byte_data(data->client, +				LTR501_PS_CONTR, data->ps_contr); +		default: +			return -EINVAL; +		} +	} +	return -EINVAL; +} + +static IIO_CONST_ATTR(in_proximity_scale_available, "1 0.25 0.125 0.0625"); +static IIO_CONST_ATTR(in_intensity_scale_available, "1 0.005"); + +static struct attribute *ltr501_attributes[] = { +	&iio_const_attr_in_proximity_scale_available.dev_attr.attr, +	&iio_const_attr_in_intensity_scale_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group ltr501_attribute_group = { +	.attrs = ltr501_attributes, +}; + +static const struct iio_info ltr501_info = { +	.read_raw = ltr501_read_raw, +	.write_raw = ltr501_write_raw, +	.attrs = <r501_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int ltr501_write_contr(struct i2c_client *client, u8 als_val, u8 ps_val) +{ +	int ret = i2c_smbus_write_byte_data(client, LTR501_ALS_CONTR, als_val); +	if (ret < 0) +		return ret; + +	return i2c_smbus_write_byte_data(client, LTR501_PS_CONTR, ps_val); +} + +static irqreturn_t ltr501_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct ltr501_data *data = iio_priv(indio_dev); +	u16 buf[8]; +	__le16 als_buf[2]; +	u8 mask = 0; +	int j = 0; +	int ret; + +	memset(buf, 0, sizeof(buf)); + +	/* figure out which data needs to be ready */ +	if (test_bit(0, indio_dev->active_scan_mask) || +		test_bit(1, indio_dev->active_scan_mask)) +		mask |= LTR501_STATUS_ALS_RDY; +	if (test_bit(2, indio_dev->active_scan_mask)) +		mask |= LTR501_STATUS_PS_RDY; + +	ret = ltr501_drdy(data, mask); +	if (ret < 0) +		goto done; + +	if (mask & LTR501_STATUS_ALS_RDY) { +		ret = i2c_smbus_read_i2c_block_data(data->client, +			LTR501_ALS_DATA1, sizeof(als_buf), (u8 *) als_buf); +		if (ret < 0) +			return ret; +		if (test_bit(0, indio_dev->active_scan_mask)) +			buf[j++] = le16_to_cpu(als_buf[1]); +		if (test_bit(1, indio_dev->active_scan_mask)) +			buf[j++] = le16_to_cpu(als_buf[0]); +	} + +	if (mask & LTR501_STATUS_PS_RDY) { +		ret = i2c_smbus_read_word_data(data->client, LTR501_PS_DATA); +		if (ret < 0) +			goto done; +		buf[j++] = ret & LTR501_PS_DATA_MASK; +	} + +	iio_push_to_buffers_with_timestamp(indio_dev, buf, +		iio_get_time_ns()); + +done: +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +static int ltr501_init(struct ltr501_data *data) +{ +	int ret; + +	ret = i2c_smbus_read_byte_data(data->client, LTR501_ALS_CONTR); +	if (ret < 0) +		return ret; +	data->als_contr = ret | LTR501_CONTR_ACTIVE; + +	ret = i2c_smbus_read_byte_data(data->client, LTR501_PS_CONTR); +	if (ret < 0) +		return ret; +	data->ps_contr = ret | LTR501_CONTR_ACTIVE; + +	return ltr501_write_contr(data->client, data->als_contr, +		data->ps_contr); +} + +static int ltr501_probe(struct i2c_client *client, +			  const struct i2c_device_id *id) +{ +	struct ltr501_data *data; +	struct iio_dev *indio_dev; +	int ret; + +	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; +	mutex_init(&data->lock_als); +	mutex_init(&data->lock_ps); + +	ret = i2c_smbus_read_byte_data(data->client, LTR501_PART_ID); +	if (ret < 0) +		return ret; +	if ((ret >> 4) != 0x8) +		return -ENODEV; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = <r501_info; +	indio_dev->channels = ltr501_channels; +	indio_dev->num_channels = ARRAY_SIZE(ltr501_channels); +	indio_dev->name = LTR501_DRV_NAME; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = ltr501_init(data); +	if (ret < 0) +		return ret; + +	ret = iio_triggered_buffer_setup(indio_dev, NULL, +		ltr501_trigger_handler, NULL); +	if (ret) +		return ret; + +	ret = iio_device_register(indio_dev); +	if (ret) +		goto error_unreg_buffer; + +	return 0; + +error_unreg_buffer: +	iio_triggered_buffer_cleanup(indio_dev); +	return ret; +} + +static int ltr501_powerdown(struct ltr501_data *data) +{ +	return ltr501_write_contr(data->client, +		data->als_contr & ~LTR501_CONTR_ACTIVE, +		data->ps_contr & ~LTR501_CONTR_ACTIVE); +} + +static int ltr501_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); +	ltr501_powerdown(iio_priv(indio_dev)); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ltr501_suspend(struct device *dev) +{ +	struct ltr501_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); +	return ltr501_powerdown(data); +} + +static int ltr501_resume(struct device *dev) +{ +	struct ltr501_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); + +	return ltr501_write_contr(data->client, data->als_contr, +		data->ps_contr); +} +#endif + +static SIMPLE_DEV_PM_OPS(ltr501_pm_ops, ltr501_suspend, ltr501_resume); + +static const struct i2c_device_id ltr501_id[] = { +	{ "ltr501", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, ltr501_id); + +static struct i2c_driver ltr501_driver = { +	.driver = { +		.name   = LTR501_DRV_NAME, +		.pm	= <r501_pm_ops, +		.owner  = THIS_MODULE, +	}, +	.probe  = ltr501_probe, +	.remove	= ltr501_remove, +	.id_table = ltr501_id, +}; + +module_i2c_driver(ltr501_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Lite-On LTR501 ambient light and proximity sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c new file mode 100644 index 00000000000..752569985d1 --- /dev/null +++ b/drivers/iio/light/tcs3472.c @@ -0,0 +1,379 @@ +/* + * tcs3472.c - Support for TAOS TCS3472 color light-to-digital converter + * + * Copyright (c) 2013 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. + * + * Color light sensor with 16-bit channels for red, green, blue, clear); + * 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725, + * TCS34727) + * + * TODO: interrupt support, thresholds, wait time + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <linux/pm.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> + +#define TCS3472_DRV_NAME "tcs3472" + +#define TCS3472_COMMAND BIT(7) +#define TCS3472_AUTO_INCR BIT(5) + +#define TCS3472_ENABLE (TCS3472_COMMAND | 0x00) +#define TCS3472_ATIME (TCS3472_COMMAND | 0x01) +#define TCS3472_WTIME (TCS3472_COMMAND | 0x03) +#define TCS3472_AILT (TCS3472_COMMAND | 0x04) +#define TCS3472_AIHT (TCS3472_COMMAND | 0x06) +#define TCS3472_PERS (TCS3472_COMMAND | 0x0c) +#define TCS3472_CONFIG (TCS3472_COMMAND | 0x0d) +#define TCS3472_CONTROL (TCS3472_COMMAND | 0x0f) +#define TCS3472_ID (TCS3472_COMMAND | 0x12) +#define TCS3472_STATUS (TCS3472_COMMAND | 0x13) +#define TCS3472_CDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x14) +#define TCS3472_RDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x16) +#define TCS3472_GDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x18) +#define TCS3472_BDATA (TCS3472_COMMAND | TCS3472_AUTO_INCR | 0x1a) + +#define TCS3472_STATUS_AVALID BIT(0) +#define TCS3472_ENABLE_AEN BIT(1) +#define TCS3472_ENABLE_PON BIT(0) +#define TCS3472_CONTROL_AGAIN_MASK (BIT(0) | BIT(1)) + +struct tcs3472_data { +	struct i2c_client *client; +	struct mutex lock; +	u8 enable; +	u8 control; +	u8 atime; +	u16 buffer[8]; /* 4 16-bit channels + 64-bit timestamp */ +}; + +#define TCS3472_CHANNEL(_color, _si, _addr) { \ +	.type = IIO_INTENSITY, \ +	.modified = 1, \ +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBSCALE) | \ +		BIT(IIO_CHAN_INFO_INT_TIME), \ +	.channel2 = IIO_MOD_LIGHT_##_color, \ +	.address = _addr, \ +	.scan_index = _si, \ +	.scan_type = { \ +		.sign = 'u', \ +		.realbits = 16, \ +		.storagebits = 16, \ +		.endianness = IIO_CPU, \ +	}, \ +} + +static const int tcs3472_agains[] = { 1, 4, 16, 60 }; + +static const struct iio_chan_spec tcs3472_channels[] = { +	TCS3472_CHANNEL(CLEAR, 0, TCS3472_CDATA), +	TCS3472_CHANNEL(RED, 1, TCS3472_RDATA), +	TCS3472_CHANNEL(GREEN, 2, TCS3472_GDATA), +	TCS3472_CHANNEL(BLUE, 3, TCS3472_BDATA), +	IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static int tcs3472_req_data(struct tcs3472_data *data) +{ +	int tries = 50; +	int ret; + +	while (tries--) { +		ret = i2c_smbus_read_byte_data(data->client, TCS3472_STATUS); +		if (ret < 0) +			return ret; +		if (ret & TCS3472_STATUS_AVALID) +			break; +		msleep(20); +	} + +	if (tries < 0) { +		dev_err(&data->client->dev, "data not ready\n"); +		return -EIO; +	} + +	return 0; +} + +static int tcs3472_read_raw(struct iio_dev *indio_dev, +			   struct iio_chan_spec const *chan, +			   int *val, int *val2, long mask) +{ +	struct tcs3472_data *data = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		if (iio_buffer_enabled(indio_dev)) +			return -EBUSY; + +		mutex_lock(&data->lock); +		ret = tcs3472_req_data(data); +		if (ret < 0) { +			mutex_unlock(&data->lock); +			return ret; +		} +		ret = i2c_smbus_read_word_data(data->client, chan->address); +		mutex_unlock(&data->lock); +		if (ret < 0) +			return ret; +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_CALIBSCALE: +		*val = tcs3472_agains[data->control & +			TCS3472_CONTROL_AGAIN_MASK]; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_INT_TIME: +		*val = 0; +		*val2 = (256 - data->atime) * 2400; +		return IIO_VAL_INT_PLUS_MICRO; +	} +	return -EINVAL; +} + +static int tcs3472_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, int val2, long mask) +{ +	struct tcs3472_data *data = iio_priv(indio_dev); +	int i; + +	switch (mask) { +	case IIO_CHAN_INFO_CALIBSCALE: +		if (val2 != 0) +			return -EINVAL; +		for (i = 0; i < ARRAY_SIZE(tcs3472_agains); i++) { +			if (val == tcs3472_agains[i]) { +				data->control &= ~TCS3472_CONTROL_AGAIN_MASK; +				data->control |= i; +				return i2c_smbus_write_byte_data( +					data->client, TCS3472_CONTROL, +					data->control); +			} +		} +		return -EINVAL; +	case IIO_CHAN_INFO_INT_TIME: +		if (val != 0) +			return -EINVAL; +		for (i = 0; i < 256; i++) { +			if (val2 == (256 - i) * 2400) { +				data->atime = i; +				return i2c_smbus_write_word_data( +					data->client, TCS3472_ATIME, +					data->atime); +			} + +		} +		return -EINVAL; +	} +	return -EINVAL; +} + +static irqreturn_t tcs3472_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct tcs3472_data *data = iio_priv(indio_dev); +	int i, j = 0; + +	int ret = tcs3472_req_data(data); +	if (ret < 0) +		goto done; + +	for_each_set_bit(i, indio_dev->active_scan_mask, +		indio_dev->masklength) { +		ret = i2c_smbus_read_word_data(data->client, +			TCS3472_CDATA + 2*i); +		if (ret < 0) +			goto done; + +		data->buffer[j++] = ret; +	} + +	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, +		iio_get_time_ns()); + +done: +	iio_trigger_notify_done(indio_dev->trig); + +	return IRQ_HANDLED; +} + +static ssize_t tcs3472_show_int_time_available(struct device *dev, +					struct device_attribute *attr, +					char *buf) +{ +	size_t len = 0; +	int i; + +	for (i = 1; i <= 256; i++) +		len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06d ", +			2400 * i); + +	/* replace trailing space by newline */ +	buf[len - 1] = '\n'; + +	return len; +} + +static IIO_CONST_ATTR(calibscale_available, "1 4 16 60"); +static IIO_DEV_ATTR_INT_TIME_AVAIL(tcs3472_show_int_time_available); + +static struct attribute *tcs3472_attributes[] = { +	&iio_const_attr_calibscale_available.dev_attr.attr, +	&iio_dev_attr_integration_time_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group tcs3472_attribute_group = { +	.attrs = tcs3472_attributes, +}; + +static const struct iio_info tcs3472_info = { +	.read_raw = tcs3472_read_raw, +	.write_raw = tcs3472_write_raw, +	.attrs = &tcs3472_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int tcs3472_probe(struct i2c_client *client, +			   const struct i2c_device_id *id) +{ +	struct tcs3472_data *data; +	struct iio_dev *indio_dev; +	int ret; + +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	data = iio_priv(indio_dev); +	i2c_set_clientdata(client, indio_dev); +	data->client = client; +	mutex_init(&data->lock); + +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &tcs3472_info; +	indio_dev->name = TCS3472_DRV_NAME; +	indio_dev->channels = tcs3472_channels; +	indio_dev->num_channels = ARRAY_SIZE(tcs3472_channels); +	indio_dev->modes = INDIO_DIRECT_MODE; + +	ret = i2c_smbus_read_byte_data(data->client, TCS3472_ID); +	if (ret < 0) +		return ret; + +	if (ret == 0x44) +		dev_info(&client->dev, "TCS34721/34725 found\n"); +	else if (ret == 0x4d) +		dev_info(&client->dev, "TCS34723/34727 found\n"); +	else +		return -ENODEV; + +	ret = i2c_smbus_read_byte_data(data->client, TCS3472_CONTROL); +	if (ret < 0) +		return ret; +	data->control = ret; + +	ret = i2c_smbus_read_byte_data(data->client, TCS3472_ATIME); +	if (ret < 0) +		return ret; +	data->atime = ret; + +	ret = i2c_smbus_read_byte_data(data->client, TCS3472_ENABLE); +	if (ret < 0) +		return ret; + +	/* enable device */ +	data->enable = ret | TCS3472_ENABLE_PON | TCS3472_ENABLE_AEN; +	ret = i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, +		data->enable); +	if (ret < 0) +		return ret; + +	ret = iio_triggered_buffer_setup(indio_dev, NULL, +		tcs3472_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 tcs3472_powerdown(struct tcs3472_data *data) +{ +	return i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, +		data->enable & ~(TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON)); +} + +static int tcs3472_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); +	tcs3472_powerdown(iio_priv(indio_dev)); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tcs3472_suspend(struct device *dev) +{ +	struct tcs3472_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); +	return tcs3472_powerdown(data); +} + +static int tcs3472_resume(struct device *dev) +{ +	struct tcs3472_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); +	return i2c_smbus_write_byte_data(data->client, TCS3472_ENABLE, +		data->enable | (TCS3472_ENABLE_AEN | TCS3472_ENABLE_PON)); +} +#endif + +static SIMPLE_DEV_PM_OPS(tcs3472_pm_ops, tcs3472_suspend, tcs3472_resume); + +static const struct i2c_device_id tcs3472_id[] = { +	{ "tcs3472", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, tcs3472_id); + +static struct i2c_driver tcs3472_driver = { +	.driver = { +		.name	= TCS3472_DRV_NAME, +		.pm	= &tcs3472_pm_ops, +		.owner	= THIS_MODULE, +	}, +	.probe		= tcs3472_probe, +	.remove		= tcs3472_remove, +	.id_table	= tcs3472_id, +}; +module_i2c_driver(tcs3472_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("TCS3472 color light sensors driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c index ebb962c5c32..94daa9fc124 100644 --- a/drivers/iio/light/tsl2563.c +++ b/drivers/iio/light/tsl2563.c @@ -460,10 +460,14 @@ static int tsl2563_write_raw(struct iio_dev *indio_dev,  {  	struct tsl2563_chip *chip = iio_priv(indio_dev); -	if (chan->channel == IIO_MOD_LIGHT_BOTH) +	if (mask != IIO_CHAN_INFO_CALIBSCALE) +		return -EINVAL; +	if (chan->channel2 == IIO_MOD_LIGHT_BOTH)  		chip->calib0 = calib_from_sysfs(val); -	else +	else if (chan->channel2 == IIO_MOD_LIGHT_IR)  		chip->calib1 = calib_from_sysfs(val); +	else +		return -EINVAL;  	return 0;  } @@ -472,14 +476,14 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,  			    struct iio_chan_spec const *chan,  			    int *val,  			    int *val2, -			    long m) +			    long mask)  {  	int ret = -EINVAL;  	u32 calib0, calib1;  	struct tsl2563_chip *chip = iio_priv(indio_dev);  	mutex_lock(&chip->lock); -	switch (m) { +	switch (mask) {  	case IIO_CHAN_INFO_RAW:  	case IIO_CHAN_INFO_PROCESSED:  		switch (chan->type) { @@ -498,7 +502,7 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,  			ret = tsl2563_get_adc(chip);  			if (ret)  				goto error_ret; -			if (chan->channel == 0) +			if (chan->channel2 == IIO_MOD_LIGHT_BOTH)  				*val = chip->data0;  			else  				*val = chip->data1; @@ -510,7 +514,7 @@ static int tsl2563_read_raw(struct iio_dev *indio_dev,  		break;  	case IIO_CHAN_INFO_CALIBSCALE: -		if (chan->channel == 0) +		if (chan->channel2 == IIO_MOD_LIGHT_BOTH)  			*val = calib_to_sysfs(chip->calib0);  		else  			*val = calib_to_sysfs(chip->calib1); @@ -526,6 +530,20 @@ error_ret:  	return ret;  } +static const struct iio_event_spec tsl2563_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), +	}, +}; +  static const struct iio_chan_spec tsl2563_channels[] = {  	{  		.type = IIO_LIGHT, @@ -538,10 +556,8 @@ static const struct iio_chan_spec tsl2563_channels[] = {  		.channel2 = IIO_MOD_LIGHT_BOTH,  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  		BIT(IIO_CHAN_INFO_CALIBSCALE), -		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH, -					  IIO_EV_DIR_RISING) | -			       IIO_EV_BIT(IIO_EV_TYPE_THRESH, -					  IIO_EV_DIR_FALLING)), +		.event_spec = tsl2563_events, +		.num_event_specs = ARRAY_SIZE(tsl2563_events),  	}, {  		.type = IIO_INTENSITY,  		.modified = 1, @@ -552,12 +568,13 @@ static const struct iio_chan_spec tsl2563_channels[] = {  };  static int tsl2563_read_thresh(struct iio_dev *indio_dev, -			       u64 event_code, -			       int *val) +	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)  {  	struct tsl2563_chip *chip = iio_priv(indio_dev); -	switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { +	switch (dir) {  	case IIO_EV_DIR_RISING:  		*val = chip->high_thres;  		break; @@ -568,18 +585,19 @@ static int tsl2563_read_thresh(struct iio_dev *indio_dev,  		return -EINVAL;  	} -	return 0; +	return IIO_VAL_INT;  }  static int tsl2563_write_thresh(struct iio_dev *indio_dev, -				  u64 event_code, -				  int val) +	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)  {  	struct tsl2563_chip *chip = iio_priv(indio_dev);  	int ret;  	u8 address; -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) +	if (dir == IIO_EV_DIR_RISING)  		address = TSL2563_REG_HIGHLOW;  	else  		address = TSL2563_REG_LOWLOW; @@ -591,7 +609,7 @@ static int tsl2563_write_thresh(struct iio_dev *indio_dev,  	ret = i2c_smbus_write_byte_data(chip->client,  					TSL2563_CMD | (address + 1),  					(val >> 8) & 0xFF); -	if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) +	if (dir == IIO_EV_DIR_RISING)  		chip->high_thres = val;  	else  		chip->low_thres = val; @@ -620,8 +638,8 @@ static irqreturn_t tsl2563_event_handler(int irq, void *private)  }  static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, -					  u64 event_code, -					  int state) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir, int state)  {  	struct tsl2563_chip *chip = iio_priv(indio_dev);  	int ret = 0; @@ -662,7 +680,8 @@ out:  }  static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev, -					 u64 event_code) +	const struct iio_chan_spec *chan, enum iio_event_type type, +	enum iio_event_direction dir)  {  	struct tsl2563_chip *chip = iio_priv(indio_dev);  	int ret; @@ -699,6 +718,7 @@ static int tsl2563_probe(struct i2c_client *client,  	struct iio_dev *indio_dev;  	struct tsl2563_chip *chip;  	struct tsl2563_platform_data *pdata = client->dev.platform_data; +	struct device_node *np = client->dev.of_node;  	int err = 0;  	u8 id = 0; @@ -735,6 +755,9 @@ static int tsl2563_probe(struct i2c_client *client,  	if (pdata)  		chip->cover_comp_gain = pdata->cover_comp_gain; +	else if (np) +		of_property_read_u32(np, "amstaos,cover-comp-gain", +				     &chip->cover_comp_gain);  	else  		chip->cover_comp_gain = 1; diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c new file mode 100644 index 00000000000..a15006efa13 --- /dev/null +++ b/drivers/iio/light/tsl4531.c @@ -0,0 +1,258 @@ +/* + * tsl4531.c - Support for TAOS TSL4531 ambient light sensor + * + * Copyright 2013 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. + * + * IIO driver for the TSL4531x family + *   TSL45311/TSL45313: 7-bit I2C slave address 0x39 + *   TSL45315/TSL45317: 7-bit I2C slave address 0x29 + * + * TODO: single cycle measurement + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define TSL4531_DRV_NAME "tsl4531" + +#define TCS3472_COMMAND BIT(7) + +#define TSL4531_CONTROL (TCS3472_COMMAND | 0x00) +#define TSL4531_CONFIG (TCS3472_COMMAND | 0x01) +#define TSL4531_DATA (TCS3472_COMMAND | 0x04) +#define TSL4531_ID (TCS3472_COMMAND | 0x0a) + +/* operating modes in control register */ +#define TSL4531_MODE_POWERDOWN 0x00 +#define TSL4531_MODE_SINGLE_ADC 0x02 +#define TSL4531_MODE_NORMAL 0x03 + +/* integration time control in config register */ +#define TSL4531_TCNTRL_400MS 0x00 +#define TSL4531_TCNTRL_200MS 0x01 +#define TSL4531_TCNTRL_100MS 0x02 + +/* part number in id register */ +#define TSL45311_ID 0x8 +#define TSL45313_ID 0x9 +#define TSL45315_ID 0xa +#define TSL45317_ID 0xb +#define TSL4531_ID_SHIFT 4 + +struct tsl4531_data { +	struct i2c_client *client; +	struct mutex lock; +	int int_time; +}; + +static IIO_CONST_ATTR_INT_TIME_AVAIL("0.1 0.2 0.4"); + +static struct attribute *tsl4531_attributes[] = { +	&iio_const_attr_integration_time_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group tsl4531_attribute_group = { +	.attrs = tsl4531_attributes, +}; + +static const struct iio_chan_spec tsl4531_channels[] = { +	{ +		.type = IIO_LIGHT, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_INT_TIME) +	} +}; + +static int tsl4531_read_raw(struct iio_dev *indio_dev, +				struct iio_chan_spec const *chan, +				int *val, int *val2, long mask) +{ +	struct tsl4531_data *data = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		ret = i2c_smbus_read_word_data(data->client, +			TSL4531_DATA); +		if (ret < 0) +			return ret; +		*val = ret; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_SCALE: +		/* 0.. 1x, 1 .. 2x, 2 .. 4x */ +		*val = 1 << data->int_time; +		return IIO_VAL_INT; +	case IIO_CHAN_INFO_INT_TIME: +		if (data->int_time == 0) +			*val2 = 400000; +		else if (data->int_time == 1) +			*val2 = 200000; +		else if (data->int_time == 2) +			*val2 = 100000; +		else +			return -EINVAL; +		*val = 0; +		return IIO_VAL_INT_PLUS_MICRO; +	default: +		return -EINVAL; +	} +} + +static int tsl4531_write_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int val, int val2, long mask) +{ +	struct tsl4531_data *data = iio_priv(indio_dev); +	int int_time, ret; + +	switch (mask) { +	case IIO_CHAN_INFO_INT_TIME: +		if (val != 0) +			return -EINVAL; +		if (val2 == 400000) +			int_time = 0; +		else if (val2 == 200000) +			int_time = 1; +		else if (val2 == 100000) +			int_time = 2; +		else +			return -EINVAL; +		mutex_lock(&data->lock); +		ret = i2c_smbus_write_byte_data(data->client, +			TSL4531_CONFIG, int_time); +		if (ret >= 0) +			data->int_time = int_time; +		mutex_unlock(&data->lock); +		return ret; +	default: +		return -EINVAL; +	} +} + +static const struct iio_info tsl4531_info = { +	.read_raw = tsl4531_read_raw, +	.write_raw = tsl4531_write_raw, +	.attrs = &tsl4531_attribute_group, +	.driver_module = THIS_MODULE, +}; + +static int tsl4531_check_id(struct i2c_client *client) +{ +	int ret = i2c_smbus_read_byte_data(client, TSL4531_ID); +	if (ret < 0) +		return ret; + +	switch (ret >> TSL4531_ID_SHIFT) { +	case TSL45311_ID: +	case TSL45313_ID: +	case TSL45315_ID: +	case TSL45317_ID: +		return 1; +	default: +		return 0; +	} +} + +static int tsl4531_probe(struct i2c_client *client, +			  const struct i2c_device_id *id) +{ +	struct tsl4531_data *data; +	struct iio_dev *indio_dev; +	int ret; + +	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; +	mutex_init(&data->lock); + +	if (!tsl4531_check_id(client)) { +		dev_err(&client->dev, "no TSL4531 sensor\n"); +		return -ENODEV; +	} + +	ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONTROL, +		TSL4531_MODE_NORMAL); +	if (ret < 0) +		return ret; + +	ret = i2c_smbus_write_byte_data(data->client, TSL4531_CONFIG, +		TSL4531_TCNTRL_400MS); +	if (ret < 0) +		return ret; + +	indio_dev->dev.parent = &client->dev; +	indio_dev->info = &tsl4531_info; +	indio_dev->channels = tsl4531_channels; +	indio_dev->num_channels = ARRAY_SIZE(tsl4531_channels); +	indio_dev->name = TSL4531_DRV_NAME; +	indio_dev->modes = INDIO_DIRECT_MODE; + +	return iio_device_register(indio_dev); +} + +static int tsl4531_powerdown(struct i2c_client *client) +{ +	return i2c_smbus_write_byte_data(client, TSL4531_CONTROL, +		TSL4531_MODE_POWERDOWN); +} + +static int tsl4531_remove(struct i2c_client *client) +{ +	iio_device_unregister(i2c_get_clientdata(client)); +	tsl4531_powerdown(client); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tsl4531_suspend(struct device *dev) +{ +	return tsl4531_powerdown(to_i2c_client(dev)); +} + +static int tsl4531_resume(struct device *dev) +{ +	return i2c_smbus_write_byte_data(to_i2c_client(dev), TSL4531_CONTROL, +		TSL4531_MODE_NORMAL); +} +#endif + +static SIMPLE_DEV_PM_OPS(tsl4531_pm_ops, tsl4531_suspend, tsl4531_resume); + +static const struct i2c_device_id tsl4531_id[] = { +	{ "tsl4531", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, tsl4531_id); + +static struct i2c_driver tsl4531_driver = { +	.driver = { +		.name   = TSL4531_DRV_NAME, +		.pm	= &tsl4531_pm_ops, +		.owner  = THIS_MODULE, +	}, +	.probe  = tsl4531_probe, +	.remove = tsl4531_remove, +	.id_table = tsl4531_id, +}; + +module_i2c_driver(tsl4531_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("TAOS TSL4531 ambient light sensors driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 2bb304215b1..d948c4778ba 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -56,7 +56,7 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,  				u8 rdy_mask, u8 data_reg, int *val)  {  	int tries = 20; -	u16 buf; +	__be16 buf;  	int ret;  	ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, @@ -179,17 +179,7 @@ static int vcnl4000_probe(struct i2c_client *client,  	indio_dev->name = VCNL4000_DRV_NAME;  	indio_dev->modes = INDIO_DIRECT_MODE; -	ret = iio_device_register(indio_dev); -	if (ret < 0) -		return ret; - -	return 0; -} - -static int vcnl4000_remove(struct i2c_client *client) -{ -	iio_device_unregister(i2c_get_clientdata(client)); -	return 0; +	return devm_iio_device_register(&client->dev, indio_dev);  }  static struct i2c_driver vcnl4000_driver = { @@ -198,7 +188,6 @@ static struct i2c_driver vcnl4000_driver = {  		.owner  = THIS_MODULE,  	},  	.probe  = vcnl4000_probe, -	.remove = vcnl4000_remove,  	.id_table = vcnl4000_id,  }; diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig index 4fa923f37b9..05a364c543f 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -11,11 +11,24 @@ 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. +config MAG3110 +	tristate "Freescale MAG3110 3-Axis Magnetometer" +	depends on I2C +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	  Say yes here to build support for the Freescale MAG3110 3-Axis +	  magnetometer. + +	  To compile this driver as a module, choose M here: the module +	  will be called mag3110. +  config HID_SENSOR_MAGNETOMETER_3D  	depends on HID_SENSOR_HUB  	select IIO_BUFFER diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile index f91b1b68d39..0f5d3c98579 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -4,6 +4,7 @@  # When adding new entries keep the list in alphabetical order  obj-$(CONFIG_AK8975)	+= ak8975.o +obj-$(CONFIG_MAG3110)	+= mag3110.o  obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o  obj-$(CONFIG_IIO_ST_MAGN_3AXIS) += st_magn.o diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 7105f22d6cd..ea08313af0d 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,6 +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_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. @@ -100,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[] = { @@ -263,17 +273,29 @@ static int ak8975_setup(struct i2c_client *client)   *   * HuT = H * 1229/4096, or roughly, 3/10.   * - * Since 1uT = 100 gauss, our final scale factor becomes: + * Since 1uT = 0.01 gauss, our final scale factor becomes:   * - * Hadj = H * ((ASA + 128) / 256) * 3/10 * 100 - * Hadj = H * ((ASA + 128) * 30 / 256 + * Hadj = H * ((ASA + 128) / 256) * 3/10 * 1/100 + * Hadj = H * ((ASA + 128) * 0.003) / 256   *   * Since ASA doesn't change, we cache the resultant scale factor into the   * device context in ak8975_setup().   */ -	data->raw_to_gauss[0] = ((data->asa[0] + 128) * 30) >> 8; -	data->raw_to_gauss[1] = ((data->asa[1] + 128) * 30) >> 8; -	data->raw_to_gauss[2] = ((data->asa[2] + 128) * 30) >> 8; +	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;  } @@ -351,8 +373,6 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)  {  	struct ak8975_data *data = iio_priv(indio_dev);  	struct i2c_client *client = data->client; -	u16 meas_reg; -	s16 raw;  	int ret;  	mutex_lock(&data->lock); @@ -400,16 +420,11 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)  		dev_err(&client->dev, "Read axis data fails\n");  		goto exit;  	} -	meas_reg = ret;  	mutex_unlock(&data->lock); -	/* Endian conversion of the measured values. */ -	raw = (s16) (le16_to_cpu(meas_reg)); -  	/* Clamp to valid range. */ -	raw = clamp_t(s16, raw, -4096, 4095); -	*val = raw; +	*val = clamp_t(s16, ret, -4096, 4095);  	return IIO_VAL_INT;  exit: @@ -428,8 +443,9 @@ static int ak8975_read_raw(struct iio_dev *indio_dev,  	case IIO_CHAN_INFO_RAW:  		return ak8975_read_axis(indio_dev, chan->address, val);  	case IIO_CHAN_INFO_SCALE: -		*val = data->raw_to_gauss[chan->address]; -		return IIO_VAL_INT; +		*val = 0; +		*val2 = data->raw_to_gauss[chan->address]; +		return IIO_VAL_INT_PLUS_MICRO;  	}  	return -EINVAL;  } @@ -453,6 +469,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)  { @@ -460,6 +497,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) @@ -497,6 +535,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) { @@ -512,7 +563,7 @@ static int ak8975_probe(struct i2c_client *client,  	indio_dev->num_channels = ARRAY_SIZE(ak8975_channels);  	indio_dev->info = &ak8975_info;  	indio_dev->modes = INDIO_DIRECT_MODE; - +	indio_dev->name = name;  	err = iio_device_register(indio_dev);  	if (err < 0)  		goto exit_free_iio; @@ -549,7 +600,8 @@ static int ak8975_remove(struct i2c_client *client)  }  static const struct i2c_device_id ak8975_id[] = { -	{"ak8975", 0}, +	{"ak8975", AK8975}, +	{"ak8963", AK8963},  	{}  }; @@ -566,6 +618,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 a98460b15e4..b2b0937d513 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) | @@ -102,13 +110,21 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,  	struct magn_3d_state *magn_state = iio_priv(indio_dev);  	int report_id = -1;  	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,28 +135,29 @@ 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: -		ret = hid_sensor_read_samp_freq_value( +		ret_type = hid_sensor_read_samp_freq_value(  			&magn_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	case IIO_CHAN_INFO_HYSTERESIS: -		ret = hid_sensor_read_raw_hyst_value( +		ret_type = hid_sensor_read_raw_hyst_value(  			&magn_state->common_attributes, val, val2); -		ret_type = IIO_VAL_INT_PLUS_MICRO;  		break;  	default:  		ret_type = -EINVAL; @@ -183,10 +200,11 @@ static const struct iio_info magn_3d_info = {  };  /* Function to push data to buffer */ -static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len) +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +	int len)  {  	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); -	iio_push_to_buffers(indio_dev, (u8 *)data); +	iio_push_to_buffers(indio_dev, data);  }  /* Callback handler to send event after all samples are received and captured */ @@ -197,11 +215,10 @@ 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, -				(u8 *)magn_state->magn_val, +				magn_state->magn_val,  				sizeof(magn_state->magn_val));  	return 0; @@ -262,6 +279,23 @@ 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, +			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 ret;  } @@ -321,7 +355,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) { @@ -350,7 +384,7 @@ static int hid_magn_3d_probe(struct platform_device *pdev)  error_iio_unreg:  	iio_device_unregister(indio_dev);  error_remove_trigger: -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&magn_state->common_attributes);  error_unreg_buffer_funcs:  	iio_triggered_buffer_cleanup(indio_dev);  error_free_dev_mem: @@ -363,10 +397,11 @@ static int hid_magn_3d_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 magn_3d_state *magn_state = iio_priv(indio_dev);  	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);  	iio_device_unregister(indio_dev); -	hid_sensor_remove_trigger(indio_dev); +	hid_sensor_remove_trigger(&magn_state->common_attributes);  	iio_triggered_buffer_cleanup(indio_dev);  	kfree(indio_dev->channels); diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c new file mode 100644 index 00000000000..e3106b43ef4 --- /dev/null +++ b/drivers/iio/magnetometer/mag3110.c @@ -0,0 +1,438 @@ +/* + * mag3110.c - Support for Freescale MAG3110 magnetometer sensor + * + * Copyright (c) 2013 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 0x0e) + * + * TODO: irq, user offset, oversampling, continuous mode + */ + +#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 MAG3110_STATUS 0x00 +#define MAG3110_OUT_X 0x01 /* MSB first */ +#define MAG3110_OUT_Y 0x03 +#define MAG3110_OUT_Z 0x05 +#define MAG3110_WHO_AM_I 0x07 +#define MAG3110_OFF_X 0x09 /* MSB first */ +#define MAG3110_OFF_Y 0x0b +#define MAG3110_OFF_Z 0x0d +#define MAG3110_DIE_TEMP 0x0f +#define MAG3110_CTRL_REG1 0x10 +#define MAG3110_CTRL_REG2 0x11 + +#define MAG3110_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0)) + +#define MAG3110_CTRL_DR_MASK (BIT(7) | BIT(6) | BIT(5)) +#define MAG3110_CTRL_DR_SHIFT 5 +#define MAG3110_CTRL_DR_DEFAULT 0 + +#define MAG3110_CTRL_TM BIT(1) /* trigger single measurement */ +#define MAG3110_CTRL_AC BIT(0) /* continuous measurements */ + +#define MAG3110_CTRL_AUTO_MRST_EN BIT(7) /* magnetic auto-reset */ +#define MAG3110_CTRL_RAW BIT(5) /* measurements not user-offset corrected */ + +#define MAG3110_DEVICE_ID 0xc4 + +/* Each client has this additional data */ +struct mag3110_data { +	struct i2c_client *client; +	struct mutex lock; +	u8 ctrl_reg1; +}; + +static int mag3110_request(struct mag3110_data *data) +{ +	int ret, tries = 150; + +	/* trigger measurement */ +	ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, +		data->ctrl_reg1 | MAG3110_CTRL_TM); +	if (ret < 0) +		return ret; + +	while (tries-- > 0) { +		ret = i2c_smbus_read_byte_data(data->client, MAG3110_STATUS); +		if (ret < 0) +			return ret; +		/* wait for data ready */ +		if ((ret & MAG3110_STATUS_DRDY) == MAG3110_STATUS_DRDY) +			break; +		msleep(20); +	} + +	if (tries < 0) { +		dev_err(&data->client->dev, "data not ready\n"); +		return -EIO; +	} + +	return 0; +} + +static int mag3110_read(struct mag3110_data *data, __be16 buf[3]) +{ +	int ret; + +	mutex_lock(&data->lock); +	ret = mag3110_request(data); +	if (ret < 0) { +		mutex_unlock(&data->lock); +		return ret; +	} +	ret = i2c_smbus_read_i2c_block_data(data->client, +		MAG3110_OUT_X, 3 * sizeof(__be16), (u8 *) buf); +	mutex_unlock(&data->lock); + +	return ret; +} + +static ssize_t mag3110_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 mag3110_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 mag3110_samp_freq[8][2] = { +	{80, 0}, {40, 0}, {20, 0}, {10, 0}, {5, 0}, {2, 500000}, +	{1, 250000}, {0, 625000} +}; + +static ssize_t mag3110_show_samp_freq_avail(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	return mag3110_show_int_plus_micros(buf, mag3110_samp_freq, 8); +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mag3110_show_samp_freq_avail); + +static int mag3110_get_samp_freq_index(struct mag3110_data *data, +	int val, int val2) +{ +	return mag3110_get_int_plus_micros_index(mag3110_samp_freq, 8, val, +		val2); +} + +static int mag3110_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, int *val2, long mask) +{ +	struct mag3110_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; + +		switch (chan->type) { +		case IIO_MAGN: /* in 0.1 uT / LSB */ +			ret = mag3110_read(data, buffer); +			if (ret < 0) +				return ret; +			*val = sign_extend32( +				be16_to_cpu(buffer[chan->scan_index]), 15); +			return IIO_VAL_INT; +		case IIO_TEMP: /* in 1 C / LSB */ +			mutex_lock(&data->lock); +			ret = mag3110_request(data); +			if (ret < 0) { +				mutex_unlock(&data->lock); +				return ret; +			} +			ret = i2c_smbus_read_byte_data(data->client, +				MAG3110_DIE_TEMP); +			mutex_unlock(&data->lock); +			if (ret < 0) +				return ret; +			*val = sign_extend32(ret, 7); +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_MAGN: +			*val = 0; +			*val2 = 1000; +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = 1000; +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_SAMP_FREQ: +		i = data->ctrl_reg1 >> MAG3110_CTRL_DR_SHIFT; +		*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; +} + +static int mag3110_write_raw(struct iio_dev *indio_dev, +			     struct iio_chan_spec const *chan, +			     int val, int val2, long mask) +{ +	struct mag3110_data *data = iio_priv(indio_dev); +	int rate; + +	if (iio_buffer_enabled(indio_dev)) +		return -EBUSY; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		rate = mag3110_get_samp_freq_index(data, val, val2); +		if (rate < 0) +			return -EINVAL; + +		data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK; +		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; +	} +} + +static irqreturn_t mag3110_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct mag3110_data *data = iio_priv(indio_dev); +	u8 buffer[16]; /* 3 16-bit channels + 1 byte temp + padding + ts */ +	int ret; + +	ret = mag3110_read(data, (__be16 *) buffer); +	if (ret < 0) +		goto done; + +	if (test_bit(3, indio_dev->active_scan_mask)) { +		ret = i2c_smbus_read_byte_data(data->client, +			MAG3110_DIE_TEMP); +		if (ret < 0) +			goto done; +		buffer[6] = ret; +	} + +	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 MAG3110_CHANNEL(axis, idx) { \ +	.type = IIO_MAGN, \ +	.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 = 16, \ +		.storagebits = 16, \ +		.endianness = IIO_BE, \ +	}, \ +} + +static const struct iio_chan_spec mag3110_channels[] = { +	MAG3110_CHANNEL(X, 0), +	MAG3110_CHANNEL(Y, 1), +	MAG3110_CHANNEL(Z, 2), +	{ +		.type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE), +		.scan_index = 3, +		.scan_type = { +			.sign = 's', +			.realbits = 8, +			.storagebits = 8, +			}, +	}, +	IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static struct attribute *mag3110_attributes[] = { +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr, +	NULL +}; + +static const struct attribute_group mag3110_group = { +	.attrs = mag3110_attributes, +}; + +static const struct iio_info mag3110_info = { +	.attrs = &mag3110_group, +	.read_raw = &mag3110_read_raw, +	.write_raw = &mag3110_write_raw, +	.driver_module = THIS_MODULE, +}; + +static const unsigned long mag3110_scan_masks[] = {0x7, 0xf, 0}; + +static int mag3110_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct mag3110_data *data; +	struct iio_dev *indio_dev; +	int ret; + +	ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I); +	if (ret < 0) +		return ret; +	if (ret != MAG3110_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 = &mag3110_info; +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = mag3110_channels; +	indio_dev->num_channels = ARRAY_SIZE(mag3110_channels); +	indio_dev->available_scan_masks = mag3110_scan_masks; + +	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); +	if (ret < 0) +		return ret; + +	ret = iio_triggered_buffer_setup(indio_dev, NULL, +		mag3110_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 mag3110_standby(struct mag3110_data *data) +{ +	return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, +		data->ctrl_reg1 & ~MAG3110_CTRL_AC); +} + +static int mag3110_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); +	mag3110_standby(iio_priv(indio_dev)); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mag3110_suspend(struct device *dev) +{ +	return mag3110_standby(iio_priv(i2c_get_clientdata( +		to_i2c_client(dev)))); +} + +static int mag3110_resume(struct device *dev) +{ +	struct mag3110_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); + +	return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, +		data->ctrl_reg1); +} + +static SIMPLE_DEV_PM_OPS(mag3110_pm_ops, mag3110_suspend, mag3110_resume); +#define MAG3110_PM_OPS (&mag3110_pm_ops) +#else +#define MAG3110_PM_OPS NULL +#endif + +static const struct i2c_device_id mag3110_id[] = { +	{ "mag3110", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, mag3110_id); + +static struct i2c_driver mag3110_driver = { +	.driver = { +		.name	= "mag3110", +		.pm	= MAG3110_PM_OPS, +	}, +	.probe = mag3110_probe, +	.remove = mag3110_remove, +	.id_table = mag3110_id, +}; +module_i2c_driver(mag3110_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Freescale MAG3110 magnetometer driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c index 708857bdb47..bf427dc0d22 100644 --- a/drivers/iio/magnetometer/st_magn_buffer.c +++ b/drivers/iio/magnetometer/st_magn_buffer.c @@ -25,16 +25,7 @@  static int st_magn_buffer_preenable(struct iio_dev *indio_dev)  { -	int err; - -	err = st_sensors_set_enable(indio_dev, true); -	if (err < 0) -		goto st_magn_set_enable_error; - -	err = iio_sw_buffer_preenable(indio_dev); - -st_magn_set_enable_error: -	return err; +	return st_sensors_set_enable(indio_dev, true);  }  static int st_magn_buffer_postenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index e8d2849cc81..240a21dd0c6 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -29,9 +29,9 @@  #define ST_MAGN_NUMBER_DATA_CHANNELS		3  /* DEFAULT VALUE FOR SENSORS */ -#define ST_MAGN_DEFAULT_OUT_X_L_ADDR		0X04 -#define ST_MAGN_DEFAULT_OUT_Y_L_ADDR		0X08 -#define ST_MAGN_DEFAULT_OUT_Z_L_ADDR		0X06 +#define ST_MAGN_DEFAULT_OUT_X_H_ADDR		0X03 +#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR		0X07 +#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR		0X05  /* FULLSCALE */  #define ST_MAGN_FS_AVL_1300MG			1300 @@ -117,16 +117,16 @@  static const struct iio_chan_spec st_magn_16bit_channels[] = {  	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,  			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), -			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, -			ST_MAGN_DEFAULT_OUT_X_L_ADDR), +			ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16, +			ST_MAGN_DEFAULT_OUT_X_H_ADDR),  	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,  			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), -			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, -			ST_MAGN_DEFAULT_OUT_Y_L_ADDR), +			ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16, +			ST_MAGN_DEFAULT_OUT_Y_H_ADDR),  	ST_SENSORS_LSM_CHANNELS(IIO_MAGN,  			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), -			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, -			ST_MAGN_DEFAULT_OUT_Z_L_ADDR), +			ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16, +			ST_MAGN_DEFAULT_OUT_Z_H_ADDR),  	IIO_CHAN_SOFT_TIMESTAMP(3)  }; @@ -348,16 +348,19 @@ static const struct iio_info magn_info = {  int st_magn_common_probe(struct iio_dev *indio_dev,  					struct st_sensors_platform_data *pdata)  { -	int err;  	struct st_sensor_data *mdata = iio_priv(indio_dev); +	int irq = mdata->get_irq_data_ready(indio_dev); +	int err;  	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) -		goto st_magn_common_probe_error; +		return err;  	mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;  	mdata->multiread_bit = mdata->sensor->multi_read_bit; @@ -370,12 +373,13 @@ int st_magn_common_probe(struct iio_dev *indio_dev,  	err = st_sensors_init_sensor(indio_dev, pdata);  	if (err < 0) -		goto st_magn_common_probe_error; +		return err; -	if (mdata->get_irq_data_ready(indio_dev) > 0) { -		err = st_magn_allocate_ring(indio_dev); -		if (err < 0) -			goto st_magn_common_probe_error; +	err = st_magn_allocate_ring(indio_dev); +	if (err < 0) +		return err; + +	if (irq > 0) {  		err = st_sensors_allocate_trigger(indio_dev, NULL);  		if (err < 0)  			goto st_magn_probe_trigger_error; @@ -385,15 +389,17 @@ int st_magn_common_probe(struct iio_dev *indio_dev,  	if (err)  		goto st_magn_device_register_error; -	return err; +	dev_info(&indio_dev->dev, "registered magnetometer %s\n", +		 indio_dev->name); + +	return 0;  st_magn_device_register_error: -	if (mdata->get_irq_data_ready(indio_dev) > 0) +	if (irq > 0)  		st_sensors_deallocate_trigger(indio_dev);  st_magn_probe_trigger_error: -	if (mdata->get_irq_data_ready(indio_dev) > 0) -		st_magn_deallocate_ring(indio_dev); -st_magn_common_probe_error: +	st_magn_deallocate_ring(indio_dev); +  	return err;  }  EXPORT_SYMBOL(st_magn_common_probe); @@ -402,11 +408,13 @@ 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) { +	if (mdata->get_irq_data_ready(indio_dev) > 0)  		st_sensors_deallocate_trigger(indio_dev); -		st_magn_deallocate_ring(indio_dev); -	} + +	st_magn_deallocate_ring(indio_dev);  }  EXPORT_SYMBOL(st_magn_common_remove); diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig new file mode 100644 index 00000000000..e3aa1e58d92 --- /dev/null +++ b/drivers/iio/orientation/Kconfig @@ -0,0 +1,31 @@ +# +# Inclinometer sensors +# +# When adding new entries keep the list in alphabetical order + +menu "Inclinometer sensors" + +config HID_SENSOR_INCLINOMETER_3D +	depends on HID_SENSOR_HUB +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	select HID_SENSOR_IIO_COMMON +	select HID_SENSOR_IIO_TRIGGER +	tristate "HID Inclinometer 3D" +	help +	  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 new file mode 100644 index 00000000000..4734dabbde1 --- /dev/null +++ b/drivers/iio/orientation/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for industrial I/O Inclinometer sensor drivers +# + +# 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 new file mode 100644 index 00000000000..2478f6c2ef2 --- /dev/null +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -0,0 +1,449 @@ +/* + * HID Sensors Driver + * Copyright (c) 2013, 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. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc. + * + */ + +#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/delay.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" + +enum incl_3d_channel { +	CHANNEL_SCAN_INDEX_X, +	CHANNEL_SCAN_INDEX_Y, +	CHANNEL_SCAN_INDEX_Z, +	INCLI_3D_CHANNEL_MAX, +}; + +struct incl_3d_state { +	struct hid_sensor_hub_callbacks callbacks; +	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] = { +	HID_USAGE_SENSOR_ORIENT_TILT_X, +	HID_USAGE_SENSOR_ORIENT_TILT_Y, +	HID_USAGE_SENSOR_ORIENT_TILT_Z +}; + +/* Channel definitions */ +static const struct iio_chan_spec incl_3d_channels[] = { +	{ +		.type = IIO_INCLI, +		.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) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_X, +	}, { +		.type = IIO_INCLI, +		.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) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_Y, +	}, { +		.type = IIO_INCLI, +		.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) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_Z, +	} +}; + +/* Adjust channel real bits based on report descriptor */ +static void incl_3d_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; +} + +/* Channel read_raw handler */ +static int incl_3d_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct incl_3d_state *incl_state = iio_priv(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]; +		if (report_id >= 0) +			*val = sensor_hub_input_attr_get_raw_value( +				incl_state->common_attributes.hsdev, +				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->scale_pre_decml; +		*val2 = incl_state->scale_post_decml; +		ret_type = incl_state->scale_precision; +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = incl_state->value_offset; +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret_type = hid_sensor_read_samp_freq_value( +			&incl_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret_type = hid_sensor_read_raw_hyst_value( +			&incl_state->common_attributes, val, val2); +		break; +	default: +		ret_type = -EINVAL; +		break; +	} + +	return ret_type; +} + +/* Channel write_raw handler */ +static int incl_3d_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct incl_3d_state *incl_state = iio_priv(indio_dev); +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret = hid_sensor_write_samp_freq_value( +				&incl_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret = hid_sensor_write_raw_hyst_value( +				&incl_state->common_attributes, val, val2); +		break; +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static const struct iio_info incl_3d_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &incl_3d_read_raw, +	.write_raw = &incl_3d_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); +} + +/* Callback handler to send event after all samples are received and captured */ +static int incl_3d_proc_event(struct hid_sensor_hub_device *hsdev, +				unsigned usage_id, +				void *priv) +{ +	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\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)); + +	return 0; +} + +/* Capture samples in local storage */ +static int incl_3d_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 incl_3d_state *incl_state = iio_priv(indio_dev); +	int ret = 0; + +	switch (usage_id) { +	case HID_USAGE_SENSOR_ORIENT_TILT_X: +		incl_state->incl_val[CHANNEL_SCAN_INDEX_X] = *(u32 *)raw_data; +	break; +	case HID_USAGE_SENSOR_ORIENT_TILT_Y: +		incl_state->incl_val[CHANNEL_SCAN_INDEX_Y] = *(u32 *)raw_data; +	break; +	case HID_USAGE_SENSOR_ORIENT_TILT_Z: +		incl_state->incl_val[CHANNEL_SCAN_INDEX_Z] = *(u32 *)raw_data; +	break; +	default: +		ret = -EINVAL; +		break; +	} + +	return ret; +} + +/* Parse report which is specific to an usage id*/ +static int incl_3d_parse_report(struct platform_device *pdev, +				struct hid_sensor_hub_device *hsdev, +				struct iio_chan_spec *channels, +				unsigned usage_id, +				struct incl_3d_state *st) +{ +	int ret; + +	ret = sensor_hub_input_get_attribute_info(hsdev, +				HID_INPUT_REPORT, +				usage_id, +				HID_USAGE_SENSOR_ORIENT_TILT_X, +				&st->incl[CHANNEL_SCAN_INDEX_X]); +	if (ret) +		return ret; +	incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_X], +				st->incl[CHANNEL_SCAN_INDEX_X].size); + +	ret = sensor_hub_input_get_attribute_info(hsdev, +				HID_INPUT_REPORT, +				usage_id, +				HID_USAGE_SENSOR_ORIENT_TILT_Y, +				&st->incl[CHANNEL_SCAN_INDEX_Y]); +	if (ret) +		return ret; +	incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Y], +				st->incl[CHANNEL_SCAN_INDEX_Y].size); + +	ret = sensor_hub_input_get_attribute_info(hsdev, +				HID_INPUT_REPORT, +				usage_id, +				HID_USAGE_SENSOR_ORIENT_TILT_Z, +				&st->incl[CHANNEL_SCAN_INDEX_Z]); +	if (ret) +		return ret; +	incl_3d_adjust_channel_bit_mask(&channels[CHANNEL_SCAN_INDEX_Z], +				st->incl[CHANNEL_SCAN_INDEX_Z].size); + +	dev_dbg(&pdev->dev, "incl_3d %x:%x, %x:%x, %x:%x\n", +			st->incl[0].index, +			st->incl[0].report_id, +			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, +			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 ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_incl_3d_probe(struct platform_device *pdev) +{ +	int ret; +	static char *name = "incli_3d"; +	struct iio_dev *indio_dev; +	struct incl_3d_state *incl_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 incl_3d_state)); +	if (indio_dev == NULL) +		return -ENOMEM; + +	platform_set_drvdata(pdev, indio_dev); + +	incl_state = iio_priv(indio_dev); +	incl_state->common_attributes.hsdev = hsdev; +	incl_state->common_attributes.pdev = pdev; + +	ret = hid_sensor_parse_common_attributes(hsdev, +				HID_USAGE_SENSOR_INCLINOMETER_3D, +				&incl_state->common_attributes); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup common attributes\n"); +		return ret; +	} + +	channels = kmemdup(incl_3d_channels, sizeof(incl_3d_channels), +			   GFP_KERNEL); +	if (!channels) { +		dev_err(&pdev->dev, "failed to duplicate channels\n"); +		return -ENOMEM; +	} + +	ret = incl_3d_parse_report(pdev, hsdev, channels, +				HID_USAGE_SENSOR_INCLINOMETER_3D, incl_state); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup attributes\n"); +		goto error_free_dev_mem; +	} + +	indio_dev->channels = channels; +	indio_dev->num_channels = ARRAY_SIZE(incl_3d_channels); +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->info = &incl_3d_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"); +		goto error_free_dev_mem; +	} +	atomic_set(&incl_state->common_attributes.data_ready, 0); +	ret = hid_sensor_setup_trigger(indio_dev, name, +					&incl_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; +	} + +	incl_state->callbacks.send_event = incl_3d_proc_event; +	incl_state->callbacks.capture_sample = incl_3d_capture_sample; +	incl_state->callbacks.pdev = pdev; +	ret = sensor_hub_register_callback(hsdev, +					HID_USAGE_SENSOR_INCLINOMETER_3D, +					&incl_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(&incl_state->common_attributes); +error_unreg_buffer_funcs: +	iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: +	kfree(indio_dev->channels); +	return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_incl_3d_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 incl_3d_state *incl_state = iio_priv(indio_dev); + +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D); +	iio_device_unregister(indio_dev); +	hid_sensor_remove_trigger(&incl_state->common_attributes); +	iio_triggered_buffer_cleanup(indio_dev); +	kfree(indio_dev->channels); + +	return 0; +} + +static struct platform_device_id hid_incl_3d_ids[] = { +	{ +		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */ +		.name = "HID-SENSOR-200086", +	}, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_incl_3d_ids); + +static struct platform_driver hid_incl_3d_platform_driver = { +	.id_table = hid_incl_3d_ids, +	.driver = { +		.name	= KBUILD_MODNAME, +		.owner	= THIS_MODULE, +	}, +	.probe		= hid_incl_3d_probe, +	.remove		= hid_incl_3d_remove, +}; +module_platform_driver(hid_incl_3d_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Inclinometer 3D"); +MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); +MODULE_LICENSE("GPL"); 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 26fdc0bdb99..ffac8ac1efc 100644 --- a/drivers/iio/pressure/Kconfig +++ b/drivers/iio/pressure/Kconfig @@ -5,6 +5,42 @@  menu "Pressure sensors" +config HID_SENSOR_PRESS +	depends on HID_SENSOR_HUB +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	select HID_SENSOR_IIO_COMMON +	select HID_SENSOR_IIO_TRIGGER +	tristate "HID PRESS" +	help +	  Say yes here to build support for the HID SENSOR +	  Pressure driver + +          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 +	select IIO_BUFFER +	select IIO_TRIGGERED_BUFFER +	help +	  Say yes here to build support for the Freescale MPL3115A2 +	  pressure sensor / altimeter. + +          To compile this driver as a module, choose M here: the module +          will be called mpl3115. +  config IIO_ST_PRESS  	tristate "STMicroelectronics pressure sensor Driver"  	depends on (I2C || SPI_MASTER) && SYSFS @@ -14,7 +50,7 @@ config IIO_ST_PRESS  	select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)  	help  	  Say yes here to build support for STMicroelectronics pressure -	  sensors: LPS331AP. +	  sensors: LPS001WP, LPS25H, LPS331AP.  	  This driver can also be built as a module. If so, these modules  	  will be created: diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile index be71464c275..c53d2500737 100644 --- a/drivers/iio/pressure/Makefile +++ b/drivers/iio/pressure/Makefile @@ -3,6 +3,9 @@  #  # 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  st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c new file mode 100644 index 00000000000..2c0d2a4fed8 --- /dev/null +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -0,0 +1,394 @@ +/* + * 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. + * + * You should have received a copy of the GNU General Public License along with + * this program. + * + */ +#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/delay.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" + +#define CHANNEL_SCAN_INDEX_PRESSURE 0 + +struct press_state { +	struct hid_sensor_hub_callbacks callbacks; +	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 */ +static const struct iio_chan_spec press_channels[] = { +	{ +		.type = IIO_PRESSURE, +		.modified = 1, +		.channel2 = IIO_NO_MOD, +		.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) | +		BIT(IIO_CHAN_INFO_HYSTERESIS), +		.scan_index = CHANNEL_SCAN_INDEX_PRESSURE, +	} +}; + +/* Adjust channel real bits based on report descriptor */ +static void press_adjust_channel_bit_mask(struct iio_chan_spec *channels, +					int channel, int size) +{ +	channels[channel].scan_type.sign = 's'; +	/* Real storage bits will change based on the report desc. */ +	channels[channel].scan_type.realbits = size * 8; +	/* Maximum size of a sample to capture is u32 */ +	channels[channel].scan_type.storagebits = sizeof(u32) * 8; +} + +/* Channel read_raw handler */ +static int press_read_raw(struct iio_dev *indio_dev, +			      struct iio_chan_spec const *chan, +			      int *val, int *val2, +			      long mask) +{ +	struct press_state *press_state = iio_priv(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: +		switch (chan->scan_index) { +		case  CHANNEL_SCAN_INDEX_PRESSURE: +			report_id = press_state->press_attr.report_id; +			address = +			HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE; +			break; +		default: +			report_id = -1; +			break; +		} +		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); +			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->scale_pre_decml; +		*val2 = press_state->scale_post_decml; +		ret_type = press_state->scale_precision; +		break; +	case IIO_CHAN_INFO_OFFSET: +		*val = press_state->value_offset; +		ret_type = IIO_VAL_INT; +		break; +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret_type = hid_sensor_read_samp_freq_value( +				&press_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret_type = hid_sensor_read_raw_hyst_value( +				&press_state->common_attributes, val, val2); +		break; +	default: +		ret_type = -EINVAL; +		break; +	} + +	return ret_type; +} + +/* Channel write_raw handler */ +static int press_write_raw(struct iio_dev *indio_dev, +			       struct iio_chan_spec const *chan, +			       int val, +			       int val2, +			       long mask) +{ +	struct press_state *press_state = iio_priv(indio_dev); +	int ret = 0; + +	switch (mask) { +	case IIO_CHAN_INFO_SAMP_FREQ: +		ret = hid_sensor_write_samp_freq_value( +				&press_state->common_attributes, val, val2); +		break; +	case IIO_CHAN_INFO_HYSTERESIS: +		ret = hid_sensor_write_raw_hyst_value( +				&press_state->common_attributes, val, val2); +		break; +	default: +		ret = -EINVAL; +	} + +	return ret; +} + +static const struct iio_info press_info = { +	.driver_module = THIS_MODULE, +	.read_raw = &press_read_raw, +	.write_raw = &press_write_raw, +}; + +/* Function to push data to buffer */ +static void hid_sensor_push_data(struct iio_dev *indio_dev, const void *data, +					int len) +{ +	dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n"); +	iio_push_to_buffers(indio_dev, data); +} + +/* Callback handler to send event after all samples are received and captured */ +static int press_proc_event(struct hid_sensor_hub_device *hsdev, +				unsigned usage_id, +				void *priv) +{ +	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\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)); + +	return 0; +} + +/* Capture samples in local storage */ +static int press_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 press_state *press_state = iio_priv(indio_dev); +	int ret = -EINVAL; + +	switch (usage_id) { +	case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE: +		press_state->press_data = *(u32 *)raw_data; +		ret = 0; +		break; +	default: +		break; +	} + +	return ret; +} + +/* Parse report which is specific to an usage id*/ +static int press_parse_report(struct platform_device *pdev, +				struct hid_sensor_hub_device *hsdev, +				struct iio_chan_spec *channels, +				unsigned usage_id, +				struct press_state *st) +{ +	int ret; + +	ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT, +			usage_id, +			HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE, +			&st->press_attr); +	if (ret < 0) +		return ret; +	press_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_PRESSURE, +					st->press_attr.size); + +	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, +			HID_FEATURE_REPORT, usage_id, +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | +			HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, +			&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 ret; +} + +/* Function to initialize the processing for usage id */ +static int hid_press_probe(struct platform_device *pdev) +{ +	int ret = 0; +	static const char *name = "press"; +	struct iio_dev *indio_dev; +	struct press_state *press_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 press_state)); +	if (!indio_dev) +		return -ENOMEM; +	platform_set_drvdata(pdev, indio_dev); + +	press_state = iio_priv(indio_dev); +	press_state->common_attributes.hsdev = hsdev; +	press_state->common_attributes.pdev = pdev; + +	ret = hid_sensor_parse_common_attributes(hsdev, +					HID_USAGE_SENSOR_PRESSURE, +					&press_state->common_attributes); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup common attributes\n"); +		return ret; +	} + +	channels = kmemdup(press_channels, sizeof(press_channels), GFP_KERNEL); +	if (!channels) { +		dev_err(&pdev->dev, "failed to duplicate channels\n"); +		return -ENOMEM; +	} + +	ret = press_parse_report(pdev, hsdev, channels, +				HID_USAGE_SENSOR_PRESSURE, press_state); +	if (ret) { +		dev_err(&pdev->dev, "failed to setup attributes\n"); +		goto error_free_dev_mem; +	} + +	indio_dev->channels = channels; +	indio_dev->num_channels = +				ARRAY_SIZE(press_channels); +	indio_dev->dev.parent = &pdev->dev; +	indio_dev->info = &press_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"); +		goto error_free_dev_mem; +	} +	atomic_set(&press_state->common_attributes.data_ready, 0); +	ret = hid_sensor_setup_trigger(indio_dev, name, +				&press_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; +	} + +	press_state->callbacks.send_event = press_proc_event; +	press_state->callbacks.capture_sample = press_capture_sample; +	press_state->callbacks.pdev = pdev; +	ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE, +					&press_state->callbacks); +	if (ret < 0) { +		dev_err(&pdev->dev, "callback reg failed\n"); +		goto error_iio_unreg; +	} + +	return ret; + +error_iio_unreg: +	iio_device_unregister(indio_dev); +error_remove_trigger: +	hid_sensor_remove_trigger(&press_state->common_attributes); +error_unreg_buffer_funcs: +	iio_triggered_buffer_cleanup(indio_dev); +error_free_dev_mem: +	kfree(indio_dev->channels); +	return ret; +} + +/* Function to deinitialize the processing for usage id */ +static int hid_press_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 press_state *press_state = iio_priv(indio_dev); + +	sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE); +	iio_device_unregister(indio_dev); +	hid_sensor_remove_trigger(&press_state->common_attributes); +	iio_triggered_buffer_cleanup(indio_dev); +	kfree(indio_dev->channels); + +	return 0; +} + +static struct platform_device_id hid_press_ids[] = { +	{ +		/* Format: HID-SENSOR-usage_id_in_hex_lowercase */ +		.name = "HID-SENSOR-200031", +	}, +	{ /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, hid_press_ids); + +static struct platform_driver hid_press_platform_driver = { +	.id_table = hid_press_ids, +	.driver = { +		.name	= KBUILD_MODNAME, +		.owner	= THIS_MODULE, +	}, +	.probe		= hid_press_probe, +	.remove		= hid_press_remove, +}; +module_platform_driver(hid_press_platform_driver); + +MODULE_DESCRIPTION("HID Sensor Pressure"); +MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); +MODULE_LICENSE("GPL"); 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/mpl3115.c b/drivers/iio/pressure/mpl3115.c new file mode 100644 index 00000000000..01b2e0b1887 --- /dev/null +++ b/drivers/iio/pressure/mpl3115.c @@ -0,0 +1,329 @@ +/* + * mpl3115.c - Support for Freescale MPL3115A2 pressure/temperature sensor + * + * Copyright (c) 2013 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: FIFO buffer, altimeter mode, oversampling, continuous mode, + * interrupts, user offset correction, raw mode + */ + +#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 MPL3115_STATUS 0x00 +#define MPL3115_OUT_PRESS 0x01 /* MSB first, 20 bit */ +#define MPL3115_OUT_TEMP 0x04 /* MSB first, 12 bit */ +#define MPL3115_WHO_AM_I 0x0c +#define MPL3115_CTRL_REG1 0x26 + +#define MPL3115_DEVICE_ID 0xc4 + +#define MPL3115_STATUS_PRESS_RDY BIT(2) +#define MPL3115_STATUS_TEMP_RDY BIT(1) + +#define MPL3115_CTRL_RESET BIT(2) /* software reset */ +#define MPL3115_CTRL_OST BIT(1) /* initiate measurement */ +#define MPL3115_CTRL_ACTIVE BIT(0) /* continuous measurement */ +#define MPL3115_CTRL_OS_258MS (BIT(5) | BIT(4)) /* 64x oversampling */ + +struct mpl3115_data { +	struct i2c_client *client; +	struct mutex lock; +	u8 ctrl_reg1; +}; + +static int mpl3115_request(struct mpl3115_data *data) +{ +	int ret, tries = 15; + +	/* trigger measurement */ +	ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, +		data->ctrl_reg1 | MPL3115_CTRL_OST); +	if (ret < 0) +		return ret; + +	while (tries-- > 0) { +		ret = i2c_smbus_read_byte_data(data->client, MPL3115_CTRL_REG1); +		if (ret < 0) +			return ret; +		/* wait for data ready, i.e. OST cleared */ +		if (!(ret & MPL3115_CTRL_OST)) +			break; +		msleep(20); +	} + +	if (tries < 0) { +		dev_err(&data->client->dev, "data not ready\n"); +		return -EIO; +	} + +	return 0; +} + +static int mpl3115_read_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int *val, int *val2, long mask) +{ +	struct mpl3115_data *data = iio_priv(indio_dev); +	__be32 tmp = 0; +	int ret; + +	switch (mask) { +	case IIO_CHAN_INFO_RAW: +		if (iio_buffer_enabled(indio_dev)) +			return -EBUSY; + +		switch (chan->type) { +		case IIO_PRESSURE: /* in 0.25 pascal / LSB */ +			mutex_lock(&data->lock); +			ret = mpl3115_request(data); +			if (ret < 0) { +				mutex_unlock(&data->lock); +				return ret; +			} +			ret = i2c_smbus_read_i2c_block_data(data->client, +				MPL3115_OUT_PRESS, 3, (u8 *) &tmp); +			mutex_unlock(&data->lock); +			if (ret < 0) +				return ret; +			*val = be32_to_cpu(tmp) >> 12; +			return IIO_VAL_INT; +		case IIO_TEMP: /* in 0.0625 celsius / LSB */ +			mutex_lock(&data->lock); +			ret = mpl3115_request(data); +			if (ret < 0) { +				mutex_unlock(&data->lock); +				return ret; +			} +			ret = i2c_smbus_read_i2c_block_data(data->client, +				MPL3115_OUT_TEMP, 2, (u8 *) &tmp); +			mutex_unlock(&data->lock); +			if (ret < 0) +				return ret; +			*val = sign_extend32(be32_to_cpu(tmp) >> 20, 11); +			return IIO_VAL_INT; +		default: +			return -EINVAL; +		} +	case IIO_CHAN_INFO_SCALE: +		switch (chan->type) { +		case IIO_PRESSURE: +			*val = 0; +			*val2 = 250; /* want kilopascal */ +			return IIO_VAL_INT_PLUS_MICRO; +		case IIO_TEMP: +			*val = 0; +			*val2 = 62500; +			return IIO_VAL_INT_PLUS_MICRO; +		default: +			return -EINVAL; +		} +	} +	return -EINVAL; +} + +static irqreturn_t mpl3115_trigger_handler(int irq, void *p) +{ +	struct iio_poll_func *pf = p; +	struct iio_dev *indio_dev = pf->indio_dev; +	struct mpl3115_data *data = iio_priv(indio_dev); +	u8 buffer[16]; /* 32-bit channel + 16-bit channel + padding + ts */ +	int ret, pos = 0; + +	mutex_lock(&data->lock); +	ret = mpl3115_request(data); +	if (ret < 0) { +		mutex_unlock(&data->lock); +		goto done; +	} + +	memset(buffer, 0, sizeof(buffer)); +	if (test_bit(0, indio_dev->active_scan_mask)) { +		ret = i2c_smbus_read_i2c_block_data(data->client, +			MPL3115_OUT_PRESS, 3, &buffer[pos]); +		if (ret < 0) { +			mutex_unlock(&data->lock); +			goto done; +		} +		pos += 4; +	} + +	if (test_bit(1, indio_dev->active_scan_mask)) { +		ret = i2c_smbus_read_i2c_block_data(data->client, +			MPL3115_OUT_TEMP, 2, &buffer[pos]); +		if (ret < 0) { +			mutex_unlock(&data->lock); +			goto done; +		} +	} +	mutex_unlock(&data->lock); + +	iio_push_to_buffers_with_timestamp(indio_dev, buffer, +		iio_get_time_ns()); + +done: +	iio_trigger_notify_done(indio_dev->trig); +	return IRQ_HANDLED; +} + +static const struct iio_chan_spec mpl3115_channels[] = { +	{ +		.type = IIO_PRESSURE, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			BIT(IIO_CHAN_INFO_SCALE), +		.scan_index = 0, +		.scan_type = { +			.sign = 'u', +			.realbits = 20, +			.storagebits = 32, +			.shift = 12, +			.endianness = IIO_BE, +		} +	}, +	{ +		.type = IIO_TEMP, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +			BIT(IIO_CHAN_INFO_SCALE), +		.scan_index = 1, +		.scan_type = { +			.sign = 's', +			.realbits = 12, +			.storagebits = 16, +			.shift = 4, +			.endianness = IIO_BE, +		} +	}, +	IIO_CHAN_SOFT_TIMESTAMP(2), +}; + +static const struct iio_info mpl3115_info = { +	.read_raw = &mpl3115_read_raw, +	.driver_module = THIS_MODULE, +}; + +static int mpl3115_probe(struct i2c_client *client, +			 const struct i2c_device_id *id) +{ +	struct mpl3115_data *data; +	struct iio_dev *indio_dev; +	int ret; + +	ret = i2c_smbus_read_byte_data(client, MPL3115_WHO_AM_I); +	if (ret < 0) +		return ret; +	if (ret != MPL3115_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 = &mpl3115_info; +	indio_dev->name = id->name; +	indio_dev->dev.parent = &client->dev; +	indio_dev->modes = INDIO_DIRECT_MODE; +	indio_dev->channels = mpl3115_channels; +	indio_dev->num_channels = ARRAY_SIZE(mpl3115_channels); + +	/* software reset, I2C transfer is aborted (fails) */ +	i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1, +		MPL3115_CTRL_RESET); +	msleep(50); + +	data->ctrl_reg1 = MPL3115_CTRL_OS_258MS; +	ret = i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1, +		data->ctrl_reg1); +	if (ret < 0) +		return ret; + +	ret = iio_triggered_buffer_setup(indio_dev, NULL, +		mpl3115_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 mpl3115_standby(struct mpl3115_data *data) +{ +	return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, +		data->ctrl_reg1 & ~MPL3115_CTRL_ACTIVE); +} + +static int mpl3115_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); +	mpl3115_standby(iio_priv(indio_dev)); + +	return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mpl3115_suspend(struct device *dev) +{ +	return mpl3115_standby(iio_priv(i2c_get_clientdata( +		to_i2c_client(dev)))); +} + +static int mpl3115_resume(struct device *dev) +{ +	struct mpl3115_data *data = iio_priv(i2c_get_clientdata( +		to_i2c_client(dev))); + +	return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1, +		data->ctrl_reg1); +} + +static SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend, mpl3115_resume); +#define MPL3115_PM_OPS (&mpl3115_pm_ops) +#else +#define MPL3115_PM_OPS NULL +#endif + +static const struct i2c_device_id mpl3115_id[] = { +	{ "mpl3115", 0 }, +	{ } +}; +MODULE_DEVICE_TABLE(i2c, mpl3115_id); + +static struct i2c_driver mpl3115_driver = { +	.driver = { +		.name	= "mpl3115", +		.pm	= MPL3115_PM_OPS, +	}, +	.probe = mpl3115_probe, +	.remove = mpl3115_remove, +	.id_table = mpl3115_id, +}; +module_i2c_driver(mpl3115_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); +MODULE_DESCRIPTION("Freescale MPL3115 pressure/temperature driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index b0b630688da..242943c0c4e 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -14,6 +14,8 @@  #include <linux/types.h>  #include <linux/iio/common/st_sensors.h> +#define LPS001WP_PRESS_DEV_NAME		"lps001wp" +#define LPS25H_PRESS_DEV_NAME		"lps25h"  #define LPS331AP_PRESS_DEV_NAME		"lps331ap"  /** diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c index f877ef8af52..b37b1c9ac93 100644 --- a/drivers/iio/pressure/st_pressure_buffer.c +++ b/drivers/iio/pressure/st_pressure_buffer.c @@ -32,16 +32,7 @@ int st_press_trig_set_state(struct iio_trigger *trig, bool state)  static int st_press_buffer_preenable(struct iio_dev *indio_dev)  { -	int err; - -	err = st_sensors_set_enable(indio_dev, true); -	if (err < 0) -		goto st_press_set_enable_error; - -	err = iio_sw_buffer_preenable(indio_dev); - -st_press_set_enable_error: -	return err; +	return st_sensors_set_enable(indio_dev, true);  }  static int st_press_buffer_postenable(struct iio_dev *indio_dev) diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index ceebd3c2789..cd7e01f3a93 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -36,94 +36,273 @@  						 ST_PRESS_LSB_PER_CELSIUS)  #define ST_PRESS_NUMBER_DATA_CHANNELS		1 -/* DEFAULT VALUE FOR SENSORS */ -#define ST_PRESS_DEFAULT_OUT_XL_ADDR		0x28 -#define ST_TEMP_DEFAULT_OUT_L_ADDR		0x2b -  /* FULLSCALE */  #define ST_PRESS_FS_AVL_1260MB			1260 -/* CUSTOM VALUES FOR SENSOR 1 */ -#define ST_PRESS_1_WAI_EXP			0xbb -#define ST_PRESS_1_ODR_ADDR			0x20 -#define ST_PRESS_1_ODR_MASK			0x70 -#define ST_PRESS_1_ODR_AVL_1HZ_VAL		0x01 -#define ST_PRESS_1_ODR_AVL_7HZ_VAL		0x05 -#define ST_PRESS_1_ODR_AVL_13HZ_VAL		0x06 -#define ST_PRESS_1_ODR_AVL_25HZ_VAL		0x07 -#define ST_PRESS_1_PW_ADDR			0x20 -#define ST_PRESS_1_PW_MASK			0x80 -#define ST_PRESS_1_FS_ADDR			0x23 -#define ST_PRESS_1_FS_MASK			0x30 -#define ST_PRESS_1_FS_AVL_1260_VAL		0x00 -#define ST_PRESS_1_FS_AVL_1260_GAIN		ST_PRESS_KPASCAL_NANO_SCALE -#define ST_PRESS_1_FS_AVL_TEMP_GAIN		ST_PRESS_CELSIUS_NANO_SCALE -#define ST_PRESS_1_BDU_ADDR			0x20 -#define ST_PRESS_1_BDU_MASK			0x04 -#define ST_PRESS_1_DRDY_IRQ_ADDR		0x22 -#define ST_PRESS_1_DRDY_IRQ_INT1_MASK		0x04 -#define ST_PRESS_1_DRDY_IRQ_INT2_MASK		0x20 -#define ST_PRESS_1_MULTIREAD_BIT		true -#define ST_PRESS_1_TEMP_OFFSET			42500 - -static const struct iio_chan_spec st_press_channels[] = { -	ST_SENSORS_LSM_CHANNELS(IIO_PRESSURE, +#define ST_PRESS_1_OUT_XL_ADDR			0x28 +#define ST_TEMP_1_OUT_L_ADDR			0x2b + +/* CUSTOM VALUES FOR LPS331AP SENSOR */ +#define ST_PRESS_LPS331AP_WAI_EXP		0xbb +#define ST_PRESS_LPS331AP_ODR_ADDR		0x20 +#define ST_PRESS_LPS331AP_ODR_MASK		0x70 +#define ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL	0x01 +#define ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL	0x05 +#define ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL	0x06 +#define ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL	0x07 +#define ST_PRESS_LPS331AP_PW_ADDR		0x20 +#define ST_PRESS_LPS331AP_PW_MASK		0x80 +#define ST_PRESS_LPS331AP_FS_ADDR		0x23 +#define ST_PRESS_LPS331AP_FS_MASK		0x30 +#define ST_PRESS_LPS331AP_FS_AVL_1260_VAL	0x00 +#define ST_PRESS_LPS331AP_FS_AVL_1260_GAIN	ST_PRESS_KPASCAL_NANO_SCALE +#define ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN	ST_PRESS_CELSIUS_NANO_SCALE +#define ST_PRESS_LPS331AP_BDU_ADDR		0x20 +#define ST_PRESS_LPS331AP_BDU_MASK		0x04 +#define ST_PRESS_LPS331AP_DRDY_IRQ_ADDR		0x22 +#define ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK	0x04 +#define ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK	0x20 +#define ST_PRESS_LPS331AP_MULTIREAD_BIT		true +#define ST_PRESS_LPS331AP_TEMP_OFFSET		42500 + +/* CUSTOM VALUES FOR LPS001WP SENSOR */ +#define ST_PRESS_LPS001WP_WAI_EXP		0xba +#define ST_PRESS_LPS001WP_ODR_ADDR		0x20 +#define ST_PRESS_LPS001WP_ODR_MASK		0x30 +#define ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL	0x01 +#define ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL	0x02 +#define ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL	0x03 +#define ST_PRESS_LPS001WP_PW_ADDR		0x20 +#define ST_PRESS_LPS001WP_PW_MASK		0x40 +#define ST_PRESS_LPS001WP_BDU_ADDR		0x20 +#define ST_PRESS_LPS001WP_BDU_MASK		0x04 +#define ST_PRESS_LPS001WP_MULTIREAD_BIT		true +#define ST_PRESS_LPS001WP_OUT_L_ADDR		0x28 +#define ST_TEMP_LPS001WP_OUT_L_ADDR		0x2a + +/* CUSTOM VALUES FOR LPS25H SENSOR */ +#define ST_PRESS_LPS25H_WAI_EXP			0xbd +#define ST_PRESS_LPS25H_ODR_ADDR		0x20 +#define ST_PRESS_LPS25H_ODR_MASK		0x70 +#define ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL		0x01 +#define ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL		0x02 +#define ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL	0x03 +#define ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL	0x04 +#define ST_PRESS_LPS25H_PW_ADDR			0x20 +#define ST_PRESS_LPS25H_PW_MASK			0x80 +#define ST_PRESS_LPS25H_FS_ADDR			0x00 +#define ST_PRESS_LPS25H_FS_MASK			0x00 +#define ST_PRESS_LPS25H_FS_AVL_1260_VAL		0x00 +#define ST_PRESS_LPS25H_FS_AVL_1260_GAIN	ST_PRESS_KPASCAL_NANO_SCALE +#define ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN	ST_PRESS_CELSIUS_NANO_SCALE +#define ST_PRESS_LPS25H_BDU_ADDR		0x20 +#define ST_PRESS_LPS25H_BDU_MASK		0x04 +#define ST_PRESS_LPS25H_DRDY_IRQ_ADDR		0x23 +#define ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK	0x01 +#define ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK	0x10 +#define ST_PRESS_LPS25H_MULTIREAD_BIT		true +#define ST_PRESS_LPS25H_TEMP_OFFSET		42500 +#define ST_PRESS_LPS25H_OUT_XL_ADDR		0x28 +#define ST_TEMP_LPS25H_OUT_L_ADDR		0x2b + +static const struct iio_chan_spec st_press_1_channels[] = { +	{ +		.type = IIO_PRESSURE, +		.channel2 = IIO_NO_MOD, +		.address = ST_PRESS_1_OUT_XL_ADDR, +		.scan_index = ST_SENSORS_SCAN_X, +		.scan_type = { +			.sign = 'u', +			.realbits = 24, +			.storagebits = 24, +			.endianness = IIO_LE, +		}, +		.info_mask_separate =  			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), -			ST_SENSORS_SCAN_X, 0, IIO_NO_MOD, 'u', IIO_LE, 24, 24, -			ST_PRESS_DEFAULT_OUT_XL_ADDR), -	ST_SENSORS_LSM_CHANNELS(IIO_TEMP, -			BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE) | -						BIT(IIO_CHAN_INFO_OFFSET), -			-1, 0, IIO_NO_MOD, 's', IIO_LE, 16, 16, -			ST_TEMP_DEFAULT_OUT_L_ADDR), +		.modified = 0, +	}, +	{ +		.type = IIO_TEMP, +		.channel2 = IIO_NO_MOD, +		.address = ST_TEMP_1_OUT_L_ADDR, +		.scan_index = -1, +		.scan_type = { +			.sign = 'u', +			.realbits = 16, +			.storagebits = 16, +			.endianness = IIO_LE, +		}, +		.info_mask_separate = +			BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_SCALE) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.modified = 0, +	}, +	IIO_CHAN_SOFT_TIMESTAMP(1) +}; + +static const struct iio_chan_spec st_press_lps001wp_channels[] = { +	{ +		.type = IIO_PRESSURE, +		.channel2 = IIO_NO_MOD, +		.address = ST_PRESS_LPS001WP_OUT_L_ADDR, +		.scan_index = ST_SENSORS_SCAN_X, +		.scan_type = { +			.sign = 'u', +			.realbits = 16, +			.storagebits = 16, +			.endianness = IIO_LE, +		}, +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), +		.modified = 0, +	}, +	{ +		.type = IIO_TEMP, +		.channel2 = IIO_NO_MOD, +		.address = ST_TEMP_LPS001WP_OUT_L_ADDR, +		.scan_index = -1, +		.scan_type = { +			.sign = 'u', +			.realbits = 16, +			.storagebits = 16, +			.endianness = IIO_LE, +		}, +		.info_mask_separate = +			BIT(IIO_CHAN_INFO_RAW) | +			BIT(IIO_CHAN_INFO_OFFSET), +		.modified = 0, +	},  	IIO_CHAN_SOFT_TIMESTAMP(1)  };  static const struct st_sensors st_press_sensors[] = {  	{ -		.wai = ST_PRESS_1_WAI_EXP, +		.wai = ST_PRESS_LPS331AP_WAI_EXP,  		.sensors_supported = {  			[0] = LPS331AP_PRESS_DEV_NAME,  		}, -		.ch = (struct iio_chan_spec *)st_press_channels, +		.ch = (struct iio_chan_spec *)st_press_1_channels, +		.num_ch = ARRAY_SIZE(st_press_1_channels),  		.odr = { -			.addr = ST_PRESS_1_ODR_ADDR, -			.mask = ST_PRESS_1_ODR_MASK, +			.addr = ST_PRESS_LPS331AP_ODR_ADDR, +			.mask = ST_PRESS_LPS331AP_ODR_MASK,  			.odr_avl = { -				{ 1, ST_PRESS_1_ODR_AVL_1HZ_VAL, }, -				{ 7, ST_PRESS_1_ODR_AVL_7HZ_VAL, }, -				{ 13, ST_PRESS_1_ODR_AVL_13HZ_VAL, }, -				{ 25, ST_PRESS_1_ODR_AVL_25HZ_VAL, }, +				{ 1, ST_PRESS_LPS331AP_ODR_AVL_1HZ_VAL, }, +				{ 7, ST_PRESS_LPS331AP_ODR_AVL_7HZ_VAL, }, +				{ 13, ST_PRESS_LPS331AP_ODR_AVL_13HZ_VAL, }, +				{ 25, ST_PRESS_LPS331AP_ODR_AVL_25HZ_VAL, },  			},  		},  		.pw = { -			.addr = ST_PRESS_1_PW_ADDR, -			.mask = ST_PRESS_1_PW_MASK, +			.addr = ST_PRESS_LPS331AP_PW_ADDR, +			.mask = ST_PRESS_LPS331AP_PW_MASK,  			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,  			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,  		},  		.fs = { -			.addr = ST_PRESS_1_FS_ADDR, -			.mask = ST_PRESS_1_FS_MASK, +			.addr = ST_PRESS_LPS331AP_FS_ADDR, +			.mask = ST_PRESS_LPS331AP_FS_MASK,  			.fs_avl = {  				[0] = {  					.num = ST_PRESS_FS_AVL_1260MB, -					.value = ST_PRESS_1_FS_AVL_1260_VAL, -					.gain = ST_PRESS_1_FS_AVL_1260_GAIN, -					.gain2 = ST_PRESS_1_FS_AVL_TEMP_GAIN, +					.value = ST_PRESS_LPS331AP_FS_AVL_1260_VAL, +					.gain = ST_PRESS_LPS331AP_FS_AVL_1260_GAIN, +					.gain2 = ST_PRESS_LPS331AP_FS_AVL_TEMP_GAIN,  				},  			},  		},  		.bdu = { -			.addr = ST_PRESS_1_BDU_ADDR, -			.mask = ST_PRESS_1_BDU_MASK, +			.addr = ST_PRESS_LPS331AP_BDU_ADDR, +			.mask = ST_PRESS_LPS331AP_BDU_MASK,  		},  		.drdy_irq = { -			.addr = ST_PRESS_1_DRDY_IRQ_ADDR, -			.mask_int1 = ST_PRESS_1_DRDY_IRQ_INT1_MASK, -			.mask_int2 = ST_PRESS_1_DRDY_IRQ_INT2_MASK, +			.addr = ST_PRESS_LPS331AP_DRDY_IRQ_ADDR, +			.mask_int1 = ST_PRESS_LPS331AP_DRDY_IRQ_INT1_MASK, +			.mask_int2 = ST_PRESS_LPS331AP_DRDY_IRQ_INT2_MASK,  		}, -		.multi_read_bit = ST_PRESS_1_MULTIREAD_BIT, +		.multi_read_bit = ST_PRESS_LPS331AP_MULTIREAD_BIT, +		.bootime = 2, +	}, +	{ +		.wai = ST_PRESS_LPS001WP_WAI_EXP, +		.sensors_supported = { +			[0] = LPS001WP_PRESS_DEV_NAME, +		}, +		.ch = (struct iio_chan_spec *)st_press_lps001wp_channels, +		.num_ch = ARRAY_SIZE(st_press_lps001wp_channels), +		.odr = { +			.addr = ST_PRESS_LPS001WP_ODR_ADDR, +			.mask = ST_PRESS_LPS001WP_ODR_MASK, +			.odr_avl = { +				{ 1, ST_PRESS_LPS001WP_ODR_AVL_1HZ_VAL, }, +				{ 7, ST_PRESS_LPS001WP_ODR_AVL_7HZ_VAL, }, +				{ 13, ST_PRESS_LPS001WP_ODR_AVL_13HZ_VAL, }, +			}, +		}, +		.pw = { +			.addr = ST_PRESS_LPS001WP_PW_ADDR, +			.mask = ST_PRESS_LPS001WP_PW_MASK, +			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, +			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, +		}, +		.fs = { +			.addr = 0, +		}, +		.bdu = { +			.addr = ST_PRESS_LPS001WP_BDU_ADDR, +			.mask = ST_PRESS_LPS001WP_BDU_MASK, +		}, +		.drdy_irq = { +			.addr = 0, +		}, +		.multi_read_bit = ST_PRESS_LPS001WP_MULTIREAD_BIT, +		.bootime = 2, +	}, +	{ +		.wai = ST_PRESS_LPS25H_WAI_EXP, +		.sensors_supported = { +			[0] = LPS25H_PRESS_DEV_NAME, +		}, +		.ch = (struct iio_chan_spec *)st_press_1_channels, +		.num_ch = ARRAY_SIZE(st_press_1_channels), +		.odr = { +			.addr = ST_PRESS_LPS25H_ODR_ADDR, +			.mask = ST_PRESS_LPS25H_ODR_MASK, +			.odr_avl = { +				{ 1, ST_PRESS_LPS25H_ODR_AVL_1HZ_VAL, }, +				{ 7, ST_PRESS_LPS25H_ODR_AVL_7HZ_VAL, }, +				{ 13, ST_PRESS_LPS25H_ODR_AVL_13HZ_VAL, }, +				{ 25, ST_PRESS_LPS25H_ODR_AVL_25HZ_VAL, }, +			}, +		}, +		.pw = { +			.addr = ST_PRESS_LPS25H_PW_ADDR, +			.mask = ST_PRESS_LPS25H_PW_MASK, +			.value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE, +			.value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, +		}, +		.fs = { +			.addr = ST_PRESS_LPS25H_FS_ADDR, +			.mask = ST_PRESS_LPS25H_FS_MASK, +			.fs_avl = { +				[0] = { +					.num = ST_PRESS_FS_AVL_1260MB, +					.value = ST_PRESS_LPS25H_FS_AVL_1260_VAL, +					.gain = ST_PRESS_LPS25H_FS_AVL_1260_GAIN, +					.gain2 = ST_PRESS_LPS25H_FS_AVL_TEMP_GAIN, +				}, +			}, +		}, +		.bdu = { +			.addr = ST_PRESS_LPS25H_BDU_ADDR, +			.mask = ST_PRESS_LPS25H_BDU_MASK, +		}, +		.drdy_irq = { +			.addr = ST_PRESS_LPS25H_DRDY_IRQ_ADDR, +			.mask_int1 = ST_PRESS_LPS25H_DRDY_IRQ_INT1_MASK, +			.mask_int2 = ST_PRESS_LPS25H_DRDY_IRQ_INT2_MASK, +		}, +		.multi_read_bit = ST_PRESS_LPS25H_MULTIREAD_BIT,  		.bootime = 2,  	},  }; @@ -210,41 +389,48 @@ static const struct iio_trigger_ops st_press_trigger_ops = {  int st_press_common_probe(struct iio_dev *indio_dev,  				struct st_sensors_platform_data *plat_data)  { -	int err;  	struct st_sensor_data *pdata = iio_priv(indio_dev); +	int irq = pdata->get_irq_data_ready(indio_dev); +	int err;  	indio_dev->modes = INDIO_DIRECT_MODE;  	indio_dev->info = &press_info; +	st_sensors_power_enable(indio_dev); +  	err = st_sensors_check_device_support(indio_dev, -				ARRAY_SIZE(st_press_sensors), st_press_sensors); +					      ARRAY_SIZE(st_press_sensors), +					      st_press_sensors);  	if (err < 0) -		goto st_press_common_probe_error; +		return err;  	pdata->num_data_channels = ST_PRESS_NUMBER_DATA_CHANNELS; -	pdata->multiread_bit = pdata->sensor->multi_read_bit; -	indio_dev->channels = pdata->sensor->ch; -	indio_dev->num_channels = ARRAY_SIZE(st_press_channels); +	pdata->multiread_bit     = pdata->sensor->multi_read_bit; +	indio_dev->channels      = pdata->sensor->ch; +	indio_dev->num_channels  = pdata->sensor->num_ch; + +	if (pdata->sensor->fs.addr != 0) +		pdata->current_fullscale = (struct st_sensor_fullscale_avl *) +			&pdata->sensor->fs.fs_avl[0]; -	pdata->current_fullscale = (struct st_sensor_fullscale_avl *) -						&pdata->sensor->fs.fs_avl[0];  	pdata->odr = pdata->sensor->odr.odr_avl[0].hz; -	if (!plat_data) +	/* Some devices don't support a data ready pin. */ +	if (!plat_data && pdata->sensor->drdy_irq.addr)  		plat_data =  			(struct st_sensors_platform_data *)&default_press_pdata;  	err = st_sensors_init_sensor(indio_dev, plat_data);  	if (err < 0) -		goto st_press_common_probe_error; +		return err; -	if (pdata->get_irq_data_ready(indio_dev) > 0) { -		err = st_press_allocate_ring(indio_dev); -		if (err < 0) -			goto st_press_common_probe_error; +	err = st_press_allocate_ring(indio_dev); +	if (err < 0) +		return err; +	if (irq > 0) {  		err = st_sensors_allocate_trigger(indio_dev, -							ST_PRESS_TRIGGER_OPS); +						  ST_PRESS_TRIGGER_OPS);  		if (err < 0)  			goto st_press_probe_trigger_error;  	} @@ -253,15 +439,17 @@ 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: -	if (pdata->get_irq_data_ready(indio_dev) > 0) +	if (irq > 0)  		st_sensors_deallocate_trigger(indio_dev);  st_press_probe_trigger_error: -	if (pdata->get_irq_data_ready(indio_dev) > 0) -		st_press_deallocate_ring(indio_dev); -st_press_common_probe_error: +	st_press_deallocate_ring(indio_dev); +  	return err;  }  EXPORT_SYMBOL(st_press_common_probe); @@ -270,11 +458,13 @@ void st_press_common_remove(struct iio_dev *indio_dev)  {  	struct st_sensor_data *pdata = iio_priv(indio_dev); +	st_sensors_power_disable(indio_dev); +  	iio_device_unregister(indio_dev); -	if (pdata->get_irq_data_ready(indio_dev) > 0) { +	if (pdata->get_irq_data_ready(indio_dev) > 0)  		st_sensors_deallocate_trigger(indio_dev); -		st_press_deallocate_ring(indio_dev); -	} + +	st_press_deallocate_ring(indio_dev);  }  EXPORT_SYMBOL(st_press_common_remove); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 08aac5e6251..3cd73e39b84 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -49,6 +49,8 @@ static int st_press_i2c_remove(struct i2c_client *client)  }  static const struct i2c_device_id st_press_id_table[] = { +	{ LPS001WP_PRESS_DEV_NAME }, +	{ LPS25H_PRESS_DEV_NAME },  	{ LPS331AP_PRESS_DEV_NAME },  	{},  }; diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index 399a29b6017..f45d430ec52 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -48,6 +48,8 @@ static int st_press_spi_remove(struct spi_device *spi)  }  static const struct spi_device_id st_press_id_table[] = { +	{ LPS001WP_PRESS_DEV_NAME }, +	{ LPS25H_PRESS_DEV_NAME },  	{ LPS331AP_PRESS_DEV_NAME },  	{},  }; 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"); diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c index 6d63883da1a..84a0789c3d9 100644 --- a/drivers/iio/temperature/tmp006.c +++ b/drivers/iio/temperature/tmp006.c @@ -70,12 +70,16 @@ static int tmp006_read_measurement(struct tmp006_data *data, u8 reg)  	return i2c_smbus_read_word_swapped(data->client, reg);  } +static const int tmp006_freqs[5][2] = { {4, 0}, {2, 0}, {1, 0}, +					{0, 500000}, {0, 250000} }; +  static int tmp006_read_raw(struct iio_dev *indio_dev,  			    struct iio_chan_spec const *channel, int *val,  			    int *val2, long mask)  {  	struct tmp006_data *data = iio_priv(indio_dev);  	s32 ret; +	int cr;  	switch (mask) {  	case IIO_CHAN_INFO_RAW: @@ -106,6 +110,12 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,  			break;  		}  		return IIO_VAL_INT_PLUS_MICRO; +	case IIO_CHAN_INFO_SAMP_FREQ: +		cr = (data->config & TMP006_CONFIG_CR_MASK) +			>> TMP006_CONFIG_CR_SHIFT; +		*val = tmp006_freqs[cr][0]; +		*val2 = tmp006_freqs[cr][1]; +		return IIO_VAL_INT_PLUS_MICRO;  	default:  		break;  	} @@ -113,48 +123,32 @@ static int tmp006_read_raw(struct iio_dev *indio_dev,  	return -EINVAL;  } -static const char * const tmp006_freqs[] = { "4", "2", "1", "0.5", "0.25" }; - -static ssize_t tmp006_show_freq(struct device *dev, -				struct device_attribute *attr, char *buf) -{ -	struct tmp006_data *data = iio_priv(dev_to_iio_dev(dev)); -	int cr = (data->config & TMP006_CONFIG_CR_MASK) -		>> TMP006_CONFIG_CR_SHIFT; -	return sprintf(buf, "%s\n", tmp006_freqs[cr]); -} - -static ssize_t tmp006_store_freq(struct device *dev, -				 struct device_attribute *attr, -				 const char *buf, size_t len) +static int tmp006_write_raw(struct iio_dev *indio_dev, +			    struct iio_chan_spec const *chan, +			    int val, +			    int val2, +			    long mask)  { -	struct iio_dev *indio_dev = dev_to_iio_dev(dev);  	struct tmp006_data *data = iio_priv(indio_dev);  	int i; -	bool found = false;  	for (i = 0; i < ARRAY_SIZE(tmp006_freqs); i++) -		if (sysfs_streq(buf, tmp006_freqs[i])) { -			found = true; -			break; -		} -	if (!found) -		return -EINVAL; +		if ((val == tmp006_freqs[i][0]) && +		    (val2 == tmp006_freqs[i][1])) { +			data->config &= ~TMP006_CONFIG_CR_MASK; +			data->config |= i << TMP006_CONFIG_CR_SHIFT; -	data->config &= ~TMP006_CONFIG_CR_MASK; -	data->config |= i << TMP006_CONFIG_CR_SHIFT; +			return i2c_smbus_write_word_swapped(data->client, +							    TMP006_CONFIG, +							    data->config); -	return i2c_smbus_write_word_swapped(data->client, TMP006_CONFIG, -		data->config); +		} +	return -EINVAL;  } -static IIO_DEV_ATTR_SAMP_FREQ(S_IRUGO | S_IWUSR, -			tmp006_show_freq, tmp006_store_freq); -  static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25");  static struct attribute *tmp006_attributes[] = { -	&iio_dev_attr_sampling_frequency.dev_attr.attr,  	&iio_const_attr_sampling_frequency_available.dev_attr.attr,  	NULL  }; @@ -168,16 +162,19 @@ static const struct iio_chan_spec tmp006_channels[] = {  		.type = IIO_VOLTAGE,  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  			BIT(IIO_CHAN_INFO_SCALE), +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),  	},  	{  		.type = IIO_TEMP,  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |  			BIT(IIO_CHAN_INFO_SCALE), +		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),  	}  };  static const struct iio_info tmp006_info = {  	.read_raw = tmp006_read_raw, +	.write_raw = tmp006_write_raw,  	.attrs = &tmp006_attribute_group,  	.driver_module = THIS_MODULE,  }; diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index effcd0ac98d..15e3b850f51 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -23,7 +23,7 @@ struct iio_sysfs_trig {  };  static LIST_HEAD(iio_sysfs_trig_list); -static DEFINE_MUTEX(iio_syfs_trig_list_mut); +static DEFINE_MUTEX(iio_sysfs_trig_list_mut);  static int iio_sysfs_trigger_probe(int id);  static ssize_t iio_sysfs_trig_add(struct device *dev, @@ -135,7 +135,7 @@ static int iio_sysfs_trigger_probe(int id)  	struct iio_sysfs_trig *t;  	int ret;  	bool foundit = false; -	mutex_lock(&iio_syfs_trig_list_mut); +	mutex_lock(&iio_sysfs_trig_list_mut);  	list_for_each_entry(t, &iio_sysfs_trig_list, l)  		if (id == t->id) {  			foundit = true; @@ -169,7 +169,7 @@ static int iio_sysfs_trigger_probe(int id)  		goto out2;  	list_add(&t->l, &iio_sysfs_trig_list);  	__module_get(THIS_MODULE); -	mutex_unlock(&iio_syfs_trig_list_mut); +	mutex_unlock(&iio_sysfs_trig_list_mut);  	return 0;  out2: @@ -177,7 +177,7 @@ out2:  free_t:  	kfree(t);  out1: -	mutex_unlock(&iio_syfs_trig_list_mut); +	mutex_unlock(&iio_sysfs_trig_list_mut);  	return ret;  } @@ -185,14 +185,14 @@ static int iio_sysfs_trigger_remove(int id)  {  	bool foundit = false;  	struct iio_sysfs_trig *t; -	mutex_lock(&iio_syfs_trig_list_mut); +	mutex_lock(&iio_sysfs_trig_list_mut);  	list_for_each_entry(t, &iio_sysfs_trig_list, l)  		if (id == t->id) {  			foundit = true;  			break;  		}  	if (!foundit) { -		mutex_unlock(&iio_syfs_trig_list_mut); +		mutex_unlock(&iio_sysfs_trig_list_mut);  		return -EINVAL;  	} @@ -202,7 +202,7 @@ static int iio_sysfs_trigger_remove(int id)  	list_del(&t->l);  	kfree(t);  	module_put(THIS_MODULE); -	mutex_unlock(&iio_syfs_trig_list_mut); +	mutex_unlock(&iio_sysfs_trig_list_mut);  	return 0;  }  | 
