Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drivers/misc/isl29003.c: driver for the ISL29003 ambient light sensor

Add a driver for Intersil's ISL29003 ambient light sensor device plus some
documentation. Inspired by tsl2550.c, a driver for a similar device.

It is put in drivers/misc for now until the industrial I/O framework gets
merged.

Signed-off-by: Daniel Mack <daniel@caiaq.de>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Daniel Mack and committed by
Linus Torvalds
3cdbbeeb 891f7d73

+543
+62
Documentation/misc-devices/isl29003
··· 1 + Kernel driver isl29003 2 + ===================== 3 + 4 + Supported chips: 5 + * Intersil ISL29003 6 + Prefix: 'isl29003' 7 + Addresses scanned: none 8 + Datasheet: 9 + http://www.intersil.com/data/fn/fn7464.pdf 10 + 11 + Author: Daniel Mack <daniel@caiaq.de> 12 + 13 + 14 + Description 15 + ----------- 16 + The ISL29003 is an integrated light sensor with a 16-bit integrating type 17 + ADC, I2C user programmable lux range select for optimized counts/lux, and 18 + I2C multi-function control and monitoring capabilities. The internal ADC 19 + provides 16-bit resolution while rejecting 50Hz and 60Hz flicker caused by 20 + artificial light sources. 21 + 22 + The driver allows to set the lux range, the bit resolution, the operational 23 + mode (see below) and the power state of device and can read the current lux 24 + value, of course. 25 + 26 + 27 + Detection 28 + --------- 29 + 30 + The ISL29003 does not have an ID register which could be used to identify 31 + it, so the detection routine will just try to read from the configured I2C 32 + addess and consider the device to be present as soon as it ACKs the 33 + transfer. 34 + 35 + 36 + Sysfs entries 37 + ------------- 38 + 39 + range: 40 + 0: 0 lux to 1000 lux (default) 41 + 1: 0 lux to 4000 lux 42 + 2: 0 lux to 16,000 lux 43 + 3: 0 lux to 64,000 lux 44 + 45 + resolution: 46 + 0: 2^16 cycles (default) 47 + 1: 2^12 cycles 48 + 2: 2^8 cycles 49 + 3: 2^4 cycles 50 + 51 + mode: 52 + 0: diode1's current (unsigned 16bit) (default) 53 + 1: diode1's current (unsigned 16bit) 54 + 2: difference between diodes (l1 - l2, signed 15bit) 55 + 56 + power_state: 57 + 0: device is disabled (default) 58 + 1: device is enabled 59 + 60 + lux (read only): 61 + returns the value from the last sensor reading 62 +
+10
drivers/misc/Kconfig
··· 223 223 This driver adds support for rfkill and backlight control to Dell 224 224 laptops. 225 225 226 + config ISL29003 227 + tristate "Intersil ISL29003 ambient light sensor" 228 + depends on I2C && SYSFS 229 + help 230 + If you say yes here you get support for the Intersil ISL29003 231 + ambient light sensor. 232 + 233 + This driver can also be built as a module. If so, the module 234 + will be called isl29003. 235 + 226 236 source "drivers/misc/c2port/Kconfig" 227 237 source "drivers/misc/eeprom/Kconfig" 228 238
+1
drivers/misc/Makefile
··· 18 18 obj-$(CONFIG_SGI_XP) += sgi-xp/ 19 19 obj-$(CONFIG_SGI_GRU) += sgi-gru/ 20 20 obj-$(CONFIG_HP_ILO) += hpilo.o 21 + obj-$(CONFIG_ISL29003) += isl29003.o 21 22 obj-$(CONFIG_C2PORT) += c2port/ 22 23 obj-y += eeprom/
+470
drivers/misc/isl29003.c
··· 1 + /* 2 + * isl29003.c - Linux kernel module for 3 + * Intersil ISL29003 ambient light sensor 4 + * 5 + * See file:Documentation/misc-devices/isl29003 6 + * 7 + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> 8 + * 9 + * Based on code written by 10 + * Rodolfo Giometti <giometti@linux.it> 11 + * Eurotech S.p.A. <info@eurotech.it> 12 + * 13 + * This program is free software; you can redistribute it and/or modify 14 + * it under the terms of the GNU General Public License as published by 15 + * the Free Software Foundation; either version 2 of the License, or 16 + * (at your option) any later version. 17 + * 18 + * This program is distributed in the hope that it will be useful, 19 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 + * GNU General Public License for more details. 22 + * 23 + * You should have received a copy of the GNU General Public License 24 + * along with this program; if not, write to the Free Software 25 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 + */ 27 + 28 + #include <linux/module.h> 29 + #include <linux/init.h> 30 + #include <linux/slab.h> 31 + #include <linux/i2c.h> 32 + #include <linux/mutex.h> 33 + #include <linux/delay.h> 34 + 35 + #define ISL29003_DRV_NAME "isl29003" 36 + #define DRIVER_VERSION "1.0" 37 + 38 + #define ISL29003_REG_COMMAND 0x00 39 + #define ISL29003_ADC_ENABLED (1 << 7) 40 + #define ISL29003_ADC_PD (1 << 6) 41 + #define ISL29003_TIMING_INT (1 << 5) 42 + #define ISL29003_MODE_SHIFT (2) 43 + #define ISL29003_MODE_MASK (0x3 << ISL29003_MODE_SHIFT) 44 + #define ISL29003_RES_SHIFT (0) 45 + #define ISL29003_RES_MASK (0x3 << ISL29003_RES_SHIFT) 46 + 47 + #define ISL29003_REG_CONTROL 0x01 48 + #define ISL29003_INT_FLG (1 << 5) 49 + #define ISL29003_RANGE_SHIFT (2) 50 + #define ISL29003_RANGE_MASK (0x3 << ISL29003_RANGE_SHIFT) 51 + #define ISL29003_INT_PERSISTS_SHIFT (0) 52 + #define ISL29003_INT_PERSISTS_MASK (0xf << ISL29003_INT_PERSISTS_SHIFT) 53 + 54 + #define ISL29003_REG_IRQ_THRESH_HI 0x02 55 + #define ISL29003_REG_IRQ_THRESH_LO 0x03 56 + #define ISL29003_REG_LSB_SENSOR 0x04 57 + #define ISL29003_REG_MSB_SENSOR 0x05 58 + #define ISL29003_REG_LSB_TIMER 0x06 59 + #define ISL29003_REG_MSB_TIMER 0x07 60 + 61 + #define ISL29003_NUM_CACHABLE_REGS 4 62 + 63 + struct isl29003_data { 64 + struct i2c_client *client; 65 + struct mutex lock; 66 + u8 reg_cache[ISL29003_NUM_CACHABLE_REGS]; 67 + }; 68 + 69 + static int gain_range[] = { 70 + 1000, 4000, 16000, 64000 71 + }; 72 + 73 + /* 74 + * register access helpers 75 + */ 76 + 77 + static int __isl29003_read_reg(struct i2c_client *client, 78 + u32 reg, u8 mask, u8 shift) 79 + { 80 + struct isl29003_data *data = i2c_get_clientdata(client); 81 + return (data->reg_cache[reg] & mask) >> shift; 82 + } 83 + 84 + static int __isl29003_write_reg(struct i2c_client *client, 85 + u32 reg, u8 mask, u8 shift, u8 val) 86 + { 87 + struct isl29003_data *data = i2c_get_clientdata(client); 88 + int ret = 0; 89 + u8 tmp; 90 + 91 + if (reg >= ISL29003_NUM_CACHABLE_REGS) 92 + return -EINVAL; 93 + 94 + mutex_lock(&data->lock); 95 + 96 + tmp = data->reg_cache[reg]; 97 + tmp &= ~mask; 98 + tmp |= val << shift; 99 + 100 + ret = i2c_smbus_write_byte_data(client, reg, tmp); 101 + if (!ret) 102 + data->reg_cache[reg] = tmp; 103 + 104 + mutex_unlock(&data->lock); 105 + return ret; 106 + } 107 + 108 + /* 109 + * internally used functions 110 + */ 111 + 112 + /* range */ 113 + static int isl29003_get_range(struct i2c_client *client) 114 + { 115 + return __isl29003_read_reg(client, ISL29003_REG_CONTROL, 116 + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT); 117 + } 118 + 119 + static int isl29003_set_range(struct i2c_client *client, int range) 120 + { 121 + return __isl29003_write_reg(client, ISL29003_REG_CONTROL, 122 + ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range); 123 + } 124 + 125 + /* resolution */ 126 + static int isl29003_get_resolution(struct i2c_client *client) 127 + { 128 + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, 129 + ISL29003_RES_MASK, ISL29003_RES_SHIFT); 130 + } 131 + 132 + static int isl29003_set_resolution(struct i2c_client *client, int res) 133 + { 134 + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 135 + ISL29003_RES_MASK, ISL29003_RES_SHIFT, res); 136 + } 137 + 138 + /* mode */ 139 + static int isl29003_get_mode(struct i2c_client *client) 140 + { 141 + return __isl29003_read_reg(client, ISL29003_REG_COMMAND, 142 + ISL29003_RES_MASK, ISL29003_RES_SHIFT); 143 + } 144 + 145 + static int isl29003_set_mode(struct i2c_client *client, int mode) 146 + { 147 + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 148 + ISL29003_RES_MASK, ISL29003_RES_SHIFT, mode); 149 + } 150 + 151 + /* power_state */ 152 + static int isl29003_set_power_state(struct i2c_client *client, int state) 153 + { 154 + return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 155 + ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0, 156 + state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD); 157 + } 158 + 159 + static int isl29003_get_power_state(struct i2c_client *client) 160 + { 161 + struct isl29003_data *data = i2c_get_clientdata(client); 162 + u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND]; 163 + return ~cmdreg & ISL29003_ADC_PD; 164 + } 165 + 166 + static int isl29003_get_adc_value(struct i2c_client *client) 167 + { 168 + struct isl29003_data *data = i2c_get_clientdata(client); 169 + int lsb, msb, range, bitdepth; 170 + 171 + mutex_lock(&data->lock); 172 + lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR); 173 + 174 + if (lsb < 0) { 175 + mutex_unlock(&data->lock); 176 + return lsb; 177 + } 178 + 179 + msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR); 180 + mutex_unlock(&data->lock); 181 + 182 + if (msb < 0) 183 + return msb; 184 + 185 + range = isl29003_get_range(client); 186 + bitdepth = (4 - isl29003_get_resolution(client)) * 4; 187 + return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth; 188 + } 189 + 190 + /* 191 + * sysfs layer 192 + */ 193 + 194 + /* range */ 195 + static ssize_t isl29003_show_range(struct device *dev, 196 + struct device_attribute *attr, char *buf) 197 + { 198 + struct i2c_client *client = to_i2c_client(dev); 199 + return sprintf(buf, "%i\n", isl29003_get_range(client)); 200 + } 201 + 202 + static ssize_t isl29003_store_range(struct device *dev, 203 + struct device_attribute *attr, 204 + const char *buf, size_t count) 205 + { 206 + struct i2c_client *client = to_i2c_client(dev); 207 + unsigned long val; 208 + int ret; 209 + 210 + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) 211 + return -EINVAL; 212 + 213 + ret = isl29003_set_range(client, val); 214 + if (ret < 0) 215 + return ret; 216 + 217 + return count; 218 + } 219 + 220 + static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, 221 + isl29003_show_range, isl29003_store_range); 222 + 223 + 224 + /* resolution */ 225 + static ssize_t isl29003_show_resolution(struct device *dev, 226 + struct device_attribute *attr, 227 + char *buf) 228 + { 229 + struct i2c_client *client = to_i2c_client(dev); 230 + return sprintf(buf, "%d\n", isl29003_get_resolution(client)); 231 + } 232 + 233 + static ssize_t isl29003_store_resolution(struct device *dev, 234 + struct device_attribute *attr, 235 + const char *buf, size_t count) 236 + { 237 + struct i2c_client *client = to_i2c_client(dev); 238 + unsigned long val; 239 + int ret; 240 + 241 + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 3)) 242 + return -EINVAL; 243 + 244 + ret = isl29003_set_resolution(client, val); 245 + if (ret < 0) 246 + return ret; 247 + 248 + return count; 249 + } 250 + 251 + static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, 252 + isl29003_show_resolution, isl29003_store_resolution); 253 + 254 + /* mode */ 255 + static ssize_t isl29003_show_mode(struct device *dev, 256 + struct device_attribute *attr, char *buf) 257 + { 258 + struct i2c_client *client = to_i2c_client(dev); 259 + return sprintf(buf, "%d\n", isl29003_get_mode(client)); 260 + } 261 + 262 + static ssize_t isl29003_store_mode(struct device *dev, 263 + struct device_attribute *attr, const char *buf, size_t count) 264 + { 265 + struct i2c_client *client = to_i2c_client(dev); 266 + unsigned long val; 267 + int ret; 268 + 269 + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 2)) 270 + return -EINVAL; 271 + 272 + ret = isl29003_set_mode(client, val); 273 + if (ret < 0) 274 + return ret; 275 + 276 + return count; 277 + } 278 + 279 + static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, 280 + isl29003_show_mode, isl29003_store_mode); 281 + 282 + 283 + /* power state */ 284 + static ssize_t isl29003_show_power_state(struct device *dev, 285 + struct device_attribute *attr, 286 + char *buf) 287 + { 288 + struct i2c_client *client = to_i2c_client(dev); 289 + return sprintf(buf, "%d\n", isl29003_get_power_state(client)); 290 + } 291 + 292 + static ssize_t isl29003_store_power_state(struct device *dev, 293 + struct device_attribute *attr, 294 + const char *buf, size_t count) 295 + { 296 + struct i2c_client *client = to_i2c_client(dev); 297 + unsigned long val; 298 + int ret; 299 + 300 + if ((strict_strtoul(buf, 10, &val) < 0) || (val > 1)) 301 + return -EINVAL; 302 + 303 + ret = isl29003_set_power_state(client, val); 304 + return ret ? ret : count; 305 + } 306 + 307 + static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, 308 + isl29003_show_power_state, isl29003_store_power_state); 309 + 310 + 311 + /* lux */ 312 + static ssize_t isl29003_show_lux(struct device *dev, 313 + struct device_attribute *attr, char *buf) 314 + { 315 + struct i2c_client *client = to_i2c_client(dev); 316 + 317 + /* No LUX data if not operational */ 318 + if (!isl29003_get_power_state(client)) 319 + return -EBUSY; 320 + 321 + return sprintf(buf, "%d\n", isl29003_get_adc_value(client)); 322 + } 323 + 324 + static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL); 325 + 326 + static struct attribute *isl29003_attributes[] = { 327 + &dev_attr_range.attr, 328 + &dev_attr_resolution.attr, 329 + &dev_attr_mode.attr, 330 + &dev_attr_power_state.attr, 331 + &dev_attr_lux.attr, 332 + NULL 333 + }; 334 + 335 + static const struct attribute_group isl29003_attr_group = { 336 + .attrs = isl29003_attributes, 337 + }; 338 + 339 + static int isl29003_init_client(struct i2c_client *client) 340 + { 341 + struct isl29003_data *data = i2c_get_clientdata(client); 342 + int i; 343 + 344 + /* read all the registers once to fill the cache. 345 + * if one of the reads fails, we consider the init failed */ 346 + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { 347 + int v = i2c_smbus_read_byte_data(client, i); 348 + if (v < 0) 349 + return -ENODEV; 350 + 351 + data->reg_cache[i] = v; 352 + } 353 + 354 + /* set defaults */ 355 + isl29003_set_range(client, 0); 356 + isl29003_set_resolution(client, 0); 357 + isl29003_set_mode(client, 0); 358 + isl29003_set_power_state(client, 0); 359 + 360 + return 0; 361 + } 362 + 363 + /* 364 + * I2C layer 365 + */ 366 + 367 + static int __devinit isl29003_probe(struct i2c_client *client, 368 + const struct i2c_device_id *id) 369 + { 370 + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); 371 + struct isl29003_data *data; 372 + int err = 0; 373 + 374 + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 375 + return -EIO; 376 + 377 + data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL); 378 + if (!data) 379 + return -ENOMEM; 380 + 381 + data->client = client; 382 + i2c_set_clientdata(client, data); 383 + mutex_init(&data->lock); 384 + 385 + /* initialize the ISL29003 chip */ 386 + err = isl29003_init_client(client); 387 + if (err) 388 + goto exit_kfree; 389 + 390 + /* register sysfs hooks */ 391 + err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group); 392 + if (err) 393 + goto exit_kfree; 394 + 395 + dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); 396 + return 0; 397 + 398 + exit_kfree: 399 + kfree(data); 400 + return err; 401 + } 402 + 403 + static int __devexit isl29003_remove(struct i2c_client *client) 404 + { 405 + sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group); 406 + isl29003_set_power_state(client, 0); 407 + kfree(i2c_get_clientdata(client)); 408 + return 0; 409 + } 410 + 411 + #ifdef CONFIG_PM 412 + static int isl29003_suspend(struct i2c_client *client, pm_message_t mesg) 413 + { 414 + return isl29003_set_power_state(client, 0); 415 + } 416 + 417 + static int isl29003_resume(struct i2c_client *client) 418 + { 419 + int i; 420 + struct isl29003_data *data = i2c_get_clientdata(client); 421 + 422 + /* restore registers from cache */ 423 + for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) 424 + if (!i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) 425 + return -EIO; 426 + 427 + return 0; 428 + } 429 + 430 + #else 431 + #define isl29003_suspend NULL 432 + #define isl29003_resume NULL 433 + #endif /* CONFIG_PM */ 434 + 435 + static const struct i2c_device_id isl29003_id[] = { 436 + { "isl29003", 0 }, 437 + {} 438 + }; 439 + MODULE_DEVICE_TABLE(i2c, isl29003_id); 440 + 441 + static struct i2c_driver isl29003_driver = { 442 + .driver = { 443 + .name = ISL29003_DRV_NAME, 444 + .owner = THIS_MODULE, 445 + }, 446 + .suspend = isl29003_suspend, 447 + .resume = isl29003_resume, 448 + .probe = isl29003_probe, 449 + .remove = __devexit_p(isl29003_remove), 450 + .id_table = isl29003_id, 451 + }; 452 + 453 + static int __init isl29003_init(void) 454 + { 455 + return i2c_add_driver(&isl29003_driver); 456 + } 457 + 458 + static void __exit isl29003_exit(void) 459 + { 460 + i2c_del_driver(&isl29003_driver); 461 + } 462 + 463 + MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); 464 + MODULE_DESCRIPTION("ISL29003 ambient light sensor driver"); 465 + MODULE_LICENSE("GPL v2"); 466 + MODULE_VERSION(DRIVER_VERSION); 467 + 468 + module_init(isl29003_init); 469 + module_exit(isl29003_exit); 470 +