linux/drivers/misc/isl29020.c
<<
>>
Prefs
   1/*
   2 * isl29020.c - Intersil  ALS Driver
   3 *
   4 * Copyright (C) 2008 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 * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf
  23 */
  24
  25#include <linux/module.h>
  26#include <linux/slab.h>
  27#include <linux/i2c.h>
  28#include <linux/err.h>
  29#include <linux/delay.h>
  30#include <linux/sysfs.h>
  31#include <linux/pm_runtime.h>
  32
  33static DEFINE_MUTEX(mutex);
  34
  35static ssize_t als_sensing_range_show(struct device *dev,
  36                        struct device_attribute *attr,  char *buf)
  37{
  38        struct i2c_client *client = to_i2c_client(dev);
  39        int  val;
  40
  41        val = i2c_smbus_read_byte_data(client, 0x00);
  42
  43        if (val < 0)
  44                return val;
  45        return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
  46
  47}
  48
  49static ssize_t als_lux_input_data_show(struct device *dev,
  50                        struct device_attribute *attr, char *buf)
  51{
  52        struct i2c_client *client = to_i2c_client(dev);
  53        int ret_val, val;
  54        unsigned long int lux;
  55        int temp;
  56
  57        pm_runtime_get_sync(dev);
  58        msleep(100);
  59
  60        mutex_lock(&mutex);
  61        temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
  62        if (temp < 0) {
  63                pm_runtime_put_sync(dev);
  64                mutex_unlock(&mutex);
  65                return temp;
  66        }
  67
  68        ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
  69        mutex_unlock(&mutex);
  70
  71        if (ret_val < 0) {
  72                pm_runtime_put_sync(dev);
  73                return ret_val;
  74        }
  75
  76        ret_val |= temp << 8;
  77        val = i2c_smbus_read_byte_data(client, 0x00);
  78        pm_runtime_put_sync(dev);
  79        if (val < 0)
  80                return val;
  81        lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536;
  82        return sprintf(buf, "%ld\n", lux);
  83}
  84
  85static ssize_t als_sensing_range_store(struct device *dev,
  86                struct device_attribute *attr, const  char *buf, size_t count)
  87{
  88        struct i2c_client *client = to_i2c_client(dev);
  89        int ret_val;
  90        unsigned long val;
  91
  92        ret_val = kstrtoul(buf, 10, &val);
  93        if (ret_val)
  94                return ret_val;
  95
  96        if (val < 1 || val > 64000)
  97                return -EINVAL;
  98
  99        /* Pick the smallest sensor range that will meet our requirements */
 100        if (val <= 1000)
 101                val = 1;
 102        else if (val <= 4000)
 103                val = 2;
 104        else if (val <= 16000)
 105                val = 3;
 106        else
 107                val = 4;
 108
 109        ret_val = i2c_smbus_read_byte_data(client, 0x00);
 110        if (ret_val < 0)
 111                return ret_val;
 112
 113        ret_val &= 0xFC; /*reset the bit before setting them */
 114        ret_val |= val - 1;
 115        ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val);
 116
 117        if (ret_val < 0)
 118                return ret_val;
 119        return count;
 120}
 121
 122static void als_set_power_state(struct i2c_client *client, int enable)
 123{
 124        int ret_val;
 125
 126        ret_val = i2c_smbus_read_byte_data(client, 0x00);
 127        if (ret_val < 0)
 128                return;
 129
 130        if (enable)
 131                ret_val |= 0x80;
 132        else
 133                ret_val &= 0x7F;
 134
 135        i2c_smbus_write_byte_data(client, 0x00, ret_val);
 136}
 137
 138static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
 139        als_sensing_range_show, als_sensing_range_store);
 140static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL);
 141
 142static struct attribute *mid_att_als[] = {
 143        &dev_attr_lux0_sensor_range.attr,
 144        &dev_attr_lux0_input.attr,
 145        NULL
 146};
 147
 148static struct attribute_group m_als_gr = {
 149        .name = "isl29020",
 150        .attrs = mid_att_als
 151};
 152
 153static int als_set_default_config(struct i2c_client *client)
 154{
 155        int retval;
 156
 157        retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0);
 158        if (retval < 0) {
 159                dev_err(&client->dev, "default write failed.");
 160                return retval;
 161        }
 162        return 0;
 163}
 164
 165static int  isl29020_probe(struct i2c_client *client,
 166                                        const struct i2c_device_id *id)
 167{
 168        int res;
 169
 170        res = als_set_default_config(client);
 171        if (res <  0)
 172                return res;
 173
 174        res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
 175        if (res) {
 176                dev_err(&client->dev, "isl29020: device create file failed\n");
 177                return res;
 178        }
 179        dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);
 180        als_set_power_state(client, 0);
 181        pm_runtime_enable(&client->dev);
 182        return res;
 183}
 184
 185static int isl29020_remove(struct i2c_client *client)
 186{
 187        sysfs_remove_group(&client->dev.kobj, &m_als_gr);
 188        return 0;
 189}
 190
 191static struct i2c_device_id isl29020_id[] = {
 192        { "isl29020", 0 },
 193        { }
 194};
 195
 196MODULE_DEVICE_TABLE(i2c, isl29020_id);
 197
 198#ifdef CONFIG_PM
 199
 200static int isl29020_runtime_suspend(struct device *dev)
 201{
 202        struct i2c_client *client = to_i2c_client(dev);
 203        als_set_power_state(client, 0);
 204        return 0;
 205}
 206
 207static int isl29020_runtime_resume(struct device *dev)
 208{
 209        struct i2c_client *client = to_i2c_client(dev);
 210        als_set_power_state(client, 1);
 211        return 0;
 212}
 213
 214static const struct dev_pm_ops isl29020_pm_ops = {
 215        .runtime_suspend = isl29020_runtime_suspend,
 216        .runtime_resume = isl29020_runtime_resume,
 217};
 218
 219#define ISL29020_PM_OPS (&isl29020_pm_ops)
 220#else   /* CONFIG_PM */
 221#define ISL29020_PM_OPS NULL
 222#endif  /* CONFIG_PM */
 223
 224static struct i2c_driver isl29020_driver = {
 225        .driver = {
 226                .name = "isl29020",
 227                .pm = ISL29020_PM_OPS,
 228        },
 229        .probe = isl29020_probe,
 230        .remove = isl29020_remove,
 231        .id_table = isl29020_id,
 232};
 233
 234module_i2c_driver(isl29020_driver);
 235
 236MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>");
 237MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");
 238MODULE_LICENSE("GPL v2");
 239