CHROMIUM: iio: isl29018: make read delay depend on resolution
[cascardo/linux.git] / drivers / staging / iio / light / isl29018.c
index 38ec52b..826c510 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/slab.h>
 #include "../iio.h"
 #include "../sysfs.h"
-#define CONVERSION_TIME_MS             100
 
 #define ISL29018_REG_ADD_COMMAND1      0x00
 #define COMMMAND1_OPMODE_SHIFT         5
 #define ISL29018_TEST_SHIFT            0
 #define ISL29018_TEST_MASK             (0xFF << ISL29018_TEST_SHIFT)
 
+#define ISL29018_REG_TEST              0x08
+#define ISL29018_TEST_SHIFT            0
+#define ISL29018_TEST_MASK             (0xFF << ISL29018_TEST_SHIFT)
+
 struct isl29018_chip {
        struct i2c_client       *client;
        struct mutex            lock;
@@ -134,11 +137,26 @@ static int isl29018_set_resolution(struct i2c_client *client,
                        COMMANDII_RESOLUTION_SHIFT);
 }
 
+static void integration_wait(struct isl29018_chip *chip)
+{
+       int wait_time;
+
+       /* Integration and conversion time is described in the data sheet
+        * as 2^n x (constant), and for 16 bits it's 90ms. So calculate
+        * from there.
+        *
+        * Since the time taken depends on a resistor value, leave some
+        * margin above the 90ms timeout, otherwise we risk reading a
+        * stale value.
+        */
+       wait_time = DIV_ROUND_UP(100, 1 << (16 - chip->adc_bit));
+       msleep(wait_time);
+}
+
 static int isl29018_read_sensor_input(struct i2c_client *client, int mode)
 {
        int status;
-       int lsb;
-       int msb;
+       int lux;
 
        /* Set mode */
        status = isl29018_write_data(client, ISL29018_REG_ADD_COMMAND1,
@@ -147,21 +165,17 @@ static int isl29018_read_sensor_input(struct i2c_client *client, int mode)
                dev_err(&client->dev, "Error in setting operating mode\n");
                return status;
        }
-       msleep(CONVERSION_TIME_MS);
-       lsb = i2c_smbus_read_byte_data(client, ISL29018_REG_ADD_DATA_LSB);
-       if (lsb < 0) {
-               dev_err(&client->dev, "Error in reading LSB DATA\n");
-               return lsb;
+       integration_wait(iio_priv(i2c_get_clientdata(client)));
+       status = i2c_smbus_read_word_data(client, ISL29018_REG_ADD_DATA_LSB);
+       if (status < 0) {
+               dev_err(&client->dev, "Error in reading Lux DATA\n");
+               return status;
        }
 
-       msb = i2c_smbus_read_byte_data(client, ISL29018_REG_ADD_DATA_MSB);
-       if (msb < 0) {
-               dev_err(&client->dev, "Error in reading MSB DATA\n");
-               return msb;
-       }
-       dev_vdbg(&client->dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);
+       lux = le16_to_cpu(status);
+       dev_vdbg(&client->dev, "lux = %u\n", lux);
 
-       return (msb << 8) | lsb;
+       return lux;
 }
 
 static int isl29018_read_lux(struct i2c_client *client, int *lux)
@@ -234,6 +248,96 @@ static int isl29018_read_proximity_ir(struct i2c_client *client, int scheme,
 }
 
 /* Sysfs interface */
+/* lux_scale */
+static ssize_t show_lux_scale(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct isl29018_chip *chip = iio_priv(indio_dev);
+
+       return sprintf(buf, "%d\n", chip->lux_scale);
+}
+
+static ssize_t store_lux_scale(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct isl29018_chip *chip = iio_priv(indio_dev);
+       unsigned long lval;
+
+       lval = simple_strtoul(buf, NULL, 10);
+       if (lval == 0)
+               return -EINVAL;
+
+       mutex_lock(&chip->lock);
+       chip->lux_scale = lval;
+       mutex_unlock(&chip->lock);
+
+       return count;
+}
+
+static ssize_t get_sensor_data(struct device *dev, char *buf, int mode)
+{
+       struct iio_dev *indio_dev = dev_get_drvdata(dev);
+       struct isl29018_chip *chip = iio_priv(indio_dev);
+       struct i2c_client *client = chip->client;
+       int value = 0;
+       int status;
+
+       mutex_lock(&chip->lock);
+       switch (mode) {
+       case COMMMAND1_OPMODE_PROX_ONCE:
+               status = isl29018_read_proximity_ir(client,
+                                               chip->prox_scheme, &value);
+       break;
+
+       case COMMMAND1_OPMODE_ALS_ONCE:
+               status = isl29018_read_lux(client, &value);
+               break;
+
+       case COMMMAND1_OPMODE_IR_ONCE:
+               status = isl29018_read_ir(client, &value);
+               break;
+
+       default:
+               dev_err(&client->dev, "Mode %d is not supported\n", mode);
+               mutex_unlock(&chip->lock);
+               return -EBUSY;
+       }
+
+       if (status < 0) {
+               dev_err(&client->dev, "Error in Reading data");
+               mutex_unlock(&chip->lock);
+               return status;
+       }
+
+       mutex_unlock(&chip->lock);
+
+       return sprintf(buf, "%d\n", value);
+}
+
+
+/* Read lux */
+static ssize_t show_lux(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       return get_sensor_data(dev, buf, COMMMAND1_OPMODE_ALS_ONCE);
+}
+
+/* Read ir */
+static ssize_t show_ir(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       return get_sensor_data(dev, buf, COMMMAND1_OPMODE_IR_ONCE);
+}
+
+/* Read nearest ir */
+static ssize_t show_proxim_ir(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       return get_sensor_data(dev, buf, COMMMAND1_OPMODE_PROX_ONCE);
+}
+
 /* range */
 static ssize_t show_range(struct device *dev,
                        struct device_attribute *attr, char *buf)
@@ -440,6 +544,11 @@ static IIO_DEVICE_ATTR(proximity_on_chip_ambient_infrared_supression,
                                        S_IRUGO | S_IWUSR,
                                        show_prox_infrared_supression,
                                        store_prox_infrared_supression, 0);
+static IIO_DEVICE_ATTR(illuminance0_input, S_IRUGO, show_lux, NULL, 0);
+static IIO_DEVICE_ATTR(illuminance0_calibscale, S_IRUGO | S_IWUSR,
+                                       show_lux_scale, store_lux_scale, 0);
+static IIO_DEVICE_ATTR(intensity_infrared_raw, S_IRUGO, show_ir, NULL, 0);
+static IIO_DEVICE_ATTR(proximity_raw, S_IRUGO, show_proxim_ir, NULL, 0);
 
 #define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
 #define ISL29018_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr)
@@ -449,6 +558,10 @@ static struct attribute *isl29018_attributes[] = {
        ISL29018_DEV_ATTR(adc_resolution),
        ISL29018_CONST_ATTR(adc_resolution_available),
        ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_supression),
+       ISL29018_DEV_ATTR(illuminance0_input),
+       ISL29018_DEV_ATTR(illuminance0_calibscale),
+       ISL29018_DEV_ATTR(intensity_infrared_raw),
+       ISL29018_DEV_ATTR(proximity_raw),
        NULL
 };