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

iio: light: add driver for MAX44009

The MAX44009 is a low-power ambient light sensor from Maxim
Integrated. It differs from the MAX44000 in that it doesn't have
proximity sensing and that it requires far less current (1 micro-amp
vs 5 micro-amps). The register mapping and feature set between the
two are different enough to require a new driver for the MAX44009.

Developed and tested with a BeagleBone Black and UDOO Neo (i.MX6SX)

Supported features:

* Reading lux (processed value)

* Rising and falling illuminance threshold
events

* Configuring integration time

https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf

Signed-off-by: Robert Eshleman <bobbyeshleman@gmail.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Robert Eshleman and committed by
Jonathan Cameron
6aef699a b64d9730

+566
+10
drivers/iio/light/Kconfig
··· 299 299 To compile this driver as a module, choose M here: 300 300 the module will be called max44000. 301 301 302 + config MAX44009 303 + tristate "MAX44009 Ambient Light Sensor" 304 + depends on I2C 305 + help 306 + Say Y here if you want to build support for Maxim Integrated's 307 + MAX44009 ambient light sensor device. 308 + 309 + To compile this driver as a module, choose M here: 310 + the module will be called max44009. 311 + 302 312 config OPT3001 303 313 tristate "Texas Instruments OPT3001 Light Sensor" 304 314 depends on I2C
+1
drivers/iio/light/Makefile
··· 28 28 obj-$(CONFIG_LTR501) += ltr501.o 29 29 obj-$(CONFIG_LV0104CS) += lv0104cs.o 30 30 obj-$(CONFIG_MAX44000) += max44000.o 31 + obj-$(CONFIG_MAX44009) += max44009.o 31 32 obj-$(CONFIG_OPT3001) += opt3001.o 32 33 obj-$(CONFIG_PA12203001) += pa12203001.o 33 34 obj-$(CONFIG_RPR0521) += rpr0521.o
+555
drivers/iio/light/max44009.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * max44009.c - Support for MAX44009 Ambient Light Sensor 4 + * 5 + * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com> 6 + * 7 + * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf 8 + * 9 + * TODO: Support continuous mode and configuring from manual mode to 10 + * automatic mode. 11 + * 12 + * Default I2C address: 0x4a 13 + */ 14 + 15 + #include <linux/init.h> 16 + #include <linux/kernel.h> 17 + #include <linux/bits.h> 18 + #include <linux/i2c.h> 19 + #include <linux/iio/events.h> 20 + #include <linux/iio/iio.h> 21 + #include <linux/iio/sysfs.h> 22 + #include <linux/interrupt.h> 23 + #include <linux/module.h> 24 + #include <linux/util_macros.h> 25 + 26 + #define MAX44009_DRV_NAME "max44009" 27 + 28 + /* Registers in datasheet order */ 29 + #define MAX44009_REG_INT_STATUS 0x0 30 + #define MAX44009_REG_INT_EN 0x1 31 + #define MAX44009_REG_CFG 0x2 32 + #define MAX44009_REG_LUX_HI 0x3 33 + #define MAX44009_REG_LUX_LO 0x4 34 + #define MAX44009_REG_UPPER_THR 0x5 35 + #define MAX44009_REG_LOWER_THR 0x6 36 + #define MAX44009_REG_THR_TIMER 0x7 37 + 38 + #define MAX44009_CFG_TIM_MASK GENMASK(2, 0) 39 + #define MAX44009_CFG_MAN_MODE_MASK BIT(6) 40 + 41 + /* The maximum rising threshold for the max44009 */ 42 + #define MAX44009_MAXIMUM_THRESHOLD 7520256 43 + 44 + #define MAX44009_THRESH_EXP_MASK (0xf << 4) 45 + #define MAX44009_THRESH_EXP_RSHIFT 4 46 + #define MAX44009_THRESH_MANT_LSHIFT 4 47 + #define MAX44009_THRESH_MANT_MASK 0xf 48 + 49 + #define MAX44009_UPPER_THR_MINIMUM 15 50 + 51 + /* The max44009 always scales raw readings by 0.045 and is non-configurable */ 52 + #define MAX44009_SCALE_NUMERATOR 45 53 + #define MAX44009_SCALE_DENOMINATOR 1000 54 + 55 + /* The fixed-point fractional multiplier for de-scaling threshold values */ 56 + #define MAX44009_FRACT_MULT 1000000 57 + 58 + static const u32 max44009_int_time_ns_array[] = { 59 + 800000000, 60 + 400000000, 61 + 200000000, 62 + 100000000, 63 + 50000000, /* Manual mode only */ 64 + 25000000, /* Manual mode only */ 65 + 12500000, /* Manual mode only */ 66 + 6250000, /* Manual mode only */ 67 + }; 68 + 69 + static const char max44009_int_time_str[] = 70 + "0.8 " 71 + "0.4 " 72 + "0.2 " 73 + "0.1 " 74 + "0.05 " 75 + "0.025 " 76 + "0.0125 " 77 + "0.00625"; 78 + 79 + struct max44009_data { 80 + struct i2c_client *client; 81 + struct mutex lock; 82 + }; 83 + 84 + static const struct iio_event_spec max44009_event_spec[] = { 85 + { 86 + .type = IIO_EV_TYPE_THRESH, 87 + .dir = IIO_EV_DIR_RISING, 88 + .mask_separate = BIT(IIO_EV_INFO_VALUE) | 89 + BIT(IIO_EV_INFO_ENABLE), 90 + }, 91 + { 92 + .type = IIO_EV_TYPE_THRESH, 93 + .dir = IIO_EV_DIR_FALLING, 94 + .mask_separate = BIT(IIO_EV_INFO_VALUE) | 95 + BIT(IIO_EV_INFO_ENABLE), 96 + }, 97 + }; 98 + 99 + static const struct iio_chan_spec max44009_channels[] = { 100 + { 101 + .type = IIO_LIGHT, 102 + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | 103 + BIT(IIO_CHAN_INFO_INT_TIME), 104 + .event_spec = max44009_event_spec, 105 + .num_event_specs = ARRAY_SIZE(max44009_event_spec), 106 + }, 107 + }; 108 + 109 + static int max44009_read_int_time(struct max44009_data *data) 110 + { 111 + 112 + int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG); 113 + 114 + if (ret < 0) 115 + return ret; 116 + 117 + return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK]; 118 + } 119 + 120 + static int max44009_write_int_time(struct max44009_data *data, 121 + int val, int val2) 122 + { 123 + struct i2c_client *client = data->client; 124 + int ret, int_time, config; 125 + s64 ns; 126 + 127 + ns = val * NSEC_PER_SEC + val2; 128 + int_time = find_closest_descending( 129 + ns, 130 + max44009_int_time_ns_array, 131 + ARRAY_SIZE(max44009_int_time_ns_array)); 132 + 133 + ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG); 134 + if (ret < 0) 135 + return ret; 136 + 137 + config = ret; 138 + config &= int_time; 139 + 140 + /* 141 + * To set the integration time, the device must also be in manual 142 + * mode. 143 + */ 144 + config |= MAX44009_CFG_MAN_MODE_MASK; 145 + 146 + return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config); 147 + } 148 + 149 + static int max44009_write_raw(struct iio_dev *indio_dev, 150 + struct iio_chan_spec const *chan, int val, 151 + int val2, long mask) 152 + { 153 + struct max44009_data *data = iio_priv(indio_dev); 154 + int ret; 155 + 156 + if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) { 157 + mutex_lock(&data->lock); 158 + ret = max44009_write_int_time(data, val, val2); 159 + mutex_unlock(&data->lock); 160 + return ret; 161 + } 162 + return -EINVAL; 163 + } 164 + 165 + static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev, 166 + struct iio_chan_spec const *chan, 167 + long mask) 168 + { 169 + return IIO_VAL_INT_PLUS_NANO; 170 + } 171 + 172 + static int max44009_lux_raw(u8 hi, u8 lo) 173 + { 174 + int mantissa; 175 + int exponent; 176 + 177 + /* 178 + * The mantissa consists of the low nibble of the Lux High Byte 179 + * and the low nibble of the Lux Low Byte. 180 + */ 181 + mantissa = ((hi & 0xf) << 4) | (lo & 0xf); 182 + 183 + /* The exponent byte is just the upper nibble of the Lux High Byte */ 184 + exponent = (hi >> 4) & 0xf; 185 + 186 + /* 187 + * The exponent value is base 2 to the power of the raw exponent byte. 188 + */ 189 + exponent = 1 << exponent; 190 + 191 + return exponent * mantissa; 192 + } 193 + 194 + #define MAX44009_READ_LUX_XFER_LEN (4) 195 + 196 + static int max44009_read_lux_raw(struct max44009_data *data) 197 + { 198 + int ret; 199 + u8 hireg = MAX44009_REG_LUX_HI; 200 + u8 loreg = MAX44009_REG_LUX_LO; 201 + u8 lo = 0; 202 + u8 hi = 0; 203 + 204 + struct i2c_msg msgs[] = { 205 + { 206 + .addr = data->client->addr, 207 + .flags = 0, 208 + .len = sizeof(hireg), 209 + .buf = &hireg, 210 + }, 211 + { 212 + .addr = data->client->addr, 213 + .flags = I2C_M_RD, 214 + .len = sizeof(hi), 215 + .buf = &hi, 216 + }, 217 + { 218 + .addr = data->client->addr, 219 + .flags = 0, 220 + .len = sizeof(loreg), 221 + .buf = &loreg, 222 + }, 223 + { 224 + .addr = data->client->addr, 225 + .flags = I2C_M_RD, 226 + .len = sizeof(lo), 227 + .buf = &lo, 228 + } 229 + }; 230 + 231 + /* 232 + * Use i2c_transfer instead of smbus read because i2c_transfer 233 + * does NOT use a stop bit between address write and data read. 234 + * Using a stop bit causes disjoint upper/lower byte reads and 235 + * reduces accuracy. 236 + */ 237 + ret = i2c_transfer(data->client->adapter, 238 + msgs, MAX44009_READ_LUX_XFER_LEN); 239 + 240 + if (ret != MAX44009_READ_LUX_XFER_LEN) 241 + return -EIO; 242 + 243 + return max44009_lux_raw(hi, lo); 244 + } 245 + 246 + static int max44009_read_raw(struct iio_dev *indio_dev, 247 + struct iio_chan_spec const *chan, int *val, 248 + int *val2, long mask) 249 + { 250 + struct max44009_data *data = iio_priv(indio_dev); 251 + int lux_raw; 252 + int ret; 253 + 254 + switch (mask) { 255 + case IIO_CHAN_INFO_PROCESSED: 256 + switch (chan->type) { 257 + case IIO_LIGHT: 258 + ret = max44009_read_lux_raw(data); 259 + if (ret < 0) 260 + return ret; 261 + lux_raw = ret; 262 + 263 + *val = lux_raw * MAX44009_SCALE_NUMERATOR; 264 + *val2 = MAX44009_SCALE_DENOMINATOR; 265 + return IIO_VAL_FRACTIONAL; 266 + default: 267 + return -EINVAL; 268 + } 269 + case IIO_CHAN_INFO_INT_TIME: 270 + switch (chan->type) { 271 + case IIO_LIGHT: 272 + ret = max44009_read_int_time(data); 273 + if (ret < 0) 274 + return ret; 275 + 276 + *val2 = ret; 277 + *val = 0; 278 + return IIO_VAL_INT_PLUS_NANO; 279 + default: 280 + return -EINVAL; 281 + } 282 + default: 283 + return -EINVAL; 284 + } 285 + } 286 + 287 + static IIO_CONST_ATTR(illuminance_integration_time_available, 288 + max44009_int_time_str); 289 + 290 + static struct attribute *max44009_attributes[] = { 291 + &iio_const_attr_illuminance_integration_time_available.dev_attr.attr, 292 + NULL, 293 + }; 294 + 295 + static const struct attribute_group max44009_attribute_group = { 296 + .attrs = max44009_attributes, 297 + }; 298 + 299 + static int max44009_threshold_byte_from_fraction(int integral, int fractional) 300 + { 301 + int mantissa, exp; 302 + 303 + if ((integral <= 0 && fractional <= 0) || 304 + integral > MAX44009_MAXIMUM_THRESHOLD || 305 + (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0)) 306 + return -EINVAL; 307 + 308 + /* Reverse scaling of fixed-point integral */ 309 + mantissa = integral * MAX44009_SCALE_DENOMINATOR; 310 + mantissa /= MAX44009_SCALE_NUMERATOR; 311 + 312 + /* Reverse scaling of fixed-point fractional */ 313 + mantissa += fractional / MAX44009_FRACT_MULT * 314 + (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR); 315 + 316 + for (exp = 0; mantissa > 0xff; exp++) 317 + mantissa >>= 1; 318 + 319 + mantissa >>= 4; 320 + mantissa &= 0xf; 321 + exp <<= 4; 322 + 323 + return exp | mantissa; 324 + } 325 + 326 + static int max44009_get_thr_reg(enum iio_event_direction dir) 327 + { 328 + switch (dir) { 329 + case IIO_EV_DIR_RISING: 330 + return MAX44009_REG_UPPER_THR; 331 + case IIO_EV_DIR_FALLING: 332 + return MAX44009_REG_LOWER_THR; 333 + default: 334 + return -EINVAL; 335 + } 336 + } 337 + 338 + static int max44009_write_event_value(struct iio_dev *indio_dev, 339 + const struct iio_chan_spec *chan, 340 + enum iio_event_type type, 341 + enum iio_event_direction dir, 342 + enum iio_event_info info, 343 + int val, int val2) 344 + { 345 + struct max44009_data *data = iio_priv(indio_dev); 346 + int reg, threshold; 347 + 348 + if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT) 349 + return -EINVAL; 350 + 351 + threshold = max44009_threshold_byte_from_fraction(val, val2); 352 + if (threshold < 0) 353 + return threshold; 354 + 355 + reg = max44009_get_thr_reg(dir); 356 + if (reg < 0) 357 + return reg; 358 + 359 + return i2c_smbus_write_byte_data(data->client, reg, threshold); 360 + } 361 + 362 + static int max44009_read_threshold(struct iio_dev *indio_dev, 363 + enum iio_event_direction dir) 364 + { 365 + struct max44009_data *data = iio_priv(indio_dev); 366 + int byte, reg; 367 + int mantissa, exponent; 368 + 369 + reg = max44009_get_thr_reg(dir); 370 + if (reg < 0) 371 + return reg; 372 + 373 + byte = i2c_smbus_read_byte_data(data->client, reg); 374 + if (byte < 0) 375 + return byte; 376 + 377 + mantissa = byte & MAX44009_THRESH_MANT_MASK; 378 + mantissa <<= MAX44009_THRESH_MANT_LSHIFT; 379 + 380 + /* 381 + * To get the upper threshold, always adds the minimum upper threshold 382 + * value to the shifted byte value (see datasheet). 383 + */ 384 + if (dir == IIO_EV_DIR_RISING) 385 + mantissa += MAX44009_UPPER_THR_MINIMUM; 386 + 387 + /* 388 + * Exponent is base 2 to the power of the threshold exponent byte 389 + * value 390 + */ 391 + exponent = byte & MAX44009_THRESH_EXP_MASK; 392 + exponent >>= MAX44009_THRESH_EXP_RSHIFT; 393 + 394 + return (1 << exponent) * mantissa; 395 + } 396 + 397 + static int max44009_read_event_value(struct iio_dev *indio_dev, 398 + const struct iio_chan_spec *chan, 399 + enum iio_event_type type, 400 + enum iio_event_direction dir, 401 + enum iio_event_info info, 402 + int *val, int *val2) 403 + { 404 + int ret; 405 + int threshold; 406 + 407 + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) 408 + return -EINVAL; 409 + 410 + ret = max44009_read_threshold(indio_dev, dir); 411 + if (ret < 0) 412 + return ret; 413 + threshold = ret; 414 + 415 + *val = threshold * MAX44009_SCALE_NUMERATOR; 416 + *val2 = MAX44009_SCALE_DENOMINATOR; 417 + 418 + return IIO_VAL_FRACTIONAL; 419 + } 420 + 421 + static int max44009_write_event_config(struct iio_dev *indio_dev, 422 + const struct iio_chan_spec *chan, 423 + enum iio_event_type type, 424 + enum iio_event_direction dir, 425 + int state) 426 + { 427 + struct max44009_data *data = iio_priv(indio_dev); 428 + int ret; 429 + 430 + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) 431 + return -EINVAL; 432 + 433 + ret = i2c_smbus_write_byte_data(data->client, 434 + MAX44009_REG_INT_EN, state); 435 + if (ret < 0) 436 + return ret; 437 + 438 + /* 439 + * Set device to trigger interrupt immediately upon exceeding 440 + * the threshold limit. 441 + */ 442 + return i2c_smbus_write_byte_data(data->client, 443 + MAX44009_REG_THR_TIMER, 0); 444 + } 445 + 446 + static int max44009_read_event_config(struct iio_dev *indio_dev, 447 + const struct iio_chan_spec *chan, 448 + enum iio_event_type type, 449 + enum iio_event_direction dir) 450 + { 451 + struct max44009_data *data = iio_priv(indio_dev); 452 + 453 + if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH) 454 + return -EINVAL; 455 + 456 + return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN); 457 + } 458 + 459 + static const struct iio_info max44009_info = { 460 + .read_raw = max44009_read_raw, 461 + .write_raw = max44009_write_raw, 462 + .write_raw_get_fmt = max44009_write_raw_get_fmt, 463 + .read_event_value = max44009_read_event_value, 464 + .read_event_config = max44009_read_event_config, 465 + .write_event_value = max44009_write_event_value, 466 + .write_event_config = max44009_write_event_config, 467 + .attrs = &max44009_attribute_group, 468 + }; 469 + 470 + static irqreturn_t max44009_threaded_irq_handler(int irq, void *p) 471 + { 472 + struct iio_dev *indio_dev = p; 473 + struct max44009_data *data = iio_priv(indio_dev); 474 + int ret; 475 + 476 + ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS); 477 + if (ret) { 478 + iio_push_event(indio_dev, 479 + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 480 + IIO_EV_TYPE_THRESH, 481 + IIO_EV_DIR_EITHER), 482 + iio_get_time_ns(indio_dev)); 483 + 484 + return IRQ_HANDLED; 485 + } 486 + 487 + return IRQ_NONE; 488 + } 489 + 490 + static int max44009_probe(struct i2c_client *client, 491 + const struct i2c_device_id *id) 492 + { 493 + struct max44009_data *data; 494 + struct iio_dev *indio_dev; 495 + int ret; 496 + 497 + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 498 + if (!indio_dev) 499 + return -ENOMEM; 500 + 501 + data = iio_priv(indio_dev); 502 + i2c_set_clientdata(client, indio_dev); 503 + data->client = client; 504 + indio_dev->dev.parent = &client->dev; 505 + indio_dev->info = &max44009_info; 506 + indio_dev->modes = INDIO_DIRECT_MODE; 507 + indio_dev->name = MAX44009_DRV_NAME; 508 + indio_dev->channels = max44009_channels; 509 + indio_dev->num_channels = ARRAY_SIZE(max44009_channels); 510 + mutex_init(&data->lock); 511 + 512 + /* Clear any stale interrupt bit */ 513 + ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG); 514 + if (ret < 0) 515 + return ret; 516 + 517 + if (client->irq > 0) { 518 + ret = devm_request_threaded_irq(&client->dev, client->irq, 519 + NULL, 520 + max44009_threaded_irq_handler, 521 + IRQF_TRIGGER_FALLING | 522 + IRQF_ONESHOT | IRQF_SHARED, 523 + "max44009_event", 524 + indio_dev); 525 + if (ret < 0) 526 + return ret; 527 + } 528 + 529 + return devm_iio_device_register(&client->dev, indio_dev); 530 + } 531 + 532 + static const struct i2c_device_id max44009_id[] = { 533 + { "max44009", 0 }, 534 + { } 535 + }; 536 + MODULE_DEVICE_TABLE(i2c, max44009_id); 537 + 538 + static struct i2c_driver max44009_driver = { 539 + .driver = { 540 + .name = MAX44009_DRV_NAME, 541 + }, 542 + .probe = max44009_probe, 543 + .id_table = max44009_id, 544 + }; 545 + module_i2c_driver(max44009_driver); 546 + 547 + static const struct of_device_id max44009_of_match[] = { 548 + { .compatible = "maxim,max44009" }, 549 + { } 550 + }; 551 + MODULE_DEVICE_TABLE(of, max44009_of_match); 552 + 553 + MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>"); 554 + MODULE_LICENSE("GPL v2"); 555 + MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");