346 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IIO driver for the light sensor ISL76682.4 * ISL76682 is Ambient Light Sensor5 *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 0x0023 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 0x029#define ISL76682_COMMAND_RANGE_LUX_4K 0x130#define ISL76682_COMMAND_RANGE_LUX_16K 0x231#define ISL76682_COMMAND_RANGE_LUX_64K 0x332#define ISL76682_COMMAND_RANGE_LUX_MASK GENMASK(1, 0)33 34#define ISL76682_REG_ALSIR_L 0x0135 36#define ISL76682_REG_ALSIR_U 0x0237 38#define ISL76682_NUM_REGS (ISL76682_REG_ALSIR_U + 1)39 40#define ISL76682_CONV_TIME_MS 10041#define ISL76682_INT_TIME_US 9000042 43#define ISL76682_ADC_MAX (BIT(16) - 1)44 45struct isl76682_chip {46 /*47 * Lock to synchronize access to device command register48 * and the content of range variable below.49 */50 struct mutex lock;51 struct regmap *regmap;52 u8 range;53 u8 command;54};55 56struct isl76682_range {57 u8 range;58 u32 als;59 u32 ir;60};61 62static 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 69static 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 96static 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 123static 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 case IIO_CHAN_INFO_SCALE:146 for (i = 0; i < ARRAY_SIZE(isl76682_range_table); i++) {147 if (chip->range != isl76682_range_table[i].range)148 continue;149 150 *val = 0;151 switch (chan->type) {152 case IIO_LIGHT:153 *val2 = isl76682_range_table[i].als;154 return IIO_VAL_INT_PLUS_MICRO;155 case IIO_INTENSITY:156 *val2 = isl76682_range_table[i].ir;157 return IIO_VAL_INT_PLUS_MICRO;158 default:159 return -EINVAL;160 }161 }162 return -EINVAL;163 case IIO_CHAN_INFO_INT_TIME:164 *val = 0;165 *val2 = ISL76682_INT_TIME_US;166 return IIO_VAL_INT_PLUS_MICRO;167 default:168 return -EINVAL;169 }170}171 172static int illuminance_scale_available[] = {173 0, 15000,174 0, 60000,175 0, 240000,176 0, 960000,177};178 179static int intensity_scale_available[] = {180 0, 10500,181 0, 42000,182 0, 168000,183 0, 673000,184};185 186static int isl76682_read_avail(struct iio_dev *indio_dev,187 struct iio_chan_spec const *chan,188 const int **vals, int *type,189 int *length, long mask)190{191 switch (mask) {192 case IIO_CHAN_INFO_SCALE:193 switch (chan->type) {194 case IIO_LIGHT:195 *vals = illuminance_scale_available;196 *length = ARRAY_SIZE(illuminance_scale_available);197 *type = IIO_VAL_INT_PLUS_MICRO;198 return IIO_AVAIL_LIST;199 case IIO_INTENSITY:200 *vals = intensity_scale_available;201 *length = ARRAY_SIZE(intensity_scale_available);202 *type = IIO_VAL_INT_PLUS_MICRO;203 return IIO_AVAIL_LIST;204 default:205 return -EINVAL;206 }207 default:208 return -EINVAL;209 }210}211 212static const struct iio_chan_spec isl76682_channels[] = {213 {214 .type = IIO_LIGHT,215 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |216 BIT(IIO_CHAN_INFO_SCALE),217 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),218 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),219 }, {220 .type = IIO_INTENSITY,221 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |222 BIT(IIO_CHAN_INFO_SCALE),223 .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE),224 .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),225 }226};227 228static const struct iio_info isl76682_info = {229 .read_avail = isl76682_read_avail,230 .read_raw = isl76682_read_raw,231 .write_raw = isl76682_write_raw,232};233 234static int isl76682_clear_configure_reg(struct isl76682_chip *chip)235{236 struct device *dev = regmap_get_device(chip->regmap);237 int ret;238 239 ret = regmap_write(chip->regmap, ISL76682_REG_COMMAND, 0x0);240 if (ret < 0)241 dev_err(dev, "Error %d clearing the CONFIGURE register\n", ret);242 243 /*244 * In the success case, the command register was zeroed out.245 *246 * In the error case, we do not know in which state the command247 * register is, so we assume it is zeroed out, so that it would248 * be reprogrammed at the next data read out, and at that time249 * we hope it would be reprogrammed successfully. That is very250 * much a best effort approach.251 */252 chip->command = 0;253 254 return ret;255}256 257static void isl76682_reset_action(void *chip)258{259 isl76682_clear_configure_reg(chip);260}261 262static bool isl76682_is_volatile_reg(struct device *dev, unsigned int reg)263{264 switch (reg) {265 case ISL76682_REG_ALSIR_L:266 case ISL76682_REG_ALSIR_U:267 return true;268 default:269 return false;270 }271}272 273static const struct regmap_config isl76682_regmap_config = {274 .reg_bits = 8,275 .val_bits = 8,276 .volatile_reg = isl76682_is_volatile_reg,277 .max_register = ISL76682_NUM_REGS - 1,278 .num_reg_defaults_raw = ISL76682_NUM_REGS,279 .cache_type = REGCACHE_FLAT,280};281 282static int isl76682_probe(struct i2c_client *client)283{284 struct device *dev = &client->dev;285 struct isl76682_chip *chip;286 struct iio_dev *indio_dev;287 int ret;288 289 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));290 if (!indio_dev)291 return -ENOMEM;292 293 chip = iio_priv(indio_dev);294 295 mutex_init(&chip->lock);296 297 chip->regmap = devm_regmap_init_i2c(client, &isl76682_regmap_config);298 ret = PTR_ERR_OR_ZERO(chip->regmap);299 if (ret)300 return dev_err_probe(dev, ret, "Error initializing regmap\n");301 302 chip->range = ISL76682_COMMAND_RANGE_LUX_1K;303 304 ret = isl76682_clear_configure_reg(chip);305 if (ret < 0)306 return ret;307 308 ret = devm_add_action_or_reset(dev, isl76682_reset_action, chip);309 if (ret)310 return ret;311 312 indio_dev->info = &isl76682_info;313 indio_dev->channels = isl76682_channels;314 indio_dev->num_channels = ARRAY_SIZE(isl76682_channels);315 indio_dev->name = "isl76682";316 indio_dev->modes = INDIO_DIRECT_MODE;317 318 return devm_iio_device_register(dev, indio_dev);319}320 321static const struct i2c_device_id isl76682_id[] = {322 { "isl76682" },323 { }324};325MODULE_DEVICE_TABLE(i2c, isl76682_id);326 327static const struct of_device_id isl76682_of_match[] = {328 { .compatible = "isil,isl76682" },329 { }330};331MODULE_DEVICE_TABLE(of, isl76682_of_match);332 333static struct i2c_driver isl76682_driver = {334 .driver = {335 .name = "isl76682",336 .of_match_table = isl76682_of_match,337 },338 .probe = isl76682_probe,339 .id_table = isl76682_id,340};341module_i2c_driver(isl76682_driver);342 343MODULE_DESCRIPTION("ISL76682 Ambient Light Sensor driver");344MODULE_LICENSE("GPL");345MODULE_AUTHOR("Marek Vasut <marex@denx.de>");346