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