On 01/06/2013 04:31 PM, Peter Meerwald wrote: > > nitpicking inline Thanks, I'll do these as a precursor patch as they are fixes of the original driver, rather than todo with the move. Jonathan > >> 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"); >> > -- 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