473 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * isl29003.c - Linux kernel module for4 * Intersil ISL29003 ambient light sensor5 *6 * See file:Documentation/misc-devices/isl29003.rst7 *8 * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>9 *10 * Based on code written by11 * 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 0x0025#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 0x0134#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 0x0241#define ISL29003_REG_IRQ_THRESH_LO 0x0342#define ISL29003_REG_LSB_SENSOR 0x0443#define ISL29003_REG_MSB_SENSOR 0x0544#define ISL29003_REG_LSB_TIMER 0x0645#define ISL29003_REG_MSB_TIMER 0x0746 47#define ISL29003_NUM_CACHABLE_REGS 448 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, 6400058};59 60/*61 * register access helpers62 */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 functions98 */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 layer181 */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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 NULL342};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 layer375 */376 377static int isl29003_probe(struct i2c_client *client)378{379 struct i2c_adapter *adapter = client->adapter;380 struct isl29003_data *data;381 int err = 0;382 383 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))384 return -EIO;385 386 data = kzalloc(sizeof(struct isl29003_data), GFP_KERNEL);387 if (!data)388 return -ENOMEM;389 390 data->client = client;391 i2c_set_clientdata(client, data);392 mutex_init(&data->lock);393 394 /* initialize the ISL29003 chip */395 err = isl29003_init_client(client);396 if (err)397 goto exit_kfree;398 399 /* register sysfs hooks */400 err = sysfs_create_group(&client->dev.kobj, &isl29003_attr_group);401 if (err)402 goto exit_kfree;403 404 dev_info(&client->dev, "driver version %s enabled\n", DRIVER_VERSION);405 return 0;406 407exit_kfree:408 kfree(data);409 return err;410}411 412static void isl29003_remove(struct i2c_client *client)413{414 sysfs_remove_group(&client->dev.kobj, &isl29003_attr_group);415 isl29003_set_power_state(client, 0);416 kfree(i2c_get_clientdata(client));417}418 419#ifdef CONFIG_PM_SLEEP420static int isl29003_suspend(struct device *dev)421{422 struct i2c_client *client = to_i2c_client(dev);423 struct isl29003_data *data = i2c_get_clientdata(client);424 425 data->power_state_before_suspend = isl29003_get_power_state(client);426 return isl29003_set_power_state(client, 0);427}428 429static int isl29003_resume(struct device *dev)430{431 int i;432 struct i2c_client *client = to_i2c_client(dev);433 struct isl29003_data *data = i2c_get_clientdata(client);434 435 /* restore registers from cache */436 for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++)437 if (i2c_smbus_write_byte_data(client, i, data->reg_cache[i]))438 return -EIO;439 440 return isl29003_set_power_state(client,441 data->power_state_before_suspend);442}443 444static SIMPLE_DEV_PM_OPS(isl29003_pm_ops, isl29003_suspend, isl29003_resume);445#define ISL29003_PM_OPS (&isl29003_pm_ops)446 447#else448#define ISL29003_PM_OPS NULL449#endif /* CONFIG_PM_SLEEP */450 451static const struct i2c_device_id isl29003_id[] = {452 { "isl29003" },453 {}454};455MODULE_DEVICE_TABLE(i2c, isl29003_id);456 457static struct i2c_driver isl29003_driver = {458 .driver = {459 .name = ISL29003_DRV_NAME,460 .pm = ISL29003_PM_OPS,461 },462 .probe = isl29003_probe,463 .remove = isl29003_remove,464 .id_table = isl29003_id,465};466 467module_i2c_driver(isl29003_driver);468 469MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");470MODULE_DESCRIPTION("ISL29003 ambient light sensor driver");471MODULE_LICENSE("GPL v2");472MODULE_VERSION(DRIVER_VERSION);473