683 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x604 *5 * Copyright (c) 2018, DENX Software Engineering GmbH6 * Author: Parthiban Nallathambi <pn@denx.de>7 *8 * TODO: Proximity9 */10#include <linux/bitops.h>11#include <linux/bitfield.h>12#include <linux/i2c.h>13#include <linux/module.h>14#include <linux/pm_runtime.h>15#include <linux/regmap.h>16 17#include <linux/iio/buffer.h>18#include <linux/iio/events.h>19#include <linux/iio/iio.h>20#include <linux/iio/sysfs.h>21#include <linux/iio/trigger.h>22#include <linux/iio/trigger_consumer.h>23#include <linux/iio/triggered_buffer.h>24 25#define VCNL4035_DRV_NAME "vcnl4035"26#define VCNL4035_IRQ_NAME "vcnl4035_event"27#define VCNL4035_REGMAP_NAME "vcnl4035_regmap"28 29/* Device registers */30#define VCNL4035_ALS_CONF 0x0031#define VCNL4035_ALS_THDH 0x0132#define VCNL4035_ALS_THDL 0x0233#define VCNL4035_ALS_DATA 0x0B34#define VCNL4035_WHITE_DATA 0x0C35#define VCNL4035_INT_FLAG 0x0D36#define VCNL4035_DEV_ID 0x0E37 38/* Register masks */39#define VCNL4035_MODE_ALS_MASK BIT(0)40#define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8)41#define VCNL4035_MODE_ALS_INT_MASK BIT(1)42#define VCNL4035_ALS_IT_MASK GENMASK(7, 5)43#define VCNL4035_ALS_PERS_MASK GENMASK(3, 2)44#define VCNL4035_INT_ALS_IF_H_MASK BIT(12)45#define VCNL4035_INT_ALS_IF_L_MASK BIT(13)46#define VCNL4035_DEV_ID_MASK GENMASK(7, 0)47 48/* Default values */49#define VCNL4035_MODE_ALS_ENABLE BIT(0)50#define VCNL4035_MODE_ALS_DISABLE 0x0051#define VCNL4035_MODE_ALS_INT_ENABLE BIT(1)52#define VCNL4035_MODE_ALS_INT_DISABLE 053#define VCNL4035_DEV_ID_VAL 0x8054#define VCNL4035_ALS_IT_DEFAULT 0x0155#define VCNL4035_ALS_PERS_DEFAULT 0x0056#define VCNL4035_ALS_THDH_DEFAULT 500057#define VCNL4035_ALS_THDL_DEFAULT 10058#define VCNL4035_SLEEP_DELAY_MS 200059 60struct vcnl4035_data {61 struct i2c_client *client;62 struct regmap *regmap;63 unsigned int als_it_val;64 unsigned int als_persistence;65 unsigned int als_thresh_low;66 unsigned int als_thresh_high;67 struct iio_trigger *drdy_trigger0;68};69 70static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data)71{72 int ret;73 int reg;74 75 ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, ®);76 if (ret < 0)77 return false;78 79 return !!(reg &80 (VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK));81}82 83static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private)84{85 struct iio_dev *indio_dev = private;86 struct vcnl4035_data *data = iio_priv(indio_dev);87 88 if (vcnl4035_is_triggered(data)) {89 iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,90 0,91 IIO_EV_TYPE_THRESH,92 IIO_EV_DIR_EITHER),93 iio_get_time_ns(indio_dev));94 iio_trigger_poll_nested(data->drdy_trigger0);95 return IRQ_HANDLED;96 }97 98 return IRQ_NONE;99}100 101/* Triggered buffer */102static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)103{104 struct iio_poll_func *pf = p;105 struct iio_dev *indio_dev = pf->indio_dev;106 struct vcnl4035_data *data = iio_priv(indio_dev);107 /* Ensure naturally aligned timestamp */108 u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8);109 int ret;110 111 ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);112 if (ret < 0) {113 dev_err(&data->client->dev,114 "Trigger consumer can't read from sensor.\n");115 goto fail_read;116 }117 iio_push_to_buffers_with_timestamp(indio_dev, buffer,118 iio_get_time_ns(indio_dev));119 120fail_read:121 iio_trigger_notify_done(indio_dev->trig);122 123 return IRQ_HANDLED;124}125 126static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger,127 bool enable_drdy)128{129 struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);130 struct vcnl4035_data *data = iio_priv(indio_dev);131 int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE :132 VCNL4035_MODE_ALS_INT_DISABLE;133 134 return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,135 VCNL4035_MODE_ALS_INT_MASK,136 val);137}138 139static const struct iio_trigger_ops vcnl4035_trigger_ops = {140 .validate_device = iio_trigger_validate_own_device,141 .set_trigger_state = vcnl4035_als_drdy_set_state,142};143 144static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)145{146 int ret;147 struct device *dev = &data->client->dev;148 149 if (on) {150 ret = pm_runtime_resume_and_get(dev);151 } else {152 pm_runtime_mark_last_busy(dev);153 ret = pm_runtime_put_autosuspend(dev);154 }155 156 return ret;157}158 159/*160 * Device IT INT Time (ms) Scale (lux/step)161 * 000 50 0.064162 * 001 100 0.032163 * 010 200 0.016164 * 100 400 0.008165 * 101 - 111 800 0.004166 * Values are proportional, so ALS INT is selected for input due to167 * simplicity reason. Integration time value and scaling is168 * calculated based on device INT value169 *170 * Raw value needs to be scaled using ALS steps171 */172static int vcnl4035_read_raw(struct iio_dev *indio_dev,173 struct iio_chan_spec const *chan, int *val,174 int *val2, long mask)175{176 struct vcnl4035_data *data = iio_priv(indio_dev);177 int ret;178 int raw_data;179 unsigned int reg;180 181 switch (mask) {182 case IIO_CHAN_INFO_RAW:183 ret = vcnl4035_set_pm_runtime_state(data, true);184 if (ret < 0)185 return ret;186 187 ret = iio_device_claim_direct_mode(indio_dev);188 if (!ret) {189 if (chan->channel)190 reg = VCNL4035_ALS_DATA;191 else192 reg = VCNL4035_WHITE_DATA;193 ret = regmap_read(data->regmap, reg, &raw_data);194 iio_device_release_direct_mode(indio_dev);195 if (!ret) {196 *val = raw_data;197 ret = IIO_VAL_INT;198 }199 }200 vcnl4035_set_pm_runtime_state(data, false);201 return ret;202 case IIO_CHAN_INFO_INT_TIME:203 *val = 50;204 if (data->als_it_val)205 *val = data->als_it_val * 100;206 return IIO_VAL_INT;207 case IIO_CHAN_INFO_SCALE:208 *val = 64;209 if (!data->als_it_val)210 *val2 = 1000;211 else212 *val2 = data->als_it_val * 2 * 1000;213 return IIO_VAL_FRACTIONAL;214 default:215 return -EINVAL;216 }217}218 219static int vcnl4035_write_raw(struct iio_dev *indio_dev,220 struct iio_chan_spec const *chan,221 int val, int val2, long mask)222{223 int ret;224 struct vcnl4035_data *data = iio_priv(indio_dev);225 226 switch (mask) {227 case IIO_CHAN_INFO_INT_TIME:228 if (val <= 0 || val > 800)229 return -EINVAL;230 231 ret = vcnl4035_set_pm_runtime_state(data, true);232 if (ret < 0)233 return ret;234 235 ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,236 VCNL4035_ALS_IT_MASK,237 val / 100);238 if (!ret)239 data->als_it_val = val / 100;240 241 vcnl4035_set_pm_runtime_state(data, false);242 return ret;243 default:244 return -EINVAL;245 }246}247 248/* No direct ABI for persistence and threshold, so eventing */249static int vcnl4035_read_thresh(struct iio_dev *indio_dev,250 const struct iio_chan_spec *chan, enum iio_event_type type,251 enum iio_event_direction dir, enum iio_event_info info,252 int *val, int *val2)253{254 struct vcnl4035_data *data = iio_priv(indio_dev);255 256 switch (info) {257 case IIO_EV_INFO_VALUE:258 switch (dir) {259 case IIO_EV_DIR_RISING:260 *val = data->als_thresh_high;261 return IIO_VAL_INT;262 case IIO_EV_DIR_FALLING:263 *val = data->als_thresh_low;264 return IIO_VAL_INT;265 default:266 return -EINVAL;267 }268 break;269 case IIO_EV_INFO_PERIOD:270 *val = data->als_persistence;271 return IIO_VAL_INT;272 default:273 return -EINVAL;274 }275 276}277 278static int vcnl4035_write_thresh(struct iio_dev *indio_dev,279 const struct iio_chan_spec *chan, enum iio_event_type type,280 enum iio_event_direction dir, enum iio_event_info info, int val,281 int val2)282{283 struct vcnl4035_data *data = iio_priv(indio_dev);284 int ret;285 286 switch (info) {287 case IIO_EV_INFO_VALUE:288 /* 16 bit threshold range 0 - 65535 */289 if (val < 0 || val > 65535)290 return -EINVAL;291 if (dir == IIO_EV_DIR_RISING) {292 if (val < data->als_thresh_low)293 return -EINVAL;294 ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,295 val);296 if (ret)297 return ret;298 data->als_thresh_high = val;299 } else {300 if (val > data->als_thresh_high)301 return -EINVAL;302 ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,303 val);304 if (ret)305 return ret;306 data->als_thresh_low = val;307 }308 return ret;309 case IIO_EV_INFO_PERIOD:310 /* allow only 1 2 4 8 as persistence value */311 if (val < 0 || val > 8 || hweight8(val) != 1)312 return -EINVAL;313 ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,314 VCNL4035_ALS_PERS_MASK, val);315 if (!ret)316 data->als_persistence = val;317 return ret;318 default:319 return -EINVAL;320 }321}322 323static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800");324 325static struct attribute *vcnl4035_attributes[] = {326 &iio_const_attr_integration_time_available.dev_attr.attr,327 NULL,328};329 330static const struct attribute_group vcnl4035_attribute_group = {331 .attrs = vcnl4035_attributes,332};333 334static const struct iio_info vcnl4035_info = {335 .read_raw = vcnl4035_read_raw,336 .write_raw = vcnl4035_write_raw,337 .read_event_value = vcnl4035_read_thresh,338 .write_event_value = vcnl4035_write_thresh,339 .attrs = &vcnl4035_attribute_group,340};341 342static const struct iio_event_spec vcnl4035_event_spec[] = {343 {344 .type = IIO_EV_TYPE_THRESH,345 .dir = IIO_EV_DIR_RISING,346 .mask_separate = BIT(IIO_EV_INFO_VALUE),347 }, {348 .type = IIO_EV_TYPE_THRESH,349 .dir = IIO_EV_DIR_FALLING,350 .mask_separate = BIT(IIO_EV_INFO_VALUE),351 }, {352 .type = IIO_EV_TYPE_THRESH,353 .dir = IIO_EV_DIR_EITHER,354 .mask_separate = BIT(IIO_EV_INFO_PERIOD),355 },356};357 358enum vcnl4035_scan_index_order {359 VCNL4035_CHAN_INDEX_LIGHT,360 VCNL4035_CHAN_INDEX_WHITE_LED,361};362 363static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {364 .validate_scan_mask = &iio_validate_scan_mask_onehot,365};366 367static const struct iio_chan_spec vcnl4035_channels[] = {368 {369 .type = IIO_LIGHT,370 .channel = 0,371 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |372 BIT(IIO_CHAN_INFO_INT_TIME) |373 BIT(IIO_CHAN_INFO_SCALE),374 .event_spec = vcnl4035_event_spec,375 .num_event_specs = ARRAY_SIZE(vcnl4035_event_spec),376 .scan_index = VCNL4035_CHAN_INDEX_LIGHT,377 .scan_type = {378 .sign = 'u',379 .realbits = 16,380 .storagebits = 16,381 .endianness = IIO_LE,382 },383 },384 {385 .type = IIO_INTENSITY,386 .channel = 1,387 .modified = 1,388 .channel2 = IIO_MOD_LIGHT_BOTH,389 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),390 .scan_index = VCNL4035_CHAN_INDEX_WHITE_LED,391 .scan_type = {392 .sign = 'u',393 .realbits = 16,394 .storagebits = 16,395 .endianness = IIO_LE,396 },397 },398};399 400static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status)401{402 return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,403 VCNL4035_MODE_ALS_MASK,404 status);405}406 407static int vcnl4035_init(struct vcnl4035_data *data)408{409 int ret;410 int id;411 412 ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id);413 if (ret < 0) {414 dev_err(&data->client->dev, "Failed to read DEV_ID register\n");415 return ret;416 }417 418 id = FIELD_GET(VCNL4035_DEV_ID_MASK, id);419 if (id != VCNL4035_DEV_ID_VAL) {420 dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",421 id, VCNL4035_DEV_ID_VAL);422 return -ENODEV;423 }424 425 ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);426 if (ret < 0)427 return ret;428 429 /* ALS white channel enable */430 ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,431 VCNL4035_MODE_ALS_WHITE_CHAN,432 1);433 if (ret) {434 dev_err(&data->client->dev, "set white channel enable %d\n",435 ret);436 return ret;437 }438 439 /* set default integration time - 100 ms for ALS */440 ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,441 VCNL4035_ALS_IT_MASK,442 VCNL4035_ALS_IT_DEFAULT);443 if (ret) {444 dev_err(&data->client->dev, "set default ALS IT returned %d\n",445 ret);446 return ret;447 }448 data->als_it_val = VCNL4035_ALS_IT_DEFAULT;449 450 /* set default persistence time - 1 for ALS */451 ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,452 VCNL4035_ALS_PERS_MASK,453 VCNL4035_ALS_PERS_DEFAULT);454 if (ret) {455 dev_err(&data->client->dev, "set default PERS returned %d\n",456 ret);457 return ret;458 }459 data->als_persistence = VCNL4035_ALS_PERS_DEFAULT;460 461 /* set default HIGH threshold for ALS */462 ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,463 VCNL4035_ALS_THDH_DEFAULT);464 if (ret) {465 dev_err(&data->client->dev, "set default THDH returned %d\n",466 ret);467 return ret;468 }469 data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT;470 471 /* set default LOW threshold for ALS */472 ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,473 VCNL4035_ALS_THDL_DEFAULT);474 if (ret) {475 dev_err(&data->client->dev, "set default THDL returned %d\n",476 ret);477 return ret;478 }479 data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT;480 481 return 0;482}483 484static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg)485{486 switch (reg) {487 case VCNL4035_ALS_CONF:488 case VCNL4035_DEV_ID:489 return false;490 default:491 return true;492 }493}494 495static const struct regmap_config vcnl4035_regmap_config = {496 .name = VCNL4035_REGMAP_NAME,497 .reg_bits = 8,498 .val_bits = 16,499 .max_register = VCNL4035_DEV_ID,500 .cache_type = REGCACHE_RBTREE,501 .volatile_reg = vcnl4035_is_volatile_reg,502 .val_format_endian = REGMAP_ENDIAN_LITTLE,503};504 505static int vcnl4035_probe_trigger(struct iio_dev *indio_dev)506{507 int ret;508 struct vcnl4035_data *data = iio_priv(indio_dev);509 510 data->drdy_trigger0 = devm_iio_trigger_alloc(511 indio_dev->dev.parent,512 "%s-dev%d", indio_dev->name, iio_device_id(indio_dev));513 if (!data->drdy_trigger0)514 return -ENOMEM;515 516 data->drdy_trigger0->ops = &vcnl4035_trigger_ops;517 iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);518 ret = devm_iio_trigger_register(indio_dev->dev.parent,519 data->drdy_trigger0);520 if (ret) {521 dev_err(&data->client->dev, "iio trigger register failed\n");522 return ret;523 }524 525 /* Trigger setup */526 ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,527 NULL, vcnl4035_trigger_consumer_handler,528 &iio_triggered_buffer_setup_ops);529 if (ret < 0) {530 dev_err(&data->client->dev, "iio triggered buffer setup failed\n");531 return ret;532 }533 534 /* IRQ to trigger mapping */535 ret = devm_request_threaded_irq(&data->client->dev, data->client->irq,536 NULL, vcnl4035_drdy_irq_thread,537 IRQF_TRIGGER_LOW | IRQF_ONESHOT,538 VCNL4035_IRQ_NAME, indio_dev);539 if (ret < 0)540 dev_err(&data->client->dev, "request irq %d for trigger0 failed\n",541 data->client->irq);542 return ret;543}544 545static int vcnl4035_probe(struct i2c_client *client)546{547 struct vcnl4035_data *data;548 struct iio_dev *indio_dev;549 struct regmap *regmap;550 int ret;551 552 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));553 if (!indio_dev)554 return -ENOMEM;555 556 regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config);557 if (IS_ERR(regmap)) {558 dev_err(&client->dev, "regmap_init failed!\n");559 return PTR_ERR(regmap);560 }561 562 data = iio_priv(indio_dev);563 i2c_set_clientdata(client, indio_dev);564 data->client = client;565 data->regmap = regmap;566 567 indio_dev->info = &vcnl4035_info;568 indio_dev->name = VCNL4035_DRV_NAME;569 indio_dev->channels = vcnl4035_channels;570 indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels);571 indio_dev->modes = INDIO_DIRECT_MODE;572 573 ret = vcnl4035_init(data);574 if (ret < 0) {575 dev_err(&client->dev, "vcnl4035 chip init failed\n");576 return ret;577 }578 579 if (client->irq > 0) {580 ret = vcnl4035_probe_trigger(indio_dev);581 if (ret < 0) {582 dev_err(&client->dev, "vcnl4035 unable init trigger\n");583 goto fail_poweroff;584 }585 }586 587 ret = pm_runtime_set_active(&client->dev);588 if (ret < 0)589 goto fail_poweroff;590 591 ret = iio_device_register(indio_dev);592 if (ret < 0)593 goto fail_poweroff;594 595 pm_runtime_enable(&client->dev);596 pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS);597 pm_runtime_use_autosuspend(&client->dev);598 599 return 0;600 601fail_poweroff:602 vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);603 return ret;604}605 606static void vcnl4035_remove(struct i2c_client *client)607{608 struct iio_dev *indio_dev = i2c_get_clientdata(client);609 int ret;610 611 pm_runtime_dont_use_autosuspend(&client->dev);612 pm_runtime_disable(&client->dev);613 iio_device_unregister(indio_dev);614 pm_runtime_set_suspended(&client->dev);615 616 ret = vcnl4035_set_als_power_state(iio_priv(indio_dev),617 VCNL4035_MODE_ALS_DISABLE);618 if (ret)619 dev_warn(&client->dev, "Failed to put device into standby (%pe)\n",620 ERR_PTR(ret));621}622 623static int vcnl4035_runtime_suspend(struct device *dev)624{625 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));626 struct vcnl4035_data *data = iio_priv(indio_dev);627 int ret;628 629 ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);630 regcache_mark_dirty(data->regmap);631 632 return ret;633}634 635static int vcnl4035_runtime_resume(struct device *dev)636{637 struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));638 struct vcnl4035_data *data = iio_priv(indio_dev);639 int ret;640 641 regcache_sync(data->regmap);642 ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);643 if (ret < 0)644 return ret;645 646 /* wait for 1 ALS integration cycle */647 msleep(data->als_it_val * 100);648 649 return 0;650}651 652static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4035_pm_ops, vcnl4035_runtime_suspend,653 vcnl4035_runtime_resume, NULL);654 655static const struct i2c_device_id vcnl4035_id[] = {656 { "vcnl4035" },657 { }658};659MODULE_DEVICE_TABLE(i2c, vcnl4035_id);660 661static const struct of_device_id vcnl4035_of_match[] = {662 { .compatible = "vishay,vcnl4035", },663 { }664};665MODULE_DEVICE_TABLE(of, vcnl4035_of_match);666 667static struct i2c_driver vcnl4035_driver = {668 .driver = {669 .name = VCNL4035_DRV_NAME,670 .pm = pm_ptr(&vcnl4035_pm_ops),671 .of_match_table = vcnl4035_of_match,672 },673 .probe = vcnl4035_probe,674 .remove = vcnl4035_remove,675 .id_table = vcnl4035_id,676};677 678module_i2c_driver(vcnl4035_driver);679 680MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>");681MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver");682MODULE_LICENSE("GPL v2");683