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 34 static DEFINE_MUTEX(mutex); 35 36 static 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 50 static 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 86 static 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 unsigned 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 110 ret_val &= 0xFC; /*reset the bit before setting them */ 111 ret_val |= val - 1; 112 ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); 113 114 if (ret_val < 0) 115 return ret_val; 116 return count; 117 } 118 119 static void als_set_power_state(struct i2c_client *client, int enable) 120 { 121 int ret_val; 122 123 ret_val = i2c_smbus_read_byte_data(client, 0x00); 124 if (ret_val < 0) 125 return; 126 127 if (enable) 128 ret_val |= 0x80; 129 else 130 ret_val &= 0x7F; 131 132 i2c_smbus_write_byte_data(client, 0x00, ret_val); 133 } 134 135 static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 136 als_sensing_range_show, als_sensing_range_store); 137 static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); 138 139 static struct attribute *mid_att_als[] = { 140 &dev_attr_lux0_sensor_range.attr, 141 &dev_attr_lux0_input.attr, 142 NULL 143 }; 144 145 static struct attribute_group m_als_gr = { 146 .name = "isl29020", 147 .attrs = mid_att_als 148 }; 149 150 static int als_set_default_config(struct i2c_client *client) 151 { 152 int retval; 153 154 retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); 155 if (retval < 0) { 156 dev_err(&client->dev, "default write failed."); 157 return retval; 158 } 159 return 0;; 160 } 161 162 static int isl29020_probe(struct i2c_client *client, 163 const struct i2c_device_id *id) 164 { 165 int res; 166 167 res = als_set_default_config(client); 168 if (res < 0) 169 return res; 170 171 res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 172 if (res) { 173 dev_err(&client->dev, "isl29020: device create file failed\n"); 174 return res; 175 } 176 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 177 als_set_power_state(client, 0); 178 pm_runtime_enable(&client->dev); 179 return res; 180 } 181 182 static int isl29020_remove(struct i2c_client *client) 183 { 184 struct als_data *data = i2c_get_clientdata(client); 185 sysfs_remove_group(&client->dev.kobj, &m_als_gr); 186 kfree(data); 187 return 0; 188 } 189 190 static struct i2c_device_id isl29020_id[] = { 191 { "isl29020", 0 }, 192 { } 193 }; 194 195 MODULE_DEVICE_TABLE(i2c, isl29020_id); 196 197 #ifdef CONFIG_PM 198 199 static 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 206 static 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 213 static 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 223 static 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 233 static int __init sensor_isl29020_init(void) 234 { 235 return i2c_add_driver(&isl29020_driver); 236 } 237 238 static void __exit sensor_isl29020_exit(void) 239 { 240 i2c_del_driver(&isl29020_driver); 241 } 242 243 module_init(sensor_isl29020_init); 244 module_exit(sensor_isl29020_exit); 245 246 MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com"); 247 MODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); 248 MODULE_LICENSE("GPL v2"); 249