Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * isl29020.c - Intersil  ALS Driver
0004  *
0005  * Copyright (C) 2008 Intel Corp
0006  *
0007  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0008  *
0009  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0010  *
0011  * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf
0012  */
0013 
0014 #include <linux/module.h>
0015 #include <linux/slab.h>
0016 #include <linux/i2c.h>
0017 #include <linux/err.h>
0018 #include <linux/delay.h>
0019 #include <linux/sysfs.h>
0020 #include <linux/pm_runtime.h>
0021 
0022 static DEFINE_MUTEX(mutex);
0023 
0024 static ssize_t als_sensing_range_show(struct device *dev,
0025             struct device_attribute *attr,  char *buf)
0026 {
0027     struct i2c_client *client = to_i2c_client(dev);
0028     int  val;
0029 
0030     val = i2c_smbus_read_byte_data(client, 0x00);
0031 
0032     if (val < 0)
0033         return val;
0034     return sprintf(buf, "%d000\n", 1 << (2 * (val & 3)));
0035 
0036 }
0037 
0038 static ssize_t als_lux_input_data_show(struct device *dev,
0039             struct device_attribute *attr, char *buf)
0040 {
0041     struct i2c_client *client = to_i2c_client(dev);
0042     int ret_val, val;
0043     unsigned long int lux;
0044     int temp;
0045 
0046     pm_runtime_get_sync(dev);
0047     msleep(100);
0048 
0049     mutex_lock(&mutex);
0050     temp = i2c_smbus_read_byte_data(client, 0x02); /* MSB data */
0051     if (temp < 0) {
0052         pm_runtime_put_sync(dev);
0053         mutex_unlock(&mutex);
0054         return temp;
0055     }
0056 
0057     ret_val = i2c_smbus_read_byte_data(client, 0x01); /* LSB data */
0058     mutex_unlock(&mutex);
0059 
0060     if (ret_val < 0) {
0061         pm_runtime_put_sync(dev);
0062         return ret_val;
0063     }
0064 
0065     ret_val |= temp << 8;
0066     val = i2c_smbus_read_byte_data(client, 0x00);
0067     pm_runtime_put_sync(dev);
0068     if (val < 0)
0069         return val;
0070     lux = ((((1 << (2 * (val & 3))))*1000) * ret_val) / 65536;
0071     return sprintf(buf, "%ld\n", lux);
0072 }
0073 
0074 static ssize_t als_sensing_range_store(struct device *dev,
0075         struct device_attribute *attr, const  char *buf, size_t count)
0076 {
0077     struct i2c_client *client = to_i2c_client(dev);
0078     int ret_val;
0079     unsigned long val;
0080 
0081     ret_val = kstrtoul(buf, 10, &val);
0082     if (ret_val)
0083         return ret_val;
0084 
0085     if (val < 1 || val > 64000)
0086         return -EINVAL;
0087 
0088     /* Pick the smallest sensor range that will meet our requirements */
0089     if (val <= 1000)
0090         val = 1;
0091     else if (val <= 4000)
0092         val = 2;
0093     else if (val <= 16000)
0094         val = 3;
0095     else
0096         val = 4;
0097 
0098     ret_val = i2c_smbus_read_byte_data(client, 0x00);
0099     if (ret_val < 0)
0100         return ret_val;
0101 
0102     ret_val &= 0xFC; /*reset the bit before setting them */
0103     ret_val |= val - 1;
0104     ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val);
0105 
0106     if (ret_val < 0)
0107         return ret_val;
0108     return count;
0109 }
0110 
0111 static void als_set_power_state(struct i2c_client *client, int enable)
0112 {
0113     int ret_val;
0114 
0115     ret_val = i2c_smbus_read_byte_data(client, 0x00);
0116     if (ret_val < 0)
0117         return;
0118 
0119     if (enable)
0120         ret_val |= 0x80;
0121     else
0122         ret_val &= 0x7F;
0123 
0124     i2c_smbus_write_byte_data(client, 0x00, ret_val);
0125 }
0126 
0127 static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR,
0128     als_sensing_range_show, als_sensing_range_store);
0129 static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL);
0130 
0131 static struct attribute *mid_att_als[] = {
0132     &dev_attr_lux0_sensor_range.attr,
0133     &dev_attr_lux0_input.attr,
0134     NULL
0135 };
0136 
0137 static const struct attribute_group m_als_gr = {
0138     .name = "isl29020",
0139     .attrs = mid_att_als
0140 };
0141 
0142 static int als_set_default_config(struct i2c_client *client)
0143 {
0144     int retval;
0145 
0146     retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0);
0147     if (retval < 0) {
0148         dev_err(&client->dev, "default write failed.");
0149         return retval;
0150     }
0151     return 0;
0152 }
0153 
0154 static int  isl29020_probe(struct i2c_client *client,
0155                     const struct i2c_device_id *id)
0156 {
0157     int res;
0158 
0159     res = als_set_default_config(client);
0160     if (res <  0)
0161         return res;
0162 
0163     res = sysfs_create_group(&client->dev.kobj, &m_als_gr);
0164     if (res) {
0165         dev_err(&client->dev, "isl29020: device create file failed\n");
0166         return res;
0167     }
0168     dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);
0169     als_set_power_state(client, 0);
0170     pm_runtime_enable(&client->dev);
0171     return res;
0172 }
0173 
0174 static int isl29020_remove(struct i2c_client *client)
0175 {
0176     pm_runtime_disable(&client->dev);
0177     sysfs_remove_group(&client->dev.kobj, &m_als_gr);
0178     return 0;
0179 }
0180 
0181 static const struct i2c_device_id isl29020_id[] = {
0182     { "isl29020", 0 },
0183     { }
0184 };
0185 
0186 MODULE_DEVICE_TABLE(i2c, isl29020_id);
0187 
0188 #ifdef CONFIG_PM
0189 
0190 static int isl29020_runtime_suspend(struct device *dev)
0191 {
0192     struct i2c_client *client = to_i2c_client(dev);
0193     als_set_power_state(client, 0);
0194     return 0;
0195 }
0196 
0197 static int isl29020_runtime_resume(struct device *dev)
0198 {
0199     struct i2c_client *client = to_i2c_client(dev);
0200     als_set_power_state(client, 1);
0201     return 0;
0202 }
0203 
0204 static const struct dev_pm_ops isl29020_pm_ops = {
0205     .runtime_suspend = isl29020_runtime_suspend,
0206     .runtime_resume = isl29020_runtime_resume,
0207 };
0208 
0209 #define ISL29020_PM_OPS (&isl29020_pm_ops)
0210 #else   /* CONFIG_PM */
0211 #define ISL29020_PM_OPS NULL
0212 #endif  /* CONFIG_PM */
0213 
0214 static struct i2c_driver isl29020_driver = {
0215     .driver = {
0216         .name = "isl29020",
0217         .pm = ISL29020_PM_OPS,
0218     },
0219     .probe = isl29020_probe,
0220     .remove = isl29020_remove,
0221     .id_table = isl29020_id,
0222 };
0223 
0224 module_i2c_driver(isl29020_driver);
0225 
0226 MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>");
0227 MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");
0228 MODULE_LICENSE("GPL v2");