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

Configure Feed

Select the types of activity you want to include in your feed.

iio: light: isl76682: Add ISL76682 driver

The ISL76682 is very basic ALS which only supports ALS or IR mode
in four ranges, 1k/4k/16k/64k LUX. There is no IRQ support or any
other fancy functionality.

Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Reviewed-by: Matti Vaittinen <mazziesaccount@gmail.com>
Signed-off-by: Marek Vasut <marex@denx.de>
Link: https://lore.kernel.org/r/20231127212726.77707-2-marex@denx.de
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Marek Vasut and committed by
Jonathan Cameron
4347f511 8c82e9e3

+362
+15
drivers/iio/light/Kconfig
··· 252 252 To compile this driver as a module, choose M here: the module will be 253 253 called isl29125. 254 254 255 + config ISL76682 256 + tristate "Intersil ISL76682 Light Sensor" 257 + depends on I2C 258 + select REGMAP_I2C 259 + help 260 + Say Y here if you want to build a driver for the Intersil ISL76682 261 + Ambient Light Sensor and IR Intensity sensor. This driver provides 262 + the readouts via standard IIO sysfs and device interface. Both ALS 263 + illuminance and IR illuminance are provided raw with separate scale 264 + setting which can be configured via sysfs, the default scale is 1000 265 + lux, other options are 4000/16000/64000 lux. 266 + 267 + To compile this driver as a module, choose M here: the module will be 268 + called isl76682. 269 + 255 270 config HID_SENSOR_ALS 256 271 depends on HID_SENSOR_HUB 257 272 select IIO_BUFFER
+1
drivers/iio/light/Makefile
··· 28 28 obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o 29 29 obj-$(CONFIG_SENSORS_ISL29028) += isl29028.o 30 30 obj-$(CONFIG_ISL29125) += isl29125.o 31 + obj-$(CONFIG_ISL76682) += isl76682.o 31 32 obj-$(CONFIG_JSA1212) += jsa1212.o 32 33 obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o 33 34 obj-$(CONFIG_LTR501) += ltr501.o
+346
drivers/iio/light/isl76682.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * IIO driver for the light sensor ISL76682. 4 + * ISL76682 is Ambient Light Sensor 5 + * 6 + * Copyright (c) 2023 Marek Vasut <marex@denx.de> 7 + */ 8 + 9 + #include <linux/array_size.h> 10 + #include <linux/bits.h> 11 + #include <linux/cleanup.h> 12 + #include <linux/delay.h> 13 + #include <linux/err.h> 14 + #include <linux/i2c.h> 15 + #include <linux/module.h> 16 + #include <linux/mutex.h> 17 + #include <linux/regmap.h> 18 + #include <linux/types.h> 19 + 20 + #include <linux/iio/iio.h> 21 + 22 + #define ISL76682_REG_COMMAND 0x00 23 + 24 + #define ISL76682_COMMAND_EN BIT(7) 25 + #define ISL76682_COMMAND_MODE_CONTINUOUS BIT(6) 26 + #define ISL76682_COMMAND_LIGHT_IR BIT(5) 27 + 28 + #define ISL76682_COMMAND_RANGE_LUX_1K 0x0 29 + #define ISL76682_COMMAND_RANGE_LUX_4K 0x1 30 + #define ISL76682_COMMAND_RANGE_LUX_16K 0x2 31 + #define ISL76682_COMMAND_RANGE_LUX_64K 0x3 32 + #define ISL76682_COMMAND_RANGE_LUX_MASK GENMASK(1, 0) 33 + 34 + #define ISL76682_REG_ALSIR_L 0x01 35 + 36 + #define ISL76682_REG_ALSIR_U 0x02 37 + 38 + #define ISL76682_NUM_REGS (ISL76682_REG_ALSIR_U + 1) 39 + 40 + #define ISL76682_CONV_TIME_MS 100 41 + #define ISL76682_INT_TIME_US 90000 42 + 43 + #define ISL76682_ADC_MAX (BIT(16) - 1) 44 + 45 + struct isl76682_chip { 46 + /* 47 + * Lock to synchronize access to device command register 48 + * and the content of range variable below. 49 + */ 50 + struct mutex lock; 51 + struct regmap *regmap; 52 + u8 range; 53 + u8 command; 54 + }; 55 + 56 + struct isl76682_range { 57 + u8 range; 58 + u32 als; 59 + u32 ir; 60 + }; 61 + 62 + static struct isl76682_range isl76682_range_table[] = { 63 + { ISL76682_COMMAND_RANGE_LUX_1K, 15000, 10500 }, 64 + { ISL76682_COMMAND_RANGE_LUX_4K, 60000, 42000 }, 65 + { ISL76682_COMMAND_RANGE_LUX_16K, 240000, 168000 }, 66 + { ISL76682_COMMAND_RANGE_LUX_64K, 960000, 673000 } 67 + }; 68 + 69 + static int isl76682_get(struct isl76682_chip *chip, bool mode_ir, int *data) 70 + { 71 + u8 command; 72 + int ret; 73 + 74 + command = ISL76682_COMMAND_EN | ISL76682_COMMAND_MODE_CONTINUOUS | 75 + chip->range; 76 + 77 + if (mode_ir) 78 + command |= ISL76682_COMMAND_LIGHT_IR; 79 + 80 + if (command != chip->command) { 81 + ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, command); 82 + if (ret) 83 + return ret; 84 + 85 + /* Need to wait for conversion time if ALS/IR mode enabled */ 86 + msleep(ISL76682_CONV_TIME_MS); 87 + 88 + chip->command = command; 89 + } 90 + 91 + ret = regmap_bulk_read(chip->regmap, ISL76682_REG_ALSIR_L, data, 2); 92 + *data &= ISL76682_ADC_MAX; 93 + return ret; 94 + } 95 + 96 + static int isl76682_write_raw(struct iio_dev *indio_dev, 97 + struct iio_chan_spec const *chan, 98 + int val, int val2, long mask) 99 + { 100 + struct isl76682_chip *chip = iio_priv(indio_dev); 101 + int i; 102 + 103 + if (mask != IIO_CHAN_INFO_SCALE) 104 + return -EINVAL; 105 + 106 + if (val != 0) 107 + return -EINVAL; 108 + 109 + for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) { 110 + if (chan->type == IIO_LIGHT && val2 != isl76682_range_table[i].als) 111 + continue; 112 + if (chan->type == IIO_INTENSITY && val2 != isl76682_range_table[i].ir) 113 + continue; 114 + 115 + scoped_guard(mutex, &chip->lock) 116 + chip->range = isl76682_range_table[i].range; 117 + return 0; 118 + } 119 + 120 + return -EINVAL; 121 + } 122 + 123 + static int isl76682_read_raw(struct iio_dev *indio_dev, 124 + struct iio_chan_spec const *chan, 125 + int *val, int *val2, long mask) 126 + { 127 + struct isl76682_chip *chip = iio_priv(indio_dev); 128 + int ret; 129 + int i; 130 + 131 + guard(mutex)(&chip->lock); 132 + 133 + switch (mask) { 134 + case IIO_CHAN_INFO_RAW: 135 + switch (chan->type) { 136 + case IIO_LIGHT: 137 + ret = isl76682_get(chip, false, val); 138 + return (ret < 0) ? ret : IIO_VAL_INT; 139 + case IIO_INTENSITY: 140 + ret = isl76682_get(chip, true, val); 141 + return (ret < 0) ? ret : IIO_VAL_INT; 142 + default: 143 + return -EINVAL; 144 + } 145 + return -EINVAL; 146 + case IIO_CHAN_INFO_SCALE: 147 + for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) { 148 + if (chip->range != isl76682_range_table[i].range) 149 + continue; 150 + 151 + *val = 0; 152 + switch (chan->type) { 153 + case IIO_LIGHT: 154 + *val2 = isl76682_range_table[i].als; 155 + return IIO_VAL_INT_PLUS_MICRO; 156 + case IIO_INTENSITY: 157 + *val2 = isl76682_range_table[i].ir; 158 + return IIO_VAL_INT_PLUS_MICRO; 159 + default: 160 + return -EINVAL; 161 + } 162 + } 163 + return -EINVAL; 164 + case IIO_CHAN_INFO_INT_TIME: 165 + *val = 0; 166 + *val2 = ISL76682_INT_TIME_US; 167 + return IIO_VAL_INT_PLUS_MICRO; 168 + default: 169 + return -EINVAL; 170 + } 171 + } 172 + 173 + static int illuminance_scale_available[] = { 174 + 0, 15000, 175 + 0, 60000, 176 + 0, 240000, 177 + 0, 960000, 178 + }; 179 + 180 + static int intensity_scale_available[] = { 181 + 0, 10500, 182 + 0, 42000, 183 + 0, 168000, 184 + 0, 673000, 185 + }; 186 + 187 + static int isl76682_read_avail(struct iio_dev *indio_dev, 188 + struct iio_chan_spec const *chan, 189 + const int **vals, int *type, 190 + int *length, long mask) 191 + { 192 + switch (mask) { 193 + case IIO_CHAN_INFO_SCALE: 194 + switch (chan->type) { 195 + case IIO_LIGHT: 196 + *vals = illuminance_scale_available; 197 + *length = ARRAY_SIZE(illuminance_scale_available); 198 + *type = IIO_VAL_INT_PLUS_MICRO; 199 + return IIO_AVAIL_LIST; 200 + case IIO_INTENSITY: 201 + *vals = intensity_scale_available; 202 + *length = ARRAY_SIZE(intensity_scale_available); 203 + *type = IIO_VAL_INT_PLUS_MICRO; 204 + return IIO_AVAIL_LIST; 205 + default: 206 + return -EINVAL; 207 + } 208 + default: 209 + return -EINVAL; 210 + } 211 + } 212 + 213 + static const struct iio_chan_spec isl76682_channels[] = { 214 + { 215 + .type = IIO_LIGHT, 216 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 217 + BIT(IIO_CHAN_INFO_SCALE), 218 + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), 219 + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 220 + }, { 221 + .type = IIO_INTENSITY, 222 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 223 + BIT(IIO_CHAN_INFO_SCALE), 224 + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), 225 + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME), 226 + } 227 + }; 228 + 229 + static const struct iio_info isl76682_info = { 230 + .read_avail = isl76682_read_avail, 231 + .read_raw = isl76682_read_raw, 232 + .write_raw = isl76682_write_raw, 233 + }; 234 + 235 + static int isl76682_clear_configure_reg(struct isl76682_chip *chip) 236 + { 237 + struct device *dev = regmap_get_device(chip->regmap); 238 + int ret; 239 + 240 + ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, 0x0); 241 + if (ret < 0) 242 + dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret); 243 + 244 + /* 245 + * In the success case, the command register was zeroed out. 246 + * 247 + * In the error case, we do not know in which state the command 248 + * register is, so we assume it is zeroed out, so that it would 249 + * be reprogrammed at the next data read out, and at that time 250 + * we hope it would be reprogrammed successfully. That is very 251 + * much a best effort approach. 252 + */ 253 + chip->command = 0; 254 + 255 + return ret; 256 + } 257 + 258 + static void isl76682_reset_action(void *chip) 259 + { 260 + isl76682_clear_configure_reg(chip); 261 + } 262 + 263 + static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg) 264 + { 265 + switch (reg) { 266 + case ISL76682_REG_ALSIR_L: 267 + case ISL76682_REG_ALSIR_U: 268 + return true; 269 + default: 270 + return false; 271 + } 272 + } 273 + 274 + static const struct regmap_config isl76682_regmap_config = { 275 + .reg_bits = 8, 276 + .val_bits = 8, 277 + .volatile_reg = isl76682_is_volatile_reg, 278 + .max_register = ISL76682_NUM_REGS - 1, 279 + .num_reg_defaults_raw = ISL76682_NUM_REGS, 280 + .cache_type = REGCACHE_FLAT, 281 + }; 282 + 283 + static int isl76682_probe(struct i2c_client *client) 284 + { 285 + struct device *dev = &client->dev; 286 + struct isl76682_chip *chip; 287 + struct iio_dev *indio_dev; 288 + int ret; 289 + 290 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); 291 + if (!indio_dev) 292 + return -ENOMEM; 293 + 294 + chip = iio_priv(indio_dev); 295 + 296 + mutex_init(&chip->lock); 297 + 298 + chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config); 299 + ret = PTR_ERR_OR_ZERO(chip->regmap); 300 + if (ret) 301 + return dev_err_probe(dev, ret, "Error initializing regmap\n"); 302 + 303 + chip->range = ISL76682_COMMAND_RANGE_LUX_1K; 304 + 305 + ret = isl76682_clear_configure_reg(chip); 306 + if (ret < 0) 307 + return ret; 308 + 309 + ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip); 310 + if (ret) 311 + return ret; 312 + 313 + indio_dev->info = &isl76682_info; 314 + indio_dev->channels = isl76682_channels; 315 + indio_dev->num_channels = ARRAY_SIZE(isl76682_channels); 316 + indio_dev->name = "isl76682"; 317 + indio_dev->modes = INDIO_DIRECT_MODE; 318 + 319 + return devm_iio_device_register(dev, indio_dev); 320 + } 321 + 322 + static const struct i2c_device_id isl76682_id[] = { 323 + { "isl76682" }, 324 + { } 325 + }; 326 + MODULE_DEVICE_TABLE(i2c, isl76682_id); 327 + 328 + static const struct of_device_id isl76682_of_match[] = { 329 + { .compatible = "isil,isl76682" }, 330 + { } 331 + }; 332 + MODULE_DEVICE_TABLE(of, isl76682_of_match); 333 + 334 + static struct i2c_driver isl76682_driver = { 335 + .driver = { 336 + .name = "isl76682", 337 + .of_match_table = isl76682_of_match, 338 + }, 339 + .probe = isl76682_probe, 340 + .id_table = isl76682_id, 341 + }; 342 + module_i2c_driver(isl76682_driver); 343 + 344 + MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver"); 345 + MODULE_LICENSE("GPL"); 346 + MODULE_AUTHOR("Marek Vasut <marex@denx.de>");