Add driver for the TAOS 4531x family of I2C ambient light sensors; the chip outputs a 16-bit lux value. RFCs: (1) naming: tsl45315 or tsl4531x or tsl4531? I just happend to have/test the 45315 (2) tsl45315_id is not really used, the variants just have different i2c addresses and i2c voltages; drop? (3) integration_time: driver uses tsl45315_ext_info to set/get the interation time in_illuminance_integration_time (0 .. 400ms, 1 .. 200ms, 2 .. 100ms) which affects in_illuminance_scale (1x, 2x, 4x) in order to produce lux when multiplied with in_illuminance_raw; ok? better way to represent? --- drivers/iio/light/Kconfig | 10 ++ drivers/iio/light/Makefile | 1 + drivers/iio/light/tsl45315.c | 263 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 drivers/iio/light/tsl45315.c diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 5ef1a39..c1dfd57 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -42,6 +42,16 @@ config SENSORS_TSL2563 This driver can also be built as a module. If so, the module will be called tsl2563. +config TSL45315 + tristate "TAOS TSL45311, TSL45313, TSL45315, TSL45317 ambient light sensors" + depends on I2C + help + Say Y here if you want to build a driver for the TAOS TSL4531x family + of ambient light sensors with direct lux output. + + To compile this driver as a module, choose M here: the + module will be called tsl45315. + config VCNL4000 tristate "VCNL4000 combined ALS and proximity sensor" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 040d9c7..333c292 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_ADJD_S311) += adjd_s311.o obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o +obj-$(CONFIG_TSL45315) += tsl45315.o obj-$(CONFIG_VCNL4000) += vcnl4000.o obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o diff --git a/drivers/iio/light/tsl45315.c b/drivers/iio/light/tsl45315.c new file mode 100644 index 0000000..41c173d --- /dev/null +++ b/drivers/iio/light/tsl45315.c @@ -0,0 +1,263 @@ +/* + * tsl45315.c - Support for TAOS TSL45315 ambient light sensor + * + * Copyright 2013 Peter Meerwald <pmeerw@xxxxxxxxxx> + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * IIO driver for the TSL4531x family + * TSL45311/TSL45313: 7-bit I2C slave address 0x39 + * TSL45315/TSL45317: 7-bit I2C slave address 0x29 + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/delay.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define TSL45315_DRV_NAME "tsl45315" + +#define TSL45315_CONTROL 0x80 +#define TSL45315_CONFIG 0x81 +#define TSL45315_DATALOW 0x84 +#define TSL45315_DATAHIGH 0x85 +#define TSL45315_ID 0x8a + +#define TSL45315_MODE_POWERDOWN 0x00 +#define TSL45315_MODE_SINGLE_ADC 0x02 +#define TSL45315_MODE_NORMAL 0x03 + +struct tsl45315_data { + struct i2c_client *client; + int int_time; /* 0 .. 400ms, 1 .. 200ms, 2 .. 100ms */ +}; + +static const struct i2c_device_id tsl45315_id[] = { + { "tsl45311", 0 }, + { "tsl45313", 1 }, + { "tsl45315", 2 }, + { "tsl45317", 3 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tsl45315_id); + +static int tsl45315_measure(struct tsl45315_data *data, int *val) +{ + u16 buf; + int ret; + + ret = i2c_smbus_read_i2c_block_data(data->client, + TSL45315_DATALOW, sizeof(buf), (u8 *) &buf); + if (ret < 0) + return ret; + + *val = le16_to_cpu(buf); + + return 0; +} + +static ssize_t tsl45315_read_int_time(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, char *buf) +{ + struct tsl45315_data *data = iio_priv(indio_dev); + s32 ret; + + ret = i2c_smbus_read_byte_data(data->client, TSL45315_CONFIG); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret & 0x03); +} + +static ssize_t tsl45315_write_int_time(struct iio_dev *indio_dev, + uintptr_t private, const struct iio_chan_spec *chan, const char *buf, + size_t len) +{ + struct tsl45315_data *data = iio_priv(indio_dev); + unsigned long int_time; + int ret; + + ret = kstrtoul(buf, 10, &int_time); + if (ret) + return ret; + + if (int_time > 2) + return -EINVAL; + + ret = i2c_smbus_write_byte_data(data->client, + TSL45315_CONFIG, int_time); + if (ret < 0) + return ret; + + data->int_time = int_time; + + return len; +} + +static const struct iio_chan_spec_ext_info tsl45315_ext_info[] = { + { + .name = "integration_time", + .read = tsl45315_read_int_time, + .write = tsl45315_write_int_time, + }, + { } +}; + +static const struct iio_chan_spec tsl45315_channels[] = { + { + .type = IIO_LIGHT, + .ext_info = tsl45315_ext_info, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) + } +}; + +static int tsl45315_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + int ret = -EINVAL; + struct tsl45315_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_LIGHT: + ret = tsl45315_measure(data, val); + if (ret < 0) + return ret; + ret = IIO_VAL_INT; + break; + default: + break; + } + break; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_LIGHT) { + /* 0.. 1x, 1 .. 2x, 2 .. 4x */ + *val = 1 << data->int_time; + ret = IIO_VAL_INT; + } + break; + default: + break; + } + + return ret; +} + +static const struct iio_info tsl45315_info = { + .read_raw = tsl45315_read_raw, + .driver_module = THIS_MODULE, +}; + +static int tsl45315_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tsl45315_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = iio_device_alloc(sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + ret = i2c_smbus_read_byte_data(data->client, TSL45315_ID); + if (ret < 0) + goto error_free_dev; + + dev_info(&client->dev, "TSL4531x Ambient light sensor, Id %02x\n", + ret >> 4); + + ret = i2c_smbus_write_byte_data(data->client, TSL45315_CONTROL, + TSL45315_MODE_NORMAL); + if (ret < 0) + goto error_free_dev; + + ret = i2c_smbus_write_byte_data(data->client, TSL45315_CONFIG, 0); + if (ret < 0) + goto error_free_dev; + + indio_dev->dev.parent = &client->dev; + indio_dev->info = &tsl45315_info; + indio_dev->channels = tsl45315_channels; + indio_dev->num_channels = ARRAY_SIZE(tsl45315_channels); + indio_dev->name = TSL45315_DRV_NAME; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = iio_device_register(indio_dev); + if (ret < 0) + goto error_free_dev; + + return 0; + +error_free_dev: + iio_device_free(indio_dev); + return ret; +} + +static int tsl45315_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + iio_device_unregister(indio_dev); + iio_device_free(indio_dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tsl45315_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl45315_data *data = iio_priv(indio_dev); + int ret; + + ret = i2c_smbus_write_byte_data(data->client, TSL45315_CONTROL, + TSL45315_MODE_POWERDOWN); + + return ret; +} + +static int tsl45315_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_write_byte_data(client, TSL45315_CONTROL, + TSL45315_MODE_NORMAL); + + return ret; +} + +static SIMPLE_DEV_PM_OPS(tsl45315_pm_ops, tsl45315_suspend, tsl45315_resume); +#define TSL45315_PM_OPS (&tsl45315_pm_ops) +#else +#define TSL45315_PM_OPS NULL +#endif + +static struct i2c_driver tsl45315_driver = { + .driver = { + .name = TSL45315_DRV_NAME, + .pm = TSL45315_PM_OPS, + .owner = THIS_MODULE, + }, + .probe = tsl45315_probe, + .remove = tsl45315_remove, + .id_table = tsl45315_id, +}; + +module_i2c_driver(tsl45315_driver); + +MODULE_AUTHOR("Peter Meerwald <pmeerw@xxxxxxxxxx>"); +MODULE_DESCRIPTION("TAOS TSL45315 ambient light sensor driver"); +MODULE_LICENSE("GPL"); -- 1.8.3 -- To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html