All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V1] TAOS tsl2x7x
@ 2012-02-10 19:06 ` Jon Brenner
  0 siblings, 0 replies; 4+ messages in thread
From: Jon Brenner @ 2012-02-10 19:06 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-kernel, linux-iio

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

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

Signed-off-by: Jon Brenner <jbrenner@taosinc.com>

---
 .../iio/Documentation/sysfs-bus-iio-light-tsl2x7x  |   71 +
 drivers/staging/iio/light/Kconfig                  |   11 +
 drivers/staging/iio/light/Makefile                 |    1 +
 drivers/staging/iio/light/tsl2x7x_core.c           | 2053 ++++++++++++++++++++
 drivers/staging/iio/light/tsl2x7x_core.h           |  138 ++
 5 files changed, 2274 insertions(+), 0 deletions(-)

diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
new file mode 100644
index 0000000..f1bb5f5
--- /dev/null
+++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
@@ -0,0 +1,71 @@
+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_thresh_falling_value
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Low (falling) trigger point for the internal ALS comparison
+		function for interrupt generation.
+
+What:		/sys/bus/iio/devices/device[n]/illuminance0_thresh_rising_value
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		high (rising) trigger point for the internal ALS comparison
+		function for interrupt generation.
+
+What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Simultainious ALS channel 1 and channel 2 data represented as
+		a 32 bit integer.
+
+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.
+
+What:		/sys/bus/iio/devices/device[n]/proximity_thresh_falling_value
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		Low (falling) trigger point for the internal proximity comparison
+		function for interrupt generation.
+
+What:		/sys/bus/iio/devices/device[n]/proximity_thresh_rising_value
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		high (rising) trigger point for the internal proximity comparison
+		function for interrupt generation.
+
+What:		/sys/bus/iio/devices/device[n]/proximity_calibscale
+KernelVersion:	3.3-rc1
+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).
+
+What:		/sys/bus/iio/devices/device[n]/proximity_raw
+KernelVersion:	3.3-rc1
+Contact:	linux-iio@vger.kernel.org
+Description:
+		State of proximity detection based on the
+		proximity_thresh_rising_value.
+
diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
index e7e9159..29de333 100644
--- a/drivers/staging/iio/light/Kconfig
+++ b/drivers/staging/iio/light/Kconfig
@@ -31,4 +31,15 @@ config TSL2583
 	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
 	 Access ALS data via iio, sysfs.

+	 This driver can also be built as a module.  If so, the module
+	 will be called tsl2583.
+
+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..671f476
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.c
@@ -0,0 +1,2053 @@
+/*
+ * 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/string.h>
+#include <linux/mutex.h>
+#include <linux/unistd.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/cdev.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include "tsl2x7x_core.h"
+#include "../events.h"
+#include "../buffer.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 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_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;/* for calibration mode*/
+
+};
+
+struct tsl2X7X_chip_info {
+	struct iio_chan_spec	channel[0];
+	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 iio_dev *iio_dev;
+	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_REG_MAX];
+	bool init_done;
+	struct work_struct work_thresh;
+	s64 event_timestamp;
+	/* This structure is intentionally large to accommodate
+	 * updates via sysfs. */
+	/* Sized to 9 = max 8 segments + 1 termination segment */
+	/* Assumption is one and only one type of glass used  */
+	struct tsl2x7x_lux tsl2x7x_device_lux[MAX_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,
+};
+
+struct als_gainadj {
+	s16 ch0;
+	s16 ch1;
+};
+
+/* Used to validate the ALS gain selection index */
+static const struct als_gainadj tsl2X7X_als_gainadj[] = {
+	{ 1, 1 },
+	{ 8, 8 },
+	{ 16, 16 },
+	{ 120, 120 }
+};
+
+
+/* Not using channels */
+static const struct iio_chan_spec tsl2X7X_channels[] = {};
+
+/*
+ * 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, unsigned int len)
+{
+	int ret;
+	int i;
+
+	for (i = 0; i < len; i++) {
+		/* select register to write */
+		ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
+		if (ret < 0) {
+			dev_err(&client->dev, "tsl2x7x_i2c_read failed to write"
+				" register %x\n", reg);
+			return ret;
+		}
+		/* read the data */
+		*val = i2c_smbus_read_byte(client);
+		val++;
+		reg++;
+	}
+	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;
+#pragma pack(4)
+	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, "tsl2x7x_get_lux device is not enabled\n");
+		ret = -EBUSY ;
+		goto out_unlock;
+	}
+
+	ret = tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &buf[0], 1);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"tsl2x7x_get_lux failed to read CMD_REG\n");
+		goto out_unlock;
+	}
+	/* is data new & valid */
+	if (!(buf[0] & TSL2X7X_STA_ADC_VALID)) {
+		dev_err(&chip->client->dev,
+			"tsl2x7x_get_lux data not valid yet\n");
+		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], 1);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+				"tsl2x7x_get_lux failed to read"
+				" ret: %x\n", 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,
+		"tsl2x7x_i2c_write_command failed in tsl2x7x_get_lux, err = %d\n",
+			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))
+		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 */
+	for (p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
+	     p->ratio != 0 && p->ratio < ratio; p++)
+		;
+
+	if (p->ratio == 0) {
+		lux = 0;
+	} else {
+		ch0lux = ((ch0 * p->ch0) +
+		(tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0 >> 1))
+		/ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0;
+
+		ch1lux = ((ch1 * p->ch1) +
+		(tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1 >> 1))
+		/ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1;
+
+		lux = ch0lux - ch1lux;
+	}
+
+	/* note: lux is 31 bit max at this point */
+	if (ch1lux > ch0lux) {
+		dev_dbg(&chip->client->dev, "No Data - Return last value\n");
+		ret = chip->als_cur_info.lux = 0;
+		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 */
+return_max:
+		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
+	}
+
+	/* Update the structure with the latest VALID lux. */
+	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, "Can't get prox mutex\n");
+		return -EBUSY;
+	}
+
+	err_cnt = 0;
+
+try_again:
+	ret = tsl2x7x_i2c_read(chip->client,
+		(TSL2X7X_CMD_REG | TSL2X7X_STATUS), &status, 1);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"Read regs failed in tsl2x7x_prox_poll() - A\n");
+		mutex_unlock(&chip->prox_mutex);
+		return ret;
+	}
+
+	/*Prox interrupt asserted*/
+	if (((chip->tsl2x7x_settings.interrupts_en << 4)
+			& CNTL_PROX_INT_ENBL)) {
+		if (!(status & TSL2X7X_STA_ADC_VALID)) {
+			err_cnt++;
+			if (err_cnt > CONSECUTIVE_RETRIES) {
+				mutex_unlock(&chip->prox_mutex);
+				dev_err(&chip->client->dev,
+				"Consec. retries exceeded\n");
+				return chip->prox_cur_info.prox_event;
+			}
+			goto try_again;
+		}
+	}
+
+	for (i = 0; i < 2; i++) {
+		ret = tsl2x7x_i2c_read(chip->client,
+			(TSL2X7X_CMD_REG |
+					(TSL2X7X_PRX_LO + i)), &chdata[i], 1);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+			"Read regs failed in tsl2x7x_prox_poll() - B\n");
+			mutex_unlock(&chip->prox_mutex);
+			return ret;
+		}
+	}
+
+	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
+
+	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;
+}
+
+/*
+ * Provides initial operational parameter defaults.
+ * These defaults may be changed through the device's sysfs files.
+ */
+static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
+{
+	/* Operational parameters */
+	chip->tsl2x7x_settings.als_time = 200;
+	/* must be a multiple of 50mS */
+	chip->tsl2x7x_settings.als_gain = 0;
+	/* this is actually an index into the gain table */
+	chip->tsl2x7x_settings.prx_time = 0xfe; /*5.4 mS */
+	/* 2.7ms prox integration time - decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	chip->tsl2x7x_settings.prox_gain = 1;
+	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
+	/* assume clear glass as default */
+	chip->tsl2x7x_settings.wait_time = 245;
+	/* Time between PRX and ALS cycles -decrease to increase time */
+	/* decreases in 2.7 ms intervals */
+	chip->tsl2x7x_settings.prox_config = 0;
+	/* Prox configuration filters */
+	chip->tsl2x7x_settings.als_gain_trim = 1000;
+	/* default gain trim to account for aperture effects */
+	chip->tsl2x7x_settings.als_cal_target = 150;
+	/* Known external ALS reading used for calibration */
+	chip->tsl2x7x_settings.als_thresh_low = 200;
+	/* CH0 'low' count to trigger interrupt */
+	chip->tsl2x7x_settings.als_thresh_high = 256;
+	/* CH0 'high' count to trigger interrupt */
+	chip->tsl2x7x_settings.als_persistence = 0xFF;
+	/* Number of 'out of limits' ADC readings PRX/ALS*/
+	chip->tsl2x7x_settings.interrupts_en = 0x00;
+	/* Default interrupt(s) enabled.
+	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
+	chip->tsl2x7x_settings.prox_thres_low  = 0;
+	chip->tsl2x7x_settings.prox_thres_high = 512;
+	/*default threshold (adjust either manually or with cal routine*/
+	chip->tsl2x7x_settings.prox_max_samples_cal = 30;
+	chip->tsl2x7x_settings.prox_pulse_count = 8;
+
+	/* Load up the lux table. Can be changed later via ABI */
+	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,
+		"tsl2x7x_als_calibrate failed to write CNTRL register, ret=%d\n",
+			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,
+			"tsl2x7x_als_calibrate failed: ADC not enabled\n");
+		return -1;
+	}
+
+	ret = i2c_smbus_write_byte(chip->client,
+			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+		"tsl2x7x_als_calibrate failed to write ctrl reg: ret=%d\n",
+			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,
+		"tsl2x7x_als_calibrate failed: STATUS - ADC not valid.\n");
+		return -ENODATA;
+	}
+	lux_val = tsl2x7x_get_lux(indio_dev);
+	if (lux_val < 0) {
+		dev_err(&chip->client->dev,
+		"tsl2x7x_als_calibrate failed to get lux\n");
+		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 | (mA100 | 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, "tsl2x7x_chip_on failed on CNTRL reg.\n");
+		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_REG_MAX; i++) {
+		ret = i2c_smbus_write_byte_data(chip->client,
+				TSL2X7X_CMD_REG + i, *dev_reg++);
+		if (ret < 0) {
+			dev_err(&chip->client->dev,
+			"tsl2x7x_chip_on failed on write to reg %d.\n", 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 | 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, "tsl2x7x_chip_on failed on 2nd CTRL reg.\n");
+		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");
+		/* first time interrupt */
+		chip->init_done = 0;
+
+
+	reg_val = TSL2X7X_CNTL_PWR_ON;
+
+	if (chip->tsl2x7x_settings.interrupts_en == 0x10)
+		reg_val |= CNTL_ADC_ENBL;
+
+	if (chip->tsl2x7x_settings.interrupts_en == 0x20)
+		reg_val |= CNTL_PROX_DET_ENBL;
+
+	if (chip->tsl2x7x_settings.interrupts_en == 0x30)
+		reg_val |= (CNTL_ADC_ENBL | 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,
+		"tsl2x7x_i2c_write to device failed in tsl2x7x_IOCTL_INT_SET.\n");
+
+	/* Clear out any initial interrupts  */
+	ret = i2c_smbus_write_byte(chip->client,
+		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
+		TSL2X7X_CMD_PROXALS_INTCLR);
+	if (ret < 0) {
+		dev_err(&chip->client->dev,
+			"tsl2x7x_i2c_write_command failed in tsl2x7x_chip_on\n");
+		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;
+}
+
+/**
+ * Integer Square Root
+ * We need an integer version since 1st Floating point is not allowed
+ * in driver world, 2nd, cannot count on the devices having a FPU, and
+ * 3rd software FP emulation may be excessive.
+ */
+static unsigned long tsl2x7x_isqrt(unsigned long x)
+{
+	register unsigned long op, res, one;
+	op = x;
+	res = 0;
+
+	one = 1 << 30;
+	while (one > op)
+		one >>= 2;
+
+	while (one != 0) {
+		if (op >= res + one) {
+			op -= res + one;
+			res += one << 1;
+		}
+		res >>= 1;
+		one >>= 2;
+	}
+	return res;
+}
+
+/*
+ * 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 = 0xffff;
+	max = 0;
+	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 = tsl2x7x_isqrt((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,
+			"max prox samples cal is too big: %d\n",
+			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 |= 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);
+}
+
+/* ---------- Sysfs Interface Functions ------------- */
+
+
+static ssize_t tsl2x7x_power_state_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	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 *dev_info = dev_get_drvdata(dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 0)
+		tsl2x7x_chip_off(dev_info);
+	else
+		tsl2x7x_chip_on(dev_info);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_proximity_detect_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	tsl2x7x_prox_poll(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->prox_cur_info.prox_event);
+}
+
+static ssize_t tsl2x7x_gain_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+		tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0);
+}
+
+static ssize_t tsl2x7x_gain_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	switch (value) {
+	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:
+		chip->tsl2x7x_settings.als_gain = 3;
+		break;
+	case 128:
+		chip->tsl2x7x_settings.als_gain = 3;
+		break;
+
+	default:
+		dev_err(dev,
+			"Invalid Gain Index\n");
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+static ssize_t tsl2x7x_gain_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	if (strncmp(chip->client->name, "tsl2772", 7) == 0)
+		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_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			(1 << chip->tsl2x7x_settings.prox_gain));
+}
+
+static ssize_t tsl2x7x_prox_gain_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	switch (value) {
+	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:
+		dev_err(dev,
+			"Invalid Prox Gain Index\n");
+		return -EINVAL;
+	}
+
+	return len;
+}
+
+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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	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 ssize_t tsl2x7x_als_time_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+		"50 100 150 200 250 300 350 400 450 500 550 600 650");
+}
+
+static ssize_t tsl2x7x_als_trim_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_gain_trim);
+}
+
+static ssize_t tsl2x7x_als_trim_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		chip->tsl2x7x_settings.als_gain_trim = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value)
+		chip->tsl2x7x_settings.als_cal_target = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_interrupts_en_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	if (chip->tsl2x7x_settings.interrupts_en & 0x10)
+		return snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	else
+		return snprintf(buf, PAGE_SIZE, "%d\n", 0);
+}
+
+static ssize_t tsl2x7x_interrupts_en_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value > 1)
+		return -EINVAL;
+	if (value)
+		chip->tsl2x7x_settings.interrupts_en |= 0x10;
+	else
+		chip->tsl2x7x_settings.interrupts_en &= 0x20;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_prox_interrupt_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	if (chip->tsl2x7x_settings.interrupts_en & 0x20)
+		return snprintf(buf, PAGE_SIZE, "%d\n", 1);
+	else
+		return snprintf(buf, PAGE_SIZE, "%d\n", 0);
+}
+
+static ssize_t tsl2x7x_prox_interrupt_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value > 1)
+		return -EINVAL;
+	if (value)
+		chip->tsl2x7x_settings.interrupts_en |= 0x20;
+	else
+		chip->tsl2x7x_settings.interrupts_en &= 0x10;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_als_thresh_low_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_thresh_low);
+}
+
+static ssize_t tsl2x7x_als_thresh_low_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.als_thresh_low = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_als_thresh_high_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.als_thresh_high);
+}
+
+static ssize_t tsl2x7x_als_thresh_high_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.als_thresh_high = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_prox_thresh_low_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.prox_thres_low);
+}
+
+static ssize_t tsl2x7x_prox_thresh_low_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.prox_thres_low = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_prox_thresh_high_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->tsl2x7x_settings.prox_thres_high);
+}
+
+static ssize_t tsl2x7x_prox_thresh_high_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.prox_thres_high = value;
+
+	return len;
+}
+
+static ssize_t tsl2x7x_prox_data_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	tsl2x7x_prox_poll(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			chip->prox_cur_info.prox_data);
+}
+
+/* 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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "0x%02X\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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	chip->tsl2x7x_settings.als_persistence = value;
+
+	return len;
+}
+
+static
+ssize_t tsl2x7x_als_persistence_available_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "0x00 - 0xFF (0 - 255)\n");
+}
+
+static
+ssize_t tsl2x7x_lux_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	int lux;
+
+	lux = tsl2x7x_get_lux(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", lux);
+}
+
+static
+ssize_t tsl2x7x_adc_show(struct device *dev, struct device_attribute *attr,
+	char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	tsl2x7x_get_lux(dev_info);
+
+	return snprintf(buf, PAGE_SIZE, "0x%08x\n",
+			((chip->als_cur_info.als_ch0 << 16) |
+					chip->als_cur_info.als_ch1));
+}
+
+static ssize_t tsl2x7x_do_calibrate(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t len)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		tsl2x7x_als_calibrate(dev_info);
+
+	return len;
+}
+
+static ssize_t tsl2x7x_luxtable_show(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	int i;
+	int offset = 0;
+
+	i = 0;
+	while (i < (MAX_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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	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(dev_info);
+
+	/* 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 *dev_info = dev_get_drvdata(dev);
+	unsigned long value;
+
+	if (kstrtoul(buf, 0, &value))
+		return -EINVAL;
+
+	if (value == 1)
+		tsl2x7x_prox_cal(dev_info);
+
+	return len;
+}
+
+static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
+		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
+
+static DEVICE_ATTR(proximity_raw, S_IRUGO,
+		tsl2x7x_proximity_detect_show, NULL);
+
+static DEVICE_ATTR(intensity_infrared_raw, S_IRUGO,
+		tsl2x7x_prox_data_show, NULL);
+
+
+static DEVICE_ATTR(proximity_calibscale, S_IRUGO | S_IWUSR,
+		tsl2x7x_prox_gain_show, tsl2x7x_prox_gain_store);
+static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
+		tsl2x7x_prox_gain_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
+		tsl2x7x_gain_show, tsl2x7x_gain_store);
+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_integration_time_available, S_IRUGO,
+		tsl2x7x_als_time_available_show, NULL);
+
+static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_trim_show, tsl2x7x_als_trim_store);
+
+static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
+
+static DEVICE_ATTR(illuminance0_both_raw, S_IRUGO, tsl2x7x_adc_show,
+		NULL);
+
+static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2x7x_lux_show,
+		NULL);
+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(illuminance0_thresh_falling_value, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_thresh_low_show, tsl2x7x_als_thresh_low_store);
+
+static DEVICE_ATTR(illuminance0_thresh_rising_value, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_thresh_high_show, tsl2x7x_als_thresh_high_store);
+
+static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
+		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
+
+static DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
+		tsl2x7x_als_persistence_available_show, NULL);
+
+static DEVICE_ATTR(proximity_thresh_rising_value, S_IRUGO | S_IWUSR,
+		tsl2x7x_prox_thresh_high_show, tsl2x7x_prox_thresh_high_store);
+
+static DEVICE_ATTR(proximity_thresh_falling_value, S_IRUGO | S_IWUSR,
+		tsl2x7x_prox_thresh_low_show, tsl2x7x_prox_thresh_low_store);
+
+static struct attribute *tsl2571_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&dev_attr_illuminance0_integration_time_available.attr,
+	&dev_attr_illuminance0_calibbias.attr,
+	&dev_attr_illuminance0_input_target.attr,
+	&dev_attr_illuminance0_both_raw.attr,
+	&dev_attr_illuminance0_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_illuminance0_thresh_falling_value.attr,
+	&dev_attr_illuminance0_thresh_rising_value.attr,
+	&dev_attr_sampling_frequency.attr,
+	&dev_attr_sampling_frequency_available.attr,
+	NULL
+};
+
+static struct attribute *tsl2671_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_proximity_raw.attr,
+	&dev_attr_intensity_infrared_raw.attr,
+	&dev_attr_sampling_frequency.attr,
+	&dev_attr_sampling_frequency_available.attr,
+	&dev_attr_proximity_thresh_rising_value.attr,
+	&dev_attr_proximity_thresh_falling_value.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2771_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_proximity_raw.attr,
+	&dev_attr_intensity_infrared_raw.attr,
+	&dev_attr_illuminance0_calibscale.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&dev_attr_illuminance0_integration_time_available.attr,
+	&dev_attr_illuminance0_calibbias.attr,
+	&dev_attr_illuminance0_input_target.attr,
+	&dev_attr_illuminance0_both_raw.attr,
+	&dev_attr_illuminance0_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_illuminance0_thresh_falling_value.attr,
+	&dev_attr_illuminance0_thresh_rising_value.attr,
+	&dev_attr_sampling_frequency.attr,
+	&dev_attr_sampling_frequency_available.attr,
+	&dev_attr_proximity_thresh_rising_value.attr,
+	&dev_attr_proximity_thresh_falling_value.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2572_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_illuminance0_calibscale.attr,
+	&dev_attr_illuminance0_calibscale_available.attr,
+	&dev_attr_illuminance0_integration_time.attr,
+	&dev_attr_illuminance0_integration_time_available.attr,
+	&dev_attr_illuminance0_calibbias.attr,
+	&dev_attr_illuminance0_input_target.attr,
+	&dev_attr_illuminance0_both_raw.attr,
+	&dev_attr_illuminance0_input.attr,
+	&dev_attr_illuminance0_calibrate.attr,
+	&dev_attr_illuminance0_lux_table.attr,
+	&dev_attr_illuminance0_thresh_falling_value.attr,
+	&dev_attr_illuminance0_thresh_rising_value.attr,
+	&dev_attr_sampling_frequency.attr,
+	&dev_attr_sampling_frequency_available.attr,
+	NULL
+};
+
+static struct attribute *tsl2672_device_attrs[] = {
+	&dev_attr_power_state.attr,
+	&dev_attr_proximity_raw.attr,
+	&dev_attr_intensity_infrared_raw.attr,
+	&dev_attr_proximity_calibscale.attr,
+	&dev_attr_sampling_frequency.attr,
+	&dev_attr_sampling_frequency_available.attr,
+	&dev_attr_proximity_thresh_rising_value.attr,
+	&dev_attr_proximity_thresh_falling_value.attr,
+	&dev_attr_proximity_calibrate.attr,
+	NULL
+};
+
+static struct attribute *tsl2772_device_attrs[] = {
+		&dev_attr_power_state.attr,
+		&dev_attr_proximity_raw.attr,
+		&dev_attr_proximity_calibscale.attr,
+		&dev_attr_intensity_infrared_raw.attr,
+		&dev_attr_illuminance0_calibscale.attr,
+		&dev_attr_illuminance0_calibscale_available.attr,
+		&dev_attr_illuminance0_integration_time.attr,
+		&dev_attr_illuminance0_integration_time_available.attr,
+		&dev_attr_illuminance0_calibbias.attr,
+		&dev_attr_illuminance0_input_target.attr,
+		&dev_attr_illuminance0_both_raw.attr,
+		&dev_attr_illuminance0_input.attr,
+		&dev_attr_illuminance0_calibrate.attr,
+		&dev_attr_illuminance0_lux_table.attr,
+		&dev_attr_illuminance0_thresh_falling_value.attr,
+		&dev_attr_illuminance0_thresh_rising_value.attr,
+		&dev_attr_sampling_frequency.attr,
+		&dev_attr_sampling_frequency_available.attr,
+		&dev_attr_proximity_thresh_rising_value.attr,
+		&dev_attr_proximity_thresh_falling_value.attr,
+		&dev_attr_proximity_calibrate.attr,
+		&dev_attr_proximity_calibscale_available.attr,
+		NULL
+};
+
+static struct attribute_group tsl2X7X_dev_attr_group_tbl[] = {
+	[tsl2571] = {
+		.attrs = tsl2571_device_attrs,
+	},
+	[tsl2671] = {
+		.attrs = tsl2671_device_attrs,
+	},
+	[tmd2671] = {
+		.attrs = tsl2671_device_attrs,
+	},
+	[tsl2771] = {
+		.attrs = tsl2771_device_attrs,
+	},
+	[tmd2771] = {
+		.attrs = tsl2771_device_attrs,
+	},
+	[tsl2572] = {
+		.attrs = tsl2572_device_attrs,
+	},
+	[tsl2672] = {
+		.attrs = tsl2672_device_attrs,
+	},
+	[tmd2672] = {
+		.attrs = tsl2672_device_attrs,
+	},
+	[tsl2772] = {
+		.attrs = tsl2772_device_attrs,
+	},
+	[tmd2772] = {
+		.attrs = tsl2772_device_attrs,
+	},
+
+};
+
+static IIO_DEVICE_ATTR(illuminance_thresh_both_en,
+		S_IRUGO | S_IWUSR,
+		tsl2x7x_interrupts_en_show, tsl2x7x_interrupts_en_store, 0);
+
+static IIO_DEVICE_ATTR(proximity_thresh_both_en,
+		S_IRUGO | S_IWUSR,
+		tsl2x7x_prox_interrupt_show, tsl2x7x_prox_interrupt_store, 0);
+
+static struct attribute *tsl2X7X_prox_event_attributes[] = {
+	&iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *tsl2X7X_als_event_attributes[] = {
+	&iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute *tsl2X7X_proxals_event_attributes[] = {
+	&iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
+	&iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
+	NULL,
+};
+
+static struct attribute_group tsl2X7X_event_attr_group_tbl[] = {
+	[tsl2571] = {
+		.attrs = tsl2X7X_als_event_attributes,
+		.name  = "events",
+	},
+	[tsl2671] = {
+		.attrs = tsl2X7X_prox_event_attributes,
+		.name  = "events",
+	},
+	[tmd2671] = {
+		.attrs = tsl2X7X_prox_event_attributes,
+		.name  = "events",
+	},
+	[tsl2771] = {
+		.attrs = tsl2X7X_proxals_event_attributes,
+		.name  = "events",
+	},
+	[tmd2771] = {
+		.attrs = tsl2X7X_proxals_event_attributes,
+		.name  = "events",
+	},
+
+	[tsl2572] = {
+		.attrs = tsl2X7X_als_event_attributes,
+		.name  = "events",
+	},
+	[tsl2672] = {
+		.attrs = tsl2X7X_prox_event_attributes,
+		.name  = "events",
+	},
+	[tmd2672] = {
+		.attrs = tsl2X7X_prox_event_attributes,
+		.name  = "events",
+	},
+	[tsl2772] = {
+		.attrs = tsl2X7X_proxals_event_attributes,
+		.name  = "events",
+	},
+	[tmd2772] = {
+		.attrs = tsl2X7X_proxals_event_attributes,
+		.name  = "events",
+	}
+};
+
+
+/* Use the default register values to identify the Taos device */
+static int tsl2x7x_device_id(unsigned char *bufp, int target)
+{
+	switch (target) {
+	case tsl2571:
+	case tsl2671:
+	case tsl2771:
+		return ((bufp[TSL2X7X_CHIPID] & 0xf0) == TRITON_ID);
+	break;
+	case tmd2671:
+	case tmd2771:
+		return ((bufp[TSL2X7X_CHIPID] & 0xf0) == HALIBUT_ID);
+	break;
+	case tsl2572:
+	case tsl2672:
+	case tmd2672:
+	case tsl2772:
+	case tmd2772:
+		return ((bufp[TSL2X7X_CHIPID] & 0xf0) == SWORDFISH_ID);
+	break;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info tsl2X7X_info[] = {
+	[tsl2571] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2571],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2571],
+			.driver_module = THIS_MODULE,
+	},
+	[tsl2671] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2671],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2671],
+			.driver_module = THIS_MODULE,
+	},
+	[tmd2671] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2671],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2671],
+			.driver_module = THIS_MODULE,
+	},
+	[tsl2771] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2771],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2771],
+			.driver_module = THIS_MODULE,
+	},
+	[tmd2771] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2771],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2771],
+			.driver_module = THIS_MODULE,
+	},
+	[tsl2572] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2572],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2572],
+			.driver_module = THIS_MODULE,
+	},
+	[tsl2672] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2672],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2672],
+			.driver_module = THIS_MODULE,
+	},
+	[tmd2672] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2672],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2672],
+			.driver_module = THIS_MODULE,
+	},
+	[tsl2772] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2772],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2772],
+			.driver_module = THIS_MODULE,
+	},
+	[tmd2772] = {
+			.attrs = &tsl2X7X_dev_attr_group_tbl[tsl2772],
+			.event_attrs = &tsl2X7X_event_attr_group_tbl[tsl2772],
+			.driver_module = THIS_MODULE,
+	},
+
+};
+
+/*
+ * Run-time interrupt handler - depending on whether the device is in ambient
+ * light sensing interrupt mode, this handler can queue up
+ * a thread, to handle valid interrupts.
+ */
+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_INTCLR);
+	if (ret < 0)
+		dev_err(&chip->client->dev,
+		"Failed to clear irq from event handler. err = %d\n", ret);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * Client probe function.
+ */
+static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
+	const struct i2c_device_id *id)
+{
+	int i, ret;
+	unsigned char buf[TSL2X7X_MAX_DEVICE_REGS];
+	struct iio_dev *indio_dev =
+			iio_allocate_device(sizeof(struct tsl2X7X_chip));
+	struct tsl2X7X_chip	*chip = iio_priv(indio_dev);
+
+	if (indio_dev == NULL) {
+		ret = -ENOMEM;
+		dev_err(&clientp->dev, "iio allocation failed\n");
+		goto fail1;
+	}
+
+	chip = iio_priv(indio_dev);
+	chip->client = clientp;
+	i2c_set_clientdata(clientp, indio_dev);
+
+	mutex_init(&chip->als_mutex);
+	mutex_init(&chip->prox_mutex);
+
+	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
+
+	chip->pdata = clientp->dev.platform_data;
+	if (chip->pdata && chip->pdata->init)
+			chip->pdata->init(clientp);
+
+	for (i = 0; i < TSL2X7X_MAX_DEVICE_REGS; i++) {
+		ret = i2c_smbus_write_byte(clientp,
+				(TSL2X7X_CMD_REG | (TSL2X7X_CNTRL + i)));
+		if (ret < 0) {
+			dev_err(&clientp->dev, "i2c_smbus_write_bytes() to cmd "
+			"reg failed in tsl2x7x_probe(), err = %d\n", ret);
+			goto fail1;
+		}
+		ret = i2c_smbus_read_byte(clientp);
+		if (ret < 0) {
+			dev_err(&clientp->dev, "i2c_smbus_read_byte from "
+			"reg failed in tsl2x7x_probe(), err = %d\n", ret);
+
+			goto fail1;
+		}
+		buf[i] = ret;
+	}
+
+	if ((!tsl2x7x_device_id(buf, id->driver_data)) ||
+			(tsl2x7x_device_id(buf, id->driver_data) == -EINVAL)) {
+		dev_info(&chip->client->dev, "i2c device found but does not match "
+			"expected id in tsl2x7x_probe()\n");
+		goto fail1;
+	}
+
+	chip->id = id->driver_data;
+
+	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
+	if (ret < 0) {
+		dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
+			"failed in tsl2x7x_probe(), err = %d\n", ret);
+		goto fail1;
+	}
+
+	indio_dev->info = &tsl2X7X_info[id->driver_data];
+	indio_dev->dev.parent = &clientp->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->name = chip->client->name;
+	indio_dev->channels = tsl2X7X_channels;
+	indio_dev->num_channels = ARRAY_SIZE(tsl2X7X_channels);
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(&clientp->dev, "iio registration failed\n");
+		goto fail2;
+	}
+
+	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, "irq request failed");
+	}
+
+	/* Load up the defaults */
+	tsl2x7x_defaults(chip);
+
+	/* Make sure the chip is on */
+	tsl2x7x_chip_on(indio_dev);
+
+	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
+
+	return 0;
+
+fail1:
+	if (chip->pdata && chip->pdata->teardown)
+		chip->pdata->teardown(clientp);
+	iio_free_device(indio_dev);
+
+fail2:
+	return ret;
+
+}
+
+
+static int tsl2x7x_suspend(struct device *dev)
+{
+	struct iio_dev *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+
+	int ret = 0;
+
+	/* Blocks if work is active */
+	cancel_work_sync(&chip->work_thresh);
+
+	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
+		ret = tsl2x7x_chip_off(dev_info);
+		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 *dev_info = dev_get_drvdata(dev);
+	struct tsl2X7X_chip *chip = iio_priv(dev_info);
+	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(dev_info);
+
+	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);
+
+	flush_scheduled_work();
+
+	iio_device_unregister(chip->iio_dev);
+	kfree(chip);
+
+	if (chip->pdata && chip->pdata->teardown)
+		chip->pdata->teardown(client);
+
+	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),
+};
+
+static int __init tsl2x7x_init(void)
+{
+	return i2c_add_driver(&tsl2x7x_driver);
+}
+
+static void __exit tsl2x7x_exit(void)
+{
+	i2c_del_driver(&tsl2x7x_driver);
+}
+
+module_init(tsl2x7x_init);
+module_exit(tsl2x7x_exit);
+
+MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
+MODULE_DESCRIPTION("TAOS tsl2X7X ambient 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..9a64412
--- /dev/null
+++ b/drivers/staging/iio/light/tsl2x7x_core.h
@@ -0,0 +1,138 @@
+/*
+ * 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>
+
+#include "../iio.h"
+
+/* 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_DEVICE_REGS 32
+#define TSL2X7X_REG_MAX         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 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 CNTL_REG_CLEAR                 0x00
+#define CNTL_PROX_INT_ENBL             0X20
+#define CNTL_ALS_INT_ENBL              0X10
+#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
+#define CNTL_PROX_DET_ENBL             0X04
+#define CNTL_ADC_ENBL                  0x02
+#define TSL2X7X_CNTL_PWRON             0x01
+#define CNTL_ALSPON_ENBL               0x03
+#define CNTL_INTALSPON_ENBL            0x13
+#define CNTL_PROXPON_ENBL              0x0F
+#define CNTL_INTPROXPON_ENBL           0x2F
+#define TSL2X7X_CMD_PROXALS_INTCLR     0X07
+
+/*Prox diode to use */
+#define DIODE0                         0x10
+#define DIODE1                         0x20
+#define DIODE_BOTH                     0x30
+
+/* LED Power */
+#define mA100                          0x00
+#define mA50                           0x40
+#define mA25                           0x80
+#define mA13                           0xD0
+
+/* Max number of segments allowable in LUX table */
+#define MAX_TABLE_SIZE		9
+#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * MAX_TABLE_SIZE)
+
+struct tsl2x7x_lux {
+	unsigned int ratio;
+	unsigned int ch0;
+	unsigned int ch1;
+};
+
+
+/* struct tsl2x7x_platform_data - Assume all varients will have this */
+struct tsl2X7X_platform_data {
+	int (*platform_power)(struct device *dev, pm_message_t);
+	/* The following callback gets called when the TSL2772 is powered on */
+	int (*power_on)      (struct iio_dev *indio_dev);
+	/* The following callback gets called when the TSL2772 is powered off */
+	int (*power_off)     (struct i2c_client *dev);
+	/* The following callback gets called when the driver is added */
+	int (*init)          (struct i2c_client *dev);
+	/* The following callback gets called when the driver is removed */
+	int (*teardown)      (struct i2c_client *dev);
+	/* These are the device specific glass coefficents used to
+	 * calculate Lux */
+	struct tsl2x7x_lux platform_lux_table[MAX_TABLE_SIZE];
+};
+
+#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 related	[flat|nested] 4+ messages in thread

* [PATCH V1] TAOS tsl2x7x
@ 2012-02-10 19:06 ` Jon Brenner
  0 siblings, 0 replies; 4+ messages in thread
From: Jon Brenner @ 2012-02-10 19:06 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-kernel, linux-iio

VEFPUyBkZXZpY2UgZHJpdmVyIGZvciB0c2wvdG1kIDI3NzEgYW5kIDI3NzIgZGV2aWNlIGZhbWls
aWVzIChpbmMgYWxsIHZhcmlhbnRzKS4NCg0KU2lnbmVkLW9mZi1ieTogSm9uIEJyZW5uZXIgPGpi
cmVubmVyQHRhb3NpbmMuY29tPg0KDQotLS0NCiAuLi4vaWlvL0RvY3VtZW50YXRpb24vc3lzZnMt
YnVzLWlpby1saWdodC10c2wyeDd4ICB8ICAgNzEgKw0KIGRyaXZlcnMvc3RhZ2luZy9paW8vbGln
aHQvS2NvbmZpZyAgICAgICAgICAgICAgICAgIHwgICAxMSArDQogZHJpdmVycy9zdGFnaW5nL2lp
by9saWdodC9NYWtlZmlsZSAgICAgICAgICAgICAgICAgfCAgICAxICsNCiBkcml2ZXJzL3N0YWdp
bmcvaWlvL2xpZ2h0L3RzbDJ4N3hfY29yZS5jICAgICAgICAgICB8IDIwNTMgKysrKysrKysrKysr
KysrKysrKysNCiBkcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4N3hfY29yZS5oICAgICAg
ICAgICB8ICAxMzggKysNCiA1IGZpbGVzIGNoYW5nZWQsIDIyNzQgaW5zZXJ0aW9ucygrKSwgMCBk
ZWxldGlvbnMoLSkNCg0KZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vRG9jdW1lbnRh
dGlvbi9zeXNmcy1idXMtaWlvLWxpZ2h0LXRzbDJ4N3ggYi9kcml2ZXJzL3N0YWdpbmcvaWlvL0Rv
Y3VtZW50YXRpb24vc3lzZnMtYnVzLWlpby1saWdodC10c2wyeDd4DQpuZXcgZmlsZSBtb2RlIDEw
MDY0NA0KaW5kZXggMDAwMDAwMC4uZjFiYjVmNQ0KLS0tIC9kZXYvbnVsbA0KKysrIGIvZHJpdmVy
cy9zdGFnaW5nL2lpby9Eb2N1bWVudGF0aW9uL3N5c2ZzLWJ1cy1paW8tbGlnaHQtdHNsMng3eA0K
QEAgLTAsMCArMSw3MSBAQA0KK1doYXQ6CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0v
bHV4X3RhYmxlDQorS2VybmVsVmVyc2lvbjoJMi42LjM3DQorQ29udGFjdDoJbGludXgtaWlvQHZn
ZXIua2VybmVsLm9yZw0KK0Rlc2NyaXB0aW9uOg0KKwkJVGhpcyBwcm9wZXJ0eSBnZXRzL3NldHMg
dGhlIHRhYmxlIG9mIGNvZWZmaWNpZW50cw0KKwkJdXNlZCBpbiBjYWxjdWxhdGluZyBpbGx1bWlu
YW5jZSBpbiBsdXguDQorDQorV2hhdDoJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9p
bGx1bWluYW5jZTBfY2FsaWJyYXRlDQorS2VybmVsVmVyc2lvbjoJMi42LjM3DQorQ29udGFjdDoJ
bGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KK0Rlc2NyaXB0aW9uOg0KKwkJVGhpcyBwcm9wZXJ0
eSBjYXVzZXMgYW4gaW50ZXJuYWwgY2FsaWJyYXRpb24gb2YgdGhlIGFscyBnYWluIHRyaW0NCisJ
CXZhbHVlIHdoaWNoIGlzIGxhdGVyIHVzZWQgaW4gY2FsY3VsYXRpbmcgaWxsdW1pbmFuY2UgaW4g
bHV4Lg0KKw0KK1doYXQ6CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0vaWxsdW1pbmFu
Y2UwX3RocmVzaF9mYWxsaW5nX3ZhbHVlDQorS2VybmVsVmVyc2lvbjoJMy4zLXJjMQ0KK0NvbnRh
Y3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcNCitEZXNjcmlwdGlvbjoNCisJCUxvdyAoZmFs
bGluZykgdHJpZ2dlciBwb2ludCBmb3IgdGhlIGludGVybmFsIEFMUyBjb21wYXJpc29uDQorCQlm
dW5jdGlvbiBmb3IgaW50ZXJydXB0IGdlbmVyYXRpb24uDQorDQorV2hhdDoJCS9zeXMvYnVzL2lp
by9kZXZpY2VzL2RldmljZVtuXS9pbGx1bWluYW5jZTBfdGhyZXNoX3Jpc2luZ192YWx1ZQ0KK0tl
cm5lbFZlcnNpb246CTMuMy1yYzENCitDb250YWN0OglsaW51eC1paW9Admdlci5rZXJuZWwub3Jn
DQorRGVzY3JpcHRpb246DQorCQloaWdoIChyaXNpbmcpIHRyaWdnZXIgcG9pbnQgZm9yIHRoZSBp
bnRlcm5hbCBBTFMgY29tcGFyaXNvbg0KKwkJZnVuY3Rpb24gZm9yIGludGVycnVwdCBnZW5lcmF0
aW9uLg0KKw0KK1doYXQ6CQkvc3lzL2J1cy9paW8vZGV2aWNlcy9kZXZpY2Vbbl0vaWxsdW1pbmFu
Y2UwX2JvdGhfcmF3DQorS2VybmVsVmVyc2lvbjoJMy4zLXJjMQ0KK0NvbnRhY3Q6CWxpbnV4LWlp
b0B2Z2VyLmtlcm5lbC5vcmcNCitEZXNjcmlwdGlvbjoNCisJCVNpbXVsdGFpbmlvdXMgQUxTIGNo
YW5uZWwgMSBhbmQgY2hhbm5lbCAyIGRhdGEgcmVwcmVzZW50ZWQgYXMNCisJCWEgMzIgYml0IGlu
dGVnZXIuDQorDQorV2hhdDoJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9wcm94aW1p
dHlfY2FsaWJyYXRlDQorS2VybmVsVmVyc2lvbjoJMy4zLXJjMQ0KK0NvbnRhY3Q6CWxpbnV4LWlp
b0B2Z2VyLmtlcm5lbC5vcmcNCitEZXNjcmlwdGlvbjoNCisJCUNhdXNlcyBhbiByZWNhbGN1bGF0
aW9uIGFuZCBhZGp1c3RtZW50IHRvIHRoZQ0KKwkJcHJveGltaXR5X3RocmVzaF9yaXNpbmdfdmFs
dWUuDQorDQorV2hhdDoJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9wcm94aW1pdHlf
dGhyZXNoX2ZhbGxpbmdfdmFsdWUNCitLZXJuZWxWZXJzaW9uOgkzLjMtcmMxDQorQ29udGFjdDoJ
bGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KK0Rlc2NyaXB0aW9uOg0KKwkJTG93IChmYWxsaW5n
KSB0cmlnZ2VyIHBvaW50IGZvciB0aGUgaW50ZXJuYWwgcHJveGltaXR5IGNvbXBhcmlzb24NCisJ
CWZ1bmN0aW9uIGZvciBpbnRlcnJ1cHQgZ2VuZXJhdGlvbi4NCisNCitXaGF0OgkJL3N5cy9idXMv
aWlvL2RldmljZXMvZGV2aWNlW25dL3Byb3hpbWl0eV90aHJlc2hfcmlzaW5nX3ZhbHVlDQorS2Vy
bmVsVmVyc2lvbjoJMy4zLXJjMQ0KK0NvbnRhY3Q6CWxpbnV4LWlpb0B2Z2VyLmtlcm5lbC5vcmcN
CitEZXNjcmlwdGlvbjoNCisJCWhpZ2ggKHJpc2luZykgdHJpZ2dlciBwb2ludCBmb3IgdGhlIGlu
dGVybmFsIHByb3hpbWl0eSBjb21wYXJpc29uDQorCQlmdW5jdGlvbiBmb3IgaW50ZXJydXB0IGdl
bmVyYXRpb24uDQorDQorV2hhdDoJCS9zeXMvYnVzL2lpby9kZXZpY2VzL2RldmljZVtuXS9wcm94
aW1pdHlfY2FsaWJzY2FsZQ0KK0tlcm5lbFZlcnNpb246CTMuMy1yYzENCitDb250YWN0OglsaW51
eC1paW9Admdlci5rZXJuZWwub3JnDQorRGVzY3JpcHRpb246DQorCQlIYXJkd2FyZSBvciBzb2Z0
d2FyZSBhcHBsaWVkIGNhbGlicmF0aW9uIHNjYWxlIGZhY3RvciBhc3N1bWVkDQorCQl0byBhY2Nv
dW50IGZvciBhdHRlbnVhdGlvbiBkdWUgdG8gaW5kdXN0cmlhbCBkZXNpZ24gKGdsYXNzDQorCQlm
aWx0ZXJzIG9yIGFwZXJ0dXJlIGhvbGVzKS4NCisNCitXaGF0OgkJL3N5cy9idXMvaWlvL2Rldmlj
ZXMvZGV2aWNlW25dL3Byb3hpbWl0eV9yYXcNCitLZXJuZWxWZXJzaW9uOgkzLjMtcmMxDQorQ29u
dGFjdDoJbGludXgtaWlvQHZnZXIua2VybmVsLm9yZw0KK0Rlc2NyaXB0aW9uOg0KKwkJU3RhdGUg
b2YgcHJveGltaXR5IGRldGVjdGlvbiBiYXNlZCBvbiB0aGUNCisJCXByb3hpbWl0eV90aHJlc2hf
cmlzaW5nX3ZhbHVlLg0KKw0KZGlmZiAtLWdpdCBhL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQv
S2NvbmZpZyBiL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvS2NvbmZpZw0KaW5kZXggZTdlOTE1
OS4uMjlkZTMzMyAxMDA2NDQNCi0tLSBhL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvS2NvbmZp
Zw0KKysrIGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC9LY29uZmlnDQpAQCAtMzEsNCArMzEs
MTUgQEAgY29uZmlnIFRTTDI1ODMNCiAJIFByb3ZpZGVzIHN1cHBvcnQgZm9yIHRoZSBUQU9TIHRz
bDI1ODAsIHRzbDI1ODEgYW5kIHRzbDI1ODMgZGV2aWNlcy4NCiAJIEFjY2VzcyBBTFMgZGF0YSB2
aWEgaWlvLCBzeXNmcy4NCg0KKwkgVGhpcyBkcml2ZXIgY2FuIGFsc28gYmUgYnVpbHQgYXMgYSBt
b2R1bGUuICBJZiBzbywgdGhlIG1vZHVsZQ0KKwkgd2lsbCBiZSBjYWxsZWQgdHNsMjU4My4NCisN
Citjb25maWcgVFNMMng3eA0KKwl0cmlzdGF0ZSAiVEFPUyBUU0wvVE1EMng3MSBhbmQgVFNML1RN
RDJ4NzIgRmFtaWx5IG9mIGxpZ2h0IGFuZCBwcm94aW1pdHkgc2Vuc29ycyINCisJZGVwZW5kcyBv
biBJMkMNCisJaGVscA0KKwkgU3VwcG9ydCBmb3I6IHRzbDI1NzEsIHRzbDI2NzEsIHRtZDI2NzEs
IHRzbDI3NzEsIHRtZDI3NzEsIHRzbDI1NzIsIHRzbDI2NzIsDQorCSB0bWQyNjcyLCB0c2wyNzcy
LCB0bWQyNzcyIGRldmljZXMuDQorCSBQcm92aWRlcyBpaW9fZXZlbnRzIGFuZCBkaXJlY3QgYWNj
ZXNzIHZpYSBzeXNmcy4NCisNCiBlbmRtZW51DQpkaWZmIC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5n
L2lpby9saWdodC9NYWtlZmlsZSBiL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvTWFrZWZpbGUN
CmluZGV4IDMwMTFmYmYuLmZmMTJjNGIgMTAwNjQ0DQotLS0gYS9kcml2ZXJzL3N0YWdpbmcvaWlv
L2xpZ2h0L01ha2VmaWxlDQorKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L01ha2VmaWxl
DQpAQCAtNSwzICs1LDQgQEANCiBvYmotJChDT05GSUdfU0VOU09SU19UU0wyNTYzKQkrPSB0c2wy
NTYzLm8NCiBvYmotJChDT05GSUdfU0VOU09SU19JU0wyOTAxOCkJKz0gaXNsMjkwMTgubw0KIG9i
ai0kKENPTkZJR19UU0wyNTgzKQkrPSB0c2wyNTgzLm8NCitvYmotJChDT05GSUdfVFNMMng3eCkJ
Kz0gdHNsMng3eF9jb3JlLm8NCmRpZmYgLS1naXQgYS9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0
L3RzbDJ4N3hfY29yZS5jIGIvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90c2wyeDd4X2NvcmUu
Yw0KbmV3IGZpbGUgbW9kZSAxMDA2NDQNCmluZGV4IDAwMDAwMDAuLjY3MWY0NzYNCi0tLSAvZGV2
L251bGwNCisrKyBiL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eF9jb3JlLmMNCkBA
IC0wLDAgKzEsMjA1MyBAQA0KKy8qDQorICogRGV2aWNlIGRyaXZlciBmb3IgbW9uaXRvcmluZyBh
bWJpZW50IGxpZ2h0IGludGVuc2l0eSBpbiAobHV4KQ0KKyAqIGFuZCBwcm94aW1pdHkgZGV0ZWN0
aW9uIChwcm94KSB3aXRoaW4gdGhlIFRBT1MgVFNMMlg3WCBmYW1pbHkgb2YgZGV2aWNlcy4NCisg
Kg0KKyAqIENvcHlyaWdodCAoYykgMjAxMiwgVEFPUyBDb3Jwb3JhdGlvbi4NCisgKg0KKyAqIFRo
aXMgcHJvZ3JhbSBpcyBmcmVlIHNvZnR3YXJlOyB5b3UgY2FuIHJlZGlzdHJpYnV0ZSBpdCBhbmQv
b3IgbW9kaWZ5DQorICogaXQgdW5kZXIgdGhlIHRlcm1zIG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJs
aWMgTGljZW5zZSBhcyBwdWJsaXNoZWQgYnkNCisgKiB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0
aW9uOyBlaXRoZXIgdmVyc2lvbiAyIG9mIHRoZSBMaWNlbnNlLCBvcg0KKyAqIChhdCB5b3VyIG9w
dGlvbikgYW55IGxhdGVyIHZlcnNpb24uDQorICoNCisgKiBUaGlzIHByb2dyYW0gaXMgZGlzdHJp
YnV0ZWQgaW4gdGhlIGhvcGUgdGhhdCBpdCB3aWxsIGJlIHVzZWZ1bCwgYnV0IFdJVEhPVVQNCisg
KiBBTlkgV0FSUkFOVFk7IHdpdGhvdXQgZXZlbiB0aGUgaW1wbGllZCB3YXJyYW50eSBvZiBNRVJD
SEFOVEFCSUxJVFkgb3INCisgKiBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRS4gIFNl
ZSB0aGUgR05VIEdlbmVyYWwgUHVibGljIExpY2Vuc2UgZm9yDQorICogbW9yZSBkZXRhaWxzLg0K
KyAqDQorICogWW91IHNob3VsZCBoYXZlIHJlY2VpdmVkIGEgY29weSBvZiB0aGUgR05VIEdlbmVy
YWwgUHVibGljIExpY2Vuc2UgYWxvbmcNCisgKiB3aXRoIHRoaXMgcHJvZ3JhbTsgaWYgbm90LCB3
cml0ZSB0byB0aGUgRnJlZSBTb2Z0d2FyZSBGb3VuZGF0aW9uLCBJbmMuLA0KKyAqIDUxIEZyYW5r
bGluIFN0cmVldCwgRmlmdGggRmxvb3IsIEJvc3RvbiwgTUEgICAgICAgIDAyMTEwLTEzMDEsIFVT
QS4NCisgKi8NCisNCisjaW5jbHVkZSA8bGludXgva2VybmVsLmg+DQorI2luY2x1ZGUgPGxpbnV4
L2kyYy5oPg0KKyNpbmNsdWRlIDxsaW51eC9lcnJuby5oPg0KKyNpbmNsdWRlIDxsaW51eC9kZWxh
eS5oPg0KKyNpbmNsdWRlIDxsaW51eC9zdHJpbmcuaD4NCisjaW5jbHVkZSA8bGludXgvbXV0ZXgu
aD4NCisjaW5jbHVkZSA8bGludXgvdW5pc3RkLmg+DQorI2luY2x1ZGUgPGxpbnV4L2ludGVycnVw
dC5oPg0KKyNpbmNsdWRlIDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4NCisjaW5jbHVkZSA8bGlu
dXgvaW5wdXQuaD4NCisjaW5jbHVkZSA8bGludXgvc2xhYi5oPg0KKyNpbmNsdWRlIDxsaW51eC9w
bS5oPg0KKyNpbmNsdWRlIDxsaW51eC9tb2R1bGUuaD4NCisjaW5jbHVkZSA8bGludXgvdmVyc2lv
bi5oPg0KKw0KKyNpbmNsdWRlIDxsaW51eC9jZGV2Lmg+DQorI2luY2x1ZGUgPGxpbnV4L3N0YXQu
aD4NCisjaW5jbHVkZSA8bGludXgvbW9kdWxlLmg+DQorI2luY2x1ZGUgInRzbDJ4N3hfY29yZS5o
Ig0KKyNpbmNsdWRlICIuLi9ldmVudHMuaCINCisjaW5jbHVkZSAiLi4vYnVmZmVyLmgiDQorI2lu
Y2x1ZGUgIi4uL2lpby5oIg0KKyNpbmNsdWRlICIuLi9zeXNmcy5oIg0KKw0KKy8qIENhbCBkZWZz
Ki8NCisjZGVmaW5lIFBST1hfU1RBVF9DQUwgICAgICAgIDANCisjZGVmaW5lIFBST1hfU1RBVF9T
QU1QICAgICAgIDENCisjZGVmaW5lIE1BWF9TQU1QTEVTX0NBTCAgICAgIDIwMA0KKw0KKy8qIFRT
TDJYN1ggRGV2aWNlIElEICovDQorI2RlZmluZSBUUklUT05fSUQgICAgMHgwMA0KKyNkZWZpbmUg
U1dPUkRGSVNIX0lEIDB4MzANCisjZGVmaW5lIEhBTElCVVRfSUQgICAweDIwDQorDQorLyogTHV4
IGNhbGN1bGF0aW9uIGNvbnN0YW50cyAqLw0KKyNkZWZpbmUgVFNMMlg3WF9MVVhfQ0FMQ19PVkVS
X0ZMT1cgICAgICAgIDY1NTM1DQorDQorLyogVEFPUyB0eHgyeDd4IERldmljZSBmYW1pbHkgbWVt
YmVycyAqLw0KK2VudW0gew0KKwl0c2wyNTcxLA0KKwl0c2wyNjcxLA0KKwl0bWQyNjcxLA0KKwl0
c2wyNzcxLA0KKwl0bWQyNzcxLA0KKwl0c2wyNTcyLA0KKwl0c2wyNjcyLA0KKwl0bWQyNjcyLA0K
Kwl0c2wyNzcyLA0KKwl0bWQyNzcyDQorfTsNCisNCitlbnVtIHsNCisJVFNMMlg3WF9DSElQX1VO
S05PV04gPSAwLA0KKwlUU0wyWDdYX0NISVBfV09SS0lORyA9IDEsDQorCVRTTDJYN1hfQ0hJUF9T
VVNQRU5ERUQgPSAyDQorfTsNCisNCisvKiBQZXItZGV2aWNlIGRhdGEgKi8NCitzdHJ1Y3QgdHNs
Mng3eF9hbHNfaW5mbyB7DQorCXUxNiBhbHNfY2gwOw0KKwl1MTYgYWxzX2NoMTsNCisJdTE2IGx1
eDsNCit9Ow0KKw0KKy8qIHByb3hpbWl0eSBkYXRhICovDQorc3RydWN0IHRzbDJ4N3hfcHJveF9p
bmZvIHsNCisJdTE2IHByb3hfZGF0YTsNCisJaW50IHByb3hfZXZlbnQ7DQorfTsNCisNCitzdHJ1
Y3QgcHJveF9zdGF0IHsNCisJdTE2IG1pbjsNCisJdTE2IG1heDsNCisJdTE2IG1lYW47DQorCXVu
c2lnbmVkIGxvbmcgc3RkZGV2Ow0KK307DQorDQorc3RydWN0IHRzbDJ4N3hfc2V0dGluZ3Mgew0K
KwlpbnQgYWxzX3RpbWU7DQorCWludCBhbHNfZ2FpbjsNCisJaW50IGFsc19nYWluX3RyaW07DQor
CWludCB3YWl0X3RpbWU7DQorCWludCBwcnhfdGltZTsNCisJaW50IHByb3hfZ2FpbjsNCisJaW50
IHByb3hfY29uZmlnOw0KKwlpbnQgYWxzX2NhbF90YXJnZXQ7DQorCXU4ICBpbnRlcnJ1cHRzX2Vu
Ow0KKwl1OCAgYWxzX3BlcnNpc3RlbmNlOw0KKwlpbnQgYWxzX3RocmVzaF9sb3c7DQorCWludCBh
bHNfdGhyZXNoX2hpZ2g7DQorCWludCBwcm94X3RocmVzX2xvdzsNCisJaW50IHByb3hfdGhyZXNf
aGlnaDsNCisJaW50IHByb3hfcHVsc2VfY291bnQ7DQorCWludCBwcm94X21heF9zYW1wbGVzX2Nh
bDsvKiBmb3IgY2FsaWJyYXRpb24gbW9kZSovDQorDQorfTsNCisNCitzdHJ1Y3QgdHNsMlg3WF9j
aGlwX2luZm8gew0KKwlzdHJ1Y3QgaWlvX2NoYW5fc3BlYwljaGFubmVsWzBdOw0KKwljb25zdCBz
dHJ1Y3QgaWlvX2luZm8JKmluZm87DQorfTsNCisNCitzdHJ1Y3QgdHNsMlg3WF9jaGlwIHsNCisJ
a2VybmVsX3Vsb25nX3QgaWQ7DQorCXN0cnVjdCBtdXRleCBwcm94X211dGV4Ow0KKwlzdHJ1Y3Qg
bXV0ZXggYWxzX211dGV4Ow0KKwlzdHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50Ow0KKwlzdHJ1Y3Qg
aWlvX2RldiAqaWlvX2RldjsNCisJc3RydWN0IHRzbDJ4N3hfcHJveF9pbmZvIHByb3hfY3VyX2lu
Zm87DQorCXN0cnVjdCB0c2wyeDd4X2Fsc19pbmZvIGFsc19jdXJfaW5mbzsNCisJc3RydWN0IHRz
bDJ4N3hfc2V0dGluZ3MgdHNsMng3eF9zZXR0aW5nczsNCisJc3RydWN0IHRzbDJYN1hfcGxhdGZv
cm1fZGF0YSAqcGRhdGE7DQorCWludCBhbHNfdGltZV9zY2FsZTsNCisJaW50IGFsc19zYXR1cmF0
aW9uOw0KKwlpbnQgdHNsMng3eF9jaGlwX3N0YXR1czsNCisJdTggdHNsMng3eF9jb25maWdbVFNM
Mlg3WF9SRUdfTUFYXTsNCisJYm9vbCBpbml0X2RvbmU7DQorCXN0cnVjdCB3b3JrX3N0cnVjdCB3
b3JrX3RocmVzaDsNCisJczY0IGV2ZW50X3RpbWVzdGFtcDsNCisJLyogVGhpcyBzdHJ1Y3R1cmUg
aXMgaW50ZW50aW9uYWxseSBsYXJnZSB0byBhY2NvbW1vZGF0ZQ0KKwkgKiB1cGRhdGVzIHZpYSBz
eXNmcy4gKi8NCisJLyogU2l6ZWQgdG8gOSA9IG1heCA4IHNlZ21lbnRzICsgMSB0ZXJtaW5hdGlv
biBzZWdtZW50ICovDQorCS8qIEFzc3VtcHRpb24gaXMgb25lIGFuZCBvbmx5IG9uZSB0eXBlIG9m
IGdsYXNzIHVzZWQgICovDQorCXN0cnVjdCB0c2wyeDd4X2x1eCB0c2wyeDd4X2RldmljZV9sdXhb
TUFYX1RBQkxFX1NJWkVdOw0KK307DQorDQorLyogRGlmZmVyZW50IGRldmljZXMgcmVxdWlyZSBk
aWZmZXJlbnQgY29lZmZpY2VudHMgKi8NCitzdGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfbHV4
IHRzbDJ4NzFfbHV4X3RhYmxlW10gPSB7DQorCXsgMTQ0NjEsICAgNjExLCAgIDEyMTEgfSwNCisJ
eyAxODU0MCwgICAzNTIsICAgIDYyMyB9LA0KKwl7ICAgICAwLCAgICAgMCwgICAgICAwIH0sDQor
fTsNCisNCitzdGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfbHV4IHRtZDJ4NzFfbHV4X3RhYmxl
W10gPSB7DQorCXsgMTE2MzUsICAgMTE1LCAgICAyNTYgfSwNCisJeyAxNTUzNiwgICAgODcsICAg
IDE3OSB9LA0KKwl7ICAgICAwLCAgICAgMCwgICAgICAwIH0sDQorfTsNCisNCitzdGF0aWMgY29u
c3Qgc3RydWN0IHRzbDJ4N3hfbHV4IHRzbDJ4NzJfbHV4X3RhYmxlW10gPSB7DQorCXsgMTQwMTMs
ICAgNDY2LCAgIDkxNyB9LA0KKwl7IDE4MjIyLCAgIDMxMCwgICA1NTIgfSwNCisJeyAgICAgMCwg
ICAgIDAsICAgICAwIH0sDQorfTsNCisNCitzdGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfbHV4
IHRtZDJ4NzJfbHV4X3RhYmxlW10gPSB7DQorCXsgMTMyMTgsICAgMTMwLCAgIDI2MiB9LA0KKwl7
IDE3NTkyLCAgIDkyLCAgICAxNjkgfSwNCisJeyAgICAgMCwgICAgIDAsICAgICAwIH0sDQorfTsN
CisNCitzdGF0aWMgY29uc3Qgc3RydWN0IHRzbDJ4N3hfbHV4ICp0c2wyeDd4X2RlZmF1bHRfbHV4
X3RhYmxlX2dyb3VwW10gPSB7DQorCVt0c2wyNTcxXSA9IHRzbDJ4NzFfbHV4X3RhYmxlLA0KKwlb
dHNsMjY3MV0gPQl0c2wyeDcxX2x1eF90YWJsZSwNCisJW3RtZDI2NzFdID0JdG1kMng3MV9sdXhf
dGFibGUsDQorCVt0c2wyNzcxXSA9CXRzbDJ4NzFfbHV4X3RhYmxlLA0KKwlbdG1kMjc3MV0gPQl0
bWQyeDcxX2x1eF90YWJsZSwNCisJW3RzbDI1NzJdID0JdHNsMng3Ml9sdXhfdGFibGUsDQorCVt0
c2wyNjcyXSA9CXRzbDJ4NzJfbHV4X3RhYmxlLA0KKwlbdG1kMjY3Ml0gPSB0bWQyeDcyX2x1eF90
YWJsZSwNCisJW3RzbDI3NzJdID0JdHNsMng3Ml9sdXhfdGFibGUsDQorCVt0bWQyNzcyXSA9CXRt
ZDJ4NzJfbHV4X3RhYmxlLA0KK307DQorDQorc3RydWN0IGFsc19nYWluYWRqIHsNCisJczE2IGNo
MDsNCisJczE2IGNoMTsNCit9Ow0KKw0KKy8qIFVzZWQgdG8gdmFsaWRhdGUgdGhlIEFMUyBnYWlu
IHNlbGVjdGlvbiBpbmRleCAqLw0KK3N0YXRpYyBjb25zdCBzdHJ1Y3QgYWxzX2dhaW5hZGogdHNs
Mlg3WF9hbHNfZ2FpbmFkaltdID0gew0KKwl7IDEsIDEgfSwNCisJeyA4LCA4IH0sDQorCXsgMTYs
IDE2IH0sDQorCXsgMTIwLCAxMjAgfQ0KK307DQorDQorDQorLyogTm90IHVzaW5nIGNoYW5uZWxz
ICovDQorc3RhdGljIGNvbnN0IHN0cnVjdCBpaW9fY2hhbl9zcGVjIHRzbDJYN1hfY2hhbm5lbHNb
XSA9IHt9Ow0KKw0KKy8qDQorICogUmVhZCBhIG51bWJlciBvZiBieXRlcyBzdGFydGluZyBhdCBy
ZWdpc3RlciAocmVnKSBsb2NhdGlvbi4NCisgKiBSZXR1cm4gMCwgb3IgaTJjX3NtYnVzX3dyaXRl
X2J5dGUgRVJST1IgY29kZS4NCisgKi8NCitzdGF0aWMgaW50DQordHNsMng3eF9pMmNfcmVhZChz
dHJ1Y3QgaTJjX2NsaWVudCAqY2xpZW50LCB1OCByZWcsIHU4ICp2YWwsIHVuc2lnbmVkIGludCBs
ZW4pDQorew0KKwlpbnQgcmV0Ow0KKwlpbnQgaTsNCisNCisJZm9yIChpID0gMDsgaSA8IGxlbjsg
aSsrKSB7DQorCQkvKiBzZWxlY3QgcmVnaXN0ZXIgdG8gd3JpdGUgKi8NCisJCXJldCA9IGkyY19z
bWJ1c193cml0ZV9ieXRlKGNsaWVudCwgKFRTTDJYN1hfQ01EX1JFRyB8IHJlZykpOw0KKwkJaWYg
KHJldCA8IDApIHsNCisJCQlkZXZfZXJyKCZjbGllbnQtPmRldiwgInRzbDJ4N3hfaTJjX3JlYWQg
ZmFpbGVkIHRvIHdyaXRlIg0KKwkJCQkiIHJlZ2lzdGVyICV4XG4iLCByZWcpOw0KKwkJCXJldHVy
biByZXQ7DQorCQl9DQorCQkvKiByZWFkIHRoZSBkYXRhICovDQorCQkqdmFsID0gaTJjX3NtYnVz
X3JlYWRfYnl0ZShjbGllbnQpOw0KKwkJdmFsKys7DQorCQlyZWcrKzsNCisJfQ0KKwlyZXR1cm4g
MDsNCit9DQorDQorLyoNCisgKiBSZWFkcyBhbmQgY2FsY3VsYXRlcyBjdXJyZW50IGx1eCB2YWx1
ZS4NCisgKiBUaGUgcmF3IGNoMCBhbmQgY2gxIHZhbHVlcyBvZiB0aGUgYW1iaWVudCBsaWdodCBz
ZW5zZWQgaW4gdGhlIGxhc3QNCisgKiBpbnRlZ3JhdGlvbiBjeWNsZSBhcmUgcmVhZCBmcm9tIHRo
ZSBkZXZpY2UuDQorICogVGltZSBzY2FsZSBmYWN0b3IgYXJyYXkgdmFsdWVzIGFyZSBhZGp1c3Rl
ZCBiYXNlZCBvbiB0aGUgaW50ZWdyYXRpb24gdGltZS4NCisgKiBUaGUgcmF3IHZhbHVlcyBhcmUg
bXVsdGlwbGllZCBieSBhIHNjYWxlIGZhY3RvciwgYW5kIGRldmljZSBnYWluIGlzIG9idGFpbmVk
DQorICogdXNpbmcgZ2FpbiBpbmRleC4gTGltaXQgY2hlY2tzIGFyZSBkb25lIG5leHQsIHRoZW4g
dGhlIHJhdGlvIG9mIGEgbXVsdGlwbGUNCisgKiBvZiBjaDEgdmFsdWUsIHRvIHRoZSBjaDAgdmFs
dWUsIGlzIGNhbGN1bGF0ZWQuIFRoZSBhcnJheSB0c2wyeDd4X2RldmljZV9sdXhbXQ0KKyAqIGRl
Y2xhcmVkIGFib3ZlIGlzIHRoZW4gc2Nhbm5lZCB0byBmaW5kIHRoZSBmaXJzdCByYXRpbyB2YWx1
ZSB0aGF0IGlzIGp1c3QNCisgKiBhYm92ZSB0aGUgcmF0aW8gd2UganVzdCBjYWxjdWxhdGVkLiBU
aGUgY2gwIGFuZCBjaDEgbXVsdGlwbGllciBjb25zdGFudHMgaW4NCisgKiB0aGUgYXJyYXkgYXJl
IHRoZW4gdXNlZCBhbG9uZyB3aXRoIHRoZSB0aW1lIHNjYWxlIGZhY3RvciBhcnJheSB2YWx1ZXMs
IHRvDQorICogY2FsY3VsYXRlIHRoZSBsdXguDQorICovDQorc3RhdGljIGludCB0c2wyeDd4X2dl
dF9sdXgoc3RydWN0IGlpb19kZXYgKmluZGlvX2RldikNCit7DQorCXUxNiBjaDAsIGNoMTsgLyog
c2VwYXJhdGVkIGNoMC9jaDEgZGF0YSBmcm9tIGRldmljZSAqLw0KKwl1MzIgbHV4OyAvKiByYXcg
bHV4IGNhbGN1bGF0ZWQgZnJvbSBkZXZpY2UgZGF0YSAqLw0KKwl1NjQgbHV4NjQ7DQorCXUzMiBy
YXRpbzsNCisjcHJhZ21hIHBhY2soNCkNCisJdTggYnVmWzRdOw0KKwlzdHJ1Y3QgdHNsMng3eF9s
dXggKnA7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYp
Ow0KKwlpbnQgaSwgcmV0Ow0KKwl1MzIgY2gwbHV4ID0gMDsNCisJdTMyIGNoMWx1eCA9IDA7DQor
DQorCWlmIChtdXRleF90cnlsb2NrKCZjaGlwLT5hbHNfbXV0ZXgpID09IDApIHsNCisJCWRldl9p
bmZvKCZjaGlwLT5jbGllbnQtPmRldiwgInRzbDJ4N3hfZ2V0X2x1eCBkZXZpY2UgaXMgYnVzeVxu
Iik7DQorCQlyZXR1cm4gY2hpcC0+YWxzX2N1cl9pbmZvLmx1eDsgLyogYnVzeSwgc28gcmV0dXJu
IExBU1QgVkFMVUUgKi8NCisJfQ0KKw0KKwlpZiAoY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyAh
PSBUU0wyWDdYX0NISVBfV09SS0lORykgew0KKwkJLyogZGV2aWNlIGlzIG5vdCBlbmFibGVkICov
DQorCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwgInRzbDJ4N3hfZ2V0X2x1eCBkZXZpY2Ug
aXMgbm90IGVuYWJsZWRcbiIpOw0KKwkJcmV0ID0gLUVCVVNZIDsNCisJCWdvdG8gb3V0X3VubG9j
azsNCisJfQ0KKw0KKwlyZXQgPSB0c2wyeDd4X2kyY19yZWFkKGNoaXAtPmNsaWVudCwNCisJCShU
U0wyWDdYX0NNRF9SRUcgfCBUU0wyWDdYX1NUQVRVUyksICZidWZbMF0sIDEpOw0KKwlpZiAocmV0
IDwgMCkgew0KKwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQorCQkJInRzbDJ4N3hfZ2V0
X2x1eCBmYWlsZWQgdG8gcmVhZCBDTURfUkVHXG4iKTsNCisJCWdvdG8gb3V0X3VubG9jazsNCisJ
fQ0KKwkvKiBpcyBkYXRhIG5ldyAmIHZhbGlkICovDQorCWlmICghKGJ1ZlswXSAmIFRTTDJYN1hf
U1RBX0FEQ19WQUxJRCkpIHsNCisJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJCSJ0
c2wyeDd4X2dldF9sdXggZGF0YSBub3QgdmFsaWQgeWV0XG4iKTsNCisJCXJldCA9IGNoaXAtPmFs
c19jdXJfaW5mby5sdXg7IC8qIHJldHVybiBMQVNUIFZBTFVFICovDQorCQlnb3RvIG91dF91bmxv
Y2s7DQorCX0NCisNCisJZm9yIChpID0gMDsgaSA8IDQ7IGkrKykgew0KKwkJcmV0ID0gdHNsMng3
eF9pMmNfcmVhZChjaGlwLT5jbGllbnQsDQorCQkJKFRTTDJYN1hfQ01EX1JFRyB8IChUU0wyWDdY
X0FMU19DSEFOMExPICsgaSkpLA0KKwkJCSZidWZbaV0sIDEpOw0KKwkJaWYgKHJldCA8IDApIHsN
CisJCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCisJCQkJInRzbDJ4N3hfZ2V0X2x1eCBm
YWlsZWQgdG8gcmVhZCINCisJCQkJIiByZXQ6ICV4XG4iLCByZXQpOw0KKwkJCWdvdG8gb3V0X3Vu
bG9jazsNCisJCX0NCisJfQ0KKw0KKwkvKiBjbGVhciBzdGF0dXMsIHJlYWxseSBpbnRlcnJ1cHQg
c3RhdHVzICggYXJlIG9mZiksDQorCWJ1dCB3ZSB1c2UgdGhlIGJpdCBhbnl3YXkgKi8NCisJcmV0
ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2hpcC0+Y2xpZW50LA0KKwkJKFRTTDJYN1hfQ01EX1JF
RyB8DQorCQkJCVRTTDJYN1hfQ01EX1NQTF9GTiB8DQorCQkJCVRTTDJYN1hfQ01EX0FMU19JTlRf
Q0xSKSk7DQorDQorCWlmIChyZXQgPCAwKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRl
diwNCisJCSJ0c2wyeDd4X2kyY193cml0ZV9jb21tYW5kIGZhaWxlZCBpbiB0c2wyeDd4X2dldF9s
dXgsIGVyciA9ICVkXG4iLA0KKwkJCXJldCk7DQorCQlnb3RvIG91dF91bmxvY2s7IC8qIGhhdmUg
bm8gZGF0YSwgc28gcmV0dXJuIGZhaWx1cmUgKi8NCisJfQ0KKw0KKwkvKiBleHRyYWN0IEFMUy9s
dXggZGF0YSAqLw0KKwljaDAgPSBsZTE2X3RvX2NwdXAoKGNvbnN0IF9fbGUxNiAqKSZidWZbMF0p
Ow0KKwljaDEgPSBsZTE2X3RvX2NwdXAoKGNvbnN0IF9fbGUxNiAqKSZidWZbMl0pOw0KKw0KKwlj
aGlwLT5hbHNfY3VyX2luZm8uYWxzX2NoMCA9IGNoMDsNCisJY2hpcC0+YWxzX2N1cl9pbmZvLmFs
c19jaDEgPSBjaDE7DQorDQorCWlmICgoY2gwID49IGNoaXAtPmFsc19zYXR1cmF0aW9uKSB8fCAo
Y2gxID49IGNoaXAtPmFsc19zYXR1cmF0aW9uKSkNCisJCWdvdG8gcmV0dXJuX21heDsNCisNCisJ
aWYgKGNoMCA9PSAwKSB7DQorCQkvKiBoYXZlIG5vIGRhdGEsIHNvIHJldHVybiBMQVNUIFZBTFVF
ICovDQorCQlyZXQgPSBjaGlwLT5hbHNfY3VyX2luZm8ubHV4ID0gMDsNCisJCWdvdG8gb3V0X3Vu
bG9jazsNCisJfQ0KKwkvKiBjYWxjdWxhdGUgcmF0aW8gKi8NCisJcmF0aW8gPSAoY2gxIDw8IDE1
KSAvIGNoMDsNCisJLyogY29udmVydCB0byB1bnNjYWxlZCBsdXggdXNpbmcgdGhlIHBvaW50ZXIg
dG8gdGhlIHRhYmxlICovDQorCWZvciAocCA9IChzdHJ1Y3QgdHNsMng3eF9sdXggKikgY2hpcC0+
dHNsMng3eF9kZXZpY2VfbHV4Ow0KKwkgICAgIHAtPnJhdGlvICE9IDAgJiYgcC0+cmF0aW8gPCBy
YXRpbzsgcCsrKQ0KKwkJOw0KKw0KKwlpZiAocC0+cmF0aW8gPT0gMCkgew0KKwkJbHV4ID0gMDsN
CisJfSBlbHNlIHsNCisJCWNoMGx1eCA9ICgoY2gwICogcC0+Y2gwKSArDQorCQkodHNsMlg3WF9h
bHNfZ2FpbmFkaltjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluXS5jaDAgPj4gMSkpDQor
CQkvIHRzbDJYN1hfYWxzX2dhaW5hZGpbY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fpbl0u
Y2gwOw0KKw0KKwkJY2gxbHV4ID0gKChjaDEgKiBwLT5jaDEpICsNCisJCSh0c2wyWDdYX2Fsc19n
YWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5dLmNoMSA+PiAxKSkNCisJCS8g
dHNsMlg3WF9hbHNfZ2FpbmFkaltjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluXS5jaDE7
DQorDQorCQlsdXggPSBjaDBsdXggLSBjaDFsdXg7DQorCX0NCisNCisJLyogbm90ZTogbHV4IGlz
IDMxIGJpdCBtYXggYXQgdGhpcyBwb2ludCAqLw0KKwlpZiAoY2gxbHV4ID4gY2gwbHV4KSB7DQor
CQlkZXZfZGJnKCZjaGlwLT5jbGllbnQtPmRldiwgIk5vIERhdGEgLSBSZXR1cm4gbGFzdCB2YWx1
ZVxuIik7DQorCQlyZXQgPSBjaGlwLT5hbHNfY3VyX2luZm8ubHV4ID0gMDsNCisJCWdvdG8gb3V0
X3VubG9jazsNCisJfQ0KKw0KKwkvKiBhZGp1c3QgZm9yIGFjdGl2ZSB0aW1lIHNjYWxlICovDQor
CWlmIChjaGlwLT5hbHNfdGltZV9zY2FsZSA9PSAwKQ0KKwkJbHV4ID0gMDsNCisJZWxzZQ0KKwkJ
bHV4ID0gKGx1eCArIChjaGlwLT5hbHNfdGltZV9zY2FsZSA+PiAxKSkgLw0KKwkJCWNoaXAtPmFs
c190aW1lX3NjYWxlOw0KKw0KKwkvKiBhZGp1c3QgZm9yIGFjdGl2ZSBnYWluIHNjYWxlDQorCSAq
IFRoZSB0c2wyeDd4X2RldmljZV9sdXggdGFibGVzIGhhdmUgYSBmYWN0b3Igb2YgMjU2IGJ1aWx0
LWluLg0KKwkgKiBVc2VyLXNwZWNpZmllZCBnYWluIHByb3ZpZGVzIGEgbXVsdGlwbGllci4NCisJ
ICogQXBwbHkgdXNlci1zcGVjaWZpZWQgZ2FpbiBiZWZvcmUgc2hpZnRpbmcgcmlnaHQgdG8gcmV0
YWluIHByZWNpc2lvbi4NCisJICogVXNlIDY0IGJpdHMgdG8gYXZvaWQgb3ZlcmZsb3cgb24gbXVs
dGlwbGljYXRpb24uDQorCSAqIFRoZW4gZ28gYmFjayB0byAzMiBiaXRzIGJlZm9yZSBkaXZpc2lv
biB0byBhdm9pZCB1c2luZyBkaXZfdTY0KCkuDQorCSAqLw0KKwlsdXg2NCA9IGx1eDsNCisJbHV4
NjQgPSBsdXg2NCAqIGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5fdHJpbTsNCisJbHV4
NjQgPj49IDg7DQorCWx1eCA9IGx1eDY0Ow0KKwlsdXggPSAobHV4ICsgNTAwKSAvIDEwMDA7DQor
DQorCWlmIChsdXggPiBUU0wyWDdYX0xVWF9DQUxDX09WRVJfRkxPVykgeyAvKiBjaGVjayBmb3Ig
b3ZlcmZsb3cgKi8NCityZXR1cm5fbWF4Og0KKwkJbHV4ID0gVFNMMlg3WF9MVVhfQ0FMQ19PVkVS
X0ZMT1c7DQorCX0NCisNCisJLyogVXBkYXRlIHRoZSBzdHJ1Y3R1cmUgd2l0aCB0aGUgbGF0ZXN0
IFZBTElEIGx1eC4gKi8NCisJY2hpcC0+YWxzX2N1cl9pbmZvLmx1eCA9IGx1eDsNCisJcmV0ID0g
bHV4Ow0KKw0KK291dF91bmxvY2s6DQorCW11dGV4X3VubG9jaygmY2hpcC0+YWxzX211dGV4KTsN
CisJcmV0dXJuIHJldDsNCisNCit9DQorDQorLyoNCisgKiBQcm94aW1pdHkgcG9sbCBmdW5jdGlv
biAtIGlmIHZhbGlkIGRhdGEgaXMgYXZhaWxhYmxlLCByZWFkIGFuZCBmb3JtIHRoZSBjaDANCisg
KiBhbmQgcHJveCBkYXRhIHZhbHVlcywgY2hlY2sgZm9yIGxpbWl0cyBvbiB0aGUgY2gwIHZhbHVl
LCBhbmQgY2hlY2sgdGhlIHByb3gNCisgKiBkYXRhIGFnYWluc3QgdGhlIGN1cnJlbnQgdGhyZXNo
b2xkcywgdG8gc2V0IHRoZSBldmVudCBzdGF0dXMgYWNjb3JkaW5nbHkuDQorICovDQorc3RhdGlj
IGludCB0c2wyeDd4X3Byb3hfcG9sbChzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2KQ0KK3sNCisj
ZGVmaW5lIENPTlNFQ1VUSVZFX1JFVFJJRVMgNTANCisNCisJaW50IGk7DQorCWludCByZXQ7DQor
CXU4IHN0YXR1czsNCisJdTggY2hkYXRhWzJdOw0KKwlpbnQgZXJyX2NudDsNCisJc3RydWN0IHRz
bDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQorDQorCWlmIChtdXRleF90
cnlsb2NrKCZjaGlwLT5wcm94X211dGV4KSA9PSAwKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5jbGll
bnQtPmRldiwgIkNhbid0IGdldCBwcm94IG11dGV4XG4iKTsNCisJCXJldHVybiAtRUJVU1k7DQor
CX0NCisNCisJZXJyX2NudCA9IDA7DQorDQordHJ5X2FnYWluOg0KKwlyZXQgPSB0c2wyeDd4X2ky
Y19yZWFkKGNoaXAtPmNsaWVudCwNCisJCShUU0wyWDdYX0NNRF9SRUcgfCBUU0wyWDdYX1NUQVRV
UyksICZzdGF0dXMsIDEpOw0KKwlpZiAocmV0IDwgMCkgew0KKwkJZGV2X2VycigmY2hpcC0+Y2xp
ZW50LT5kZXYsDQorCQkJIlJlYWQgcmVncyBmYWlsZWQgaW4gdHNsMng3eF9wcm94X3BvbGwoKSAt
IEFcbiIpOw0KKwkJbXV0ZXhfdW5sb2NrKCZjaGlwLT5wcm94X211dGV4KTsNCisJCXJldHVybiBy
ZXQ7DQorCX0NCisNCisJLypQcm94IGludGVycnVwdCBhc3NlcnRlZCovDQorCWlmICgoKGNoaXAt
PnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiA8PCA0KQ0KKwkJCSYgQ05UTF9QUk9YX0lO
VF9FTkJMKSkgew0KKwkJaWYgKCEoc3RhdHVzICYgVFNMMlg3WF9TVEFfQURDX1ZBTElEKSkgew0K
KwkJCWVycl9jbnQrKzsNCisJCQlpZiAoZXJyX2NudCA+IENPTlNFQ1VUSVZFX1JFVFJJRVMpIHsN
CisJCQkJbXV0ZXhfdW5sb2NrKCZjaGlwLT5wcm94X211dGV4KTsNCisJCQkJZGV2X2VycigmY2hp
cC0+Y2xpZW50LT5kZXYsDQorCQkJCSJDb25zZWMuIHJldHJpZXMgZXhjZWVkZWRcbiIpOw0KKwkJ
CQlyZXR1cm4gY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2ZW50Ow0KKwkJCX0NCisJCQlnb3Rv
IHRyeV9hZ2FpbjsNCisJCX0NCisJfQ0KKw0KKwlmb3IgKGkgPSAwOyBpIDwgMjsgaSsrKSB7DQor
CQlyZXQgPSB0c2wyeDd4X2kyY19yZWFkKGNoaXAtPmNsaWVudCwNCisJCQkoVFNMMlg3WF9DTURf
UkVHIHwNCisJCQkJCShUU0wyWDdYX1BSWF9MTyArIGkpKSwgJmNoZGF0YVtpXSwgMSk7DQorCQlp
ZiAocmV0IDwgMCkgew0KKwkJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJCSJSZWFk
IHJlZ3MgZmFpbGVkIGluIHRzbDJ4N3hfcHJveF9wb2xsKCkgLSBCXG4iKTsNCisJCQltdXRleF91
bmxvY2soJmNoaXAtPnByb3hfbXV0ZXgpOw0KKwkJCXJldHVybiByZXQ7DQorCQl9DQorCX0NCisN
CisJY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2RhdGEgPSAoY2hkYXRhWzFdPDw4KXxjaGRhdGFb
MF07DQorDQorCWlmIChjaGlwLT5wcm94X2N1cl9pbmZvLnByb3hfZGF0YSA+PQ0KKwkJCWNoaXAt
PnRzbDJ4N3hfc2V0dGluZ3MucHJveF90aHJlc19oaWdoKQ0KKwkJY2hpcC0+cHJveF9jdXJfaW5m
by5wcm94X2V2ZW50ID0gMTsNCisJZWxzZQ0KKwkJY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2V2
ZW50ID0gMDsNCisNCisJbXV0ZXhfdW5sb2NrKCZjaGlwLT5wcm94X211dGV4KTsNCisJcmV0dXJu
IGNoaXAtPnByb3hfY3VyX2luZm8ucHJveF9ldmVudDsNCit9DQorDQorLyoNCisgKiBQcm92aWRl
cyBpbml0aWFsIG9wZXJhdGlvbmFsIHBhcmFtZXRlciBkZWZhdWx0cy4NCisgKiBUaGVzZSBkZWZh
dWx0cyBtYXkgYmUgY2hhbmdlZCB0aHJvdWdoIHRoZSBkZXZpY2UncyBzeXNmcyBmaWxlcy4NCisg
Ki8NCitzdGF0aWMgdm9pZCB0c2wyeDd4X2RlZmF1bHRzKHN0cnVjdCB0c2wyWDdYX2NoaXAgKmNo
aXApDQorew0KKwkvKiBPcGVyYXRpb25hbCBwYXJhbWV0ZXJzICovDQorCWNoaXAtPnRzbDJ4N3hf
c2V0dGluZ3MuYWxzX3RpbWUgPSAyMDA7DQorCS8qIG11c3QgYmUgYSBtdWx0aXBsZSBvZiA1MG1T
ICovDQorCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAwOw0KKwkvKiB0aGlzIGlz
IGFjdHVhbGx5IGFuIGluZGV4IGludG8gdGhlIGdhaW4gdGFibGUgKi8NCisJY2hpcC0+dHNsMng3
eF9zZXR0aW5ncy5wcnhfdGltZSA9IDB4ZmU7IC8qNS40IG1TICovDQorCS8qIDIuN21zIHByb3gg
aW50ZWdyYXRpb24gdGltZSAtIGRlY3JlYXNlIHRvIGluY3JlYXNlIHRpbWUgKi8NCisJLyogZGVj
cmVhc2VzIGluIDIuNyBtcyBpbnRlcnZhbHMgKi8NCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5w
cm94X2dhaW4gPSAxOw0KKwkvKiB0aGVzZSBhcmUgYml0cyAzOjIgb2YgcmVnIDB4MGY6IDA9eDEs
MT14MiwyPXg0LDM9eDggKi8NCisJLyogYXNzdW1lIGNsZWFyIGdsYXNzIGFzIGRlZmF1bHQgKi8N
CisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy53YWl0X3RpbWUgPSAyNDU7DQorCS8qIFRpbWUgYmV0
d2VlbiBQUlggYW5kIEFMUyBjeWNsZXMgLWRlY3JlYXNlIHRvIGluY3JlYXNlIHRpbWUgKi8NCisJ
LyogZGVjcmVhc2VzIGluIDIuNyBtcyBpbnRlcnZhbHMgKi8NCisJY2hpcC0+dHNsMng3eF9zZXR0
aW5ncy5wcm94X2NvbmZpZyA9IDA7DQorCS8qIFByb3ggY29uZmlndXJhdGlvbiBmaWx0ZXJzICov
DQorCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5fdHJpbSA9IDEwMDA7DQorCS8qIGRl
ZmF1bHQgZ2FpbiB0cmltIHRvIGFjY291bnQgZm9yIGFwZXJ0dXJlIGVmZmVjdHMgKi8NCisJY2hp
cC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfY2FsX3RhcmdldCA9IDE1MDsNCisJLyogS25vd24gZXh0
ZXJuYWwgQUxTIHJlYWRpbmcgdXNlZCBmb3IgY2FsaWJyYXRpb24gKi8NCisJY2hpcC0+dHNsMng3
eF9zZXR0aW5ncy5hbHNfdGhyZXNoX2xvdyA9IDIwMDsNCisJLyogQ0gwICdsb3cnIGNvdW50IHRv
IHRyaWdnZXIgaW50ZXJydXB0ICovDQorCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVz
aF9oaWdoID0gMjU2Ow0KKwkvKiBDSDAgJ2hpZ2gnIGNvdW50IHRvIHRyaWdnZXIgaW50ZXJydXB0
ICovDQorCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3BlcnNpc3RlbmNlID0gMHhGRjsNCisJ
LyogTnVtYmVyIG9mICdvdXQgb2YgbGltaXRzJyBBREMgcmVhZGluZ3MgUFJYL0FMUyovDQorCWNo
aXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiA9IDB4MDA7DQorCS8qIERlZmF1bHQg
aW50ZXJydXB0KHMpIGVuYWJsZWQuDQorCSAqIDB4MDAgPSBub25lLCAweDEwID0gYWxzLCAweDIw
ID0gcHJ4IDB4MzAgPSBidGggKi8NCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVz
X2xvdyAgPSAwOw0KKwljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNfaGlnaCA9IDUx
MjsNCisJLypkZWZhdWx0IHRocmVzaG9sZCAoYWRqdXN0IGVpdGhlciBtYW51YWxseSBvciB3aXRo
IGNhbCByb3V0aW5lKi8NCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X21heF9zYW1wbGVz
X2NhbCA9IDMwOw0KKwljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfcHVsc2VfY291bnQgPSA4
Ow0KKw0KKwkvKiBMb2FkIHVwIHRoZSBsdXggdGFibGUuIENhbiBiZSBjaGFuZ2VkIGxhdGVyIHZp
YSBBQkkgKi8NCisJaWYgKGNoaXAtPnBkYXRhICYmIGNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9sdXhf
dGFibGVbMF0ucmF0aW8gIT0gMCkNCisJCW1lbWNweShjaGlwLT50c2wyeDd4X2RldmljZV9sdXgs
DQorCQkJY2hpcC0+cGRhdGEtPnBsYXRmb3JtX2x1eF90YWJsZSwNCisJCQlzaXplb2YoY2hpcC0+
cGRhdGEtPnBsYXRmb3JtX2x1eF90YWJsZSkpOw0KKwllbHNlDQorCQltZW1jcHkoY2hpcC0+dHNs
Mng3eF9kZXZpY2VfbHV4LA0KKwkJKHN0cnVjdCB0c2wyeDd4X2x1eCAqKXRzbDJ4N3hfZGVmYXVs
dF9sdXhfdGFibGVfZ3JvdXBbY2hpcC0+aWRdLA0KKwkJCQlNQVhfREVGQVVMVF9UQUJMRV9CWVRF
Uyk7DQorDQorfQ0KKw0KKy8qDQorICogT2J0YWluIHNpbmdsZSByZWFkaW5nIGFuZCBjYWxjdWxh
dGUgdGhlIGFsc19nYWluX3RyaW0NCisgKiAobGF0ZXIgdXNlZCB0byBkZXJpdmUgYWN0dWFsIGx1
eCkuDQorICogUmV0dXJuIHVwZGF0ZWQgZ2Fpbl90cmltIHZhbHVlLg0KKyAqLw0KK3N0YXRpYyBp
bnQgdHNsMng3eF9hbHNfY2FsaWJyYXRlKHN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYpDQorew0K
KwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCisJdTgg
cmVnX3ZhbDsNCisJaW50IGdhaW5fdHJpbV92YWw7DQorCWludCByZXQ7DQorCWludCBsdXhfdmFs
Ow0KKw0KKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjaGlwLT5jbGllbnQsDQorCQkJKFRT
TDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfQ05UUkwpKTsNCisJaWYgKHJldCA8IDApIHsNCisJCWRl
dl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJInRzbDJ4N3hfYWxzX2NhbGlicmF0ZSBmYWls
ZWQgdG8gd3JpdGUgQ05UUkwgcmVnaXN0ZXIsIHJldD0lZFxuIiwNCisJCQlyZXQpOw0KKwkJcmV0
dXJuIHJldDsNCisJfQ0KKw0KKwlyZWdfdmFsID0gaTJjX3NtYnVzX3JlYWRfYnl0ZShjaGlwLT5j
bGllbnQpOw0KKwlpZiAoKHJlZ192YWwgJiAoVFNMMlg3WF9DTlRMX0FEQ19FTkJMIHwgVFNMMlg3
WF9DTlRMX1BXUl9PTikpDQorCQkJIT0gKFRTTDJYN1hfQ05UTF9BRENfRU5CTCB8IFRTTDJYN1hf
Q05UTF9QV1JfT04pKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwNCisJCQkidHNs
Mng3eF9hbHNfY2FsaWJyYXRlIGZhaWxlZDogQURDIG5vdCBlbmFibGVkXG4iKTsNCisJCXJldHVy
biAtMTsNCisJfQ0KKw0KKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjaGlwLT5jbGllbnQs
DQorCQkJKFRTTDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfQ05UUkwpKTsNCisJaWYgKHJldCA8IDAp
IHsNCisJCWRldl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJInRzbDJ4N3hfYWxzX2NhbGli
cmF0ZSBmYWlsZWQgdG8gd3JpdGUgY3RybCByZWc6IHJldD0lZFxuIiwNCisJCQlyZXQpOw0KKwkJ
cmV0dXJuIHJldDsNCisJfQ0KKwlyZWdfdmFsID0gaTJjX3NtYnVzX3JlYWRfYnl0ZShjaGlwLT5j
bGllbnQpOw0KKw0KKwlpZiAoKHJlZ192YWwgJiBUU0wyWDdYX1NUQV9BRENfVkFMSUQpICE9IFRT
TDJYN1hfU1RBX0FEQ19WQUxJRCkgew0KKwkJZGV2X2VycigmY2hpcC0+Y2xpZW50LT5kZXYsDQor
CQkidHNsMng3eF9hbHNfY2FsaWJyYXRlIGZhaWxlZDogU1RBVFVTIC0gQURDIG5vdCB2YWxpZC5c
biIpOw0KKwkJcmV0dXJuIC1FTk9EQVRBOw0KKwl9DQorCWx1eF92YWwgPSB0c2wyeDd4X2dldF9s
dXgoaW5kaW9fZGV2KTsNCisJaWYgKGx1eF92YWwgPCAwKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5j
bGllbnQtPmRldiwNCisJCSJ0c2wyeDd4X2Fsc19jYWxpYnJhdGUgZmFpbGVkIHRvIGdldCBsdXhc
biIpOw0KKwkJcmV0dXJuIGx1eF92YWw7DQorCX0NCisJZ2Fpbl90cmltX3ZhbCA9ICAoKChjaGlw
LT50c2wyeDd4X3NldHRpbmdzLmFsc19jYWxfdGFyZ2V0KQ0KKwkJCSogY2hpcC0+dHNsMng3eF9z
ZXR0aW5ncy5hbHNfZ2Fpbl90cmltKSAvIGx1eF92YWwpOw0KKw0KKwlpZiAoKGdhaW5fdHJpbV92
YWwgPCAyNTApIHx8IChnYWluX3RyaW1fdmFsID4gNDAwMCkpDQorCQlyZXR1cm4gLUVSQU5HRTsN
CisNCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfZ2Fpbl90cmltID0gZ2Fpbl90cmltX3Zh
bDsNCisNCisJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJCSIlcyBhbHNfY2FsaWJy
YXRlIGNvbXBsZXRlZFxuIiwgY2hpcC0+Y2xpZW50LT5uYW1lKTsNCisNCisJcmV0dXJuIChpbnQp
IGdhaW5fdHJpbV92YWw7DQorfQ0KKw0KKy8qDQorICogVHVybiB0aGUgZGV2aWNlIG9uLg0KKyAq
IENvbmZpZ3VyYXRpb24gbXVzdCBiZSBzZXQgYmVmb3JlIGNhbGxpbmcgdGhpcyBmdW5jdGlvbi4N
CisgKi8NCitzdGF0aWMgaW50IHRzbDJ4N3hfY2hpcF9vbihzdHJ1Y3QgaWlvX2RldiAqaW5kaW9f
ZGV2KQ0KK3sNCisJaW50IGk7DQorCWludCByZXQgPSAwOw0KKwl1OCAqZGV2X3JlZzsNCisJdTgg
dXRtcDsNCisJaW50IGFsc19jb3VudDsNCisJaW50IGFsc190aW1lOw0KKwlzdHJ1Y3QgdHNsMlg3
WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCisJdTggcmVnX3ZhbCA9IDA7DQor
DQorCWlmIChjaGlwLT5wZGF0YSAmJiBjaGlwLT5wZGF0YS0+cG93ZXJfb24pDQorCQljaGlwLT5w
ZGF0YS0+cG93ZXJfb24oaW5kaW9fZGV2KTsNCisNCisJLyogTm9uIGNhbGN1bGF0ZWQgcGFyYW1l
dGVycyAqLw0KKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX1BSWF9USU1FXSA9DQorCQkJ
Y2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcnhfdGltZTsNCisJY2hpcC0+dHNsMng3eF9jb25maWdb
VFNMMlg3WF9XQUlUX1RJTUVdID0NCisJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLndhaXRfdGlt
ZTsNCisJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9QUlhfQ09ORklHXSA9DQorCQkJY2hp
cC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2NvbmZpZzsNCisNCisJY2hpcC0+dHNsMng3eF9jb25m
aWdbVFNMMlg3WF9BTFNfTUlOVEhSRVNITE9dID0NCisJCShjaGlwLT50c2wyeDd4X3NldHRpbmdz
LmFsc190aHJlc2hfbG93KSAmIDB4RkY7DQorCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hf
QUxTX01JTlRIUkVTSEhJXSA9DQorCQkoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfdGhyZXNo
X2xvdyA+PiA4KSAmIDB4RkY7DQorCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfQUxTX01B
WFRIUkVTSExPXSA9DQorCQkoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfdGhyZXNoX2hpZ2gp
ICYgMHhGRjsNCisJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9BTFNfTUFYVEhSRVNISEld
ID0NCisJCShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hfaGlnaCA+PiA4KSAmIDB4
RkY7DQorCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfUEVSU0lTVEVOQ0VdID0NCisJCWNo
aXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3BlcnNpc3RlbmNlOw0KKw0KKwljaGlwLT50c2wyeDd4
X2NvbmZpZ1tUU0wyWDdYX1BSWF9DT1VOVF0gPQ0KKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu
cHJveF9wdWxzZV9jb3VudDsNCisJY2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9QUlhfTUlO
VEhSRVNITE9dID0NCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2xvdzsNCisJ
Y2hpcC0+dHNsMng3eF9jb25maWdbVFNMMlg3WF9QUlhfTUFYVEhSRVNITE9dID0NCisJCQljaGlw
LT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNfaGlnaDsNCisNCisJLyogYW5kIG1ha2Ugc3Vy
ZSB3ZSdyZSBub3QgYWxyZWFkeSBvbiAqLw0KKwlpZiAoY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1
cyA9PSBUU0wyWDdYX0NISVBfV09SS0lORykgew0KKwkJLyogaWYgZm9yY2luZyBhIHJlZ2lzdGVy
IHVwZGF0ZSAtIHR1cm4gb2ZmLCB0aGVuIG9uICovDQorCQlkZXZfaW5mbygmY2hpcC0+Y2xpZW50
LT5kZXYsICJkZXZpY2UgaXMgYWxyZWFkeSBlbmFibGVkXG4iKTsNCisJCXJldHVybiAtRUlOVkFM
Ow0KKwl9DQorDQorCS8qIGRldGVybWluZSBhbHMgaW50ZWdyYXRpb24gcmVnc3RlciAqLw0KKwlh
bHNfY291bnQgPSAoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfdGltZSAqIDEwMCArIDEzNSkg
LyAyNzA7DQorCWlmIChhbHNfY291bnQgPT0gMCkNCisJCWFsc19jb3VudCA9IDE7IC8qIGVuc3Vy
ZSBhdCBsZWFzdCBvbmUgY3ljbGUgKi8NCisNCisJLyogY29udmVydCBiYWNrIHRvIHRpbWUgKGVu
Y29tcGFzc2VzIG92ZXJyaWRlcykgKi8NCisJYWxzX3RpbWUgPSAoYWxzX2NvdW50ICogMjcgKyA1
KSAvIDEwOw0KKwljaGlwLT50c2wyeDd4X2NvbmZpZ1tUU0wyWDdYX0FMU19USU1FXSA9IDI1NiAt
IGFsc19jb3VudDsNCisNCisJLyogU2V0IHRoZSBnYWluIGJhc2VkIG9uIHRzbDJ4N3hfc2V0dGlu
Z3Mgc3RydWN0ICovDQorCWNoaXAtPnRzbDJ4N3hfY29uZmlnW1RTTDJYN1hfR0FJTl0gPQ0KKwkJ
CShjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluIHwgKG1BMTAwIHwgRElPREUxKQ0KKwkJ
CQl8ICgoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW4pIDw8IDIpKTsNCisNCisJLyog
c2V0IGNoaXAgc3RydWN0IHJlIHNjYWxpbmcgYW5kIHNhdHVyYXRpb24gKi8NCisJY2hpcC0+YWxz
X3NhdHVyYXRpb24gPSBhbHNfY291bnQgKiA5MjI7IC8qIDkwJSBvZiBmdWxsIHNjYWxlICovDQor
CWNoaXAtPmFsc190aW1lX3NjYWxlID0gKGFsc190aW1lICsgMjUpIC8gNTA7DQorDQorCS8qIFRT
TDJYN1ggU3BlY2lmaWMgcG93ZXItb24gLyBhZGMgZW5hYmxlIHNlcXVlbmNlDQorCSAqIFBvd2Vy
IG9uIHRoZSBkZXZpY2UgMXN0LiAqLw0KKwl1dG1wID0gVFNMMlg3WF9DTlRMX1BXUl9PTjsNCisJ
cmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQorCQlUU0wyWDdY
X0NNRF9SRUcgfCBUU0wyWDdYX0NOVFJMLCB1dG1wKTsNCisJaWYgKHJldCA8IDApIHsNCisJCWRl
dl9lcnIoJmNoaXAtPmNsaWVudC0+ZGV2LCAidHNsMng3eF9jaGlwX29uIGZhaWxlZCBvbiBDTlRS
TCByZWcuXG4iKTsNCisJCXJldHVybiAtMTsNCisJfQ0KKw0KKwkvKiBVc2UgdGhlIGZvbGxvd2lu
ZyBzaGFkb3cgY29weSBmb3Igb3VyIGRlbGF5IGJlZm9yZSBlbmFibGluZyBBREMuDQorCSAqIFdy
aXRlIGFsbCB0aGUgcmVnaXN0ZXJzLiAqLw0KKwlmb3IgKGkgPSAwLCBkZXZfcmVnID0gY2hpcC0+
dHNsMng3eF9jb25maWc7IGkgPCBUU0wyWDdYX1JFR19NQVg7IGkrKykgew0KKwkJcmV0ID0gaTJj
X3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlwLT5jbGllbnQsDQorCQkJCVRTTDJYN1hfQ01EX1JF
RyArIGksICpkZXZfcmVnKyspOw0KKwkJaWYgKHJldCA8IDApIHsNCisJCQlkZXZfZXJyKCZjaGlw
LT5jbGllbnQtPmRldiwNCisJCQkidHNsMng3eF9jaGlwX29uIGZhaWxlZCBvbiB3cml0ZSB0byBy
ZWcgJWQuXG4iLCBpKTsNCisJCQlyZXR1cm4gcmV0Ow0KKwkJfQ0KKwl9DQorDQorCXVkZWxheSgz
MDAwKTsJLyogUG93ZXItb24gc2V0dGxpbmcgdGltZSAqLw0KKw0KKwkvKiBOT1cgZW5hYmxlIHRo
ZSBBREMNCisJICogaW5pdGlhbGl6ZSB0aGUgZGVzaXJlZCBtb2RlIG9mIG9wZXJhdGlvbiAqLw0K
Kwl1dG1wID0gVFNMMlg3WF9DTlRMX1BXUl9PTiB8IFRTTDJYN1hfQ05UTF9BRENfRU5CTCB8IENO
VExfUFJPWF9ERVRfRU5CTDsNCisJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGVfZGF0YShjaGlw
LT5jbGllbnQsDQorCQkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCwgdXRtcCk7DQor
CWlmIChyZXQgPCAwKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQtPmRldiwgInRzbDJ4N3hf
Y2hpcF9vbiBmYWlsZWQgb24gMm5kIENUUkwgcmVnLlxuIik7DQorCQlyZXR1cm4gcmV0Ow0KKwkJ
fQ0KKw0KKwljaGlwLT50c2wyeDd4X2NoaXBfc3RhdHVzID0gVFNMMlg3WF9DSElQX1dPUktJTkc7
DQorDQorCWlmIChjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gIT0gMCkgew0K
KwkJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LCAiU2V0dGluZyBVcCBJbnRlcnJ1cHQocylc
biIpOw0KKwkJLyogZmlyc3QgdGltZSBpbnRlcnJ1cHQgKi8NCisJCWNoaXAtPmluaXRfZG9uZSA9
IDA7DQorDQorDQorCXJlZ192YWwgPSBUU0wyWDdYX0NOVExfUFdSX09OOw0KKw0KKwlpZiAoY2hp
cC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuID09IDB4MTApDQorCQlyZWdfdmFsIHw9
IENOVExfQURDX0VOQkw7DQorDQorCWlmIChjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVw
dHNfZW4gPT0gMHgyMCkNCisJCXJlZ192YWwgfD0gQ05UTF9QUk9YX0RFVF9FTkJMOw0KKw0KKwlp
ZiAoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuID09IDB4MzApDQorCQlyZWdf
dmFsIHw9IChDTlRMX0FEQ19FTkJMIHwgQ05UTF9QUk9YX0RFVF9FTkJMKTsNCisNCisJcmVnX3Zh
bCB8PSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW47DQorDQorCXJldCA9IGky
Y19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2hpcC0+Y2xpZW50LA0KKwkJKFRTTDJYN1hfQ01EX1JF
RyB8IFRTTDJYN1hfQ05UUkwpLCByZWdfdmFsKTsNCisJaWYgKHJldCA8IDApDQorCQlkZXZfZXJy
KCZjaGlwLT5jbGllbnQtPmRldiwNCisJCSJ0c2wyeDd4X2kyY193cml0ZSB0byBkZXZpY2UgZmFp
bGVkIGluIHRzbDJ4N3hfSU9DVExfSU5UX1NFVC5cbiIpOw0KKw0KKwkvKiBDbGVhciBvdXQgYW55
IGluaXRpYWwgaW50ZXJydXB0cyAgKi8NCisJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2hp
cC0+Y2xpZW50LA0KKwkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTURfU1BMX0ZOIHwNCisJ
CVRTTDJYN1hfQ01EX1BST1hBTFNfSU5UQ0xSKTsNCisJaWYgKHJldCA8IDApIHsNCisJCWRldl9l
cnIoJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJCSJ0c2wyeDd4X2kyY193cml0ZV9jb21tYW5kIGZh
aWxlZCBpbiB0c2wyeDd4X2NoaXBfb25cbiIpOw0KKwkJcmV0dXJuIHJldDsNCisJCX0NCisJfQ0K
KwlyZXR1cm4gcmV0Ow0KKw0KK30NCisNCitzdGF0aWMgaW50IHRzbDJ4N3hfY2hpcF9vZmYoc3Ry
dWN0IGlpb19kZXYgKmluZGlvX2RldikNCit7DQorCWludCByZXQ7DQorCXN0cnVjdCB0c2wyWDdY
X2NoaXAgKmNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KKw0KKwkvKiB0dXJuIGRldmljZSBv
ZmYgKi8NCisJY2hpcC0+dHNsMng3eF9jaGlwX3N0YXR1cyA9IFRTTDJYN1hfQ0hJUF9TVVNQRU5E
RUQ7DQorDQorCXJldCA9IGkyY19zbWJ1c193cml0ZV9ieXRlX2RhdGEoY2hpcC0+Y2xpZW50LA0K
KwkJVFNMMlg3WF9DTURfUkVHIHwgVFNMMlg3WF9DTlRSTCwgMHgwMCk7DQorDQorCWlmIChjaGlw
LT5wZGF0YSAmJiBjaGlwLT5wZGF0YS0+cG93ZXJfb2ZmKQ0KKwkJY2hpcC0+cGRhdGEtPnBvd2Vy
X29mZihjaGlwLT5jbGllbnQpOw0KKw0KKwlyZXR1cm4gcmV0Ow0KK30NCisNCisvKioNCisgKiBJ
bnRlZ2VyIFNxdWFyZSBSb290DQorICogV2UgbmVlZCBhbiBpbnRlZ2VyIHZlcnNpb24gc2luY2Ug
MXN0IEZsb2F0aW5nIHBvaW50IGlzIG5vdCBhbGxvd2VkDQorICogaW4gZHJpdmVyIHdvcmxkLCAy
bmQsIGNhbm5vdCBjb3VudCBvbiB0aGUgZGV2aWNlcyBoYXZpbmcgYSBGUFUsIGFuZA0KKyAqIDNy
ZCBzb2Z0d2FyZSBGUCBlbXVsYXRpb24gbWF5IGJlIGV4Y2Vzc2l2ZS4NCisgKi8NCitzdGF0aWMg
dW5zaWduZWQgbG9uZyB0c2wyeDd4X2lzcXJ0KHVuc2lnbmVkIGxvbmcgeCkNCit7DQorCXJlZ2lz
dGVyIHVuc2lnbmVkIGxvbmcgb3AsIHJlcywgb25lOw0KKwlvcCA9IHg7DQorCXJlcyA9IDA7DQor
DQorCW9uZSA9IDEgPDwgMzA7DQorCXdoaWxlIChvbmUgPiBvcCkNCisJCW9uZSA+Pj0gMjsNCisN
CisJd2hpbGUgKG9uZSAhPSAwKSB7DQorCQlpZiAob3AgPj0gcmVzICsgb25lKSB7DQorCQkJb3Ag
LT0gcmVzICsgb25lOw0KKwkJCXJlcyArPSBvbmUgPDwgMTsNCisJCX0NCisJCXJlcyA+Pj0gMTsN
CisJCW9uZSA+Pj0gMjsNCisJfQ0KKwlyZXR1cm4gcmVzOw0KK30NCisNCisvKg0KKyAqIFByb3hp
bWl0eSBjYWxpYnJhdGlvbiBoZWxwZXIgZnVuY3Rpb24NCisgKiBydW5zIHRocm91Z2ggYSBjb2xs
ZWN0aW9uIG9mIGRhdGEgc2FtcGxlcywNCisgKiBzZXRzIHRoZSBtaW4sIG1heCwgbWVhbiwgYW5k
IHN0ZCBkZXYuDQorICovDQorc3RhdGljDQordm9pZCB0c2wyeDd4X3Byb3hfY2FsY3VsYXRlKHUx
NiAqZGF0YSwgaW50IGxlbmd0aCwgc3RydWN0IHByb3hfc3RhdCAqc3RhdFApDQorew0KKwlpbnQg
aTsNCisJaW50IG1pbiwgbWF4LCBzdW0sIG1lYW47DQorCXVuc2lnbmVkIGxvbmcgc3RkZGV2Ow0K
KwlpbnQgdG1wOw0KKw0KKwlpZiAobGVuZ3RoID09IDApDQorCQlsZW5ndGggPSAxOw0KKw0KKwlz
dW0gPSAwOw0KKwltaW4gPSAweGZmZmY7DQorCW1heCA9IDA7DQorCWZvciAoaSA9IDA7IGkgPCBs
ZW5ndGg7IGkrKykgew0KKwkJc3VtICs9IGRhdGFbaV07DQorCQlpZiAoZGF0YVtpXSA8IG1pbikN
CisJCQltaW4gPSBkYXRhW2ldOw0KKwkJaWYgKGRhdGFbaV0gPiBtYXgpDQorCQkJbWF4ID0gZGF0
YVtpXTsNCisJfQ0KKwltZWFuID0gc3VtL2xlbmd0aDsNCisJc3RhdFAtPm1pbiA9IG1pbjsNCisJ
c3RhdFAtPm1heCA9IG1heDsNCisJc3RhdFAtPm1lYW4gPSBtZWFuOw0KKw0KKwlzdW0gPSAwOw0K
Kwlmb3IgKGkgPSAwOyBpIDwgbGVuZ3RoOyBpKyspIHsNCisJCXRtcCA9IGRhdGFbaV0tbWVhbjsN
CisJCXN1bSArPSB0bXAgKiB0bXA7DQorCX0NCisJc3RkZGV2ID0gdHNsMng3eF9pc3FydCgobG9u
ZylzdW0pL2xlbmd0aDsNCisJc3RhdFAtPnN0ZGRldiA9IHN0ZGRldjsNCisNCit9DQorDQorLyoq
DQorICogUHJveGltaXR5IGNhbGlicmF0aW9uIC0gY29sbGVjdHMgYSBudW1iZXIgb2Ygc2FtcGxl
cywNCisgKiBjYWxjdWxhdGVzIGEgc3RhbmRhcmQgZGV2aWF0aW9uIGJhc2VkIG9uIHRoZSBzYW1w
bGVzLCBhbmQNCisgKiBzZXRzIHRoZSB0aHJlc2hvbGQgYWNjb3JkaW5nbHkuDQorICovDQorc3Rh
dGljIHZvaWQgdHNsMng3eF9wcm94X2NhbChzdHJ1Y3QgaWlvX2RldiAqaW5kaW9fZGV2KQ0KK3sN
CisJdTE2IHByb3hfaGlzdG9yeVtNQVhfU0FNUExFU19DQUwrMV07DQorCWludCBpOw0KKwlzdHJ1
Y3QgcHJveF9zdGF0IHByb3hfc3RhdF9kYXRhWzJdOw0KKwlzdHJ1Y3QgcHJveF9zdGF0ICpjYWxQ
Ow0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCisJ
dTggdG1wX2lycV9zZXR0aW5nczsNCisJdTggY3VycmVudF9zdGF0ZSA9IGNoaXAtPnRzbDJ4N3hf
Y2hpcF9zdGF0dXM7DQorDQorCWlmIChjaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfbWF4X3Nh
bXBsZXNfY2FsID4gTUFYX1NBTVBMRVNfQ0FMKSB7DQorCQlkZXZfZXJyKCZjaGlwLT5jbGllbnQt
PmRldiwNCisJCQkibWF4IHByb3ggc2FtcGxlcyBjYWwgaXMgdG9vIGJpZzogJWRcbiIsDQorCQkJ
Y2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X21heF9zYW1wbGVzX2NhbCk7DQorCQljaGlwLT50
c2wyeDd4X3NldHRpbmdzLnByb3hfbWF4X3NhbXBsZXNfY2FsID0gTUFYX1NBTVBMRVNfQ0FMOw0K
Kwl9DQorDQorCS8qIGhhdmUgdG8gc3RvcCB0byBjaGFuZ2Ugc2V0dGluZ3MgKi8NCisJdHNsMng3
eF9jaGlwX29mZihpbmRpb19kZXYpOw0KKw0KKwkvKiBFbmFibGUgcHJveGltaXR5IGRldGVjdGlv
biBzYXZlIGp1c3QgaW4gY2FzZSBwcm94IG5vdCB3YW50ZWQgeWV0Ki8NCisJdG1wX2lycV9zZXR0
aW5ncyA9IGNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbjsNCisJY2hpcC0+dHNs
Mng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuIHw9IENOVExfUFJPWF9JTlRfRU5CTDsNCisNCisJ
Lyp0dXJuIG9uIGRldmljZSBpZiBub3QgYWxyZWFkeSBvbiovDQorCXRzbDJ4N3hfY2hpcF9vbihp
bmRpb19kZXYpOw0KKw0KKwkvKmdhdGhlciB0aGUgc2FtcGxlcyovDQorCWZvciAoaSA9IDA7IGkg
PCBjaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfbWF4X3NhbXBsZXNfY2FsOyBpKyspIHsNCisJ
CW1kZWxheSgxNSk7DQorCQl0c2wyeDd4X3Byb3hfcG9sbChpbmRpb19kZXYpOw0KKwkJcHJveF9o
aXN0b3J5W2ldID0gY2hpcC0+cHJveF9jdXJfaW5mby5wcm94X2RhdGE7DQorCQlkZXZfaW5mbygm
Y2hpcC0+Y2xpZW50LT5kZXYsICIyIGk9JWQgcHJveCBkYXRhPSAlZFxuIiwNCisJCQlpLCBjaGlw
LT5wcm94X2N1cl9pbmZvLnByb3hfZGF0YSk7DQorDQorCX0NCisNCisJdHNsMng3eF9jaGlwX29m
ZihpbmRpb19kZXYpOw0KKw0KKwljYWxQID0gJnByb3hfc3RhdF9kYXRhW1BST1hfU1RBVF9DQUxd
Ow0KKwl0c2wyeDd4X3Byb3hfY2FsY3VsYXRlKHByb3hfaGlzdG9yeSwNCisJCWNoaXAtPnRzbDJ4
N3hfc2V0dGluZ3MucHJveF9tYXhfc2FtcGxlc19jYWwsIGNhbFApOw0KKwljaGlwLT50c2wyeDd4
X3NldHRpbmdzLnByb3hfdGhyZXNfaGlnaCA9IChjYWxQLT5tYXggPDwgMSkgLSBjYWxQLT5tZWFu
Ow0KKw0KKwlkZXZfaW5mbygmY2hpcC0+Y2xpZW50LT5kZXYsICIgY2FsIG1pbj0lZCBtZWFuPSVk
IG1heD0lZFxuIiwNCisJCWNhbFAtPm1pbiwgY2FsUC0+bWVhbiwgY2FsUC0+bWF4KTsNCisJZGV2
X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2LA0KKwkJIiVzIHByb3hpbWl0eSB0aHJlc2hvbGQgc2V0
IHRvICVkXG4iLA0KKwkJY2hpcC0+Y2xpZW50LT5uYW1lLCBjaGlwLT50c2wyeDd4X3NldHRpbmdz
LnByb3hfdGhyZXNfaGlnaCk7DQorDQorCS8qIGJhY2sgdG8gdGhlIHdheSB0aGV5IHdlcmUgKi8N
CisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuID0gdG1wX2lycV9zZXR0aW5n
czsNCisJaWYgKGN1cnJlbnRfc3RhdGUgPT0gVFNMMlg3WF9DSElQX1dPUktJTkcpDQorCQl0c2wy
eDd4X2NoaXBfb24oaW5kaW9fZGV2KTsNCit9DQorDQorLyogLS0tLS0tLS0tLSBTeXNmcyBJbnRl
cmZhY2UgRnVuY3Rpb25zIC0tLS0tLS0tLS0tLS0gKi8NCisNCisNCitzdGF0aWMgc3NpemVfdCB0
c2wyeDd4X3Bvd2VyX3N0YXRlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2
aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRl
dl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNo
aXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorDQorCXJldHVybiBzbnByaW50ZihidWYsIFBBR0Vf
U0laRSwgIiVkXG4iLCBjaGlwLT50c2wyeDd4X2NoaXBfc3RhdHVzKTsNCit9DQorDQorc3RhdGlj
IHNzaXplX3QgdHNsMng3eF9wb3dlcl9zdGF0ZV9zdG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQor
CXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBs
ZW4pDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2
KTsNCisJdW5zaWduZWQgbG9uZyB2YWx1ZTsNCisNCisJaWYgKGtzdHJ0b3VsKGJ1ZiwgMCwgJnZh
bHVlKSkNCisJCXJldHVybiAtRUlOVkFMOw0KKw0KKwlpZiAodmFsdWUgPT0gMCkNCisJCXRzbDJ4
N3hfY2hpcF9vZmYoZGV2X2luZm8pOw0KKwllbHNlDQorCQl0c2wyeDd4X2NoaXBfb24oZGV2X2lu
Zm8pOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4X3By
b3hpbWl0eV9kZXRlY3Rfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2Vf
YXR0cmlidXRlICphdHRyLCBjaGFyICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2lu
Zm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9
IGlpb19wcml2KGRldl9pbmZvKTsNCisNCisJdHNsMng3eF9wcm94X3BvbGwoZGV2X2luZm8pOw0K
Kw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwNCisJCQljaGlwLT5w
cm94X2N1cl9pbmZvLnByb3hfZXZlbnQpOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4
X2dhaW5fc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRl
ICphdHRyLCBjaGFyICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZf
Z2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2
KGRldl9pbmZvKTsNCisNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIs
DQorCQl0c2wyWDdYX2Fsc19nYWluYWRqW2NoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW5d
LmNoMCk7DQorfQ0KKw0KK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfZ2Fpbl9zdG9yZShzdHJ1Y3Qg
ZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFy
ICpidWYsIHNpemVfdCBsZW4pDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZf
Z2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2
KGRldl9pbmZvKTsNCisJdW5zaWduZWQgbG9uZyB2YWx1ZTsNCisJaWYgKGtzdHJ0b3VsKGJ1Ziwg
MCwgJnZhbHVlKSkNCisJCXJldHVybiAtRUlOVkFMOw0KKw0KKwlzd2l0Y2ggKHZhbHVlKSB7DQor
CWNhc2UgMToNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAwOw0KKwkJYnJl
YWs7DQorCWNhc2UgODoNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2dhaW4gPSAxOw0K
KwkJYnJlYWs7DQorCWNhc2UgMTY6DQorCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWlu
ID0gMjsNCisJCWJyZWFrOw0KKwljYXNlIDEyMDoNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3Mu
YWxzX2dhaW4gPSAzOw0KKwkJYnJlYWs7DQorCWNhc2UgMTI4Og0KKwkJY2hpcC0+dHNsMng3eF9z
ZXR0aW5ncy5hbHNfZ2FpbiA9IDM7DQorCQlicmVhazsNCisNCisJZGVmYXVsdDoNCisJCWRldl9l
cnIoZGV2LA0KKwkJCSJJbnZhbGlkIEdhaW4gSW5kZXhcbiIpOw0KKwkJcmV0dXJuIC1FSU5WQUw7
DQorCX0NCisNCisJcmV0dXJuIGxlbjsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9n
YWluX2F2YWlsYWJsZV9zaG93KHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9h
dHRyaWJ1dGUgKmF0dHIsIGNoYXIgKmJ1ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5m
byA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0g
aWlvX3ByaXYoZGV2X2luZm8pOw0KKw0KKwlpZiAoc3RybmNtcChjaGlwLT5jbGllbnQtPm5hbWUs
ICJ0c2wyNzcyIiwgNykgPT0gMCkNCisJCXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwg
IiVzXG4iLCAiMSA4IDE2IDEyOCIpOw0KKwllbHNlDQorCQlyZXR1cm4gc25wcmludGYoYnVmLCBQ
QUdFX1NJWkUsICIlc1xuIiwgIjEgOCAxNiAxMjAiKTsNCit9DQorDQorc3RhdGljIHNzaXplX3Qg
dHNsMng3eF9wcm94X2dhaW5fc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZp
Y2VfYXR0cmlidXRlICphdHRyLCBjaGFyICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2
X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hp
cCA9IGlpb19wcml2KGRldl9pbmZvKTsNCisNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9T
SVpFLCAiJWRcbiIsDQorCQkJKDEgPDwgY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW4p
KTsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9wcm94X2dhaW5fc3RvcmUoc3RydWN0
IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hh
ciAqYnVmLCBzaXplX3QgbGVuKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9pbmZvID0gZGV2
X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJp
dihkZXZfaW5mbyk7DQorCXVuc2lnbmVkIGxvbmcgdmFsdWU7DQorCWlmIChrc3RydG91bChidWYs
IDAsICZ2YWx1ZSkpDQorCQlyZXR1cm4gLUVJTlZBTDsNCisNCisJc3dpdGNoICh2YWx1ZSkgew0K
KwljYXNlIDE6DQorCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfZ2FpbiA9IDA7DQorCQli
cmVhazsNCisJY2FzZSAyOg0KKwkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X2dhaW4gPSAx
Ow0KKwkJYnJlYWs7DQorCWNhc2UgNDoNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MucHJveF9n
YWluID0gMjsNCisJCWJyZWFrOw0KKwljYXNlIDg6DQorCQljaGlwLT50c2wyeDd4X3NldHRpbmdz
LnByb3hfZ2FpbiA9IDM7DQorCQlicmVhazsNCisJZGVmYXVsdDoNCisJCWRldl9lcnIoZGV2LA0K
KwkJCSJJbnZhbGlkIFByb3ggR2FpbiBJbmRleFxuIik7DQorCQlyZXR1cm4gLUVJTlZBTDsNCisJ
fQ0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4X3Byb3hf
Z2Fpbl9hdmFpbGFibGVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2Vf
YXR0cmlidXRlICphdHRyLCBjaGFyICpidWYpDQorew0KKwkJcmV0dXJuIHNucHJpbnRmKGJ1Ziwg
UEFHRV9TSVpFLCAiJXNcbiIsICIxIDIgNCA4Iik7DQorfQ0KKw0KK3N0YXRpYyBzc2l6ZV90IHRz
bDJ4N3hfYWxzX3RpbWVfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2Vf
YXR0cmlidXRlICphdHRyLCBjaGFyICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2lu
Zm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9
IGlpb19wcml2KGRldl9pbmZvKTsNCisNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpF
LCAiJWRcbiIsDQorCQkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5hbHNfdGltZSk7DQorfQ0KKw0K
K3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfYWxzX3RpbWVfc3RvcmUoc3RydWN0IGRldmljZSAqZGV2
LA0KKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXpl
X3QgbGVuKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRh
KGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7
DQorCXVuc2lnbmVkIGxvbmcgdmFsdWU7DQorDQorCWlmIChrc3RydG91bChidWYsIDAsICZ2YWx1
ZSkpDQorCQlyZXR1cm4gLUVJTlZBTDsNCisNCisJaWYgKCh2YWx1ZSA8IDUwKSB8fCAodmFsdWUg
PiA2NTApKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCWlmICh2YWx1ZSAlIDUwKQ0KKwkJcmV0
dXJuIC1FSU5WQUw7DQorDQorCSBjaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aW1lID0gdmFs
dWU7DQorDQorCXJldHVybiBsZW47DQorfQ0KKw0KK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfYWxz
X3RpbWVfYXZhaWxhYmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2aWNl
X2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJcmV0dXJuIHNucHJpbnRmKGJ1Ziwg
UEFHRV9TSVpFLCAiJXNcbiIsDQorCQkiNTAgMTAwIDE1MCAyMDAgMjUwIDMwMCAzNTAgNDAwIDQ1
MCA1MDAgNTUwIDYwMCA2NTAiKTsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9hbHNf
dHJpbV9zaG93KHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUg
KmF0dHIsIGNoYXIgKmJ1ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9n
ZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYo
ZGV2X2luZm8pOw0KKw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwN
CisJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19nYWluX3RyaW0pOw0KK30NCisNCitzdGF0
aWMgc3NpemVfdCB0c2wyeDd4X2Fsc190cmltX3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwNCisJ
c3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxl
bikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYp
Ow0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0KKwl1
bnNpZ25lZCBsb25nIHZhbHVlOw0KKw0KKwlpZiAoa3N0cnRvdWwoYnVmLCAwLCAmdmFsdWUpKQ0K
KwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCWlmICh2YWx1ZSkNCisJCWNoaXAtPnRzbDJ4N3hfc2V0
dGluZ3MuYWxzX2dhaW5fdHJpbSA9IHZhbHVlOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitz
dGF0aWMgc3NpemVfdCB0c2wyeDd4X2Fsc19jYWxfdGFyZ2V0X3Nob3coc3RydWN0IGRldmljZSAq
ZGV2LA0KKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJ
c3RydWN0IGlpb19kZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVj
dCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorDQorCXJldHVybiBz
bnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVkXG4iLA0KKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGlu
Z3MuYWxzX2NhbF90YXJnZXQpOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2Fsc19j
YWxfdGFyZ2V0X3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9hdHRy
aWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCit7DQorCXN0cnVjdCBp
aW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3
WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0KKwl1bnNpZ25lZCBsb25nIHZhbHVl
Ow0KKw0KKwlpZiAoa3N0cnRvdWwoYnVmLCAwLCAmdmFsdWUpKQ0KKwkJcmV0dXJuIC1FSU5WQUw7
DQorDQorCWlmICh2YWx1ZSkNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX2NhbF90YXJn
ZXQgPSB2YWx1ZTsNCisNCisJcmV0dXJuIGxlbjsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNs
Mng3eF9pbnRlcnJ1cHRzX2VuX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2
aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRl
dl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNo
aXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorDQorCWlmIChjaGlwLT50c2wyeDd4X3NldHRpbmdz
LmludGVycnVwdHNfZW4gJiAweDEwKQ0KKwkJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpF
LCAiJWRcbiIsIDEpOw0KKwllbHNlDQorCQlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUs
ICIlZFxuIiwgMCk7DQorfQ0KKw0KK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfaW50ZXJydXB0c19l
bl9zdG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICph
dHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBsZW4pDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAq
ZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAq
Y2hpcCA9IGlpb19wcml2KGRldl9pbmZvKTsNCisJdW5zaWduZWQgbG9uZyB2YWx1ZTsNCisNCisJ
aWYgKGtzdHJ0b3VsKGJ1ZiwgMCwgJnZhbHVlKSkNCisJCXJldHVybiAtRUlOVkFMOw0KKw0KKwlp
ZiAodmFsdWUgPiAxKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorCWlmICh2YWx1ZSkNCisJCWNoaXAt
PnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJydXB0c19lbiB8PSAweDEwOw0KKwllbHNlDQorCQljaGlw
LT50c2wyeDd4X3NldHRpbmdzLmludGVycnVwdHNfZW4gJj0gMHgyMDsNCisNCisJcmV0dXJuIGxl
bjsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9wcm94X2ludGVycnVwdF9zaG93KHN0
cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNoYXIg
KmJ1ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShk
ZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0K
Kw0KKwlpZiAoY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1cHRzX2VuICYgMHgyMCkNCisJ
CXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0laRSwgIiVkXG4iLCAxKTsNCisJZWxzZQ0KKwkJ
cmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIsIDApOw0KK30NCisNCitzdGF0
aWMgc3NpemVfdCB0c2wyeDd4X3Byb3hfaW50ZXJydXB0X3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRl
diwNCisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6
ZV90IGxlbikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0
YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8p
Ow0KKwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KKw0KKwlpZiAoa3N0cnRvdWwoYnVmLCAwLCAmdmFs
dWUpKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCWlmICh2YWx1ZSA+IDEpDQorCQlyZXR1cm4g
LUVJTlZBTDsNCisJaWYgKHZhbHVlKQ0KKwkJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5pbnRlcnJ1
cHRzX2VuIHw9IDB4MjA7DQorCWVsc2UNCisJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuaW50ZXJy
dXB0c19lbiAmPSAweDEwOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0aWMgc3NpemVf
dCB0c2wyeDd4X2Fsc190aHJlc2hfbG93X3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1
Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJc3RydWN0IGlpb19k
ZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2No
aXAgKmNoaXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorDQorCXJldHVybiBzbnByaW50ZihidWYs
IFBBR0VfU0laRSwgIiVkXG4iLA0KKwkJCWNoaXAtPnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVz
aF9sb3cpOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4X2Fsc190aHJlc2hfbG93X3N0
b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIs
IGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZf
aW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlw
ID0gaWlvX3ByaXYoZGV2X2luZm8pOw0KKwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KKw0KKwlpZiAo
a3N0cnRvdWwoYnVmLCAwLCAmdmFsdWUpKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCWNoaXAt
PnRzbDJ4N3hfc2V0dGluZ3MuYWxzX3RocmVzaF9sb3cgPSB2YWx1ZTsNCisNCisJcmV0dXJuIGxl
bjsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9hbHNfdGhyZXNoX2hpZ2hfc2hvdyhz
dHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjaGFy
ICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEo
ZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGRldl9pbmZvKTsN
CisNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIsDQorCQkJY2hpcC0+
dHNsMng3eF9zZXR0aW5ncy5hbHNfdGhyZXNoX2hpZ2gpOw0KK30NCisNCitzdGF0aWMgc3NpemVf
dCB0c2wyeDd4X2Fsc190aHJlc2hfaGlnaF9zdG9yZShzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0
cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjb25zdCBjaGFyICpidWYsIHNpemVfdCBsZW4p
DQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsN
CisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGRldl9pbmZvKTsNCisJdW5z
aWduZWQgbG9uZyB2YWx1ZTsNCisNCisJaWYgKGtzdHJ0b3VsKGJ1ZiwgMCwgJnZhbHVlKSkNCisJ
CXJldHVybiAtRUlOVkFMOw0KKw0KKwljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc190aHJlc2hf
aGlnaCA9IHZhbHVlOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0
c2wyeDd4X3Byb3hfdGhyZXNoX2xvd19zaG93KHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0
IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNoYXIgKmJ1ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2
ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlw
ICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0KKw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQ
QUdFX1NJWkUsICIlZFxuIiwNCisJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLnByb3hfdGhyZXNf
bG93KTsNCit9DQorDQorc3RhdGljIHNzaXplX3QgdHNsMng3eF9wcm94X3RocmVzaF9sb3dfc3Rv
cmUoc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwg
Y29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVuKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9p
bmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAg
PSBpaW9fcHJpdihkZXZfaW5mbyk7DQorCXVuc2lnbmVkIGxvbmcgdmFsdWU7DQorDQorCWlmIChr
c3RydG91bChidWYsIDAsICZ2YWx1ZSkpDQorCQlyZXR1cm4gLUVJTlZBTDsNCisNCisJY2hpcC0+
dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2xvdyA9IHZhbHVlOw0KKw0KKwlyZXR1cm4gbGVu
Ow0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0c2wyeDd4X3Byb3hfdGhyZXNoX2hpZ2hfc2hvdyhz
dHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZpY2VfYXR0cmlidXRlICphdHRyLCBjaGFy
ICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEo
ZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hpcCA9IGlpb19wcml2KGRldl9pbmZvKTsN
CisNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAiJWRcbiIsDQorCQkJY2hpcC0+
dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVzX2hpZ2gpOw0KK30NCisNCitzdGF0aWMgc3NpemVf
dCB0c2wyeDd4X3Byb3hfdGhyZXNoX2hpZ2hfc3RvcmUoc3RydWN0IGRldmljZSAqZGV2LA0KKwlz
dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVu
KQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7
DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorCXVu
c2lnbmVkIGxvbmcgdmFsdWU7DQorDQorCWlmIChrc3RydG91bChidWYsIDAsICZ2YWx1ZSkpDQor
CQlyZXR1cm4gLUVJTlZBTDsNCisNCisJY2hpcC0+dHNsMng3eF9zZXR0aW5ncy5wcm94X3RocmVz
X2hpZ2ggPSB2YWx1ZTsNCisNCisJcmV0dXJuIGxlbjsNCit9DQorDQorc3RhdGljIHNzaXplX3Qg
dHNsMng3eF9wcm94X2RhdGFfc2hvdyhzdHJ1Y3QgZGV2aWNlICpkZXYsDQorCXN0cnVjdCBkZXZp
Y2VfYXR0cmlidXRlICphdHRyLCBjaGFyICpidWYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAqZGV2
X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAqY2hp
cCA9IGlpb19wcml2KGRldl9pbmZvKTsNCisJdHNsMng3eF9wcm94X3BvbGwoZGV2X2luZm8pOw0K
Kw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxuIiwNCisJCQljaGlwLT5w
cm94X2N1cl9pbmZvLnByb3hfZGF0YSk7DQorfQ0KKw0KKy8qIHNhbXBsaW5nX2ZyZXF1ZW5jeSBB
S0EgcGVyc2lzdGVuY2UgaW4gZGF0YSBzaGVldCAqLw0KK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hf
YWxzX3BlcnNpc3RlbmNlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2aWNl
X2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9p
bmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdYX2NoaXAgKmNoaXAg
PSBpaW9fcHJpdihkZXZfaW5mbyk7DQorDQorCXJldHVybiBzbnByaW50ZihidWYsIFBBR0VfU0la
RSwgIjB4JTAyWFxuIiwNCisJCQljaGlwLT50c2wyeDd4X3NldHRpbmdzLmFsc19wZXJzaXN0ZW5j
ZSk7DQorfQ0KKw0KK3N0YXRpYyBzc2l6ZV90IHRzbDJ4N3hfYWxzX3BlcnNpc3RlbmNlX3N0b3Jl
KHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNv
bnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5m
byA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0g
aWlvX3ByaXYoZGV2X2luZm8pOw0KKwl1bnNpZ25lZCBsb25nIHZhbHVlOw0KKw0KKwlpZiAoa3N0
cnRvdWwoYnVmLCAwLCAmdmFsdWUpKQ0KKwkJcmV0dXJuIC1FSU5WQUw7DQorDQorCWNoaXAtPnRz
bDJ4N3hfc2V0dGluZ3MuYWxzX3BlcnNpc3RlbmNlID0gdmFsdWU7DQorDQorCXJldHVybiBsZW47
DQorfQ0KKw0KK3N0YXRpYw0KK3NzaXplX3QgdHNsMng3eF9hbHNfcGVyc2lzdGVuY2VfYXZhaWxh
YmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlzdHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAq
YXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJcmV0dXJuIHNucHJpbnRmKGJ1ZiwgUEFHRV9TSVpFLCAi
MHgwMCAtIDB4RkYgKDAgLSAyNTUpXG4iKTsNCit9DQorDQorc3RhdGljDQorc3NpemVfdCB0c2wy
eDd4X2x1eF9zaG93KHN0cnVjdCBkZXZpY2UgKmRldiwgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUg
KmF0dHIsDQorCWNoYXIgKmJ1ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRl
dl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwlpbnQgbHV4Ow0KKw0KKwlsdXggPSB0c2wyeDd4X2dldF9s
dXgoZGV2X2luZm8pOw0KKw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQQUdFX1NJWkUsICIlZFxu
IiwgbHV4KTsNCit9DQorDQorc3RhdGljDQorc3NpemVfdCB0c2wyeDd4X2FkY19zaG93KHN0cnVj
dCBkZXZpY2UgKmRldiwgc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsDQorCWNoYXIgKmJ1
ZikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYp
Ow0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0KKw0K
Kwl0c2wyeDd4X2dldF9sdXgoZGV2X2luZm8pOw0KKw0KKwlyZXR1cm4gc25wcmludGYoYnVmLCBQ
QUdFX1NJWkUsICIweCUwOHhcbiIsDQorCQkJKChjaGlwLT5hbHNfY3VyX2luZm8uYWxzX2NoMCA8
PCAxNikgfA0KKwkJCQkJY2hpcC0+YWxzX2N1cl9pbmZvLmFsc19jaDEpKTsNCit9DQorDQorc3Rh
dGljIHNzaXplX3QgdHNsMng3eF9kb19jYWxpYnJhdGUoc3RydWN0IGRldmljZSAqZGV2LA0KKwlz
dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY29uc3QgY2hhciAqYnVmLCBzaXplX3QgbGVu
KQ0KK3sNCisJc3RydWN0IGlpb19kZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7
DQorCXVuc2lnbmVkIGxvbmcgdmFsdWU7DQorDQorCWlmIChrc3RydG91bChidWYsIDAsICZ2YWx1
ZSkpDQorCQlyZXR1cm4gLUVJTlZBTDsNCisNCisJaWYgKHZhbHVlID09IDEpDQorCQl0c2wyeDd4
X2Fsc19jYWxpYnJhdGUoZGV2X2luZm8pOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0
aWMgc3NpemVfdCB0c2wyeDd4X2x1eHRhYmxlX3Nob3coc3RydWN0IGRldmljZSAqZGV2LA0KKwlz
dHJ1Y3QgZGV2aWNlX2F0dHJpYnV0ZSAqYXR0ciwgY2hhciAqYnVmKQ0KK3sNCisJc3RydWN0IGlp
b19kZXYgKmRldl9pbmZvID0gZGV2X2dldF9kcnZkYXRhKGRldik7DQorCXN0cnVjdCB0c2wyWDdY
X2NoaXAgKmNoaXAgPSBpaW9fcHJpdihkZXZfaW5mbyk7DQorCWludCBpOw0KKwlpbnQgb2Zmc2V0
ID0gMDsNCisNCisJaSA9IDA7DQorCXdoaWxlIChpIDwgKE1BWF9UQUJMRV9TSVpFICogMykpIHsN
CisJCW9mZnNldCArPSBzbnByaW50ZihidWYgKyBvZmZzZXQsIFBBR0VfU0laRSwgIiVkLCVkLCVk
LCIsDQorCQkJY2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4W2ldLnJhdGlvLA0KKwkJCWNoaXAtPnRz
bDJ4N3hfZGV2aWNlX2x1eFtpXS5jaDAsDQorCQkJY2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4W2ld
LmNoMSk7DQorCQlpZiAoY2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4W2ldLnJhdGlvID09IDApIHsN
CisJCQkvKiBXZSBqdXN0IHByaW50ZWQgdGhlIGZpcnN0ICIwIiBlbnRyeS4NCisJCQkgKiBOb3cg
Z2V0IHJpZCBvZiB0aGUgZXh0cmEgIiwiIGFuZCBicmVhay4gKi8NCisJCQlvZmZzZXQtLTsNCisJ
CQlicmVhazsNCisJCX0NCisJCWkrKzsNCisJfQ0KKw0KKwlvZmZzZXQgKz0gc25wcmludGYoYnVm
ICsgb2Zmc2V0LCBQQUdFX1NJWkUsICJcbiIpOw0KKwlyZXR1cm4gb2Zmc2V0Ow0KK30NCisNCitz
dGF0aWMgc3NpemVfdCB0c2wyeDd4X2x1eHRhYmxlX3N0b3JlKHN0cnVjdCBkZXZpY2UgKmRldiwN
CisJc3RydWN0IGRldmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90
IGxlbikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShk
ZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYoZGV2X2luZm8pOw0K
KwlpbnQgdmFsdWVbQVJSQVlfU0laRShjaGlwLT50c2wyeDd4X2RldmljZV9sdXgpKjMgKyAxXTsN
CisJaW50IG47DQorDQorCWdldF9vcHRpb25zKGJ1ZiwgQVJSQVlfU0laRSh2YWx1ZSksIHZhbHVl
KTsNCisNCisJLyogV2Ugbm93IGhhdmUgYW4gYXJyYXkgb2YgaW50cyBzdGFydGluZyBhdCB2YWx1
ZVsxXSwgYW5kDQorCSAqIGVudW1lcmF0ZWQgYnkgdmFsdWVbMF0uDQorCSAqIFdlIGV4cGVjdCBl
YWNoIGdyb3VwIG9mIHRocmVlIGludHMgaXMgb25lIHRhYmxlIGVudHJ5LA0KKwkgKiBhbmQgdGhl
IGxhc3QgdGFibGUgZW50cnkgaXMgYWxsIDAuDQorCSAqLw0KKwluID0gdmFsdWVbMF07DQorCWlm
ICgobiAlIDMpIHx8IG4gPCA2IHx8DQorCQkJbiA+ICgoQVJSQVlfU0laRShjaGlwLT50c2wyeDd4
X2RldmljZV9sdXgpIC0gMSkgKiAzKSkgew0KKwkJZGV2X2luZm8oZGV2LCAiTFVYIFRBQkxFIElO
UFVUIEVSUk9SIDEgVmFsdWVbMF09JWRcbiIsIG4pOw0KKwkJcmV0dXJuIC1FSU5WQUw7DQorCX0N
CisNCisJaWYgKCh2YWx1ZVsobiAtIDIpXSB8IHZhbHVlWyhuIC0gMSldIHwgdmFsdWVbbl0pICE9
IDApIHsNCisJCWRldl9pbmZvKGRldiwgIkxVWCBUQUJMRSBJTlBVVCBFUlJPUiAyIFZhbHVlWzBd
PSVkXG4iLCBuKTsNCisJCXJldHVybiAtRUlOVkFMOw0KKwl9DQorDQorCWlmIChjaGlwLT50c2wy
eDd4X2NoaXBfc3RhdHVzID09IFRTTDJYN1hfQ0hJUF9XT1JLSU5HKQ0KKwkJdHNsMng3eF9jaGlw
X29mZihkZXZfaW5mbyk7DQorDQorCS8qIFplcm8gb3V0IHRoZSB0YWJsZSAqLw0KKwltZW1zZXQo
Y2hpcC0+dHNsMng3eF9kZXZpY2VfbHV4LCAwLCBzaXplb2YoY2hpcC0+dHNsMng3eF9kZXZpY2Vf
bHV4KSk7DQorCW1lbWNweShjaGlwLT50c2wyeDd4X2RldmljZV9sdXgsICZ2YWx1ZVsxXSwgKHZh
bHVlWzBdICogNCkpOw0KKw0KKwlyZXR1cm4gbGVuOw0KK30NCisNCitzdGF0aWMgc3NpemVfdCB0
c2wyeDd4X2RvX3Byb3hfY2FsaWJyYXRlKHN0cnVjdCBkZXZpY2UgKmRldiwNCisJc3RydWN0IGRl
dmljZV9hdHRyaWJ1dGUgKmF0dHIsIGNvbnN0IGNoYXIgKmJ1Ziwgc2l6ZV90IGxlbikNCit7DQor
CXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KKwl1bnNp
Z25lZCBsb25nIHZhbHVlOw0KKw0KKwlpZiAoa3N0cnRvdWwoYnVmLCAwLCAmdmFsdWUpKQ0KKwkJ
cmV0dXJuIC1FSU5WQUw7DQorDQorCWlmICh2YWx1ZSA9PSAxKQ0KKwkJdHNsMng3eF9wcm94X2Nh
bChkZXZfaW5mbyk7DQorDQorCXJldHVybiBsZW47DQorfQ0KKw0KK3N0YXRpYyBERVZJQ0VfQVRU
Uihwb3dlcl9zdGF0ZSwgU19JUlVHTyB8IFNfSVdVU1IsDQorCQl0c2wyeDd4X3Bvd2VyX3N0YXRl
X3Nob3csIHRzbDJ4N3hfcG93ZXJfc3RhdGVfc3RvcmUpOw0KKw0KK3N0YXRpYyBERVZJQ0VfQVRU
Uihwcm94aW1pdHlfcmF3LCBTX0lSVUdPLA0KKwkJdHNsMng3eF9wcm94aW1pdHlfZGV0ZWN0X3No
b3csIE5VTEwpOw0KKw0KK3N0YXRpYyBERVZJQ0VfQVRUUihpbnRlbnNpdHlfaW5mcmFyZWRfcmF3
LCBTX0lSVUdPLA0KKwkJdHNsMng3eF9wcm94X2RhdGFfc2hvdywgTlVMTCk7DQorDQorDQorc3Rh
dGljIERFVklDRV9BVFRSKHByb3hpbWl0eV9jYWxpYnNjYWxlLCBTX0lSVUdPIHwgU19JV1VTUiwN
CisJCXRzbDJ4N3hfcHJveF9nYWluX3Nob3csIHRzbDJ4N3hfcHJveF9nYWluX3N0b3JlKTsNCitz
dGF0aWMgREVWSUNFX0FUVFIocHJveGltaXR5X2NhbGlic2NhbGVfYXZhaWxhYmxlLCBTX0lSVUdP
LA0KKwkJdHNsMng3eF9wcm94X2dhaW5fYXZhaWxhYmxlX3Nob3csIE5VTEwpOw0KKw0KK3N0YXRp
YyBERVZJQ0VfQVRUUihpbGx1bWluYW5jZTBfY2FsaWJzY2FsZSwgU19JUlVHTyB8IFNfSVdVU1Is
DQorCQl0c2wyeDd4X2dhaW5fc2hvdywgdHNsMng3eF9nYWluX3N0b3JlKTsNCitzdGF0aWMgREVW
SUNFX0FUVFIoaWxsdW1pbmFuY2UwX2NhbGlic2NhbGVfYXZhaWxhYmxlLCBTX0lSVUdPLA0KKwkJ
dHNsMng3eF9nYWluX2F2YWlsYWJsZV9zaG93LCBOVUxMKTsNCisNCitzdGF0aWMgREVWSUNFX0FU
VFIoaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWUsIFNfSVJVR08gfCBTX0lXVVNSLA0KKwkJ
dHNsMng3eF9hbHNfdGltZV9zaG93LCB0c2wyeDd4X2Fsc190aW1lX3N0b3JlKTsNCitzdGF0aWMg
REVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLCBTX0lS
VUdPLA0KKwkJdHNsMng3eF9hbHNfdGltZV9hdmFpbGFibGVfc2hvdywgTlVMTCk7DQorDQorc3Rh
dGljIERFVklDRV9BVFRSKGlsbHVtaW5hbmNlMF9jYWxpYmJpYXMsIFNfSVJVR08gfCBTX0lXVVNS
LA0KKwkJdHNsMng3eF9hbHNfdHJpbV9zaG93LCB0c2wyeDd4X2Fsc190cmltX3N0b3JlKTsNCisN
CitzdGF0aWMgREVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2lucHV0X3RhcmdldCwgU19JUlVHTyB8
IFNfSVdVU1IsDQorCQl0c2wyeDd4X2Fsc19jYWxfdGFyZ2V0X3Nob3csIHRzbDJ4N3hfYWxzX2Nh
bF90YXJnZXRfc3RvcmUpOw0KKw0KK3N0YXRpYyBERVZJQ0VfQVRUUihpbGx1bWluYW5jZTBfYm90
aF9yYXcsIFNfSVJVR08sIHRzbDJ4N3hfYWRjX3Nob3csDQorCQlOVUxMKTsNCisNCitzdGF0aWMg
REVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2lucHV0LCBTX0lSVUdPLCB0c2wyeDd4X2x1eF9zaG93
LA0KKwkJTlVMTCk7DQorc3RhdGljIERFVklDRV9BVFRSKGlsbHVtaW5hbmNlMF9jYWxpYnJhdGUs
IFNfSVdVU1IsIE5VTEwsDQorCQl0c2wyeDd4X2RvX2NhbGlicmF0ZSk7DQorc3RhdGljIERFVklD
RV9BVFRSKHByb3hpbWl0eV9jYWxpYnJhdGUsIFNfSVdVU1IsIE5VTEwsDQorCQl0c2wyeDd4X2Rv
X3Byb3hfY2FsaWJyYXRlKTsNCisNCitzdGF0aWMgREVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX2x1
eF90YWJsZSwgU19JUlVHTyB8IFNfSVdVU1IsDQorCQl0c2wyeDd4X2x1eHRhYmxlX3Nob3csIHRz
bDJ4N3hfbHV4dGFibGVfc3RvcmUpOw0KKw0KK3N0YXRpYyBERVZJQ0VfQVRUUihpbGx1bWluYW5j
ZTBfdGhyZXNoX2ZhbGxpbmdfdmFsdWUsIFNfSVJVR08gfCBTX0lXVVNSLA0KKwkJdHNsMng3eF9h
bHNfdGhyZXNoX2xvd19zaG93LCB0c2wyeDd4X2Fsc190aHJlc2hfbG93X3N0b3JlKTsNCisNCitz
dGF0aWMgREVWSUNFX0FUVFIoaWxsdW1pbmFuY2UwX3RocmVzaF9yaXNpbmdfdmFsdWUsIFNfSVJV
R08gfCBTX0lXVVNSLA0KKwkJdHNsMng3eF9hbHNfdGhyZXNoX2hpZ2hfc2hvdywgdHNsMng3eF9h
bHNfdGhyZXNoX2hpZ2hfc3RvcmUpOw0KKw0KK3N0YXRpYyBERVZJQ0VfQVRUUihzYW1wbGluZ19m
cmVxdWVuY3ksIFNfSVJVR08gfCBTX0lXVVNSLA0KKwkJdHNsMng3eF9hbHNfcGVyc2lzdGVuY2Vf
c2hvdywgdHNsMng3eF9hbHNfcGVyc2lzdGVuY2Vfc3RvcmUpOw0KKw0KK3N0YXRpYyBERVZJQ0Vf
QVRUUihzYW1wbGluZ19mcmVxdWVuY3lfYXZhaWxhYmxlLCBTX0lSVUdPLA0KKwkJdHNsMng3eF9h
bHNfcGVyc2lzdGVuY2VfYXZhaWxhYmxlX3Nob3csIE5VTEwpOw0KKw0KK3N0YXRpYyBERVZJQ0Vf
QVRUUihwcm94aW1pdHlfdGhyZXNoX3Jpc2luZ192YWx1ZSwgU19JUlVHTyB8IFNfSVdVU1IsDQor
CQl0c2wyeDd4X3Byb3hfdGhyZXNoX2hpZ2hfc2hvdywgdHNsMng3eF9wcm94X3RocmVzaF9oaWdo
X3N0b3JlKTsNCisNCitzdGF0aWMgREVWSUNFX0FUVFIocHJveGltaXR5X3RocmVzaF9mYWxsaW5n
X3ZhbHVlLCBTX0lSVUdPIHwgU19JV1VTUiwNCisJCXRzbDJ4N3hfcHJveF90aHJlc2hfbG93X3No
b3csIHRzbDJ4N3hfcHJveF90aHJlc2hfbG93X3N0b3JlKTsNCisNCitzdGF0aWMgc3RydWN0IGF0
dHJpYnV0ZSAqdHNsMjU3MV9kZXZpY2VfYXR0cnNbXSA9IHsNCisJJmRldl9hdHRyX3Bvd2VyX3N0
YXRlLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZS5hdHRyLA0KKwkm
ZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2NhbGlic2NhbGVfYXZhaWxhYmxlLmF0dHIsDQorCSZkZXZf
YXR0cl9pbGx1bWluYW5jZTBfaW50ZWdyYXRpb25fdGltZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxs
dW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLmF0dHIsDQorCSZkZXZfYXR0cl9p
bGx1bWluYW5jZTBfY2FsaWJiaWFzLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfaW5w
dXRfdGFyZ2V0LmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfYm90aF9yYXcuYXR0ciwN
CisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnB1dC5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1p
bmFuY2UwX2NhbGlicmF0ZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2x1eF90YWJs
ZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX3RocmVzaF9mYWxsaW5nX3ZhbHVlLmF0
dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfdGhyZXNoX3Jpc2luZ192YWx1ZS5hdHRyLA0K
KwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQorCSZkZXZfYXR0cl9zYW1wbGlu
Z19mcmVxdWVuY3lfYXZhaWxhYmxlLmF0dHIsDQorCU5VTEwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1
Y3QgYXR0cmlidXRlICp0c2wyNjcxX2RldmljZV9hdHRyc1tdID0gew0KKwkmZGV2X2F0dHJfcG93
ZXJfc3RhdGUuYXR0ciwNCisJJmRldl9hdHRyX3Byb3hpbWl0eV9yYXcuYXR0ciwNCisJJmRldl9h
dHRyX2ludGVuc2l0eV9pbmZyYXJlZF9yYXcuYXR0ciwNCisJJmRldl9hdHRyX3NhbXBsaW5nX2Zy
ZXF1ZW5jeS5hdHRyLA0KKwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5h
dHRyLA0KKwkmZGV2X2F0dHJfcHJveGltaXR5X3RocmVzaF9yaXNpbmdfdmFsdWUuYXR0ciwNCisJ
JmRldl9hdHRyX3Byb3hpbWl0eV90aHJlc2hfZmFsbGluZ192YWx1ZS5hdHRyLA0KKwkmZGV2X2F0
dHJfcHJveGltaXR5X2NhbGlicmF0ZS5hdHRyLA0KKwlOVUxMDQorfTsNCisNCitzdGF0aWMgc3Ry
dWN0IGF0dHJpYnV0ZSAqdHNsMjc3MV9kZXZpY2VfYXR0cnNbXSA9IHsNCisJJmRldl9hdHRyX3Bv
d2VyX3N0YXRlLmF0dHIsDQorCSZkZXZfYXR0cl9wcm94aW1pdHlfcmF3LmF0dHIsDQorCSZkZXZf
YXR0cl9pbnRlbnNpdHlfaW5mcmFyZWRfcmF3LmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5j
ZTBfY2FsaWJzY2FsZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2NhbGlic2NhbGVf
YXZhaWxhYmxlLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfaW50ZWdyYXRpb25fdGlt
ZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxh
YmxlLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfY2FsaWJiaWFzLmF0dHIsDQorCSZk
ZXZfYXR0cl9pbGx1bWluYW5jZTBfaW5wdXRfdGFyZ2V0LmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1
bWluYW5jZTBfYm90aF9yYXcuYXR0ciwNCisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnB1dC5h
dHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2NhbGlicmF0ZS5hdHRyLA0KKwkmZGV2X2F0
dHJfaWxsdW1pbmFuY2UwX2x1eF90YWJsZS5hdHRyLA0KKwkmZGV2X2F0dHJfaWxsdW1pbmFuY2Uw
X3RocmVzaF9mYWxsaW5nX3ZhbHVlLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfdGhy
ZXNoX3Jpc2luZ192YWx1ZS5hdHRyLA0KKwkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0
dHIsDQorCSZkZXZfYXR0cl9zYW1wbGluZ19mcmVxdWVuY3lfYXZhaWxhYmxlLmF0dHIsDQorCSZk
ZXZfYXR0cl9wcm94aW1pdHlfdGhyZXNoX3Jpc2luZ192YWx1ZS5hdHRyLA0KKwkmZGV2X2F0dHJf
cHJveGltaXR5X3RocmVzaF9mYWxsaW5nX3ZhbHVlLmF0dHIsDQorCSZkZXZfYXR0cl9wcm94aW1p
dHlfY2FsaWJyYXRlLmF0dHIsDQorCU5VTEwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1Y3QgYXR0cmli
dXRlICp0c2wyNTcyX2RldmljZV9hdHRyc1tdID0gew0KKwkmZGV2X2F0dHJfcG93ZXJfc3RhdGUu
YXR0ciwNCisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9jYWxpYnNjYWxlLmF0dHIsDQorCSZkZXZf
YXR0cl9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZV9hdmFpbGFibGUuYXR0ciwNCisJJmRldl9hdHRy
X2lsbHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWlu
YW5jZTBfaW50ZWdyYXRpb25fdGltZV9hdmFpbGFibGUuYXR0ciwNCisJJmRldl9hdHRyX2lsbHVt
aW5hbmNlMF9jYWxpYmJpYXMuYXR0ciwNCisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnB1dF90
YXJnZXQuYXR0ciwNCisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9ib3RoX3Jhdy5hdHRyLA0KKwkm
ZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2lucHV0LmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5j
ZTBfY2FsaWJyYXRlLmF0dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfbHV4X3RhYmxlLmF0
dHIsDQorCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfdGhyZXNoX2ZhbGxpbmdfdmFsdWUuYXR0ciwN
CisJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF90aHJlc2hfcmlzaW5nX3ZhbHVlLmF0dHIsDQorCSZk
ZXZfYXR0cl9zYW1wbGluZ19mcmVxdWVuY3kuYXR0ciwNCisJJmRldl9hdHRyX3NhbXBsaW5nX2Zy
ZXF1ZW5jeV9hdmFpbGFibGUuYXR0ciwNCisJTlVMTA0KK307DQorDQorc3RhdGljIHN0cnVjdCBh
dHRyaWJ1dGUgKnRzbDI2NzJfZGV2aWNlX2F0dHJzW10gPSB7DQorCSZkZXZfYXR0cl9wb3dlcl9z
dGF0ZS5hdHRyLA0KKwkmZGV2X2F0dHJfcHJveGltaXR5X3Jhdy5hdHRyLA0KKwkmZGV2X2F0dHJf
aW50ZW5zaXR5X2luZnJhcmVkX3Jhdy5hdHRyLA0KKwkmZGV2X2F0dHJfcHJveGltaXR5X2NhbGli
c2NhbGUuYXR0ciwNCisJJmRldl9hdHRyX3NhbXBsaW5nX2ZyZXF1ZW5jeS5hdHRyLA0KKwkmZGV2
X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5hdHRyLA0KKwkmZGV2X2F0dHJfcHJv
eGltaXR5X3RocmVzaF9yaXNpbmdfdmFsdWUuYXR0ciwNCisJJmRldl9hdHRyX3Byb3hpbWl0eV90
aHJlc2hfZmFsbGluZ192YWx1ZS5hdHRyLA0KKwkmZGV2X2F0dHJfcHJveGltaXR5X2NhbGlicmF0
ZS5hdHRyLA0KKwlOVUxMDQorfTsNCisNCitzdGF0aWMgc3RydWN0IGF0dHJpYnV0ZSAqdHNsMjc3
Ml9kZXZpY2VfYXR0cnNbXSA9IHsNCisJCSZkZXZfYXR0cl9wb3dlcl9zdGF0ZS5hdHRyLA0KKwkJ
JmRldl9hdHRyX3Byb3hpbWl0eV9yYXcuYXR0ciwNCisJCSZkZXZfYXR0cl9wcm94aW1pdHlfY2Fs
aWJzY2FsZS5hdHRyLA0KKwkJJmRldl9hdHRyX2ludGVuc2l0eV9pbmZyYXJlZF9yYXcuYXR0ciwN
CisJCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfY2FsaWJzY2FsZS5hdHRyLA0KKwkJJmRldl9hdHRy
X2lsbHVtaW5hbmNlMF9jYWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRyLA0KKwkJJmRldl9hdHRyX2ls
bHVtaW5hbmNlMF9pbnRlZ3JhdGlvbl90aW1lLmF0dHIsDQorCQkmZGV2X2F0dHJfaWxsdW1pbmFu
Y2UwX2ludGVncmF0aW9uX3RpbWVfYXZhaWxhYmxlLmF0dHIsDQorCQkmZGV2X2F0dHJfaWxsdW1p
bmFuY2UwX2NhbGliYmlhcy5hdHRyLA0KKwkJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF9pbnB1dF90
YXJnZXQuYXR0ciwNCisJCSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfYm90aF9yYXcuYXR0ciwNCisJ
CSZkZXZfYXR0cl9pbGx1bWluYW5jZTBfaW5wdXQuYXR0ciwNCisJCSZkZXZfYXR0cl9pbGx1bWlu
YW5jZTBfY2FsaWJyYXRlLmF0dHIsDQorCQkmZGV2X2F0dHJfaWxsdW1pbmFuY2UwX2x1eF90YWJs
ZS5hdHRyLA0KKwkJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF90aHJlc2hfZmFsbGluZ192YWx1ZS5h
dHRyLA0KKwkJJmRldl9hdHRyX2lsbHVtaW5hbmNlMF90aHJlc2hfcmlzaW5nX3ZhbHVlLmF0dHIs
DQorCQkmZGV2X2F0dHJfc2FtcGxpbmdfZnJlcXVlbmN5LmF0dHIsDQorCQkmZGV2X2F0dHJfc2Ft
cGxpbmdfZnJlcXVlbmN5X2F2YWlsYWJsZS5hdHRyLA0KKwkJJmRldl9hdHRyX3Byb3hpbWl0eV90
aHJlc2hfcmlzaW5nX3ZhbHVlLmF0dHIsDQorCQkmZGV2X2F0dHJfcHJveGltaXR5X3RocmVzaF9m
YWxsaW5nX3ZhbHVlLmF0dHIsDQorCQkmZGV2X2F0dHJfcHJveGltaXR5X2NhbGlicmF0ZS5hdHRy
LA0KKwkJJmRldl9hdHRyX3Byb3hpbWl0eV9jYWxpYnNjYWxlX2F2YWlsYWJsZS5hdHRyLA0KKwkJ
TlVMTA0KK307DQorDQorc3RhdGljIHN0cnVjdCBhdHRyaWJ1dGVfZ3JvdXAgdHNsMlg3WF9kZXZf
YXR0cl9ncm91cF90YmxbXSA9IHsNCisJW3RzbDI1NzFdID0gew0KKwkJLmF0dHJzID0gdHNsMjU3
MV9kZXZpY2VfYXR0cnMsDQorCX0sDQorCVt0c2wyNjcxXSA9IHsNCisJCS5hdHRycyA9IHRzbDI2
NzFfZGV2aWNlX2F0dHJzLA0KKwl9LA0KKwlbdG1kMjY3MV0gPSB7DQorCQkuYXR0cnMgPSB0c2wy
NjcxX2RldmljZV9hdHRycywNCisJfSwNCisJW3RzbDI3NzFdID0gew0KKwkJLmF0dHJzID0gdHNs
Mjc3MV9kZXZpY2VfYXR0cnMsDQorCX0sDQorCVt0bWQyNzcxXSA9IHsNCisJCS5hdHRycyA9IHRz
bDI3NzFfZGV2aWNlX2F0dHJzLA0KKwl9LA0KKwlbdHNsMjU3Ml0gPSB7DQorCQkuYXR0cnMgPSB0
c2wyNTcyX2RldmljZV9hdHRycywNCisJfSwNCisJW3RzbDI2NzJdID0gew0KKwkJLmF0dHJzID0g
dHNsMjY3Ml9kZXZpY2VfYXR0cnMsDQorCX0sDQorCVt0bWQyNjcyXSA9IHsNCisJCS5hdHRycyA9
IHRzbDI2NzJfZGV2aWNlX2F0dHJzLA0KKwl9LA0KKwlbdHNsMjc3Ml0gPSB7DQorCQkuYXR0cnMg
PSB0c2wyNzcyX2RldmljZV9hdHRycywNCisJfSwNCisJW3RtZDI3NzJdID0gew0KKwkJLmF0dHJz
ID0gdHNsMjc3Ml9kZXZpY2VfYXR0cnMsDQorCX0sDQorDQorfTsNCisNCitzdGF0aWMgSUlPX0RF
VklDRV9BVFRSKGlsbHVtaW5hbmNlX3RocmVzaF9ib3RoX2VuLA0KKwkJU19JUlVHTyB8IFNfSVdV
U1IsDQorCQl0c2wyeDd4X2ludGVycnVwdHNfZW5fc2hvdywgdHNsMng3eF9pbnRlcnJ1cHRzX2Vu
X3N0b3JlLCAwKTsNCisNCitzdGF0aWMgSUlPX0RFVklDRV9BVFRSKHByb3hpbWl0eV90aHJlc2hf
Ym90aF9lbiwNCisJCVNfSVJVR08gfCBTX0lXVVNSLA0KKwkJdHNsMng3eF9wcm94X2ludGVycnVw
dF9zaG93LCB0c2wyeDd4X3Byb3hfaW50ZXJydXB0X3N0b3JlLCAwKTsNCisNCitzdGF0aWMgc3Ry
dWN0IGF0dHJpYnV0ZSAqdHNsMlg3WF9wcm94X2V2ZW50X2F0dHJpYnV0ZXNbXSA9IHsNCisJJmlp
b19kZXZfYXR0cl9wcm94aW1pdHlfdGhyZXNoX2JvdGhfZW4uZGV2X2F0dHIuYXR0ciwNCisJTlVM
TCwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1Y3QgYXR0cmlidXRlICp0c2wyWDdYX2Fsc19ldmVudF9h
dHRyaWJ1dGVzW10gPSB7DQorCSZpaW9fZGV2X2F0dHJfaWxsdW1pbmFuY2VfdGhyZXNoX2JvdGhf
ZW4uZGV2X2F0dHIuYXR0ciwNCisJTlVMTCwNCit9Ow0KKw0KK3N0YXRpYyBzdHJ1Y3QgYXR0cmli
dXRlICp0c2wyWDdYX3Byb3hhbHNfZXZlbnRfYXR0cmlidXRlc1tdID0gew0KKwkmaWlvX2Rldl9h
dHRyX2lsbHVtaW5hbmNlX3RocmVzaF9ib3RoX2VuLmRldl9hdHRyLmF0dHIsDQorCSZpaW9fZGV2
X2F0dHJfcHJveGltaXR5X3RocmVzaF9ib3RoX2VuLmRldl9hdHRyLmF0dHIsDQorCU5VTEwsDQor
fTsNCisNCitzdGF0aWMgc3RydWN0IGF0dHJpYnV0ZV9ncm91cCB0c2wyWDdYX2V2ZW50X2F0dHJf
Z3JvdXBfdGJsW10gPSB7DQorCVt0c2wyNTcxXSA9IHsNCisJCS5hdHRycyA9IHRzbDJYN1hfYWxz
X2V2ZW50X2F0dHJpYnV0ZXMsDQorCQkubmFtZSAgPSAiZXZlbnRzIiwNCisJfSwNCisJW3RzbDI2
NzFdID0gew0KKwkJLmF0dHJzID0gdHNsMlg3WF9wcm94X2V2ZW50X2F0dHJpYnV0ZXMsDQorCQku
bmFtZSAgPSAiZXZlbnRzIiwNCisJfSwNCisJW3RtZDI2NzFdID0gew0KKwkJLmF0dHJzID0gdHNs
Mlg3WF9wcm94X2V2ZW50X2F0dHJpYnV0ZXMsDQorCQkubmFtZSAgPSAiZXZlbnRzIiwNCisJfSwN
CisJW3RzbDI3NzFdID0gew0KKwkJLmF0dHJzID0gdHNsMlg3WF9wcm94YWxzX2V2ZW50X2F0dHJp
YnV0ZXMsDQorCQkubmFtZSAgPSAiZXZlbnRzIiwNCisJfSwNCisJW3RtZDI3NzFdID0gew0KKwkJ
LmF0dHJzID0gdHNsMlg3WF9wcm94YWxzX2V2ZW50X2F0dHJpYnV0ZXMsDQorCQkubmFtZSAgPSAi
ZXZlbnRzIiwNCisJfSwNCisNCisJW3RzbDI1NzJdID0gew0KKwkJLmF0dHJzID0gdHNsMlg3WF9h
bHNfZXZlbnRfYXR0cmlidXRlcywNCisJCS5uYW1lICA9ICJldmVudHMiLA0KKwl9LA0KKwlbdHNs
MjY3Ml0gPSB7DQorCQkuYXR0cnMgPSB0c2wyWDdYX3Byb3hfZXZlbnRfYXR0cmlidXRlcywNCisJ
CS5uYW1lICA9ICJldmVudHMiLA0KKwl9LA0KKwlbdG1kMjY3Ml0gPSB7DQorCQkuYXR0cnMgPSB0
c2wyWDdYX3Byb3hfZXZlbnRfYXR0cmlidXRlcywNCisJCS5uYW1lICA9ICJldmVudHMiLA0KKwl9
LA0KKwlbdHNsMjc3Ml0gPSB7DQorCQkuYXR0cnMgPSB0c2wyWDdYX3Byb3hhbHNfZXZlbnRfYXR0
cmlidXRlcywNCisJCS5uYW1lICA9ICJldmVudHMiLA0KKwl9LA0KKwlbdG1kMjc3Ml0gPSB7DQor
CQkuYXR0cnMgPSB0c2wyWDdYX3Byb3hhbHNfZXZlbnRfYXR0cmlidXRlcywNCisJCS5uYW1lICA9
ICJldmVudHMiLA0KKwl9DQorfTsNCisNCisNCisvKiBVc2UgdGhlIGRlZmF1bHQgcmVnaXN0ZXIg
dmFsdWVzIHRvIGlkZW50aWZ5IHRoZSBUYW9zIGRldmljZSAqLw0KK3N0YXRpYyBpbnQgdHNsMng3
eF9kZXZpY2VfaWQodW5zaWduZWQgY2hhciAqYnVmcCwgaW50IHRhcmdldCkNCit7DQorCXN3aXRj
aCAodGFyZ2V0KSB7DQorCWNhc2UgdHNsMjU3MToNCisJY2FzZSB0c2wyNjcxOg0KKwljYXNlIHRz
bDI3NzE6DQorCQlyZXR1cm4gKChidWZwW1RTTDJYN1hfQ0hJUElEXSAmIDB4ZjApID09IFRSSVRP
Tl9JRCk7DQorCWJyZWFrOw0KKwljYXNlIHRtZDI2NzE6DQorCWNhc2UgdG1kMjc3MToNCisJCXJl
dHVybiAoKGJ1ZnBbVFNMMlg3WF9DSElQSURdICYgMHhmMCkgPT0gSEFMSUJVVF9JRCk7DQorCWJy
ZWFrOw0KKwljYXNlIHRzbDI1NzI6DQorCWNhc2UgdHNsMjY3MjoNCisJY2FzZSB0bWQyNjcyOg0K
KwljYXNlIHRzbDI3NzI6DQorCWNhc2UgdG1kMjc3MjoNCisJCXJldHVybiAoKGJ1ZnBbVFNMMlg3
WF9DSElQSURdICYgMHhmMCkgPT0gU1dPUkRGSVNIX0lEKTsNCisJYnJlYWs7DQorCX0NCisNCisJ
cmV0dXJuIC1FSU5WQUw7DQorfQ0KKw0KK3N0YXRpYyBjb25zdCBzdHJ1Y3QgaWlvX2luZm8gdHNs
Mlg3WF9pbmZvW10gPSB7DQorCVt0c2wyNTcxXSA9IHsNCisJCQkuYXR0cnMgPSAmdHNsMlg3WF9k
ZXZfYXR0cl9ncm91cF90YmxbdHNsMjU3MV0sDQorCQkJLmV2ZW50X2F0dHJzID0gJnRzbDJYN1hf
ZXZlbnRfYXR0cl9ncm91cF90YmxbdHNsMjU3MV0sDQorCQkJLmRyaXZlcl9tb2R1bGUgPSBUSElT
X01PRFVMRSwNCisJfSwNCisJW3RzbDI2NzFdID0gew0KKwkJCS5hdHRycyA9ICZ0c2wyWDdYX2Rl
dl9hdHRyX2dyb3VwX3RibFt0c2wyNjcxXSwNCisJCQkuZXZlbnRfYXR0cnMgPSAmdHNsMlg3WF9l
dmVudF9hdHRyX2dyb3VwX3RibFt0c2wyNjcxXSwNCisJCQkuZHJpdmVyX21vZHVsZSA9IFRISVNf
TU9EVUxFLA0KKwl9LA0KKwlbdG1kMjY3MV0gPSB7DQorCQkJLmF0dHJzID0gJnRzbDJYN1hfZGV2
X2F0dHJfZ3JvdXBfdGJsW3RzbDI2NzFdLA0KKwkJCS5ldmVudF9hdHRycyA9ICZ0c2wyWDdYX2V2
ZW50X2F0dHJfZ3JvdXBfdGJsW3RzbDI2NzFdLA0KKwkJCS5kcml2ZXJfbW9kdWxlID0gVEhJU19N
T0RVTEUsDQorCX0sDQorCVt0c2wyNzcxXSA9IHsNCisJCQkuYXR0cnMgPSAmdHNsMlg3WF9kZXZf
YXR0cl9ncm91cF90YmxbdHNsMjc3MV0sDQorCQkJLmV2ZW50X2F0dHJzID0gJnRzbDJYN1hfZXZl
bnRfYXR0cl9ncm91cF90YmxbdHNsMjc3MV0sDQorCQkJLmRyaXZlcl9tb2R1bGUgPSBUSElTX01P
RFVMRSwNCisJfSwNCisJW3RtZDI3NzFdID0gew0KKwkJCS5hdHRycyA9ICZ0c2wyWDdYX2Rldl9h
dHRyX2dyb3VwX3RibFt0c2wyNzcxXSwNCisJCQkuZXZlbnRfYXR0cnMgPSAmdHNsMlg3WF9ldmVu
dF9hdHRyX2dyb3VwX3RibFt0c2wyNzcxXSwNCisJCQkuZHJpdmVyX21vZHVsZSA9IFRISVNfTU9E
VUxFLA0KKwl9LA0KKwlbdHNsMjU3Ml0gPSB7DQorCQkJLmF0dHJzID0gJnRzbDJYN1hfZGV2X2F0
dHJfZ3JvdXBfdGJsW3RzbDI1NzJdLA0KKwkJCS5ldmVudF9hdHRycyA9ICZ0c2wyWDdYX2V2ZW50
X2F0dHJfZ3JvdXBfdGJsW3RzbDI1NzJdLA0KKwkJCS5kcml2ZXJfbW9kdWxlID0gVEhJU19NT0RV
TEUsDQorCX0sDQorCVt0c2wyNjcyXSA9IHsNCisJCQkuYXR0cnMgPSAmdHNsMlg3WF9kZXZfYXR0
cl9ncm91cF90YmxbdHNsMjY3Ml0sDQorCQkJLmV2ZW50X2F0dHJzID0gJnRzbDJYN1hfZXZlbnRf
YXR0cl9ncm91cF90YmxbdHNsMjY3Ml0sDQorCQkJLmRyaXZlcl9tb2R1bGUgPSBUSElTX01PRFVM
RSwNCisJfSwNCisJW3RtZDI2NzJdID0gew0KKwkJCS5hdHRycyA9ICZ0c2wyWDdYX2Rldl9hdHRy
X2dyb3VwX3RibFt0c2wyNjcyXSwNCisJCQkuZXZlbnRfYXR0cnMgPSAmdHNsMlg3WF9ldmVudF9h
dHRyX2dyb3VwX3RibFt0c2wyNjcyXSwNCisJCQkuZHJpdmVyX21vZHVsZSA9IFRISVNfTU9EVUxF
LA0KKwl9LA0KKwlbdHNsMjc3Ml0gPSB7DQorCQkJLmF0dHJzID0gJnRzbDJYN1hfZGV2X2F0dHJf
Z3JvdXBfdGJsW3RzbDI3NzJdLA0KKwkJCS5ldmVudF9hdHRycyA9ICZ0c2wyWDdYX2V2ZW50X2F0
dHJfZ3JvdXBfdGJsW3RzbDI3NzJdLA0KKwkJCS5kcml2ZXJfbW9kdWxlID0gVEhJU19NT0RVTEUs
DQorCX0sDQorCVt0bWQyNzcyXSA9IHsNCisJCQkuYXR0cnMgPSAmdHNsMlg3WF9kZXZfYXR0cl9n
cm91cF90YmxbdHNsMjc3Ml0sDQorCQkJLmV2ZW50X2F0dHJzID0gJnRzbDJYN1hfZXZlbnRfYXR0
cl9ncm91cF90YmxbdHNsMjc3Ml0sDQorCQkJLmRyaXZlcl9tb2R1bGUgPSBUSElTX01PRFVMRSwN
CisJfSwNCisNCit9Ow0KKw0KKy8qDQorICogUnVuLXRpbWUgaW50ZXJydXB0IGhhbmRsZXIgLSBk
ZXBlbmRpbmcgb24gd2hldGhlciB0aGUgZGV2aWNlIGlzIGluIGFtYmllbnQNCisgKiBsaWdodCBz
ZW5zaW5nIGludGVycnVwdCBtb2RlLCB0aGlzIGhhbmRsZXIgY2FuIHF1ZXVlIHVwDQorICogYSB0
aHJlYWQsIHRvIGhhbmRsZSB2YWxpZCBpbnRlcnJ1cHRzLg0KKyAqLw0KK3N0YXRpYyBpcnFyZXR1
cm5fdCB0c2wyeDd4X2V2ZW50X2hhbmRsZXIoaW50IGlycSwgdm9pZCAqcHJpdmF0ZSkNCit7DQor
CXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPSBwcml2YXRlOw0KKwlzdHJ1Y3QgdHNsMlg3WF9j
aGlwICpjaGlwID0gaWlvX3ByaXYoaW5kaW9fZGV2KTsNCisJczY0IHRpbWVzdGFtcCA9IGlpb19n
ZXRfdGltZV9ucygpOw0KKwlpbnQgcmV0Ow0KKwlpbnQgdmFsdWU7DQorDQorCXZhbHVlID0gaTJj
X3NtYnVzX3JlYWRfYnl0ZV9kYXRhKGNoaXAtPmNsaWVudCwNCisJCVRTTDJYN1hfQ01EX1JFRyB8
IFRTTDJYN1hfU1RBVFVTKTsNCisNCisJLyogV2hhdCB0eXBlIG9mIGludGVycnVwdCBkbyB3ZSBu
ZWVkIHRvIHByb2Nlc3MgKi8NCisJaWYgKHZhbHVlICYgVFNMMlg3WF9TVEFfUFJYX0lOVFIpIHsN
CisJCXRzbDJ4N3hfcHJveF9wb2xsKGluZGlvX2Rldik7DQorCQlpaW9fcHVzaF9ldmVudChpbmRp
b19kZXYsDQorCQkJICAgICAgIElJT19VTk1PRF9FVkVOVF9DT0RFKElJT19QUk9YSU1JVFksDQor
CQkJCQkJICAgIDAsDQorCQkJCQkJICAgIElJT19FVl9UWVBFX1RIUkVTSCwNCisJCQkJCQkgICAg
SUlPX0VWX0RJUl9FSVRIRVIpLA0KKwkJCQkJCSAgICB0aW1lc3RhbXApOw0KKwl9DQorDQorCWlm
ICh2YWx1ZSAmIFRTTDJYN1hfU1RBX0FMU19JTlRSKSB7DQorCQl0c2wyeDd4X2dldF9sdXgoaW5k
aW9fZGV2KTsNCisJCWlpb19wdXNoX2V2ZW50KGluZGlvX2RldiwNCisJCSAgICAgICBJSU9fVU5N
T0RfRVZFTlRfQ09ERShJSU9fTElHSFQsDQorCQkJCQkgICAgMCwNCisJCQkJCSAgICBJSU9fRVZf
VFlQRV9USFJFU0gsDQorCQkJCQkgICAgSUlPX0VWX0RJUl9FSVRIRVIpLA0KKwkJCQkJICAgIHRp
bWVzdGFtcCk7DQorCX0NCisJLyogQ2xlYXIgaW50ZXJydXB0IG5vdyB0aGF0IHdlIGhhdmUgdGhl
IHN0YXR1cyAqLw0KKwlyZXQgPSBpMmNfc21idXNfd3JpdGVfYnl0ZShjaGlwLT5jbGllbnQsDQor
CQlUU0wyWDdYX0NNRF9SRUcgfCBUU0wyWDdYX0NNRF9TUExfRk4gfA0KKwkJVFNMMlg3WF9DTURf
UFJPWEFMU19JTlRDTFIpOw0KKwlpZiAocmV0IDwgMCkNCisJCWRldl9lcnIoJmNoaXAtPmNsaWVu
dC0+ZGV2LA0KKwkJIkZhaWxlZCB0byBjbGVhciBpcnEgZnJvbSBldmVudCBoYW5kbGVyLiBlcnIg
PSAlZFxuIiwgcmV0KTsNCisNCisJcmV0dXJuIElSUV9IQU5ETEVEOw0KK30NCisNCisvKg0KKyAq
IENsaWVudCBwcm9iZSBmdW5jdGlvbi4NCisgKi8NCitzdGF0aWMgaW50IF9fZGV2aW5pdCB0c2wy
eDd4X3Byb2JlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnRwLA0KKwljb25zdCBzdHJ1Y3QgaTJj
X2RldmljZV9pZCAqaWQpDQorew0KKwlpbnQgaSwgcmV0Ow0KKwl1bnNpZ25lZCBjaGFyIGJ1ZltU
U0wyWDdYX01BWF9ERVZJQ0VfUkVHU107DQorCXN0cnVjdCBpaW9fZGV2ICppbmRpb19kZXYgPQ0K
KwkJCWlpb19hbGxvY2F0ZV9kZXZpY2Uoc2l6ZW9mKHN0cnVjdCB0c2wyWDdYX2NoaXApKTsNCisJ
c3RydWN0IHRzbDJYN1hfY2hpcAkqY2hpcCA9IGlpb19wcml2KGluZGlvX2Rldik7DQorDQorCWlm
IChpbmRpb19kZXYgPT0gTlVMTCkgew0KKwkJcmV0ID0gLUVOT01FTTsNCisJCWRldl9lcnIoJmNs
aWVudHAtPmRldiwgImlpbyBhbGxvY2F0aW9uIGZhaWxlZFxuIik7DQorCQlnb3RvIGZhaWwxOw0K
Kwl9DQorDQorCWNoaXAgPSBpaW9fcHJpdihpbmRpb19kZXYpOw0KKwljaGlwLT5jbGllbnQgPSBj
bGllbnRwOw0KKwlpMmNfc2V0X2NsaWVudGRhdGEoY2xpZW50cCwgaW5kaW9fZGV2KTsNCisNCisJ
bXV0ZXhfaW5pdCgmY2hpcC0+YWxzX211dGV4KTsNCisJbXV0ZXhfaW5pdCgmY2hpcC0+cHJveF9t
dXRleCk7DQorDQorCWNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPSBUU0wyWDdYX0NISVBfVU5L
Tk9XTjsNCisNCisJY2hpcC0+cGRhdGEgPSBjbGllbnRwLT5kZXYucGxhdGZvcm1fZGF0YTsNCisJ
aWYgKGNoaXAtPnBkYXRhICYmIGNoaXAtPnBkYXRhLT5pbml0KQ0KKwkJCWNoaXAtPnBkYXRhLT5p
bml0KGNsaWVudHApOw0KKw0KKwlmb3IgKGkgPSAwOyBpIDwgVFNMMlg3WF9NQVhfREVWSUNFX1JF
R1M7IGkrKykgew0KKwkJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUoY2xpZW50cCwNCisJCQkJ
KFRTTDJYN1hfQ01EX1JFRyB8IChUU0wyWDdYX0NOVFJMICsgaSkpKTsNCisJCWlmIChyZXQgPCAw
KSB7DQorCQkJZGV2X2VycigmY2xpZW50cC0+ZGV2LCAiaTJjX3NtYnVzX3dyaXRlX2J5dGVzKCkg
dG8gY21kICINCisJCQkicmVnIGZhaWxlZCBpbiB0c2wyeDd4X3Byb2JlKCksIGVyciA9ICVkXG4i
LCByZXQpOw0KKwkJCWdvdG8gZmFpbDE7DQorCQl9DQorCQlyZXQgPSBpMmNfc21idXNfcmVhZF9i
eXRlKGNsaWVudHApOw0KKwkJaWYgKHJldCA8IDApIHsNCisJCQlkZXZfZXJyKCZjbGllbnRwLT5k
ZXYsICJpMmNfc21idXNfcmVhZF9ieXRlIGZyb20gIg0KKwkJCSJyZWcgZmFpbGVkIGluIHRzbDJ4
N3hfcHJvYmUoKSwgZXJyID0gJWRcbiIsIHJldCk7DQorDQorCQkJZ290byBmYWlsMTsNCisJCX0N
CisJCWJ1ZltpXSA9IHJldDsNCisJfQ0KKw0KKwlpZiAoKCF0c2wyeDd4X2RldmljZV9pZChidWYs
IGlkLT5kcml2ZXJfZGF0YSkpIHx8DQorCQkJKHRzbDJ4N3hfZGV2aWNlX2lkKGJ1ZiwgaWQtPmRy
aXZlcl9kYXRhKSA9PSAtRUlOVkFMKSkgew0KKwkJZGV2X2luZm8oJmNoaXAtPmNsaWVudC0+ZGV2
LCAiaTJjIGRldmljZSBmb3VuZCBidXQgZG9lcyBub3QgbWF0Y2ggIg0KKwkJCSJleHBlY3RlZCBp
ZCBpbiB0c2wyeDd4X3Byb2JlKClcbiIpOw0KKwkJZ290byBmYWlsMTsNCisJfQ0KKw0KKwljaGlw
LT5pZCA9IGlkLT5kcml2ZXJfZGF0YTsNCisNCisJcmV0ID0gaTJjX3NtYnVzX3dyaXRlX2J5dGUo
Y2xpZW50cCwgKFRTTDJYN1hfQ01EX1JFRyB8IFRTTDJYN1hfQ05UUkwpKTsNCisJaWYgKHJldCA8
IDApIHsNCisJCWRldl9lcnIoJmNsaWVudHAtPmRldiwgImkyY19zbWJ1c193cml0ZV9ieXRlKCkg
dG8gY21kIHJlZyAiDQorCQkJImZhaWxlZCBpbiB0c2wyeDd4X3Byb2JlKCksIGVyciA9ICVkXG4i
LCByZXQpOw0KKwkJZ290byBmYWlsMTsNCisJfQ0KKw0KKwlpbmRpb19kZXYtPmluZm8gPSAmdHNs
Mlg3WF9pbmZvW2lkLT5kcml2ZXJfZGF0YV07DQorCWluZGlvX2Rldi0+ZGV2LnBhcmVudCA9ICZj
bGllbnRwLT5kZXY7DQorCWluZGlvX2Rldi0+bW9kZXMgPSBJTkRJT19ESVJFQ1RfTU9ERTsNCisJ
aW5kaW9fZGV2LT5uYW1lID0gY2hpcC0+Y2xpZW50LT5uYW1lOw0KKwlpbmRpb19kZXYtPmNoYW5u
ZWxzID0gdHNsMlg3WF9jaGFubmVsczsNCisJaW5kaW9fZGV2LT5udW1fY2hhbm5lbHMgPSBBUlJB
WV9TSVpFKHRzbDJYN1hfY2hhbm5lbHMpOw0KKw0KKwlyZXQgPSBpaW9fZGV2aWNlX3JlZ2lzdGVy
KGluZGlvX2Rldik7DQorCWlmIChyZXQpIHsNCisJCWRldl9lcnIoJmNsaWVudHAtPmRldiwgImlp
byByZWdpc3RyYXRpb24gZmFpbGVkXG4iKTsNCisJCWdvdG8gZmFpbDI7DQorCX0NCisNCisJaWYg
KGNsaWVudHAtPmlycSkgew0KKwkJcmV0ID0gcmVxdWVzdF90aHJlYWRlZF9pcnEoY2xpZW50cC0+
aXJxLA0KKwkJCQkJICAgTlVMTCwNCisJCQkJCSAgICZ0c2wyeDd4X2V2ZW50X2hhbmRsZXIsDQor
CQkJCQkgICBJUlFGX1RSSUdHRVJfUklTSU5HIHwgSVJRRl9PTkVTSE9ULA0KKwkJCQkJICAgIlRT
TDJYN1hfZXZlbnQiLA0KKwkJCQkJICAgaW5kaW9fZGV2KTsNCisJCWlmIChyZXQpDQorCQkJZGV2
X2VycigmY2xpZW50cC0+ZGV2LCAiaXJxIHJlcXVlc3QgZmFpbGVkIik7DQorCX0NCisNCisJLyog
TG9hZCB1cCB0aGUgZGVmYXVsdHMgKi8NCisJdHNsMng3eF9kZWZhdWx0cyhjaGlwKTsNCisNCisJ
LyogTWFrZSBzdXJlIHRoZSBjaGlwIGlzIG9uICovDQorCXRzbDJ4N3hfY2hpcF9vbihpbmRpb19k
ZXYpOw0KKw0KKwlkZXZfaW5mbygmY2xpZW50cC0+ZGV2LCAiJXMgTGlnaHQgc2Vuc29yIGZvdW5k
LlxuIiwgaWQtPm5hbWUpOw0KKw0KKwlyZXR1cm4gMDsNCisNCitmYWlsMToNCisJaWYgKGNoaXAt
PnBkYXRhICYmIGNoaXAtPnBkYXRhLT50ZWFyZG93bikNCisJCWNoaXAtPnBkYXRhLT50ZWFyZG93
bihjbGllbnRwKTsNCisJaWlvX2ZyZWVfZGV2aWNlKGluZGlvX2Rldik7DQorDQorZmFpbDI6DQor
CXJldHVybiByZXQ7DQorDQorfQ0KKw0KKw0KK3N0YXRpYyBpbnQgdHNsMng3eF9zdXNwZW5kKHN0
cnVjdCBkZXZpY2UgKmRldikNCit7DQorCXN0cnVjdCBpaW9fZGV2ICpkZXZfaW5mbyA9IGRldl9n
ZXRfZHJ2ZGF0YShkZXYpOw0KKwlzdHJ1Y3QgdHNsMlg3WF9jaGlwICpjaGlwID0gaWlvX3ByaXYo
ZGV2X2luZm8pOw0KKw0KKwlpbnQgcmV0ID0gMDsNCisNCisJLyogQmxvY2tzIGlmIHdvcmsgaXMg
YWN0aXZlICovDQorCWNhbmNlbF93b3JrX3N5bmMoJmNoaXAtPndvcmtfdGhyZXNoKTsNCisNCisJ
aWYgKGNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPT0gVFNMMlg3WF9DSElQX1dPUktJTkcpIHsN
CisJCXJldCA9IHRzbDJ4N3hfY2hpcF9vZmYoZGV2X2luZm8pOw0KKwkJY2hpcC0+dHNsMng3eF9j
aGlwX3N0YXR1cyA9IFRTTDJYN1hfQ0hJUF9TVVNQRU5ERUQ7DQorCX0NCisNCisJaWYgKGNoaXAt
PnBkYXRhICYmIGNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9wb3dlcikgew0KKwkJcG1fbWVzc2FnZV90
IHBtbSA9IHtQTV9FVkVOVF9TVVNQRU5EfTsNCisJCWNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9wb3dl
cihkZXYsIHBtbSk7DQorCX0NCisNCisJcmV0dXJuIHJldDsNCit9DQorDQorc3RhdGljIGludCB0
c2wyeDd4X3Jlc3VtZShzdHJ1Y3QgZGV2aWNlICpkZXYpDQorew0KKwlzdHJ1Y3QgaWlvX2RldiAq
ZGV2X2luZm8gPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCisJc3RydWN0IHRzbDJYN1hfY2hpcCAq
Y2hpcCA9IGlpb19wcml2KGRldl9pbmZvKTsNCisJaW50IHJldCA9IDA7DQorDQorCWlmIChjaGlw
LT5wZGF0YSAmJiBjaGlwLT5wZGF0YS0+cGxhdGZvcm1fcG93ZXIpIHsNCisJCXBtX21lc3NhZ2Vf
dCBwbW0gPSB7UE1fRVZFTlRfUkVTVU1FfTsNCisJCWNoaXAtPnBkYXRhLT5wbGF0Zm9ybV9wb3dl
cihkZXYsIHBtbSk7DQorCX0NCisNCisJaWYgKGNoaXAtPnRzbDJ4N3hfY2hpcF9zdGF0dXMgPT0g
VFNMMlg3WF9DSElQX1NVU1BFTkRFRCkNCisJCXJldCA9IHRzbDJ4N3hfY2hpcF9vbihkZXZfaW5m
byk7DQorDQorCXJldHVybiByZXQ7DQorfQ0KKw0KK3N0YXRpYyBpbnQgX19kZXZleGl0IHRzbDJ4
N3hfcmVtb3ZlKHN0cnVjdCBpMmNfY2xpZW50ICpjbGllbnQpDQorew0KKwlzdHJ1Y3QgaWlvX2Rl
diAqaW5kaW9fZGV2ID0gaTJjX2dldF9jbGllbnRkYXRhKGNsaWVudCk7DQorCXN0cnVjdCB0c2wy
WDdYX2NoaXAgKmNoaXAgPSBpMmNfZ2V0X2NsaWVudGRhdGEoY2xpZW50KTsNCisNCisJdHNsMng3
eF9jaGlwX29mZihpbmRpb19kZXYpOw0KKw0KKwlpZiAoY2xpZW50LT5pcnEpDQorCQlmcmVlX2ly
cShjbGllbnQtPmlycSwgY2hpcC0+Y2xpZW50LT5uYW1lKTsNCisNCisJZmx1c2hfc2NoZWR1bGVk
X3dvcmsoKTsNCisNCisJaWlvX2RldmljZV91bnJlZ2lzdGVyKGNoaXAtPmlpb19kZXYpOw0KKwlr
ZnJlZShjaGlwKTsNCisNCisJaWYgKGNoaXAtPnBkYXRhICYmIGNoaXAtPnBkYXRhLT50ZWFyZG93
bikNCisJCWNoaXAtPnBkYXRhLT50ZWFyZG93bihjbGllbnQpOw0KKw0KKwlyZXR1cm4gMDsNCit9
DQorDQorc3RhdGljIHN0cnVjdCBpMmNfZGV2aWNlX2lkIHRzbDJ4N3hfaWR0YWJsZVtdID0gew0K
Kwl7ICJ0c2wyNTcxIiwgdHNsMjU3MSB9LA0KKwl7ICJ0c2wyNjcxIiwgdHNsMjY3MSB9LA0KKwl7
ICJ0bWQyNjcxIiwgdG1kMjY3MSB9LA0KKwl7ICJ0c2wyNzcxIiwgdHNsMjc3MSB9LA0KKwl7ICJ0
bWQyNzcxIiwgdG1kMjc3MSB9LA0KKwl7ICJ0c2wyNTcyIiwgdHNsMjU3MiB9LA0KKwl7ICJ0c2wy
NjcyIiwgdHNsMjY3MiB9LA0KKwl7ICJ0bWQyNjcyIiwgdG1kMjY3MiB9LA0KKwl7ICJ0c2wyNzcy
IiwgdHNsMjc3MiB9LA0KKwl7ICJ0bWQyNzcyIiwgdG1kMjc3MiB9LA0KKwl7fQ0KK307DQorDQor
TU9EVUxFX0RFVklDRV9UQUJMRShpMmMsIHRzbDJ4N3hfaWR0YWJsZSk7DQorDQorc3RhdGljIGNv
bnN0IHN0cnVjdCBkZXZfcG1fb3BzIHRzbDJ4N3hfcG1fb3BzID0gew0KKwkuc3VzcGVuZCA9IHRz
bDJ4N3hfc3VzcGVuZCwNCisJLnJlc3VtZSAgPSB0c2wyeDd4X3Jlc3VtZSwNCit9Ow0KKw0KKy8q
IERyaXZlciBkZWZpbml0aW9uICovDQorc3RhdGljIHN0cnVjdCBpMmNfZHJpdmVyIHRzbDJ4N3hf
ZHJpdmVyID0gew0KKwkuZHJpdmVyID0gew0KKwkJLm5hbWUgPSAidHNsMlg3WCIsDQorCQkucG0g
PSAmdHNsMng3eF9wbV9vcHMsDQorDQorCX0sDQorCS5pZF90YWJsZSA9IHRzbDJ4N3hfaWR0YWJs
ZSwNCisJLnByb2JlID0gdHNsMng3eF9wcm9iZSwNCisJLnJlbW92ZSA9IF9fZGV2ZXhpdF9wKHRz
bDJ4N3hfcmVtb3ZlKSwNCit9Ow0KKw0KK3N0YXRpYyBpbnQgX19pbml0IHRzbDJ4N3hfaW5pdCh2
b2lkKQ0KK3sNCisJcmV0dXJuIGkyY19hZGRfZHJpdmVyKCZ0c2wyeDd4X2RyaXZlcik7DQorfQ0K
Kw0KK3N0YXRpYyB2b2lkIF9fZXhpdCB0c2wyeDd4X2V4aXQodm9pZCkNCit7DQorCWkyY19kZWxf
ZHJpdmVyKCZ0c2wyeDd4X2RyaXZlcik7DQorfQ0KKw0KK21vZHVsZV9pbml0KHRzbDJ4N3hfaW5p
dCk7DQorbW9kdWxlX2V4aXQodHNsMng3eF9leGl0KTsNCisNCitNT0RVTEVfQVVUSE9SKCJKLiBB
dWd1c3QgQnJlbm5lcjxqYnJlbm5lckB0YW9zaW5jLmNvbT4iKTsNCitNT0RVTEVfREVTQ1JJUFRJ
T04oIlRBT1MgdHNsMlg3WCBhbWJpZW50IGxpZ2h0IHNlbnNvciBkcml2ZXIiKTsNCitNT0RVTEVf
TElDRU5TRSgiR1BMIik7DQpkaWZmIC0tZ2l0IGEvZHJpdmVycy9zdGFnaW5nL2lpby9saWdodC90
c2wyeDd4X2NvcmUuaCBiL2RyaXZlcnMvc3RhZ2luZy9paW8vbGlnaHQvdHNsMng3eF9jb3JlLmgN
Cm5ldyBmaWxlIG1vZGUgMTAwNjQ0DQppbmRleCAwMDAwMDAwLi45YTY0NDEyDQotLS0gL2Rldi9u
dWxsDQorKysgYi9kcml2ZXJzL3N0YWdpbmcvaWlvL2xpZ2h0L3RzbDJ4N3hfY29yZS5oDQpAQCAt
MCwwICsxLDEzOCBAQA0KKy8qDQorICogRGV2aWNlIGRyaXZlciBmb3IgbW9uaXRvcmluZyBhbWJp
ZW50IGxpZ2h0IGludGVuc2l0eSAobHV4KQ0KKyAqIGFuZCBwcm94aW1pdHkgKHByb3gpIHdpdGhp
biB0aGUgVEFPUyBUU0wyWDdYIGZhbWlseSBvZiBkZXZpY2VzLg0KKyAqDQorICogQ29weXJpZ2h0
IChjKSAyMDEyLCBUQU9TIENvcnBvcmF0aW9uLg0KKyAqDQorICogVGhpcyBwcm9ncmFtIGlzIGZy
ZWUgc29mdHdhcmU7IHlvdSBjYW4gcmVkaXN0cmlidXRlIGl0IGFuZC9vciBtb2RpZnkNCisgKiBp
dCB1bmRlciB0aGUgdGVybXMgb2YgdGhlIEdOVSBHZW5lcmFsIFB1YmxpYyBMaWNlbnNlIGFzIHB1
Ymxpc2hlZCBieQ0KKyAqIHRoZSBGcmVlIFNvZnR3YXJlIEZvdW5kYXRpb247IGVpdGhlciB2ZXJz
aW9uIDIgb2YgdGhlIExpY2Vuc2UsIG9yDQorICogKGF0IHlvdXIgb3B0aW9uKSBhbnkgbGF0ZXIg
dmVyc2lvbi4NCisgKg0KKyAqIFRoaXMgcHJvZ3JhbSBpcyBkaXN0cmlidXRlZCBpbiB0aGUgaG9w
ZSB0aGF0IGl0IHdpbGwgYmUgdXNlZnVsLCBidXQgV0lUSE9VVA0KKyAqIEFOWSBXQVJSQU5UWTsg
d2l0aG91dCBldmVuIHRoZSBpbXBsaWVkIHdhcnJhbnR5IG9mIE1FUkNIQU5UQUJJTElUWSBvcg0K
KyAqIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFLiAgU2VlIHRoZSBHTlUgR2VuZXJh
bCBQdWJsaWMgTGljZW5zZSBmb3INCisgKiBtb3JlIGRldGFpbHMuDQorICoNCisgKiBZb3Ugc2hv
dWxkIGhhdmUgcmVjZWl2ZWQgYSBjb3B5IG9mIHRoZSBHTlUgR2VuZXJhbCBQdWJsaWMgTGljZW5z
ZSBhbG9uZw0KKyAqIHdpdGggdGhpcyBwcm9ncmFtOyBpZiBub3QsIHdyaXRlIHRvIHRoZSBGcmVl
IFNvZnR3YXJlIEZvdW5kYXRpb24sIEluYy4sDQorICogNTEgRnJhbmtsaW4gU3RyZWV0LCBGaWZ0
aCBGbG9vciwgQm9zdG9uLCBNQQkwMjExMC0xMzAxLCBVU0EuDQorICovDQorDQorI2lmbmRlZiBf
X1RTTDJYN1hfSA0KKyNkZWZpbmUgX19UU0wyWDdYX0gNCisjaW5jbHVkZSA8bGludXgvcG0uaD4N
CisNCisjaW5jbHVkZSAiLi4vaWlvLmgiDQorDQorLyogVEFPUyBSZWdpc3RlciBkZWZpbml0aW9u
cyAtIG5vdGU6DQorICogZGVwZW5kaW5nIG9uIGRldmljZSwgc29tZSBvZiB0aGVzZSByZWdpc3Rl
ciBhcmUgbm90IHVzZWQgYW5kIHRoZQ0KKyAqIHJlZ2lzdGVyIGFkZHJlc3MgaXMgYmVuaWduLg0K
KyAqLw0KKy8qIDJYN1ggcmVnaXN0ZXIgb2Zmc2V0cyAqLw0KKyNkZWZpbmUgVFNMMlg3WF9NQVhf
REVWSUNFX1JFR1MgMzINCisjZGVmaW5lIFRTTDJYN1hfUkVHX01BWCAgICAgICAgIDE2DQorDQor
LyogRGV2aWNlIFJlZ2lzdGVycyBhbmQgTWFza3MgKi8NCisjZGVmaW5lIFRTTDJYN1hfQ05UUkwg
ICAgICAgICAgICAgICAgICAweDAwDQorI2RlZmluZSBUU0wyWDdYX0FMU19USU1FICAgICAgICAg
ICAgICAgMFgwMQ0KKyNkZWZpbmUgVFNMMlg3WF9QUlhfVElNRSAgICAgICAgICAgICAgIDB4MDIN
CisjZGVmaW5lIFRTTDJYN1hfV0FJVF9USU1FICAgICAgICAgICAgICAweDAzDQorI2RlZmluZSBU
U0wyWDdYX0FMU19NSU5USFJFU0hMTyAgICAgICAgMFgwNA0KKyNkZWZpbmUgVFNMMlg3WF9BTFNf
TUlOVEhSRVNISEkgICAgICAgIDBYMDUNCisjZGVmaW5lIFRTTDJYN1hfQUxTX01BWFRIUkVTSExP
ICAgICAgICAwWDA2DQorI2RlZmluZSBUU0wyWDdYX0FMU19NQVhUSFJFU0hISSAgICAgICAgMFgw
Nw0KKyNkZWZpbmUgVFNMMlg3WF9QUlhfTUlOVEhSRVNITE8gICAgICAgIDBYMDgNCisjZGVmaW5l
IFRTTDJYN1hfUFJYX01JTlRIUkVTSEhJICAgICAgICAwWDA5DQorI2RlZmluZSBUU0wyWDdYX1BS
WF9NQVhUSFJFU0hMTyAgICAgICAgMFgwQQ0KKyNkZWZpbmUgVFNMMlg3WF9QUlhfTUFYVEhSRVNI
SEkgICAgICAgIDBYMEINCisjZGVmaW5lIFRTTDJYN1hfUEVSU0lTVEVOQ0UgICAgICAgICAgICAw
eDBDDQorI2RlZmluZSBUU0wyWDdYX1BSWF9DT05GSUcgICAgICAgICAgICAgMHgwRA0KKyNkZWZp
bmUgVFNMMlg3WF9QUlhfQ09VTlQgICAgICAgICAgICAgIDB4MEUNCisjZGVmaW5lIFRTTDJYN1hf
R0FJTiAgICAgICAgICAgICAgICAgICAweDBGDQorI2RlZmluZSBUU0wyWDdYX05PVFVTRUQgICAg
ICAgICAgICAgICAgMHgxMA0KKyNkZWZpbmUgVFNMMlg3WF9SRVZJRCAgICAgICAgICAgICAgICAg
IDB4MTENCisjZGVmaW5lIFRTTDJYN1hfQ0hJUElEICAgICAgICAgICAgICAgICAweDEyDQorI2Rl
ZmluZSBUU0wyWDdYX1NUQVRVUyAgICAgICAgICAgICAgICAgMHgxMw0KKyNkZWZpbmUgVFNMMlg3
WF9BTFNfQ0hBTjBMTyAgICAgICAgICAgIDB4MTQNCisjZGVmaW5lIFRTTDJYN1hfQUxTX0NIQU4w
SEkgICAgICAgICAgICAweDE1DQorI2RlZmluZSBUU0wyWDdYX0FMU19DSEFOMUxPICAgICAgICAg
ICAgMHgxNg0KKyNkZWZpbmUgVFNMMlg3WF9BTFNfQ0hBTjFISSAgICAgICAgICAgIDB4MTcNCisj
ZGVmaW5lIFRTTDJYN1hfUFJYX0xPICAgICAgICAgICAgICAgICAweDE4DQorI2RlZmluZSBUU0wy
WDdYX1BSWF9ISSAgICAgICAgICAgICAgICAgMHgxOQ0KKw0KKy8qIHRzbDJYN1ggY21kIHJlZyBt
YXNrcyAqLw0KKyNkZWZpbmUgVFNMMlg3WF9DTURfUkVHICAgICAgICAgICAgICAgIDB4ODANCisj
ZGVmaW5lIFRTTDJYN1hfQ01EX1NQTF9GTiAgICAgICAgICAgICAweDYwDQorDQorI2RlZmluZSBU
U0wyWDdYX0NNRF9QUk9YX0lOVF9DTFIgICAgICAgMFgwNQ0KKyNkZWZpbmUgVFNMMlg3WF9DTURf
QUxTX0lOVF9DTFIgICAgICAgIDB4MDYNCisjZGVmaW5lIENNRF9QUk9YQUxTX0lOVF9DTFIgICAg
ICAgICAgICAwWDA3DQorDQorLyogdHNsMlg3WCBjbnRybCByZWcgbWFza3MgKi8NCisjZGVmaW5l
IFRTTDJYN1hfQ05UTF9BRENfRU5CTCAgICAgICAgICAweDAyDQorI2RlZmluZSBUU0wyWDdYX0NO
VExfUFdSX09OICAgICAgICAgICAgMHgwMQ0KKw0KKy8qIHRzbDJYN1ggc3RhdHVzIHJlZyBtYXNr
cyAqLw0KKyNkZWZpbmUgVFNMMlg3WF9TVEFfQURDX1ZBTElEICAgICAgICAgIDB4MDENCisjZGVm
aW5lIFRTTDJYN1hfU1RBX1BSWF9WQUxJRCAgICAgICAgICAweDAyDQorI2RlZmluZSBUU0wyWDdY
X1NUQV9BRENfUFJYX1ZBTElEICAgICAgMHgwMw0KKyNkZWZpbmUgVFNMMlg3WF9TVEFfQUxTX0lO
VFIgICAgICAgICAgIDB4MTANCisjZGVmaW5lIFRTTDJYN1hfU1RBX0FEQ19JTlRSICAgICAgICAg
ICAweDEwDQorI2RlZmluZSBUU0wyWDdYX1NUQV9QUlhfSU5UUiAgICAgICAgICAgMHgyMA0KKw0K
KyNkZWZpbmUgVFNMMlg3WF9TVEFfQURDX0lOVFIgICAgICAgICAgIDB4MTANCisNCisvKiB0c2wy
WDdYIGNudHJsIHJlZyBtYXNrcyAqLw0KKyNkZWZpbmUgQ05UTF9SRUdfQ0xFQVIgICAgICAgICAg
ICAgICAgIDB4MDANCisjZGVmaW5lIENOVExfUFJPWF9JTlRfRU5CTCAgICAgICAgICAgICAwWDIw
DQorI2RlZmluZSBDTlRMX0FMU19JTlRfRU5CTCAgICAgICAgICAgICAgMFgxMA0KKyNkZWZpbmUg
VFNMMlg3WF9DTlRMX1dBSVRfVE1SX0VOQkwgICAgIDBYMDgNCisjZGVmaW5lIENOVExfUFJPWF9E
RVRfRU5CTCAgICAgICAgICAgICAwWDA0DQorI2RlZmluZSBDTlRMX0FEQ19FTkJMICAgICAgICAg
ICAgICAgICAgMHgwMg0KKyNkZWZpbmUgVFNMMlg3WF9DTlRMX1BXUk9OICAgICAgICAgICAgIDB4
MDENCisjZGVmaW5lIENOVExfQUxTUE9OX0VOQkwgICAgICAgICAgICAgICAweDAzDQorI2RlZmlu
ZSBDTlRMX0lOVEFMU1BPTl9FTkJMICAgICAgICAgICAgMHgxMw0KKyNkZWZpbmUgQ05UTF9QUk9Y
UE9OX0VOQkwgICAgICAgICAgICAgIDB4MEYNCisjZGVmaW5lIENOVExfSU5UUFJPWFBPTl9FTkJM
ICAgICAgICAgICAweDJGDQorI2RlZmluZSBUU0wyWDdYX0NNRF9QUk9YQUxTX0lOVENMUiAgICAg
MFgwNw0KKw0KKy8qUHJveCBkaW9kZSB0byB1c2UgKi8NCisjZGVmaW5lIERJT0RFMCAgICAgICAg
ICAgICAgICAgICAgICAgICAweDEwDQorI2RlZmluZSBESU9ERTEgICAgICAgICAgICAgICAgICAg
ICAgICAgMHgyMA0KKyNkZWZpbmUgRElPREVfQk9USCAgICAgICAgICAgICAgICAgICAgIDB4MzAN
CisNCisvKiBMRUQgUG93ZXIgKi8NCisjZGVmaW5lIG1BMTAwICAgICAgICAgICAgICAgICAgICAg
ICAgICAweDAwDQorI2RlZmluZSBtQTUwICAgICAgICAgICAgICAgICAgICAgICAgICAgMHg0MA0K
KyNkZWZpbmUgbUEyNSAgICAgICAgICAgICAgICAgICAgICAgICAgIDB4ODANCisjZGVmaW5lIG1B
MTMgICAgICAgICAgICAgICAgICAgICAgICAgICAweEQwDQorDQorLyogTWF4IG51bWJlciBvZiBz
ZWdtZW50cyBhbGxvd2FibGUgaW4gTFVYIHRhYmxlICovDQorI2RlZmluZSBNQVhfVEFCTEVfU0la
RQkJOQ0KKyNkZWZpbmUgTUFYX0RFRkFVTFRfVEFCTEVfQllURVMgKHNpemVvZihpbnQpICogTUFY
X1RBQkxFX1NJWkUpDQorDQorc3RydWN0IHRzbDJ4N3hfbHV4IHsNCisJdW5zaWduZWQgaW50IHJh
dGlvOw0KKwl1bnNpZ25lZCBpbnQgY2gwOw0KKwl1bnNpZ25lZCBpbnQgY2gxOw0KK307DQorDQor
DQorLyogc3RydWN0IHRzbDJ4N3hfcGxhdGZvcm1fZGF0YSAtIEFzc3VtZSBhbGwgdmFyaWVudHMg
d2lsbCBoYXZlIHRoaXMgKi8NCitzdHJ1Y3QgdHNsMlg3WF9wbGF0Zm9ybV9kYXRhIHsNCisJaW50
ICgqcGxhdGZvcm1fcG93ZXIpKHN0cnVjdCBkZXZpY2UgKmRldiwgcG1fbWVzc2FnZV90KTsNCisJ
LyogVGhlIGZvbGxvd2luZyBjYWxsYmFjayBnZXRzIGNhbGxlZCB3aGVuIHRoZSBUU0wyNzcyIGlz
IHBvd2VyZWQgb24gKi8NCisJaW50ICgqcG93ZXJfb24pICAgICAgKHN0cnVjdCBpaW9fZGV2ICpp
bmRpb19kZXYpOw0KKwkvKiBUaGUgZm9sbG93aW5nIGNhbGxiYWNrIGdldHMgY2FsbGVkIHdoZW4g
dGhlIFRTTDI3NzIgaXMgcG93ZXJlZCBvZmYgKi8NCisJaW50ICgqcG93ZXJfb2ZmKSAgICAgKHN0
cnVjdCBpMmNfY2xpZW50ICpkZXYpOw0KKwkvKiBUaGUgZm9sbG93aW5nIGNhbGxiYWNrIGdldHMg
Y2FsbGVkIHdoZW4gdGhlIGRyaXZlciBpcyBhZGRlZCAqLw0KKwlpbnQgKCppbml0KSAgICAgICAg
ICAoc3RydWN0IGkyY19jbGllbnQgKmRldik7DQorCS8qIFRoZSBmb2xsb3dpbmcgY2FsbGJhY2sg
Z2V0cyBjYWxsZWQgd2hlbiB0aGUgZHJpdmVyIGlzIHJlbW92ZWQgKi8NCisJaW50ICgqdGVhcmRv
d24pICAgICAgKHN0cnVjdCBpMmNfY2xpZW50ICpkZXYpOw0KKwkvKiBUaGVzZSBhcmUgdGhlIGRl
dmljZSBzcGVjaWZpYyBnbGFzcyBjb2VmZmljZW50cyB1c2VkIHRvDQorCSAqIGNhbGN1bGF0ZSBM
dXggKi8NCisJc3RydWN0IHRzbDJ4N3hfbHV4IHBsYXRmb3JtX2x1eF90YWJsZVtNQVhfVEFCTEVf
U0laRV07DQorfTsNCisNCisjZW5kaWYgLyogX19UU0wyWDdYX0ggKi8NCi0tDQoxLjcuNC4xDQoN
Cg==

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

* Re: [PATCH V1] TAOS tsl2x7x
  2012-02-10 19:06 ` Jon Brenner
  (?)
@ 2012-02-13 15:19 ` Jonathan Cameron
  2012-02-13 15:28   ` Jonathan Cameron
  -1 siblings, 1 reply; 4+ messages in thread
From: Jonathan Cameron @ 2012-02-13 15:19 UTC (permalink / raw)
  To: Jon Brenner; +Cc: Jonathan Cameron, linux-kernel, linux-iio

On 2/10/2012 7:06 PM, Jon Brenner wrote:
> TAOS device driver for tsl/tmd 2771 and 2772 device families (inc all variants).
>
> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
Hi Jon,

Good to see another driver from you. Reasonably keen but couple of 
medium sized issues
a) Use iio_chan_spec and callbacks in iio_info structure where 
possible.  Propose additional
elements where they make sense (such as your integration time).
b) Documentation should be slotted in relevant places in more general files.
c) I'm unconvinced the calibration stuff should be in kernel.

Thanks,

Jonathan
>
> ---
>   .../iio/Documentation/sysfs-bus-iio-light-tsl2x7x  |   71 +
>   drivers/staging/iio/light/Kconfig                  |   11 +
>   drivers/staging/iio/light/Makefile                 |    1 +
>   drivers/staging/iio/light/tsl2x7x_core.c           | 2053 ++++++++++++++++++++
>   drivers/staging/iio/light/tsl2x7x_core.h           |  138 ++
>   5 files changed, 2274 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
> new file mode 100644
> index 0000000..f1bb5f5
> --- /dev/null
> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
> @@ -0,0 +1,71 @@
Most of these are fairly general purpose so should be sysfs-bus-iio or 
sysfs-bus-iio-light.
Some are also shared with the tsl2583 doc so maybe move those into the 
light one.

Generally if the last bit (after the channel type) is restricted to the 
actual device then
it wants to be in this file.
If it's restricted to the class (light / proximity) then it wants to be
in sysfs-bus-iio-light or sysfs-bus-iio-proximity
If like proximity_raw or the thresh_value ones then it wants to be
in sysfs-bus-iio.

Please do fix any cases where this isn't currently the case.  Our 
documentation outside the top
level file is far from well written or organized.

> +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_thresh_falling_value
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Low (falling) trigger point for the internal ALS comparison
> +		function for interrupt generation.
This one just needs adding to sysfs-bus-iio.
> +
> +What:		/sys/bus/iio/devices/device[n]/illuminance0_thresh_rising_value
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		high (rising) trigger point for the internal ALS comparison
> +		function for interrupt generation.
> +
> +What:		/sys/bus/iio/devices/device[n]/illuminance0_both_raw
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Simultainious ALS channel 1 and channel 2 data represented as
> +		a 32 bit integer.
err. previously the both has referred to the light frequencies covered. 
Please don't do what you
describe here. If you really need to be sure they are pared then you 
want to use the buffered
infrastructure not sysfs.

> +
> +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.
> +
> +What:		/sys/bus/iio/devices/device[n]/proximity_thresh_falling_value
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Low (falling) trigger point for the internal proximity comparison
> +		function for interrupt generation.
> +
Again, standard form so add to sysfs-bus-iio.
> +What:		/sys/bus/iio/devices/device[n]/proximity_thresh_rising_value
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		high (rising) trigger point for the internal proximity comparison
> +		function for interrupt generation.
> +
> +What:		/sys/bus/iio/devices/device[n]/proximity_calibscale
> +KernelVersion:	3.3-rc1
> +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).
> +
All of these are standard so add to sysfs-bus-iio lists for calibscale
> +What:		/sys/bus/iio/devices/device[n]/proximity_raw
> +KernelVersion:	3.3-rc1
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		State of proximity detection based on the
> +		proximity_thresh_rising_value.
If you wouldn't mind can you lift he proximity documentation out of 
sysfs-bus-iio-ilght into sysfs-bus-iio
then we can take all the standard definitions without adding them yet 
again. (as a separate patch).

I'm still kind of regretting ever introducing multiple sysfs 
documentatino files.
> +
> diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig
> index e7e9159..29de333 100644
> --- a/drivers/staging/iio/light/Kconfig
> +++ b/drivers/staging/iio/light/Kconfig
> @@ -31,4 +31,15 @@ config TSL2583
>   	 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
>   	 Access ALS data via iio, sysfs.
>
> +	 This driver can also be built as a module.  If so, the module
> +	 will be called tsl2583.
errr.  This change to a comment about a completely different driver 
should not be in this patch.
> +
> +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..671f476
> --- /dev/null
> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
> @@ -0,0 +1,2053 @@
> +/*
> + * 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/string.h>
> +#include<linux/mutex.h>
> +#include<linux/unistd.h>
> +#include<linux/interrupt.h>
> +#include<linux/platform_device.h>
> +#include<linux/input.h>
> +#include<linux/slab.h>
> +#include<linux/pm.h>
> +#include<linux/module.h>
> +#include<linux/version.h>
> +
> +#include<linux/cdev.h>
> +#include<linux/stat.h>
> +#include<linux/module.h>
> +#include "tsl2x7x_core.h"
> +#include "../events.h"
> +#include "../buffer.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 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_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;/* for calibration mode*/
> +
> +};
> +
> +struct tsl2X7X_chip_info {
> +	struct iio_chan_spec	channel[0];
> +	const struct iio_info	*info;
> +};
err. what is channel[0] for?  Please do the iio_chan_spec stuff fully.
> +
> +struct tsl2X7X_chip {
> +	kernel_ulong_t id;
> +	struct mutex prox_mutex;
> +	struct mutex als_mutex;
> +	struct i2c_client *client;
> +	struct iio_dev *iio_dev;
> +	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_REG_MAX];
> +	bool init_done;
> +	struct work_struct work_thresh;
> +	s64 event_timestamp;
> +	/* This structure is intentionally large to accommodate
> +	 * updates via sysfs. */
> +	/* Sized to 9 = max 8 segments + 1 termination segment */
> +	/* Assumption is one and only one type of glass used  */
> +	struct tsl2x7x_lux tsl2x7x_device_lux[MAX_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,
Check your spacing vs tabs.
> +	[tmd2672] = tmd2x72_lux_table,
> +	[tsl2772] =	tsl2x72_lux_table,
> +	[tmd2772] =	tmd2x72_lux_table,
> +};
> +
> +struct als_gainadj {
> +	s16 ch0;
> +	s16 ch1;
> +};
> +
> +/* Used to validate the ALS gain selection index */
> +static const struct als_gainadj tsl2X7X_als_gainadj[] = {
> +	{ 1, 1 },
> +	{ 8, 8 },
> +	{ 16, 16 },
> +	{ 120, 120 }
> +};
This is a somewhat novel structure....  Any real point in the two columns?
> +
> +
> +/* Not using channels */
> +static const struct iio_chan_spec tsl2X7X_channels[] = {};
> +
> +/*
> + * 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, unsigned int len)
> +{
> +	int ret;
> +	int i;
> +
> +	for (i = 0; i<  len; i++) {
Just to check, does this correspond to i2c_smbus_read_byte_data(client, 
(TSL2X7X_CMD_REG | REG))?
Also, a quick look makes me think you only ever use this for one byte at 
a time...

> +		/* select register to write */
> +		ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
> +		if (ret<  0) {
> +			dev_err(&client->dev, "tsl2x7x_i2c_read failed to write"
> +				" register %x\n", reg);
> +			return ret;
> +		}
> +		/* read the data */
> +		*val = i2c_smbus_read_byte(client);
> +		val++;
> +		reg++;
> +	}
> +	return 0;
> +}
> +
> +/*
Kernel doc please.
> + * 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;
> +#pragma pack(4)
? that needs some explanation...
> +	u8 buf[4];
> +	struct tsl2x7x_lux *p;
> +	struct tsl2X7X_chip *chip = iio_priv(indio_dev);
> +	int i, ret;
> +	u32 ch0lux = 0;
> +	u32 ch1lux = 0;
> +
as below, I find these try locks rather suspicious.   Please justify.
> +	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, "tsl2x7x_get_lux device is not enabled\n");
> +		ret = -EBUSY ;
> +		goto out_unlock;
> +	}
> +
> +	ret = tsl2x7x_i2c_read(chip->client,
> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0], 1);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"tsl2x7x_get_lux failed to read CMD_REG\n");
> +		goto out_unlock;
> +	}
> +	/* is data new&  valid */
> +	if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
> +		dev_err(&chip->client->dev,
> +			"tsl2x7x_get_lux data not valid yet\n");
> +		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], 1);
> +		if (ret<  0) {
> +			dev_err(&chip->client->dev,
> +				"tsl2x7x_get_lux failed to read"
> +				" ret: %x\n", 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,
> +		"tsl2x7x_i2c_write_command failed in tsl2x7x_get_lux, err = %d\n",
> +			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))
> +		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 */
> +	for (p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
> +	     p->ratio != 0&&  p->ratio<  ratio; p++)
stray semi colon.
> +		;
> +
> +	if (p->ratio == 0) {
> +		lux = 0;
> +	} else {
> +		ch0lux = ((ch0 * p->ch0) +
> +		(tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0>>  1))
> +		/ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0;
> +
> +		ch1lux = ((ch1 * p->ch1) +
> +		(tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1>>  1))
> +		/ tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1;
> +
> +		lux = ch0lux - ch1lux;
> +	}
> +
> +	/* note: lux is 31 bit max at this point */
> +	if (ch1lux>  ch0lux) {
> +		dev_dbg(&chip->client->dev, "No Data - Return last value\n");
> +		ret = chip->als_cur_info.lux = 0;
> +		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 */
> +return_max:
> +		lux = TSL2X7X_LUX_CALC_OVER_FLOW;
> +	}
> +
> +	/* Update the structure with the latest VALID lux. */
> +	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, "Can't get prox mutex\n");
> +		return -EBUSY;
> +	}
Trylocks always need an explanation as far as I am concerned. why not wait?
> +
> +	err_cnt = 0;
> +
> +try_again:
> +	ret = tsl2x7x_i2c_read(chip->client,
> +		(TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status, 1);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"Read regs failed in tsl2x7x_prox_poll() - A\n");
> +		mutex_unlock(&chip->prox_mutex);
> +		return ret;
> +	}
> +
> +	/*Prox interrupt asserted*/
> +	if (((chip->tsl2x7x_settings.interrupts_en<<  4)
> +			&  CNTL_PROX_INT_ENBL)) {
> +		if (!(status&  TSL2X7X_STA_ADC_VALID)) {
I've not read the datasheet but this seems unusual.  You get an 
interrupt but the
value isn't valid?
> +			err_cnt++;
> +			if (err_cnt>  CONSECUTIVE_RETRIES) {
> +				mutex_unlock(&chip->prox_mutex);
> +				dev_err(&chip->client->dev,
> +				"Consec. retries exceeded\n");
> +				return chip->prox_cur_info.prox_event;
> +			}
> +			goto try_again;
> +		}
> +	}
> +
> +	for (i = 0; i<  2; i++) {
> +		ret = tsl2x7x_i2c_read(chip->client,
> +			(TSL2X7X_CMD_REG |
> +					(TSL2X7X_PRX_LO + i)),&chdata[i], 1);
> +		if (ret<  0) {
Errors like this are extremely unlikely (I hope) so I'd drop the 
message.  Also use a goto
and have the mutex_unlock in only one place as it's easier to read.
> +			dev_err(&chip->client->dev,
> +			"Read regs failed in tsl2x7x_prox_poll() - B\n");
> +			mutex_unlock(&chip->prox_mutex);
> +			return ret;
> +		}
> +	}
> +
> +	chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
> +
> +	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;
use chip->proc_cur_info.prox_event = chip->prox_cur_info.prox_data >= 
chip->tsl2x7x_settings.prox_thresh_high;
> +
> +	mutex_unlock(&chip->prox_mutex);
> +	return chip->prox_cur_info.prox_event;
> +}
> +
> +/*
> + * Provides initial operational parameter defaults.
> + * These defaults may be changed through the device's sysfs files.
> + */
Have a
const static struct tsl2x7x_settings tsl2x7x_defaults =
{
     .als_time = 200,
     ...
};
then memcpy it into place as necessary.
> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
> +{
> +	/* Operational parameters */
> +	chip->tsl2x7x_settings.als_time = 200;
> +	/* must be a multiple of 50mS */
> +	chip->tsl2x7x_settings.als_gain = 0;
> +	/* this is actually an index into the gain table */
> +	chip->tsl2x7x_settings.prx_time = 0xfe; /*5.4 mS */
> +	/* 2.7ms prox integration time - decrease to increase time */
> +	/* decreases in 2.7 ms intervals */
> +	chip->tsl2x7x_settings.prox_gain = 1;
> +	/* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
> +	/* assume clear glass as default */
> +	chip->tsl2x7x_settings.wait_time = 245;
> +	/* Time between PRX and ALS cycles -decrease to increase time */
> +	/* decreases in 2.7 ms intervals */
> +	chip->tsl2x7x_settings.prox_config = 0;
> +	/* Prox configuration filters */
> +	chip->tsl2x7x_settings.als_gain_trim = 1000;
> +	/* default gain trim to account for aperture effects */
> +	chip->tsl2x7x_settings.als_cal_target = 150;
> +	/* Known external ALS reading used for calibration */
> +	chip->tsl2x7x_settings.als_thresh_low = 200;
> +	/* CH0 'low' count to trigger interrupt */
> +	chip->tsl2x7x_settings.als_thresh_high = 256;
> +	/* CH0 'high' count to trigger interrupt */
> +	chip->tsl2x7x_settings.als_persistence = 0xFF;
> +	/* Number of 'out of limits' ADC readings PRX/ALS*/
> +	chip->tsl2x7x_settings.interrupts_en = 0x00;
> +	/* Default interrupt(s) enabled.
> +	 * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
> +	chip->tsl2x7x_settings.prox_thres_low  = 0;
> +	chip->tsl2x7x_settings.prox_thres_high = 512;
> +	/*default threshold (adjust either manually or with cal routine*/
> +	chip->tsl2x7x_settings.prox_max_samples_cal = 30;
> +	chip->tsl2x7x_settings.prox_pulse_count = 8;
> +
> +	/* Load up the lux table. Can be changed later via ABI */
> +	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);
> +
> +}
> +
As I've stated below, you really need to justify why we have calibration 
in the kernel rather
than userspace..
> +/*
> + * 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,
> +		"tsl2x7x_als_calibrate failed to write CNTRL register, ret=%d\n",
> +			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,
> +			"tsl2x7x_als_calibrate failed: ADC not enabled\n");
> +		return -1;
> +	}
> +
> +	ret = i2c_smbus_write_byte(chip->client,
> +			(TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +		"tsl2x7x_als_calibrate failed to write ctrl reg: ret=%d\n",
> +			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,
> +		"tsl2x7x_als_calibrate failed: STATUS - ADC not valid.\n");
> +		return -ENODATA;
> +	}
> +	lux_val = tsl2x7x_get_lux(indio_dev);
> +	if (lux_val<  0) {
> +		dev_err(&chip->client->dev,
> +		"tsl2x7x_als_calibrate failed to get lux\n");
> +		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 | (mA100 | 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, "tsl2x7x_chip_on failed on CNTRL reg.\n");
> +		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_REG_MAX; i++) {
> +		ret = i2c_smbus_write_byte_data(chip->client,
> +				TSL2X7X_CMD_REG + i, *dev_reg++);
> +		if (ret<  0) {
> +			dev_err(&chip->client->dev,
> +			"tsl2x7x_chip_on failed on write to reg %d.\n", 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 | 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, "tsl2x7x_chip_on failed on 2nd CTRL reg.\n");
> +		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");
> +		/* first time interrupt */
> +		chip->init_done = 0;
> +
> +
> +	reg_val = TSL2X7X_CNTL_PWR_ON;
> +
> +	if (chip->tsl2x7x_settings.interrupts_en == 0x10)
> +		reg_val |= CNTL_ADC_ENBL;
> +
> +	if (chip->tsl2x7x_settings.interrupts_en == 0x20)
> +		reg_val |= CNTL_PROX_DET_ENBL;
> +
> +	if (chip->tsl2x7x_settings.interrupts_en == 0x30)
> +		reg_val |= (CNTL_ADC_ENBL | 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,
> +		"tsl2x7x_i2c_write to device failed in tsl2x7x_IOCTL_INT_SET.\n");
> +
> +	/* Clear out any initial interrupts  */
> +	ret = i2c_smbus_write_byte(chip->client,
> +		TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
> +		TSL2X7X_CMD_PROXALS_INTCLR);
> +	if (ret<  0) {
> +		dev_err(&chip->client->dev,
> +			"tsl2x7x_i2c_write_command failed in tsl2x7x_chip_on\n");
> +		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;
> +}
> +
> +/**
> + * Integer Square Root
> + * We need an integer version since 1st Floating point is not allowed
> + * in driver world, 2nd, cannot count on the devices having a FPU, and
> + * 3rd software FP emulation may be excessive.
It's exactly this sort of thing that supports my argument below that 
this ought to be
in userspace...
> + */
> +static unsigned long tsl2x7x_isqrt(unsigned long x)
> +{
> +	register unsigned long op, res, one;
> +	op = x;
> +	res = 0;
> +
> +	one = 1<<  30;
> +	while (one>  op)
> +		one>>= 2;
> +
> +	while (one != 0) {
> +		if (op>= res + one) {
> +			op -= res + one;
> +			res += one<<  1;
> +		}
> +		res>>= 1;
> +		one>>= 2;
> +	}
> +	return res;
> +}
> +
> +/*
> + * 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 = 0xffff;
> +	max = 0;
> +	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 = tsl2x7x_isqrt((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.
> + */
I guess you have a demand for this, but generally I'd feel this had more
of a place in userspace than down in the kernel...
> +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,
> +			"max prox samples cal is too big: %d\n",
> +			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 |= 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);
> +}
> +
> +/* ---------- Sysfs Interface Functions ------------- */
Don't bother with the structure comments. It's readily apparent and they
have a nasty habit of not being true after a year or two.
> +
> +
> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	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 *dev_info = dev_get_drvdata(dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
strtobool
> +
> +	if (value == 0)
> +		tsl2x7x_chip_off(dev_info);
> +	else
> +		tsl2x7x_chip_on(dev_info);
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_proximity_detect_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	tsl2x7x_prox_poll(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->prox_cur_info.prox_event);
> +}
> +
> +static ssize_t tsl2x7x_gain_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +		tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0);
> +}
> +
> +static ssize_t tsl2x7x_gain_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	switch (value) {
> +	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:
> +		chip->tsl2x7x_settings.als_gain = 3;
> +		break;
It's unlikely but you probably want to verify that 120 is only used
for parts where it is valid.  (for consistency sake).
> +	case 128:
> +		chip->tsl2x7x_settings.als_gain = 3;
> +		break;
> +
> +	default:
> +		dev_err(dev,
> +			"Invalid Gain Index\n");
> +		return -EINVAL;
> +	}
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	if (strncmp(chip->client->name, "tsl2772", 7) == 0)
> +		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_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			(1<<  chip->tsl2x7x_settings.prox_gain));
> +}
> +
> +static ssize_t tsl2x7x_prox_gain_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	switch (value) {
> +	case 1:
maybe use the relevant log2? Then you just need to check if it's too big.
> +		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:
> +		dev_err(dev,
> +			"Invalid Prox Gain Index\n");
> +		return -EINVAL;
> +	}
> +
> +	return len;
> +}
> +
> +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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	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 ssize_t tsl2x7x_als_time_available_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "%s\n",
> +		"50 100 150 200 250 300 350 400 450 500 550 600 650");
> +}
> +
> +static ssize_t tsl2x7x_als_trim_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_gain_trim);
> +}
> +
> +static ssize_t tsl2x7x_als_trim_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	if (value)
I'd have thought 0 was a valid gain_trim?
> +		chip->tsl2x7x_settings.als_gain_trim = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	if (value)
> +		chip->tsl2x7x_settings.als_cal_target = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_interrupts_en_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	if (chip->tsl2x7x_settings.interrupts_en&  0x10)
> +		return snprintf(buf, PAGE_SIZE, "%d\n", 1);
> +	else
> +		return snprintf(buf, PAGE_SIZE, "%d\n", 0);
snprintf(buf, PAGE_SIZE, "%d\n", !!(chip->tsl2x7x_settings.interrupts_en 
& 0x10));
> +}
> +
> +static ssize_t tsl2x7x_interrupts_en_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
strotobool (and use the iio_chan_spec and relevant callbacks please.)
> +	if (value>  1)
> +		return -EINVAL;
> +	if (value)
> +		chip->tsl2x7x_settings.interrupts_en |= 0x10;
> +	else
> +		chip->tsl2x7x_settings.interrupts_en&= 0x20;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_prox_interrupt_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	if (chip->tsl2x7x_settings.interrupts_en&  0x20)
> +		return snprintf(buf, PAGE_SIZE, "%d\n", 1);
> +	else
> +		return snprintf(buf, PAGE_SIZE, "%d\n", 0);
> +}
> +
> +static ssize_t tsl2x7x_prox_interrupt_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	if (value>  1)
> +		return -EINVAL;
> +	if (value)
> +		chip->tsl2x7x_settings.interrupts_en |= 0x20;
> +	else
> +		chip->tsl2x7x_settings.interrupts_en&= 0x10;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_als_thresh_low_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_thresh_low);
> +}
> +
> +static ssize_t tsl2x7x_als_thresh_low_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.als_thresh_low = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_als_thresh_high_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.als_thresh_high);
> +}
> +
> +static ssize_t tsl2x7x_als_thresh_high_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.als_thresh_high = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_prox_thresh_low_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.prox_thres_low);
> +}
> +
> +static ssize_t tsl2x7x_prox_thresh_low_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.prox_thres_low = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_prox_thresh_high_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->tsl2x7x_settings.prox_thres_high);
> +}
> +
> +static ssize_t tsl2x7x_prox_thresh_high_store(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.prox_thres_high = value;
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_prox_data_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	tsl2x7x_prox_poll(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			chip->prox_cur_info.prox_data);
> +}
> +
> +/* 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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "0x%02X\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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
> +
> +	chip->tsl2x7x_settings.als_persistence = value;
> +
> +	return len;
> +}
> +
> +static
> +ssize_t tsl2x7x_als_persistence_available_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	return snprintf(buf, PAGE_SIZE, "0x00 - 0xFF (0 - 255)\n");
> +}
> +
> +static
> +ssize_t tsl2x7x_lux_show(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	int lux;
> +
> +	lux = tsl2x7x_get_lux(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "%d\n", lux);
> +}
> +
> +static
> +ssize_t tsl2x7x_adc_show(struct device *dev, struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	tsl2x7x_get_lux(dev_info);
> +
> +	return snprintf(buf, PAGE_SIZE, "0x%08x\n",
> +			((chip->als_cur_info.als_ch0<<  16) |
> +					chip->als_cur_info.als_ch1));
Your outputing in hex? Please don't and please do this through an 
iio_chan_spec
structure and read_raw callback.
> +}
> +
> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
> +	struct device_attribute *attr, const char *buf, size_t len)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
strtobool?
> +
> +	if (value == 1)
> +		tsl2x7x_als_calibrate(dev_info);
> +
> +	return len;
> +}
> +
> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
> +	struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	int i;
> +	int offset = 0;
> +
> +	i = 0;
> +	while (i<  (MAX_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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	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(dev_info);
> +
> +	/* 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 *dev_info = dev_get_drvdata(dev);
> +	unsigned long value;
> +
> +	if (kstrtoul(buf, 0,&value))
> +		return -EINVAL;
strtobool probably?
> +
> +	if (value == 1)
> +		tsl2x7x_prox_cal(dev_info);
> +
> +	return len;
> +}
> +
> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
> +		tsl2x7x_power_state_show, tsl2x7x_power_state_store);
> +
> +static DEVICE_ATTR(proximity_raw, S_IRUGO,
> +		tsl2x7x_proximity_detect_show, NULL);
> +
> +static DEVICE_ATTR(intensity_infrared_raw, S_IRUGO,
> +		tsl2x7x_prox_data_show, NULL);
> +
> +
> +static DEVICE_ATTR(proximity_calibscale, S_IRUGO | S_IWUSR,
> +		tsl2x7x_prox_gain_show, tsl2x7x_prox_gain_store);
> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
> +		tsl2x7x_prox_gain_available_show, NULL);
> +
> +static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
> +		tsl2x7x_gain_show, tsl2x7x_gain_store);
> +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_integration_time_available, S_IRUGO,
> +		tsl2x7x_als_time_available_show, NULL);
> +
> +static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_trim_show, tsl2x7x_als_trim_store);
> +
> +static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
> +
> +static DEVICE_ATTR(illuminance0_both_raw, S_IRUGO, tsl2x7x_adc_show,
> +		NULL);
> +
> +static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2x7x_lux_show,
> +		NULL);
> +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(illuminance0_thresh_falling_value, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_thresh_low_show, tsl2x7x_als_thresh_low_store);
> +
> +static DEVICE_ATTR(illuminance0_thresh_rising_value, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_thresh_high_show, tsl2x7x_als_thresh_high_store);
> +
> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
> +		tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
> +
> +static DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
> +		tsl2x7x_als_persistence_available_show, NULL);
> +
> +static DEVICE_ATTR(proximity_thresh_rising_value, S_IRUGO | S_IWUSR,
> +		tsl2x7x_prox_thresh_high_show, tsl2x7x_prox_thresh_high_store);
> +
> +static DEVICE_ATTR(proximity_thresh_falling_value, S_IRUGO | S_IWUSR,
> +		tsl2x7x_prox_thresh_low_show, tsl2x7x_prox_thresh_low_store);
> +
A lot of what we having in here is standard and should be done via a 
iio_chan_spec
definition.  That'll cover calibscal, calibbias, input, raw, 
thresh_rising_value, thresh_falling_value
(which incidentally should be in the event attributes).
That leaves us with
power_state - ideally not here but handled via a kernel wide control (it 
gets suggested,
everyone agrees it's a good idea and no one implements it...) but such 
is life.
calibscale_available - fine
integration_time - Propose adding this to the iio_chan_spec via info_mask.
integration_time_available - fine.
input_target -> target_input but otherwise fine.
calibrate -> fine
lux_table -> fine
sampling_frequency, frequency_available fine.

Just for reference, we really need to come up with a way of handing 
'available' within
the iio_chan_spec structures and the same for sampling frequency. Not 
your problem
but  if I say it often enough hopefully someone else will do it ;)
> +static struct attribute *tsl2571_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_illuminance0_calibscale.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&dev_attr_illuminance0_integration_time_available.attr,
> +	&dev_attr_illuminance0_calibbias.attr,
> +	&dev_attr_illuminance0_input_target.attr,
> +	&dev_attr_illuminance0_both_raw.attr,
> +	&dev_attr_illuminance0_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_illuminance0_thresh_falling_value.attr,
> +	&dev_attr_illuminance0_thresh_rising_value.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&dev_attr_sampling_frequency_available.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2671_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_proximity_raw.attr,
> +	&dev_attr_intensity_infrared_raw.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&dev_attr_sampling_frequency_available.attr,
> +	&dev_attr_proximity_thresh_rising_value.attr,
> +	&dev_attr_proximity_thresh_falling_value.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2771_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_proximity_raw.attr,
> +	&dev_attr_intensity_infrared_raw.attr,
> +	&dev_attr_illuminance0_calibscale.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&dev_attr_illuminance0_integration_time_available.attr,
> +	&dev_attr_illuminance0_calibbias.attr,
> +	&dev_attr_illuminance0_input_target.attr,
> +	&dev_attr_illuminance0_both_raw.attr,
> +	&dev_attr_illuminance0_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_illuminance0_thresh_falling_value.attr,
> +	&dev_attr_illuminance0_thresh_rising_value.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&dev_attr_sampling_frequency_available.attr,
> +	&dev_attr_proximity_thresh_rising_value.attr,
> +	&dev_attr_proximity_thresh_falling_value.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2572_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_illuminance0_calibscale.attr,
> +	&dev_attr_illuminance0_calibscale_available.attr,
> +	&dev_attr_illuminance0_integration_time.attr,
> +	&dev_attr_illuminance0_integration_time_available.attr,
> +	&dev_attr_illuminance0_calibbias.attr,
> +	&dev_attr_illuminance0_input_target.attr,
> +	&dev_attr_illuminance0_both_raw.attr,
> +	&dev_attr_illuminance0_input.attr,
> +	&dev_attr_illuminance0_calibrate.attr,
> +	&dev_attr_illuminance0_lux_table.attr,
> +	&dev_attr_illuminance0_thresh_falling_value.attr,
> +	&dev_attr_illuminance0_thresh_rising_value.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&dev_attr_sampling_frequency_available.attr,
> +	NULL
> +};
> +
> +static struct attribute *tsl2672_device_attrs[] = {
> +	&dev_attr_power_state.attr,
> +	&dev_attr_proximity_raw.attr,
> +	&dev_attr_intensity_infrared_raw.attr,
> +	&dev_attr_proximity_calibscale.attr,
> +	&dev_attr_sampling_frequency.attr,
> +	&dev_attr_sampling_frequency_available.attr,
> +	&dev_attr_proximity_thresh_rising_value.attr,
> +	&dev_attr_proximity_thresh_falling_value.attr,
> +	&dev_attr_proximity_calibrate.attr,
> +	NULL
> +};
> +
Indent the same for all of these.
> +static struct attribute *tsl2772_device_attrs[] = {
> +		&dev_attr_power_state.attr,
> +		&dev_attr_proximity_raw.attr,
> +		&dev_attr_proximity_calibscale.attr,
> +		&dev_attr_intensity_infrared_raw.attr,
> +		&dev_attr_illuminance0_calibscale.attr,
> +		&dev_attr_illuminance0_calibscale_available.attr,
> +		&dev_attr_illuminance0_integration_time.attr,
> +		&dev_attr_illuminance0_integration_time_available.attr,
> +		&dev_attr_illuminance0_calibbias.attr,
> +		&dev_attr_illuminance0_input_target.attr,
> +		&dev_attr_illuminance0_both_raw.attr,
> +		&dev_attr_illuminance0_input.attr,
> +		&dev_attr_illuminance0_calibrate.attr,
> +		&dev_attr_illuminance0_lux_table.attr,
> +		&dev_attr_illuminance0_thresh_falling_value.attr,
> +		&dev_attr_illuminance0_thresh_rising_value.attr,
> +		&dev_attr_sampling_frequency.attr,
> +		&dev_attr_sampling_frequency_available.attr,
> +		&dev_attr_proximity_thresh_rising_value.attr,
> +		&dev_attr_proximity_thresh_falling_value.attr,
> +		&dev_attr_proximity_calibrate.attr,
> +		&dev_attr_proximity_calibscale_available.attr,
> +		NULL
> +};
> +
> +static struct attribute_group tsl2X7X_dev_attr_group_tbl[] = {
> +	[tsl2571] = {
> +		.attrs = tsl2571_device_attrs,
> +	},
> +	[tsl2671] = {
> +		.attrs = tsl2671_device_attrs,
> +	},
> +	[tmd2671] = {
> +		.attrs = tsl2671_device_attrs,
> +	},
> +	[tsl2771] = {
> +		.attrs = tsl2771_device_attrs,
> +	},
> +	[tmd2771] = {
> +		.attrs = tsl2771_device_attrs,
> +	},
> +	[tsl2572] = {
> +		.attrs = tsl2572_device_attrs,
> +	},
> +	[tsl2672] = {
> +		.attrs = tsl2672_device_attrs,
> +	},
> +	[tmd2672] = {
> +		.attrs = tsl2672_device_attrs,
> +	},
> +	[tsl2772] = {
> +		.attrs = tsl2772_device_attrs,
> +	},
> +	[tmd2772] = {
> +		.attrs = tsl2772_device_attrs,
> +	},
> +
> +};
> +
> +static IIO_DEVICE_ATTR(illuminance_thresh_both_en,
> +		S_IRUGO | S_IWUSR,
> +		tsl2x7x_interrupts_en_show, tsl2x7x_interrupts_en_store, 0);
> +
> +static IIO_DEVICE_ATTR(proximity_thresh_both_en,
> +		S_IRUGO | S_IWUSR,
> +		tsl2x7x_prox_interrupt_show, tsl2x7x_prox_interrupt_store, 0);
> +
> +static struct attribute *tsl2X7X_prox_event_attributes[] = {
> +	&iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute *tsl2X7X_als_event_attributes[] = {
> +	&iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
> +	NULL,
> +};
> +
> +static struct attribute *tsl2X7X_proxals_event_attributes[] = {
> +	&iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
> +	&iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
> +	NULL,
> +};
The way we have handled this for some similar devices is to just have 
the two enables done
via the iio_chan_spec and chain them both to read / write the same 
value.  (By which I mean
the rising enable and falling enable).  It slightly clunkier as an 
interface, but saves code and will
ultimately enable them to be accessed by consumer drivers within the 
kernel (not that we've
even started looking at event pass
> +
> +static struct attribute_group tsl2X7X_event_attr_group_tbl[] = {
> +	[tsl2571] = {
> +		.attrs = tsl2X7X_als_event_attributes,
> +		.name  = "events",
> +	},
> +	[tsl2671] = {
> +		.attrs = tsl2X7X_prox_event_attributes,
> +		.name  = "events",
> +	},
> +	[tmd2671] = {
> +		.attrs = tsl2X7X_prox_event_attributes,
> +		.name  = "events",
> +	},
> +	[tsl2771] = {
> +		.attrs = tsl2X7X_proxals_event_attributes,
> +		.name  = "events",
> +	},
> +	[tmd2771] = {
> +		.attrs = tsl2X7X_proxals_event_attributes,
> +		.name  = "events",
> +	},
> +
> +	[tsl2572] = {
> +		.attrs = tsl2X7X_als_event_attributes,
> +		.name  = "events",
> +	},
> +	[tsl2672] = {
> +		.attrs = tsl2X7X_prox_event_attributes,
> +		.name  = "events",
> +	},
> +	[tmd2672] = {
> +		.attrs = tsl2X7X_prox_event_attributes,
> +		.name  = "events",
> +	},
> +	[tsl2772] = {
> +		.attrs = tsl2X7X_proxals_event_attributes,
> +		.name  = "events",
> +	},
> +	[tmd2772] = {
> +		.attrs = tsl2X7X_proxals_event_attributes,
> +		.name  = "events",
> +	}
> +};
> +
Being fussy, but only one blank line please.
> +
> +/* Use the default register values to identify the Taos device */
> +static int tsl2x7x_device_id(unsigned char *bufp, int target)
> +{
> +	switch (target) {
> +	case tsl2571:
> +	case tsl2671:
> +	case tsl2771:
> +		return ((bufp[TSL2X7X_CHIPID]&  0xf0) == TRITON_ID);
> +	break;
> +	case tmd2671:
> +	case tmd2771:
> +		return ((bufp[TSL2X7X_CHIPID]&  0xf0) == HALIBUT_ID);
> +	break;
> +	case tsl2572:
> +	case tsl2672:
> +	case tmd2672:
> +	case tsl2772:
> +	case tmd2772:
> +		return ((bufp[TSL2X7X_CHIPID]&  0xf0) == SWORDFISH_ID);
> +	break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info tsl2X7X_info[] = {
> +	[tsl2571] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2571],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2571],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tsl2671] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2671],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2671],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tmd2671] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2671],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2671],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tsl2771] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2771],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2771],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tmd2771] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2771],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2771],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tsl2572] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2572],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2572],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tsl2672] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2672],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2672],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tmd2672] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2672],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2672],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tsl2772] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2772],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2772],
> +			.driver_module = THIS_MODULE,
> +	},
> +	[tmd2772] = {
> +			.attrs =&tsl2X7X_dev_attr_group_tbl[tsl2772],
> +			.event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2772],
> +			.driver_module = THIS_MODULE,
A lot of repeats in here, could you not have fewer entries?  Just reuse 
the enum values
in the i2c id table below.
> +	},
> +
> +};
> +
> +/*
> + * Run-time interrupt handler - depending on whether the device is in ambient
> + * light sensing interrupt mode, this handler can queue up
> + * a thread, to handle valid interrupts.
> + */
> +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_INTCLR);
> +	if (ret<  0)
> +		dev_err(&chip->client->dev,
> +		"Failed to clear irq from event handler. err = %d\n", ret);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Client probe function.
> + */
> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
> +	const struct i2c_device_id *id)
> +{
> +	int i, ret;
> +	unsigned char buf[TSL2X7X_MAX_DEVICE_REGS];
> +	struct iio_dev *indio_dev =
> +			iio_allocate_device(sizeof(struct tsl2X7X_chip));
Ugly to do non trivial allocation in the variable definitions.  Have
     struct iio_dev *indio_dev;
     struct tsl2x7x_chip *chip;

     indio_dev = iio_allocate_device(sizeof(*chip));
     if (indio_dev == NULL) {
     }
chip = iio_priv(indio_dev);
etc.
> +	struct tsl2X7X_chip	*chip = iio_priv(indio_dev);
> +
> +	if (indio_dev == NULL) {
> +		ret = -ENOMEM;
> +		dev_err(&clientp->dev, "iio allocation failed\n");
> +		goto fail1;
> +	}
> +
> +	chip = iio_priv(indio_dev);
> +	chip->client = clientp;
> +	i2c_set_clientdata(clientp, indio_dev);
> +
> +	mutex_init(&chip->als_mutex);
> +	mutex_init(&chip->prox_mutex);
> +
> +	chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
> +
> +	chip->pdata = clientp->dev.platform_data;
> +	if (chip->pdata&&  chip->pdata->init)
> +			chip->pdata->init(clientp);
> +
> +	for (i = 0; i<  TSL2X7X_MAX_DEVICE_REGS; i++) {
> +		ret = i2c_smbus_write_byte(clientp,
> +				(TSL2X7X_CMD_REG | (TSL2X7X_CNTRL + i)));
> +		if (ret<  0) {
> +			dev_err(&clientp->dev, "i2c_smbus_write_bytes() to cmd "
> +			"reg failed in tsl2x7x_probe(), err = %d\n", ret);
> +			goto fail1;
> +		}
> +		ret = i2c_smbus_read_byte(clientp);
> +		if (ret<  0) {
> +			dev_err(&clientp->dev, "i2c_smbus_read_byte from "
> +			"reg failed in tsl2x7x_probe(), err = %d\n", ret);
> +
> +			goto fail1;
> +		}
> +		buf[i] = ret;
> +	}
> +
> +	if ((!tsl2x7x_device_id(buf, id->driver_data)) ||
> +			(tsl2x7x_device_id(buf, id->driver_data) == -EINVAL)) {
> +		dev_info(&chip->client->dev, "i2c device found but does not match "
> +			"expected id in tsl2x7x_probe()\n");
> +		goto fail1;
> +	}
> +
> +	chip->id = id->driver_data;
> +
> +	ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
> +	if (ret<  0) {
> +		dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
> +			"failed in tsl2x7x_probe(), err = %d\n", ret);
> +		goto fail1;
> +	}
> +
> +	indio_dev->info =&tsl2X7X_info[id->driver_data];
> +	indio_dev->dev.parent =&clientp->dev;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->name = chip->client->name;
> +	indio_dev->channels = tsl2X7X_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(tsl2X7X_channels);
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(&clientp->dev, "iio registration failed\n");
> +		goto fail2;
one would normally expect a later failure to result in more needing to 
be undone,
but it appears to be the other way around here.
> +	}
> +
> +	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, "irq request failed");
Unwinding after this error?  Generally set the irq up and make the 
iio_device_register
pretty much the last thing to happen rather than this way around.  
Nothing can
touch the device before the iio_device_register so it avoids the chance 
of race
conditions without having to be careful!
> +	}
> +
> +	/* Load up the defaults */
> +	tsl2x7x_defaults(chip);
> +
> +	/* Make sure the chip is on */
> +	tsl2x7x_chip_on(indio_dev);
> +
> +	dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
> +
> +	return 0;
> +
> +fail1:
> +	if (chip->pdata&&  chip->pdata->teardown)
> +		chip->pdata->teardown(clientp);
> +	iio_free_device(indio_dev);
> +
> +fail2:
> +	return ret;
loose the bonus blank line.
> +
> +}
> +
> +
> +static int tsl2x7x_suspend(struct device *dev)
> +{
> +	struct iio_dev *dev_info = dev_get_drvdata(dev);
standardize the naming please.  indio_dev  preferably but consistency is 
most
important.
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +
> +	int ret = 0;
> +
> +	/* Blocks if work is active */
> +	cancel_work_sync(&chip->work_thresh);
> +
> +	if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
> +		ret = tsl2x7x_chip_off(dev_info);
> +		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 *dev_info = dev_get_drvdata(dev);
> +	struct tsl2X7X_chip *chip = iio_priv(dev_info);
> +	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(dev_info);
> +
> +	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);
Err, the two lines above seem a little implausible.
> +
> +	tsl2x7x_chip_off(indio_dev);
> +
> +	if (client->irq)
> +		free_irq(client->irq, chip->client->name);
> +
> +	flush_scheduled_work();
> +
> +	iio_device_unregister(chip->iio_dev);
or use the indio_dev pointer you already have.  There is rarely a reason
to have a pointer to the iio_dev in the driver private bit.
> +	kfree(chip);
> +
> +	if (chip->pdata&&  chip->pdata->teardown)
> +		chip->pdata->teardown(client);
> +
> +	return 0;
> +}
> +
> +static struct i2c_device_id tsl2x7x_idtable[] = {
I'm guessing there is a magic ordering here, but might be
nicer just to go with alphabetical order..
> +	{ "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),
> +};
> +
> +static int __init tsl2x7x_init(void)
> +{
> +	return i2c_add_driver(&tsl2x7x_driver);
> +}
> +
> +static void __exit tsl2x7x_exit(void)
> +{
> +	i2c_del_driver(&tsl2x7x_driver);
> +}
> +
> +module_init(tsl2x7x_init);
> +module_exit(tsl2x7x_exit);
Some nice new macros to handle this boiler plate.
see module_i2c_driver in include/linux/i2c.h

> +
> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
> +MODULE_DESCRIPTION("TAOS tsl2X7X ambient 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..9a64412
> --- /dev/null
> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
> @@ -0,0 +1,138 @@
> +/*
> + * 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>
> +
> +#include "../iio.h"
loose this header and just forward declare the pointer.
e.g. struct iio_dev;

> +
> +/* 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_DEVICE_REGS 32
> +#define TSL2X7X_REG_MAX         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 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 CNTL_REG_CLEAR                 0x00
> +#define CNTL_PROX_INT_ENBL             0X20
> +#define CNTL_ALS_INT_ENBL              0X10
> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
> +#define CNTL_PROX_DET_ENBL             0X04
> +#define CNTL_ADC_ENBL                  0x02
> +#define TSL2X7X_CNTL_PWRON             0x01
> +#define CNTL_ALSPON_ENBL               0x03
> +#define CNTL_INTALSPON_ENBL            0x13
> +#define CNTL_PROXPON_ENBL              0x0F
> +#define CNTL_INTPROXPON_ENBL           0x2F
> +#define TSL2X7X_CMD_PROXALS_INTCLR     0X07
> +
Chances of a clash on these definitions is extremely high,
Stick a prefix in front of them to avoid that issue.
> +/*Prox diode to use */
> +#define DIODE0                         0x10
> +#define DIODE1                         0x20
> +#define DIODE_BOTH                     0x30
> +
> +/* LED Power */
> +#define mA100                          0x00
> +#define mA50                           0x40
> +#define mA25                           0x80
> +#define mA13                           0xD0
> +
> +/* Max number of segments allowable in LUX table */
> +#define MAX_TABLE_SIZE		9
> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * MAX_TABLE_SIZE)
> +
> +struct tsl2x7x_lux {
> +	unsigned int ratio;
> +	unsigned int ch0;
> +	unsigned int ch1;
> +};
> +
> +
Kernel doc for comments would be preferable.  Can you also give a 
description of what
actually tends to occur in these callbacks?  I can't help feeling there 
are rather more of
them than we'd normally expect.
> +/* struct tsl2x7x_platform_data - Assume all varients will have this */
> +struct tsl2X7X_platform_data {
> +	int (*platform_power)(struct device *dev, pm_message_t);
> +	/* The following callback gets called when the TSL2772 is powered on */
> +	int (*power_on)      (struct iio_dev *indio_dev);
> +	/* The following callback gets called when the TSL2772 is powered off */
> +	int (*power_off)     (struct i2c_client *dev);
> +	/* The following callback gets called when the driver is added */
> +	int (*init)          (struct i2c_client *dev);
> +	/* The following callback gets called when the driver is removed */
> +	int (*teardown)      (struct i2c_client *dev);
> +	/* These are the device specific glass coefficents used to
> +	 * calculate Lux */
> +	struct tsl2x7x_lux platform_lux_table[MAX_TABLE_SIZE];
> +};
> +
> +#endif /* __TSL2X7X_H */
> --
>


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

* Re: [PATCH V1] TAOS tsl2x7x
  2012-02-13 15:19 ` Jonathan Cameron
@ 2012-02-13 15:28   ` Jonathan Cameron
  0 siblings, 0 replies; 4+ messages in thread
From: Jonathan Cameron @ 2012-02-13 15:28 UTC (permalink / raw)
  To: Jon Brenner; +Cc: Jonathan Cameron, linux-kernel, linux-iio

On 2/13/2012 3:19 PM, Jonathan Cameron wrote:
> On 2/10/2012 7:06 PM, Jon Brenner wrote:
>> TAOS device driver for tsl/tmd 2771 and 2772 device families (inc all 
>> variants).
>>
>> Signed-off-by: Jon Brenner<jbrenner@taosinc.com>
> Hi Jon,
>
Not enough coffee.. Reasonably clean. ...
> Good to see another driver from you. Reasonably keen but couple of 
> medium sized issues
> a) Use iio_chan_spec and callbacks in iio_info structure where 
> possible.  Propose additional
> elements where they make sense (such as your integration time).
> b) Documentation should be slotted in relevant places in more general 
> files.
> c) I'm unconvinced the calibration stuff should be in kernel.
>
> Thanks,
>
> Jonathan
>>
>> ---
>>   .../iio/Documentation/sysfs-bus-iio-light-tsl2x7x  |   71 +
>>   drivers/staging/iio/light/Kconfig                  |   11 +
>>   drivers/staging/iio/light/Makefile                 |    1 +
>>   drivers/staging/iio/light/tsl2x7x_core.c           | 2053 
>> ++++++++++++++++++++
>>   drivers/staging/iio/light/tsl2x7x_core.h           |  138 ++
>>   5 files changed, 2274 insertions(+), 0 deletions(-)
>>
>> diff --git 
>> a/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x 
>> b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
>> new file mode 100644
>> index 0000000..f1bb5f5
>> --- /dev/null
>> +++ b/drivers/staging/iio/Documentation/sysfs-bus-iio-light-tsl2x7x
>> @@ -0,0 +1,71 @@
> Most of these are fairly general purpose so should be sysfs-bus-iio or 
> sysfs-bus-iio-light.
> Some are also shared with the tsl2583 doc so maybe move those into the 
> light one.
>
> Generally if the last bit (after the channel type) is restricted to 
> the actual device then
> it wants to be in this file.
> If it's restricted to the class (light / proximity) then it wants to be
> in sysfs-bus-iio-light or sysfs-bus-iio-proximity
> If like proximity_raw or the thresh_value ones then it wants to be
> in sysfs-bus-iio.
>
> Please do fix any cases where this isn't currently the case.  Our 
> documentation outside the top
> level file is far from well written or organized.
>
>> +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_thresh_falling_value
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        Low (falling) trigger point for the internal ALS comparison
>> +        function for interrupt generation.
> This one just needs adding to sysfs-bus-iio.
>> +
>> +What:        
>> /sys/bus/iio/devices/device[n]/illuminance0_thresh_rising_value
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        high (rising) trigger point for the internal ALS comparison
>> +        function for interrupt generation.
>> +
>> +What:        /sys/bus/iio/devices/device[n]/illuminance0_both_raw
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        Simultainious ALS channel 1 and channel 2 data represented as
>> +        a 32 bit integer.
> err. previously the both has referred to the light frequencies 
> covered. Please don't do what you
> describe here. If you really need to be sure they are pared then you 
> want to use the buffered
> infrastructure not sysfs.
>
>> +
>> +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.
>> +
>> +What:        
>> /sys/bus/iio/devices/device[n]/proximity_thresh_falling_value
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        Low (falling) trigger point for the internal proximity 
>> comparison
>> +        function for interrupt generation.
>> +
> Again, standard form so add to sysfs-bus-iio.
>> +What:        
>> /sys/bus/iio/devices/device[n]/proximity_thresh_rising_value
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        high (rising) trigger point for the internal proximity 
>> comparison
>> +        function for interrupt generation.
>> +
>> +What:        /sys/bus/iio/devices/device[n]/proximity_calibscale
>> +KernelVersion:    3.3-rc1
>> +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).
>> +
> All of these are standard so add to sysfs-bus-iio lists for calibscale
>> +What:        /sys/bus/iio/devices/device[n]/proximity_raw
>> +KernelVersion:    3.3-rc1
>> +Contact:    linux-iio@vger.kernel.org
>> +Description:
>> +        State of proximity detection based on the
>> +        proximity_thresh_rising_value.
> If you wouldn't mind can you lift he proximity documentation out of 
> sysfs-bus-iio-ilght into sysfs-bus-iio
> then we can take all the standard definitions without adding them yet 
> again. (as a separate patch).
>
> I'm still kind of regretting ever introducing multiple sysfs 
> documentatino files.
>> +
>> diff --git a/drivers/staging/iio/light/Kconfig 
>> b/drivers/staging/iio/light/Kconfig
>> index e7e9159..29de333 100644
>> --- a/drivers/staging/iio/light/Kconfig
>> +++ b/drivers/staging/iio/light/Kconfig
>> @@ -31,4 +31,15 @@ config TSL2583
>>        Provides support for the TAOS tsl2580, tsl2581 and tsl2583 
>> devices.
>>        Access ALS data via iio, sysfs.
>>
>> +     This driver can also be built as a module.  If so, the module
>> +     will be called tsl2583.
> errr.  This change to a comment about a completely different driver 
> should not be in this patch.
>> +
>> +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..671f476
>> --- /dev/null
>> +++ b/drivers/staging/iio/light/tsl2x7x_core.c
>> @@ -0,0 +1,2053 @@
>> +/*
>> + * 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/string.h>
>> +#include<linux/mutex.h>
>> +#include<linux/unistd.h>
>> +#include<linux/interrupt.h>
>> +#include<linux/platform_device.h>
>> +#include<linux/input.h>
>> +#include<linux/slab.h>
>> +#include<linux/pm.h>
>> +#include<linux/module.h>
>> +#include<linux/version.h>
>> +
>> +#include<linux/cdev.h>
>> +#include<linux/stat.h>
>> +#include<linux/module.h>
>> +#include "tsl2x7x_core.h"
>> +#include "../events.h"
>> +#include "../buffer.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 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_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;/* for calibration mode*/
>> +
>> +};
>> +
>> +struct tsl2X7X_chip_info {
>> +    struct iio_chan_spec    channel[0];
>> +    const struct iio_info    *info;
>> +};
> err. what is channel[0] for?  Please do the iio_chan_spec stuff fully.
>> +
>> +struct tsl2X7X_chip {
>> +    kernel_ulong_t id;
>> +    struct mutex prox_mutex;
>> +    struct mutex als_mutex;
>> +    struct i2c_client *client;
>> +    struct iio_dev *iio_dev;
>> +    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_REG_MAX];
>> +    bool init_done;
>> +    struct work_struct work_thresh;
>> +    s64 event_timestamp;
>> +    /* This structure is intentionally large to accommodate
>> +     * updates via sysfs. */
>> +    /* Sized to 9 = max 8 segments + 1 termination segment */
>> +    /* Assumption is one and only one type of glass used  */
>> +    struct tsl2x7x_lux tsl2x7x_device_lux[MAX_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,
> Check your spacing vs tabs.
>> +    [tmd2672] = tmd2x72_lux_table,
>> +    [tsl2772] =    tsl2x72_lux_table,
>> +    [tmd2772] =    tmd2x72_lux_table,
>> +};
>> +
>> +struct als_gainadj {
>> +    s16 ch0;
>> +    s16 ch1;
>> +};
>> +
>> +/* Used to validate the ALS gain selection index */
>> +static const struct als_gainadj tsl2X7X_als_gainadj[] = {
>> +    { 1, 1 },
>> +    { 8, 8 },
>> +    { 16, 16 },
>> +    { 120, 120 }
>> +};
> This is a somewhat novel structure....  Any real point in the two 
> columns?
>> +
>> +
>> +/* Not using channels */
>> +static const struct iio_chan_spec tsl2X7X_channels[] = {};
>> +
>> +/*
>> + * 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, 
>> unsigned int len)
>> +{
>> +    int ret;
>> +    int i;
>> +
>> +    for (i = 0; i<  len; i++) {
> Just to check, does this correspond to 
> i2c_smbus_read_byte_data(client, (TSL2X7X_CMD_REG | REG))?
> Also, a quick look makes me think you only ever use this for one byte 
> at a time...
>
>> +        /* select register to write */
>> +        ret = i2c_smbus_write_byte(client, (TSL2X7X_CMD_REG | reg));
>> +        if (ret<  0) {
>> +            dev_err(&client->dev, "tsl2x7x_i2c_read failed to write"
>> +                " register %x\n", reg);
>> +            return ret;
>> +        }
>> +        /* read the data */
>> +        *val = i2c_smbus_read_byte(client);
>> +        val++;
>> +        reg++;
>> +    }
>> +    return 0;
>> +}
>> +
>> +/*
> Kernel doc please.
>> + * 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;
>> +#pragma pack(4)
> ? that needs some explanation...
>> +    u8 buf[4];
>> +    struct tsl2x7x_lux *p;
>> +    struct tsl2X7X_chip *chip = iio_priv(indio_dev);
>> +    int i, ret;
>> +    u32 ch0lux = 0;
>> +    u32 ch1lux = 0;
>> +
> as below, I find these try locks rather suspicious.   Please justify.
>> +    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, "tsl2x7x_get_lux device is not 
>> enabled\n");
>> +        ret = -EBUSY ;
>> +        goto out_unlock;
>> +    }
>> +
>> +    ret = tsl2x7x_i2c_read(chip->client,
>> +        (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&buf[0], 1);
>> +    if (ret<  0) {
>> +        dev_err(&chip->client->dev,
>> +            "tsl2x7x_get_lux failed to read CMD_REG\n");
>> +        goto out_unlock;
>> +    }
>> +    /* is data new&  valid */
>> +    if (!(buf[0]&  TSL2X7X_STA_ADC_VALID)) {
>> +        dev_err(&chip->client->dev,
>> +            "tsl2x7x_get_lux data not valid yet\n");
>> +        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], 1);
>> +        if (ret<  0) {
>> +            dev_err(&chip->client->dev,
>> +                "tsl2x7x_get_lux failed to read"
>> +                " ret: %x\n", 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,
>> +        "tsl2x7x_i2c_write_command failed in tsl2x7x_get_lux, err = 
>> %d\n",
>> +            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))
>> +        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 */
>> +    for (p = (struct tsl2x7x_lux *) chip->tsl2x7x_device_lux;
>> +         p->ratio != 0&&  p->ratio<  ratio; p++)
> stray semi colon.
>> +        ;
>> +
>> +    if (p->ratio == 0) {
>> +        lux = 0;
>> +    } else {
>> +        ch0lux = ((ch0 * p->ch0) +
>> +        (tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0>>  
>> 1))
>> +        / tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0;
>> +
>> +        ch1lux = ((ch1 * p->ch1) +
>> +        (tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1>>  
>> 1))
>> +        / tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch1;
>> +
>> +        lux = ch0lux - ch1lux;
>> +    }
>> +
>> +    /* note: lux is 31 bit max at this point */
>> +    if (ch1lux>  ch0lux) {
>> +        dev_dbg(&chip->client->dev, "No Data - Return last value\n");
>> +        ret = chip->als_cur_info.lux = 0;
>> +        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 */
>> +return_max:
>> +        lux = TSL2X7X_LUX_CALC_OVER_FLOW;
>> +    }
>> +
>> +    /* Update the structure with the latest VALID lux. */
>> +    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, "Can't get prox mutex\n");
>> +        return -EBUSY;
>> +    }
> Trylocks always need an explanation as far as I am concerned. why not 
> wait?
>> +
>> +    err_cnt = 0;
>> +
>> +try_again:
>> +    ret = tsl2x7x_i2c_read(chip->client,
>> +        (TSL2X7X_CMD_REG | TSL2X7X_STATUS),&status, 1);
>> +    if (ret<  0) {
>> +        dev_err(&chip->client->dev,
>> +            "Read regs failed in tsl2x7x_prox_poll() - A\n");
>> +        mutex_unlock(&chip->prox_mutex);
>> +        return ret;
>> +    }
>> +
>> +    /*Prox interrupt asserted*/
>> +    if (((chip->tsl2x7x_settings.interrupts_en<<  4)
>> + &  CNTL_PROX_INT_ENBL)) {
>> +        if (!(status&  TSL2X7X_STA_ADC_VALID)) {
> I've not read the datasheet but this seems unusual.  You get an 
> interrupt but the
> value isn't valid?
>> +            err_cnt++;
>> +            if (err_cnt>  CONSECUTIVE_RETRIES) {
>> +                mutex_unlock(&chip->prox_mutex);
>> +                dev_err(&chip->client->dev,
>> +                "Consec. retries exceeded\n");
>> +                return chip->prox_cur_info.prox_event;
>> +            }
>> +            goto try_again;
>> +        }
>> +    }
>> +
>> +    for (i = 0; i<  2; i++) {
>> +        ret = tsl2x7x_i2c_read(chip->client,
>> +            (TSL2X7X_CMD_REG |
>> +                    (TSL2X7X_PRX_LO + i)),&chdata[i], 1);
>> +        if (ret<  0) {
> Errors like this are extremely unlikely (I hope) so I'd drop the 
> message.  Also use a goto
> and have the mutex_unlock in only one place as it's easier to read.
>> +            dev_err(&chip->client->dev,
>> +            "Read regs failed in tsl2x7x_prox_poll() - B\n");
>> +            mutex_unlock(&chip->prox_mutex);
>> +            return ret;
>> +        }
>> +    }
>> +
>> +    chip->prox_cur_info.prox_data = (chdata[1]<<8)|chdata[0];
>> +
>> +    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;
> use chip->proc_cur_info.prox_event = chip->prox_cur_info.prox_data >= 
> chip->tsl2x7x_settings.prox_thresh_high;
>> +
>> +    mutex_unlock(&chip->prox_mutex);
>> +    return chip->prox_cur_info.prox_event;
>> +}
>> +
>> +/*
>> + * Provides initial operational parameter defaults.
>> + * These defaults may be changed through the device's sysfs files.
>> + */
> Have a
> const static struct tsl2x7x_settings tsl2x7x_defaults =
> {
>     .als_time = 200,
>     ...
> };
> then memcpy it into place as necessary.
>> +static void tsl2x7x_defaults(struct tsl2X7X_chip *chip)
>> +{
>> +    /* Operational parameters */
>> +    chip->tsl2x7x_settings.als_time = 200;
>> +    /* must be a multiple of 50mS */
>> +    chip->tsl2x7x_settings.als_gain = 0;
>> +    /* this is actually an index into the gain table */
>> +    chip->tsl2x7x_settings.prx_time = 0xfe; /*5.4 mS */
>> +    /* 2.7ms prox integration time - decrease to increase time */
>> +    /* decreases in 2.7 ms intervals */
>> +    chip->tsl2x7x_settings.prox_gain = 1;
>> +    /* these are bits 3:2 of reg 0x0f: 0=x1,1=x2,2=x4,3=x8 */
>> +    /* assume clear glass as default */
>> +    chip->tsl2x7x_settings.wait_time = 245;
>> +    /* Time between PRX and ALS cycles -decrease to increase time */
>> +    /* decreases in 2.7 ms intervals */
>> +    chip->tsl2x7x_settings.prox_config = 0;
>> +    /* Prox configuration filters */
>> +    chip->tsl2x7x_settings.als_gain_trim = 1000;
>> +    /* default gain trim to account for aperture effects */
>> +    chip->tsl2x7x_settings.als_cal_target = 150;
>> +    /* Known external ALS reading used for calibration */
>> +    chip->tsl2x7x_settings.als_thresh_low = 200;
>> +    /* CH0 'low' count to trigger interrupt */
>> +    chip->tsl2x7x_settings.als_thresh_high = 256;
>> +    /* CH0 'high' count to trigger interrupt */
>> +    chip->tsl2x7x_settings.als_persistence = 0xFF;
>> +    /* Number of 'out of limits' ADC readings PRX/ALS*/
>> +    chip->tsl2x7x_settings.interrupts_en = 0x00;
>> +    /* Default interrupt(s) enabled.
>> +     * 0x00 = none, 0x10 = als, 0x20 = prx 0x30 = bth */
>> +    chip->tsl2x7x_settings.prox_thres_low  = 0;
>> +    chip->tsl2x7x_settings.prox_thres_high = 512;
>> +    /*default threshold (adjust either manually or with cal routine*/
>> +    chip->tsl2x7x_settings.prox_max_samples_cal = 30;
>> +    chip->tsl2x7x_settings.prox_pulse_count = 8;
>> +
>> +    /* Load up the lux table. Can be changed later via ABI */
>> +    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);
>> +
>> +}
>> +
> As I've stated below, you really need to justify why we have 
> calibration in the kernel rather
> than userspace..
>> +/*
>> + * 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,
>> +        "tsl2x7x_als_calibrate failed to write CNTRL register, 
>> ret=%d\n",
>> +            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,
>> +            "tsl2x7x_als_calibrate failed: ADC not enabled\n");
>> +        return -1;
>> +    }
>> +
>> +    ret = i2c_smbus_write_byte(chip->client,
>> +            (TSL2X7X_CMD_REG | TSL2X7X_CNTRL));
>> +    if (ret<  0) {
>> +        dev_err(&chip->client->dev,
>> +        "tsl2x7x_als_calibrate failed to write ctrl reg: ret=%d\n",
>> +            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,
>> +        "tsl2x7x_als_calibrate failed: STATUS - ADC not valid.\n");
>> +        return -ENODATA;
>> +    }
>> +    lux_val = tsl2x7x_get_lux(indio_dev);
>> +    if (lux_val<  0) {
>> +        dev_err(&chip->client->dev,
>> +        "tsl2x7x_als_calibrate failed to get lux\n");
>> +        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 | (mA100 | 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, "tsl2x7x_chip_on failed on CNTRL 
>> reg.\n");
>> +        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_REG_MAX; 
>> i++) {
>> +        ret = i2c_smbus_write_byte_data(chip->client,
>> +                TSL2X7X_CMD_REG + i, *dev_reg++);
>> +        if (ret<  0) {
>> +            dev_err(&chip->client->dev,
>> +            "tsl2x7x_chip_on failed on write to reg %d.\n", 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 | 
>> 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, "tsl2x7x_chip_on failed on 2nd 
>> CTRL reg.\n");
>> +        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");
>> +        /* first time interrupt */
>> +        chip->init_done = 0;
>> +
>> +
>> +    reg_val = TSL2X7X_CNTL_PWR_ON;
>> +
>> +    if (chip->tsl2x7x_settings.interrupts_en == 0x10)
>> +        reg_val |= CNTL_ADC_ENBL;
>> +
>> +    if (chip->tsl2x7x_settings.interrupts_en == 0x20)
>> +        reg_val |= CNTL_PROX_DET_ENBL;
>> +
>> +    if (chip->tsl2x7x_settings.interrupts_en == 0x30)
>> +        reg_val |= (CNTL_ADC_ENBL | 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,
>> +        "tsl2x7x_i2c_write to device failed in 
>> tsl2x7x_IOCTL_INT_SET.\n");
>> +
>> +    /* Clear out any initial interrupts  */
>> +    ret = i2c_smbus_write_byte(chip->client,
>> +        TSL2X7X_CMD_REG | TSL2X7X_CMD_SPL_FN |
>> +        TSL2X7X_CMD_PROXALS_INTCLR);
>> +    if (ret<  0) {
>> +        dev_err(&chip->client->dev,
>> +            "tsl2x7x_i2c_write_command failed in tsl2x7x_chip_on\n");
>> +        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;
>> +}
>> +
>> +/**
>> + * Integer Square Root
>> + * We need an integer version since 1st Floating point is not allowed
>> + * in driver world, 2nd, cannot count on the devices having a FPU, and
>> + * 3rd software FP emulation may be excessive.
> It's exactly this sort of thing that supports my argument below that 
> this ought to be
> in userspace...
>> + */
>> +static unsigned long tsl2x7x_isqrt(unsigned long x)
>> +{
>> +    register unsigned long op, res, one;
>> +    op = x;
>> +    res = 0;
>> +
>> +    one = 1<<  30;
>> +    while (one>  op)
>> +        one>>= 2;
>> +
>> +    while (one != 0) {
>> +        if (op>= res + one) {
>> +            op -= res + one;
>> +            res += one<<  1;
>> +        }
>> +        res>>= 1;
>> +        one>>= 2;
>> +    }
>> +    return res;
>> +}
>> +
>> +/*
>> + * 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 = 0xffff;
>> +    max = 0;
>> +    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 = tsl2x7x_isqrt((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.
>> + */
> I guess you have a demand for this, but generally I'd feel this had more
> of a place in userspace than down in the kernel...
>> +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,
>> +            "max prox samples cal is too big: %d\n",
>> +            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 |= 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);
>> +}
>> +
>> +/* ---------- Sysfs Interface Functions ------------- */
> Don't bother with the structure comments. It's readily apparent and they
> have a nasty habit of not being true after a year or two.
>> +
>> +
>> +static ssize_t tsl2x7x_power_state_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    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 *dev_info = dev_get_drvdata(dev);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
> strtobool
>> +
>> +    if (value == 0)
>> +        tsl2x7x_chip_off(dev_info);
>> +    else
>> +        tsl2x7x_chip_on(dev_info);
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_proximity_detect_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    tsl2x7x_prox_poll(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->prox_cur_info.prox_event);
>> +}
>> +
>> +static ssize_t tsl2x7x_gain_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +        tsl2X7X_als_gainadj[chip->tsl2x7x_settings.als_gain].ch0);
>> +}
>> +
>> +static ssize_t tsl2x7x_gain_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    switch (value) {
>> +    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:
>> +        chip->tsl2x7x_settings.als_gain = 3;
>> +        break;
> It's unlikely but you probably want to verify that 120 is only used
> for parts where it is valid.  (for consistency sake).
>> +    case 128:
>> +        chip->tsl2x7x_settings.als_gain = 3;
>> +        break;
>> +
>> +    default:
>> +        dev_err(dev,
>> +            "Invalid Gain Index\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_gain_available_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    if (strncmp(chip->client->name, "tsl2772", 7) == 0)
>> +        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_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            (1<<  chip->tsl2x7x_settings.prox_gain));
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_gain_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    switch (value) {
>> +    case 1:
> maybe use the relevant log2? Then you just need to check if it's too big.
>> +        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:
>> +        dev_err(dev,
>> +            "Invalid Prox Gain Index\n");
>> +        return -EINVAL;
>> +    }
>> +
>> +    return len;
>> +}
>> +
>> +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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    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 ssize_t tsl2x7x_als_time_available_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    return snprintf(buf, PAGE_SIZE, "%s\n",
>> +        "50 100 150 200 250 300 350 400 450 500 550 600 650");
>> +}
>> +
>> +static ssize_t tsl2x7x_als_trim_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->tsl2x7x_settings.als_gain_trim);
>> +}
>> +
>> +static ssize_t tsl2x7x_als_trim_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    if (value)
> I'd have thought 0 was a valid gain_trim?
>> +        chip->tsl2x7x_settings.als_gain_trim = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_als_cal_target_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    if (value)
>> +        chip->tsl2x7x_settings.als_cal_target = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_interrupts_en_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    if (chip->tsl2x7x_settings.interrupts_en&  0x10)
>> +        return snprintf(buf, PAGE_SIZE, "%d\n", 1);
>> +    else
>> +        return snprintf(buf, PAGE_SIZE, "%d\n", 0);
> snprintf(buf, PAGE_SIZE, "%d\n", 
> !!(chip->tsl2x7x_settings.interrupts_en & 0x10));
>> +}
>> +
>> +static ssize_t tsl2x7x_interrupts_en_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
> strotobool (and use the iio_chan_spec and relevant callbacks please.)
>> +    if (value>  1)
>> +        return -EINVAL;
>> +    if (value)
>> +        chip->tsl2x7x_settings.interrupts_en |= 0x10;
>> +    else
>> +        chip->tsl2x7x_settings.interrupts_en&= 0x20;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_interrupt_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    if (chip->tsl2x7x_settings.interrupts_en&  0x20)
>> +        return snprintf(buf, PAGE_SIZE, "%d\n", 1);
>> +    else
>> +        return snprintf(buf, PAGE_SIZE, "%d\n", 0);
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_interrupt_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    if (value>  1)
>> +        return -EINVAL;
>> +    if (value)
>> +        chip->tsl2x7x_settings.interrupts_en |= 0x20;
>> +    else
>> +        chip->tsl2x7x_settings.interrupts_en&= 0x10;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_als_thresh_low_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->tsl2x7x_settings.als_thresh_low);
>> +}
>> +
>> +static ssize_t tsl2x7x_als_thresh_low_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    chip->tsl2x7x_settings.als_thresh_low = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_als_thresh_high_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->tsl2x7x_settings.als_thresh_high);
>> +}
>> +
>> +static ssize_t tsl2x7x_als_thresh_high_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    chip->tsl2x7x_settings.als_thresh_high = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_thresh_low_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->tsl2x7x_settings.prox_thres_low);
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_thresh_low_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    chip->tsl2x7x_settings.prox_thres_low = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_thresh_high_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->tsl2x7x_settings.prox_thres_high);
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_thresh_high_store(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    chip->tsl2x7x_settings.prox_thres_high = value;
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_prox_data_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    tsl2x7x_prox_poll(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n",
>> +            chip->prox_cur_info.prox_data);
>> +}
>> +
>> +/* 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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "0x%02X\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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
>> +
>> +    chip->tsl2x7x_settings.als_persistence = value;
>> +
>> +    return len;
>> +}
>> +
>> +static
>> +ssize_t tsl2x7x_als_persistence_available_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    return snprintf(buf, PAGE_SIZE, "0x00 - 0xFF (0 - 255)\n");
>> +}
>> +
>> +static
>> +ssize_t tsl2x7x_lux_show(struct device *dev, struct device_attribute 
>> *attr,
>> +    char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    int lux;
>> +
>> +    lux = tsl2x7x_get_lux(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "%d\n", lux);
>> +}
>> +
>> +static
>> +ssize_t tsl2x7x_adc_show(struct device *dev, struct device_attribute 
>> *attr,
>> +    char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    tsl2x7x_get_lux(dev_info);
>> +
>> +    return snprintf(buf, PAGE_SIZE, "0x%08x\n",
>> +            ((chip->als_cur_info.als_ch0<<  16) |
>> +                    chip->als_cur_info.als_ch1));
> Your outputing in hex? Please don't and please do this through an 
> iio_chan_spec
> structure and read_raw callback.
>> +}
>> +
>> +static ssize_t tsl2x7x_do_calibrate(struct device *dev,
>> +    struct device_attribute *attr, const char *buf, size_t len)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
> strtobool?
>> +
>> +    if (value == 1)
>> +        tsl2x7x_als_calibrate(dev_info);
>> +
>> +    return len;
>> +}
>> +
>> +static ssize_t tsl2x7x_luxtable_show(struct device *dev,
>> +    struct device_attribute *attr, char *buf)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    int i;
>> +    int offset = 0;
>> +
>> +    i = 0;
>> +    while (i<  (MAX_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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    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(dev_info);
>> +
>> +    /* 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 *dev_info = dev_get_drvdata(dev);
>> +    unsigned long value;
>> +
>> +    if (kstrtoul(buf, 0,&value))
>> +        return -EINVAL;
> strtobool probably?
>> +
>> +    if (value == 1)
>> +        tsl2x7x_prox_cal(dev_info);
>> +
>> +    return len;
>> +}
>> +
>> +static DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_power_state_show, tsl2x7x_power_state_store);
>> +
>> +static DEVICE_ATTR(proximity_raw, S_IRUGO,
>> +        tsl2x7x_proximity_detect_show, NULL);
>> +
>> +static DEVICE_ATTR(intensity_infrared_raw, S_IRUGO,
>> +        tsl2x7x_prox_data_show, NULL);
>> +
>> +
>> +static DEVICE_ATTR(proximity_calibscale, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_prox_gain_show, tsl2x7x_prox_gain_store);
>> +static DEVICE_ATTR(proximity_calibscale_available, S_IRUGO,
>> +        tsl2x7x_prox_gain_available_show, NULL);
>> +
>> +static DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_gain_show, tsl2x7x_gain_store);
>> +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_integration_time_available, S_IRUGO,
>> +        tsl2x7x_als_time_available_show, NULL);
>> +
>> +static DEVICE_ATTR(illuminance0_calibbias, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_als_trim_show, tsl2x7x_als_trim_store);
>> +
>> +static DEVICE_ATTR(illuminance0_input_target, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_als_cal_target_show, tsl2x7x_als_cal_target_store);
>> +
>> +static DEVICE_ATTR(illuminance0_both_raw, S_IRUGO, tsl2x7x_adc_show,
>> +        NULL);
>> +
>> +static DEVICE_ATTR(illuminance0_input, S_IRUGO, tsl2x7x_lux_show,
>> +        NULL);
>> +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(illuminance0_thresh_falling_value, S_IRUGO | 
>> S_IWUSR,
>> +        tsl2x7x_als_thresh_low_show, tsl2x7x_als_thresh_low_store);
>> +
>> +static DEVICE_ATTR(illuminance0_thresh_rising_value, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_als_thresh_high_show, tsl2x7x_als_thresh_high_store);
>> +
>> +static DEVICE_ATTR(sampling_frequency, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_als_persistence_show, tsl2x7x_als_persistence_store);
>> +
>> +static DEVICE_ATTR(sampling_frequency_available, S_IRUGO,
>> +        tsl2x7x_als_persistence_available_show, NULL);
>> +
>> +static DEVICE_ATTR(proximity_thresh_rising_value, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_prox_thresh_high_show, tsl2x7x_prox_thresh_high_store);
>> +
>> +static DEVICE_ATTR(proximity_thresh_falling_value, S_IRUGO | S_IWUSR,
>> +        tsl2x7x_prox_thresh_low_show, tsl2x7x_prox_thresh_low_store);
>> +
> A lot of what we having in here is standard and should be done via a 
> iio_chan_spec
> definition.  That'll cover calibscal, calibbias, input, raw, 
> thresh_rising_value, thresh_falling_value
> (which incidentally should be in the event attributes).
> That leaves us with
> power_state - ideally not here but handled via a kernel wide control 
> (it gets suggested,
> everyone agrees it's a good idea and no one implements it...) but such 
> is life.
> calibscale_available - fine
> integration_time - Propose adding this to the iio_chan_spec via 
> info_mask.
> integration_time_available - fine.
> input_target -> target_input but otherwise fine.
> calibrate -> fine
> lux_table -> fine
> sampling_frequency, frequency_available fine.
>
> Just for reference, we really need to come up with a way of handing 
> 'available' within
> the iio_chan_spec structures and the same for sampling frequency. Not 
> your problem
> but  if I say it often enough hopefully someone else will do it ;)
>> +static struct attribute *tsl2571_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_illuminance0_calibscale.attr,
>> + &dev_attr_illuminance0_calibscale_available.attr,
>> + &dev_attr_illuminance0_integration_time.attr,
>> + &dev_attr_illuminance0_integration_time_available.attr,
>> + &dev_attr_illuminance0_calibbias.attr,
>> + &dev_attr_illuminance0_input_target.attr,
>> + &dev_attr_illuminance0_both_raw.attr,
>> + &dev_attr_illuminance0_input.attr,
>> + &dev_attr_illuminance0_calibrate.attr,
>> + &dev_attr_illuminance0_lux_table.attr,
>> + &dev_attr_illuminance0_thresh_falling_value.attr,
>> + &dev_attr_illuminance0_thresh_rising_value.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute *tsl2671_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_proximity_raw.attr,
>> + &dev_attr_intensity_infrared_raw.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> + &dev_attr_proximity_thresh_rising_value.attr,
>> + &dev_attr_proximity_thresh_falling_value.attr,
>> + &dev_attr_proximity_calibrate.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute *tsl2771_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_proximity_raw.attr,
>> + &dev_attr_intensity_infrared_raw.attr,
>> + &dev_attr_illuminance0_calibscale.attr,
>> + &dev_attr_illuminance0_calibscale_available.attr,
>> + &dev_attr_illuminance0_integration_time.attr,
>> + &dev_attr_illuminance0_integration_time_available.attr,
>> + &dev_attr_illuminance0_calibbias.attr,
>> + &dev_attr_illuminance0_input_target.attr,
>> + &dev_attr_illuminance0_both_raw.attr,
>> + &dev_attr_illuminance0_input.attr,
>> + &dev_attr_illuminance0_calibrate.attr,
>> + &dev_attr_illuminance0_lux_table.attr,
>> + &dev_attr_illuminance0_thresh_falling_value.attr,
>> + &dev_attr_illuminance0_thresh_rising_value.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> + &dev_attr_proximity_thresh_rising_value.attr,
>> + &dev_attr_proximity_thresh_falling_value.attr,
>> + &dev_attr_proximity_calibrate.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute *tsl2572_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_illuminance0_calibscale.attr,
>> + &dev_attr_illuminance0_calibscale_available.attr,
>> + &dev_attr_illuminance0_integration_time.attr,
>> + &dev_attr_illuminance0_integration_time_available.attr,
>> + &dev_attr_illuminance0_calibbias.attr,
>> + &dev_attr_illuminance0_input_target.attr,
>> + &dev_attr_illuminance0_both_raw.attr,
>> + &dev_attr_illuminance0_input.attr,
>> + &dev_attr_illuminance0_calibrate.attr,
>> + &dev_attr_illuminance0_lux_table.attr,
>> + &dev_attr_illuminance0_thresh_falling_value.attr,
>> + &dev_attr_illuminance0_thresh_rising_value.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> +    NULL
>> +};
>> +
>> +static struct attribute *tsl2672_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_proximity_raw.attr,
>> + &dev_attr_intensity_infrared_raw.attr,
>> + &dev_attr_proximity_calibscale.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> + &dev_attr_proximity_thresh_rising_value.attr,
>> + &dev_attr_proximity_thresh_falling_value.attr,
>> + &dev_attr_proximity_calibrate.attr,
>> +    NULL
>> +};
>> +
> Indent the same for all of these.
>> +static struct attribute *tsl2772_device_attrs[] = {
>> + &dev_attr_power_state.attr,
>> + &dev_attr_proximity_raw.attr,
>> + &dev_attr_proximity_calibscale.attr,
>> + &dev_attr_intensity_infrared_raw.attr,
>> + &dev_attr_illuminance0_calibscale.attr,
>> + &dev_attr_illuminance0_calibscale_available.attr,
>> + &dev_attr_illuminance0_integration_time.attr,
>> + &dev_attr_illuminance0_integration_time_available.attr,
>> + &dev_attr_illuminance0_calibbias.attr,
>> + &dev_attr_illuminance0_input_target.attr,
>> + &dev_attr_illuminance0_both_raw.attr,
>> + &dev_attr_illuminance0_input.attr,
>> + &dev_attr_illuminance0_calibrate.attr,
>> + &dev_attr_illuminance0_lux_table.attr,
>> + &dev_attr_illuminance0_thresh_falling_value.attr,
>> + &dev_attr_illuminance0_thresh_rising_value.attr,
>> + &dev_attr_sampling_frequency.attr,
>> + &dev_attr_sampling_frequency_available.attr,
>> + &dev_attr_proximity_thresh_rising_value.attr,
>> + &dev_attr_proximity_thresh_falling_value.attr,
>> + &dev_attr_proximity_calibrate.attr,
>> + &dev_attr_proximity_calibscale_available.attr,
>> +        NULL
>> +};
>> +
>> +static struct attribute_group tsl2X7X_dev_attr_group_tbl[] = {
>> +    [tsl2571] = {
>> +        .attrs = tsl2571_device_attrs,
>> +    },
>> +    [tsl2671] = {
>> +        .attrs = tsl2671_device_attrs,
>> +    },
>> +    [tmd2671] = {
>> +        .attrs = tsl2671_device_attrs,
>> +    },
>> +    [tsl2771] = {
>> +        .attrs = tsl2771_device_attrs,
>> +    },
>> +    [tmd2771] = {
>> +        .attrs = tsl2771_device_attrs,
>> +    },
>> +    [tsl2572] = {
>> +        .attrs = tsl2572_device_attrs,
>> +    },
>> +    [tsl2672] = {
>> +        .attrs = tsl2672_device_attrs,
>> +    },
>> +    [tmd2672] = {
>> +        .attrs = tsl2672_device_attrs,
>> +    },
>> +    [tsl2772] = {
>> +        .attrs = tsl2772_device_attrs,
>> +    },
>> +    [tmd2772] = {
>> +        .attrs = tsl2772_device_attrs,
>> +    },
>> +
>> +};
>> +
>> +static IIO_DEVICE_ATTR(illuminance_thresh_both_en,
>> +        S_IRUGO | S_IWUSR,
>> +        tsl2x7x_interrupts_en_show, tsl2x7x_interrupts_en_store, 0);
>> +
>> +static IIO_DEVICE_ATTR(proximity_thresh_both_en,
>> +        S_IRUGO | S_IWUSR,
>> +        tsl2x7x_prox_interrupt_show, tsl2x7x_prox_interrupt_store, 0);
>> +
>> +static struct attribute *tsl2X7X_prox_event_attributes[] = {
>> + &iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute *tsl2X7X_als_event_attributes[] = {
>> + &iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
>> +    NULL,
>> +};
>> +
>> +static struct attribute *tsl2X7X_proxals_event_attributes[] = {
>> + &iio_dev_attr_illuminance_thresh_both_en.dev_attr.attr,
>> + &iio_dev_attr_proximity_thresh_both_en.dev_attr.attr,
>> +    NULL,
>> +};
> The way we have handled this for some similar devices is to just have 
> the two enables done
> via the iio_chan_spec and chain them both to read / write the same 
> value.  (By which I mean
> the rising enable and falling enable).  It slightly clunkier as an 
> interface, but saves code and will
> ultimately enable them to be accessed by consumer drivers within the 
> kernel (not that we've
> even started looking at event pass
>> +
>> +static struct attribute_group tsl2X7X_event_attr_group_tbl[] = {
>> +    [tsl2571] = {
>> +        .attrs = tsl2X7X_als_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tsl2671] = {
>> +        .attrs = tsl2X7X_prox_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tmd2671] = {
>> +        .attrs = tsl2X7X_prox_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tsl2771] = {
>> +        .attrs = tsl2X7X_proxals_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tmd2771] = {
>> +        .attrs = tsl2X7X_proxals_event_attributes,
>> +        .name  = "events",
>> +    },
>> +
>> +    [tsl2572] = {
>> +        .attrs = tsl2X7X_als_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tsl2672] = {
>> +        .attrs = tsl2X7X_prox_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tmd2672] = {
>> +        .attrs = tsl2X7X_prox_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tsl2772] = {
>> +        .attrs = tsl2X7X_proxals_event_attributes,
>> +        .name  = "events",
>> +    },
>> +    [tmd2772] = {
>> +        .attrs = tsl2X7X_proxals_event_attributes,
>> +        .name  = "events",
>> +    }
>> +};
>> +
> Being fussy, but only one blank line please.
>> +
>> +/* Use the default register values to identify the Taos device */
>> +static int tsl2x7x_device_id(unsigned char *bufp, int target)
>> +{
>> +    switch (target) {
>> +    case tsl2571:
>> +    case tsl2671:
>> +    case tsl2771:
>> +        return ((bufp[TSL2X7X_CHIPID]&  0xf0) == TRITON_ID);
>> +    break;
>> +    case tmd2671:
>> +    case tmd2771:
>> +        return ((bufp[TSL2X7X_CHIPID]&  0xf0) == HALIBUT_ID);
>> +    break;
>> +    case tsl2572:
>> +    case tsl2672:
>> +    case tmd2672:
>> +    case tsl2772:
>> +    case tmd2772:
>> +        return ((bufp[TSL2X7X_CHIPID]&  0xf0) == SWORDFISH_ID);
>> +    break;
>> +    }
>> +
>> +    return -EINVAL;
>> +}
>> +
>> +static const struct iio_info tsl2X7X_info[] = {
>> +    [tsl2571] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2571],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2571],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tsl2671] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2671],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2671],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tmd2671] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2671],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2671],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tsl2771] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2771],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2771],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tmd2771] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2771],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2771],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tsl2572] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2572],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2572],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tsl2672] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2672],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2672],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tmd2672] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2672],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2672],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tsl2772] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2772],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2772],
>> +            .driver_module = THIS_MODULE,
>> +    },
>> +    [tmd2772] = {
>> +            .attrs =&tsl2X7X_dev_attr_group_tbl[tsl2772],
>> +            .event_attrs =&tsl2X7X_event_attr_group_tbl[tsl2772],
>> +            .driver_module = THIS_MODULE,
> A lot of repeats in here, could you not have fewer entries?  Just 
> reuse the enum values
> in the i2c id table below.
>> +    },
>> +
>> +};
>> +
>> +/*
>> + * Run-time interrupt handler - depending on whether the device is 
>> in ambient
>> + * light sensing interrupt mode, this handler can queue up
>> + * a thread, to handle valid interrupts.
>> + */
>> +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_INTCLR);
>> +    if (ret<  0)
>> +        dev_err(&chip->client->dev,
>> +        "Failed to clear irq from event handler. err = %d\n", ret);
>> +
>> +    return IRQ_HANDLED;
>> +}
>> +
>> +/*
>> + * Client probe function.
>> + */
>> +static int __devinit tsl2x7x_probe(struct i2c_client *clientp,
>> +    const struct i2c_device_id *id)
>> +{
>> +    int i, ret;
>> +    unsigned char buf[TSL2X7X_MAX_DEVICE_REGS];
>> +    struct iio_dev *indio_dev =
>> +            iio_allocate_device(sizeof(struct tsl2X7X_chip));
> Ugly to do non trivial allocation in the variable definitions.  Have
>     struct iio_dev *indio_dev;
>     struct tsl2x7x_chip *chip;
>
>     indio_dev = iio_allocate_device(sizeof(*chip));
>     if (indio_dev == NULL) {
>     }
> chip = iio_priv(indio_dev);
> etc.
>> +    struct tsl2X7X_chip    *chip = iio_priv(indio_dev);
>> +
>> +    if (indio_dev == NULL) {
>> +        ret = -ENOMEM;
>> +        dev_err(&clientp->dev, "iio allocation failed\n");
>> +        goto fail1;
>> +    }
>> +
>> +    chip = iio_priv(indio_dev);
>> +    chip->client = clientp;
>> +    i2c_set_clientdata(clientp, indio_dev);
>> +
>> +    mutex_init(&chip->als_mutex);
>> +    mutex_init(&chip->prox_mutex);
>> +
>> +    chip->tsl2x7x_chip_status = TSL2X7X_CHIP_UNKNOWN;
>> +
>> +    chip->pdata = clientp->dev.platform_data;
>> +    if (chip->pdata&&  chip->pdata->init)
>> +            chip->pdata->init(clientp);
>> +
>> +    for (i = 0; i<  TSL2X7X_MAX_DEVICE_REGS; i++) {
>> +        ret = i2c_smbus_write_byte(clientp,
>> +                (TSL2X7X_CMD_REG | (TSL2X7X_CNTRL + i)));
>> +        if (ret<  0) {
>> +            dev_err(&clientp->dev, "i2c_smbus_write_bytes() to cmd "
>> +            "reg failed in tsl2x7x_probe(), err = %d\n", ret);
>> +            goto fail1;
>> +        }
>> +        ret = i2c_smbus_read_byte(clientp);
>> +        if (ret<  0) {
>> +            dev_err(&clientp->dev, "i2c_smbus_read_byte from "
>> +            "reg failed in tsl2x7x_probe(), err = %d\n", ret);
>> +
>> +            goto fail1;
>> +        }
>> +        buf[i] = ret;
>> +    }
>> +
>> +    if ((!tsl2x7x_device_id(buf, id->driver_data)) ||
>> +            (tsl2x7x_device_id(buf, id->driver_data) == -EINVAL)) {
>> +        dev_info(&chip->client->dev, "i2c device found but does not 
>> match "
>> +            "expected id in tsl2x7x_probe()\n");
>> +        goto fail1;
>> +    }
>> +
>> +    chip->id = id->driver_data;
>> +
>> +    ret = i2c_smbus_write_byte(clientp, (TSL2X7X_CMD_REG | 
>> TSL2X7X_CNTRL));
>> +    if (ret<  0) {
>> +        dev_err(&clientp->dev, "i2c_smbus_write_byte() to cmd reg "
>> +            "failed in tsl2x7x_probe(), err = %d\n", ret);
>> +        goto fail1;
>> +    }
>> +
>> +    indio_dev->info =&tsl2X7X_info[id->driver_data];
>> +    indio_dev->dev.parent =&clientp->dev;
>> +    indio_dev->modes = INDIO_DIRECT_MODE;
>> +    indio_dev->name = chip->client->name;
>> +    indio_dev->channels = tsl2X7X_channels;
>> +    indio_dev->num_channels = ARRAY_SIZE(tsl2X7X_channels);
>> +
>> +    ret = iio_device_register(indio_dev);
>> +    if (ret) {
>> +        dev_err(&clientp->dev, "iio registration failed\n");
>> +        goto fail2;
> one would normally expect a later failure to result in more needing to 
> be undone,
> but it appears to be the other way around here.
>> +    }
>> +
>> +    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, "irq request failed");
> Unwinding after this error?  Generally set the irq up and make the 
> iio_device_register
> pretty much the last thing to happen rather than this way around.  
> Nothing can
> touch the device before the iio_device_register so it avoids the 
> chance of race
> conditions without having to be careful!
>> +    }
>> +
>> +    /* Load up the defaults */
>> +    tsl2x7x_defaults(chip);
>> +
>> +    /* Make sure the chip is on */
>> +    tsl2x7x_chip_on(indio_dev);
>> +
>> +    dev_info(&clientp->dev, "%s Light sensor found.\n", id->name);
>> +
>> +    return 0;
>> +
>> +fail1:
>> +    if (chip->pdata&&  chip->pdata->teardown)
>> +        chip->pdata->teardown(clientp);
>> +    iio_free_device(indio_dev);
>> +
>> +fail2:
>> +    return ret;
> loose the bonus blank line.
>> +
>> +}
>> +
>> +
>> +static int tsl2x7x_suspend(struct device *dev)
>> +{
>> +    struct iio_dev *dev_info = dev_get_drvdata(dev);
> standardize the naming please.  indio_dev  preferably but consistency 
> is most
> important.
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +
>> +    int ret = 0;
>> +
>> +    /* Blocks if work is active */
>> +    cancel_work_sync(&chip->work_thresh);
>> +
>> +    if (chip->tsl2x7x_chip_status == TSL2X7X_CHIP_WORKING) {
>> +        ret = tsl2x7x_chip_off(dev_info);
>> +        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 *dev_info = dev_get_drvdata(dev);
>> +    struct tsl2X7X_chip *chip = iio_priv(dev_info);
>> +    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(dev_info);
>> +
>> +    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);
> Err, the two lines above seem a little implausible.
>> +
>> +    tsl2x7x_chip_off(indio_dev);
>> +
>> +    if (client->irq)
>> +        free_irq(client->irq, chip->client->name);
>> +
>> +    flush_scheduled_work();
>> +
>> +    iio_device_unregister(chip->iio_dev);
> or use the indio_dev pointer you already have.  There is rarely a reason
> to have a pointer to the iio_dev in the driver private bit.
>> +    kfree(chip);
>> +
>> +    if (chip->pdata&&  chip->pdata->teardown)
>> +        chip->pdata->teardown(client);
>> +
>> +    return 0;
>> +}
>> +
>> +static struct i2c_device_id tsl2x7x_idtable[] = {
> I'm guessing there is a magic ordering here, but might be
> nicer just to go with alphabetical order..
>> +    { "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),
>> +};
>> +
>> +static int __init tsl2x7x_init(void)
>> +{
>> +    return i2c_add_driver(&tsl2x7x_driver);
>> +}
>> +
>> +static void __exit tsl2x7x_exit(void)
>> +{
>> +    i2c_del_driver(&tsl2x7x_driver);
>> +}
>> +
>> +module_init(tsl2x7x_init);
>> +module_exit(tsl2x7x_exit);
> Some nice new macros to handle this boiler plate.
> see module_i2c_driver in include/linux/i2c.h
>
>> +
>> +MODULE_AUTHOR("J. August Brenner<jbrenner@taosinc.com>");
>> +MODULE_DESCRIPTION("TAOS tsl2X7X ambient 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..9a64412
>> --- /dev/null
>> +++ b/drivers/staging/iio/light/tsl2x7x_core.h
>> @@ -0,0 +1,138 @@
>> +/*
>> + * 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>
>> +
>> +#include "../iio.h"
> loose this header and just forward declare the pointer.
> e.g. struct iio_dev;
>
>> +
>> +/* 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_DEVICE_REGS 32
>> +#define TSL2X7X_REG_MAX         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 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 CNTL_REG_CLEAR                 0x00
>> +#define CNTL_PROX_INT_ENBL             0X20
>> +#define CNTL_ALS_INT_ENBL              0X10
>> +#define TSL2X7X_CNTL_WAIT_TMR_ENBL     0X08
>> +#define CNTL_PROX_DET_ENBL             0X04
>> +#define CNTL_ADC_ENBL                  0x02
>> +#define TSL2X7X_CNTL_PWRON             0x01
>> +#define CNTL_ALSPON_ENBL               0x03
>> +#define CNTL_INTALSPON_ENBL            0x13
>> +#define CNTL_PROXPON_ENBL              0x0F
>> +#define CNTL_INTPROXPON_ENBL           0x2F
>> +#define TSL2X7X_CMD_PROXALS_INTCLR     0X07
>> +
> Chances of a clash on these definitions is extremely high,
> Stick a prefix in front of them to avoid that issue.
>> +/*Prox diode to use */
>> +#define DIODE0                         0x10
>> +#define DIODE1                         0x20
>> +#define DIODE_BOTH                     0x30
>> +
>> +/* LED Power */
>> +#define mA100                          0x00
>> +#define mA50                           0x40
>> +#define mA25                           0x80
>> +#define mA13                           0xD0
>> +
>> +/* Max number of segments allowable in LUX table */
>> +#define MAX_TABLE_SIZE        9
>> +#define MAX_DEFAULT_TABLE_BYTES (sizeof(int) * MAX_TABLE_SIZE)
>> +
>> +struct tsl2x7x_lux {
>> +    unsigned int ratio;
>> +    unsigned int ch0;
>> +    unsigned int ch1;
>> +};
>> +
>> +
> Kernel doc for comments would be preferable.  Can you also give a 
> description of what
> actually tends to occur in these callbacks?  I can't help feeling 
> there are rather more of
> them than we'd normally expect.
>> +/* struct tsl2x7x_platform_data - Assume all varients will have this */
>> +struct tsl2X7X_platform_data {
>> +    int (*platform_power)(struct device *dev, pm_message_t);
>> +    /* The following callback gets called when the TSL2772 is 
>> powered on */
>> +    int (*power_on)      (struct iio_dev *indio_dev);
>> +    /* The following callback gets called when the TSL2772 is 
>> powered off */
>> +    int (*power_off)     (struct i2c_client *dev);
>> +    /* The following callback gets called when the driver is added */
>> +    int (*init)          (struct i2c_client *dev);
>> +    /* The following callback gets called when the driver is removed */
>> +    int (*teardown)      (struct i2c_client *dev);
>> +    /* These are the device specific glass coefficents used to
>> +     * calculate Lux */
>> +    struct tsl2x7x_lux platform_lux_table[MAX_TABLE_SIZE];
>> +};
>> +
>> +#endif /* __TSL2X7X_H */
>> -- 
>>
>
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


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

end of thread, other threads:[~2012-02-13 15:29 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-10 19:06 [PATCH V1] TAOS tsl2x7x Jon Brenner
2012-02-10 19:06 ` Jon Brenner
2012-02-13 15:19 ` Jonathan Cameron
2012-02-13 15:28   ` 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.