Signed-off-by: Jonathan Cameron <[email protected]>
---
V3. Against current mainline with the addition of the two als patches
as posted by Amit.
Changes since V2.
Couple of bug fixes.
Change from using illuminance_range_max to exposure_time. As Jean has
described, using the range is rather problematic as actual range is
dependent on the difference in infrared and visible + infrared light
levels. Thus setting it to a particular value far from guarantees that
the sensor will be able to read anywhere near the desired range.
exposure_time brings it's own problems. For this particular chip there
are two separate ADC's and no way of separating the proportions of
time in which light is captured as opposed to when the ADC conversion is
occurring. Other suggestions for how to handle this would be most welcome.
At the moment, <800msecs is set to 160msecs, everything else to 800msecs
which is actually made up of 400msecs on each of the two channels.
Documentation has been updated appropriately.
I've also removed the now unnecessary kconfig and makefile form i2c/chips.
Jean, yell if you would prefer to split that into a separate patch or do
it yourself.
Documentation/ABI/testing/sysfs-class-als | 9 +
drivers/als/Kconfig | 14 +
drivers/als/Makefile | 2 +
drivers/als/tsl2550.c | 496 +++++++++++++++++++++++++++++
drivers/i2c/Kconfig | 1 -
drivers/i2c/Makefile | 2 +-
drivers/i2c/chips/Kconfig | 19 --
drivers/i2c/chips/Makefile | 18 -
drivers/i2c/chips/tsl2550.c | 473 ---------------------------
9 files changed, 522 insertions(+), 512 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
index d3b33f3..732f449 100644
--- a/Documentation/ABI/testing/sysfs-class-als
+++ b/Documentation/ABI/testing/sysfs-class-als
@@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
Unit: lux (lumens per square meter)
RO
+What: /sys/class/als/.../exposure_time[n]
+Date: Dec. 2009
+KernelVersion: 2.6.32
+Contact: Jonathan Cameron <[email protected]>
+Description: Sensor exposure time. In some devices this
+ corresponds to the combined time needed to
+ to internally read several different sensors.
+ Unit: microseconds
+ RW
diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
index 200c52b..1564ffc 100644
--- a/drivers/als/Kconfig
+++ b/drivers/als/Kconfig
@@ -8,3 +8,17 @@ menuconfig ALS
This framework provides a generic sysfs I/F for Ambient Light
Sensor devices.
If you want this support, you should say Y or M here.
+
+if ALS
+
+config ALS_TSL2550
+ tristate "Taos TSL2550 ambient light sensor"
+ depends on EXPERIMENTAL && I2C
+ help
+ If you say yes here you get support for the Taos TSL2550
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2550.
+
+endif #ALS
diff --git a/drivers/als/Makefile b/drivers/als/Makefile
index a527197..7be5631 100644
--- a/drivers/als/Makefile
+++ b/drivers/als/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_ALS) += als_sys.o
+
+obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
\ No newline at end of file
diff --git a/drivers/als/tsl2550.c b/drivers/als/tsl2550.c
new file mode 100644
index 0000000..64f7f96
--- /dev/null
+++ b/drivers/als/tsl2550.c
@@ -0,0 +1,496 @@
+/*
+ * tsl2550.c - Linux kernel modules for ambient light sensor
+ *
+ * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
+ * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
+ * Copyright (C) 2009 Jonathan Cameron <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/als_sys.h>
+
+#define TSL2550_DRV_NAME "tsl2550"
+#define DRIVER_VERSION "2.0"
+
+/*
+ * Defines
+ */
+
+#define TSL2550_POWER_DOWN 0x00
+#define TSL2550_POWER_UP 0x03
+#define TSL2550_STANDARD_RANGE 0x18
+#define TSL2550_EXTENDED_RANGE 0x1d
+#define TSL2550_READ_ADC0 0x43
+#define TSL2550_READ_ADC1 0x83
+
+/*
+ * Structs
+ */
+
+struct tsl2550_data {
+ struct device *classdev;
+ struct i2c_client *client;
+ struct mutex update_lock;
+ unsigned int power_state:1;
+ unsigned int operating_mode:1;
+};
+
+/*
+ * Global data
+ */
+
+static const u8 TSL2550_MODE_RANGE[2] = {
+ TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
+};
+
+/*
+ * Management functions
+ */
+
+static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
+
+ data->operating_mode = mode;
+
+ return ret;
+}
+
+static int tsl2550_set_power_state(struct i2c_client *client, int state)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ if (state == 0)
+ ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
+ else {
+ ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
+
+ /* On power up we should reset operating mode also... */
+ tsl2550_set_operating_mode(client, data->operating_mode);
+ }
+
+ data->power_state = state;
+
+ return ret;
+}
+
+static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
+{
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, cmd);
+ if (ret < 0)
+ return ret;
+ if (!(ret & 0x80))
+ return -EAGAIN;
+ if (ret == 0x7f)
+ return -ERANGE;
+ return ret & 0x7f; /* remove the "valid" bit */
+}
+
+/*
+ * LUX calculation - note the range is dependent on combination
+ * of infrared level and visible light levels.
+ */
+
+#define TSL2550_MAX_LUX 1568
+
+static const u8 ratio_lut[] = {
+ 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 100, 100, 100, 99, 99,
+ 99, 99, 99, 99, 99, 99, 99, 99,
+ 99, 99, 99, 98, 98, 98, 98, 98,
+ 98, 98, 97, 97, 97, 97, 97, 96,
+ 96, 96, 96, 95, 95, 95, 94, 94,
+ 93, 93, 93, 92, 92, 91, 91, 90,
+ 89, 89, 88, 87, 87, 86, 85, 84,
+ 83, 82, 81, 80, 79, 78, 77, 75,
+ 74, 73, 71, 69, 68, 66, 64, 62,
+ 60, 58, 56, 54, 52, 49, 47, 44,
+ 42, 41, 40, 40, 39, 39, 38, 38,
+ 37, 37, 37, 36, 36, 36, 35, 35,
+ 35, 35, 34, 34, 34, 34, 33, 33,
+ 33, 33, 32, 32, 32, 32, 32, 31,
+ 31, 31, 31, 31, 30, 30, 30, 30,
+ 30,
+};
+
+static const u16 count_lut[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 18, 20, 22, 24, 26, 28, 30,
+ 32, 34, 36, 38, 40, 42, 44, 46,
+ 49, 53, 57, 61, 65, 69, 73, 77,
+ 81, 85, 89, 93, 97, 101, 105, 109,
+ 115, 123, 131, 139, 147, 155, 163, 171,
+ 179, 187, 195, 203, 211, 219, 227, 235,
+ 247, 263, 279, 295, 311, 327, 343, 359,
+ 375, 391, 407, 423, 439, 455, 471, 487,
+ 511, 543, 575, 607, 639, 671, 703, 735,
+ 767, 799, 831, 863, 895, 927, 959, 991,
+ 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
+ 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
+ 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
+ 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
+};
+
+/*
+ * This function is described into Taos TSL2550 Designer's Notebook
+ * pages 2, 3.
+ */
+static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
+{
+ unsigned int lux;
+
+ /* Look up count from channel values */
+ u16 c0 = count_lut[ch0];
+ u16 c1 = count_lut[ch1];
+
+ /*
+ * Calculate ratio.
+ * Note: the "128" is a scaling factor
+ */
+ u8 r = 128;
+
+ /* Avoid division by 0 and count 1 cannot be greater than count 0 */
+ if (c1 <= c0)
+ if (c0) {
+ r = c1 * 128 / c0;
+
+ /* Calculate LUX */
+ lux = ((c0 - c1) * ratio_lut[r]) / 256;
+ } else
+ lux = 0;
+ else
+ return -EAGAIN;
+
+ /* LUX range check */
+ return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
+}
+
+/*
+ * SysFS support
+ */
+
+static ssize_t tsl2550_show_power_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ return sprintf(buf, "%u\n", data->power_state);
+}
+
+static ssize_t tsl2550_store_power_state(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+ int ret = strict_strtoul(buf, 10, &val);
+
+ if (val < 0 || val > 1 || ret)
+ return -EINVAL;
+
+ mutex_lock(&data->update_lock);
+ ret = tsl2550_set_power_state(client, val);
+ mutex_unlock(&data->update_lock);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
+ tsl2550_show_power_state, tsl2550_store_power_state);
+
+static ssize_t tsl2550_show_exposure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ if (data->operating_mode)
+ return sprintf(buf, "160000\n");
+ else
+ return sprintf(buf, "800000\n");
+}
+
+static ssize_t tsl2550_store_exposure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ unsigned long val;
+
+ int ret = strict_strtoul(buf, 10, &val);
+
+ if (ret)
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ if (val >= 800000)
+ ret = tsl2550_set_operating_mode(client, 0);
+ else
+ ret = tsl2550_set_operating_mode(client, 1);
+ mutex_unlock(&data->update_lock);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
+ tsl2550_show_exposure, tsl2550_store_exposure);
+
+static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ u8 ch0, ch1;
+ int ret;
+
+ ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
+ if (ret < 0)
+ return ret;
+ ch0 = ret;
+
+ ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
+ if (ret < 0)
+ return ret;
+ ch1 = ret;
+
+ /* Do the job */
+ ret = tsl2550_calculate_lux(ch0, ch1);
+ if (ret < 0)
+ return ret;
+ if (data->operating_mode == 1)
+ ret *= 5;
+
+ return sprintf(buf, "%d\n", ret);
+}
+
+static ssize_t tsl2550_show_lux1_input(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ /* No LUX data if not operational */
+ if (!data->power_state)
+ return -EBUSY;
+
+ mutex_lock(&data->update_lock);
+ ret = __tsl2550_show_lux(client, buf);
+ mutex_unlock(&data->update_lock);
+
+ return ret;
+}
+
+static DEVICE_ATTR(illuminance0, S_IRUGO,
+ tsl2550_show_lux1_input, NULL);
+
+static struct attribute *tsl2550_attributes[] = {
+ &dev_attr_power_state.attr,
+ &dev_attr_exposure_time0.attr,
+ &dev_attr_illuminance0.attr,
+ NULL
+};
+
+static const struct attribute_group tsl2550_attr_group = {
+ .attrs = tsl2550_attributes,
+};
+
+/*
+ * Initialization function
+ */
+
+static int tsl2550_init_client(struct i2c_client *client)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ int err;
+
+ /*
+ * Probe the chip. To do so we try to power up the device and then to
+ * read back the 0x03 code
+ */
+ err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
+ if (err < 0)
+ return err;
+ if (err != TSL2550_POWER_UP)
+ return -ENODEV;
+ data->power_state = 1;
+
+ /* Set the default operating mode */
+ err = i2c_smbus_write_byte(client,
+ TSL2550_MODE_RANGE[data->operating_mode]);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+/*
+ * I2C init/probing/exit functions
+ */
+
+static struct i2c_driver tsl2550_driver;
+static int __devinit tsl2550_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct tsl2550_data *data;
+ int *opmode, err = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
+ | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
+ err = -EIO;
+ goto exit;
+ }
+
+ data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
+ if (!data) {
+ err = -ENOMEM;
+ goto exit;
+ }
+ data->client = client;
+ i2c_set_clientdata(client, data);
+
+ /* Check platform data */
+ opmode = client->dev.platform_data;
+ if (opmode) {
+ if (*opmode < 0 || *opmode > 1) {
+ dev_err(&client->dev, "invalid operating_mode (%d)\n",
+ *opmode);
+ err = -EINVAL;
+ goto exit_kfree;
+ }
+ data->operating_mode = *opmode;
+ } else
+ data->operating_mode = 0; /* default mode is standard */
+ dev_info(&client->dev, "%s operating mode\n",
+ data->operating_mode ? "extended" : "standard");
+
+ mutex_init(&data->update_lock);
+
+ /* Initialize the TSL2550 chip */
+ err = tsl2550_init_client(client);
+ if (err)
+ goto exit_kfree;
+
+ /* Register sysfs hooks */
+ data->classdev = als_device_register(&client->dev);
+ if (IS_ERR(data->classdev)) {
+ err = PTR_ERR(data->classdev);
+ goto exit_kfree;
+ }
+
+ err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
+ if (err)
+ goto exit_unreg;
+
+ dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
+
+ return 0;
+
+exit_unreg:
+ als_device_unregister(data->classdev);
+exit_kfree:
+ kfree(data);
+exit:
+ return err;
+}
+
+static int __devexit tsl2550_remove(struct i2c_client *client)
+{
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
+ als_device_unregister(data->classdev);
+
+ /* Power down the device */
+ tsl2550_set_power_state(client, 0);
+
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ return tsl2550_set_power_state(client, 0);
+}
+
+static int tsl2550_resume(struct i2c_client *client)
+{
+ return tsl2550_set_power_state(client, 1);
+}
+
+#else
+
+#define tsl2550_suspend NULL
+#define tsl2550_resume NULL
+
+#endif /* CONFIG_PM */
+
+static const struct i2c_device_id tsl2550_id[] = {
+ { "tsl2550", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tsl2550_id);
+
+static struct i2c_driver tsl2550_driver = {
+ .driver = {
+ .name = TSL2550_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .suspend = tsl2550_suspend,
+ .resume = tsl2550_resume,
+ .probe = tsl2550_probe,
+ .remove = __devexit_p(tsl2550_remove),
+ .id_table = tsl2550_id,
+};
+
+static int __init tsl2550_init(void)
+{
+ return i2c_add_driver(&tsl2550_driver);
+}
+
+static void __exit tsl2550_exit(void)
+{
+ i2c_del_driver(&tsl2550_driver);
+}
+
+MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
+MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(tsl2550_init);
+module_exit(tsl2550_exit);
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 8d8a00e..5c33a71 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -63,7 +63,6 @@ config I2C_HELPER_AUTO
source drivers/i2c/algos/Kconfig
source drivers/i2c/busses/Kconfig
-source drivers/i2c/chips/Kconfig
config I2C_DEBUG_CORE
bool "I2C Core debugging messages"
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index ba26e6c..ce5fd62 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
-obj-y += busses/ chips/ algos/
+obj-y += busses/ algos/
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
deleted file mode 100644
index ae4539d..0000000
--- a/drivers/i2c/chips/Kconfig
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Miscellaneous I2C chip drivers configuration
-#
-# *** DEPRECATED! Do not add new entries! See Makefile ***
-#
-
-menu "Miscellaneous I2C Chip support"
-
-config SENSORS_TSL2550
- tristate "Taos TSL2550 ambient light sensor"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for the Taos TSL2550
- ambient light sensor.
-
- This driver can also be built as a module. If so, the module
- will be called tsl2550.
-
-endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
deleted file mode 100644
index fe0af0f..0000000
--- a/drivers/i2c/chips/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Makefile for miscellaneous I2C chip drivers.
-#
-# Do not add new drivers to this directory! It is DEPRECATED.
-#
-# Device drivers are better grouped according to the functionality they
-# implement rather than to the bus they are connected to. In particular:
-# * Hardware monitoring chip drivers go to drivers/hwmon
-# * RTC chip drivers go to drivers/rtc
-# * I/O expander drivers go to drivers/gpio
-#
-
-obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
-
-ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
-EXTRA_CFLAGS += -DDEBUG
-endif
-
diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c
deleted file mode 100644
index a0702f3..0000000
--- a/drivers/i2c/chips/tsl2550.c
+++ /dev/null
@@ -1,473 +0,0 @@
-/*
- * tsl2550.c - Linux kernel modules for ambient light sensor
- *
- * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
- * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/i2c.h>
-#include <linux/mutex.h>
-
-#define TSL2550_DRV_NAME "tsl2550"
-#define DRIVER_VERSION "1.2"
-
-/*
- * Defines
- */
-
-#define TSL2550_POWER_DOWN 0x00
-#define TSL2550_POWER_UP 0x03
-#define TSL2550_STANDARD_RANGE 0x18
-#define TSL2550_EXTENDED_RANGE 0x1d
-#define TSL2550_READ_ADC0 0x43
-#define TSL2550_READ_ADC1 0x83
-
-/*
- * Structs
- */
-
-struct tsl2550_data {
- struct i2c_client *client;
- struct mutex update_lock;
-
- unsigned int power_state : 1;
- unsigned int operating_mode : 1;
-};
-
-/*
- * Global data
- */
-
-static const u8 TSL2550_MODE_RANGE[2] = {
- TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
-};
-
-/*
- * Management functions
- */
-
-static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
-{
- struct tsl2550_data *data = i2c_get_clientdata(client);
-
- int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
-
- data->operating_mode = mode;
-
- return ret;
-}
-
-static int tsl2550_set_power_state(struct i2c_client *client, int state)
-{
- struct tsl2550_data *data = i2c_get_clientdata(client);
- int ret;
-
- if (state == 0)
- ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
- else {
- ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
-
- /* On power up we should reset operating mode also... */
- tsl2550_set_operating_mode(client, data->operating_mode);
- }
-
- data->power_state = state;
-
- return ret;
-}
-
-static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
-{
- int ret;
-
- ret = i2c_smbus_read_byte_data(client, cmd);
- if (ret < 0)
- return ret;
- if (!(ret & 0x80))
- return -EAGAIN;
- return ret & 0x7f; /* remove the "valid" bit */
-}
-
-/*
- * LUX calculation
- */
-
-#define TSL2550_MAX_LUX 1846
-
-static const u8 ratio_lut[] = {
- 100, 100, 100, 100, 100, 100, 100, 100,
- 100, 100, 100, 100, 100, 100, 99, 99,
- 99, 99, 99, 99, 99, 99, 99, 99,
- 99, 99, 99, 98, 98, 98, 98, 98,
- 98, 98, 97, 97, 97, 97, 97, 96,
- 96, 96, 96, 95, 95, 95, 94, 94,
- 93, 93, 93, 92, 92, 91, 91, 90,
- 89, 89, 88, 87, 87, 86, 85, 84,
- 83, 82, 81, 80, 79, 78, 77, 75,
- 74, 73, 71, 69, 68, 66, 64, 62,
- 60, 58, 56, 54, 52, 49, 47, 44,
- 42, 41, 40, 40, 39, 39, 38, 38,
- 37, 37, 37, 36, 36, 36, 35, 35,
- 35, 35, 34, 34, 34, 34, 33, 33,
- 33, 33, 32, 32, 32, 32, 32, 31,
- 31, 31, 31, 31, 30, 30, 30, 30,
- 30,
-};
-
-static const u16 count_lut[] = {
- 0, 1, 2, 3, 4, 5, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15,
- 16, 18, 20, 22, 24, 26, 28, 30,
- 32, 34, 36, 38, 40, 42, 44, 46,
- 49, 53, 57, 61, 65, 69, 73, 77,
- 81, 85, 89, 93, 97, 101, 105, 109,
- 115, 123, 131, 139, 147, 155, 163, 171,
- 179, 187, 195, 203, 211, 219, 227, 235,
- 247, 263, 279, 295, 311, 327, 343, 359,
- 375, 391, 407, 423, 439, 455, 471, 487,
- 511, 543, 575, 607, 639, 671, 703, 735,
- 767, 799, 831, 863, 895, 927, 959, 991,
- 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
- 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
- 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
- 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
-};
-
-/*
- * This function is described into Taos TSL2550 Designer's Notebook
- * pages 2, 3.
- */
-static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
-{
- unsigned int lux;
-
- /* Look up count from channel values */
- u16 c0 = count_lut[ch0];
- u16 c1 = count_lut[ch1];
-
- /*
- * Calculate ratio.
- * Note: the "128" is a scaling factor
- */
- u8 r = 128;
-
- /* Avoid division by 0 and count 1 cannot be greater than count 0 */
- if (c1 <= c0)
- if (c0) {
- r = c1 * 128 / c0;
-
- /* Calculate LUX */
- lux = ((c0 - c1) * ratio_lut[r]) / 256;
- } else
- lux = 0;
- else
- return -EAGAIN;
-
- /* LUX range check */
- return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
-}
-
-/*
- * SysFS support
- */
-
-static ssize_t tsl2550_show_power_state(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
-
- return sprintf(buf, "%u\n", data->power_state);
-}
-
-static ssize_t tsl2550_store_power_state(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
-
- if (val < 0 || val > 1)
- return -EINVAL;
-
- mutex_lock(&data->update_lock);
- ret = tsl2550_set_power_state(client, val);
- mutex_unlock(&data->update_lock);
-
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
- tsl2550_show_power_state, tsl2550_store_power_state);
-
-static ssize_t tsl2550_show_operating_mode(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
-
- return sprintf(buf, "%u\n", data->operating_mode);
-}
-
-static ssize_t tsl2550_store_operating_mode(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
-
- if (val < 0 || val > 1)
- return -EINVAL;
-
- if (data->power_state == 0)
- return -EBUSY;
-
- mutex_lock(&data->update_lock);
- ret = tsl2550_set_operating_mode(client, val);
- mutex_unlock(&data->update_lock);
-
- if (ret < 0)
- return ret;
-
- return count;
-}
-
-static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
- tsl2550_show_operating_mode, tsl2550_store_operating_mode);
-
-static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
-{
- struct tsl2550_data *data = i2c_get_clientdata(client);
- u8 ch0, ch1;
- int ret;
-
- ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
- if (ret < 0)
- return ret;
- ch0 = ret;
-
- ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
- if (ret < 0)
- return ret;
- ch1 = ret;
-
- /* Do the job */
- ret = tsl2550_calculate_lux(ch0, ch1);
- if (ret < 0)
- return ret;
- if (data->operating_mode == 1)
- ret *= 5;
-
- return sprintf(buf, "%d\n", ret);
-}
-
-static ssize_t tsl2550_show_lux1_input(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct tsl2550_data *data = i2c_get_clientdata(client);
- int ret;
-
- /* No LUX data if not operational */
- if (!data->power_state)
- return -EBUSY;
-
- mutex_lock(&data->update_lock);
- ret = __tsl2550_show_lux(client, buf);
- mutex_unlock(&data->update_lock);
-
- return ret;
-}
-
-static DEVICE_ATTR(lux1_input, S_IRUGO,
- tsl2550_show_lux1_input, NULL);
-
-static struct attribute *tsl2550_attributes[] = {
- &dev_attr_power_state.attr,
- &dev_attr_operating_mode.attr,
- &dev_attr_lux1_input.attr,
- NULL
-};
-
-static const struct attribute_group tsl2550_attr_group = {
- .attrs = tsl2550_attributes,
-};
-
-/*
- * Initialization function
- */
-
-static int tsl2550_init_client(struct i2c_client *client)
-{
- struct tsl2550_data *data = i2c_get_clientdata(client);
- int err;
-
- /*
- * Probe the chip. To do so we try to power up the device and then to
- * read back the 0x03 code
- */
- err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
- if (err < 0)
- return err;
- if (err != TSL2550_POWER_UP)
- return -ENODEV;
- data->power_state = 1;
-
- /* Set the default operating mode */
- err = i2c_smbus_write_byte(client,
- TSL2550_MODE_RANGE[data->operating_mode]);
- if (err < 0)
- return err;
-
- return 0;
-}
-
-/*
- * I2C init/probing/exit functions
- */
-
-static struct i2c_driver tsl2550_driver;
-static int __devinit tsl2550_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
-{
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct tsl2550_data *data;
- int *opmode, err = 0;
-
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
- | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
- err = -EIO;
- goto exit;
- }
-
- data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
- data->client = client;
- i2c_set_clientdata(client, data);
-
- /* Check platform data */
- opmode = client->dev.platform_data;
- if (opmode) {
- if (*opmode < 0 || *opmode > 1) {
- dev_err(&client->dev, "invalid operating_mode (%d)\n",
- *opmode);
- err = -EINVAL;
- goto exit_kfree;
- }
- data->operating_mode = *opmode;
- } else
- data->operating_mode = 0; /* default mode is standard */
- dev_info(&client->dev, "%s operating mode\n",
- data->operating_mode ? "extended" : "standard");
-
- mutex_init(&data->update_lock);
-
- /* Initialize the TSL2550 chip */
- err = tsl2550_init_client(client);
- if (err)
- goto exit_kfree;
-
- /* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
- if (err)
- goto exit_kfree;
-
- dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
-
- return 0;
-
-exit_kfree:
- kfree(data);
-exit:
- return err;
-}
-
-static int __devexit tsl2550_remove(struct i2c_client *client)
-{
- sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
-
- /* Power down the device */
- tsl2550_set_power_state(client, 0);
-
- kfree(i2c_get_clientdata(client));
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
-{
- return tsl2550_set_power_state(client, 0);
-}
-
-static int tsl2550_resume(struct i2c_client *client)
-{
- return tsl2550_set_power_state(client, 1);
-}
-
-#else
-
-#define tsl2550_suspend NULL
-#define tsl2550_resume NULL
-
-#endif /* CONFIG_PM */
-
-static const struct i2c_device_id tsl2550_id[] = {
- { "tsl2550", 0 },
- { }
-};
-MODULE_DEVICE_TABLE(i2c, tsl2550_id);
-
-static struct i2c_driver tsl2550_driver = {
- .driver = {
- .name = TSL2550_DRV_NAME,
- .owner = THIS_MODULE,
- },
- .suspend = tsl2550_suspend,
- .resume = tsl2550_resume,
- .probe = tsl2550_probe,
- .remove = __devexit_p(tsl2550_remove),
- .id_table = tsl2550_id,
-};
-
-static int __init tsl2550_init(void)
-{
- return i2c_add_driver(&tsl2550_driver);
-}
-
-static void __exit tsl2550_exit(void)
-{
- i2c_del_driver(&tsl2550_driver);
-}
-
-MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
-MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
-MODULE_LICENSE("GPL");
-MODULE_VERSION(DRIVER_VERSION);
-
-module_init(tsl2550_init);
-module_exit(tsl2550_exit);
--
1.6.4.4
Hi Jonathan,
Sorry for the late answer.
On Sat, 12 Dec 2009 17:17:29 +0000, Jonathan Cameron wrote:
>
> Signed-off-by: Jonathan Cameron <[email protected]>
> ---
> V3. Against current mainline with the addition of the two als patches
> as posted by Amit.
>
> Changes since V2.
>
> Couple of bug fixes.
>
> Change from using illuminance_range_max to exposure_time. As Jean has
> described, using the range is rather problematic as actual range is
> dependent on the difference in infrared and visible + infrared light
> levels. Thus setting it to a particular value far from guarantees that
> the sensor will be able to read anywhere near the desired range.
> exposure_time brings it's own problems. For this particular chip there
> are two separate ADC's and no way of separating the proportions of
> time in which light is captured as opposed to when the ADC conversion is
> occurring. Other suggestions for how to handle this would be most welcome.
> At the moment, <800msecs is set to 160msecs, everything else to 800msecs
> which is actually made up of 400msecs on each of the two channels.
Sounds reasonable.
> Documentation has been updated appropriately.
>
> I've also removed the now unnecessary kconfig and makefile form i2c/chips.
> Jean, yell if you would prefer to split that into a separate patch or do
> it yourself.
I would prefer a separate patch, yes. Whether you do it or I do it,
doesn't really matter, as I don't expect any conflict.
>
> Documentation/ABI/testing/sysfs-class-als | 9 +
> drivers/als/Kconfig | 14 +
> drivers/als/Makefile | 2 +
> drivers/als/tsl2550.c | 496 +++++++++++++++++++++++++++++
> drivers/i2c/Kconfig | 1 -
> drivers/i2c/Makefile | 2 +-
> drivers/i2c/chips/Kconfig | 19 --
> drivers/i2c/chips/Makefile | 18 -
> drivers/i2c/chips/tsl2550.c | 473 ---------------------------
> 9 files changed, 522 insertions(+), 512 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
> index d3b33f3..732f449 100644
> --- a/Documentation/ABI/testing/sysfs-class-als
> +++ b/Documentation/ABI/testing/sysfs-class-als
> @@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
> Unit: lux (lumens per square meter)
> RO
>
> +What: /sys/class/als/.../exposure_time[n]
> +Date: Dec. 2009
> +KernelVersion: 2.6.32
> +Contact: Jonathan Cameron <[email protected]>
> +Description: Sensor exposure time. In some devices this
> + corresponds to the combined time needed to
> + to internally read several different sensors.
> + Unit: microseconds
> + RW
> diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
> index 200c52b..1564ffc 100644
> --- a/drivers/als/Kconfig
> +++ b/drivers/als/Kconfig
> @@ -8,3 +8,17 @@ menuconfig ALS
> This framework provides a generic sysfs I/F for Ambient Light
> Sensor devices.
> If you want this support, you should say Y or M here.
> +
> +if ALS
> +
> +config ALS_TSL2550
> + tristate "Taos TSL2550 ambient light sensor"
> + depends on EXPERIMENTAL && I2C
> + help
> + If you say yes here you get support for the Taos TSL2550
> + ambient light sensor.
> +
> + This driver can also be built as a module. If so, the module
> + will be called tsl2550.
> +
> +endif #ALS
> diff --git a/drivers/als/Makefile b/drivers/als/Makefile
> index a527197..7be5631 100644
> --- a/drivers/als/Makefile
> +++ b/drivers/als/Makefile
> @@ -3,3 +3,5 @@
> #
>
> obj-$(CONFIG_ALS) += als_sys.o
> +
> +obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
> \ No newline at end of file
> diff --git a/drivers/als/tsl2550.c b/drivers/als/tsl2550.c
> new file mode 100644
> index 0000000..64f7f96
> --- /dev/null
> +++ b/drivers/als/tsl2550.c
> @@ -0,0 +1,496 @@
> +/*
> + * tsl2550.c - Linux kernel modules for ambient light sensor
> + *
> + * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
> + * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
> + * Copyright (C) 2009 Jonathan Cameron <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/i2c.h>
> +#include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/als_sys.h>
> +
> +#define TSL2550_DRV_NAME "tsl2550"
> +#define DRIVER_VERSION "2.0"
> +
> +/*
> + * Defines
> + */
> +
> +#define TSL2550_POWER_DOWN 0x00
> +#define TSL2550_POWER_UP 0x03
> +#define TSL2550_STANDARD_RANGE 0x18
> +#define TSL2550_EXTENDED_RANGE 0x1d
> +#define TSL2550_READ_ADC0 0x43
> +#define TSL2550_READ_ADC1 0x83
> +
> +/*
> + * Structs
> + */
> +
> +struct tsl2550_data {
> + struct device *classdev;
> + struct i2c_client *client;
> + struct mutex update_lock;
The original driver had a blank like here, which I think made sense
(separating the administrative part from the actual device settings.
> + unsigned int power_state:1;
> + unsigned int operating_mode:1;
> +};
> +
> +/*
> + * Global data
> + */
> +
> +static const u8 TSL2550_MODE_RANGE[2] = {
> + TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
> +};
> +
> +/*
> + * Management functions
> + */
> +
> +static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
> +{
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> +
> + int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
> +
> + data->operating_mode = mode;
> +
> + return ret;
> +}
> +
> +static int tsl2550_set_power_state(struct i2c_client *client, int state)
> +{
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + int ret;
> +
> + if (state == 0)
> + ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
> + else {
> + ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
> +
> + /* On power up we should reset operating mode also... */
> + tsl2550_set_operating_mode(client, data->operating_mode);
> + }
> +
> + data->power_state = state;
> +
> + return ret;
> +}
> +
> +static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
> +{
> + int ret;
> +
> + ret = i2c_smbus_read_byte_data(client, cmd);
> + if (ret < 0)
> + return ret;
> + if (!(ret & 0x80))
> + return -EAGAIN;
> + if (ret == 0x7f)
> + return -ERANGE;
> + return ret & 0x7f; /* remove the "valid" bit */
> +}
> +
> +/*
> + * LUX calculation - note the range is dependent on combination
> + * of infrared level and visible light levels.
> + */
> +
> +#define TSL2550_MAX_LUX 1568
I would get rid of this entirely. As I explained before, there is no
way the computations can lead to a value larger than this. If we start
diverging from the "official" algorithm then we might as well drop the
useless parts altogether.
> +
> +static const u8 ratio_lut[] = {
> + 100, 100, 100, 100, 100, 100, 100, 100,
> + 100, 100, 100, 100, 100, 100, 99, 99,
> + 99, 99, 99, 99, 99, 99, 99, 99,
> + 99, 99, 99, 98, 98, 98, 98, 98,
> + 98, 98, 97, 97, 97, 97, 97, 96,
> + 96, 96, 96, 95, 95, 95, 94, 94,
> + 93, 93, 93, 92, 92, 91, 91, 90,
> + 89, 89, 88, 87, 87, 86, 85, 84,
> + 83, 82, 81, 80, 79, 78, 77, 75,
> + 74, 73, 71, 69, 68, 66, 64, 62,
> + 60, 58, 56, 54, 52, 49, 47, 44,
> + 42, 41, 40, 40, 39, 39, 38, 38,
> + 37, 37, 37, 36, 36, 36, 35, 35,
> + 35, 35, 34, 34, 34, 34, 33, 33,
> + 33, 33, 32, 32, 32, 32, 32, 31,
> + 31, 31, 31, 31, 30, 30, 30, 30,
> + 30,
> +};
> +
> +static const u16 count_lut[] = {
> + 0, 1, 2, 3, 4, 5, 6, 7,
> + 8, 9, 10, 11, 12, 13, 14, 15,
> + 16, 18, 20, 22, 24, 26, 28, 30,
> + 32, 34, 36, 38, 40, 42, 44, 46,
> + 49, 53, 57, 61, 65, 69, 73, 77,
> + 81, 85, 89, 93, 97, 101, 105, 109,
> + 115, 123, 131, 139, 147, 155, 163, 171,
> + 179, 187, 195, 203, 211, 219, 227, 235,
> + 247, 263, 279, 295, 311, 327, 343, 359,
> + 375, 391, 407, 423, 439, 455, 471, 487,
> + 511, 543, 575, 607, 639, 671, 703, 735,
> + 767, 799, 831, 863, 895, 927, 959, 991,
> + 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
> + 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
> + 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
> + 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
> +};
> +
> +/*
> + * This function is described into Taos TSL2550 Designer's Notebook
> + * pages 2, 3.
> + */
> +static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> +{
> + unsigned int lux;
> +
> + /* Look up count from channel values */
> + u16 c0 = count_lut[ch0];
> + u16 c1 = count_lut[ch1];
> +
> + /*
> + * Calculate ratio.
> + * Note: the "128" is a scaling factor
> + */
> + u8 r = 128;
> +
> + /* Avoid division by 0 and count 1 cannot be greater than count 0 */
> + if (c1 <= c0)
> + if (c0) {
> + r = c1 * 128 / c0;
> +
> + /* Calculate LUX */
> + lux = ((c0 - c1) * ratio_lut[r]) / 256;
> + } else
> + lux = 0;
> + else
> + return -EAGAIN;
> +
> + /* LUX range check */
> + return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
> +}
> +
> +/*
> + * SysFS support
> + */
> +
> +static ssize_t tsl2550_show_power_state(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> +
> + return sprintf(buf, "%u\n", data->power_state);
> +}
> +
> +static ssize_t tsl2550_store_power_state(struct device *dev,
> + struct device_attribute *attr, const char *buf, size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + unsigned long val;
> + int ret = strict_strtoul(buf, 10, &val);
> +
> + if (val < 0 || val > 1 || ret)
> + return -EINVAL;
> +
> + mutex_lock(&data->update_lock);
> + ret = tsl2550_set_power_state(client, val);
> + mutex_unlock(&data->update_lock);
> +
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
> + tsl2550_show_power_state, tsl2550_store_power_state);
> +
> +static ssize_t tsl2550_show_exposure(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + if (data->operating_mode)
> + return sprintf(buf, "160000\n");
> + else
> + return sprintf(buf, "800000\n");
> +}
> +
> +static ssize_t tsl2550_store_exposure(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t count)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + unsigned long val;
> +
> + int ret = strict_strtoul(buf, 10, &val);
> +
> + if (ret)
> + return -EINVAL;
> + mutex_lock(&data->update_lock);
> + if (val >= 800000)
> + ret = tsl2550_set_operating_mode(client, 0);
> + else
> + ret = tsl2550_set_operating_mode(client, 1);
> + mutex_unlock(&data->update_lock);
> + if (ret < 0)
> + return ret;
> +
> + return count;
> +}
> +
> +static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
> + tsl2550_show_exposure, tsl2550_store_exposure);
> +
> +static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> +{
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + u8 ch0, ch1;
> + int ret;
> +
> + ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
> + if (ret < 0)
> + return ret;
> + ch0 = ret;
> +
> + ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
> + if (ret < 0)
> + return ret;
> + ch1 = ret;
> +
> + /* Do the job */
> + ret = tsl2550_calculate_lux(ch0, ch1);
> + if (ret < 0)
> + return ret;
> + if (data->operating_mode == 1)
> + ret *= 5;
> +
> + return sprintf(buf, "%d\n", ret);
> +}
> +
> +static ssize_t tsl2550_show_lux1_input(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + int ret;
> +
> + /* No LUX data if not operational */
> + if (!data->power_state)
> + return -EBUSY;
> +
> + mutex_lock(&data->update_lock);
> + ret = __tsl2550_show_lux(client, buf);
> + mutex_unlock(&data->update_lock);
> +
> + return ret;
> +}
> +
> +static DEVICE_ATTR(illuminance0, S_IRUGO,
> + tsl2550_show_lux1_input, NULL);
> +
> +static struct attribute *tsl2550_attributes[] = {
> + &dev_attr_power_state.attr,
> + &dev_attr_exposure_time0.attr,
> + &dev_attr_illuminance0.attr,
> + NULL
> +};
> +
> +static const struct attribute_group tsl2550_attr_group = {
> + .attrs = tsl2550_attributes,
> +};
> +
> +/*
> + * Initialization function
> + */
> +
> +static int tsl2550_init_client(struct i2c_client *client)
> +{
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + int err;
> +
> + /*
> + * Probe the chip. To do so we try to power up the device and then to
> + * read back the 0x03 code
> + */
> + err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
> + if (err < 0)
> + return err;
> + if (err != TSL2550_POWER_UP)
> + return -ENODEV;
> + data->power_state = 1;
> +
> + /* Set the default operating mode */
> + err = i2c_smbus_write_byte(client,
> + TSL2550_MODE_RANGE[data->operating_mode]);
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +/*
> + * I2C init/probing/exit functions
> + */
> +
> +static struct i2c_driver tsl2550_driver;
> +static int __devinit tsl2550_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> + struct tsl2550_data *data;
> + int *opmode, err = 0;
> +
> + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
> + | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
> + err = -EIO;
> + goto exit;
> + }
> +
> + data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
> + if (!data) {
> + err = -ENOMEM;
> + goto exit;
> + }
> + data->client = client;
> + i2c_set_clientdata(client, data);
> +
> + /* Check platform data */
> + opmode = client->dev.platform_data;
> + if (opmode) {
> + if (*opmode < 0 || *opmode > 1) {
> + dev_err(&client->dev, "invalid operating_mode (%d)\n",
> + *opmode);
> + err = -EINVAL;
> + goto exit_kfree;
> + }
> + data->operating_mode = *opmode;
> + } else
> + data->operating_mode = 0; /* default mode is standard */
> + dev_info(&client->dev, "%s operating mode\n",
> + data->operating_mode ? "extended" : "standard");
> +
> + mutex_init(&data->update_lock);
> +
> + /* Initialize the TSL2550 chip */
> + err = tsl2550_init_client(client);
> + if (err)
> + goto exit_kfree;
> +
> + /* Register sysfs hooks */
> + data->classdev = als_device_register(&client->dev);
> + if (IS_ERR(data->classdev)) {
> + err = PTR_ERR(data->classdev);
> + goto exit_kfree;
> + }
> +
> + err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
> + if (err)
> + goto exit_unreg;
> +
> + dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
> +
> + return 0;
> +
> +exit_unreg:
> + als_device_unregister(data->classdev);
> +exit_kfree:
> + kfree(data);
> +exit:
> + return err;
> +}
> +
> +static int __devexit tsl2550_remove(struct i2c_client *client)
> +{
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> +
> + sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
> + als_device_unregister(data->classdev);
> +
> + /* Power down the device */
> + tsl2550_set_power_state(client, 0);
> +
> + kfree(data);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +
> +static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
> +{
> + return tsl2550_set_power_state(client, 0);
> +}
> +
> +static int tsl2550_resume(struct i2c_client *client)
> +{
> + return tsl2550_set_power_state(client, 1);
> +}
> +
> +#else
> +
> +#define tsl2550_suspend NULL
> +#define tsl2550_resume NULL
> +
> +#endif /* CONFIG_PM */
> +
> +static const struct i2c_device_id tsl2550_id[] = {
> + { "tsl2550", 0 },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, tsl2550_id);
> +
> +static struct i2c_driver tsl2550_driver = {
> + .driver = {
> + .name = TSL2550_DRV_NAME,
> + .owner = THIS_MODULE,
> + },
> + .suspend = tsl2550_suspend,
> + .resume = tsl2550_resume,
> + .probe = tsl2550_probe,
> + .remove = __devexit_p(tsl2550_remove),
> + .id_table = tsl2550_id,
> +};
> +
> +static int __init tsl2550_init(void)
> +{
> + return i2c_add_driver(&tsl2550_driver);
> +}
> +
> +static void __exit tsl2550_exit(void)
> +{
> + i2c_del_driver(&tsl2550_driver);
> +}
> +
> +MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
> +MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(DRIVER_VERSION);
> +
> +module_init(tsl2550_init);
> +module_exit(tsl2550_exit);
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 8d8a00e..5c33a71 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -63,7 +63,6 @@ config I2C_HELPER_AUTO
>
> source drivers/i2c/algos/Kconfig
> source drivers/i2c/busses/Kconfig
> -source drivers/i2c/chips/Kconfig
>
> config I2C_DEBUG_CORE
> bool "I2C Core debugging messages"
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index ba26e6c..ce5fd62 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -5,7 +5,7 @@
> obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
> obj-$(CONFIG_I2C) += i2c-core.o
> obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
> -obj-y += busses/ chips/ algos/
> +obj-y += busses/ algos/
>
> ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
> EXTRA_CFLAGS += -DDEBUG
> diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
> deleted file mode 100644
> index ae4539d..0000000
> --- a/drivers/i2c/chips/Kconfig
> +++ /dev/null
> @@ -1,19 +0,0 @@
> -#
> -# Miscellaneous I2C chip drivers configuration
> -#
> -# *** DEPRECATED! Do not add new entries! See Makefile ***
> -#
> -
> -menu "Miscellaneous I2C Chip support"
> -
> -config SENSORS_TSL2550
> - tristate "Taos TSL2550 ambient light sensor"
> - depends on EXPERIMENTAL
> - help
> - If you say yes here you get support for the Taos TSL2550
> - ambient light sensor.
> -
> - This driver can also be built as a module. If so, the module
> - will be called tsl2550.
> -
> -endmenu
> diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
> deleted file mode 100644
> index fe0af0f..0000000
> --- a/drivers/i2c/chips/Makefile
> +++ /dev/null
> @@ -1,18 +0,0 @@
> -#
> -# Makefile for miscellaneous I2C chip drivers.
> -#
> -# Do not add new drivers to this directory! It is DEPRECATED.
> -#
> -# Device drivers are better grouped according to the functionality they
> -# implement rather than to the bus they are connected to. In particular:
> -# * Hardware monitoring chip drivers go to drivers/hwmon
> -# * RTC chip drivers go to drivers/rtc
> -# * I/O expander drivers go to drivers/gpio
> -#
> -
> -obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
> -
> -ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
> -EXTRA_CFLAGS += -DDEBUG
> -endif
> -
> diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/i2c/chips/tsl2550.c
> deleted file mode 100644
> index a0702f3..0000000
> --- a/drivers/i2c/chips/tsl2550.c
> +++ /dev/null
> @@ -1,473 +0,0 @@
> -/*
> - * tsl2550.c - Linux kernel modules for ambient light sensor
> - *
> - * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
> - * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License as published by
> - * the Free Software Foundation; either version 2 of the License, or
> - * (at your option) any later version.
> - *
> - * This program is distributed in the hope that it will be useful,
> - * but WITHOUT ANY WARRANTY; without even the implied warranty of
> - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> - * GNU General Public License for more details.
> - *
> - * You should have received a copy of the GNU General Public License
> - * along with this program; if not, write to the Free Software
> - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
> - */
> -
> -#include <linux/module.h>
> -#include <linux/init.h>
> -#include <linux/slab.h>
> -#include <linux/i2c.h>
> -#include <linux/mutex.h>
> -
> -#define TSL2550_DRV_NAME "tsl2550"
> -#define DRIVER_VERSION "1.2"
> -
> -/*
> - * Defines
> - */
> -
> -#define TSL2550_POWER_DOWN 0x00
> -#define TSL2550_POWER_UP 0x03
> -#define TSL2550_STANDARD_RANGE 0x18
> -#define TSL2550_EXTENDED_RANGE 0x1d
> -#define TSL2550_READ_ADC0 0x43
> -#define TSL2550_READ_ADC1 0x83
> -
> -/*
> - * Structs
> - */
> -
> -struct tsl2550_data {
> - struct i2c_client *client;
> - struct mutex update_lock;
> -
> - unsigned int power_state : 1;
> - unsigned int operating_mode : 1;
> -};
> -
> -/*
> - * Global data
> - */
> -
> -static const u8 TSL2550_MODE_RANGE[2] = {
> - TSL2550_STANDARD_RANGE, TSL2550_EXTENDED_RANGE,
> -};
> -
> -/*
> - * Management functions
> - */
> -
> -static int tsl2550_set_operating_mode(struct i2c_client *client, int mode)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> -
> - int ret = i2c_smbus_write_byte(client, TSL2550_MODE_RANGE[mode]);
> -
> - data->operating_mode = mode;
> -
> - return ret;
> -}
> -
> -static int tsl2550_set_power_state(struct i2c_client *client, int state)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - int ret;
> -
> - if (state == 0)
> - ret = i2c_smbus_write_byte(client, TSL2550_POWER_DOWN);
> - else {
> - ret = i2c_smbus_write_byte(client, TSL2550_POWER_UP);
> -
> - /* On power up we should reset operating mode also... */
> - tsl2550_set_operating_mode(client, data->operating_mode);
> - }
> -
> - data->power_state = state;
> -
> - return ret;
> -}
> -
> -static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
> -{
> - int ret;
> -
> - ret = i2c_smbus_read_byte_data(client, cmd);
> - if (ret < 0)
> - return ret;
> - if (!(ret & 0x80))
> - return -EAGAIN;
> - return ret & 0x7f; /* remove the "valid" bit */
> -}
> -
> -/*
> - * LUX calculation
> - */
> -
> -#define TSL2550_MAX_LUX 1846
> -
> -static const u8 ratio_lut[] = {
> - 100, 100, 100, 100, 100, 100, 100, 100,
> - 100, 100, 100, 100, 100, 100, 99, 99,
> - 99, 99, 99, 99, 99, 99, 99, 99,
> - 99, 99, 99, 98, 98, 98, 98, 98,
> - 98, 98, 97, 97, 97, 97, 97, 96,
> - 96, 96, 96, 95, 95, 95, 94, 94,
> - 93, 93, 93, 92, 92, 91, 91, 90,
> - 89, 89, 88, 87, 87, 86, 85, 84,
> - 83, 82, 81, 80, 79, 78, 77, 75,
> - 74, 73, 71, 69, 68, 66, 64, 62,
> - 60, 58, 56, 54, 52, 49, 47, 44,
> - 42, 41, 40, 40, 39, 39, 38, 38,
> - 37, 37, 37, 36, 36, 36, 35, 35,
> - 35, 35, 34, 34, 34, 34, 33, 33,
> - 33, 33, 32, 32, 32, 32, 32, 31,
> - 31, 31, 31, 31, 30, 30, 30, 30,
> - 30,
> -};
> -
> -static const u16 count_lut[] = {
> - 0, 1, 2, 3, 4, 5, 6, 7,
> - 8, 9, 10, 11, 12, 13, 14, 15,
> - 16, 18, 20, 22, 24, 26, 28, 30,
> - 32, 34, 36, 38, 40, 42, 44, 46,
> - 49, 53, 57, 61, 65, 69, 73, 77,
> - 81, 85, 89, 93, 97, 101, 105, 109,
> - 115, 123, 131, 139, 147, 155, 163, 171,
> - 179, 187, 195, 203, 211, 219, 227, 235,
> - 247, 263, 279, 295, 311, 327, 343, 359,
> - 375, 391, 407, 423, 439, 455, 471, 487,
> - 511, 543, 575, 607, 639, 671, 703, 735,
> - 767, 799, 831, 863, 895, 927, 959, 991,
> - 1039, 1103, 1167, 1231, 1295, 1359, 1423, 1487,
> - 1551, 1615, 1679, 1743, 1807, 1871, 1935, 1999,
> - 2095, 2223, 2351, 2479, 2607, 2735, 2863, 2991,
> - 3119, 3247, 3375, 3503, 3631, 3759, 3887, 4015,
> -};
> -
> -/*
> - * This function is described into Taos TSL2550 Designer's Notebook
> - * pages 2, 3.
> - */
> -static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> -{
> - unsigned int lux;
> -
> - /* Look up count from channel values */
> - u16 c0 = count_lut[ch0];
> - u16 c1 = count_lut[ch1];
> -
> - /*
> - * Calculate ratio.
> - * Note: the "128" is a scaling factor
> - */
> - u8 r = 128;
> -
> - /* Avoid division by 0 and count 1 cannot be greater than count 0 */
> - if (c1 <= c0)
> - if (c0) {
> - r = c1 * 128 / c0;
> -
> - /* Calculate LUX */
> - lux = ((c0 - c1) * ratio_lut[r]) / 256;
> - } else
> - lux = 0;
> - else
> - return -EAGAIN;
> -
> - /* LUX range check */
> - return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
> -}
> -
> -/*
> - * SysFS support
> - */
> -
> -static ssize_t tsl2550_show_power_state(struct device *dev,
> - struct device_attribute *attr, char *buf)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> -
> - return sprintf(buf, "%u\n", data->power_state);
> -}
> -
> -static ssize_t tsl2550_store_power_state(struct device *dev,
> - struct device_attribute *attr, const char *buf, size_t count)
> -{
> - struct i2c_client *client = to_i2c_client(dev);
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> -
> - if (val < 0 || val > 1)
> - return -EINVAL;
> -
> - mutex_lock(&data->update_lock);
> - ret = tsl2550_set_power_state(client, val);
> - mutex_unlock(&data->update_lock);
> -
> - if (ret < 0)
> - return ret;
> -
> - return count;
> -}
> -
> -static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
> - tsl2550_show_power_state, tsl2550_store_power_state);
> -
> -static ssize_t tsl2550_show_operating_mode(struct device *dev,
> - struct device_attribute *attr, char *buf)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> -
> - return sprintf(buf, "%u\n", data->operating_mode);
> -}
> -
> -static ssize_t tsl2550_store_operating_mode(struct device *dev,
> - struct device_attribute *attr, const char *buf, size_t count)
> -{
> - struct i2c_client *client = to_i2c_client(dev);
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> -
> - if (val < 0 || val > 1)
> - return -EINVAL;
> -
> - if (data->power_state == 0)
> - return -EBUSY;
> -
> - mutex_lock(&data->update_lock);
> - ret = tsl2550_set_operating_mode(client, val);
> - mutex_unlock(&data->update_lock);
> -
> - if (ret < 0)
> - return ret;
> -
> - return count;
> -}
> -
> -static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
> - tsl2550_show_operating_mode, tsl2550_store_operating_mode);
> -
> -static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - u8 ch0, ch1;
> - int ret;
> -
> - ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC0);
> - if (ret < 0)
> - return ret;
> - ch0 = ret;
> -
> - ret = tsl2550_get_adc_value(client, TSL2550_READ_ADC1);
> - if (ret < 0)
> - return ret;
> - ch1 = ret;
> -
> - /* Do the job */
> - ret = tsl2550_calculate_lux(ch0, ch1);
> - if (ret < 0)
> - return ret;
> - if (data->operating_mode == 1)
> - ret *= 5;
> -
> - return sprintf(buf, "%d\n", ret);
> -}
> -
> -static ssize_t tsl2550_show_lux1_input(struct device *dev,
> - struct device_attribute *attr, char *buf)
> -{
> - struct i2c_client *client = to_i2c_client(dev);
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - int ret;
> -
> - /* No LUX data if not operational */
> - if (!data->power_state)
> - return -EBUSY;
> -
> - mutex_lock(&data->update_lock);
> - ret = __tsl2550_show_lux(client, buf);
> - mutex_unlock(&data->update_lock);
> -
> - return ret;
> -}
> -
> -static DEVICE_ATTR(lux1_input, S_IRUGO,
> - tsl2550_show_lux1_input, NULL);
> -
> -static struct attribute *tsl2550_attributes[] = {
> - &dev_attr_power_state.attr,
> - &dev_attr_operating_mode.attr,
> - &dev_attr_lux1_input.attr,
> - NULL
> -};
> -
> -static const struct attribute_group tsl2550_attr_group = {
> - .attrs = tsl2550_attributes,
> -};
> -
> -/*
> - * Initialization function
> - */
> -
> -static int tsl2550_init_client(struct i2c_client *client)
> -{
> - struct tsl2550_data *data = i2c_get_clientdata(client);
> - int err;
> -
> - /*
> - * Probe the chip. To do so we try to power up the device and then to
> - * read back the 0x03 code
> - */
> - err = i2c_smbus_read_byte_data(client, TSL2550_POWER_UP);
> - if (err < 0)
> - return err;
> - if (err != TSL2550_POWER_UP)
> - return -ENODEV;
> - data->power_state = 1;
> -
> - /* Set the default operating mode */
> - err = i2c_smbus_write_byte(client,
> - TSL2550_MODE_RANGE[data->operating_mode]);
> - if (err < 0)
> - return err;
> -
> - return 0;
> -}
> -
> -/*
> - * I2C init/probing/exit functions
> - */
> -
> -static struct i2c_driver tsl2550_driver;
> -static int __devinit tsl2550_probe(struct i2c_client *client,
> - const struct i2c_device_id *id)
> -{
> - struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
> - struct tsl2550_data *data;
> - int *opmode, err = 0;
> -
> - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE
> - | I2C_FUNC_SMBUS_READ_BYTE_DATA)) {
> - err = -EIO;
> - goto exit;
> - }
> -
> - data = kzalloc(sizeof(struct tsl2550_data), GFP_KERNEL);
> - if (!data) {
> - err = -ENOMEM;
> - goto exit;
> - }
> - data->client = client;
> - i2c_set_clientdata(client, data);
> -
> - /* Check platform data */
> - opmode = client->dev.platform_data;
> - if (opmode) {
> - if (*opmode < 0 || *opmode > 1) {
> - dev_err(&client->dev, "invalid operating_mode (%d)\n",
> - *opmode);
> - err = -EINVAL;
> - goto exit_kfree;
> - }
> - data->operating_mode = *opmode;
> - } else
> - data->operating_mode = 0; /* default mode is standard */
> - dev_info(&client->dev, "%s operating mode\n",
> - data->operating_mode ? "extended" : "standard");
> -
> - mutex_init(&data->update_lock);
> -
> - /* Initialize the TSL2550 chip */
> - err = tsl2550_init_client(client);
> - if (err)
> - goto exit_kfree;
> -
> - /* Register sysfs hooks */
> - err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
> - if (err)
> - goto exit_kfree;
> -
> - dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
> -
> - return 0;
> -
> -exit_kfree:
> - kfree(data);
> -exit:
> - return err;
> -}
> -
> -static int __devexit tsl2550_remove(struct i2c_client *client)
> -{
> - sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
> -
> - /* Power down the device */
> - tsl2550_set_power_state(client, 0);
> -
> - kfree(i2c_get_clientdata(client));
> -
> - return 0;
> -}
> -
> -#ifdef CONFIG_PM
> -
> -static int tsl2550_suspend(struct i2c_client *client, pm_message_t mesg)
> -{
> - return tsl2550_set_power_state(client, 0);
> -}
> -
> -static int tsl2550_resume(struct i2c_client *client)
> -{
> - return tsl2550_set_power_state(client, 1);
> -}
> -
> -#else
> -
> -#define tsl2550_suspend NULL
> -#define tsl2550_resume NULL
> -
> -#endif /* CONFIG_PM */
> -
> -static const struct i2c_device_id tsl2550_id[] = {
> - { "tsl2550", 0 },
> - { }
> -};
> -MODULE_DEVICE_TABLE(i2c, tsl2550_id);
> -
> -static struct i2c_driver tsl2550_driver = {
> - .driver = {
> - .name = TSL2550_DRV_NAME,
> - .owner = THIS_MODULE,
> - },
> - .suspend = tsl2550_suspend,
> - .resume = tsl2550_resume,
> - .probe = tsl2550_probe,
> - .remove = __devexit_p(tsl2550_remove),
> - .id_table = tsl2550_id,
> -};
> -
> -static int __init tsl2550_init(void)
> -{
> - return i2c_add_driver(&tsl2550_driver);
> -}
> -
> -static void __exit tsl2550_exit(void)
> -{
> - i2c_del_driver(&tsl2550_driver);
> -}
> -
> -MODULE_AUTHOR("Rodolfo Giometti <[email protected]>");
> -MODULE_DESCRIPTION("TSL2550 ambient light sensor driver");
> -MODULE_LICENSE("GPL");
> -MODULE_VERSION(DRIVER_VERSION);
> -
> -module_init(tsl2550_init);
> -module_exit(tsl2550_exit);
All the rest looks just fine. Thanks a lot for driving this. I really
hope we can get rid of drivers/i2c/chips really soon now!
--
Jean Delvare
Signed-off-by: Jonathan Cameron <[email protected]>
---
Simple changes to meet all of Jean's comments.
Made formatting changes, removed the defined maximum lux value
and separated out the crushing of drivers/i2c/chips.
If Jean is happy with fixes I would propose taking this through the ALS tree.
Attached is a full patch without git move detection.
Documentation/ABI/testing/sysfs-class-als | 9 +++
drivers/als/Kconfig | 14 ++++
drivers/als/Makefile | 2 +
drivers/{i2c/chips => als}/tsl2550.c | 101 +++++++++++++++++-----------
drivers/i2c/Kconfig | 1 -
drivers/i2c/Makefile | 2 +-
drivers/i2c/chips/Kconfig | 10 ---
drivers/i2c/chips/Makefile | 2 -
8 files changed, 87 insertions(+), 54 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
index d3b33f3..732f449 100644
--- a/Documentation/ABI/testing/sysfs-class-als
+++ b/Documentation/ABI/testing/sysfs-class-als
@@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
Unit: lux (lumens per square meter)
RO
+What: /sys/class/als/.../exposure_time[n]
+Date: Dec. 2009
+KernelVersion: 2.6.32
+Contact: Jonathan Cameron <[email protected]>
+Description: Sensor exposure time. In some devices this
+ corresponds to the combined time needed to
+ to internally read several different sensors.
+ Unit: microseconds
+ RW
diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
index 200c52b..1564ffc 100644
--- a/drivers/als/Kconfig
+++ b/drivers/als/Kconfig
@@ -8,3 +8,17 @@ menuconfig ALS
This framework provides a generic sysfs I/F for Ambient Light
Sensor devices.
If you want this support, you should say Y or M here.
+
+if ALS
+
+config ALS_TSL2550
+ tristate "Taos TSL2550 ambient light sensor"
+ depends on EXPERIMENTAL && I2C
+ help
+ If you say yes here you get support for the Taos TSL2550
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2550.
+
+endif #ALS
diff --git a/drivers/als/Makefile b/drivers/als/Makefile
index a527197..7be5631 100644
--- a/drivers/als/Makefile
+++ b/drivers/als/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_ALS) += als_sys.o
+
+obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
\ No newline at end of file
diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
similarity index 81%
rename from drivers/i2c/chips/tsl2550.c
rename to drivers/als/tsl2550.c
index a0702f3..8d5f2a1 100644
--- a/drivers/i2c/chips/tsl2550.c
+++ b/drivers/als/tsl2550.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2007 Rodolfo Giometti <[email protected]>
* Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
+ * Copyright (C) 2009 Jonathan Cameron <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,9 +25,11 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/als_sys.h>
#define TSL2550_DRV_NAME "tsl2550"
-#define DRIVER_VERSION "1.2"
+#define DRIVER_VERSION "2.0"
/*
* Defines
@@ -44,11 +47,12 @@
*/
struct tsl2550_data {
+ struct device *classdev;
struct i2c_client *client;
struct mutex update_lock;
- unsigned int power_state : 1;
- unsigned int operating_mode : 1;
+ unsigned int power_state:1;
+ unsigned int operating_mode:1;
};
/*
@@ -102,15 +106,16 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
return ret;
if (!(ret & 0x80))
return -EAGAIN;
+ if (ret == 0x7f)
+ return -ERANGE;
return ret & 0x7f; /* remove the "valid" bit */
}
/*
- * LUX calculation
+ * LUX calculation - note the range is dependent on combination
+ * of infrared level and visible light levels.
*/
-#define TSL2550_MAX_LUX 1846
-
static const u8 ratio_lut[] = {
100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 99, 99,
@@ -180,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
else
return -EAGAIN;
- /* LUX range check */
- return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
+ return lux;
}
/*
@@ -191,7 +195,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
static ssize_t tsl2550_show_power_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%u\n", data->power_state);
}
@@ -199,12 +204,12 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
static ssize_t tsl2550_store_power_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
+ unsigned long val;
+ int ret = strict_strtoul(buf, 10, &val);
- if (val < 0 || val > 1)
+ if (val < 0 || val > 1 || ret)
return -EINVAL;
mutex_lock(&data->update_lock);
@@ -220,40 +225,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
tsl2550_show_power_state, tsl2550_store_power_state);
-static ssize_t tsl2550_show_operating_mode(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t tsl2550_show_exposure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
-
- return sprintf(buf, "%u\n", data->operating_mode);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ if (data->operating_mode)
+ return sprintf(buf, "160000\n");
+ else
+ return sprintf(buf, "800000\n");
}
-static ssize_t tsl2550_store_operating_mode(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t tsl2550_store_exposure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
-
- if (val < 0 || val > 1)
- return -EINVAL;
+ unsigned long val;
- if (data->power_state == 0)
- return -EBUSY;
+ int ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
mutex_lock(&data->update_lock);
- ret = tsl2550_set_operating_mode(client, val);
+ if (val >= 800000)
+ ret = tsl2550_set_operating_mode(client, 0);
+ else
+ ret = tsl2550_set_operating_mode(client, 1);
mutex_unlock(&data->update_lock);
-
if (ret < 0)
return ret;
return count;
}
-static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
- tsl2550_show_operating_mode, tsl2550_store_operating_mode);
+static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
+ tsl2550_show_exposure, tsl2550_store_exposure);
static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
{
@@ -284,7 +294,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
static ssize_t tsl2550_show_lux1_input(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
int ret;
@@ -299,13 +309,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
return ret;
}
-static DEVICE_ATTR(lux1_input, S_IRUGO,
+static DEVICE_ATTR(illuminance0, S_IRUGO,
tsl2550_show_lux1_input, NULL);
static struct attribute *tsl2550_attributes[] = {
&dev_attr_power_state.attr,
- &dev_attr_operating_mode.attr,
- &dev_attr_lux1_input.attr,
+ &dev_attr_exposure_time0.attr,
+ &dev_attr_illuminance0.attr,
NULL
};
@@ -391,14 +401,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
goto exit_kfree;
/* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
- if (err)
+ data->classdev = als_device_register(&client->dev);
+ if (IS_ERR(data->classdev)) {
+ err = PTR_ERR(data->classdev);
goto exit_kfree;
+ }
+
+ err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
+ if (err)
+ goto exit_unreg;
dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
return 0;
+exit_unreg:
+ als_device_unregister(data->classdev);
exit_kfree:
kfree(data);
exit:
@@ -407,12 +425,15 @@ exit:
static int __devexit tsl2550_remove(struct i2c_client *client)
{
- sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
+ als_device_unregister(data->classdev);
/* Power down the device */
tsl2550_set_power_state(client, 0);
- kfree(i2c_get_clientdata(client));
+ kfree(data);
return 0;
}
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 8d8a00e..5c33a71 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -63,7 +63,6 @@ config I2C_HELPER_AUTO
source drivers/i2c/algos/Kconfig
source drivers/i2c/busses/Kconfig
-source drivers/i2c/chips/Kconfig
config I2C_DEBUG_CORE
bool "I2C Core debugging messages"
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index ba26e6c..ce5fd62 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
obj-$(CONFIG_I2C) += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
-obj-y += busses/ chips/ algos/
+obj-y += busses/ algos/
ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index ae4539d..c11f8d6 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -6,14 +6,4 @@
menu "Miscellaneous I2C Chip support"
-config SENSORS_TSL2550
- tristate "Taos TSL2550 ambient light sensor"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for the Taos TSL2550
- ambient light sensor.
-
- This driver can also be built as a module. If so, the module
- will be called tsl2550.
-
endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index fe0af0f..ffde18d 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -10,8 +10,6 @@
# * I/O expander drivers go to drivers/gpio
#
-obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
-
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif
--
1.6.4.4
Gah! Sorry all, I forgot to move the i2c/Makefile etc changes
to the directory removal patch!
New version in a few mins.
>
> Signed-off-by: Jonathan Cameron <[email protected]>
> ---
>
> Simple changes to meet all of Jean's comments.
>
> Made formatting changes, removed the defined maximum lux value
> and separated out the crushing of drivers/i2c/chips.
>
> If Jean is happy with fixes I would propose taking this through the ALS tree.
>
> Attached is a full patch without git move detection.
>
> Documentation/ABI/testing/sysfs-class-als | 9 +++
> drivers/als/Kconfig | 14 ++++
> drivers/als/Makefile | 2 +
> drivers/{i2c/chips => als}/tsl2550.c | 101 +++++++++++++++++-----------
> drivers/i2c/Kconfig | 1 -
> drivers/i2c/Makefile | 2 +-
> drivers/i2c/chips/Kconfig | 10 ---
> drivers/i2c/chips/Makefile | 2 -
> 8 files changed, 87 insertions(+), 54 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
> index d3b33f3..732f449 100644
> --- a/Documentation/ABI/testing/sysfs-class-als
> +++ b/Documentation/ABI/testing/sysfs-class-als
> @@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
> Unit: lux (lumens per square meter)
> RO
>
> +What: /sys/class/als/.../exposure_time[n]
> +Date: Dec. 2009
> +KernelVersion: 2.6.32
> +Contact: Jonathan Cameron <[email protected]>
> +Description: Sensor exposure time. In some devices this
> + corresponds to the combined time needed to
> + to internally read several different sensors.
> + Unit: microseconds
> + RW
> diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
> index 200c52b..1564ffc 100644
> --- a/drivers/als/Kconfig
> +++ b/drivers/als/Kconfig
> @@ -8,3 +8,17 @@ menuconfig ALS
> This framework provides a generic sysfs I/F for Ambient Light
> Sensor devices.
> If you want this support, you should say Y or M here.
> +
> +if ALS
> +
> +config ALS_TSL2550
> + tristate "Taos TSL2550 ambient light sensor"
> + depends on EXPERIMENTAL && I2C
> + help
> + If you say yes here you get support for the Taos TSL2550
> + ambient light sensor.
> +
> + This driver can also be built as a module. If so, the module
> + will be called tsl2550.
> +
> +endif #ALS
> diff --git a/drivers/als/Makefile b/drivers/als/Makefile
> index a527197..7be5631 100644
> --- a/drivers/als/Makefile
> +++ b/drivers/als/Makefile
> @@ -3,3 +3,5 @@
> #
>
> obj-$(CONFIG_ALS) += als_sys.o
> +
> +obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
> \ No newline at end of file
> diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
> similarity index 81%
> rename from drivers/i2c/chips/tsl2550.c
> rename to drivers/als/tsl2550.c
> index a0702f3..8d5f2a1 100644
> --- a/drivers/i2c/chips/tsl2550.c
> +++ b/drivers/als/tsl2550.c
> @@ -3,6 +3,7 @@
> *
> * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
> * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
> + * Copyright (C) 2009 Jonathan Cameron <[email protected]>
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> @@ -24,9 +25,11 @@
> #include <linux/slab.h>
> #include <linux/i2c.h>
> #include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/als_sys.h>
>
> #define TSL2550_DRV_NAME "tsl2550"
> -#define DRIVER_VERSION "1.2"
> +#define DRIVER_VERSION "2.0"
>
> /*
> * Defines
> @@ -44,11 +47,12 @@
> */
>
> struct tsl2550_data {
> + struct device *classdev;
> struct i2c_client *client;
> struct mutex update_lock;
>
> - unsigned int power_state : 1;
> - unsigned int operating_mode : 1;
> + unsigned int power_state:1;
> + unsigned int operating_mode:1;
> };
>
> /*
> @@ -102,15 +106,16 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
> return ret;
> if (!(ret & 0x80))
> return -EAGAIN;
> + if (ret == 0x7f)
> + return -ERANGE;
> return ret & 0x7f; /* remove the "valid" bit */
> }
>
> /*
> - * LUX calculation
> + * LUX calculation - note the range is dependent on combination
> + * of infrared level and visible light levels.
> */
>
> -#define TSL2550_MAX_LUX 1846
> -
> static const u8 ratio_lut[] = {
> 100, 100, 100, 100, 100, 100, 100, 100,
> 100, 100, 100, 100, 100, 100, 99, 99,
> @@ -180,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> else
> return -EAGAIN;
>
> - /* LUX range check */
> - return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
> + return lux;
> }
>
> /*
> @@ -191,7 +195,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> static ssize_t tsl2550_show_power_state(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
>
> return sprintf(buf, "%u\n", data->power_state);
> }
> @@ -199,12 +204,12 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
> static ssize_t tsl2550_store_power_state(struct device *dev,
> struct device_attribute *attr, const char *buf, size_t count)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> + unsigned long val;
> + int ret = strict_strtoul(buf, 10, &val);
>
> - if (val < 0 || val > 1)
> + if (val < 0 || val > 1 || ret)
> return -EINVAL;
>
> mutex_lock(&data->update_lock);
> @@ -220,40 +225,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
> static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
> tsl2550_show_power_state, tsl2550_store_power_state);
>
> -static ssize_t tsl2550_show_operating_mode(struct device *dev,
> - struct device_attribute *attr, char *buf)
> +static ssize_t tsl2550_show_exposure(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> {
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> -
> - return sprintf(buf, "%u\n", data->operating_mode);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + if (data->operating_mode)
> + return sprintf(buf, "160000\n");
> + else
> + return sprintf(buf, "800000\n");
> }
>
> -static ssize_t tsl2550_store_operating_mode(struct device *dev,
> - struct device_attribute *attr, const char *buf, size_t count)
> +static ssize_t tsl2550_store_exposure(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t count)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> -
> - if (val < 0 || val > 1)
> - return -EINVAL;
> + unsigned long val;
>
> - if (data->power_state == 0)
> - return -EBUSY;
> + int ret = strict_strtoul(buf, 10, &val);
>
> + if (ret)
> + return -EINVAL;
> mutex_lock(&data->update_lock);
> - ret = tsl2550_set_operating_mode(client, val);
> + if (val >= 800000)
> + ret = tsl2550_set_operating_mode(client, 0);
> + else
> + ret = tsl2550_set_operating_mode(client, 1);
> mutex_unlock(&data->update_lock);
> -
> if (ret < 0)
> return ret;
>
> return count;
> }
>
> -static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
> - tsl2550_show_operating_mode, tsl2550_store_operating_mode);
> +static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
> + tsl2550_show_exposure, tsl2550_store_exposure);
>
> static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> {
> @@ -284,7 +294,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> static ssize_t tsl2550_show_lux1_input(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> int ret;
>
> @@ -299,13 +309,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
> return ret;
> }
>
> -static DEVICE_ATTR(lux1_input, S_IRUGO,
> +static DEVICE_ATTR(illuminance0, S_IRUGO,
> tsl2550_show_lux1_input, NULL);
>
> static struct attribute *tsl2550_attributes[] = {
> &dev_attr_power_state.attr,
> - &dev_attr_operating_mode.attr,
> - &dev_attr_lux1_input.attr,
> + &dev_attr_exposure_time0.attr,
> + &dev_attr_illuminance0.attr,
> NULL
> };
>
> @@ -391,14 +401,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
> goto exit_kfree;
>
> /* Register sysfs hooks */
> - err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
> - if (err)
> + data->classdev = als_device_register(&client->dev);
> + if (IS_ERR(data->classdev)) {
> + err = PTR_ERR(data->classdev);
> goto exit_kfree;
> + }
> +
> + err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
> + if (err)
> + goto exit_unreg;
>
> dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
>
> return 0;
>
> +exit_unreg:
> + als_device_unregister(data->classdev);
> exit_kfree:
> kfree(data);
> exit:
> @@ -407,12 +425,15 @@ exit:
>
> static int __devexit tsl2550_remove(struct i2c_client *client)
> {
> - sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> +
> + sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
> + als_device_unregister(data->classdev);
>
> /* Power down the device */
> tsl2550_set_power_state(client, 0);
>
> - kfree(i2c_get_clientdata(client));
> + kfree(data);
>
> return 0;
> }
> diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
> index 8d8a00e..5c33a71 100644
> --- a/drivers/i2c/Kconfig
> +++ b/drivers/i2c/Kconfig
> @@ -63,7 +63,6 @@ config I2C_HELPER_AUTO
>
> source drivers/i2c/algos/Kconfig
> source drivers/i2c/busses/Kconfig
> -source drivers/i2c/chips/Kconfig
>
> config I2C_DEBUG_CORE
> bool "I2C Core debugging messages"
> diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
> index ba26e6c..ce5fd62 100644
> --- a/drivers/i2c/Makefile
> +++ b/drivers/i2c/Makefile
> @@ -5,7 +5,7 @@
> obj-$(CONFIG_I2C_BOARDINFO) += i2c-boardinfo.o
> obj-$(CONFIG_I2C) += i2c-core.o
> obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
> -obj-y += busses/ chips/ algos/
> +obj-y += busses/ algos/
>
> ifeq ($(CONFIG_I2C_DEBUG_CORE),y)
> EXTRA_CFLAGS += -DDEBUG
> diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
> index ae4539d..c11f8d6 100644
> --- a/drivers/i2c/chips/Kconfig
> +++ b/drivers/i2c/chips/Kconfig
> @@ -6,14 +6,4 @@
>
> menu "Miscellaneous I2C Chip support"
>
> -config SENSORS_TSL2550
> - tristate "Taos TSL2550 ambient light sensor"
> - depends on EXPERIMENTAL
> - help
> - If you say yes here you get support for the Taos TSL2550
> - ambient light sensor.
> -
> - This driver can also be built as a module. If so, the module
> - will be called tsl2550.
> -
> endmenu
> diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
> index fe0af0f..ffde18d 100644
> --- a/drivers/i2c/chips/Makefile
> +++ b/drivers/i2c/chips/Makefile
> @@ -10,8 +10,6 @@
> # * I/O expander drivers go to drivers/gpio
> #
>
> -obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
> -
> ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
> EXTRA_CFLAGS += -DDEBUG
> endif
Signed-off-by: Jonathan Cameron <[email protected]>
---
Reverted incorrect removal of i2c/chips references in i2c/Kconfig etc.
Documentation/ABI/testing/sysfs-class-als | 9 +++
drivers/als/Kconfig | 14 ++++
drivers/als/Makefile | 2 +
drivers/{i2c/chips => als}/tsl2550.c | 101 +++++++++++++++++-----------
drivers/i2c/chips/Kconfig | 10 ---
drivers/i2c/chips/Makefile | 2 -
6 files changed, 86 insertions(+), 52 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
index d3b33f3..732f449 100644
--- a/Documentation/ABI/testing/sysfs-class-als
+++ b/Documentation/ABI/testing/sysfs-class-als
@@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
Unit: lux (lumens per square meter)
RO
+What: /sys/class/als/.../exposure_time[n]
+Date: Dec. 2009
+KernelVersion: 2.6.32
+Contact: Jonathan Cameron <[email protected]>
+Description: Sensor exposure time. In some devices this
+ corresponds to the combined time needed to
+ to internally read several different sensors.
+ Unit: microseconds
+ RW
diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
index 200c52b..1564ffc 100644
--- a/drivers/als/Kconfig
+++ b/drivers/als/Kconfig
@@ -8,3 +8,17 @@ menuconfig ALS
This framework provides a generic sysfs I/F for Ambient Light
Sensor devices.
If you want this support, you should say Y or M here.
+
+if ALS
+
+config ALS_TSL2550
+ tristate "Taos TSL2550 ambient light sensor"
+ depends on EXPERIMENTAL && I2C
+ help
+ If you say yes here you get support for the Taos TSL2550
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2550.
+
+endif #ALS
diff --git a/drivers/als/Makefile b/drivers/als/Makefile
index a527197..7be5631 100644
--- a/drivers/als/Makefile
+++ b/drivers/als/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_ALS) += als_sys.o
+
+obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
\ No newline at end of file
diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
similarity index 81%
rename from drivers/i2c/chips/tsl2550.c
rename to drivers/als/tsl2550.c
index a0702f3..8d5f2a1 100644
--- a/drivers/i2c/chips/tsl2550.c
+++ b/drivers/als/tsl2550.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2007 Rodolfo Giometti <[email protected]>
* Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
+ * Copyright (C) 2009 Jonathan Cameron <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,9 +25,11 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/als_sys.h>
#define TSL2550_DRV_NAME "tsl2550"
-#define DRIVER_VERSION "1.2"
+#define DRIVER_VERSION "2.0"
/*
* Defines
@@ -44,11 +47,12 @@
*/
struct tsl2550_data {
+ struct device *classdev;
struct i2c_client *client;
struct mutex update_lock;
- unsigned int power_state : 1;
- unsigned int operating_mode : 1;
+ unsigned int power_state:1;
+ unsigned int operating_mode:1;
};
/*
@@ -102,15 +106,16 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
return ret;
if (!(ret & 0x80))
return -EAGAIN;
+ if (ret == 0x7f)
+ return -ERANGE;
return ret & 0x7f; /* remove the "valid" bit */
}
/*
- * LUX calculation
+ * LUX calculation - note the range is dependent on combination
+ * of infrared level and visible light levels.
*/
-#define TSL2550_MAX_LUX 1846
-
static const u8 ratio_lut[] = {
100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 99, 99,
@@ -180,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
else
return -EAGAIN;
- /* LUX range check */
- return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
+ return lux;
}
/*
@@ -191,7 +195,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
static ssize_t tsl2550_show_power_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%u\n", data->power_state);
}
@@ -199,12 +204,12 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
static ssize_t tsl2550_store_power_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
+ unsigned long val;
+ int ret = strict_strtoul(buf, 10, &val);
- if (val < 0 || val > 1)
+ if (val < 0 || val > 1 || ret)
return -EINVAL;
mutex_lock(&data->update_lock);
@@ -220,40 +225,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
tsl2550_show_power_state, tsl2550_store_power_state);
-static ssize_t tsl2550_show_operating_mode(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t tsl2550_show_exposure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
-
- return sprintf(buf, "%u\n", data->operating_mode);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ if (data->operating_mode)
+ return sprintf(buf, "160000\n");
+ else
+ return sprintf(buf, "800000\n");
}
-static ssize_t tsl2550_store_operating_mode(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t tsl2550_store_exposure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
-
- if (val < 0 || val > 1)
- return -EINVAL;
+ unsigned long val;
- if (data->power_state == 0)
- return -EBUSY;
+ int ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
mutex_lock(&data->update_lock);
- ret = tsl2550_set_operating_mode(client, val);
+ if (val >= 800000)
+ ret = tsl2550_set_operating_mode(client, 0);
+ else
+ ret = tsl2550_set_operating_mode(client, 1);
mutex_unlock(&data->update_lock);
-
if (ret < 0)
return ret;
return count;
}
-static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
- tsl2550_show_operating_mode, tsl2550_store_operating_mode);
+static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
+ tsl2550_show_exposure, tsl2550_store_exposure);
static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
{
@@ -284,7 +294,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
static ssize_t tsl2550_show_lux1_input(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
int ret;
@@ -299,13 +309,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
return ret;
}
-static DEVICE_ATTR(lux1_input, S_IRUGO,
+static DEVICE_ATTR(illuminance0, S_IRUGO,
tsl2550_show_lux1_input, NULL);
static struct attribute *tsl2550_attributes[] = {
&dev_attr_power_state.attr,
- &dev_attr_operating_mode.attr,
- &dev_attr_lux1_input.attr,
+ &dev_attr_exposure_time0.attr,
+ &dev_attr_illuminance0.attr,
NULL
};
@@ -391,14 +401,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
goto exit_kfree;
/* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
- if (err)
+ data->classdev = als_device_register(&client->dev);
+ if (IS_ERR(data->classdev)) {
+ err = PTR_ERR(data->classdev);
goto exit_kfree;
+ }
+
+ err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
+ if (err)
+ goto exit_unreg;
dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
return 0;
+exit_unreg:
+ als_device_unregister(data->classdev);
exit_kfree:
kfree(data);
exit:
@@ -407,12 +425,15 @@ exit:
static int __devexit tsl2550_remove(struct i2c_client *client)
{
- sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
+ als_device_unregister(data->classdev);
/* Power down the device */
tsl2550_set_power_state(client, 0);
- kfree(i2c_get_clientdata(client));
+ kfree(data);
return 0;
}
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index ae4539d..c11f8d6 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -6,14 +6,4 @@
menu "Miscellaneous I2C Chip support"
-config SENSORS_TSL2550
- tristate "Taos TSL2550 ambient light sensor"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for the Taos TSL2550
- ambient light sensor.
-
- This driver can also be built as a module. If so, the module
- will be called tsl2550.
-
endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index fe0af0f..ffde18d 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -10,8 +10,6 @@
# * I/O expander drivers go to drivers/gpio
#
-obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
-
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif
--
1.6.4.4
Hi Jonathan,
On Tue, 26 Jan 2010 18:57:02 +0000, Jonathan Cameron wrote:
> Signed-off-by: Jonathan Cameron <[email protected]>
> ---
> Reverted incorrect removal of i2c/chips references in i2c/Kconfig etc.
>
> Documentation/ABI/testing/sysfs-class-als | 9 +++
> drivers/als/Kconfig | 14 ++++
> drivers/als/Makefile | 2 +
> drivers/{i2c/chips => als}/tsl2550.c | 101 +++++++++++++++++-----------
> drivers/i2c/chips/Kconfig | 10 ---
> drivers/i2c/chips/Makefile | 2 -
> 6 files changed, 86 insertions(+), 52 deletions(-)
>
Some more comments... sorry for not seeing this before, the git patch
format for moving files makes things easier to review for sure.
> diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
> index d3b33f3..732f449 100644
> --- a/Documentation/ABI/testing/sysfs-class-als
> +++ b/Documentation/ABI/testing/sysfs-class-als
> @@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
> Unit: lux (lumens per square meter)
> RO
>
> +What: /sys/class/als/.../exposure_time[n]
> +Date: Dec. 2009
> +KernelVersion: 2.6.32
> +Contact: Jonathan Cameron <[email protected]>
> +Description: Sensor exposure time. In some devices this
> + corresponds to the combined time needed to
> + to internally read several different sensors.
> + Unit: microseconds
> + RW
> diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
> index 200c52b..1564ffc 100644
> --- a/drivers/als/Kconfig
> +++ b/drivers/als/Kconfig
> @@ -8,3 +8,17 @@ menuconfig ALS
> This framework provides a generic sysfs I/F for Ambient Light
> Sensor devices.
> If you want this support, you should say Y or M here.
> +
> +if ALS
> +
> +config ALS_TSL2550
> + tristate "Taos TSL2550 ambient light sensor"
> + depends on EXPERIMENTAL && I2C
> + help
> + If you say yes here you get support for the Taos TSL2550
> + ambient light sensor.
> +
> + This driver can also be built as a module. If so, the module
> + will be called tsl2550.
> +
> +endif #ALS
> diff --git a/drivers/als/Makefile b/drivers/als/Makefile
> index a527197..7be5631 100644
> --- a/drivers/als/Makefile
> +++ b/drivers/als/Makefile
> @@ -3,3 +3,5 @@
> #
>
> obj-$(CONFIG_ALS) += als_sys.o
> +
> +obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
> \ No newline at end of file
Not sure if the missing new-line is in the old file or the new. If the
new, please fix it.
> diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
> similarity index 81%
> rename from drivers/i2c/chips/tsl2550.c
> rename to drivers/als/tsl2550.c
> index a0702f3..8d5f2a1 100644
> --- a/drivers/i2c/chips/tsl2550.c
> +++ b/drivers/als/tsl2550.c
> @@ -3,6 +3,7 @@
> *
> * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
> * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
> + * Copyright (C) 2009 Jonathan Cameron <[email protected]>
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License as published by
> @@ -24,9 +25,11 @@
> #include <linux/slab.h>
> #include <linux/i2c.h>
> #include <linux/mutex.h>
> +#include <linux/err.h>
> +#include <linux/als_sys.h>
>
> #define TSL2550_DRV_NAME "tsl2550"
> -#define DRIVER_VERSION "1.2"
> +#define DRIVER_VERSION "2.0"
>
> /*
> * Defines
> @@ -44,11 +47,12 @@
> */
>
> struct tsl2550_data {
> + struct device *classdev;
> struct i2c_client *client;
> struct mutex update_lock;
>
> - unsigned int power_state : 1;
> - unsigned int operating_mode : 1;
> + unsigned int power_state:1;
> + unsigned int operating_mode:1;
These style changes don't seem needed.
> };
>
> /*
> @@ -102,15 +106,16 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
> return ret;
> if (!(ret & 0x80))
> return -EAGAIN;
> + if (ret == 0x7f)
> + return -ERANGE;
> return ret & 0x7f; /* remove the "valid" bit */
> }
>
> /*
> - * LUX calculation
> + * LUX calculation - note the range is dependent on combination
> + * of infrared level and visible light levels.
> */
>
> -#define TSL2550_MAX_LUX 1846
> -
> static const u8 ratio_lut[] = {
> 100, 100, 100, 100, 100, 100, 100, 100,
> 100, 100, 100, 100, 100, 100, 99, 99,
> @@ -180,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> else
> return -EAGAIN;
>
> - /* LUX range check */
> - return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
> + return lux;
> }
On second thought, this clean-up is unrelated to the driver move to
ALS... so it might be better left for a later, separate patch?
>
> /*
> @@ -191,7 +195,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> static ssize_t tsl2550_show_power_state(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
>
> return sprintf(buf, "%u\n", data->power_state);
> }
> @@ -199,12 +204,12 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
> static ssize_t tsl2550_store_power_state(struct device *dev,
> struct device_attribute *attr, const char *buf, size_t count)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> + unsigned long val;
> + int ret = strict_strtoul(buf, 10, &val);
>
> - if (val < 0 || val > 1)
> + if (val < 0 || val > 1 || ret)
It would be much more logical to test ret first and val next, rather
than the other way around.
I also have a personal preference for not including code that can fail
in the variable declaration section. But up to you of course.
> return -EINVAL;
>
> mutex_lock(&data->update_lock);
> @@ -220,40 +225,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
> static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
> tsl2550_show_power_state, tsl2550_store_power_state);
>
> -static ssize_t tsl2550_show_operating_mode(struct device *dev,
> - struct device_attribute *attr, char *buf)
> +static ssize_t tsl2550_show_exposure(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> {
> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
> -
> - return sprintf(buf, "%u\n", data->operating_mode);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> + if (data->operating_mode)
> + return sprintf(buf, "160000\n");
> + else
> + return sprintf(buf, "800000\n");
> }
>
> -static ssize_t tsl2550_store_operating_mode(struct device *dev,
> - struct device_attribute *attr, const char *buf, size_t count)
> +static ssize_t tsl2550_store_exposure(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t count)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> - unsigned long val = simple_strtoul(buf, NULL, 10);
> - int ret;
> -
> - if (val < 0 || val > 1)
> - return -EINVAL;
> + unsigned long val;
>
> - if (data->power_state == 0)
> - return -EBUSY;
> + int ret = strict_strtoul(buf, 10, &val);
>
> + if (ret)
> + return -EINVAL;
> mutex_lock(&data->update_lock);
> - ret = tsl2550_set_operating_mode(client, val);
> + if (val >= 800000)
> + ret = tsl2550_set_operating_mode(client, 0);
> + else
> + ret = tsl2550_set_operating_mode(client, 1);
> mutex_unlock(&data->update_lock);
> -
> if (ret < 0)
> return ret;
>
> return count;
> }
>
> -static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
> - tsl2550_show_operating_mode, tsl2550_store_operating_mode);
> +static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
> + tsl2550_show_exposure, tsl2550_store_exposure);
>
> static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> {
> @@ -284,7 +294,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
> static ssize_t tsl2550_show_lux1_input(struct device *dev,
> struct device_attribute *attr, char *buf)
> {
> - struct i2c_client *client = to_i2c_client(dev);
> + struct i2c_client *client = to_i2c_client(dev->parent);
> struct tsl2550_data *data = i2c_get_clientdata(client);
> int ret;
>
> @@ -299,13 +309,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
> return ret;
> }
>
> -static DEVICE_ATTR(lux1_input, S_IRUGO,
> +static DEVICE_ATTR(illuminance0, S_IRUGO,
> tsl2550_show_lux1_input, NULL);
>
> static struct attribute *tsl2550_attributes[] = {
> &dev_attr_power_state.attr,
> - &dev_attr_operating_mode.attr,
> - &dev_attr_lux1_input.attr,
> + &dev_attr_exposure_time0.attr,
> + &dev_attr_illuminance0.attr,
> NULL
> };
>
> @@ -391,14 +401,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
> goto exit_kfree;
>
> /* Register sysfs hooks */
> - err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
> - if (err)
> + data->classdev = als_device_register(&client->dev);
> + if (IS_ERR(data->classdev)) {
> + err = PTR_ERR(data->classdev);
> goto exit_kfree;
> + }
> +
> + err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
> + if (err)
> + goto exit_unreg;
>
> dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
>
> return 0;
>
> +exit_unreg:
> + als_device_unregister(data->classdev);
> exit_kfree:
> kfree(data);
> exit:
> @@ -407,12 +425,15 @@ exit:
>
> static int __devexit tsl2550_remove(struct i2c_client *client)
> {
> - sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
> + struct tsl2550_data *data = i2c_get_clientdata(client);
> +
> + sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
> + als_device_unregister(data->classdev);
>
> /* Power down the device */
> tsl2550_set_power_state(client, 0);
>
> - kfree(i2c_get_clientdata(client));
> + kfree(data);
>
> return 0;
> }
> diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
> index ae4539d..c11f8d6 100644
> --- a/drivers/i2c/chips/Kconfig
> +++ b/drivers/i2c/chips/Kconfig
> @@ -6,14 +6,4 @@
>
> menu "Miscellaneous I2C Chip support"
>
> -config SENSORS_TSL2550
> - tristate "Taos TSL2550 ambient light sensor"
> - depends on EXPERIMENTAL
> - help
> - If you say yes here you get support for the Taos TSL2550
> - ambient light sensor.
> -
> - This driver can also be built as a module. If so, the module
> - will be called tsl2550.
> -
> endmenu
> diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
> index fe0af0f..ffde18d 100644
> --- a/drivers/i2c/chips/Makefile
> +++ b/drivers/i2c/chips/Makefile
> @@ -10,8 +10,6 @@
> # * I/O expander drivers go to drivers/gpio
> #
>
> -obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
> -
> ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
> EXTRA_CFLAGS += -DDEBUG
> endif
All the rest looks OK to me. Feel free to add:
Acked-by: Jean Delvare <[email protected]>
--
Jean Delvare
On 01/27/10 13:36, Jean Delvare wrote:
> Hi Jonathan,
>
> On Tue, 26 Jan 2010 18:57:02 +0000, Jonathan Cameron wrote:
>> Signed-off-by: Jonathan Cameron <[email protected]>
>> ---
>> Reverted incorrect removal of i2c/chips references in i2c/Kconfig etc.
>>
>> Documentation/ABI/testing/sysfs-class-als | 9 +++
>> drivers/als/Kconfig | 14 ++++
>> drivers/als/Makefile | 2 +
>> drivers/{i2c/chips => als}/tsl2550.c | 101 +++++++++++++++++-----------
>> drivers/i2c/chips/Kconfig | 10 ---
>> drivers/i2c/chips/Makefile | 2 -
>> 6 files changed, 86 insertions(+), 52 deletions(-)
>>
>
> Some more comments... sorry for not seeing this before, the git patch
> format for moving files makes things easier to review for sure.
Indeed, very handy feature when making these sorts of changes.
>
>> diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
>> index d3b33f3..732f449 100644
>> --- a/Documentation/ABI/testing/sysfs-class-als
>> +++ b/Documentation/ABI/testing/sysfs-class-als
>> @@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
>> Unit: lux (lumens per square meter)
>> RO
>>
>> +What: /sys/class/als/.../exposure_time[n]
>> +Date: Dec. 2009
>> +KernelVersion: 2.6.32
>> +Contact: Jonathan Cameron <[email protected]>
>> +Description: Sensor exposure time. In some devices this
>> + corresponds to the combined time needed to
>> + to internally read several different sensors.
>> + Unit: microseconds
>> + RW
>> diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
>> index 200c52b..1564ffc 100644
>> --- a/drivers/als/Kconfig
>> +++ b/drivers/als/Kconfig
>> @@ -8,3 +8,17 @@ menuconfig ALS
>> This framework provides a generic sysfs I/F for Ambient Light
>> Sensor devices.
>> If you want this support, you should say Y or M here.
>> +
>> +if ALS
>> +
>> +config ALS_TSL2550
>> + tristate "Taos TSL2550 ambient light sensor"
>> + depends on EXPERIMENTAL && I2C
>> + help
>> + If you say yes here you get support for the Taos TSL2550
>> + ambient light sensor.
>> +
>> + This driver can also be built as a module. If so, the module
>> + will be called tsl2550.
>> +
>> +endif #ALS
>> diff --git a/drivers/als/Makefile b/drivers/als/Makefile
>> index a527197..7be5631 100644
>> --- a/drivers/als/Makefile
>> +++ b/drivers/als/Makefile
>> @@ -3,3 +3,5 @@
>> #
>>
>> obj-$(CONFIG_ALS) += als_sys.o
>> +
>> +obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
>> \ No newline at end of file
>
> Not sure if the missing new-line is in the old file or the new. If the
> new, please fix it.
Good point.
>
>> diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
>> similarity index 81%
>> rename from drivers/i2c/chips/tsl2550.c
>> rename to drivers/als/tsl2550.c
>> index a0702f3..8d5f2a1 100644
>> --- a/drivers/i2c/chips/tsl2550.c
>> +++ b/drivers/als/tsl2550.c
>> @@ -3,6 +3,7 @@
>> *
>> * Copyright (C) 2007 Rodolfo Giometti <[email protected]>
>> * Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
>> + * Copyright (C) 2009 Jonathan Cameron <[email protected]>
>> *
>> * This program is free software; you can redistribute it and/or modify
>> * it under the terms of the GNU General Public License as published by
>> @@ -24,9 +25,11 @@
>> #include <linux/slab.h>
>> #include <linux/i2c.h>
>> #include <linux/mutex.h>
>> +#include <linux/err.h>
>> +#include <linux/als_sys.h>
>>
>> #define TSL2550_DRV_NAME "tsl2550"
>> -#define DRIVER_VERSION "1.2"
>> +#define DRIVER_VERSION "2.0"
>>
>> /*
>> * Defines
>> @@ -44,11 +47,12 @@
>> */
>>
>> struct tsl2550_data {
>> + struct device *classdev;
>> struct i2c_client *client;
>> struct mutex update_lock;
>>
>> - unsigned int power_state : 1;
>> - unsigned int operating_mode : 1;
>> + unsigned int power_state:1;
>> + unsigned int operating_mode:1;
>
> These style changes don't seem needed.
iirc checkpatch.pl fussiness, but arguably shouldn't be in this patch which claims
just to be a move. Personally I don't care about this one, so for simplicity
I'll just drop the change. Afterall, checkpatch won't see it if I do move detection :)
>
>> };
>>
>> /*
>> @@ -102,15 +106,16 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
>> return ret;
>> if (!(ret & 0x80))
>> return -EAGAIN;
>> + if (ret == 0x7f)
>> + return -ERANGE;
>> return ret & 0x7f; /* remove the "valid" bit */
>> }
>>
>> /*
>> - * LUX calculation
>> + * LUX calculation - note the range is dependent on combination
>> + * of infrared level and visible light levels.
>> */
>>
>> -#define TSL2550_MAX_LUX 1846
>> -
>> static const u8 ratio_lut[] = {
>> 100, 100, 100, 100, 100, 100, 100, 100,
>> 100, 100, 100, 100, 100, 100, 99, 99,
>> @@ -180,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
>> else
>> return -EAGAIN;
>>
>> - /* LUX range check */
>> - return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
>> + return lux;
>> }
>
> On second thought, this clean-up is unrelated to the driver move to
> ALS... so it might be better left for a later, separate patch?
That is true. I'll break it out so we have a pair of patches.
Just for clarity (and because you spotted the issue in the first place!)
I'd like your ack on that new patch.
>
>>
>> /*
>> @@ -191,7 +195,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
>> static ssize_t tsl2550_show_power_state(struct device *dev,
>> struct device_attribute *attr, char *buf)
>> {
>> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> + struct tsl2550_data *data = i2c_get_clientdata(client);
>>
>> return sprintf(buf, "%u\n", data->power_state);
>> }
>> @@ -199,12 +204,12 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
>> static ssize_t tsl2550_store_power_state(struct device *dev,
>> struct device_attribute *attr, const char *buf, size_t count)
>> {
>> - struct i2c_client *client = to_i2c_client(dev);
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> struct tsl2550_data *data = i2c_get_clientdata(client);
>> - unsigned long val = simple_strtoul(buf, NULL, 10);
>> - int ret;
>> + unsigned long val;
>> + int ret = strict_strtoul(buf, 10, &val);
>>
>> - if (val < 0 || val > 1)
>> + if (val < 0 || val > 1 || ret)
>
> It would be much more logical to test ret first and val next, rather
> than the other way around.
Good point.
>
> I also have a personal preference for not including code that can fail
> in the variable declaration section. But up to you of course.
I don't care so I'll make the change...
>
>> return -EINVAL;
>>
>> mutex_lock(&data->update_lock);
>> @@ -220,40 +225,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
>> static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
>> tsl2550_show_power_state, tsl2550_store_power_state);
>>
>> -static ssize_t tsl2550_show_operating_mode(struct device *dev,
>> - struct device_attribute *attr, char *buf)
>> +static ssize_t tsl2550_show_exposure(struct device *dev,
>> + struct device_attribute *attr,
>> + char *buf)
>> {
>> - struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
>> -
>> - return sprintf(buf, "%u\n", data->operating_mode);
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> + struct tsl2550_data *data = i2c_get_clientdata(client);
>> + if (data->operating_mode)
>> + return sprintf(buf, "160000\n");
>> + else
>> + return sprintf(buf, "800000\n");
>> }
>>
>> -static ssize_t tsl2550_store_operating_mode(struct device *dev,
>> - struct device_attribute *attr, const char *buf, size_t count)
>> +static ssize_t tsl2550_store_exposure(struct device *dev,
>> + struct device_attribute *attr,
>> + const char *buf,
>> + size_t count)
>> {
>> - struct i2c_client *client = to_i2c_client(dev);
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> struct tsl2550_data *data = i2c_get_clientdata(client);
>> - unsigned long val = simple_strtoul(buf, NULL, 10);
>> - int ret;
>> -
>> - if (val < 0 || val > 1)
>> - return -EINVAL;
>> + unsigned long val;
>>
>> - if (data->power_state == 0)
>> - return -EBUSY;
>> + int ret = strict_strtoul(buf, 10, &val);
>>
>> + if (ret)
>> + return -EINVAL;
>> mutex_lock(&data->update_lock);
>> - ret = tsl2550_set_operating_mode(client, val);
>> + if (val >= 800000)
>> + ret = tsl2550_set_operating_mode(client, 0);
>> + else
>> + ret = tsl2550_set_operating_mode(client, 1);
>> mutex_unlock(&data->update_lock);
>> -
>> if (ret < 0)
>> return ret;
>>
>> return count;
>> }
>>
>> -static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
>> - tsl2550_show_operating_mode, tsl2550_store_operating_mode);
>> +static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
>> + tsl2550_show_exposure, tsl2550_store_exposure);
>>
>> static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
>> {
>> @@ -284,7 +294,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
>> static ssize_t tsl2550_show_lux1_input(struct device *dev,
>> struct device_attribute *attr, char *buf)
>> {
>> - struct i2c_client *client = to_i2c_client(dev);
>> + struct i2c_client *client = to_i2c_client(dev->parent);
>> struct tsl2550_data *data = i2c_get_clientdata(client);
>> int ret;
>>
>> @@ -299,13 +309,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
>> return ret;
>> }
>>
>> -static DEVICE_ATTR(lux1_input, S_IRUGO,
>> +static DEVICE_ATTR(illuminance0, S_IRUGO,
>> tsl2550_show_lux1_input, NULL);
>>
>> static struct attribute *tsl2550_attributes[] = {
>> &dev_attr_power_state.attr,
>> - &dev_attr_operating_mode.attr,
>> - &dev_attr_lux1_input.attr,
>> + &dev_attr_exposure_time0.attr,
>> + &dev_attr_illuminance0.attr,
>> NULL
>> };
>>
>> @@ -391,14 +401,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
>> goto exit_kfree;
>>
>> /* Register sysfs hooks */
>> - err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
>> - if (err)
>> + data->classdev = als_device_register(&client->dev);
>> + if (IS_ERR(data->classdev)) {
>> + err = PTR_ERR(data->classdev);
>> goto exit_kfree;
>> + }
>> +
>> + err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
>> + if (err)
>> + goto exit_unreg;
>>
>> dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
>>
>> return 0;
>>
>> +exit_unreg:
>> + als_device_unregister(data->classdev);
>> exit_kfree:
>> kfree(data);
>> exit:
>> @@ -407,12 +425,15 @@ exit:
>>
>> static int __devexit tsl2550_remove(struct i2c_client *client)
>> {
>> - sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
>> + struct tsl2550_data *data = i2c_get_clientdata(client);
>> +
>> + sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
>> + als_device_unregister(data->classdev);
>>
>> /* Power down the device */
>> tsl2550_set_power_state(client, 0);
>>
>> - kfree(i2c_get_clientdata(client));
>> + kfree(data);
>>
>> return 0;
>> }
>> diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
>> index ae4539d..c11f8d6 100644
>> --- a/drivers/i2c/chips/Kconfig
>> +++ b/drivers/i2c/chips/Kconfig
>> @@ -6,14 +6,4 @@
>>
>> menu "Miscellaneous I2C Chip support"
>>
>> -config SENSORS_TSL2550
>> - tristate "Taos TSL2550 ambient light sensor"
>> - depends on EXPERIMENTAL
>> - help
>> - If you say yes here you get support for the Taos TSL2550
>> - ambient light sensor.
>> -
>> - This driver can also be built as a module. If so, the module
>> - will be called tsl2550.
>> -
>> endmenu
>> diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
>> index fe0af0f..ffde18d 100644
>> --- a/drivers/i2c/chips/Makefile
>> +++ b/drivers/i2c/chips/Makefile
>> @@ -10,8 +10,6 @@
>> # * I/O expander drivers go to drivers/gpio
>> #
>>
>> -obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
>> -
>> ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
>> EXTRA_CFLAGS += -DDEBUG
>> endif
>
> All the rest looks OK to me. Feel free to add:
>
> Acked-by: Jean Delvare <[email protected]>
Cool, for reference I'll post the final version as a reply to this and take it into
the ALS for-next tree.
Jonathan
Signed-off-by: Jonathan Cameron <[email protected]>
Acked-by: Jean Delvare <[email protected]>
---
Final version with change Jean requested + Jean's ack.
Patch 2 will contain the changes to clear out the spurious max lux
test.
Documentation/ABI/testing/sysfs-class-als | 9 +++
drivers/als/Kconfig | 14 +++++
drivers/als/Makefile | 2 +
drivers/{i2c/chips => als}/tsl2550.c | 91 ++++++++++++++++++----------
drivers/i2c/chips/Kconfig | 10 ---
drivers/i2c/chips/Makefile | 2 -
6 files changed, 83 insertions(+), 45 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-class-als b/Documentation/ABI/testing/sysfs-class-als
index d3b33f3..732f449 100644
--- a/Documentation/ABI/testing/sysfs-class-als
+++ b/Documentation/ABI/testing/sysfs-class-als
@@ -7,3 +7,12 @@ Description: Current Ambient Light Illuminance reported by
Unit: lux (lumens per square meter)
RO
+What: /sys/class/als/.../exposure_time[n]
+Date: Dec. 2009
+KernelVersion: 2.6.32
+Contact: Jonathan Cameron <[email protected]>
+Description: Sensor exposure time. In some devices this
+ corresponds to the combined time needed to
+ to internally read several different sensors.
+ Unit: microseconds
+ RW
diff --git a/drivers/als/Kconfig b/drivers/als/Kconfig
index 200c52b..1564ffc 100644
--- a/drivers/als/Kconfig
+++ b/drivers/als/Kconfig
@@ -8,3 +8,17 @@ menuconfig ALS
This framework provides a generic sysfs I/F for Ambient Light
Sensor devices.
If you want this support, you should say Y or M here.
+
+if ALS
+
+config ALS_TSL2550
+ tristate "Taos TSL2550 ambient light sensor"
+ depends on EXPERIMENTAL && I2C
+ help
+ If you say yes here you get support for the Taos TSL2550
+ ambient light sensor.
+
+ This driver can also be built as a module. If so, the module
+ will be called tsl2550.
+
+endif #ALS
diff --git a/drivers/als/Makefile b/drivers/als/Makefile
index a527197..314c645 100644
--- a/drivers/als/Makefile
+++ b/drivers/als/Makefile
@@ -3,3 +3,5 @@
#
obj-$(CONFIG_ALS) += als_sys.o
+
+obj-$(CONFIG_ALS_TSL2550) += tsl2550.o
diff --git a/drivers/i2c/chips/tsl2550.c b/drivers/als/tsl2550.c
similarity index 82%
rename from drivers/i2c/chips/tsl2550.c
rename to drivers/als/tsl2550.c
index a0702f3..b911e9c 100644
--- a/drivers/i2c/chips/tsl2550.c
+++ b/drivers/als/tsl2550.c
@@ -3,6 +3,7 @@
*
* Copyright (C) 2007 Rodolfo Giometti <[email protected]>
* Copyright (C) 2007 Eurotech S.p.A. <[email protected]>
+ * Copyright (C) 2009 Jonathan Cameron <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,9 +25,11 @@
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/als_sys.h>
#define TSL2550_DRV_NAME "tsl2550"
-#define DRIVER_VERSION "1.2"
+#define DRIVER_VERSION "2.0"
/*
* Defines
@@ -44,6 +47,7 @@
*/
struct tsl2550_data {
+ struct device *classdev;
struct i2c_client *client;
struct mutex update_lock;
@@ -102,11 +106,14 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
return ret;
if (!(ret & 0x80))
return -EAGAIN;
+ if (ret == 0x7f)
+ return -ERANGE;
return ret & 0x7f; /* remove the "valid" bit */
}
/*
- * LUX calculation
+ * LUX calculation - note the range is dependent on combination
+ * of infrared level and visible light levels.
*/
#define TSL2550_MAX_LUX 1846
@@ -191,7 +198,8 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
static ssize_t tsl2550_show_power_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
return sprintf(buf, "%u\n", data->power_state);
}
@@ -199,12 +207,13 @@ static ssize_t tsl2550_show_power_state(struct device *dev,
static ssize_t tsl2550_store_power_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
+ unsigned long val;
int ret;
- if (val < 0 || val > 1)
+ ret = strict_strtoul(buf, 10, &val);
+ if (ret || val < 0 || val > 1)
return -EINVAL;
mutex_lock(&data->update_lock);
@@ -220,40 +229,45 @@ static ssize_t tsl2550_store_power_state(struct device *dev,
static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO,
tsl2550_show_power_state, tsl2550_store_power_state);
-static ssize_t tsl2550_show_operating_mode(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t tsl2550_show_exposure(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
- struct tsl2550_data *data = i2c_get_clientdata(to_i2c_client(dev));
-
- return sprintf(buf, "%u\n", data->operating_mode);
+ struct i2c_client *client = to_i2c_client(dev->parent);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+ if (data->operating_mode)
+ return sprintf(buf, "160000\n");
+ else
+ return sprintf(buf, "800000\n");
}
-static ssize_t tsl2550_store_operating_mode(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
+static ssize_t tsl2550_store_exposure(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
- unsigned long val = simple_strtoul(buf, NULL, 10);
- int ret;
+ unsigned long val;
- if (val < 0 || val > 1)
- return -EINVAL;
-
- if (data->power_state == 0)
- return -EBUSY;
+ int ret = strict_strtoul(buf, 10, &val);
+ if (ret)
+ return -EINVAL;
mutex_lock(&data->update_lock);
- ret = tsl2550_set_operating_mode(client, val);
+ if (val >= 800000)
+ ret = tsl2550_set_operating_mode(client, 0);
+ else
+ ret = tsl2550_set_operating_mode(client, 1);
mutex_unlock(&data->update_lock);
-
if (ret < 0)
return ret;
return count;
}
-static DEVICE_ATTR(operating_mode, S_IWUSR | S_IRUGO,
- tsl2550_show_operating_mode, tsl2550_store_operating_mode);
+static DEVICE_ATTR(exposure_time0, S_IWUSR | S_IRUGO,
+ tsl2550_show_exposure, tsl2550_store_exposure);
static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
{
@@ -284,7 +298,7 @@ static ssize_t __tsl2550_show_lux(struct i2c_client *client, char *buf)
static ssize_t tsl2550_show_lux1_input(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
+ struct i2c_client *client = to_i2c_client(dev->parent);
struct tsl2550_data *data = i2c_get_clientdata(client);
int ret;
@@ -299,13 +313,13 @@ static ssize_t tsl2550_show_lux1_input(struct device *dev,
return ret;
}
-static DEVICE_ATTR(lux1_input, S_IRUGO,
+static DEVICE_ATTR(illuminance0, S_IRUGO,
tsl2550_show_lux1_input, NULL);
static struct attribute *tsl2550_attributes[] = {
&dev_attr_power_state.attr,
- &dev_attr_operating_mode.attr,
- &dev_attr_lux1_input.attr,
+ &dev_attr_exposure_time0.attr,
+ &dev_attr_illuminance0.attr,
NULL
};
@@ -391,14 +405,22 @@ static int __devinit tsl2550_probe(struct i2c_client *client,
goto exit_kfree;
/* Register sysfs hooks */
- err = sysfs_create_group(&client->dev.kobj, &tsl2550_attr_group);
- if (err)
+ data->classdev = als_device_register(&client->dev);
+ if (IS_ERR(data->classdev)) {
+ err = PTR_ERR(data->classdev);
goto exit_kfree;
+ }
+
+ err = sysfs_create_group(&data->classdev->kobj, &tsl2550_attr_group);
+ if (err)
+ goto exit_unreg;
dev_info(&client->dev, "support ver. %s enabled\n", DRIVER_VERSION);
return 0;
+exit_unreg:
+ als_device_unregister(data->classdev);
exit_kfree:
kfree(data);
exit:
@@ -407,12 +429,15 @@ exit:
static int __devexit tsl2550_remove(struct i2c_client *client)
{
- sysfs_remove_group(&client->dev.kobj, &tsl2550_attr_group);
+ struct tsl2550_data *data = i2c_get_clientdata(client);
+
+ sysfs_remove_group(&data->classdev->kobj, &tsl2550_attr_group);
+ als_device_unregister(data->classdev);
/* Power down the device */
tsl2550_set_power_state(client, 0);
- kfree(i2c_get_clientdata(client));
+ kfree(data);
return 0;
}
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index ae4539d..c11f8d6 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -6,14 +6,4 @@
menu "Miscellaneous I2C Chip support"
-config SENSORS_TSL2550
- tristate "Taos TSL2550 ambient light sensor"
- depends on EXPERIMENTAL
- help
- If you say yes here you get support for the Taos TSL2550
- ambient light sensor.
-
- This driver can also be built as a module. If so, the module
- will be called tsl2550.
-
endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index fe0af0f..ffde18d 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -10,8 +10,6 @@
# * I/O expander drivers go to drivers/gpio
#
-obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
-
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
endif
--
1.6.4.4
Signed-off-by: Jonathan Cameron <[email protected]>
---
As Jean Delvare observed, this check is ineffective so lets clear it out whilst
we are working on this driver.
drivers/als/tsl2550.c | 5 +----
1 files changed, 1 insertions(+), 4 deletions(-)
diff --git a/drivers/als/tsl2550.c b/drivers/als/tsl2550.c
index b911e9c..27745a5 100644
--- a/drivers/als/tsl2550.c
+++ b/drivers/als/tsl2550.c
@@ -116,8 +116,6 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
* of infrared level and visible light levels.
*/
-#define TSL2550_MAX_LUX 1846
-
static const u8 ratio_lut[] = {
100, 100, 100, 100, 100, 100, 100, 100,
100, 100, 100, 100, 100, 100, 99, 99,
@@ -187,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
else
return -EAGAIN;
- /* LUX range check */
- return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
+ return lux;
}
/*
--
1.6.4.4
On Wed, 27 Jan 2010 14:53:19 +0000, Jonathan Cameron wrote:
>
> Signed-off-by: Jonathan Cameron <[email protected]>
Acked-by: Jean Delvare <[email protected]>
> ---
>
> As Jean Delvare observed, this check is ineffective so lets clear it out whilst
> we are working on this driver.
>
> drivers/als/tsl2550.c | 5 +----
> 1 files changed, 1 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/als/tsl2550.c b/drivers/als/tsl2550.c
> index b911e9c..27745a5 100644
> --- a/drivers/als/tsl2550.c
> +++ b/drivers/als/tsl2550.c
> @@ -116,8 +116,6 @@ static int tsl2550_get_adc_value(struct i2c_client *client, u8 cmd)
> * of infrared level and visible light levels.
> */
>
> -#define TSL2550_MAX_LUX 1846
> -
> static const u8 ratio_lut[] = {
> 100, 100, 100, 100, 100, 100, 100, 100,
> 100, 100, 100, 100, 100, 100, 99, 99,
> @@ -187,8 +185,7 @@ static int tsl2550_calculate_lux(u8 ch0, u8 ch1)
> else
> return -EAGAIN;
>
> - /* LUX range check */
> - return lux > TSL2550_MAX_LUX ? TSL2550_MAX_LUX : lux;
> + return lux;
> }
>
> /*
--
Jean Delvare