nitpicking inline > This driver is simple, uses the latest interfaces and contains few if > any controversial elements. All of its interfaces have been in place > for a long time now. Hence let's move it out of staging. > > Signed-off-by: Jonathan Cameron <jic23@xxxxxxxxxx> > --- > drivers/iio/light/Kconfig | 10 + > drivers/iio/light/Makefile | 1 + > drivers/iio/light/tsl2563.c | 899 ++++++++++++++++++++++++++++++++++++ > drivers/staging/iio/light/Kconfig | 10 - > drivers/staging/iio/light/Makefile | 1 - > drivers/staging/iio/light/tsl2563.c | 899 ------------------------------------ > 6 files changed, 910 insertions(+), 910 deletions(-) > > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig > index 1763c9b..2dd472a 100644 > --- a/drivers/iio/light/Kconfig > +++ b/drivers/iio/light/Kconfig > @@ -32,6 +32,16 @@ config SENSORS_LM3533 > changes. The ALS-control output values can be set per zone for the > three current output channels. > > +config SENSORS_TSL2563 > + tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors" > + depends on I2C > + help > + If you say yes here you get support for the Taos TSL2560, > + TSL2561, TSL2562 and TSL2563 ambient light sensors. > + > + This driver can also be built as a module. If so, the module > + will be called tsl2563. > + > config VCNL4000 > tristate "VCNL4000 combined ALS and proximity sensor" > depends on I2C > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile > index 21a8f0d..040d9c7 100644 > --- a/drivers/iio/light/Makefile > +++ b/drivers/iio/light/Makefile > @@ -4,5 +4,6 @@ > > obj-$(CONFIG_ADJD_S311) += adjd_s311.o > obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o > +obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > obj-$(CONFIG_VCNL4000) += vcnl4000.o > obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o > diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c > new file mode 100644 > index 0000000..1a9adc0 > --- /dev/null > +++ b/drivers/iio/light/tsl2563.c > @@ -0,0 +1,899 @@ > +/* > + * drivers/i2c/chips/tsl2563.c > + * > + * Copyright (C) 2008 Nokia Corporation > + * > + * Written by Timo O. Karjalainen <timo.o.karjalainen@xxxxxxxxx> > + * Contact: Amit Kucheria <amit.kucheria@xxxxxxxxxxxxx> > + * > + * Converted to IIO driver > + * Amit Kucheria <amit.kucheria@xxxxxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or > + * modify it under the terms of the GNU General Public License > + * version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA > + * 02110-1301 USA > + */ > + > +#include <linux/module.h> > +#include <linux/i2c.h> > +#include <linux/interrupt.h> > +#include <linux/irq.h> > +#include <linux/sched.h> > +#include <linux/mutex.h> > +#include <linux/delay.h> > +#include <linux/pm.h> > +#include <linux/err.h> > +#include <linux/slab.h> > + > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/iio/events.h> > +#include "tsl2563.h" > + > +/* Use this many bits for fraction part. */ > +#define ADC_FRAC_BITS (14) > + > +/* Given number of 1/10000's in ADC_FRAC_BITS precision. */ > +#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000)) > + > +/* Bits used for fraction in calibration coefficients.*/ > +#define CALIB_FRAC_BITS (10) > +/* 0.5 in CALIB_FRAC_BITS precision */ > +#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1)) > +/* Make a fraction from a number n that was multiplied with b. */ > +#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b)) > +/* Decimal 10^(digits in sysfs presentation) */ > +#define CALIB_BASE_SYSFS (1000) > + > +#define TSL2563_CMD (0x80) > +#define TSL2563_CLEARINT (0x40) > + > +#define TSL2563_REG_CTRL (0x00) > +#define TSL2563_REG_TIMING (0x01) > +#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */ > +#define TSL2563_REG_LOWHIGH (0x03) > +#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */ > +#define TSL2563_REG_HIGHHIGH (0x05) > +#define TSL2563_REG_INT (0x06) > +#define TSL2563_REG_ID (0x0a) > +#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */ > +#define TSL2563_REG_DATA0HIGH (0x0d) > +#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */ > +#define TSL2563_REG_DATA1HIGH (0x0f) > + > +#define TSL2563_CMD_POWER_ON (0x03) > +#define TSL2563_CMD_POWER_OFF (0x00) > +#define TSL2563_CTRL_POWER_MASK (0x03) > + > +#define TSL2563_TIMING_13MS (0x00) > +#define TSL2563_TIMING_100MS (0x01) > +#define TSL2563_TIMING_400MS (0x02) > +#define TSL2563_TIMING_MASK (0x03) > +#define TSL2563_TIMING_GAIN16 (0x10) > +#define TSL2563_TIMING_GAIN1 (0x00) > + > +#define TSL2563_INT_DISBLED (0x00) > +#define TSL2563_INT_LEVEL (0x10) it there a need to have constants in parenthesis? > +#define TSL2563_INT_PERSIST(n) ((n) & 0x0F) > + > +struct tsl2563_gainlevel_coeff { > + u8 gaintime; > + u16 min; > + u16 max; > +}; > + > +static const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = { > + { > + .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16, > + .min = 0, > + .max = 65534, > + }, { > + .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1, > + .min = 2048, > + .max = 65534, > + }, { > + .gaintime = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1, > + .min = 4095, > + .max = 37177, > + }, { > + .gaintime = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1, > + .min = 3000, > + .max = 65535, > + }, > +}; > + > +struct tsl2563_chip { > + struct mutex lock; > + struct i2c_client *client; > + struct delayed_work poweroff_work; > + > + /* Remember state for suspend and resume functions */ > + bool suspended; > + > + struct tsl2563_gainlevel_coeff const *gainlevel; > + > + u16 low_thres; > + u16 high_thres; > + u8 intr; > + bool int_enabled; > + > + /* Calibration coefficients */ > + u32 calib0; > + u32 calib1; > + int cover_comp_gain; > + > + /* Cache current values, to be returned while suspended */ > + u32 data0; > + u32 data1; > +}; > + > +static int tsl2563_set_power(struct tsl2563_chip *chip, int on) > +{ > + struct i2c_client *client = chip->client; > + u8 cmd; > + > + cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF; > + return i2c_smbus_write_byte_data(client, > + TSL2563_CMD | TSL2563_REG_CTRL, cmd); > +} > + > +/* > + * Return value is 0 for off, 1 for on, or a negative error > + * code if reading failed. > + */ > +static int tsl2563_get_power(struct tsl2563_chip *chip) > +{ > + struct i2c_client *client = chip->client; > + int ret; > + > + ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_CTRL); > + if (ret < 0) > + return ret; > + > + return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON; > +} > + > +static int tsl2563_configure(struct tsl2563_chip *chip) > +{ > + int ret; > + > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_TIMING, > + chip->gainlevel->gaintime); > + if (ret) > + goto error_ret; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_HIGHLOW, > + chip->high_thres & 0xFF); > + if (ret) > + goto error_ret; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_HIGHHIGH, > + (chip->high_thres >> 8) & 0xFF); > + if (ret) > + goto error_ret; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_LOWLOW, > + chip->low_thres & 0xFF); > + if (ret) > + goto error_ret; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_LOWHIGH, > + (chip->low_thres >> 8) & 0xFF); > +/* Interrupt register is automatically written anyway if it is relevant > + so is not here */ proper multi-line comment? > +error_ret: > + return ret; > +} > + > +static void tsl2563_poweroff_work(struct work_struct *work) > +{ > + struct tsl2563_chip *chip = > + container_of(work, struct tsl2563_chip, poweroff_work.work); > + tsl2563_set_power(chip, 0); > +} > + > +static int tsl2563_detect(struct tsl2563_chip *chip) > +{ > + int ret; > + > + ret = tsl2563_set_power(chip, 1); > + if (ret) > + return ret; > + > + ret = tsl2563_get_power(chip); > + if (ret < 0) > + return ret; > + > + return ret ? 0 : -ENODEV; > +} > + > +static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id) > +{ > + struct i2c_client *client = chip->client; > + int ret; > + > + ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_ID); > + if (ret < 0) > + return ret; > + > + *id = ret; > + > + return 0; > +} > + > +/* > + * "Normalized" ADC value is one obtained with 400ms of integration time and > + * 16x gain. This function returns the number of bits of shift needed to > + * convert between normalized values and HW values obtained using given > + * timing and gain settings. > + */ > +static int adc_shiftbits(u8 timing) > +{ > + int shift = 0; > + > + switch (timing & TSL2563_TIMING_MASK) { > + case TSL2563_TIMING_13MS: > + shift += 5; > + break; > + case TSL2563_TIMING_100MS: > + shift += 2; > + break; > + case TSL2563_TIMING_400MS: > + /* no-op */ > + break; > + } > + > + if (!(timing & TSL2563_TIMING_GAIN16)) > + shift += 4; > + > + return shift; > +} > + > +/* Convert a HW ADC value to normalized scale. */ > +static u32 normalize_adc(u16 adc, u8 timing) > +{ > + return adc << adc_shiftbits(timing); > +} > + > +static void tsl2563_wait_adc(struct tsl2563_chip *chip) > +{ > + unsigned int delay; > + > + switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) { > + case TSL2563_TIMING_13MS: > + delay = 14; > + break; > + case TSL2563_TIMING_100MS: > + delay = 101; > + break; > + default: > + delay = 402; > + } > + /* > + * TODO: Make sure that we wait at least required delay but why we > + * have to extend it one tick more? > + */ > + schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2); > +} > + > +static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc) > +{ > + struct i2c_client *client = chip->client; > + > + if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) { > + > + (adc > chip->gainlevel->max) ? > + chip->gainlevel++ : chip->gainlevel--; > + > + i2c_smbus_write_byte_data(client, > + TSL2563_CMD | TSL2563_REG_TIMING, > + chip->gainlevel->gaintime); > + > + tsl2563_wait_adc(chip); > + tsl2563_wait_adc(chip); > + > + return 1; > + } else > + return 0; > +} > + > +static int tsl2563_get_adc(struct tsl2563_chip *chip) > +{ > + struct i2c_client *client = chip->client; > + u16 adc0, adc1; > + int retry = 1; > + int ret = 0; > + > + if (chip->suspended) > + goto out; > + > + if (!chip->int_enabled) { > + cancel_delayed_work(&chip->poweroff_work); > + > + if (!tsl2563_get_power(chip)) { > + ret = tsl2563_set_power(chip, 1); > + if (ret) > + goto out; > + ret = tsl2563_configure(chip); > + if (ret) > + goto out; > + tsl2563_wait_adc(chip); > + } > + } > + > + while (retry) { > + ret = i2c_smbus_read_word_data(client, > + TSL2563_CMD | TSL2563_REG_DATA0LOW); > + if (ret < 0) > + goto out; > + adc0 = ret; > + > + ret = i2c_smbus_read_word_data(client, > + TSL2563_CMD | TSL2563_REG_DATA1LOW); > + if (ret < 0) > + goto out; > + adc1 = ret; > + > + retry = tsl2563_adjust_gainlevel(chip, adc0); > + } > + > + chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime); > + chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime); > + > + if (!chip->int_enabled) > + schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > + > + ret = 0; > +out: > + return ret; > +} > + > +static inline int calib_to_sysfs(u32 calib) > +{ > + return (int) (((calib * CALIB_BASE_SYSFS) + > + CALIB_FRAC_HALF) >> CALIB_FRAC_BITS); > +} > + > +static inline u32 calib_from_sysfs(int value) > +{ > + return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS; > +} > + > +/* > + * Conversions between lux and ADC values. > + * > + * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are > + * appropriate constants. Different constants are needed for different > + * kinds of light, determined by the ratio adc1/adc0 (basically the ratio > + * of the intensities in infrared and visible wavelengths). lux_table below > + * lists the upper threshold of the adc1/adc0 ratio and the corresponding > + * constants. > + */ > + > +struct tsl2563_lux_coeff { > + unsigned long ch_ratio; > + unsigned long ch0_coeff; > + unsigned long ch1_coeff; > +}; > + > +static const struct tsl2563_lux_coeff lux_table[] = { > + { > + .ch_ratio = FRAC10K(1300), > + .ch0_coeff = FRAC10K(315), > + .ch1_coeff = FRAC10K(262), > + }, { > + .ch_ratio = FRAC10K(2600), > + .ch0_coeff = FRAC10K(337), > + .ch1_coeff = FRAC10K(430), > + }, { > + .ch_ratio = FRAC10K(3900), > + .ch0_coeff = FRAC10K(363), > + .ch1_coeff = FRAC10K(529), > + }, { > + .ch_ratio = FRAC10K(5200), > + .ch0_coeff = FRAC10K(392), > + .ch1_coeff = FRAC10K(605), > + }, { > + .ch_ratio = FRAC10K(6500), > + .ch0_coeff = FRAC10K(229), > + .ch1_coeff = FRAC10K(291), > + }, { > + .ch_ratio = FRAC10K(8000), > + .ch0_coeff = FRAC10K(157), > + .ch1_coeff = FRAC10K(180), > + }, { > + .ch_ratio = FRAC10K(13000), > + .ch0_coeff = FRAC10K(34), > + .ch1_coeff = FRAC10K(26), > + }, { > + .ch_ratio = ULONG_MAX, > + .ch0_coeff = 0, > + .ch1_coeff = 0, > + }, > +}; > + > +/* > + * Convert normalized, scaled ADC values to lux. > + */ just a single comment line > +static unsigned int adc_to_lux(u32 adc0, u32 adc1) > +{ > + const struct tsl2563_lux_coeff *lp = lux_table; > + unsigned long ratio, lux, ch0 = adc0, ch1 = adc1; > + > + ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX; > + > + while (lp->ch_ratio < ratio) > + lp++; > + > + lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff; > + > + return (unsigned int) (lux >> ADC_FRAC_BITS); > +} > + > +/*--------------------------------------------------------------*/ > +/* Sysfs interface */ > +/*--------------------------------------------------------------*/ > + > + > +/* Apply calibration coefficient to ADC count. */ > +static u32 calib_adc(u32 adc, u32 calib) > +{ > + unsigned long scaled = adc; > + > + scaled *= calib; > + scaled >>= CALIB_FRAC_BITS; > + > + return (u32) scaled; > +} > + > +static int tsl2563_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int val, > + int val2, > + long mask) > +{ > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + > + if (chan->channel == IIO_MOD_LIGHT_BOTH) > + chip->calib0 = calib_from_sysfs(val); > + else > + chip->calib1 = calib_from_sysfs(val); > + > + return 0; > +} > + > +static int tsl2563_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, > + int *val2, > + long m) > +{ > + int ret = -EINVAL; > + u32 calib0, calib1; > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + > + mutex_lock(&chip->lock); > + switch (m) { > + case IIO_CHAN_INFO_RAW: > + case IIO_CHAN_INFO_PROCESSED: > + switch (chan->type) { > + case IIO_LIGHT: > + ret = tsl2563_get_adc(chip); > + if (ret) > + goto error_ret; > + calib0 = calib_adc(chip->data0, chip->calib0) * > + chip->cover_comp_gain; > + calib1 = calib_adc(chip->data1, chip->calib1) * > + chip->cover_comp_gain; > + *val = adc_to_lux(calib0, calib1); > + ret = IIO_VAL_INT; > + break; > + case IIO_INTENSITY: > + ret = tsl2563_get_adc(chip); > + if (ret) > + goto error_ret; > + if (chan->channel == 0) > + *val = chip->data0; > + else > + *val = chip->data1; > + ret = IIO_VAL_INT; > + break; > + default: > + break; > + } > + break; > + > + case IIO_CHAN_INFO_CALIBSCALE: > + if (chan->channel == 0) > + *val = calib_to_sysfs(chip->calib0); > + else > + *val = calib_to_sysfs(chip->calib1); > + ret = IIO_VAL_INT; > + break; > + default: > + ret = -EINVAL; > + goto error_ret; > + } > + > +error_ret: > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static const struct iio_chan_spec tsl2563_channels[] = { > + { > + .type = IIO_LIGHT, > + .indexed = 1, > + .info_mask = IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT, > + .channel = 0, > + }, { > + .type = IIO_INTENSITY, > + .modified = 1, > + .channel2 = IIO_MOD_LIGHT_BOTH, > + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > + .event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_RISING) | > + IIO_EV_BIT(IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_FALLING)), > + }, { > + .type = IIO_INTENSITY, > + .modified = 1, > + .channel2 = IIO_MOD_LIGHT_IR, > + .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | > + IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > + } > +}; > + > +static int tsl2563_read_thresh(struct iio_dev *indio_dev, > + u64 event_code, > + int *val) > +{ > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + > + switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > + case IIO_EV_DIR_RISING: > + *val = chip->high_thres; > + break; > + case IIO_EV_DIR_FALLING: > + *val = chip->low_thres; > + break; > + default: > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int tsl2563_write_thresh(struct iio_dev *indio_dev, > + u64 event_code, > + int val) > +{ > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + int ret; > + u8 address; > + > + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) > + address = TSL2563_REG_HIGHLOW; > + else > + address = TSL2563_REG_LOWLOW; > + mutex_lock(&chip->lock); > + ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address, > + val & 0xFF); > + if (ret) > + goto error_ret; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | (address + 1), > + (val >> 8) & 0xFF); > + if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) > + chip->high_thres = val; > + else > + chip->low_thres = val; > + > +error_ret: > + mutex_unlock(&chip->lock); > + > + return ret; > +} > + > +static irqreturn_t tsl2563_event_handler(int irq, void *private) > +{ > + struct iio_dev *dev_info = private; > + struct tsl2563_chip *chip = iio_priv(dev_info); > + > + iio_push_event(dev_info, > + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, > + 0, > + IIO_EV_TYPE_THRESH, > + IIO_EV_DIR_EITHER), > + iio_get_time_ns()); > + > + /* clear the interrupt and push the event */ > + i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT); > + return IRQ_HANDLED; > +} > + > +static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, > + u64 event_code, > + int state) > +{ > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + int ret = 0; > + > + mutex_lock(&chip->lock); > + if (state && !(chip->intr & 0x30)) { > + chip->intr &= ~0x30; > + chip->intr |= 0x10; > + /* ensure the chip is actually on */ > + cancel_delayed_work(&chip->poweroff_work); > + if (!tsl2563_get_power(chip)) { > + ret = tsl2563_set_power(chip, 1); > + if (ret) > + goto out; > + ret = tsl2563_configure(chip); > + if (ret) > + goto out; > + } > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_INT, > + chip->intr); > + chip->int_enabled = true; > + } > + > + if (!state && (chip->intr & 0x30)) { > + chip->intr &= ~0x30; > + ret = i2c_smbus_write_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_INT, > + chip->intr); > + chip->int_enabled = false; > + /* now the interrupt is not enabled, we can go to sleep */ > + schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > + } > +out: > + mutex_unlock(&chip->lock); > + > + return ret; > +} > + > +static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev, > + u64 event_code) > +{ > + struct tsl2563_chip *chip = iio_priv(indio_dev); > + int ret; > + > + mutex_lock(&chip->lock); > + ret = i2c_smbus_read_byte_data(chip->client, > + TSL2563_CMD | TSL2563_REG_INT); > + mutex_unlock(&chip->lock); > + if (ret < 0) > + goto error_ret; > + ret = !!(ret & 0x30); > +error_ret: > + > + return ret; > +} save the goto and just return ret? > + > +/*--------------------------------------------------------------*/ > +/* Probe, Attach, Remove */ > +/*--------------------------------------------------------------*/ > +static struct i2c_driver tsl2563_i2c_driver; > + > +static const struct iio_info tsl2563_info_no_irq = { > + .driver_module = THIS_MODULE, > + .read_raw = &tsl2563_read_raw, > + .write_raw = &tsl2563_write_raw, > +}; > + > +static const struct iio_info tsl2563_info = { > + .driver_module = THIS_MODULE, > + .read_raw = &tsl2563_read_raw, > + .write_raw = &tsl2563_write_raw, > + .read_event_value = &tsl2563_read_thresh, > + .write_event_value = &tsl2563_write_thresh, > + .read_event_config = &tsl2563_read_interrupt_config, > + .write_event_config = &tsl2563_write_interrupt_config, > +}; > + > +static int tsl2563_probe(struct i2c_client *client, > + const struct i2c_device_id *device_id) > +{ > + struct iio_dev *indio_dev; > + struct tsl2563_chip *chip; > + struct tsl2563_platform_data *pdata = client->dev.platform_data; > + int err = 0; > + u8 id = 0; > + > + indio_dev = iio_device_alloc(sizeof(*chip)); > + if (!indio_dev) > + return -ENOMEM; > + > + chip = iio_priv(indio_dev); > + > + i2c_set_clientdata(client, chip); > + chip->client = client; > + > + err = tsl2563_detect(chip); > + if (err) { > + dev_err(&client->dev, "detect error %d\n", -err); > + goto fail1; > + } > + > + err = tsl2563_read_id(chip, &id); > + if (err) { > + dev_err(&client->dev, "read id error %d\n", -err); > + goto fail1; > + } > + > + mutex_init(&chip->lock); > + > + /* Default values used until userspace says otherwise */ > + chip->low_thres = 0x0; > + chip->high_thres = 0xffff; > + chip->gainlevel = tsl2563_gainlevel_table; > + chip->intr = TSL2563_INT_PERSIST(4); > + chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS); > + chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS); > + > + if (pdata) > + chip->cover_comp_gain = pdata->cover_comp_gain; > + else > + chip->cover_comp_gain = 1; > + > + dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); > + indio_dev->name = client->name; > + indio_dev->channels = tsl2563_channels; > + indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels); > + indio_dev->dev.parent = &client->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + if (client->irq) > + indio_dev->info = &tsl2563_info; > + else > + indio_dev->info = &tsl2563_info_no_irq; > + > + if (client->irq) { > + err = request_threaded_irq(client->irq, > + NULL, > + &tsl2563_event_handler, > + IRQF_TRIGGER_RISING | IRQF_ONESHOT, > + "tsl2563_event", > + indio_dev); > + if (err) { > + dev_err(&client->dev, "irq request error %d\n", -err); > + goto fail1; > + } > + } > + > + err = tsl2563_configure(chip); > + if (err) { > + dev_err(&client->dev, "configure error %d\n", -err); > + goto fail2; > + } > + > + INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); > + > + /* The interrupt cannot yet be enabled so this is fine without lock */ > + schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > + > + err = iio_device_register(indio_dev); > + if (err) { > + dev_err(&client->dev, "iio registration error %d\n", -err); > + goto fail3; > + } > + > + return 0; > + > +fail3: > + cancel_delayed_work(&chip->poweroff_work); > + flush_scheduled_work(); > +fail2: > + if (client->irq) > + free_irq(client->irq, indio_dev); > +fail1: > + iio_device_free(indio_dev); > + return err; > +} > + > +static int tsl2563_remove(struct i2c_client *client) > +{ > + struct tsl2563_chip *chip = i2c_get_clientdata(client); > + struct iio_dev *indio_dev = iio_priv_to_dev(chip); > + > + iio_device_unregister(indio_dev); > + if (!chip->int_enabled) > + cancel_delayed_work(&chip->poweroff_work); > + /* Ensure that interrupts are disabled - then flush any bottom halves */ > + chip->intr &= ~0x30; > + i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT, > + chip->intr); > + flush_scheduled_work(); > + tsl2563_set_power(chip, 0); > + if (client->irq) > + free_irq(client->irq, indio_dev); > + > + iio_device_free(indio_dev); > + > + return 0; > +} > + > +#ifdef CONFIG_PM_SLEEP > +static int tsl2563_suspend(struct device *dev) > +{ > + struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); > + int ret; > + > + mutex_lock(&chip->lock); > + > + ret = tsl2563_set_power(chip, 0); > + if (ret) > + goto out; > + > + chip->suspended = true; > + > +out: > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static int tsl2563_resume(struct device *dev) > +{ > + struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); > + int ret; > + > + mutex_lock(&chip->lock); > + > + ret = tsl2563_set_power(chip, 1); > + if (ret) > + goto out; > + > + ret = tsl2563_configure(chip); > + if (ret) > + goto out; > + > + chip->suspended = false; > + > +out: > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume); > +#define TSL2563_PM_OPS (&tsl2563_pm_ops) > +#else > +#define TSL2563_PM_OPS NULL > +#endif > + > +static const struct i2c_device_id tsl2563_id[] = { > + { "tsl2560", 0 }, > + { "tsl2561", 1 }, > + { "tsl2562", 2 }, > + { "tsl2563", 3 }, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, tsl2563_id); > + > +static struct i2c_driver tsl2563_i2c_driver = { > + .driver = { > + .name = "tsl2563", > + .pm = TSL2563_PM_OPS, > + }, > + .probe = tsl2563_probe, > + .remove = tsl2563_remove, > + .id_table = tsl2563_id, > +}; > +module_i2c_driver(tsl2563_i2c_driver); > + > +MODULE_AUTHOR("Nokia Corporation"); > +MODULE_DESCRIPTION("tsl2563 light sensor driver"); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig > index 4bed30e..ca8d6e6 100644 > --- a/drivers/staging/iio/light/Kconfig > +++ b/drivers/staging/iio/light/Kconfig > @@ -25,16 +25,6 @@ config SENSORS_ISL29028 > Proximity value via iio. The ISL29028 provides the concurrent sensing > of ambient light and proximity. > > -config SENSORS_TSL2563 > - tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors" > - depends on I2C > - help > - If you say yes here you get support for the Taos TSL2560, > - TSL2561, TSL2562 and TSL2563 ambient light sensors. > - > - This driver can also be built as a module. If so, the module > - will be called tsl2563. > - > config TSL2583 > tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters" > depends on I2C > diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile > index 141af1e..9960fdf 100644 > --- a/drivers/staging/iio/light/Makefile > +++ b/drivers/staging/iio/light/Makefile > @@ -2,7 +2,6 @@ > # Makefile for industrial I/O Light sensors > # > > -obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o > obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o > obj-$(CONFIG_TSL2583) += tsl2583.o > diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c > deleted file mode 100644 > index 1a9adc0..0000000 > --- a/drivers/staging/iio/light/tsl2563.c > +++ /dev/null > @@ -1,899 +0,0 @@ > -/* > - * drivers/i2c/chips/tsl2563.c > - * > - * Copyright (C) 2008 Nokia Corporation > - * > - * Written by Timo O. Karjalainen <timo.o.karjalainen@xxxxxxxxx> > - * Contact: Amit Kucheria <amit.kucheria@xxxxxxxxxxxxx> > - * > - * Converted to IIO driver > - * Amit Kucheria <amit.kucheria@xxxxxxxxxxxxx> > - * > - * This program is free software; you can redistribute it and/or > - * modify it under the terms of the GNU General Public License > - * version 2 as published by the Free Software Foundation. > - * > - * This program is distributed in the hope that it will be useful, but > - * WITHOUT ANY WARRANTY; without even the implied warranty of > - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > - * General Public License for more details. > - * > - * You should have received a copy of the GNU General Public License > - * along with this program; if not, write to the Free Software > - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA > - * 02110-1301 USA > - */ > - > -#include <linux/module.h> > -#include <linux/i2c.h> > -#include <linux/interrupt.h> > -#include <linux/irq.h> > -#include <linux/sched.h> > -#include <linux/mutex.h> > -#include <linux/delay.h> > -#include <linux/pm.h> > -#include <linux/err.h> > -#include <linux/slab.h> > - > -#include <linux/iio/iio.h> > -#include <linux/iio/sysfs.h> > -#include <linux/iio/events.h> > -#include "tsl2563.h" > - > -/* Use this many bits for fraction part. */ > -#define ADC_FRAC_BITS (14) > - > -/* Given number of 1/10000's in ADC_FRAC_BITS precision. */ > -#define FRAC10K(f) (((f) * (1L << (ADC_FRAC_BITS))) / (10000)) > - > -/* Bits used for fraction in calibration coefficients.*/ > -#define CALIB_FRAC_BITS (10) > -/* 0.5 in CALIB_FRAC_BITS precision */ > -#define CALIB_FRAC_HALF (1 << (CALIB_FRAC_BITS - 1)) > -/* Make a fraction from a number n that was multiplied with b. */ > -#define CALIB_FRAC(n, b) (((n) << CALIB_FRAC_BITS) / (b)) > -/* Decimal 10^(digits in sysfs presentation) */ > -#define CALIB_BASE_SYSFS (1000) > - > -#define TSL2563_CMD (0x80) > -#define TSL2563_CLEARINT (0x40) > - > -#define TSL2563_REG_CTRL (0x00) > -#define TSL2563_REG_TIMING (0x01) > -#define TSL2563_REG_LOWLOW (0x02) /* data0 low threshold, 2 bytes */ > -#define TSL2563_REG_LOWHIGH (0x03) > -#define TSL2563_REG_HIGHLOW (0x04) /* data0 high threshold, 2 bytes */ > -#define TSL2563_REG_HIGHHIGH (0x05) > -#define TSL2563_REG_INT (0x06) > -#define TSL2563_REG_ID (0x0a) > -#define TSL2563_REG_DATA0LOW (0x0c) /* broadband sensor value, 2 bytes */ > -#define TSL2563_REG_DATA0HIGH (0x0d) > -#define TSL2563_REG_DATA1LOW (0x0e) /* infrared sensor value, 2 bytes */ > -#define TSL2563_REG_DATA1HIGH (0x0f) > - > -#define TSL2563_CMD_POWER_ON (0x03) > -#define TSL2563_CMD_POWER_OFF (0x00) > -#define TSL2563_CTRL_POWER_MASK (0x03) > - > -#define TSL2563_TIMING_13MS (0x00) > -#define TSL2563_TIMING_100MS (0x01) > -#define TSL2563_TIMING_400MS (0x02) > -#define TSL2563_TIMING_MASK (0x03) > -#define TSL2563_TIMING_GAIN16 (0x10) > -#define TSL2563_TIMING_GAIN1 (0x00) > - > -#define TSL2563_INT_DISBLED (0x00) > -#define TSL2563_INT_LEVEL (0x10) > -#define TSL2563_INT_PERSIST(n) ((n) & 0x0F) > - > -struct tsl2563_gainlevel_coeff { > - u8 gaintime; > - u16 min; > - u16 max; > -}; > - > -static const struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = { > - { > - .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16, > - .min = 0, > - .max = 65534, > - }, { > - .gaintime = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1, > - .min = 2048, > - .max = 65534, > - }, { > - .gaintime = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1, > - .min = 4095, > - .max = 37177, > - }, { > - .gaintime = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1, > - .min = 3000, > - .max = 65535, > - }, > -}; > - > -struct tsl2563_chip { > - struct mutex lock; > - struct i2c_client *client; > - struct delayed_work poweroff_work; > - > - /* Remember state for suspend and resume functions */ > - bool suspended; > - > - struct tsl2563_gainlevel_coeff const *gainlevel; > - > - u16 low_thres; > - u16 high_thres; > - u8 intr; > - bool int_enabled; > - > - /* Calibration coefficients */ > - u32 calib0; > - u32 calib1; > - int cover_comp_gain; > - > - /* Cache current values, to be returned while suspended */ > - u32 data0; > - u32 data1; > -}; > - > -static int tsl2563_set_power(struct tsl2563_chip *chip, int on) > -{ > - struct i2c_client *client = chip->client; > - u8 cmd; > - > - cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF; > - return i2c_smbus_write_byte_data(client, > - TSL2563_CMD | TSL2563_REG_CTRL, cmd); > -} > - > -/* > - * Return value is 0 for off, 1 for on, or a negative error > - * code if reading failed. > - */ > -static int tsl2563_get_power(struct tsl2563_chip *chip) > -{ > - struct i2c_client *client = chip->client; > - int ret; > - > - ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_CTRL); > - if (ret < 0) > - return ret; > - > - return (ret & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON; > -} > - > -static int tsl2563_configure(struct tsl2563_chip *chip) > -{ > - int ret; > - > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_TIMING, > - chip->gainlevel->gaintime); > - if (ret) > - goto error_ret; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_HIGHLOW, > - chip->high_thres & 0xFF); > - if (ret) > - goto error_ret; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_HIGHHIGH, > - (chip->high_thres >> 8) & 0xFF); > - if (ret) > - goto error_ret; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_LOWLOW, > - chip->low_thres & 0xFF); > - if (ret) > - goto error_ret; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_LOWHIGH, > - (chip->low_thres >> 8) & 0xFF); > -/* Interrupt register is automatically written anyway if it is relevant > - so is not here */ > -error_ret: > - return ret; > -} > - > -static void tsl2563_poweroff_work(struct work_struct *work) > -{ > - struct tsl2563_chip *chip = > - container_of(work, struct tsl2563_chip, poweroff_work.work); > - tsl2563_set_power(chip, 0); > -} > - > -static int tsl2563_detect(struct tsl2563_chip *chip) > -{ > - int ret; > - > - ret = tsl2563_set_power(chip, 1); > - if (ret) > - return ret; > - > - ret = tsl2563_get_power(chip); > - if (ret < 0) > - return ret; > - > - return ret ? 0 : -ENODEV; > -} > - > -static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id) > -{ > - struct i2c_client *client = chip->client; > - int ret; > - > - ret = i2c_smbus_read_byte_data(client, TSL2563_CMD | TSL2563_REG_ID); > - if (ret < 0) > - return ret; > - > - *id = ret; > - > - return 0; > -} > - > -/* > - * "Normalized" ADC value is one obtained with 400ms of integration time and > - * 16x gain. This function returns the number of bits of shift needed to > - * convert between normalized values and HW values obtained using given > - * timing and gain settings. > - */ > -static int adc_shiftbits(u8 timing) > -{ > - int shift = 0; > - > - switch (timing & TSL2563_TIMING_MASK) { > - case TSL2563_TIMING_13MS: > - shift += 5; > - break; > - case TSL2563_TIMING_100MS: > - shift += 2; > - break; > - case TSL2563_TIMING_400MS: > - /* no-op */ > - break; > - } > - > - if (!(timing & TSL2563_TIMING_GAIN16)) > - shift += 4; > - > - return shift; > -} > - > -/* Convert a HW ADC value to normalized scale. */ > -static u32 normalize_adc(u16 adc, u8 timing) > -{ > - return adc << adc_shiftbits(timing); > -} > - > -static void tsl2563_wait_adc(struct tsl2563_chip *chip) > -{ > - unsigned int delay; > - > - switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) { > - case TSL2563_TIMING_13MS: > - delay = 14; > - break; > - case TSL2563_TIMING_100MS: > - delay = 101; > - break; > - default: > - delay = 402; > - } > - /* > - * TODO: Make sure that we wait at least required delay but why we > - * have to extend it one tick more? > - */ > - schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2); > -} > - > -static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc) > -{ > - struct i2c_client *client = chip->client; > - > - if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) { > - > - (adc > chip->gainlevel->max) ? > - chip->gainlevel++ : chip->gainlevel--; > - > - i2c_smbus_write_byte_data(client, > - TSL2563_CMD | TSL2563_REG_TIMING, > - chip->gainlevel->gaintime); > - > - tsl2563_wait_adc(chip); > - tsl2563_wait_adc(chip); > - > - return 1; > - } else > - return 0; > -} > - > -static int tsl2563_get_adc(struct tsl2563_chip *chip) > -{ > - struct i2c_client *client = chip->client; > - u16 adc0, adc1; > - int retry = 1; > - int ret = 0; > - > - if (chip->suspended) > - goto out; > - > - if (!chip->int_enabled) { > - cancel_delayed_work(&chip->poweroff_work); > - > - if (!tsl2563_get_power(chip)) { > - ret = tsl2563_set_power(chip, 1); > - if (ret) > - goto out; > - ret = tsl2563_configure(chip); > - if (ret) > - goto out; > - tsl2563_wait_adc(chip); > - } > - } > - > - while (retry) { > - ret = i2c_smbus_read_word_data(client, > - TSL2563_CMD | TSL2563_REG_DATA0LOW); > - if (ret < 0) > - goto out; > - adc0 = ret; > - > - ret = i2c_smbus_read_word_data(client, > - TSL2563_CMD | TSL2563_REG_DATA1LOW); > - if (ret < 0) > - goto out; > - adc1 = ret; > - > - retry = tsl2563_adjust_gainlevel(chip, adc0); > - } > - > - chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime); > - chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime); > - > - if (!chip->int_enabled) > - schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > - > - ret = 0; > -out: > - return ret; > -} > - > -static inline int calib_to_sysfs(u32 calib) > -{ > - return (int) (((calib * CALIB_BASE_SYSFS) + > - CALIB_FRAC_HALF) >> CALIB_FRAC_BITS); > -} > - > -static inline u32 calib_from_sysfs(int value) > -{ > - return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS; > -} > - > -/* > - * Conversions between lux and ADC values. > - * > - * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are > - * appropriate constants. Different constants are needed for different > - * kinds of light, determined by the ratio adc1/adc0 (basically the ratio > - * of the intensities in infrared and visible wavelengths). lux_table below > - * lists the upper threshold of the adc1/adc0 ratio and the corresponding > - * constants. > - */ > - > -struct tsl2563_lux_coeff { > - unsigned long ch_ratio; > - unsigned long ch0_coeff; > - unsigned long ch1_coeff; > -}; > - > -static const struct tsl2563_lux_coeff lux_table[] = { > - { > - .ch_ratio = FRAC10K(1300), > - .ch0_coeff = FRAC10K(315), > - .ch1_coeff = FRAC10K(262), > - }, { > - .ch_ratio = FRAC10K(2600), > - .ch0_coeff = FRAC10K(337), > - .ch1_coeff = FRAC10K(430), > - }, { > - .ch_ratio = FRAC10K(3900), > - .ch0_coeff = FRAC10K(363), > - .ch1_coeff = FRAC10K(529), > - }, { > - .ch_ratio = FRAC10K(5200), > - .ch0_coeff = FRAC10K(392), > - .ch1_coeff = FRAC10K(605), > - }, { > - .ch_ratio = FRAC10K(6500), > - .ch0_coeff = FRAC10K(229), > - .ch1_coeff = FRAC10K(291), > - }, { > - .ch_ratio = FRAC10K(8000), > - .ch0_coeff = FRAC10K(157), > - .ch1_coeff = FRAC10K(180), > - }, { > - .ch_ratio = FRAC10K(13000), > - .ch0_coeff = FRAC10K(34), > - .ch1_coeff = FRAC10K(26), > - }, { > - .ch_ratio = ULONG_MAX, > - .ch0_coeff = 0, > - .ch1_coeff = 0, > - }, > -}; > - > -/* > - * Convert normalized, scaled ADC values to lux. > - */ > -static unsigned int adc_to_lux(u32 adc0, u32 adc1) > -{ > - const struct tsl2563_lux_coeff *lp = lux_table; > - unsigned long ratio, lux, ch0 = adc0, ch1 = adc1; > - > - ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX; > - > - while (lp->ch_ratio < ratio) > - lp++; > - > - lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff; > - > - return (unsigned int) (lux >> ADC_FRAC_BITS); > -} > - > -/*--------------------------------------------------------------*/ > -/* Sysfs interface */ > -/*--------------------------------------------------------------*/ > - > - > -/* Apply calibration coefficient to ADC count. */ > -static u32 calib_adc(u32 adc, u32 calib) > -{ > - unsigned long scaled = adc; > - > - scaled *= calib; > - scaled >>= CALIB_FRAC_BITS; > - > - return (u32) scaled; > -} > - > -static int tsl2563_write_raw(struct iio_dev *indio_dev, > - struct iio_chan_spec const *chan, > - int val, > - int val2, > - long mask) > -{ > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - > - if (chan->channel == IIO_MOD_LIGHT_BOTH) > - chip->calib0 = calib_from_sysfs(val); > - else > - chip->calib1 = calib_from_sysfs(val); > - > - return 0; > -} > - > -static int tsl2563_read_raw(struct iio_dev *indio_dev, > - struct iio_chan_spec const *chan, > - int *val, > - int *val2, > - long m) > -{ > - int ret = -EINVAL; > - u32 calib0, calib1; > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - > - mutex_lock(&chip->lock); > - switch (m) { > - case IIO_CHAN_INFO_RAW: > - case IIO_CHAN_INFO_PROCESSED: > - switch (chan->type) { > - case IIO_LIGHT: > - ret = tsl2563_get_adc(chip); > - if (ret) > - goto error_ret; > - calib0 = calib_adc(chip->data0, chip->calib0) * > - chip->cover_comp_gain; > - calib1 = calib_adc(chip->data1, chip->calib1) * > - chip->cover_comp_gain; > - *val = adc_to_lux(calib0, calib1); > - ret = IIO_VAL_INT; > - break; > - case IIO_INTENSITY: > - ret = tsl2563_get_adc(chip); > - if (ret) > - goto error_ret; > - if (chan->channel == 0) > - *val = chip->data0; > - else > - *val = chip->data1; > - ret = IIO_VAL_INT; > - break; > - default: > - break; > - } > - break; > - > - case IIO_CHAN_INFO_CALIBSCALE: > - if (chan->channel == 0) > - *val = calib_to_sysfs(chip->calib0); > - else > - *val = calib_to_sysfs(chip->calib1); > - ret = IIO_VAL_INT; > - break; > - default: > - ret = -EINVAL; > - goto error_ret; > - } > - > -error_ret: > - mutex_unlock(&chip->lock); > - return ret; > -} > - > -static const struct iio_chan_spec tsl2563_channels[] = { > - { > - .type = IIO_LIGHT, > - .indexed = 1, > - .info_mask = IIO_CHAN_INFO_PROCESSED_SEPARATE_BIT, > - .channel = 0, > - }, { > - .type = IIO_INTENSITY, > - .modified = 1, > - .channel2 = IIO_MOD_LIGHT_BOTH, > - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | > - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > - .event_mask = (IIO_EV_BIT(IIO_EV_TYPE_THRESH, > - IIO_EV_DIR_RISING) | > - IIO_EV_BIT(IIO_EV_TYPE_THRESH, > - IIO_EV_DIR_FALLING)), > - }, { > - .type = IIO_INTENSITY, > - .modified = 1, > - .channel2 = IIO_MOD_LIGHT_IR, > - .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | > - IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT, > - } > -}; > - > -static int tsl2563_read_thresh(struct iio_dev *indio_dev, > - u64 event_code, > - int *val) > -{ > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - > - switch (IIO_EVENT_CODE_EXTRACT_DIR(event_code)) { > - case IIO_EV_DIR_RISING: > - *val = chip->high_thres; > - break; > - case IIO_EV_DIR_FALLING: > - *val = chip->low_thres; > - break; > - default: > - return -EINVAL; > - } > - > - return 0; > -} > - > -static int tsl2563_write_thresh(struct iio_dev *indio_dev, > - u64 event_code, > - int val) > -{ > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - int ret; > - u8 address; > - > - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) > - address = TSL2563_REG_HIGHLOW; > - else > - address = TSL2563_REG_LOWLOW; > - mutex_lock(&chip->lock); > - ret = i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | address, > - val & 0xFF); > - if (ret) > - goto error_ret; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | (address + 1), > - (val >> 8) & 0xFF); > - if (IIO_EVENT_CODE_EXTRACT_DIR(event_code) == IIO_EV_DIR_RISING) > - chip->high_thres = val; > - else > - chip->low_thres = val; > - > -error_ret: > - mutex_unlock(&chip->lock); > - > - return ret; > -} > - > -static irqreturn_t tsl2563_event_handler(int irq, void *private) > -{ > - struct iio_dev *dev_info = private; > - struct tsl2563_chip *chip = iio_priv(dev_info); > - > - iio_push_event(dev_info, > - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, > - 0, > - IIO_EV_TYPE_THRESH, > - IIO_EV_DIR_EITHER), > - iio_get_time_ns()); > - > - /* clear the interrupt and push the event */ > - i2c_smbus_write_byte(chip->client, TSL2563_CMD | TSL2563_CLEARINT); > - return IRQ_HANDLED; > -} > - > -static int tsl2563_write_interrupt_config(struct iio_dev *indio_dev, > - u64 event_code, > - int state) > -{ > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - int ret = 0; > - > - mutex_lock(&chip->lock); > - if (state && !(chip->intr & 0x30)) { > - chip->intr &= ~0x30; > - chip->intr |= 0x10; > - /* ensure the chip is actually on */ > - cancel_delayed_work(&chip->poweroff_work); > - if (!tsl2563_get_power(chip)) { > - ret = tsl2563_set_power(chip, 1); > - if (ret) > - goto out; > - ret = tsl2563_configure(chip); > - if (ret) > - goto out; > - } > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_INT, > - chip->intr); > - chip->int_enabled = true; > - } > - > - if (!state && (chip->intr & 0x30)) { > - chip->intr &= ~0x30; > - ret = i2c_smbus_write_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_INT, > - chip->intr); > - chip->int_enabled = false; > - /* now the interrupt is not enabled, we can go to sleep */ > - schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > - } > -out: > - mutex_unlock(&chip->lock); > - > - return ret; > -} > - > -static int tsl2563_read_interrupt_config(struct iio_dev *indio_dev, > - u64 event_code) > -{ > - struct tsl2563_chip *chip = iio_priv(indio_dev); > - int ret; > - > - mutex_lock(&chip->lock); > - ret = i2c_smbus_read_byte_data(chip->client, > - TSL2563_CMD | TSL2563_REG_INT); > - mutex_unlock(&chip->lock); > - if (ret < 0) > - goto error_ret; > - ret = !!(ret & 0x30); > -error_ret: > - > - return ret; > -} > - > -/*--------------------------------------------------------------*/ > -/* Probe, Attach, Remove */ > -/*--------------------------------------------------------------*/ > -static struct i2c_driver tsl2563_i2c_driver; > - > -static const struct iio_info tsl2563_info_no_irq = { > - .driver_module = THIS_MODULE, > - .read_raw = &tsl2563_read_raw, > - .write_raw = &tsl2563_write_raw, > -}; > - > -static const struct iio_info tsl2563_info = { > - .driver_module = THIS_MODULE, > - .read_raw = &tsl2563_read_raw, > - .write_raw = &tsl2563_write_raw, > - .read_event_value = &tsl2563_read_thresh, > - .write_event_value = &tsl2563_write_thresh, > - .read_event_config = &tsl2563_read_interrupt_config, > - .write_event_config = &tsl2563_write_interrupt_config, > -}; > - > -static int tsl2563_probe(struct i2c_client *client, > - const struct i2c_device_id *device_id) > -{ > - struct iio_dev *indio_dev; > - struct tsl2563_chip *chip; > - struct tsl2563_platform_data *pdata = client->dev.platform_data; > - int err = 0; > - u8 id = 0; > - > - indio_dev = iio_device_alloc(sizeof(*chip)); > - if (!indio_dev) > - return -ENOMEM; > - > - chip = iio_priv(indio_dev); > - > - i2c_set_clientdata(client, chip); > - chip->client = client; > - > - err = tsl2563_detect(chip); > - if (err) { > - dev_err(&client->dev, "detect error %d\n", -err); > - goto fail1; > - } > - > - err = tsl2563_read_id(chip, &id); > - if (err) { > - dev_err(&client->dev, "read id error %d\n", -err); > - goto fail1; > - } > - > - mutex_init(&chip->lock); > - > - /* Default values used until userspace says otherwise */ > - chip->low_thres = 0x0; > - chip->high_thres = 0xffff; > - chip->gainlevel = tsl2563_gainlevel_table; > - chip->intr = TSL2563_INT_PERSIST(4); > - chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS); > - chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS); > - > - if (pdata) > - chip->cover_comp_gain = pdata->cover_comp_gain; > - else > - chip->cover_comp_gain = 1; > - > - dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f); > - indio_dev->name = client->name; > - indio_dev->channels = tsl2563_channels; > - indio_dev->num_channels = ARRAY_SIZE(tsl2563_channels); > - indio_dev->dev.parent = &client->dev; > - indio_dev->modes = INDIO_DIRECT_MODE; > - > - if (client->irq) > - indio_dev->info = &tsl2563_info; > - else > - indio_dev->info = &tsl2563_info_no_irq; > - > - if (client->irq) { > - err = request_threaded_irq(client->irq, > - NULL, > - &tsl2563_event_handler, > - IRQF_TRIGGER_RISING | IRQF_ONESHOT, > - "tsl2563_event", > - indio_dev); > - if (err) { > - dev_err(&client->dev, "irq request error %d\n", -err); > - goto fail1; > - } > - } > - > - err = tsl2563_configure(chip); > - if (err) { > - dev_err(&client->dev, "configure error %d\n", -err); > - goto fail2; > - } > - > - INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); > - > - /* The interrupt cannot yet be enabled so this is fine without lock */ > - schedule_delayed_work(&chip->poweroff_work, 5 * HZ); > - > - err = iio_device_register(indio_dev); > - if (err) { > - dev_err(&client->dev, "iio registration error %d\n", -err); > - goto fail3; > - } > - > - return 0; > - > -fail3: > - cancel_delayed_work(&chip->poweroff_work); > - flush_scheduled_work(); > -fail2: > - if (client->irq) > - free_irq(client->irq, indio_dev); > -fail1: > - iio_device_free(indio_dev); > - return err; > -} > - > -static int tsl2563_remove(struct i2c_client *client) > -{ > - struct tsl2563_chip *chip = i2c_get_clientdata(client); > - struct iio_dev *indio_dev = iio_priv_to_dev(chip); > - > - iio_device_unregister(indio_dev); > - if (!chip->int_enabled) > - cancel_delayed_work(&chip->poweroff_work); > - /* Ensure that interrupts are disabled - then flush any bottom halves */ > - chip->intr &= ~0x30; > - i2c_smbus_write_byte_data(chip->client, TSL2563_CMD | TSL2563_REG_INT, > - chip->intr); > - flush_scheduled_work(); > - tsl2563_set_power(chip, 0); > - if (client->irq) > - free_irq(client->irq, indio_dev); > - > - iio_device_free(indio_dev); > - > - return 0; > -} > - > -#ifdef CONFIG_PM_SLEEP > -static int tsl2563_suspend(struct device *dev) > -{ > - struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); > - int ret; > - > - mutex_lock(&chip->lock); > - > - ret = tsl2563_set_power(chip, 0); > - if (ret) > - goto out; > - > - chip->suspended = true; > - > -out: > - mutex_unlock(&chip->lock); > - return ret; > -} > - > -static int tsl2563_resume(struct device *dev) > -{ > - struct tsl2563_chip *chip = i2c_get_clientdata(to_i2c_client(dev)); > - int ret; > - > - mutex_lock(&chip->lock); > - > - ret = tsl2563_set_power(chip, 1); > - if (ret) > - goto out; > - > - ret = tsl2563_configure(chip); > - if (ret) > - goto out; > - > - chip->suspended = false; > - > -out: > - mutex_unlock(&chip->lock); > - return ret; > -} > - > -static SIMPLE_DEV_PM_OPS(tsl2563_pm_ops, tsl2563_suspend, tsl2563_resume); > -#define TSL2563_PM_OPS (&tsl2563_pm_ops) > -#else > -#define TSL2563_PM_OPS NULL > -#endif > - > -static const struct i2c_device_id tsl2563_id[] = { > - { "tsl2560", 0 }, > - { "tsl2561", 1 }, > - { "tsl2562", 2 }, > - { "tsl2563", 3 }, > - {} > -}; > -MODULE_DEVICE_TABLE(i2c, tsl2563_id); > - > -static struct i2c_driver tsl2563_i2c_driver = { > - .driver = { > - .name = "tsl2563", > - .pm = TSL2563_PM_OPS, > - }, > - .probe = tsl2563_probe, > - .remove = tsl2563_remove, > - .id_table = tsl2563_id, > -}; > -module_i2c_driver(tsl2563_i2c_driver); > - > -MODULE_AUTHOR("Nokia Corporation"); > -MODULE_DESCRIPTION("tsl2563 light sensor driver"); > -MODULE_LICENSE("GPL"); > -- Peter Meerwald +43-664-2444418 (mobile) -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html