Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755984AbZKIODO (ORCPT ); Mon, 9 Nov 2009 09:03:14 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1755966AbZKIODO (ORCPT ); Mon, 9 Nov 2009 09:03:14 -0500 Received: from ppsw-0.csi.cam.ac.uk ([131.111.8.130]:40270 "EHLO ppsw-0.csi.cam.ac.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755931AbZKIODM (ORCPT ); Mon, 9 Nov 2009 09:03:12 -0500 X-Cam-AntiVirus: no malware found X-Cam-SpamDetails: not scanned X-Cam-ScannerInfo: http://www.cam.ac.uk/cs/email/scanner/ Message-ID: <4AF82141.10206@cam.ac.uk> Date: Mon, 09 Nov 2009 14:03:45 +0000 From: Jonathan Cameron User-Agent: Thunderbird 2.0.0.23 (X11/20091029) MIME-Version: 1.0 To: Amit Kucheria CC: List Linux Kernel , Greg Kroah-Hartman , linux-omap@vger.kernel.org, Zhang Rui , Jean Delvare Subject: Re: [PATCH 1/2] staging: iio: tsl2563 ambient light sensor driver References: <1257772468-4004-1-git-send-email-amit.kucheria@verdurent.com> <4AF81FE5.5080102@cam.ac.uk> In-Reply-To: <4AF81FE5.5080102@cam.ac.uk> X-Enigmail-Version: 0.96.0 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 25433 Lines: 881 > Hi Amit, > > Normally I'd welcome this in IIO, except that all ambient light sensors are in the > process of moving to the new ALS subsystem. There are still some issues to resolve > in that subsystem (mainly to do with naming conventions) but hopefully we will > get them sorted out shortly. Sorry, I meant to add some links to relevant threads but clean forgot! http://lkml.org/lkml/2009/9/21/392 for als patches http://lkml.org/lkml/2009/10/9/198 for the tsl2550 (with a bug - Jean is kindly testing a fixed version so will repost after that.( I also meant to copy some of the als and other similar sensors people who might be interested. Have now done so. > I'll take a close look at this sometime over the next few days though. On a quick > glance at the data sheet, it looks very similar to the tsl2561. Perhaps we can merge > the drivers? Yours is certainly more complete than the tsl2561 version in IIO so it > would make sense to lift the functional elements in to the code I have for an ALS > driver. I hadn't posted that previously as I hadn't quite worked out how to handle > the various gain related settings. What you have done seems to make sense (from a very > quick look.) > > Thanks, > > Jonathan > > > > >> Add driver support for the tsl2563 TAOS ambient light sensor. After looking at >> discussions on LKML, the driver was modified from a 'hwmon' driver to an 'iio' >> driver. The sysfs interfaces have been tested on an RX51 (N900) to see if it >> responds to changing light conditions. >> >> The only real reason for submitting this to staging is that it is dependent on >> the IIO subsystem. >> >> Signed-off-by: Amit Kucheria >> Cc: Jonathan Cameron >> Cc: Greg Kroah-Hartman >> Cc: linux-omap@vger.kernel.org >> --- >> drivers/staging/iio/light/Kconfig | 11 + >> drivers/staging/iio/light/Makefile | 1 + >> drivers/staging/iio/light/tsl2563.c | 772 +++++++++++++++++++++++++++++++++++ >> drivers/staging/iio/light/tsl2563.h | 9 + >> 4 files changed, 793 insertions(+), 0 deletions(-) >> create mode 100644 drivers/staging/iio/light/tsl2563.c >> create mode 100644 drivers/staging/iio/light/tsl2563.h >> >> diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig >> index 12af0c4..cf0d0d6 100644 >> --- a/drivers/staging/iio/light/Kconfig >> +++ b/drivers/staging/iio/light/Kconfig >> @@ -11,3 +11,14 @@ config TSL2561 >> convertor. This chip has two light sensors. One is broadband >> including infrared whilst the other measures only infrared. >> Provides direct access via sysfs. >> + >> +config SENSORS_TSL2563 >> + tristate "TAOS TSL2563 ambient light sensor" >> + depends on I2C >> + help >> + If you say yes here you get support for the Taos TSL2563 >> + ambient light sensor (found in N900). >> + >> + This driver can also be built as a module. If so, the module >> + will be called tsl2563. >> + >> diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile >> index ccff151..30dbfb1 100644 >> --- a/drivers/staging/iio/light/Makefile >> +++ b/drivers/staging/iio/light/Makefile >> @@ -3,3 +3,4 @@ >> # >> >> obj-$(CONFIG_TSL2561) += tsl2561.o >> +obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o >> diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c >> new file mode 100644 >> index 0000000..3e812b2 >> --- /dev/null >> +++ b/drivers/staging/iio/light/tsl2563.c >> @@ -0,0 +1,772 @@ >> +/* >> + * drivers/i2c/chips/tsl2563.c >> + * >> + * Copyright (C) 2008 Nokia Corporation >> + * >> + * Written by Timo O. Karjalainen >> + * Contact: Amit Kucheria >> + * >> + * Converted to IIO driver >> + * Amit Kucheria >> + * >> + * 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 >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> +#include >> + >> +#include "../iio.h" >> +#include "tsl2563.h" >> + >> +#define DRIVER_NAME "tsl2563" >> + >> +/* 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 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 iio_dev *indio_dev; >> + struct delayed_work poweroff_work; >> + >> + /* Remember state for suspend and resume functions */ >> + pm_message_t state; >> + >> + struct tsl2563_gainlevel_coeff *gainlevel; >> + >> + /* Thresholds are in lux */ >> + u16 low_thres; >> + u16 high_thres; >> + u8 intr; >> + >> + /* 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_write(struct i2c_client *client, u8 reg, u8 value) >> +{ >> + int ret; >> + u8 buf[2]; >> + >> + buf[0] = TSL2563_CMD | reg; >> + buf[1] = value; >> + >> + ret = i2c_master_send(client, buf, sizeof(buf)); >> + return (ret == sizeof(buf)) ? 0 : ret; >> +} >> + >> +static int tsl2563_read(struct i2c_client *client, u8 reg, void *buf, int len) >> +{ >> + int ret; >> + u8 cmd = TSL2563_CMD | reg; >> + >> + ret = i2c_master_send(client, &cmd, sizeof(cmd)); >> + if (ret != sizeof(cmd)) >> + return ret; >> + >> + return i2c_master_recv(client, buf, len); >> +} >> + >> +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 tsl2563_write(client, 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; >> + u8 val; >> + >> + ret = tsl2563_read(client, TSL2563_REG_CTRL, &val, sizeof(val)); >> + if (ret != sizeof(val)) >> + return ret; >> + >> + return (val & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON; >> +} >> + >> +static int tsl2563_configure(struct tsl2563_chip *chip) >> +{ >> + struct i2c_client *client = chip->client; >> + int ret; >> + >> + ret = tsl2563_write(client, TSL2563_REG_TIMING, >> + chip->gainlevel->gaintime); >> + if (ret) >> + goto out; >> + >> + ret = tsl2563_write(client, TSL2563_REG_INT, chip->intr); >> + >> +out: >> + 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 = tsl2563_read(client, TSL2563_REG_ID, id, sizeof(*id)); >> + if (ret != sizeof(*id)) >> + return 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--; >> + >> + tsl2563_write(client, 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; >> + u8 buf0[2], buf1[2]; >> + u16 adc0, adc1; >> + int retry = 1; >> + int ret = 0; >> + >> + if (chip->state.event != PM_EVENT_ON) >> + goto out; >> + >> + 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 = tsl2563_read(client, >> + TSL2563_REG_DATA0LOW | TSL2563_CLEARINT, >> + buf0, sizeof(buf0)); >> + if (ret != sizeof(buf0)) >> + goto out; >> + >> + ret = tsl2563_read(client, TSL2563_REG_DATA1LOW, >> + buf1, sizeof(buf1)); >> + if (ret != sizeof(buf1)) >> + goto out; >> + >> + adc0 = (buf0[1] << 8) + buf0[0]; >> + adc1 = (buf1[1] << 8) + buf1[0]; >> + >> + retry = tsl2563_adjust_gainlevel(chip, adc0); >> + } >> + >> + chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime); >> + chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime); >> + >> + 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 */ >> +/*--------------------------------------------------------------*/ >> + >> +static ssize_t tsl2563_adc0_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + >> + ret = tsl2563_get_adc(chip); >> + if (ret) >> + goto out; >> + >> + ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data0); >> +out: >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static ssize_t tsl2563_adc1_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + >> + ret = tsl2563_get_adc(chip); >> + if (ret) >> + goto out; >> + >> + ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data1); >> +out: >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +/* 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 ssize_t tsl2563_lux_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + u32 calib0, calib1; >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + >> + ret = tsl2563_get_adc(chip); >> + if (ret) >> + goto out; >> + >> + calib0 = calib_adc(chip->data0, chip->calib0) * chip->cover_comp_gain; >> + calib1 = calib_adc(chip->data1, chip->calib1) * chip->cover_comp_gain; >> + >> + ret = snprintf(buf, PAGE_SIZE, "%d\n", adc_to_lux(calib0, calib1)); >> + >> +out: >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static ssize_t format_calib(char *buf, int len, u32 calib) >> +{ >> + return snprintf(buf, PAGE_SIZE, "%d\n", calib_to_sysfs(calib)); >> +} >> + >> +static ssize_t tsl2563_calib0_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + ret = format_calib(buf, PAGE_SIZE, chip->calib0); >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static ssize_t tsl2563_calib1_show(struct device *dev, >> + struct device_attribute *attr, char *buf) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + ret = format_calib(buf, PAGE_SIZE, chip->calib1); >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static int do_calib_store(struct device *dev, const char *buf, size_t len, >> + int ch) >> +{ >> + struct iio_dev *indio_dev = dev_get_drvdata(dev); >> + struct tsl2563_chip *chip = indio_dev->dev_data; >> + int value; >> + u32 calib; >> + >> + if (1 != sscanf(buf, "%d", &value)) >> + return -EINVAL; >> + >> + calib = calib_from_sysfs(value); >> + >> + if (ch) >> + chip->calib1 = calib; >> + else >> + chip->calib0 = calib; >> + >> + return len; >> +} >> + >> +static ssize_t tsl2563_calib0_store(struct device *dev, >> + struct device_attribute *attr, >> + const char *buf, size_t len) >> +{ >> + return do_calib_store(dev, buf, len, 0); >> +} >> + >> +static ssize_t tsl2563_calib1_store(struct device *dev, >> + struct device_attribute *attr, >> + const char *buf, size_t len) >> +{ >> + return do_calib_store(dev, buf, len, 1); >> +} >> + >> +/* AmitXXXX: Convert to IIO_DEV_ATTR_LIGHT* as in tsl2561 >> + * once I understand what they mean */ >> +static DEVICE_ATTR(adc0, S_IRUGO, tsl2563_adc0_show, NULL); >> +static DEVICE_ATTR(adc1, S_IRUGO, tsl2563_adc1_show, NULL); >> +static DEVICE_ATTR(lux, S_IRUGO, tsl2563_lux_show, NULL); >> +static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR, >> + tsl2563_calib0_show, tsl2563_calib0_store); >> +static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR, >> + tsl2563_calib1_show, tsl2563_calib1_store); >> + >> +static struct attribute *tsl2563_attributes[] = { >> + &dev_attr_adc0.attr, >> + &dev_attr_adc1.attr, >> + &dev_attr_lux.attr, >> + &dev_attr_calib0.attr, >> + &dev_attr_calib1.attr, >> + NULL >> +}; >> + >> +static const struct attribute_group tsl2563_group = { >> + .attrs = tsl2563_attributes, >> +}; >> + >> +/*--------------------------------------------------------------*/ >> +/* Probe, Attach, Remove */ >> +/*--------------------------------------------------------------*/ >> +static struct i2c_driver tsl2563_i2c_driver; >> + >> +static int __devinit tsl2563_probe(struct i2c_client *client, >> + const struct i2c_device_id *device_id) >> +{ >> + struct tsl2563_chip *chip; >> + struct tsl2563_platform_data *pdata = client->dev.platform_data; >> + int err = 0; >> + int ret; >> + u8 id; >> + >> + chip = kzalloc(sizeof(*chip), GFP_KERNEL); >> + if (!chip) >> + return -ENOMEM; >> + >> + i2c_set_clientdata(client, chip); >> + chip->client = client; >> + >> + err = tsl2563_detect(chip); >> + if (err) { >> + dev_err(&client->dev, "device not found, error %d \n", -err); >> + goto fail1; >> + } >> + >> + err = tsl2563_read_id(chip, &id); >> + if (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); >> + >> + chip->indio_dev = iio_allocate_device(); >> + if (!chip->indio_dev) >> + goto fail1; >> + chip->indio_dev->attrs = &tsl2563_group; >> + chip->indio_dev->dev.parent = &client->dev; >> + chip->indio_dev->dev_data = (void *)(chip); >> + chip->indio_dev->driver_module = THIS_MODULE; >> + chip->indio_dev->modes = INDIO_DIRECT_MODE; >> + ret = iio_device_register(chip->indio_dev); >> + if (ret) >> + goto fail1; >> + >> + err = tsl2563_configure(chip); >> + if (err) >> + goto fail2; >> + >> + INIT_DELAYED_WORK(&chip->poweroff_work, tsl2563_poweroff_work); >> + schedule_delayed_work(&chip->poweroff_work, 5 * HZ); >> + >> + return 0; >> +fail2: >> + iio_device_unregister(chip->indio_dev); >> +fail1: >> + kfree(chip); >> + return err; >> +} >> + >> +static int tsl2563_remove(struct i2c_client *client) >> +{ >> + struct tsl2563_chip *chip = i2c_get_clientdata(client); >> + >> + iio_device_unregister(chip->indio_dev); >> + >> + kfree(chip); >> + return 0; >> +} >> + >> +static int tsl2563_suspend(struct i2c_client *client, pm_message_t state) >> +{ >> + struct tsl2563_chip *chip = i2c_get_clientdata(client); >> + int ret; >> + >> + mutex_lock(&chip->lock); >> + >> + ret = tsl2563_set_power(chip, 0); >> + if (ret) >> + goto out; >> + >> + chip->state = state; >> + >> +out: >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static int tsl2563_resume(struct i2c_client *client) >> +{ >> + struct tsl2563_chip *chip = i2c_get_clientdata(client); >> + 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->state.event = PM_EVENT_ON; >> + >> +out: >> + mutex_unlock(&chip->lock); >> + return ret; >> +} >> + >> +static const struct i2c_device_id tsl2563_id[] = { >> + { DRIVER_NAME, 0 }, >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(i2c, tsl2563_id); >> + >> +static struct i2c_driver tsl2563_i2c_driver = { >> + .driver = { >> + .name = DRIVER_NAME, >> + }, >> + .suspend = tsl2563_suspend, >> + .resume = tsl2563_resume, >> + .probe = tsl2563_probe, >> + .remove = __devexit_p(tsl2563_remove), >> + .id_table = tsl2563_id, >> +}; >> + >> +static int __init tsl2563_init(void) >> +{ >> + return i2c_add_driver(&tsl2563_i2c_driver); >> +} >> + >> +static void __exit tsl2563_exit(void) >> +{ >> + i2c_del_driver(&tsl2563_i2c_driver); >> +} >> + >> +MODULE_AUTHOR("Nokia Corporation"); >> +MODULE_DESCRIPTION("tsl2563 light sensor driver"); >> +MODULE_LICENSE("GPL"); >> + >> +module_init(tsl2563_init); >> +module_exit(tsl2563_exit); >> diff --git a/drivers/staging/iio/light/tsl2563.h b/drivers/staging/iio/light/tsl2563.h >> new file mode 100644 >> index 0000000..b97368b >> --- /dev/null >> +++ b/drivers/staging/iio/light/tsl2563.h >> @@ -0,0 +1,9 @@ >> +#ifndef __LINUX_TSL2563_H >> +#define __LINUX_TSL2563_H >> + >> +struct tsl2563_platform_data { >> + int cover_comp_gain; >> +}; >> + >> +#endif /* __LINUX_TSL2563_H */ >> + > > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/