From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757670Ab2DLOem (ORCPT ); Thu, 12 Apr 2012 10:34:42 -0400 Received: from ppsw-41.csi.cam.ac.uk ([131.111.8.141]:34883 "EHLO ppsw-41.csi.cam.ac.uk" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755212Ab2DLOej (ORCPT ); Thu, 12 Apr 2012 10:34:39 -0400 X-Cam-AntiVirus: no malware found X-Cam-SpamDetails: not scanned X-Cam-ScannerInfo: http://www.cam.ac.uk/cs/email/scanner/ Message-ID: <4F86E7F9.7000303@cam.ac.uk> Date: Thu, 12 Apr 2012 15:34:33 +0100 From: Jonathan Cameron User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20120327 Thunderbird/11.0.1 MIME-Version: 1.0 To: Laxman Dewangan CC: gregkh@linuxfoundation.org, grant.likely@secretlab.ca, rob.herring@calxeda.com, jbrenner@taosinc.com, rklein@nvidia.com, max@stro.at, linux-iio@vger.kernel.org, devel@driverdev.osuosl.org, linux-kernel@vger.kernel.org, devicetree-discuss@lists.ozlabs.org Subject: Re: [PATCH V4 2/2] staging: iio: add driver for isl29028 References: <1334239266-29505-1-git-send-email-ldewangan@nvidia.com> <1334239266-29505-3-git-send-email-ldewangan@nvidia.com> In-Reply-To: <1334239266-29505-3-git-send-email-ldewangan@nvidia.com> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On 4/12/2012 3:01 PM, Laxman Dewangan wrote: > Intersil's ISL29028 is concurrent Ambient Light and > Proximity Sensor device. > Add driver to access the light and IR intensity and > proximity value via iio interface. Very nearly there. The available attributes need to match naming of what they are providing values for. (we need a better way of handling these but that's a job for another day). Otherwise, all good. > > Signed-off-by: Laxman Dewangan > --- > Changes from V1: > - Taken care of cleanups comments. > > Changes from V2: > - use the channel info for sampling frequency selection rather than > exposing separate sysfs from driver. > > Changes from V3: > - Remove sysfs range and add scale through channel info. > > drivers/staging/iio/light/Kconfig | 10 + > drivers/staging/iio/light/Makefile | 1 + > drivers/staging/iio/light/isl29028.c | 563 ++++++++++++++++++++++++++++++++++ > 3 files changed, 574 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/light/isl29028.c > > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig > index e7e9159..53b49f7 100644 > --- a/drivers/staging/iio/light/Kconfig > +++ b/drivers/staging/iio/light/Kconfig > @@ -14,6 +14,16 @@ config SENSORS_ISL29018 > in lux, proximity infrared sensing and normal infrared sensing. > Data from sensor is accessible via sysfs. > > +config SENSORS_ISL29028 > + tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor" > + depends on I2C > + select REGMAP_I2C > + help > + Provides driver for the Intersil's ISL29028 device. > + This driver supports the sysfs interface to get the ALS, IR intensity, > + Proximity value via iio. The ISL29028 provides the concurrent sensing > + of ambient light and proximity. > + > config SENSORS_TSL2563 > tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors" > depends on I2C > diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile > index 3011fbf..535d313 100644 > --- a/drivers/staging/iio/light/Makefile > +++ b/drivers/staging/iio/light/Makefile > @@ -4,4 +4,5 @@ > > obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o > +obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o > obj-$(CONFIG_TSL2583) += tsl2583.o > diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c > new file mode 100644 > index 0000000..faa64f7 > --- /dev/null > +++ b/drivers/staging/iio/light/isl29028.c > @@ -0,0 +1,563 @@ > +/* > + * IIO driver for the light sensor ISL29028. > + * ISL29028 is Concurrent Ambient Light and Proximity Sensor > + * > + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope 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, see. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "../iio.h" > +#include "../sysfs.h" > + > +#define CONVERSION_TIME_MS 100 > + > +#define ISL29028_REG_CONFIGURE 0x01 > + > +#define CONFIGURE_ALS_IR_MODE_ALS 0 > +#define CONFIGURE_ALS_IR_MODE_IR BIT(0) > +#define CONFIGURE_ALS_IR_MODE_MASK BIT(0) > + > +#define CONFIGURE_ALS_RANGE_LOW_LUX 0 > +#define CONFIGURE_ALS_RANGE_HIGH_LUX BIT(1) > +#define CONFIGURE_ALS_RANGE_MASK BIT(1) > + > +#define CONFIGURE_ALS_DIS 0 > +#define CONFIGURE_ALS_EN BIT(2) > +#define CONFIGURE_ALS_EN_MASK BIT(2) > + > +#define CONFIGURE_PROX_DRIVE BIT(3) > + > +#define CONFIGURE_PROX_SLP_SH 4 > +#define CONFIGURE_PROX_SLP_MASK (7<< CONFIGURE_PROX_SLP_SH) > + > +#define CONFIGURE_PROX_EN BIT(7) > +#define CONFIGURE_PROX_EN_MASK BIT(7) > + > +#define ISL29028_REG_INTERRUPT 0x02 > + > +#define ISL29028_REG_PROX_DATA 0x08 > +#define ISL29028_REG_ALSIR_L 0x09 > +#define ISL29028_REG_ALSIR_U 0x0A > + > +#define ISL29028_REG_TEST1_MODE 0x0E > +#define ISL29028_REG_TEST2_MODE 0x0F > + > +#define ISL29028_MAX_REGS (ISL29028_REG_TEST2_MODE + 1) > + > +enum als_ir_mode { > + MODE_NONE = 0, > + MODE_ALS, > + MODE_IR > +}; > + > +struct isl29028_chip { > + struct device *dev; > + struct mutex lock; > + struct regmap *regmap; > + > + unsigned int prox_sampling; > + bool enable_prox; > + > + int lux_scale; > + int als_ir_mode; > +}; > + > +static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, > + unsigned int sampling) > +{ > + static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0}; > + int sel; > + unsigned int period = DIV_ROUND_UP(1000, sampling); > + > + for (sel = 0; sel< ARRAY_SIZE(prox_period); ++sel) { > + if (period>= prox_period[sel]) > + break; > + } > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_PROX_SLP_MASK, sel<< CONFIGURE_PROX_SLP_SH); > +} > + > +static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) > +{ > + int ret; > + int val = 0; > + > + if (enable) > + val = CONFIGURE_PROX_EN; > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_PROX_EN_MASK, val); > + if (ret< 0) > + return ret; > + > + /* Wait for conversion to be complete for first sample */ > + mdelay(DIV_ROUND_UP(1000, chip->prox_sampling)); > + return 0; > +} > + > +static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) > +{ > + int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX : > + CONFIGURE_ALS_RANGE_LOW_LUX; > + > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_RANGE_MASK, val); > +} > + > +static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, > + enum als_ir_mode mode) > +{ > + int ret = 0; > + > + switch (mode) { > + case MODE_ALS: > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_ALS); > + if (ret< 0) > + return ret; > + > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_RANGE_MASK, CONFIGURE_ALS_RANGE_HIGH_LUX); > + break; > + > + case MODE_IR: > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_IR); > + break; > + > + case MODE_NONE: > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS); > + } > + > + if (ret< 0) > + return ret; > + > + /* Enable the ALS/IR */ > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN); > + if (ret< 0) > + return ret; > + > + /* Need to wait for conversion time if ALS/IR mode enabled */ > + mdelay(CONVERSION_TIME_MS); > + return 0; > +} > + > +static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) > +{ > + unsigned int lsb; > + unsigned int msb; > + int ret; > + > + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L,&lsb); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in reading register ALSIR_L err %d\n", ret); > + return ret; > + } > + > + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U,&msb); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in reading register ALSIR_U err %d\n", ret); > + return ret; > + } > + > + *als_ir = ((msb& 0xF)<< 8) | (lsb& 0xFF); > + return 0; > +} > + > +static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) > +{ > + unsigned int data; > + int ret; > + > + ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA,&data); > + if (ret< 0) { > + dev_err(chip->dev, "Error in reading register %d, error %d\n", > + ISL29028_REG_PROX_DATA, ret); > + return ret; > + } > + *prox = data; > + return 0; > +} > + > +static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data) > +{ > + int ret; > + > + if (!chip->enable_prox) { > + ret = isl29028_enable_proximity(chip, true); > + if (ret< 0) > + return ret; > + chip->enable_prox = true; > + } > + return isl29028_read_proxim(chip, prox_data); > +} > + > +static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) > +{ > + int ret; > + int als_ir_data; > + > + if (chip->als_ir_mode != MODE_ALS) { > + ret = isl29028_set_als_ir_mode(chip, MODE_ALS); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in enabling ALS mode err %d\n", ret); > + return ret; > + } > + chip->als_ir_mode = MODE_ALS; > + } > + > + ret = isl29028_read_als_ir(chip,&als_ir_data); > + if (ret< 0) > + return ret; > + > + /* > + * convert als data count to lux. > + * if lux_scale = 125, lux = count * 0.031 > + * if lux_scale = 2000, lux = count * 0.49 > + */ > + if (chip->lux_scale == 125) > + als_ir_data = (als_ir_data * 31) / 1000; > + else > + als_ir_data = (als_ir_data * 49) / 100; > + > + *als_data = als_ir_data; > + return 0; > +} > + > +static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) > +{ > + int ret; > + > + if (chip->als_ir_mode != MODE_IR) { > + ret = isl29028_set_als_ir_mode(chip, MODE_IR); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in enabling IR mode err %d\n", ret); > + return ret; > + } > + chip->als_ir_mode = MODE_IR; > + } > + return isl29028_read_als_ir(chip, ir_data); > +} > + > +/* Channel IO */ > +static int isl29028_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int val, int val2, long mask) > +{ > + struct isl29028_chip *chip = iio_priv(indio_dev); > + int ret = -EINVAL; > + > + mutex_lock(&chip->lock); > + switch (chan->type) { > + case IIO_PROXIMITY: > + if (mask != IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT) { > + dev_err(chip->dev, > + "proximity: mask value 0x%08lx not supported\n", > + mask); > + break; > + } > + if (val< 1 || val> 100) { > + dev_err(chip->dev, > + "Samp_freq %d is not in range[1:100]\n", val); > + break; > + } > + ret = isl29028_set_proxim_sampling(chip, val); > + if (ret< 0) { > + dev_err(chip->dev, > + "Setting proximity samp_freq fail, err %d\n", > + ret); > + break; > + } > + chip->prox_sampling = val; > + break; > + > + case IIO_LIGHT: > + if (mask != IIO_CHAN_INFO_SCALE_SEPARATE_BIT) { > + dev_err(chip->dev, > + "light: mask value 0x%08lx not supported\n", > + mask); > + break; > + } > + if ((val != 125)&& (val != 2000)) { > + dev_err(chip->dev, > + "lux scale %d is invalid [125, 2000]\n", val); > + break; > + } > + ret = isl29028_set_als_scale(chip, val); > + if (ret< 0) { > + dev_err(chip->dev, > + "Setting lux scale fail with error %d\n", ret); > + break; > + } > + chip->lux_scale = val; > + break; > + > + default: > + dev_err(chip->dev, "Unsupported channel type\n"); > + break; > + } > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static int isl29028_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int *val, int *val2, long mask) > +{ > + struct isl29028_chip *chip = iio_priv(indio_dev); > + int ret = -EINVAL; > + > + mutex_lock(&chip->lock); > + switch (mask) { > + case 0: > + switch (chan->type) { > + case IIO_LIGHT: > + ret = isl29028_als_get(chip, val); > + break; > + case IIO_INTENSITY: > + ret = isl29028_ir_get(chip, val); > + break; > + case IIO_PROXIMITY: > + ret = isl29028_proxim_get(chip, val); > + break; > + default: > + break; > + } > + if (ret< 0) > + break; > + ret = IIO_VAL_INT; > + break; > + > + case IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT: > + if (chan->type != IIO_PROXIMITY) > + break; > + *val = chip->prox_sampling; > + ret = IIO_VAL_INT; > + break; > + > + case IIO_CHAN_INFO_SCALE_SEPARATE_BIT: > + if (chan->type != IIO_LIGHT) > + break; > + *val = chip->lux_scale; > + ret = IIO_VAL_INT; > + break; > + > + default: > + dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask); > + break; > + } > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static IIO_CONST_ATTR(proximity_sampling_frequency_available, > + "1, 3, 5, 10, 13, 20, 83, 100"); > +static IIO_CONST_ATTR(illuminance_scale_available, "125, 2000"); > + > +#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) > +#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) > +static struct attribute *isl29028_attributes[] = { > + ISL29028_CONST_ATTR(proximity_sampling_frequency_available), tiny disconnect now that you have in_proximity0_sampling frequency, this should be in_proximity0_sampling_frequency_available > + ISL29028_CONST_ATTR(illuminance_scale_available), snap here (sorry, missed that last time). in_illuminance0_scale_available > + NULL, > +}; > + > +static const struct attribute_group isl29108_group = { > + .attrs = isl29028_attributes, > +}; > + > +static const struct iio_chan_spec isl29028_channels[] = { > + { > + .type = IIO_LIGHT, > + .processed_val = 1, > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + }, { > + .type = IIO_INTENSITY, > + }, { > + .type = IIO_PROXIMITY, > + .info_mask = IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT, > + } > +}; > + > +static const struct iio_info isl29028_info = { > + .attrs =&isl29108_group, > + .driver_module = THIS_MODULE, > + .read_raw =&isl29028_read_raw, > + .write_raw =&isl29028_write_raw, > +}; > + > +static int isl29028_chip_init(struct isl29028_chip *chip) > +{ > + int ret; > + > + chip->enable_prox = false; > + chip->prox_sampling = 20; > + chip->lux_scale = 2000; > + chip->als_ir_mode = MODE_NONE; > + > + ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_TEST1_MODE, ret); > + return ret; > + } > + ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_TEST2_MODE, ret); > + return ret; > + } > + > + ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_CONFIGURE, ret); > + return ret; > + } > + > + ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): setting the proximity, err = %d\n", > + __func__, ret); > + return ret; > + } > + > + ret = isl29028_set_als_scale(chip, chip->lux_scale); > + if (ret< 0) > + dev_err(chip->dev, "%s(): setting als scale failed, err = %d\n", > + __func__, ret); > + return ret; > +} > + > +static bool is_volatile_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case ISL29028_REG_INTERRUPT: > + case ISL29028_REG_PROX_DATA: > + case ISL29028_REG_ALSIR_L: > + case ISL29028_REG_ALSIR_U: > + return true; > + default: > + return false; > + } > +} > + > +static const struct regmap_config isl29028_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + .volatile_reg = is_volatile_reg, > + .max_register = ISL29028_MAX_REGS - 1, > + .num_reg_defaults_raw = ISL29028_MAX_REGS, > + .cache_type = REGCACHE_RBTREE, > +}; > + > +static int __devinit isl29028_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct isl29028_chip *chip; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = iio_allocate_device(sizeof(*chip)); > + if (!indio_dev) { > + dev_err(&client->dev, "iio allocation fails\n"); > + return -ENOMEM; > + } > + > + chip = iio_priv(indio_dev); > + > + i2c_set_clientdata(client, indio_dev); > + chip->dev =&client->dev; > + mutex_init(&chip->lock); > + > + chip->regmap = devm_regmap_init_i2c(client,&isl29028_regmap_config); > + if (IS_ERR(chip->regmap)) { > + ret = PTR_ERR(chip->regmap); > + dev_err(chip->dev, "regmap initialization failed: %d\n", ret); > + goto exit_iio_free; > + } > + > + ret = isl29028_chip_init(chip); > + if (ret< 0) { > + dev_err(chip->dev, "chip initialization failed: %d\n", ret); > + goto exit_iio_free; > + } > + > + indio_dev->info =&isl29028_info; > + indio_dev->channels = isl29028_channels; > + indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); > + indio_dev->name = id->name; > + indio_dev->dev.parent =&client->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + ret = iio_device_register(indio_dev); > + if (ret< 0) { > + dev_err(chip->dev, "iio registration fails with error %d\n", > + ret); > + goto exit_iio_free; > + } > + return 0; > + > +exit_iio_free: > + iio_free_device(indio_dev); > + return ret; > +} > + > +static int __devexit isl29028_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + iio_free_device(indio_dev); > + return 0; > +} > + > +static const struct i2c_device_id isl29028_id[] = { > + {"isl29028", 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, isl29028_id); > + > +static const struct of_device_id isl29028_of_match[] = { > + { .compatible = "isl,isl29028", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, isl29028_of_match); > + > +static struct i2c_driver isl29028_driver = { > + .class = I2C_CLASS_HWMON, > + .driver = { > + .name = "isl29028", > + .owner = THIS_MODULE, > + .of_match_table = isl29028_of_match, > + }, > + .probe = isl29028_probe, > + .remove = __devexit_p(isl29028_remove), > + .id_table = isl29028_id, > +}; > + > +module_i2c_driver(isl29028_driver); > + > +MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Laxman Dewangan"); From mboxrd@z Thu Jan 1 00:00:00 1970 From: Jonathan Cameron Subject: Re: [PATCH V4 2/2] staging: iio: add driver for isl29028 Date: Thu, 12 Apr 2012 15:34:33 +0100 Message-ID: <4F86E7F9.7000303@cam.ac.uk> References: <1334239266-29505-1-git-send-email-ldewangan@nvidia.com> <1334239266-29505-3-git-send-email-ldewangan@nvidia.com> Mime-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit Return-path: In-Reply-To: <1334239266-29505-3-git-send-email-ldewangan-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> Sender: linux-iio-owner-u79uwXL29TY76Z2rM5mHXA@public.gmane.org To: Laxman Dewangan Cc: gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org, grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org, rob.herring-bsGFqQB8/DxBDgjK7y7TUQ@public.gmane.org, jbrenner-yYKgigLBUwlBDgjK7y7TUQ@public.gmane.org, rklein-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org, max-U9r9yeDMy7A@public.gmane.org, linux-iio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b@public.gmane.org, linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, devicetree-discuss-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org List-Id: devicetree@vger.kernel.org On 4/12/2012 3:01 PM, Laxman Dewangan wrote: > Intersil's ISL29028 is concurrent Ambient Light and > Proximity Sensor device. > Add driver to access the light and IR intensity and > proximity value via iio interface. Very nearly there. The available attributes need to match naming of what they are providing values for. (we need a better way of handling these but that's a job for another day). Otherwise, all good. > > Signed-off-by: Laxman Dewangan > --- > Changes from V1: > - Taken care of cleanups comments. > > Changes from V2: > - use the channel info for sampling frequency selection rather than > exposing separate sysfs from driver. > > Changes from V3: > - Remove sysfs range and add scale through channel info. > > drivers/staging/iio/light/Kconfig | 10 + > drivers/staging/iio/light/Makefile | 1 + > drivers/staging/iio/light/isl29028.c | 563 ++++++++++++++++++++++++++++++++++ > 3 files changed, 574 insertions(+), 0 deletions(-) > create mode 100644 drivers/staging/iio/light/isl29028.c > > diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig > index e7e9159..53b49f7 100644 > --- a/drivers/staging/iio/light/Kconfig > +++ b/drivers/staging/iio/light/Kconfig > @@ -14,6 +14,16 @@ config SENSORS_ISL29018 > in lux, proximity infrared sensing and normal infrared sensing. > Data from sensor is accessible via sysfs. > > +config SENSORS_ISL29028 > + tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor" > + depends on I2C > + select REGMAP_I2C > + help > + Provides driver for the Intersil's ISL29028 device. > + This driver supports the sysfs interface to get the ALS, IR intensity, > + Proximity value via iio. The ISL29028 provides the concurrent sensing > + of ambient light and proximity. > + > config SENSORS_TSL2563 > tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors" > depends on I2C > diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile > index 3011fbf..535d313 100644 > --- a/drivers/staging/iio/light/Makefile > +++ b/drivers/staging/iio/light/Makefile > @@ -4,4 +4,5 @@ > > obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o > obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o > +obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o > obj-$(CONFIG_TSL2583) += tsl2583.o > diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c > new file mode 100644 > index 0000000..faa64f7 > --- /dev/null > +++ b/drivers/staging/iio/light/isl29028.c > @@ -0,0 +1,563 @@ > +/* > + * IIO driver for the light sensor ISL29028. > + * ISL29028 is Concurrent Ambient Light and Proximity Sensor > + * > + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms and conditions of the GNU General Public License, > + * version 2, as published by the Free Software Foundation. > + * > + * This program is distributed in the hope 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, see. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "../iio.h" > +#include "../sysfs.h" > + > +#define CONVERSION_TIME_MS 100 > + > +#define ISL29028_REG_CONFIGURE 0x01 > + > +#define CONFIGURE_ALS_IR_MODE_ALS 0 > +#define CONFIGURE_ALS_IR_MODE_IR BIT(0) > +#define CONFIGURE_ALS_IR_MODE_MASK BIT(0) > + > +#define CONFIGURE_ALS_RANGE_LOW_LUX 0 > +#define CONFIGURE_ALS_RANGE_HIGH_LUX BIT(1) > +#define CONFIGURE_ALS_RANGE_MASK BIT(1) > + > +#define CONFIGURE_ALS_DIS 0 > +#define CONFIGURE_ALS_EN BIT(2) > +#define CONFIGURE_ALS_EN_MASK BIT(2) > + > +#define CONFIGURE_PROX_DRIVE BIT(3) > + > +#define CONFIGURE_PROX_SLP_SH 4 > +#define CONFIGURE_PROX_SLP_MASK (7<< CONFIGURE_PROX_SLP_SH) > + > +#define CONFIGURE_PROX_EN BIT(7) > +#define CONFIGURE_PROX_EN_MASK BIT(7) > + > +#define ISL29028_REG_INTERRUPT 0x02 > + > +#define ISL29028_REG_PROX_DATA 0x08 > +#define ISL29028_REG_ALSIR_L 0x09 > +#define ISL29028_REG_ALSIR_U 0x0A > + > +#define ISL29028_REG_TEST1_MODE 0x0E > +#define ISL29028_REG_TEST2_MODE 0x0F > + > +#define ISL29028_MAX_REGS (ISL29028_REG_TEST2_MODE + 1) > + > +enum als_ir_mode { > + MODE_NONE = 0, > + MODE_ALS, > + MODE_IR > +}; > + > +struct isl29028_chip { > + struct device *dev; > + struct mutex lock; > + struct regmap *regmap; > + > + unsigned int prox_sampling; > + bool enable_prox; > + > + int lux_scale; > + int als_ir_mode; > +}; > + > +static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, > + unsigned int sampling) > +{ > + static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0}; > + int sel; > + unsigned int period = DIV_ROUND_UP(1000, sampling); > + > + for (sel = 0; sel< ARRAY_SIZE(prox_period); ++sel) { > + if (period>= prox_period[sel]) > + break; > + } > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_PROX_SLP_MASK, sel<< CONFIGURE_PROX_SLP_SH); > +} > + > +static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) > +{ > + int ret; > + int val = 0; > + > + if (enable) > + val = CONFIGURE_PROX_EN; > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_PROX_EN_MASK, val); > + if (ret< 0) > + return ret; > + > + /* Wait for conversion to be complete for first sample */ > + mdelay(DIV_ROUND_UP(1000, chip->prox_sampling)); > + return 0; > +} > + > +static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) > +{ > + int val = (lux_scale == 2000) ? CONFIGURE_ALS_RANGE_HIGH_LUX : > + CONFIGURE_ALS_RANGE_LOW_LUX; > + > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_RANGE_MASK, val); > +} > + > +static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, > + enum als_ir_mode mode) > +{ > + int ret = 0; > + > + switch (mode) { > + case MODE_ALS: > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_ALS); > + if (ret< 0) > + return ret; > + > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_RANGE_MASK, CONFIGURE_ALS_RANGE_HIGH_LUX); > + break; > + > + case MODE_IR: > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_IR_MODE_MASK, CONFIGURE_ALS_IR_MODE_IR); > + break; > + > + case MODE_NONE: > + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_DIS); > + } > + > + if (ret< 0) > + return ret; > + > + /* Enable the ALS/IR */ > + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, > + CONFIGURE_ALS_EN_MASK, CONFIGURE_ALS_EN); > + if (ret< 0) > + return ret; > + > + /* Need to wait for conversion time if ALS/IR mode enabled */ > + mdelay(CONVERSION_TIME_MS); > + return 0; > +} > + > +static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) > +{ > + unsigned int lsb; > + unsigned int msb; > + int ret; > + > + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L,&lsb); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in reading register ALSIR_L err %d\n", ret); > + return ret; > + } > + > + ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U,&msb); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in reading register ALSIR_U err %d\n", ret); > + return ret; > + } > + > + *als_ir = ((msb& 0xF)<< 8) | (lsb& 0xFF); > + return 0; > +} > + > +static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) > +{ > + unsigned int data; > + int ret; > + > + ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA,&data); > + if (ret< 0) { > + dev_err(chip->dev, "Error in reading register %d, error %d\n", > + ISL29028_REG_PROX_DATA, ret); > + return ret; > + } > + *prox = data; > + return 0; > +} > + > +static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data) > +{ > + int ret; > + > + if (!chip->enable_prox) { > + ret = isl29028_enable_proximity(chip, true); > + if (ret< 0) > + return ret; > + chip->enable_prox = true; > + } > + return isl29028_read_proxim(chip, prox_data); > +} > + > +static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) > +{ > + int ret; > + int als_ir_data; > + > + if (chip->als_ir_mode != MODE_ALS) { > + ret = isl29028_set_als_ir_mode(chip, MODE_ALS); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in enabling ALS mode err %d\n", ret); > + return ret; > + } > + chip->als_ir_mode = MODE_ALS; > + } > + > + ret = isl29028_read_als_ir(chip,&als_ir_data); > + if (ret< 0) > + return ret; > + > + /* > + * convert als data count to lux. > + * if lux_scale = 125, lux = count * 0.031 > + * if lux_scale = 2000, lux = count * 0.49 > + */ > + if (chip->lux_scale == 125) > + als_ir_data = (als_ir_data * 31) / 1000; > + else > + als_ir_data = (als_ir_data * 49) / 100; > + > + *als_data = als_ir_data; > + return 0; > +} > + > +static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) > +{ > + int ret; > + > + if (chip->als_ir_mode != MODE_IR) { > + ret = isl29028_set_als_ir_mode(chip, MODE_IR); > + if (ret< 0) { > + dev_err(chip->dev, > + "Error in enabling IR mode err %d\n", ret); > + return ret; > + } > + chip->als_ir_mode = MODE_IR; > + } > + return isl29028_read_als_ir(chip, ir_data); > +} > + > +/* Channel IO */ > +static int isl29028_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int val, int val2, long mask) > +{ > + struct isl29028_chip *chip = iio_priv(indio_dev); > + int ret = -EINVAL; > + > + mutex_lock(&chip->lock); > + switch (chan->type) { > + case IIO_PROXIMITY: > + if (mask != IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT) { > + dev_err(chip->dev, > + "proximity: mask value 0x%08lx not supported\n", > + mask); > + break; > + } > + if (val< 1 || val> 100) { > + dev_err(chip->dev, > + "Samp_freq %d is not in range[1:100]\n", val); > + break; > + } > + ret = isl29028_set_proxim_sampling(chip, val); > + if (ret< 0) { > + dev_err(chip->dev, > + "Setting proximity samp_freq fail, err %d\n", > + ret); > + break; > + } > + chip->prox_sampling = val; > + break; > + > + case IIO_LIGHT: > + if (mask != IIO_CHAN_INFO_SCALE_SEPARATE_BIT) { > + dev_err(chip->dev, > + "light: mask value 0x%08lx not supported\n", > + mask); > + break; > + } > + if ((val != 125)&& (val != 2000)) { > + dev_err(chip->dev, > + "lux scale %d is invalid [125, 2000]\n", val); > + break; > + } > + ret = isl29028_set_als_scale(chip, val); > + if (ret< 0) { > + dev_err(chip->dev, > + "Setting lux scale fail with error %d\n", ret); > + break; > + } > + chip->lux_scale = val; > + break; > + > + default: > + dev_err(chip->dev, "Unsupported channel type\n"); > + break; > + } > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static int isl29028_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int *val, int *val2, long mask) > +{ > + struct isl29028_chip *chip = iio_priv(indio_dev); > + int ret = -EINVAL; > + > + mutex_lock(&chip->lock); > + switch (mask) { > + case 0: > + switch (chan->type) { > + case IIO_LIGHT: > + ret = isl29028_als_get(chip, val); > + break; > + case IIO_INTENSITY: > + ret = isl29028_ir_get(chip, val); > + break; > + case IIO_PROXIMITY: > + ret = isl29028_proxim_get(chip, val); > + break; > + default: > + break; > + } > + if (ret< 0) > + break; > + ret = IIO_VAL_INT; > + break; > + > + case IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT: > + if (chan->type != IIO_PROXIMITY) > + break; > + *val = chip->prox_sampling; > + ret = IIO_VAL_INT; > + break; > + > + case IIO_CHAN_INFO_SCALE_SEPARATE_BIT: > + if (chan->type != IIO_LIGHT) > + break; > + *val = chip->lux_scale; > + ret = IIO_VAL_INT; > + break; > + > + default: > + dev_err(chip->dev, "mask value 0x%08lx not supported\n", mask); > + break; > + } > + mutex_unlock(&chip->lock); > + return ret; > +} > + > +static IIO_CONST_ATTR(proximity_sampling_frequency_available, > + "1, 3, 5, 10, 13, 20, 83, 100"); > +static IIO_CONST_ATTR(illuminance_scale_available, "125, 2000"); > + > +#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) > +#define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) > +static struct attribute *isl29028_attributes[] = { > + ISL29028_CONST_ATTR(proximity_sampling_frequency_available), tiny disconnect now that you have in_proximity0_sampling frequency, this should be in_proximity0_sampling_frequency_available > + ISL29028_CONST_ATTR(illuminance_scale_available), snap here (sorry, missed that last time). in_illuminance0_scale_available > + NULL, > +}; > + > +static const struct attribute_group isl29108_group = { > + .attrs = isl29028_attributes, > +}; > + > +static const struct iio_chan_spec isl29028_channels[] = { > + { > + .type = IIO_LIGHT, > + .processed_val = 1, > + .info_mask = IIO_CHAN_INFO_SCALE_SEPARATE_BIT, > + }, { > + .type = IIO_INTENSITY, > + }, { > + .type = IIO_PROXIMITY, > + .info_mask = IIO_CHAN_INFO_SAMP_FREQ_SEPARATE_BIT, > + } > +}; > + > +static const struct iio_info isl29028_info = { > + .attrs =&isl29108_group, > + .driver_module = THIS_MODULE, > + .read_raw =&isl29028_read_raw, > + .write_raw =&isl29028_write_raw, > +}; > + > +static int isl29028_chip_init(struct isl29028_chip *chip) > +{ > + int ret; > + > + chip->enable_prox = false; > + chip->prox_sampling = 20; > + chip->lux_scale = 2000; > + chip->als_ir_mode = MODE_NONE; > + > + ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_TEST1_MODE, ret); > + return ret; > + } > + ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_TEST2_MODE, ret); > + return ret; > + } > + > + ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): write to reg %d failed, err = %d\n", > + __func__, ISL29028_REG_CONFIGURE, ret); > + return ret; > + } > + > + ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); > + if (ret< 0) { > + dev_err(chip->dev, "%s(): setting the proximity, err = %d\n", > + __func__, ret); > + return ret; > + } > + > + ret = isl29028_set_als_scale(chip, chip->lux_scale); > + if (ret< 0) > + dev_err(chip->dev, "%s(): setting als scale failed, err = %d\n", > + __func__, ret); > + return ret; > +} > + > +static bool is_volatile_reg(struct device *dev, unsigned int reg) > +{ > + switch (reg) { > + case ISL29028_REG_INTERRUPT: > + case ISL29028_REG_PROX_DATA: > + case ISL29028_REG_ALSIR_L: > + case ISL29028_REG_ALSIR_U: > + return true; > + default: > + return false; > + } > +} > + > +static const struct regmap_config isl29028_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > + .volatile_reg = is_volatile_reg, > + .max_register = ISL29028_MAX_REGS - 1, > + .num_reg_defaults_raw = ISL29028_MAX_REGS, > + .cache_type = REGCACHE_RBTREE, > +}; > + > +static int __devinit isl29028_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct isl29028_chip *chip; > + struct iio_dev *indio_dev; > + int ret; > + > + indio_dev = iio_allocate_device(sizeof(*chip)); > + if (!indio_dev) { > + dev_err(&client->dev, "iio allocation fails\n"); > + return -ENOMEM; > + } > + > + chip = iio_priv(indio_dev); > + > + i2c_set_clientdata(client, indio_dev); > + chip->dev =&client->dev; > + mutex_init(&chip->lock); > + > + chip->regmap = devm_regmap_init_i2c(client,&isl29028_regmap_config); > + if (IS_ERR(chip->regmap)) { > + ret = PTR_ERR(chip->regmap); > + dev_err(chip->dev, "regmap initialization failed: %d\n", ret); > + goto exit_iio_free; > + } > + > + ret = isl29028_chip_init(chip); > + if (ret< 0) { > + dev_err(chip->dev, "chip initialization failed: %d\n", ret); > + goto exit_iio_free; > + } > + > + indio_dev->info =&isl29028_info; > + indio_dev->channels = isl29028_channels; > + indio_dev->num_channels = ARRAY_SIZE(isl29028_channels); > + indio_dev->name = id->name; > + indio_dev->dev.parent =&client->dev; > + indio_dev->modes = INDIO_DIRECT_MODE; > + ret = iio_device_register(indio_dev); > + if (ret< 0) { > + dev_err(chip->dev, "iio registration fails with error %d\n", > + ret); > + goto exit_iio_free; > + } > + return 0; > + > +exit_iio_free: > + iio_free_device(indio_dev); > + return ret; > +} > + > +static int __devexit isl29028_remove(struct i2c_client *client) > +{ > + struct iio_dev *indio_dev = i2c_get_clientdata(client); > + > + iio_device_unregister(indio_dev); > + iio_free_device(indio_dev); > + return 0; > +} > + > +static const struct i2c_device_id isl29028_id[] = { > + {"isl29028", 0}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, isl29028_id); > + > +static const struct of_device_id isl29028_of_match[] = { > + { .compatible = "isl,isl29028", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, isl29028_of_match); > + > +static struct i2c_driver isl29028_driver = { > + .class = I2C_CLASS_HWMON, > + .driver = { > + .name = "isl29028", > + .owner = THIS_MODULE, > + .of_match_table = isl29028_of_match, > + }, > + .probe = isl29028_probe, > + .remove = __devexit_p(isl29028_remove), > + .id_table = isl29028_id, > +}; > + > +module_i2c_driver(isl29028_driver); > + > +MODULE_DESCRIPTION("ISL29028 Ambient Light and Proximity Sensor driver"); > +MODULE_LICENSE("GPL v2"); > +MODULE_AUTHOR("Laxman Dewangan");