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