linux/drivers/misc/apds9802als.c
<<
>>
Prefs
   1/*
   2 * apds9802als.c - apds9802  ALS Driver
   3 *
   4 * Copyright (C) 2009 Intel Corp
   5 *
   6 *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   7 *
   8 * This program is free software; you can redistribute it and/or modify
   9 * it under the terms of the GNU General Public License as published by
  10 * the Free Software Foundation; version 2 of the License.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License along
  18 * with this program; if not, write to the Free Software Foundation, Inc.,
  19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
  20 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  21 *
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/slab.h>
  26#include <linux/i2c.h>
  27#include <linux/err.h>
  28#include <linux/delay.h>
  29#include <linux/mutex.h>
  30#include <linux/sysfs.h>
  31#include <linux/pm_runtime.h>
  32
  33#define ALS_MIN_RANGE_VAL 1
  34#define ALS_MAX_RANGE_VAL 2
  35#define POWER_STA_ENABLE 1
  36#define POWER_STA_DISABLE 0
  37
  38#define DRIVER_NAME "apds9802als"
  39
  40struct als_data {
  41        struct mutex mutex;
  42};
  43
  44static ssize_t als_sensing_range_show(struct device *dev,
  45                        struct device_attribute *attr,  char *buf)
  46{
  47        struct i2c_client *client = to_i2c_client(dev);
  48        int  val;
  49
  50        val = i2c_smbus_read_byte_data(client, 0x81);
  51        if (val < 0)
  52                return val;
  53        if (val & 1)
  54                return sprintf(buf, "4095\n");
  55        else
  56                return sprintf(buf, "65535\n");
  57}
  58
  59static int als_wait_for_data_ready(struct device *dev)
  60{
  61        struct i2c_client *client = to_i2c_client(dev);
  62        int ret;
  63        int retry = 10;
  64
  65        do {
  66                msleep(30);
  67                ret = i2c_smbus_read_byte_data(client, 0x86);
  68        } while (!(ret & 0x80) && retry--);
  69
  70        if (retry < 0) {
  71                dev_warn(dev, "timeout waiting for data ready\n");
  72                return -ETIMEDOUT;
  73        }
  74
  75        return 0;
  76}
  77
  78static ssize_t als_lux0_input_data_show(struct device *dev,
  79                        struct device_attribute *attr, char *buf)
  80{
  81        struct i2c_client *client = to_i2c_client(dev);
  82        struct als_data *data = i2c_get_clientdata(client);
  83        int ret_val;
  84        int temp;
  85
  86        /* Protect against parallel reads */
  87        pm_runtime_get_sync(dev);
  88        mutex_lock(&data->mutex);
  89
  90        /* clear EOC interrupt status */
  91        i2c_smbus_write_byte(client, 0x40);
  92        /* start measurement */
  93        temp = i2c_smbus_read_byte_data(client, 0x81);
  94        i2c_smbus_write_byte_data(client, 0x81, temp | 0x08);
  95
  96        ret_val = als_wait_for_data_ready(dev);
  97        if (ret_val < 0)
  98                goto failed;
  99
 100        temp = i2c_smbus_read_byte_data(client, 0x8C); /* LSB data */
 101        if (temp < 0) {
 102                ret_val = temp;
 103                goto failed;
 104        }
 105        ret_val = i2c_smbus_read_byte_data(client, 0x8D); /* MSB data */
 106        if (ret_val < 0)
 107                goto failed;
 108
 109        mutex_unlock(&data->mutex);
 110        pm_runtime_put_sync(dev);
 111
 112        temp = (ret_val << 8) | temp;
 113        return sprintf(buf, "%d\n", temp);
 114failed:
 115        mutex_unlock(&data->mutex);
 116        pm_runtime_put_sync(dev);
 117        return ret_val;
 118}
 119
 120static ssize_t als_sensing_range_store(struct device *dev,
 121                struct device_attribute *attr, const  char *buf, size_t count)
 122{
 123        struct i2c_client *client = to_i2c_client(dev);
 124        struct als_data *data = i2c_get_clientdata(client);
 125        int ret_val;
 126        unsigned long val;
 127
 128        ret_val = kstrtoul(buf, 10, &val);
 129        if (ret_val)
 130                return ret_val;
 131
 132        if (val < 4096)
 133                val = 1;
 134        else if (val < 65536)
 135                val = 2;
 136        else
 137                return -ERANGE;
 138
 139        pm_runtime_get_sync(dev);
 140
 141        /* Make sure nobody else reads/modifies/writes 0x81 while we
 142           are active */
 143        mutex_lock(&data->mutex);
 144
 145        ret_val = i2c_smbus_read_byte_data(client, 0x81);
 146        if (ret_val < 0)
 147                goto fail;
 148
 149        /* Reset the bits before setting them */
 150        ret_val = ret_val & 0xFA;
 151
 152        if (val == 1) /* Setting detection range up to 4k LUX */
 153                ret_val = (ret_val | 0x01);
 154        else /* Setting detection range up to 64k LUX*/
 155                ret_val = (ret_val | 0x00);
 156
 157        ret_val = i2c_smbus_write_byte_data(client, 0x81, ret_val);
 158
 159        if (ret_val >= 0) {
 160                /* All OK */
 161                mutex_unlock(&data->mutex);
 162                pm_runtime_put_sync(dev);
 163                return count;
 164        }
 165fail:
 166        mutex_unlock(&data->mutex);
 167        pm_runtime_put_sync(dev);
 168        return ret_val;
 169}
 170
 171static int als_set_power_state(struct i2c_client *client, bool on_off)
 172{
 173        int ret_val;
 174        struct als_data *data = i2c_get_clientdata(client);
 175
 176        mutex_lock(&data->mutex);
 177        ret_val = i2c_smbus_read_byte_data(client, 0x80);
 178        if (ret_val < 0)
 179                goto fail;
 180        if (on_off)
 181                ret_val = ret_val | 0x01;
 182        else
 183                ret_val = ret_val & 0xFE;
 184        ret_val = i2c_smbus_write_byte_data(client, 0x80, ret_val);
 185fail:
 186        mutex_unlock(&data->mutex);
 187        return ret_val;
 188}
 189
 190static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
 191        als_sensing_range_show, als_sensing_range_store);
 192static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux0_input_data_show, NULL);
 193
 194static struct attribute *mid_att_als[] = {
 195        &dev_attr_lux0_sensor_range.attr,
 196        &dev_attr_lux0_input.attr,
 197        NULL
 198};
 199
 200static struct attribute_group m_als_gr = {
 201        .name = "apds9802als",
 202        .attrs = mid_att_als
 203};
 204
 205static int als_set_default_config(struct i2c_client *client)
 206{
 207        int ret_val;
 208        /* Write the command and then switch on */
 209        ret_val = i2c_smbus_write_byte_data(client, 0x80, 0x01);
 210        if (ret_val < 0) {
 211                dev_err(&client->dev, "failed default switch on write\n");
 212                return ret_val;
 213        }
 214        /* detection range: 1~64K Lux, maunal measurement */
 215        ret_val = i2c_smbus_write_byte_data(client, 0x81, 0x08);
 216        if (ret_val < 0)
 217                dev_err(&client->dev, "failed default LUX on write\n");
 218
 219        /*  We always get 0 for the 1st measurement after system power on,
 220         *  so make sure it is finished before user asks for data.
 221         */
 222        als_wait_for_data_ready(&client->dev);
 223
 224        return ret_val;
 225}
 226
 227static int apds9802als_probe(struct i2c_client *client,
 228                             const struct i2c_device_id *id)
 229{
 230        int res;
 231        struct als_data *data;
 232
 233        data = kzalloc(sizeof(struct als_data), GFP_KERNEL);
 234        if (data == NULL) {
 235                dev_err(&client->dev, "Memory allocation failed\n");
 236                return -ENOMEM;
 237        }
 238        i2c_set_clientdata(client, data);
 239        res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
 240        if (res) {
 241                dev_err(&client->dev, "device create file failed\n");
 242                goto als_error1;
 243        }
 244        dev_info(&client->dev, "ALS chip found\n");
 245        als_set_default_config(client);
 246        mutex_init(&data->mutex);
 247
 248        pm_runtime_set_active(&client->dev);
 249        pm_runtime_enable(&client->dev);
 250
 251        return res;
 252als_error1:
 253        kfree(data);
 254        return res;
 255}
 256
 257static int apds9802als_remove(struct i2c_client *client)
 258{
 259        struct als_data *data = i2c_get_clientdata(client);
 260
 261        pm_runtime_get_sync(&client->dev);
 262
 263        als_set_power_state(client, false);
 264        sysfs_remove_group(&client->dev.kobj, &m_als_gr);
 265
 266        pm_runtime_disable(&client->dev);
 267        pm_runtime_set_suspended(&client->dev);
 268        pm_runtime_put_noidle(&client->dev);
 269
 270        kfree(data);
 271        return 0;
 272}
 273
 274#ifdef CONFIG_PM
 275
 276static int apds9802als_suspend(struct device *dev)
 277{
 278        struct i2c_client *client = to_i2c_client(dev);
 279
 280        als_set_power_state(client, false);
 281        return 0;
 282}
 283
 284static int apds9802als_resume(struct device *dev)
 285{
 286        struct i2c_client *client = to_i2c_client(dev);
 287
 288        als_set_power_state(client, true);
 289        return 0;
 290}
 291
 292static UNIVERSAL_DEV_PM_OPS(apds9802als_pm_ops, apds9802als_suspend,
 293        apds9802als_resume, NULL);
 294
 295#define APDS9802ALS_PM_OPS (&apds9802als_pm_ops)
 296
 297#else   /* CONFIG_PM */
 298#define APDS9802ALS_PM_OPS NULL
 299#endif  /* CONFIG_PM */
 300
 301static struct i2c_device_id apds9802als_id[] = {
 302        { DRIVER_NAME, 0 },
 303        { }
 304};
 305
 306MODULE_DEVICE_TABLE(i2c, apds9802als_id);
 307
 308static struct i2c_driver apds9802als_driver = {
 309        .driver = {
 310                .name = DRIVER_NAME,
 311                .pm = APDS9802ALS_PM_OPS,
 312        },
 313        .probe = apds9802als_probe,
 314        .remove = apds9802als_remove,
 315        .id_table = apds9802als_id,
 316};
 317
 318module_i2c_driver(apds9802als_driver);
 319
 320MODULE_AUTHOR("Anantha Narayanan <Anantha.Narayanan@intel.com");
 321MODULE_DESCRIPTION("Avago apds9802als ALS Driver");
 322MODULE_LICENSE("GPL v2");
 323