227 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * isl29020.c - Intersil ALS Driver4 *5 * Copyright (C) 2008 Intel Corp6 *7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~8 *9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~10 *11 * Data sheet at: http://www.intersil.com/data/fn/fn6505.pdf12 */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 else96 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 else122 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 NULL135};136 137static const struct attribute_group m_als_gr = {138 .name = "isl29020",139 .attrs = mid_att_als140};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{156 int res;157 158 res = als_set_default_config(client);159 if (res < 0)160 return res;161 162 res = sysfs_create_group(&client->dev.kobj, &m_als_gr);163 if (res) {164 dev_err(&client->dev, "isl29020: device create file failed\n");165 return res;166 }167 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name);168 als_set_power_state(client, 0);169 pm_runtime_enable(&client->dev);170 return res;171}172 173static void isl29020_remove(struct i2c_client *client)174{175 pm_runtime_disable(&client->dev);176 sysfs_remove_group(&client->dev.kobj, &m_als_gr);177}178 179static const struct i2c_device_id isl29020_id[] = {180 { "isl29020" },181 { }182};183 184MODULE_DEVICE_TABLE(i2c, isl29020_id);185 186#ifdef CONFIG_PM187 188static int isl29020_runtime_suspend(struct device *dev)189{190 struct i2c_client *client = to_i2c_client(dev);191 als_set_power_state(client, 0);192 return 0;193}194 195static int isl29020_runtime_resume(struct device *dev)196{197 struct i2c_client *client = to_i2c_client(dev);198 als_set_power_state(client, 1);199 return 0;200}201 202static const struct dev_pm_ops isl29020_pm_ops = {203 .runtime_suspend = isl29020_runtime_suspend,204 .runtime_resume = isl29020_runtime_resume,205};206 207#define ISL29020_PM_OPS (&isl29020_pm_ops)208#else /* CONFIG_PM */209#define ISL29020_PM_OPS NULL210#endif /* CONFIG_PM */211 212static struct i2c_driver isl29020_driver = {213 .driver = {214 .name = "isl29020",215 .pm = ISL29020_PM_OPS,216 },217 .probe = isl29020_probe,218 .remove = isl29020_remove,219 .id_table = isl29020_id,220};221 222module_i2c_driver(isl29020_driver);223 224MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>");225MODULE_DESCRIPTION("Intersil isl29020 ALS Driver");226MODULE_LICENSE("GPL v2");227