All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V4] TAOS tsl2x7x
@ 2012-03-21 16:16 ` Jon Brenner
  0 siblings, 0 replies; 8+ messages in thread
From: Jon Brenner @ 2012-03-21 16:16 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel

TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families (inc. all variants).

Signed-off-by: Jon Brenner <jbrenner@taosinc.com>
---
 .../light/sysfs-bus-iio-light-tsl2583              |    6 +
 .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
 drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
 .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
 .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
 drivers/staging/iio/light/Kconfig                  |    8 +
 drivers/staging/iio/light/Makefile                 |    1 +
 drivers/staging/iio/light/tsl2x7x_core.c           | 1911 ++++++++++++++++++++
 drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
 9 files changed, 2032 insertions(+), 24 deletions(-)

diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
new file mode 100644
index 0000000..8f2a038
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
@@ -0,0 +1,6 @@
+What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion:	2.6.37
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This property causes an internal calibration of the als gain trim
+		value which is later used in calculating illuminance in lux.
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
new file mode 100644
index 0000000..cceadae
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
@@ -0,0 +1,20 @@
+What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion:	2.6.37
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This property causes an internal calibration of the als gain trim
+		value which is later used in calculating illuminance in lux.
+
+What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Simultainious ALS channel data.
+
+What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Causes an recalculation and adjustment to the
+		proximity_thresh_rising_value.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio
index 46a995d..5b2b5d3 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
@@ -258,6 +258,8 @@ What		/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
+what		/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
+what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -457,6 +459,10 @@ What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
 What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
 What:		/sys/.../events/in_tempY_raw_thresh_falling_value
 What:		/sys/.../events/in_tempY_raw_thresh_falling_value
+What:		/sys/.../events/illuminance0_thresh_falling_value
+what:		/sys/.../events/illuminance0_thresh_rising_value
+what:		/sys/.../events/proximity_thresh_falling_value
+what:		/sys/.../events/proximity_thresh_rising_value
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -739,3 +745,4 @@ Description:
 		system. To minimize the current consumption of the system,
 		the bridge can be disconnected (when it is not being used
 		using the bridge_switch_en attribute.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
index edbf470..4385c70 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
@@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		This property gets/sets the sensors ADC analog integration time.

-What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
+What:		/sys/bus/iio/devices/device[n]/lux_table
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Hardware or software applied calibration scale factor assumed
-		to account for attenuation due to industrial design (glass
-		filters or aperture holes).
+		This property gets/sets the table of coefficients
+		used in calculating illuminance in lux.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
deleted file mode 100644
index 660781d..0000000
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
+++ /dev/null
@@ -1,20 +0,0 @@
-What:		/sys/bus/iio/devices/device[n]/lux_table
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property gets/sets the table of coefficients
-		used in calculating illuminance in lux.
-
-What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property causes an internal calibration of the als gain trim
-		value which is later used in calculating illuminance in lux.
-
-What:		/sys/bus/iio/devices/device[n]/illuminance0_input_target
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property is the known externally illuminance (in lux).
-		It is used in the process of calibrating the device accuracy.
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index e7e9159..976f790 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -31,4 +31,12 @@ config TSL2583
 	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
 	 Access ALS data via iio, sysfs.

+config TSL2x7x
+	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
+	depends on I2C
+	help
+	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
+	 tmd2672, tsl2772, tmd2772 devices.
+	 Provides iio_events and direct access via sysfs.
+
 endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
index 3011fbf..ff12c4b 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
 obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
 obj-$(CONFIG_TSL2583)	+= tsl2583.o
+obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
new file mode 100644
index 0000000..c0d9d6e
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -0,0 +1,1911 @@
+/*
+ * Device driver for monitoring ambient light intensity in (lux)
+ * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include "tsl2x7x_core.h"
+#include "../events.h"
+#include "../iio.h"
+#include "../sysfs.h"
+
+/* Cal defs*/
+#define PROX_STAT_CAL        0
+#define PROX_STAT_SAMP       1
+#define MAX_SAMPLES_CAL      200
+
+/* TSL2X7X Device ID */
+#define TRITON_ID    0x00
+#define SWORDFISH_ID 0x30
+#define HALIBUT_ID   0x20
+
+/* Lux calculation constants */
+#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
+
+/* TAOS Register definitions - note:
+ * depending on device, some of these register are not used and the
+ * register address is benign.
+ */
+/* 2X7X register offsets */
+#define TSL2X7X_MAX_CONFIG_REG         16
+
+/* Device Registers and Masks */
+#define TSL2X7X_CNTRL                  0x00
+#define TSL2X7X_ALS_TIME               0X01
+#define TSL2X7X_PRX_TIME               0x02
+#define TSL2X7X_WAIT_TIME              0x03
+#define TSL2X7X_ALS_MINTHRESHLO        0X04
+#define TSL2X7X_ALS_MINTHRESHHI        0X05
+#define TSL2X7X_ALS_MAXTHRESHLO        0X06
+#define TSL2X7X_ALS_MAXTHRESHHI        0X07
+#define TSL2X7X_PRX_MINTHRESHLO        0X08
+#define TSL2X7X_PRX_MINTHRESHHI        0X09
+#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
+#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
+#define TSL2X7X_PERSISTENCE            0x0C
+#define TSL2X7X_PRX_CONFIG             0x0D
+#define TSL2X7X_PRX_COUNT              0x0E
+#define TSL2X7X_GAIN                   0x0F
+#define TSL2X7X_NOTUSED                0x10
+#define TSL2X7X_REVID                  0x11
+#define TSL2X7X_CHIPID                 0x12
+#define TSL2X7X_STATUS                 0x13
+#define TSL2X7X_ALS_CHAN0LO            0x14
+#define TSL2X7X_ALS_CHAN0HI            0x15
+#define TSL2X7X_ALS_CHAN1LO            0x16
+#define TSL2X7X_ALS_CHAN1HI            0x17
+#define TSL2X7X_PRX_LO                 0x18
+#define TSL2X7X_PRX_HI                 0x19
+
+/* tsl2X7X cmd reg masks */
+#define TSL2X7X_CMD_REG                0x80
+#define TSL2X7X_CMD_SPL_FN             0x60
+
+#define TSL2X7X_CMD_PROX_INT_CLR       0X05
+#define TSL2X7X_CMD_ALS_INT_CLR        0x06
+#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_ADC_ENBL          0x02
+#define TSL2X7X_CNTL_PWR_ON            0x01
+
+/* tsl2X7X status reg masks */
+#define TSL2X7X_STA_ADC_VALID          0x01
+#define TSL2X7X_STA_PRX_VALID          0x02
+#define TSL2X7X_STA_ADC_PRX_VALID      0x03
+#define TSL2X7X_STA_ALS_INTR           0x10
+#define TSL2X7X_STA_ADC_INTR           0x10
+#define TSL2X7X_STA_PRX_INTR           0x20
+
+#define TSL2X7X_STA_ADC_INTR           0x10
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_REG_CLEAR         0x00
+#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
+#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
+#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
+#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
+#define TSL2X7X_CNTL_PWRON             0x01
+#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
+#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
+#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
+#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
+
+/*Prox diode to use */
+#define TSL2X7X_DIODE0                 0x10
+#define TSL2X7X_DIODE1                 0x20
+#define TSL2X7X_DIODE_BOTH             0x30
+
+/* LED Power */
+#define TSL2X7X_mA100                  0x00
+#define TSL2X7X_mA50                   0x40
+#define TSL2X7X_mA25                   0x80
+#define TSL2X7X_mA13                   0xD0
+
+/*Common device IIO EventMask */
+#define TSL2X7X_EVENT_MASK \
+		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
+		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
+
+/* TAOS txx2x7x Device family members */
+enum {
+	tsl2571,
+	tsl2671,
+	tmd2671,
+	tsl2771,
+	tmd2771,
+	tsl2572,
+	tsl2672,
+	tmd2672,
+	tsl2772,
+	tmd2772
+};
+
+enum {
+	TSL2X7X_CHIP_UNKNOWN = 0,
+	TSL2X7X_CHIP_WORKING = 1,
+	TSL2X7X_CHIP_SUSPENDED = 2
+};
+
+/* Per-device data */
+struct tsl2x7x_als_info {
+	u16 als_ch0;
+	u16 als_ch1;
+	u16 lux;
+};
+
+/* proximity data */
+struct tsl2x7x_prox_info {
+	u16 prox_data;
+	int prox_event;
+};
+
+struct prox_stat {
+	u16 min;
+	u16 max;
+	u16 mean;
+	unsigned long stddev;
+};
+
+struct tsl2x7x_chip_info {
+	int num_channels;
+	struct iio_chan_spec		channel[9];
+	const struct iio_info		*info;
+};
+
+struct tsl2X7X_chip {
+	kernel_ulong_t id;
+	struct mutex prox_mutex;
+	struct mutex als_mutex;
+	struct i2c_client *client;
+	struct tsl2x7x_prox_info prox_cur_info;
+	struct tsl2x7x_als_info als_cur_info;
+	struct tsl2x7x_settings tsl2x7x_settings;
+	struct tsl2X7X_platform_data *pdata;
+	int als_time_scale;
+	int als_saturation;
+	int tsl2x7x_chip_status;
+	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
+	const struct tsl2x7x_chip_info	*chip_info;
+	const struct iio_info *info;
+	s64 event_timestamp;
+	/* This structure is intentionally large to accommodate
+	 * updates via sysfs. */
+	/* Sized to 9 = max 8 segments + 1 termination segment */
+	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
+};
+
+/* Different devices require different coefficents */
+static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
+	{ 14461,   611,   1211 },
+	{ 18540,   352,    623 },
+	{     0,     0,      0 },
+};
+
+static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
+	{ 11635,   115,    256 },
+	{ 15536,    87,    179 },
+	{     0,     0,      0 },
+};
+
+static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
+	{ 14013,   466,   917 },
+	{ 18222,   310,   552 },
+	{     0,     0,     0 },
+};
+
+static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
+	{ 13218,   130,   262 },
+	{ 17592,   92,    169 },
+	{     0,     0,     0 },
+};
+
+static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
+	[tsl2571] = tsl2x71_lux_table,
+	[tsl2671] =	tsl2x71_lux_table,
+	[tmd2671] =	tmd2x71_lux_table,
+	[tsl2771] =	tsl2x71_lux_table,
+	[tmd2771] =	tmd2x71_lux_table,
+	[tsl2572] =	tsl2x72_lux_table,
+	[tsl2672] =	tsl2x72_lux_table,
+	[tmd2672] = tmd2x72_lux_table,
+	[tsl2772] =	tsl2x72_lux_table,
+	[tmd2772] =	tmd2x72_lux_table,
+};
+
+static const struct tsl2x7x_settings tsl2x7x_default_settings = {
+	.als_time = 200,
+	/* must be a multiple of 50mS */
+	.als_gain = 0,
+	/* this is actually an index into the gain table */
+	.prx_time = 0xfe, /*5.4 mS */
+	/* 2.7ms prox integration time - decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	.prox_gain = 1,
+	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
+	/* assume clear glass as default */
+	.wait_time = 245,
+	/* Time between PRX and ALS cycles -decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	.prox_config = 0,
+	/* Prox configuration filters */
+	.als_gain_trim = 1000,
+	/* default gain trim to account for aperture effects */
+	.als_cal_target = 150,
+	/* Known external ALS reading used for calibration */
+	.als_thresh_low = 200,
+	/* CH0 'low' count to trigger interrupt */
+	.als_thresh_high = 256,
+	/* CH0 'high' count to trigger interrupt */
+	.als_persistence = 0xFF,
+	/* Number of 'out of limits' ADC readings PRX/ALS*/
+	.interrupts_en = 0x00,
+	/* Default interrupt(s) enabled.
+	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
+	.prox_thres_low  = 0,
+	.prox_thres_high = 512,
+	/*default threshold adjust either manually or with cal routine*/
+	.prox_max_samples_cal = 30,
+	.prox_pulse_count = 8
+};
+
+static const s16 tsl2X7X_als_gainadj[] = {
+	1,
+	8,
+	16,
+	120
+};
+
+static const s16 tsl2X7X_prx_gainadj[] = {
+	1,
+	2,
+	4,
+	8
+};
+
+/* Channel variations */
+enum {
+	ALS,
+	PRX,
+	ALSPRX,
+	PRX2,
+	ALSPRX2,
+};
+
+const u8 device_channel_config[] = {
+	ALS,
+	PRX,
+	PRX,
+	ALSPRX,
+	ALSPRX,
+	ALS,
+	PRX2,
+	PRX2,
+	ALSPRX2,
+	ALSPRX2
+};
+
+/*
+ * Read a number of bytes starting at register (reg) location.
+ * Return 0, or i2c_smbus_write_byte ERROR code.
+ */
+static int
+tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret;
+
+	/* select register to write */
+	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: failed to write register %x\n"
+				, __func__, reg);
+		return ret;
+	}
+	/* read the data */
+	*val = i2c_smbus_read_byte(client);
+
+	return 0;
+}
+
+/*
+ * Reads and calculates current lux value.
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration time.
+ * The raw values are multiplied by a scale factor, and device gain is obtained
+ * using gain index. Limit checks are done next, then the ratio of a multiple
+ * of ch1 value, to the ch0 value, is calculated. The array tsl2x7x_device_lux[]
+ * declared above is then scanned to find the first ratio value that is just
+ * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
+ * the array are then used along with the time scale factor array values, to
+ * calculate the lux.
+ */
+static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
+{
+	u16 ch0, ch1; /* separated ch0/ch1 data from device */
+	u32 lux; /* raw lux calculated from device data */
+	u64 lux64;
+	u32 ratio;
+	u8 buf[4];
+	struct tsl2x7x_lux *p;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int i, ret;
+	u32 ch0lux = 0;
+	u32 ch1lux = 0;
+
+	if (mutex_trylock(&chip->als_mutex) == 0) {
+		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is busy\n");
+		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+	}
+
+	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
+		/* device is not enabled */
+		dev_err(&chip->client->dev, "%s: device is not enabled\n",
+				__func__);
+		ret = -EBUSY ;
+		goto out_unlock;
+	}
+
+	ret = tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to read CMD_REG\n", __func__);
+		goto out_unlock;
+	}
+	/* is data new & valid */
+	if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
+		dev_err(&chip->client->dev,
+			"%s: data not valid yet\n", __func__);
+		ret = chip->als_cur_info.lux; /* return LAST VALUE */
+		goto out_unlock;
+	}
+
+	for (i = 0; i < 4; i++) {
+		ret = tsl2x7x_i2c_read(chip->client,
+			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
+			&buf[i]);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				"%s: failed to read. err=%x\n", __func__, ret);
+			goto out_unlock;
+		}
+	}
+
+	/* clear status, really interrupt status ( are off),
+	but we use the bit anyway */
+	ret = i2c_smbus_write_byte(chip->client,
+		(TSL2X7X_CMD_REG |
+				TSL2X7X_CMD_SPL_FN |
+				TSL2X7X_CMD_ALS_INT_CLR));
+
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"i2c_write_command failed in %s, err = %d\n",
+			__func__, ret);
+		goto out_unlock; /* have no data, so return failure */
+	}
+
+	/* extract ALS/lux data */
+	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
+	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
+
+	chip->als_cur_info.als_ch0 = ch0;
+	chip->als_cur_info.als_ch1 = ch1;
+
+	if ((ch0 >= chip->als_saturation) || (ch1 >= chip->als_saturation)) {
+		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+		goto return_max;
+	}
+
+	if (ch0 == 0) {
+		/* have no data, so return LAST VALUE */
+		ret = chip->als_cur_info.lux = 0;
+		goto out_unlock;
+	}
+	/* calculate ratio */
+	ratio = (ch1 << 15) / ch0;
+	/* convert to unscaled lux using the pointer to the table */
+	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
+	while (p->ratio != 0 && p->ratio < ratio)
+			p++;
+
+	if (p->ratio == 0) {
+		lux = 0;
+	} else {
+		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+		lux = ch0lux - ch1lux;
+	}
+
+	/* note: lux is 31 bit max at this point */
+	if (ch1lux > ch0lux) {
+		dev_dbg(&chip->client->dev, "Returning last value\n");
+		ret = chip->als_cur_info.lux;
+		goto out_unlock;
+	}
+
+	/* adjust for active time scale */
+	if (chip->als_time_scale == 0)
+		lux = 0;
+	else
+		lux = (lux + (chip->als_time_scale >> 1)) /
+			chip->als_time_scale;
+
+	/* adjust for active gain scale
+	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
+	 * User-specified gain provides a multiplier.
+	 * Apply user-specified gain before shifting right to retain precision.
+	 * Use 64 bits to avoid overflow on multiplication.
+	 * Then go back to 32 bits before division to avoid using div_u64().
+	 */
+	lux64 = lux;
+	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
+	lux64 >>= 8;
+	lux = lux64;
+	lux = (lux + 500) / 1000;
+
+	if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
+		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+
+	/* Update the structure with the latest lux. */
+return_max:
+	chip->als_cur_info.lux = lux;
+	ret = lux;
+
+out_unlock:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+/*
+ * Proximity poll function - if valid data is available, read and form the ch0
+ * and prox data values, check for limits on the ch0 value, and check the prox
+ * data against the current thresholds, to set the event status accordingly.
+ */
+static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
+{
+#define CONSECUTIVE_RETRIES 50
+
+	int i;
+	int ret;
+	u8 status;
+	u8 chdata[2];
+	int err_cnt;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	if (mutex_trylock(&chip->prox_mutex) == 0) {
+		dev_err(&chip->client->dev,
+			"%s: Can't get prox mutex\n", __func__);
+		return -EBUSY;
+	}
+
+	err_cnt = 0;
+
+try_again:
+
+	ret = tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"%s: i2c err=%d\n", __func__, ret);
+		goto prox_poll_err;
+	}
+
+	if (chip->id < tsl2572) {
+		if (!(status & TSL2X7X_STA_ADC_VALID)) {
+			err_cnt++;
+			if (err_cnt > CONSECUTIVE_RETRIES) {
+				dev_err(&chip->client->dev,
+				"%s: Consec. retries exceeded\n", __func__);
+				goto prox_poll_err;
+			}
+		goto try_again;
+		}
+	} else {
+		if (!(status & TSL2X7X_STA_PRX_VALID)) {
+			err_cnt++;
+			if (err_cnt > CONSECUTIVE_RETRIES) {
+				dev_err(&chip->client->dev,
+				"%s: Consec. retries exceeded\n", __func__);
+				goto prox_poll_err;
+			}
+		goto try_again;
+		}
+	}
+
+	for (i = 0; i < 2; i++) {
+		ret = tsl2x7x_i2c_read(chip->client,
+			(TSL2X7X_CMD_REG |
+					(TSL2X7X_PRX_LO + i)), &chdata[i]);
+		if (ret < 0)
+			goto prox_poll_err;
+	}
+
+	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
+	if (chip->prox_cur_info.prox_data == 0)
+		goto try_again;
+
+	if (chip->prox_cur_info.prox_data >=
+			chip->tsl2x7x_settings.prox_thres_high)
+		chip->prox_cur_info.prox_event = 1;
+	else
+		chip->prox_cur_info.prox_event = 0;
+
+	mutex_unlock(&chip->prox_mutex);
+	return chip->prox_cur_info.prox_event;
+
+prox_poll_err:
+	mutex_unlock(&chip->prox_mutex);
+
+	return ret;
+}
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
+{
+	/* If Operational settings defined elsewhere.. */
+	if (chip->pdata && chip->pdata->platform_default_settings != 0)
+		memcpy(&(chip->tsl2x7x_settings),
+			chip->pdata->platform_default_settings,
+			sizeof(tsl2x7x_default_settings));
+	else
+		memcpy(&(chip->tsl2x7x_settings),
+			&tsl2x7x_default_settings,
+			sizeof(tsl2x7x_default_settings));
+
+	/* Load up the proper lux table. */
+	if (chip->pdata && chip->pdata->platform_lux_table[0].ratio != 0)
+		memcpy(chip->tsl2x7x_device_lux,
+			chip->pdata->platform_lux_table,
+			sizeof(chip->pdata->platform_lux_table));
+	else
+		memcpy(chip->tsl2x7x_device_lux,
+		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
+				MAX_DEFAULT_TABLE_BYTES);
+
+}
+
+/*
+ * Obtain single reading and calculate the als_gain_trim
+ * (later used to derive actual lux).
+ * Return updated gain_trim value.
+ */
+static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	u8 reg_val;
+	int gain_trim_val;
+	int ret;
+	int lux_val;
+
+	ret = i2c_smbus_write_byte(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"%s: failed to write CNTRL register, ret=%d\n",
+		__func__, ret);
+		return ret;
+	}
+
+	reg_val = i2c_smbus_read_byte(chip->client);
+	if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
+		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
+		dev_err(&chip->client->dev,
+			"%s: failed: ADC not enabled\n", __func__);
+		return -1;
+	}
+
+	ret = i2c_smbus_write_byte(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to write ctrl reg: ret=%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	reg_val = i2c_smbus_read_byte(chip->client);
+	if ((reg_val & TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
+		dev_err(&chip->client->dev,
+			"%s: failed: STATUS - ADC not valid.\n", __func__);
+		return -ENODATA;
+	}
+
+	lux_val = tsl2x7x_get_lux(indio_dev);
+	if (lux_val < 0) {
+		dev_err(&chip->client->dev,
+		"%s: failed to get lux\n", __func__);
+		return lux_val;
+	}
+
+	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
+			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
+	if ((gain_trim_val < 250) || (gain_trim_val > 4000))
+		return -ERANGE;
+
+	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
+	dev_info(&chip->client->dev,
+		"%s als_calibrate completed\n", chip->client->name);
+
+	return (int) gain_trim_val;
+}
+
+/*
+ * Turn the device on.
+ * Configuration must be set before calling this function.
+ */
+static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
+{
+	int i;
+	int ret = 0;
+	u8 *dev_reg;
+	u8 utmp;
+	int als_count;
+	int als_time;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	u8 reg_val = 0;
+
+	if (chip->pdata && chip->pdata->power_on)
+		chip->pdata->power_on(indio_dev);
+
+	/* Non calculated parameters */
+	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
+			chip->tsl2x7x_settings.prx_time;
+	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
+			chip->tsl2x7x_settings.wait_time;
+	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
+			chip->tsl2x7x_settings.prox_config;
+
+	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
+		(chip->tsl2x7x_settings.als_thresh_low) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
+		(chip->tsl2x7x_settings.als_thresh_low >> 8) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
+		(chip->tsl2x7x_settings.als_thresh_high) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
+		(chip->tsl2x7x_settings.als_thresh_high >> 8) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
+		chip->tsl2x7x_settings.als_persistence;
+
+	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
+			chip->tsl2x7x_settings.prox_pulse_count;
+	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
+	chip->tsl2x7x_settings.prox_thres_low;
+	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
+			chip->tsl2x7x_settings.prox_thres_high;
+
+	/* and make sure we're not already on */
+	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+		/* if forcing a register update - turn off, then on */
+		dev_info(&chip->client->dev, "device is already enabled\n");
+		return -EINVAL;
+	}
+
+	/* determine als integration regster */
+	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
+	if (als_count == 0)
+		als_count = 1; /* ensure at least one cycle */
+
+	/* convert back to time (encompasses overrides) */
+	als_time = (als_count * 27 + 5) / 10;
+	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
+
+	/* Set the gain based on tsl2x7x_settings struct */
+	chip->tsl2x7x_config[TSL2X7X_GAIN] =
+		(chip->tsl2x7x_settings.als_gain |
+			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
+			| ((chip->tsl2x7x_settings.prox_gain) << 2));
+
+	/* set chip struct re scaling and saturation */
+	chip->als_saturation = als_count * 922; /* 90% of full scale */
+	chip->als_time_scale = (als_time + 25) / 50;
+
+	/* TSL2X7X Specific power-on / adc enable sequence
+	 * Power on the device 1st. */
+	utmp = TSL2X7X_CNTL_PWR_ON;
+	ret = i2c_smbus_write_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed on CNTRL reg.\n", __func__);
+		return -1;
+	}
+
+	/* Use the following shadow copy for our delay before enabling ADC.
+	 * Write all the registers. */
+	for (i = 0, dev_reg = chip->tsl2x7x_config;
+			i < TSL2X7X_MAX_CONFIG_REG; i++) {
+		ret = i2c_smbus_write_byte_data(chip->client,
+				TSL2X7X_CMD_REG + i, *dev_reg++);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+			"%s: failed on write to reg %d.\n", __func__, i);
+			return ret;
+		}
+	}
+
+	udelay(3000);	/* Power-on settling time */
+
+	/* NOW enable the ADC
+	 * initialize the desired mode of operation */
+	utmp = TSL2X7X_CNTL_PWR_ON |
+			TSL2X7X_CNTL_ADC_ENBL |
+			TSL2X7X_CNTL_PROX_DET_ENBL;
+	ret = i2c_smbus_write_byte_data(chip->client,
+			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed on 2nd CTRL reg.\n", __func__);
+		return ret;
+		}
+
+	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
+
+	if (chip->tsl2x7x_settings.interrupts_en != 0) {
+		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
+
+		reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL;
+		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
+			(chip->tsl2x7x_settings.interrupts_en == 0x30))
+			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
+
+		reg_val |= chip->tsl2x7x_settings.interrupts_en;
+		ret = i2c_smbus_write_byte_data(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
+		if (ret < 0)
+			dev_err(&chip->client->dev,
+				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
+				__func__);
+
+		/* Clear out any initial interrupts  */
+		ret = i2c_smbus_write_byte(chip->client,
+			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+			TSL2X7X_CMD_PROXALS_INT_CLR);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				"%s: failed in tsl2x7x_chip_on\n",
+				__func__);
+		return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	/* turn device off */
+	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+
+	ret = i2c_smbus_write_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
+
+	if (chip->pdata && chip->pdata->power_off)
+		chip->pdata->power_off(chip->client);
+
+	return ret;
+}
+
+/*
+ * Proximity calibration helper function
+ * runs through a collection of data samples,
+ * sets the min, max, mean, and std dev.
+ */
+static
+void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
+{
+	int i;
+	int min, max, sum, mean;
+	unsigned long stddev;
+	int tmp;
+
+	if (length == 0)
+		length = 1;
+
+	sum = 0;
+	min = INT_MAX;
+	max = INT_MIN;
+	for (i = 0; i < length; i++) {
+		sum += data[i];
+		if (data[i] < min)
+			min = data[i];
+		if (data[i] > max)
+			max = data[i];
+	}
+	mean = sum/length;
+	statP->min = min;
+	statP->max = max;
+	statP->mean = mean;
+
+	sum = 0;
+	for (i = 0; i < length; i++) {
+		tmp = data[i]-mean;
+		sum += tmp * tmp;
+	}
+	stddev = int_sqrt((long)sum)/length;
+	statP->stddev = stddev;
+}
+
+/**
+ * Proximity calibration - collects a number of samples,
+ * calculates a standard deviation based on the samples, and
+ * sets the threshold accordingly.
+ */
+static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
+{
+	u16 prox_history[MAX_SAMPLES_CAL+1];
+	int i;
+	struct prox_stat prox_stat_data[2];
+	struct prox_stat *calP;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	u8 tmp_irq_settings;
+	u8 current_state = chip->tsl2x7x_chip_status;
+
+	if (chip->tsl2x7x_settings.prox_max_samples_cal > MAX_SAMPLES_CAL) {
+		dev_err(&chip->client->dev,
+			"%s: max prox samples cal is too big: %d\n",
+			__func__, chip->tsl2x7x_settings.prox_max_samples_cal);
+		chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL;
+	}
+
+	/* have to stop to change settings */
+	tsl2x7x_chip_off(indio_dev);
+
+	/* Enable proximity detection save just in case prox not wanted yet*/
+	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
+	chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL;
+
+	/*turn on device if not already on*/
+	tsl2x7x_chip_on(indio_dev);
+
+	/*gather the samples*/
+	for (i = 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
+		mdelay(15);
+		tsl2x7x_prox_poll(indio_dev);
+		prox_history[i] = chip->prox_cur_info.prox_data;
+		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
+			i, chip->prox_cur_info.prox_data);
+	}
+
+	tsl2x7x_chip_off(indio_dev);
+	calP = &prox_stat_data[PROX_STAT_CAL];
+	tsl2x7x_prox_calculate(prox_history,
+		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
+	chip->tsl2x7x_settings.prox_thres_high = (calP->max << 1) - calP->mean;
+
+	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
+		calP->min, calP->mean, calP->max);
+	dev_info(&chip->client->dev,
+		"%s proximity threshold set to %d\n",
+		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
+
+	/* back to the way they were */
+	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
+	if (current_state == TSL2X7X_CHIP_WORKING)
+		tsl2x7x_chip_on(indio_dev);
+}
+
+static ssize_t tsl2x7x_power_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
+}
+
+static ssize_t tsl2x7x_power_state_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (!value)
+		tsl2x7x_chip_off(indio_dev);
+	else
+		tsl2x7x_chip_on(indio_dev);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_gain_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	if (chip->id > tsl2771)
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
+	else
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
+}
+
+static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
+}
+
+static ssize_t tsl2x7x_als_time_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_time);
+}
+
+static ssize_t tsl2x7x_als_time_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if ((value < 50) || (value > 650))
+		return -EINVAL;
+
+	if (value % 50)
+		return -EINVAL;
+
+	 chip->tsl2x7x_settings.als_time = value;
+
+	return len;
+}
+
+static IIO_CONST_ATTR(illuminance0_integration_time_available,
+		"50 100 150 200 250 300 350 400 450 500 550 600 650");
+
+static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_cal_target);
+}
+
+static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		chip->tsl2x7x_settings.als_cal_target = value;
+
+	return len;
+}
+
+/* sampling_frequency AKA persistence in data sheet */
+static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_persistence);
+}
+
+static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.als_persistence = value;
+
+	return len;
+}
+
+static IIO_CONST_ATTR(sampling_frequency_available,
+		"0x00 - 0xFF (0 - 255)");
+
+static ssize_t tsl2x7x_do_calibrate(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (value)
+		tsl2x7x_als_calibrate(indio_dev);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_luxtable_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int i;
+	int offset = 0;
+
+	i = 0;
+	while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
+		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
+			chip->tsl2x7x_device_lux[i].ratio,
+			chip->tsl2x7x_device_lux[i].ch0,
+			chip->tsl2x7x_device_lux[i].ch1);
+		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
+			/* We just printed the first "0" entry.
+			 * Now get rid of the extra "," and break. */
+			offset--;
+			break;
+		}
+		i++;
+	}
+
+	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
+	return offset;
+}
+
+static ssize_t tsl2x7x_luxtable_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
+	int n;
+
+	get_options(buf, ARRAY_SIZE(value), value);
+
+	/* We now have an array of ints starting at value[1], and
+	 * enumerated by value[0].
+	 * We expect each group of three ints is one table entry,
+	 * and the last table entry is all 0.
+	 */
+	n = value[0];
+	if ((n % 3) || n < 6 ||
+			n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
+		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
+		return -EINVAL;
+	}
+
+	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
+		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
+		return -EINVAL;
+	}
+
+	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
+		tsl2x7x_chip_off(indio_dev);
+
+	/* Zero out the table */
+	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
+	memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4));
+
+	return len;
+}
+
+static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (value)
+		tsl2x7x_prox_cal(indio_dev);
+
+	return len;
+}
+
+static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
+					 u64 event_code)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int ret;
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
+		ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x10);
+	else
+		ret = !!(chip->tsl2x7x_settings.interrupts_en & 0x20);
+
+	return ret;
+}
+
+static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
+					  u64 event_code,
+					  int val)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
+		if (val)
+			chip->tsl2x7x_settings.interrupts_en |= 0x10;
+		else
+			chip->tsl2x7x_settings.interrupts_en &= 0x20;
+	} else {
+		if (val)
+			chip->tsl2x7x_settings.interrupts_en |= 0x20;
+		else
+			chip->tsl2x7x_settings.interrupts_en &= 0x10;
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
+				  u64 event_code,
+				  int val)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			chip->tsl2x7x_settings.als_thresh_high = val;
+			break;
+		case IIO_EV_DIR_FALLING:
+			chip->tsl2x7x_settings.als_thresh_low = val;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			chip->tsl2x7x_settings.prox_thres_high = val;
+			break;
+		case IIO_EV_DIR_FALLING:
+			chip->tsl2x7x_settings.prox_thres_low = val;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
+			       u64 event_code,
+			       int *val)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			*val = chip->tsl2x7x_settings.als_thresh_high;
+			break;
+		case IIO_EV_DIR_FALLING:
+			*val = chip->tsl2x7x_settings.als_thresh_low;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			*val = chip->tsl2x7x_settings.prox_thres_high;
+			break;
+		case IIO_EV_DIR_FALLING:
+			*val = chip->tsl2x7x_settings.prox_thres_low;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	int ret = -EINVAL;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	switch (mask) {
+	case 0:
+		switch (chan->type) {
+		case IIO_LIGHT:
+			tsl2x7x_get_lux(indio_dev);
+			if (chan->processed_val)
+				*val = chip->als_cur_info.lux;
+			else
+				*val = (((chip->als_cur_info.als_ch0 << 16) |
+					chip->als_cur_info.als_ch1));
+			ret = IIO_VAL_INT;
+			break;
+		case IIO_PROXIMITY:
+			tsl2x7x_prox_poll(indio_dev);
+			if (chan->processed_val)
+				*val = chip->prox_cur_info.prox_event;
+			else
+				*val = chip->prox_cur_info.prox_data;
+			ret = IIO_VAL_INT;
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type == IIO_LIGHT)
+			*val =
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
+		else
+			*val =
+			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
+		ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		*val = chip->tsl2x7x_settings.als_gain_trim;
+		ret = IIO_VAL_INT;
+		break;
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type == IIO_LIGHT) {
+			switch (val) {
+			case 1:
+				chip->tsl2x7x_settings.als_gain = 0;
+				break;
+			case 8:
+				chip->tsl2x7x_settings.als_gain = 1;
+				break;
+			case 16:
+				chip->tsl2x7x_settings.als_gain = 2;
+				break;
+			case 120:
+				if (chip->id > tsl2771)
+					return -EINVAL;
+				chip->tsl2x7x_settings.als_gain = 3;
+				break;
+			case 128:
+				if (chip->id < tsl2572)
+					return -EINVAL;
+				chip->tsl2x7x_settings.als_gain = 3;
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else {
+			switch (val) {
+			case 1:
+				chip->tsl2x7x_settings.prox_gain = 0;
+				break;
+			case 2:
+				chip->tsl2x7x_settings.prox_gain = 1;
+				break;
+			case 4:
+				chip->tsl2x7x_settings.prox_gain = 2;
+				break;
+			case 8:
+				chip->tsl2x7x_settings.prox_gain = 3;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		chip->tsl2x7x_settings.als_gain_trim = val;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
+
+static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
+		tsl2x7x_prox_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
+		tsl2x7x_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
+
+static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
+
+static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
+		tsl2x7x_do_calibrate);
+
+static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
+		tsl2x7x_do_prox_calibrate);
+
+static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
+		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
+
+/* Use the default register values to identify the Taos device */
+static int tsl2x7x_device_id(unsigned char *id, int target)
+{
+	switch (target) {
+	case tsl2571:
+	case tsl2671:
+	case tsl2771:
+		return ((*id & 0xf0) == TRITON_ID);
+	break;
+	case tmd2671:
+	case tmd2771:
+		return ((*id & 0xf0) == HALIBUT_ID);
+	break;
+	case tsl2572:
+	case tsl2672:
+	case tmd2672:
+	case tsl2772:
+	case tmd2772:
+		return ((*id & 0xf0) == SWORDFISH_ID);
+	break;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Interrupt Event Handler */
+static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	s64 timestamp = iio_get_time_ns();
+	int ret;
+	int value;
+
+	value = i2c_smbus_read_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+
+	/* What type of interrupt do we need to process */
+	if (value & TSL2X7X_STA_PRX_INTR) {
+		tsl2x7x_prox_poll(indio_dev);
+		iio_push_event(indio_dev,
+			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
+						    0,
+						    IIO_EV_TYPE_THRESH,
+						    IIO_EV_DIR_EITHER),
+						    timestamp);
+	}
+
+	if (value & TSL2X7X_STA_ALS_INTR) {
+		tsl2x7x_get_lux(indio_dev);
+		iio_push_event(indio_dev,
+		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+					    0,
+					    IIO_EV_TYPE_THRESH,
+					    IIO_EV_DIR_EITHER),
+					    timestamp);
+	}
+	/* Clear interrupt now that we have the status */
+	ret = i2c_smbus_write_byte(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+		TSL2X7X_CMD_PROXALS_INT_CLR);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+			"%s: Failed to clear irq from event handler. err = %d\n",
+			__func__, ret);
+
+	return IRQ_HANDLED;
+}
+
+static struct attribute *tsl2x7x_ALS_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_PRX_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	&dev_attr_proximity_calibscale_available.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	&dev_attr_proximity_calibscale_available.attr,
+	NULL
+};
+
+static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
+	[ALS] = {
+		.attrs = tsl2x7x_ALS_device_attrs,
+	},
+	[PRX] = {
+		.attrs = tsl2x7x_PRX_device_attrs,
+	},
+	[ALSPRX] = {
+		.attrs = tsl2x7x_ALSPRX_device_attrs,
+	},
+	[PRX2] = {
+		.attrs = tsl2x7x_PRX2_device_attrs,
+	},
+	[ALSPRX2] = {
+		.attrs = tsl2x7x_ALSPRX2_device_attrs,
+	},
+};
+
+static const struct iio_info tsl2X7X_device_info[] = {
+	[ALS] = {
+		.attrs = &tsl2X7X_device_attr_group_tbl[ALS],
+		.driver_module = THIS_MODULE,
+		.read_raw = &tsl2x7x_read_raw,
+		.write_raw = &tsl2x7x_write_raw,
+		.read_event_value = &tsl2x7x_read_thresh,
+		.write_event_value = &tsl2x7x_write_thresh,
+		.read_event_config = &tsl2x7x_read_interrupt_config,
+		.write_event_config = &tsl2x7x_write_interrupt_config,
+	},
+	[PRX] = {
+		.attrs = &tsl2X7X_device_attr_group_tbl[PRX],
+		.driver_module = THIS_MODULE,
+		.read_raw = &tsl2x7x_read_raw,
+		.write_raw = &tsl2x7x_write_raw,
+		.read_event_value = &tsl2x7x_read_thresh,
+		.write_event_value = &tsl2x7x_write_thresh,
+		.read_event_config = &tsl2x7x_read_interrupt_config,
+		.write_event_config = &tsl2x7x_write_interrupt_config,
+	},
+	[ALSPRX] = {
+		.attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX],
+		.driver_module = THIS_MODULE,
+		.read_raw = &tsl2x7x_read_raw,
+		.write_raw = &tsl2x7x_write_raw,
+		.read_event_value = &tsl2x7x_read_thresh,
+		.write_event_value = &tsl2x7x_write_thresh,
+		.read_event_config = &tsl2x7x_read_interrupt_config,
+		.write_event_config = &tsl2x7x_write_interrupt_config,
+	},
+	[PRX2] = {
+		.attrs = &tsl2X7X_device_attr_group_tbl[PRX2],
+		.driver_module = THIS_MODULE,
+		.read_raw = &tsl2x7x_read_raw,
+		.write_raw = &tsl2x7x_write_raw,
+		.read_event_value = &tsl2x7x_read_thresh,
+		.write_event_value = &tsl2x7x_write_thresh,
+		.read_event_config = &tsl2x7x_read_interrupt_config,
+		.write_event_config = &tsl2x7x_write_interrupt_config,
+	},
+	[ALSPRX2] = {
+		.attrs = &tsl2X7X_device_attr_group_tbl[ALSPRX2],
+		.driver_module = THIS_MODULE,
+		.read_raw = &tsl2x7x_read_raw,
+		.write_raw = &tsl2x7x_write_raw,
+		.read_event_value = &tsl2x7x_read_thresh,
+		.write_event_value = &tsl2x7x_write_thresh,
+		.read_event_config = &tsl2x7x_read_interrupt_config,
+		.write_event_config = &tsl2x7x_write_interrupt_config,
+	},
+};
+
+static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
+	[ALS] = {
+		.channel = {
+			[0] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[1] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask =
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels = 2,
+	.info = &tsl2X7X_device_info[ALS],
+	},
+	[PRX] = {
+		.channel = {
+			[0] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[1] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels = 2,
+	.info = &tsl2X7X_device_info[PRX],
+	},
+	[ALSPRX] = {
+		.channel = {
+			[0] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[1] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask =
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+			[2] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[3] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 0,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels = 4,
+	.info = &tsl2X7X_device_info[ALSPRX],
+	},
+	[PRX2] = {
+		.channel = {
+			[0] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[1] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask =
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels = 2,
+	.info = &tsl2X7X_device_info[PRX2],
+	},
+	[ALSPRX2] = {
+		.channel = {
+				[0] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[1] = {
+				.type = IIO_LIGHT,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask =
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+			[2] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.processed_val = 1,
+			},
+			[3] = {
+				.type = IIO_PROXIMITY,
+				.indexed = 1,
+				.channel = 0,
+				.info_mask =
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+				.event_mask = TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels = 4,
+	.info = &tsl2X7X_device_info[ALSPRX2],
+	},
+};
+
+/*
+ * Client probe function.
+ */
+static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
+	const struct i2c_device_id *id)
+{
+	int ret;
+	unsigned char device_id;
+	struct iio_dev *indio_dev;
+	struct tsl2X7X_chip *chip;
+
+	indio_dev = iio_allocate_device(sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip = iio_priv(indio_dev);
+	chip->client = clientp;
+	i2c_set_clientdata(clientp, indio_dev);
+
+	ret = tsl2x7x_i2c_read(chip->client,
+		TSL2X7X_CHIPID, &device_id);
+	if (ret < 0)
+		goto fail1;
+
+	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
+		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
+		dev_info(&chip->client->dev,
+				"i2c device found does not match expected id in %s\n",
+				__func__);
+		goto fail1;
+	}
+
+	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&clientp->dev, "%s: write to cmd reg failed. err = %d\n",
+				__func__, ret);
+		goto fail1;
+	}
+
+	/* ALS and PROX functions can be invoked via user space poll
+	 * or H/W interrupt. If busy return last sample. */
+	mutex_init(&chip->als_mutex);
+	mutex_init(&chip->prox_mutex);
+
+	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
+	chip->pdata = clientp->dev.platform_data;
+	chip->id = id->driver_data;
+	chip->chip_info =
+		&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
+
+	indio_dev->info = chip->chip_info->info;
+	indio_dev->dev.parent = &clientp->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = chip->client->name;
+	indio_dev->channels = chip->chip_info->channel;
+	indio_dev->num_channels = chip->chip_info->num_channels;
+
+	if (clientp->irq) {
+		ret = request_threaded_irq(clientp->irq,
+					   NULL,
+					   &tsl2x7x_event_handler,
+					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					   "TSL2X7X_event",
+					   indio_dev);
+		if (ret) {
+			dev_err(&clientp->dev,
+				"%s: irq request failed", __func__);
+			goto fail2;
+		}
+	}
+
+	/* Load up the defaults */
+	tsl2x7x_defaults(chip);
+	/* Make sure the chip is on */
+	tsl2x7x_chip_on(indio_dev);
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&clientp->dev,
+			"%s: iio registration failed\n", __func__);
+		goto fail3;
+	}
+
+	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
+
+	return 0;
+
+fail3:
+	if (clientp->irq)
+		free_irq(clientp->irq, indio_dev);
+fail2:
+	iio_free_device(indio_dev);
+fail1:
+	kfree(chip);
+	return ret;
+}
+
+static int tsl2x7x_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int ret = 0;
+
+	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+		ret = tsl2x7x_chip_off(indio_dev);
+		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
+	}
+
+	if (chip->pdata && chip->pdata->platform_power) {
+		pm_message_t pmm = {PM_EVENT_SUSPEND};
+		chip->pdata->platform_power(dev, pmm);
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
+	int ret = 0;
+
+	if (chip->pdata && chip->pdata->platform_power) {
+		pm_message_t pmm = {PM_EVENT_RESUME};
+		chip->pdata->platform_power(dev, pmm);
+	}
+
+	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
+		ret = tsl2x7x_chip_on(indio_dev);
+
+	return ret;
+}
+
+static int __devexit tsl2x7x_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
+
+	tsl2x7x_chip_off(indio_dev);
+
+	if (client->irq)
+		free_irq(client->irq, chip->client->name);
+
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static struct i2c_device_id tsl2x7x_idtable[] = {
+	{ "tsl2571", tsl2571 },
+	{ "tsl2671", tsl2671 },
+	{ "tmd2671", tmd2671 },
+	{ "tsl2771", tsl2771 },
+	{ "tmd2771", tmd2771 },
+	{ "tsl2572", tsl2572 },
+	{ "tsl2672", tsl2672 },
+	{ "tmd2672", tmd2672 },
+	{ "tsl2772", tsl2772 },
+	{ "tmd2772", tmd2772 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
+
+static const struct dev_pm_ops tsl2x7x_pm_ops = {
+	.suspend = tsl2x7x_suspend,
+	.resume  = tsl2x7x_resume,
+};
+
+/* Driver definition */
+static struct i2c_driver tsl2x7x_driver = {
+	.driver = {
+		.name = "tsl2x7x",
+		.pm = &tsl2x7x_pm_ops,
+	},
+	.id_table = tsl2x7x_idtable,
+	.probe = tsl2x7x_probe,
+	.remove = __devexit_p(tsl2x7x_remove),
+};
+
+module_i2c_driver(tsl2x7x_driver);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/light/tsl2x7x_core.h b/drivers/staging/iio/light/tsl2x7x_core.h
new file mode 100644
index 0000000..663e846
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.h
@@ -0,0 +1,75 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * and proximity (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
+ */
+
+#ifndef __TSL2X7X_H
+#define __TSL2X7X_H
+#include <linux/pm.h>
+
+/* Max number of segments allowable in LUX table */
+#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
+#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE)
+
+struct iio_dev;
+struct iio_chan_spec;
+
+struct tsl2x7x_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;
+};
+
+/* Refer to tsl2x7x_default_settings for member desc. */
+struct tsl2x7x_settings {
+	int als_time;
+	int als_gain;
+	int als_gain_trim;
+	int wait_time;
+	int prx_time;
+	int prox_gain;
+	int prox_config;
+	int als_cal_target;
+	u8  interrupts_en;
+	u8  als_persistence;
+	int als_thresh_low;
+	int als_thresh_high;
+	int prox_thres_low;
+	int prox_thres_high;
+	int prox_pulse_count;
+	int prox_max_samples_cal;
+};
+
+/* struct tsl2x7x_platform_data -
+ * Platform unique glass and defaults
+ * Platform PM functions. */
+struct tsl2X7X_platform_data {
+	/* Suspend/resume platform cb */
+	int (*platform_power)(struct device *dev, pm_message_t);
+	/* The following callback gets called when the device is powered on */
+	int (*power_on)      (struct iio_dev *indio_dev);
+	/* The following callback gets called when the device is powered off */
+	int (*power_off)     (struct i2c_client *dev);
+	/* These are the device specific glass coefficents used to
+	 * calculate Lux */
+	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
+	struct tsl2x7x_settings *platform_default_settings;
+};
+
+#endif /* __TSL2X7X_H */
--
1.7.4.1


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH V4] TAOS tsl2x7x
@ 2012-03-21 16:16 ` Jon Brenner
  0 siblings, 0 replies; 8+ messages in thread
From: Jon Brenner @ 2012-03-21 16:16 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel

TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device familie=
s (inc. all variants).

Signed-off-by: Jon Brenner <jbrenner@taosinc.com>
---
 .../light/sysfs-bus-iio-light-tsl2583              |    6 +
 .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
 drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
 .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
 .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
 drivers/staging/iio/light/Kconfig                  |    8 +
 drivers/staging/iio/light/Makefile                 |    1 +
 drivers/staging/iio/light/tsl2x7x_core.c           | 1911 ++++++++++++++++=
++++
 drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
 9 files changed, 2032 insertions(+), 24 deletions(-)

diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-ts=
l2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
new file mode 100644
index 0000000..8f2a038
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
@@ -0,0 +1,6 @@
+What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion:	2.6.37
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This property causes an internal calibration of the als gain trim
+		value which is later used in calculating illuminance in lux.
diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-ts=
l2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
new file mode 100644
index 0000000..cceadae
--- /dev/null
+++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
@@ -0,0 +1,20 @@
+What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
+KernelVersion:	2.6.37
+Contact:	linux-iio@vger.kernel.org
+Description:
+		This property causes an internal calibration of the als gain trim
+		value which is later used in calculating illuminance in lux.
+
+What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Simultainious ALS channel data.
+
+What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Causes an recalculation and adjustment to the
+		proximity_thresh_rising_value.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/stag=
ing/iio/Documentation/sysfs-bus-iio
index 46a995d..5b2b5d3 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
@@ -258,6 +258,8 @@ What		/sys/bus/iio/devices/iio:deviceX/in_accel_z_calib=
scale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
 What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
+what		/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
+what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
 KernelVersion:	2.6.35
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -457,6 +459,10 @@ What:		/sys/.../events/in_voltageY_raw_thresh_falling_=
value
 What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
 What:		/sys/.../events/in_tempY_raw_thresh_falling_value
 What:		/sys/.../events/in_tempY_raw_thresh_falling_value
+What:		/sys/.../events/illuminance0_thresh_falling_value
+what:		/sys/.../events/illuminance0_thresh_rising_value
+what:		/sys/.../events/proximity_thresh_falling_value
+what:		/sys/.../events/proximity_thresh_rising_value
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
@@ -739,3 +745,4 @@ Description:
 		system. To minimize the current consumption of the system,
 		the bridge can be disconnected (when it is not being used
 		using the bridge_switch_en attribute.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/driver=
s/staging/iio/Documentation/sysfs-bus-iio-light
index edbf470..4385c70 100644
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
@@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
 Description:
 		This property gets/sets the sensors ADC analog integration time.

-What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
+What:		/sys/bus/iio/devices/device[n]/lux_table
 KernelVersion:	2.6.37
 Contact:	linux-iio@vger.kernel.org
 Description:
-		Hardware or software applied calibration scale factor assumed
-		to account for attenuation due to industrial design (glass
-		filters or aperture holes).
+		This property gets/sets the table of coefficients
+		used in calculating illuminance in lux.
+
diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 =
b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
deleted file mode 100644
index 660781d..0000000
--- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
+++ /dev/null
@@ -1,20 +0,0 @@
-What:		/sys/bus/iio/devices/device[n]/lux_table
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property gets/sets the table of coefficients
-		used in calculating illuminance in lux.
-
-What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property causes an internal calibration of the als gain trim
-		value which is later used in calculating illuminance in lux.
-
-What:		/sys/bus/iio/devices/device[n]/illuminance0_input_target
-KernelVersion:	2.6.37
-Contact:	linux-iio@vger.kernel.org
-Description:
-		This property is the known externally illuminance (in lux).
-		It is used in the process of calibrating the device accuracy.
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/=
Kconfig
index e7e9159..976f790 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -31,4 +31,12 @@ config TSL2583
 	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
 	 Access ALS data via iio, sysfs.

+config TSL2x7x
+	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity =
sensors"
+	depends on I2C
+	help
+	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl26=
72,
+	 tmd2672, tsl2772, tmd2772 devices.
+	 Provides iio_events and direct access via sysfs.
+
 endmenu
diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light=
/Makefile
index 3011fbf..ff12c4b 100644
--- a/drivers/staging/iio/light/Makefile
+++ b/drivers/staging/iio/light/Makefile
@@ -5,3 +5,4 @@
 obj-$(CONFIG_SENSORS_TSL2563)	+=3D tsl2563.o
 obj-$(CONFIG_SENSORS_ISL29018)	+=3D isl29018.o
 obj-$(CONFIG_TSL2583)	+=3D tsl2583.o
+obj-$(CONFIG_TSL2x7x)	+=3D tsl2x7x_core.o
diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio=
/light/tsl2x7x_core.c
new file mode 100644
index 0000000..c0d9d6e
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -0,0 +1,1911 @@
+/*
+ * Device driver for monitoring ambient light intensity in (lux)
+ * and proximity detection (prox) within the TAOS TSL2X7X family of device=
s.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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 WIT=
HOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License f=
or
+ * 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 Street, Fifth Floor, Boston, MA        02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include "tsl2x7x_core.h"
+#include "../events.h"
+#include "../iio.h"
+#include "../sysfs.h"
+
+/* Cal defs*/
+#define PROX_STAT_CAL        0
+#define PROX_STAT_SAMP       1
+#define MAX_SAMPLES_CAL      200
+
+/* TSL2X7X Device ID */
+#define TRITON_ID    0x00
+#define SWORDFISH_ID 0x30
+#define HALIBUT_ID   0x20
+
+/* Lux calculation constants */
+#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
+
+/* TAOS Register definitions - note:
+ * depending on device, some of these register are not used and the
+ * register address is benign.
+ */
+/* 2X7X register offsets */
+#define TSL2X7X_MAX_CONFIG_REG         16
+
+/* Device Registers and Masks */
+#define TSL2X7X_CNTRL                  0x00
+#define TSL2X7X_ALS_TIME               0X01
+#define TSL2X7X_PRX_TIME               0x02
+#define TSL2X7X_WAIT_TIME              0x03
+#define TSL2X7X_ALS_MINTHRESHLO        0X04
+#define TSL2X7X_ALS_MINTHRESHHI        0X05
+#define TSL2X7X_ALS_MAXTHRESHLO        0X06
+#define TSL2X7X_ALS_MAXTHRESHHI        0X07
+#define TSL2X7X_PRX_MINTHRESHLO        0X08
+#define TSL2X7X_PRX_MINTHRESHHI        0X09
+#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
+#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
+#define TSL2X7X_PERSISTENCE            0x0C
+#define TSL2X7X_PRX_CONFIG             0x0D
+#define TSL2X7X_PRX_COUNT              0x0E
+#define TSL2X7X_GAIN                   0x0F
+#define TSL2X7X_NOTUSED                0x10
+#define TSL2X7X_REVID                  0x11
+#define TSL2X7X_CHIPID                 0x12
+#define TSL2X7X_STATUS                 0x13
+#define TSL2X7X_ALS_CHAN0LO            0x14
+#define TSL2X7X_ALS_CHAN0HI            0x15
+#define TSL2X7X_ALS_CHAN1LO            0x16
+#define TSL2X7X_ALS_CHAN1HI            0x17
+#define TSL2X7X_PRX_LO                 0x18
+#define TSL2X7X_PRX_HI                 0x19
+
+/* tsl2X7X cmd reg masks */
+#define TSL2X7X_CMD_REG                0x80
+#define TSL2X7X_CMD_SPL_FN             0x60
+
+#define TSL2X7X_CMD_PROX_INT_CLR       0X05
+#define TSL2X7X_CMD_ALS_INT_CLR        0x06
+#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_ADC_ENBL          0x02
+#define TSL2X7X_CNTL_PWR_ON            0x01
+
+/* tsl2X7X status reg masks */
+#define TSL2X7X_STA_ADC_VALID          0x01
+#define TSL2X7X_STA_PRX_VALID          0x02
+#define TSL2X7X_STA_ADC_PRX_VALID      0x03
+#define TSL2X7X_STA_ALS_INTR           0x10
+#define TSL2X7X_STA_ADC_INTR           0x10
+#define TSL2X7X_STA_PRX_INTR           0x20
+
+#define TSL2X7X_STA_ADC_INTR           0x10
+
+/* tsl2X7X cntrl reg masks */
+#define TSL2X7X_CNTL_REG_CLEAR         0x00
+#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
+#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
+#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
+#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
+#define TSL2X7X_CNTL_PWRON             0x01
+#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
+#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
+#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
+#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
+
+/*Prox diode to use */
+#define TSL2X7X_DIODE0                 0x10
+#define TSL2X7X_DIODE1                 0x20
+#define TSL2X7X_DIODE_BOTH             0x30
+
+/* LED Power */
+#define TSL2X7X_mA100                  0x00
+#define TSL2X7X_mA50                   0x40
+#define TSL2X7X_mA25                   0x80
+#define TSL2X7X_mA13                   0xD0
+
+/*Common device IIO EventMask */
+#define TSL2X7X_EVENT_MASK \
+		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
+		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
+
+/* TAOS txx2x7x Device family members */
+enum {
+	tsl2571,
+	tsl2671,
+	tmd2671,
+	tsl2771,
+	tmd2771,
+	tsl2572,
+	tsl2672,
+	tmd2672,
+	tsl2772,
+	tmd2772
+};
+
+enum {
+	TSL2X7X_CHIP_UNKNOWN =3D 0,
+	TSL2X7X_CHIP_WORKING =3D 1,
+	TSL2X7X_CHIP_SUSPENDED =3D 2
+};
+
+/* Per-device data */
+struct tsl2x7x_als_info {
+	u16 als_ch0;
+	u16 als_ch1;
+	u16 lux;
+};
+
+/* proximity data */
+struct tsl2x7x_prox_info {
+	u16 prox_data;
+	int prox_event;
+};
+
+struct prox_stat {
+	u16 min;
+	u16 max;
+	u16 mean;
+	unsigned long stddev;
+};
+
+struct tsl2x7x_chip_info {
+	int num_channels;
+	struct iio_chan_spec		channel[9];
+	const struct iio_info		*info;
+};
+
+struct tsl2X7X_chip {
+	kernel_ulong_t id;
+	struct mutex prox_mutex;
+	struct mutex als_mutex;
+	struct i2c_client *client;
+	struct tsl2x7x_prox_info prox_cur_info;
+	struct tsl2x7x_als_info als_cur_info;
+	struct tsl2x7x_settings tsl2x7x_settings;
+	struct tsl2X7X_platform_data *pdata;
+	int als_time_scale;
+	int als_saturation;
+	int tsl2x7x_chip_status;
+	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
+	const struct tsl2x7x_chip_info	*chip_info;
+	const struct iio_info *info;
+	s64 event_timestamp;
+	/* This structure is intentionally large to accommodate
+	 * updates via sysfs. */
+	/* Sized to 9 =3D max 8 segments + 1 termination segment */
+	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
+};
+
+/* Different devices require different coefficents */
+static const struct tsl2x7x_lux tsl2x71_lux_table[] =3D {
+	{ 14461,   611,   1211 },
+	{ 18540,   352,    623 },
+	{     0,     0,      0 },
+};
+
+static const struct tsl2x7x_lux tmd2x71_lux_table[] =3D {
+	{ 11635,   115,    256 },
+	{ 15536,    87,    179 },
+	{     0,     0,      0 },
+};
+
+static const struct tsl2x7x_lux tsl2x72_lux_table[] =3D {
+	{ 14013,   466,   917 },
+	{ 18222,   310,   552 },
+	{     0,     0,     0 },
+};
+
+static const struct tsl2x7x_lux tmd2x72_lux_table[] =3D {
+	{ 13218,   130,   262 },
+	{ 17592,   92,    169 },
+	{     0,     0,     0 },
+};
+
+static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] =3D {
+	[tsl2571] =3D tsl2x71_lux_table,
+	[tsl2671] =3D	tsl2x71_lux_table,
+	[tmd2671] =3D	tmd2x71_lux_table,
+	[tsl2771] =3D	tsl2x71_lux_table,
+	[tmd2771] =3D	tmd2x71_lux_table,
+	[tsl2572] =3D	tsl2x72_lux_table,
+	[tsl2672] =3D	tsl2x72_lux_table,
+	[tmd2672] =3D tmd2x72_lux_table,
+	[tsl2772] =3D	tsl2x72_lux_table,
+	[tmd2772] =3D	tmd2x72_lux_table,
+};
+
+static const struct tsl2x7x_settings tsl2x7x_default_settings =3D {
+	.als_time =3D 200,
+	/* must be a multiple of 50mS */
+	.als_gain =3D 0,
+	/* this is actually an index into the gain table */
+	.prx_time =3D 0xfe, /*5.4 mS */
+	/* 2.7ms prox integration time - decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	.prox_gain =3D 1,
+	/* these are bits 3:2 of reg 0x0f: 0=3Dx1,1=3Dx2,2=3Dx4,3=3Dx8 */
+	/* assume clear glass as default */
+	.wait_time =3D 245,
+	/* Time between PRX and ALS cycles -decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	.prox_config =3D 0,
+	/* Prox configuration filters */
+	.als_gain_trim =3D 1000,
+	/* default gain trim to account for aperture effects */
+	.als_cal_target =3D 150,
+	/* Known external ALS reading used for calibration */
+	.als_thresh_low =3D 200,
+	/* CH0 'low' count to trigger interrupt */
+	.als_thresh_high =3D 256,
+	/* CH0 'high' count to trigger interrupt */
+	.als_persistence =3D 0xFF,
+	/* Number of 'out of limits' ADC readings PRX/ALS*/
+	.interrupts_en =3D 0x00,
+	/* Default interrupt(s) enabled.
+	 * 0x00 =3D none, 0x10 =3D als, 0x20 =3D prx 0x30 =3D bth */
+	.prox_thres_low  =3D 0,
+	.prox_thres_high =3D 512,
+	/*default threshold adjust either manually or with cal routine*/
+	.prox_max_samples_cal =3D 30,
+	.prox_pulse_count =3D 8
+};
+
+static const s16 tsl2X7X_als_gainadj[] =3D {
+	1,
+	8,
+	16,
+	120
+};
+
+static const s16 tsl2X7X_prx_gainadj[] =3D {
+	1,
+	2,
+	4,
+	8
+};
+
+/* Channel variations */
+enum {
+	ALS,
+	PRX,
+	ALSPRX,
+	PRX2,
+	ALSPRX2,
+};
+
+const u8 device_channel_config[] =3D {
+	ALS,
+	PRX,
+	PRX,
+	ALSPRX,
+	ALSPRX,
+	ALS,
+	PRX2,
+	PRX2,
+	ALSPRX2,
+	ALSPRX2
+};
+
+/*
+ * Read a number of bytes starting at register (reg) location.
+ * Return 0, or i2c_smbus_write_byte ERROR code.
+ */
+static int
+tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
+{
+	int ret;
+
+	/* select register to write */
+	ret =3D i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
+	if (ret < 0) {
+		dev_err(&client->dev, "%s: failed to write register %x\n"
+				, __func__, reg);
+		return ret;
+	}
+	/* read the data */
+	*val =3D i2c_smbus_read_byte(client);
+
+	return 0;
+}
+
+/*
+ * Reads and calculates current lux value.
+ * The raw ch0 and ch1 values of the ambient light sensed in the last
+ * integration cycle are read from the device.
+ * Time scale factor array values are adjusted based on the integration ti=
me.
+ * The raw values are multiplied by a scale factor, and device gain is obt=
ained
+ * using gain index. Limit checks are done next, then the ratio of a multi=
ple
+ * of ch1 value, to the ch0 value, is calculated. The array tsl2x7x_device=
_lux[]
+ * declared above is then scanned to find the first ratio value that is ju=
st
+ * above the ratio we just calculated. The ch0 and ch1 multiplier constant=
s in
+ * the array are then used along with the time scale factor array values, =
to
+ * calculate the lux.
+ */
+static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
+{
+	u16 ch0, ch1; /* separated ch0/ch1 data from device */
+	u32 lux; /* raw lux calculated from device data */
+	u64 lux64;
+	u32 ratio;
+	u8 buf[4];
+	struct tsl2x7x_lux *p;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int i, ret;
+	u32 ch0lux =3D 0;
+	u32 ch1lux =3D 0;
+
+	if (mutex_trylock(&chip->als_mutex) =3D=3D 0) {
+		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is busy\n");
+		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
+	}
+
+	if (chip->tsl2x7x_chip_status !=3D TSL2X7X_CHIP_WORKING) {
+		/* device is not enabled */
+		dev_err(&chip->client->dev, "%s: device is not enabled\n",
+				__func__);
+		ret =3D -EBUSY ;
+		goto out_unlock;
+	}
+
+	ret =3D tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0]);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to read CMD_REG\n", __func__);
+		goto out_unlock;
+	}
+	/* is data new & valid */
+	if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
+		dev_err(&chip->client->dev,
+			"%s: data not valid yet\n", __func__);
+		ret =3D chip->als_cur_info.lux; /* return LAST VALUE */
+		goto out_unlock;
+	}
+
+	for (i =3D 0; i < 4; i++) {
+		ret =3D tsl2x7x_i2c_read(chip->client,
+			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
+			&buf[i]);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				"%s: failed to read. err=3D%x\n", __func__, ret);
+			goto out_unlock;
+		}
+	}
+
+	/* clear status, really interrupt status ( are off),
+	but we use the bit anyway */
+	ret =3D i2c_smbus_write_byte(chip->client,
+		(TSL2X7X_CMD_REG |
+				TSL2X7X_CMD_SPL_FN |
+				TSL2X7X_CMD_ALS_INT_CLR));
+
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"i2c_write_command failed in %s, err =3D %d\n",
+			__func__, ret);
+		goto out_unlock; /* have no data, so return failure */
+	}
+
+	/* extract ALS/lux data */
+	ch0 =3D le16_to_cpup((const __le16 *)&buf[0]);
+	ch1 =3D le16_to_cpup((const __le16 *)&buf[2]);
+
+	chip->als_cur_info.als_ch0 =3D ch0;
+	chip->als_cur_info.als_ch1 =3D ch1;
+
+	if ((ch0 >=3D chip->als_saturation) || (ch1 >=3D chip->als_saturation)) {
+		lux =3D TSL2X7X_LUX_CALC_OVER_FLOW;
+		goto return_max;
+	}
+
+	if (ch0 =3D=3D 0) {
+		/* have no data, so return LAST VALUE */
+		ret =3D chip->als_cur_info.lux =3D 0;
+		goto out_unlock;
+	}
+	/* calculate ratio */
+	ratio =3D (ch1 << 15) / ch0;
+	/* convert to unscaled lux using the pointer to the table */
+	p =3D (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
+	while (p->ratio !=3D 0 && p->ratio < ratio)
+			p++;
+
+	if (p->ratio =3D=3D 0) {
+		lux =3D 0;
+	} else {
+		ch0lux =3D DIV_ROUND_UP((ch0 * p->ch0),
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+		ch1lux =3D DIV_ROUND_UP((ch1 * p->ch1),
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
+		lux =3D ch0lux - ch1lux;
+	}
+
+	/* note: lux is 31 bit max at this point */
+	if (ch1lux > ch0lux) {
+		dev_dbg(&chip->client->dev, "Returning last value\n");
+		ret =3D chip->als_cur_info.lux;
+		goto out_unlock;
+	}
+
+	/* adjust for active time scale */
+	if (chip->als_time_scale =3D=3D 0)
+		lux =3D 0;
+	else
+		lux =3D (lux + (chip->als_time_scale >> 1)) /
+			chip->als_time_scale;
+
+	/* adjust for active gain scale
+	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
+	 * User-specified gain provides a multiplier.
+	 * Apply user-specified gain before shifting right to retain precision.
+	 * Use 64 bits to avoid overflow on multiplication.
+	 * Then go back to 32 bits before division to avoid using div_u64().
+	 */
+	lux64 =3D lux;
+	lux64 =3D lux64 * chip->tsl2x7x_settings.als_gain_trim;
+	lux64 >>=3D 8;
+	lux =3D lux64;
+	lux =3D (lux + 500) / 1000;
+
+	if (lux > TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
+		lux =3D TSL2X7X_LUX_CALC_OVER_FLOW;
+
+	/* Update the structure with the latest lux. */
+return_max:
+	chip->als_cur_info.lux =3D lux;
+	ret =3D lux;
+
+out_unlock:
+	mutex_unlock(&chip->als_mutex);
+
+	return ret;
+}
+
+/*
+ * Proximity poll function - if valid data is available, read and form the=
 ch0
+ * and prox data values, check for limits on the ch0 value, and check the =
prox
+ * data against the current thresholds, to set the event status accordingl=
y.
+ */
+static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
+{
+#define CONSECUTIVE_RETRIES 50
+
+	int i;
+	int ret;
+	u8 status;
+	u8 chdata[2];
+	int err_cnt;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	if (mutex_trylock(&chip->prox_mutex) =3D=3D 0) {
+		dev_err(&chip->client->dev,
+			"%s: Can't get prox mutex\n", __func__);
+		return -EBUSY;
+	}
+
+	err_cnt =3D 0;
+
+try_again:
+
+	ret =3D tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"%s: i2c err=3D%d\n", __func__, ret);
+		goto prox_poll_err;
+	}
+
+	if (chip->id < tsl2572) {
+		if (!(status & TSL2X7X_STA_ADC_VALID)) {
+			err_cnt++;
+			if (err_cnt > CONSECUTIVE_RETRIES) {
+				dev_err(&chip->client->dev,
+				"%s: Consec. retries exceeded\n", __func__);
+				goto prox_poll_err;
+			}
+		goto try_again;
+		}
+	} else {
+		if (!(status & TSL2X7X_STA_PRX_VALID)) {
+			err_cnt++;
+			if (err_cnt > CONSECUTIVE_RETRIES) {
+				dev_err(&chip->client->dev,
+				"%s: Consec. retries exceeded\n", __func__);
+				goto prox_poll_err;
+			}
+		goto try_again;
+		}
+	}
+
+	for (i =3D 0; i < 2; i++) {
+		ret =3D tsl2x7x_i2c_read(chip->client,
+			(TSL2X7X_CMD_REG |
+					(TSL2X7X_PRX_LO + i)), &chdata[i]);
+		if (ret < 0)
+			goto prox_poll_err;
+	}
+
+	chip->prox_cur_info.prox_data =3D (chdata[1]<<8)|chdata[0];
+	if (chip->prox_cur_info.prox_data =3D=3D 0)
+		goto try_again;
+
+	if (chip->prox_cur_info.prox_data >=3D
+			chip->tsl2x7x_settings.prox_thres_high)
+		chip->prox_cur_info.prox_event =3D 1;
+	else
+		chip->prox_cur_info.prox_event =3D 0;
+
+	mutex_unlock(&chip->prox_mutex);
+	return chip->prox_cur_info.prox_event;
+
+prox_poll_err:
+	mutex_unlock(&chip->prox_mutex);
+
+	return ret;
+}
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
+{
+	/* If Operational settings defined elsewhere.. */
+	if (chip->pdata && chip->pdata->platform_default_settings !=3D 0)
+		memcpy(&(chip->tsl2x7x_settings),
+			chip->pdata->platform_default_settings,
+			sizeof(tsl2x7x_default_settings));
+	else
+		memcpy(&(chip->tsl2x7x_settings),
+			&tsl2x7x_default_settings,
+			sizeof(tsl2x7x_default_settings));
+
+	/* Load up the proper lux table. */
+	if (chip->pdata && chip->pdata->platform_lux_table[0].ratio !=3D 0)
+		memcpy(chip->tsl2x7x_device_lux,
+			chip->pdata->platform_lux_table,
+			sizeof(chip->pdata->platform_lux_table));
+	else
+		memcpy(chip->tsl2x7x_device_lux,
+		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
+				MAX_DEFAULT_TABLE_BYTES);
+
+}
+
+/*
+ * Obtain single reading and calculate the als_gain_trim
+ * (later used to derive actual lux).
+ * Return updated gain_trim value.
+ */
+static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	u8 reg_val;
+	int gain_trim_val;
+	int ret;
+	int lux_val;
+
+	ret =3D i2c_smbus_write_byte(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"%s: failed to write CNTRL register, ret=3D%d\n",
+		__func__, ret);
+		return ret;
+	}
+
+	reg_val =3D i2c_smbus_read_byte(chip->client);
+	if ((reg_val & (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
+		!=3D (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
+		dev_err(&chip->client->dev,
+			"%s: failed: ADC not enabled\n", __func__);
+		return -1;
+	}
+
+	ret =3D i2c_smbus_write_byte(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed to write ctrl reg: ret=3D%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	reg_val =3D i2c_smbus_read_byte(chip->client);
+	if ((reg_val & TSL2X7X_STA_ADC_VALID) !=3D TSL2X7X_STA_ADC_VALID) {
+		dev_err(&chip->client->dev,
+			"%s: failed: STATUS - ADC not valid.\n", __func__);
+		return -ENODATA;
+	}
+
+	lux_val =3D tsl2x7x_get_lux(indio_dev);
+	if (lux_val < 0) {
+		dev_err(&chip->client->dev,
+		"%s: failed to get lux\n", __func__);
+		return lux_val;
+	}
+
+	gain_trim_val =3D  (((chip->tsl2x7x_settings.als_cal_target)
+			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
+	if ((gain_trim_val < 250) || (gain_trim_val > 4000))
+		return -ERANGE;
+
+	chip->tsl2x7x_settings.als_gain_trim =3D gain_trim_val;
+	dev_info(&chip->client->dev,
+		"%s als_calibrate completed\n", chip->client->name);
+
+	return (int) gain_trim_val;
+}
+
+/*
+ * Turn the device on.
+ * Configuration must be set before calling this function.
+ */
+static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
+{
+	int i;
+	int ret =3D 0;
+	u8 *dev_reg;
+	u8 utmp;
+	int als_count;
+	int als_time;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	u8 reg_val =3D 0;
+
+	if (chip->pdata && chip->pdata->power_on)
+		chip->pdata->power_on(indio_dev);
+
+	/* Non calculated parameters */
+	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =3D
+			chip->tsl2x7x_settings.prx_time;
+	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =3D
+			chip->tsl2x7x_settings.wait_time;
+	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =3D
+			chip->tsl2x7x_settings.prox_config;
+
+	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =3D
+		(chip->tsl2x7x_settings.als_thresh_low) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =3D
+		(chip->tsl2x7x_settings.als_thresh_low >> 8) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =3D
+		(chip->tsl2x7x_settings.als_thresh_high) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =3D
+		(chip->tsl2x7x_settings.als_thresh_high >> 8) & 0xFF;
+	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =3D
+		chip->tsl2x7x_settings.als_persistence;
+
+	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =3D
+			chip->tsl2x7x_settings.prox_pulse_count;
+	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =3D
+	chip->tsl2x7x_settings.prox_thres_low;
+	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =3D
+			chip->tsl2x7x_settings.prox_thres_high;
+
+	/* and make sure we're not already on */
+	if (chip->tsl2x7x_chip_status =3D=3D TSL2X7X_CHIP_WORKING) {
+		/* if forcing a register update - turn off, then on */
+		dev_info(&chip->client->dev, "device is already enabled\n");
+		return -EINVAL;
+	}
+
+	/* determine als integration regster */
+	als_count =3D (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
+	if (als_count =3D=3D 0)
+		als_count =3D 1; /* ensure at least one cycle */
+
+	/* convert back to time (encompasses overrides) */
+	als_time =3D (als_count * 27 + 5) / 10;
+	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] =3D 256 - als_count;
+
+	/* Set the gain based on tsl2x7x_settings struct */
+	chip->tsl2x7x_config[TSL2X7X_GAIN] =3D
+		(chip->tsl2x7x_settings.als_gain |
+			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
+			| ((chip->tsl2x7x_settings.prox_gain) << 2));
+
+	/* set chip struct re scaling and saturation */
+	chip->als_saturation =3D als_count * 922; /* 90% of full scale */
+	chip->als_time_scale =3D (als_time + 25) / 50;
+
+	/* TSL2X7X Specific power-on / adc enable sequence
+	 * Power on the device 1st. */
+	utmp =3D TSL2X7X_CNTL_PWR_ON;
+	ret =3D i2c_smbus_write_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed on CNTRL reg.\n", __func__);
+		return -1;
+	}
+
+	/* Use the following shadow copy for our delay before enabling ADC.
+	 * Write all the registers. */
+	for (i =3D 0, dev_reg =3D chip->tsl2x7x_config;
+			i < TSL2X7X_MAX_CONFIG_REG; i++) {
+		ret =3D i2c_smbus_write_byte_data(chip->client,
+				TSL2X7X_CMD_REG + i, *dev_reg++);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+			"%s: failed on write to reg %d.\n", __func__, i);
+			return ret;
+		}
+	}
+
+	udelay(3000);	/* Power-on settling time */
+
+	/* NOW enable the ADC
+	 * initialize the desired mode of operation */
+	utmp =3D TSL2X7X_CNTL_PWR_ON |
+			TSL2X7X_CNTL_ADC_ENBL |
+			TSL2X7X_CNTL_PROX_DET_ENBL;
+	ret =3D i2c_smbus_write_byte_data(chip->client,
+			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"%s: failed on 2nd CTRL reg.\n", __func__);
+		return ret;
+		}
+
+	chip->tsl2x7x_chip_status =3D TSL2X7X_CHIP_WORKING;
+
+	if (chip->tsl2x7x_settings.interrupts_en !=3D 0) {
+		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
+
+		reg_val =3D TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL;
+		if ((chip->tsl2x7x_settings.interrupts_en =3D=3D 0x20) ||
+			(chip->tsl2x7x_settings.interrupts_en =3D=3D 0x30))
+			reg_val |=3D TSL2X7X_CNTL_PROX_DET_ENBL;
+
+		reg_val |=3D chip->tsl2x7x_settings.interrupts_en;
+		ret =3D i2c_smbus_write_byte_data(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
+		if (ret < 0)
+			dev_err(&chip->client->dev,
+				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
+				__func__);
+
+		/* Clear out any initial interrupts  */
+		ret =3D i2c_smbus_write_byte(chip->client,
+			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+			TSL2X7X_CMD_PROXALS_INT_CLR);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				"%s: failed in tsl2x7x_chip_on\n",
+				__func__);
+		return ret;
+		}
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	/* turn device off */
+	chip->tsl2x7x_chip_status =3D TSL2X7X_CHIP_SUSPENDED;
+
+	ret =3D i2c_smbus_write_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
+
+	if (chip->pdata && chip->pdata->power_off)
+		chip->pdata->power_off(chip->client);
+
+	return ret;
+}
+
+/*
+ * Proximity calibration helper function
+ * runs through a collection of data samples,
+ * sets the min, max, mean, and std dev.
+ */
+static
+void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP=
)
+{
+	int i;
+	int min, max, sum, mean;
+	unsigned long stddev;
+	int tmp;
+
+	if (length =3D=3D 0)
+		length =3D 1;
+
+	sum =3D 0;
+	min =3D INT_MAX;
+	max =3D INT_MIN;
+	for (i =3D 0; i < length; i++) {
+		sum +=3D data[i];
+		if (data[i] < min)
+			min =3D data[i];
+		if (data[i] > max)
+			max =3D data[i];
+	}
+	mean =3D sum/length;
+	statP->min =3D min;
+	statP->max =3D max;
+	statP->mean =3D mean;
+
+	sum =3D 0;
+	for (i =3D 0; i < length; i++) {
+		tmp =3D data[i]-mean;
+		sum +=3D tmp * tmp;
+	}
+	stddev =3D int_sqrt((long)sum)/length;
+	statP->stddev =3D stddev;
+}
+
+/**
+ * Proximity calibration - collects a number of samples,
+ * calculates a standard deviation based on the samples, and
+ * sets the threshold accordingly.
+ */
+static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
+{
+	u16 prox_history[MAX_SAMPLES_CAL+1];
+	int i;
+	struct prox_stat prox_stat_data[2];
+	struct prox_stat *calP;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	u8 tmp_irq_settings;
+	u8 current_state =3D chip->tsl2x7x_chip_status;
+
+	if (chip->tsl2x7x_settings.prox_max_samples_cal > MAX_SAMPLES_CAL) {
+		dev_err(&chip->client->dev,
+			"%s: max prox samples cal is too big: %d\n",
+			__func__, chip->tsl2x7x_settings.prox_max_samples_cal);
+		chip->tsl2x7x_settings.prox_max_samples_cal =3D MAX_SAMPLES_CAL;
+	}
+
+	/* have to stop to change settings */
+	tsl2x7x_chip_off(indio_dev);
+
+	/* Enable proximity detection save just in case prox not wanted yet*/
+	tmp_irq_settings =3D chip->tsl2x7x_settings.interrupts_en;
+	chip->tsl2x7x_settings.interrupts_en |=3D TSL2X7X_CNTL_PROX_INT_ENBL;
+
+	/*turn on device if not already on*/
+	tsl2x7x_chip_on(indio_dev);
+
+	/*gather the samples*/
+	for (i =3D 0; i < chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
+		mdelay(15);
+		tsl2x7x_prox_poll(indio_dev);
+		prox_history[i] =3D chip->prox_cur_info.prox_data;
+		dev_info(&chip->client->dev, "2 i=3D%d prox data=3D %d\n",
+			i, chip->prox_cur_info.prox_data);
+	}
+
+	tsl2x7x_chip_off(indio_dev);
+	calP =3D &prox_stat_data[PROX_STAT_CAL];
+	tsl2x7x_prox_calculate(prox_history,
+		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
+	chip->tsl2x7x_settings.prox_thres_high =3D (calP->max << 1) - calP->mean;
+
+	dev_info(&chip->client->dev, " cal min=3D%d mean=3D%d max=3D%d\n",
+		calP->min, calP->mean, calP->max);
+	dev_info(&chip->client->dev,
+		"%s proximity threshold set to %d\n",
+		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
+
+	/* back to the way they were */
+	chip->tsl2x7x_settings.interrupts_en =3D tmp_irq_settings;
+	if (current_state =3D=3D TSL2X7X_CHIP_WORKING)
+		tsl2x7x_chip_on(indio_dev);
+}
+
+static ssize_t tsl2x7x_power_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
+}
+
+static ssize_t tsl2x7x_power_state_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (!value)
+		tsl2x7x_chip_off(indio_dev);
+	else
+		tsl2x7x_chip_on(indio_dev);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_gain_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	if (chip->id > tsl2771)
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
+	else
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
+}
+
+static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
+}
+
+static ssize_t tsl2x7x_als_time_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_time);
+}
+
+static ssize_t tsl2x7x_als_time_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if ((value < 50) || (value > 650))
+		return -EINVAL;
+
+	if (value % 50)
+		return -EINVAL;
+
+	 chip->tsl2x7x_settings.als_time =3D value;
+
+	return len;
+}
+
+static IIO_CONST_ATTR(illuminance0_integration_time_available,
+		"50 100 150 200 250 300 350 400 450 500 550 600 650");
+
+static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_cal_target);
+}
+
+static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		chip->tsl2x7x_settings.als_cal_target =3D value;
+
+	return len;
+}
+
+/* sampling_frequency AKA persistence in data sheet */
+static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_persistence);
+}
+
+static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.als_persistence =3D value;
+
+	return len;
+}
+
+static IIO_CONST_ATTR(sampling_frequency_available,
+		"0x00 - 0xFF (0 - 255)");
+
+static ssize_t tsl2x7x_do_calibrate(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (value)
+		tsl2x7x_als_calibrate(indio_dev);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_luxtable_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int i;
+	int offset =3D 0;
+
+	i =3D 0;
+	while (i < (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
+		offset +=3D snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
+			chip->tsl2x7x_device_lux[i].ratio,
+			chip->tsl2x7x_device_lux[i].ch0,
+			chip->tsl2x7x_device_lux[i].ch1);
+		if (chip->tsl2x7x_device_lux[i].ratio =3D=3D 0) {
+			/* We just printed the first "0" entry.
+			 * Now get rid of the extra "," and break. */
+			offset--;
+			break;
+		}
+		i++;
+	}
+
+	offset +=3D snprintf(buf + offset, PAGE_SIZE, "\n");
+	return offset;
+}
+
+static ssize_t tsl2x7x_luxtable_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
+	int n;
+
+	get_options(buf, ARRAY_SIZE(value), value);
+
+	/* We now have an array of ints starting at value[1], and
+	 * enumerated by value[0].
+	 * We expect each group of three ints is one table entry,
+	 * and the last table entry is all 0.
+	 */
+	n =3D value[0];
+	if ((n % 3) || n < 6 ||
+			n > ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
+		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=3D%d\n", n);
+		return -EINVAL;
+	}
+
+	if ((value[(n - 2)] | value[(n - 1)] | value[n]) !=3D 0) {
+		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=3D%d\n", n);
+		return -EINVAL;
+	}
+
+	if (chip->tsl2x7x_chip_status =3D=3D TSL2X7X_CHIP_WORKING)
+		tsl2x7x_chip_off(indio_dev);
+
+	/* Zero out the table */
+	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
+	memcpy(chip->tsl2x7x_device_lux, &value[1], (value[0] * 4));
+
+	return len;
+}
+
+static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	bool value;
+
+	if (strtobool(buf, &value))
+		return -EINVAL;
+
+	if (value)
+		tsl2x7x_prox_cal(indio_dev);
+
+	return len;
+}
+
+static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
+					 u64 event_code)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int ret;
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) =3D=3D IIO_LIGHT)
+		ret =3D !!(chip->tsl2x7x_settings.interrupts_en & 0x10);
+	else
+		ret =3D !!(chip->tsl2x7x_settings.interrupts_en & 0x20);
+
+	return ret;
+}
+
+static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
+					  u64 event_code,
+					  int val)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) =3D=3D IIO_LIGHT) {
+		if (val)
+			chip->tsl2x7x_settings.interrupts_en |=3D 0x10;
+		else
+			chip->tsl2x7x_settings.interrupts_en &=3D 0x20;
+	} else {
+		if (val)
+			chip->tsl2x7x_settings.interrupts_en |=3D 0x20;
+		else
+			chip->tsl2x7x_settings.interrupts_en &=3D 0x10;
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
+				  u64 event_code,
+				  int val)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) =3D=3D IIO_LIGHT) {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			chip->tsl2x7x_settings.als_thresh_high =3D val;
+			break;
+		case IIO_EV_DIR_FALLING:
+			chip->tsl2x7x_settings.als_thresh_low =3D val;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			chip->tsl2x7x_settings.prox_thres_high =3D val;
+			break;
+		case IIO_EV_DIR_FALLING:
+			chip->tsl2x7x_settings.prox_thres_low =3D val;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
+			       u64 event_code,
+			       int *val)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) =3D=3D IIO_LIGHT) {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			*val =3D chip->tsl2x7x_settings.als_thresh_high;
+			break;
+		case IIO_EV_DIR_FALLING:
+			*val =3D chip->tsl2x7x_settings.als_thresh_low;
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else {
+		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
+		case IIO_EV_DIR_RISING:
+			*val =3D chip->tsl2x7x_settings.prox_thres_high;
+			break;
+		case IIO_EV_DIR_FALLING:
+			*val =3D chip->tsl2x7x_settings.prox_thres_low;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val,
+			    int *val2,
+			    long mask)
+{
+	int ret =3D -EINVAL;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	switch (mask) {
+	case 0:
+		switch (chan->type) {
+		case IIO_LIGHT:
+			tsl2x7x_get_lux(indio_dev);
+			if (chan->processed_val)
+				*val =3D chip->als_cur_info.lux;
+			else
+				*val =3D (((chip->als_cur_info.als_ch0 << 16) |
+					chip->als_cur_info.als_ch1));
+			ret =3D IIO_VAL_INT;
+			break;
+		case IIO_PROXIMITY:
+			tsl2x7x_prox_poll(indio_dev);
+			if (chan->processed_val)
+				*val =3D chip->prox_cur_info.prox_event;
+			else
+				*val =3D chip->prox_cur_info.prox_data;
+			ret =3D IIO_VAL_INT;
+			break;
+		default:
+			return -EINVAL;
+			break;
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type =3D=3D IIO_LIGHT)
+			*val =3D
+			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
+		else
+			*val =3D
+			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
+		ret =3D IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		*val =3D chip->tsl2x7x_settings.als_gain_trim;
+		ret =3D IIO_VAL_INT;
+		break;
+
+	default:
+		ret =3D -EINVAL;
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       int val,
+			       int val2,
+			       long mask)
+{
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_CALIBSCALE:
+		if (chan->type =3D=3D IIO_LIGHT) {
+			switch (val) {
+			case 1:
+				chip->tsl2x7x_settings.als_gain =3D 0;
+				break;
+			case 8:
+				chip->tsl2x7x_settings.als_gain =3D 1;
+				break;
+			case 16:
+				chip->tsl2x7x_settings.als_gain =3D 2;
+				break;
+			case 120:
+				if (chip->id > tsl2771)
+					return -EINVAL;
+				chip->tsl2x7x_settings.als_gain =3D 3;
+				break;
+			case 128:
+				if (chip->id < tsl2572)
+					return -EINVAL;
+				chip->tsl2x7x_settings.als_gain =3D 3;
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else {
+			switch (val) {
+			case 1:
+				chip->tsl2x7x_settings.prox_gain =3D 0;
+				break;
+			case 2:
+				chip->tsl2x7x_settings.prox_gain =3D 1;
+				break;
+			case 4:
+				chip->tsl2x7x_settings.prox_gain =3D 2;
+				break;
+			case 8:
+				chip->tsl2x7x_settings.prox_gain =3D 3;
+				break;
+			default:
+				return -EINVAL;
+			}
+		}
+		break;
+	case IIO_CHAN_INFO_CALIBBIAS:
+		chip->tsl2x7x_settings.als_gain_trim =3D val;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
+
+static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
+		tsl2x7x_prox_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
+		tsl2x7x_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
+
+static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
+
+static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
+		tsl2x7x_do_calibrate);
+
+static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
+		tsl2x7x_do_prox_calibrate);
+
+static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
+		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
+
+/* Use the default register values to identify the Taos device */
+static int tsl2x7x_device_id(unsigned char *id, int target)
+{
+	switch (target) {
+	case tsl2571:
+	case tsl2671:
+	case tsl2771:
+		return ((*id & 0xf0) =3D=3D TRITON_ID);
+	break;
+	case tmd2671:
+	case tmd2771:
+		return ((*id & 0xf0) =3D=3D HALIBUT_ID);
+	break;
+	case tsl2572:
+	case tsl2672:
+	case tmd2672:
+	case tsl2772:
+	case tmd2772:
+		return ((*id & 0xf0) =3D=3D SWORDFISH_ID);
+	break;
+	}
+
+	return -EINVAL;
+}
+
+/*
+ * Interrupt Event Handler */
+static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
+{
+	struct iio_dev *indio_dev =3D private;
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	s64 timestamp =3D iio_get_time_ns();
+	int ret;
+	int value;
+
+	value =3D i2c_smbus_read_byte_data(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
+
+	/* What type of interrupt do we need to process */
+	if (value & TSL2X7X_STA_PRX_INTR) {
+		tsl2x7x_prox_poll(indio_dev);
+		iio_push_event(indio_dev,
+			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
+						    0,
+						    IIO_EV_TYPE_THRESH,
+						    IIO_EV_DIR_EITHER),
+						    timestamp);
+	}
+
+	if (value & TSL2X7X_STA_ALS_INTR) {
+		tsl2x7x_get_lux(indio_dev);
+		iio_push_event(indio_dev,
+		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+					    0,
+					    IIO_EV_TYPE_THRESH,
+					    IIO_EV_DIR_EITHER),
+					    timestamp);
+	}
+	/* Clear interrupt now that we have the status */
+	ret =3D i2c_smbus_write_byte(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+		TSL2X7X_CMD_PROXALS_INT_CLR);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+			"%s: Failed to clear irq from event handler. err =3D %d\n",
+			__func__, ret);
+
+	return IRQ_HANDLED;
+}
+
+static struct attribute *tsl2x7x_ALS_device_attrs[] =3D {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_PRX_device_attrs[] =3D {
+	&dev_attr_power_state.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX_device_attrs[] =3D {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_PRX2_device_attrs[] =3D {
+	&dev_attr_power_state.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	&dev_attr_proximity_calibscale_available.attr,
+	NULL
+};
+
+static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] =3D {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
+	&dev_attr_illuminance0_target_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_sampling_frequency.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	&dev_attr_proximity_calibrate.attr,
+	&dev_attr_proximity_calibscale_available.attr,
+	NULL
+};
+
+static const struct attribute_group tsl2X7X_device_attr_group_tbl[] =3D {
+	[ALS] =3D {
+		.attrs =3D tsl2x7x_ALS_device_attrs,
+	},
+	[PRX] =3D {
+		.attrs =3D tsl2x7x_PRX_device_attrs,
+	},
+	[ALSPRX] =3D {
+		.attrs =3D tsl2x7x_ALSPRX_device_attrs,
+	},
+	[PRX2] =3D {
+		.attrs =3D tsl2x7x_PRX2_device_attrs,
+	},
+	[ALSPRX2] =3D {
+		.attrs =3D tsl2x7x_ALSPRX2_device_attrs,
+	},
+};
+
+static const struct iio_info tsl2X7X_device_info[] =3D {
+	[ALS] =3D {
+		.attrs =3D &tsl2X7X_device_attr_group_tbl[ALS],
+		.driver_module =3D THIS_MODULE,
+		.read_raw =3D &tsl2x7x_read_raw,
+		.write_raw =3D &tsl2x7x_write_raw,
+		.read_event_value =3D &tsl2x7x_read_thresh,
+		.write_event_value =3D &tsl2x7x_write_thresh,
+		.read_event_config =3D &tsl2x7x_read_interrupt_config,
+		.write_event_config =3D &tsl2x7x_write_interrupt_config,
+	},
+	[PRX] =3D {
+		.attrs =3D &tsl2X7X_device_attr_group_tbl[PRX],
+		.driver_module =3D THIS_MODULE,
+		.read_raw =3D &tsl2x7x_read_raw,
+		.write_raw =3D &tsl2x7x_write_raw,
+		.read_event_value =3D &tsl2x7x_read_thresh,
+		.write_event_value =3D &tsl2x7x_write_thresh,
+		.read_event_config =3D &tsl2x7x_read_interrupt_config,
+		.write_event_config =3D &tsl2x7x_write_interrupt_config,
+	},
+	[ALSPRX] =3D {
+		.attrs =3D &tsl2X7X_device_attr_group_tbl[ALSPRX],
+		.driver_module =3D THIS_MODULE,
+		.read_raw =3D &tsl2x7x_read_raw,
+		.write_raw =3D &tsl2x7x_write_raw,
+		.read_event_value =3D &tsl2x7x_read_thresh,
+		.write_event_value =3D &tsl2x7x_write_thresh,
+		.read_event_config =3D &tsl2x7x_read_interrupt_config,
+		.write_event_config =3D &tsl2x7x_write_interrupt_config,
+	},
+	[PRX2] =3D {
+		.attrs =3D &tsl2X7X_device_attr_group_tbl[PRX2],
+		.driver_module =3D THIS_MODULE,
+		.read_raw =3D &tsl2x7x_read_raw,
+		.write_raw =3D &tsl2x7x_write_raw,
+		.read_event_value =3D &tsl2x7x_read_thresh,
+		.write_event_value =3D &tsl2x7x_write_thresh,
+		.read_event_config =3D &tsl2x7x_read_interrupt_config,
+		.write_event_config =3D &tsl2x7x_write_interrupt_config,
+	},
+	[ALSPRX2] =3D {
+		.attrs =3D &tsl2X7X_device_attr_group_tbl[ALSPRX2],
+		.driver_module =3D THIS_MODULE,
+		.read_raw =3D &tsl2x7x_read_raw,
+		.write_raw =3D &tsl2x7x_write_raw,
+		.read_event_value =3D &tsl2x7x_read_thresh,
+		.write_event_value =3D &tsl2x7x_write_thresh,
+		.read_event_config =3D &tsl2x7x_read_interrupt_config,
+		.write_event_config =3D &tsl2x7x_write_interrupt_config,
+	},
+};
+
+static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] =3D {
+	[ALS] =3D {
+		.channel =3D {
+			[0] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[1] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.info_mask =3D
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels =3D 2,
+	.info =3D &tsl2X7X_device_info[ALS],
+	},
+	[PRX] =3D {
+		.channel =3D {
+			[0] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[1] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels =3D 2,
+	.info =3D &tsl2X7X_device_info[PRX],
+	},
+	[ALSPRX] =3D {
+		.channel =3D {
+			[0] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[1] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.info_mask =3D
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+			[2] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[3] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 0,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels =3D 4,
+	.info =3D &tsl2X7X_device_info[ALSPRX],
+	},
+	[PRX2] =3D {
+		.channel =3D {
+			[0] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[1] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.info_mask =3D
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels =3D 2,
+	.info =3D &tsl2X7X_device_info[PRX2],
+	},
+	[ALSPRX2] =3D {
+		.channel =3D {
+				[0] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[1] =3D {
+				.type =3D IIO_LIGHT,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.info_mask =3D
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
+					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+			[2] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.processed_val =3D 1,
+			},
+			[3] =3D {
+				.type =3D IIO_PROXIMITY,
+				.indexed =3D 1,
+				.channel =3D 0,
+				.info_mask =3D
+					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
+				.event_mask =3D TSL2X7X_EVENT_MASK
+			},
+		},
+	.num_channels =3D 4,
+	.info =3D &tsl2X7X_device_info[ALSPRX2],
+	},
+};
+
+/*
+ * Client probe function.
+ */
+static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
+	const struct i2c_device_id *id)
+{
+	int ret;
+	unsigned char device_id;
+	struct iio_dev *indio_dev;
+	struct tsl2X7X_chip *chip;
+
+	indio_dev =3D iio_allocate_device(sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip =3D iio_priv(indio_dev);
+	chip->client =3D clientp;
+	i2c_set_clientdata(clientp, indio_dev);
+
+	ret =3D tsl2x7x_i2c_read(chip->client,
+		TSL2X7X_CHIPID, &device_id);
+	if (ret < 0)
+		goto fail1;
+
+	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
+		(tsl2x7x_device_id(&device_id, id->driver_data) =3D=3D -EINVAL)) {
+		dev_info(&chip->client->dev,
+				"i2c device found does not match expected id in %s\n",
+				__func__);
+		goto fail1;
+	}
+
+	ret =3D i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&clientp->dev, "%s: write to cmd reg failed. err =3D %d\n",
+				__func__, ret);
+		goto fail1;
+	}
+
+	/* ALS and PROX functions can be invoked via user space poll
+	 * or H/W interrupt. If busy return last sample. */
+	mutex_init(&chip->als_mutex);
+	mutex_init(&chip->prox_mutex);
+
+	chip->tsl2x7x_chip_status =3D TSL2X7X_CHIP_UNKNOWN;
+	chip->pdata =3D clientp->dev.platform_data;
+	chip->id =3D id->driver_data;
+	chip->chip_info =3D
+		&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
+
+	indio_dev->info =3D chip->chip_info->info;
+	indio_dev->dev.parent =3D &clientp->dev;
+	indio_dev->modes =3D INDIO_DIRECT_MODE;
+	indio_dev->name =3D chip->client->name;
+	indio_dev->channels =3D chip->chip_info->channel;
+	indio_dev->num_channels =3D chip->chip_info->num_channels;
+
+	if (clientp->irq) {
+		ret =3D request_threaded_irq(clientp->irq,
+					   NULL,
+					   &tsl2x7x_event_handler,
+					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+					   "TSL2X7X_event",
+					   indio_dev);
+		if (ret) {
+			dev_err(&clientp->dev,
+				"%s: irq request failed", __func__);
+			goto fail2;
+		}
+	}
+
+	/* Load up the defaults */
+	tsl2x7x_defaults(chip);
+	/* Make sure the chip is on */
+	tsl2x7x_chip_on(indio_dev);
+
+	ret =3D iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&clientp->dev,
+			"%s: iio registration failed\n", __func__);
+		goto fail3;
+	}
+
+	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
+
+	return 0;
+
+fail3:
+	if (clientp->irq)
+		free_irq(clientp->irq, indio_dev);
+fail2:
+	iio_free_device(indio_dev);
+fail1:
+	kfree(chip);
+	return ret;
+}
+
+static int tsl2x7x_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int ret =3D 0;
+
+	if (chip->tsl2x7x_chip_status =3D=3D TSL2X7X_CHIP_WORKING) {
+		ret =3D tsl2x7x_chip_off(indio_dev);
+		chip->tsl2x7x_chip_status =3D TSL2X7X_CHIP_SUSPENDED;
+	}
+
+	if (chip->pdata && chip->pdata->platform_power) {
+		pm_message_t pmm =3D {PM_EVENT_SUSPEND};
+		chip->pdata->platform_power(dev, pmm);
+	}
+
+	return ret;
+}
+
+static int tsl2x7x_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev =3D dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip =3D iio_priv(indio_dev);
+	int ret =3D 0;
+
+	if (chip->pdata && chip->pdata->platform_power) {
+		pm_message_t pmm =3D {PM_EVENT_RESUME};
+		chip->pdata->platform_power(dev, pmm);
+	}
+
+	if (chip->tsl2x7x_chip_status =3D=3D TSL2X7X_CHIP_SUSPENDED)
+		ret =3D tsl2x7x_chip_on(indio_dev);
+
+	return ret;
+}
+
+static int __devexit tsl2x7x_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev =3D i2c_get_clientdata(client);
+	struct tsl2X7X_chip *chip =3D i2c_get_clientdata(client);
+
+	tsl2x7x_chip_off(indio_dev);
+
+	if (client->irq)
+		free_irq(client->irq, chip->client->name);
+
+	iio_free_device(indio_dev);
+
+	return 0;
+}
+
+static struct i2c_device_id tsl2x7x_idtable[] =3D {
+	{ "tsl2571", tsl2571 },
+	{ "tsl2671", tsl2671 },
+	{ "tmd2671", tmd2671 },
+	{ "tsl2771", tsl2771 },
+	{ "tmd2771", tmd2771 },
+	{ "tsl2572", tsl2572 },
+	{ "tsl2672", tsl2672 },
+	{ "tmd2672", tmd2672 },
+	{ "tsl2772", tsl2772 },
+	{ "tmd2772", tmd2772 },
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
+
+static const struct dev_pm_ops tsl2x7x_pm_ops =3D {
+	.suspend =3D tsl2x7x_suspend,
+	.resume  =3D tsl2x7x_resume,
+};
+
+/* Driver definition */
+static struct i2c_driver tsl2x7x_driver =3D {
+	.driver =3D {
+		.name =3D "tsl2x7x",
+		.pm =3D &tsl2x7x_pm_ops,
+	},
+	.id_table =3D tsl2x7x_idtable,
+	.probe =3D tsl2x7x_probe,
+	.remove =3D __devexit_p(tsl2x7x_remove),
+};
+
+module_i2c_driver(tsl2x7x_driver);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver=
");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/iio/light/tsl2x7x_core.h b/drivers/staging/iio=
/light/tsl2x7x_core.h
new file mode 100644
index 0000000..663e846
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.h
@@ -0,0 +1,75 @@
+/*
+ * Device driver for monitoring ambient light intensity (lux)
+ * and proximity (prox) within the TAOS TSL2X7X family of devices.
+ *
+ * Copyright (c) 2012, TAOS Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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 WIT=
HOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License f=
or
+ * 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 Street, Fifth Floor, Boston, MA	02110-1301, USA.
+ */
+
+#ifndef __TSL2X7X_H
+#define __TSL2X7X_H
+#include <linux/pm.h>
+
+/* Max number of segments allowable in LUX table */
+#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
+#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE)
+
+struct iio_dev;
+struct iio_chan_spec;
+
+struct tsl2x7x_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;
+};
+
+/* Refer to tsl2x7x_default_settings for member desc. */
+struct tsl2x7x_settings {
+	int als_time;
+	int als_gain;
+	int als_gain_trim;
+	int wait_time;
+	int prx_time;
+	int prox_gain;
+	int prox_config;
+	int als_cal_target;
+	u8  interrupts_en;
+	u8  als_persistence;
+	int als_thresh_low;
+	int als_thresh_high;
+	int prox_thres_low;
+	int prox_thres_high;
+	int prox_pulse_count;
+	int prox_max_samples_cal;
+};
+
+/* struct tsl2x7x_platform_data -
+ * Platform unique glass and defaults
+ * Platform PM functions. */
+struct tsl2X7X_platform_data {
+	/* Suspend/resume platform cb */
+	int (*platform_power)(struct device *dev, pm_message_t);
+	/* The following callback gets called when the device is powered on */
+	int (*power_on)      (struct iio_dev *indio_dev);
+	/* The following callback gets called when the device is powered off */
+	int (*power_off)     (struct i2c_client *dev);
+	/* These are the device specific glass coefficents used to
+	 * calculate Lux */
+	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
+	struct tsl2x7x_settings *platform_default_settings;
+};
+
+#endif /* __TSL2X7X_H */
--
1.7.4.1

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH V4] TAOS tsl2x7x
  2012-03-21 16:16 ` Jon Brenner
  (?)
@ 2012-03-23 13:53 ` Jonathan Cameron
  2012-03-27 19:58   ` Jon Brenner
  -1 siblings, 1 reply; 8+ messages in thread
From: Jonathan Cameron @ 2012-03-23 13:53 UTC (permalink / raw)
  To: Jon Brenner; +Cc: linux-iio, Linux Kernel

On 3/21/2012 4:16 PM, Jon Brenner wrote:
> TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families (inc. all variants).
Mostly looking good...

Couple of issues remaining.
* Why have processed and raw accesses to the same channels?  That is 
definitely not the intent.
Either it's worth processing these raw adc channels in kernel, or not. 
If it isn't you certainly shouldn't
be munging together the two adc's into a single value.

So you have done this for two reasons...
* For light sensors it was to allow the processed illuminance value and 
also the raw adc values.
Previously we have done this by having IIO_LIGHT (and hence illuminance) 
for the processed one
and marking the other two as IIO_INTENSITY with modifiers for the 
frequency range they cover...
* For proximity one is the raw reading (fine), the other is a means of 
getting at the threshold event
if interrupts are not supported.  It is done by a software comparison of 
the threshold and the raw
reading.  This should not be in driver as if the functionality is 
desired, it should be done in userspace.

Various other minor bits like error paths that don't clean up commented 
inline.


>
> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
> ---
>   .../light/sysfs-bus-iio-light-tsl2583              |    6 +
>   .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
>   drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
>   .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
>   .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
>   drivers/staging/iio/light/Kconfig                  |    8 +
>   drivers/staging/iio/light/Makefile                 |    1 +
>   drivers/staging/iio/light/tsl2x7x_core.c           | 1911 ++++++++++++++++++++
>   drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
>   9 files changed, 2032 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> new file mode 100644
> index 0000000..8f2a038
> --- /dev/null
> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> @@ -0,0 +1,6 @@
> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> +KernelVersion:	2.6.37
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This property causes an internal calibration of the als gain trim
> +		value which is later used in calculating illuminance in lux.
> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> new file mode 100644
> index 0000000..cceadae
> --- /dev/null
> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> @@ -0,0 +1,20 @@
> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> +KernelVersion:	2.6.37
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		This property causes an internal calibration of the als gain trim
> +		value which is later used in calculating illuminance in lux.
> +
> +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Simultainious ALS channel data.
That wasn't the intent of 'both' at all.  (+ typo).  It means a raw 
reading from a diod that
detects the 'sum' of infrared and visible.
> +
> +What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Causes an recalculation and adjustment to the
> +		proximity_thresh_rising_value.
> +
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio b/drivers/staging/iio/Documentation/sysfs-bus-iio
> index 46a995d..5b2b5d3 100644
> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio
> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
> @@ -258,6 +258,8 @@ What		/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
>   What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
>   What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
>   What		/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
> +what		/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
> +what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
>   KernelVersion:	2.6.35
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -457,6 +459,10 @@ What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
>   What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
> +What:		/sys/.../events/illuminance0_thresh_falling_value
> +what:		/sys/.../events/illuminance0_thresh_rising_value
> +what:		/sys/.../events/proximity_thresh_falling_value
> +what:		/sys/.../events/proximity_thresh_rising_value
>   KernelVersion:	2.6.37
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> @@ -739,3 +745,4 @@ Description:
>   		system. To minimize the current consumption of the system,
>   		the bridge can be disconnected (when it is not being used
>   		using the bridge_switch_en attribute.
> +
spurious blank line?
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> index edbf470..4385c70 100644
> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> @@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
>   Description:
>   		This property gets/sets the sensors ADC analog integration time.
>
> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
> +What:		/sys/bus/iio/devices/device[n]/lux_table
>   KernelVersion:	2.6.37
>   Contact:	linux-iio@vger.kernel.org
>   Description:
> -		Hardware or software applied calibration scale factor assumed
> -		to account for attenuation due to industrial design (glass
> -		filters or aperture holes).
> +		This property gets/sets the table of coefficients
> +		used in calculating illuminance in lux.
> +
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583 b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> deleted file mode 100644
> index 660781d..0000000
> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> +++ /dev/null
> @@ -1,20 +0,0 @@
> -What:		/sys/bus/iio/devices/device[n]/lux_table
> -KernelVersion:	2.6.37
> -Contact:	linux-iio@vger.kernel.org
> -Description:
> -		This property gets/sets the table of coefficients
> -		used in calculating illuminance in lux.
> -
> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> -KernelVersion:	2.6.37
> -Contact:	linux-iio@vger.kernel.org
> -Description:
> -		This property causes an internal calibration of the als gain trim
> -		value which is later used in calculating illuminance in lux.
> -
> -What:		/sys/bus/iio/devices/device[n]/illuminance0_input_target
> -KernelVersion:	2.6.37
> -Contact:	linux-iio@vger.kernel.org
> -Description:
> -		This property is the known externally illuminance (in lux).
> -		It is used in the process of calibrating the device accuracy.
> diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
> index e7e9159..976f790 100644
> --- a/drivers/staging/iio/light/Kconfig
> +++ b/drivers/staging/iio/light/Kconfig
> @@ -31,4 +31,12 @@ config TSL2583
>   	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
>   	 Access ALS data via iio, sysfs.
>
> +config TSL2x7x
> +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
> +	depends on I2C
> +	help
> +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
> +	 tmd2672, tsl2772, tmd2772 devices.
> +	 Provides iio_events and direct access via sysfs.
> +
>   endmenu
> diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile
> index 3011fbf..ff12c4b 100644
> --- a/drivers/staging/iio/light/Makefile
> +++ b/drivers/staging/iio/light/Makefile
> @@ -5,3 +5,4 @@
>   obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
>   obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
>   obj-$(CONFIG_TSL2583)	+= tsl2583.o
> +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
> diff --git a/drivers/staging/iio/light/tsl2x7x_core.c b/drivers/staging/iio/light/tsl2x7x_core.c
> new file mode 100644
> index 0000000..c0d9d6e
> --- /dev/null
> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
> @@ -0,0 +1,1911 @@
> +/*
> + * Device driver for monitoring ambient light intensity in (lux)
> + * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
> + *
> + * Copyright (c) 2012, TAOS Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; 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.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
> + */
> +
> +#include<linux/kernel.h>
> +#include<linux/i2c.h>
> +#include<linux/errno.h>
> +#include<linux/delay.h>
> +#include<linux/mutex.h>
> +#include<linux/interrupt.h>
> +#include<linux/slab.h>
> +#include<linux/module.h>
> +#include<linux/version.h>
> +#include "tsl2x7x_core.h"
> +#include "../events.h"
> +#include "../iio.h"
> +#include "../sysfs.h"
> +
> +/* Cal defs*/
> +#define PROX_STAT_CAL        0
> +#define PROX_STAT_SAMP       1
> +#define MAX_SAMPLES_CAL      200
> +
> +/* TSL2X7X Device ID */
> +#define TRITON_ID    0x00
> +#define SWORDFISH_ID 0x30
> +#define HALIBUT_ID   0x20
> +
> +/* Lux calculation constants */
> +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
> +
> +/* TAOS Register definitions - note:
> + * depending on device, some of these register are not used and the
> + * register address is benign.
> + */
> +/* 2X7X register offsets */
> +#define TSL2X7X_MAX_CONFIG_REG         16
> +
> +/* Device Registers and Masks */
> +#define TSL2X7X_CNTRL                  0x00
> +#define TSL2X7X_ALS_TIME               0X01
> +#define TSL2X7X_PRX_TIME               0x02
> +#define TSL2X7X_WAIT_TIME              0x03
> +#define TSL2X7X_ALS_MINTHRESHLO        0X04
> +#define TSL2X7X_ALS_MINTHRESHHI        0X05
> +#define TSL2X7X_ALS_MAXTHRESHLO        0X06
> +#define TSL2X7X_ALS_MAXTHRESHHI        0X07
> +#define TSL2X7X_PRX_MINTHRESHLO        0X08
> +#define TSL2X7X_PRX_MINTHRESHHI        0X09
> +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
> +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
> +#define TSL2X7X_PERSISTENCE            0x0C
> +#define TSL2X7X_PRX_CONFIG             0x0D
> +#define TSL2X7X_PRX_COUNT              0x0E
> +#define TSL2X7X_GAIN                   0x0F
> +#define TSL2X7X_NOTUSED                0x10
> +#define TSL2X7X_REVID                  0x11
> +#define TSL2X7X_CHIPID                 0x12
> +#define TSL2X7X_STATUS                 0x13
> +#define TSL2X7X_ALS_CHAN0LO            0x14
> +#define TSL2X7X_ALS_CHAN0HI            0x15
> +#define TSL2X7X_ALS_CHAN1LO            0x16
> +#define TSL2X7X_ALS_CHAN1HI            0x17
> +#define TSL2X7X_PRX_LO                 0x18
> +#define TSL2X7X_PRX_HI                 0x19
> +
> +/* tsl2X7X cmd reg masks */
> +#define TSL2X7X_CMD_REG                0x80
> +#define TSL2X7X_CMD_SPL_FN             0x60
> +
> +#define TSL2X7X_CMD_PROX_INT_CLR       0X05
> +#define TSL2X7X_CMD_ALS_INT_CLR        0x06
> +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
> +
> +/* tsl2X7X cntrl reg masks */
> +#define TSL2X7X_CNTL_ADC_ENBL          0x02
> +#define TSL2X7X_CNTL_PWR_ON            0x01
> +
> +/* tsl2X7X status reg masks */
> +#define TSL2X7X_STA_ADC_VALID          0x01
> +#define TSL2X7X_STA_PRX_VALID          0x02
> +#define TSL2X7X_STA_ADC_PRX_VALID      0x03
> +#define TSL2X7X_STA_ALS_INTR           0x10
> +#define TSL2X7X_STA_ADC_INTR           0x10
> +#define TSL2X7X_STA_PRX_INTR           0x20
> +
> +#define TSL2X7X_STA_ADC_INTR           0x10
> +
> +/* tsl2X7X cntrl reg masks */
> +#define TSL2X7X_CNTL_REG_CLEAR         0x00
> +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
> +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
> +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
> +#define TSL2X7X_CNTL_PWRON             0x01
> +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
> +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
> +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
> +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
> +
> +/*Prox diode to use */
> +#define TSL2X7X_DIODE0                 0x10
> +#define TSL2X7X_DIODE1                 0x20
> +#define TSL2X7X_DIODE_BOTH             0x30
> +
> +/* LED Power */
> +#define TSL2X7X_mA100                  0x00
> +#define TSL2X7X_mA50                   0x40
> +#define TSL2X7X_mA25                   0x80
> +#define TSL2X7X_mA13                   0xD0
> +
> +/*Common device IIO EventMask */
> +#define TSL2X7X_EVENT_MASK \
> +		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
> +		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
> +
> +/* TAOS txx2x7x Device family members */
> +enum {
> +	tsl2571,
> +	tsl2671,
> +	tmd2671,
> +	tsl2771,
> +	tmd2771,
> +	tsl2572,
> +	tsl2672,
> +	tmd2672,
> +	tsl2772,
> +	tmd2772
> +};
> +
> +enum {
> +	TSL2X7X_CHIP_UNKNOWN = 0,
> +	TSL2X7X_CHIP_WORKING = 1,
> +	TSL2X7X_CHIP_SUSPENDED = 2
> +};
> +
> +/* Per-device data */
> +struct tsl2x7x_als_info {
> +	u16 als_ch0;
> +	u16 als_ch1;
> +	u16 lux;
> +};
> +
> +/* proximity data */
> +struct tsl2x7x_prox_info {
> +	u16 prox_data;
> +	int prox_event;
> +};
> +
> +struct prox_stat {
> +	u16 min;
> +	u16 max;
> +	u16 mean;
> +	unsigned long stddev;
> +};
> +
> +struct tsl2x7x_chip_info {
> +	int num_channels;
> +	struct iio_chan_spec		channel[9];
> +	const struct iio_info		*info;
> +};
> +
> +struct tsl2X7X_chip {
> +	kernel_ulong_t id;
> +	struct mutex prox_mutex;
> +	struct mutex als_mutex;
> +	struct i2c_client *client;
> +	struct tsl2x7x_prox_info prox_cur_info;
> +	struct tsl2x7x_als_info als_cur_info;
> +	struct tsl2x7x_settings tsl2x7x_settings;
> +	struct tsl2X7X_platform_data *pdata;
> +	int als_time_scale;
> +	int als_saturation;
> +	int tsl2x7x_chip_status;
> +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
> +	const struct tsl2x7x_chip_info	*chip_info;
> +	const struct iio_info *info;
> +	s64 event_timestamp;
> +	/* This structure is intentionally large to accommodate
> +	 * updates via sysfs. */
> +	/* Sized to 9 = max 8 segments + 1 termination segment */
> +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
> +};
> +
> +/* Different devices require different coefficents */
> +static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
> +	{ 14461,   611,   1211 },
> +	{ 18540,   352,    623 },
> +	{     0,     0,      0 },
> +};
> +
> +static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
> +	{ 11635,   115,    256 },
> +	{ 15536,    87,    179 },
> +	{     0,     0,      0 },
> +};
> +
> +static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
> +	{ 14013,   466,   917 },
> +	{ 18222,   310,   552 },
> +	{     0,     0,     0 },
> +};
> +
> +static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
> +	{ 13218,   130,   262 },
> +	{ 17592,   92,    169 },
> +	{     0,     0,     0 },
> +};
> +
> +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
check white space around here.  looks like a mixture of tabs and spaces...
> +	[tsl2571] = tsl2x71_lux_table,
> +	[tsl2671] =	tsl2x71_lux_table,
> +	[tmd2671] =	tmd2x71_lux_table,
> +	[tsl2771] =	tsl2x71_lux_table,
> +	[tmd2771] =	tmd2x71_lux_table,
> +	[tsl2572] =	tsl2x72_lux_table,
> +	[tsl2672] =	tsl2x72_lux_table,
> +	[tmd2672] = tmd2x72_lux_table,
> +	[tsl2772] =	tsl2x72_lux_table,
> +	[tmd2772] =	tmd2x72_lux_table,
> +};
> +
> +static const struct tsl2x7x_settings tsl2x7x_default_settings = {
> +	.als_time = 200,
> +	/* must be a multiple of 50mS */
> +	.als_gain = 0,
> +	/* this is actually an index into the gain table */
> +	.prx_time = 0xfe, /*5.4 mS */
> +	/* 2.7ms prox integration time - decrease to increase time */
> +	/* decreases in 2.7 ms intervals */
> +	.prox_gain = 1,
> +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
> +	/* assume clear glass as default */
> +	.wait_time = 245,
> +	/* Time between PRX and ALS cycles -decrease to increase time */
> +	/* decreases in 2.7 ms intervals */
> +	.prox_config = 0,
> +	/* Prox configuration filters */
> +	.als_gain_trim = 1000,
> +	/* default gain trim to account for aperture effects */
> +	.als_cal_target = 150,
> +	/* Known external ALS reading used for calibration */
> +	.als_thresh_low = 200,
> +	/* CH0 'low' count to trigger interrupt */
> +	.als_thresh_high = 256,
> +	/* CH0 'high' count to trigger interrupt */
> +	.als_persistence = 0xFF,
> +	/* Number of 'out of limits' ADC readings PRX/ALS*/
> +	.interrupts_en = 0x00,
> +	/* Default interrupt(s) enabled.
> +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
> +	.prox_thres_low  = 0,
> +	.prox_thres_high = 512,
> +	/*default threshold adjust either manually or with cal routine*/
> +	.prox_max_samples_cal = 30,
> +	.prox_pulse_count = 8
> +};
> +
> +static const s16 tsl2X7X_als_gainadj[] = {
> +	1,
> +	8,
> +	16,
> +	120
> +};
> +
> +static const s16 tsl2X7X_prx_gainadj[] = {
> +	1,
> +	2,
> +	4,
> +	8
> +};
> +
> +/* Channel variations */
> +enum {
> +	ALS,
> +	PRX,
> +	ALSPRX,
> +	PRX2,
> +	ALSPRX2,
> +};
> +
> +const u8 device_channel_config[] = {
> +	ALS,
> +	PRX,
> +	PRX,
> +	ALSPRX,
> +	ALSPRX,
> +	ALS,
> +	PRX2,
> +	PRX2,
> +	ALSPRX2,
> +	ALSPRX2
> +};
> +
> +/*
> + * Read a number of bytes starting at register (reg) location.
> + * Return 0, or i2c_smbus_write_byte ERROR code.
> + */
> +static int
> +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
> +{
> +	int ret;
> +
> +	/* select register to write */
> +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
> +	if (ret<  0) {
> +		dev_err(&client->dev, "%s: failed to write register %x\n"
> +				, __func__, reg);
> +		return ret;
> +	}
> +	/* read the data */
> +	*val = i2c_smbus_read_byte(client);
> +
> +	return 0;
> +}
> +
I would much prefer if you'd use kernel-doc for comments oabout functions.
> +/*
> + * Reads and calculates current lux value.
> + * The raw ch0 and ch1 values of the ambient light sensed in the last
> + * integration cycle are read from the device.
> + * Time scale factor array values are adjusted based on the integration time.
> + * The raw values are multiplied by a scale factor, and device gain is obtained
> + * using gain index. Limit checks are done next, then the ratio of a multiple
> + * of ch1 value, to the ch0 value, is calculated. The array tsl2x7x_device_lux[]
> + * declared above is then scanned to find the first ratio value that is just
> + * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
> + * the array are then used along with the time scale factor array values, to
> + * calculate the lux.
> + */
> +static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
> +{
> +	u16 ch0, ch1; /* separated ch0/ch1 data from device */
> +	u32 lux; /* raw lux calculated from device data */
> +	u64 lux64;
> +	u32 ratio;
> +	u8 buf[4];
> +	struct tsl2x7x_lux *p;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int i, ret;
> +	u32 ch0lux = 0;
> +	u32 ch1lux = 0;
> +
> +	if (mutex_trylock(&chip->als_mutex) == 0) {
> +		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is busy\n");
> +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
> +	}
> +
> +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
> +		/* device is not enabled */
> +		dev_err(&chip->client->dev, "%s: device is not enabled\n",
> +				__func__);
> +		ret = -EBUSY ;
> +		goto out_unlock;
> +	}
> +
> +	ret = tsl2x7x_i2c_read(chip->client,
> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed to read CMD_REG\n", __func__);
> +		goto out_unlock;
> +	}
> +	/* is data new&  valid */
> +	if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
> +		dev_err(&chip->client->dev,
> +			"%s: data not valid yet\n", __func__);
> +		ret = chip->als_cur_info.lux; /* return LAST VALUE */
> +		goto out_unlock;
> +	}
> +
> +	for (i = 0; i<  4; i++) {
> +		ret = tsl2x7x_i2c_read(chip->client,
> +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
> +			&buf[i]);
> +		if (ret<  0) {
> +			dev_err(&chip->client->dev,
> +				"%s: failed to read. err=%x\n", __func__, ret);
> +			goto out_unlock;
> +		}
> +	}
> +
> +	/* clear status, really interrupt status ( are off),
> +	but we use the bit anyway */
> +	ret = i2c_smbus_write_byte(chip->client,
> +		(TSL2X7X_CMD_REG |
> +				TSL2X7X_CMD_SPL_FN |
> +				TSL2X7X_CMD_ALS_INT_CLR));
> +
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +		"i2c_write_command failed in %s, err = %d\n",
> +			__func__, ret);
> +		goto out_unlock; /* have no data, so return failure */
> +	}
> +
> +	/* extract ALS/lux data */
> +	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
> +	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
> +
> +	chip->als_cur_info.als_ch0 = ch0;
> +	chip->als_cur_info.als_ch1 = ch1;
> +
> +	if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) {
> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> +		goto return_max;
> +	}
> +
> +	if (ch0 == 0) {
> +		/* have no data, so return LAST VALUE */
> +		ret = chip->als_cur_info.lux = 0;
> +		goto out_unlock;
> +	}
> +	/* calculate ratio */
> +	ratio = (ch1<<  15) / ch0;
> +	/* convert to unscaled lux using the pointer to the table */
> +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
> +	while (p->ratio != 0&&  p->ratio<  ratio)
> +			p++;
> +
> +	if (p->ratio == 0) {
> +		lux = 0;
> +	} else {
> +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> +		lux = ch0lux - ch1lux;
> +	}
> +
> +	/* note: lux is 31 bit max at this point */
> +	if (ch1lux>  ch0lux) {
> +		dev_dbg(&chip->client->dev, "Returning last value\n");
> +		ret = chip->als_cur_info.lux;
> +		goto out_unlock;
> +	}
> +
> +	/* adjust for active time scale */
> +	if (chip->als_time_scale == 0)
> +		lux = 0;
> +	else
> +		lux = (lux + (chip->als_time_scale>>  1)) /
> +			chip->als_time_scale;
> +
> +	/* adjust for active gain scale
> +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
> +	 * User-specified gain provides a multiplier.
> +	 * Apply user-specified gain before shifting right to retain precision.
> +	 * Use 64 bits to avoid overflow on multiplication.
> +	 * Then go back to 32 bits before division to avoid using div_u64().
> +	 */
> +	lux64 = lux;
> +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
> +	lux64>>= 8;
> +	lux = lux64;
> +	lux = (lux + 500) / 1000;
> +
> +	if (lux>  TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> +
> +	/* Update the structure with the latest lux. */
> +return_max:
> +	chip->als_cur_info.lux = lux;
> +	ret = lux;
> +
> +out_unlock:
> +	mutex_unlock(&chip->als_mutex);
> +
> +	return ret;
> +}
> +
> +/*
> + * Proximity poll function - if valid data is available, read and form the ch0
> + * and prox data values, check for limits on the ch0 value, and check the prox
> + * data against the current thresholds, to set the event status accordingly.
> + */
> +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
> +{
> +#define CONSECUTIVE_RETRIES 50
> +
> +	int i;
> +	int ret;
> +	u8 status;
> +	u8 chdata[2];
> +	int err_cnt;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	if (mutex_trylock(&chip->prox_mutex) == 0) {
> +		dev_err(&chip->client->dev,
> +			"%s: Can't get prox mutex\n", __func__);
> +		return -EBUSY;
> +	}
> +
> +	err_cnt = 0;
> +
> +try_again:
I'd like a comment on why this looping is necessary....
> +
> +	ret = tsl2x7x_i2c_read(chip->client,
> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +		"%s: i2c err=%d\n", __func__, ret);
> +		goto prox_poll_err;
> +	}
> +
> +	if (chip->id<  tsl2572) {
> +		if (!(status&  TSL2X7X_STA_ADC_VALID)) {
> +			err_cnt++;
> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> +				dev_err(&chip->client->dev,
> +				"%s: Consec. retries exceeded\n", __func__);
> +				goto prox_poll_err;
> +			}
> +		goto try_again;
> +		}
> +	} else {
> +		if (!(status&  TSL2X7X_STA_PRX_VALID)) {
> +			err_cnt++;
> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> +				dev_err(&chip->client->dev,
> +				"%s: Consec. retries exceeded\n", __func__);
> +				goto prox_poll_err;
> +			}
> +		goto try_again;
> +		}
> +	}
> +
> +	for (i = 0; i<  2; i++) {
> +		ret = tsl2x7x_i2c_read(chip->client,
> +			(TSL2X7X_CMD_REG |
> +					(TSL2X7X_PRX_LO + i)),&chdata[i]);
> +		if (ret<  0)
> +			goto prox_poll_err;
> +	}
> +
> +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
> +	if (chip->prox_cur_info.prox_data == 0)
> +		goto try_again;
> +
> +	if (chip->prox_cur_info.prox_data>=
> +			chip->tsl2x7x_settings.prox_thres_high)
> +		chip->prox_cur_info.prox_event = 1;
> +	else
> +		chip->prox_cur_info.prox_event = 0;
So this is manually polling the event signal. I'd argue that this is a 
job for userspace
if the device isn't doing it hardware (or the interrupt signal is 
connected).
> +
> +	mutex_unlock(&chip->prox_mutex);
> +	return chip->prox_cur_info.prox_event;
> +
> +prox_poll_err:
> +	mutex_unlock(&chip->prox_mutex);
> +
> +	return ret;
> +}
> +
> +/*
> + * Provides initial operational parameter defaults.
> + * These defaults may be changed through the device's sysfs files.
> + */
> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
> +{
> +	/* If Operational settings defined elsewhere.. */
> +	if (chip->pdata&&  chip->pdata->platform_default_settings != 0)
> +		memcpy(&(chip->tsl2x7x_settings),
> +			chip->pdata->platform_default_settings,
> +			sizeof(tsl2x7x_default_settings));
> +	else
> +		memcpy(&(chip->tsl2x7x_settings),
> +			&tsl2x7x_default_settings,
> +			sizeof(tsl2x7x_default_settings));
> +
> +	/* Load up the proper lux table. */
> +	if (chip->pdata&&  chip->pdata->platform_lux_table[0].ratio != 0)
> +		memcpy(chip->tsl2x7x_device_lux,
> +			chip->pdata->platform_lux_table,
> +			sizeof(chip->pdata->platform_lux_table));
> +	else
> +		memcpy(chip->tsl2x7x_device_lux,
> +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
> +				MAX_DEFAULT_TABLE_BYTES);
> +
> +}
> +
> +/*
> + * Obtain single reading and calculate the als_gain_trim
> + * (later used to derive actual lux).
> + * Return updated gain_trim value.
> + */
> +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	u8 reg_val;
> +	int gain_trim_val;
> +	int ret;
> +	int lux_val;
> +
> +	ret = i2c_smbus_write_byte(chip->client,
> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +		"%s: failed to write CNTRL register, ret=%d\n",
> +		__func__, ret);
> +		return ret;
> +	}
> +
> +	reg_val = i2c_smbus_read_byte(chip->client);
> +	if ((reg_val&  (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
> +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed: ADC not enabled\n", __func__);
> +		return -1;
> +	}
> +
> +	ret = i2c_smbus_write_byte(chip->client,
> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed to write ctrl reg: ret=%d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
> +	reg_val = i2c_smbus_read_byte(chip->client);
> +	if ((reg_val&  TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed: STATUS - ADC not valid.\n", __func__);
> +		return -ENODATA;
> +	}
> +
> +	lux_val = tsl2x7x_get_lux(indio_dev);
> +	if (lux_val<  0) {
> +		dev_err(&chip->client->dev,
> +		"%s: failed to get lux\n", __func__);
> +		return lux_val;
> +	}
> +
> +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
> +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
> +	if ((gain_trim_val<  250) || (gain_trim_val>  4000))
> +		return -ERANGE;
> +
> +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
> +	dev_info(&chip->client->dev,
> +		"%s als_calibrate completed\n", chip->client->name);
> +
> +	return (int) gain_trim_val;
> +}
> +
> +/*
> + * Turn the device on.
> + * Configuration must be set before calling this function.
> + */
> +static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
> +{
> +	int i;
> +	int ret = 0;
> +	u8 *dev_reg;
> +	u8 utmp;
> +	int als_count;
> +	int als_time;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	u8 reg_val = 0;
> +
> +	if (chip->pdata&&  chip->pdata->power_on)
> +		chip->pdata->power_on(indio_dev);
> +
> +	/* Non calculated parameters */
> +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
> +			chip->tsl2x7x_settings.prx_time;
> +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
> +			chip->tsl2x7x_settings.wait_time;
> +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
> +			chip->tsl2x7x_settings.prox_config;
> +
> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
> +		(chip->tsl2x7x_settings.als_thresh_low)&  0xFF;
> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
> +		(chip->tsl2x7x_settings.als_thresh_low>>  8)&  0xFF;
> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
> +		(chip->tsl2x7x_settings.als_thresh_high)&  0xFF;
> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
> +		(chip->tsl2x7x_settings.als_thresh_high>>  8)&  0xFF;
> +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
> +		chip->tsl2x7x_settings.als_persistence;
> +
> +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
> +			chip->tsl2x7x_settings.prox_pulse_count;
> +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
> +	chip->tsl2x7x_settings.prox_thres_low;
> +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
> +			chip->tsl2x7x_settings.prox_thres_high;
> +
> +	/* and make sure we're not already on */
> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> +		/* if forcing a register update - turn off, then on */
> +		dev_info(&chip->client->dev, "device is already enabled\n");
> +		return -EINVAL;
> +	}
> +
> +	/* determine als integration regster */
> +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
> +	if (als_count == 0)
> +		als_count = 1; /* ensure at least one cycle */
> +
> +	/* convert back to time (encompasses overrides) */
> +	als_time = (als_count * 27 + 5) / 10;
> +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
> +
> +	/* Set the gain based on tsl2x7x_settings struct */
> +	chip->tsl2x7x_config[TSL2X7X_GAIN] =
> +		(chip->tsl2x7x_settings.als_gain |
> +			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
> +			| ((chip->tsl2x7x_settings.prox_gain)<<  2));
> +
> +	/* set chip struct re scaling and saturation */
> +	chip->als_saturation = als_count * 922; /* 90% of full scale */
> +	chip->als_time_scale = (als_time + 25) / 50;
> +
> +	/* TSL2X7X Specific power-on / adc enable sequence
> +	 * Power on the device 1st. */
> +	utmp = TSL2X7X_CNTL_PWR_ON;
> +	ret = i2c_smbus_write_byte_data(chip->client,
> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed on CNTRL reg.\n", __func__);
> +		return -1;
> +	}
> +
> +	/* Use the following shadow copy for our delay before enabling ADC.
> +	 * Write all the registers. */
> +	for (i = 0, dev_reg = chip->tsl2x7x_config;
> +			i<  TSL2X7X_MAX_CONFIG_REG; i++) {
> +		ret = i2c_smbus_write_byte_data(chip->client,
> +				TSL2X7X_CMD_REG + i, *dev_reg++);
> +		if (ret<  0) {
> +			dev_err(&chip->client->dev,
> +			"%s: failed on write to reg %d.\n", __func__, i);
> +			return ret;
> +		}
> +	}
> +
> +	udelay(3000);	/* Power-on settling time */
> +
> +	/* NOW enable the ADC
> +	 * initialize the desired mode of operation */
> +	utmp = TSL2X7X_CNTL_PWR_ON |
> +			TSL2X7X_CNTL_ADC_ENBL |
> +			TSL2X7X_CNTL_PROX_DET_ENBL;
> +	ret = i2c_smbus_write_byte_data(chip->client,
> +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"%s: failed on 2nd CTRL reg.\n", __func__);
> +		return ret;
> +		}
> +
> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
> +
> +	if (chip->tsl2x7x_settings.interrupts_en != 0) {
> +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
> +
> +		reg_val = TSL2X7X_CNTL_PWR_ON | TSL2X7X_CNTL_ADC_ENBL;
> +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
> +			(chip->tsl2x7x_settings.interrupts_en == 0x30))
> +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
> +
> +		reg_val |= chip->tsl2x7x_settings.interrupts_en;
> +		ret = i2c_smbus_write_byte_data(chip->client,
> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
> +		if (ret<  0)
> +			dev_err(&chip->client->dev,
> +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
> +				__func__);
> +
> +		/* Clear out any initial interrupts  */
> +		ret = i2c_smbus_write_byte(chip->client,
> +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> +			TSL2X7X_CMD_PROXALS_INT_CLR);
> +		if (ret<  0) {
> +			dev_err(&chip->client->dev,
> +				"%s: failed in tsl2x7x_chip_on\n",
> +				__func__);
> +		return ret;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	/* turn device off */
> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> +
> +	ret = i2c_smbus_write_byte_data(chip->client,
> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
> +
> +	if (chip->pdata&&  chip->pdata->power_off)
> +		chip->pdata->power_off(chip->client);
> +
> +	return ret;
> +}
> +
> +/*
> + * Proximity calibration helper function
> + * runs through a collection of data samples,
> + * sets the min, max, mean, and std dev.
> + */
> +static
> +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
> +{
> +	int i;
> +	int min, max, sum, mean;
> +	unsigned long stddev;
> +	int tmp;
> +
> +	if (length == 0)
> +		length = 1;
> +
> +	sum = 0;
> +	min = INT_MAX;
> +	max = INT_MIN;
> +	for (i = 0; i<  length; i++) {
> +		sum += data[i];
avoid using min as a variable name (as it's also an appropriate function)
_min = MIN(data[i], _min); saves you a line of code.
> +		if (data[i]<  min)
> +			min = data[i];
> +		if (data[i]>  max)
> +			max = data[i];
> +	}
> +	mean = sum/length;
> +	statP->min = min;
> +	statP->max = max;
> +	statP->mean = mean;
> +
> +	sum = 0;
> +	for (i = 0; i<  length; i++) {
> +		tmp = data[i]-mean;
> +		sum += tmp * tmp;
> +	}
> +	stddev = int_sqrt((long)sum)/length;
> +	statP->stddev = stddev;
> +}
> +
> +/**
> + * Proximity calibration - collects a number of samples,
> + * calculates a standard deviation based on the samples, and
> + * sets the threshold accordingly.
> + */
> +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
> +{
> +	u16 prox_history[MAX_SAMPLES_CAL+1];
spaces around that +
> +	int i;
> +	struct prox_stat prox_stat_data[2];
> +	struct prox_stat *calP;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	u8 tmp_irq_settings;
> +	u8 current_state = chip->tsl2x7x_chip_status;
> +
> +	if (chip->tsl2x7x_settings.prox_max_samples_cal>  MAX_SAMPLES_CAL) {
> +		dev_err(&chip->client->dev,
> +			"%s: max prox samples cal is too big: %d\n",
> +			__func__, chip->tsl2x7x_settings.prox_max_samples_cal);
> +		chip->tsl2x7x_settings.prox_max_samples_cal = MAX_SAMPLES_CAL;
> +	}
> +
> +	/* have to stop to change settings */
> +	tsl2x7x_chip_off(indio_dev);
> +
> +	/* Enable proximity detection save just in case prox not wanted yet*/
> +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
> +	chip->tsl2x7x_settings.interrupts_en |= TSL2X7X_CNTL_PROX_INT_ENBL;
> +
> +	/*turn on device if not already on*/
> +	tsl2x7x_chip_on(indio_dev);
> +
> +	/*gather the samples*/
> +	for (i = 0; i<  chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
> +		mdelay(15);
> +		tsl2x7x_prox_poll(indio_dev);
> +		prox_history[i] = chip->prox_cur_info.prox_data;
> +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
> +			i, chip->prox_cur_info.prox_data);
> +	}
> +
> +	tsl2x7x_chip_off(indio_dev);
> +	calP =&prox_stat_data[PROX_STAT_CAL];
> +	tsl2x7x_prox_calculate(prox_history,
> +		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
> +	chip->tsl2x7x_settings.prox_thres_high = (calP->max<<  1) - calP->mean;
> +
> +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
> +		calP->min, calP->mean, calP->max);
> +	dev_info(&chip->client->dev,
> +		"%s proximity threshold set to %d\n",
> +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
> +
> +	/* back to the way they were */
> +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
> +	if (current_state == TSL2X7X_CHIP_WORKING)
> +		tsl2x7x_chip_on(indio_dev);
> +}
> +
> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
> +}
> +
> +static ssize_t tsl2x7x_power_state_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	bool value;
> +
> +	if (strtobool(buf,&value))
> +		return -EINVAL;
> +
> +	if (!value)
> +		tsl2x7x_chip_off(indio_dev);
> +	else
> +		tsl2x7x_chip_on(indio_dev);
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	if (chip->id>  tsl2771)
> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
> +	else
> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
> +}
> +
> +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
> +}
> +
> +static ssize_t tsl2x7x_als_time_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_time);
> +}
> +
> +static ssize_t tsl2x7x_als_time_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	if ((value<  50) || (value>  650))
> +		return -EINVAL;
> +
> +	if (value % 50)
> +		return -EINVAL;
> +
> +	 chip->tsl2x7x_settings.als_time = value;
> +
> +	return len;
> +}
> +
> +static IIO_CONST_ATTR(illuminance0_integration_time_available,
> +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
> +
> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_cal_target);
> +}
> +
> +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	if (value)
> +		chip->tsl2x7x_settings.als_cal_target = value;
> +
> +	return len;
> +}
> +
> +/* sampling_frequency AKA persistence in data sheet */
> +static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_persistence);
> +}
> +
> +static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.als_persistence = value;
> +
> +	return len;
> +}
> +
> +static IIO_CONST_ATTR(sampling_frequency_available,
> +		"0x00 - 0xFF (0 - 255)");
> +
> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	bool value;
> +
> +	if (strtobool(buf,&value))
> +		return -EINVAL;
> +
> +	if (value)
> +		tsl2x7x_als_calibrate(indio_dev);
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int i;
> +	int offset = 0;
> +
> +	i = 0;
Set i at the declaration above.
> +	while (i<  (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
> +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
> +			chip->tsl2x7x_device_lux[i].ratio,
> +			chip->tsl2x7x_device_lux[i].ch0,
> +			chip->tsl2x7x_device_lux[i].ch1);
> +		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
> +			/* We just printed the first "0" entry.
> +			 * Now get rid of the extra "," and break. */
> +			offset--;
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
> +	return offset;
> +}
> +
> +static ssize_t tsl2x7x_luxtable_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
> +	int n;
> +
> +	get_options(buf, ARRAY_SIZE(value), value);
> +
> +	/* We now have an array of ints starting at value[1], and
> +	 * enumerated by value[0].
> +	 * We expect each group of three ints is one table entry,
> +	 * and the last table entry is all 0.
> +	 */
> +	n = value[0];
> +	if ((n % 3) || n<  6 ||
> +			n>  ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
> +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
> +		return -EINVAL;
> +	}
> +
> +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
> +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
> +		return -EINVAL;
> +	}
> +
> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
> +		tsl2x7x_chip_off(indio_dev);
> +
> +	/* Zero out the table */
> +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
> +	memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4));
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	bool value;
> +
> +	if (strtobool(buf,&value))
> +		return -EINVAL;
> +
> +	if (value)
> +		tsl2x7x_prox_cal(indio_dev);
> +
> +	return len;
> +}
> +
> +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
> +					 u64 event_code)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x10);
> +	else
> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x20);
> +
> +	return ret;
> +}
> +
> +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
> +					  u64 event_code,
> +					  int val)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
> +		if (val)
> +			chip->tsl2x7x_settings.interrupts_en |= 0x10;
> +		else
> +			chip->tsl2x7x_settings.interrupts_en&= 0x20;
> +	} else {
> +		if (val)
> +			chip->tsl2x7x_settings.interrupts_en |= 0x20;
> +		else
> +			chip->tsl2x7x_settings.interrupts_en&= 0x10;
> +	}
> +
> +	return 0;
> +}
> +
> +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
> +				  u64 event_code,
> +				  int val)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> +		case IIO_EV_DIR_RISING:
> +			chip->tsl2x7x_settings.als_thresh_high = val;
> +			break;
> +		case IIO_EV_DIR_FALLING:
> +			chip->tsl2x7x_settings.als_thresh_low = val;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	} else {
> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> +		case IIO_EV_DIR_RISING:
> +			chip->tsl2x7x_settings.prox_thres_high = val;
> +			break;
> +		case IIO_EV_DIR_FALLING:
> +			chip->tsl2x7x_settings.prox_thres_low = val;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
> +			       u64 event_code,
> +			       int *val)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT) {
> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> +		case IIO_EV_DIR_RISING:
> +			*val = chip->tsl2x7x_settings.als_thresh_high;
> +			break;
> +		case IIO_EV_DIR_FALLING:
> +			*val = chip->tsl2x7x_settings.als_thresh_low;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	} else {
> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> +		case IIO_EV_DIR_RISING:
> +			*val = chip->tsl2x7x_settings.prox_thres_high;
> +			break;
> +		case IIO_EV_DIR_FALLING:
> +			*val = chip->tsl2x7x_settings.prox_thres_low;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val,
> +			    int *val2,
> +			    long mask)
> +{
> +	int ret = -EINVAL;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case 0:
> +		switch (chan->type) {
> +		case IIO_LIGHT:
> +			tsl2x7x_get_lux(indio_dev);
> +			if (chan->processed_val)
> +				*val = chip->als_cur_info.lux;
> +			else
> +				*val = (((chip->als_cur_info.als_ch0<<  16) |
> +					chip->als_cur_info.als_ch1));
Why have a processed read back and a raw readback?
> +			ret = IIO_VAL_INT;
> +			break;
> +		case IIO_PROXIMITY:
> +			tsl2x7x_prox_poll(indio_dev);
> +			if (chan->processed_val)
Hmm.. this is uggly.  We effectively have polling of an event status.  
Normally
I'd expect the event to only occur as a an IIO event rather than being 
readable like
this...
> +				*val = chip->prox_cur_info.prox_event;
> +			else
> +				*val = chip->prox_cur_info.prox_data;
> +			ret = IIO_VAL_INT;
> +			break;
> +		default:
> +			return -EINVAL;
> +			break;
> +		}
> +		break;
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		if (chan->type == IIO_LIGHT)
> +			*val =
> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
> +		else
> +			*val =
> +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
> +		ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		*val = chip->tsl2x7x_settings.als_gain_trim;
> +		ret = IIO_VAL_INT;
> +		break;
> +
> +	default:
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
> +			       struct iio_chan_spec const *chan,
> +			       int val,
> +			       int val2,
> +			       long mask)
> +{
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_CALIBSCALE:
> +		if (chan->type == IIO_LIGHT) {
> +			switch (val) {
> +			case 1:
> +				chip->tsl2x7x_settings.als_gain = 0;
> +				break;
> +			case 8:
> +				chip->tsl2x7x_settings.als_gain = 1;
> +				break;
> +			case 16:
> +				chip->tsl2x7x_settings.als_gain = 2;
> +				break;
> +			case 120:
> +				if (chip->id>  tsl2771)
> +					return -EINVAL;
> +				chip->tsl2x7x_settings.als_gain = 3;
> +				break;
> +			case 128:
> +				if (chip->id<  tsl2572)
> +					return -EINVAL;
> +				chip->tsl2x7x_settings.als_gain = 3;
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
> +		} else {
> +			switch (val) {
> +			case 1:
> +				chip->tsl2x7x_settings.prox_gain = 0;
> +				break;
> +			case 2:
> +				chip->tsl2x7x_settings.prox_gain = 1;
> +				break;
> +			case 4:
> +				chip->tsl2x7x_settings.prox_gain = 2;
> +				break;
> +			case 8:
> +				chip->tsl2x7x_settings.prox_gain = 3;
> +				break;
> +			default:
> +				return -EINVAL;
> +			}
> +		}
> +		break;
> +	case IIO_CHAN_INFO_CALIBBIAS:
> +		chip->tsl2x7x_settings.als_gain_trim = val;
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
> +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
> +
> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
> +		tsl2x7x_prox_gain_available_show, NULL);
> +
> +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
> +		tsl2x7x_gain_available_show, NULL);
> +
> +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
> +
> +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
> +
> +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
> +		tsl2x7x_do_calibrate);
> +
> +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
> +		tsl2x7x_do_prox_calibrate);
> +
> +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
> +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
> +
> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
> +
> +/* Use the default register values to identify the Taos device */
> +static int tsl2x7x_device_id(unsigned char *id, int target)
> +{
> +	switch (target) {
> +	case tsl2571:
> +	case tsl2671:
> +	case tsl2771:
> +		return ((*id&  0xf0) == TRITON_ID);
> +	break;
> +	case tmd2671:
> +	case tmd2771:
> +		return ((*id&  0xf0) == HALIBUT_ID);
> +	break;
> +	case tsl2572:
> +	case tsl2672:
> +	case tmd2672:
> +	case tsl2772:
> +	case tmd2772:
> +		return ((*id&  0xf0) == SWORDFISH_ID);
> +	break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +/*
> + * Interrupt Event Handler */
> +static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	s64 timestamp = iio_get_time_ns();
> +	int ret;
> +	int value;
> +
> +	value = i2c_smbus_read_byte_data(chip->client,
> +		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
> +
> +	/* What type of interrupt do we need to process */
> +	if (value&  TSL2X7X_STA_PRX_INTR) {
> +		tsl2x7x_prox_poll(indio_dev);
> +		iio_push_event(indio_dev,
> +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
> +						    0,
> +						    IIO_EV_TYPE_THRESH,
> +						    IIO_EV_DIR_EITHER),
> +						    timestamp);
> +	}
> +
> +	if (value&  TSL2X7X_STA_ALS_INTR) {
> +		tsl2x7x_get_lux(indio_dev);
why the get value?  No real guarantee you'll get the one that caused the
event as far as I can see.
> +		iio_push_event(indio_dev,
> +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
> +					    0,
> +					    IIO_EV_TYPE_THRESH,
> +					    IIO_EV_DIR_EITHER),
> +					    timestamp);
> +	}
> +	/* Clear interrupt now that we have the status */
> +	ret = i2c_smbus_write_byte(chip->client,
> +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> +		TSL2X7X_CMD_PROXALS_INT_CLR);
> +	if (ret<  0)
> +		dev_err(&chip->client->dev,
> +			"%s: Failed to clear irq from event handler. err = %d\n",
> +			__func__, ret);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static struct attribute *tsl2x7x_ALS_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> +	&dev_attr_illuminance0_target_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2x7x_PRX_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> +	&dev_attr_illuminance0_target_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	&dev_attr_proximity_calibscale_available.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> +	&dev_attr_illuminance0_target_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	&dev_attr_proximity_calibscale_available.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
> +	[ALS] = {
> +		.attrs = tsl2x7x_ALS_device_attrs,
> +	},
> +	[PRX] = {
> +		.attrs = tsl2x7x_PRX_device_attrs,
> +	},
> +	[ALSPRX] = {
> +		.attrs = tsl2x7x_ALSPRX_device_attrs,
> +	},
> +	[PRX2] = {
> +		.attrs = tsl2x7x_PRX2_device_attrs,
> +	},
> +	[ALSPRX2] = {
> +		.attrs = tsl2x7x_ALSPRX2_device_attrs,
> +	},
> +};
> +
> +static const struct iio_info tsl2X7X_device_info[] = {
> +	[ALS] = {
> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALS],
> +		.driver_module = THIS_MODULE,
> +		.read_raw =&tsl2x7x_read_raw,
> +		.write_raw =&tsl2x7x_write_raw,
> +		.read_event_value =&tsl2x7x_read_thresh,
> +		.write_event_value =&tsl2x7x_write_thresh,
> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> +	},
> +	[PRX] = {
> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX],
> +		.driver_module = THIS_MODULE,
> +		.read_raw =&tsl2x7x_read_raw,
> +		.write_raw =&tsl2x7x_write_raw,
> +		.read_event_value =&tsl2x7x_read_thresh,
> +		.write_event_value =&tsl2x7x_write_thresh,
> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> +	},
> +	[ALSPRX] = {
> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX],
> +		.driver_module = THIS_MODULE,
> +		.read_raw =&tsl2x7x_read_raw,
> +		.write_raw =&tsl2x7x_write_raw,
> +		.read_event_value =&tsl2x7x_read_thresh,
> +		.write_event_value =&tsl2x7x_write_thresh,
> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> +	},
> +	[PRX2] = {
> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX2],
> +		.driver_module = THIS_MODULE,
> +		.read_raw =&tsl2x7x_read_raw,
> +		.write_raw =&tsl2x7x_write_raw,
> +		.read_event_value =&tsl2x7x_read_thresh,
> +		.write_event_value =&tsl2x7x_write_thresh,
> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> +	},
> +	[ALSPRX2] = {
> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2],
> +		.driver_module = THIS_MODULE,
> +		.read_raw =&tsl2x7x_read_raw,
> +		.write_raw =&tsl2x7x_write_raw,
> +		.read_event_value =&tsl2x7x_read_thresh,
> +		.write_event_value =&tsl2x7x_write_thresh,
> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> +	},
> +};
> +
> +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
> +	[ALS] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[1] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask =
> +					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> +					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +		},
> +	.num_channels = 2,
> +	.info =&tsl2X7X_device_info[ALS],
> +	},
> +	[PRX] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[1] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +		},
> +	.num_channels = 2,
> +	.info =&tsl2X7X_device_info[PRX],
> +	},
> +	[ALSPRX] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[1] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask =
> +					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> +					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +			[2] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[3] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 0,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +		},
> +	.num_channels = 4,
> +	.info =&tsl2X7X_device_info[ALSPRX],
> +	},
> +	[PRX2] = {
> +		.channel = {
> +			[0] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[1] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask =
> +					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +		},
> +	.num_channels = 2,
> +	.info =&tsl2X7X_device_info[PRX2],
> +	},
> +	[ALSPRX2] = {
> +		.channel = {
> +				[0] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[1] = {
> +				.type = IIO_LIGHT,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask =
> +					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> +					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +			[2] = {
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.processed_val = 1,
> +			},
> +			[3] = {
I'm more than a little confused here.  There are only 2 actually 
channels?  If so I don't
see why we have 4 channels here...
> +				.type = IIO_PROXIMITY,
> +				.indexed = 1,
> +				.channel = 0,
> +				.info_mask =
> +					IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> +				.event_mask = TSL2X7X_EVENT_MASK
> +			},
> +		},
> +	.num_channels = 4,
> +	.info =&tsl2X7X_device_info[ALSPRX2],
> +	},
> +};
> +
> +/*
> + * Client probe function.
> + */
> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
> +	const struct i2c_device_id *id)
> +{
> +	int ret;
> +	unsigned char device_id;
> +	struct iio_dev *indio_dev;
> +	struct tsl2X7X_chip *chip;
> +
> +	indio_dev = iio_allocate_device(sizeof(*chip));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	chip = iio_priv(indio_dev);
> +	chip->client = clientp;
> +	i2c_set_clientdata(clientp, indio_dev);
> +
> +	ret = tsl2x7x_i2c_read(chip->client,
> +		TSL2X7X_CHIPID,&device_id);
> +	if (ret<  0)
> +		goto fail1;
> +
> +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
> +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
> +		dev_info(&chip->client->dev,
> +				"i2c device found does not match expected id in %s\n",
> +				__func__);
> +		goto fail1;
> +	}
> +
> +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> +	if (ret<  0) {
> +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err = %d\n",
> +				__func__, ret);
> +		goto fail1;
> +	}
> +
> +	/* ALS and PROX functions can be invoked via user space poll
> +	 * or H/W interrupt. If busy return last sample. */
> +	mutex_init(&chip->als_mutex);
> +	mutex_init(&chip->prox_mutex);
> +
> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
> +	chip->pdata = clientp->dev.platform_data;
> +	chip->id = id->driver_data;
> +	chip->chip_info =
> +		&tsl2x7x_chip_info_tbl[device_channel_config[id->driver_data]];
> +
> +	indio_dev->info = chip->chip_info->info;
> +	indio_dev->dev.parent =&clientp->dev;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->name = chip->client->name;
> +	indio_dev->channels = chip->chip_info->channel;
> +	indio_dev->num_channels = chip->chip_info->num_channels;
> +
> +	if (clientp->irq) {
> +		ret = request_threaded_irq(clientp->irq,
> +					   NULL,
> +					&tsl2x7x_event_handler,
> +					   IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +					   "TSL2X7X_event",
> +					   indio_dev);
> +		if (ret) {
> +			dev_err(&clientp->dev,
> +				"%s: irq request failed", __func__);
> +			goto fail2;
> +		}
> +	}
> +
> +	/* Load up the defaults */
> +	tsl2x7x_defaults(chip);
> +	/* Make sure the chip is on */
> +	tsl2x7x_chip_on(indio_dev);
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&clientp->dev,
> +			"%s: iio registration failed\n", __func__);
> +		goto fail3;
> +	}
> +
> +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
> +
> +	return 0;
> +
> +fail3:
> +	if (clientp->irq)
> +		free_irq(clientp->irq, indio_dev);
> +fail2:
> +	iio_free_device(indio_dev);
> +fail1:
> +	kfree(chip);
double free of chip.  It's allocated and managed by the 
iio_allocate_device and iio_free_device calls.
For that matter, most of the goto fail2's need the iio_free_device call..
> +	return ret;
> +}
> +
> +static int tsl2x7x_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int ret = 0;
> +
> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> +		ret = tsl2x7x_chip_off(indio_dev);
> +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> +	}
> +
> +	if (chip->pdata&&  chip->pdata->platform_power) {
> +		pm_message_t pmm = {PM_EVENT_SUSPEND};
> +		chip->pdata->platform_power(dev, pmm);
> +	}
> +
> +	return ret;
> +}
> +
> +static int tsl2x7x_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int ret = 0;
> +
> +	if (chip->pdata&&  chip->pdata->platform_power) {
> +		pm_message_t pmm = {PM_EVENT_RESUME};
> +		chip->pdata->platform_power(dev, pmm);
> +	}
> +
> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
> +		ret = tsl2x7x_chip_on(indio_dev);
> +
> +	return ret;
> +}
> +
> +static int __devexit tsl2x7x_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
These shouldn't both be true....
would expect an iio_unregister_device call here.
> +
> +	tsl2x7x_chip_off(indio_dev);
> +
> +	if (client->irq)
> +		free_irq(client->irq, chip->client->name);
> +
> +	iio_free_device(indio_dev);
> +
> +	return 0;
> +}
> +
> +static struct i2c_device_id tsl2x7x_idtable[] = {
> +	{ "tsl2571", tsl2571 },
> +	{ "tsl2671", tsl2671 },
> +	{ "tmd2671", tmd2671 },
> +	{ "tsl2771", tsl2771 },
> +	{ "tmd2771", tmd2771 },
> +	{ "tsl2572", tsl2572 },
> +	{ "tsl2672", tsl2672 },
> +	{ "tmd2672", tmd2672 },
> +	{ "tsl2772", tsl2772 },
> +	{ "tmd2772", tmd2772 },
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
> +
> +static const struct dev_pm_ops tsl2x7x_pm_ops = {
> +	.suspend = tsl2x7x_suspend,
> +	.resume  = tsl2x7x_resume,
> +};
> +
> +/* Driver definition */
> +static struct i2c_driver tsl2x7x_driver = {
> +	.driver = {
> +		.name = "tsl2x7x",
> +		.pm =&tsl2x7x_pm_ops,
> +	},
> +	.id_table = tsl2x7x_idtable,
> +	.probe = tsl2x7x_probe,
> +	.remove = __devexit_p(tsl2x7x_remove),
> +};
> +
> +module_i2c_driver(tsl2x7x_driver);
> +
> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
> +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/staging/iio/light/tsl2x7x_core.h b/drivers/staging/iio/light/tsl2x7x_core.h
> new file mode 100644
> index 0000000..663e846
> --- /dev/null
> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
why not just tsl2x7x.h?
> @@ -0,0 +1,75 @@
> +/*
> + * Device driver for monitoring ambient light intensity (lux)
> + * and proximity (prox) within the TAOS TSL2X7X family of devices.
> + *
> + * Copyright (c) 2012, TAOS Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; 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.,
> + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
> + */
> +
> +#ifndef __TSL2X7X_H
> +#define __TSL2X7X_H
> +#include<linux/pm.h>
> +
> +/* Max number of segments allowable in LUX table */
> +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * TSL2X7X_MAX_LUX_TABLE_SIZE)
> +
> +struct iio_dev;

Why the forward declaration of iio_chan_spec?
> +struct iio_chan_spec;
> +
> +struct tsl2x7x_lux {
> +	unsigned int ratio;
> +	unsigned int ch0;
> +	unsigned int ch1;
> +};
> +
> +/* Refer to tsl2x7x_default_settings for member desc. */
> +struct tsl2x7x_settings {
> +	int als_time;
> +	int als_gain;
> +	int als_gain_trim;
> +	int wait_time;
> +	int prx_time;
> +	int prox_gain;
> +	int prox_config;
> +	int als_cal_target;
> +	u8  interrupts_en;
> +	u8  als_persistence;
> +	int als_thresh_low;
> +	int als_thresh_high;
> +	int prox_thres_low;
> +	int prox_thres_high;
> +	int prox_pulse_count;
> +	int prox_max_samples_cal;
> +};
> +
> +/* struct tsl2x7x_platform_data -
> + * Platform unique glass and defaults
> + * Platform PM functions. */
Would prefer this to be in kernel doc.
> +struct tsl2X7X_platform_data {
> +	/* Suspend/resume platform cb */
> +	int (*platform_power)(struct device *dev, pm_message_t);
> +	/* The following callback gets called when the device is powered on */
> +	int (*power_on)      (struct iio_dev *indio_dev);
> +	/* The following callback gets called when the device is powered off */
> +	int (*power_off)     (struct i2c_client *dev);
> +	/* These are the device specific glass coefficents used to
> +	 * calculate Lux */
> +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
> +	struct tsl2x7x_settings *platform_default_settings;
> +};
> +
> +#endif /* __TSL2X7X_H */
> --
> 1.7.4.1
>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH V4] TAOS tsl2x7x
  2012-03-23 13:53 ` Jonathan Cameron
@ 2012-03-27 19:58   ` Jon Brenner
  2012-03-28 17:51     ` Jonathan Cameron
  0 siblings, 1 reply; 8+ messages in thread
From: Jon Brenner @ 2012-03-27 19:58 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 75610 bytes --]

Hello Jonathan,
Still a little confused (and stuck) here.
Using your code from the tsl2563:
1. The case for IIO_LIGHT appears to return the computed LUX.
Yet you have no ".processed_val = 1: in your channel table - so where in in_illuminance0_input (aka lux)  coming from?
 
2. The case for IIO_INTENSITY looks for 'chan->channel' to determine when to present 'chip->data0' or 'chip->data1' - 
But it appears that 'chan->channel' will always be 0 as '.channel =' isn't defined in the table?
So how can you ever get chip->data01?

For example, tsl2563 code portion follows.

<Snip>
static int tsl2563_read_raw(struct iio_dev *indio_dev,
			    struct iio_chan_spec const *chan,
			    int *val,
			    int *val2,
			    long m)
{
	int ret = -EINVAL;
	u32 calib0, calib1;
	struct tsl2563_chip *chip = iio_priv(indio_dev);

	mutex_lock(&chip->lock);
	switch (m) {
	case 0:
		switch (chan->type) {
		case IIO_LIGHT:
			ret = tsl2563_get_adc(chip);
			if (ret)
				goto error_ret;
			calib0 = calib_adc(chip->data0, chip->calib0) *
				chip->cover_comp_gain;
			calib1 = calib_adc(chip->data1, chip->calib1) *
				chip->cover_comp_gain;
			*val = adc_to_lux(calib0, calib1);
			ret = IIO_VAL_INT;
			break;
		case IIO_INTENSITY:
			ret = tsl2563_get_adc(chip);
			if (ret)
				goto error_ret;
			if (chan->channel == 0)
				*val = chip->data0;
			else
				*val = chip->data1;
			ret = IIO_VAL_INT;
			break;
		default:
			break;
		}
		break;

	case IIO_CHAN_INFO_CALIBSCALE:
		if (chan->channel == 0)
			*val = calib_to_sysfs(chip->calib0);
		else
			*val = calib_to_sysfs(chip->calib1);
		ret = IIO_VAL_INT;
		break;
	default:
		ret = -EINVAL;
		goto error_ret;
	}

error_ret:
	mutex_unlock(&chip->lock);
	return ret;
}

static const struct iio_chan_spec tsl2563_channels[] = {
	{
		.type = IIO_LIGHT,
		.indexed = 1,
		.channel = 0,
	}, {
		.type = IIO_INTENSITY,
		.modified = 1,
		.channel2 = IIO_MOD_LIGHT_BOTH,
		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
					  IIO_EV_DIR_RISING) |
			       IIO_EV_BIT(IIO_EV_TYPE_THRESH,
					  IIO_EV_DIR_FALLING)),
	}, {
		.type = IIO_INTENSITY,
		.modified = 1,
		.channel2 = IIO_MOD_LIGHT_IR,
		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
	}
};


What am I not seeing here?

Jon

> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
> Sent: Friday, March 23, 2012 8:54 AM
> To: Jon Brenner
> Cc: linux-iio; Linux Kernel
> Subject: Re: [PATCH V4] TAOS tsl2x7x
> 
> On 3/21/2012 4:16 PM, Jon Brenner wrote:
> > TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families
> (inc. all variants).
> Mostly looking good...
> 
> Couple of issues remaining.
> * Why have processed and raw accesses to the same channels?  That is
> definitely not the intent.
> Either it's worth processing these raw adc channels in kernel, or not.
> If it isn't you certainly shouldn't
> be munging together the two adc's into a single value.
> 
> So you have done this for two reasons...
> * For light sensors it was to allow the processed illuminance value and
> also the raw adc values.
> Previously we have done this by having IIO_LIGHT (and hence illuminance)
> for the processed one
> and marking the other two as IIO_INTENSITY with modifiers for the
> frequency range they cover...
> * For proximity one is the raw reading (fine), the other is a means of
> getting at the threshold event
> if interrupts are not supported.  It is done by a software comparison of
> the threshold and the raw
> reading.  This should not be in driver as if the functionality is
> desired, it should be done in userspace.
> 
> Various other minor bits like error paths that don't clean up commented
> inline.
> 
> 
> >
> > Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
> > ---
> >   .../light/sysfs-bus-iio-light-tsl2583              |    6 +
> >   .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
> >   drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
> >   .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
> >   .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
> >   drivers/staging/iio/light/Kconfig                  |    8 +
> >   drivers/staging/iio/light/Makefile                 |    1 +
> >   drivers/staging/iio/light/tsl2x7x_core.c           | 1911 ++++++++++++++++++++
> >   drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
> >   9 files changed, 2032 insertions(+), 24 deletions(-)
> >
> > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> > new file mode 100644
> > index 0000000..8f2a038
> > --- /dev/null
> > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> > @@ -0,0 +1,6 @@
> > +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> > +KernelVersion:	2.6.37
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		This property causes an internal calibration of the als gain trim
> > +		value which is later used in calculating illuminance in lux.
> > diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> > new file mode 100644
> > index 0000000..cceadae
> > --- /dev/null
> > +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> > @@ -0,0 +1,20 @@
> > +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> > +KernelVersion:	2.6.37
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		This property causes an internal calibration of the als gain trim
> > +		value which is later used in calculating illuminance in lux.
> > +
> > +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
> > +KernelVersion:	3.3-rc1
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Simultainious ALS channel data.
> That wasn't the intent of 'both' at all.  (+ typo).  It means a raw
> reading from a diod that
> detects the 'sum' of infrared and visible.
> > +
> > +What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
> > +KernelVersion:	3.3-rc1
> > +Contact:	linux-iio@vger.kernel.org
> > +Description:
> > +		Causes an recalculation and adjustment to the
> > +		proximity_thresh_rising_value.
> > +
> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio
> b/drivers/staging/iio/Documentation/sysfs-bus-iio
> > index 46a995d..5b2b5d3 100644
> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio
> > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
> > @@ -258,6 +258,8 @@ What
> 	/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
> >   What
> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
> >   What
> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
> >   What
> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
> > +what
> 	/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
> > +what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
> >   KernelVersion:	2.6.35
> >   Contact:	linux-iio@vger.kernel.org
> >   Description:
> > @@ -457,6 +459,10 @@ What:
> 	/sys/.../events/in_voltageY_raw_thresh_falling_value
> >   What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
> >   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
> >   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
> > +What:		/sys/.../events/illuminance0_thresh_falling_value
> > +what:		/sys/.../events/illuminance0_thresh_rising_value
> > +what:		/sys/.../events/proximity_thresh_falling_value
> > +what:		/sys/.../events/proximity_thresh_rising_value
> >   KernelVersion:	2.6.37
> >   Contact:	linux-iio@vger.kernel.org
> >   Description:
> > @@ -739,3 +745,4 @@ Description:
> >   		system. To minimize the current consumption of the system,
> >   		the bridge can be disconnected (when it is not being used
> >   		using the bridge_switch_en attribute.
> > +
> spurious blank line?
> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> > index edbf470..4385c70 100644
> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> > +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> > @@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
> >   Description:
> >   		This property gets/sets the sensors ADC analog integration
> time.
> >
> > -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
> > +What:		/sys/bus/iio/devices/device[n]/lux_table
> >   KernelVersion:	2.6.37
> >   Contact:	linux-iio@vger.kernel.org
> >   Description:
> > -		Hardware or software applied calibration scale factor assumed
> > -		to account for attenuation due to industrial design (glass
> > -		filters or aperture holes).
> > +		This property gets/sets the table of coefficients
> > +		used in calculating illuminance in lux.
> > +
> > diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> > deleted file mode 100644
> > index 660781d..0000000
> > --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> > +++ /dev/null
> > @@ -1,20 +0,0 @@
> > -What:		/sys/bus/iio/devices/device[n]/lux_table
> > -KernelVersion:	2.6.37
> > -Contact:	linux-iio@vger.kernel.org
> > -Description:
> > -		This property gets/sets the table of coefficients
> > -		used in calculating illuminance in lux.
> > -
> > -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> > -KernelVersion:	2.6.37
> > -Contact:	linux-iio@vger.kernel.org
> > -Description:
> > -		This property causes an internal calibration of the als gain trim
> > -		value which is later used in calculating illuminance in lux.
> > -
> > -What:
> 	/sys/bus/iio/devices/device[n]/illuminance0_input_target
> > -KernelVersion:	2.6.37
> > -Contact:	linux-iio@vger.kernel.org
> > -Description:
> > -		This property is the known externally illuminance (in lux).
> > -		It is used in the process of calibrating the device accuracy.
> > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
> > index e7e9159..976f790 100644
> > --- a/drivers/staging/iio/light/Kconfig
> > +++ b/drivers/staging/iio/light/Kconfig
> > @@ -31,4 +31,12 @@ config TSL2583
> >   	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
> >   	 Access ALS data via iio, sysfs.
> >
> > +config TSL2x7x
> > +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and
> proximity sensors"
> > +	depends on I2C
> > +	help
> > +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572,
> tsl2672,
> > +	 tmd2672, tsl2772, tmd2772 devices.
> > +	 Provides iio_events and direct access via sysfs.
> > +
> >   endmenu
> > diff --git a/drivers/staging/iio/light/Makefile
> b/drivers/staging/iio/light/Makefile
> > index 3011fbf..ff12c4b 100644
> > --- a/drivers/staging/iio/light/Makefile
> > +++ b/drivers/staging/iio/light/Makefile
> > @@ -5,3 +5,4 @@
> >   obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
> >   obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
> >   obj-$(CONFIG_TSL2583)	+= tsl2583.o
> > +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
> > diff --git a/drivers/staging/iio/light/tsl2x7x_core.c
> b/drivers/staging/iio/light/tsl2x7x_core.c
> > new file mode 100644
> > index 0000000..c0d9d6e
> > --- /dev/null
> > +++ b/drivers/staging/iio/light/tsl2x7x_core.c
> > @@ -0,0 +1,1911 @@
> > +/*
> > + * Device driver for monitoring ambient light intensity in (lux)
> > + * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
> > + *
> > + * Copyright (c) 2012, TAOS Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; 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.,
> > + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
> > + */
> > +
> > +#include<linux/kernel.h>
> > +#include<linux/i2c.h>
> > +#include<linux/errno.h>
> > +#include<linux/delay.h>
> > +#include<linux/mutex.h>
> > +#include<linux/interrupt.h>
> > +#include<linux/slab.h>
> > +#include<linux/module.h>
> > +#include<linux/version.h>
> > +#include "tsl2x7x_core.h"
> > +#include "../events.h"
> > +#include "../iio.h"
> > +#include "../sysfs.h"
> > +
> > +/* Cal defs*/
> > +#define PROX_STAT_CAL        0
> > +#define PROX_STAT_SAMP       1
> > +#define MAX_SAMPLES_CAL      200
> > +
> > +/* TSL2X7X Device ID */
> > +#define TRITON_ID    0x00
> > +#define SWORDFISH_ID 0x30
> > +#define HALIBUT_ID   0x20
> > +
> > +/* Lux calculation constants */
> > +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
> > +
> > +/* TAOS Register definitions - note:
> > + * depending on device, some of these register are not used and the
> > + * register address is benign.
> > + */
> > +/* 2X7X register offsets */
> > +#define TSL2X7X_MAX_CONFIG_REG         16
> > +
> > +/* Device Registers and Masks */
> > +#define TSL2X7X_CNTRL                  0x00
> > +#define TSL2X7X_ALS_TIME               0X01
> > +#define TSL2X7X_PRX_TIME               0x02
> > +#define TSL2X7X_WAIT_TIME              0x03
> > +#define TSL2X7X_ALS_MINTHRESHLO        0X04
> > +#define TSL2X7X_ALS_MINTHRESHHI        0X05
> > +#define TSL2X7X_ALS_MAXTHRESHLO        0X06
> > +#define TSL2X7X_ALS_MAXTHRESHHI        0X07
> > +#define TSL2X7X_PRX_MINTHRESHLO        0X08
> > +#define TSL2X7X_PRX_MINTHRESHHI        0X09
> > +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
> > +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
> > +#define TSL2X7X_PERSISTENCE            0x0C
> > +#define TSL2X7X_PRX_CONFIG             0x0D
> > +#define TSL2X7X_PRX_COUNT              0x0E
> > +#define TSL2X7X_GAIN                   0x0F
> > +#define TSL2X7X_NOTUSED                0x10
> > +#define TSL2X7X_REVID                  0x11
> > +#define TSL2X7X_CHIPID                 0x12
> > +#define TSL2X7X_STATUS                 0x13
> > +#define TSL2X7X_ALS_CHAN0LO            0x14
> > +#define TSL2X7X_ALS_CHAN0HI            0x15
> > +#define TSL2X7X_ALS_CHAN1LO            0x16
> > +#define TSL2X7X_ALS_CHAN1HI            0x17
> > +#define TSL2X7X_PRX_LO                 0x18
> > +#define TSL2X7X_PRX_HI                 0x19
> > +
> > +/* tsl2X7X cmd reg masks */
> > +#define TSL2X7X_CMD_REG                0x80
> > +#define TSL2X7X_CMD_SPL_FN             0x60
> > +
> > +#define TSL2X7X_CMD_PROX_INT_CLR       0X05
> > +#define TSL2X7X_CMD_ALS_INT_CLR        0x06
> > +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
> > +
> > +/* tsl2X7X cntrl reg masks */
> > +#define TSL2X7X_CNTL_ADC_ENBL          0x02
> > +#define TSL2X7X_CNTL_PWR_ON            0x01
> > +
> > +/* tsl2X7X status reg masks */
> > +#define TSL2X7X_STA_ADC_VALID          0x01
> > +#define TSL2X7X_STA_PRX_VALID          0x02
> > +#define TSL2X7X_STA_ADC_PRX_VALID      0x03
> > +#define TSL2X7X_STA_ALS_INTR           0x10
> > +#define TSL2X7X_STA_ADC_INTR           0x10
> > +#define TSL2X7X_STA_PRX_INTR           0x20
> > +
> > +#define TSL2X7X_STA_ADC_INTR           0x10
> > +
> > +/* tsl2X7X cntrl reg masks */
> > +#define TSL2X7X_CNTL_REG_CLEAR         0x00
> > +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
> > +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
> > +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
> > +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
> > +#define TSL2X7X_CNTL_PWRON             0x01
> > +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
> > +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
> > +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
> > +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
> > +
> > +/*Prox diode to use */
> > +#define TSL2X7X_DIODE0                 0x10
> > +#define TSL2X7X_DIODE1                 0x20
> > +#define TSL2X7X_DIODE_BOTH             0x30
> > +
> > +/* LED Power */
> > +#define TSL2X7X_mA100                  0x00
> > +#define TSL2X7X_mA50                   0x40
> > +#define TSL2X7X_mA25                   0x80
> > +#define TSL2X7X_mA13                   0xD0
> > +
> > +/*Common device IIO EventMask */
> > +#define TSL2X7X_EVENT_MASK \
> > +		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
> > +		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
> > +
> > +/* TAOS txx2x7x Device family members */
> > +enum {
> > +	tsl2571,
> > +	tsl2671,
> > +	tmd2671,
> > +	tsl2771,
> > +	tmd2771,
> > +	tsl2572,
> > +	tsl2672,
> > +	tmd2672,
> > +	tsl2772,
> > +	tmd2772
> > +};
> > +
> > +enum {
> > +	TSL2X7X_CHIP_UNKNOWN = 0,
> > +	TSL2X7X_CHIP_WORKING = 1,
> > +	TSL2X7X_CHIP_SUSPENDED = 2
> > +};
> > +
> > +/* Per-device data */
> > +struct tsl2x7x_als_info {
> > +	u16 als_ch0;
> > +	u16 als_ch1;
> > +	u16 lux;
> > +};
> > +
> > +/* proximity data */
> > +struct tsl2x7x_prox_info {
> > +	u16 prox_data;
> > +	int prox_event;
> > +};
> > +
> > +struct prox_stat {
> > +	u16 min;
> > +	u16 max;
> > +	u16 mean;
> > +	unsigned long stddev;
> > +};
> > +
> > +struct tsl2x7x_chip_info {
> > +	int num_channels;
> > +	struct iio_chan_spec		channel[9];
> > +	const struct iio_info		*info;
> > +};
> > +
> > +struct tsl2X7X_chip {
> > +	kernel_ulong_t id;
> > +	struct mutex prox_mutex;
> > +	struct mutex als_mutex;
> > +	struct i2c_client *client;
> > +	struct tsl2x7x_prox_info prox_cur_info;
> > +	struct tsl2x7x_als_info als_cur_info;
> > +	struct tsl2x7x_settings tsl2x7x_settings;
> > +	struct tsl2X7X_platform_data *pdata;
> > +	int als_time_scale;
> > +	int als_saturation;
> > +	int tsl2x7x_chip_status;
> > +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
> > +	const struct tsl2x7x_chip_info	*chip_info;
> > +	const struct iio_info *info;
> > +	s64 event_timestamp;
> > +	/* This structure is intentionally large to accommodate
> > +	 * updates via sysfs. */
> > +	/* Sized to 9 = max 8 segments + 1 termination segment */
> > +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
> > +};
> > +
> > +/* Different devices require different coefficents */
> > +static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
> > +	{ 14461,   611,   1211 },
> > +	{ 18540,   352,    623 },
> > +	{     0,     0,      0 },
> > +};
> > +
> > +static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
> > +	{ 11635,   115,    256 },
> > +	{ 15536,    87,    179 },
> > +	{     0,     0,      0 },
> > +};
> > +
> > +static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
> > +	{ 14013,   466,   917 },
> > +	{ 18222,   310,   552 },
> > +	{     0,     0,     0 },
> > +};
> > +
> > +static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
> > +	{ 13218,   130,   262 },
> > +	{ 17592,   92,    169 },
> > +	{     0,     0,     0 },
> > +};
> > +
> > +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
> check white space around here.  looks like a mixture of tabs and spaces...
> > +	[tsl2571] = tsl2x71_lux_table,
> > +	[tsl2671] =	tsl2x71_lux_table,
> > +	[tmd2671] =	tmd2x71_lux_table,
> > +	[tsl2771] =	tsl2x71_lux_table,
> > +	[tmd2771] =	tmd2x71_lux_table,
> > +	[tsl2572] =	tsl2x72_lux_table,
> > +	[tsl2672] =	tsl2x72_lux_table,
> > +	[tmd2672] = tmd2x72_lux_table,
> > +	[tsl2772] =	tsl2x72_lux_table,
> > +	[tmd2772] =	tmd2x72_lux_table,
> > +};
> > +
> > +static const struct tsl2x7x_settings tsl2x7x_default_settings = {
> > +	.als_time = 200,
> > +	/* must be a multiple of 50mS */
> > +	.als_gain = 0,
> > +	/* this is actually an index into the gain table */
> > +	.prx_time = 0xfe, /*5.4 mS */
> > +	/* 2.7ms prox integration time - decrease to increase time */
> > +	/* decreases in 2.7 ms intervals */
> > +	.prox_gain = 1,
> > +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
> > +	/* assume clear glass as default */
> > +	.wait_time = 245,
> > +	/* Time between PRX and ALS cycles -decrease to increase time */
> > +	/* decreases in 2.7 ms intervals */
> > +	.prox_config = 0,
> > +	/* Prox configuration filters */
> > +	.als_gain_trim = 1000,
> > +	/* default gain trim to account for aperture effects */
> > +	.als_cal_target = 150,
> > +	/* Known external ALS reading used for calibration */
> > +	.als_thresh_low = 200,
> > +	/* CH0 'low' count to trigger interrupt */
> > +	.als_thresh_high = 256,
> > +	/* CH0 'high' count to trigger interrupt */
> > +	.als_persistence = 0xFF,
> > +	/* Number of 'out of limits' ADC readings PRX/ALS*/
> > +	.interrupts_en = 0x00,
> > +	/* Default interrupt(s) enabled.
> > +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
> > +	.prox_thres_low  = 0,
> > +	.prox_thres_high = 512,
> > +	/*default threshold adjust either manually or with cal routine*/
> > +	.prox_max_samples_cal = 30,
> > +	.prox_pulse_count = 8
> > +};
> > +
> > +static const s16 tsl2X7X_als_gainadj[] = {
> > +	1,
> > +	8,
> > +	16,
> > +	120
> > +};
> > +
> > +static const s16 tsl2X7X_prx_gainadj[] = {
> > +	1,
> > +	2,
> > +	4,
> > +	8
> > +};
> > +
> > +/* Channel variations */
> > +enum {
> > +	ALS,
> > +	PRX,
> > +	ALSPRX,
> > +	PRX2,
> > +	ALSPRX2,
> > +};
> > +
> > +const u8 device_channel_config[] = {
> > +	ALS,
> > +	PRX,
> > +	PRX,
> > +	ALSPRX,
> > +	ALSPRX,
> > +	ALS,
> > +	PRX2,
> > +	PRX2,
> > +	ALSPRX2,
> > +	ALSPRX2
> > +};
> > +
> > +/*
> > + * Read a number of bytes starting at register (reg) location.
> > + * Return 0, or i2c_smbus_write_byte ERROR code.
> > + */
> > +static int
> > +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
> > +{
> > +	int ret;
> > +
> > +	/* select register to write */
> > +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
> > +	if (ret<  0) {
> > +		dev_err(&client->dev, "%s: failed to write register %x\n"
> > +				, __func__, reg);
> > +		return ret;
> > +	}
> > +	/* read the data */
> > +	*val = i2c_smbus_read_byte(client);
> > +
> > +	return 0;
> > +}
> > +
> I would much prefer if you'd use kernel-doc for comments oabout functions.
> > +/*
> > + * Reads and calculates current lux value.
> > + * The raw ch0 and ch1 values of the ambient light sensed in the last
> > + * integration cycle are read from the device.
> > + * Time scale factor array values are adjusted based on the integration time.
> > + * The raw values are multiplied by a scale factor, and device gain is obtained
> > + * using gain index. Limit checks are done next, then the ratio of a multiple
> > + * of ch1 value, to the ch0 value, is calculated. The array tsl2x7x_device_lux[]
> > + * declared above is then scanned to find the first ratio value that is just
> > + * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
> > + * the array are then used along with the time scale factor array values, to
> > + * calculate the lux.
> > + */
> > +static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
> > +{
> > +	u16 ch0, ch1; /* separated ch0/ch1 data from device */
> > +	u32 lux; /* raw lux calculated from device data */
> > +	u64 lux64;
> > +	u32 ratio;
> > +	u8 buf[4];
> > +	struct tsl2x7x_lux *p;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int i, ret;
> > +	u32 ch0lux = 0;
> > +	u32 ch1lux = 0;
> > +
> > +	if (mutex_trylock(&chip->als_mutex) == 0) {
> > +		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is
> busy\n");
> > +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
> > +	}
> > +
> > +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
> > +		/* device is not enabled */
> > +		dev_err(&chip->client->dev, "%s: device is not enabled\n",
> > +				__func__);
> > +		ret = -EBUSY ;
> > +		goto out_unlock;
> > +	}
> > +
> > +	ret = tsl2x7x_i2c_read(chip->client,
> > +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]);
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed to read CMD_REG\n", __func__);
> > +		goto out_unlock;
> > +	}
> > +	/* is data new&  valid */
> > +	if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: data not valid yet\n", __func__);
> > +		ret = chip->als_cur_info.lux; /* return LAST VALUE */
> > +		goto out_unlock;
> > +	}
> > +
> > +	for (i = 0; i<  4; i++) {
> > +		ret = tsl2x7x_i2c_read(chip->client,
> > +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
> > +			&buf[i]);
> > +		if (ret<  0) {
> > +			dev_err(&chip->client->dev,
> > +				"%s: failed to read. err=%x\n", __func__, ret);
> > +			goto out_unlock;
> > +		}
> > +	}
> > +
> > +	/* clear status, really interrupt status ( are off),
> > +	but we use the bit anyway */
> > +	ret = i2c_smbus_write_byte(chip->client,
> > +		(TSL2X7X_CMD_REG |
> > +				TSL2X7X_CMD_SPL_FN |
> > +				TSL2X7X_CMD_ALS_INT_CLR));
> > +
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +		"i2c_write_command failed in %s, err = %d\n",
> > +			__func__, ret);
> > +		goto out_unlock; /* have no data, so return failure */
> > +	}
> > +
> > +	/* extract ALS/lux data */
> > +	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
> > +	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
> > +
> > +	chip->als_cur_info.als_ch0 = ch0;
> > +	chip->als_cur_info.als_ch1 = ch1;
> > +
> > +	if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) {
> > +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> > +		goto return_max;
> > +	}
> > +
> > +	if (ch0 == 0) {
> > +		/* have no data, so return LAST VALUE */
> > +		ret = chip->als_cur_info.lux = 0;
> > +		goto out_unlock;
> > +	}
> > +	/* calculate ratio */
> > +	ratio = (ch1<<  15) / ch0;
> > +	/* convert to unscaled lux using the pointer to the table */
> > +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
> > +	while (p->ratio != 0&&  p->ratio<  ratio)
> > +			p++;
> > +
> > +	if (p->ratio == 0) {
> > +		lux = 0;
> > +	} else {
> > +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
> > +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> > +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
> > +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> > +		lux = ch0lux - ch1lux;
> > +	}
> > +
> > +	/* note: lux is 31 bit max at this point */
> > +	if (ch1lux>  ch0lux) {
> > +		dev_dbg(&chip->client->dev, "Returning last value\n");
> > +		ret = chip->als_cur_info.lux;
> > +		goto out_unlock;
> > +	}
> > +
> > +	/* adjust for active time scale */
> > +	if (chip->als_time_scale == 0)
> > +		lux = 0;
> > +	else
> > +		lux = (lux + (chip->als_time_scale>>  1)) /
> > +			chip->als_time_scale;
> > +
> > +	/* adjust for active gain scale
> > +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
> > +	 * User-specified gain provides a multiplier.
> > +	 * Apply user-specified gain before shifting right to retain precision.
> > +	 * Use 64 bits to avoid overflow on multiplication.
> > +	 * Then go back to 32 bits before division to avoid using div_u64().
> > +	 */
> > +	lux64 = lux;
> > +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
> > +	lux64>>= 8;
> > +	lux = lux64;
> > +	lux = (lux + 500) / 1000;
> > +
> > +	if (lux>  TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
> > +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> > +
> > +	/* Update the structure with the latest lux. */
> > +return_max:
> > +	chip->als_cur_info.lux = lux;
> > +	ret = lux;
> > +
> > +out_unlock:
> > +	mutex_unlock(&chip->als_mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * Proximity poll function - if valid data is available, read and form the ch0
> > + * and prox data values, check for limits on the ch0 value, and check the prox
> > + * data against the current thresholds, to set the event status accordingly.
> > + */
> > +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
> > +{
> > +#define CONSECUTIVE_RETRIES 50
> > +
> > +	int i;
> > +	int ret;
> > +	u8 status;
> > +	u8 chdata[2];
> > +	int err_cnt;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	if (mutex_trylock(&chip->prox_mutex) == 0) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: Can't get prox mutex\n", __func__);
> > +		return -EBUSY;
> > +	}
> > +
> > +	err_cnt = 0;
> > +
> > +try_again:
> I'd like a comment on why this looping is necessary....
> > +
> > +	ret = tsl2x7x_i2c_read(chip->client,
> > +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status);
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +		"%s: i2c err=%d\n", __func__, ret);
> > +		goto prox_poll_err;
> > +	}
> > +
> > +	if (chip->id<  tsl2572) {
> > +		if (!(status&  TSL2X7X_STA_ADC_VALID)) {
> > +			err_cnt++;
> > +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> > +				dev_err(&chip->client->dev,
> > +				"%s: Consec. retries exceeded\n", __func__);
> > +				goto prox_poll_err;
> > +			}
> > +		goto try_again;
> > +		}
> > +	} else {
> > +		if (!(status&  TSL2X7X_STA_PRX_VALID)) {
> > +			err_cnt++;
> > +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> > +				dev_err(&chip->client->dev,
> > +				"%s: Consec. retries exceeded\n", __func__);
> > +				goto prox_poll_err;
> > +			}
> > +		goto try_again;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i<  2; i++) {
> > +		ret = tsl2x7x_i2c_read(chip->client,
> > +			(TSL2X7X_CMD_REG |
> > +					(TSL2X7X_PRX_LO + i)),&chdata[i]);
> > +		if (ret<  0)
> > +			goto prox_poll_err;
> > +	}
> > +
> > +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
> > +	if (chip->prox_cur_info.prox_data == 0)
> > +		goto try_again;
> > +
> > +	if (chip->prox_cur_info.prox_data>=
> > +			chip->tsl2x7x_settings.prox_thres_high)
> > +		chip->prox_cur_info.prox_event = 1;
> > +	else
> > +		chip->prox_cur_info.prox_event = 0;
> So this is manually polling the event signal. I'd argue that this is a
> job for userspace
> if the device isn't doing it hardware (or the interrupt signal is
> connected).
> > +
> > +	mutex_unlock(&chip->prox_mutex);
> > +	return chip->prox_cur_info.prox_event;
> > +
> > +prox_poll_err:
> > +	mutex_unlock(&chip->prox_mutex);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * Provides initial operational parameter defaults.
> > + * These defaults may be changed through the device's sysfs files.
> > + */
> > +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
> > +{
> > +	/* If Operational settings defined elsewhere.. */
> > +	if (chip->pdata&&  chip->pdata->platform_default_settings != 0)
> > +		memcpy(&(chip->tsl2x7x_settings),
> > +			chip->pdata->platform_default_settings,
> > +			sizeof(tsl2x7x_default_settings));
> > +	else
> > +		memcpy(&(chip->tsl2x7x_settings),
> > +			&tsl2x7x_default_settings,
> > +			sizeof(tsl2x7x_default_settings));
> > +
> > +	/* Load up the proper lux table. */
> > +	if (chip->pdata&&  chip->pdata->platform_lux_table[0].ratio != 0)
> > +		memcpy(chip->tsl2x7x_device_lux,
> > +			chip->pdata->platform_lux_table,
> > +			sizeof(chip->pdata->platform_lux_table));
> > +	else
> > +		memcpy(chip->tsl2x7x_device_lux,
> > +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
> > +				MAX_DEFAULT_TABLE_BYTES);
> > +
> > +}
> > +
> > +/*
> > + * Obtain single reading and calculate the als_gain_trim
> > + * (later used to derive actual lux).
> > + * Return updated gain_trim value.
> > + */
> > +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	u8 reg_val;
> > +	int gain_trim_val;
> > +	int ret;
> > +	int lux_val;
> > +
> > +	ret = i2c_smbus_write_byte(chip->client,
> > +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +		"%s: failed to write CNTRL register, ret=%d\n",
> > +		__func__, ret);
> > +		return ret;
> > +	}
> > +
> > +	reg_val = i2c_smbus_read_byte(chip->client);
> > +	if ((reg_val&  (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
> > +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed: ADC not enabled\n", __func__);
> > +		return -1;
> > +	}
> > +
> > +	ret = i2c_smbus_write_byte(chip->client,
> > +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed to write ctrl reg: ret=%d\n",
> > +			__func__, ret);
> > +		return ret;
> > +	}
> > +
> > +	reg_val = i2c_smbus_read_byte(chip->client);
> > +	if ((reg_val&  TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed: STATUS - ADC not valid.\n", __func__);
> > +		return -ENODATA;
> > +	}
> > +
> > +	lux_val = tsl2x7x_get_lux(indio_dev);
> > +	if (lux_val<  0) {
> > +		dev_err(&chip->client->dev,
> > +		"%s: failed to get lux\n", __func__);
> > +		return lux_val;
> > +	}
> > +
> > +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
> > +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
> > +	if ((gain_trim_val<  250) || (gain_trim_val>  4000))
> > +		return -ERANGE;
> > +
> > +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
> > +	dev_info(&chip->client->dev,
> > +		"%s als_calibrate completed\n", chip->client->name);
> > +
> > +	return (int) gain_trim_val;
> > +}
> > +
> > +/*
> > + * Turn the device on.
> > + * Configuration must be set before calling this function.
> > + */
> > +static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
> > +{
> > +	int i;
> > +	int ret = 0;
> > +	u8 *dev_reg;
> > +	u8 utmp;
> > +	int als_count;
> > +	int als_time;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	u8 reg_val = 0;
> > +
> > +	if (chip->pdata&&  chip->pdata->power_on)
> > +		chip->pdata->power_on(indio_dev);
> > +
> > +	/* Non calculated parameters */
> > +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
> > +			chip->tsl2x7x_settings.prx_time;
> > +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
> > +			chip->tsl2x7x_settings.wait_time;
> > +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
> > +			chip->tsl2x7x_settings.prox_config;
> > +
> > +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
> > +		(chip->tsl2x7x_settings.als_thresh_low)&  0xFF;
> > +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
> > +		(chip->tsl2x7x_settings.als_thresh_low>>  8)&  0xFF;
> > +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
> > +		(chip->tsl2x7x_settings.als_thresh_high)&  0xFF;
> > +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
> > +		(chip->tsl2x7x_settings.als_thresh_high>>  8)&  0xFF;
> > +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
> > +		chip->tsl2x7x_settings.als_persistence;
> > +
> > +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
> > +			chip->tsl2x7x_settings.prox_pulse_count;
> > +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
> > +	chip->tsl2x7x_settings.prox_thres_low;
> > +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
> > +			chip->tsl2x7x_settings.prox_thres_high;
> > +
> > +	/* and make sure we're not already on */
> > +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> > +		/* if forcing a register update - turn off, then on */
> > +		dev_info(&chip->client->dev, "device is already enabled\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* determine als integration regster */
> > +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
> > +	if (als_count == 0)
> > +		als_count = 1; /* ensure at least one cycle */
> > +
> > +	/* convert back to time (encompasses overrides) */
> > +	als_time = (als_count * 27 + 5) / 10;
> > +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
> > +
> > +	/* Set the gain based on tsl2x7x_settings struct */
> > +	chip->tsl2x7x_config[TSL2X7X_GAIN] =
> > +		(chip->tsl2x7x_settings.als_gain |
> > +			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
> > +			| ((chip->tsl2x7x_settings.prox_gain)<<  2));
> > +
> > +	/* set chip struct re scaling and saturation */
> > +	chip->als_saturation = als_count * 922; /* 90% of full scale */
> > +	chip->als_time_scale = (als_time + 25) / 50;
> > +
> > +	/* TSL2X7X Specific power-on / adc enable sequence
> > +	 * Power on the device 1st. */
> > +	utmp = TSL2X7X_CNTL_PWR_ON;
> > +	ret = i2c_smbus_write_byte_data(chip->client,
> > +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed on CNTRL reg.\n", __func__);
> > +		return -1;
> > +	}
> > +
> > +	/* Use the following shadow copy for our delay before enabling ADC.
> > +	 * Write all the registers. */
> > +	for (i = 0, dev_reg = chip->tsl2x7x_config;
> > +			i<  TSL2X7X_MAX_CONFIG_REG; i++) {
> > +		ret = i2c_smbus_write_byte_data(chip->client,
> > +				TSL2X7X_CMD_REG + i, *dev_reg++);
> > +		if (ret<  0) {
> > +			dev_err(&chip->client->dev,
> > +			"%s: failed on write to reg %d.\n", __func__, i);
> > +			return ret;
> > +		}
> > +	}
> > +
> > +	udelay(3000);	/* Power-on settling time */
> > +
> > +	/* NOW enable the ADC
> > +	 * initialize the desired mode of operation */
> > +	utmp = TSL2X7X_CNTL_PWR_ON |
> > +			TSL2X7X_CNTL_ADC_ENBL |
> > +			TSL2X7X_CNTL_PROX_DET_ENBL;
> > +	ret = i2c_smbus_write_byte_data(chip->client,
> > +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> > +	if (ret<  0) {
> > +		dev_err(&chip->client->dev,
> > +			"%s: failed on 2nd CTRL reg.\n", __func__);
> > +		return ret;
> > +		}
> > +
> > +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
> > +
> > +	if (chip->tsl2x7x_settings.interrupts_en != 0) {
> > +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
> > +
> > +		reg_val = TSL2X7X_CNTL_PWR_ON |
> TSL2X7X_CNTL_ADC_ENBL;
> > +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
> > +			(chip->tsl2x7x_settings.interrupts_en == 0x30))
> > +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
> > +
> > +		reg_val |= chip->tsl2x7x_settings.interrupts_en;
> > +		ret = i2c_smbus_write_byte_data(chip->client,
> > +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
> > +		if (ret<  0)
> > +			dev_err(&chip->client->dev,
> > +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
> > +				__func__);
> > +
> > +		/* Clear out any initial interrupts  */
> > +		ret = i2c_smbus_write_byte(chip->client,
> > +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> > +			TSL2X7X_CMD_PROXALS_INT_CLR);
> > +		if (ret<  0) {
> > +			dev_err(&chip->client->dev,
> > +				"%s: failed in tsl2x7x_chip_on\n",
> > +				__func__);
> > +		return ret;
> > +		}
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
> > +{
> > +	int ret;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	/* turn device off */
> > +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> > +
> > +	ret = i2c_smbus_write_byte_data(chip->client,
> > +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
> > +
> > +	if (chip->pdata&&  chip->pdata->power_off)
> > +		chip->pdata->power_off(chip->client);
> > +
> > +	return ret;
> > +}
> > +
> > +/*
> > + * Proximity calibration helper function
> > + * runs through a collection of data samples,
> > + * sets the min, max, mean, and std dev.
> > + */
> > +static
> > +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
> > +{
> > +	int i;
> > +	int min, max, sum, mean;
> > +	unsigned long stddev;
> > +	int tmp;
> > +
> > +	if (length == 0)
> > +		length = 1;
> > +
> > +	sum = 0;
> > +	min = INT_MAX;
> > +	max = INT_MIN;
> > +	for (i = 0; i<  length; i++) {
> > +		sum += data[i];
> avoid using min as a variable name (as it's also an appropriate function)
> _min = MIN(data[i], _min); saves you a line of code.
> > +		if (data[i]<  min)
> > +			min = data[i];
> > +		if (data[i]>  max)
> > +			max = data[i];
> > +	}
> > +	mean = sum/length;
> > +	statP->min = min;
> > +	statP->max = max;
> > +	statP->mean = mean;
> > +
> > +	sum = 0;
> > +	for (i = 0; i<  length; i++) {
> > +		tmp = data[i]-mean;
> > +		sum += tmp * tmp;
> > +	}
> > +	stddev = int_sqrt((long)sum)/length;
> > +	statP->stddev = stddev;
> > +}
> > +
> > +/**
> > + * Proximity calibration - collects a number of samples,
> > + * calculates a standard deviation based on the samples, and
> > + * sets the threshold accordingly.
> > + */
> > +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
> > +{
> > +	u16 prox_history[MAX_SAMPLES_CAL+1];
> spaces around that +
> > +	int i;
> > +	struct prox_stat prox_stat_data[2];
> > +	struct prox_stat *calP;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	u8 tmp_irq_settings;
> > +	u8 current_state = chip->tsl2x7x_chip_status;
> > +
> > +	if (chip->tsl2x7x_settings.prox_max_samples_cal>  MAX_SAMPLES_CAL)
> {
> > +		dev_err(&chip->client->dev,
> > +			"%s: max prox samples cal is too big: %d\n",
> > +			__func__, chip-
> >tsl2x7x_settings.prox_max_samples_cal);
> > +		chip->tsl2x7x_settings.prox_max_samples_cal =
> MAX_SAMPLES_CAL;
> > +	}
> > +
> > +	/* have to stop to change settings */
> > +	tsl2x7x_chip_off(indio_dev);
> > +
> > +	/* Enable proximity detection save just in case prox not wanted yet*/
> > +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
> > +	chip->tsl2x7x_settings.interrupts_en |=
> TSL2X7X_CNTL_PROX_INT_ENBL;
> > +
> > +	/*turn on device if not already on*/
> > +	tsl2x7x_chip_on(indio_dev);
> > +
> > +	/*gather the samples*/
> > +	for (i = 0; i<  chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
> > +		mdelay(15);
> > +		tsl2x7x_prox_poll(indio_dev);
> > +		prox_history[i] = chip->prox_cur_info.prox_data;
> > +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
> > +			i, chip->prox_cur_info.prox_data);
> > +	}
> > +
> > +	tsl2x7x_chip_off(indio_dev);
> > +	calP =&prox_stat_data[PROX_STAT_CAL];
> > +	tsl2x7x_prox_calculate(prox_history,
> > +		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
> > +	chip->tsl2x7x_settings.prox_thres_high = (calP->max<<  1) - calP-
> >mean;
> > +
> > +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
> > +		calP->min, calP->mean, calP->max);
> > +	dev_info(&chip->client->dev,
> > +		"%s proximity threshold set to %d\n",
> > +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
> > +
> > +	/* back to the way they were */
> > +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
> > +	if (current_state == TSL2X7X_CHIP_WORKING)
> > +		tsl2x7x_chip_on(indio_dev);
> > +}
> > +
> > +static ssize_t tsl2x7x_power_state_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
> > +}
> > +
> > +static ssize_t tsl2x7x_power_state_store(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	bool value;
> > +
> > +	if (strtobool(buf,&value))
> > +		return -EINVAL;
> > +
> > +	if (!value)
> > +		tsl2x7x_chip_off(indio_dev);
> > +	else
> > +		tsl2x7x_chip_on(indio_dev);
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	if (chip->id>  tsl2771)
> > +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
> > +	else
> > +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
> > +}
> > +
> > +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
> > +}
> > +
> > +static ssize_t tsl2x7x_als_time_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	return snprintf(buf, PAGE_SIZE, "%d\n",
> > +			chip->tsl2x7x_settings.als_time);
> > +}
> > +
> > +static ssize_t tsl2x7x_als_time_store(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	unsigned long value;
> > +
> > +	if (kstrtoul(buf, 0,&value))
> > +		return -EINVAL;
> > +
> > +	if ((value<  50) || (value>  650))
> > +		return -EINVAL;
> > +
> > +	if (value % 50)
> > +		return -EINVAL;
> > +
> > +	 chip->tsl2x7x_settings.als_time = value;
> > +
> > +	return len;
> > +}
> > +
> > +static IIO_CONST_ATTR(illuminance0_integration_time_available,
> > +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
> > +
> > +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	return snprintf(buf, PAGE_SIZE, "%d\n",
> > +			chip->tsl2x7x_settings.als_cal_target);
> > +}
> > +
> > +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	unsigned long value;
> > +
> > +	if (kstrtoul(buf, 0,&value))
> > +		return -EINVAL;
> > +
> > +	if (value)
> > +		chip->tsl2x7x_settings.als_cal_target = value;
> > +
> > +	return len;
> > +}
> > +
> > +/* sampling_frequency AKA persistence in data sheet */
> > +static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	return snprintf(buf, PAGE_SIZE, "%d\n",
> > +			chip->tsl2x7x_settings.als_persistence);
> > +}
> > +
> > +static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	unsigned long value;
> > +
> > +	if (kstrtoul(buf, 0,&value))
> > +		return -EINVAL;
> > +
> > +	chip->tsl2x7x_settings.als_persistence = value;
> > +
> > +	return len;
> > +}
> > +
> > +static IIO_CONST_ATTR(sampling_frequency_available,
> > +		"0x00 - 0xFF (0 - 255)");
> > +
> > +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	bool value;
> > +
> > +	if (strtobool(buf,&value))
> > +		return -EINVAL;
> > +
> > +	if (value)
> > +		tsl2x7x_als_calibrate(indio_dev);
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
> > +	struct device_attribute *attr, char *buf)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int i;
> > +	int offset = 0;
> > +
> > +	i = 0;
> Set i at the declaration above.
> > +	while (i<  (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
> > +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
> > +			chip->tsl2x7x_device_lux[i].ratio,
> > +			chip->tsl2x7x_device_lux[i].ch0,
> > +			chip->tsl2x7x_device_lux[i].ch1);
> > +		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
> > +			/* We just printed the first "0" entry.
> > +			 * Now get rid of the extra "," and break. */
> > +			offset--;
> > +			break;
> > +		}
> > +		i++;
> > +	}
> > +
> > +	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
> > +	return offset;
> > +}
> > +
> > +static ssize_t tsl2x7x_luxtable_store(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
> > +	int n;
> > +
> > +	get_options(buf, ARRAY_SIZE(value), value);
> > +
> > +	/* We now have an array of ints starting at value[1], and
> > +	 * enumerated by value[0].
> > +	 * We expect each group of three ints is one table entry,
> > +	 * and the last table entry is all 0.
> > +	 */
> > +	n = value[0];
> > +	if ((n % 3) || n<  6 ||
> > +			n>  ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
> > +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
> > +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
> > +		tsl2x7x_chip_off(indio_dev);
> > +
> > +	/* Zero out the table */
> > +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
> > +	memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4));
> > +
> > +	return len;
> > +}
> > +
> > +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
> > +	struct device_attribute *attr, const char *buf, size_t len)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	bool value;
> > +
> > +	if (strtobool(buf,&value))
> > +		return -EINVAL;
> > +
> > +	if (value)
> > +		tsl2x7x_prox_cal(indio_dev);
> > +
> > +	return len;
> > +}
> > +
> > +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
> > +					 u64 event_code)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> > +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x10);
> > +	else
> > +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x20);
> > +
> > +	return ret;
> > +}
> > +
> > +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
> > +					  u64 event_code,
> > +					  int val)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> {
> > +		if (val)
> > +			chip->tsl2x7x_settings.interrupts_en |= 0x10;
> > +		else
> > +			chip->tsl2x7x_settings.interrupts_en&= 0x20;
> > +	} else {
> > +		if (val)
> > +			chip->tsl2x7x_settings.interrupts_en |= 0x20;
> > +		else
> > +			chip->tsl2x7x_settings.interrupts_en&= 0x10;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
> > +				  u64 event_code,
> > +				  int val)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> {
> > +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> > +		case IIO_EV_DIR_RISING:
> > +			chip->tsl2x7x_settings.als_thresh_high = val;
> > +			break;
> > +		case IIO_EV_DIR_FALLING:
> > +			chip->tsl2x7x_settings.als_thresh_low = val;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	} else {
> > +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> > +		case IIO_EV_DIR_RISING:
> > +			chip->tsl2x7x_settings.prox_thres_high = val;
> > +			break;
> > +		case IIO_EV_DIR_FALLING:
> > +			chip->tsl2x7x_settings.prox_thres_low = val;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
> > +			       u64 event_code,
> > +			       int *val)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> {
> > +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> > +		case IIO_EV_DIR_RISING:
> > +			*val = chip->tsl2x7x_settings.als_thresh_high;
> > +			break;
> > +		case IIO_EV_DIR_FALLING:
> > +			*val = chip->tsl2x7x_settings.als_thresh_low;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	} else {
> > +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> > +		case IIO_EV_DIR_RISING:
> > +			*val = chip->tsl2x7x_settings.prox_thres_high;
> > +			break;
> > +		case IIO_EV_DIR_FALLING:
> > +			*val = chip->tsl2x7x_settings.prox_thres_low;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int *val,
> > +			    int *val2,
> > +			    long mask)
> > +{
> > +	int ret = -EINVAL;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	switch (mask) {
> > +	case 0:
> > +		switch (chan->type) {
> > +		case IIO_LIGHT:
> > +			tsl2x7x_get_lux(indio_dev);
> > +			if (chan->processed_val)
> > +				*val = chip->als_cur_info.lux;
> > +			else
> > +				*val = (((chip->als_cur_info.als_ch0<<  16) |
> > +					chip->als_cur_info.als_ch1));
> Why have a processed read back and a raw readback?
> > +			ret = IIO_VAL_INT;
> > +			break;
> > +		case IIO_PROXIMITY:
> > +			tsl2x7x_prox_poll(indio_dev);
> > +			if (chan->processed_val)
> Hmm.. this is uggly.  We effectively have polling of an event status.
> Normally
> I'd expect the event to only occur as a an IIO event rather than being
> readable like
> this...
> > +				*val = chip->prox_cur_info.prox_event;
> > +			else
> > +				*val = chip->prox_cur_info.prox_data;
> > +			ret = IIO_VAL_INT;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +			break;
> > +		}
> > +		break;
> > +	case IIO_CHAN_INFO_CALIBSCALE:
> > +		if (chan->type == IIO_LIGHT)
> > +			*val =
> > +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
> > +		else
> > +			*val =
> > +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
> > +		ret = IIO_VAL_INT;
> > +		break;
> > +	case IIO_CHAN_INFO_CALIBBIAS:
> > +		*val = chip->tsl2x7x_settings.als_gain_trim;
> > +		ret = IIO_VAL_INT;
> > +		break;
> > +
> > +	default:
> > +		ret = -EINVAL;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
> > +			       struct iio_chan_spec const *chan,
> > +			       int val,
> > +			       int val2,
> > +			       long mask)
> > +{
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_CALIBSCALE:
> > +		if (chan->type == IIO_LIGHT) {
> > +			switch (val) {
> > +			case 1:
> > +				chip->tsl2x7x_settings.als_gain = 0;
> > +				break;
> > +			case 8:
> > +				chip->tsl2x7x_settings.als_gain = 1;
> > +				break;
> > +			case 16:
> > +				chip->tsl2x7x_settings.als_gain = 2;
> > +				break;
> > +			case 120:
> > +				if (chip->id>  tsl2771)
> > +					return -EINVAL;
> > +				chip->tsl2x7x_settings.als_gain = 3;
> > +				break;
> > +			case 128:
> > +				if (chip->id<  tsl2572)
> > +					return -EINVAL;
> > +				chip->tsl2x7x_settings.als_gain = 3;
> > +				break;
> > +			default:
> > +				return -EINVAL;
> > +			}
> > +		} else {
> > +			switch (val) {
> > +			case 1:
> > +				chip->tsl2x7x_settings.prox_gain = 0;
> > +				break;
> > +			case 2:
> > +				chip->tsl2x7x_settings.prox_gain = 1;
> > +				break;
> > +			case 4:
> > +				chip->tsl2x7x_settings.prox_gain = 2;
> > +				break;
> > +			case 8:
> > +				chip->tsl2x7x_settings.prox_gain = 3;
> > +				break;
> > +			default:
> > +				return -EINVAL;
> > +			}
> > +		}
> > +		break;
> > +	case IIO_CHAN_INFO_CALIBBIAS:
> > +		chip->tsl2x7x_settings.als_gain_trim = val;
> > +		break;
> > +
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
> > +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
> > +
> > +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
> > +		tsl2x7x_prox_gain_available_show, NULL);
> > +
> > +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
> > +		tsl2x7x_gain_available_show, NULL);
> > +
> > +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
> > +		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
> > +
> > +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
> > +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
> > +
> > +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
> > +		tsl2x7x_do_calibrate);
> > +
> > +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
> > +		tsl2x7x_do_prox_calibrate);
> > +
> > +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
> > +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
> > +
> > +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
> > +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
> > +
> > +/* Use the default register values to identify the Taos device */
> > +static int tsl2x7x_device_id(unsigned char *id, int target)
> > +{
> > +	switch (target) {
> > +	case tsl2571:
> > +	case tsl2671:
> > +	case tsl2771:
> > +		return ((*id&  0xf0) == TRITON_ID);
> > +	break;
> > +	case tmd2671:
> > +	case tmd2771:
> > +		return ((*id&  0xf0) == HALIBUT_ID);
> > +	break;
> > +	case tsl2572:
> > +	case tsl2672:
> > +	case tmd2672:
> > +	case tsl2772:
> > +	case tmd2772:
> > +		return ((*id&  0xf0) == SWORDFISH_ID);
> > +	break;
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +/*
> > + * Interrupt Event Handler */
> > +static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
> > +{
> > +	struct iio_dev *indio_dev = private;
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	s64 timestamp = iio_get_time_ns();
> > +	int ret;
> > +	int value;
> > +
> > +	value = i2c_smbus_read_byte_data(chip->client,
> > +		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
> > +
> > +	/* What type of interrupt do we need to process */
> > +	if (value&  TSL2X7X_STA_PRX_INTR) {
> > +		tsl2x7x_prox_poll(indio_dev);
> > +		iio_push_event(indio_dev,
> > +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
> > +						    0,
> > +						    IIO_EV_TYPE_THRESH,
> > +						    IIO_EV_DIR_EITHER),
> > +						    timestamp);
> > +	}
> > +
> > +	if (value&  TSL2X7X_STA_ALS_INTR) {
> > +		tsl2x7x_get_lux(indio_dev);
> why the get value?  No real guarantee you'll get the one that caused the
> event as far as I can see.
> > +		iio_push_event(indio_dev,
> > +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
> > +					    0,
> > +					    IIO_EV_TYPE_THRESH,
> > +					    IIO_EV_DIR_EITHER),
> > +					    timestamp);
> > +	}
> > +	/* Clear interrupt now that we have the status */
> > +	ret = i2c_smbus_write_byte(chip->client,
> > +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> > +		TSL2X7X_CMD_PROXALS_INT_CLR);
> > +	if (ret<  0)
> > +		dev_err(&chip->client->dev,
> > +			"%s: Failed to clear irq from event handler. err = %d\n",
> > +			__func__, ret);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static struct attribute *tsl2x7x_ALS_device_attrs[] = {
> > +	&dev_attr_power_state.attr,
> > +	&dev_attr_illuminance0_calibscale_available.attr,
> > +	&dev_attr_illuminance0_integration_time.attr,
> > +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> > +	&dev_attr_illuminance0_target_input.attr,
> > +	&dev_attr_illuminance0_calibrate.attr,
> > +	&dev_attr_illuminance0_lux_table.attr,
> > +	&dev_attr_sampling_frequency.attr,
> > +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute *tsl2x7x_PRX_device_attrs[] = {
> > +	&dev_attr_power_state.attr,
> > +	&dev_attr_sampling_frequency.attr,
> > +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> > +	&dev_attr_proximity_calibrate.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
> > +	&dev_attr_power_state.attr,
> > +	&dev_attr_illuminance0_calibscale_available.attr,
> > +	&dev_attr_illuminance0_integration_time.attr,
> > +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> > +	&dev_attr_illuminance0_target_input.attr,
> > +	&dev_attr_illuminance0_calibrate.attr,
> > +	&dev_attr_illuminance0_lux_table.attr,
> > +	&dev_attr_sampling_frequency.attr,
> > +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> > +	&dev_attr_proximity_calibrate.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
> > +	&dev_attr_power_state.attr,
> > +	&dev_attr_sampling_frequency.attr,
> > +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> > +	&dev_attr_proximity_calibrate.attr,
> > +	&dev_attr_proximity_calibscale_available.attr,
> > +	NULL
> > +};
> > +
> > +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
> > +	&dev_attr_power_state.attr,
> > +	&dev_attr_illuminance0_calibscale_available.attr,
> > +	&dev_attr_illuminance0_integration_time.attr,
> > +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> > +	&dev_attr_illuminance0_target_input.attr,
> > +	&dev_attr_illuminance0_calibrate.attr,
> > +	&dev_attr_illuminance0_lux_table.attr,
> > +	&dev_attr_sampling_frequency.attr,
> > +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> > +	&dev_attr_proximity_calibrate.attr,
> > +	&dev_attr_proximity_calibscale_available.attr,
> > +	NULL
> > +};
> > +
> > +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
> > +	[ALS] = {
> > +		.attrs = tsl2x7x_ALS_device_attrs,
> > +	},
> > +	[PRX] = {
> > +		.attrs = tsl2x7x_PRX_device_attrs,
> > +	},
> > +	[ALSPRX] = {
> > +		.attrs = tsl2x7x_ALSPRX_device_attrs,
> > +	},
> > +	[PRX2] = {
> > +		.attrs = tsl2x7x_PRX2_device_attrs,
> > +	},
> > +	[ALSPRX2] = {
> > +		.attrs = tsl2x7x_ALSPRX2_device_attrs,
> > +	},
> > +};
> > +
> > +static const struct iio_info tsl2X7X_device_info[] = {
> > +	[ALS] = {
> > +		.attrs =&tsl2X7X_device_attr_group_tbl[ALS],
> > +		.driver_module = THIS_MODULE,
> > +		.read_raw =&tsl2x7x_read_raw,
> > +		.write_raw =&tsl2x7x_write_raw,
> > +		.read_event_value =&tsl2x7x_read_thresh,
> > +		.write_event_value =&tsl2x7x_write_thresh,
> > +		.read_event_config =&tsl2x7x_read_interrupt_config,
> > +		.write_event_config =&tsl2x7x_write_interrupt_config,
> > +	},
> > +	[PRX] = {
> > +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX],
> > +		.driver_module = THIS_MODULE,
> > +		.read_raw =&tsl2x7x_read_raw,
> > +		.write_raw =&tsl2x7x_write_raw,
> > +		.read_event_value =&tsl2x7x_read_thresh,
> > +		.write_event_value =&tsl2x7x_write_thresh,
> > +		.read_event_config =&tsl2x7x_read_interrupt_config,
> > +		.write_event_config =&tsl2x7x_write_interrupt_config,
> > +	},
> > +	[ALSPRX] = {
> > +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX],
> > +		.driver_module = THIS_MODULE,
> > +		.read_raw =&tsl2x7x_read_raw,
> > +		.write_raw =&tsl2x7x_write_raw,
> > +		.read_event_value =&tsl2x7x_read_thresh,
> > +		.write_event_value =&tsl2x7x_write_thresh,
> > +		.read_event_config =&tsl2x7x_read_interrupt_config,
> > +		.write_event_config =&tsl2x7x_write_interrupt_config,
> > +	},
> > +	[PRX2] = {
> > +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX2],
> > +		.driver_module = THIS_MODULE,
> > +		.read_raw =&tsl2x7x_read_raw,
> > +		.write_raw =&tsl2x7x_write_raw,
> > +		.read_event_value =&tsl2x7x_read_thresh,
> > +		.write_event_value =&tsl2x7x_write_thresh,
> > +		.read_event_config =&tsl2x7x_read_interrupt_config,
> > +		.write_event_config =&tsl2x7x_write_interrupt_config,
> > +	},
> > +	[ALSPRX2] = {
> > +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2],
> > +		.driver_module = THIS_MODULE,
> > +		.read_raw =&tsl2x7x_read_raw,
> > +		.write_raw =&tsl2x7x_write_raw,
> > +		.read_event_value =&tsl2x7x_read_thresh,
> > +		.write_event_value =&tsl2x7x_write_thresh,
> > +		.read_event_config =&tsl2x7x_read_interrupt_config,
> > +		.write_event_config =&tsl2x7x_write_interrupt_config,
> > +	},
> > +};
> > +
> > +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
> > +	[ALS] = {
> > +		.channel = {
> > +			[0] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[1] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.info_mask =
> > +
> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> > +
> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +		},
> > +	.num_channels = 2,
> > +	.info =&tsl2X7X_device_info[ALS],
> > +	},
> > +	[PRX] = {
> > +		.channel = {
> > +			[0] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[1] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +		},
> > +	.num_channels = 2,
> > +	.info =&tsl2X7X_device_info[PRX],
> > +	},
> > +	[ALSPRX] = {
> > +		.channel = {
> > +			[0] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[1] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.info_mask =
> > +
> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> > +
> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +			[2] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[3] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 0,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +		},
> > +	.num_channels = 4,
> > +	.info =&tsl2X7X_device_info[ALSPRX],
> > +	},
> > +	[PRX2] = {
> > +		.channel = {
> > +			[0] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[1] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.info_mask =
> > +
> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +		},
> > +	.num_channels = 2,
> > +	.info =&tsl2X7X_device_info[PRX2],
> > +	},
> > +	[ALSPRX2] = {
> > +		.channel = {
> > +				[0] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[1] = {
> > +				.type = IIO_LIGHT,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.info_mask =
> > +
> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> > +
> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +			[2] = {
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.processed_val = 1,
> > +			},
> > +			[3] = {
> I'm more than a little confused here.  There are only 2 actually
> channels?  If so I don't
> see why we have 4 channels here...
> > +				.type = IIO_PROXIMITY,
> > +				.indexed = 1,
> > +				.channel = 0,
> > +				.info_mask =
> > +
> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> > +				.event_mask = TSL2X7X_EVENT_MASK
> > +			},
> > +		},
> > +	.num_channels = 4,
> > +	.info =&tsl2X7X_device_info[ALSPRX2],
> > +	},
> > +};
> > +
> > +/*
> > + * Client probe function.
> > + */
> > +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
> > +	const struct i2c_device_id *id)
> > +{
> > +	int ret;
> > +	unsigned char device_id;
> > +	struct iio_dev *indio_dev;
> > +	struct tsl2X7X_chip *chip;
> > +
> > +	indio_dev = iio_allocate_device(sizeof(*chip));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	chip = iio_priv(indio_dev);
> > +	chip->client = clientp;
> > +	i2c_set_clientdata(clientp, indio_dev);
> > +
> > +	ret = tsl2x7x_i2c_read(chip->client,
> > +		TSL2X7X_CHIPID,&device_id);
> > +	if (ret<  0)
> > +		goto fail1;
> > +
> > +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
> > +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
> > +		dev_info(&chip->client->dev,
> > +				"i2c device found does not match expected id
> in %s\n",
> > +				__func__);
> > +		goto fail1;
> > +	}
> > +
> > +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG |
> TSL2X7X_CNTRL));
> > +	if (ret<  0) {
> > +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err =
> %d\n",
> > +				__func__, ret);
> > +		goto fail1;
> > +	}
> > +
> > +	/* ALS and PROX functions can be invoked via user space poll
> > +	 * or H/W interrupt. If busy return last sample. */
> > +	mutex_init(&chip->als_mutex);
> > +	mutex_init(&chip->prox_mutex);
> > +
> > +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
> > +	chip->pdata = clientp->dev.platform_data;
> > +	chip->id = id->driver_data;
> > +	chip->chip_info =
> > +		&tsl2x7x_chip_info_tbl[device_channel_config[id-
> >driver_data]];
> > +
> > +	indio_dev->info = chip->chip_info->info;
> > +	indio_dev->dev.parent =&clientp->dev;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->name = chip->client->name;
> > +	indio_dev->channels = chip->chip_info->channel;
> > +	indio_dev->num_channels = chip->chip_info->num_channels;
> > +
> > +	if (clientp->irq) {
> > +		ret = request_threaded_irq(clientp->irq,
> > +					   NULL,
> > +					&tsl2x7x_event_handler,
> > +					   IRQF_TRIGGER_RISING |
> IRQF_ONESHOT,
> > +					   "TSL2X7X_event",
> > +					   indio_dev);
> > +		if (ret) {
> > +			dev_err(&clientp->dev,
> > +				"%s: irq request failed", __func__);
> > +			goto fail2;
> > +		}
> > +	}
> > +
> > +	/* Load up the defaults */
> > +	tsl2x7x_defaults(chip);
> > +	/* Make sure the chip is on */
> > +	tsl2x7x_chip_on(indio_dev);
> > +
> > +	ret = iio_device_register(indio_dev);
> > +	if (ret) {
> > +		dev_err(&clientp->dev,
> > +			"%s: iio registration failed\n", __func__);
> > +		goto fail3;
> > +	}
> > +
> > +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
> > +
> > +	return 0;
> > +
> > +fail3:
> > +	if (clientp->irq)
> > +		free_irq(clientp->irq, indio_dev);
> > +fail2:
> > +	iio_free_device(indio_dev);
> > +fail1:
> > +	kfree(chip);
> double free of chip.  It's allocated and managed by the
> iio_allocate_device and iio_free_device calls.
> For that matter, most of the goto fail2's need the iio_free_device call..
> > +	return ret;
> > +}
> > +
> > +static int tsl2x7x_suspend(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int ret = 0;
> > +
> > +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> > +		ret = tsl2x7x_chip_off(indio_dev);
> > +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> > +	}
> > +
> > +	if (chip->pdata&&  chip->pdata->platform_power) {
> > +		pm_message_t pmm = {PM_EVENT_SUSPEND};
> > +		chip->pdata->platform_power(dev, pmm);
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +static int tsl2x7x_resume(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> > +	int ret = 0;
> > +
> > +	if (chip->pdata&&  chip->pdata->platform_power) {
> > +		pm_message_t pmm = {PM_EVENT_RESUME};
> > +		chip->pdata->platform_power(dev, pmm);
> > +	}
> > +
> > +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
> > +		ret = tsl2x7x_chip_on(indio_dev);
> > +
> > +	return ret;
> > +}
> > +
> > +static int __devexit tsl2x7x_remove(struct i2c_client *client)
> > +{
> > +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> > +	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
> These shouldn't both be true....
> would expect an iio_unregister_device call here.
> > +
> > +	tsl2x7x_chip_off(indio_dev);
> > +
> > +	if (client->irq)
> > +		free_irq(client->irq, chip->client->name);
> > +
> > +	iio_free_device(indio_dev);
> > +
> > +	return 0;
> > +}
> > +
> > +static struct i2c_device_id tsl2x7x_idtable[] = {
> > +	{ "tsl2571", tsl2571 },
> > +	{ "tsl2671", tsl2671 },
> > +	{ "tmd2671", tmd2671 },
> > +	{ "tsl2771", tsl2771 },
> > +	{ "tmd2771", tmd2771 },
> > +	{ "tsl2572", tsl2572 },
> > +	{ "tsl2672", tsl2672 },
> > +	{ "tmd2672", tmd2672 },
> > +	{ "tsl2772", tsl2772 },
> > +	{ "tmd2772", tmd2772 },
> > +	{}
> > +};
> > +
> > +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
> > +
> > +static const struct dev_pm_ops tsl2x7x_pm_ops = {
> > +	.suspend = tsl2x7x_suspend,
> > +	.resume  = tsl2x7x_resume,
> > +};
> > +
> > +/* Driver definition */
> > +static struct i2c_driver tsl2x7x_driver = {
> > +	.driver = {
> > +		.name = "tsl2x7x",
> > +		.pm =&tsl2x7x_pm_ops,
> > +	},
> > +	.id_table = tsl2x7x_idtable,
> > +	.probe = tsl2x7x_probe,
> > +	.remove = __devexit_p(tsl2x7x_remove),
> > +};
> > +
> > +module_i2c_driver(tsl2x7x_driver);
> > +
> > +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
> > +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor
> driver");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/staging/iio/light/tsl2x7x_core.h
> b/drivers/staging/iio/light/tsl2x7x_core.h
> > new file mode 100644
> > index 0000000..663e846
> > --- /dev/null
> > +++ b/drivers/staging/iio/light/tsl2x7x_core.h
> why not just tsl2x7x.h?
> > @@ -0,0 +1,75 @@
> > +/*
> > + * Device driver for monitoring ambient light intensity (lux)
> > + * and proximity (prox) within the TAOS TSL2X7X family of devices.
> > + *
> > + * Copyright (c) 2012, TAOS Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License as published by
> > + * the Free Software Foundation; 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.,
> > + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
> > + */
> > +
> > +#ifndef __TSL2X7X_H
> > +#define __TSL2X7X_H
> > +#include<linux/pm.h>
> > +
> > +/* Max number of segments allowable in LUX table */
> > +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
> > +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) *
> TSL2X7X_MAX_LUX_TABLE_SIZE)
> > +
> > +struct iio_dev;
> 
> Why the forward declaration of iio_chan_spec?
> > +struct iio_chan_spec;
> > +
> > +struct tsl2x7x_lux {
> > +	unsigned int ratio;
> > +	unsigned int ch0;
> > +	unsigned int ch1;
> > +};
> > +
> > +/* Refer to tsl2x7x_default_settings for member desc. */
> > +struct tsl2x7x_settings {
> > +	int als_time;
> > +	int als_gain;
> > +	int als_gain_trim;
> > +	int wait_time;
> > +	int prx_time;
> > +	int prox_gain;
> > +	int prox_config;
> > +	int als_cal_target;
> > +	u8  interrupts_en;
> > +	u8  als_persistence;
> > +	int als_thresh_low;
> > +	int als_thresh_high;
> > +	int prox_thres_low;
> > +	int prox_thres_high;
> > +	int prox_pulse_count;
> > +	int prox_max_samples_cal;
> > +};
> > +
> > +/* struct tsl2x7x_platform_data -
> > + * Platform unique glass and defaults
> > + * Platform PM functions. */
> Would prefer this to be in kernel doc.
> > +struct tsl2X7X_platform_data {
> > +	/* Suspend/resume platform cb */
> > +	int (*platform_power)(struct device *dev, pm_message_t);
> > +	/* The following callback gets called when the device is powered on */
> > +	int (*power_on)      (struct iio_dev *indio_dev);
> > +	/* The following callback gets called when the device is powered off */
> > +	int (*power_off)     (struct i2c_client *dev);
> > +	/* These are the device specific glass coefficents used to
> > +	 * calculate Lux */
> > +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
> > +	struct tsl2x7x_settings *platform_default_settings;
> > +};
> > +
> > +#endif /* __TSL2X7X_H */
> > --
> > 1.7.4.1
> >

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH V4] TAOS tsl2x7x
  2012-03-27 19:58   ` Jon Brenner
@ 2012-03-28 17:51     ` Jonathan Cameron
  2012-03-28 23:29         ` Jon Brenner
  0 siblings, 1 reply; 8+ messages in thread
From: Jonathan Cameron @ 2012-03-28 17:51 UTC (permalink / raw)
  To: Jon Brenner; +Cc: Jonathan Cameron, linux-iio, Linux Kernel

On 03/27/2012 08:58 PM, Jon Brenner wrote:
> Hello Jonathan,
> Still a little confused (and stuck) here.
> Using your code from the tsl2563:
> 1. The case for IIO_LIGHT appears to return the computed LUX.
> Yet you have no ".processed_val = 1: in your channel table - so where in in_illuminance0_input (aka lux)  coming from?
Indeed. That's bug number 1...
>  
> 2. The case for IIO_INTENSITY looks for 'chan->channel' to determine when to present 'chip->data0' or 'chip->data1' - 
> But it appears that 'chan->channel' will always be 0 as '.channel =' isn't defined in the table?
> So how can you ever get chip->data01?
> 
Gah, this driver clearly needs another look.  You are quite correct,
that is a bug as well as it will always return the first channel.

Clearly a little bit of code rot has occured here.  Oops.
Do you want to do the patch, or shall I (with a reported by
of course!).

Jonathan
> For example, tsl2563 code portion follows.
> 
> <Snip>
> static int tsl2563_read_raw(struct iio_dev *indio_dev,
> 			    struct iio_chan_spec const *chan,
> 			    int *val,
> 			    int *val2,
> 			    long m)
> {
> 	int ret = -EINVAL;
> 	u32 calib0, calib1;
> 	struct tsl2563_chip *chip = iio_priv(indio_dev);
> 
> 	mutex_lock(&chip->lock);
> 	switch (m) {
> 	case 0:
> 		switch (chan->type) {
> 		case IIO_LIGHT:
> 			ret = tsl2563_get_adc(chip);
> 			if (ret)
> 				goto error_ret;
> 			calib0 = calib_adc(chip->data0, chip->calib0) *
> 				chip->cover_comp_gain;
> 			calib1 = calib_adc(chip->data1, chip->calib1) *
> 				chip->cover_comp_gain;
> 			*val = adc_to_lux(calib0, calib1);
> 			ret = IIO_VAL_INT;
> 			break;
> 		case IIO_INTENSITY:
> 			ret = tsl2563_get_adc(chip);
> 			if (ret)
> 				goto error_ret;
> 			if (chan->channel == 0)
> 				*val = chip->data0;
> 			else
> 				*val = chip->data1;
> 			ret = IIO_VAL_INT;
> 			break;
> 		default:
> 			break;
> 		}
> 		break;
> 
> 	case IIO_CHAN_INFO_CALIBSCALE:
> 		if (chan->channel == 0)
> 			*val = calib_to_sysfs(chip->calib0);
> 		else
> 			*val = calib_to_sysfs(chip->calib1);
> 		ret = IIO_VAL_INT;
> 		break;
> 	default:
> 		ret = -EINVAL;
> 		goto error_ret;
> 	}
> 
> error_ret:
> 	mutex_unlock(&chip->lock);
> 	return ret;
> }
> 
> static const struct iio_chan_spec tsl2563_channels[] = {
> 	{
> 		.type = IIO_LIGHT,
> 		.indexed = 1,
> 		.channel = 0,
> 	}, {
> 		.type = IIO_INTENSITY,
> 		.modified = 1,
> 		.channel2 = IIO_MOD_LIGHT_BOTH,
> 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> 		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> 					  IIO_EV_DIR_RISING) |
> 			       IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> 					  IIO_EV_DIR_FALLING)),
> 	}, {
> 		.type = IIO_INTENSITY,
> 		.modified = 1,
> 		.channel2 = IIO_MOD_LIGHT_IR,
> 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> 	}
> };
> 
> 
> What am I not seeing here?
> 
> Jon
> 
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>> Sent: Friday, March 23, 2012 8:54 AM
>> To: Jon Brenner
>> Cc: linux-iio; Linux Kernel
>> Subject: Re: [PATCH V4] TAOS tsl2x7x
>>
>> On 3/21/2012 4:16 PM, Jon Brenner wrote:
>>> TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families
>> (inc. all variants).
>> Mostly looking good...
>>
>> Couple of issues remaining.
>> * Why have processed and raw accesses to the same channels?  That is
>> definitely not the intent.
>> Either it's worth processing these raw adc channels in kernel, or not.
>> If it isn't you certainly shouldn't
>> be munging together the two adc's into a single value.
>>
>> So you have done this for two reasons...
>> * For light sensors it was to allow the processed illuminance value and
>> also the raw adc values.
>> Previously we have done this by having IIO_LIGHT (and hence illuminance)
>> for the processed one
>> and marking the other two as IIO_INTENSITY with modifiers for the
>> frequency range they cover...
>> * For proximity one is the raw reading (fine), the other is a means of
>> getting at the threshold event
>> if interrupts are not supported.  It is done by a software comparison of
>> the threshold and the raw
>> reading.  This should not be in driver as if the functionality is
>> desired, it should be done in userspace.
>>
>> Various other minor bits like error paths that don't clean up commented
>> inline.
>>
>>
>>>
>>> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
>>> ---
>>>   .../light/sysfs-bus-iio-light-tsl2583              |    6 +
>>>   .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
>>>   drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
>>>   .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
>>>   .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
>>>   drivers/staging/iio/light/Kconfig                  |    8 +
>>>   drivers/staging/iio/light/Makefile                 |    1 +
>>>   drivers/staging/iio/light/tsl2x7x_core.c           | 1911 ++++++++++++++++++++
>>>   drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
>>>   9 files changed, 2032 insertions(+), 24 deletions(-)
>>>
>>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
>> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
>>> new file mode 100644
>>> index 0000000..8f2a038
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
>>> @@ -0,0 +1,6 @@
>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>> +KernelVersion:	2.6.37
>>> +Contact:	linux-iio@vger.kernel.org
>>> +Description:
>>> +		This property causes an internal calibration of the als gain trim
>>> +		value which is later used in calculating illuminance in lux.
>>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
>> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
>>> new file mode 100644
>>> index 0000000..cceadae
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
>>> @@ -0,0 +1,20 @@
>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>> +KernelVersion:	2.6.37
>>> +Contact:	linux-iio@vger.kernel.org
>>> +Description:
>>> +		This property causes an internal calibration of the als gain trim
>>> +		value which is later used in calculating illuminance in lux.
>>> +
>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
>>> +KernelVersion:	3.3-rc1
>>> +Contact:	linux-iio@vger.kernel.org
>>> +Description:
>>> +		Simultainious ALS channel data.
>> That wasn't the intent of 'both' at all.  (+ typo).  It means a raw
>> reading from a diod that
>> detects the 'sum' of infrared and visible.
>>> +
>>> +What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
>>> +KernelVersion:	3.3-rc1
>>> +Contact:	linux-iio@vger.kernel.org
>>> +Description:
>>> +		Causes an recalculation and adjustment to the
>>> +		proximity_thresh_rising_value.
>>> +
>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio
>>> index 46a995d..5b2b5d3 100644
>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio
>>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
>>> @@ -258,6 +258,8 @@ What
>> 	/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
>>>   What
>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
>>>   What
>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
>>>   What
>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
>>> +what
>> 	/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
>>> +what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
>>>   KernelVersion:	2.6.35
>>>   Contact:	linux-iio@vger.kernel.org
>>>   Description:
>>> @@ -457,6 +459,10 @@ What:
>> 	/sys/.../events/in_voltageY_raw_thresh_falling_value
>>>   What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
>>>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
>>>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
>>> +What:		/sys/.../events/illuminance0_thresh_falling_value
>>> +what:		/sys/.../events/illuminance0_thresh_rising_value
>>> +what:		/sys/.../events/proximity_thresh_falling_value
>>> +what:		/sys/.../events/proximity_thresh_rising_value
>>>   KernelVersion:	2.6.37
>>>   Contact:	linux-iio@vger.kernel.org
>>>   Description:
>>> @@ -739,3 +745,4 @@ Description:
>>>   		system. To minimize the current consumption of the system,
>>>   		the bridge can be disconnected (when it is not being used
>>>   		using the bridge_switch_en attribute.
>>> +
>> spurious blank line?
>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>> index edbf470..4385c70 100644
>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>> @@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
>>>   Description:
>>>   		This property gets/sets the sensors ADC analog integration
>> time.
>>>
>>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
>>> +What:		/sys/bus/iio/devices/device[n]/lux_table
>>>   KernelVersion:	2.6.37
>>>   Contact:	linux-iio@vger.kernel.org
>>>   Description:
>>> -		Hardware or software applied calibration scale factor assumed
>>> -		to account for attenuation due to industrial design (glass
>>> -		filters or aperture holes).
>>> +		This property gets/sets the table of coefficients
>>> +		used in calculating illuminance in lux.
>>> +
>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>>> deleted file mode 100644
>>> index 660781d..0000000
>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>>> +++ /dev/null
>>> @@ -1,20 +0,0 @@
>>> -What:		/sys/bus/iio/devices/device[n]/lux_table
>>> -KernelVersion:	2.6.37
>>> -Contact:	linux-iio@vger.kernel.org
>>> -Description:
>>> -		This property gets/sets the table of coefficients
>>> -		used in calculating illuminance in lux.
>>> -
>>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>> -KernelVersion:	2.6.37
>>> -Contact:	linux-iio@vger.kernel.org
>>> -Description:
>>> -		This property causes an internal calibration of the als gain trim
>>> -		value which is later used in calculating illuminance in lux.
>>> -
>>> -What:
>> 	/sys/bus/iio/devices/device[n]/illuminance0_input_target
>>> -KernelVersion:	2.6.37
>>> -Contact:	linux-iio@vger.kernel.org
>>> -Description:
>>> -		This property is the known externally illuminance (in lux).
>>> -		It is used in the process of calibrating the device accuracy.
>>> diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
>>> index e7e9159..976f790 100644
>>> --- a/drivers/staging/iio/light/Kconfig
>>> +++ b/drivers/staging/iio/light/Kconfig
>>> @@ -31,4 +31,12 @@ config TSL2583
>>>   	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
>>>   	 Access ALS data via iio, sysfs.
>>>
>>> +config TSL2x7x
>>> +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and
>> proximity sensors"
>>> +	depends on I2C
>>> +	help
>>> +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572,
>> tsl2672,
>>> +	 tmd2672, tsl2772, tmd2772 devices.
>>> +	 Provides iio_events and direct access via sysfs.
>>> +
>>>   endmenu
>>> diff --git a/drivers/staging/iio/light/Makefile
>> b/drivers/staging/iio/light/Makefile
>>> index 3011fbf..ff12c4b 100644
>>> --- a/drivers/staging/iio/light/Makefile
>>> +++ b/drivers/staging/iio/light/Makefile
>>> @@ -5,3 +5,4 @@
>>>   obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
>>>   obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
>>>   obj-$(CONFIG_TSL2583)	+= tsl2583.o
>>> +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
>>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.c
>> b/drivers/staging/iio/light/tsl2x7x_core.c
>>> new file mode 100644
>>> index 0000000..c0d9d6e
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
>>> @@ -0,0 +1,1911 @@
>>> +/*
>>> + * Device driver for monitoring ambient light intensity in (lux)
>>> + * and proximity detection (prox) within the TAOS TSL2X7X family of devices.
>>> + *
>>> + * Copyright (c) 2012, TAOS Corporation.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; 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.,
>>> + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
>>> + */
>>> +
>>> +#include<linux/kernel.h>
>>> +#include<linux/i2c.h>
>>> +#include<linux/errno.h>
>>> +#include<linux/delay.h>
>>> +#include<linux/mutex.h>
>>> +#include<linux/interrupt.h>
>>> +#include<linux/slab.h>
>>> +#include<linux/module.h>
>>> +#include<linux/version.h>
>>> +#include "tsl2x7x_core.h"
>>> +#include "../events.h"
>>> +#include "../iio.h"
>>> +#include "../sysfs.h"
>>> +
>>> +/* Cal defs*/
>>> +#define PROX_STAT_CAL        0
>>> +#define PROX_STAT_SAMP       1
>>> +#define MAX_SAMPLES_CAL      200
>>> +
>>> +/* TSL2X7X Device ID */
>>> +#define TRITON_ID    0x00
>>> +#define SWORDFISH_ID 0x30
>>> +#define HALIBUT_ID   0x20
>>> +
>>> +/* Lux calculation constants */
>>> +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
>>> +
>>> +/* TAOS Register definitions - note:
>>> + * depending on device, some of these register are not used and the
>>> + * register address is benign.
>>> + */
>>> +/* 2X7X register offsets */
>>> +#define TSL2X7X_MAX_CONFIG_REG         16
>>> +
>>> +/* Device Registers and Masks */
>>> +#define TSL2X7X_CNTRL                  0x00
>>> +#define TSL2X7X_ALS_TIME               0X01
>>> +#define TSL2X7X_PRX_TIME               0x02
>>> +#define TSL2X7X_WAIT_TIME              0x03
>>> +#define TSL2X7X_ALS_MINTHRESHLO        0X04
>>> +#define TSL2X7X_ALS_MINTHRESHHI        0X05
>>> +#define TSL2X7X_ALS_MAXTHRESHLO        0X06
>>> +#define TSL2X7X_ALS_MAXTHRESHHI        0X07
>>> +#define TSL2X7X_PRX_MINTHRESHLO        0X08
>>> +#define TSL2X7X_PRX_MINTHRESHHI        0X09
>>> +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
>>> +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
>>> +#define TSL2X7X_PERSISTENCE            0x0C
>>> +#define TSL2X7X_PRX_CONFIG             0x0D
>>> +#define TSL2X7X_PRX_COUNT              0x0E
>>> +#define TSL2X7X_GAIN                   0x0F
>>> +#define TSL2X7X_NOTUSED                0x10
>>> +#define TSL2X7X_REVID                  0x11
>>> +#define TSL2X7X_CHIPID                 0x12
>>> +#define TSL2X7X_STATUS                 0x13
>>> +#define TSL2X7X_ALS_CHAN0LO            0x14
>>> +#define TSL2X7X_ALS_CHAN0HI            0x15
>>> +#define TSL2X7X_ALS_CHAN1LO            0x16
>>> +#define TSL2X7X_ALS_CHAN1HI            0x17
>>> +#define TSL2X7X_PRX_LO                 0x18
>>> +#define TSL2X7X_PRX_HI                 0x19
>>> +
>>> +/* tsl2X7X cmd reg masks */
>>> +#define TSL2X7X_CMD_REG                0x80
>>> +#define TSL2X7X_CMD_SPL_FN             0x60
>>> +
>>> +#define TSL2X7X_CMD_PROX_INT_CLR       0X05
>>> +#define TSL2X7X_CMD_ALS_INT_CLR        0x06
>>> +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
>>> +
>>> +/* tsl2X7X cntrl reg masks */
>>> +#define TSL2X7X_CNTL_ADC_ENBL          0x02
>>> +#define TSL2X7X_CNTL_PWR_ON            0x01
>>> +
>>> +/* tsl2X7X status reg masks */
>>> +#define TSL2X7X_STA_ADC_VALID          0x01
>>> +#define TSL2X7X_STA_PRX_VALID          0x02
>>> +#define TSL2X7X_STA_ADC_PRX_VALID      0x03
>>> +#define TSL2X7X_STA_ALS_INTR           0x10
>>> +#define TSL2X7X_STA_ADC_INTR           0x10
>>> +#define TSL2X7X_STA_PRX_INTR           0x20
>>> +
>>> +#define TSL2X7X_STA_ADC_INTR           0x10
>>> +
>>> +/* tsl2X7X cntrl reg masks */
>>> +#define TSL2X7X_CNTL_REG_CLEAR         0x00
>>> +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
>>> +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
>>> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
>>> +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
>>> +#define TSL2X7X_CNTL_PWRON             0x01
>>> +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
>>> +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
>>> +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
>>> +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
>>> +
>>> +/*Prox diode to use */
>>> +#define TSL2X7X_DIODE0                 0x10
>>> +#define TSL2X7X_DIODE1                 0x20
>>> +#define TSL2X7X_DIODE_BOTH             0x30
>>> +
>>> +/* LED Power */
>>> +#define TSL2X7X_mA100                  0x00
>>> +#define TSL2X7X_mA50                   0x40
>>> +#define TSL2X7X_mA25                   0x80
>>> +#define TSL2X7X_mA13                   0xD0
>>> +
>>> +/*Common device IIO EventMask */
>>> +#define TSL2X7X_EVENT_MASK \
>>> +		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
>>> +		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
>>> +
>>> +/* TAOS txx2x7x Device family members */
>>> +enum {
>>> +	tsl2571,
>>> +	tsl2671,
>>> +	tmd2671,
>>> +	tsl2771,
>>> +	tmd2771,
>>> +	tsl2572,
>>> +	tsl2672,
>>> +	tmd2672,
>>> +	tsl2772,
>>> +	tmd2772
>>> +};
>>> +
>>> +enum {
>>> +	TSL2X7X_CHIP_UNKNOWN = 0,
>>> +	TSL2X7X_CHIP_WORKING = 1,
>>> +	TSL2X7X_CHIP_SUSPENDED = 2
>>> +};
>>> +
>>> +/* Per-device data */
>>> +struct tsl2x7x_als_info {
>>> +	u16 als_ch0;
>>> +	u16 als_ch1;
>>> +	u16 lux;
>>> +};
>>> +
>>> +/* proximity data */
>>> +struct tsl2x7x_prox_info {
>>> +	u16 prox_data;
>>> +	int prox_event;
>>> +};
>>> +
>>> +struct prox_stat {
>>> +	u16 min;
>>> +	u16 max;
>>> +	u16 mean;
>>> +	unsigned long stddev;
>>> +};
>>> +
>>> +struct tsl2x7x_chip_info {
>>> +	int num_channels;
>>> +	struct iio_chan_spec		channel[9];
>>> +	const struct iio_info		*info;
>>> +};
>>> +
>>> +struct tsl2X7X_chip {
>>> +	kernel_ulong_t id;
>>> +	struct mutex prox_mutex;
>>> +	struct mutex als_mutex;
>>> +	struct i2c_client *client;
>>> +	struct tsl2x7x_prox_info prox_cur_info;
>>> +	struct tsl2x7x_als_info als_cur_info;
>>> +	struct tsl2x7x_settings tsl2x7x_settings;
>>> +	struct tsl2X7X_platform_data *pdata;
>>> +	int als_time_scale;
>>> +	int als_saturation;
>>> +	int tsl2x7x_chip_status;
>>> +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
>>> +	const struct tsl2x7x_chip_info	*chip_info;
>>> +	const struct iio_info *info;
>>> +	s64 event_timestamp;
>>> +	/* This structure is intentionally large to accommodate
>>> +	 * updates via sysfs. */
>>> +	/* Sized to 9 = max 8 segments + 1 termination segment */
>>> +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
>>> +};
>>> +
>>> +/* Different devices require different coefficents */
>>> +static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
>>> +	{ 14461,   611,   1211 },
>>> +	{ 18540,   352,    623 },
>>> +	{     0,     0,      0 },
>>> +};
>>> +
>>> +static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
>>> +	{ 11635,   115,    256 },
>>> +	{ 15536,    87,    179 },
>>> +	{     0,     0,      0 },
>>> +};
>>> +
>>> +static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
>>> +	{ 14013,   466,   917 },
>>> +	{ 18222,   310,   552 },
>>> +	{     0,     0,     0 },
>>> +};
>>> +
>>> +static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
>>> +	{ 13218,   130,   262 },
>>> +	{ 17592,   92,    169 },
>>> +	{     0,     0,     0 },
>>> +};
>>> +
>>> +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
>> check white space around here.  looks like a mixture of tabs and spaces...
>>> +	[tsl2571] = tsl2x71_lux_table,
>>> +	[tsl2671] =	tsl2x71_lux_table,
>>> +	[tmd2671] =	tmd2x71_lux_table,
>>> +	[tsl2771] =	tsl2x71_lux_table,
>>> +	[tmd2771] =	tmd2x71_lux_table,
>>> +	[tsl2572] =	tsl2x72_lux_table,
>>> +	[tsl2672] =	tsl2x72_lux_table,
>>> +	[tmd2672] = tmd2x72_lux_table,
>>> +	[tsl2772] =	tsl2x72_lux_table,
>>> +	[tmd2772] =	tmd2x72_lux_table,
>>> +};
>>> +
>>> +static const struct tsl2x7x_settings tsl2x7x_default_settings = {
>>> +	.als_time = 200,
>>> +	/* must be a multiple of 50mS */
>>> +	.als_gain = 0,
>>> +	/* this is actually an index into the gain table */
>>> +	.prx_time = 0xfe, /*5.4 mS */
>>> +	/* 2.7ms prox integration time - decrease to increase time */
>>> +	/* decreases in 2.7 ms intervals */
>>> +	.prox_gain = 1,
>>> +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
>>> +	/* assume clear glass as default */
>>> +	.wait_time = 245,
>>> +	/* Time between PRX and ALS cycles -decrease to increase time */
>>> +	/* decreases in 2.7 ms intervals */
>>> +	.prox_config = 0,
>>> +	/* Prox configuration filters */
>>> +	.als_gain_trim = 1000,
>>> +	/* default gain trim to account for aperture effects */
>>> +	.als_cal_target = 150,
>>> +	/* Known external ALS reading used for calibration */
>>> +	.als_thresh_low = 200,
>>> +	/* CH0 'low' count to trigger interrupt */
>>> +	.als_thresh_high = 256,
>>> +	/* CH0 'high' count to trigger interrupt */
>>> +	.als_persistence = 0xFF,
>>> +	/* Number of 'out of limits' ADC readings PRX/ALS*/
>>> +	.interrupts_en = 0x00,
>>> +	/* Default interrupt(s) enabled.
>>> +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
>>> +	.prox_thres_low  = 0,
>>> +	.prox_thres_high = 512,
>>> +	/*default threshold adjust either manually or with cal routine*/
>>> +	.prox_max_samples_cal = 30,
>>> +	.prox_pulse_count = 8
>>> +};
>>> +
>>> +static const s16 tsl2X7X_als_gainadj[] = {
>>> +	1,
>>> +	8,
>>> +	16,
>>> +	120
>>> +};
>>> +
>>> +static const s16 tsl2X7X_prx_gainadj[] = {
>>> +	1,
>>> +	2,
>>> +	4,
>>> +	8
>>> +};
>>> +
>>> +/* Channel variations */
>>> +enum {
>>> +	ALS,
>>> +	PRX,
>>> +	ALSPRX,
>>> +	PRX2,
>>> +	ALSPRX2,
>>> +};
>>> +
>>> +const u8 device_channel_config[] = {
>>> +	ALS,
>>> +	PRX,
>>> +	PRX,
>>> +	ALSPRX,
>>> +	ALSPRX,
>>> +	ALS,
>>> +	PRX2,
>>> +	PRX2,
>>> +	ALSPRX2,
>>> +	ALSPRX2
>>> +};
>>> +
>>> +/*
>>> + * Read a number of bytes starting at register (reg) location.
>>> + * Return 0, or i2c_smbus_write_byte ERROR code.
>>> + */
>>> +static int
>>> +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
>>> +{
>>> +	int ret;
>>> +
>>> +	/* select register to write */
>>> +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
>>> +	if (ret<  0) {
>>> +		dev_err(&client->dev, "%s: failed to write register %x\n"
>>> +				, __func__, reg);
>>> +		return ret;
>>> +	}
>>> +	/* read the data */
>>> +	*val = i2c_smbus_read_byte(client);
>>> +
>>> +	return 0;
>>> +}
>>> +
>> I would much prefer if you'd use kernel-doc for comments oabout functions.
>>> +/*
>>> + * Reads and calculates current lux value.
>>> + * The raw ch0 and ch1 values of the ambient light sensed in the last
>>> + * integration cycle are read from the device.
>>> + * Time scale factor array values are adjusted based on the integration time.
>>> + * The raw values are multiplied by a scale factor, and device gain is obtained
>>> + * using gain index. Limit checks are done next, then the ratio of a multiple
>>> + * of ch1 value, to the ch0 value, is calculated. The array tsl2x7x_device_lux[]
>>> + * declared above is then scanned to find the first ratio value that is just
>>> + * above the ratio we just calculated. The ch0 and ch1 multiplier constants in
>>> + * the array are then used along with the time scale factor array values, to
>>> + * calculate the lux.
>>> + */
>>> +static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
>>> +{
>>> +	u16 ch0, ch1; /* separated ch0/ch1 data from device */
>>> +	u32 lux; /* raw lux calculated from device data */
>>> +	u64 lux64;
>>> +	u32 ratio;
>>> +	u8 buf[4];
>>> +	struct tsl2x7x_lux *p;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int i, ret;
>>> +	u32 ch0lux = 0;
>>> +	u32 ch1lux = 0;
>>> +
>>> +	if (mutex_trylock(&chip->als_mutex) == 0) {
>>> +		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is
>> busy\n");
>>> +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
>>> +	}
>>> +
>>> +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
>>> +		/* device is not enabled */
>>> +		dev_err(&chip->client->dev, "%s: device is not enabled\n",
>>> +				__func__);
>>> +		ret = -EBUSY ;
>>> +		goto out_unlock;
>>> +	}
>>> +
>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]);
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed to read CMD_REG\n", __func__);
>>> +		goto out_unlock;
>>> +	}
>>> +	/* is data new&  valid */
>>> +	if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: data not valid yet\n", __func__);
>>> +		ret = chip->als_cur_info.lux; /* return LAST VALUE */
>>> +		goto out_unlock;
>>> +	}
>>> +
>>> +	for (i = 0; i<  4; i++) {
>>> +		ret = tsl2x7x_i2c_read(chip->client,
>>> +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
>>> +			&buf[i]);
>>> +		if (ret<  0) {
>>> +			dev_err(&chip->client->dev,
>>> +				"%s: failed to read. err=%x\n", __func__, ret);
>>> +			goto out_unlock;
>>> +		}
>>> +	}
>>> +
>>> +	/* clear status, really interrupt status ( are off),
>>> +	but we use the bit anyway */
>>> +	ret = i2c_smbus_write_byte(chip->client,
>>> +		(TSL2X7X_CMD_REG |
>>> +				TSL2X7X_CMD_SPL_FN |
>>> +				TSL2X7X_CMD_ALS_INT_CLR));
>>> +
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +		"i2c_write_command failed in %s, err = %d\n",
>>> +			__func__, ret);
>>> +		goto out_unlock; /* have no data, so return failure */
>>> +	}
>>> +
>>> +	/* extract ALS/lux data */
>>> +	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
>>> +	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
>>> +
>>> +	chip->als_cur_info.als_ch0 = ch0;
>>> +	chip->als_cur_info.als_ch1 = ch1;
>>> +
>>> +	if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) {
>>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
>>> +		goto return_max;
>>> +	}
>>> +
>>> +	if (ch0 == 0) {
>>> +		/* have no data, so return LAST VALUE */
>>> +		ret = chip->als_cur_info.lux = 0;
>>> +		goto out_unlock;
>>> +	}
>>> +	/* calculate ratio */
>>> +	ratio = (ch1<<  15) / ch0;
>>> +	/* convert to unscaled lux using the pointer to the table */
>>> +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
>>> +	while (p->ratio != 0&&  p->ratio<  ratio)
>>> +			p++;
>>> +
>>> +	if (p->ratio == 0) {
>>> +		lux = 0;
>>> +	} else {
>>> +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
>>> +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
>>> +		lux = ch0lux - ch1lux;
>>> +	}
>>> +
>>> +	/* note: lux is 31 bit max at this point */
>>> +	if (ch1lux>  ch0lux) {
>>> +		dev_dbg(&chip->client->dev, "Returning last value\n");
>>> +		ret = chip->als_cur_info.lux;
>>> +		goto out_unlock;
>>> +	}
>>> +
>>> +	/* adjust for active time scale */
>>> +	if (chip->als_time_scale == 0)
>>> +		lux = 0;
>>> +	else
>>> +		lux = (lux + (chip->als_time_scale>>  1)) /
>>> +			chip->als_time_scale;
>>> +
>>> +	/* adjust for active gain scale
>>> +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
>>> +	 * User-specified gain provides a multiplier.
>>> +	 * Apply user-specified gain before shifting right to retain precision.
>>> +	 * Use 64 bits to avoid overflow on multiplication.
>>> +	 * Then go back to 32 bits before division to avoid using div_u64().
>>> +	 */
>>> +	lux64 = lux;
>>> +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
>>> +	lux64>>= 8;
>>> +	lux = lux64;
>>> +	lux = (lux + 500) / 1000;
>>> +
>>> +	if (lux>  TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
>>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
>>> +
>>> +	/* Update the structure with the latest lux. */
>>> +return_max:
>>> +	chip->als_cur_info.lux = lux;
>>> +	ret = lux;
>>> +
>>> +out_unlock:
>>> +	mutex_unlock(&chip->als_mutex);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/*
>>> + * Proximity poll function - if valid data is available, read and form the ch0
>>> + * and prox data values, check for limits on the ch0 value, and check the prox
>>> + * data against the current thresholds, to set the event status accordingly.
>>> + */
>>> +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
>>> +{
>>> +#define CONSECUTIVE_RETRIES 50
>>> +
>>> +	int i;
>>> +	int ret;
>>> +	u8 status;
>>> +	u8 chdata[2];
>>> +	int err_cnt;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	if (mutex_trylock(&chip->prox_mutex) == 0) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: Can't get prox mutex\n", __func__);
>>> +		return -EBUSY;
>>> +	}
>>> +
>>> +	err_cnt = 0;
>>> +
>>> +try_again:
>> I'd like a comment on why this looping is necessary....
>>> +
>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status);
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +		"%s: i2c err=%d\n", __func__, ret);
>>> +		goto prox_poll_err;
>>> +	}
>>> +
>>> +	if (chip->id<  tsl2572) {
>>> +		if (!(status&  TSL2X7X_STA_ADC_VALID)) {
>>> +			err_cnt++;
>>> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
>>> +				dev_err(&chip->client->dev,
>>> +				"%s: Consec. retries exceeded\n", __func__);
>>> +				goto prox_poll_err;
>>> +			}
>>> +		goto try_again;
>>> +		}
>>> +	} else {
>>> +		if (!(status&  TSL2X7X_STA_PRX_VALID)) {
>>> +			err_cnt++;
>>> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
>>> +				dev_err(&chip->client->dev,
>>> +				"%s: Consec. retries exceeded\n", __func__);
>>> +				goto prox_poll_err;
>>> +			}
>>> +		goto try_again;
>>> +		}
>>> +	}
>>> +
>>> +	for (i = 0; i<  2; i++) {
>>> +		ret = tsl2x7x_i2c_read(chip->client,
>>> +			(TSL2X7X_CMD_REG |
>>> +					(TSL2X7X_PRX_LO + i)),&chdata[i]);
>>> +		if (ret<  0)
>>> +			goto prox_poll_err;
>>> +	}
>>> +
>>> +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
>>> +	if (chip->prox_cur_info.prox_data == 0)
>>> +		goto try_again;
>>> +
>>> +	if (chip->prox_cur_info.prox_data>=
>>> +			chip->tsl2x7x_settings.prox_thres_high)
>>> +		chip->prox_cur_info.prox_event = 1;
>>> +	else
>>> +		chip->prox_cur_info.prox_event = 0;
>> So this is manually polling the event signal. I'd argue that this is a
>> job for userspace
>> if the device isn't doing it hardware (or the interrupt signal is
>> connected).
>>> +
>>> +	mutex_unlock(&chip->prox_mutex);
>>> +	return chip->prox_cur_info.prox_event;
>>> +
>>> +prox_poll_err:
>>> +	mutex_unlock(&chip->prox_mutex);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/*
>>> + * Provides initial operational parameter defaults.
>>> + * These defaults may be changed through the device's sysfs files.
>>> + */
>>> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
>>> +{
>>> +	/* If Operational settings defined elsewhere.. */
>>> +	if (chip->pdata&&  chip->pdata->platform_default_settings != 0)
>>> +		memcpy(&(chip->tsl2x7x_settings),
>>> +			chip->pdata->platform_default_settings,
>>> +			sizeof(tsl2x7x_default_settings));
>>> +	else
>>> +		memcpy(&(chip->tsl2x7x_settings),
>>> +			&tsl2x7x_default_settings,
>>> +			sizeof(tsl2x7x_default_settings));
>>> +
>>> +	/* Load up the proper lux table. */
>>> +	if (chip->pdata&&  chip->pdata->platform_lux_table[0].ratio != 0)
>>> +		memcpy(chip->tsl2x7x_device_lux,
>>> +			chip->pdata->platform_lux_table,
>>> +			sizeof(chip->pdata->platform_lux_table));
>>> +	else
>>> +		memcpy(chip->tsl2x7x_device_lux,
>>> +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
>>> +				MAX_DEFAULT_TABLE_BYTES);
>>> +
>>> +}
>>> +
>>> +/*
>>> + * Obtain single reading and calculate the als_gain_trim
>>> + * (later used to derive actual lux).
>>> + * Return updated gain_trim value.
>>> + */
>>> +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	u8 reg_val;
>>> +	int gain_trim_val;
>>> +	int ret;
>>> +	int lux_val;
>>> +
>>> +	ret = i2c_smbus_write_byte(chip->client,
>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +		"%s: failed to write CNTRL register, ret=%d\n",
>>> +		__func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	reg_val = i2c_smbus_read_byte(chip->client);
>>> +	if ((reg_val&  (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
>>> +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed: ADC not enabled\n", __func__);
>>> +		return -1;
>>> +	}
>>> +
>>> +	ret = i2c_smbus_write_byte(chip->client,
>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed to write ctrl reg: ret=%d\n",
>>> +			__func__, ret);
>>> +		return ret;
>>> +	}
>>> +
>>> +	reg_val = i2c_smbus_read_byte(chip->client);
>>> +	if ((reg_val&  TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed: STATUS - ADC not valid.\n", __func__);
>>> +		return -ENODATA;
>>> +	}
>>> +
>>> +	lux_val = tsl2x7x_get_lux(indio_dev);
>>> +	if (lux_val<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +		"%s: failed to get lux\n", __func__);
>>> +		return lux_val;
>>> +	}
>>> +
>>> +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
>>> +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
>>> +	if ((gain_trim_val<  250) || (gain_trim_val>  4000))
>>> +		return -ERANGE;
>>> +
>>> +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
>>> +	dev_info(&chip->client->dev,
>>> +		"%s als_calibrate completed\n", chip->client->name);
>>> +
>>> +	return (int) gain_trim_val;
>>> +}
>>> +
>>> +/*
>>> + * Turn the device on.
>>> + * Configuration must be set before calling this function.
>>> + */
>>> +static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
>>> +{
>>> +	int i;
>>> +	int ret = 0;
>>> +	u8 *dev_reg;
>>> +	u8 utmp;
>>> +	int als_count;
>>> +	int als_time;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	u8 reg_val = 0;
>>> +
>>> +	if (chip->pdata&&  chip->pdata->power_on)
>>> +		chip->pdata->power_on(indio_dev);
>>> +
>>> +	/* Non calculated parameters */
>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
>>> +			chip->tsl2x7x_settings.prx_time;
>>> +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
>>> +			chip->tsl2x7x_settings.wait_time;
>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
>>> +			chip->tsl2x7x_settings.prox_config;
>>> +
>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
>>> +		(chip->tsl2x7x_settings.als_thresh_low)&  0xFF;
>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
>>> +		(chip->tsl2x7x_settings.als_thresh_low>>  8)&  0xFF;
>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
>>> +		(chip->tsl2x7x_settings.als_thresh_high)&  0xFF;
>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
>>> +		(chip->tsl2x7x_settings.als_thresh_high>>  8)&  0xFF;
>>> +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
>>> +		chip->tsl2x7x_settings.als_persistence;
>>> +
>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
>>> +			chip->tsl2x7x_settings.prox_pulse_count;
>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
>>> +	chip->tsl2x7x_settings.prox_thres_low;
>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
>>> +			chip->tsl2x7x_settings.prox_thres_high;
>>> +
>>> +	/* and make sure we're not already on */
>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
>>> +		/* if forcing a register update - turn off, then on */
>>> +		dev_info(&chip->client->dev, "device is already enabled\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	/* determine als integration regster */
>>> +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
>>> +	if (als_count == 0)
>>> +		als_count = 1; /* ensure at least one cycle */
>>> +
>>> +	/* convert back to time (encompasses overrides) */
>>> +	als_time = (als_count * 27 + 5) / 10;
>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
>>> +
>>> +	/* Set the gain based on tsl2x7x_settings struct */
>>> +	chip->tsl2x7x_config[TSL2X7X_GAIN] =
>>> +		(chip->tsl2x7x_settings.als_gain |
>>> +			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
>>> +			| ((chip->tsl2x7x_settings.prox_gain)<<  2));
>>> +
>>> +	/* set chip struct re scaling and saturation */
>>> +	chip->als_saturation = als_count * 922; /* 90% of full scale */
>>> +	chip->als_time_scale = (als_time + 25) / 50;
>>> +
>>> +	/* TSL2X7X Specific power-on / adc enable sequence
>>> +	 * Power on the device 1st. */
>>> +	utmp = TSL2X7X_CNTL_PWR_ON;
>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed on CNTRL reg.\n", __func__);
>>> +		return -1;
>>> +	}
>>> +
>>> +	/* Use the following shadow copy for our delay before enabling ADC.
>>> +	 * Write all the registers. */
>>> +	for (i = 0, dev_reg = chip->tsl2x7x_config;
>>> +			i<  TSL2X7X_MAX_CONFIG_REG; i++) {
>>> +		ret = i2c_smbus_write_byte_data(chip->client,
>>> +				TSL2X7X_CMD_REG + i, *dev_reg++);
>>> +		if (ret<  0) {
>>> +			dev_err(&chip->client->dev,
>>> +			"%s: failed on write to reg %d.\n", __func__, i);
>>> +			return ret;
>>> +		}
>>> +	}
>>> +
>>> +	udelay(3000);	/* Power-on settling time */
>>> +
>>> +	/* NOW enable the ADC
>>> +	 * initialize the desired mode of operation */
>>> +	utmp = TSL2X7X_CNTL_PWR_ON |
>>> +			TSL2X7X_CNTL_ADC_ENBL |
>>> +			TSL2X7X_CNTL_PROX_DET_ENBL;
>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>> +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
>>> +	if (ret<  0) {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: failed on 2nd CTRL reg.\n", __func__);
>>> +		return ret;
>>> +		}
>>> +
>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
>>> +
>>> +	if (chip->tsl2x7x_settings.interrupts_en != 0) {
>>> +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
>>> +
>>> +		reg_val = TSL2X7X_CNTL_PWR_ON |
>> TSL2X7X_CNTL_ADC_ENBL;
>>> +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
>>> +			(chip->tsl2x7x_settings.interrupts_en == 0x30))
>>> +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
>>> +
>>> +		reg_val |= chip->tsl2x7x_settings.interrupts_en;
>>> +		ret = i2c_smbus_write_byte_data(chip->client,
>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
>>> +		if (ret<  0)
>>> +			dev_err(&chip->client->dev,
>>> +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
>>> +				__func__);
>>> +
>>> +		/* Clear out any initial interrupts  */
>>> +		ret = i2c_smbus_write_byte(chip->client,
>>> +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
>>> +			TSL2X7X_CMD_PROXALS_INT_CLR);
>>> +		if (ret<  0) {
>>> +			dev_err(&chip->client->dev,
>>> +				"%s: failed in tsl2x7x_chip_on\n",
>>> +				__func__);
>>> +		return ret;
>>> +		}
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
>>> +{
>>> +	int ret;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	/* turn device off */
>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
>>> +
>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
>>> +
>>> +	if (chip->pdata&&  chip->pdata->power_off)
>>> +		chip->pdata->power_off(chip->client);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +/*
>>> + * Proximity calibration helper function
>>> + * runs through a collection of data samples,
>>> + * sets the min, max, mean, and std dev.
>>> + */
>>> +static
>>> +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
>>> +{
>>> +	int i;
>>> +	int min, max, sum, mean;
>>> +	unsigned long stddev;
>>> +	int tmp;
>>> +
>>> +	if (length == 0)
>>> +		length = 1;
>>> +
>>> +	sum = 0;
>>> +	min = INT_MAX;
>>> +	max = INT_MIN;
>>> +	for (i = 0; i<  length; i++) {
>>> +		sum += data[i];
>> avoid using min as a variable name (as it's also an appropriate function)
>> _min = MIN(data[i], _min); saves you a line of code.
>>> +		if (data[i]<  min)
>>> +			min = data[i];
>>> +		if (data[i]>  max)
>>> +			max = data[i];
>>> +	}
>>> +	mean = sum/length;
>>> +	statP->min = min;
>>> +	statP->max = max;
>>> +	statP->mean = mean;
>>> +
>>> +	sum = 0;
>>> +	for (i = 0; i<  length; i++) {
>>> +		tmp = data[i]-mean;
>>> +		sum += tmp * tmp;
>>> +	}
>>> +	stddev = int_sqrt((long)sum)/length;
>>> +	statP->stddev = stddev;
>>> +}
>>> +
>>> +/**
>>> + * Proximity calibration - collects a number of samples,
>>> + * calculates a standard deviation based on the samples, and
>>> + * sets the threshold accordingly.
>>> + */
>>> +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
>>> +{
>>> +	u16 prox_history[MAX_SAMPLES_CAL+1];
>> spaces around that +
>>> +	int i;
>>> +	struct prox_stat prox_stat_data[2];
>>> +	struct prox_stat *calP;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	u8 tmp_irq_settings;
>>> +	u8 current_state = chip->tsl2x7x_chip_status;
>>> +
>>> +	if (chip->tsl2x7x_settings.prox_max_samples_cal>  MAX_SAMPLES_CAL)
>> {
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: max prox samples cal is too big: %d\n",
>>> +			__func__, chip-
>>> tsl2x7x_settings.prox_max_samples_cal);
>>> +		chip->tsl2x7x_settings.prox_max_samples_cal =
>> MAX_SAMPLES_CAL;
>>> +	}
>>> +
>>> +	/* have to stop to change settings */
>>> +	tsl2x7x_chip_off(indio_dev);
>>> +
>>> +	/* Enable proximity detection save just in case prox not wanted yet*/
>>> +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
>>> +	chip->tsl2x7x_settings.interrupts_en |=
>> TSL2X7X_CNTL_PROX_INT_ENBL;
>>> +
>>> +	/*turn on device if not already on*/
>>> +	tsl2x7x_chip_on(indio_dev);
>>> +
>>> +	/*gather the samples*/
>>> +	for (i = 0; i<  chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
>>> +		mdelay(15);
>>> +		tsl2x7x_prox_poll(indio_dev);
>>> +		prox_history[i] = chip->prox_cur_info.prox_data;
>>> +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
>>> +			i, chip->prox_cur_info.prox_data);
>>> +	}
>>> +
>>> +	tsl2x7x_chip_off(indio_dev);
>>> +	calP =&prox_stat_data[PROX_STAT_CAL];
>>> +	tsl2x7x_prox_calculate(prox_history,
>>> +		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
>>> +	chip->tsl2x7x_settings.prox_thres_high = (calP->max<<  1) - calP-
>>> mean;
>>> +
>>> +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
>>> +		calP->min, calP->mean, calP->max);
>>> +	dev_info(&chip->client->dev,
>>> +		"%s proximity threshold set to %d\n",
>>> +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
>>> +
>>> +	/* back to the way they were */
>>> +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
>>> +	if (current_state == TSL2X7X_CHIP_WORKING)
>>> +		tsl2x7x_chip_on(indio_dev);
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_power_state_store(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	bool value;
>>> +
>>> +	if (strtobool(buf,&value))
>>> +		return -EINVAL;
>>> +
>>> +	if (!value)
>>> +		tsl2x7x_chip_off(indio_dev);
>>> +	else
>>> +		tsl2x7x_chip_on(indio_dev);
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	if (chip->id>  tsl2771)
>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
>>> +	else
>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_als_time_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>> +			chip->tsl2x7x_settings.als_time);
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_als_time_store(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	unsigned long value;
>>> +
>>> +	if (kstrtoul(buf, 0,&value))
>>> +		return -EINVAL;
>>> +
>>> +	if ((value<  50) || (value>  650))
>>> +		return -EINVAL;
>>> +
>>> +	if (value % 50)
>>> +		return -EINVAL;
>>> +
>>> +	 chip->tsl2x7x_settings.als_time = value;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static IIO_CONST_ATTR(illuminance0_integration_time_available,
>>> +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
>>> +
>>> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>> +			chip->tsl2x7x_settings.als_cal_target);
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	unsigned long value;
>>> +
>>> +	if (kstrtoul(buf, 0,&value))
>>> +		return -EINVAL;
>>> +
>>> +	if (value)
>>> +		chip->tsl2x7x_settings.als_cal_target = value;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +/* sampling_frequency AKA persistence in data sheet */
>>> +static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>> +			chip->tsl2x7x_settings.als_persistence);
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	unsigned long value;
>>> +
>>> +	if (kstrtoul(buf, 0,&value))
>>> +		return -EINVAL;
>>> +
>>> +	chip->tsl2x7x_settings.als_persistence = value;
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static IIO_CONST_ATTR(sampling_frequency_available,
>>> +		"0x00 - 0xFF (0 - 255)");
>>> +
>>> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	bool value;
>>> +
>>> +	if (strtobool(buf,&value))
>>> +		return -EINVAL;
>>> +
>>> +	if (value)
>>> +		tsl2x7x_als_calibrate(indio_dev);
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
>>> +	struct device_attribute *attr, char *buf)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int i;
>>> +	int offset = 0;
>>> +
>>> +	i = 0;
>> Set i at the declaration above.
>>> +	while (i<  (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
>>> +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
>>> +			chip->tsl2x7x_device_lux[i].ratio,
>>> +			chip->tsl2x7x_device_lux[i].ch0,
>>> +			chip->tsl2x7x_device_lux[i].ch1);
>>> +		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
>>> +			/* We just printed the first "0" entry.
>>> +			 * Now get rid of the extra "," and break. */
>>> +			offset--;
>>> +			break;
>>> +		}
>>> +		i++;
>>> +	}
>>> +
>>> +	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
>>> +	return offset;
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_luxtable_store(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
>>> +	int n;
>>> +
>>> +	get_options(buf, ARRAY_SIZE(value), value);
>>> +
>>> +	/* We now have an array of ints starting at value[1], and
>>> +	 * enumerated by value[0].
>>> +	 * We expect each group of three ints is one table entry,
>>> +	 * and the last table entry is all 0.
>>> +	 */
>>> +	n = value[0];
>>> +	if ((n % 3) || n<  6 ||
>>> +			n>  ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
>>> +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
>>> +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
>>> +		tsl2x7x_chip_off(indio_dev);
>>> +
>>> +	/* Zero out the table */
>>> +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
>>> +	memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4));
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	bool value;
>>> +
>>> +	if (strtobool(buf,&value))
>>> +		return -EINVAL;
>>> +
>>> +	if (value)
>>> +		tsl2x7x_prox_cal(indio_dev);
>>> +
>>> +	return len;
>>> +}
>>> +
>>> +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
>>> +					 u64 event_code)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int ret;
>>> +
>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x10);
>>> +	else
>>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x20);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
>>> +					  u64 event_code,
>>> +					  int val)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>> {
>>> +		if (val)
>>> +			chip->tsl2x7x_settings.interrupts_en |= 0x10;
>>> +		else
>>> +			chip->tsl2x7x_settings.interrupts_en&= 0x20;
>>> +	} else {
>>> +		if (val)
>>> +			chip->tsl2x7x_settings.interrupts_en |= 0x20;
>>> +		else
>>> +			chip->tsl2x7x_settings.interrupts_en&= 0x10;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
>>> +				  u64 event_code,
>>> +				  int val)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>> {
>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>> +		case IIO_EV_DIR_RISING:
>>> +			chip->tsl2x7x_settings.als_thresh_high = val;
>>> +			break;
>>> +		case IIO_EV_DIR_FALLING:
>>> +			chip->tsl2x7x_settings.als_thresh_low = val;
>>> +			break;
>>> +		default:
>>> +			return -EINVAL;
>>> +		}
>>> +	} else {
>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>> +		case IIO_EV_DIR_RISING:
>>> +			chip->tsl2x7x_settings.prox_thres_high = val;
>>> +			break;
>>> +		case IIO_EV_DIR_FALLING:
>>> +			chip->tsl2x7x_settings.prox_thres_low = val;
>>> +			break;
>>> +		default:
>>> +			return -EINVAL;
>>> +		}
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
>>> +			       u64 event_code,
>>> +			       int *val)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>> {
>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>> +		case IIO_EV_DIR_RISING:
>>> +			*val = chip->tsl2x7x_settings.als_thresh_high;
>>> +			break;
>>> +		case IIO_EV_DIR_FALLING:
>>> +			*val = chip->tsl2x7x_settings.als_thresh_low;
>>> +			break;
>>> +		default:
>>> +			return -EINVAL;
>>> +		}
>>> +	} else {
>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>> +		case IIO_EV_DIR_RISING:
>>> +			*val = chip->tsl2x7x_settings.prox_thres_high;
>>> +			break;
>>> +		case IIO_EV_DIR_FALLING:
>>> +			*val = chip->tsl2x7x_settings.prox_thres_low;
>>> +			break;
>>> +		default:
>>> +			return -EINVAL;
>>> +		}
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
>>> +			    struct iio_chan_spec const *chan,
>>> +			    int *val,
>>> +			    int *val2,
>>> +			    long mask)
>>> +{
>>> +	int ret = -EINVAL;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	switch (mask) {
>>> +	case 0:
>>> +		switch (chan->type) {
>>> +		case IIO_LIGHT:
>>> +			tsl2x7x_get_lux(indio_dev);
>>> +			if (chan->processed_val)
>>> +				*val = chip->als_cur_info.lux;
>>> +			else
>>> +				*val = (((chip->als_cur_info.als_ch0<<  16) |
>>> +					chip->als_cur_info.als_ch1));
>> Why have a processed read back and a raw readback?
>>> +			ret = IIO_VAL_INT;
>>> +			break;
>>> +		case IIO_PROXIMITY:
>>> +			tsl2x7x_prox_poll(indio_dev);
>>> +			if (chan->processed_val)
>> Hmm.. this is uggly.  We effectively have polling of an event status.
>> Normally
>> I'd expect the event to only occur as a an IIO event rather than being
>> readable like
>> this...
>>> +				*val = chip->prox_cur_info.prox_event;
>>> +			else
>>> +				*val = chip->prox_cur_info.prox_data;
>>> +			ret = IIO_VAL_INT;
>>> +			break;
>>> +		default:
>>> +			return -EINVAL;
>>> +			break;
>>> +		}
>>> +		break;
>>> +	case IIO_CHAN_INFO_CALIBSCALE:
>>> +		if (chan->type == IIO_LIGHT)
>>> +			*val =
>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
>>> +		else
>>> +			*val =
>>> +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
>>> +		ret = IIO_VAL_INT;
>>> +		break;
>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>> +		*val = chip->tsl2x7x_settings.als_gain_trim;
>>> +		ret = IIO_VAL_INT;
>>> +		break;
>>> +
>>> +	default:
>>> +		ret = -EINVAL;
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
>>> +			       struct iio_chan_spec const *chan,
>>> +			       int val,
>>> +			       int val2,
>>> +			       long mask)
>>> +{
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +
>>> +	switch (mask) {
>>> +	case IIO_CHAN_INFO_CALIBSCALE:
>>> +		if (chan->type == IIO_LIGHT) {
>>> +			switch (val) {
>>> +			case 1:
>>> +				chip->tsl2x7x_settings.als_gain = 0;
>>> +				break;
>>> +			case 8:
>>> +				chip->tsl2x7x_settings.als_gain = 1;
>>> +				break;
>>> +			case 16:
>>> +				chip->tsl2x7x_settings.als_gain = 2;
>>> +				break;
>>> +			case 120:
>>> +				if (chip->id>  tsl2771)
>>> +					return -EINVAL;
>>> +				chip->tsl2x7x_settings.als_gain = 3;
>>> +				break;
>>> +			case 128:
>>> +				if (chip->id<  tsl2572)
>>> +					return -EINVAL;
>>> +				chip->tsl2x7x_settings.als_gain = 3;
>>> +				break;
>>> +			default:
>>> +				return -EINVAL;
>>> +			}
>>> +		} else {
>>> +			switch (val) {
>>> +			case 1:
>>> +				chip->tsl2x7x_settings.prox_gain = 0;
>>> +				break;
>>> +			case 2:
>>> +				chip->tsl2x7x_settings.prox_gain = 1;
>>> +				break;
>>> +			case 4:
>>> +				chip->tsl2x7x_settings.prox_gain = 2;
>>> +				break;
>>> +			case 8:
>>> +				chip->tsl2x7x_settings.prox_gain = 3;
>>> +				break;
>>> +			default:
>>> +				return -EINVAL;
>>> +			}
>>> +		}
>>> +		break;
>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>> +		chip->tsl2x7x_settings.als_gain_trim = val;
>>> +		break;
>>> +
>>> +	default:
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
>>> +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
>>> +
>>> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
>>> +		tsl2x7x_prox_gain_available_show, NULL);
>>> +
>>> +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
>>> +		tsl2x7x_gain_available_show, NULL);
>>> +
>>> +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
>>> +		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
>>> +
>>> +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
>>> +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
>>> +
>>> +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
>>> +		tsl2x7x_do_calibrate);
>>> +
>>> +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
>>> +		tsl2x7x_do_prox_calibrate);
>>> +
>>> +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
>>> +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
>>> +
>>> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
>>> +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
>>> +
>>> +/* Use the default register values to identify the Taos device */
>>> +static int tsl2x7x_device_id(unsigned char *id, int target)
>>> +{
>>> +	switch (target) {
>>> +	case tsl2571:
>>> +	case tsl2671:
>>> +	case tsl2771:
>>> +		return ((*id&  0xf0) == TRITON_ID);
>>> +	break;
>>> +	case tmd2671:
>>> +	case tmd2771:
>>> +		return ((*id&  0xf0) == HALIBUT_ID);
>>> +	break;
>>> +	case tsl2572:
>>> +	case tsl2672:
>>> +	case tmd2672:
>>> +	case tsl2772:
>>> +	case tmd2772:
>>> +		return ((*id&  0xf0) == SWORDFISH_ID);
>>> +	break;
>>> +	}
>>> +
>>> +	return -EINVAL;
>>> +}
>>> +
>>> +/*
>>> + * Interrupt Event Handler */
>>> +static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
>>> +{
>>> +	struct iio_dev *indio_dev = private;
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	s64 timestamp = iio_get_time_ns();
>>> +	int ret;
>>> +	int value;
>>> +
>>> +	value = i2c_smbus_read_byte_data(chip->client,
>>> +		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
>>> +
>>> +	/* What type of interrupt do we need to process */
>>> +	if (value&  TSL2X7X_STA_PRX_INTR) {
>>> +		tsl2x7x_prox_poll(indio_dev);
>>> +		iio_push_event(indio_dev,
>>> +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
>>> +						    0,
>>> +						    IIO_EV_TYPE_THRESH,
>>> +						    IIO_EV_DIR_EITHER),
>>> +						    timestamp);
>>> +	}
>>> +
>>> +	if (value&  TSL2X7X_STA_ALS_INTR) {
>>> +		tsl2x7x_get_lux(indio_dev);
>> why the get value?  No real guarantee you'll get the one that caused the
>> event as far as I can see.
>>> +		iio_push_event(indio_dev,
>>> +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
>>> +					    0,
>>> +					    IIO_EV_TYPE_THRESH,
>>> +					    IIO_EV_DIR_EITHER),
>>> +					    timestamp);
>>> +	}
>>> +	/* Clear interrupt now that we have the status */
>>> +	ret = i2c_smbus_write_byte(chip->client,
>>> +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
>>> +		TSL2X7X_CMD_PROXALS_INT_CLR);
>>> +	if (ret<  0)
>>> +		dev_err(&chip->client->dev,
>>> +			"%s: Failed to clear irq from event handler. err = %d\n",
>>> +			__func__, ret);
>>> +
>>> +	return IRQ_HANDLED;
>>> +}
>>> +
>>> +static struct attribute *tsl2x7x_ALS_device_attrs[] = {
>>> +	&dev_attr_power_state.attr,
>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>> +	&dev_attr_illuminance0_integration_time.attr,
>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>> +	&dev_attr_illuminance0_target_input.attr,
>>> +	&dev_attr_illuminance0_calibrate.attr,
>>> +	&dev_attr_illuminance0_lux_table.attr,
>>> +	&dev_attr_sampling_frequency.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static struct attribute *tsl2x7x_PRX_device_attrs[] = {
>>> +	&dev_attr_power_state.attr,
>>> +	&dev_attr_sampling_frequency.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	&dev_attr_proximity_calibrate.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
>>> +	&dev_attr_power_state.attr,
>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>> +	&dev_attr_illuminance0_integration_time.attr,
>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>> +	&dev_attr_illuminance0_target_input.attr,
>>> +	&dev_attr_illuminance0_calibrate.attr,
>>> +	&dev_attr_illuminance0_lux_table.attr,
>>> +	&dev_attr_sampling_frequency.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	&dev_attr_proximity_calibrate.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
>>> +	&dev_attr_power_state.attr,
>>> +	&dev_attr_sampling_frequency.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	&dev_attr_proximity_calibrate.attr,
>>> +	&dev_attr_proximity_calibscale_available.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
>>> +	&dev_attr_power_state.attr,
>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>> +	&dev_attr_illuminance0_integration_time.attr,
>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>> +	&dev_attr_illuminance0_target_input.attr,
>>> +	&dev_attr_illuminance0_calibrate.attr,
>>> +	&dev_attr_illuminance0_lux_table.attr,
>>> +	&dev_attr_sampling_frequency.attr,
>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>> +	&dev_attr_proximity_calibrate.attr,
>>> +	&dev_attr_proximity_calibscale_available.attr,
>>> +	NULL
>>> +};
>>> +
>>> +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
>>> +	[ALS] = {
>>> +		.attrs = tsl2x7x_ALS_device_attrs,
>>> +	},
>>> +	[PRX] = {
>>> +		.attrs = tsl2x7x_PRX_device_attrs,
>>> +	},
>>> +	[ALSPRX] = {
>>> +		.attrs = tsl2x7x_ALSPRX_device_attrs,
>>> +	},
>>> +	[PRX2] = {
>>> +		.attrs = tsl2x7x_PRX2_device_attrs,
>>> +	},
>>> +	[ALSPRX2] = {
>>> +		.attrs = tsl2x7x_ALSPRX2_device_attrs,
>>> +	},
>>> +};
>>> +
>>> +static const struct iio_info tsl2X7X_device_info[] = {
>>> +	[ALS] = {
>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALS],
>>> +		.driver_module = THIS_MODULE,
>>> +		.read_raw =&tsl2x7x_read_raw,
>>> +		.write_raw =&tsl2x7x_write_raw,
>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>> +	},
>>> +	[PRX] = {
>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX],
>>> +		.driver_module = THIS_MODULE,
>>> +		.read_raw =&tsl2x7x_read_raw,
>>> +		.write_raw =&tsl2x7x_write_raw,
>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>> +	},
>>> +	[ALSPRX] = {
>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX],
>>> +		.driver_module = THIS_MODULE,
>>> +		.read_raw =&tsl2x7x_read_raw,
>>> +		.write_raw =&tsl2x7x_write_raw,
>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>> +	},
>>> +	[PRX2] = {
>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX2],
>>> +		.driver_module = THIS_MODULE,
>>> +		.read_raw =&tsl2x7x_read_raw,
>>> +		.write_raw =&tsl2x7x_write_raw,
>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>> +	},
>>> +	[ALSPRX2] = {
>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2],
>>> +		.driver_module = THIS_MODULE,
>>> +		.read_raw =&tsl2x7x_read_raw,
>>> +		.write_raw =&tsl2x7x_write_raw,
>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>> +	},
>>> +};
>>> +
>>> +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
>>> +	[ALS] = {
>>> +		.channel = {
>>> +			[0] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[1] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.info_mask =
>>> +
>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>> +
>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +		},
>>> +	.num_channels = 2,
>>> +	.info =&tsl2X7X_device_info[ALS],
>>> +	},
>>> +	[PRX] = {
>>> +		.channel = {
>>> +			[0] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[1] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +		},
>>> +	.num_channels = 2,
>>> +	.info =&tsl2X7X_device_info[PRX],
>>> +	},
>>> +	[ALSPRX] = {
>>> +		.channel = {
>>> +			[0] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[1] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.info_mask =
>>> +
>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>> +
>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +			[2] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[3] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 0,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +		},
>>> +	.num_channels = 4,
>>> +	.info =&tsl2X7X_device_info[ALSPRX],
>>> +	},
>>> +	[PRX2] = {
>>> +		.channel = {
>>> +			[0] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[1] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.info_mask =
>>> +
>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +		},
>>> +	.num_channels = 2,
>>> +	.info =&tsl2X7X_device_info[PRX2],
>>> +	},
>>> +	[ALSPRX2] = {
>>> +		.channel = {
>>> +				[0] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[1] = {
>>> +				.type = IIO_LIGHT,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.info_mask =
>>> +
>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>> +
>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +			[2] = {
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.processed_val = 1,
>>> +			},
>>> +			[3] = {
>> I'm more than a little confused here.  There are only 2 actually
>> channels?  If so I don't
>> see why we have 4 channels here...
>>> +				.type = IIO_PROXIMITY,
>>> +				.indexed = 1,
>>> +				.channel = 0,
>>> +				.info_mask =
>>> +
>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>> +			},
>>> +		},
>>> +	.num_channels = 4,
>>> +	.info =&tsl2X7X_device_info[ALSPRX2],
>>> +	},
>>> +};
>>> +
>>> +/*
>>> + * Client probe function.
>>> + */
>>> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
>>> +	const struct i2c_device_id *id)
>>> +{
>>> +	int ret;
>>> +	unsigned char device_id;
>>> +	struct iio_dev *indio_dev;
>>> +	struct tsl2X7X_chip *chip;
>>> +
>>> +	indio_dev = iio_allocate_device(sizeof(*chip));
>>> +	if (!indio_dev)
>>> +		return -ENOMEM;
>>> +
>>> +	chip = iio_priv(indio_dev);
>>> +	chip->client = clientp;
>>> +	i2c_set_clientdata(clientp, indio_dev);
>>> +
>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>> +		TSL2X7X_CHIPID,&device_id);
>>> +	if (ret<  0)
>>> +		goto fail1;
>>> +
>>> +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
>>> +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
>>> +		dev_info(&chip->client->dev,
>>> +				"i2c device found does not match expected id
>> in %s\n",
>>> +				__func__);
>>> +		goto fail1;
>>> +	}
>>> +
>>> +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG |
>> TSL2X7X_CNTRL));
>>> +	if (ret<  0) {
>>> +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err =
>> %d\n",
>>> +				__func__, ret);
>>> +		goto fail1;
>>> +	}
>>> +
>>> +	/* ALS and PROX functions can be invoked via user space poll
>>> +	 * or H/W interrupt. If busy return last sample. */
>>> +	mutex_init(&chip->als_mutex);
>>> +	mutex_init(&chip->prox_mutex);
>>> +
>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
>>> +	chip->pdata = clientp->dev.platform_data;
>>> +	chip->id = id->driver_data;
>>> +	chip->chip_info =
>>> +		&tsl2x7x_chip_info_tbl[device_channel_config[id-
>>> driver_data]];
>>> +
>>> +	indio_dev->info = chip->chip_info->info;
>>> +	indio_dev->dev.parent =&clientp->dev;
>>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>>> +	indio_dev->name = chip->client->name;
>>> +	indio_dev->channels = chip->chip_info->channel;
>>> +	indio_dev->num_channels = chip->chip_info->num_channels;
>>> +
>>> +	if (clientp->irq) {
>>> +		ret = request_threaded_irq(clientp->irq,
>>> +					   NULL,
>>> +					&tsl2x7x_event_handler,
>>> +					   IRQF_TRIGGER_RISING |
>> IRQF_ONESHOT,
>>> +					   "TSL2X7X_event",
>>> +					   indio_dev);
>>> +		if (ret) {
>>> +			dev_err(&clientp->dev,
>>> +				"%s: irq request failed", __func__);
>>> +			goto fail2;
>>> +		}
>>> +	}
>>> +
>>> +	/* Load up the defaults */
>>> +	tsl2x7x_defaults(chip);
>>> +	/* Make sure the chip is on */
>>> +	tsl2x7x_chip_on(indio_dev);
>>> +
>>> +	ret = iio_device_register(indio_dev);
>>> +	if (ret) {
>>> +		dev_err(&clientp->dev,
>>> +			"%s: iio registration failed\n", __func__);
>>> +		goto fail3;
>>> +	}
>>> +
>>> +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
>>> +
>>> +	return 0;
>>> +
>>> +fail3:
>>> +	if (clientp->irq)
>>> +		free_irq(clientp->irq, indio_dev);
>>> +fail2:
>>> +	iio_free_device(indio_dev);
>>> +fail1:
>>> +	kfree(chip);
>> double free of chip.  It's allocated and managed by the
>> iio_allocate_device and iio_free_device calls.
>> For that matter, most of the goto fail2's need the iio_free_device call..
>>> +	return ret;
>>> +}
>>> +
>>> +static int tsl2x7x_suspend(struct device *dev)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int ret = 0;
>>> +
>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
>>> +		ret = tsl2x7x_chip_off(indio_dev);
>>> +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
>>> +	}
>>> +
>>> +	if (chip->pdata&&  chip->pdata->platform_power) {
>>> +		pm_message_t pmm = {PM_EVENT_SUSPEND};
>>> +		chip->pdata->platform_power(dev, pmm);
>>> +	}
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int tsl2x7x_resume(struct device *dev)
>>> +{
>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>> +	int ret = 0;
>>> +
>>> +	if (chip->pdata&&  chip->pdata->platform_power) {
>>> +		pm_message_t pmm = {PM_EVENT_RESUME};
>>> +		chip->pdata->platform_power(dev, pmm);
>>> +	}
>>> +
>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
>>> +		ret = tsl2x7x_chip_on(indio_dev);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static int __devexit tsl2x7x_remove(struct i2c_client *client)
>>> +{
>>> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>>> +	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
>> These shouldn't both be true....
>> would expect an iio_unregister_device call here.
>>> +
>>> +	tsl2x7x_chip_off(indio_dev);
>>> +
>>> +	if (client->irq)
>>> +		free_irq(client->irq, chip->client->name);
>>> +
>>> +	iio_free_device(indio_dev);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static struct i2c_device_id tsl2x7x_idtable[] = {
>>> +	{ "tsl2571", tsl2571 },
>>> +	{ "tsl2671", tsl2671 },
>>> +	{ "tmd2671", tmd2671 },
>>> +	{ "tsl2771", tsl2771 },
>>> +	{ "tmd2771", tmd2771 },
>>> +	{ "tsl2572", tsl2572 },
>>> +	{ "tsl2672", tsl2672 },
>>> +	{ "tmd2672", tmd2672 },
>>> +	{ "tsl2772", tsl2772 },
>>> +	{ "tmd2772", tmd2772 },
>>> +	{}
>>> +};
>>> +
>>> +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
>>> +
>>> +static const struct dev_pm_ops tsl2x7x_pm_ops = {
>>> +	.suspend = tsl2x7x_suspend,
>>> +	.resume  = tsl2x7x_resume,
>>> +};
>>> +
>>> +/* Driver definition */
>>> +static struct i2c_driver tsl2x7x_driver = {
>>> +	.driver = {
>>> +		.name = "tsl2x7x",
>>> +		.pm =&tsl2x7x_pm_ops,
>>> +	},
>>> +	.id_table = tsl2x7x_idtable,
>>> +	.probe = tsl2x7x_probe,
>>> +	.remove = __devexit_p(tsl2x7x_remove),
>>> +};
>>> +
>>> +module_i2c_driver(tsl2x7x_driver);
>>> +
>>> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
>>> +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor
>> driver");
>>> +MODULE_LICENSE("GPL");
>>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.h
>> b/drivers/staging/iio/light/tsl2x7x_core.h
>>> new file mode 100644
>>> index 0000000..663e846
>>> --- /dev/null
>>> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
>> why not just tsl2x7x.h?
>>> @@ -0,0 +1,75 @@
>>> +/*
>>> + * Device driver for monitoring ambient light intensity (lux)
>>> + * and proximity (prox) within the TAOS TSL2X7X family of devices.
>>> + *
>>> + * Copyright (c) 2012, TAOS Corporation.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License as published by
>>> + * the Free Software Foundation; 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.,
>>> + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
>>> + */
>>> +
>>> +#ifndef __TSL2X7X_H
>>> +#define __TSL2X7X_H
>>> +#include<linux/pm.h>
>>> +
>>> +/* Max number of segments allowable in LUX table */
>>> +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
>>> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) *
>> TSL2X7X_MAX_LUX_TABLE_SIZE)
>>> +
>>> +struct iio_dev;
>>
>> Why the forward declaration of iio_chan_spec?
>>> +struct iio_chan_spec;
>>> +
>>> +struct tsl2x7x_lux {
>>> +	unsigned int ratio;
>>> +	unsigned int ch0;
>>> +	unsigned int ch1;
>>> +};
>>> +
>>> +/* Refer to tsl2x7x_default_settings for member desc. */
>>> +struct tsl2x7x_settings {
>>> +	int als_time;
>>> +	int als_gain;
>>> +	int als_gain_trim;
>>> +	int wait_time;
>>> +	int prx_time;
>>> +	int prox_gain;
>>> +	int prox_config;
>>> +	int als_cal_target;
>>> +	u8  interrupts_en;
>>> +	u8  als_persistence;
>>> +	int als_thresh_low;
>>> +	int als_thresh_high;
>>> +	int prox_thres_low;
>>> +	int prox_thres_high;
>>> +	int prox_pulse_count;
>>> +	int prox_max_samples_cal;
>>> +};
>>> +
>>> +/* struct tsl2x7x_platform_data -
>>> + * Platform unique glass and defaults
>>> + * Platform PM functions. */
>> Would prefer this to be in kernel doc.
>>> +struct tsl2X7X_platform_data {
>>> +	/* Suspend/resume platform cb */
>>> +	int (*platform_power)(struct device *dev, pm_message_t);
>>> +	/* The following callback gets called when the device is powered on */
>>> +	int (*power_on)      (struct iio_dev *indio_dev);
>>> +	/* The following callback gets called when the device is powered off */
>>> +	int (*power_off)     (struct i2c_client *dev);
>>> +	/* These are the device specific glass coefficents used to
>>> +	 * calculate Lux */
>>> +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
>>> +	struct tsl2x7x_settings *platform_default_settings;
>>> +};
>>> +
>>> +#endif /* __TSL2X7X_H */
>>> --
>>> 1.7.4.1
>>>
> 


^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH V4] TAOS tsl2x7x
  2012-03-28 17:51     ` Jonathan Cameron
@ 2012-03-28 23:29         ` Jon Brenner
  0 siblings, 0 replies; 8+ messages in thread
From: Jon Brenner @ 2012-03-28 23:29 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 83264 bytes --]

Hello Jonathan,
Unless the 2563 patch can wait, I'll leave it for you.
I need to get the 2x7x finished asap - because I have a yet another driver to do - immediately following it.
If it can wait till later (2563 driver patch), I'll be glad to do it.

That brings me back to some basic questions.
1. Channel 0 - for in_illuminance0_input - correct?
2. What channel for in_intensity (data channel 0 data)?
3  What channel for in_intensity (data channel 1 data)?
4 What channel for in_proximity?_raw ?

What do you think about the following channel 'table def' for the device that has ALS and Prox?
Please read statements following this snippet.

[ALSPRX] = {
	.channel = {
			{
			.type = IIO_LIGHT,
			.indexed = 1,
			.channel = 0,
			.processed_val = 1,
			}, {
			.type = IIO_INTENSITY,
			.indexed = 1,
			.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
			.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
					IIO_EV_DIR_RISING) |
					 IIO_EV_BIT(IIO_EV_TYPE_THRESH,
					IIO_EV_DIR_FALLING)),
			}, {
					.type = IIO_INTENSITY,
					.indexed = 1,
					.channel = 1,
			}, {
			.type = IIO_PROXIMITY,
			.indexed = 1,
			.event_mask = TSL2X7X_EVENT_MASK
			},
		},
	.num_channels = 4,
	.info = &tsl2X7X_device_info[ALSPRX],
	},

Notice I left out the modifiers?
This yields:
 	in_intensity0_raw	(gives us channel 0 raw data)
	in_intensity1_raw	(gives us channel 1 raw data)
	in_proximity0_raw           (gives us prox AD raw data)
as well as 
	in_intensity0_calibbias
	in_intensity0_calibscale
and of course
	in_illuminance0_input

With the modifiers left out, isn't the '_raw' and indication of a raw AD value (AD counts)?
We TAOS don't use the term "IR" in our channel designations - thus 0 and 1 more closely match the data sheets.
(Another reason why I don't want to use the modifiers of BOTH and IR)

(note also that .num_channels is really number of table elements - I can change that name)

What do you think?

Jon

> -----Original Message-----
> From: Jonathan Cameron [mailto:jic23@kernel.org]
> Sent: Wednesday, March 28, 2012 12:52 PM
> To: Jon Brenner
> Cc: Jonathan Cameron; linux-iio; Linux Kernel
> Subject: Re: [PATCH V4] TAOS tsl2x7x
> 
> On 03/27/2012 08:58 PM, Jon Brenner wrote:
> > Hello Jonathan,
> > Still a little confused (and stuck) here.
> > Using your code from the tsl2563:
> > 1. The case for IIO_LIGHT appears to return the computed LUX.
> > Yet you have no ".processed_val = 1: in your channel table - so where in
> in_illuminance0_input (aka lux)  coming from?
> Indeed. That's bug number 1...
> >
> > 2. The case for IIO_INTENSITY looks for 'chan->channel' to determine when to
> present 'chip->data0' or 'chip->data1' -
> > But it appears that 'chan->channel' will always be 0 as '.channel =' isn't defined
> in the table?
> > So how can you ever get chip->data01?
> >
> Gah, this driver clearly needs another look.  You are quite correct,
> that is a bug as well as it will always return the first channel.
> 
> Clearly a little bit of code rot has occured here.  Oops.
> Do you want to do the patch, or shall I (with a reported by
> of course!).
> 
> Jonathan
> > For example, tsl2563 code portion follows.
> >
> > <Snip>
> > static int tsl2563_read_raw(struct iio_dev *indio_dev,
> > 			    struct iio_chan_spec const *chan,
> > 			    int *val,
> > 			    int *val2,
> > 			    long m)
> > {
> > 	int ret = -EINVAL;
> > 	u32 calib0, calib1;
> > 	struct tsl2563_chip *chip = iio_priv(indio_dev);
> >
> > 	mutex_lock(&chip->lock);
> > 	switch (m) {
> > 	case 0:
> > 		switch (chan->type) {
> > 		case IIO_LIGHT:
> > 			ret = tsl2563_get_adc(chip);
> > 			if (ret)
> > 				goto error_ret;
> > 			calib0 = calib_adc(chip->data0, chip->calib0) *
> > 				chip->cover_comp_gain;
> > 			calib1 = calib_adc(chip->data1, chip->calib1) *
> > 				chip->cover_comp_gain;
> > 			*val = adc_to_lux(calib0, calib1);
> > 			ret = IIO_VAL_INT;
> > 			break;
> > 		case IIO_INTENSITY:
> > 			ret = tsl2563_get_adc(chip);
> > 			if (ret)
> > 				goto error_ret;
> > 			if (chan->channel == 0)
> > 				*val = chip->data0;
> > 			else
> > 				*val = chip->data1;
> > 			ret = IIO_VAL_INT;
> > 			break;
> > 		default:
> > 			break;
> > 		}
> > 		break;
> >
> > 	case IIO_CHAN_INFO_CALIBSCALE:
> > 		if (chan->channel == 0)
> > 			*val = calib_to_sysfs(chip->calib0);
> > 		else
> > 			*val = calib_to_sysfs(chip->calib1);
> > 		ret = IIO_VAL_INT;
> > 		break;
> > 	default:
> > 		ret = -EINVAL;
> > 		goto error_ret;
> > 	}
> >
> > error_ret:
> > 	mutex_unlock(&chip->lock);
> > 	return ret;
> > }
> >
> > static const struct iio_chan_spec tsl2563_channels[] = {
> > 	{
> > 		.type = IIO_LIGHT,
> > 		.indexed = 1,
> > 		.channel = 0,
> > 	}, {
> > 		.type = IIO_INTENSITY,
> > 		.modified = 1,
> > 		.channel2 = IIO_MOD_LIGHT_BOTH,
> > 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> > 		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> > 					  IIO_EV_DIR_RISING) |
> > 			       IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> > 					  IIO_EV_DIR_FALLING)),
> > 	}, {
> > 		.type = IIO_INTENSITY,
> > 		.modified = 1,
> > 		.channel2 = IIO_MOD_LIGHT_IR,
> > 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> > 	}
> > };
> >
> >
> > What am I not seeing here?
> >
> > Jon
> >
> >> -----Original Message-----
> >> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
> >> Sent: Friday, March 23, 2012 8:54 AM
> >> To: Jon Brenner
> >> Cc: linux-iio; Linux Kernel
> >> Subject: Re: [PATCH V4] TAOS tsl2x7x
> >>
> >> On 3/21/2012 4:16 PM, Jon Brenner wrote:
> >>> TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families
> >> (inc. all variants).
> >> Mostly looking good...
> >>
> >> Couple of issues remaining.
> >> * Why have processed and raw accesses to the same channels?  That is
> >> definitely not the intent.
> >> Either it's worth processing these raw adc channels in kernel, or not.
> >> If it isn't you certainly shouldn't
> >> be munging together the two adc's into a single value.
> >>
> >> So you have done this for two reasons...
> >> * For light sensors it was to allow the processed illuminance value and
> >> also the raw adc values.
> >> Previously we have done this by having IIO_LIGHT (and hence illuminance)
> >> for the processed one
> >> and marking the other two as IIO_INTENSITY with modifiers for the
> >> frequency range they cover...
> >> * For proximity one is the raw reading (fine), the other is a means of
> >> getting at the threshold event
> >> if interrupts are not supported.  It is done by a software comparison of
> >> the threshold and the raw
> >> reading.  This should not be in driver as if the functionality is
> >> desired, it should be done in userspace.
> >>
> >> Various other minor bits like error paths that don't clean up commented
> >> inline.
> >>
> >>
> >>>
> >>> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
> >>> ---
> >>>   .../light/sysfs-bus-iio-light-tsl2583              |    6 +
> >>>   .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
> >>>   drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
> >>>   .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
> >>>   .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
> >>>   drivers/staging/iio/light/Kconfig                  |    8 +
> >>>   drivers/staging/iio/light/Makefile                 |    1 +
> >>>   drivers/staging/iio/light/tsl2x7x_core.c           | 1911
> ++++++++++++++++++++
> >>>   drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
> >>>   9 files changed, 2032 insertions(+), 24 deletions(-)
> >>>
> >>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-
> tsl2583
> >> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> >>> new file mode 100644
> >>> index 0000000..8f2a038
> >>> --- /dev/null
> >>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
> >>> @@ -0,0 +1,6 @@
> >>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> >>> +KernelVersion:	2.6.37
> >>> +Contact:	linux-iio@vger.kernel.org
> >>> +Description:
> >>> +		This property causes an internal calibration of the als gain trim
> >>> +		value which is later used in calculating illuminance in lux.
> >>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-
> tsl2x7x
> >> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> >>> new file mode 100644
> >>> index 0000000..cceadae
> >>> --- /dev/null
> >>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
> >>> @@ -0,0 +1,20 @@
> >>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> >>> +KernelVersion:	2.6.37
> >>> +Contact:	linux-iio@vger.kernel.org
> >>> +Description:
> >>> +		This property causes an internal calibration of the als gain trim
> >>> +		value which is later used in calculating illuminance in lux.
> >>> +
> >>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
> >>> +KernelVersion:	3.3-rc1
> >>> +Contact:	linux-iio@vger.kernel.org
> >>> +Description:
> >>> +		Simultainious ALS channel data.
> >> That wasn't the intent of 'both' at all.  (+ typo).  It means a raw
> >> reading from a diod that
> >> detects the 'sum' of infrared and visible.
> >>> +
> >>> +What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
> >>> +KernelVersion:	3.3-rc1
> >>> +Contact:	linux-iio@vger.kernel.org
> >>> +Description:
> >>> +		Causes an recalculation and adjustment to the
> >>> +		proximity_thresh_rising_value.
> >>> +
> >>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio
> >> b/drivers/staging/iio/Documentation/sysfs-bus-iio
> >>> index 46a995d..5b2b5d3 100644
> >>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio
> >>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
> >>> @@ -258,6 +258,8 @@ What
> >> 	/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
> >>>   What
> >> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
> >>>   What
> >> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
> >>>   What
> >> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
> >>> +what
> >> 	/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
> >>> +what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
> >>>   KernelVersion:	2.6.35
> >>>   Contact:	linux-iio@vger.kernel.org
> >>>   Description:
> >>> @@ -457,6 +459,10 @@ What:
> >> 	/sys/.../events/in_voltageY_raw_thresh_falling_value
> >>>   What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
> >>>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
> >>>   What:		/sys/.../events/in_tempY_raw_thresh_falling_value
> >>> +What:		/sys/.../events/illuminance0_thresh_falling_value
> >>> +what:		/sys/.../events/illuminance0_thresh_rising_value
> >>> +what:		/sys/.../events/proximity_thresh_falling_value
> >>> +what:		/sys/.../events/proximity_thresh_rising_value
> >>>   KernelVersion:	2.6.37
> >>>   Contact:	linux-iio@vger.kernel.org
> >>>   Description:
> >>> @@ -739,3 +745,4 @@ Description:
> >>>   		system. To minimize the current consumption of the system,
> >>>   		the bridge can be disconnected (when it is not being used
> >>>   		using the bridge_switch_en attribute.
> >>> +
> >> spurious blank line?
> >>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> >> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> >>> index edbf470..4385c70 100644
> >>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> >>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
> >>> @@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
> >>>   Description:
> >>>   		This property gets/sets the sensors ADC analog integration
> >> time.
> >>>
> >>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
> >>> +What:		/sys/bus/iio/devices/device[n]/lux_table
> >>>   KernelVersion:	2.6.37
> >>>   Contact:	linux-iio@vger.kernel.org
> >>>   Description:
> >>> -		Hardware or software applied calibration scale factor assumed
> >>> -		to account for attenuation due to industrial design (glass
> >>> -		filters or aperture holes).
> >>> +		This property gets/sets the table of coefficients
> >>> +		used in calculating illuminance in lux.
> >>> +
> >>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> >> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> >>> deleted file mode 100644
> >>> index 660781d..0000000
> >>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
> >>> +++ /dev/null
> >>> @@ -1,20 +0,0 @@
> >>> -What:		/sys/bus/iio/devices/device[n]/lux_table
> >>> -KernelVersion:	2.6.37
> >>> -Contact:	linux-iio@vger.kernel.org
> >>> -Description:
> >>> -		This property gets/sets the table of coefficients
> >>> -		used in calculating illuminance in lux.
> >>> -
> >>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
> >>> -KernelVersion:	2.6.37
> >>> -Contact:	linux-iio@vger.kernel.org
> >>> -Description:
> >>> -		This property causes an internal calibration of the als gain trim
> >>> -		value which is later used in calculating illuminance in lux.
> >>> -
> >>> -What:
> >> 	/sys/bus/iio/devices/device[n]/illuminance0_input_target
> >>> -KernelVersion:	2.6.37
> >>> -Contact:	linux-iio@vger.kernel.org
> >>> -Description:
> >>> -		This property is the known externally illuminance (in lux).
> >>> -		It is used in the process of calibrating the device accuracy.
> >>> diff --git a/drivers/staging/iio/light/Kconfig
> b/drivers/staging/iio/light/Kconfig
> >>> index e7e9159..976f790 100644
> >>> --- a/drivers/staging/iio/light/Kconfig
> >>> +++ b/drivers/staging/iio/light/Kconfig
> >>> @@ -31,4 +31,12 @@ config TSL2583
> >>>   	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
> >>>   	 Access ALS data via iio, sysfs.
> >>>
> >>> +config TSL2x7x
> >>> +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and
> >> proximity sensors"
> >>> +	depends on I2C
> >>> +	help
> >>> +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572,
> >> tsl2672,
> >>> +	 tmd2672, tsl2772, tmd2772 devices.
> >>> +	 Provides iio_events and direct access via sysfs.
> >>> +
> >>>   endmenu
> >>> diff --git a/drivers/staging/iio/light/Makefile
> >> b/drivers/staging/iio/light/Makefile
> >>> index 3011fbf..ff12c4b 100644
> >>> --- a/drivers/staging/iio/light/Makefile
> >>> +++ b/drivers/staging/iio/light/Makefile
> >>> @@ -5,3 +5,4 @@
> >>>   obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
> >>>   obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
> >>>   obj-$(CONFIG_TSL2583)	+= tsl2583.o
> >>> +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
> >>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.c
> >> b/drivers/staging/iio/light/tsl2x7x_core.c
> >>> new file mode 100644
> >>> index 0000000..c0d9d6e
> >>> --- /dev/null
> >>> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
> >>> @@ -0,0 +1,1911 @@
> >>> +/*
> >>> + * Device driver for monitoring ambient light intensity in (lux)
> >>> + * and proximity detection (prox) within the TAOS TSL2X7X family of
> devices.
> >>> + *
> >>> + * Copyright (c) 2012, TAOS Corporation.
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or modify
> >>> + * it under the terms of the GNU General Public License as published by
> >>> + * the Free Software Foundation; 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.,
> >>> + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
> >>> + */
> >>> +
> >>> +#include<linux/kernel.h>
> >>> +#include<linux/i2c.h>
> >>> +#include<linux/errno.h>
> >>> +#include<linux/delay.h>
> >>> +#include<linux/mutex.h>
> >>> +#include<linux/interrupt.h>
> >>> +#include<linux/slab.h>
> >>> +#include<linux/module.h>
> >>> +#include<linux/version.h>
> >>> +#include "tsl2x7x_core.h"
> >>> +#include "../events.h"
> >>> +#include "../iio.h"
> >>> +#include "../sysfs.h"
> >>> +
> >>> +/* Cal defs*/
> >>> +#define PROX_STAT_CAL        0
> >>> +#define PROX_STAT_SAMP       1
> >>> +#define MAX_SAMPLES_CAL      200
> >>> +
> >>> +/* TSL2X7X Device ID */
> >>> +#define TRITON_ID    0x00
> >>> +#define SWORDFISH_ID 0x30
> >>> +#define HALIBUT_ID   0x20
> >>> +
> >>> +/* Lux calculation constants */
> >>> +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
> >>> +
> >>> +/* TAOS Register definitions - note:
> >>> + * depending on device, some of these register are not used and the
> >>> + * register address is benign.
> >>> + */
> >>> +/* 2X7X register offsets */
> >>> +#define TSL2X7X_MAX_CONFIG_REG         16
> >>> +
> >>> +/* Device Registers and Masks */
> >>> +#define TSL2X7X_CNTRL                  0x00
> >>> +#define TSL2X7X_ALS_TIME               0X01
> >>> +#define TSL2X7X_PRX_TIME               0x02
> >>> +#define TSL2X7X_WAIT_TIME              0x03
> >>> +#define TSL2X7X_ALS_MINTHRESHLO        0X04
> >>> +#define TSL2X7X_ALS_MINTHRESHHI        0X05
> >>> +#define TSL2X7X_ALS_MAXTHRESHLO        0X06
> >>> +#define TSL2X7X_ALS_MAXTHRESHHI        0X07
> >>> +#define TSL2X7X_PRX_MINTHRESHLO        0X08
> >>> +#define TSL2X7X_PRX_MINTHRESHHI        0X09
> >>> +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
> >>> +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
> >>> +#define TSL2X7X_PERSISTENCE            0x0C
> >>> +#define TSL2X7X_PRX_CONFIG             0x0D
> >>> +#define TSL2X7X_PRX_COUNT              0x0E
> >>> +#define TSL2X7X_GAIN                   0x0F
> >>> +#define TSL2X7X_NOTUSED                0x10
> >>> +#define TSL2X7X_REVID                  0x11
> >>> +#define TSL2X7X_CHIPID                 0x12
> >>> +#define TSL2X7X_STATUS                 0x13
> >>> +#define TSL2X7X_ALS_CHAN0LO            0x14
> >>> +#define TSL2X7X_ALS_CHAN0HI            0x15
> >>> +#define TSL2X7X_ALS_CHAN1LO            0x16
> >>> +#define TSL2X7X_ALS_CHAN1HI            0x17
> >>> +#define TSL2X7X_PRX_LO                 0x18
> >>> +#define TSL2X7X_PRX_HI                 0x19
> >>> +
> >>> +/* tsl2X7X cmd reg masks */
> >>> +#define TSL2X7X_CMD_REG                0x80
> >>> +#define TSL2X7X_CMD_SPL_FN             0x60
> >>> +
> >>> +#define TSL2X7X_CMD_PROX_INT_CLR       0X05
> >>> +#define TSL2X7X_CMD_ALS_INT_CLR        0x06
> >>> +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
> >>> +
> >>> +/* tsl2X7X cntrl reg masks */
> >>> +#define TSL2X7X_CNTL_ADC_ENBL          0x02
> >>> +#define TSL2X7X_CNTL_PWR_ON            0x01
> >>> +
> >>> +/* tsl2X7X status reg masks */
> >>> +#define TSL2X7X_STA_ADC_VALID          0x01
> >>> +#define TSL2X7X_STA_PRX_VALID          0x02
> >>> +#define TSL2X7X_STA_ADC_PRX_VALID      0x03
> >>> +#define TSL2X7X_STA_ALS_INTR           0x10
> >>> +#define TSL2X7X_STA_ADC_INTR           0x10
> >>> +#define TSL2X7X_STA_PRX_INTR           0x20
> >>> +
> >>> +#define TSL2X7X_STA_ADC_INTR           0x10
> >>> +
> >>> +/* tsl2X7X cntrl reg masks */
> >>> +#define TSL2X7X_CNTL_REG_CLEAR         0x00
> >>> +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
> >>> +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
> >>> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
> >>> +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
> >>> +#define TSL2X7X_CNTL_PWRON             0x01
> >>> +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
> >>> +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
> >>> +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
> >>> +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
> >>> +
> >>> +/*Prox diode to use */
> >>> +#define TSL2X7X_DIODE0                 0x10
> >>> +#define TSL2X7X_DIODE1                 0x20
> >>> +#define TSL2X7X_DIODE_BOTH             0x30
> >>> +
> >>> +/* LED Power */
> >>> +#define TSL2X7X_mA100                  0x00
> >>> +#define TSL2X7X_mA50                   0x40
> >>> +#define TSL2X7X_mA25                   0x80
> >>> +#define TSL2X7X_mA13                   0xD0
> >>> +
> >>> +/*Common device IIO EventMask */
> >>> +#define TSL2X7X_EVENT_MASK \
> >>> +		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
> >>> +		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
> >>> +
> >>> +/* TAOS txx2x7x Device family members */
> >>> +enum {
> >>> +	tsl2571,
> >>> +	tsl2671,
> >>> +	tmd2671,
> >>> +	tsl2771,
> >>> +	tmd2771,
> >>> +	tsl2572,
> >>> +	tsl2672,
> >>> +	tmd2672,
> >>> +	tsl2772,
> >>> +	tmd2772
> >>> +};
> >>> +
> >>> +enum {
> >>> +	TSL2X7X_CHIP_UNKNOWN = 0,
> >>> +	TSL2X7X_CHIP_WORKING = 1,
> >>> +	TSL2X7X_CHIP_SUSPENDED = 2
> >>> +};
> >>> +
> >>> +/* Per-device data */
> >>> +struct tsl2x7x_als_info {
> >>> +	u16 als_ch0;
> >>> +	u16 als_ch1;
> >>> +	u16 lux;
> >>> +};
> >>> +
> >>> +/* proximity data */
> >>> +struct tsl2x7x_prox_info {
> >>> +	u16 prox_data;
> >>> +	int prox_event;
> >>> +};
> >>> +
> >>> +struct prox_stat {
> >>> +	u16 min;
> >>> +	u16 max;
> >>> +	u16 mean;
> >>> +	unsigned long stddev;
> >>> +};
> >>> +
> >>> +struct tsl2x7x_chip_info {
> >>> +	int num_channels;
> >>> +	struct iio_chan_spec		channel[9];
> >>> +	const struct iio_info		*info;
> >>> +};
> >>> +
> >>> +struct tsl2X7X_chip {
> >>> +	kernel_ulong_t id;
> >>> +	struct mutex prox_mutex;
> >>> +	struct mutex als_mutex;
> >>> +	struct i2c_client *client;
> >>> +	struct tsl2x7x_prox_info prox_cur_info;
> >>> +	struct tsl2x7x_als_info als_cur_info;
> >>> +	struct tsl2x7x_settings tsl2x7x_settings;
> >>> +	struct tsl2X7X_platform_data *pdata;
> >>> +	int als_time_scale;
> >>> +	int als_saturation;
> >>> +	int tsl2x7x_chip_status;
> >>> +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
> >>> +	const struct tsl2x7x_chip_info	*chip_info;
> >>> +	const struct iio_info *info;
> >>> +	s64 event_timestamp;
> >>> +	/* This structure is intentionally large to accommodate
> >>> +	 * updates via sysfs. */
> >>> +	/* Sized to 9 = max 8 segments + 1 termination segment */
> >>> +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
> >>> +};
> >>> +
> >>> +/* Different devices require different coefficents */
> >>> +static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
> >>> +	{ 14461,   611,   1211 },
> >>> +	{ 18540,   352,    623 },
> >>> +	{     0,     0,      0 },
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
> >>> +	{ 11635,   115,    256 },
> >>> +	{ 15536,    87,    179 },
> >>> +	{     0,     0,      0 },
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
> >>> +	{ 14013,   466,   917 },
> >>> +	{ 18222,   310,   552 },
> >>> +	{     0,     0,     0 },
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
> >>> +	{ 13218,   130,   262 },
> >>> +	{ 17592,   92,    169 },
> >>> +	{     0,     0,     0 },
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
> >> check white space around here.  looks like a mixture of tabs and spaces...
> >>> +	[tsl2571] = tsl2x71_lux_table,
> >>> +	[tsl2671] =	tsl2x71_lux_table,
> >>> +	[tmd2671] =	tmd2x71_lux_table,
> >>> +	[tsl2771] =	tsl2x71_lux_table,
> >>> +	[tmd2771] =	tmd2x71_lux_table,
> >>> +	[tsl2572] =	tsl2x72_lux_table,
> >>> +	[tsl2672] =	tsl2x72_lux_table,
> >>> +	[tmd2672] = tmd2x72_lux_table,
> >>> +	[tsl2772] =	tsl2x72_lux_table,
> >>> +	[tmd2772] =	tmd2x72_lux_table,
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_settings tsl2x7x_default_settings = {
> >>> +	.als_time = 200,
> >>> +	/* must be a multiple of 50mS */
> >>> +	.als_gain = 0,
> >>> +	/* this is actually an index into the gain table */
> >>> +	.prx_time = 0xfe, /*5.4 mS */
> >>> +	/* 2.7ms prox integration time - decrease to increase time */
> >>> +	/* decreases in 2.7 ms intervals */
> >>> +	.prox_gain = 1,
> >>> +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
> >>> +	/* assume clear glass as default */
> >>> +	.wait_time = 245,
> >>> +	/* Time between PRX and ALS cycles -decrease to increase time */
> >>> +	/* decreases in 2.7 ms intervals */
> >>> +	.prox_config = 0,
> >>> +	/* Prox configuration filters */
> >>> +	.als_gain_trim = 1000,
> >>> +	/* default gain trim to account for aperture effects */
> >>> +	.als_cal_target = 150,
> >>> +	/* Known external ALS reading used for calibration */
> >>> +	.als_thresh_low = 200,
> >>> +	/* CH0 'low' count to trigger interrupt */
> >>> +	.als_thresh_high = 256,
> >>> +	/* CH0 'high' count to trigger interrupt */
> >>> +	.als_persistence = 0xFF,
> >>> +	/* Number of 'out of limits' ADC readings PRX/ALS*/
> >>> +	.interrupts_en = 0x00,
> >>> +	/* Default interrupt(s) enabled.
> >>> +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
> >>> +	.prox_thres_low  = 0,
> >>> +	.prox_thres_high = 512,
> >>> +	/*default threshold adjust either manually or with cal routine*/
> >>> +	.prox_max_samples_cal = 30,
> >>> +	.prox_pulse_count = 8
> >>> +};
> >>> +
> >>> +static const s16 tsl2X7X_als_gainadj[] = {
> >>> +	1,
> >>> +	8,
> >>> +	16,
> >>> +	120
> >>> +};
> >>> +
> >>> +static const s16 tsl2X7X_prx_gainadj[] = {
> >>> +	1,
> >>> +	2,
> >>> +	4,
> >>> +	8
> >>> +};
> >>> +
> >>> +/* Channel variations */
> >>> +enum {
> >>> +	ALS,
> >>> +	PRX,
> >>> +	ALSPRX,
> >>> +	PRX2,
> >>> +	ALSPRX2,
> >>> +};
> >>> +
> >>> +const u8 device_channel_config[] = {
> >>> +	ALS,
> >>> +	PRX,
> >>> +	PRX,
> >>> +	ALSPRX,
> >>> +	ALSPRX,
> >>> +	ALS,
> >>> +	PRX2,
> >>> +	PRX2,
> >>> +	ALSPRX2,
> >>> +	ALSPRX2
> >>> +};
> >>> +
> >>> +/*
> >>> + * Read a number of bytes starting at register (reg) location.
> >>> + * Return 0, or i2c_smbus_write_byte ERROR code.
> >>> + */
> >>> +static int
> >>> +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
> >>> +{
> >>> +	int ret;
> >>> +
> >>> +	/* select register to write */
> >>> +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
> >>> +	if (ret<  0) {
> >>> +		dev_err(&client->dev, "%s: failed to write register %x\n"
> >>> +				, __func__, reg);
> >>> +		return ret;
> >>> +	}
> >>> +	/* read the data */
> >>> +	*val = i2c_smbus_read_byte(client);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >> I would much prefer if you'd use kernel-doc for comments oabout functions.
> >>> +/*
> >>> + * Reads and calculates current lux value.
> >>> + * The raw ch0 and ch1 values of the ambient light sensed in the last
> >>> + * integration cycle are read from the device.
> >>> + * Time scale factor array values are adjusted based on the integration
> time.
> >>> + * The raw values are multiplied by a scale factor, and device gain is
> obtained
> >>> + * using gain index. Limit checks are done next, then the ratio of a multiple
> >>> + * of ch1 value, to the ch0 value, is calculated. The array
> tsl2x7x_device_lux[]
> >>> + * declared above is then scanned to find the first ratio value that is just
> >>> + * above the ratio we just calculated. The ch0 and ch1 multiplier constants
> in
> >>> + * the array are then used along with the time scale factor array values, to
> >>> + * calculate the lux.
> >>> + */
> >>> +static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
> >>> +{
> >>> +	u16 ch0, ch1; /* separated ch0/ch1 data from device */
> >>> +	u32 lux; /* raw lux calculated from device data */
> >>> +	u64 lux64;
> >>> +	u32 ratio;
> >>> +	u8 buf[4];
> >>> +	struct tsl2x7x_lux *p;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int i, ret;
> >>> +	u32 ch0lux = 0;
> >>> +	u32 ch1lux = 0;
> >>> +
> >>> +	if (mutex_trylock(&chip->als_mutex) == 0) {
> >>> +		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is
> >> busy\n");
> >>> +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
> >>> +	}
> >>> +
> >>> +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
> >>> +		/* device is not enabled */
> >>> +		dev_err(&chip->client->dev, "%s: device is not enabled\n",
> >>> +				__func__);
> >>> +		ret = -EBUSY ;
> >>> +		goto out_unlock;
> >>> +	}
> >>> +
> >>> +	ret = tsl2x7x_i2c_read(chip->client,
> >>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]);
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed to read CMD_REG\n", __func__);
> >>> +		goto out_unlock;
> >>> +	}
> >>> +	/* is data new&  valid */
> >>> +	if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: data not valid yet\n", __func__);
> >>> +		ret = chip->als_cur_info.lux; /* return LAST VALUE */
> >>> +		goto out_unlock;
> >>> +	}
> >>> +
> >>> +	for (i = 0; i<  4; i++) {
> >>> +		ret = tsl2x7x_i2c_read(chip->client,
> >>> +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
> >>> +			&buf[i]);
> >>> +		if (ret<  0) {
> >>> +			dev_err(&chip->client->dev,
> >>> +				"%s: failed to read. err=%x\n", __func__, ret);
> >>> +			goto out_unlock;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	/* clear status, really interrupt status ( are off),
> >>> +	but we use the bit anyway */
> >>> +	ret = i2c_smbus_write_byte(chip->client,
> >>> +		(TSL2X7X_CMD_REG |
> >>> +				TSL2X7X_CMD_SPL_FN |
> >>> +				TSL2X7X_CMD_ALS_INT_CLR));
> >>> +
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +		"i2c_write_command failed in %s, err = %d\n",
> >>> +			__func__, ret);
> >>> +		goto out_unlock; /* have no data, so return failure */
> >>> +	}
> >>> +
> >>> +	/* extract ALS/lux data */
> >>> +	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
> >>> +	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
> >>> +
> >>> +	chip->als_cur_info.als_ch0 = ch0;
> >>> +	chip->als_cur_info.als_ch1 = ch1;
> >>> +
> >>> +	if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) {
> >>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> >>> +		goto return_max;
> >>> +	}
> >>> +
> >>> +	if (ch0 == 0) {
> >>> +		/* have no data, so return LAST VALUE */
> >>> +		ret = chip->als_cur_info.lux = 0;
> >>> +		goto out_unlock;
> >>> +	}
> >>> +	/* calculate ratio */
> >>> +	ratio = (ch1<<  15) / ch0;
> >>> +	/* convert to unscaled lux using the pointer to the table */
> >>> +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
> >>> +	while (p->ratio != 0&&  p->ratio<  ratio)
> >>> +			p++;
> >>> +
> >>> +	if (p->ratio == 0) {
> >>> +		lux = 0;
> >>> +	} else {
> >>> +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
> >>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> >>> +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
> >>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
> >>> +		lux = ch0lux - ch1lux;
> >>> +	}
> >>> +
> >>> +	/* note: lux is 31 bit max at this point */
> >>> +	if (ch1lux>  ch0lux) {
> >>> +		dev_dbg(&chip->client->dev, "Returning last value\n");
> >>> +		ret = chip->als_cur_info.lux;
> >>> +		goto out_unlock;
> >>> +	}
> >>> +
> >>> +	/* adjust for active time scale */
> >>> +	if (chip->als_time_scale == 0)
> >>> +		lux = 0;
> >>> +	else
> >>> +		lux = (lux + (chip->als_time_scale>>  1)) /
> >>> +			chip->als_time_scale;
> >>> +
> >>> +	/* adjust for active gain scale
> >>> +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
> >>> +	 * User-specified gain provides a multiplier.
> >>> +	 * Apply user-specified gain before shifting right to retain precision.
> >>> +	 * Use 64 bits to avoid overflow on multiplication.
> >>> +	 * Then go back to 32 bits before division to avoid using div_u64().
> >>> +	 */
> >>> +	lux64 = lux;
> >>> +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
> >>> +	lux64>>= 8;
> >>> +	lux = lux64;
> >>> +	lux = (lux + 500) / 1000;
> >>> +
> >>> +	if (lux>  TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
> >>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> >>> +
> >>> +	/* Update the structure with the latest lux. */
> >>> +return_max:
> >>> +	chip->als_cur_info.lux = lux;
> >>> +	ret = lux;
> >>> +
> >>> +out_unlock:
> >>> +	mutex_unlock(&chip->als_mutex);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Proximity poll function - if valid data is available, read and form the ch0
> >>> + * and prox data values, check for limits on the ch0 value, and check the
> prox
> >>> + * data against the current thresholds, to set the event status accordingly.
> >>> + */
> >>> +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
> >>> +{
> >>> +#define CONSECUTIVE_RETRIES 50
> >>> +
> >>> +	int i;
> >>> +	int ret;
> >>> +	u8 status;
> >>> +	u8 chdata[2];
> >>> +	int err_cnt;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	if (mutex_trylock(&chip->prox_mutex) == 0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: Can't get prox mutex\n", __func__);
> >>> +		return -EBUSY;
> >>> +	}
> >>> +
> >>> +	err_cnt = 0;
> >>> +
> >>> +try_again:
> >> I'd like a comment on why this looping is necessary....
> >>> +
> >>> +	ret = tsl2x7x_i2c_read(chip->client,
> >>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status);
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +		"%s: i2c err=%d\n", __func__, ret);
> >>> +		goto prox_poll_err;
> >>> +	}
> >>> +
> >>> +	if (chip->id<  tsl2572) {
> >>> +		if (!(status&  TSL2X7X_STA_ADC_VALID)) {
> >>> +			err_cnt++;
> >>> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> >>> +				dev_err(&chip->client->dev,
> >>> +				"%s: Consec. retries exceeded\n", __func__);
> >>> +				goto prox_poll_err;
> >>> +			}
> >>> +		goto try_again;
> >>> +		}
> >>> +	} else {
> >>> +		if (!(status&  TSL2X7X_STA_PRX_VALID)) {
> >>> +			err_cnt++;
> >>> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> >>> +				dev_err(&chip->client->dev,
> >>> +				"%s: Consec. retries exceeded\n", __func__);
> >>> +				goto prox_poll_err;
> >>> +			}
> >>> +		goto try_again;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	for (i = 0; i<  2; i++) {
> >>> +		ret = tsl2x7x_i2c_read(chip->client,
> >>> +			(TSL2X7X_CMD_REG |
> >>> +					(TSL2X7X_PRX_LO + i)),&chdata[i]);
> >>> +		if (ret<  0)
> >>> +			goto prox_poll_err;
> >>> +	}
> >>> +
> >>> +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
> >>> +	if (chip->prox_cur_info.prox_data == 0)
> >>> +		goto try_again;
> >>> +
> >>> +	if (chip->prox_cur_info.prox_data>=
> >>> +			chip->tsl2x7x_settings.prox_thres_high)
> >>> +		chip->prox_cur_info.prox_event = 1;
> >>> +	else
> >>> +		chip->prox_cur_info.prox_event = 0;
> >> So this is manually polling the event signal. I'd argue that this is a
> >> job for userspace
> >> if the device isn't doing it hardware (or the interrupt signal is
> >> connected).
> >>> +
> >>> +	mutex_unlock(&chip->prox_mutex);
> >>> +	return chip->prox_cur_info.prox_event;
> >>> +
> >>> +prox_poll_err:
> >>> +	mutex_unlock(&chip->prox_mutex);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Provides initial operational parameter defaults.
> >>> + * These defaults may be changed through the device's sysfs files.
> >>> + */
> >>> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
> >>> +{
> >>> +	/* If Operational settings defined elsewhere.. */
> >>> +	if (chip->pdata&&  chip->pdata->platform_default_settings != 0)
> >>> +		memcpy(&(chip->tsl2x7x_settings),
> >>> +			chip->pdata->platform_default_settings,
> >>> +			sizeof(tsl2x7x_default_settings));
> >>> +	else
> >>> +		memcpy(&(chip->tsl2x7x_settings),
> >>> +			&tsl2x7x_default_settings,
> >>> +			sizeof(tsl2x7x_default_settings));
> >>> +
> >>> +	/* Load up the proper lux table. */
> >>> +	if (chip->pdata&&  chip->pdata->platform_lux_table[0].ratio != 0)
> >>> +		memcpy(chip->tsl2x7x_device_lux,
> >>> +			chip->pdata->platform_lux_table,
> >>> +			sizeof(chip->pdata->platform_lux_table));
> >>> +	else
> >>> +		memcpy(chip->tsl2x7x_device_lux,
> >>> +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
> >>> +				MAX_DEFAULT_TABLE_BYTES);
> >>> +
> >>> +}
> >>> +
> >>> +/*
> >>> + * Obtain single reading and calculate the als_gain_trim
> >>> + * (later used to derive actual lux).
> >>> + * Return updated gain_trim value.
> >>> + */
> >>> +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	u8 reg_val;
> >>> +	int gain_trim_val;
> >>> +	int ret;
> >>> +	int lux_val;
> >>> +
> >>> +	ret = i2c_smbus_write_byte(chip->client,
> >>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +		"%s: failed to write CNTRL register, ret=%d\n",
> >>> +		__func__, ret);
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	reg_val = i2c_smbus_read_byte(chip->client);
> >>> +	if ((reg_val&  (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
> >>> +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed: ADC not enabled\n", __func__);
> >>> +		return -1;
> >>> +	}
> >>> +
> >>> +	ret = i2c_smbus_write_byte(chip->client,
> >>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed to write ctrl reg: ret=%d\n",
> >>> +			__func__, ret);
> >>> +		return ret;
> >>> +	}
> >>> +
> >>> +	reg_val = i2c_smbus_read_byte(chip->client);
> >>> +	if ((reg_val&  TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed: STATUS - ADC not valid.\n", __func__);
> >>> +		return -ENODATA;
> >>> +	}
> >>> +
> >>> +	lux_val = tsl2x7x_get_lux(indio_dev);
> >>> +	if (lux_val<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +		"%s: failed to get lux\n", __func__);
> >>> +		return lux_val;
> >>> +	}
> >>> +
> >>> +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
> >>> +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
> >>> +	if ((gain_trim_val<  250) || (gain_trim_val>  4000))
> >>> +		return -ERANGE;
> >>> +
> >>> +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
> >>> +	dev_info(&chip->client->dev,
> >>> +		"%s als_calibrate completed\n", chip->client->name);
> >>> +
> >>> +	return (int) gain_trim_val;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Turn the device on.
> >>> + * Configuration must be set before calling this function.
> >>> + */
> >>> +static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
> >>> +{
> >>> +	int i;
> >>> +	int ret = 0;
> >>> +	u8 *dev_reg;
> >>> +	u8 utmp;
> >>> +	int als_count;
> >>> +	int als_time;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	u8 reg_val = 0;
> >>> +
> >>> +	if (chip->pdata&&  chip->pdata->power_on)
> >>> +		chip->pdata->power_on(indio_dev);
> >>> +
> >>> +	/* Non calculated parameters */
> >>> +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
> >>> +			chip->tsl2x7x_settings.prx_time;
> >>> +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
> >>> +			chip->tsl2x7x_settings.wait_time;
> >>> +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
> >>> +			chip->tsl2x7x_settings.prox_config;
> >>> +
> >>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
> >>> +		(chip->tsl2x7x_settings.als_thresh_low)&  0xFF;
> >>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
> >>> +		(chip->tsl2x7x_settings.als_thresh_low>>  8)&  0xFF;
> >>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
> >>> +		(chip->tsl2x7x_settings.als_thresh_high)&  0xFF;
> >>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
> >>> +		(chip->tsl2x7x_settings.als_thresh_high>>  8)&  0xFF;
> >>> +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
> >>> +		chip->tsl2x7x_settings.als_persistence;
> >>> +
> >>> +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
> >>> +			chip->tsl2x7x_settings.prox_pulse_count;
> >>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
> >>> +	chip->tsl2x7x_settings.prox_thres_low;
> >>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
> >>> +			chip->tsl2x7x_settings.prox_thres_high;
> >>> +
> >>> +	/* and make sure we're not already on */
> >>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> >>> +		/* if forcing a register update - turn off, then on */
> >>> +		dev_info(&chip->client->dev, "device is already enabled\n");
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	/* determine als integration regster */
> >>> +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
> >>> +	if (als_count == 0)
> >>> +		als_count = 1; /* ensure at least one cycle */
> >>> +
> >>> +	/* convert back to time (encompasses overrides) */
> >>> +	als_time = (als_count * 27 + 5) / 10;
> >>> +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
> >>> +
> >>> +	/* Set the gain based on tsl2x7x_settings struct */
> >>> +	chip->tsl2x7x_config[TSL2X7X_GAIN] =
> >>> +		(chip->tsl2x7x_settings.als_gain |
> >>> +			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
> >>> +			| ((chip->tsl2x7x_settings.prox_gain)<<  2));
> >>> +
> >>> +	/* set chip struct re scaling and saturation */
> >>> +	chip->als_saturation = als_count * 922; /* 90% of full scale */
> >>> +	chip->als_time_scale = (als_time + 25) / 50;
> >>> +
> >>> +	/* TSL2X7X Specific power-on / adc enable sequence
> >>> +	 * Power on the device 1st. */
> >>> +	utmp = TSL2X7X_CNTL_PWR_ON;
> >>> +	ret = i2c_smbus_write_byte_data(chip->client,
> >>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed on CNTRL reg.\n", __func__);
> >>> +		return -1;
> >>> +	}
> >>> +
> >>> +	/* Use the following shadow copy for our delay before enabling ADC.
> >>> +	 * Write all the registers. */
> >>> +	for (i = 0, dev_reg = chip->tsl2x7x_config;
> >>> +			i<  TSL2X7X_MAX_CONFIG_REG; i++) {
> >>> +		ret = i2c_smbus_write_byte_data(chip->client,
> >>> +				TSL2X7X_CMD_REG + i, *dev_reg++);
> >>> +		if (ret<  0) {
> >>> +			dev_err(&chip->client->dev,
> >>> +			"%s: failed on write to reg %d.\n", __func__, i);
> >>> +			return ret;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	udelay(3000);	/* Power-on settling time */
> >>> +
> >>> +	/* NOW enable the ADC
> >>> +	 * initialize the desired mode of operation */
> >>> +	utmp = TSL2X7X_CNTL_PWR_ON |
> >>> +			TSL2X7X_CNTL_ADC_ENBL |
> >>> +			TSL2X7X_CNTL_PROX_DET_ENBL;
> >>> +	ret = i2c_smbus_write_byte_data(chip->client,
> >>> +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
> >>> +	if (ret<  0) {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: failed on 2nd CTRL reg.\n", __func__);
> >>> +		return ret;
> >>> +		}
> >>> +
> >>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
> >>> +
> >>> +	if (chip->tsl2x7x_settings.interrupts_en != 0) {
> >>> +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
> >>> +
> >>> +		reg_val = TSL2X7X_CNTL_PWR_ON |
> >> TSL2X7X_CNTL_ADC_ENBL;
> >>> +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
> >>> +			(chip->tsl2x7x_settings.interrupts_en == 0x30))
> >>> +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
> >>> +
> >>> +		reg_val |= chip->tsl2x7x_settings.interrupts_en;
> >>> +		ret = i2c_smbus_write_byte_data(chip->client,
> >>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
> >>> +		if (ret<  0)
> >>> +			dev_err(&chip->client->dev,
> >>> +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
> >>> +				__func__);
> >>> +
> >>> +		/* Clear out any initial interrupts  */
> >>> +		ret = i2c_smbus_write_byte(chip->client,
> >>> +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> >>> +			TSL2X7X_CMD_PROXALS_INT_CLR);
> >>> +		if (ret<  0) {
> >>> +			dev_err(&chip->client->dev,
> >>> +				"%s: failed in tsl2x7x_chip_on\n",
> >>> +				__func__);
> >>> +		return ret;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
> >>> +{
> >>> +	int ret;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	/* turn device off */
> >>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> >>> +
> >>> +	ret = i2c_smbus_write_byte_data(chip->client,
> >>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
> >>> +
> >>> +	if (chip->pdata&&  chip->pdata->power_off)
> >>> +		chip->pdata->power_off(chip->client);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Proximity calibration helper function
> >>> + * runs through a collection of data samples,
> >>> + * sets the min, max, mean, and std dev.
> >>> + */
> >>> +static
> >>> +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
> >>> +{
> >>> +	int i;
> >>> +	int min, max, sum, mean;
> >>> +	unsigned long stddev;
> >>> +	int tmp;
> >>> +
> >>> +	if (length == 0)
> >>> +		length = 1;
> >>> +
> >>> +	sum = 0;
> >>> +	min = INT_MAX;
> >>> +	max = INT_MIN;
> >>> +	for (i = 0; i<  length; i++) {
> >>> +		sum += data[i];
> >> avoid using min as a variable name (as it's also an appropriate function)
> >> _min = MIN(data[i], _min); saves you a line of code.
> >>> +		if (data[i]<  min)
> >>> +			min = data[i];
> >>> +		if (data[i]>  max)
> >>> +			max = data[i];
> >>> +	}
> >>> +	mean = sum/length;
> >>> +	statP->min = min;
> >>> +	statP->max = max;
> >>> +	statP->mean = mean;
> >>> +
> >>> +	sum = 0;
> >>> +	for (i = 0; i<  length; i++) {
> >>> +		tmp = data[i]-mean;
> >>> +		sum += tmp * tmp;
> >>> +	}
> >>> +	stddev = int_sqrt((long)sum)/length;
> >>> +	statP->stddev = stddev;
> >>> +}
> >>> +
> >>> +/**
> >>> + * Proximity calibration - collects a number of samples,
> >>> + * calculates a standard deviation based on the samples, and
> >>> + * sets the threshold accordingly.
> >>> + */
> >>> +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
> >>> +{
> >>> +	u16 prox_history[MAX_SAMPLES_CAL+1];
> >> spaces around that +
> >>> +	int i;
> >>> +	struct prox_stat prox_stat_data[2];
> >>> +	struct prox_stat *calP;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	u8 tmp_irq_settings;
> >>> +	u8 current_state = chip->tsl2x7x_chip_status;
> >>> +
> >>> +	if (chip->tsl2x7x_settings.prox_max_samples_cal>  MAX_SAMPLES_CAL)
> >> {
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: max prox samples cal is too big: %d\n",
> >>> +			__func__, chip-
> >>> tsl2x7x_settings.prox_max_samples_cal);
> >>> +		chip->tsl2x7x_settings.prox_max_samples_cal =
> >> MAX_SAMPLES_CAL;
> >>> +	}
> >>> +
> >>> +	/* have to stop to change settings */
> >>> +	tsl2x7x_chip_off(indio_dev);
> >>> +
> >>> +	/* Enable proximity detection save just in case prox not wanted yet*/
> >>> +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
> >>> +	chip->tsl2x7x_settings.interrupts_en |=
> >> TSL2X7X_CNTL_PROX_INT_ENBL;
> >>> +
> >>> +	/*turn on device if not already on*/
> >>> +	tsl2x7x_chip_on(indio_dev);
> >>> +
> >>> +	/*gather the samples*/
> >>> +	for (i = 0; i<  chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
> >>> +		mdelay(15);
> >>> +		tsl2x7x_prox_poll(indio_dev);
> >>> +		prox_history[i] = chip->prox_cur_info.prox_data;
> >>> +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
> >>> +			i, chip->prox_cur_info.prox_data);
> >>> +	}
> >>> +
> >>> +	tsl2x7x_chip_off(indio_dev);
> >>> +	calP =&prox_stat_data[PROX_STAT_CAL];
> >>> +	tsl2x7x_prox_calculate(prox_history,
> >>> +		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
> >>> +	chip->tsl2x7x_settings.prox_thres_high = (calP->max<<  1) - calP-
> >>> mean;
> >>> +
> >>> +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
> >>> +		calP->min, calP->mean, calP->max);
> >>> +	dev_info(&chip->client->dev,
> >>> +		"%s proximity threshold set to %d\n",
> >>> +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
> >>> +
> >>> +	/* back to the way they were */
> >>> +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
> >>> +	if (current_state == TSL2X7X_CHIP_WORKING)
> >>> +		tsl2x7x_chip_on(indio_dev);
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_power_state_store(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	bool value;
> >>> +
> >>> +	if (strtobool(buf,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (!value)
> >>> +		tsl2x7x_chip_off(indio_dev);
> >>> +	else
> >>> +		tsl2x7x_chip_on(indio_dev);
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	if (chip->id>  tsl2771)
> >>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
> >>> +	else
> >>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_als_time_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> >>> +			chip->tsl2x7x_settings.als_time);
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_als_time_store(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	unsigned long value;
> >>> +
> >>> +	if (kstrtoul(buf, 0,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if ((value<  50) || (value>  650))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (value % 50)
> >>> +		return -EINVAL;
> >>> +
> >>> +	 chip->tsl2x7x_settings.als_time = value;
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static IIO_CONST_ATTR(illuminance0_integration_time_available,
> >>> +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
> >>> +
> >>> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> >>> +			chip->tsl2x7x_settings.als_cal_target);
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	unsigned long value;
> >>> +
> >>> +	if (kstrtoul(buf, 0,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (value)
> >>> +		chip->tsl2x7x_settings.als_cal_target = value;
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +/* sampling_frequency AKA persistence in data sheet */
> >>> +static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> >>> +			chip->tsl2x7x_settings.als_persistence);
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	unsigned long value;
> >>> +
> >>> +	if (kstrtoul(buf, 0,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	chip->tsl2x7x_settings.als_persistence = value;
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static IIO_CONST_ATTR(sampling_frequency_available,
> >>> +		"0x00 - 0xFF (0 - 255)");
> >>> +
> >>> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	bool value;
> >>> +
> >>> +	if (strtobool(buf,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (value)
> >>> +		tsl2x7x_als_calibrate(indio_dev);
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
> >>> +	struct device_attribute *attr, char *buf)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int i;
> >>> +	int offset = 0;
> >>> +
> >>> +	i = 0;
> >> Set i at the declaration above.
> >>> +	while (i<  (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
> >>> +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
> >>> +			chip->tsl2x7x_device_lux[i].ratio,
> >>> +			chip->tsl2x7x_device_lux[i].ch0,
> >>> +			chip->tsl2x7x_device_lux[i].ch1);
> >>> +		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
> >>> +			/* We just printed the first "0" entry.
> >>> +			 * Now get rid of the extra "," and break. */
> >>> +			offset--;
> >>> +			break;
> >>> +		}
> >>> +		i++;
> >>> +	}
> >>> +
> >>> +	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
> >>> +	return offset;
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_luxtable_store(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
> >>> +	int n;
> >>> +
> >>> +	get_options(buf, ARRAY_SIZE(value), value);
> >>> +
> >>> +	/* We now have an array of ints starting at value[1], and
> >>> +	 * enumerated by value[0].
> >>> +	 * We expect each group of three ints is one table entry,
> >>> +	 * and the last table entry is all 0.
> >>> +	 */
> >>> +	n = value[0];
> >>> +	if ((n % 3) || n<  6 ||
> >>> +			n>  ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
> >>> +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
> >>> +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
> >>> +		tsl2x7x_chip_off(indio_dev);
> >>> +
> >>> +	/* Zero out the table */
> >>> +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
> >>> +	memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4));
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
> >>> +	struct device_attribute *attr, const char *buf, size_t len)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	bool value;
> >>> +
> >>> +	if (strtobool(buf,&value))
> >>> +		return -EINVAL;
> >>> +
> >>> +	if (value)
> >>> +		tsl2x7x_prox_cal(indio_dev);
> >>> +
> >>> +	return len;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
> >>> +					 u64 event_code)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int ret;
> >>> +
> >>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> >>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x10);
> >>> +	else
> >>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&  0x20);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
> >>> +					  u64 event_code,
> >>> +					  int val)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> >> {
> >>> +		if (val)
> >>> +			chip->tsl2x7x_settings.interrupts_en |= 0x10;
> >>> +		else
> >>> +			chip->tsl2x7x_settings.interrupts_en&= 0x20;
> >>> +	} else {
> >>> +		if (val)
> >>> +			chip->tsl2x7x_settings.interrupts_en |= 0x20;
> >>> +		else
> >>> +			chip->tsl2x7x_settings.interrupts_en&= 0x10;
> >>> +	}
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
> >>> +				  u64 event_code,
> >>> +				  int val)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> >> {
> >>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> >>> +		case IIO_EV_DIR_RISING:
> >>> +			chip->tsl2x7x_settings.als_thresh_high = val;
> >>> +			break;
> >>> +		case IIO_EV_DIR_FALLING:
> >>> +			chip->tsl2x7x_settings.als_thresh_low = val;
> >>> +			break;
> >>> +		default:
> >>> +			return -EINVAL;
> >>> +		}
> >>> +	} else {
> >>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> >>> +		case IIO_EV_DIR_RISING:
> >>> +			chip->tsl2x7x_settings.prox_thres_high = val;
> >>> +			break;
> >>> +		case IIO_EV_DIR_FALLING:
> >>> +			chip->tsl2x7x_settings.prox_thres_low = val;
> >>> +			break;
> >>> +		default:
> >>> +			return -EINVAL;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
> >>> +			       u64 event_code,
> >>> +			       int *val)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
> >> {
> >>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> >>> +		case IIO_EV_DIR_RISING:
> >>> +			*val = chip->tsl2x7x_settings.als_thresh_high;
> >>> +			break;
> >>> +		case IIO_EV_DIR_FALLING:
> >>> +			*val = chip->tsl2x7x_settings.als_thresh_low;
> >>> +			break;
> >>> +		default:
> >>> +			return -EINVAL;
> >>> +		}
> >>> +	} else {
> >>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
> >>> +		case IIO_EV_DIR_RISING:
> >>> +			*val = chip->tsl2x7x_settings.prox_thres_high;
> >>> +			break;
> >>> +		case IIO_EV_DIR_FALLING:
> >>> +			*val = chip->tsl2x7x_settings.prox_thres_low;
> >>> +			break;
> >>> +		default:
> >>> +			return -EINVAL;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
> >>> +			    struct iio_chan_spec const *chan,
> >>> +			    int *val,
> >>> +			    int *val2,
> >>> +			    long mask)
> >>> +{
> >>> +	int ret = -EINVAL;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	switch (mask) {
> >>> +	case 0:
> >>> +		switch (chan->type) {
> >>> +		case IIO_LIGHT:
> >>> +			tsl2x7x_get_lux(indio_dev);
> >>> +			if (chan->processed_val)
> >>> +				*val = chip->als_cur_info.lux;
> >>> +			else
> >>> +				*val = (((chip->als_cur_info.als_ch0<<  16) |
> >>> +					chip->als_cur_info.als_ch1));
> >> Why have a processed read back and a raw readback?
> >>> +			ret = IIO_VAL_INT;
> >>> +			break;
> >>> +		case IIO_PROXIMITY:
> >>> +			tsl2x7x_prox_poll(indio_dev);
> >>> +			if (chan->processed_val)
> >> Hmm.. this is uggly.  We effectively have polling of an event status.
> >> Normally
> >> I'd expect the event to only occur as a an IIO event rather than being
> >> readable like
> >> this...
> >>> +				*val = chip->prox_cur_info.prox_event;
> >>> +			else
> >>> +				*val = chip->prox_cur_info.prox_data;
> >>> +			ret = IIO_VAL_INT;
> >>> +			break;
> >>> +		default:
> >>> +			return -EINVAL;
> >>> +			break;
> >>> +		}
> >>> +		break;
> >>> +	case IIO_CHAN_INFO_CALIBSCALE:
> >>> +		if (chan->type == IIO_LIGHT)
> >>> +			*val =
> >>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
> >>> +		else
> >>> +			*val =
> >>> +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
> >>> +		ret = IIO_VAL_INT;
> >>> +		break;
> >>> +	case IIO_CHAN_INFO_CALIBBIAS:
> >>> +		*val = chip->tsl2x7x_settings.als_gain_trim;
> >>> +		ret = IIO_VAL_INT;
> >>> +		break;
> >>> +
> >>> +	default:
> >>> +		ret = -EINVAL;
> >>> +	}
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
> >>> +			       struct iio_chan_spec const *chan,
> >>> +			       int val,
> >>> +			       int val2,
> >>> +			       long mask)
> >>> +{
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +
> >>> +	switch (mask) {
> >>> +	case IIO_CHAN_INFO_CALIBSCALE:
> >>> +		if (chan->type == IIO_LIGHT) {
> >>> +			switch (val) {
> >>> +			case 1:
> >>> +				chip->tsl2x7x_settings.als_gain = 0;
> >>> +				break;
> >>> +			case 8:
> >>> +				chip->tsl2x7x_settings.als_gain = 1;
> >>> +				break;
> >>> +			case 16:
> >>> +				chip->tsl2x7x_settings.als_gain = 2;
> >>> +				break;
> >>> +			case 120:
> >>> +				if (chip->id>  tsl2771)
> >>> +					return -EINVAL;
> >>> +				chip->tsl2x7x_settings.als_gain = 3;
> >>> +				break;
> >>> +			case 128:
> >>> +				if (chip->id<  tsl2572)
> >>> +					return -EINVAL;
> >>> +				chip->tsl2x7x_settings.als_gain = 3;
> >>> +				break;
> >>> +			default:
> >>> +				return -EINVAL;
> >>> +			}
> >>> +		} else {
> >>> +			switch (val) {
> >>> +			case 1:
> >>> +				chip->tsl2x7x_settings.prox_gain = 0;
> >>> +				break;
> >>> +			case 2:
> >>> +				chip->tsl2x7x_settings.prox_gain = 1;
> >>> +				break;
> >>> +			case 4:
> >>> +				chip->tsl2x7x_settings.prox_gain = 2;
> >>> +				break;
> >>> +			case 8:
> >>> +				chip->tsl2x7x_settings.prox_gain = 3;
> >>> +				break;
> >>> +			default:
> >>> +				return -EINVAL;
> >>> +			}
> >>> +		}
> >>> +		break;
> >>> +	case IIO_CHAN_INFO_CALIBBIAS:
> >>> +		chip->tsl2x7x_settings.als_gain_trim = val;
> >>> +		break;
> >>> +
> >>> +	default:
> >>> +		return -EINVAL;
> >>> +	}
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
> >>> +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
> >>> +
> >>> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
> >>> +		tsl2x7x_prox_gain_available_show, NULL);
> >>> +
> >>> +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
> >>> +		tsl2x7x_gain_available_show, NULL);
> >>> +
> >>> +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
> >>> +		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
> >>> +
> >>> +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
> >>> +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
> >>> +
> >>> +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
> >>> +		tsl2x7x_do_calibrate);
> >>> +
> >>> +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
> >>> +		tsl2x7x_do_prox_calibrate);
> >>> +
> >>> +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
> >>> +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
> >>> +
> >>> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
> >>> +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
> >>> +
> >>> +/* Use the default register values to identify the Taos device */
> >>> +static int tsl2x7x_device_id(unsigned char *id, int target)
> >>> +{
> >>> +	switch (target) {
> >>> +	case tsl2571:
> >>> +	case tsl2671:
> >>> +	case tsl2771:
> >>> +		return ((*id&  0xf0) == TRITON_ID);
> >>> +	break;
> >>> +	case tmd2671:
> >>> +	case tmd2771:
> >>> +		return ((*id&  0xf0) == HALIBUT_ID);
> >>> +	break;
> >>> +	case tsl2572:
> >>> +	case tsl2672:
> >>> +	case tmd2672:
> >>> +	case tsl2772:
> >>> +	case tmd2772:
> >>> +		return ((*id&  0xf0) == SWORDFISH_ID);
> >>> +	break;
> >>> +	}
> >>> +
> >>> +	return -EINVAL;
> >>> +}
> >>> +
> >>> +/*
> >>> + * Interrupt Event Handler */
> >>> +static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
> >>> +{
> >>> +	struct iio_dev *indio_dev = private;
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	s64 timestamp = iio_get_time_ns();
> >>> +	int ret;
> >>> +	int value;
> >>> +
> >>> +	value = i2c_smbus_read_byte_data(chip->client,
> >>> +		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
> >>> +
> >>> +	/* What type of interrupt do we need to process */
> >>> +	if (value&  TSL2X7X_STA_PRX_INTR) {
> >>> +		tsl2x7x_prox_poll(indio_dev);
> >>> +		iio_push_event(indio_dev,
> >>> +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
> >>> +						    0,
> >>> +						    IIO_EV_TYPE_THRESH,
> >>> +						    IIO_EV_DIR_EITHER),
> >>> +						    timestamp);
> >>> +	}
> >>> +
> >>> +	if (value&  TSL2X7X_STA_ALS_INTR) {
> >>> +		tsl2x7x_get_lux(indio_dev);
> >> why the get value?  No real guarantee you'll get the one that caused the
> >> event as far as I can see.
> >>> +		iio_push_event(indio_dev,
> >>> +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
> >>> +					    0,
> >>> +					    IIO_EV_TYPE_THRESH,
> >>> +					    IIO_EV_DIR_EITHER),
> >>> +					    timestamp);
> >>> +	}
> >>> +	/* Clear interrupt now that we have the status */
> >>> +	ret = i2c_smbus_write_byte(chip->client,
> >>> +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> >>> +		TSL2X7X_CMD_PROXALS_INT_CLR);
> >>> +	if (ret<  0)
> >>> +		dev_err(&chip->client->dev,
> >>> +			"%s: Failed to clear irq from event handler. err = %d\n",
> >>> +			__func__, ret);
> >>> +
> >>> +	return IRQ_HANDLED;
> >>> +}
> >>> +
> >>> +static struct attribute *tsl2x7x_ALS_device_attrs[] = {
> >>> +	&dev_attr_power_state.attr,
> >>> +	&dev_attr_illuminance0_calibscale_available.attr,
> >>> +	&dev_attr_illuminance0_integration_time.attr,
> >>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> >>> +	&dev_attr_illuminance0_target_input.attr,
> >>> +	&dev_attr_illuminance0_calibrate.attr,
> >>> +	&dev_attr_illuminance0_lux_table.attr,
> >>> +	&dev_attr_sampling_frequency.attr,
> >>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >>> +	NULL
> >>> +};
> >>> +
> >>> +static struct attribute *tsl2x7x_PRX_device_attrs[] = {
> >>> +	&dev_attr_power_state.attr,
> >>> +	&dev_attr_sampling_frequency.attr,
> >>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >>> +	&dev_attr_proximity_calibrate.attr,
> >>> +	NULL
> >>> +};
> >>> +
> >>> +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
> >>> +	&dev_attr_power_state.attr,
> >>> +	&dev_attr_illuminance0_calibscale_available.attr,
> >>> +	&dev_attr_illuminance0_integration_time.attr,
> >>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> >>> +	&dev_attr_illuminance0_target_input.attr,
> >>> +	&dev_attr_illuminance0_calibrate.attr,
> >>> +	&dev_attr_illuminance0_lux_table.attr,
> >>> +	&dev_attr_sampling_frequency.attr,
> >>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >>> +	&dev_attr_proximity_calibrate.attr,
> >>> +	NULL
> >>> +};
> >>> +
> >>> +static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
> >>> +	&dev_attr_power_state.attr,
> >>> +	&dev_attr_sampling_frequency.attr,
> >>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >>> +	&dev_attr_proximity_calibrate.attr,
> >>> +	&dev_attr_proximity_calibscale_available.attr,
> >>> +	NULL
> >>> +};
> >>> +
> >>> +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
> >>> +	&dev_attr_power_state.attr,
> >>> +	&dev_attr_illuminance0_calibscale_available.attr,
> >>> +	&dev_attr_illuminance0_integration_time.attr,
> >>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
> >>> +	&dev_attr_illuminance0_target_input.attr,
> >>> +	&dev_attr_illuminance0_calibrate.attr,
> >>> +	&dev_attr_illuminance0_lux_table.attr,
> >>> +	&dev_attr_sampling_frequency.attr,
> >>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >>> +	&dev_attr_proximity_calibrate.attr,
> >>> +	&dev_attr_proximity_calibscale_available.attr,
> >>> +	NULL
> >>> +};
> >>> +
> >>> +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
> >>> +	[ALS] = {
> >>> +		.attrs = tsl2x7x_ALS_device_attrs,
> >>> +	},
> >>> +	[PRX] = {
> >>> +		.attrs = tsl2x7x_PRX_device_attrs,
> >>> +	},
> >>> +	[ALSPRX] = {
> >>> +		.attrs = tsl2x7x_ALSPRX_device_attrs,
> >>> +	},
> >>> +	[PRX2] = {
> >>> +		.attrs = tsl2x7x_PRX2_device_attrs,
> >>> +	},
> >>> +	[ALSPRX2] = {
> >>> +		.attrs = tsl2x7x_ALSPRX2_device_attrs,
> >>> +	},
> >>> +};
> >>> +
> >>> +static const struct iio_info tsl2X7X_device_info[] = {
> >>> +	[ALS] = {
> >>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALS],
> >>> +		.driver_module = THIS_MODULE,
> >>> +		.read_raw =&tsl2x7x_read_raw,
> >>> +		.write_raw =&tsl2x7x_write_raw,
> >>> +		.read_event_value =&tsl2x7x_read_thresh,
> >>> +		.write_event_value =&tsl2x7x_write_thresh,
> >>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> >>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> >>> +	},
> >>> +	[PRX] = {
> >>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX],
> >>> +		.driver_module = THIS_MODULE,
> >>> +		.read_raw =&tsl2x7x_read_raw,
> >>> +		.write_raw =&tsl2x7x_write_raw,
> >>> +		.read_event_value =&tsl2x7x_read_thresh,
> >>> +		.write_event_value =&tsl2x7x_write_thresh,
> >>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> >>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> >>> +	},
> >>> +	[ALSPRX] = {
> >>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX],
> >>> +		.driver_module = THIS_MODULE,
> >>> +		.read_raw =&tsl2x7x_read_raw,
> >>> +		.write_raw =&tsl2x7x_write_raw,
> >>> +		.read_event_value =&tsl2x7x_read_thresh,
> >>> +		.write_event_value =&tsl2x7x_write_thresh,
> >>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> >>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> >>> +	},
> >>> +	[PRX2] = {
> >>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX2],
> >>> +		.driver_module = THIS_MODULE,
> >>> +		.read_raw =&tsl2x7x_read_raw,
> >>> +		.write_raw =&tsl2x7x_write_raw,
> >>> +		.read_event_value =&tsl2x7x_read_thresh,
> >>> +		.write_event_value =&tsl2x7x_write_thresh,
> >>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> >>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> >>> +	},
> >>> +	[ALSPRX2] = {
> >>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2],
> >>> +		.driver_module = THIS_MODULE,
> >>> +		.read_raw =&tsl2x7x_read_raw,
> >>> +		.write_raw =&tsl2x7x_write_raw,
> >>> +		.read_event_value =&tsl2x7x_read_thresh,
> >>> +		.write_event_value =&tsl2x7x_write_thresh,
> >>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
> >>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
> >>> +	},
> >>> +};
> >>> +
> >>> +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
> >>> +	[ALS] = {
> >>> +		.channel = {
> >>> +			[0] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[1] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.info_mask =
> >>> +
> >> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> >>> +
> >> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +		},
> >>> +	.num_channels = 2,
> >>> +	.info =&tsl2X7X_device_info[ALS],
> >>> +	},
> >>> +	[PRX] = {
> >>> +		.channel = {
> >>> +			[0] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[1] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +		},
> >>> +	.num_channels = 2,
> >>> +	.info =&tsl2X7X_device_info[PRX],
> >>> +	},
> >>> +	[ALSPRX] = {
> >>> +		.channel = {
> >>> +			[0] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[1] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.info_mask =
> >>> +
> >> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> >>> +
> >> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +			[2] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[3] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 0,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +		},
> >>> +	.num_channels = 4,
> >>> +	.info =&tsl2X7X_device_info[ALSPRX],
> >>> +	},
> >>> +	[PRX2] = {
> >>> +		.channel = {
> >>> +			[0] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[1] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.info_mask =
> >>> +
> >> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +		},
> >>> +	.num_channels = 2,
> >>> +	.info =&tsl2X7X_device_info[PRX2],
> >>> +	},
> >>> +	[ALSPRX2] = {
> >>> +		.channel = {
> >>> +				[0] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[1] = {
> >>> +				.type = IIO_LIGHT,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.info_mask =
> >>> +
> >> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> >>> +
> >> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +			[2] = {
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.processed_val = 1,
> >>> +			},
> >>> +			[3] = {
> >> I'm more than a little confused here.  There are only 2 actually
> >> channels?  If so I don't
> >> see why we have 4 channels here...
> >>> +				.type = IIO_PROXIMITY,
> >>> +				.indexed = 1,
> >>> +				.channel = 0,
> >>> +				.info_mask =
> >>> +
> >> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
> >>> +				.event_mask = TSL2X7X_EVENT_MASK
> >>> +			},
> >>> +		},
> >>> +	.num_channels = 4,
> >>> +	.info =&tsl2X7X_device_info[ALSPRX2],
> >>> +	},
> >>> +};
> >>> +
> >>> +/*
> >>> + * Client probe function.
> >>> + */
> >>> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
> >>> +	const struct i2c_device_id *id)
> >>> +{
> >>> +	int ret;
> >>> +	unsigned char device_id;
> >>> +	struct iio_dev *indio_dev;
> >>> +	struct tsl2X7X_chip *chip;
> >>> +
> >>> +	indio_dev = iio_allocate_device(sizeof(*chip));
> >>> +	if (!indio_dev)
> >>> +		return -ENOMEM;
> >>> +
> >>> +	chip = iio_priv(indio_dev);
> >>> +	chip->client = clientp;
> >>> +	i2c_set_clientdata(clientp, indio_dev);
> >>> +
> >>> +	ret = tsl2x7x_i2c_read(chip->client,
> >>> +		TSL2X7X_CHIPID,&device_id);
> >>> +	if (ret<  0)
> >>> +		goto fail1;
> >>> +
> >>> +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
> >>> +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
> >>> +		dev_info(&chip->client->dev,
> >>> +				"i2c device found does not match expected id
> >> in %s\n",
> >>> +				__func__);
> >>> +		goto fail1;
> >>> +	}
> >>> +
> >>> +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG |
> >> TSL2X7X_CNTRL));
> >>> +	if (ret<  0) {
> >>> +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err =
> >> %d\n",
> >>> +				__func__, ret);
> >>> +		goto fail1;
> >>> +	}
> >>> +
> >>> +	/* ALS and PROX functions can be invoked via user space poll
> >>> +	 * or H/W interrupt. If busy return last sample. */
> >>> +	mutex_init(&chip->als_mutex);
> >>> +	mutex_init(&chip->prox_mutex);
> >>> +
> >>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
> >>> +	chip->pdata = clientp->dev.platform_data;
> >>> +	chip->id = id->driver_data;
> >>> +	chip->chip_info =
> >>> +		&tsl2x7x_chip_info_tbl[device_channel_config[id-
> >>> driver_data]];
> >>> +
> >>> +	indio_dev->info = chip->chip_info->info;
> >>> +	indio_dev->dev.parent =&clientp->dev;
> >>> +	indio_dev->modes = INDIO_DIRECT_MODE;
> >>> +	indio_dev->name = chip->client->name;
> >>> +	indio_dev->channels = chip->chip_info->channel;
> >>> +	indio_dev->num_channels = chip->chip_info->num_channels;
> >>> +
> >>> +	if (clientp->irq) {
> >>> +		ret = request_threaded_irq(clientp->irq,
> >>> +					   NULL,
> >>> +					&tsl2x7x_event_handler,
> >>> +					   IRQF_TRIGGER_RISING |
> >> IRQF_ONESHOT,
> >>> +					   "TSL2X7X_event",
> >>> +					   indio_dev);
> >>> +		if (ret) {
> >>> +			dev_err(&clientp->dev,
> >>> +				"%s: irq request failed", __func__);
> >>> +			goto fail2;
> >>> +		}
> >>> +	}
> >>> +
> >>> +	/* Load up the defaults */
> >>> +	tsl2x7x_defaults(chip);
> >>> +	/* Make sure the chip is on */
> >>> +	tsl2x7x_chip_on(indio_dev);
> >>> +
> >>> +	ret = iio_device_register(indio_dev);
> >>> +	if (ret) {
> >>> +		dev_err(&clientp->dev,
> >>> +			"%s: iio registration failed\n", __func__);
> >>> +		goto fail3;
> >>> +	}
> >>> +
> >>> +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
> >>> +
> >>> +	return 0;
> >>> +
> >>> +fail3:
> >>> +	if (clientp->irq)
> >>> +		free_irq(clientp->irq, indio_dev);
> >>> +fail2:
> >>> +	iio_free_device(indio_dev);
> >>> +fail1:
> >>> +	kfree(chip);
> >> double free of chip.  It's allocated and managed by the
> >> iio_allocate_device and iio_free_device calls.
> >> For that matter, most of the goto fail2's need the iio_free_device call..
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_suspend(struct device *dev)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int ret = 0;
> >>> +
> >>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> >>> +		ret = tsl2x7x_chip_off(indio_dev);
> >>> +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
> >>> +	}
> >>> +
> >>> +	if (chip->pdata&&  chip->pdata->platform_power) {
> >>> +		pm_message_t pmm = {PM_EVENT_SUSPEND};
> >>> +		chip->pdata->platform_power(dev, pmm);
> >>> +	}
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int tsl2x7x_resume(struct device *dev)
> >>> +{
> >>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> >>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> >>> +	int ret = 0;
> >>> +
> >>> +	if (chip->pdata&&  chip->pdata->platform_power) {
> >>> +		pm_message_t pmm = {PM_EVENT_RESUME};
> >>> +		chip->pdata->platform_power(dev, pmm);
> >>> +	}
> >>> +
> >>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
> >>> +		ret = tsl2x7x_chip_on(indio_dev);
> >>> +
> >>> +	return ret;
> >>> +}
> >>> +
> >>> +static int __devexit tsl2x7x_remove(struct i2c_client *client)
> >>> +{
> >>> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> >>> +	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
> >> These shouldn't both be true....
> >> would expect an iio_unregister_device call here.
> >>> +
> >>> +	tsl2x7x_chip_off(indio_dev);
> >>> +
> >>> +	if (client->irq)
> >>> +		free_irq(client->irq, chip->client->name);
> >>> +
> >>> +	iio_free_device(indio_dev);
> >>> +
> >>> +	return 0;
> >>> +}
> >>> +
> >>> +static struct i2c_device_id tsl2x7x_idtable[] = {
> >>> +	{ "tsl2571", tsl2571 },
> >>> +	{ "tsl2671", tsl2671 },
> >>> +	{ "tmd2671", tmd2671 },
> >>> +	{ "tsl2771", tsl2771 },
> >>> +	{ "tmd2771", tmd2771 },
> >>> +	{ "tsl2572", tsl2572 },
> >>> +	{ "tsl2672", tsl2672 },
> >>> +	{ "tmd2672", tmd2672 },
> >>> +	{ "tsl2772", tsl2772 },
> >>> +	{ "tmd2772", tmd2772 },
> >>> +	{}
> >>> +};
> >>> +
> >>> +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
> >>> +
> >>> +static const struct dev_pm_ops tsl2x7x_pm_ops = {
> >>> +	.suspend = tsl2x7x_suspend,
> >>> +	.resume  = tsl2x7x_resume,
> >>> +};
> >>> +
> >>> +/* Driver definition */
> >>> +static struct i2c_driver tsl2x7x_driver = {
> >>> +	.driver = {
> >>> +		.name = "tsl2x7x",
> >>> +		.pm =&tsl2x7x_pm_ops,
> >>> +	},
> >>> +	.id_table = tsl2x7x_idtable,
> >>> +	.probe = tsl2x7x_probe,
> >>> +	.remove = __devexit_p(tsl2x7x_remove),
> >>> +};
> >>> +
> >>> +module_i2c_driver(tsl2x7x_driver);
> >>> +
> >>> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
> >>> +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor
> >> driver");
> >>> +MODULE_LICENSE("GPL");
> >>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.h
> >> b/drivers/staging/iio/light/tsl2x7x_core.h
> >>> new file mode 100644
> >>> index 0000000..663e846
> >>> --- /dev/null
> >>> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
> >> why not just tsl2x7x.h?
> >>> @@ -0,0 +1,75 @@
> >>> +/*
> >>> + * Device driver for monitoring ambient light intensity (lux)
> >>> + * and proximity (prox) within the TAOS TSL2X7X family of devices.
> >>> + *
> >>> + * Copyright (c) 2012, TAOS Corporation.
> >>> + *
> >>> + * This program is free software; you can redistribute it and/or modify
> >>> + * it under the terms of the GNU General Public License as published by
> >>> + * the Free Software Foundation; 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.,
> >>> + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
> >>> + */
> >>> +
> >>> +#ifndef __TSL2X7X_H
> >>> +#define __TSL2X7X_H
> >>> +#include<linux/pm.h>
> >>> +
> >>> +/* Max number of segments allowable in LUX table */
> >>> +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
> >>> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) *
> >> TSL2X7X_MAX_LUX_TABLE_SIZE)
> >>> +
> >>> +struct iio_dev;
> >>
> >> Why the forward declaration of iio_chan_spec?
> >>> +struct iio_chan_spec;
> >>> +
> >>> +struct tsl2x7x_lux {
> >>> +	unsigned int ratio;
> >>> +	unsigned int ch0;
> >>> +	unsigned int ch1;
> >>> +};
> >>> +
> >>> +/* Refer to tsl2x7x_default_settings for member desc. */
> >>> +struct tsl2x7x_settings {
> >>> +	int als_time;
> >>> +	int als_gain;
> >>> +	int als_gain_trim;
> >>> +	int wait_time;
> >>> +	int prx_time;
> >>> +	int prox_gain;
> >>> +	int prox_config;
> >>> +	int als_cal_target;
> >>> +	u8  interrupts_en;
> >>> +	u8  als_persistence;
> >>> +	int als_thresh_low;
> >>> +	int als_thresh_high;
> >>> +	int prox_thres_low;
> >>> +	int prox_thres_high;
> >>> +	int prox_pulse_count;
> >>> +	int prox_max_samples_cal;
> >>> +};
> >>> +
> >>> +/* struct tsl2x7x_platform_data -
> >>> + * Platform unique glass and defaults
> >>> + * Platform PM functions. */
> >> Would prefer this to be in kernel doc.
> >>> +struct tsl2X7X_platform_data {
> >>> +	/* Suspend/resume platform cb */
> >>> +	int (*platform_power)(struct device *dev, pm_message_t);
> >>> +	/* The following callback gets called when the device is powered on */
> >>> +	int (*power_on)      (struct iio_dev *indio_dev);
> >>> +	/* The following callback gets called when the device is powered off */
> >>> +	int (*power_off)     (struct i2c_client *dev);
> >>> +	/* These are the device specific glass coefficents used to
> >>> +	 * calculate Lux */
> >>> +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
> >>> +	struct tsl2x7x_settings *platform_default_settings;
> >>> +};
> >>> +
> >>> +#endif /* __TSL2X7X_H */
> >>> --
> >>> 1.7.4.1
> >>>
> >

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

^ permalink raw reply	[flat|nested] 8+ messages in thread

* RE: [PATCH V4] TAOS tsl2x7x
@ 2012-03-28 23:29         ` Jon Brenner
  0 siblings, 0 replies; 8+ messages in thread
From: Jon Brenner @ 2012-03-28 23:29 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, Linux Kernel

SGVsbG8gSm9uYXRoYW4sDQpVbmxlc3MgdGhlIDI1NjMgcGF0Y2ggY2FuIHdhaXQsIEknbGwgbGVh
dmUgaXQgZm9yIHlvdS4NCkkgbmVlZCB0byBnZXQgdGhlIDJ4N3ggZmluaXNoZWQgYXNhcCAtIGJl
Y2F1c2UgSSBoYXZlIGEgeWV0IGFub3RoZXIgZHJpdmVyIHRvIGRvIC0gaW1tZWRpYXRlbHkgZm9s
bG93aW5nIGl0Lg0KSWYgaXQgY2FuIHdhaXQgdGlsbCBsYXRlciAoMjU2MyBkcml2ZXIgcGF0Y2gp
LCBJJ2xsIGJlIGdsYWQgdG8gZG8gaXQuDQoNClRoYXQgYnJpbmdzIG1lIGJhY2sgdG8gc29tZSBi
YXNpYyBxdWVzdGlvbnMuDQoxLiBDaGFubmVsIDAgLSBmb3IgaW5faWxsdW1pbmFuY2UwX2lucHV0
IC0gY29ycmVjdD8NCjIuIFdoYXQgY2hhbm5lbCBmb3IgaW5faW50ZW5zaXR5IChkYXRhIGNoYW5u
ZWwgMCBkYXRhKT8NCjMgIFdoYXQgY2hhbm5lbCBmb3IgaW5faW50ZW5zaXR5IChkYXRhIGNoYW5u
ZWwgMSBkYXRhKT8NCjQgV2hhdCBjaGFubmVsIGZvciBpbl9wcm94aW1pdHk/X3JhdyA/DQoNCldo
YXQgZG8geW91IHRoaW5rIGFib3V0IHRoZSBmb2xsb3dpbmcgY2hhbm5lbCAndGFibGUgZGVmJyBm
b3IgdGhlIGRldmljZSB0aGF0IGhhcyBBTFMgYW5kIFByb3g/DQpQbGVhc2UgcmVhZCBzdGF0ZW1l
bnRzIGZvbGxvd2luZyB0aGlzIHNuaXBwZXQuDQoNCltBTFNQUlhdID0gew0KCS5jaGFubmVsID0g
ew0KCQkJew0KCQkJLnR5cGUgPSBJSU9fTElHSFQsDQoJCQkuaW5kZXhlZCA9IDEsDQoJCQkuY2hh
bm5lbCA9IDAsDQoJCQkucHJvY2Vzc2VkX3ZhbCA9IDEsDQoJCQl9LCB7DQoJCQkudHlwZSA9IElJ
T19JTlRFTlNJVFksDQoJCQkuaW5kZXhlZCA9IDEsDQoJCQkuaW5mb19tYXNrID0gSUlPX0NIQU5f
SU5GT19DQUxJQlNDQUxFX1NFUEFSQVRFX0JJVCB8DQoJCQkJCUlJT19DSEFOX0lORk9fQ0FMSUJC
SUFTX1NFUEFSQVRFX0JJVCwNCgkJCS5ldmVudF9tYXNrID0gKElJT19FVl9CSVQoSUlPX0VWX1RZ
UEVfVEhSRVNILA0KCQkJCQlJSU9fRVZfRElSX1JJU0lORykgfA0KCQkJCQkgSUlPX0VWX0JJVChJ
SU9fRVZfVFlQRV9USFJFU0gsDQoJCQkJCUlJT19FVl9ESVJfRkFMTElORykpLA0KCQkJfSwgew0K
CQkJCQkudHlwZSA9IElJT19JTlRFTlNJVFksDQoJCQkJCS5pbmRleGVkID0gMSwNCgkJCQkJLmNo
YW5uZWwgPSAxLA0KCQkJfSwgew0KCQkJLnR5cGUgPSBJSU9fUFJPWElNSVRZLA0KCQkJLmluZGV4
ZWQgPSAxLA0KCQkJLmV2ZW50X21hc2sgPSBUU0wyWDdYX0VWRU5UX01BU0sNCgkJCX0sDQoJCX0s
DQoJLm51bV9jaGFubmVscyA9IDQsDQoJLmluZm8gPSAmdHNsMlg3WF9kZXZpY2VfaW5mb1tBTFNQ
UlhdLA0KCX0sDQoNCk5vdGljZSBJIGxlZnQgb3V0IHRoZSBtb2RpZmllcnM/DQpUaGlzIHlpZWxk
czoNCiAJaW5faW50ZW5zaXR5MF9yYXcJKGdpdmVzIHVzIGNoYW5uZWwgMCByYXcgZGF0YSkNCglp
bl9pbnRlbnNpdHkxX3JhdwkoZ2l2ZXMgdXMgY2hhbm5lbCAxIHJhdyBkYXRhKQ0KCWluX3Byb3hp
bWl0eTBfcmF3ICAgICAgICAgICAoZ2l2ZXMgdXMgcHJveCBBRCByYXcgZGF0YSkNCmFzIHdlbGwg
YXMgDQoJaW5faW50ZW5zaXR5MF9jYWxpYmJpYXMNCglpbl9pbnRlbnNpdHkwX2NhbGlic2NhbGUN
CmFuZCBvZiBjb3Vyc2UNCglpbl9pbGx1bWluYW5jZTBfaW5wdXQNCg0KV2l0aCB0aGUgbW9kaWZp
ZXJzIGxlZnQgb3V0LCBpc24ndCB0aGUgJ19yYXcnIGFuZCBpbmRpY2F0aW9uIG9mIGEgcmF3IEFE
IHZhbHVlIChBRCBjb3VudHMpPw0KV2UgVEFPUyBkb24ndCB1c2UgdGhlIHRlcm0gIklSIiBpbiBv
dXIgY2hhbm5lbCBkZXNpZ25hdGlvbnMgLSB0aHVzIDAgYW5kIDEgbW9yZSBjbG9zZWx5IG1hdGNo
IHRoZSBkYXRhIHNoZWV0cy4NCihBbm90aGVyIHJlYXNvbiB3aHkgSSBkb24ndCB3YW50IHRvIHVz
ZSB0aGUgbW9kaWZpZXJzIG9mIEJPVEggYW5kIElSKQ0KDQoobm90ZSBhbHNvIHRoYXQgLm51bV9j
aGFubmVscyBpcyByZWFsbHkgbnVtYmVyIG9mIHRhYmxlIGVsZW1lbnRzIC0gSSBjYW4gY2hhbmdl
IHRoYXQgbmFtZSkNCg0KV2hhdCBkbyB5b3UgdGhpbms/DQoNCkpvbg0KDQo+IC0tLS0tT3JpZ2lu
YWwgTWVzc2FnZS0tLS0tDQo+IEZyb206IEpvbmF0aGFuIENhbWVyb24gW21haWx0bzpqaWMyM0Br
ZXJuZWwub3JnXQ0KPiBTZW50OiBXZWRuZXNkYXksIE1hcmNoIDI4LCAyMDEyIDEyOjUyIFBNDQo+
IFRvOiBKb24gQnJlbm5lcg0KPiBDYzogSm9uYXRoYW4gQ2FtZXJvbjsgbGludXgtaWlvOyBMaW51
eCBLZXJuZWwNCj4gU3ViamVjdDogUmU6IFtQQVRDSCBWNF0gVEFPUyB0c2wyeDd4DQo+IA0KPiBP
biAwMy8yNy8yMDEyIDA4OjU4IFBNLCBKb24gQnJlbm5lciB3cm90ZToNCj4gPiBIZWxsbyBKb25h
dGhhbiwNCj4gPiBTdGlsbCBhIGxpdHRsZSBjb25mdXNlZCAoYW5kIHN0dWNrKSBoZXJlLg0KPiA+
IFVzaW5nIHlvdXIgY29kZSBmcm9tIHRoZSB0c2wyNTYzOg0KPiA+IDEuIFRoZSBjYXNlIGZvciBJ
SU9fTElHSFQgYXBwZWFycyB0byByZXR1cm4gdGhlIGNvbXB1dGVkIExVWC4NCj4gPiBZZXQgeW91
IGhhdmUgbm8gIi5wcm9jZXNzZWRfdmFsID0gMTogaW4geW91ciBjaGFubmVsIHRhYmxlIC0gc28g
d2hlcmUgaW4NCj4gaW5faWxsdW1pbmFuY2UwX2lucHV0IChha2EgbHV4KSAgY29taW5nIGZyb20/
DQo+IEluZGVlZC4gVGhhdCdzIGJ1ZyBudW1iZXIgMS4uLg0KPiA+DQo+ID4gMi4gVGhlIGNhc2Ug
Zm9yIElJT19JTlRFTlNJVFkgbG9va3MgZm9yICdjaGFuLT5jaGFubmVsJyB0byBkZXRlcm1pbmUg
d2hlbiB0bw0KPiBwcmVzZW50ICdjaGlwLT5kYXRhMCcgb3IgJ2NoaXAtPmRhdGExJyAtDQo+ID4g
QnV0IGl0IGFwcGVhcnMgdGhhdCAnY2hhbi0+Y2hhbm5lbCcgd2lsbCBhbHdheXMgYmUgMCBhcyAn
LmNoYW5uZWwgPScgaXNuJ3QgZGVmaW5lZA0KPiBpbiB0aGUgdGFibGU/DQo+ID4gU28gaG93IGNh
biB5b3UgZXZlciBnZXQgY2hpcC0+ZGF0YTAxPw0KPiA+DQo+IEdhaCwgdGhpcyBkcml2ZXIgY2xl
YXJseSBuZWVkcyBhbm90aGVyIGxvb2suICBZb3UgYXJlIHF1aXRlIGNvcnJlY3QsDQo+IHRoYXQg
aXMgYSBidWcgYXMgd2VsbCBhcyBpdCB3aWxsIGFsd2F5cyByZXR1cm4gdGhlIGZpcnN0IGNoYW5u
ZWwuDQo+IA0KPiBDbGVhcmx5IGEgbGl0dGxlIGJpdCBvZiBjb2RlIHJvdCBoYXMgb2NjdXJlZCBo
ZXJlLiAgT29wcy4NCj4gRG8geW91IHdhbnQgdG8gZG8gdGhlIHBhdGNoLCBvciBzaGFsbCBJICh3
aXRoIGEgcmVwb3J0ZWQgYnkNCj4gb2YgY291cnNlISkuDQo+IA0KPiBKb25hdGhhbg0KPiA+IEZv
ciBleGFtcGxlLCB0c2wyNTYzIGNvZGUgcG9ydGlvbiBmb2xsb3dzLg0KPiA+DQo+ID4gPFNuaXA+
DQo+ID4gc3RhdGljIGludCB0c2wyNTYzX3JlYWRfcmF3KHN0cnVjdCBpaW9fZGV2ICppbmRpb19k
ZXYsDQo+ID4gCQkJICAgIHN0cnVjdCBpaW9fY2hhbl9zcGVjIGNvbnN0ICpjaGFuLA0KPiA+IAkJ
CSAgICBpbnQgKnZhbCwNCj4gPiAJCQkgICAgaW50ICp2YWwyLA0KPiA+IAkJCSAgICBsb25nIG0p
DQo+ID4gew0KPiA+IAlpbnQgcmV0ID0gLUVJTlZBTDsNCj4gPiAJdTMyIGNhbGliMCwgY2FsaWIx
Ow0KPiA+IAlzdHJ1Y3QgdHNsMjU2M19jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsN
Cj4gPg0KPiA+IAltdXRleF9sb2NrKCZjaGlwLT5sb2NrKTsNCj4gPiAJc3dpdGNoIChtKSB7DQo+
ID4gCWNhc2UgMDoNCj4gPiAJCXN3aXRjaCAoY2hhbi0+dHlwZSkgew0KPiA+IAkJY2FzZSBJSU9f
TElHSFQ6DQo+ID4gCQkJcmV0ID0gdHNsMjU2M19nZXRfYWRjKGNoaXApOw0KPiA+IAkJCWlmIChy
ZXQpDQo+ID4gCQkJCWdvdG8gZXJyb3JfcmV0Ow0KPiA+IAkJCWNhbGliMCA9IGNhbGliX2FkYyhj
aGlwLT5kYXRhMCwgY2hpcC0+Y2FsaWIwKSAqDQo+ID4gCQkJCWNoaXAtPmNvdmVyX2NvbXBfZ2Fp
bjsNCj4gPiAJCQljYWxpYjEgPSBjYWxpYl9hZGMoY2hpcC0+ZGF0YTEsIGNoaXAtPmNhbGliMSkg
Kg0KPiA+IAkJCQljaGlwLT5jb3Zlcl9jb21wX2dhaW47DQo+ID4gCQkJKnZhbCA9IGFkY190b19s
dXgoY2FsaWIwLCBjYWxpYjEpOw0KPiA+IAkJCXJldCA9IElJT19WQUxfSU5UOw0KPiA+IAkJCWJy
ZWFrOw0KPiA+IAkJY2FzZSBJSU9fSU5URU5TSVRZOg0KPiA+IAkJCXJldCA9IHRzbDI1NjNfZ2V0
X2FkYyhjaGlwKTsNCj4gPiAJCQlpZiAocmV0KQ0KPiA+IAkJCQlnb3RvIGVycm9yX3JldDsNCj4g
PiAJCQlpZiAoY2hhbi0+Y2hhbm5lbCA9PSAwKQ0KPiA+IAkJCQkqdmFsID0gY2hpcC0+ZGF0YTA7
DQo+ID4gCQkJZWxzZQ0KPiA+IAkJCQkqdmFsID0gY2hpcC0+ZGF0YTE7DQo+ID4gCQkJcmV0ID0g
SUlPX1ZBTF9JTlQ7DQo+ID4gCQkJYnJlYWs7DQo+ID4gCQlkZWZhdWx0Og0KPiA+IAkJCWJyZWFr
Ow0KPiA+IAkJfQ0KPiA+IAkJYnJlYWs7DQo+ID4NCj4gPiAJY2FzZSBJSU9fQ0hBTl9JTkZPX0NB
TElCU0NBTEU6DQo+ID4gCQlpZiAoY2hhbi0+Y2hhbm5lbCA9PSAwKQ0KPiA+IAkJCSp2YWwgPSBj
YWxpYl90b19zeXNmcyhjaGlwLT5jYWxpYjApOw0KPiA+IAkJZWxzZQ0KPiA+IAkJCSp2YWwgPSBj
YWxpYl90b19zeXNmcyhjaGlwLT5jYWxpYjEpOw0KPiA+IAkJcmV0ID0gSUlPX1ZBTF9JTlQ7DQo+
ID4gCQlicmVhazsNCj4gPiAJZGVmYXVsdDoNCj4gPiAJCXJldCA9IC1FSU5WQUw7DQo+ID4gCQln
b3RvIGVycm9yX3JldDsNCj4gPiAJfQ0KPiA+DQo+ID4gZXJyb3JfcmV0Og0KPiA+IAltdXRleF91
bmxvY2soJmNoaXAtPmxvY2spOw0KPiA+IAlyZXR1cm4gcmV0Ow0KPiA+IH0NCj4gPg0KPiA+IHN0
YXRpYyBjb25zdCBzdHJ1Y3QgaWlvX2NoYW5fc3BlYyB0c2wyNTYzX2NoYW5uZWxzW10gPSB7DQo+
ID4gCXsNCj4gPiAJCS50eXBlID0gSUlPX0xJR0hULA0KPiA+IAkJLmluZGV4ZWQgPSAxLA0KPiA+
IAkJLmNoYW5uZWwgPSAwLA0KPiA+IAl9LCB7DQo+ID4gCQkudHlwZSA9IElJT19JTlRFTlNJVFks
DQo+ID4gCQkubW9kaWZpZWQgPSAxLA0KPiA+IAkJLmNoYW5uZWwyID0gSUlPX01PRF9MSUdIVF9C
T1RILA0KPiA+IAkJLmluZm9fbWFzayA9IElJT19DSEFOX0lORk9fQ0FMSUJTQ0FMRV9TRVBBUkFU
RV9CSVQsDQo+ID4gCQkuZXZlbnRfbWFzayA9IChJSU9fRVZfQklUKElJT19FVl9UWVBFX1RIUkVT
SCwNCj4gPiAJCQkJCSAgSUlPX0VWX0RJUl9SSVNJTkcpIHwNCj4gPiAJCQkgICAgICAgSUlPX0VW
X0JJVChJSU9fRVZfVFlQRV9USFJFU0gsDQo+ID4gCQkJCQkgIElJT19FVl9ESVJfRkFMTElORykp
LA0KPiA+IAl9LCB7DQo+ID4gCQkudHlwZSA9IElJT19JTlRFTlNJVFksDQo+ID4gCQkubW9kaWZp
ZWQgPSAxLA0KPiA+IAkJLmNoYW5uZWwyID0gSUlPX01PRF9MSUdIVF9JUiwNCj4gPiAJCS5pbmZv
X21hc2sgPSBJSU9fQ0hBTl9JTkZPX0NBTElCU0NBTEVfU0VQQVJBVEVfQklULA0KPiA+IAl9DQo+
ID4gfTsNCj4gPg0KPiA+DQo+ID4gV2hhdCBhbSBJIG5vdCBzZWVpbmcgaGVyZT8NCj4gPg0KPiA+
IEpvbg0KPiA+DQo+ID4+IC0tLS0tT3JpZ2luYWwgTWVzc2FnZS0tLS0tDQo+ID4+IEZyb206IEpv
bmF0aGFuIENhbWVyb24gW21haWx0bzpqaWMyM0BjYW0uYWMudWtdDQo+ID4+IFNlbnQ6IEZyaWRh
eSwgTWFyY2ggMjMsIDIwMTIgODo1NCBBTQ0KPiA+PiBUbzogSm9uIEJyZW5uZXINCj4gPj4gQ2M6
IGxpbnV4LWlpbzsgTGludXggS2VybmVsDQo+ID4+IFN1YmplY3Q6IFJlOiBbUEFUQ0ggVjRdIFRB
T1MgdHNsMng3eA0KPiA+Pg0KPiA+PiBPbiAzLzIxLzIwMTIgNDoxNiBQTSwgSm9uIEJyZW5uZXIg
d3JvdGU6DQo+ID4+PiBUQU9TIGRldmljZSBkcml2ZXIgKHZlcnNpb24gNCkgZm9yIHRoZSB0c2wv
dG1kIDI3NzEgYW5kIDI3NzIgZGV2aWNlIGZhbWlsaWVzDQo+ID4+IChpbmMuIGFsbCB2YXJpYW50
cykuDQo+ID4+IE1vc3RseSBsb29raW5nIGdvb2QuLi4NCj4gPj4NCj4gPj4gQ291cGxlIG9mIGlz
c3VlcyByZW1haW5pbmcuDQo+ID4+ICogV2h5IGhhdmUgcHJvY2Vzc2VkIGFuZCByYXcgYWNjZXNz
ZXMgdG8gdGhlIHNhbWUgY2hhbm5lbHM/ICBUaGF0IGlzDQo+ID4+IGRlZmluaXRlbHkgbm90IHRo
ZSBpbnRlbnQuDQo+ID4+IEVpdGhlciBpdCdzIHdvcnRoIHByb2Nlc3NpbmcgdGhlc2UgcmF3IGFk
YyBjaGFubmVscyBpbiBrZXJuZWwsIG9yIG5vdC4NCj4gPj4gSWYgaXQgaXNuJ3QgeW91IGNlcnRh
aW5seSBzaG91bGRuJ3QNCj4gPj4gYmUgbXVuZ2luZyB0b2dldGhlciB0aGUgdHdvIGFkYydzIGlu
dG8gYSBzaW5nbGUgdmFsdWUuDQo+ID4+DQo+ID4+IFNvIHlvdSBoYXZlIGRvbmUgdGhpcyBmb3Ig
dHdvIHJlYXNvbnMuLi4NCj4gPj4gKiBGb3IgbGlnaHQgc2Vuc29ycyBpdCB3YXMgdG8gYWxsb3cg
dGhlIHByb2Nlc3NlZCBpbGx1bWluYW5jZSB2YWx1ZSBhbmQNCj4gPj4gYWxzbyB0aGUgcmF3IGFk
YyB2YWx1ZXMuDQo+ID4+IFByZXZpb3VzbHkgd2UgaGF2ZSBkb25lIHRoaXMgYnkgaGF2aW5nIElJ
T19MSUdIVCAoYW5kIGhlbmNlIGlsbHVtaW5hbmNlKQ0KPiA+PiBmb3IgdGhlIHByb2Nlc3NlZCBv
bmUNCj4gPj4gYW5kIG1hcmtpbmcgdGhlIG90aGVyIHR3byBhcyBJSU9fSU5URU5TSVRZIHdpdGgg
bW9kaWZpZXJzIGZvciB0aGUNCj4gPj4gZnJlcXVlbmN5IHJhbmdlIHRoZXkgY292ZXIuLi4NCj4g
Pj4gKiBGb3IgcHJveGltaXR5IG9uZSBpcyB0aGUgcmF3IHJlYWRpbmcgKGZpbmUpLCB0aGUgb3Ro
ZXIgaXMgYSBtZWFucyBvZg0KPiA+PiBnZXR0aW5nIGF0IHRoZSB0aHJlc2hvbGQgZXZlbnQNCj4g
Pj4gaWYgaW50ZXJydXB0cyBhcmUgbm90IHN1cHBvcnRlZC4gIEl0IGlzIGRvbmUgYnkgYSBzb2Z0
d2FyZSBjb21wYXJpc29uIG9mDQo+ID4+IHRoZSB0aHJlc2hvbGQgYW5kIHRoZSByYXcNCj4gPj4g
cmVhZGluZy4gIFRoaXMgc2hvdWxkIG5vdCBiZSBpbiBkcml2ZXIgYXMgaWYgdGhlIGZ1bmN0aW9u
YWxpdHkgaXMNCj4gPj4gZGVzaXJlZCwgaXQgc2hvdWxkIGJlIGRvbmUgaW4gdXNlcnNwYWNlLg0K
PiA+Pg0KPiA+PiBWYXJpb3VzIG90aGVyIG1pbm9yIGJpdHMgbGlrZSBlcnJvciBwYXRocyB0aGF0
IGRvbid0IGNsZWFuIHVwIGNvbW1lbnRlZA0KPiA+PiBpbmxpbmUuDQo+ID4+DQo+ID4+DQo+ID4+
Pg0KPiA+Pj4gU2lnbmVkLW9mZi1ieTogSm9uIEJyZW5uZXI8amJyZW5uZXJAdGFvc2luYy5jb20+
DQo+ID4+PiAtLS0NCj4gPj4+ICAgLi4uL2xpZ2h0L3N5c2ZzLWJ1cy1paW8tbGlnaHQtdHNsMjU4
MyAgICAgICAgICAgICAgfCAgICA2ICsNCj4gPj4+ICAgLi4uL2xpZ2h0L3N5c2ZzLWJ1cy1paW8t
bGlnaHQtdHNsMng3eCAgICAgICAgICAgICAgfCAgIDIwICsNCj4gPj4+ICAgZHJpdmVycy9zdGFn
aW5nL2lpby9Eb2N1bWVudGF0aW9uL3N5c2ZzLWJ1cy1paW8gICAgfCAgICA3ICsNCj4gPj4+ICAg
Li4uL3N0YWdpbmcvaWlvL0RvY3VtZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodCAgfCAgICA4
ICstDQo+ID4+PiAgIC4uLi9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1idXMtaWlvLWxpZ2h0LXRz
bDI1ODMgIHwgICAyMCAtDQo+ID4+PiAgIGRyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvS2NvbmZp
ZyAgICAgICAgICAgICAgICAgIHwgICAgOCArDQo+ID4+PiAgIGRyaXZlcnMvc3RhZ2luZy9paW8v
bGlnaHQvTWFrZWZpbGUgICAgICAgICAgICAgICAgIHwgICAgMSArDQo+ID4+PiAgIGRyaXZlcnMv
c3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eF9jb3JlLmMgICAgICAgICAgIHwgMTkxMQ0KPiArKysr
KysrKysrKysrKysrKysrKw0KPiA+Pj4gICBkcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4
N3hfY29yZS5oICAgICAgICAgICB8ICAgNzUgKw0KPiA+Pj4gICA5IGZpbGVzIGNoYW5nZWQsIDIw
MzIgaW5zZXJ0aW9ucygrKSwgMjQgZGVsZXRpb25zKC0pDQo+ID4+Pg0KPiA+Pj4gZGlmZiAtLWdp
dCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9saWdodC9zeXNmcy1idXMtaWlv
LWxpZ2h0LQ0KPiB0c2wyNTgzDQo+ID4+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0
aW9uL2xpZ2h0L3N5c2ZzLWJ1cy1paW8tbGlnaHQtdHNsMjU4Mw0KPiA+Pj4gbmV3IGZpbGUgbW9k
ZSAxMDA2NDQNCj4gPj4+IGluZGV4IDAwMDAwMDAuLjhmMmEwMzgNCj4gPj4+IC0tLSAvZGV2L251
bGwNCj4gPj4+ICsrKyBiL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9saWdodC9z
eXNmcy1idXMtaWlvLWxpZ2h0LXRzbDI1ODMNCj4gPj4+IEBAIC0wLDAgKzEsNiBAQA0KPiA+Pj4g
K1doYXQ6CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0vaWxsdW1pbmFuY2UwX2NhbGli
cmF0ZQ0KPiA+Pj4gK0tlcm5lbFZlcnNpb246CTIuNi4zNw0KPiA+Pj4gK0NvbnRhY3Q6CWxpbnV4
LWlpb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPj4+ICtEZXNjcmlwdGlvbjoNCj4gPj4+ICsJCVRoaXMg
cHJvcGVydHkgY2F1c2VzIGFuIGludGVybmFsIGNhbGlicmF0aW9uIG9mIHRoZSBhbHMgZ2FpbiB0
cmltDQo+ID4+PiArCQl2YWx1ZSB3aGljaCBpcyBsYXRlciB1c2VkIGluIGNhbGN1bGF0aW5nIGls
bHVtaW5hbmNlIGluIGx1eC4NCj4gPj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcvaWlv
L0RvY3VtZW50YXRpb24vbGlnaHQvc3lzZnMtYnVzLWlpby1saWdodC0NCj4gdHNsMng3eA0KPiA+
PiBiL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9saWdodC9zeXNmcy1idXMtaWlv
LWxpZ2h0LXRzbDJ4N3gNCj4gPj4+IG5ldyBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4+PiBpbmRleCAw
MDAwMDAwLi5jY2VhZGFlDQo+ID4+PiAtLS0gL2Rldi9udWxsDQo+ID4+PiArKysgYi9kcml2ZXJz
L3N0YWdpbmcvaWlvL0RvY3VtZW50YXRpb24vbGlnaHQvc3lzZnMtYnVzLWlpby1saWdodC10c2wy
eDd4DQo+ID4+PiBAQCAtMCwwICsxLDIwIEBADQo+ID4+PiArV2hhdDoJCS9zeXMvYnVzL2lpby9k
ZXZpY2VzL2RldmljZVtuXS9pbGx1bWluYW5jZTBfY2FsaWJyYXRlDQo+ID4+PiArS2VybmVsVmVy
c2lvbjoJMi42LjM3DQo+ID4+PiArQ29udGFjdDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0K
PiA+Pj4gK0Rlc2NyaXB0aW9uOg0KPiA+Pj4gKwkJVGhpcyBwcm9wZXJ0eSBjYXVzZXMgYW4gaW50
ZXJuYWwgY2FsaWJyYXRpb24gb2YgdGhlIGFscyBnYWluIHRyaW0NCj4gPj4+ICsJCXZhbHVlIHdo
aWNoIGlzIGxhdGVyIHVzZWQgaW4gY2FsY3VsYXRpbmcgaWxsdW1pbmFuY2UgaW4gbHV4Lg0KPiA+
Pj4gKw0KPiA+Pj4gK1doYXQ6CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0vaWxsdW1p
bmFuY2UwX2JvdGhfcmF3DQo+ID4+PiArS2VybmVsVmVyc2lvbjoJMy4zLXJjMQ0KPiA+Pj4gK0Nv
bnRhY3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcNCj4gPj4+ICtEZXNjcmlwdGlvbjoNCj4g
Pj4+ICsJCVNpbXVsdGFpbmlvdXMgQUxTIGNoYW5uZWwgZGF0YS4NCj4gPj4gVGhhdCB3YXNuJ3Qg
dGhlIGludGVudCBvZiAnYm90aCcgYXQgYWxsLiAgKCsgdHlwbykuICBJdCBtZWFucyBhIHJhdw0K
PiA+PiByZWFkaW5nIGZyb20gYSBkaW9kIHRoYXQNCj4gPj4gZGV0ZWN0cyB0aGUgJ3N1bScgb2Yg
aW5mcmFyZWQgYW5kIHZpc2libGUuDQo+ID4+PiArDQo+ID4+PiArV2hhdDoJCS9zeXMvYnVzL2lp
by9kZXZpY2VzL2RldmljZVtuXS9wcm94aW1pdHlfY2FsaWJyYXRlDQo+ID4+PiArS2VybmVsVmVy
c2lvbjoJMy4zLXJjMQ0KPiA+Pj4gK0NvbnRhY3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcN
Cj4gPj4+ICtEZXNjcmlwdGlvbjoNCj4gPj4+ICsJCUNhdXNlcyBhbiByZWNhbGN1bGF0aW9uIGFu
ZCBhZGp1c3RtZW50IHRvIHRoZQ0KPiA+Pj4gKwkJcHJveGltaXR5X3RocmVzaF9yaXNpbmdfdmFs
dWUuDQo+ID4+PiArDQo+ID4+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1
bWVudGF0aW9uL3N5c2ZzLWJ1cy1paW8NCj4gPj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3Vt
ZW50YXRpb24vc3lzZnMtYnVzLWlpbw0KPiA+Pj4gaW5kZXggNDZhOTk1ZC4uNWIyYjVkMyAxMDA2
NDQNCj4gPj4+IC0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9zeXNmcy1i
dXMtaWlvDQo+ID4+PiArKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3VtZW50YXRpb24vc3lz
ZnMtYnVzLWlpbw0KPiA+Pj4gQEAgLTI1OCw2ICsyNTgsOCBAQCBXaGF0DQo+ID4+IAkvc3lzL2J1
cy9paW8vZGV2aWNlcy9paW86ZGV2aWNlWC9pbl9hY2NlbF96X2NhbGlic2NhbGUNCj4gPj4+ICAg
V2hhdA0KPiA+PiAJL3N5cy9idXMvaWlvL2RldmljZXMvaWlvOmRldmljZVgvaW5fYW5nbHZlbF94
X2NhbGlic2NhbGUNCj4gPj4+ICAgV2hhdA0KPiA+PiAJL3N5cy9idXMvaWlvL2RldmljZXMvaWlv
OmRldmljZVgvaW5fYW5nbHZlbF95X2NhbGlic2NhbGUNCj4gPj4+ICAgV2hhdA0KPiA+PiAJL3N5
cy9idXMvaWlvL2RldmljZXMvaWlvOmRldmljZVgvaW5fYW5nbHZlbF96X2NhbGlic2NhbGUNCj4g
Pj4+ICt3aGF0DQo+ID4+IAkvc3lzL2J1cy9paW8vZGV2aWNlcy9paW86ZGV2aWNlWC9pbGx1bWlu
YW5jZTBfY2FsaWJzY2FsZQ0KPiA+Pj4gK3doYXQJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2lpbzpk
ZXZpY2VYL3Byb3hpbWl0eV9jYWxpYnNjYWxlDQo+ID4+PiAgIEtlcm5lbFZlcnNpb246CTIuNi4z
NQ0KPiA+Pj4gICBDb250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4+PiAgIERl
c2NyaXB0aW9uOg0KPiA+Pj4gQEAgLTQ1Nyw2ICs0NTksMTAgQEAgV2hhdDoNCj4gPj4gCS9zeXMv
Li4uL2V2ZW50cy9pbl92b2x0YWdlWV9yYXdfdGhyZXNoX2ZhbGxpbmdfdmFsdWUNCj4gPj4+ICAg
V2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9pbl92b2x0YWdlWV9yYXdfdGhyZXNoX2ZhbGxpbmdfdmFs
dWUNCj4gPj4+ICAgV2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9pbl90ZW1wWV9yYXdfdGhyZXNoX2Zh
bGxpbmdfdmFsdWUNCj4gPj4+ICAgV2hhdDoJCS9zeXMvLi4uL2V2ZW50cy9pbl90ZW1wWV9yYXdf
dGhyZXNoX2ZhbGxpbmdfdmFsdWUNCj4gPj4+ICtXaGF0OgkJL3N5cy8uLi4vZXZlbnRzL2lsbHVt
aW5hbmNlMF90aHJlc2hfZmFsbGluZ192YWx1ZQ0KPiA+Pj4gK3doYXQ6CQkvc3lzLy4uLi9ldmVu
dHMvaWxsdW1pbmFuY2UwX3RocmVzaF9yaXNpbmdfdmFsdWUNCj4gPj4+ICt3aGF0OgkJL3N5cy8u
Li4vZXZlbnRzL3Byb3hpbWl0eV90aHJlc2hfZmFsbGluZ192YWx1ZQ0KPiA+Pj4gK3doYXQ6CQkv
c3lzLy4uLi9ldmVudHMvcHJveGltaXR5X3RocmVzaF9yaXNpbmdfdmFsdWUNCj4gPj4+ICAgS2Vy
bmVsVmVyc2lvbjoJMi42LjM3DQo+ID4+PiAgIENvbnRhY3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5l
bC5vcmcNCj4gPj4+ICAgRGVzY3JpcHRpb246DQo+ID4+PiBAQCAtNzM5LDMgKzc0NSw0IEBAIERl
c2NyaXB0aW9uOg0KPiA+Pj4gICAJCXN5c3RlbS4gVG8gbWluaW1pemUgdGhlIGN1cnJlbnQgY29u
c3VtcHRpb24gb2YgdGhlIHN5c3RlbSwNCj4gPj4+ICAgCQl0aGUgYnJpZGdlIGNhbiBiZSBkaXNj
b25uZWN0ZWQgKHdoZW4gaXQgaXMgbm90IGJlaW5nIHVzZWQNCj4gPj4+ICAgCQl1c2luZyB0aGUg
YnJpZGdlX3N3aXRjaF9lbiBhdHRyaWJ1dGUuDQo+ID4+PiArDQo+ID4+IHNwdXJpb3VzIGJsYW5r
IGxpbmU/DQo+ID4+PiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0
aW9uL3N5c2ZzLWJ1cy1paW8tbGlnaHQNCj4gPj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3Vt
ZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodA0KPiA+Pj4gaW5kZXggZWRiZjQ3MC4uNDM4NWM3
MCAxMDA2NDQNCj4gPj4+IC0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRhdGlvbi9z
eXNmcy1idXMtaWlvLWxpZ2h0DQo+ID4+PiArKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3Vt
ZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodA0KPiA+Pj4gQEAgLTc2LDEwICs3NiwxMCBAQCBD
b250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4+PiAgIERlc2NyaXB0aW9uOg0K
PiA+Pj4gICAJCVRoaXMgcHJvcGVydHkgZ2V0cy9zZXRzIHRoZSBzZW5zb3JzIEFEQyBhbmFsb2cg
aW50ZWdyYXRpb24NCj4gPj4gdGltZS4NCj4gPj4+DQo+ID4+PiAtV2hhdDoJCS9zeXMvYnVzL2lp
by9kZXZpY2VzL2RldmljZVtuXS9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZQ0KPiA+Pj4gK1doYXQ6
CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0vbHV4X3RhYmxlDQo+ID4+PiAgIEtlcm5l
bFZlcnNpb246CTIuNi4zNw0KPiA+Pj4gICBDb250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwu
b3JnDQo+ID4+PiAgIERlc2NyaXB0aW9uOg0KPiA+Pj4gLQkJSGFyZHdhcmUgb3Igc29mdHdhcmUg
YXBwbGllZCBjYWxpYnJhdGlvbiBzY2FsZSBmYWN0b3IgYXNzdW1lZA0KPiA+Pj4gLQkJdG8gYWNj
b3VudCBmb3IgYXR0ZW51YXRpb24gZHVlIHRvIGluZHVzdHJpYWwgZGVzaWduIChnbGFzcw0KPiA+
Pj4gLQkJZmlsdGVycyBvciBhcGVydHVyZSBob2xlcykuDQo+ID4+PiArCQlUaGlzIHByb3BlcnR5
IGdldHMvc2V0cyB0aGUgdGFibGUgb2YgY29lZmZpY2llbnRzDQo+ID4+PiArCQl1c2VkIGluIGNh
bGN1bGF0aW5nIGlsbHVtaW5hbmNlIGluIGx1eC4NCj4gPj4+ICsNCj4gPj4+IGRpZmYgLS1naXQg
YS9kcml2ZXJzL3N0YWdpbmcvaWlvL0RvY3VtZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodC10
c2wyNTgzDQo+ID4+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL3N5c2ZzLWJ1
cy1paW8tbGlnaHQtdHNsMjU4Mw0KPiA+Pj4gZGVsZXRlZCBmaWxlIG1vZGUgMTAwNjQ0DQo+ID4+
PiBpbmRleCA2NjA3ODFkLi4wMDAwMDAwDQo+ID4+PiAtLS0gYS9kcml2ZXJzL3N0YWdpbmcvaWlv
L0RvY3VtZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodC10c2wyNTgzDQo+ID4+PiArKysgL2Rl
di9udWxsDQo+ID4+PiBAQCAtMSwyMCArMCwwIEBADQo+ID4+PiAtV2hhdDoJCS9zeXMvYnVzL2lp
by9kZXZpY2VzL2RldmljZVtuXS9sdXhfdGFibGUNCj4gPj4+IC1LZXJuZWxWZXJzaW9uOgkyLjYu
MzcNCj4gPj4+IC1Db250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4+PiAtRGVz
Y3JpcHRpb246DQo+ID4+PiAtCQlUaGlzIHByb3BlcnR5IGdldHMvc2V0cyB0aGUgdGFibGUgb2Yg
Y29lZmZpY2llbnRzDQo+ID4+PiAtCQl1c2VkIGluIGNhbGN1bGF0aW5nIGlsbHVtaW5hbmNlIGlu
IGx1eC4NCj4gPj4+IC0NCj4gPj4+IC1XaGF0OgkJL3N5cy9idXMvaWlvL2RldmljZXMvZGV2aWNl
W25dL2lsbHVtaW5hbmNlMF9jYWxpYnJhdGUNCj4gPj4+IC1LZXJuZWxWZXJzaW9uOgkyLjYuMzcN
Cj4gPj4+IC1Db250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3JnDQo+ID4+PiAtRGVzY3Jp
cHRpb246DQo+ID4+PiAtCQlUaGlzIHByb3BlcnR5IGNhdXNlcyBhbiBpbnRlcm5hbCBjYWxpYnJh
dGlvbiBvZiB0aGUgYWxzIGdhaW4gdHJpbQ0KPiA+Pj4gLQkJdmFsdWUgd2hpY2ggaXMgbGF0ZXIg
dXNlZCBpbiBjYWxjdWxhdGluZyBpbGx1bWluYW5jZSBpbiBsdXguDQo+ID4+PiAtDQo+ID4+PiAt
V2hhdDoNCj4gPj4gCS9zeXMvYnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9pbGx1bWluYW5jZTBf
aW5wdXRfdGFyZ2V0DQo+ID4+PiAtS2VybmVsVmVyc2lvbjoJMi42LjM3DQo+ID4+PiAtQ29udGFj
dDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KPiA+Pj4gLURlc2NyaXB0aW9uOg0KPiA+Pj4g
LQkJVGhpcyBwcm9wZXJ0eSBpcyB0aGUga25vd24gZXh0ZXJuYWxseSBpbGx1bWluYW5jZSAoaW4g
bHV4KS4NCj4gPj4+IC0JCUl0IGlzIHVzZWQgaW4gdGhlIHByb2Nlc3Mgb2YgY2FsaWJyYXRpbmcg
dGhlIGRldmljZSBhY2N1cmFjeS4NCj4gPj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcv
aWlvL2xpZ2h0L0tjb25maWcNCj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L0tjb25maWcN
Cj4gPj4+IGluZGV4IGU3ZTkxNTkuLjk3NmY3OTAgMTAwNjQ0DQo+ID4+PiAtLS0gYS9kcml2ZXJz
L3N0YWdpbmcvaWlvL2xpZ2h0L0tjb25maWcNCj4gPj4+ICsrKyBiL2RyaXZlcnMvc3RhZ2luZy9p
aW8vbGlnaHQvS2NvbmZpZw0KPiA+Pj4gQEAgLTMxLDQgKzMxLDEyIEBAIGNvbmZpZyBUU0wyNTgz
DQo+ID4+PiAgIAkgUHJvdmlkZXMgc3VwcG9ydCBmb3IgdGhlIFRBT1MgdHNsMjU4MCwgdHNsMjU4
MSBhbmQgdHNsMjU4MyBkZXZpY2VzLg0KPiA+Pj4gICAJIEFjY2VzcyBBTFMgZGF0YSB2aWEgaWlv
LCBzeXNmcy4NCj4gPj4+DQo+ID4+PiArY29uZmlnIFRTTDJ4N3gNCj4gPj4+ICsJdHJpc3RhdGUg
IlRBT1MgVFNML1RNRDJ4NzEgYW5kIFRTTC9UTUQyeDcyIEZhbWlseSBvZiBsaWdodCBhbmQNCj4g
Pj4gcHJveGltaXR5IHNlbnNvcnMiDQo+ID4+PiArCWRlcGVuZHMgb24gSTJDDQo+ID4+PiArCWhl
bHANCj4gPj4+ICsJIFN1cHBvcnQgZm9yOiB0c2wyNTcxLCB0c2wyNjcxLCB0bWQyNjcxLCB0c2wy
NzcxLCB0bWQyNzcxLCB0c2wyNTcyLA0KPiA+PiB0c2wyNjcyLA0KPiA+Pj4gKwkgdG1kMjY3Miwg
dHNsMjc3MiwgdG1kMjc3MiBkZXZpY2VzLg0KPiA+Pj4gKwkgUHJvdmlkZXMgaWlvX2V2ZW50cyBh
bmQgZGlyZWN0IGFjY2VzcyB2aWEgc3lzZnMuDQo+ID4+PiArDQo+ID4+PiAgIGVuZG1lbnUNCj4g
Pj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L01ha2VmaWxlDQo+ID4+
IGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC9NYWtlZmlsZQ0KPiA+Pj4gaW5kZXggMzAxMWZi
Zi4uZmYxMmM0YiAxMDA2NDQNCj4gPj4+IC0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQv
TWFrZWZpbGUNCj4gPj4+ICsrKyBiL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvTWFrZWZpbGUN
Cj4gPj4+IEBAIC01LDMgKzUsNCBAQA0KPiA+Pj4gICBvYmotJChDT05GSUdfU0VOU09SU19UU0wy
NTYzKQkrPSB0c2wyNTYzLm8NCj4gPj4+ICAgb2JqLSQoQ09ORklHX1NFTlNPUlNfSVNMMjkwMTgp
CSs9IGlzbDI5MDE4Lm8NCj4gPj4+ICAgb2JqLSQoQ09ORklHX1RTTDI1ODMpCSs9IHRzbDI1ODMu
bw0KPiA+Pj4gK29iai0kKENPTkZJR19UU0wyeDd4KQkrPSB0c2wyeDd4X2NvcmUubw0KPiA+Pj4g
ZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eF9jb3JlLmMNCj4g
Pj4gYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4N3hfY29yZS5jDQo+ID4+PiBuZXcg
ZmlsZSBtb2RlIDEwMDY0NA0KPiA+Pj4gaW5kZXggMDAwMDAwMC4uYzBkOWQ2ZQ0KPiA+Pj4gLS0t
IC9kZXYvbnVsbA0KPiA+Pj4gKysrIGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90c2wyeDd4
X2NvcmUuYw0KPiA+Pj4gQEAgLTAsMCArMSwxOTExIEBADQo+ID4+PiArLyoNCj4gPj4+ICsgKiBE
ZXZpY2UgZHJpdmVyIGZvciBtb25pdG9yaW5nIGFtYmllbnQgbGlnaHQgaW50ZW5zaXR5IGluIChs
dXgpDQo+ID4+PiArICogYW5kIHByb3hpbWl0eSBkZXRlY3Rpb24gKHByb3gpIHdpdGhpbiB0aGUg
VEFPUyBUU0wyWDdYIGZhbWlseSBvZg0KPiBkZXZpY2VzLg0KPiA+Pj4gKyAqDQo+ID4+PiArICog
Q29weXJpZ2h0IChjKSAyMDEyLCBUQU9TIENvcnBvcmF0aW9uLg0KPiA+Pj4gKyAqDQo+ID4+PiAr
ICogVGhpcyBwcm9ncmFtIGlzIGZyZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0
IGFuZC9vciBtb2RpZnkNCj4gPj4+ICsgKiBpdCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBH
ZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1Ymxpc2hlZCBieQ0KPiA+Pj4gKyAqIHRoZSBGcmVl
IFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJzaW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9y
DQo+ID4+PiArICogKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIgdmVyc2lvbi4NCj4gPj4+ICsg
Kg0KPiA+Pj4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9wZSB0aGF0
IGl0IHdpbGwgYmUgdXNlZnVsLCBidXQNCj4gV0lUSE9VVA0KPiA+Pj4gKyAqIEFOWSBXQVJSQU5U
WTsgd2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mDQo+IE1FUkNIQU5UQUJJTElU
WQ0KPiA+PiBvcg0KPiA+Pj4gKyAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAg
U2VlIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMNCj4gTGljZW5zZQ0KPiA+PiBmb3INCj4gPj4+ICsg
KiBtb3JlIGRldGFpbHMuDQo+ID4+PiArICoNCj4gPj4+ICsgKiBZb3Ugc2hvdWxkIGhhdmUgcmVj
ZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5zZQ0KPiBhbG9uZw0K
PiA+Pj4gKyAqIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVlIFNv
ZnR3YXJlIEZvdW5kYXRpb24sIEluYy4sDQo+ID4+PiArICogNTEgRnJhbmtsaW4gU3RyZWV0LCBG
aWZ0aCBGbG9vciwgQm9zdG9uLCBNQSAgICAgICAgMDIxMTAtMTMwMSwgVVNBLg0KPiA+Pj4gKyAq
Lw0KPiA+Pj4gKw0KPiA+Pj4gKyNpbmNsdWRlPGxpbnV4L2tlcm5lbC5oPg0KPiA+Pj4gKyNpbmNs
dWRlPGxpbnV4L2kyYy5oPg0KPiA+Pj4gKyNpbmNsdWRlPGxpbnV4L2Vycm5vLmg+DQo+ID4+PiAr
I2luY2x1ZGU8bGludXgvZGVsYXkuaD4NCj4gPj4+ICsjaW5jbHVkZTxsaW51eC9tdXRleC5oPg0K
PiA+Pj4gKyNpbmNsdWRlPGxpbnV4L2ludGVycnVwdC5oPg0KPiA+Pj4gKyNpbmNsdWRlPGxpbnV4
L3NsYWIuaD4NCj4gPj4+ICsjaW5jbHVkZTxsaW51eC9tb2R1bGUuaD4NCj4gPj4+ICsjaW5jbHVk
ZTxsaW51eC92ZXJzaW9uLmg+DQo+ID4+PiArI2luY2x1ZGUgInRzbDJ4N3hfY29yZS5oIg0KPiA+
Pj4gKyNpbmNsdWRlICIuLi9ldmVudHMuaCINCj4gPj4+ICsjaW5jbHVkZSAiLi4vaWlvLmgiDQo+
ID4+PiArI2luY2x1ZGUgIi4uL3N5c2ZzLmgiDQo+ID4+PiArDQo+ID4+PiArLyogQ2FsIGRlZnMq
Lw0KPiA+Pj4gKyNkZWZpbmUgUFJPWF9TVEFUX0NBTCAgICAgICAgMA0KPiA+Pj4gKyNkZWZpbmUg
UFJPWF9TVEFUX1NBTVAgICAgICAgMQ0KPiA+Pj4gKyNkZWZpbmUgTUFYX1NBTVBMRVNfQ0FMICAg
ICAgMjAwDQo+ID4+PiArDQo+ID4+PiArLyogVFNMMlg3WCBEZXZpY2UgSUQgKi8NCj4gPj4+ICsj
ZGVmaW5lIFRSSVRPTl9JRCAgICAweDAwDQo+ID4+PiArI2RlZmluZSBTV09SREZJU0hfSUQgMHgz
MA0KPiA+Pj4gKyNkZWZpbmUgSEFMSUJVVF9JRCAgIDB4MjANCj4gPj4+ICsNCj4gPj4+ICsvKiBM
dXggY2FsY3VsYXRpb24gY29uc3RhbnRzICovDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0xVWF9D
QUxDX09WRVJfRkxPVyAgICAgNjU1MzUNCj4gPj4+ICsNCj4gPj4+ICsvKiBUQU9TIFJlZ2lzdGVy
IGRlZmluaXRpb25zIC0gbm90ZToNCj4gPj4+ICsgKiBkZXBlbmRpbmcgb24gZGV2aWNlLCBzb21l
IG9mIHRoZXNlIHJlZ2lzdGVyIGFyZSBub3QgdXNlZCBhbmQgdGhlDQo+ID4+PiArICogcmVnaXN0
ZXIgYWRkcmVzcyBpcyBiZW5pZ24uDQo+ID4+PiArICovDQo+ID4+PiArLyogMlg3WCByZWdpc3Rl
ciBvZmZzZXRzICovDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX01BWF9DT05GSUdfUkVHICAgICAg
ICAgMTYNCj4gPj4+ICsNCj4gPj4+ICsvKiBEZXZpY2UgUmVnaXN0ZXJzIGFuZCBNYXNrcyAqLw0K
PiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTlRSTCAgICAgICAgICAgICAgICAgIDB4MDANCj4gPj4+
ICsjZGVmaW5lIFRTTDJYN1hfQUxTX1RJTUUgICAgICAgICAgICAgICAwWDAxDQo+ID4+PiArI2Rl
ZmluZSBUU0wyWDdYX1BSWF9USU1FICAgICAgICAgICAgICAgMHgwMg0KPiA+Pj4gKyNkZWZpbmUg
VFNMMlg3WF9XQUlUX1RJTUUgICAgICAgICAgICAgIDB4MDMNCj4gPj4+ICsjZGVmaW5lIFRTTDJY
N1hfQUxTX01JTlRIUkVTSExPICAgICAgICAwWDA0DQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0FM
U19NSU5USFJFU0hISSAgICAgICAgMFgwNQ0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9BTFNfTUFY
VEhSRVNITE8gICAgICAgIDBYMDYNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfQUxTX01BWFRIUkVT
SEhJICAgICAgICAwWDA3DQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX1BSWF9NSU5USFJFU0hMTyAg
ICAgICAgMFgwOA0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9QUlhfTUlOVEhSRVNISEkgICAgICAg
IDBYMDkNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfUFJYX01BWFRIUkVTSExPICAgICAgICAwWDBB
DQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX1BSWF9NQVhUSFJFU0hISSAgICAgICAgMFgwQg0KPiA+
Pj4gKyNkZWZpbmUgVFNMMlg3WF9QRVJTSVNURU5DRSAgICAgICAgICAgIDB4MEMNCj4gPj4+ICsj
ZGVmaW5lIFRTTDJYN1hfUFJYX0NPTkZJRyAgICAgICAgICAgICAweDBEDQo+ID4+PiArI2RlZmlu
ZSBUU0wyWDdYX1BSWF9DT1VOVCAgICAgICAgICAgICAgMHgwRQ0KPiA+Pj4gKyNkZWZpbmUgVFNM
Mlg3WF9HQUlOICAgICAgICAgICAgICAgICAgIDB4MEYNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hf
Tk9UVVNFRCAgICAgICAgICAgICAgICAweDEwDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX1JFVklE
ICAgICAgICAgICAgICAgICAgMHgxMQ0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DSElQSUQgICAg
ICAgICAgICAgICAgIDB4MTINCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfU1RBVFVTICAgICAgICAg
ICAgICAgICAweDEzDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0FMU19DSEFOMExPICAgICAgICAg
ICAgMHgxNA0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9BTFNfQ0hBTjBISSAgICAgICAgICAgIDB4
MTUNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfQUxTX0NIQU4xTE8gICAgICAgICAgICAweDE2DQo+
ID4+PiArI2RlZmluZSBUU0wyWDdYX0FMU19DSEFOMUhJICAgICAgICAgICAgMHgxNw0KPiA+Pj4g
KyNkZWZpbmUgVFNMMlg3WF9QUlhfTE8gICAgICAgICAgICAgICAgIDB4MTgNCj4gPj4+ICsjZGVm
aW5lIFRTTDJYN1hfUFJYX0hJICAgICAgICAgICAgICAgICAweDE5DQo+ID4+PiArDQo+ID4+PiAr
LyogdHNsMlg3WCBjbWQgcmVnIG1hc2tzICovDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0NNRF9S
RUcgICAgICAgICAgICAgICAgMHg4MA0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTURfU1BMX0ZO
ICAgICAgICAgICAgIDB4NjANCj4gPj4+ICsNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfQ01EX1BS
T1hfSU5UX0NMUiAgICAgICAwWDA1DQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0NNRF9BTFNfSU5U
X0NMUiAgICAgICAgMHgwNg0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTURfUFJPWEFMU19JTlRf
Q0xSICAgIDBYMDcNCj4gPj4+ICsNCj4gPj4+ICsvKiB0c2wyWDdYIGNudHJsIHJlZyBtYXNrcyAq
Lw0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX0FEQ19FTkJMICAgICAgICAgIDB4MDINCj4g
Pj4+ICsjZGVmaW5lIFRTTDJYN1hfQ05UTF9QV1JfT04gICAgICAgICAgICAweDAxDQo+ID4+PiAr
DQo+ID4+PiArLyogdHNsMlg3WCBzdGF0dXMgcmVnIG1hc2tzICovDQo+ID4+PiArI2RlZmluZSBU
U0wyWDdYX1NUQV9BRENfVkFMSUQgICAgICAgICAgMHgwMQ0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3
WF9TVEFfUFJYX1ZBTElEICAgICAgICAgIDB4MDINCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfU1RB
X0FEQ19QUlhfVkFMSUQgICAgICAweDAzDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX1NUQV9BTFNf
SU5UUiAgICAgICAgICAgMHgxMA0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9TVEFfQURDX0lOVFIg
ICAgICAgICAgIDB4MTANCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfU1RBX1BSWF9JTlRSICAgICAg
ICAgICAweDIwDQo+ID4+PiArDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX1NUQV9BRENfSU5UUiAg
ICAgICAgICAgMHgxMA0KPiA+Pj4gKw0KPiA+Pj4gKy8qIHRzbDJYN1ggY250cmwgcmVnIG1hc2tz
ICovDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0NOVExfUkVHX0NMRUFSICAgICAgICAgMHgwMA0K
PiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX1BST1hfSU5UX0VOQkwgICAgIDBYMjANCj4gPj4+
ICsjZGVmaW5lIFRTTDJYN1hfQ05UTF9BTFNfSU5UX0VOQkwgICAgICAwWDEwDQo+ID4+PiArI2Rl
ZmluZSBUU0wyWDdYX0NOVExfV0FJVF9UTVJfRU5CTCAgICAgMFgwOA0KPiA+Pj4gKyNkZWZpbmUg
VFNMMlg3WF9DTlRMX1BST1hfREVUX0VOQkwgICAgIDBYMDQNCj4gPj4+ICsjZGVmaW5lIFRTTDJY
N1hfQ05UTF9QV1JPTiAgICAgICAgICAgICAweDAxDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0NO
VExfQUxTUE9OX0VOQkwgICAgICAgMHgwMw0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9DTlRMX0lO
VEFMU1BPTl9FTkJMICAgIDB4MTMNCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfQ05UTF9QUk9YUE9O
X0VOQkwgICAgICAweDBGDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX0NOVExfSU5UUFJPWFBPTl9F
TkJMICAgMHgyRg0KPiA+Pj4gKw0KPiA+Pj4gKy8qUHJveCBkaW9kZSB0byB1c2UgKi8NCj4gPj4+
ICsjZGVmaW5lIFRTTDJYN1hfRElPREUwICAgICAgICAgICAgICAgICAweDEwDQo+ID4+PiArI2Rl
ZmluZSBUU0wyWDdYX0RJT0RFMSAgICAgICAgICAgICAgICAgMHgyMA0KPiA+Pj4gKyNkZWZpbmUg
VFNMMlg3WF9ESU9ERV9CT1RIICAgICAgICAgICAgIDB4MzANCj4gPj4+ICsNCj4gPj4+ICsvKiBM
RUQgUG93ZXIgKi8NCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfbUExMDAgICAgICAgICAgICAgICAg
ICAweDAwDQo+ID4+PiArI2RlZmluZSBUU0wyWDdYX21BNTAgICAgICAgICAgICAgICAgICAgMHg0
MA0KPiA+Pj4gKyNkZWZpbmUgVFNMMlg3WF9tQTI1ICAgICAgICAgICAgICAgICAgIDB4ODANCj4g
Pj4+ICsjZGVmaW5lIFRTTDJYN1hfbUExMyAgICAgICAgICAgICAgICAgICAweEQwDQo+ID4+PiAr
DQo+ID4+PiArLypDb21tb24gZGV2aWNlIElJTyBFdmVudE1hc2sgKi8NCj4gPj4+ICsjZGVmaW5l
IFRTTDJYN1hfRVZFTlRfTUFTSyBcDQo+ID4+PiArCQkoSUlPX0VWX0JJVChJSU9fRVZfVFlQRV9U
SFJFU0gsIElJT19FVl9ESVJfUklTSU5HKSB8IFwNCj4gPj4+ICsJCUlJT19FVl9CSVQoSUlPX0VW
X1RZUEVfVEhSRVNILCBJSU9fRVZfRElSX0ZBTExJTkcpKSwNCj4gPj4+ICsNCj4gPj4+ICsvKiBU
QU9TIHR4eDJ4N3ggRGV2aWNlIGZhbWlseSBtZW1iZXJzICovDQo+ID4+PiArZW51bSB7DQo+ID4+
PiArCXRzbDI1NzEsDQo+ID4+PiArCXRzbDI2NzEsDQo+ID4+PiArCXRtZDI2NzEsDQo+ID4+PiAr
CXRzbDI3NzEsDQo+ID4+PiArCXRtZDI3NzEsDQo+ID4+PiArCXRzbDI1NzIsDQo+ID4+PiArCXRz
bDI2NzIsDQo+ID4+PiArCXRtZDI2NzIsDQo+ID4+PiArCXRzbDI3NzIsDQo+ID4+PiArCXRtZDI3
NzINCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK2VudW0gew0KPiA+Pj4gKwlUU0wyWDdYX0NI
SVBfVU5LTk9XTiA9IDAsDQo+ID4+PiArCVRTTDJYN1hfQ0hJUF9XT1JLSU5HID0gMSwNCj4gPj4+
ICsJVFNMMlg3WF9DSElQX1NVU1BFTkRFRCA9IDINCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4g
Ky8qIFBlci1kZXZpY2UgZGF0YSAqLw0KPiA+Pj4gK3N0cnVjdCB0c2wyeDd4X2Fsc19pbmZvIHsN
Cj4gPj4+ICsJdTE2IGFsc19jaDA7DQo+ID4+PiArCXUxNiBhbHNfY2gxOw0KPiA+Pj4gKwl1MTYg
bHV4Ow0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+PiArLyogcHJveGltaXR5IGRhdGEgKi8NCj4g
Pj4+ICtzdHJ1Y3QgdHNsMng3eF9wcm94X2luZm8gew0KPiA+Pj4gKwl1MTYgcHJveF9kYXRhOw0K
PiA+Pj4gKwlpbnQgcHJveF9ldmVudDsNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0cnVj
dCBwcm94X3N0YXQgew0KPiA+Pj4gKwl1MTYgbWluOw0KPiA+Pj4gKwl1MTYgbWF4Ow0KPiA+Pj4g
Kwl1MTYgbWVhbjsNCj4gPj4+ICsJdW5zaWduZWQgbG9uZyBzdGRkZXY7DQo+ID4+PiArfTsNCj4g
Pj4+ICsNCj4gPj4+ICtzdHJ1Y3QgdHNsMng3eF9jaGlwX2luZm8gew0KPiA+Pj4gKwlpbnQgbnVt
X2NoYW5uZWxzOw0KPiA+Pj4gKwlzdHJ1Y3QgaWlvX2NoYW5fc3BlYwkJY2hhbm5lbFs5XTsNCj4g
Pj4+ICsJY29uc3Qgc3RydWN0IGlpb19pbmZvCQkqaW5mbzsNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0K
PiA+Pj4gK3N0cnVjdCB0c2wyWDdYX2NoaXAgew0KPiA+Pj4gKwlrZXJuZWxfdWxvbmdfdCBpZDsN
Cj4gPj4+ICsJc3RydWN0IG11dGV4IHByb3hfbXV0ZXg7DQo+ID4+PiArCXN0cnVjdCBtdXRleCBh
bHNfbXV0ZXg7DQo+ID4+PiArCXN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQ7DQo+ID4+PiArCXN0
cnVjdCB0c2wyeDd4X3Byb3hfaW5mbyBwcm94X2N1cl9pbmZvOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNs
Mng3eF9hbHNfaW5mbyBhbHNfY3VyX2luZm87DQo+ID4+PiArCXN0cnVjdCB0c2wyeDd4X3NldHRp
bmdzIHRzbDJ4N3hfc2V0dGluZ3M7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX3BsYXRmb3JtX2Rh
dGEgKnBkYXRhOw0KPiA+Pj4gKwlpbnQgYWxzX3RpbWVfc2NhbGU7DQo+ID4+PiArCWludCBhbHNf
c2F0dXJhdGlvbjsNCj4gPj4+ICsJaW50IHRzbDJ4N3hfY2hpcF9zdGF0dXM7DQo+ID4+PiArCXU4
IHRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfTUFYX0NPTkZJR19SRUddOw0KPiA+Pj4gKwljb25zdCBz
dHJ1Y3QgdHNsMng3eF9jaGlwX2luZm8JKmNoaXBfaW5mbzsNCj4gPj4+ICsJY29uc3Qgc3RydWN0
IGlpb19pbmZvICppbmZvOw0KPiA+Pj4gKwlzNjQgZXZlbnRfdGltZXN0YW1wOw0KPiA+Pj4gKwkv
KiBUaGlzIHN0cnVjdHVyZSBpcyBpbnRlbnRpb25hbGx5IGxhcmdlIHRvIGFjY29tbW9kYXRlDQo+
ID4+PiArCSAqIHVwZGF0ZXMgdmlhIHN5c2ZzLiAqLw0KPiA+Pj4gKwkvKiBTaXplZCB0byA5ID0g
bWF4IDggc2VnbWVudHMgKyAxIHRlcm1pbmF0aW9uIHNlZ21lbnQgKi8NCj4gPj4+ICsJc3RydWN0
IHRzbDJ4N3hfbHV4IHRzbDJ4N3hfZGV2aWNlX2x1eFtUU0wyWDdYX01BWF9MVVhfVEFCTEVfU0la
RV07DQo+ID4+PiArfTsNCj4gPj4+ICsNCj4gPj4+ICsvKiBEaWZmZXJlbnQgZGV2aWNlcyByZXF1
aXJlIGRpZmZlcmVudCBjb2VmZmljZW50cyAqLw0KPiA+Pj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3Qg
dHNsMng3eF9sdXggdHNsMng3MV9sdXhfdGFibGVbXSA9IHsNCj4gPj4+ICsJeyAxNDQ2MSwgICA2
MTEsICAgMTIxMSB9LA0KPiA+Pj4gKwl7IDE4NTQwLCAgIDM1MiwgICAgNjIzIH0sDQo+ID4+PiAr
CXsgICAgIDAsICAgICAwLCAgICAgIDAgfSwNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0
YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXggdG1kMng3MV9sdXhfdGFibGVbXSA9IHsNCj4g
Pj4+ICsJeyAxMTYzNSwgICAxMTUsICAgIDI1NiB9LA0KPiA+Pj4gKwl7IDE1NTM2LCAgICA4Nywg
ICAgMTc5IH0sDQo+ID4+PiArCXsgICAgIDAsICAgICAwLCAgICAgIDAgfSwNCj4gPj4+ICt9Ow0K
PiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3eF9sdXggdHNsMng3Ml9s
dXhfdGFibGVbXSA9IHsNCj4gPj4+ICsJeyAxNDAxMywgICA0NjYsICAgOTE3IH0sDQo+ID4+PiAr
CXsgMTgyMjIsICAgMzEwLCAgIDU1MiB9LA0KPiA+Pj4gKwl7ICAgICAwLCAgICAgMCwgICAgIDAg
fSwNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgdHNsMng3
eF9sdXggdG1kMng3Ml9sdXhfdGFibGVbXSA9IHsNCj4gPj4+ICsJeyAxMzIxOCwgICAxMzAsICAg
MjYyIH0sDQo+ID4+PiArCXsgMTc1OTIsICAgOTIsICAgIDE2OSB9LA0KPiA+Pj4gKwl7ICAgICAw
LCAgICAgMCwgICAgIDAgfSwNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBjb25z
dCBzdHJ1Y3QgdHNsMng3eF9sdXggKnRzbDJ4N3hfZGVmYXVsdF9sdXhfdGFibGVfZ3JvdXBbXSA9
IHsNCj4gPj4gY2hlY2sgd2hpdGUgc3BhY2UgYXJvdW5kIGhlcmUuICBsb29rcyBsaWtlIGEgbWl4
dHVyZSBvZiB0YWJzIGFuZCBzcGFjZXMuLi4NCj4gPj4+ICsJW3RzbDI1NzFdID0gdHNsMng3MV9s
dXhfdGFibGUsDQo+ID4+PiArCVt0c2wyNjcxXSA9CXRzbDJ4NzFfbHV4X3RhYmxlLA0KPiA+Pj4g
KwlbdG1kMjY3MV0gPQl0bWQyeDcxX2x1eF90YWJsZSwNCj4gPj4+ICsJW3RzbDI3NzFdID0JdHNs
Mng3MV9sdXhfdGFibGUsDQo+ID4+PiArCVt0bWQyNzcxXSA9CXRtZDJ4NzFfbHV4X3RhYmxlLA0K
PiA+Pj4gKwlbdHNsMjU3Ml0gPQl0c2wyeDcyX2x1eF90YWJsZSwNCj4gPj4+ICsJW3RzbDI2NzJd
ID0JdHNsMng3Ml9sdXhfdGFibGUsDQo+ID4+PiArCVt0bWQyNjcyXSA9IHRtZDJ4NzJfbHV4X3Rh
YmxlLA0KPiA+Pj4gKwlbdHNsMjc3Ml0gPQl0c2wyeDcyX2x1eF90YWJsZSwNCj4gPj4+ICsJW3Rt
ZDI3NzJdID0JdG1kMng3Ml9sdXhfdGFibGUsDQo+ID4+PiArfTsNCj4gPj4+ICsNCj4gPj4+ICtz
dGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfc2V0dGluZ3MgdHNsMng3eF9kZWZhdWx0X3NldHRp
bmdzID0gew0KPiA+Pj4gKwkuYWxzX3RpbWUgPSAyMDAsDQo+ID4+PiArCS8qIG11c3QgYmUgYSBt
dWx0aXBsZSBvZiA1MG1TICovDQo+ID4+PiArCS5hbHNfZ2FpbiA9IDAsDQo+ID4+PiArCS8qIHRo
aXMgaXMgYWN0dWFsbHkgYW4gaW5kZXggaW50byB0aGUgZ2FpbiB0YWJsZSAqLw0KPiA+Pj4gKwku
cHJ4X3RpbWUgPSAweGZlLCAvKjUuNCBtUyAqLw0KPiA+Pj4gKwkvKiAyLjdtcyBwcm94IGludGVn
cmF0aW9uIHRpbWUgLSBkZWNyZWFzZSB0byBpbmNyZWFzZSB0aW1lICovDQo+ID4+PiArCS8qIGRl
Y3JlYXNlcyBpbiAyLjcgbXMgaW50ZXJ2YWxzICovDQo+ID4+PiArCS5wcm94X2dhaW4gPSAxLA0K
PiA+Pj4gKwkvKiB0aGVzZSBhcmUgYml0cyAzOjIgb2YgcmVnIDB4MGY6IDA9eDEsMT14MiwyPXg0
LDM9eDggKi8NCj4gPj4+ICsJLyogYXNzdW1lIGNsZWFyIGdsYXNzIGFzIGRlZmF1bHQgKi8NCj4g
Pj4+ICsJLndhaXRfdGltZSA9IDI0NSwNCj4gPj4+ICsJLyogVGltZSBiZXR3ZWVuIFBSWCBhbmQg
QUxTIGN5Y2xlcyAtZGVjcmVhc2UgdG8gaW5jcmVhc2UgdGltZSAqLw0KPiA+Pj4gKwkvKiBkZWNy
ZWFzZXMgaW4gMi43IG1zIGludGVydmFscyAqLw0KPiA+Pj4gKwkucHJveF9jb25maWcgPSAwLA0K
PiA+Pj4gKwkvKiBQcm94IGNvbmZpZ3VyYXRpb24gZmlsdGVycyAqLw0KPiA+Pj4gKwkuYWxzX2dh
aW5fdHJpbSA9IDEwMDAsDQo+ID4+PiArCS8qIGRlZmF1bHQgZ2FpbiB0cmltIHRvIGFjY291bnQg
Zm9yIGFwZXJ0dXJlIGVmZmVjdHMgKi8NCj4gPj4+ICsJLmFsc19jYWxfdGFyZ2V0ID0gMTUwLA0K
PiA+Pj4gKwkvKiBLbm93biBleHRlcm5hbCBBTFMgcmVhZGluZyB1c2VkIGZvciBjYWxpYnJhdGlv
biAqLw0KPiA+Pj4gKwkuYWxzX3RocmVzaF9sb3cgPSAyMDAsDQo+ID4+PiArCS8qIENIMCAnbG93
JyBjb3VudCB0byB0cmlnZ2VyIGludGVycnVwdCAqLw0KPiA+Pj4gKwkuYWxzX3RocmVzaF9oaWdo
ID0gMjU2LA0KPiA+Pj4gKwkvKiBDSDAgJ2hpZ2gnIGNvdW50IHRvIHRyaWdnZXIgaW50ZXJydXB0
ICovDQo+ID4+PiArCS5hbHNfcGVyc2lzdGVuY2UgPSAweEZGLA0KPiA+Pj4gKwkvKiBOdW1iZXIg
b2YgJ291dCBvZiBsaW1pdHMnIEFEQyByZWFkaW5ncyBQUlgvQUxTKi8NCj4gPj4+ICsJLmludGVy
cnVwdHNfZW4gPSAweDAwLA0KPiA+Pj4gKwkvKiBEZWZhdWx0IGludGVycnVwdChzKSBlbmFibGVk
Lg0KPiA+Pj4gKwkgKiAweDAwID0gbm9uZSwgMHgxMCA9IGFscywgMHgyMCA9IHByeCAweDMwID0g
YnRoICovDQo+ID4+PiArCS5wcm94X3RocmVzX2xvdyAgPSAwLA0KPiA+Pj4gKwkucHJveF90aHJl
c19oaWdoID0gNTEyLA0KPiA+Pj4gKwkvKmRlZmF1bHQgdGhyZXNob2xkIGFkanVzdCBlaXRoZXIg
bWFudWFsbHkgb3Igd2l0aCBjYWwgcm91dGluZSovDQo+ID4+PiArCS5wcm94X21heF9zYW1wbGVz
X2NhbCA9IDMwLA0KPiA+Pj4gKwkucHJveF9wdWxzZV9jb3VudCA9IDgNCj4gPj4+ICt9Ow0KPiA+
Pj4gKw0KPiA+Pj4gK3N0YXRpYyBjb25zdCBzMTYgdHNsMlg3WF9hbHNfZ2FpbmFkaltdID0gew0K
PiA+Pj4gKwkxLA0KPiA+Pj4gKwk4LA0KPiA+Pj4gKwkxNiwNCj4gPj4+ICsJMTIwDQo+ID4+PiAr
fTsNCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgY29uc3QgczE2IHRzbDJYN1hfcHJ4X2dhaW5hZGpb
XSA9IHsNCj4gPj4+ICsJMSwNCj4gPj4+ICsJMiwNCj4gPj4+ICsJNCwNCj4gPj4+ICsJOA0KPiA+
Pj4gK307DQo+ID4+PiArDQo+ID4+PiArLyogQ2hhbm5lbCB2YXJpYXRpb25zICovDQo+ID4+PiAr
ZW51bSB7DQo+ID4+PiArCUFMUywNCj4gPj4+ICsJUFJYLA0KPiA+Pj4gKwlBTFNQUlgsDQo+ID4+
PiArCVBSWDIsDQo+ID4+PiArCUFMU1BSWDIsDQo+ID4+PiArfTsNCj4gPj4+ICsNCj4gPj4+ICtj
b25zdCB1OCBkZXZpY2VfY2hhbm5lbF9jb25maWdbXSA9IHsNCj4gPj4+ICsJQUxTLA0KPiA+Pj4g
KwlQUlgsDQo+ID4+PiArCVBSWCwNCj4gPj4+ICsJQUxTUFJYLA0KPiA+Pj4gKwlBTFNQUlgsDQo+
ID4+PiArCUFMUywNCj4gPj4+ICsJUFJYMiwNCj4gPj4+ICsJUFJYMiwNCj4gPj4+ICsJQUxTUFJY
MiwNCj4gPj4+ICsJQUxTUFJYMg0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+PiArLyoNCj4gPj4+
ICsgKiBSZWFkIGEgbnVtYmVyIG9mIGJ5dGVzIHN0YXJ0aW5nIGF0IHJlZ2lzdGVyIChyZWcpIGxv
Y2F0aW9uLg0KPiA+Pj4gKyAqIFJldHVybiAwLCBvciBpMmNfc21idXNfd3JpdGVfYnl0ZSBFUlJP
UiBjb2RlLg0KPiA+Pj4gKyAqLw0KPiA+Pj4gK3N0YXRpYyBpbnQNCj4gPj4+ICt0c2wyeDd4X2ky
Y19yZWFkKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQsIHU4IHJlZywgdTggKnZhbCkNCj4gPj4+
ICt7DQo+ID4+PiArCWludCByZXQ7DQo+ID4+PiArDQo+ID4+PiArCS8qIHNlbGVjdCByZWdpc3Rl
ciB0byB3cml0ZSAqLw0KPiA+Pj4gKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjbGllbnQs
IChUU0wyWDdYX0NNRF9SRUcgfCByZWcpKTsNCj4gPj4+ICsJaWYgKHJldDwgIDApIHsNCj4gPj4+
ICsJCWRldl9lcnIoJmNsaWVudC0+ZGV2LCAiJXM6IGZhaWxlZCB0byB3cml0ZSByZWdpc3RlciAl
eFxuIg0KPiA+Pj4gKwkJCQksIF9fZnVuY19fLCByZWcpOw0KPiA+Pj4gKwkJcmV0dXJuIHJldDsN
Cj4gPj4+ICsJfQ0KPiA+Pj4gKwkvKiByZWFkIHRoZSBkYXRhICovDQo+ID4+PiArCSp2YWwgPSBp
MmNfc21idXNfcmVhZF9ieXRlKGNsaWVudCk7DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiAwOw0K
PiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4gSSB3b3VsZCBtdWNoIHByZWZlciBpZiB5b3UnZCB1c2Ug
a2VybmVsLWRvYyBmb3IgY29tbWVudHMgb2Fib3V0IGZ1bmN0aW9ucy4NCj4gPj4+ICsvKg0KPiA+
Pj4gKyAqIFJlYWRzIGFuZCBjYWxjdWxhdGVzIGN1cnJlbnQgbHV4IHZhbHVlLg0KPiA+Pj4gKyAq
IFRoZSByYXcgY2gwIGFuZCBjaDEgdmFsdWVzIG9mIHRoZSBhbWJpZW50IGxpZ2h0IHNlbnNlZCBp
biB0aGUgbGFzdA0KPiA+Pj4gKyAqIGludGVncmF0aW9uIGN5Y2xlIGFyZSByZWFkIGZyb20gdGhl
IGRldmljZS4NCj4gPj4+ICsgKiBUaW1lIHNjYWxlIGZhY3RvciBhcnJheSB2YWx1ZXMgYXJlIGFk
anVzdGVkIGJhc2VkIG9uIHRoZSBpbnRlZ3JhdGlvbg0KPiB0aW1lLg0KPiA+Pj4gKyAqIFRoZSBy
YXcgdmFsdWVzIGFyZSBtdWx0aXBsaWVkIGJ5IGEgc2NhbGUgZmFjdG9yLCBhbmQgZGV2aWNlIGdh
aW4gaXMNCj4gb2J0YWluZWQNCj4gPj4+ICsgKiB1c2luZyBnYWluIGluZGV4LiBMaW1pdCBjaGVj
a3MgYXJlIGRvbmUgbmV4dCwgdGhlbiB0aGUgcmF0aW8gb2YgYSBtdWx0aXBsZQ0KPiA+Pj4gKyAq
IG9mIGNoMSB2YWx1ZSwgdG8gdGhlIGNoMCB2YWx1ZSwgaXMgY2FsY3VsYXRlZC4gVGhlIGFycmF5
DQo+IHRzbDJ4N3hfZGV2aWNlX2x1eFtdDQo+ID4+PiArICogZGVjbGFyZWQgYWJvdmUgaXMgdGhl
biBzY2FubmVkIHRvIGZpbmQgdGhlIGZpcnN0IHJhdGlvIHZhbHVlIHRoYXQgaXMganVzdA0KPiA+
Pj4gKyAqIGFib3ZlIHRoZSByYXRpbyB3ZSBqdXN0IGNhbGN1bGF0ZWQuIFRoZSBjaDAgYW5kIGNo
MSBtdWx0aXBsaWVyIGNvbnN0YW50cw0KPiBpbg0KPiA+Pj4gKyAqIHRoZSBhcnJheSBhcmUgdGhl
biB1c2VkIGFsb25nIHdpdGggdGhlIHRpbWUgc2NhbGUgZmFjdG9yIGFycmF5IHZhbHVlcywgdG8N
Cj4gPj4+ICsgKiBjYWxjdWxhdGUgdGhlIGx1eC4NCj4gPj4+ICsgKi8NCj4gPj4+ICtzdGF0aWMg
aW50IHRzbDJ4N3hfZ2V0X2x1eChzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2KQ0KPiA+Pj4gK3sN
Cj4gPj4+ICsJdTE2IGNoMCwgY2gxOyAvKiBzZXBhcmF0ZWQgY2gwL2NoMSBkYXRhIGZyb20gZGV2
aWNlICovDQo+ID4+PiArCXUzMiBsdXg7IC8qIHJhdyBsdXggY2FsY3VsYXRlZCBmcm9tIGRldmlj
ZSBkYXRhICovDQo+ID4+PiArCXU2NCBsdXg2NDsNCj4gPj4+ICsJdTMyIHJhdGlvOw0KPiA+Pj4g
Kwl1OCBidWZbNF07DQo+ID4+PiArCXN0cnVjdCB0c2wyeDd4X2x1eCAqcDsNCj4gPj4+ICsJc3Ry
dWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4+PiArCWlu
dCBpLCByZXQ7DQo+ID4+PiArCXUzMiBjaDBsdXggPSAwOw0KPiA+Pj4gKwl1MzIgY2gxbHV4ID0g
MDsNCj4gPj4+ICsNCj4gPj4+ICsJaWYgKG11dGV4X3RyeWxvY2soJmNoaXAtPmFsc19tdXRleCkg
PT0gMCkgew0KPiA+Pj4gKwkJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LCAidHNsMng3eF9n
ZXRfbHV4IGRldmljZSBpcw0KPiA+PiBidXN5XG4iKTsNCj4gPj4+ICsJCXJldHVybiBjaGlwLT5h
bHNfY3VyX2luZm8ubHV4OyAvKiBidXN5LCBzbyByZXR1cm4gTEFTVCBWQUxVRSAqLw0KPiA+Pj4g
Kwl9DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaGlwLT50c2wyeDd4X2NoaXBfc3RhdHVzICE9IFRT
TDJYN1hfQ0hJUF9XT1JLSU5HKSB7DQo+ID4+PiArCQkvKiBkZXZpY2UgaXMgbm90IGVuYWJsZWQg
Ki8NCj4gPj4+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LCAiJXM6IGRldmljZSBpcyBu
b3QgZW5hYmxlZFxuIiwNCj4gPj4+ICsJCQkJX19mdW5jX18pOw0KPiA+Pj4gKwkJcmV0ID0gLUVC
VVNZIDsNCj4gPj4+ICsJCWdvdG8gb3V0X3VubG9jazsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+
Pj4gKwlyZXQgPSB0c2wyeDd4X2kyY19yZWFkKGNoaXAtPmNsaWVudCwNCj4gPj4+ICsJCShUU0wy
WDdYX0NNRF9SRUcgfCBUU0wyWDdYX1NUQVRVUyksJmJ1ZlswXSk7DQo+ID4+PiArCWlmIChyZXQ8
ICAwKSB7DQo+ID4+PiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQki
JXM6IGZhaWxlZCB0byByZWFkIENNRF9SRUdcbiIsIF9fZnVuY19fKTsNCj4gPj4+ICsJCWdvdG8g
b3V0X3VubG9jazsNCj4gPj4+ICsJfQ0KPiA+Pj4gKwkvKiBpcyBkYXRhIG5ldyYgIHZhbGlkICov
DQo+ID4+PiArCWlmICghKGJ1ZlswXSYgIFRTTDJYN1hfU1RBX0FEQ19WQUxJRCkpIHsNCj4gPj4+
ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+Pj4gKwkJCSIlczogZGF0YSBub3Qg
dmFsaWQgeWV0XG4iLCBfX2Z1bmNfXyk7DQo+ID4+PiArCQlyZXQgPSBjaGlwLT5hbHNfY3VyX2lu
Zm8ubHV4OyAvKiByZXR1cm4gTEFTVCBWQUxVRSAqLw0KPiA+Pj4gKwkJZ290byBvdXRfdW5sb2Nr
Ow0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWZvciAoaSA9IDA7IGk8ICA0OyBpKyspIHsN
Cj4gPj4+ICsJCXJldCA9IHRzbDJ4N3hfaTJjX3JlYWQoY2hpcC0+Y2xpZW50LA0KPiA+Pj4gKwkJ
CShUU0wyWDdYX0NNRF9SRUcgfCAoVFNMMlg3WF9BTFNfQ0hBTjBMTyArIGkpKSwNCj4gPj4+ICsJ
CQkmYnVmW2ldKTsNCj4gPj4+ICsJCWlmIChyZXQ8ICAwKSB7DQo+ID4+PiArCQkJZGV2X2Vycigm
Y2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4+PiArCQkJCSIlczogZmFpbGVkIHRvIHJlYWQuIGVycj0l
eFxuIiwgX19mdW5jX18sIHJldCk7DQo+ID4+PiArCQkJZ290byBvdXRfdW5sb2NrOw0KPiA+Pj4g
KwkJfQ0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCS8qIGNsZWFyIHN0YXR1cywgcmVhbGx5
IGludGVycnVwdCBzdGF0dXMgKCBhcmUgb2ZmKSwNCj4gPj4+ICsJYnV0IHdlIHVzZSB0aGUgYml0
IGFueXdheSAqLw0KPiA+Pj4gKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjaGlwLT5jbGll
bnQsDQo+ID4+PiArCQkoVFNMMlg3WF9DTURfUkVHIHwNCj4gPj4+ICsJCQkJVFNMMlg3WF9DTURf
U1BMX0ZOIHwNCj4gPj4+ICsJCQkJVFNMMlg3WF9DTURfQUxTX0lOVF9DTFIpKTsNCj4gPj4+ICsN
Cj4gPj4+ICsJaWYgKHJldDwgIDApIHsNCj4gPj4+ICsJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+
ZGV2LA0KPiA+Pj4gKwkJImkyY193cml0ZV9jb21tYW5kIGZhaWxlZCBpbiAlcywgZXJyID0gJWRc
biIsDQo+ID4+PiArCQkJX19mdW5jX18sIHJldCk7DQo+ID4+PiArCQlnb3RvIG91dF91bmxvY2s7
IC8qIGhhdmUgbm8gZGF0YSwgc28gcmV0dXJuIGZhaWx1cmUgKi8NCj4gPj4+ICsJfQ0KPiA+Pj4g
Kw0KPiA+Pj4gKwkvKiBleHRyYWN0IEFMUy9sdXggZGF0YSAqLw0KPiA+Pj4gKwljaDAgPSBsZTE2
X3RvX2NwdXAoKGNvbnN0IF9fbGUxNiAqKSZidWZbMF0pOw0KPiA+Pj4gKwljaDEgPSBsZTE2X3Rv
X2NwdXAoKGNvbnN0IF9fbGUxNiAqKSZidWZbMl0pOw0KPiA+Pj4gKw0KPiA+Pj4gKwljaGlwLT5h
bHNfY3VyX2luZm8uYWxzX2NoMCA9IGNoMDsNCj4gPj4+ICsJY2hpcC0+YWxzX2N1cl9pbmZvLmFs
c19jaDEgPSBjaDE7DQo+ID4+PiArDQo+ID4+PiArCWlmICgoY2gwPj0gY2hpcC0+YWxzX3NhdHVy
YXRpb24pIHx8IChjaDE+PSBjaGlwLT5hbHNfc2F0dXJhdGlvbikpIHsNCj4gPj4+ICsJCWx1eCA9
IFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9XOw0KPiA+Pj4gKwkJZ290byByZXR1cm5fbWF4Ow0K
PiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaDAgPT0gMCkgew0KPiA+Pj4gKwkJLyog
aGF2ZSBubyBkYXRhLCBzbyByZXR1cm4gTEFTVCBWQUxVRSAqLw0KPiA+Pj4gKwkJcmV0ID0gY2hp
cC0+YWxzX2N1cl9pbmZvLmx1eCA9IDA7DQo+ID4+PiArCQlnb3RvIG91dF91bmxvY2s7DQo+ID4+
PiArCX0NCj4gPj4+ICsJLyogY2FsY3VsYXRlIHJhdGlvICovDQo+ID4+PiArCXJhdGlvID0gKGNo
MTw8ICAxNSkgLyBjaDA7DQo+ID4+PiArCS8qIGNvbnZlcnQgdG8gdW5zY2FsZWQgbHV4IHVzaW5n
IHRoZSBwb2ludGVyIHRvIHRoZSB0YWJsZSAqLw0KPiA+Pj4gKwlwID0gKHN0cnVjdCB0c2wyeDd4
X2x1eCAqKSBjaGlwLT50c2wyeDd4X2RldmljZV9sdXg7DQo+ID4+PiArCXdoaWxlIChwLT5yYXRp
byAhPSAwJiYgIHAtPnJhdGlvPCAgcmF0aW8pDQo+ID4+PiArCQkJcCsrOw0KPiA+Pj4gKw0KPiA+
Pj4gKwlpZiAocC0+cmF0aW8gPT0gMCkgew0KPiA+Pj4gKwkJbHV4ID0gMDsNCj4gPj4+ICsJfSBl
bHNlIHsNCj4gPj4+ICsJCWNoMGx1eCA9IERJVl9ST1VORF9VUCgoY2gwICogcC0+Y2gwKSwNCj4g
Pj4+ICsJCQl0c2wyWDdYX2Fsc19nYWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dh
aW5dKTsNCj4gPj4+ICsJCWNoMWx1eCA9IERJVl9ST1VORF9VUCgoY2gxICogcC0+Y2gxKSwNCj4g
Pj4+ICsJCQl0c2wyWDdYX2Fsc19nYWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dh
aW5dKTsNCj4gPj4+ICsJCWx1eCA9IGNoMGx1eCAtIGNoMWx1eDsNCj4gPj4+ICsJfQ0KPiA+Pj4g
Kw0KPiA+Pj4gKwkvKiBub3RlOiBsdXggaXMgMzEgYml0IG1heCBhdCB0aGlzIHBvaW50ICovDQo+
ID4+PiArCWlmIChjaDFsdXg+ICBjaDBsdXgpIHsNCj4gPj4+ICsJCWRldl9kYmcoJmNoaXAtPmNs
aWVudC0+ZGV2LCAiUmV0dXJuaW5nIGxhc3QgdmFsdWVcbiIpOw0KPiA+Pj4gKwkJcmV0ID0gY2hp
cC0+YWxzX2N1cl9pbmZvLmx1eDsNCj4gPj4+ICsJCWdvdG8gb3V0X3VubG9jazsNCj4gPj4+ICsJ
fQ0KPiA+Pj4gKw0KPiA+Pj4gKwkvKiBhZGp1c3QgZm9yIGFjdGl2ZSB0aW1lIHNjYWxlICovDQo+
ID4+PiArCWlmIChjaGlwLT5hbHNfdGltZV9zY2FsZSA9PSAwKQ0KPiA+Pj4gKwkJbHV4ID0gMDsN
Cj4gPj4+ICsJZWxzZQ0KPiA+Pj4gKwkJbHV4ID0gKGx1eCArIChjaGlwLT5hbHNfdGltZV9zY2Fs
ZT4+ICAxKSkgLw0KPiA+Pj4gKwkJCWNoaXAtPmFsc190aW1lX3NjYWxlOw0KPiA+Pj4gKw0KPiA+
Pj4gKwkvKiBhZGp1c3QgZm9yIGFjdGl2ZSBnYWluIHNjYWxlDQo+ID4+PiArCSAqIFRoZSB0c2wy
eDd4X2RldmljZV9sdXggdGFibGVzIGhhdmUgYSBmYWN0b3Igb2YgMjU2IGJ1aWx0LWluLg0KPiA+
Pj4gKwkgKiBVc2VyLXNwZWNpZmllZCBnYWluIHByb3ZpZGVzIGEgbXVsdGlwbGllci4NCj4gPj4+
ICsJICogQXBwbHkgdXNlci1zcGVjaWZpZWQgZ2FpbiBiZWZvcmUgc2hpZnRpbmcgcmlnaHQgdG8g
cmV0YWluIHByZWNpc2lvbi4NCj4gPj4+ICsJICogVXNlIDY0IGJpdHMgdG8gYXZvaWQgb3ZlcmZs
b3cgb24gbXVsdGlwbGljYXRpb24uDQo+ID4+PiArCSAqIFRoZW4gZ28gYmFjayB0byAzMiBiaXRz
IGJlZm9yZSBkaXZpc2lvbiB0byBhdm9pZCB1c2luZyBkaXZfdTY0KCkuDQo+ID4+PiArCSAqLw0K
PiA+Pj4gKwlsdXg2NCA9IGx1eDsNCj4gPj4+ICsJbHV4NjQgPSBsdXg2NCAqIGNoaXAtPnRzbDJ4
N3hfc2V0dGluZ3MuYWxzX2dhaW5fdHJpbTsNCj4gPj4+ICsJbHV4NjQ+Pj0gODsNCj4gPj4+ICsJ
bHV4ID0gbHV4NjQ7DQo+ID4+PiArCWx1eCA9IChsdXggKyA1MDApIC8gMTAwMDsNCj4gPj4+ICsN
Cj4gPj4+ICsJaWYgKGx1eD4gIFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9XKSAvKiBjaGVjayBm
b3Igb3ZlcmZsb3cgKi8NCj4gPj4+ICsJCWx1eCA9IFRTTDJYN1hfTFVYX0NBTENfT1ZFUl9GTE9X
Ow0KPiA+Pj4gKw0KPiA+Pj4gKwkvKiBVcGRhdGUgdGhlIHN0cnVjdHVyZSB3aXRoIHRoZSBsYXRl
c3QgbHV4LiAqLw0KPiA+Pj4gK3JldHVybl9tYXg6DQo+ID4+PiArCWNoaXAtPmFsc19jdXJfaW5m
by5sdXggPSBsdXg7DQo+ID4+PiArCXJldCA9IGx1eDsNCj4gPj4+ICsNCj4gPj4+ICtvdXRfdW5s
b2NrOg0KPiA+Pj4gKwltdXRleF91bmxvY2soJmNoaXAtPmFsc19tdXRleCk7DQo+ID4+PiArDQo+
ID4+PiArCXJldHVybiByZXQ7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gKy8qDQo+ID4+PiAr
ICogUHJveGltaXR5IHBvbGwgZnVuY3Rpb24gLSBpZiB2YWxpZCBkYXRhIGlzIGF2YWlsYWJsZSwg
cmVhZCBhbmQgZm9ybSB0aGUgY2gwDQo+ID4+PiArICogYW5kIHByb3ggZGF0YSB2YWx1ZXMsIGNo
ZWNrIGZvciBsaW1pdHMgb24gdGhlIGNoMCB2YWx1ZSwgYW5kIGNoZWNrIHRoZQ0KPiBwcm94DQo+
ID4+PiArICogZGF0YSBhZ2FpbnN0IHRoZSBjdXJyZW50IHRocmVzaG9sZHMsIHRvIHNldCB0aGUg
ZXZlbnQgc3RhdHVzIGFjY29yZGluZ2x5Lg0KPiA+Pj4gKyAqLw0KPiA+Pj4gK3N0YXRpYyBpbnQg
dHNsMng3eF9wcm94X3BvbGwoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldikNCj4gPj4+ICt7DQo+
ID4+PiArI2RlZmluZSBDT05TRUNVVElWRV9SRVRSSUVTIDUwDQo+ID4+PiArDQo+ID4+PiArCWlu
dCBpOw0KPiA+Pj4gKwlpbnQgcmV0Ow0KPiA+Pj4gKwl1OCBzdGF0dXM7DQo+ID4+PiArCXU4IGNo
ZGF0YVsyXTsNCj4gPj4+ICsJaW50IGVycl9jbnQ7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2No
aXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAobXV0
ZXhfdHJ5bG9jaygmY2hpcC0+cHJveF9tdXRleCkgPT0gMCkgew0KPiA+Pj4gKwkJZGV2X2Vycigm
Y2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4+PiArCQkJIiVzOiBDYW4ndCBnZXQgcHJveCBtdXRleFxu
IiwgX19mdW5jX18pOw0KPiA+Pj4gKwkJcmV0dXJuIC1FQlVTWTsNCj4gPj4+ICsJfQ0KPiA+Pj4g
Kw0KPiA+Pj4gKwllcnJfY250ID0gMDsNCj4gPj4+ICsNCj4gPj4+ICt0cnlfYWdhaW46DQo+ID4+
IEknZCBsaWtlIGEgY29tbWVudCBvbiB3aHkgdGhpcyBsb29waW5nIGlzIG5lY2Vzc2FyeS4uLi4N
Cj4gPj4+ICsNCj4gPj4+ICsJcmV0ID0gdHNsMng3eF9pMmNfcmVhZChjaGlwLT5jbGllbnQsDQo+
ID4+PiArCQkoVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9TVEFUVVMpLCZzdGF0dXMpOw0KPiA+
Pj4gKwlpZiAocmV0PCAgMCkgew0KPiA+Pj4gKwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYs
DQo+ID4+PiArCQkiJXM6IGkyYyBlcnI9JWRcbiIsIF9fZnVuY19fLCByZXQpOw0KPiA+Pj4gKwkJ
Z290byBwcm94X3BvbGxfZXJyOw0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaGlw
LT5pZDwgIHRzbDI1NzIpIHsNCj4gPj4+ICsJCWlmICghKHN0YXR1cyYgIFRTTDJYN1hfU1RBX0FE
Q19WQUxJRCkpIHsNCj4gPj4+ICsJCQllcnJfY250Kys7DQo+ID4+PiArCQkJaWYgKGVycl9jbnQ+
ICBDT05TRUNVVElWRV9SRVRSSUVTKSB7DQo+ID4+PiArCQkJCWRldl9lcnIoJmNoaXAtPmNsaWVu
dC0+ZGV2LA0KPiA+Pj4gKwkJCQkiJXM6IENvbnNlYy4gcmV0cmllcyBleGNlZWRlZFxuIiwgX19m
dW5jX18pOw0KPiA+Pj4gKwkJCQlnb3RvIHByb3hfcG9sbF9lcnI7DQo+ID4+PiArCQkJfQ0KPiA+
Pj4gKwkJZ290byB0cnlfYWdhaW47DQo+ID4+PiArCQl9DQo+ID4+PiArCX0gZWxzZSB7DQo+ID4+
PiArCQlpZiAoIShzdGF0dXMmICBUU0wyWDdYX1NUQV9QUlhfVkFMSUQpKSB7DQo+ID4+PiArCQkJ
ZXJyX2NudCsrOw0KPiA+Pj4gKwkJCWlmIChlcnJfY250PiAgQ09OU0VDVVRJVkVfUkVUUklFUykg
ew0KPiA+Pj4gKwkJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQkJIiVz
OiBDb25zZWMuIHJldHJpZXMgZXhjZWVkZWRcbiIsIF9fZnVuY19fKTsNCj4gPj4+ICsJCQkJZ290
byBwcm94X3BvbGxfZXJyOw0KPiA+Pj4gKwkJCX0NCj4gPj4+ICsJCWdvdG8gdHJ5X2FnYWluOw0K
PiA+Pj4gKwkJfQ0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWZvciAoaSA9IDA7IGk8ICAy
OyBpKyspIHsNCj4gPj4+ICsJCXJldCA9IHRzbDJ4N3hfaTJjX3JlYWQoY2hpcC0+Y2xpZW50LA0K
PiA+Pj4gKwkJCShUU0wyWDdYX0NNRF9SRUcgfA0KPiA+Pj4gKwkJCQkJKFRTTDJYN1hfUFJYX0xP
ICsgaSkpLCZjaGRhdGFbaV0pOw0KPiA+Pj4gKwkJaWYgKHJldDwgIDApDQo+ID4+PiArCQkJZ290
byBwcm94X3BvbGxfZXJyOw0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWNoaXAtPnByb3hf
Y3VyX2luZm8ucHJveF9kYXRhID0gKGNoZGF0YVsxXTw8OCl8Y2hkYXRhWzBdOw0KPiA+Pj4gKwlp
ZiAoY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2RhdGEgPT0gMCkNCj4gPj4+ICsJCWdvdG8gdHJ5
X2FnYWluOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2Rh
dGE+PQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF90aHJlc19oaWdoKQ0K
PiA+Pj4gKwkJY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2ZW50ID0gMTsNCj4gPj4+ICsJZWxz
ZQ0KPiA+Pj4gKwkJY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2ZW50ID0gMDsNCj4gPj4gU28g
dGhpcyBpcyBtYW51YWxseSBwb2xsaW5nIHRoZSBldmVudCBzaWduYWwuIEknZCBhcmd1ZSB0aGF0
IHRoaXMgaXMgYQ0KPiA+PiBqb2IgZm9yIHVzZXJzcGFjZQ0KPiA+PiBpZiB0aGUgZGV2aWNlIGlz
bid0IGRvaW5nIGl0IGhhcmR3YXJlIChvciB0aGUgaW50ZXJydXB0IHNpZ25hbCBpcw0KPiA+PiBj
b25uZWN0ZWQpLg0KPiA+Pj4gKw0KPiA+Pj4gKwltdXRleF91bmxvY2soJmNoaXAtPnByb3hfbXV0
ZXgpOw0KPiA+Pj4gKwlyZXR1cm4gY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2ZW50Ow0KPiA+
Pj4gKw0KPiA+Pj4gK3Byb3hfcG9sbF9lcnI6DQo+ID4+PiArCW11dGV4X3VubG9jaygmY2hpcC0+
cHJveF9tdXRleCk7DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiByZXQ7DQo+ID4+PiArfQ0KPiA+
Pj4gKw0KPiA+Pj4gKy8qDQo+ID4+PiArICogUHJvdmlkZXMgaW5pdGlhbCBvcGVyYXRpb25hbCBw
YXJhbWV0ZXIgZGVmYXVsdHMuDQo+ID4+PiArICogVGhlc2UgZGVmYXVsdHMgbWF5IGJlIGNoYW5n
ZWQgdGhyb3VnaCB0aGUgZGV2aWNlJ3Mgc3lzZnMgZmlsZXMuDQo+ID4+PiArICovDQo+ID4+PiAr
c3RhdGljIHZvaWQgdHNsMng3eF9kZWZhdWx0cyhzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwKQ0K
PiA+Pj4gK3sNCj4gPj4+ICsJLyogSWYgT3BlcmF0aW9uYWwgc2V0dGluZ3MgZGVmaW5lZCBlbHNl
d2hlcmUuLiAqLw0KPiA+Pj4gKwlpZiAoY2hpcC0+cGRhdGEmJiAgY2hpcC0+cGRhdGEtPnBsYXRm
b3JtX2RlZmF1bHRfc2V0dGluZ3MgIT0gMCkNCj4gPj4+ICsJCW1lbWNweSgmKGNoaXAtPnRzbDJ4
N3hfc2V0dGluZ3MpLA0KPiA+Pj4gKwkJCWNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9kZWZhdWx0X3Nl
dHRpbmdzLA0KPiA+Pj4gKwkJCXNpemVvZih0c2wyeDd4X2RlZmF1bHRfc2V0dGluZ3MpKTsNCj4g
Pj4+ICsJZWxzZQ0KPiA+Pj4gKwkJbWVtY3B5KCYoY2hpcC0+dHNsMng3eF9zZXR0aW5ncyksDQo+
ID4+PiArCQkJJnRzbDJ4N3hfZGVmYXVsdF9zZXR0aW5ncywNCj4gPj4+ICsJCQlzaXplb2YodHNs
Mng3eF9kZWZhdWx0X3NldHRpbmdzKSk7DQo+ID4+PiArDQo+ID4+PiArCS8qIExvYWQgdXAgdGhl
IHByb3BlciBsdXggdGFibGUuICovDQo+ID4+PiArCWlmIChjaGlwLT5wZGF0YSYmICBjaGlwLT5w
ZGF0YS0+cGxhdGZvcm1fbHV4X3RhYmxlWzBdLnJhdGlvICE9IDApDQo+ID4+PiArCQltZW1jcHko
Y2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4LA0KPiA+Pj4gKwkJCWNoaXAtPnBkYXRhLT5wbGF0Zm9y
bV9sdXhfdGFibGUsDQo+ID4+PiArCQkJc2l6ZW9mKGNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9sdXhf
dGFibGUpKTsNCj4gPj4+ICsJZWxzZQ0KPiA+Pj4gKwkJbWVtY3B5KGNoaXAtPnRzbDJ4N3hfZGV2
aWNlX2x1eCwNCj4gPj4+ICsJCShzdHJ1Y3QgdHNsMng3eF9sdXggKil0c2wyeDd4X2RlZmF1bHRf
bHV4X3RhYmxlX2dyb3VwW2NoaXAtPmlkXSwNCj4gPj4+ICsJCQkJTUFYX0RFRkFVTFRfVEFCTEVf
QllURVMpOw0KPiA+Pj4gKw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICsvKg0KPiA+Pj4gKyAq
IE9idGFpbiBzaW5nbGUgcmVhZGluZyBhbmQgY2FsY3VsYXRlIHRoZSBhbHNfZ2Fpbl90cmltDQo+
ID4+PiArICogKGxhdGVyIHVzZWQgdG8gZGVyaXZlIGFjdHVhbCBsdXgpLg0KPiA+Pj4gKyAqIFJl
dHVybiB1cGRhdGVkIGdhaW5fdHJpbSB2YWx1ZS4NCj4gPj4+ICsgKi8NCj4gPj4+ICtzdGF0aWMg
aW50IHRzbDJ4N3hfYWxzX2NhbGlicmF0ZShzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2KQ0KPiA+
Pj4gK3sNCj4gPj4+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlv
X2Rldik7DQo+ID4+PiArCXU4IHJlZ192YWw7DQo+ID4+PiArCWludCBnYWluX3RyaW1fdmFsOw0K
PiA+Pj4gKwlpbnQgcmV0Ow0KPiA+Pj4gKwlpbnQgbHV4X3ZhbDsNCj4gPj4+ICsNCj4gPj4+ICsJ
cmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2hpcC0+Y2xpZW50LA0KPiA+Pj4gKwkJCShUU0wy
WDdYX0NNRF9SRUcgfCBUU0wyWDdYX0NOVFJMKSk7DQo+ID4+PiArCWlmIChyZXQ8ICAwKSB7DQo+
ID4+PiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCSIlczogZmFpbGVk
IHRvIHdyaXRlIENOVFJMIHJlZ2lzdGVyLCByZXQ9JWRcbiIsDQo+ID4+PiArCQlfX2Z1bmNfXywg
cmV0KTsNCj4gPj4+ICsJCXJldHVybiByZXQ7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJ
cmVnX3ZhbCA9IGkyY19zbWJ1c19yZWFkX2J5dGUoY2hpcC0+Y2xpZW50KTsNCj4gPj4+ICsJaWYg
KChyZWdfdmFsJiAgKFRTTDJYN1hfQ05UTF9BRENfRU5CTCB8IFRTTDJYN1hfQ05UTF9QV1JfT04p
KQ0KPiA+Pj4gKwkJIT0gKFRTTDJYN1hfQ05UTF9BRENfRU5CTCB8IFRTTDJYN1hfQ05UTF9QV1Jf
T04pKSB7DQo+ID4+PiArCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQki
JXM6IGZhaWxlZDogQURDIG5vdCBlbmFibGVkXG4iLCBfX2Z1bmNfXyk7DQo+ID4+PiArCQlyZXR1
cm4gLTE7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRl
X2J5dGUoY2hpcC0+Y2xpZW50LA0KPiA+Pj4gKwkJCShUU0wyWDdYX0NNRF9SRUcgfCBUU0wyWDdY
X0NOVFJMKSk7DQo+ID4+PiArCWlmIChyZXQ8ICAwKSB7DQo+ID4+PiArCQlkZXZfZXJyKCZjaGlw
LT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQkiJXM6IGZhaWxlZCB0byB3cml0ZSBjdHJsIHJlZzog
cmV0PSVkXG4iLA0KPiA+Pj4gKwkJCV9fZnVuY19fLCByZXQpOw0KPiA+Pj4gKwkJcmV0dXJuIHJl
dDsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwlyZWdfdmFsID0gaTJjX3NtYnVzX3JlYWRf
Ynl0ZShjaGlwLT5jbGllbnQpOw0KPiA+Pj4gKwlpZiAoKHJlZ192YWwmICBUU0wyWDdYX1NUQV9B
RENfVkFMSUQpICE9IFRTTDJYN1hfU1RBX0FEQ19WQUxJRCkgew0KPiA+Pj4gKwkJZGV2X2Vycigm
Y2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4+PiArCQkJIiVzOiBmYWlsZWQ6IFNUQVRVUyAtIEFEQyBu
b3QgdmFsaWQuXG4iLCBfX2Z1bmNfXyk7DQo+ID4+PiArCQlyZXR1cm4gLUVOT0RBVEE7DQo+ID4+
PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJbHV4X3ZhbCA9IHRzbDJ4N3hfZ2V0X2x1eChpbmRpb19k
ZXYpOw0KPiA+Pj4gKwlpZiAobHV4X3ZhbDwgIDApIHsNCj4gPj4+ICsJCWRldl9lcnIoJmNoaXAt
PmNsaWVudC0+ZGV2LA0KPiA+Pj4gKwkJIiVzOiBmYWlsZWQgdG8gZ2V0IGx1eFxuIiwgX19mdW5j
X18pOw0KPiA+Pj4gKwkJcmV0dXJuIGx1eF92YWw7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+
ICsJZ2Fpbl90cmltX3ZhbCA9ICAoKChjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19jYWxfdGFy
Z2V0KQ0KPiA+Pj4gKwkJCSogY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fpbl90cmltKSAv
IGx1eF92YWwpOw0KPiA+Pj4gKwlpZiAoKGdhaW5fdHJpbV92YWw8ICAyNTApIHx8IChnYWluX3Ry
aW1fdmFsPiAgNDAwMCkpDQo+ID4+PiArCQlyZXR1cm4gLUVSQU5HRTsNCj4gPj4+ICsNCj4gPj4+
ICsJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fpbl90cmltID0gZ2Fpbl90cmltX3ZhbDsN
Cj4gPj4+ICsJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+Pj4gKwkJIiVzIGFsc19j
YWxpYnJhdGUgY29tcGxldGVkXG4iLCBjaGlwLT5jbGllbnQtPm5hbWUpOw0KPiA+Pj4gKw0KPiA+
Pj4gKwlyZXR1cm4gKGludCkgZ2Fpbl90cmltX3ZhbDsNCj4gPj4+ICt9DQo+ID4+PiArDQo+ID4+
PiArLyoNCj4gPj4+ICsgKiBUdXJuIHRoZSBkZXZpY2Ugb24uDQo+ID4+PiArICogQ29uZmlndXJh
dGlvbiBtdXN0IGJlIHNldCBiZWZvcmUgY2FsbGluZyB0aGlzIGZ1bmN0aW9uLg0KPiA+Pj4gKyAq
Lw0KPiA+Pj4gK3N0YXRpYyBpbnQgdHNsMng3eF9jaGlwX29uKHN0cnVjdCBpaW9fZGV2ICppbmRp
b19kZXYpDQo+ID4+PiArew0KPiA+Pj4gKwlpbnQgaTsNCj4gPj4+ICsJaW50IHJldCA9IDA7DQo+
ID4+PiArCXU4ICpkZXZfcmVnOw0KPiA+Pj4gKwl1OCB1dG1wOw0KPiA+Pj4gKwlpbnQgYWxzX2Nv
dW50Ow0KPiA+Pj4gKwlpbnQgYWxzX3RpbWU7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAg
KmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwl1OCByZWdfdmFsID0gMDsNCj4g
Pj4+ICsNCj4gPj4+ICsJaWYgKGNoaXAtPnBkYXRhJiYgIGNoaXAtPnBkYXRhLT5wb3dlcl9vbikN
Cj4gPj4+ICsJCWNoaXAtPnBkYXRhLT5wb3dlcl9vbihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+
Pj4gKwkvKiBOb24gY2FsY3VsYXRlZCBwYXJhbWV0ZXJzICovDQo+ID4+PiArCWNoaXAtPnRzbDJ4
N3hfY29uZmlnW1RTTDJYN1hfUFJYX1RJTUVdID0NCj4gPj4+ICsJCQljaGlwLT50c2wyeDd4X3Nl
dHRpbmdzLnByeF90aW1lOw0KPiA+Pj4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1dB
SVRfVElNRV0gPQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mud2FpdF90aW1lOw0K
PiA+Pj4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1BSWF9DT05GSUddID0NCj4gPj4+
ICsJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfY29uZmlnOw0KPiA+Pj4gKw0KPiA+Pj4g
KwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX0FMU19NSU5USFJFU0hMT10gPQ0KPiA+Pj4g
KwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVzaF9sb3cpJiAgMHhGRjsNCj4gPj4+
ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9BTFNfTUlOVEhSRVNISEldID0NCj4gPj4+
ICsJCShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfbG93Pj4gIDgpJiAgMHhGRjsN
Cj4gPj4+ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9BTFNfTUFYVEhSRVNITE9dID0N
Cj4gPj4+ICsJCShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfaGlnaCkmICAweEZG
Ow0KPiA+Pj4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX0FMU19NQVhUSFJFU0hISV0g
PQ0KPiA+Pj4gKwkJKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVzaF9oaWdoPj4gIDgp
JiAgMHhGRjsNCj4gPj4+ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9QRVJTSVNURU5D
RV0gPQ0KPiA+Pj4gKwkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfcGVyc2lzdGVuY2U7DQo+
ID4+PiArDQo+ID4+PiArCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfUFJYX0NPVU5UXSA9
DQo+ID4+PiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3B1bHNlX2NvdW50Ow0KPiA+
Pj4gKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1BSWF9NSU5USFJFU0hMT10gPQ0KPiA+
Pj4gKwljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNfbG93Ow0KPiA+Pj4gKwljaGlw
LT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1BSWF9NQVhUSFJFU0hMT10gPQ0KPiA+Pj4gKwkJCWNo
aXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF90aHJlc19oaWdoOw0KPiA+Pj4gKw0KPiA+Pj4gKwkv
KiBhbmQgbWFrZSBzdXJlIHdlJ3JlIG5vdCBhbHJlYWR5IG9uICovDQo+ID4+PiArCWlmIChjaGlw
LT50c2wyeDd4X2NoaXBfc3RhdHVzID09IFRTTDJYN1hfQ0hJUF9XT1JLSU5HKSB7DQo+ID4+PiAr
CQkvKiBpZiBmb3JjaW5nIGEgcmVnaXN0ZXIgdXBkYXRlIC0gdHVybiBvZmYsIHRoZW4gb24gKi8N
Cj4gPj4+ICsJCWRldl9pbmZvKCZjaGlwLT5jbGllbnQtPmRldiwgImRldmljZSBpcyBhbHJlYWR5
IGVuYWJsZWRcbiIpOw0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiArCX0NCj4gPj4+
ICsNCj4gPj4+ICsJLyogZGV0ZXJtaW5lIGFscyBpbnRlZ3JhdGlvbiByZWdzdGVyICovDQo+ID4+
PiArCWFsc19jb3VudCA9IChjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aW1lICogMTAwICsg
MTM1KSAvIDI3MDsNCj4gPj4+ICsJaWYgKGFsc19jb3VudCA9PSAwKQ0KPiA+Pj4gKwkJYWxzX2Nv
dW50ID0gMTsgLyogZW5zdXJlIGF0IGxlYXN0IG9uZSBjeWNsZSAqLw0KPiA+Pj4gKw0KPiA+Pj4g
KwkvKiBjb252ZXJ0IGJhY2sgdG8gdGltZSAoZW5jb21wYXNzZXMgb3ZlcnJpZGVzKSAqLw0KPiA+
Pj4gKwlhbHNfdGltZSA9IChhbHNfY291bnQgKiAyNyArIDUpIC8gMTA7DQo+ID4+PiArCWNoaXAt
PnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfQUxTX1RJTUVdID0gMjU2IC0gYWxzX2NvdW50Ow0KPiA+
Pj4gKw0KPiA+Pj4gKwkvKiBTZXQgdGhlIGdhaW4gYmFzZWQgb24gdHNsMng3eF9zZXR0aW5ncyBz
dHJ1Y3QgKi8NCj4gPj4+ICsJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9HQUlOXSA9DQo+
ID4+PiArCQkoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2FpbiB8DQo+ID4+PiArCQkJKFRT
TDJYN1hfbUExMDAgfCBUU0wyWDdYX0RJT0RFMSkNCj4gPj4+ICsJCQl8ICgoY2hpcC0+dHNsMng3
eF9zZXR0aW5ncy5wcm94X2dhaW4pPDwgIDIpKTsNCj4gPj4+ICsNCj4gPj4+ICsJLyogc2V0IGNo
aXAgc3RydWN0IHJlIHNjYWxpbmcgYW5kIHNhdHVyYXRpb24gKi8NCj4gPj4+ICsJY2hpcC0+YWxz
X3NhdHVyYXRpb24gPSBhbHNfY291bnQgKiA5MjI7IC8qIDkwJSBvZiBmdWxsIHNjYWxlICovDQo+
ID4+PiArCWNoaXAtPmFsc190aW1lX3NjYWxlID0gKGFsc190aW1lICsgMjUpIC8gNTA7DQo+ID4+
PiArDQo+ID4+PiArCS8qIFRTTDJYN1ggU3BlY2lmaWMgcG93ZXItb24gLyBhZGMgZW5hYmxlIHNl
cXVlbmNlDQo+ID4+PiArCSAqIFBvd2VyIG9uIHRoZSBkZXZpY2UgMXN0LiAqLw0KPiA+Pj4gKwl1
dG1wID0gVFNMMlg3WF9DTlRMX1BXUl9PTjsNCj4gPj4+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRl
X2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQo+ID4+PiArCQlUU0wyWDdYX0NNRF9SRUcgfCBUU0wy
WDdYX0NOVFJMLCB1dG1wKTsNCj4gPj4+ICsJaWYgKHJldDwgIDApIHsNCj4gPj4+ICsJCWRldl9l
cnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+Pj4gKwkJCSIlczogZmFpbGVkIG9uIENOVFJMIHJl
Zy5cbiIsIF9fZnVuY19fKTsNCj4gPj4+ICsJCXJldHVybiAtMTsNCj4gPj4+ICsJfQ0KPiA+Pj4g
Kw0KPiA+Pj4gKwkvKiBVc2UgdGhlIGZvbGxvd2luZyBzaGFkb3cgY29weSBmb3Igb3VyIGRlbGF5
IGJlZm9yZSBlbmFibGluZyBBREMuDQo+ID4+PiArCSAqIFdyaXRlIGFsbCB0aGUgcmVnaXN0ZXJz
LiAqLw0KPiA+Pj4gKwlmb3IgKGkgPSAwLCBkZXZfcmVnID0gY2hpcC0+dHNsMng3eF9jb25maWc7
DQo+ID4+PiArCQkJaTwgIFRTTDJYN1hfTUFYX0NPTkZJR19SRUc7IGkrKykgew0KPiA+Pj4gKwkJ
cmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQo+ID4+PiArCQkJ
CVRTTDJYN1hfQ01EX1JFRyArIGksICpkZXZfcmVnKyspOw0KPiA+Pj4gKwkJaWYgKHJldDwgIDAp
IHsNCj4gPj4+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQkiJXM6
IGZhaWxlZCBvbiB3cml0ZSB0byByZWcgJWQuXG4iLCBfX2Z1bmNfXywgaSk7DQo+ID4+PiArCQkJ
cmV0dXJuIHJldDsNCj4gPj4+ICsJCX0NCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwl1ZGVs
YXkoMzAwMCk7CS8qIFBvd2VyLW9uIHNldHRsaW5nIHRpbWUgKi8NCj4gPj4+ICsNCj4gPj4+ICsJ
LyogTk9XIGVuYWJsZSB0aGUgQURDDQo+ID4+PiArCSAqIGluaXRpYWxpemUgdGhlIGRlc2lyZWQg
bW9kZSBvZiBvcGVyYXRpb24gKi8NCj4gPj4+ICsJdXRtcCA9IFRTTDJYN1hfQ05UTF9QV1JfT04g
fA0KPiA+Pj4gKwkJCVRTTDJYN1hfQ05UTF9BRENfRU5CTCB8DQo+ID4+PiArCQkJVFNMMlg3WF9D
TlRMX1BST1hfREVUX0VOQkw7DQo+ID4+PiArCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlX2Rh
dGEoY2hpcC0+Y2xpZW50LA0KPiA+Pj4gKwkJCVRTTDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfQ05U
UkwsIHV0bXApOw0KPiA+Pj4gKwlpZiAocmV0PCAgMCkgew0KPiA+Pj4gKwkJZGV2X2VycigmY2hp
cC0+Y2xpZW50LT5kZXYsDQo+ID4+PiArCQkJIiVzOiBmYWlsZWQgb24gMm5kIENUUkwgcmVnLlxu
IiwgX19mdW5jX18pOw0KPiA+Pj4gKwkJcmV0dXJuIHJldDsNCj4gPj4+ICsJCX0NCj4gPj4+ICsN
Cj4gPj4+ICsJY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyA9IFRTTDJYN1hfQ0hJUF9XT1JLSU5H
Ow0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRz
X2VuICE9IDApIHsNCj4gPj4+ICsJCWRldl9pbmZvKCZjaGlwLT5jbGllbnQtPmRldiwgIlNldHRp
bmcgVXAgSW50ZXJydXB0KHMpXG4iKTsNCj4gPj4+ICsNCj4gPj4+ICsJCXJlZ192YWwgPSBUU0wy
WDdYX0NOVExfUFdSX09OIHwNCj4gPj4gVFNMMlg3WF9DTlRMX0FEQ19FTkJMOw0KPiA+Pj4gKwkJ
aWYgKChjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gPT0gMHgyMCkgfHwNCj4g
Pj4+ICsJCQkoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuID09IDB4MzApKQ0K
PiA+Pj4gKwkJCXJlZ192YWwgfD0gVFNMMlg3WF9DTlRMX1BST1hfREVUX0VOQkw7DQo+ID4+PiAr
DQo+ID4+PiArCQlyZWdfdmFsIHw9IGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19l
bjsNCj4gPj4+ICsJCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2hpcC0+Y2xpZW50
LA0KPiA+Pj4gKwkJCShUU0wyWDdYX0NNRF9SRUcgfCBUU0wyWDdYX0NOVFJMKSwgcmVnX3ZhbCk7
DQo+ID4+PiArCQlpZiAocmV0PCAgMCkNCj4gPj4+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQt
PmRldiwNCj4gPj4+ICsJCQkJIiVzOiBmYWlsZWQgaW4gdHNsMng3eF9JT0NUTF9JTlRfU0VULlxu
IiwNCj4gPj4+ICsJCQkJX19mdW5jX18pOw0KPiA+Pj4gKw0KPiA+Pj4gKwkJLyogQ2xlYXIgb3V0
IGFueSBpbml0aWFsIGludGVycnVwdHMgICovDQo+ID4+PiArCQlyZXQgPSBpMmNfc21idXNfd3Jp
dGVfYnl0ZShjaGlwLT5jbGllbnQsDQo+ID4+PiArCQkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3
WF9DTURfU1BMX0ZOIHwNCj4gPj4+ICsJCQlUU0wyWDdYX0NNRF9QUk9YQUxTX0lOVF9DTFIpOw0K
PiA+Pj4gKwkJaWYgKHJldDwgIDApIHsNCj4gPj4+ICsJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQt
PmRldiwNCj4gPj4+ICsJCQkJIiVzOiBmYWlsZWQgaW4gdHNsMng3eF9jaGlwX29uXG4iLA0KPiA+
Pj4gKwkJCQlfX2Z1bmNfXyk7DQo+ID4+PiArCQlyZXR1cm4gcmV0Ow0KPiA+Pj4gKwkJfQ0KPiA+
Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiByZXQ7DQo+ID4+PiArfQ0KPiA+Pj4gKw0K
PiA+Pj4gK3N0YXRpYyBpbnQgdHNsMng3eF9jaGlwX29mZihzdHJ1Y3QgaWlvX2RldiAqaW5kaW9f
ZGV2KQ0KPiA+Pj4gK3sNCj4gPj4+ICsJaW50IHJldDsNCj4gPj4+ICsJc3RydWN0IHRzbDJYN1hf
Y2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4+PiArDQo+ID4+PiArCS8qIHR1
cm4gZGV2aWNlIG9mZiAqLw0KPiA+Pj4gKwljaGlwLT50c2wyeDd4X2NoaXBfc3RhdHVzID0gVFNM
Mlg3WF9DSElQX1NVU1BFTkRFRDsNCj4gPj4+ICsNCj4gPj4+ICsJcmV0ID0gaTJjX3NtYnVzX3dy
aXRlX2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQo+ID4+PiArCQlUU0wyWDdYX0NNRF9SRUcgfCBU
U0wyWDdYX0NOVFJMLCAweDAwKTsNCj4gPj4+ICsNCj4gPj4+ICsJaWYgKGNoaXAtPnBkYXRhJiYg
IGNoaXAtPnBkYXRhLT5wb3dlcl9vZmYpDQo+ID4+PiArCQljaGlwLT5wZGF0YS0+cG93ZXJfb2Zm
KGNoaXAtPmNsaWVudCk7DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiByZXQ7DQo+ID4+PiArfQ0K
PiA+Pj4gKw0KPiA+Pj4gKy8qDQo+ID4+PiArICogUHJveGltaXR5IGNhbGlicmF0aW9uIGhlbHBl
ciBmdW5jdGlvbg0KPiA+Pj4gKyAqIHJ1bnMgdGhyb3VnaCBhIGNvbGxlY3Rpb24gb2YgZGF0YSBz
YW1wbGVzLA0KPiA+Pj4gKyAqIHNldHMgdGhlIG1pbiwgbWF4LCBtZWFuLCBhbmQgc3RkIGRldi4N
Cj4gPj4+ICsgKi8NCj4gPj4+ICtzdGF0aWMNCj4gPj4+ICt2b2lkIHRzbDJ4N3hfcHJveF9jYWxj
dWxhdGUodTE2ICpkYXRhLCBpbnQgbGVuZ3RoLCBzdHJ1Y3QgcHJveF9zdGF0ICpzdGF0UCkNCj4g
Pj4+ICt7DQo+ID4+PiArCWludCBpOw0KPiA+Pj4gKwlpbnQgbWluLCBtYXgsIHN1bSwgbWVhbjsN
Cj4gPj4+ICsJdW5zaWduZWQgbG9uZyBzdGRkZXY7DQo+ID4+PiArCWludCB0bXA7DQo+ID4+PiAr
DQo+ID4+PiArCWlmIChsZW5ndGggPT0gMCkNCj4gPj4+ICsJCWxlbmd0aCA9IDE7DQo+ID4+PiAr
DQo+ID4+PiArCXN1bSA9IDA7DQo+ID4+PiArCW1pbiA9IElOVF9NQVg7DQo+ID4+PiArCW1heCA9
IElOVF9NSU47DQo+ID4+PiArCWZvciAoaSA9IDA7IGk8ICBsZW5ndGg7IGkrKykgew0KPiA+Pj4g
KwkJc3VtICs9IGRhdGFbaV07DQo+ID4+IGF2b2lkIHVzaW5nIG1pbiBhcyBhIHZhcmlhYmxlIG5h
bWUgKGFzIGl0J3MgYWxzbyBhbiBhcHByb3ByaWF0ZSBmdW5jdGlvbikNCj4gPj4gX21pbiA9IE1J
TihkYXRhW2ldLCBfbWluKTsgc2F2ZXMgeW91IGEgbGluZSBvZiBjb2RlLg0KPiA+Pj4gKwkJaWYg
KGRhdGFbaV08ICBtaW4pDQo+ID4+PiArCQkJbWluID0gZGF0YVtpXTsNCj4gPj4+ICsJCWlmIChk
YXRhW2ldPiAgbWF4KQ0KPiA+Pj4gKwkJCW1heCA9IGRhdGFbaV07DQo+ID4+PiArCX0NCj4gPj4+
ICsJbWVhbiA9IHN1bS9sZW5ndGg7DQo+ID4+PiArCXN0YXRQLT5taW4gPSBtaW47DQo+ID4+PiAr
CXN0YXRQLT5tYXggPSBtYXg7DQo+ID4+PiArCXN0YXRQLT5tZWFuID0gbWVhbjsNCj4gPj4+ICsN
Cj4gPj4+ICsJc3VtID0gMDsNCj4gPj4+ICsJZm9yIChpID0gMDsgaTwgIGxlbmd0aDsgaSsrKSB7
DQo+ID4+PiArCQl0bXAgPSBkYXRhW2ldLW1lYW47DQo+ID4+PiArCQlzdW0gKz0gdG1wICogdG1w
Ow0KPiA+Pj4gKwl9DQo+ID4+PiArCXN0ZGRldiA9IGludF9zcXJ0KChsb25nKXN1bSkvbGVuZ3Ro
Ow0KPiA+Pj4gKwlzdGF0UC0+c3RkZGV2ID0gc3RkZGV2Ow0KPiA+Pj4gK30NCj4gPj4+ICsNCj4g
Pj4+ICsvKioNCj4gPj4+ICsgKiBQcm94aW1pdHkgY2FsaWJyYXRpb24gLSBjb2xsZWN0cyBhIG51
bWJlciBvZiBzYW1wbGVzLA0KPiA+Pj4gKyAqIGNhbGN1bGF0ZXMgYSBzdGFuZGFyZCBkZXZpYXRp
b24gYmFzZWQgb24gdGhlIHNhbXBsZXMsIGFuZA0KPiA+Pj4gKyAqIHNldHMgdGhlIHRocmVzaG9s
ZCBhY2NvcmRpbmdseS4NCj4gPj4+ICsgKi8NCj4gPj4+ICtzdGF0aWMgdm9pZCB0c2wyeDd4X3By
b3hfY2FsKHN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYpDQo+ID4+PiArew0KPiA+Pj4gKwl1MTYg
cHJveF9oaXN0b3J5W01BWF9TQU1QTEVTX0NBTCsxXTsNCj4gPj4gc3BhY2VzIGFyb3VuZCB0aGF0
ICsNCj4gPj4+ICsJaW50IGk7DQo+ID4+PiArCXN0cnVjdCBwcm94X3N0YXQgcHJveF9zdGF0X2Rh
dGFbMl07DQo+ID4+PiArCXN0cnVjdCBwcm94X3N0YXQgKmNhbFA7DQo+ID4+PiArCXN0cnVjdCB0
c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwl1OCB0bXBf
aXJxX3NldHRpbmdzOw0KPiA+Pj4gKwl1OCBjdXJyZW50X3N0YXRlID0gY2hpcC0+dHNsMng3eF9j
aGlwX3N0YXR1czsNCj4gPj4+ICsNCj4gPj4+ICsJaWYgKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu
cHJveF9tYXhfc2FtcGxlc19jYWw+ICBNQVhfU0FNUExFU19DQUwpDQo+ID4+IHsNCj4gPj4+ICsJ
CWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KPiA+Pj4gKwkJCSIlczogbWF4IHByb3ggc2Ft
cGxlcyBjYWwgaXMgdG9vIGJpZzogJWRcbiIsDQo+ID4+PiArCQkJX19mdW5jX18sIGNoaXAtDQo+
ID4+PiB0c2wyeDd4X3NldHRpbmdzLnByb3hfbWF4X3NhbXBsZXNfY2FsKTsNCj4gPj4+ICsJCWNo
aXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF9tYXhfc2FtcGxlc19jYWwgPQ0KPiA+PiBNQVhfU0FN
UExFU19DQUw7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJLyogaGF2ZSB0byBzdG9wIHRv
IGNoYW5nZSBzZXR0aW5ncyAqLw0KPiA+Pj4gKwl0c2wyeDd4X2NoaXBfb2ZmKGluZGlvX2Rldik7
DQo+ID4+PiArDQo+ID4+PiArCS8qIEVuYWJsZSBwcm94aW1pdHkgZGV0ZWN0aW9uIHNhdmUganVz
dCBpbiBjYXNlIHByb3ggbm90IHdhbnRlZCB5ZXQqLw0KPiA+Pj4gKwl0bXBfaXJxX3NldHRpbmdz
ID0gY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuOw0KPiA+Pj4gKwljaGlwLT50
c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gfD0NCj4gPj4gVFNMMlg3WF9DTlRMX1BST1hf
SU5UX0VOQkw7DQo+ID4+PiArDQo+ID4+PiArCS8qdHVybiBvbiBkZXZpY2UgaWYgbm90IGFscmVh
ZHkgb24qLw0KPiA+Pj4gKwl0c2wyeDd4X2NoaXBfb24oaW5kaW9fZGV2KTsNCj4gPj4+ICsNCj4g
Pj4+ICsJLypnYXRoZXIgdGhlIHNhbXBsZXMqLw0KPiA+Pj4gKwlmb3IgKGkgPSAwOyBpPCAgY2hp
cC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X21heF9zYW1wbGVzX2NhbDsgaSsrKSB7DQo+ID4+PiAr
CQltZGVsYXkoMTUpOw0KPiA+Pj4gKwkJdHNsMng3eF9wcm94X3BvbGwoaW5kaW9fZGV2KTsNCj4g
Pj4+ICsJCXByb3hfaGlzdG9yeVtpXSA9IGNoaXAtPnByb3hfY3VyX2luZm8ucHJveF9kYXRhOw0K
PiA+Pj4gKwkJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LCAiMiBpPSVkIHByb3ggZGF0YT0g
JWRcbiIsDQo+ID4+PiArCQkJaSwgY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2RhdGEpOw0KPiA+
Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCXRzbDJ4N3hfY2hpcF9vZmYoaW5kaW9fZGV2KTsNCj4g
Pj4+ICsJY2FsUCA9JnByb3hfc3RhdF9kYXRhW1BST1hfU1RBVF9DQUxdOw0KPiA+Pj4gKwl0c2wy
eDd4X3Byb3hfY2FsY3VsYXRlKHByb3hfaGlzdG9yeSwNCj4gPj4+ICsJCWNoaXAtPnRzbDJ4N3hf
c2V0dGluZ3MucHJveF9tYXhfc2FtcGxlc19jYWwsIGNhbFApOw0KPiA+Pj4gKwljaGlwLT50c2wy
eDd4X3NldHRpbmdzLnByb3hfdGhyZXNfaGlnaCA9IChjYWxQLT5tYXg8PCAgMSkgLSBjYWxQLQ0K
PiA+Pj4gbWVhbjsNCj4gPj4+ICsNCj4gPj4+ICsJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2
LCAiIGNhbCBtaW49JWQgbWVhbj0lZCBtYXg9JWRcbiIsDQo+ID4+PiArCQljYWxQLT5taW4sIGNh
bFAtPm1lYW4sIGNhbFAtPm1heCk7DQo+ID4+PiArCWRldl9pbmZvKCZjaGlwLT5jbGllbnQtPmRl
diwNCj4gPj4+ICsJCSIlcyBwcm94aW1pdHkgdGhyZXNob2xkIHNldCB0byAlZFxuIiwNCj4gPj4+
ICsJCWNoaXAtPmNsaWVudC0+bmFtZSwgY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVz
X2hpZ2gpOw0KPiA+Pj4gKw0KPiA+Pj4gKwkvKiBiYWNrIHRvIHRoZSB3YXkgdGhleSB3ZXJlICov
DQo+ID4+PiArCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiA9IHRtcF9pcnFf
c2V0dGluZ3M7DQo+ID4+PiArCWlmIChjdXJyZW50X3N0YXRlID09IFRTTDJYN1hfQ0hJUF9XT1JL
SU5HKQ0KPiA+Pj4gKwkJdHNsMng3eF9jaGlwX29uKGluZGlvX2Rldik7DQo+ID4+PiArfQ0KPiA+
Pj4gKw0KPiA+Pj4gK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfcG93ZXJfc3RhdGVfc2hvdyhzdHJ1
Y3QgZGV2aWNlICpkZXYsDQo+ID4+PiArCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBj
aGFyICpidWYpDQo+ID4+PiArew0KPiA+Pj4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0g
ZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAg
PSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gc25wcmludGYo
YnVmLCBQQUdFX1NJWkUsICIlZFxuIiwgY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyk7DQo+ID4+
PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfcG93ZXJfc3RhdGVf
c3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0
ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVuKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJ
c3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+Pj4g
Kwlib29sIHZhbHVlOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoc3RydG9ib29sKGJ1ZiwmdmFsdWUp
KQ0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiArDQo+ID4+PiArCWlmICghdmFsdWUp
DQo+ID4+PiArCQl0c2wyeDd4X2NoaXBfb2ZmKGluZGlvX2Rldik7DQo+ID4+PiArCWVsc2UNCj4g
Pj4+ICsJCXRzbDJ4N3hfY2hpcF9vbihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1
cm4gbGVuOw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4
X2dhaW5fYXZhaWxhYmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlzdHJ1Y3Qg
ZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3Ry
dWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+Pj4gKwlz
dHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsN
Cj4gPj4+ICsJaWYgKGNoaXAtPmlkPiAgdHNsMjc3MSkNCj4gPj4+ICsJCXJldHVybiBzbnByaW50
ZihidWYsIFBBR0VfU0laRSwgIiVzXG4iLCAiMSA4IDE2IDEyOCIpOw0KPiA+Pj4gKwllbHNlDQo+
ID4+PiArCQlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlc1xuIiwgIjEgOCAxNiAx
MjAiKTsNCj4gPj4+ICt9DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIHNzaXplX3QgdHNsMng3eF9w
cm94X2dhaW5fYXZhaWxhYmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlzdHJ1
Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJ
CXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVzXG4iLCAiMSAyIDQgOCIpOw0KPiA+
Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2Fsc190aW1lX3No
b3coc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAq
YXR0ciwgY2hhciAqYnVmKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19kZXYgKmluZGlv
X2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlw
ICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsNCj4gPj4+ICsJcmV0dXJuIHNu
cHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIsDQo+ID4+PiArCQkJY2hpcC0+dHNsMng3eF9z
ZXR0aW5ncy5hbHNfdGltZSk7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBzc2l6
ZV90IHRzbDJ4N3hfYWxzX3RpbWVfc3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlz
dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVu
KQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRf
ZHJ2ZGF0YShkZXYpOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3By
aXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsJdW5zaWduZWQgbG9uZyB2YWx1ZTsNCj4gPj4+ICsNCj4g
Pj4+ICsJaWYgKGtzdHJ0b3VsKGJ1ZiwgMCwmdmFsdWUpKQ0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5W
QUw7DQo+ID4+PiArDQo+ID4+PiArCWlmICgodmFsdWU8ICA1MCkgfHwgKHZhbHVlPiAgNjUwKSkN
Cj4gPj4+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAodmFsdWUgJSA1
MCkNCj4gPj4+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+Pj4gKw0KPiA+Pj4gKwkgY2hpcC0+dHNs
Mng3eF9zZXR0aW5ncy5hbHNfdGltZSA9IHZhbHVlOw0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4g
bGVuOw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgSUlPX0NPTlNUX0FUVFIoaWxs
dW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLA0KPiA+Pj4gKwkJIjUwIDEwMCAx
NTAgMjAwIDI1MCAzMDAgMzUwIDQwMCA0NTAgNTAwIDU1MCA2MDAgNjUwIik7DQo+ID4+PiArDQo+
ID4+PiArc3RhdGljIHNzaXplX3QgdHNsMng3eF9hbHNfY2FsX3RhcmdldF9zaG93KHN0cnVjdCBk
ZXZpY2UgKmRldiwNCj4gPj4+ICsJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNoYXIg
KmJ1ZikNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBkZXZf
Z2V0X2RydmRhdGEoZGV2KTsNCj4gPj4+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlp
b19wcml2KGluZGlvX2Rldik7DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiBzbnByaW50ZihidWYs
IFBBR0VfU0laRSwgIiVkXG4iLA0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxz
X2NhbF90YXJnZXQpOw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgc3NpemVfdCB0
c2wyeDd4X2Fsc19jYWxfdGFyZ2V0X3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwNCj4gPj4+ICsJ
c3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxl
bikNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBkZXZfZ2V0
X2RydmRhdGEoZGV2KTsNCj4gPj4+ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19w
cml2KGluZGlvX2Rldik7DQo+ID4+PiArCXVuc2lnbmVkIGxvbmcgdmFsdWU7DQo+ID4+PiArDQo+
ID4+PiArCWlmIChrc3RydG91bChidWYsIDAsJnZhbHVlKSkNCj4gPj4+ICsJCXJldHVybiAtRUlO
VkFMOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAodmFsdWUpDQo+ID4+PiArCQljaGlwLT50c2wyeDd4
X3NldHRpbmdzLmFsc19jYWxfdGFyZ2V0ID0gdmFsdWU7DQo+ID4+PiArDQo+ID4+PiArCXJldHVy
biBsZW47DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gKy8qIHNhbXBsaW5nX2ZyZXF1ZW5jeSBB
S0EgcGVyc2lzdGVuY2UgaW4gZGF0YSBzaGVldCAqLw0KPiA+Pj4gK3N0YXRpYyBzc2l6ZV90IHRz
bDJ4N3hfYWxzX3BlcnNpc3RlbmNlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlz
dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+Pj4gK3sNCj4gPj4+
ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+
Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4g
Pj4+ICsNCj4gPj4+ICsJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIsDQo+
ID4+PiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfcGVyc2lzdGVuY2UpOw0KPiA+Pj4g
K30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2Fsc19wZXJzaXN0ZW5j
ZV9zdG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4+PiArCXN0cnVjdCBkZXZpY2VfYXR0cmli
dXRlICphdHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBsZW4pDQo+ID4+PiArew0KPiA+Pj4g
KwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4+
PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+
Pj4gKwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoa3N0cnRvdWwo
YnVmLCAwLCZ2YWx1ZSkpDQo+ID4+PiArCQlyZXR1cm4gLUVJTlZBTDsNCj4gPj4+ICsNCj4gPj4+
ICsJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfcGVyc2lzdGVuY2UgPSB2YWx1ZTsNCj4gPj4+
ICsNCj4gPj4+ICsJcmV0dXJuIGxlbjsNCj4gPj4+ICt9DQo+ID4+PiArDQo+ID4+PiArc3RhdGlj
IElJT19DT05TVF9BVFRSKHNhbXBsaW5nX2ZyZXF1ZW5jeV9hdmFpbGFibGUsDQo+ID4+PiArCQki
MHgwMCAtIDB4RkYgKDAgLSAyNTUpIik7DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIHNzaXplX3Qg
dHNsMng3eF9kb19jYWxpYnJhdGUoc3RydWN0IGRldmljZSAqZGV2LA0KPiA+Pj4gKwlzdHJ1Y3Qg
ZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVuKQ0KPiA+
Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0
YShkZXYpOw0KPiA+Pj4gKwlib29sIHZhbHVlOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoc3RydG9i
b29sKGJ1ZiwmdmFsdWUpKQ0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiArDQo+ID4+
PiArCWlmICh2YWx1ZSkNCj4gPj4+ICsJCXRzbDJ4N3hfYWxzX2NhbGlicmF0ZShpbmRpb19kZXYp
Ow0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gbGVuOw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+
ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2x1eHRhYmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2
LA0KPiA+Pj4gKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KPiA+
Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0
YShkZXYpOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5k
aW9fZGV2KTsNCj4gPj4+ICsJaW50IGk7DQo+ID4+PiArCWludCBvZmZzZXQgPSAwOw0KPiA+Pj4g
Kw0KPiA+Pj4gKwlpID0gMDsNCj4gPj4gU2V0IGkgYXQgdGhlIGRlY2xhcmF0aW9uIGFib3ZlLg0K
PiA+Pj4gKwl3aGlsZSAoaTwgIChUU0wyWDdYX01BWF9MVVhfVEFCTEVfU0laRSAqIDMpKSB7DQo+
ID4+PiArCQlvZmZzZXQgKz0gc25wcmludGYoYnVmICsgb2Zmc2V0LCBQQUdFX1NJWkUsICIlZCwl
ZCwlZCwiLA0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1eFtpXS5yYXRpbywNCj4g
Pj4+ICsJCQljaGlwLT50c2wyeDd4X2RldmljZV9sdXhbaV0uY2gwLA0KPiA+Pj4gKwkJCWNoaXAt
PnRzbDJ4N3hfZGV2aWNlX2x1eFtpXS5jaDEpOw0KPiA+Pj4gKwkJaWYgKGNoaXAtPnRzbDJ4N3hf
ZGV2aWNlX2x1eFtpXS5yYXRpbyA9PSAwKSB7DQo+ID4+PiArCQkJLyogV2UganVzdCBwcmludGVk
IHRoZSBmaXJzdCAiMCIgZW50cnkuDQo+ID4+PiArCQkJICogTm93IGdldCByaWQgb2YgdGhlIGV4
dHJhICIsIiBhbmQgYnJlYWsuICovDQo+ID4+PiArCQkJb2Zmc2V0LS07DQo+ID4+PiArCQkJYnJl
YWs7DQo+ID4+PiArCQl9DQo+ID4+PiArCQlpKys7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+
ICsJb2Zmc2V0ICs9IHNucHJpbnRmKGJ1ZiArIG9mZnNldCwgUEFHRV9TSVpFLCAiXG4iKTsNCj4g
Pj4+ICsJcmV0dXJuIG9mZnNldDsNCj4gPj4+ICt9DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIHNz
aXplX3QgdHNsMng3eF9sdXh0YWJsZV9zdG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQo+ID4+PiAr
CXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBs
ZW4pDQo+ID4+PiArew0KPiA+Pj4gKwlzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dl
dF9kcnZkYXRhKGRldik7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9f
cHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwlpbnQgdmFsdWVbQVJSQVlfU0laRShjaGlwLT50c2wy
eDd4X2RldmljZV9sdXgpKjMgKyAxXTsNCj4gPj4+ICsJaW50IG47DQo+ID4+PiArDQo+ID4+PiAr
CWdldF9vcHRpb25zKGJ1ZiwgQVJSQVlfU0laRSh2YWx1ZSksIHZhbHVlKTsNCj4gPj4+ICsNCj4g
Pj4+ICsJLyogV2Ugbm93IGhhdmUgYW4gYXJyYXkgb2YgaW50cyBzdGFydGluZyBhdCB2YWx1ZVsx
XSwgYW5kDQo+ID4+PiArCSAqIGVudW1lcmF0ZWQgYnkgdmFsdWVbMF0uDQo+ID4+PiArCSAqIFdl
IGV4cGVjdCBlYWNoIGdyb3VwIG9mIHRocmVlIGludHMgaXMgb25lIHRhYmxlIGVudHJ5LA0KPiA+
Pj4gKwkgKiBhbmQgdGhlIGxhc3QgdGFibGUgZW50cnkgaXMgYWxsIDAuDQo+ID4+PiArCSAqLw0K
PiA+Pj4gKwluID0gdmFsdWVbMF07DQo+ID4+PiArCWlmICgobiAlIDMpIHx8IG48ICA2IHx8DQo+
ID4+PiArCQkJbj4gICgoQVJSQVlfU0laRShjaGlwLT50c2wyeDd4X2RldmljZV9sdXgpIC0gMSkg
KiAzKSkgew0KPiA+Pj4gKwkJZGV2X2luZm8oZGV2LCAiTFVYIFRBQkxFIElOUFVUIEVSUk9SIDEg
VmFsdWVbMF09JWRcbiIsIG4pOw0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiArCX0N
Cj4gPj4+ICsNCj4gPj4+ICsJaWYgKCh2YWx1ZVsobiAtIDIpXSB8IHZhbHVlWyhuIC0gMSldIHwg
dmFsdWVbbl0pICE9IDApIHsNCj4gPj4+ICsJCWRldl9pbmZvKGRldiwgIkxVWCBUQUJMRSBJTlBV
VCBFUlJPUiAyIFZhbHVlWzBdPSVkXG4iLCBuKTsNCj4gPj4+ICsJCXJldHVybiAtRUlOVkFMOw0K
PiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaGlwLT50c2wyeDd4X2NoaXBfc3RhdHVz
ID09IFRTTDJYN1hfQ0hJUF9XT1JLSU5HKQ0KPiA+Pj4gKwkJdHNsMng3eF9jaGlwX29mZihpbmRp
b19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwkvKiBaZXJvIG91dCB0aGUgdGFibGUgKi8NCj4gPj4+
ICsJbWVtc2V0KGNoaXAtPnRzbDJ4N3hfZGV2aWNlX2x1eCwgMCwgc2l6ZW9mKGNoaXAtPnRzbDJ4
N3hfZGV2aWNlX2x1eCkpOw0KPiA+Pj4gKwltZW1jcHkoY2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4
LCZ2YWx1ZVsxXSwgKHZhbHVlWzBdICogNCkpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gbGVu
Ow0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2RvX3By
b3hfY2FsaWJyYXRlKHN0cnVjdCBkZXZpY2UgKmRldiwNCj4gPj4+ICsJc3RydWN0IGRldmljZV9h
dHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCj4gPj4+ICt7DQo+
ID4+PiArCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsN
Cj4gPj4+ICsJYm9vbCB2YWx1ZTsNCj4gPj4+ICsNCj4gPj4+ICsJaWYgKHN0cnRvYm9vbChidWYs
JnZhbHVlKSkNCj4gPj4+ICsJCXJldHVybiAtRUlOVkFMOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAo
dmFsdWUpDQo+ID4+PiArCQl0c2wyeDd4X3Byb3hfY2FsKGluZGlvX2Rldik7DQo+ID4+PiArDQo+
ID4+PiArCXJldHVybiBsZW47DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBpbnQg
dHNsMng3eF9yZWFkX2ludGVycnVwdF9jb25maWcoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwN
Cj4gPj4+ICsJCQkJCSB1NjQgZXZlbnRfY29kZSkNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCB0
c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwlpbnQgcmV0
Ow0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9DSEFOX1RZUEUo
ZXZlbnRfY29kZSkgPT0gSUlPX0xJR0hUKQ0KPiA+Pj4gKwkJcmV0ID0gISEoY2hpcC0+dHNsMng3
eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuJiAgMHgxMCk7DQo+ID4+PiArCWVsc2UNCj4gPj4+ICsJ
CXJldCA9ICEhKGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiYgIDB4MjApOw0K
PiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gcmV0Ow0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtz
dGF0aWMgaW50IHRzbDJ4N3hfd3JpdGVfaW50ZXJydXB0X2NvbmZpZyhzdHJ1Y3QgaWlvX2RldiAq
aW5kaW9fZGV2LA0KPiA+Pj4gKwkJCQkJICB1NjQgZXZlbnRfY29kZSwNCj4gPj4+ICsJCQkJCSAg
aW50IHZhbCkNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBp
aW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoSUlPX0VWRU5UX0NPREVf
RVhUUkFDVF9DSEFOX1RZUEUoZXZlbnRfY29kZSkgPT0gSUlPX0xJR0hUKQ0KPiA+PiB7DQo+ID4+
PiArCQlpZiAodmFsKQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0
c19lbiB8PSAweDEwOw0KPiA+Pj4gKwkJZWxzZQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0
dGluZ3MuaW50ZXJydXB0c19lbiY9IDB4MjA7DQo+ID4+PiArCX0gZWxzZSB7DQo+ID4+PiArCQlp
ZiAodmFsKQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiB8
PSAweDIwOw0KPiA+Pj4gKwkJZWxzZQ0KPiA+Pj4gKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu
aW50ZXJydXB0c19lbiY9IDB4MTA7DQo+ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJcmV0dXJu
IDA7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBpbnQgdHNsMng3eF93cml0ZV90
aHJlc2goc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwNCj4gPj4+ICsJCQkJICB1NjQgZXZlbnRf
Y29kZSwNCj4gPj4+ICsJCQkJICBpbnQgdmFsKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3RydWN0IHRz
bDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4+PiArDQo+ID4+PiAr
CWlmIChJSU9fRVZFTlRfQ09ERV9FWFRSQUNUX0NIQU5fVFlQRShldmVudF9jb2RlKSA9PSBJSU9f
TElHSFQpDQo+ID4+IHsNCj4gPj4+ICsJCXN3aXRjaCAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9E
SVIoZXZlbnRfY29kZSkpIHsNCj4gPj4+ICsJCWNhc2UgSUlPX0VWX0RJUl9SSVNJTkc6DQo+ID4+
PiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfdGhyZXNoX2hpZ2ggPSB2YWw7DQo+ID4+
PiArCQkJYnJlYWs7DQo+ID4+PiArCQljYXNlIElJT19FVl9ESVJfRkFMTElORzoNCj4gPj4+ICsJ
CQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfbG93ID0gdmFsOw0KPiA+Pj4gKwkJ
CWJyZWFrOw0KPiA+Pj4gKwkJZGVmYXVsdDoNCj4gPj4+ICsJCQlyZXR1cm4gLUVJTlZBTDsNCj4g
Pj4+ICsJCX0NCj4gPj4+ICsJfSBlbHNlIHsNCj4gPj4+ICsJCXN3aXRjaCAoSUlPX0VWRU5UX0NP
REVfRVhUUkFDVF9ESVIoZXZlbnRfY29kZSkpIHsNCj4gPj4+ICsJCWNhc2UgSUlPX0VWX0RJUl9S
SVNJTkc6DQo+ID4+PiArCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2hpZ2gg
PSB2YWw7DQo+ID4+PiArCQkJYnJlYWs7DQo+ID4+PiArCQljYXNlIElJT19FVl9ESVJfRkFMTElO
RzoNCj4gPj4+ICsJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNfbG93ID0gdmFs
Ow0KPiA+Pj4gKwkJCWJyZWFrOw0KPiA+Pj4gKwkJZGVmYXVsdDoNCj4gPj4+ICsJCQlyZXR1cm4g
LUVJTlZBTDsNCj4gPj4+ICsJCX0NCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4g
MDsNCj4gPj4+ICt9DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIGludCB0c2wyeDd4X3JlYWRfdGhy
ZXNoKHN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYsDQo+ID4+PiArCQkJICAgICAgIHU2NCBldmVu
dF9jb2RlLA0KPiA+Pj4gKwkJCSAgICAgICBpbnQgKnZhbCkNCj4gPj4+ICt7DQo+ID4+PiArCXN0
cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKw0K
PiA+Pj4gKwlpZiAoSUlPX0VWRU5UX0NPREVfRVhUUkFDVF9DSEFOX1RZUEUoZXZlbnRfY29kZSkg
PT0gSUlPX0xJR0hUKQ0KPiA+PiB7DQo+ID4+PiArCQlzd2l0Y2ggKElJT19FVkVOVF9DT0RFX0VY
VFJBQ1RfRElSKGV2ZW50X2NvZGUpKSB7DQo+ID4+PiArCQljYXNlIElJT19FVl9ESVJfUklTSU5H
Og0KPiA+Pj4gKwkJCSp2YWwgPSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfaGln
aDsNCj4gPj4+ICsJCQlicmVhazsNCj4gPj4+ICsJCWNhc2UgSUlPX0VWX0RJUl9GQUxMSU5HOg0K
PiA+Pj4gKwkJCSp2YWwgPSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfbG93Ow0K
PiA+Pj4gKwkJCWJyZWFrOw0KPiA+Pj4gKwkJZGVmYXVsdDoNCj4gPj4+ICsJCQlyZXR1cm4gLUVJ
TlZBTDsNCj4gPj4+ICsJCX0NCj4gPj4+ICsJfSBlbHNlIHsNCj4gPj4+ICsJCXN3aXRjaCAoSUlP
X0VWRU5UX0NPREVfRVhUUkFDVF9ESVIoZXZlbnRfY29kZSkpIHsNCj4gPj4+ICsJCWNhc2UgSUlP
X0VWX0RJUl9SSVNJTkc6DQo+ID4+PiArCQkJKnZhbCA9IGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu
cHJveF90aHJlc19oaWdoOw0KPiA+Pj4gKwkJCWJyZWFrOw0KPiA+Pj4gKwkJY2FzZSBJSU9fRVZf
RElSX0ZBTExJTkc6DQo+ID4+PiArCQkJKnZhbCA9IGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJv
eF90aHJlc19sb3c7DQo+ID4+PiArCQkJYnJlYWs7DQo+ID4+PiArCQlkZWZhdWx0Og0KPiA+Pj4g
KwkJCXJldHVybiAtRUlOVkFMOw0KPiA+Pj4gKwkJfQ0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+
PiArCXJldHVybiAwOw0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgaW50IHRzbDJ4
N3hfcmVhZF9yYXcoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldiwNCj4gPj4+ICsJCQkgICAgc3Ry
dWN0IGlpb19jaGFuX3NwZWMgY29uc3QgKmNoYW4sDQo+ID4+PiArCQkJICAgIGludCAqdmFsLA0K
PiA+Pj4gKwkJCSAgICBpbnQgKnZhbDIsDQo+ID4+PiArCQkJICAgIGxvbmcgbWFzaykNCj4gPj4+
ICt7DQo+ID4+PiArCWludCByZXQgPSAtRUlOVkFMOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9j
aGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsNCj4gPj4+ICsJc3dpdGNo
IChtYXNrKSB7DQo+ID4+PiArCWNhc2UgMDoNCj4gPj4+ICsJCXN3aXRjaCAoY2hhbi0+dHlwZSkg
ew0KPiA+Pj4gKwkJY2FzZSBJSU9fTElHSFQ6DQo+ID4+PiArCQkJdHNsMng3eF9nZXRfbHV4KGlu
ZGlvX2Rldik7DQo+ID4+PiArCQkJaWYgKGNoYW4tPnByb2Nlc3NlZF92YWwpDQo+ID4+PiArCQkJ
CSp2YWwgPSBjaGlwLT5hbHNfY3VyX2luZm8ubHV4Ow0KPiA+Pj4gKwkJCWVsc2UNCj4gPj4+ICsJ
CQkJKnZhbCA9ICgoKGNoaXAtPmFsc19jdXJfaW5mby5hbHNfY2gwPDwgIDE2KSB8DQo+ID4+PiAr
CQkJCQljaGlwLT5hbHNfY3VyX2luZm8uYWxzX2NoMSkpOw0KPiA+PiBXaHkgaGF2ZSBhIHByb2Nl
c3NlZCByZWFkIGJhY2sgYW5kIGEgcmF3IHJlYWRiYWNrPw0KPiA+Pj4gKwkJCXJldCA9IElJT19W
QUxfSU5UOw0KPiA+Pj4gKwkJCWJyZWFrOw0KPiA+Pj4gKwkJY2FzZSBJSU9fUFJPWElNSVRZOg0K
PiA+Pj4gKwkJCXRzbDJ4N3hfcHJveF9wb2xsKGluZGlvX2Rldik7DQo+ID4+PiArCQkJaWYgKGNo
YW4tPnByb2Nlc3NlZF92YWwpDQo+ID4+IEhtbS4uIHRoaXMgaXMgdWdnbHkuICBXZSBlZmZlY3Rp
dmVseSBoYXZlIHBvbGxpbmcgb2YgYW4gZXZlbnQgc3RhdHVzLg0KPiA+PiBOb3JtYWxseQ0KPiA+
PiBJJ2QgZXhwZWN0IHRoZSBldmVudCB0byBvbmx5IG9jY3VyIGFzIGEgYW4gSUlPIGV2ZW50IHJh
dGhlciB0aGFuIGJlaW5nDQo+ID4+IHJlYWRhYmxlIGxpa2UNCj4gPj4gdGhpcy4uLg0KPiA+Pj4g
KwkJCQkqdmFsID0gY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2ZW50Ow0KPiA+Pj4gKwkJCWVs
c2UNCj4gPj4+ICsJCQkJKnZhbCA9IGNoaXAtPnByb3hfY3VyX2luZm8ucHJveF9kYXRhOw0KPiA+
Pj4gKwkJCXJldCA9IElJT19WQUxfSU5UOw0KPiA+Pj4gKwkJCWJyZWFrOw0KPiA+Pj4gKwkJZGVm
YXVsdDoNCj4gPj4+ICsJCQlyZXR1cm4gLUVJTlZBTDsNCj4gPj4+ICsJCQlicmVhazsNCj4gPj4+
ICsJCX0NCj4gPj4+ICsJCWJyZWFrOw0KPiA+Pj4gKwljYXNlIElJT19DSEFOX0lORk9fQ0FMSUJT
Q0FMRToNCj4gPj4+ICsJCWlmIChjaGFuLT50eXBlID09IElJT19MSUdIVCkNCj4gPj4+ICsJCQkq
dmFsID0NCj4gPj4+ICsJCQl0c2wyWDdYX2Fsc19nYWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGlu
Z3MuYWxzX2dhaW5dOw0KPiA+Pj4gKwkJZWxzZQ0KPiA+Pj4gKwkJCSp2YWwgPQ0KPiA+Pj4gKwkJ
CXRzbDJYN1hfcHJ4X2dhaW5hZGpbY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW5dOw0K
PiA+Pj4gKwkJcmV0ID0gSUlPX1ZBTF9JTlQ7DQo+ID4+PiArCQlicmVhazsNCj4gPj4+ICsJY2Fz
ZSBJSU9fQ0hBTl9JTkZPX0NBTElCQklBUzoNCj4gPj4+ICsJCSp2YWwgPSBjaGlwLT50c2wyeDd4
X3NldHRpbmdzLmFsc19nYWluX3RyaW07DQo+ID4+PiArCQlyZXQgPSBJSU9fVkFMX0lOVDsNCj4g
Pj4+ICsJCWJyZWFrOw0KPiA+Pj4gKw0KPiA+Pj4gKwlkZWZhdWx0Og0KPiA+Pj4gKwkJcmV0ID0g
LUVJTlZBTDsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gcmV0Ow0KPiA+Pj4g
K30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgaW50IHRzbDJ4N3hfd3JpdGVfcmF3KHN0cnVjdCBp
aW9fZGV2ICppbmRpb19kZXYsDQo+ID4+PiArCQkJICAgICAgIHN0cnVjdCBpaW9fY2hhbl9zcGVj
IGNvbnN0ICpjaGFuLA0KPiA+Pj4gKwkJCSAgICAgICBpbnQgdmFsLA0KPiA+Pj4gKwkJCSAgICAg
ICBpbnQgdmFsMiwNCj4gPj4+ICsJCQkgICAgICAgbG9uZyBtYXNrKQ0KPiA+Pj4gK3sNCj4gPj4+
ICsJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQo+ID4+
PiArDQo+ID4+PiArCXN3aXRjaCAobWFzaykgew0KPiA+Pj4gKwljYXNlIElJT19DSEFOX0lORk9f
Q0FMSUJTQ0FMRToNCj4gPj4+ICsJCWlmIChjaGFuLT50eXBlID09IElJT19MSUdIVCkgew0KPiA+
Pj4gKwkJCXN3aXRjaCAodmFsKSB7DQo+ID4+PiArCQkJY2FzZSAxOg0KPiA+Pj4gKwkJCQljaGlw
LT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluID0gMDsNCj4gPj4+ICsJCQkJYnJlYWs7DQo+ID4+
PiArCQkJY2FzZSA4Og0KPiA+Pj4gKwkJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWlu
ID0gMTsNCj4gPj4+ICsJCQkJYnJlYWs7DQo+ID4+PiArCQkJY2FzZSAxNjoNCj4gPj4+ICsJCQkJ
Y2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2FpbiA9IDI7DQo+ID4+PiArCQkJCWJyZWFrOw0K
PiA+Pj4gKwkJCWNhc2UgMTIwOg0KPiA+Pj4gKwkJCQlpZiAoY2hpcC0+aWQ+ICB0c2wyNzcxKQ0K
PiA+Pj4gKwkJCQkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiArCQkJCWNoaXAtPnRzbDJ4N3hfc2V0
dGluZ3MuYWxzX2dhaW4gPSAzOw0KPiA+Pj4gKwkJCQlicmVhazsNCj4gPj4+ICsJCQljYXNlIDEy
ODoNCj4gPj4+ICsJCQkJaWYgKGNoaXAtPmlkPCAgdHNsMjU3MikNCj4gPj4+ICsJCQkJCXJldHVy
biAtRUlOVkFMOw0KPiA+Pj4gKwkJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluID0g
MzsNCj4gPj4+ICsJCQkJYnJlYWs7DQo+ID4+PiArCQkJZGVmYXVsdDoNCj4gPj4+ICsJCQkJcmV0
dXJuIC1FSU5WQUw7DQo+ID4+PiArCQkJfQ0KPiA+Pj4gKwkJfSBlbHNlIHsNCj4gPj4+ICsJCQlz
d2l0Y2ggKHZhbCkgew0KPiA+Pj4gKwkJCWNhc2UgMToNCj4gPj4+ICsJCQkJY2hpcC0+dHNsMng3
eF9zZXR0aW5ncy5wcm94X2dhaW4gPSAwOw0KPiA+Pj4gKwkJCQlicmVhazsNCj4gPj4+ICsJCQlj
YXNlIDI6DQo+ID4+PiArCQkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF9nYWluID0gMTsN
Cj4gPj4+ICsJCQkJYnJlYWs7DQo+ID4+PiArCQkJY2FzZSA0Og0KPiA+Pj4gKwkJCQljaGlwLT50
c2wyeDd4X3NldHRpbmdzLnByb3hfZ2FpbiA9IDI7DQo+ID4+PiArCQkJCWJyZWFrOw0KPiA+Pj4g
KwkJCWNhc2UgODoNCj4gPj4+ICsJCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW4g
PSAzOw0KPiA+Pj4gKwkJCQlicmVhazsNCj4gPj4+ICsJCQlkZWZhdWx0Og0KPiA+Pj4gKwkJCQly
ZXR1cm4gLUVJTlZBTDsNCj4gPj4+ICsJCQl9DQo+ID4+PiArCQl9DQo+ID4+PiArCQlicmVhazsN
Cj4gPj4+ICsJY2FzZSBJSU9fQ0hBTl9JTkZPX0NBTElCQklBUzoNCj4gPj4+ICsJCWNoaXAtPnRz
bDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5fdHJpbSA9IHZhbDsNCj4gPj4+ICsJCWJyZWFrOw0KPiA+
Pj4gKw0KPiA+Pj4gKwlkZWZhdWx0Og0KPiA+Pj4gKwkJcmV0dXJuIC1FSU5WQUw7DQo+ID4+PiAr
CX0NCj4gPj4+ICsNCj4gPj4+ICsJcmV0dXJuIDA7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4g
K3N0YXRpYyBERVZJQ0VfQVRUUihwb3dlcl9zdGF0ZSwgU19JUlVHTyB8IFNfSVdVU1IsDQo+ID4+
PiArCQl0c2wyeDd4X3Bvd2VyX3N0YXRlX3Nob3csIHRzbDJ4N3hfcG93ZXJfc3RhdGVfc3RvcmUp
Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBERVZJQ0VfQVRUUihwcm94aW1pdHlfY2FsaWJzY2Fs
ZV9hdmFpbGFibGUsIFNfSVJVR08sDQo+ID4+PiArCQl0c2wyeDd4X3Byb3hfZ2Fpbl9hdmFpbGFi
bGVfc2hvdywgTlVMTCk7DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIERFVklDRV9BVFRSKGlsbHVt
aW5hbmNlMF9jYWxpYnNjYWxlX2F2YWlsYWJsZSwgU19JUlVHTywNCj4gPj4+ICsJCXRzbDJ4N3hf
Z2Fpbl9hdmFpbGFibGVfc2hvdywgTlVMTCk7DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIERFVklD
RV9BVFRSKGlsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLCBTX0lSVUdPIHwgU19JV1VTUiwN
Cj4gPj4+ICsJCXRzbDJ4N3hfYWxzX3RpbWVfc2hvdywgdHNsMng3eF9hbHNfdGltZV9zdG9yZSk7
DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIERFVklDRV9BVFRSKGlsbHVtaW5hbmNlMF90YXJnZXRf
aW5wdXQsIFNfSVJVR08gfCBTX0lXVVNSLA0KPiA+Pj4gKwkJdHNsMng3eF9hbHNfY2FsX3Rhcmdl
dF9zaG93LCB0c2wyeDd4X2Fsc19jYWxfdGFyZ2V0X3N0b3JlKTsNCj4gPj4+ICsNCj4gPj4+ICtz
dGF0aWMgREVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2NhbGlicmF0ZSwgU19JV1VTUiwgTlVMTCwN
Cj4gPj4+ICsJCXRzbDJ4N3hfZG9fY2FsaWJyYXRlKTsNCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMg
REVWSUNFX0FUVFIocHJveGltaXR5X2NhbGlicmF0ZSwgU19JV1VTUiwgTlVMTCwNCj4gPj4+ICsJ
CXRzbDJ4N3hfZG9fcHJveF9jYWxpYnJhdGUpOw0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBERVZJ
Q0VfQVRUUihpbGx1bWluYW5jZTBfbHV4X3RhYmxlLCBTX0lSVUdPIHwgU19JV1VTUiwNCj4gPj4+
ICsJCXRzbDJ4N3hfbHV4dGFibGVfc2hvdywgdHNsMng3eF9sdXh0YWJsZV9zdG9yZSk7DQo+ID4+
PiArDQo+ID4+PiArc3RhdGljIERFVklDRV9BVFRSKHNhbXBsaW5nX2ZyZXF1ZW5jeSwgU19JUlVH
TyB8IFNfSVdVU1IsDQo+ID4+PiArCQl0c2wyeDd4X2Fsc19wZXJzaXN0ZW5jZV9zaG93LCB0c2wy
eDd4X2Fsc19wZXJzaXN0ZW5jZV9zdG9yZSk7DQo+ID4+PiArDQo+ID4+PiArLyogVXNlIHRoZSBk
ZWZhdWx0IHJlZ2lzdGVyIHZhbHVlcyB0byBpZGVudGlmeSB0aGUgVGFvcyBkZXZpY2UgKi8NCj4g
Pj4+ICtzdGF0aWMgaW50IHRzbDJ4N3hfZGV2aWNlX2lkKHVuc2lnbmVkIGNoYXIgKmlkLCBpbnQg
dGFyZ2V0KQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3dpdGNoICh0YXJnZXQpIHsNCj4gPj4+ICsJY2Fz
ZSB0c2wyNTcxOg0KPiA+Pj4gKwljYXNlIHRzbDI2NzE6DQo+ID4+PiArCWNhc2UgdHNsMjc3MToN
Cj4gPj4+ICsJCXJldHVybiAoKCppZCYgIDB4ZjApID09IFRSSVRPTl9JRCk7DQo+ID4+PiArCWJy
ZWFrOw0KPiA+Pj4gKwljYXNlIHRtZDI2NzE6DQo+ID4+PiArCWNhc2UgdG1kMjc3MToNCj4gPj4+
ICsJCXJldHVybiAoKCppZCYgIDB4ZjApID09IEhBTElCVVRfSUQpOw0KPiA+Pj4gKwlicmVhazsN
Cj4gPj4+ICsJY2FzZSB0c2wyNTcyOg0KPiA+Pj4gKwljYXNlIHRzbDI2NzI6DQo+ID4+PiArCWNh
c2UgdG1kMjY3MjoNCj4gPj4+ICsJY2FzZSB0c2wyNzcyOg0KPiA+Pj4gKwljYXNlIHRtZDI3NzI6
DQo+ID4+PiArCQlyZXR1cm4gKCgqaWQmICAweGYwKSA9PSBTV09SREZJU0hfSUQpOw0KPiA+Pj4g
KwlicmVhazsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gLUVJTlZBTDsNCj4g
Pj4+ICt9DQo+ID4+PiArDQo+ID4+PiArLyoNCj4gPj4+ICsgKiBJbnRlcnJ1cHQgRXZlbnQgSGFu
ZGxlciAqLw0KPiA+Pj4gK3N0YXRpYyBpcnFyZXR1cm5fdCB0c2wyeDd4X2V2ZW50X2hhbmRsZXIo
aW50IGlycSwgdm9pZCAqcHJpdmF0ZSkNCj4gPj4+ICt7DQo+ID4+PiArCXN0cnVjdCBpaW9fZGV2
ICppbmRpb19kZXYgPSBwcml2YXRlOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlw
ID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsJczY0IHRpbWVzdGFtcCA9IGlpb19nZXRf
dGltZV9ucygpOw0KPiA+Pj4gKwlpbnQgcmV0Ow0KPiA+Pj4gKwlpbnQgdmFsdWU7DQo+ID4+PiAr
DQo+ID4+PiArCXZhbHVlID0gaTJjX3NtYnVzX3JlYWRfYnl0ZV9kYXRhKGNoaXAtPmNsaWVudCwN
Cj4gPj4+ICsJCVRTTDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfU1RBVFVTKTsNCj4gPj4+ICsNCj4g
Pj4+ICsJLyogV2hhdCB0eXBlIG9mIGludGVycnVwdCBkbyB3ZSBuZWVkIHRvIHByb2Nlc3MgKi8N
Cj4gPj4+ICsJaWYgKHZhbHVlJiAgVFNMMlg3WF9TVEFfUFJYX0lOVFIpIHsNCj4gPj4+ICsJCXRz
bDJ4N3hfcHJveF9wb2xsKGluZGlvX2Rldik7DQo+ID4+PiArCQlpaW9fcHVzaF9ldmVudChpbmRp
b19kZXYsDQo+ID4+PiArCQkJICAgICAgIElJT19VTk1PRF9FVkVOVF9DT0RFKElJT19QUk9YSU1J
VFksDQo+ID4+PiArCQkJCQkJICAgIDAsDQo+ID4+PiArCQkJCQkJICAgIElJT19FVl9UWVBFX1RI
UkVTSCwNCj4gPj4+ICsJCQkJCQkgICAgSUlPX0VWX0RJUl9FSVRIRVIpLA0KPiA+Pj4gKwkJCQkJ
CSAgICB0aW1lc3RhbXApOw0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWlmICh2YWx1ZSYg
IFRTTDJYN1hfU1RBX0FMU19JTlRSKSB7DQo+ID4+PiArCQl0c2wyeDd4X2dldF9sdXgoaW5kaW9f
ZGV2KTsNCj4gPj4gd2h5IHRoZSBnZXQgdmFsdWU/ICBObyByZWFsIGd1YXJhbnRlZSB5b3UnbGwg
Z2V0IHRoZSBvbmUgdGhhdCBjYXVzZWQgdGhlDQo+ID4+IGV2ZW50IGFzIGZhciBhcyBJIGNhbiBz
ZWUuDQo+ID4+PiArCQlpaW9fcHVzaF9ldmVudChpbmRpb19kZXYsDQo+ID4+PiArCQkgICAgICAg
SUlPX1VOTU9EX0VWRU5UX0NPREUoSUlPX0xJR0hULA0KPiA+Pj4gKwkJCQkJICAgIDAsDQo+ID4+
PiArCQkJCQkgICAgSUlPX0VWX1RZUEVfVEhSRVNILA0KPiA+Pj4gKwkJCQkJICAgIElJT19FVl9E
SVJfRUlUSEVSKSwNCj4gPj4+ICsJCQkJCSAgICB0aW1lc3RhbXApOw0KPiA+Pj4gKwl9DQo+ID4+
PiArCS8qIENsZWFyIGludGVycnVwdCBub3cgdGhhdCB3ZSBoYXZlIHRoZSBzdGF0dXMgKi8NCj4g
Pj4+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2hpcC0+Y2xpZW50LA0KPiA+Pj4gKwkJ
VFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTURfU1BMX0ZOIHwNCj4gPj4+ICsJCVRTTDJYN1hf
Q01EX1BST1hBTFNfSU5UX0NMUik7DQo+ID4+PiArCWlmIChyZXQ8ICAwKQ0KPiA+Pj4gKwkJZGV2
X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQo+ID4+PiArCQkJIiVzOiBGYWlsZWQgdG8gY2xlYXIg
aXJxIGZyb20gZXZlbnQgaGFuZGxlci4gZXJyID0gJWRcbiIsDQo+ID4+PiArCQkJX19mdW5jX18s
IHJldCk7DQo+ID4+PiArDQo+ID4+PiArCXJldHVybiBJUlFfSEFORExFRDsNCj4gPj4+ICt9DQo+
ID4+PiArDQo+ID4+PiArc3RhdGljIHN0cnVjdCBhdHRyaWJ1dGUgKnRzbDJ4N3hfQUxTX2Rldmlj
ZV9hdHRyc1tdID0gew0KPiA+Pj4gKwkmZGV2X2F0dHJfcG93ZXJfc3RhdGUuYXR0ciwNCj4gPj4+
ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRyLA0KPiA+
Pj4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWUuYXR0ciwNCj4gPj4+
ICsJJmlpb19jb25zdF9hdHRyX2lsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lX2F2YWlsYWJs
ZS5kZXZfYXR0ci5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX3RhcmdldF9p
bnB1dC5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2NhbGlicmF0ZS5hdHRy
LA0KPiA+Pj4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2x1eF90YWJsZS5hdHRyLA0KPiA+Pj4g
KwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4+PiArCSZpaW9fY29uc3Rf
YXR0cl9zYW1wbGluZ19mcmVxdWVuY3lfYXZhaWxhYmxlLmRldl9hdHRyLmF0dHIsDQo+ID4+PiAr
CU5VTEwNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBzdHJ1Y3QgYXR0cmlidXRl
ICp0c2wyeDd4X1BSWF9kZXZpY2VfYXR0cnNbXSA9IHsNCj4gPj4+ICsJJmRldl9hdHRyX3Bvd2Vy
X3N0YXRlLmF0dHIsDQo+ID4+PiArCSZkZXZfYXR0cl9zYW1wbGluZ19mcmVxdWVuY3kuYXR0ciwN
Cj4gPj4+ICsJJmlpb19jb25zdF9hdHRyX3NhbXBsaW5nX2ZyZXF1ZW5jeV9hdmFpbGFibGUuZGV2
X2F0dHIuYXR0ciwNCj4gPj4+ICsJJmRldl9hdHRyX3Byb3hpbWl0eV9jYWxpYnJhdGUuYXR0ciwN
Cj4gPj4+ICsJTlVMTA0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+PiArc3RhdGljIHN0cnVjdCBh
dHRyaWJ1dGUgKnRzbDJ4N3hfQUxTUFJYX2RldmljZV9hdHRyc1tdID0gew0KPiA+Pj4gKwkmZGV2
X2F0dHJfcG93ZXJfc3RhdGUuYXR0ciwNCj4gPj4+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9j
YWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2Uw
X2ludGVncmF0aW9uX3RpbWUuYXR0ciwNCj4gPj4+ICsJJmlpb19jb25zdF9hdHRyX2lsbHVtaW5h
bmNlMF9pbnRlZ3JhdGlvbl90aW1lX2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRyLA0KPiA+Pj4gKwkm
ZGV2X2F0dHJfaWxsdW1pbmFuY2UwX3RhcmdldF9pbnB1dC5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0
dHJfaWxsdW1pbmFuY2UwX2NhbGlicmF0ZS5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfaWxsdW1p
bmFuY2UwX2x1eF90YWJsZS5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVl
bmN5LmF0dHIsDQo+ID4+PiArCSZpaW9fY29uc3RfYXR0cl9zYW1wbGluZ19mcmVxdWVuY3lfYXZh
aWxhYmxlLmRldl9hdHRyLmF0dHIsDQo+ID4+PiArCSZkZXZfYXR0cl9wcm94aW1pdHlfY2FsaWJy
YXRlLmF0dHIsDQo+ID4+PiArCU5VTEwNCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRp
YyBzdHJ1Y3QgYXR0cmlidXRlICp0c2wyeDd4X1BSWDJfZGV2aWNlX2F0dHJzW10gPSB7DQo+ID4+
PiArCSZkZXZfYXR0cl9wb3dlcl9zdGF0ZS5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfc2FtcGxp
bmdfZnJlcXVlbmN5LmF0dHIsDQo+ID4+PiArCSZpaW9fY29uc3RfYXR0cl9zYW1wbGluZ19mcmVx
dWVuY3lfYXZhaWxhYmxlLmRldl9hdHRyLmF0dHIsDQo+ID4+PiArCSZkZXZfYXR0cl9wcm94aW1p
dHlfY2FsaWJyYXRlLmF0dHIsDQo+ID4+PiArCSZkZXZfYXR0cl9wcm94aW1pdHlfY2FsaWJzY2Fs
ZV9hdmFpbGFibGUuYXR0ciwNCj4gPj4+ICsJTlVMTA0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+
PiArc3RhdGljIHN0cnVjdCBhdHRyaWJ1dGUgKnRzbDJ4N3hfQUxTUFJYMl9kZXZpY2VfYXR0cnNb
XSA9IHsNCj4gPj4+ICsJJmRldl9hdHRyX3Bvd2VyX3N0YXRlLmF0dHIsDQo+ID4+PiArCSZkZXZf
YXR0cl9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZV9hdmFpbGFibGUuYXR0ciwNCj4gPj4+ICsJJmRl
dl9hdHRyX2lsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLmF0dHIsDQo+ID4+PiArCSZpaW9f
Y29uc3RfYXR0cl9pbGx1bWluYW5jZTBfaW50ZWdyYXRpb25fdGltZV9hdmFpbGFibGUuZGV2X2F0
dHIuYXR0ciwNCj4gPj4+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF90YXJnZXRfaW5wdXQuYXR0
ciwNCj4gPj4+ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnJhdGUuYXR0ciwNCj4gPj4+
ICsJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9sdXhfdGFibGUuYXR0ciwNCj4gPj4+ICsJJmRldl9h
dHRyX3NhbXBsaW5nX2ZyZXF1ZW5jeS5hdHRyLA0KPiA+Pj4gKwkmaWlvX2NvbnN0X2F0dHJfc2Ft
cGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5kZXZfYXR0ci5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0
dHJfcHJveGltaXR5X2NhbGlicmF0ZS5hdHRyLA0KPiA+Pj4gKwkmZGV2X2F0dHJfcHJveGltaXR5
X2NhbGlic2NhbGVfYXZhaWxhYmxlLmF0dHIsDQo+ID4+PiArCU5VTEwNCj4gPj4+ICt9Ow0KPiA+
Pj4gKw0KPiA+Pj4gK3N0YXRpYyBjb25zdCBzdHJ1Y3QgYXR0cmlidXRlX2dyb3VwIHRzbDJYN1hf
ZGV2aWNlX2F0dHJfZ3JvdXBfdGJsW10gPSB7DQo+ID4+PiArCVtBTFNdID0gew0KPiA+Pj4gKwkJ
LmF0dHJzID0gdHNsMng3eF9BTFNfZGV2aWNlX2F0dHJzLA0KPiA+Pj4gKwl9LA0KPiA+Pj4gKwlb
UFJYXSA9IHsNCj4gPj4+ICsJCS5hdHRycyA9IHRzbDJ4N3hfUFJYX2RldmljZV9hdHRycywNCj4g
Pj4+ICsJfSwNCj4gPj4+ICsJW0FMU1BSWF0gPSB7DQo+ID4+PiArCQkuYXR0cnMgPSB0c2wyeDd4
X0FMU1BSWF9kZXZpY2VfYXR0cnMsDQo+ID4+PiArCX0sDQo+ID4+PiArCVtQUlgyXSA9IHsNCj4g
Pj4+ICsJCS5hdHRycyA9IHRzbDJ4N3hfUFJYMl9kZXZpY2VfYXR0cnMsDQo+ID4+PiArCX0sDQo+
ID4+PiArCVtBTFNQUlgyXSA9IHsNCj4gPj4+ICsJCS5hdHRycyA9IHRzbDJ4N3hfQUxTUFJYMl9k
ZXZpY2VfYXR0cnMsDQo+ID4+PiArCX0sDQo+ID4+PiArfTsNCj4gPj4+ICsNCj4gPj4+ICtzdGF0
aWMgY29uc3Qgc3RydWN0IGlpb19pbmZvIHRzbDJYN1hfZGV2aWNlX2luZm9bXSA9IHsNCj4gPj4+
ICsJW0FMU10gPSB7DQo+ID4+PiArCQkuYXR0cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3Vw
X3RibFtBTFNdLA0KPiA+Pj4gKwkJLmRyaXZlcl9tb2R1bGUgPSBUSElTX01PRFVMRSwNCj4gPj4+
ICsJCS5yZWFkX3JhdyA9JnRzbDJ4N3hfcmVhZF9yYXcsDQo+ID4+PiArCQkud3JpdGVfcmF3ID0m
dHNsMng3eF93cml0ZV9yYXcsDQo+ID4+PiArCQkucmVhZF9ldmVudF92YWx1ZSA9JnRzbDJ4N3hf
cmVhZF90aHJlc2gsDQo+ID4+PiArCQkud3JpdGVfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3dyaXRl
X3RocmVzaCwNCj4gPj4+ICsJCS5yZWFkX2V2ZW50X2NvbmZpZyA9JnRzbDJ4N3hfcmVhZF9pbnRl
cnJ1cHRfY29uZmlnLA0KPiA+Pj4gKwkJLndyaXRlX2V2ZW50X2NvbmZpZyA9JnRzbDJ4N3hfd3Jp
dGVfaW50ZXJydXB0X2NvbmZpZywNCj4gPj4+ICsJfSwNCj4gPj4+ICsJW1BSWF0gPSB7DQo+ID4+
PiArCQkuYXR0cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3RibFtQUlhdLA0KPiA+Pj4g
KwkJLmRyaXZlcl9tb2R1bGUgPSBUSElTX01PRFVMRSwNCj4gPj4+ICsJCS5yZWFkX3JhdyA9JnRz
bDJ4N3hfcmVhZF9yYXcsDQo+ID4+PiArCQkud3JpdGVfcmF3ID0mdHNsMng3eF93cml0ZV9yYXcs
DQo+ID4+PiArCQkucmVhZF9ldmVudF92YWx1ZSA9JnRzbDJ4N3hfcmVhZF90aHJlc2gsDQo+ID4+
PiArCQkud3JpdGVfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3dyaXRlX3RocmVzaCwNCj4gPj4+ICsJ
CS5yZWFkX2V2ZW50X2NvbmZpZyA9JnRzbDJ4N3hfcmVhZF9pbnRlcnJ1cHRfY29uZmlnLA0KPiA+
Pj4gKwkJLndyaXRlX2V2ZW50X2NvbmZpZyA9JnRzbDJ4N3hfd3JpdGVfaW50ZXJydXB0X2NvbmZp
ZywNCj4gPj4+ICsJfSwNCj4gPj4+ICsJW0FMU1BSWF0gPSB7DQo+ID4+PiArCQkuYXR0cnMgPSZ0
c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3RibFtBTFNQUlhdLA0KPiA+Pj4gKwkJLmRyaXZlcl9t
b2R1bGUgPSBUSElTX01PRFVMRSwNCj4gPj4+ICsJCS5yZWFkX3JhdyA9JnRzbDJ4N3hfcmVhZF9y
YXcsDQo+ID4+PiArCQkud3JpdGVfcmF3ID0mdHNsMng3eF93cml0ZV9yYXcsDQo+ID4+PiArCQku
cmVhZF9ldmVudF92YWx1ZSA9JnRzbDJ4N3hfcmVhZF90aHJlc2gsDQo+ID4+PiArCQkud3JpdGVf
ZXZlbnRfdmFsdWUgPSZ0c2wyeDd4X3dyaXRlX3RocmVzaCwNCj4gPj4+ICsJCS5yZWFkX2V2ZW50
X2NvbmZpZyA9JnRzbDJ4N3hfcmVhZF9pbnRlcnJ1cHRfY29uZmlnLA0KPiA+Pj4gKwkJLndyaXRl
X2V2ZW50X2NvbmZpZyA9JnRzbDJ4N3hfd3JpdGVfaW50ZXJydXB0X2NvbmZpZywNCj4gPj4+ICsJ
fSwNCj4gPj4+ICsJW1BSWDJdID0gew0KPiA+Pj4gKwkJLmF0dHJzID0mdHNsMlg3WF9kZXZpY2Vf
YXR0cl9ncm91cF90YmxbUFJYMl0sDQo+ID4+PiArCQkuZHJpdmVyX21vZHVsZSA9IFRISVNfTU9E
VUxFLA0KPiA+Pj4gKwkJLnJlYWRfcmF3ID0mdHNsMng3eF9yZWFkX3JhdywNCj4gPj4+ICsJCS53
cml0ZV9yYXcgPSZ0c2wyeDd4X3dyaXRlX3JhdywNCj4gPj4+ICsJCS5yZWFkX2V2ZW50X3ZhbHVl
ID0mdHNsMng3eF9yZWFkX3RocmVzaCwNCj4gPj4+ICsJCS53cml0ZV9ldmVudF92YWx1ZSA9JnRz
bDJ4N3hfd3JpdGVfdGhyZXNoLA0KPiA+Pj4gKwkJLnJlYWRfZXZlbnRfY29uZmlnID0mdHNsMng3
eF9yZWFkX2ludGVycnVwdF9jb25maWcsDQo+ID4+PiArCQkud3JpdGVfZXZlbnRfY29uZmlnID0m
dHNsMng3eF93cml0ZV9pbnRlcnJ1cHRfY29uZmlnLA0KPiA+Pj4gKwl9LA0KPiA+Pj4gKwlbQUxT
UFJYMl0gPSB7DQo+ID4+PiArCQkuYXR0cnMgPSZ0c2wyWDdYX2RldmljZV9hdHRyX2dyb3VwX3Ri
bFtBTFNQUlgyXSwNCj4gPj4+ICsJCS5kcml2ZXJfbW9kdWxlID0gVEhJU19NT0RVTEUsDQo+ID4+
PiArCQkucmVhZF9yYXcgPSZ0c2wyeDd4X3JlYWRfcmF3LA0KPiA+Pj4gKwkJLndyaXRlX3JhdyA9
JnRzbDJ4N3hfd3JpdGVfcmF3LA0KPiA+Pj4gKwkJLnJlYWRfZXZlbnRfdmFsdWUgPSZ0c2wyeDd4
X3JlYWRfdGhyZXNoLA0KPiA+Pj4gKwkJLndyaXRlX2V2ZW50X3ZhbHVlID0mdHNsMng3eF93cml0
ZV90aHJlc2gsDQo+ID4+PiArCQkucmVhZF9ldmVudF9jb25maWcgPSZ0c2wyeDd4X3JlYWRfaW50
ZXJydXB0X2NvbmZpZywNCj4gPj4+ICsJCS53cml0ZV9ldmVudF9jb25maWcgPSZ0c2wyeDd4X3dy
aXRlX2ludGVycnVwdF9jb25maWcsDQo+ID4+PiArCX0sDQo+ID4+PiArfTsNCj4gPj4+ICsNCj4g
Pj4+ICtzdGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfY2hpcF9pbmZvIHRzbDJ4N3hfY2hpcF9p
bmZvX3RibFtdID0gew0KPiA+Pj4gKwlbQUxTXSA9IHsNCj4gPj4+ICsJCS5jaGFubmVsID0gew0K
PiA+Pj4gKwkJCVswXSA9IHsNCj4gPj4+ICsJCQkJLnR5cGUgPSBJSU9fTElHSFQsDQo+ID4+PiAr
CQkJCS5pbmRleGVkID0gMSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4gKwkJCQku
cHJvY2Vzc2VkX3ZhbCA9IDEsDQo+ID4+PiArCQkJfSwNCj4gPj4+ICsJCQlbMV0gPSB7DQo+ID4+
PiArCQkJCS50eXBlID0gSUlPX0xJR0hULA0KPiA+Pj4gKwkJCQkuaW5kZXhlZCA9IDEsDQo+ID4+
PiArCQkJCS5jaGFubmVsID0gMCwNCj4gPj4+ICsJCQkJLmluZm9fbWFzayA9DQo+ID4+PiArDQo+
ID4+IAlJSU9fQ0hBTl9JTkZPX0NBTElCU0NBTEVfU0VQQVJBVEVfQklUIHwNCj4gPj4+ICsNCj4g
Pj4gCUlJT19DSEFOX0lORk9fQ0FMSUJCSUFTX1NFUEFSQVRFX0JJVCwNCj4gPj4+ICsJCQkJLmV2
ZW50X21hc2sgPSBUU0wyWDdYX0VWRU5UX01BU0sNCj4gPj4+ICsJCQl9LA0KPiA+Pj4gKwkJfSwN
Cj4gPj4+ICsJLm51bV9jaGFubmVscyA9IDIsDQo+ID4+PiArCS5pbmZvID0mdHNsMlg3WF9kZXZp
Y2VfaW5mb1tBTFNdLA0KPiA+Pj4gKwl9LA0KPiA+Pj4gKwlbUFJYXSA9IHsNCj4gPj4+ICsJCS5j
aGFubmVsID0gew0KPiA+Pj4gKwkJCVswXSA9IHsNCj4gPj4+ICsJCQkJLnR5cGUgPSBJSU9fUFJP
WElNSVRZLA0KPiA+Pj4gKwkJCQkuaW5kZXhlZCA9IDEsDQo+ID4+PiArCQkJCS5jaGFubmVsID0g
MCwNCj4gPj4+ICsJCQkJLnByb2Nlc3NlZF92YWwgPSAxLA0KPiA+Pj4gKwkJCX0sDQo+ID4+PiAr
CQkJWzFdID0gew0KPiA+Pj4gKwkJCQkudHlwZSA9IElJT19QUk9YSU1JVFksDQo+ID4+PiArCQkJ
CS5pbmRleGVkID0gMSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4gKwkJCQkuZXZl
bnRfbWFzayA9IFRTTDJYN1hfRVZFTlRfTUFTSw0KPiA+Pj4gKwkJCX0sDQo+ID4+PiArCQl9LA0K
PiA+Pj4gKwkubnVtX2NoYW5uZWxzID0gMiwNCj4gPj4+ICsJLmluZm8gPSZ0c2wyWDdYX2Rldmlj
ZV9pbmZvW1BSWF0sDQo+ID4+PiArCX0sDQo+ID4+PiArCVtBTFNQUlhdID0gew0KPiA+Pj4gKwkJ
LmNoYW5uZWwgPSB7DQo+ID4+PiArCQkJWzBdID0gew0KPiA+Pj4gKwkJCQkudHlwZSA9IElJT19M
SUdIVCwNCj4gPj4+ICsJCQkJLmluZGV4ZWQgPSAxLA0KPiA+Pj4gKwkJCQkuY2hhbm5lbCA9IDAs
DQo+ID4+PiArCQkJCS5wcm9jZXNzZWRfdmFsID0gMSwNCj4gPj4+ICsJCQl9LA0KPiA+Pj4gKwkJ
CVsxXSA9IHsNCj4gPj4+ICsJCQkJLnR5cGUgPSBJSU9fTElHSFQsDQo+ID4+PiArCQkJCS5pbmRl
eGVkID0gMSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4gKwkJCQkuaW5mb19tYXNr
ID0NCj4gPj4+ICsNCj4gPj4gCUlJT19DSEFOX0lORk9fQ0FMSUJTQ0FMRV9TRVBBUkFURV9CSVQg
fA0KPiA+Pj4gKw0KPiA+PiAJSUlPX0NIQU5fSU5GT19DQUxJQkJJQVNfU0VQQVJBVEVfQklULA0K
PiA+Pj4gKwkJCQkuZXZlbnRfbWFzayA9IFRTTDJYN1hfRVZFTlRfTUFTSw0KPiA+Pj4gKwkJCX0s
DQo+ID4+PiArCQkJWzJdID0gew0KPiA+Pj4gKwkJCQkudHlwZSA9IElJT19QUk9YSU1JVFksDQo+
ID4+PiArCQkJCS5pbmRleGVkID0gMSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4g
KwkJCQkucHJvY2Vzc2VkX3ZhbCA9IDEsDQo+ID4+PiArCQkJfSwNCj4gPj4+ICsJCQlbM10gPSB7
DQo+ID4+PiArCQkJCS50eXBlID0gSUlPX1BST1hJTUlUWSwNCj4gPj4+ICsJCQkJLmluZGV4ZWQg
PSAxLA0KPiA+Pj4gKwkJCQkuY2hhbm5lbCA9IDAsDQo+ID4+PiArCQkJCS5wcm9jZXNzZWRfdmFs
ID0gMCwNCj4gPj4+ICsJCQkJLmV2ZW50X21hc2sgPSBUU0wyWDdYX0VWRU5UX01BU0sNCj4gPj4+
ICsJCQl9LA0KPiA+Pj4gKwkJfSwNCj4gPj4+ICsJLm51bV9jaGFubmVscyA9IDQsDQo+ID4+PiAr
CS5pbmZvID0mdHNsMlg3WF9kZXZpY2VfaW5mb1tBTFNQUlhdLA0KPiA+Pj4gKwl9LA0KPiA+Pj4g
KwlbUFJYMl0gPSB7DQo+ID4+PiArCQkuY2hhbm5lbCA9IHsNCj4gPj4+ICsJCQlbMF0gPSB7DQo+
ID4+PiArCQkJCS50eXBlID0gSUlPX1BST1hJTUlUWSwNCj4gPj4+ICsJCQkJLmluZGV4ZWQgPSAx
LA0KPiA+Pj4gKwkJCQkuY2hhbm5lbCA9IDAsDQo+ID4+PiArCQkJCS5wcm9jZXNzZWRfdmFsID0g
MSwNCj4gPj4+ICsJCQl9LA0KPiA+Pj4gKwkJCVsxXSA9IHsNCj4gPj4+ICsJCQkJLnR5cGUgPSBJ
SU9fUFJPWElNSVRZLA0KPiA+Pj4gKwkJCQkuaW5kZXhlZCA9IDEsDQo+ID4+PiArCQkJCS5jaGFu
bmVsID0gMCwNCj4gPj4+ICsJCQkJLmluZm9fbWFzayA9DQo+ID4+PiArDQo+ID4+IAlJSU9fQ0hB
Tl9JTkZPX0NBTElCU0NBTEVfU0VQQVJBVEVfQklULA0KPiA+Pj4gKwkJCQkuZXZlbnRfbWFzayA9
IFRTTDJYN1hfRVZFTlRfTUFTSw0KPiA+Pj4gKwkJCX0sDQo+ID4+PiArCQl9LA0KPiA+Pj4gKwku
bnVtX2NoYW5uZWxzID0gMiwNCj4gPj4+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9pbmZvW1BS
WDJdLA0KPiA+Pj4gKwl9LA0KPiA+Pj4gKwlbQUxTUFJYMl0gPSB7DQo+ID4+PiArCQkuY2hhbm5l
bCA9IHsNCj4gPj4+ICsJCQkJWzBdID0gew0KPiA+Pj4gKwkJCQkudHlwZSA9IElJT19MSUdIVCwN
Cj4gPj4+ICsJCQkJLmluZGV4ZWQgPSAxLA0KPiA+Pj4gKwkJCQkuY2hhbm5lbCA9IDAsDQo+ID4+
PiArCQkJCS5wcm9jZXNzZWRfdmFsID0gMSwNCj4gPj4+ICsJCQl9LA0KPiA+Pj4gKwkJCVsxXSA9
IHsNCj4gPj4+ICsJCQkJLnR5cGUgPSBJSU9fTElHSFQsDQo+ID4+PiArCQkJCS5pbmRleGVkID0g
MSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4gKwkJCQkuaW5mb19tYXNrID0NCj4g
Pj4+ICsNCj4gPj4gCUlJT19DSEFOX0lORk9fQ0FMSUJTQ0FMRV9TRVBBUkFURV9CSVQgfA0KPiA+
Pj4gKw0KPiA+PiAJSUlPX0NIQU5fSU5GT19DQUxJQkJJQVNfU0VQQVJBVEVfQklULA0KPiA+Pj4g
KwkJCQkuZXZlbnRfbWFzayA9IFRTTDJYN1hfRVZFTlRfTUFTSw0KPiA+Pj4gKwkJCX0sDQo+ID4+
PiArCQkJWzJdID0gew0KPiA+Pj4gKwkJCQkudHlwZSA9IElJT19QUk9YSU1JVFksDQo+ID4+PiAr
CQkJCS5pbmRleGVkID0gMSwNCj4gPj4+ICsJCQkJLmNoYW5uZWwgPSAwLA0KPiA+Pj4gKwkJCQku
cHJvY2Vzc2VkX3ZhbCA9IDEsDQo+ID4+PiArCQkJfSwNCj4gPj4+ICsJCQlbM10gPSB7DQo+ID4+
IEknbSBtb3JlIHRoYW4gYSBsaXR0bGUgY29uZnVzZWQgaGVyZS4gIFRoZXJlIGFyZSBvbmx5IDIg
YWN0dWFsbHkNCj4gPj4gY2hhbm5lbHM/ICBJZiBzbyBJIGRvbid0DQo+ID4+IHNlZSB3aHkgd2Ug
aGF2ZSA0IGNoYW5uZWxzIGhlcmUuLi4NCj4gPj4+ICsJCQkJLnR5cGUgPSBJSU9fUFJPWElNSVRZ
LA0KPiA+Pj4gKwkJCQkuaW5kZXhlZCA9IDEsDQo+ID4+PiArCQkJCS5jaGFubmVsID0gMCwNCj4g
Pj4+ICsJCQkJLmluZm9fbWFzayA9DQo+ID4+PiArDQo+ID4+IAlJSU9fQ0hBTl9JTkZPX0NBTElC
U0NBTEVfU0VQQVJBVEVfQklULA0KPiA+Pj4gKwkJCQkuZXZlbnRfbWFzayA9IFRTTDJYN1hfRVZF
TlRfTUFTSw0KPiA+Pj4gKwkJCX0sDQo+ID4+PiArCQl9LA0KPiA+Pj4gKwkubnVtX2NoYW5uZWxz
ID0gNCwNCj4gPj4+ICsJLmluZm8gPSZ0c2wyWDdYX2RldmljZV9pbmZvW0FMU1BSWDJdLA0KPiA+
Pj4gKwl9LA0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+PiArLyoNCj4gPj4+ICsgKiBDbGllbnQg
cHJvYmUgZnVuY3Rpb24uDQo+ID4+PiArICovDQo+ID4+PiArc3RhdGljIGludCBfX2RldmluaXQg
dHNsMng3eF9wcm9iZShzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50cCwNCj4gPj4+ICsJY29uc3Qg
c3RydWN0IGkyY19kZXZpY2VfaWQgKmlkKQ0KPiA+Pj4gK3sNCj4gPj4+ICsJaW50IHJldDsNCj4g
Pj4+ICsJdW5zaWduZWQgY2hhciBkZXZpY2VfaWQ7DQo+ID4+PiArCXN0cnVjdCBpaW9fZGV2ICpp
bmRpb19kZXY7DQo+ID4+PiArCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXA7DQo+ID4+PiArDQo+
ID4+PiArCWluZGlvX2RldiA9IGlpb19hbGxvY2F0ZV9kZXZpY2Uoc2l6ZW9mKCpjaGlwKSk7DQo+
ID4+PiArCWlmICghaW5kaW9fZGV2KQ0KPiA+Pj4gKwkJcmV0dXJuIC1FTk9NRU07DQo+ID4+PiAr
DQo+ID4+PiArCWNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwljaGlwLT5jbGll
bnQgPSBjbGllbnRwOw0KPiA+Pj4gKwlpMmNfc2V0X2NsaWVudGRhdGEoY2xpZW50cCwgaW5kaW9f
ZGV2KTsNCj4gPj4+ICsNCj4gPj4+ICsJcmV0ID0gdHNsMng3eF9pMmNfcmVhZChjaGlwLT5jbGll
bnQsDQo+ID4+PiArCQlUU0wyWDdYX0NISVBJRCwmZGV2aWNlX2lkKTsNCj4gPj4+ICsJaWYgKHJl
dDwgIDApDQo+ID4+PiArCQlnb3RvIGZhaWwxOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpZiAoKCF0c2wy
eDd4X2RldmljZV9pZCgmZGV2aWNlX2lkLCBpZC0+ZHJpdmVyX2RhdGEpKSB8fA0KPiA+Pj4gKwkJ
KHRzbDJ4N3hfZGV2aWNlX2lkKCZkZXZpY2VfaWQsIGlkLT5kcml2ZXJfZGF0YSkgPT0gLUVJTlZB
TCkpIHsNCj4gPj4+ICsJCWRldl9pbmZvKCZjaGlwLT5jbGllbnQtPmRldiwNCj4gPj4+ICsJCQkJ
ImkyYyBkZXZpY2UgZm91bmQgZG9lcyBub3QgbWF0Y2ggZXhwZWN0ZWQgaWQNCj4gPj4gaW4gJXNc
biIsDQo+ID4+PiArCQkJCV9fZnVuY19fKTsNCj4gPj4+ICsJCWdvdG8gZmFpbDE7DQo+ID4+PiAr
CX0NCj4gPj4+ICsNCj4gPj4+ICsJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2xpZW50cCwg
KFRTTDJYN1hfQ01EX1JFRyB8DQo+ID4+IFRTTDJYN1hfQ05UUkwpKTsNCj4gPj4+ICsJaWYgKHJl
dDwgIDApIHsNCj4gPj4+ICsJCWRldl9lcnIoJmNsaWVudHAtPmRldiwgIiVzOiB3cml0ZSB0byBj
bWQgcmVnIGZhaWxlZC4gZXJyID0NCj4gPj4gJWRcbiIsDQo+ID4+PiArCQkJCV9fZnVuY19fLCBy
ZXQpOw0KPiA+Pj4gKwkJZ290byBmYWlsMTsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwkv
KiBBTFMgYW5kIFBST1ggZnVuY3Rpb25zIGNhbiBiZSBpbnZva2VkIHZpYSB1c2VyIHNwYWNlIHBv
bGwNCj4gPj4+ICsJICogb3IgSC9XIGludGVycnVwdC4gSWYgYnVzeSByZXR1cm4gbGFzdCBzYW1w
bGUuICovDQo+ID4+PiArCW11dGV4X2luaXQoJmNoaXAtPmFsc19tdXRleCk7DQo+ID4+PiArCW11
dGV4X2luaXQoJmNoaXAtPnByb3hfbXV0ZXgpOw0KPiA+Pj4gKw0KPiA+Pj4gKwljaGlwLT50c2wy
eDd4X2NoaXBfc3RhdHVzID0gVFNMMlg3WF9DSElQX1VOS05PV047DQo+ID4+PiArCWNoaXAtPnBk
YXRhID0gY2xpZW50cC0+ZGV2LnBsYXRmb3JtX2RhdGE7DQo+ID4+PiArCWNoaXAtPmlkID0gaWQt
PmRyaXZlcl9kYXRhOw0KPiA+Pj4gKwljaGlwLT5jaGlwX2luZm8gPQ0KPiA+Pj4gKwkJJnRzbDJ4
N3hfY2hpcF9pbmZvX3RibFtkZXZpY2VfY2hhbm5lbF9jb25maWdbaWQtDQo+ID4+PiBkcml2ZXJf
ZGF0YV1dOw0KPiA+Pj4gKw0KPiA+Pj4gKwlpbmRpb19kZXYtPmluZm8gPSBjaGlwLT5jaGlwX2lu
Zm8tPmluZm87DQo+ID4+PiArCWluZGlvX2Rldi0+ZGV2LnBhcmVudCA9JmNsaWVudHAtPmRldjsN
Cj4gPj4+ICsJaW5kaW9fZGV2LT5tb2RlcyA9IElORElPX0RJUkVDVF9NT0RFOw0KPiA+Pj4gKwlp
bmRpb19kZXYtPm5hbWUgPSBjaGlwLT5jbGllbnQtPm5hbWU7DQo+ID4+PiArCWluZGlvX2Rldi0+
Y2hhbm5lbHMgPSBjaGlwLT5jaGlwX2luZm8tPmNoYW5uZWw7DQo+ID4+PiArCWluZGlvX2Rldi0+
bnVtX2NoYW5uZWxzID0gY2hpcC0+Y2hpcF9pbmZvLT5udW1fY2hhbm5lbHM7DQo+ID4+PiArDQo+
ID4+PiArCWlmIChjbGllbnRwLT5pcnEpIHsNCj4gPj4+ICsJCXJldCA9IHJlcXVlc3RfdGhyZWFk
ZWRfaXJxKGNsaWVudHAtPmlycSwNCj4gPj4+ICsJCQkJCSAgIE5VTEwsDQo+ID4+PiArCQkJCQkm
dHNsMng3eF9ldmVudF9oYW5kbGVyLA0KPiA+Pj4gKwkJCQkJICAgSVJRRl9UUklHR0VSX1JJU0lO
RyB8DQo+ID4+IElSUUZfT05FU0hPVCwNCj4gPj4+ICsJCQkJCSAgICJUU0wyWDdYX2V2ZW50IiwN
Cj4gPj4+ICsJCQkJCSAgIGluZGlvX2Rldik7DQo+ID4+PiArCQlpZiAocmV0KSB7DQo+ID4+PiAr
CQkJZGV2X2VycigmY2xpZW50cC0+ZGV2LA0KPiA+Pj4gKwkJCQkiJXM6IGlycSByZXF1ZXN0IGZh
aWxlZCIsIF9fZnVuY19fKTsNCj4gPj4+ICsJCQlnb3RvIGZhaWwyOw0KPiA+Pj4gKwkJfQ0KPiA+
Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCS8qIExvYWQgdXAgdGhlIGRlZmF1bHRzICovDQo+ID4+
PiArCXRzbDJ4N3hfZGVmYXVsdHMoY2hpcCk7DQo+ID4+PiArCS8qIE1ha2Ugc3VyZSB0aGUgY2hp
cCBpcyBvbiAqLw0KPiA+Pj4gKwl0c2wyeDd4X2NoaXBfb24oaW5kaW9fZGV2KTsNCj4gPj4+ICsN
Cj4gPj4+ICsJcmV0ID0gaWlvX2RldmljZV9yZWdpc3RlcihpbmRpb19kZXYpOw0KPiA+Pj4gKwlp
ZiAocmV0KSB7DQo+ID4+PiArCQlkZXZfZXJyKCZjbGllbnRwLT5kZXYsDQo+ID4+PiArCQkJIiVz
OiBpaW8gcmVnaXN0cmF0aW9uIGZhaWxlZFxuIiwgX19mdW5jX18pOw0KPiA+Pj4gKwkJZ290byBm
YWlsMzsNCj4gPj4+ICsJfQ0KPiA+Pj4gKw0KPiA+Pj4gKwlkZXZfaW5mbygmY2xpZW50cC0+ZGV2
LCAiJXMgTGlnaHQgc2Vuc29yIGZvdW5kLlxuIiwgaWQtPm5hbWUpOw0KPiA+Pj4gKw0KPiA+Pj4g
KwlyZXR1cm4gMDsNCj4gPj4+ICsNCj4gPj4+ICtmYWlsMzoNCj4gPj4+ICsJaWYgKGNsaWVudHAt
PmlycSkNCj4gPj4+ICsJCWZyZWVfaXJxKGNsaWVudHAtPmlycSwgaW5kaW9fZGV2KTsNCj4gPj4+
ICtmYWlsMjoNCj4gPj4+ICsJaWlvX2ZyZWVfZGV2aWNlKGluZGlvX2Rldik7DQo+ID4+PiArZmFp
bDE6DQo+ID4+PiArCWtmcmVlKGNoaXApOw0KPiA+PiBkb3VibGUgZnJlZSBvZiBjaGlwLiAgSXQn
cyBhbGxvY2F0ZWQgYW5kIG1hbmFnZWQgYnkgdGhlDQo+ID4+IGlpb19hbGxvY2F0ZV9kZXZpY2Ug
YW5kIGlpb19mcmVlX2RldmljZSBjYWxscy4NCj4gPj4gRm9yIHRoYXQgbWF0dGVyLCBtb3N0IG9m
IHRoZSBnb3RvIGZhaWwyJ3MgbmVlZCB0aGUgaWlvX2ZyZWVfZGV2aWNlIGNhbGwuLg0KPiA+Pj4g
KwlyZXR1cm4gcmV0Ow0KPiA+Pj4gK30NCj4gPj4+ICsNCj4gPj4+ICtzdGF0aWMgaW50IHRzbDJ4
N3hfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpDQo+ID4+PiArew0KPiA+Pj4gKwlzdHJ1Y3Qg
aWlvX2RldiAqaW5kaW9fZGV2ID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQo+ID4+PiArCXN0cnVj
dCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KPiA+Pj4gKwlpbnQg
cmV0ID0gMDsNCj4gPj4+ICsNCj4gPj4+ICsJaWYgKGNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMg
PT0gVFNMMlg3WF9DSElQX1dPUktJTkcpIHsNCj4gPj4+ICsJCXJldCA9IHRzbDJ4N3hfY2hpcF9v
ZmYoaW5kaW9fZGV2KTsNCj4gPj4+ICsJCWNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPSBUU0wy
WDdYX0NISVBfU1VTUEVOREVEOw0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaGlw
LT5wZGF0YSYmICBjaGlwLT5wZGF0YS0+cGxhdGZvcm1fcG93ZXIpIHsNCj4gPj4+ICsJCXBtX21l
c3NhZ2VfdCBwbW0gPSB7UE1fRVZFTlRfU1VTUEVORH07DQo+ID4+PiArCQljaGlwLT5wZGF0YS0+
cGxhdGZvcm1fcG93ZXIoZGV2LCBwbW0pOw0KPiA+Pj4gKwl9DQo+ID4+PiArDQo+ID4+PiArCXJl
dHVybiByZXQ7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBpbnQgdHNsMng3eF9y
ZXN1bWUoc3RydWN0IGRldmljZSAqZGV2KQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19k
ZXYgKmluZGlvX2RldiA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNs
Mlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCj4gPj4+ICsJaW50IHJldCA9
IDA7DQo+ID4+PiArDQo+ID4+PiArCWlmIChjaGlwLT5wZGF0YSYmICBjaGlwLT5wZGF0YS0+cGxh
dGZvcm1fcG93ZXIpIHsNCj4gPj4+ICsJCXBtX21lc3NhZ2VfdCBwbW0gPSB7UE1fRVZFTlRfUkVT
VU1FfTsNCj4gPj4+ICsJCWNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9wb3dlcihkZXYsIHBtbSk7DQo+
ID4+PiArCX0NCj4gPj4+ICsNCj4gPj4+ICsJaWYgKGNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMg
PT0gVFNMMlg3WF9DSElQX1NVU1BFTkRFRCkNCj4gPj4+ICsJCXJldCA9IHRzbDJ4N3hfY2hpcF9v
bihpbmRpb19kZXYpOw0KPiA+Pj4gKw0KPiA+Pj4gKwlyZXR1cm4gcmV0Ow0KPiA+Pj4gK30NCj4g
Pj4+ICsNCj4gPj4+ICtzdGF0aWMgaW50IF9fZGV2ZXhpdCB0c2wyeDd4X3JlbW92ZShzdHJ1Y3Qg
aTJjX2NsaWVudCAqY2xpZW50KQ0KPiA+Pj4gK3sNCj4gPj4+ICsJc3RydWN0IGlpb19kZXYgKmlu
ZGlvX2RldiA9IGkyY19nZXRfY2xpZW50ZGF0YShjbGllbnQpOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNs
Mlg3WF9jaGlwICpjaGlwID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7DQo+ID4+IFRoZXNl
IHNob3VsZG4ndCBib3RoIGJlIHRydWUuLi4uDQo+ID4+IHdvdWxkIGV4cGVjdCBhbiBpaW9fdW5y
ZWdpc3Rlcl9kZXZpY2UgY2FsbCBoZXJlLg0KPiA+Pj4gKw0KPiA+Pj4gKwl0c2wyeDd4X2NoaXBf
b2ZmKGluZGlvX2Rldik7DQo+ID4+PiArDQo+ID4+PiArCWlmIChjbGllbnQtPmlycSkNCj4gPj4+
ICsJCWZyZWVfaXJxKGNsaWVudC0+aXJxLCBjaGlwLT5jbGllbnQtPm5hbWUpOw0KPiA+Pj4gKw0K
PiA+Pj4gKwlpaW9fZnJlZV9kZXZpY2UoaW5kaW9fZGV2KTsNCj4gPj4+ICsNCj4gPj4+ICsJcmV0
dXJuIDA7DQo+ID4+PiArfQ0KPiA+Pj4gKw0KPiA+Pj4gK3N0YXRpYyBzdHJ1Y3QgaTJjX2Rldmlj
ZV9pZCB0c2wyeDd4X2lkdGFibGVbXSA9IHsNCj4gPj4+ICsJeyAidHNsMjU3MSIsIHRzbDI1NzEg
fSwNCj4gPj4+ICsJeyAidHNsMjY3MSIsIHRzbDI2NzEgfSwNCj4gPj4+ICsJeyAidG1kMjY3MSIs
IHRtZDI2NzEgfSwNCj4gPj4+ICsJeyAidHNsMjc3MSIsIHRzbDI3NzEgfSwNCj4gPj4+ICsJeyAi
dG1kMjc3MSIsIHRtZDI3NzEgfSwNCj4gPj4+ICsJeyAidHNsMjU3MiIsIHRzbDI1NzIgfSwNCj4g
Pj4+ICsJeyAidHNsMjY3MiIsIHRzbDI2NzIgfSwNCj4gPj4+ICsJeyAidG1kMjY3MiIsIHRtZDI2
NzIgfSwNCj4gPj4+ICsJeyAidHNsMjc3MiIsIHRzbDI3NzIgfSwNCj4gPj4+ICsJeyAidG1kMjc3
MiIsIHRtZDI3NzIgfSwNCj4gPj4+ICsJe30NCj4gPj4+ICt9Ow0KPiA+Pj4gKw0KPiA+Pj4gK01P
RFVMRV9ERVZJQ0VfVEFCTEUoaTJjLCB0c2wyeDd4X2lkdGFibGUpOw0KPiA+Pj4gKw0KPiA+Pj4g
K3N0YXRpYyBjb25zdCBzdHJ1Y3QgZGV2X3BtX29wcyB0c2wyeDd4X3BtX29wcyA9IHsNCj4gPj4+
ICsJLnN1c3BlbmQgPSB0c2wyeDd4X3N1c3BlbmQsDQo+ID4+PiArCS5yZXN1bWUgID0gdHNsMng3
eF9yZXN1bWUsDQo+ID4+PiArfTsNCj4gPj4+ICsNCj4gPj4+ICsvKiBEcml2ZXIgZGVmaW5pdGlv
biAqLw0KPiA+Pj4gK3N0YXRpYyBzdHJ1Y3QgaTJjX2RyaXZlciB0c2wyeDd4X2RyaXZlciA9IHsN
Cj4gPj4+ICsJLmRyaXZlciA9IHsNCj4gPj4+ICsJCS5uYW1lID0gInRzbDJ4N3giLA0KPiA+Pj4g
KwkJLnBtID0mdHNsMng3eF9wbV9vcHMsDQo+ID4+PiArCX0sDQo+ID4+PiArCS5pZF90YWJsZSA9
IHRzbDJ4N3hfaWR0YWJsZSwNCj4gPj4+ICsJLnByb2JlID0gdHNsMng3eF9wcm9iZSwNCj4gPj4+
ICsJLnJlbW92ZSA9IF9fZGV2ZXhpdF9wKHRzbDJ4N3hfcmVtb3ZlKSwNCj4gPj4+ICt9Ow0KPiA+
Pj4gKw0KPiA+Pj4gK21vZHVsZV9pMmNfZHJpdmVyKHRzbDJ4N3hfZHJpdmVyKTsNCj4gPj4+ICsN
Cj4gPj4+ICtNT0RVTEVfQVVUSE9SKCJKLiBBdWd1c3QgQnJlbm5lcjxqYnJlbm5lckB0YW9zaW5j
LmNvbT4iKTsNCj4gPj4+ICtNT0RVTEVfREVTQ1JJUFRJT04oIlRBT1MgdHNsMng3eCBhbWJpZW50
IGFuZCBwcm94aW1pdHkgbGlnaHQgc2Vuc29yDQo+ID4+IGRyaXZlciIpOw0KPiA+Pj4gK01PRFVM
RV9MSUNFTlNFKCJHUEwiKTsNCj4gPj4+IGRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcvaWlv
L2xpZ2h0L3RzbDJ4N3hfY29yZS5oDQo+ID4+IGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90
c2wyeDd4X2NvcmUuaA0KPiA+Pj4gbmV3IGZpbGUgbW9kZSAxMDA2NDQNCj4gPj4+IGluZGV4IDAw
MDAwMDAuLjY2M2U4NDYNCj4gPj4+IC0tLSAvZGV2L251bGwNCj4gPj4+ICsrKyBiL2RyaXZlcnMv
c3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eF9jb3JlLmgNCj4gPj4gd2h5IG5vdCBqdXN0IHRzbDJ4
N3guaD8NCj4gPj4+IEBAIC0wLDAgKzEsNzUgQEANCj4gPj4+ICsvKg0KPiA+Pj4gKyAqIERldmlj
ZSBkcml2ZXIgZm9yIG1vbml0b3JpbmcgYW1iaWVudCBsaWdodCBpbnRlbnNpdHkgKGx1eCkNCj4g
Pj4+ICsgKiBhbmQgcHJveGltaXR5IChwcm94KSB3aXRoaW4gdGhlIFRBT1MgVFNMMlg3WCBmYW1p
bHkgb2YgZGV2aWNlcy4NCj4gPj4+ICsgKg0KPiA+Pj4gKyAqIENvcHlyaWdodCAoYykgMjAxMiwg
VEFPUyBDb3Jwb3JhdGlvbi4NCj4gPj4+ICsgKg0KPiA+Pj4gKyAqIFRoaXMgcHJvZ3JhbSBpcyBm
cmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQvb3IgbW9kaWZ5DQo+ID4+
PiArICogaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5z
ZSBhcyBwdWJsaXNoZWQgYnkNCj4gPj4+ICsgKiB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9u
OyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNlLCBvcg0KPiA+Pj4gKyAqIChhdCB5b3Vy
IG9wdGlvbikgYW55IGxhdGVyIHZlcnNpb24uDQo+ID4+PiArICoNCj4gPj4+ICsgKiBUaGlzIHBy
b2dyYW0gaXMgZGlzdHJpYnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwg
YnV0DQo+IFdJVEhPVVQNCj4gPj4+ICsgKiBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUg
aW1wbGllZCB3YXJyYW50eSBvZg0KPiBNRVJDSEFOVEFCSUxJVFkNCj4gPj4gb3INCj4gPj4+ICsg
KiBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNlZSB0aGUgR05VIEdlbmVyYWwg
UHVibGljDQo+IExpY2Vuc2UNCj4gPj4gZm9yDQo+ID4+PiArICogbW9yZSBkZXRhaWxzLg0KPiA+
Pj4gKyAqDQo+ID4+PiArICogWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUg
R05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UNCj4gYWxvbmcNCj4gPj4+ICsgKiB3aXRoIHRoaXMg
cHJvZ3JhbTsgaWYgbm90LCB3cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJ
bmMuLA0KPiA+Pj4gKyAqIDUxIEZyYW5rbGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3Rvbiwg
TUEJMDIxMTAtMTMwMSwgVVNBLg0KPiA+Pj4gKyAqLw0KPiA+Pj4gKw0KPiA+Pj4gKyNpZm5kZWYg
X19UU0wyWDdYX0gNCj4gPj4+ICsjZGVmaW5lIF9fVFNMMlg3WF9IDQo+ID4+PiArI2luY2x1ZGU8
bGludXgvcG0uaD4NCj4gPj4+ICsNCj4gPj4+ICsvKiBNYXggbnVtYmVyIG9mIHNlZ21lbnRzIGFs
bG93YWJsZSBpbiBMVVggdGFibGUgKi8NCj4gPj4+ICsjZGVmaW5lIFRTTDJYN1hfTUFYX0xVWF9U
QUJMRV9TSVpFCQk5DQo+ID4+PiArI2RlZmluZSBNQVhfREVGQVVMVF9UQUJMRV9CWVRFUyAoc2l6
ZW9mKGludCkgKg0KPiA+PiBUU0wyWDdYX01BWF9MVVhfVEFCTEVfU0laRSkNCj4gPj4+ICsNCj4g
Pj4+ICtzdHJ1Y3QgaWlvX2RldjsNCj4gPj4NCj4gPj4gV2h5IHRoZSBmb3J3YXJkIGRlY2xhcmF0
aW9uIG9mIGlpb19jaGFuX3NwZWM/DQo+ID4+PiArc3RydWN0IGlpb19jaGFuX3NwZWM7DQo+ID4+
PiArDQo+ID4+PiArc3RydWN0IHRzbDJ4N3hfbHV4IHsNCj4gPj4+ICsJdW5zaWduZWQgaW50IHJh
dGlvOw0KPiA+Pj4gKwl1bnNpZ25lZCBpbnQgY2gwOw0KPiA+Pj4gKwl1bnNpZ25lZCBpbnQgY2gx
Ow0KPiA+Pj4gK307DQo+ID4+PiArDQo+ID4+PiArLyogUmVmZXIgdG8gdHNsMng3eF9kZWZhdWx0
X3NldHRpbmdzIGZvciBtZW1iZXIgZGVzYy4gKi8NCj4gPj4+ICtzdHJ1Y3QgdHNsMng3eF9zZXR0
aW5ncyB7DQo+ID4+PiArCWludCBhbHNfdGltZTsNCj4gPj4+ICsJaW50IGFsc19nYWluOw0KPiA+
Pj4gKwlpbnQgYWxzX2dhaW5fdHJpbTsNCj4gPj4+ICsJaW50IHdhaXRfdGltZTsNCj4gPj4+ICsJ
aW50IHByeF90aW1lOw0KPiA+Pj4gKwlpbnQgcHJveF9nYWluOw0KPiA+Pj4gKwlpbnQgcHJveF9j
b25maWc7DQo+ID4+PiArCWludCBhbHNfY2FsX3RhcmdldDsNCj4gPj4+ICsJdTggIGludGVycnVw
dHNfZW47DQo+ID4+PiArCXU4ICBhbHNfcGVyc2lzdGVuY2U7DQo+ID4+PiArCWludCBhbHNfdGhy
ZXNoX2xvdzsNCj4gPj4+ICsJaW50IGFsc190aHJlc2hfaGlnaDsNCj4gPj4+ICsJaW50IHByb3hf
dGhyZXNfbG93Ow0KPiA+Pj4gKwlpbnQgcHJveF90aHJlc19oaWdoOw0KPiA+Pj4gKwlpbnQgcHJv
eF9wdWxzZV9jb3VudDsNCj4gPj4+ICsJaW50IHByb3hfbWF4X3NhbXBsZXNfY2FsOw0KPiA+Pj4g
K307DQo+ID4+PiArDQo+ID4+PiArLyogc3RydWN0IHRzbDJ4N3hfcGxhdGZvcm1fZGF0YSAtDQo+
ID4+PiArICogUGxhdGZvcm0gdW5pcXVlIGdsYXNzIGFuZCBkZWZhdWx0cw0KPiA+Pj4gKyAqIFBs
YXRmb3JtIFBNIGZ1bmN0aW9ucy4gKi8NCj4gPj4gV291bGQgcHJlZmVyIHRoaXMgdG8gYmUgaW4g
a2VybmVsIGRvYy4NCj4gPj4+ICtzdHJ1Y3QgdHNsMlg3WF9wbGF0Zm9ybV9kYXRhIHsNCj4gPj4+
ICsJLyogU3VzcGVuZC9yZXN1bWUgcGxhdGZvcm0gY2IgKi8NCj4gPj4+ICsJaW50ICgqcGxhdGZv
cm1fcG93ZXIpKHN0cnVjdCBkZXZpY2UgKmRldiwgcG1fbWVzc2FnZV90KTsNCj4gPj4+ICsJLyog
VGhlIGZvbGxvd2luZyBjYWxsYmFjayBnZXRzIGNhbGxlZCB3aGVuIHRoZSBkZXZpY2UgaXMgcG93
ZXJlZCBvbiAqLw0KPiA+Pj4gKwlpbnQgKCpwb3dlcl9vbikgICAgICAoc3RydWN0IGlpb19kZXYg
KmluZGlvX2Rldik7DQo+ID4+PiArCS8qIFRoZSBmb2xsb3dpbmcgY2FsbGJhY2sgZ2V0cyBjYWxs
ZWQgd2hlbiB0aGUgZGV2aWNlIGlzIHBvd2VyZWQgb2ZmICovDQo+ID4+PiArCWludCAoKnBvd2Vy
X29mZikgICAgIChzdHJ1Y3QgaTJjX2NsaWVudCAqZGV2KTsNCj4gPj4+ICsJLyogVGhlc2UgYXJl
IHRoZSBkZXZpY2Ugc3BlY2lmaWMgZ2xhc3MgY29lZmZpY2VudHMgdXNlZCB0bw0KPiA+Pj4gKwkg
KiBjYWxjdWxhdGUgTHV4ICovDQo+ID4+PiArCXN0cnVjdCB0c2wyeDd4X2x1eCBwbGF0Zm9ybV9s
dXhfdGFibGVbVFNMMlg3WF9NQVhfTFVYX1RBQkxFX1NJWkVdOw0KPiA+Pj4gKwlzdHJ1Y3QgdHNs
Mng3eF9zZXR0aW5ncyAqcGxhdGZvcm1fZGVmYXVsdF9zZXR0aW5nczsNCj4gPj4+ICt9Ow0KPiA+
Pj4gKw0KPiA+Pj4gKyNlbmRpZiAvKiBfX1RTTDJYN1hfSCAqLw0KPiA+Pj4gLS0NCj4gPj4+IDEu
Ny40LjENCj4gPj4+DQo+ID4NCg0K

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH V4] TAOS tsl2x7x
  2012-03-28 23:29         ` Jon Brenner
  (?)
@ 2012-03-29 14:06         ` Jonathan Cameron
  -1 siblings, 0 replies; 8+ messages in thread
From: Jonathan Cameron @ 2012-03-29 14:06 UTC (permalink / raw)
  To: Jon Brenner; +Cc: Jonathan Cameron, linux-iio, Linux Kernel

On 3/29/2012 12:29 AM, Jon Brenner wrote:
> Hello Jonathan,
> Unless the 2563 patch can wait, I'll leave it for you.
> I need to get the 2x7x finished asap - because I have a yet another driver to do - immediately following it.
> If it can wait till later (2563 driver patch), I'll be glad to do it.
>
> That brings me back to some basic questions.
> 1. Channel 0 - for in_illuminance0_input - correct?
Yup.
> 2. What channel for in_intensity (data channel 0 data)?
0 would fit with my mental model of this device.  We treat the 
illuminance channel as a separate
entity entirely hence back to 0 for a new channel type.
> 3  What channel for in_intensity (data channel 1 data)?
1
> 4 What channel for in_proximity?_raw ?
0
>
> What do you think about the following channel 'table def' for the device that has ALS and Prox?
> Please read statements following this snippet.
>
> [ALSPRX] = {
> 	.channel = {
> 			{
> 			.type = IIO_LIGHT,
> 			.indexed = 1,
> 			.channel = 0,
> 			.processed_val = 1,
> 			}, {
> 			.type = IIO_INTENSITY,
> 			.indexed = 1,
For ease of reading, I'd make channel = 0 explicity as well..
> 			.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
> 					IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
> 			.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> 					IIO_EV_DIR_RISING) |
> 					 IIO_EV_BIT(IIO_EV_TYPE_THRESH,
> 					IIO_EV_DIR_FALLING)),
> 			}, {
> 					.type = IIO_INTENSITY,
> 					.indexed = 1,
> 					.channel = 1,
> 			}, {
> 			.type = IIO_PROXIMITY,
> 			.indexed = 1,
> 			.event_mask = TSL2X7X_EVENT_MASK
> 			},
> 		},
> 	.num_channels = 4,
> 	.info =&tsl2X7X_device_info[ALSPRX],
> 	},
>
> Notice I left out the modifiers?
> This yields:
>   	in_intensity0_raw	(gives us channel 0 raw data)
> 	in_intensity1_raw	(gives us channel 1 raw data)
> 	in_proximity0_raw           (gives us prox AD raw data)
> as well as
> 	in_intensity0_calibbias
> 	in_intensity0_calibscale
> and of course
> 	in_illuminance0_input
>
> With the modifiers left out, isn't the '_raw' and indication of a raw AD value (AD counts)?
Yup.
> We TAOS don't use the term "IR" in our channel designations - thus 0 and 1 more closely match the data sheets.
> (Another reason why I don't want to use the modifiers of BOTH and IR)
Hmm.. Alright.  The reason for doing those modifiers is to allow some 
correspondance between light sensors from different
manufacturers.  Probably doesn't matter to 99% of users though so fine 
with the channel numbers.
>
> (note also that .num_channels is really number of table elements - I can change that name)
Sure, if we treat the illuminance and proximity as 'virtual channels'  
then it still makes sense.
>
> What do you think?
>
> Jon
>
>> -----Original Message-----
>> From: Jonathan Cameron [mailto:jic23@kernel.org]
>> Sent: Wednesday, March 28, 2012 12:52 PM
>> To: Jon Brenner
>> Cc: Jonathan Cameron; linux-iio; Linux Kernel
>> Subject: Re: [PATCH V4] TAOS tsl2x7x
>>
>> On 03/27/2012 08:58 PM, Jon Brenner wrote:
>>> Hello Jonathan,
>>> Still a little confused (and stuck) here.
>>> Using your code from the tsl2563:
>>> 1. The case for IIO_LIGHT appears to return the computed LUX.
>>> Yet you have no ".processed_val = 1: in your channel table - so where in
>> in_illuminance0_input (aka lux)  coming from?
>> Indeed. That's bug number 1...
>>> 2. The case for IIO_INTENSITY looks for 'chan->channel' to determine when to
>> present 'chip->data0' or 'chip->data1' -
>>> But it appears that 'chan->channel' will always be 0 as '.channel =' isn't defined
>> in the table?
>>> So how can you ever get chip->data01?
>>>
>> Gah, this driver clearly needs another look.  You are quite correct,
>> that is a bug as well as it will always return the first channel.
>>
>> Clearly a little bit of code rot has occured here.  Oops.
>> Do you want to do the patch, or shall I (with a reported by
>> of course!).
>>
>> Jonathan
>>> For example, tsl2563 code portion follows.
>>>
>>> <Snip>
>>> static int tsl2563_read_raw(struct iio_dev *indio_dev,
>>> 			    struct iio_chan_spec const *chan,
>>> 			    int *val,
>>> 			    int *val2,
>>> 			    long m)
>>> {
>>> 	int ret = -EINVAL;
>>> 	u32 calib0, calib1;
>>> 	struct tsl2563_chip *chip = iio_priv(indio_dev);
>>>
>>> 	mutex_lock(&chip->lock);
>>> 	switch (m) {
>>> 	case 0:
>>> 		switch (chan->type) {
>>> 		case IIO_LIGHT:
>>> 			ret = tsl2563_get_adc(chip);
>>> 			if (ret)
>>> 				goto error_ret;
>>> 			calib0 = calib_adc(chip->data0, chip->calib0) *
>>> 				chip->cover_comp_gain;
>>> 			calib1 = calib_adc(chip->data1, chip->calib1) *
>>> 				chip->cover_comp_gain;
>>> 			*val = adc_to_lux(calib0, calib1);
>>> 			ret = IIO_VAL_INT;
>>> 			break;
>>> 		case IIO_INTENSITY:
>>> 			ret = tsl2563_get_adc(chip);
>>> 			if (ret)
>>> 				goto error_ret;
>>> 			if (chan->channel == 0)
>>> 				*val = chip->data0;
>>> 			else
>>> 				*val = chip->data1;
>>> 			ret = IIO_VAL_INT;
>>> 			break;
>>> 		default:
>>> 			break;
>>> 		}
>>> 		break;
>>>
>>> 	case IIO_CHAN_INFO_CALIBSCALE:
>>> 		if (chan->channel == 0)
>>> 			*val = calib_to_sysfs(chip->calib0);
>>> 		else
>>> 			*val = calib_to_sysfs(chip->calib1);
>>> 		ret = IIO_VAL_INT;
>>> 		break;
>>> 	default:
>>> 		ret = -EINVAL;
>>> 		goto error_ret;
>>> 	}
>>>
>>> error_ret:
>>> 	mutex_unlock(&chip->lock);
>>> 	return ret;
>>> }
>>>
>>> static const struct iio_chan_spec tsl2563_channels[] = {
>>> 	{
>>> 		.type = IIO_LIGHT,
>>> 		.indexed = 1,
>>> 		.channel = 0,
>>> 	}, {
>>> 		.type = IIO_INTENSITY,
>>> 		.modified = 1,
>>> 		.channel2 = IIO_MOD_LIGHT_BOTH,
>>> 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>> 		.event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH,
>>> 					  IIO_EV_DIR_RISING) |
>>> 			       IIO_EV_BIT(IIO_EV_TYPE_THRESH,
>>> 					  IIO_EV_DIR_FALLING)),
>>> 	}, {
>>> 		.type = IIO_INTENSITY,
>>> 		.modified = 1,
>>> 		.channel2 = IIO_MOD_LIGHT_IR,
>>> 		.info_mask = IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>> 	}
>>> };
>>>
>>>
>>> What am I not seeing here?
>>>
>>> Jon
>>>
>>>> -----Original Message-----
>>>> From: Jonathan Cameron [mailto:jic23@cam.ac.uk]
>>>> Sent: Friday, March 23, 2012 8:54 AM
>>>> To: Jon Brenner
>>>> Cc: linux-iio; Linux Kernel
>>>> Subject: Re: [PATCH V4] TAOS tsl2x7x
>>>>
>>>> On 3/21/2012 4:16 PM, Jon Brenner wrote:
>>>>> TAOS device driver (version 4) for the tsl/tmd 2771 and 2772 device families
>>>> (inc. all variants).
>>>> Mostly looking good...
>>>>
>>>> Couple of issues remaining.
>>>> * Why have processed and raw accesses to the same channels?  That is
>>>> definitely not the intent.
>>>> Either it's worth processing these raw adc channels in kernel, or not.
>>>> If it isn't you certainly shouldn't
>>>> be munging together the two adc's into a single value.
>>>>
>>>> So you have done this for two reasons...
>>>> * For light sensors it was to allow the processed illuminance value and
>>>> also the raw adc values.
>>>> Previously we have done this by having IIO_LIGHT (and hence illuminance)
>>>> for the processed one
>>>> and marking the other two as IIO_INTENSITY with modifiers for the
>>>> frequency range they cover...
>>>> * For proximity one is the raw reading (fine), the other is a means of
>>>> getting at the threshold event
>>>> if interrupts are not supported.  It is done by a software comparison of
>>>> the threshold and the raw
>>>> reading.  This should not be in driver as if the functionality is
>>>> desired, it should be done in userspace.
>>>>
>>>> Various other minor bits like error paths that don't clean up commented
>>>> inline.
>>>>
>>>>
>>>>> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
>>>>> ---
>>>>>    .../light/sysfs-bus-iio-light-tsl2583              |    6 +
>>>>>    .../light/sysfs-bus-iio-light-tsl2x7x              |   20 +
>>>>>    drivers/staging/iio/Documentation/sysfs-bus-iio    |    7 +
>>>>>    .../staging/iio/Documentation/sysfs-bus-iio-light  |    8 +-
>>>>>    .../iio/Documentation/sysfs-bus-iio-light-tsl2583  |   20 -
>>>>>    drivers/staging/iio/light/Kconfig                  |    8 +
>>>>>    drivers/staging/iio/light/Makefile                 |    1 +
>>>>>    drivers/staging/iio/light/tsl2x7x_core.c           | 1911
>> ++++++++++++++++++++
>>>>>    drivers/staging/iio/light/tsl2x7x_core.h           |   75 +
>>>>>    9 files changed, 2032 insertions(+), 24 deletions(-)
>>>>>
>>>>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-
>> tsl2583
>>>> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
>>>>> new file mode 100644
>>>>> index 0000000..8f2a038
>>>>> --- /dev/null
>>>>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2583
>>>>> @@ -0,0 +1,6 @@
>>>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>>>> +KernelVersion:	2.6.37
>>>>> +Contact:	linux-iio@vger.kernel.org
>>>>> +Description:
>>>>> +		This property causes an internal calibration of the als gain trim
>>>>> +		value which is later used in calculating illuminance in lux.
>>>>> diff --git a/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-
>> tsl2x7x
>>>> b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
>>>>> new file mode 100644
>>>>> index 0000000..cceadae
>>>>> --- /dev/null
>>>>> +++ b/drivers/staging/iio/Documentation/light/sysfs-bus-iio-light-tsl2x7x
>>>>> @@ -0,0 +1,20 @@
>>>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>>>> +KernelVersion:	2.6.37
>>>>> +Contact:	linux-iio@vger.kernel.org
>>>>> +Description:
>>>>> +		This property causes an internal calibration of the als gain trim
>>>>> +		value which is later used in calculating illuminance in lux.
>>>>> +
>>>>> +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
>>>>> +KernelVersion:	3.3-rc1
>>>>> +Contact:	linux-iio@vger.kernel.org
>>>>> +Description:
>>>>> +		Simultainious ALS channel data.
>>>> That wasn't the intent of 'both' at all.  (+ typo).  It means a raw
>>>> reading from a diod that
>>>> detects the 'sum' of infrared and visible.
>>>>> +
>>>>> +What:		/sys/bus/iio/devices/device[n]/proximity_calibrate
>>>>> +KernelVersion:	3.3-rc1
>>>>> +Contact:	linux-iio@vger.kernel.org
>>>>> +Description:
>>>>> +		Causes an recalculation and adjustment to the
>>>>> +		proximity_thresh_rising_value.
>>>>> +
>>>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio
>>>> b/drivers/staging/iio/Documentation/sysfs-bus-iio
>>>>> index 46a995d..5b2b5d3 100644
>>>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio
>>>>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio
>>>>> @@ -258,6 +258,8 @@ What
>>>> 	/sys/bus/iio/devices/iio:deviceX/in_accel_z_calibscale
>>>>>    What
>>>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_x_calibscale
>>>>>    What
>>>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_y_calibscale
>>>>>    What
>>>> 	/sys/bus/iio/devices/iio:deviceX/in_anglvel_z_calibscale
>>>>> +what
>>>> 	/sys/bus/iio/devices/iio:deviceX/illuminance0_calibscale
>>>>> +what		/sys/bus/iio/devices/iio:deviceX/proximity_calibscale
>>>>>    KernelVersion:	2.6.35
>>>>>    Contact:	linux-iio@vger.kernel.org
>>>>>    Description:
>>>>> @@ -457,6 +459,10 @@ What:
>>>> 	/sys/.../events/in_voltageY_raw_thresh_falling_value
>>>>>    What:		/sys/.../events/in_voltageY_raw_thresh_falling_value
>>>>>    What:		/sys/.../events/in_tempY_raw_thresh_falling_value
>>>>>    What:		/sys/.../events/in_tempY_raw_thresh_falling_value
>>>>> +What:		/sys/.../events/illuminance0_thresh_falling_value
>>>>> +what:		/sys/.../events/illuminance0_thresh_rising_value
>>>>> +what:		/sys/.../events/proximity_thresh_falling_value
>>>>> +what:		/sys/.../events/proximity_thresh_rising_value
>>>>>    KernelVersion:	2.6.37
>>>>>    Contact:	linux-iio@vger.kernel.org
>>>>>    Description:
>>>>> @@ -739,3 +745,4 @@ Description:
>>>>>    		system. To minimize the current consumption of the system,
>>>>>    		the bridge can be disconnected (when it is not being used
>>>>>    		using the bridge_switch_en attribute.
>>>>> +
>>>> spurious blank line?
>>>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>>>> index edbf470..4385c70 100644
>>>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>>>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light
>>>>> @@ -76,10 +76,10 @@ Contact:	linux-iio@vger.kernel.org
>>>>>    Description:
>>>>>    		This property gets/sets the sensors ADC analog integration
>>>> time.
>>>>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibscale
>>>>> +What:		/sys/bus/iio/devices/device[n]/lux_table
>>>>>    KernelVersion:	2.6.37
>>>>>    Contact:	linux-iio@vger.kernel.org
>>>>>    Description:
>>>>> -		Hardware or software applied calibration scale factor assumed
>>>>> -		to account for attenuation due to industrial design (glass
>>>>> -		filters or aperture holes).
>>>>> +		This property gets/sets the table of coefficients
>>>>> +		used in calculating illuminance in lux.
>>>>> +
>>>>> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>>>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>>>>> deleted file mode 100644
>>>>> index 660781d..0000000
>>>>> --- a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2583
>>>>> +++ /dev/null
>>>>> @@ -1,20 +0,0 @@
>>>>> -What:		/sys/bus/iio/devices/device[n]/lux_table
>>>>> -KernelVersion:	2.6.37
>>>>> -Contact:	linux-iio@vger.kernel.org
>>>>> -Description:
>>>>> -		This property gets/sets the table of coefficients
>>>>> -		used in calculating illuminance in lux.
>>>>> -
>>>>> -What:		/sys/bus/iio/devices/device[n]/illuminance0_calibrate
>>>>> -KernelVersion:	2.6.37
>>>>> -Contact:	linux-iio@vger.kernel.org
>>>>> -Description:
>>>>> -		This property causes an internal calibration of the als gain trim
>>>>> -		value which is later used in calculating illuminance in lux.
>>>>> -
>>>>> -What:
>>>> 	/sys/bus/iio/devices/device[n]/illuminance0_input_target
>>>>> -KernelVersion:	2.6.37
>>>>> -Contact:	linux-iio@vger.kernel.org
>>>>> -Description:
>>>>> -		This property is the known externally illuminance (in lux).
>>>>> -		It is used in the process of calibrating the device accuracy.
>>>>> diff --git a/drivers/staging/iio/light/Kconfig
>> b/drivers/staging/iio/light/Kconfig
>>>>> index e7e9159..976f790 100644
>>>>> --- a/drivers/staging/iio/light/Kconfig
>>>>> +++ b/drivers/staging/iio/light/Kconfig
>>>>> @@ -31,4 +31,12 @@ config TSL2583
>>>>>    	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
>>>>>    	 Access ALS data via iio, sysfs.
>>>>>
>>>>> +config TSL2x7x
>>>>> +	tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and
>>>> proximity sensors"
>>>>> +	depends on I2C
>>>>> +	help
>>>>> +	 Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572,
>>>> tsl2672,
>>>>> +	 tmd2672, tsl2772, tmd2772 devices.
>>>>> +	 Provides iio_events and direct access via sysfs.
>>>>> +
>>>>>    endmenu
>>>>> diff --git a/drivers/staging/iio/light/Makefile
>>>> b/drivers/staging/iio/light/Makefile
>>>>> index 3011fbf..ff12c4b 100644
>>>>> --- a/drivers/staging/iio/light/Makefile
>>>>> +++ b/drivers/staging/iio/light/Makefile
>>>>> @@ -5,3 +5,4 @@
>>>>>    obj-$(CONFIG_SENSORS_TSL2563)	+= tsl2563.o
>>>>>    obj-$(CONFIG_SENSORS_ISL29018)	+= isl29018.o
>>>>>    obj-$(CONFIG_TSL2583)	+= tsl2583.o
>>>>> +obj-$(CONFIG_TSL2x7x)	+= tsl2x7x_core.o
>>>>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.c
>>>> b/drivers/staging/iio/light/tsl2x7x_core.c
>>>>> new file mode 100644
>>>>> index 0000000..c0d9d6e
>>>>> --- /dev/null
>>>>> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
>>>>> @@ -0,0 +1,1911 @@
>>>>> +/*
>>>>> + * Device driver for monitoring ambient light intensity in (lux)
>>>>> + * and proximity detection (prox) within the TAOS TSL2X7X family of
>> devices.
>>>>> + *
>>>>> + * Copyright (c) 2012, TAOS Corporation.
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License as published by
>>>>> + * the Free Software Foundation; 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.,
>>>>> + * 51 Franklin Street, Fifth Floor, Boston, MA        02110-1301, USA.
>>>>> + */
>>>>> +
>>>>> +#include<linux/kernel.h>
>>>>> +#include<linux/i2c.h>
>>>>> +#include<linux/errno.h>
>>>>> +#include<linux/delay.h>
>>>>> +#include<linux/mutex.h>
>>>>> +#include<linux/interrupt.h>
>>>>> +#include<linux/slab.h>
>>>>> +#include<linux/module.h>
>>>>> +#include<linux/version.h>
>>>>> +#include "tsl2x7x_core.h"
>>>>> +#include "../events.h"
>>>>> +#include "../iio.h"
>>>>> +#include "../sysfs.h"
>>>>> +
>>>>> +/* Cal defs*/
>>>>> +#define PROX_STAT_CAL        0
>>>>> +#define PROX_STAT_SAMP       1
>>>>> +#define MAX_SAMPLES_CAL      200
>>>>> +
>>>>> +/* TSL2X7X Device ID */
>>>>> +#define TRITON_ID    0x00
>>>>> +#define SWORDFISH_ID 0x30
>>>>> +#define HALIBUT_ID   0x20
>>>>> +
>>>>> +/* Lux calculation constants */
>>>>> +#define TSL2X7X_LUX_CALC_OVER_FLOW     65535
>>>>> +
>>>>> +/* TAOS Register definitions - note:
>>>>> + * depending on device, some of these register are not used and the
>>>>> + * register address is benign.
>>>>> + */
>>>>> +/* 2X7X register offsets */
>>>>> +#define TSL2X7X_MAX_CONFIG_REG         16
>>>>> +
>>>>> +/* Device Registers and Masks */
>>>>> +#define TSL2X7X_CNTRL                  0x00
>>>>> +#define TSL2X7X_ALS_TIME               0X01
>>>>> +#define TSL2X7X_PRX_TIME               0x02
>>>>> +#define TSL2X7X_WAIT_TIME              0x03
>>>>> +#define TSL2X7X_ALS_MINTHRESHLO        0X04
>>>>> +#define TSL2X7X_ALS_MINTHRESHHI        0X05
>>>>> +#define TSL2X7X_ALS_MAXTHRESHLO        0X06
>>>>> +#define TSL2X7X_ALS_MAXTHRESHHI        0X07
>>>>> +#define TSL2X7X_PRX_MINTHRESHLO        0X08
>>>>> +#define TSL2X7X_PRX_MINTHRESHHI        0X09
>>>>> +#define TSL2X7X_PRX_MAXTHRESHLO        0X0A
>>>>> +#define TSL2X7X_PRX_MAXTHRESHHI        0X0B
>>>>> +#define TSL2X7X_PERSISTENCE            0x0C
>>>>> +#define TSL2X7X_PRX_CONFIG             0x0D
>>>>> +#define TSL2X7X_PRX_COUNT              0x0E
>>>>> +#define TSL2X7X_GAIN                   0x0F
>>>>> +#define TSL2X7X_NOTUSED                0x10
>>>>> +#define TSL2X7X_REVID                  0x11
>>>>> +#define TSL2X7X_CHIPID                 0x12
>>>>> +#define TSL2X7X_STATUS                 0x13
>>>>> +#define TSL2X7X_ALS_CHAN0LO            0x14
>>>>> +#define TSL2X7X_ALS_CHAN0HI            0x15
>>>>> +#define TSL2X7X_ALS_CHAN1LO            0x16
>>>>> +#define TSL2X7X_ALS_CHAN1HI            0x17
>>>>> +#define TSL2X7X_PRX_LO                 0x18
>>>>> +#define TSL2X7X_PRX_HI                 0x19
>>>>> +
>>>>> +/* tsl2X7X cmd reg masks */
>>>>> +#define TSL2X7X_CMD_REG                0x80
>>>>> +#define TSL2X7X_CMD_SPL_FN             0x60
>>>>> +
>>>>> +#define TSL2X7X_CMD_PROX_INT_CLR       0X05
>>>>> +#define TSL2X7X_CMD_ALS_INT_CLR        0x06
>>>>> +#define TSL2X7X_CMD_PROXALS_INT_CLR    0X07
>>>>> +
>>>>> +/* tsl2X7X cntrl reg masks */
>>>>> +#define TSL2X7X_CNTL_ADC_ENBL          0x02
>>>>> +#define TSL2X7X_CNTL_PWR_ON            0x01
>>>>> +
>>>>> +/* tsl2X7X status reg masks */
>>>>> +#define TSL2X7X_STA_ADC_VALID          0x01
>>>>> +#define TSL2X7X_STA_PRX_VALID          0x02
>>>>> +#define TSL2X7X_STA_ADC_PRX_VALID      0x03
>>>>> +#define TSL2X7X_STA_ALS_INTR           0x10
>>>>> +#define TSL2X7X_STA_ADC_INTR           0x10
>>>>> +#define TSL2X7X_STA_PRX_INTR           0x20
>>>>> +
>>>>> +#define TSL2X7X_STA_ADC_INTR           0x10
>>>>> +
>>>>> +/* tsl2X7X cntrl reg masks */
>>>>> +#define TSL2X7X_CNTL_REG_CLEAR         0x00
>>>>> +#define TSL2X7X_CNTL_PROX_INT_ENBL     0X20
>>>>> +#define TSL2X7X_CNTL_ALS_INT_ENBL      0X10
>>>>> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
>>>>> +#define TSL2X7X_CNTL_PROX_DET_ENBL     0X04
>>>>> +#define TSL2X7X_CNTL_PWRON             0x01
>>>>> +#define TSL2X7X_CNTL_ALSPON_ENBL       0x03
>>>>> +#define TSL2X7X_CNTL_INTALSPON_ENBL    0x13
>>>>> +#define TSL2X7X_CNTL_PROXPON_ENBL      0x0F
>>>>> +#define TSL2X7X_CNTL_INTPROXPON_ENBL   0x2F
>>>>> +
>>>>> +/*Prox diode to use */
>>>>> +#define TSL2X7X_DIODE0                 0x10
>>>>> +#define TSL2X7X_DIODE1                 0x20
>>>>> +#define TSL2X7X_DIODE_BOTH             0x30
>>>>> +
>>>>> +/* LED Power */
>>>>> +#define TSL2X7X_mA100                  0x00
>>>>> +#define TSL2X7X_mA50                   0x40
>>>>> +#define TSL2X7X_mA25                   0x80
>>>>> +#define TSL2X7X_mA13                   0xD0
>>>>> +
>>>>> +/*Common device IIO EventMask */
>>>>> +#define TSL2X7X_EVENT_MASK \
>>>>> +		(IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_RISING) | \
>>>>> +		IIO_EV_BIT(IIO_EV_TYPE_THRESH, IIO_EV_DIR_FALLING)),
>>>>> +
>>>>> +/* TAOS txx2x7x Device family members */
>>>>> +enum {
>>>>> +	tsl2571,
>>>>> +	tsl2671,
>>>>> +	tmd2671,
>>>>> +	tsl2771,
>>>>> +	tmd2771,
>>>>> +	tsl2572,
>>>>> +	tsl2672,
>>>>> +	tmd2672,
>>>>> +	tsl2772,
>>>>> +	tmd2772
>>>>> +};
>>>>> +
>>>>> +enum {
>>>>> +	TSL2X7X_CHIP_UNKNOWN = 0,
>>>>> +	TSL2X7X_CHIP_WORKING = 1,
>>>>> +	TSL2X7X_CHIP_SUSPENDED = 2
>>>>> +};
>>>>> +
>>>>> +/* Per-device data */
>>>>> +struct tsl2x7x_als_info {
>>>>> +	u16 als_ch0;
>>>>> +	u16 als_ch1;
>>>>> +	u16 lux;
>>>>> +};
>>>>> +
>>>>> +/* proximity data */
>>>>> +struct tsl2x7x_prox_info {
>>>>> +	u16 prox_data;
>>>>> +	int prox_event;
>>>>> +};
>>>>> +
>>>>> +struct prox_stat {
>>>>> +	u16 min;
>>>>> +	u16 max;
>>>>> +	u16 mean;
>>>>> +	unsigned long stddev;
>>>>> +};
>>>>> +
>>>>> +struct tsl2x7x_chip_info {
>>>>> +	int num_channels;
>>>>> +	struct iio_chan_spec		channel[9];
>>>>> +	const struct iio_info		*info;
>>>>> +};
>>>>> +
>>>>> +struct tsl2X7X_chip {
>>>>> +	kernel_ulong_t id;
>>>>> +	struct mutex prox_mutex;
>>>>> +	struct mutex als_mutex;
>>>>> +	struct i2c_client *client;
>>>>> +	struct tsl2x7x_prox_info prox_cur_info;
>>>>> +	struct tsl2x7x_als_info als_cur_info;
>>>>> +	struct tsl2x7x_settings tsl2x7x_settings;
>>>>> +	struct tsl2X7X_platform_data *pdata;
>>>>> +	int als_time_scale;
>>>>> +	int als_saturation;
>>>>> +	int tsl2x7x_chip_status;
>>>>> +	u8 tsl2x7x_config[TSL2X7X_MAX_CONFIG_REG];
>>>>> +	const struct tsl2x7x_chip_info	*chip_info;
>>>>> +	const struct iio_info *info;
>>>>> +	s64 event_timestamp;
>>>>> +	/* This structure is intentionally large to accommodate
>>>>> +	 * updates via sysfs. */
>>>>> +	/* Sized to 9 = max 8 segments + 1 termination segment */
>>>>> +	struct tsl2x7x_lux tsl2x7x_device_lux[TSL2X7X_MAX_LUX_TABLE_SIZE];
>>>>> +};
>>>>> +
>>>>> +/* Different devices require different coefficents */
>>>>> +static const struct tsl2x7x_lux tsl2x71_lux_table[] = {
>>>>> +	{ 14461,   611,   1211 },
>>>>> +	{ 18540,   352,    623 },
>>>>> +	{     0,     0,      0 },
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_lux tmd2x71_lux_table[] = {
>>>>> +	{ 11635,   115,    256 },
>>>>> +	{ 15536,    87,    179 },
>>>>> +	{     0,     0,      0 },
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_lux tsl2x72_lux_table[] = {
>>>>> +	{ 14013,   466,   917 },
>>>>> +	{ 18222,   310,   552 },
>>>>> +	{     0,     0,     0 },
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_lux tmd2x72_lux_table[] = {
>>>>> +	{ 13218,   130,   262 },
>>>>> +	{ 17592,   92,    169 },
>>>>> +	{     0,     0,     0 },
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_lux *tsl2x7x_default_lux_table_group[] = {
>>>> check white space around here.  looks like a mixture of tabs and spaces...
>>>>> +	[tsl2571] = tsl2x71_lux_table,
>>>>> +	[tsl2671] =	tsl2x71_lux_table,
>>>>> +	[tmd2671] =	tmd2x71_lux_table,
>>>>> +	[tsl2771] =	tsl2x71_lux_table,
>>>>> +	[tmd2771] =	tmd2x71_lux_table,
>>>>> +	[tsl2572] =	tsl2x72_lux_table,
>>>>> +	[tsl2672] =	tsl2x72_lux_table,
>>>>> +	[tmd2672] = tmd2x72_lux_table,
>>>>> +	[tsl2772] =	tsl2x72_lux_table,
>>>>> +	[tmd2772] =	tmd2x72_lux_table,
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_settings tsl2x7x_default_settings = {
>>>>> +	.als_time = 200,
>>>>> +	/* must be a multiple of 50mS */
>>>>> +	.als_gain = 0,
>>>>> +	/* this is actually an index into the gain table */
>>>>> +	.prx_time = 0xfe, /*5.4 mS */
>>>>> +	/* 2.7ms prox integration time - decrease to increase time */
>>>>> +	/* decreases in 2.7 ms intervals */
>>>>> +	.prox_gain = 1,
>>>>> +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
>>>>> +	/* assume clear glass as default */
>>>>> +	.wait_time = 245,
>>>>> +	/* Time between PRX and ALS cycles -decrease to increase time */
>>>>> +	/* decreases in 2.7 ms intervals */
>>>>> +	.prox_config = 0,
>>>>> +	/* Prox configuration filters */
>>>>> +	.als_gain_trim = 1000,
>>>>> +	/* default gain trim to account for aperture effects */
>>>>> +	.als_cal_target = 150,
>>>>> +	/* Known external ALS reading used for calibration */
>>>>> +	.als_thresh_low = 200,
>>>>> +	/* CH0 'low' count to trigger interrupt */
>>>>> +	.als_thresh_high = 256,
>>>>> +	/* CH0 'high' count to trigger interrupt */
>>>>> +	.als_persistence = 0xFF,
>>>>> +	/* Number of 'out of limits' ADC readings PRX/ALS*/
>>>>> +	.interrupts_en = 0x00,
>>>>> +	/* Default interrupt(s) enabled.
>>>>> +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
>>>>> +	.prox_thres_low  = 0,
>>>>> +	.prox_thres_high = 512,
>>>>> +	/*default threshold adjust either manually or with cal routine*/
>>>>> +	.prox_max_samples_cal = 30,
>>>>> +	.prox_pulse_count = 8
>>>>> +};
>>>>> +
>>>>> +static const s16 tsl2X7X_als_gainadj[] = {
>>>>> +	1,
>>>>> +	8,
>>>>> +	16,
>>>>> +	120
>>>>> +};
>>>>> +
>>>>> +static const s16 tsl2X7X_prx_gainadj[] = {
>>>>> +	1,
>>>>> +	2,
>>>>> +	4,
>>>>> +	8
>>>>> +};
>>>>> +
>>>>> +/* Channel variations */
>>>>> +enum {
>>>>> +	ALS,
>>>>> +	PRX,
>>>>> +	ALSPRX,
>>>>> +	PRX2,
>>>>> +	ALSPRX2,
>>>>> +};
>>>>> +
>>>>> +const u8 device_channel_config[] = {
>>>>> +	ALS,
>>>>> +	PRX,
>>>>> +	PRX,
>>>>> +	ALSPRX,
>>>>> +	ALSPRX,
>>>>> +	ALS,
>>>>> +	PRX2,
>>>>> +	PRX2,
>>>>> +	ALSPRX2,
>>>>> +	ALSPRX2
>>>>> +};
>>>>> +
>>>>> +/*
>>>>> + * Read a number of bytes starting at register (reg) location.
>>>>> + * Return 0, or i2c_smbus_write_byte ERROR code.
>>>>> + */
>>>>> +static int
>>>>> +tsl2x7x_i2c_read(struct i2c_client *client, u8 reg, u8 *val)
>>>>> +{
>>>>> +	int ret;
>>>>> +
>>>>> +	/* select register to write */
>>>>> +	ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&client->dev, "%s: failed to write register %x\n"
>>>>> +				, __func__, reg);
>>>>> +		return ret;
>>>>> +	}
>>>>> +	/* read the data */
>>>>> +	*val = i2c_smbus_read_byte(client);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>> I would much prefer if you'd use kernel-doc for comments oabout functions.
>>>>> +/*
>>>>> + * Reads and calculates current lux value.
>>>>> + * The raw ch0 and ch1 values of the ambient light sensed in the last
>>>>> + * integration cycle are read from the device.
>>>>> + * Time scale factor array values are adjusted based on the integration
>> time.
>>>>> + * The raw values are multiplied by a scale factor, and device gain is
>> obtained
>>>>> + * using gain index. Limit checks are done next, then the ratio of a multiple
>>>>> + * of ch1 value, to the ch0 value, is calculated. The array
>> tsl2x7x_device_lux[]
>>>>> + * declared above is then scanned to find the first ratio value that is just
>>>>> + * above the ratio we just calculated. The ch0 and ch1 multiplier constants
>> in
>>>>> + * the array are then used along with the time scale factor array values, to
>>>>> + * calculate the lux.
>>>>> + */
>>>>> +static int tsl2x7x_get_lux(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +	u16 ch0, ch1; /* separated ch0/ch1 data from device */
>>>>> +	u32 lux; /* raw lux calculated from device data */
>>>>> +	u64 lux64;
>>>>> +	u32 ratio;
>>>>> +	u8 buf[4];
>>>>> +	struct tsl2x7x_lux *p;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int i, ret;
>>>>> +	u32 ch0lux = 0;
>>>>> +	u32 ch1lux = 0;
>>>>> +
>>>>> +	if (mutex_trylock(&chip->als_mutex) == 0) {
>>>>> +		dev_info(&chip->client->dev, "tsl2x7x_get_lux device is
>>>> busy\n");
>>>>> +		return chip->als_cur_info.lux; /* busy, so return LAST VALUE */
>>>>> +	}
>>>>> +
>>>>> +	if (chip->tsl2x7x_chip_status != TSL2X7X_CHIP_WORKING) {
>>>>> +		/* device is not enabled */
>>>>> +		dev_err(&chip->client->dev, "%s: device is not enabled\n",
>>>>> +				__func__);
>>>>> +		ret = -EBUSY ;
>>>>> +		goto out_unlock;
>>>>> +	}
>>>>> +
>>>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>>>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0]);
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed to read CMD_REG\n", __func__);
>>>>> +		goto out_unlock;
>>>>> +	}
>>>>> +	/* is data new&   valid */
>>>>> +	if (!(buf[0]&   TSL2X7X_STA_ADC_VALID)) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: data not valid yet\n", __func__);
>>>>> +		ret = chip->als_cur_info.lux; /* return LAST VALUE */
>>>>> +		goto out_unlock;
>>>>> +	}
>>>>> +
>>>>> +	for (i = 0; i<   4; i++) {
>>>>> +		ret = tsl2x7x_i2c_read(chip->client,
>>>>> +			(TSL2X7X_CMD_REG | (TSL2X7X_ALS_CHAN0LO + i)),
>>>>> +			&buf[i]);
>>>>> +		if (ret<   0) {
>>>>> +			dev_err(&chip->client->dev,
>>>>> +				"%s: failed to read. err=%x\n", __func__, ret);
>>>>> +			goto out_unlock;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	/* clear status, really interrupt status ( are off),
>>>>> +	but we use the bit anyway */
>>>>> +	ret = i2c_smbus_write_byte(chip->client,
>>>>> +		(TSL2X7X_CMD_REG |
>>>>> +				TSL2X7X_CMD_SPL_FN |
>>>>> +				TSL2X7X_CMD_ALS_INT_CLR));
>>>>> +
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +		"i2c_write_command failed in %s, err = %d\n",
>>>>> +			__func__, ret);
>>>>> +		goto out_unlock; /* have no data, so return failure */
>>>>> +	}
>>>>> +
>>>>> +	/* extract ALS/lux data */
>>>>> +	ch0 = le16_to_cpup((const __le16 *)&buf[0]);
>>>>> +	ch1 = le16_to_cpup((const __le16 *)&buf[2]);
>>>>> +
>>>>> +	chip->als_cur_info.als_ch0 = ch0;
>>>>> +	chip->als_cur_info.als_ch1 = ch1;
>>>>> +
>>>>> +	if ((ch0>= chip->als_saturation) || (ch1>= chip->als_saturation)) {
>>>>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
>>>>> +		goto return_max;
>>>>> +	}
>>>>> +
>>>>> +	if (ch0 == 0) {
>>>>> +		/* have no data, so return LAST VALUE */
>>>>> +		ret = chip->als_cur_info.lux = 0;
>>>>> +		goto out_unlock;
>>>>> +	}
>>>>> +	/* calculate ratio */
>>>>> +	ratio = (ch1<<   15) / ch0;
>>>>> +	/* convert to unscaled lux using the pointer to the table */
>>>>> +	p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
>>>>> +	while (p->ratio != 0&&   p->ratio<   ratio)
>>>>> +			p++;
>>>>> +
>>>>> +	if (p->ratio == 0) {
>>>>> +		lux = 0;
>>>>> +	} else {
>>>>> +		ch0lux = DIV_ROUND_UP((ch0 * p->ch0),
>>>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
>>>>> +		ch1lux = DIV_ROUND_UP((ch1 * p->ch1),
>>>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain]);
>>>>> +		lux = ch0lux - ch1lux;
>>>>> +	}
>>>>> +
>>>>> +	/* note: lux is 31 bit max at this point */
>>>>> +	if (ch1lux>   ch0lux) {
>>>>> +		dev_dbg(&chip->client->dev, "Returning last value\n");
>>>>> +		ret = chip->als_cur_info.lux;
>>>>> +		goto out_unlock;
>>>>> +	}
>>>>> +
>>>>> +	/* adjust for active time scale */
>>>>> +	if (chip->als_time_scale == 0)
>>>>> +		lux = 0;
>>>>> +	else
>>>>> +		lux = (lux + (chip->als_time_scale>>   1)) /
>>>>> +			chip->als_time_scale;
>>>>> +
>>>>> +	/* adjust for active gain scale
>>>>> +	 * The tsl2x7x_device_lux tables have a factor of 256 built-in.
>>>>> +	 * User-specified gain provides a multiplier.
>>>>> +	 * Apply user-specified gain before shifting right to retain precision.
>>>>> +	 * Use 64 bits to avoid overflow on multiplication.
>>>>> +	 * Then go back to 32 bits before division to avoid using div_u64().
>>>>> +	 */
>>>>> +	lux64 = lux;
>>>>> +	lux64 = lux64 * chip->tsl2x7x_settings.als_gain_trim;
>>>>> +	lux64>>= 8;
>>>>> +	lux = lux64;
>>>>> +	lux = (lux + 500) / 1000;
>>>>> +
>>>>> +	if (lux>   TSL2X7X_LUX_CALC_OVER_FLOW) /* check for overflow */
>>>>> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
>>>>> +
>>>>> +	/* Update the structure with the latest lux. */
>>>>> +return_max:
>>>>> +	chip->als_cur_info.lux = lux;
>>>>> +	ret = lux;
>>>>> +
>>>>> +out_unlock:
>>>>> +	mutex_unlock(&chip->als_mutex);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Proximity poll function - if valid data is available, read and form the ch0
>>>>> + * and prox data values, check for limits on the ch0 value, and check the
>> prox
>>>>> + * data against the current thresholds, to set the event status accordingly.
>>>>> + */
>>>>> +static int tsl2x7x_prox_poll(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +#define CONSECUTIVE_RETRIES 50
>>>>> +
>>>>> +	int i;
>>>>> +	int ret;
>>>>> +	u8 status;
>>>>> +	u8 chdata[2];
>>>>> +	int err_cnt;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	if (mutex_trylock(&chip->prox_mutex) == 0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: Can't get prox mutex\n", __func__);
>>>>> +		return -EBUSY;
>>>>> +	}
>>>>> +
>>>>> +	err_cnt = 0;
>>>>> +
>>>>> +try_again:
>>>> I'd like a comment on why this looping is necessary....
>>>>> +
>>>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>>>> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status);
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +		"%s: i2c err=%d\n", __func__, ret);
>>>>> +		goto prox_poll_err;
>>>>> +	}
>>>>> +
>>>>> +	if (chip->id<   tsl2572) {
>>>>> +		if (!(status&   TSL2X7X_STA_ADC_VALID)) {
>>>>> +			err_cnt++;
>>>>> +			if (err_cnt>   CONSECUTIVE_RETRIES) {
>>>>> +				dev_err(&chip->client->dev,
>>>>> +				"%s: Consec. retries exceeded\n", __func__);
>>>>> +				goto prox_poll_err;
>>>>> +			}
>>>>> +		goto try_again;
>>>>> +		}
>>>>> +	} else {
>>>>> +		if (!(status&   TSL2X7X_STA_PRX_VALID)) {
>>>>> +			err_cnt++;
>>>>> +			if (err_cnt>   CONSECUTIVE_RETRIES) {
>>>>> +				dev_err(&chip->client->dev,
>>>>> +				"%s: Consec. retries exceeded\n", __func__);
>>>>> +				goto prox_poll_err;
>>>>> +			}
>>>>> +		goto try_again;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	for (i = 0; i<   2; i++) {
>>>>> +		ret = tsl2x7x_i2c_read(chip->client,
>>>>> +			(TSL2X7X_CMD_REG |
>>>>> +					(TSL2X7X_PRX_LO + i)),&chdata[i]);
>>>>> +		if (ret<   0)
>>>>> +			goto prox_poll_err;
>>>>> +	}
>>>>> +
>>>>> +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
>>>>> +	if (chip->prox_cur_info.prox_data == 0)
>>>>> +		goto try_again;
>>>>> +
>>>>> +	if (chip->prox_cur_info.prox_data>=
>>>>> +			chip->tsl2x7x_settings.prox_thres_high)
>>>>> +		chip->prox_cur_info.prox_event = 1;
>>>>> +	else
>>>>> +		chip->prox_cur_info.prox_event = 0;
>>>> So this is manually polling the event signal. I'd argue that this is a
>>>> job for userspace
>>>> if the device isn't doing it hardware (or the interrupt signal is
>>>> connected).
>>>>> +
>>>>> +	mutex_unlock(&chip->prox_mutex);
>>>>> +	return chip->prox_cur_info.prox_event;
>>>>> +
>>>>> +prox_poll_err:
>>>>> +	mutex_unlock(&chip->prox_mutex);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Provides initial operational parameter defaults.
>>>>> + * These defaults may be changed through the device's sysfs files.
>>>>> + */
>>>>> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
>>>>> +{
>>>>> +	/* If Operational settings defined elsewhere.. */
>>>>> +	if (chip->pdata&&   chip->pdata->platform_default_settings != 0)
>>>>> +		memcpy(&(chip->tsl2x7x_settings),
>>>>> +			chip->pdata->platform_default_settings,
>>>>> +			sizeof(tsl2x7x_default_settings));
>>>>> +	else
>>>>> +		memcpy(&(chip->tsl2x7x_settings),
>>>>> +			&tsl2x7x_default_settings,
>>>>> +			sizeof(tsl2x7x_default_settings));
>>>>> +
>>>>> +	/* Load up the proper lux table. */
>>>>> +	if (chip->pdata&&   chip->pdata->platform_lux_table[0].ratio != 0)
>>>>> +		memcpy(chip->tsl2x7x_device_lux,
>>>>> +			chip->pdata->platform_lux_table,
>>>>> +			sizeof(chip->pdata->platform_lux_table));
>>>>> +	else
>>>>> +		memcpy(chip->tsl2x7x_device_lux,
>>>>> +		(struct tsl2x7x_lux *)tsl2x7x_default_lux_table_group[chip->id],
>>>>> +				MAX_DEFAULT_TABLE_BYTES);
>>>>> +
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Obtain single reading and calculate the als_gain_trim
>>>>> + * (later used to derive actual lux).
>>>>> + * Return updated gain_trim value.
>>>>> + */
>>>>> +static int tsl2x7x_als_calibrate(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	u8 reg_val;
>>>>> +	int gain_trim_val;
>>>>> +	int ret;
>>>>> +	int lux_val;
>>>>> +
>>>>> +	ret = i2c_smbus_write_byte(chip->client,
>>>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +		"%s: failed to write CNTRL register, ret=%d\n",
>>>>> +		__func__, ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	reg_val = i2c_smbus_read_byte(chip->client);
>>>>> +	if ((reg_val&   (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON))
>>>>> +		!= (TSL2X7X_CNTL_ADC_ENBL | TSL2X7X_CNTL_PWR_ON)) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed: ADC not enabled\n", __func__);
>>>>> +		return -1;
>>>>> +	}
>>>>> +
>>>>> +	ret = i2c_smbus_write_byte(chip->client,
>>>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed to write ctrl reg: ret=%d\n",
>>>>> +			__func__, ret);
>>>>> +		return ret;
>>>>> +	}
>>>>> +
>>>>> +	reg_val = i2c_smbus_read_byte(chip->client);
>>>>> +	if ((reg_val&   TSL2X7X_STA_ADC_VALID) != TSL2X7X_STA_ADC_VALID) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed: STATUS - ADC not valid.\n", __func__);
>>>>> +		return -ENODATA;
>>>>> +	}
>>>>> +
>>>>> +	lux_val = tsl2x7x_get_lux(indio_dev);
>>>>> +	if (lux_val<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +		"%s: failed to get lux\n", __func__);
>>>>> +		return lux_val;
>>>>> +	}
>>>>> +
>>>>> +	gain_trim_val =  (((chip->tsl2x7x_settings.als_cal_target)
>>>>> +			* chip->tsl2x7x_settings.als_gain_trim) / lux_val);
>>>>> +	if ((gain_trim_val<   250) || (gain_trim_val>   4000))
>>>>> +		return -ERANGE;
>>>>> +
>>>>> +	chip->tsl2x7x_settings.als_gain_trim = gain_trim_val;
>>>>> +	dev_info(&chip->client->dev,
>>>>> +		"%s als_calibrate completed\n", chip->client->name);
>>>>> +
>>>>> +	return (int) gain_trim_val;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Turn the device on.
>>>>> + * Configuration must be set before calling this function.
>>>>> + */
>>>>> +static int tsl2x7x_chip_on(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +	int i;
>>>>> +	int ret = 0;
>>>>> +	u8 *dev_reg;
>>>>> +	u8 utmp;
>>>>> +	int als_count;
>>>>> +	int als_time;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	u8 reg_val = 0;
>>>>> +
>>>>> +	if (chip->pdata&&   chip->pdata->power_on)
>>>>> +		chip->pdata->power_on(indio_dev);
>>>>> +
>>>>> +	/* Non calculated parameters */
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_TIME] =
>>>>> +			chip->tsl2x7x_settings.prx_time;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_WAIT_TIME] =
>>>>> +			chip->tsl2x7x_settings.wait_time;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_CONFIG] =
>>>>> +			chip->tsl2x7x_settings.prox_config;
>>>>> +
>>>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHLO] =
>>>>> +		(chip->tsl2x7x_settings.als_thresh_low)&   0xFF;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MINTHRESHHI] =
>>>>> +		(chip->tsl2x7x_settings.als_thresh_low>>   8)&   0xFF;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHLO] =
>>>>> +		(chip->tsl2x7x_settings.als_thresh_high)&   0xFF;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_MAXTHRESHHI] =
>>>>> +		(chip->tsl2x7x_settings.als_thresh_high>>   8)&   0xFF;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PERSISTENCE] =
>>>>> +		chip->tsl2x7x_settings.als_persistence;
>>>>> +
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_COUNT] =
>>>>> +			chip->tsl2x7x_settings.prox_pulse_count;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MINTHRESHLO] =
>>>>> +	chip->tsl2x7x_settings.prox_thres_low;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_PRX_MAXTHRESHLO] =
>>>>> +			chip->tsl2x7x_settings.prox_thres_high;
>>>>> +
>>>>> +	/* and make sure we're not already on */
>>>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
>>>>> +		/* if forcing a register update - turn off, then on */
>>>>> +		dev_info(&chip->client->dev, "device is already enabled\n");
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	/* determine als integration regster */
>>>>> +	als_count = (chip->tsl2x7x_settings.als_time * 100 + 135) / 270;
>>>>> +	if (als_count == 0)
>>>>> +		als_count = 1; /* ensure at least one cycle */
>>>>> +
>>>>> +	/* convert back to time (encompasses overrides) */
>>>>> +	als_time = (als_count * 27 + 5) / 10;
>>>>> +	chip->tsl2x7x_config[TSL2X7X_ALS_TIME] = 256 - als_count;
>>>>> +
>>>>> +	/* Set the gain based on tsl2x7x_settings struct */
>>>>> +	chip->tsl2x7x_config[TSL2X7X_GAIN] =
>>>>> +		(chip->tsl2x7x_settings.als_gain |
>>>>> +			(TSL2X7X_mA100 | TSL2X7X_DIODE1)
>>>>> +			| ((chip->tsl2x7x_settings.prox_gain)<<   2));
>>>>> +
>>>>> +	/* set chip struct re scaling and saturation */
>>>>> +	chip->als_saturation = als_count * 922; /* 90% of full scale */
>>>>> +	chip->als_time_scale = (als_time + 25) / 50;
>>>>> +
>>>>> +	/* TSL2X7X Specific power-on / adc enable sequence
>>>>> +	 * Power on the device 1st. */
>>>>> +	utmp = TSL2X7X_CNTL_PWR_ON;
>>>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>>>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed on CNTRL reg.\n", __func__);
>>>>> +		return -1;
>>>>> +	}
>>>>> +
>>>>> +	/* Use the following shadow copy for our delay before enabling ADC.
>>>>> +	 * Write all the registers. */
>>>>> +	for (i = 0, dev_reg = chip->tsl2x7x_config;
>>>>> +			i<   TSL2X7X_MAX_CONFIG_REG; i++) {
>>>>> +		ret = i2c_smbus_write_byte_data(chip->client,
>>>>> +				TSL2X7X_CMD_REG + i, *dev_reg++);
>>>>> +		if (ret<   0) {
>>>>> +			dev_err(&chip->client->dev,
>>>>> +			"%s: failed on write to reg %d.\n", __func__, i);
>>>>> +			return ret;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	udelay(3000);	/* Power-on settling time */
>>>>> +
>>>>> +	/* NOW enable the ADC
>>>>> +	 * initialize the desired mode of operation */
>>>>> +	utmp = TSL2X7X_CNTL_PWR_ON |
>>>>> +			TSL2X7X_CNTL_ADC_ENBL |
>>>>> +			TSL2X7X_CNTL_PROX_DET_ENBL;
>>>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>>>> +			TSL2X7X_CMD_REG | TSL2X7X_CNTRL, utmp);
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: failed on 2nd CTRL reg.\n", __func__);
>>>>> +		return ret;
>>>>> +		}
>>>>> +
>>>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_WORKING;
>>>>> +
>>>>> +	if (chip->tsl2x7x_settings.interrupts_en != 0) {
>>>>> +		dev_info(&chip->client->dev, "Setting Up Interrupt(s)\n");
>>>>> +
>>>>> +		reg_val = TSL2X7X_CNTL_PWR_ON |
>>>> TSL2X7X_CNTL_ADC_ENBL;
>>>>> +		if ((chip->tsl2x7x_settings.interrupts_en == 0x20) ||
>>>>> +			(chip->tsl2x7x_settings.interrupts_en == 0x30))
>>>>> +			reg_val |= TSL2X7X_CNTL_PROX_DET_ENBL;
>>>>> +
>>>>> +		reg_val |= chip->tsl2x7x_settings.interrupts_en;
>>>>> +		ret = i2c_smbus_write_byte_data(chip->client,
>>>>> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL), reg_val);
>>>>> +		if (ret<   0)
>>>>> +			dev_err(&chip->client->dev,
>>>>> +				"%s: failed in tsl2x7x_IOCTL_INT_SET.\n",
>>>>> +				__func__);
>>>>> +
>>>>> +		/* Clear out any initial interrupts  */
>>>>> +		ret = i2c_smbus_write_byte(chip->client,
>>>>> +			TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
>>>>> +			TSL2X7X_CMD_PROXALS_INT_CLR);
>>>>> +		if (ret<   0) {
>>>>> +			dev_err(&chip->client->dev,
>>>>> +				"%s: failed in tsl2x7x_chip_on\n",
>>>>> +				__func__);
>>>>> +		return ret;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_chip_off(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +	int ret;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	/* turn device off */
>>>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
>>>>> +
>>>>> +	ret = i2c_smbus_write_byte_data(chip->client,
>>>>> +		TSL2X7X_CMD_REG | TSL2X7X_CNTRL, 0x00);
>>>>> +
>>>>> +	if (chip->pdata&&   chip->pdata->power_off)
>>>>> +		chip->pdata->power_off(chip->client);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Proximity calibration helper function
>>>>> + * runs through a collection of data samples,
>>>>> + * sets the min, max, mean, and std dev.
>>>>> + */
>>>>> +static
>>>>> +void tsl2x7x_prox_calculate(u16 *data, int length, struct prox_stat *statP)
>>>>> +{
>>>>> +	int i;
>>>>> +	int min, max, sum, mean;
>>>>> +	unsigned long stddev;
>>>>> +	int tmp;
>>>>> +
>>>>> +	if (length == 0)
>>>>> +		length = 1;
>>>>> +
>>>>> +	sum = 0;
>>>>> +	min = INT_MAX;
>>>>> +	max = INT_MIN;
>>>>> +	for (i = 0; i<   length; i++) {
>>>>> +		sum += data[i];
>>>> avoid using min as a variable name (as it's also an appropriate function)
>>>> _min = MIN(data[i], _min); saves you a line of code.
>>>>> +		if (data[i]<   min)
>>>>> +			min = data[i];
>>>>> +		if (data[i]>   max)
>>>>> +			max = data[i];
>>>>> +	}
>>>>> +	mean = sum/length;
>>>>> +	statP->min = min;
>>>>> +	statP->max = max;
>>>>> +	statP->mean = mean;
>>>>> +
>>>>> +	sum = 0;
>>>>> +	for (i = 0; i<   length; i++) {
>>>>> +		tmp = data[i]-mean;
>>>>> +		sum += tmp * tmp;
>>>>> +	}
>>>>> +	stddev = int_sqrt((long)sum)/length;
>>>>> +	statP->stddev = stddev;
>>>>> +}
>>>>> +
>>>>> +/**
>>>>> + * Proximity calibration - collects a number of samples,
>>>>> + * calculates a standard deviation based on the samples, and
>>>>> + * sets the threshold accordingly.
>>>>> + */
>>>>> +static void tsl2x7x_prox_cal(struct iio_dev *indio_dev)
>>>>> +{
>>>>> +	u16 prox_history[MAX_SAMPLES_CAL+1];
>>>> spaces around that +
>>>>> +	int i;
>>>>> +	struct prox_stat prox_stat_data[2];
>>>>> +	struct prox_stat *calP;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	u8 tmp_irq_settings;
>>>>> +	u8 current_state = chip->tsl2x7x_chip_status;
>>>>> +
>>>>> +	if (chip->tsl2x7x_settings.prox_max_samples_cal>   MAX_SAMPLES_CAL)
>>>> {
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: max prox samples cal is too big: %d\n",
>>>>> +			__func__, chip-
>>>>> tsl2x7x_settings.prox_max_samples_cal);
>>>>> +		chip->tsl2x7x_settings.prox_max_samples_cal =
>>>> MAX_SAMPLES_CAL;
>>>>> +	}
>>>>> +
>>>>> +	/* have to stop to change settings */
>>>>> +	tsl2x7x_chip_off(indio_dev);
>>>>> +
>>>>> +	/* Enable proximity detection save just in case prox not wanted yet*/
>>>>> +	tmp_irq_settings = chip->tsl2x7x_settings.interrupts_en;
>>>>> +	chip->tsl2x7x_settings.interrupts_en |=
>>>> TSL2X7X_CNTL_PROX_INT_ENBL;
>>>>> +
>>>>> +	/*turn on device if not already on*/
>>>>> +	tsl2x7x_chip_on(indio_dev);
>>>>> +
>>>>> +	/*gather the samples*/
>>>>> +	for (i = 0; i<   chip->tsl2x7x_settings.prox_max_samples_cal; i++) {
>>>>> +		mdelay(15);
>>>>> +		tsl2x7x_prox_poll(indio_dev);
>>>>> +		prox_history[i] = chip->prox_cur_info.prox_data;
>>>>> +		dev_info(&chip->client->dev, "2 i=%d prox data= %d\n",
>>>>> +			i, chip->prox_cur_info.prox_data);
>>>>> +	}
>>>>> +
>>>>> +	tsl2x7x_chip_off(indio_dev);
>>>>> +	calP =&prox_stat_data[PROX_STAT_CAL];
>>>>> +	tsl2x7x_prox_calculate(prox_history,
>>>>> +		chip->tsl2x7x_settings.prox_max_samples_cal, calP);
>>>>> +	chip->tsl2x7x_settings.prox_thres_high = (calP->max<<   1) - calP-
>>>>> mean;
>>>>> +
>>>>> +	dev_info(&chip->client->dev, " cal min=%d mean=%d max=%d\n",
>>>>> +		calP->min, calP->mean, calP->max);
>>>>> +	dev_info(&chip->client->dev,
>>>>> +		"%s proximity threshold set to %d\n",
>>>>> +		chip->client->name, chip->tsl2x7x_settings.prox_thres_high);
>>>>> +
>>>>> +	/* back to the way they were */
>>>>> +	chip->tsl2x7x_settings.interrupts_en = tmp_irq_settings;
>>>>> +	if (current_state == TSL2X7X_CHIP_WORKING)
>>>>> +		tsl2x7x_chip_on(indio_dev);
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n", chip->tsl2x7x_chip_status);
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_power_state_store(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	bool value;
>>>>> +
>>>>> +	if (strtobool(buf,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (!value)
>>>>> +		tsl2x7x_chip_off(indio_dev);
>>>>> +	else
>>>>> +		tsl2x7x_chip_on(indio_dev);
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	if (chip->id>   tsl2771)
>>>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 128");
>>>>> +	else
>>>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 8 16 120");
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_prox_gain_available_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +		return snprintf(buf, PAGE_SIZE, "%s\n", "1 2 4 8");
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_als_time_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>>>> +			chip->tsl2x7x_settings.als_time);
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_als_time_store(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	unsigned long value;
>>>>> +
>>>>> +	if (kstrtoul(buf, 0,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if ((value<   50) || (value>   650))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (value % 50)
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	 chip->tsl2x7x_settings.als_time = value;
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static IIO_CONST_ATTR(illuminance0_integration_time_available,
>>>>> +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
>>>>> +
>>>>> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>>>> +			chip->tsl2x7x_settings.als_cal_target);
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_als_cal_target_store(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	unsigned long value;
>>>>> +
>>>>> +	if (kstrtoul(buf, 0,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (value)
>>>>> +		chip->tsl2x7x_settings.als_cal_target = value;
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +/* sampling_frequency AKA persistence in data sheet */
>>>>> +static ssize_t tsl2x7x_als_persistence_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	return snprintf(buf, PAGE_SIZE, "%d\n",
>>>>> +			chip->tsl2x7x_settings.als_persistence);
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_als_persistence_store(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	unsigned long value;
>>>>> +
>>>>> +	if (kstrtoul(buf, 0,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	chip->tsl2x7x_settings.als_persistence = value;
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static IIO_CONST_ATTR(sampling_frequency_available,
>>>>> +		"0x00 - 0xFF (0 - 255)");
>>>>> +
>>>>> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	bool value;
>>>>> +
>>>>> +	if (strtobool(buf,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (value)
>>>>> +		tsl2x7x_als_calibrate(indio_dev);
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
>>>>> +	struct device_attribute *attr, char *buf)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int i;
>>>>> +	int offset = 0;
>>>>> +
>>>>> +	i = 0;
>>>> Set i at the declaration above.
>>>>> +	while (i<   (TSL2X7X_MAX_LUX_TABLE_SIZE * 3)) {
>>>>> +		offset += snprintf(buf + offset, PAGE_SIZE, "%d,%d,%d,",
>>>>> +			chip->tsl2x7x_device_lux[i].ratio,
>>>>> +			chip->tsl2x7x_device_lux[i].ch0,
>>>>> +			chip->tsl2x7x_device_lux[i].ch1);
>>>>> +		if (chip->tsl2x7x_device_lux[i].ratio == 0) {
>>>>> +			/* We just printed the first "0" entry.
>>>>> +			 * Now get rid of the extra "," and break. */
>>>>> +			offset--;
>>>>> +			break;
>>>>> +		}
>>>>> +		i++;
>>>>> +	}
>>>>> +
>>>>> +	offset += snprintf(buf + offset, PAGE_SIZE, "\n");
>>>>> +	return offset;
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_luxtable_store(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int value[ARRAY_SIZE(chip->tsl2x7x_device_lux)*3 + 1];
>>>>> +	int n;
>>>>> +
>>>>> +	get_options(buf, ARRAY_SIZE(value), value);
>>>>> +
>>>>> +	/* We now have an array of ints starting at value[1], and
>>>>> +	 * enumerated by value[0].
>>>>> +	 * We expect each group of three ints is one table entry,
>>>>> +	 * and the last table entry is all 0.
>>>>> +	 */
>>>>> +	n = value[0];
>>>>> +	if ((n % 3) || n<   6 ||
>>>>> +			n>   ((ARRAY_SIZE(chip->tsl2x7x_device_lux) - 1) * 3)) {
>>>>> +		dev_info(dev, "LUX TABLE INPUT ERROR 1 Value[0]=%d\n", n);
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	if ((value[(n - 2)] | value[(n - 1)] | value[n]) != 0) {
>>>>> +		dev_info(dev, "LUX TABLE INPUT ERROR 2 Value[0]=%d\n", n);
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING)
>>>>> +		tsl2x7x_chip_off(indio_dev);
>>>>> +
>>>>> +	/* Zero out the table */
>>>>> +	memset(chip->tsl2x7x_device_lux, 0, sizeof(chip->tsl2x7x_device_lux));
>>>>> +	memcpy(chip->tsl2x7x_device_lux,&value[1], (value[0] * 4));
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static ssize_t tsl2x7x_do_prox_calibrate(struct device *dev,
>>>>> +	struct device_attribute *attr, const char *buf, size_t len)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	bool value;
>>>>> +
>>>>> +	if (strtobool(buf,&value))
>>>>> +		return -EINVAL;
>>>>> +
>>>>> +	if (value)
>>>>> +		tsl2x7x_prox_cal(indio_dev);
>>>>> +
>>>>> +	return len;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_read_interrupt_config(struct iio_dev *indio_dev,
>>>>> +					 u64 event_code)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int ret;
>>>>> +
>>>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>>>>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&   0x10);
>>>>> +	else
>>>>> +		ret = !!(chip->tsl2x7x_settings.interrupts_en&   0x20);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_write_interrupt_config(struct iio_dev *indio_dev,
>>>>> +					  u64 event_code,
>>>>> +					  int val)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>>>> {
>>>>> +		if (val)
>>>>> +			chip->tsl2x7x_settings.interrupts_en |= 0x10;
>>>>> +		else
>>>>> +			chip->tsl2x7x_settings.interrupts_en&= 0x20;
>>>>> +	} else {
>>>>> +		if (val)
>>>>> +			chip->tsl2x7x_settings.interrupts_en |= 0x20;
>>>>> +		else
>>>>> +			chip->tsl2x7x_settings.interrupts_en&= 0x10;
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_write_thresh(struct iio_dev *indio_dev,
>>>>> +				  u64 event_code,
>>>>> +				  int val)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>>>> {
>>>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>>>> +		case IIO_EV_DIR_RISING:
>>>>> +			chip->tsl2x7x_settings.als_thresh_high = val;
>>>>> +			break;
>>>>> +		case IIO_EV_DIR_FALLING:
>>>>> +			chip->tsl2x7x_settings.als_thresh_low = val;
>>>>> +			break;
>>>>> +		default:
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +	} else {
>>>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>>>> +		case IIO_EV_DIR_RISING:
>>>>> +			chip->tsl2x7x_settings.prox_thres_high = val;
>>>>> +			break;
>>>>> +		case IIO_EV_DIR_FALLING:
>>>>> +			chip->tsl2x7x_settings.prox_thres_low = val;
>>>>> +			break;
>>>>> +		default:
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_read_thresh(struct iio_dev *indio_dev,
>>>>> +			       u64 event_code,
>>>>> +			       int *val)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	if (IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code) == IIO_LIGHT)
>>>> {
>>>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>>>> +		case IIO_EV_DIR_RISING:
>>>>> +			*val = chip->tsl2x7x_settings.als_thresh_high;
>>>>> +			break;
>>>>> +		case IIO_EV_DIR_FALLING:
>>>>> +			*val = chip->tsl2x7x_settings.als_thresh_low;
>>>>> +			break;
>>>>> +		default:
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +	} else {
>>>>> +		switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) {
>>>>> +		case IIO_EV_DIR_RISING:
>>>>> +			*val = chip->tsl2x7x_settings.prox_thres_high;
>>>>> +			break;
>>>>> +		case IIO_EV_DIR_FALLING:
>>>>> +			*val = chip->tsl2x7x_settings.prox_thres_low;
>>>>> +			break;
>>>>> +		default:
>>>>> +			return -EINVAL;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_read_raw(struct iio_dev *indio_dev,
>>>>> +			    struct iio_chan_spec const *chan,
>>>>> +			    int *val,
>>>>> +			    int *val2,
>>>>> +			    long mask)
>>>>> +{
>>>>> +	int ret = -EINVAL;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	switch (mask) {
>>>>> +	case 0:
>>>>> +		switch (chan->type) {
>>>>> +		case IIO_LIGHT:
>>>>> +			tsl2x7x_get_lux(indio_dev);
>>>>> +			if (chan->processed_val)
>>>>> +				*val = chip->als_cur_info.lux;
>>>>> +			else
>>>>> +				*val = (((chip->als_cur_info.als_ch0<<   16) |
>>>>> +					chip->als_cur_info.als_ch1));
>>>> Why have a processed read back and a raw readback?
>>>>> +			ret = IIO_VAL_INT;
>>>>> +			break;
>>>>> +		case IIO_PROXIMITY:
>>>>> +			tsl2x7x_prox_poll(indio_dev);
>>>>> +			if (chan->processed_val)
>>>> Hmm.. this is uggly.  We effectively have polling of an event status.
>>>> Normally
>>>> I'd expect the event to only occur as a an IIO event rather than being
>>>> readable like
>>>> this...
>>>>> +				*val = chip->prox_cur_info.prox_event;
>>>>> +			else
>>>>> +				*val = chip->prox_cur_info.prox_data;
>>>>> +			ret = IIO_VAL_INT;
>>>>> +			break;
>>>>> +		default:
>>>>> +			return -EINVAL;
>>>>> +			break;
>>>>> +		}
>>>>> +		break;
>>>>> +	case IIO_CHAN_INFO_CALIBSCALE:
>>>>> +		if (chan->type == IIO_LIGHT)
>>>>> +			*val =
>>>>> +			tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain];
>>>>> +		else
>>>>> +			*val =
>>>>> +			tsl2X7X_prx_gainadj[chip->tsl2x7x_settings.prox_gain];
>>>>> +		ret = IIO_VAL_INT;
>>>>> +		break;
>>>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>>>> +		*val = chip->tsl2x7x_settings.als_gain_trim;
>>>>> +		ret = IIO_VAL_INT;
>>>>> +		break;
>>>>> +
>>>>> +	default:
>>>>> +		ret = -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_write_raw(struct iio_dev *indio_dev,
>>>>> +			       struct iio_chan_spec const *chan,
>>>>> +			       int val,
>>>>> +			       int val2,
>>>>> +			       long mask)
>>>>> +{
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +
>>>>> +	switch (mask) {
>>>>> +	case IIO_CHAN_INFO_CALIBSCALE:
>>>>> +		if (chan->type == IIO_LIGHT) {
>>>>> +			switch (val) {
>>>>> +			case 1:
>>>>> +				chip->tsl2x7x_settings.als_gain = 0;
>>>>> +				break;
>>>>> +			case 8:
>>>>> +				chip->tsl2x7x_settings.als_gain = 1;
>>>>> +				break;
>>>>> +			case 16:
>>>>> +				chip->tsl2x7x_settings.als_gain = 2;
>>>>> +				break;
>>>>> +			case 120:
>>>>> +				if (chip->id>   tsl2771)
>>>>> +					return -EINVAL;
>>>>> +				chip->tsl2x7x_settings.als_gain = 3;
>>>>> +				break;
>>>>> +			case 128:
>>>>> +				if (chip->id<   tsl2572)
>>>>> +					return -EINVAL;
>>>>> +				chip->tsl2x7x_settings.als_gain = 3;
>>>>> +				break;
>>>>> +			default:
>>>>> +				return -EINVAL;
>>>>> +			}
>>>>> +		} else {
>>>>> +			switch (val) {
>>>>> +			case 1:
>>>>> +				chip->tsl2x7x_settings.prox_gain = 0;
>>>>> +				break;
>>>>> +			case 2:
>>>>> +				chip->tsl2x7x_settings.prox_gain = 1;
>>>>> +				break;
>>>>> +			case 4:
>>>>> +				chip->tsl2x7x_settings.prox_gain = 2;
>>>>> +				break;
>>>>> +			case 8:
>>>>> +				chip->tsl2x7x_settings.prox_gain = 3;
>>>>> +				break;
>>>>> +			default:
>>>>> +				return -EINVAL;
>>>>> +			}
>>>>> +		}
>>>>> +		break;
>>>>> +	case IIO_CHAN_INFO_CALIBBIAS:
>>>>> +		chip->tsl2x7x_settings.als_gain_trim = val;
>>>>> +		break;
>>>>> +
>>>>> +	default:
>>>>> +		return -EINVAL;
>>>>> +	}
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
>>>>> +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
>>>>> +
>>>>> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
>>>>> +		tsl2x7x_prox_gain_available_show, NULL);
>>>>> +
>>>>> +static DEVICE_ATTR(illuminance0_calibscale_available, S_IRUGO,
>>>>> +		tsl2x7x_gain_available_show, NULL);
>>>>> +
>>>>> +static DEVICE_ATTR(illuminance0_integration_time, S_IRUGO | S_IWUSR,
>>>>> +		tsl2x7x_als_time_show, tsl2x7x_als_time_store);
>>>>> +
>>>>> +static DEVICE_ATTR(illuminance0_target_input, S_IRUGO | S_IWUSR,
>>>>> +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
>>>>> +
>>>>> +static DEVICE_ATTR(illuminance0_calibrate, S_IWUSR, NULL,
>>>>> +		tsl2x7x_do_calibrate);
>>>>> +
>>>>> +static DEVICE_ATTR(proximity_calibrate, S_IWUSR, NULL,
>>>>> +		tsl2x7x_do_prox_calibrate);
>>>>> +
>>>>> +static DEVICE_ATTR(illuminance0_lux_table, S_IRUGO | S_IWUSR,
>>>>> +		tsl2x7x_luxtable_show, tsl2x7x_luxtable_store);
>>>>> +
>>>>> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
>>>>> +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
>>>>> +
>>>>> +/* Use the default register values to identify the Taos device */
>>>>> +static int tsl2x7x_device_id(unsigned char *id, int target)
>>>>> +{
>>>>> +	switch (target) {
>>>>> +	case tsl2571:
>>>>> +	case tsl2671:
>>>>> +	case tsl2771:
>>>>> +		return ((*id&   0xf0) == TRITON_ID);
>>>>> +	break;
>>>>> +	case tmd2671:
>>>>> +	case tmd2771:
>>>>> +		return ((*id&   0xf0) == HALIBUT_ID);
>>>>> +	break;
>>>>> +	case tsl2572:
>>>>> +	case tsl2672:
>>>>> +	case tmd2672:
>>>>> +	case tsl2772:
>>>>> +	case tmd2772:
>>>>> +		return ((*id&   0xf0) == SWORDFISH_ID);
>>>>> +	break;
>>>>> +	}
>>>>> +
>>>>> +	return -EINVAL;
>>>>> +}
>>>>> +
>>>>> +/*
>>>>> + * Interrupt Event Handler */
>>>>> +static irqreturn_t tsl2x7x_event_handler(int irq, void *private)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = private;
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	s64 timestamp = iio_get_time_ns();
>>>>> +	int ret;
>>>>> +	int value;
>>>>> +
>>>>> +	value = i2c_smbus_read_byte_data(chip->client,
>>>>> +		TSL2X7X_CMD_REG | TSL2X7X_STATUS);
>>>>> +
>>>>> +	/* What type of interrupt do we need to process */
>>>>> +	if (value&   TSL2X7X_STA_PRX_INTR) {
>>>>> +		tsl2x7x_prox_poll(indio_dev);
>>>>> +		iio_push_event(indio_dev,
>>>>> +			       IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY,
>>>>> +						    0,
>>>>> +						    IIO_EV_TYPE_THRESH,
>>>>> +						    IIO_EV_DIR_EITHER),
>>>>> +						    timestamp);
>>>>> +	}
>>>>> +
>>>>> +	if (value&   TSL2X7X_STA_ALS_INTR) {
>>>>> +		tsl2x7x_get_lux(indio_dev);
>>>> why the get value?  No real guarantee you'll get the one that caused the
>>>> event as far as I can see.
>>>>> +		iio_push_event(indio_dev,
>>>>> +		       IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
>>>>> +					    0,
>>>>> +					    IIO_EV_TYPE_THRESH,
>>>>> +					    IIO_EV_DIR_EITHER),
>>>>> +					    timestamp);
>>>>> +	}
>>>>> +	/* Clear interrupt now that we have the status */
>>>>> +	ret = i2c_smbus_write_byte(chip->client,
>>>>> +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
>>>>> +		TSL2X7X_CMD_PROXALS_INT_CLR);
>>>>> +	if (ret<   0)
>>>>> +		dev_err(&chip->client->dev,
>>>>> +			"%s: Failed to clear irq from event handler. err = %d\n",
>>>>> +			__func__, ret);
>>>>> +
>>>>> +	return IRQ_HANDLED;
>>>>> +}
>>>>> +
>>>>> +static struct attribute *tsl2x7x_ALS_device_attrs[] = {
>>>>> +	&dev_attr_power_state.attr,
>>>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>>>> +	&dev_attr_illuminance0_integration_time.attr,
>>>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>>>> +	&dev_attr_illuminance0_target_input.attr,
>>>>> +	&dev_attr_illuminance0_calibrate.attr,
>>>>> +	&dev_attr_illuminance0_lux_table.attr,
>>>>> +	&dev_attr_sampling_frequency.attr,
>>>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>>>> +	NULL
>>>>> +};
>>>>> +
>>>>> +static struct attribute *tsl2x7x_PRX_device_attrs[] = {
>>>>> +	&dev_attr_power_state.attr,
>>>>> +	&dev_attr_sampling_frequency.attr,
>>>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>>>> +	&dev_attr_proximity_calibrate.attr,
>>>>> +	NULL
>>>>> +};
>>>>> +
>>>>> +static struct attribute *tsl2x7x_ALSPRX_device_attrs[] = {
>>>>> +	&dev_attr_power_state.attr,
>>>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>>>> +	&dev_attr_illuminance0_integration_time.attr,
>>>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>>>> +	&dev_attr_illuminance0_target_input.attr,
>>>>> +	&dev_attr_illuminance0_calibrate.attr,
>>>>> +	&dev_attr_illuminance0_lux_table.attr,
>>>>> +	&dev_attr_sampling_frequency.attr,
>>>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>>>> +	&dev_attr_proximity_calibrate.attr,
>>>>> +	NULL
>>>>> +};
>>>>> +
>>>>> +static struct attribute *tsl2x7x_PRX2_device_attrs[] = {
>>>>> +	&dev_attr_power_state.attr,
>>>>> +	&dev_attr_sampling_frequency.attr,
>>>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>>>> +	&dev_attr_proximity_calibrate.attr,
>>>>> +	&dev_attr_proximity_calibscale_available.attr,
>>>>> +	NULL
>>>>> +};
>>>>> +
>>>>> +static struct attribute *tsl2x7x_ALSPRX2_device_attrs[] = {
>>>>> +	&dev_attr_power_state.attr,
>>>>> +	&dev_attr_illuminance0_calibscale_available.attr,
>>>>> +	&dev_attr_illuminance0_integration_time.attr,
>>>>> +	&iio_const_attr_illuminance0_integration_time_available.dev_attr.attr,
>>>>> +	&dev_attr_illuminance0_target_input.attr,
>>>>> +	&dev_attr_illuminance0_calibrate.attr,
>>>>> +	&dev_attr_illuminance0_lux_table.attr,
>>>>> +	&dev_attr_sampling_frequency.attr,
>>>>> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
>>>>> +	&dev_attr_proximity_calibrate.attr,
>>>>> +	&dev_attr_proximity_calibscale_available.attr,
>>>>> +	NULL
>>>>> +};
>>>>> +
>>>>> +static const struct attribute_group tsl2X7X_device_attr_group_tbl[] = {
>>>>> +	[ALS] = {
>>>>> +		.attrs = tsl2x7x_ALS_device_attrs,
>>>>> +	},
>>>>> +	[PRX] = {
>>>>> +		.attrs = tsl2x7x_PRX_device_attrs,
>>>>> +	},
>>>>> +	[ALSPRX] = {
>>>>> +		.attrs = tsl2x7x_ALSPRX_device_attrs,
>>>>> +	},
>>>>> +	[PRX2] = {
>>>>> +		.attrs = tsl2x7x_PRX2_device_attrs,
>>>>> +	},
>>>>> +	[ALSPRX2] = {
>>>>> +		.attrs = tsl2x7x_ALSPRX2_device_attrs,
>>>>> +	},
>>>>> +};
>>>>> +
>>>>> +static const struct iio_info tsl2X7X_device_info[] = {
>>>>> +	[ALS] = {
>>>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALS],
>>>>> +		.driver_module = THIS_MODULE,
>>>>> +		.read_raw =&tsl2x7x_read_raw,
>>>>> +		.write_raw =&tsl2x7x_write_raw,
>>>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>>>> +	},
>>>>> +	[PRX] = {
>>>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX],
>>>>> +		.driver_module = THIS_MODULE,
>>>>> +		.read_raw =&tsl2x7x_read_raw,
>>>>> +		.write_raw =&tsl2x7x_write_raw,
>>>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>>>> +	},
>>>>> +	[ALSPRX] = {
>>>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX],
>>>>> +		.driver_module = THIS_MODULE,
>>>>> +		.read_raw =&tsl2x7x_read_raw,
>>>>> +		.write_raw =&tsl2x7x_write_raw,
>>>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>>>> +	},
>>>>> +	[PRX2] = {
>>>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[PRX2],
>>>>> +		.driver_module = THIS_MODULE,
>>>>> +		.read_raw =&tsl2x7x_read_raw,
>>>>> +		.write_raw =&tsl2x7x_write_raw,
>>>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>>>> +	},
>>>>> +	[ALSPRX2] = {
>>>>> +		.attrs =&tsl2X7X_device_attr_group_tbl[ALSPRX2],
>>>>> +		.driver_module = THIS_MODULE,
>>>>> +		.read_raw =&tsl2x7x_read_raw,
>>>>> +		.write_raw =&tsl2x7x_write_raw,
>>>>> +		.read_event_value =&tsl2x7x_read_thresh,
>>>>> +		.write_event_value =&tsl2x7x_write_thresh,
>>>>> +		.read_event_config =&tsl2x7x_read_interrupt_config,
>>>>> +		.write_event_config =&tsl2x7x_write_interrupt_config,
>>>>> +	},
>>>>> +};
>>>>> +
>>>>> +static const struct tsl2x7x_chip_info tsl2x7x_chip_info_tbl[] = {
>>>>> +	[ALS] = {
>>>>> +		.channel = {
>>>>> +			[0] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[1] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.info_mask =
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +		},
>>>>> +	.num_channels = 2,
>>>>> +	.info =&tsl2X7X_device_info[ALS],
>>>>> +	},
>>>>> +	[PRX] = {
>>>>> +		.channel = {
>>>>> +			[0] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[1] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +		},
>>>>> +	.num_channels = 2,
>>>>> +	.info =&tsl2X7X_device_info[PRX],
>>>>> +	},
>>>>> +	[ALSPRX] = {
>>>>> +		.channel = {
>>>>> +			[0] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[1] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.info_mask =
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +			[2] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[3] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 0,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +		},
>>>>> +	.num_channels = 4,
>>>>> +	.info =&tsl2X7X_device_info[ALSPRX],
>>>>> +	},
>>>>> +	[PRX2] = {
>>>>> +		.channel = {
>>>>> +			[0] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[1] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.info_mask =
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +		},
>>>>> +	.num_channels = 2,
>>>>> +	.info =&tsl2X7X_device_info[PRX2],
>>>>> +	},
>>>>> +	[ALSPRX2] = {
>>>>> +		.channel = {
>>>>> +				[0] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[1] = {
>>>>> +				.type = IIO_LIGHT,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.info_mask =
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT |
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +			[2] = {
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.processed_val = 1,
>>>>> +			},
>>>>> +			[3] = {
>>>> I'm more than a little confused here.  There are only 2 actually
>>>> channels?  If so I don't
>>>> see why we have 4 channels here...
>>>>> +				.type = IIO_PROXIMITY,
>>>>> +				.indexed = 1,
>>>>> +				.channel = 0,
>>>>> +				.info_mask =
>>>>> +
>>>> 	IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT,
>>>>> +				.event_mask = TSL2X7X_EVENT_MASK
>>>>> +			},
>>>>> +		},
>>>>> +	.num_channels = 4,
>>>>> +	.info =&tsl2X7X_device_info[ALSPRX2],
>>>>> +	},
>>>>> +};
>>>>> +
>>>>> +/*
>>>>> + * Client probe function.
>>>>> + */
>>>>> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
>>>>> +	const struct i2c_device_id *id)
>>>>> +{
>>>>> +	int ret;
>>>>> +	unsigned char device_id;
>>>>> +	struct iio_dev *indio_dev;
>>>>> +	struct tsl2X7X_chip *chip;
>>>>> +
>>>>> +	indio_dev = iio_allocate_device(sizeof(*chip));
>>>>> +	if (!indio_dev)
>>>>> +		return -ENOMEM;
>>>>> +
>>>>> +	chip = iio_priv(indio_dev);
>>>>> +	chip->client = clientp;
>>>>> +	i2c_set_clientdata(clientp, indio_dev);
>>>>> +
>>>>> +	ret = tsl2x7x_i2c_read(chip->client,
>>>>> +		TSL2X7X_CHIPID,&device_id);
>>>>> +	if (ret<   0)
>>>>> +		goto fail1;
>>>>> +
>>>>> +	if ((!tsl2x7x_device_id(&device_id, id->driver_data)) ||
>>>>> +		(tsl2x7x_device_id(&device_id, id->driver_data) == -EINVAL)) {
>>>>> +		dev_info(&chip->client->dev,
>>>>> +				"i2c device found does not match expected id
>>>> in %s\n",
>>>>> +				__func__);
>>>>> +		goto fail1;
>>>>> +	}
>>>>> +
>>>>> +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG |
>>>> TSL2X7X_CNTRL));
>>>>> +	if (ret<   0) {
>>>>> +		dev_err(&clientp->dev, "%s: write to cmd reg failed. err =
>>>> %d\n",
>>>>> +				__func__, ret);
>>>>> +		goto fail1;
>>>>> +	}
>>>>> +
>>>>> +	/* ALS and PROX functions can be invoked via user space poll
>>>>> +	 * or H/W interrupt. If busy return last sample. */
>>>>> +	mutex_init(&chip->als_mutex);
>>>>> +	mutex_init(&chip->prox_mutex);
>>>>> +
>>>>> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
>>>>> +	chip->pdata = clientp->dev.platform_data;
>>>>> +	chip->id = id->driver_data;
>>>>> +	chip->chip_info =
>>>>> +		&tsl2x7x_chip_info_tbl[device_channel_config[id-
>>>>> driver_data]];
>>>>> +
>>>>> +	indio_dev->info = chip->chip_info->info;
>>>>> +	indio_dev->dev.parent =&clientp->dev;
>>>>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>>>>> +	indio_dev->name = chip->client->name;
>>>>> +	indio_dev->channels = chip->chip_info->channel;
>>>>> +	indio_dev->num_channels = chip->chip_info->num_channels;
>>>>> +
>>>>> +	if (clientp->irq) {
>>>>> +		ret = request_threaded_irq(clientp->irq,
>>>>> +					   NULL,
>>>>> +					&tsl2x7x_event_handler,
>>>>> +					   IRQF_TRIGGER_RISING |
>>>> IRQF_ONESHOT,
>>>>> +					   "TSL2X7X_event",
>>>>> +					   indio_dev);
>>>>> +		if (ret) {
>>>>> +			dev_err(&clientp->dev,
>>>>> +				"%s: irq request failed", __func__);
>>>>> +			goto fail2;
>>>>> +		}
>>>>> +	}
>>>>> +
>>>>> +	/* Load up the defaults */
>>>>> +	tsl2x7x_defaults(chip);
>>>>> +	/* Make sure the chip is on */
>>>>> +	tsl2x7x_chip_on(indio_dev);
>>>>> +
>>>>> +	ret = iio_device_register(indio_dev);
>>>>> +	if (ret) {
>>>>> +		dev_err(&clientp->dev,
>>>>> +			"%s: iio registration failed\n", __func__);
>>>>> +		goto fail3;
>>>>> +	}
>>>>> +
>>>>> +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
>>>>> +
>>>>> +	return 0;
>>>>> +
>>>>> +fail3:
>>>>> +	if (clientp->irq)
>>>>> +		free_irq(clientp->irq, indio_dev);
>>>>> +fail2:
>>>>> +	iio_free_device(indio_dev);
>>>>> +fail1:
>>>>> +	kfree(chip);
>>>> double free of chip.  It's allocated and managed by the
>>>> iio_allocate_device and iio_free_device calls.
>>>> For that matter, most of the goto fail2's need the iio_free_device call..
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_suspend(struct device *dev)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
>>>>> +		ret = tsl2x7x_chip_off(indio_dev);
>>>>> +		chip->tsl2x7x_chip_status = TSL2X7X_CHIP_SUSPENDED;
>>>>> +	}
>>>>> +
>>>>> +	if (chip->pdata&&   chip->pdata->platform_power) {
>>>>> +		pm_message_t pmm = {PM_EVENT_SUSPEND};
>>>>> +		chip->pdata->platform_power(dev, pmm);
>>>>> +	}
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int tsl2x7x_resume(struct device *dev)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>>>>> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>>>>> +	int ret = 0;
>>>>> +
>>>>> +	if (chip->pdata&&   chip->pdata->platform_power) {
>>>>> +		pm_message_t pmm = {PM_EVENT_RESUME};
>>>>> +		chip->pdata->platform_power(dev, pmm);
>>>>> +	}
>>>>> +
>>>>> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_SUSPENDED)
>>>>> +		ret = tsl2x7x_chip_on(indio_dev);
>>>>> +
>>>>> +	return ret;
>>>>> +}
>>>>> +
>>>>> +static int __devexit tsl2x7x_remove(struct i2c_client *client)
>>>>> +{
>>>>> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>>>>> +	struct tsl2X7X_chip *chip = i2c_get_clientdata(client);
>>>> These shouldn't both be true....
>>>> would expect an iio_unregister_device call here.
>>>>> +
>>>>> +	tsl2x7x_chip_off(indio_dev);
>>>>> +
>>>>> +	if (client->irq)
>>>>> +		free_irq(client->irq, chip->client->name);
>>>>> +
>>>>> +	iio_free_device(indio_dev);
>>>>> +
>>>>> +	return 0;
>>>>> +}
>>>>> +
>>>>> +static struct i2c_device_id tsl2x7x_idtable[] = {
>>>>> +	{ "tsl2571", tsl2571 },
>>>>> +	{ "tsl2671", tsl2671 },
>>>>> +	{ "tmd2671", tmd2671 },
>>>>> +	{ "tsl2771", tsl2771 },
>>>>> +	{ "tmd2771", tmd2771 },
>>>>> +	{ "tsl2572", tsl2572 },
>>>>> +	{ "tsl2672", tsl2672 },
>>>>> +	{ "tmd2672", tmd2672 },
>>>>> +	{ "tsl2772", tsl2772 },
>>>>> +	{ "tmd2772", tmd2772 },
>>>>> +	{}
>>>>> +};
>>>>> +
>>>>> +MODULE_DEVICE_TABLE(i2c, tsl2x7x_idtable);
>>>>> +
>>>>> +static const struct dev_pm_ops tsl2x7x_pm_ops = {
>>>>> +	.suspend = tsl2x7x_suspend,
>>>>> +	.resume  = tsl2x7x_resume,
>>>>> +};
>>>>> +
>>>>> +/* Driver definition */
>>>>> +static struct i2c_driver tsl2x7x_driver = {
>>>>> +	.driver = {
>>>>> +		.name = "tsl2x7x",
>>>>> +		.pm =&tsl2x7x_pm_ops,
>>>>> +	},
>>>>> +	.id_table = tsl2x7x_idtable,
>>>>> +	.probe = tsl2x7x_probe,
>>>>> +	.remove = __devexit_p(tsl2x7x_remove),
>>>>> +};
>>>>> +
>>>>> +module_i2c_driver(tsl2x7x_driver);
>>>>> +
>>>>> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
>>>>> +MODULE_DESCRIPTION("TAOS tsl2x7x ambient and proximity light sensor
>>>> driver");
>>>>> +MODULE_LICENSE("GPL");
>>>>> diff --git a/drivers/staging/iio/light/tsl2x7x_core.h
>>>> b/drivers/staging/iio/light/tsl2x7x_core.h
>>>>> new file mode 100644
>>>>> index 0000000..663e846
>>>>> --- /dev/null
>>>>> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
>>>> why not just tsl2x7x.h?
>>>>> @@ -0,0 +1,75 @@
>>>>> +/*
>>>>> + * Device driver for monitoring ambient light intensity (lux)
>>>>> + * and proximity (prox) within the TAOS TSL2X7X family of devices.
>>>>> + *
>>>>> + * Copyright (c) 2012, TAOS Corporation.
>>>>> + *
>>>>> + * This program is free software; you can redistribute it and/or modify
>>>>> + * it under the terms of the GNU General Public License as published by
>>>>> + * the Free Software Foundation; 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.,
>>>>> + * 51 Franklin Street, Fifth Floor, Boston, MA	02110-1301, USA.
>>>>> + */
>>>>> +
>>>>> +#ifndef __TSL2X7X_H
>>>>> +#define __TSL2X7X_H
>>>>> +#include<linux/pm.h>
>>>>> +
>>>>> +/* Max number of segments allowable in LUX table */
>>>>> +#define TSL2X7X_MAX_LUX_TABLE_SIZE		9
>>>>> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) *
>>>> TSL2X7X_MAX_LUX_TABLE_SIZE)
>>>>> +
>>>>> +struct iio_dev;
>>>> Why the forward declaration of iio_chan_spec?
>>>>> +struct iio_chan_spec;
>>>>> +
>>>>> +struct tsl2x7x_lux {
>>>>> +	unsigned int ratio;
>>>>> +	unsigned int ch0;
>>>>> +	unsigned int ch1;
>>>>> +};
>>>>> +
>>>>> +/* Refer to tsl2x7x_default_settings for member desc. */
>>>>> +struct tsl2x7x_settings {
>>>>> +	int als_time;
>>>>> +	int als_gain;
>>>>> +	int als_gain_trim;
>>>>> +	int wait_time;
>>>>> +	int prx_time;
>>>>> +	int prox_gain;
>>>>> +	int prox_config;
>>>>> +	int als_cal_target;
>>>>> +	u8  interrupts_en;
>>>>> +	u8  als_persistence;
>>>>> +	int als_thresh_low;
>>>>> +	int als_thresh_high;
>>>>> +	int prox_thres_low;
>>>>> +	int prox_thres_high;
>>>>> +	int prox_pulse_count;
>>>>> +	int prox_max_samples_cal;
>>>>> +};
>>>>> +
>>>>> +/* struct tsl2x7x_platform_data -
>>>>> + * Platform unique glass and defaults
>>>>> + * Platform PM functions. */
>>>> Would prefer this to be in kernel doc.
>>>>> +struct tsl2X7X_platform_data {
>>>>> +	/* Suspend/resume platform cb */
>>>>> +	int (*platform_power)(struct device *dev, pm_message_t);
>>>>> +	/* The following callback gets called when the device is powered on */
>>>>> +	int (*power_on)      (struct iio_dev *indio_dev);
>>>>> +	/* The following callback gets called when the device is powered off */
>>>>> +	int (*power_off)     (struct i2c_client *dev);
>>>>> +	/* These are the device specific glass coefficents used to
>>>>> +	 * calculate Lux */
>>>>> +	struct tsl2x7x_lux platform_lux_table[TSL2X7X_MAX_LUX_TABLE_SIZE];
>>>>> +	struct tsl2x7x_settings *platform_default_settings;
>>>>> +};
>>>>> +
>>>>> +#endif /* __TSL2X7X_H */
>>>>> --
>>>>> 1.7.4.1
>>>>>


^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2012-03-29 14:06 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-21 16:16 [PATCH V4] TAOS tsl2x7x Jon Brenner
2012-03-21 16:16 ` Jon Brenner
2012-03-23 13:53 ` Jonathan Cameron
2012-03-27 19:58   ` Jon Brenner
2012-03-28 17:51     ` Jonathan Cameron
2012-03-28 23:29       ` Jon Brenner
2012-03-28 23:29         ` Jon Brenner
2012-03-29 14:06         ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.