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 ret_val = kstrtoul(buf, 10, &val); 94 if (ret_val) 95 return ret_val; 96 97 if (val < 1 || val > 64000) 98 return -EINVAL; 99 100 /* Pick the smallest sensor range that will meet our requirements */ 101 if (val <= 1000) 102 val = 1; 103 else if (val <= 4000) 104 val = 2; 105 else if (val <= 16000) 106 val = 3; 107 else 108 val = 4; 109 110 ret_val = i2c_smbus_read_byte_data(client, 0x00); 111 if (ret_val < 0) 112 return ret_val; 113 114 ret_val &= 0xFC; /*reset the bit before setting them */ 115 ret_val |= val - 1; 116 ret_val = i2c_smbus_write_byte_data(client, 0x00, ret_val); 117 118 if (ret_val < 0) 119 return ret_val; 120 return count; 121 } 122 123 static void als_set_power_state(struct i2c_client *client, int enable) 124 { 125 int ret_val; 126 127 ret_val = i2c_smbus_read_byte_data(client, 0x00); 128 if (ret_val < 0) 129 return; 130 131 if (enable) 132 ret_val |= 0x80; 133 else 134 ret_val &= 0x7F; 135 136 i2c_smbus_write_byte_data(client, 0x00, ret_val); 137 } 138 139 static DEVICE_ATTR(lux0_sensor_range, S_IRUGO | S_IWUSR, 140 als_sensing_range_show, als_sensing_range_store); 141 static DEVICE_ATTR(lux0_input, S_IRUGO, als_lux_input_data_show, NULL); 142 143 static struct attribute *mid_att_als[] = { 144 &dev_attr_lux0_sensor_range.attr, 145 &dev_attr_lux0_input.attr, 146 NULL 147 }; 148 149 static struct attribute_group m_als_gr = { 150 .name = "isl29020", 151 .attrs = mid_att_als 152 }; 153 154 static int als_set_default_config(struct i2c_client *client) 155 { 156 int retval; 157 158 retval = i2c_smbus_write_byte_data(client, 0x00, 0xc0); 159 if (retval < 0) { 160 dev_err(&client->dev, "default write failed."); 161 return retval; 162 } 163 return 0; 164 } 165 166 static int isl29020_probe(struct i2c_client *client, 167 const struct i2c_device_id *id) 168 { 169 int res; 170 171 res = als_set_default_config(client); 172 if (res < 0) 173 return res; 174 175 res = sysfs_create_group(&client->dev.kobj, &m_als_gr); 176 if (res) { 177 dev_err(&client->dev, "isl29020: device create file failed\n"); 178 return res; 179 } 180 dev_info(&client->dev, "%s isl29020: ALS chip found\n", client->name); 181 als_set_power_state(client, 0); 182 pm_runtime_enable(&client->dev); 183 return res; 184 } 185 186 static int isl29020_remove(struct i2c_client *client) 187 { 188 sysfs_remove_group(&client->dev.kobj, &m_als_gr); 189 return 0; 190 } 191 192 static struct i2c_device_id isl29020_id[] = { 193 { "isl29020", 0 }, 194 { } 195 }; 196 197 MODULE_DEVICE_TABLE(i2c, isl29020_id); 198 199 #ifdef CONFIG_PM 200 201 static int isl29020_runtime_suspend(struct device *dev) 202 { 203 struct i2c_client *client = to_i2c_client(dev); 204 als_set_power_state(client, 0); 205 return 0; 206 } 207 208 static int isl29020_runtime_resume(struct device *dev) 209 { 210 struct i2c_client *client = to_i2c_client(dev); 211 als_set_power_state(client, 1); 212 return 0; 213 } 214 215 static const struct dev_pm_ops isl29020_pm_ops = { 216 .runtime_suspend = isl29020_runtime_suspend, 217 .runtime_resume = isl29020_runtime_resume, 218 }; 219 220 #define ISL29020_PM_OPS (&isl29020_pm_ops) 221 #else /* CONFIG_PM */ 222 #define ISL29020_PM_OPS NULL 223 #endif /* CONFIG_PM */ 224 225 static struct i2c_driver isl29020_driver = { 226 .driver = { 227 .name = "isl29020", 228 .pm = ISL29020_PM_OPS, 229 }, 230 .probe = isl29020_probe, 231 .remove = isl29020_remove, 232 .id_table = isl29020_id, 233 }; 234 235 module_i2c_driver(isl29020_driver); 236 237 MODULE_AUTHOR("Kalhan Trisal <kalhan.trisal@intel.com>"); 238 MODULE_DESCRIPTION("Intersil isl29020 ALS Driver"); 239 MODULE_LICENSE("GPL v2"); 240