isl29003.c (11159B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * isl29003.c - Linux kernel module for 4 * Intersil ISL29003 ambient light sensor 5 * 6 * See file:Documentation/misc-devices/isl29003.rst 7 * 8 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> 9 * 10 * Based on code written by 11 * Rodolfo Giometti <giometti@linux.it> 12 * Eurotech S.p.A. <info@eurotech.it> 13 */ 14 15#include <linux/module.h> 16#include <linux/slab.h> 17#include <linux/i2c.h> 18#include <linux/mutex.h> 19#include <linux/delay.h> 20 21#define ISL29003_DRV_NAME "isl29003" 22#define DRIVER_VERSION "1.0" 23 24#define ISL29003_REG_COMMAND 0x00 25#define ISL29003_ADC_ENABLED (1 << 7) 26#define ISL29003_ADC_PD (1 << 6) 27#define ISL29003_TIMING_INT (1 << 5) 28#define ISL29003_MODE_SHIFT (2) 29#define ISL29003_MODE_MASK (0x3 << ISL29003_MODE_SHIFT) 30#define ISL29003_RES_SHIFT (0) 31#define ISL29003_RES_MASK (0x3 << ISL29003_RES_SHIFT) 32 33#define ISL29003_REG_CONTROL 0x01 34#define ISL29003_INT_FLG (1 << 5) 35#define ISL29003_RANGE_SHIFT (2) 36#define ISL29003_RANGE_MASK (0x3 << ISL29003_RANGE_SHIFT) 37#define ISL29003_INT_PERSISTS_SHIFT (0) 38#define ISL29003_INT_PERSISTS_MASK (0xf << ISL29003_INT_PERSISTS_SHIFT) 39 40#define ISL29003_REG_IRQ_THRESH_HI 0x02 41#define ISL29003_REG_IRQ_THRESH_LO 0x03 42#define ISL29003_REG_LSB_SENSOR 0x04 43#define ISL29003_REG_MSB_SENSOR 0x05 44#define ISL29003_REG_LSB_TIMER 0x06 45#define ISL29003_REG_MSB_TIMER 0x07 46 47#define ISL29003_NUM_CACHABLE_REGS 4 48 49struct isl29003_data { 50 struct i2c_client *client; 51 struct mutex lock; 52 u8 reg_cache[ISL29003_NUM_CACHABLE_REGS]; 53 u8 power_state_before_suspend; 54}; 55 56static int gain_range[] = { 57 1000, 4000, 16000, 64000 58}; 59 60/* 61 * register access helpers 62 */ 63 64static int __isl29003_read_reg(struct i2c_client *client, 65 u32 reg, u8 mask, u8 shift) 66{ 67 struct isl29003_data *data = i2c_get_clientdata(client); 68 69 return (data->reg_cache[reg] & mask) >> shift; 70} 71 72static int __isl29003_write_reg(struct i2c_client *client, 73 u32 reg, u8 mask, u8 shift, u8 val) 74{ 75 struct isl29003_data *data = i2c_get_clientdata(client); 76 int ret = 0; 77 u8 tmp; 78 79 if (reg >= ISL29003_NUM_CACHABLE_REGS) 80 return -EINVAL; 81 82 mutex_lock(&data->lock); 83 84 tmp = data->reg_cache[reg]; 85 tmp &= ~mask; 86 tmp |= val << shift; 87 88 ret = i2c_smbus_write_byte_data(client, reg, tmp); 89 if (!ret) 90 data->reg_cache[reg] = tmp; 91 92 mutex_unlock(&data->lock); 93 return ret; 94} 95 96/* 97 * internally used functions 98 */ 99 100/* range */ 101static int isl29003_get_range(struct i2c_client *client) 102{ 103 return __isl29003_read_reg(client, ISL29003_REG_CONTROL, 104 ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT); 105} 106 107static int isl29003_set_range(struct i2c_client *client, int range) 108{ 109 return __isl29003_write_reg(client, ISL29003_REG_CONTROL, 110 ISL29003_RANGE_MASK, ISL29003_RANGE_SHIFT, range); 111} 112 113/* resolution */ 114static int isl29003_get_resolution(struct i2c_client *client) 115{ 116 return __isl29003_read_reg(client, ISL29003_REG_COMMAND, 117 ISL29003_RES_MASK, ISL29003_RES_SHIFT); 118} 119 120static int isl29003_set_resolution(struct i2c_client *client, int res) 121{ 122 return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 123 ISL29003_RES_MASK, ISL29003_RES_SHIFT, res); 124} 125 126/* mode */ 127static int isl29003_get_mode(struct i2c_client *client) 128{ 129 return __isl29003_read_reg(client, ISL29003_REG_COMMAND, 130 ISL29003_MODE_MASK, ISL29003_MODE_SHIFT); 131} 132 133static int isl29003_set_mode(struct i2c_client *client, int mode) 134{ 135 return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 136 ISL29003_MODE_MASK, ISL29003_MODE_SHIFT, mode); 137} 138 139/* power_state */ 140static int isl29003_set_power_state(struct i2c_client *client, int state) 141{ 142 return __isl29003_write_reg(client, ISL29003_REG_COMMAND, 143 ISL29003_ADC_ENABLED | ISL29003_ADC_PD, 0, 144 state ? ISL29003_ADC_ENABLED : ISL29003_ADC_PD); 145} 146 147static int isl29003_get_power_state(struct i2c_client *client) 148{ 149 struct isl29003_data *data = i2c_get_clientdata(client); 150 u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND]; 151 152 return ~cmdreg & ISL29003_ADC_PD; 153} 154 155static int isl29003_get_adc_value(struct i2c_client *client) 156{ 157 struct isl29003_data *data = i2c_get_clientdata(client); 158 int lsb, msb, range, bitdepth; 159 160 mutex_lock(&data->lock); 161 lsb = i2c_smbus_read_byte_data(client, ISL29003_REG_LSB_SENSOR); 162 163 if (lsb < 0) { 164 mutex_unlock(&data->lock); 165 return lsb; 166 } 167 168 msb = i2c_smbus_read_byte_data(client, ISL29003_REG_MSB_SENSOR); 169 mutex_unlock(&data->lock); 170 171 if (msb < 0) 172 return msb; 173 174 range = isl29003_get_range(client); 175 bitdepth = (4 - isl29003_get_resolution(client)) * 4; 176 return (((msb << 8) | lsb) * gain_range[range]) >> bitdepth; 177} 178 179/* 180 * sysfs layer 181 */ 182 183/* range */ 184static ssize_t isl29003_show_range(struct device *dev, 185 struct device_attribute *attr, char *buf) 186{ 187 struct i2c_client *client = to_i2c_client(dev); 188 189 return sprintf(buf, "%i\n", isl29003_get_range(client)); 190} 191 192static ssize_t isl29003_store_range(struct device *dev, 193 struct device_attribute *attr, 194 const char *buf, size_t count) 195{ 196 struct i2c_client *client = to_i2c_client(dev); 197 unsigned long val; 198 int ret; 199 200 ret = kstrtoul(buf, 10, &val); 201 if (ret) 202 return ret; 203 204 if (val > 3) 205 return -EINVAL; 206 207 ret = isl29003_set_range(client, val); 208 if (ret < 0) 209 return ret; 210 211 return count; 212} 213 214static DEVICE_ATTR(range, S_IWUSR | S_IRUGO, 215 isl29003_show_range, isl29003_store_range); 216 217 218/* resolution */ 219static ssize_t isl29003_show_resolution(struct device *dev, 220 struct device_attribute *attr, 221 char *buf) 222{ 223 struct i2c_client *client = to_i2c_client(dev); 224 225 return sprintf(buf, "%d\n", isl29003_get_resolution(client)); 226} 227 228static ssize_t isl29003_store_resolution(struct device *dev, 229 struct device_attribute *attr, 230 const char *buf, size_t count) 231{ 232 struct i2c_client *client = to_i2c_client(dev); 233 unsigned long val; 234 int ret; 235 236 ret = kstrtoul(buf, 10, &val); 237 if (ret) 238 return ret; 239 240 if (val > 3) 241 return -EINVAL; 242 243 ret = isl29003_set_resolution(client, val); 244 if (ret < 0) 245 return ret; 246 247 return count; 248} 249 250static DEVICE_ATTR(resolution, S_IWUSR | S_IRUGO, 251 isl29003_show_resolution, isl29003_store_resolution); 252 253/* mode */ 254static ssize_t isl29003_show_mode(struct device *dev, 255 struct device_attribute *attr, char *buf) 256{ 257 struct i2c_client *client = to_i2c_client(dev); 258 259 return sprintf(buf, "%d\n", isl29003_get_mode(client)); 260} 261 262static 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 ret = kstrtoul(buf, 10, &val); 270 if (ret) 271 return ret; 272 273 if (val > 2) 274 return -EINVAL; 275 276 ret = isl29003_set_mode(client, val); 277 if (ret < 0) 278 return ret; 279 280 return count; 281} 282 283static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, 284 isl29003_show_mode, isl29003_store_mode); 285 286 287/* power state */ 288static ssize_t isl29003_show_power_state(struct device *dev, 289 struct device_attribute *attr, 290 char *buf) 291{ 292 struct i2c_client *client = to_i2c_client(dev); 293 294 return sprintf(buf, "%d\n", isl29003_get_power_state(client)); 295} 296 297static ssize_t isl29003_store_power_state(struct device *dev, 298 struct device_attribute *attr, 299 const char *buf, size_t count) 300{ 301 struct i2c_client *client = to_i2c_client(dev); 302 unsigned long val; 303 int ret; 304 305 ret = kstrtoul(buf, 10, &val); 306 if (ret) 307 return ret; 308 309 if (val > 1) 310 return -EINVAL; 311 312 ret = isl29003_set_power_state(client, val); 313 return ret ? ret : count; 314} 315 316static DEVICE_ATTR(power_state, S_IWUSR | S_IRUGO, 317 isl29003_show_power_state, isl29003_store_power_state); 318 319 320/* lux */ 321static ssize_t isl29003_show_lux(struct device *dev, 322 struct device_attribute *attr, char *buf) 323{ 324 struct i2c_client *client = to_i2c_client(dev); 325 326 /* No LUX data if not operational */ 327 if (!isl29003_get_power_state(client)) 328 return -EBUSY; 329 330 return sprintf(buf, "%d\n", isl29003_get_adc_value(client)); 331} 332 333static DEVICE_ATTR(lux, S_IRUGO, isl29003_show_lux, NULL); 334 335static struct attribute *isl29003_attributes[] = { 336 &dev_attr_range.attr, 337 &dev_attr_resolution.attr, 338 &dev_attr_mode.attr, 339 &dev_attr_power_state.attr, 340 &dev_attr_lux.attr, 341 NULL 342}; 343 344static const struct attribute_group isl29003_attr_group = { 345 .attrs = isl29003_attributes, 346}; 347 348static int isl29003_init_client(struct i2c_client *client) 349{ 350 struct isl29003_data *data = i2c_get_clientdata(client); 351 int i; 352 353 /* read all the registers once to fill the cache. 354 * if one of the reads fails, we consider the init failed */ 355 for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { 356 int v = i2c_smbus_read_byte_data(client, i); 357 358 if (v < 0) 359 return -ENODEV; 360 361 data->reg_cache[i] = v; 362 } 363 364 /* set defaults */ 365 isl29003_set_range(client, 0); 366 isl29003_set_resolution(client, 0); 367 isl29003_set_mode(client, 0); 368 isl29003_set_power_state(client, 0); 369 370 return 0; 371} 372 373/* 374 * I2C layer 375 */ 376 377static int isl29003_probe(struct i2c_client *client, 378 const struct i2c_device_id *id) 379{ 380 struct i2c_adapter *adapter = client->adapter; 381 struct isl29003_data *data; 382 int err = 0; 383 384 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) 385 return -EIO; 386 387 data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL); 388 if (!data) 389 return -ENOMEM; 390 391 data->client = client; 392 i2c_set_clientdata(client, data); 393 mutex_init(&data->lock); 394 395 /* initialize the ISL29003 chip */ 396 err = isl29003_init_client(client); 397 if (err) 398 goto exit_kfree; 399 400 /* register sysfs hooks */ 401 err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group); 402 if (err) 403 goto exit_kfree; 404 405 dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION); 406 return 0; 407 408exit_kfree: 409 kfree(data); 410 return err; 411} 412 413static int isl29003_remove(struct i2c_client *client) 414{ 415 sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group); 416 isl29003_set_power_state(client, 0); 417 kfree(i2c_get_clientdata(client)); 418 return 0; 419} 420 421#ifdef CONFIG_PM_SLEEP 422static int isl29003_suspend(struct device *dev) 423{ 424 struct i2c_client *client = to_i2c_client(dev); 425 struct isl29003_data *data = i2c_get_clientdata(client); 426 427 data->power_state_before_suspend = isl29003_get_power_state(client); 428 return isl29003_set_power_state(client, 0); 429} 430 431static int isl29003_resume(struct device *dev) 432{ 433 int i; 434 struct i2c_client *client = to_i2c_client(dev); 435 struct isl29003_data *data = i2c_get_clientdata(client); 436 437 /* restore registers from cache */ 438 for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) 439 if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i])) 440 return -EIO; 441 442 return isl29003_set_power_state(client, 443 data->power_state_before_suspend); 444} 445 446static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume); 447#define ISL29003_PM_OPS (&isl29003_pm_ops) 448 449#else 450#define ISL29003_PM_OPS NULL 451#endif /* CONFIG_PM_SLEEP */ 452 453static const struct i2c_device_id isl29003_id[] = { 454 { "isl29003", 0 }, 455 {} 456}; 457MODULE_DEVICE_TABLE(i2c, isl29003_id); 458 459static struct i2c_driver isl29003_driver = { 460 .driver = { 461 .name = ISL29003_DRV_NAME, 462 .pm = ISL29003_PM_OPS, 463 }, 464 .probe = isl29003_probe, 465 .remove = isl29003_remove, 466 .id_table = isl29003_id, 467}; 468 469module_i2c_driver(isl29003_driver); 470 471MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); 472MODULE_DESCRIPTION("ISL29003 ambient light sensor driver"); 473MODULE_LICENSE("GPL v2"); 474MODULE_VERSION(DRIVER_VERSION);