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 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 if (ret_val < 0) 110 return ret_val; 111 112 ret_val &= 0xFC; /*reset the bit before setting them */ 113 ret_val |= val - 1; 114 ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); 115 116 if (ret_val < 0) 117 return ret_val; 118 return count; 119 } 120 121 static void als_set_power_state(struct i2c_client *client, int enable) 122 { 123 int ret_val; 124 125 ret_val = i2c_smbus_read_byte_data(client, 0x00); 126 if (ret_val < 0) 127 return; 128 129 if (enable) 130 ret_val |= 0x80; 131 else 132 ret_val &= 0x7F; 133 134 i2c_smbus_write_byte_data(client, 0x00, ret_val); 135 } 136 137 static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 138 als_sensing_range_show, als_sensing_range_store); 139 static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); 140 141 static struct attribute *mid_att_als[] = { 142 &dev_attr_lux0_sensor_range.attr, 143 &dev_attr_lux0_input.attr, 144 NULL 145 }; 146 147 static struct attribute_group m_als_gr = { 148 .name = "isl29020", 149 .attrs = mid_att_als 150 }; 151 152 static int als_set_default_config(struct i2c_client *client) 153 { 154 int retval; 155 156 retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); 157 if (retval < 0) { 158 dev_err(&client->dev, "default write failed."); 159 return retval; 160 } 161 return 0; 162 } 163 164 static int isl29020_probe(struct i2c_client *client, 165 const struct i2c_device_id *id) 166 { 167 int res; 168 169 res = als_set_default_config(client); 170 if (res < 0) 171 return res; 172 173 res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 174 if (res) { 175 dev_err(&client->dev, "isl29020: device create file failed\n"); 176 return res; 177 } 178 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 179 als_set_power_state(client, 0); 180 pm_runtime_enable(&client->dev); 181 return res; 182 } 183 184 static int isl29020_remove(struct i2c_client *client) 185 { 186 sysfs_remove_group(&client->dev.kobj, &m_als_gr); 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