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