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: veml6030: add support for veml6035

The veml6035 is an ALS that shares most of its functionality with the
veml6030, which allows for some code recycling.

Some chip-specific properties differ and dedicated functions to get and
set the sensor gain as well as its initialization are required.

Signed-off-by: Javier Carrasco <javier.carrasco.cruz@gmail.com>
Link: https://patch.msgid.link/20241001-veml6035-v3-9-d789f6ff147c@gmail.com
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>

authored by

Javier Carrasco and committed by
Jonathan Cameron
ccc26bd7 f1bfc1c9

+265 -29
+2 -2
drivers/iio/light/Kconfig
··· 669 669 module will be called vcnl4035. 670 670 671 671 config VEML6030 672 - tristate "VEML6030 ambient light sensor" 672 + tristate "VEML6030 and VEML6035 ambient light sensors" 673 673 select REGMAP_I2C 674 674 depends on I2C 675 675 help 676 676 Say Y here if you want to build a driver for the Vishay VEML6030 677 - ambient light sensor (ALS). 677 + and VEML6035 ambient light sensors (ALS). 678 678 679 679 To compile this driver as a module, choose M here: the 680 680 module will be called veml6030.
+263 -27
drivers/iio/light/veml6030.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0+ 2 2 /* 3 - * VEML6030 Ambient Light Sensor 3 + * VEML6030 and VMEL6035 Ambient Light Sensors 4 4 * 5 5 * Copyright (c) 2019, Rishi Gupta <gupt21@gmail.com> 6 6 * 7 + * VEML6030: 7 8 * Datasheet: https://www.vishay.com/docs/84366/veml6030.pdf 8 9 * Appnote-84367: https://www.vishay.com/docs/84367/designingveml6030.pdf 10 + * 11 + * VEML6035: 12 + * Datasheet: https://www.vishay.com/docs/84889/veml6035.pdf 13 + * Appnote-84944: https://www.vishay.com/docs/84944/designingveml6035.pdf 9 14 */ 10 15 16 + #include <linux/bitfield.h> 11 17 #include <linux/module.h> 12 18 #include <linux/i2c.h> 13 19 #include <linux/err.h> ··· 45 39 #define VEML6030_ALS_INT_EN BIT(1) 46 40 #define VEML6030_ALS_SD BIT(0) 47 41 42 + #define VEML6035_GAIN_M GENMASK(12, 10) 43 + #define VEML6035_GAIN BIT(10) 44 + #define VEML6035_DG BIT(11) 45 + #define VEML6035_SENS BIT(12) 46 + #define VEML6035_INT_CHAN BIT(3) 47 + #define VEML6035_CHAN_EN BIT(2) 48 + 49 + struct veml603x_chip { 50 + const char *name; 51 + const int(*scale_vals)[][2]; 52 + const int num_scale_vals; 53 + const struct iio_info *info; 54 + const struct iio_info *info_no_irq; 55 + int (*hw_init)(struct iio_dev *indio_dev, struct device *dev); 56 + int (*set_als_gain)(struct iio_dev *indio_dev, int val, int val2); 57 + int (*get_als_gain)(struct iio_dev *indio_dev, int *val, int *val2); 58 + }; 59 + 48 60 /* 49 61 * The resolution depends on both gain and integration time. The 50 62 * cur_resolution stores one of the resolution mentioned in the 51 63 * table during startup and gets updated whenever integration time 52 64 * or gain is changed. 53 65 * 54 - * Table 'resolution and maximum detection range' in appnote 84367 66 + * Table 'resolution and maximum detection range' in the appnotes 55 67 * is visualized as a 2D array. The cur_gain stores index of gain 56 - * in this table (0-3) while the cur_integration_time holds index 57 - * of integration time (0-5). 68 + * in this table (0-3 for VEML6030, 0-5 for VEML6035) while the 69 + * cur_integration_time holds index of integration time (0-5). 58 70 */ 59 71 struct veml6030_data { 60 72 struct i2c_client *client; ··· 80 56 int cur_resolution; 81 57 int cur_gain; 82 58 int cur_integration_time; 59 + const struct veml603x_chip *chip; 83 60 }; 84 61 85 62 static const int veml6030_it_times[][2] = { ··· 94 69 95 70 /* 96 71 * Scale is 1/gain. Value 0.125 is ALS gain x (1/8), 0.25 is 97 - * ALS gain x (1/4), 1.0 = ALS gain x 1 and 2.0 is ALS gain x 2. 72 + * ALS gain x (1/4), 0.5 is ALS gain x (1/2), 1.0 is ALS gain x 1, 73 + * 2.0 is ALS gain x2, and 4.0 is ALS gain x 4. 98 74 */ 99 75 static const int veml6030_scale_vals[][2] = { 100 76 { 0, 125000 }, 101 77 { 0, 250000 }, 102 78 { 1, 0 }, 103 79 { 2, 0 }, 80 + }; 81 + 82 + static const int veml6035_scale_vals[][2] = { 83 + { 0, 125000 }, 84 + { 0, 250000 }, 85 + { 0, 500000 }, 86 + { 1, 0 }, 87 + { 2, 0 }, 88 + { 4, 0 }, 104 89 }; 105 90 106 91 /* ··· 421 386 return ret; 422 387 } 423 388 389 + /* 390 + * Cache currently set gain & update resolution. For every 391 + * increase in the gain to next level, resolution is halved 392 + * and vice-versa. 393 + */ 394 + static void veml6030_update_gain_res(struct veml6030_data *data, int gain_idx) 395 + { 396 + if (data->cur_gain < gain_idx) 397 + data->cur_resolution <<= gain_idx - data->cur_gain; 398 + else if (data->cur_gain > gain_idx) 399 + data->cur_resolution >>= data->cur_gain - gain_idx; 400 + 401 + data->cur_gain = gain_idx; 402 + } 403 + 424 404 static int veml6030_set_als_gain(struct iio_dev *indio_dev, 425 405 int val, int val2) 426 406 { ··· 466 416 return ret; 467 417 } 468 418 469 - /* 470 - * Cache currently set gain & update resolution. For every 471 - * increase in the gain to next level, resolution is halved 472 - * and vice-versa. 473 - */ 474 - if (data->cur_gain < gain_idx) 475 - data->cur_resolution <<= gain_idx - data->cur_gain; 476 - else if (data->cur_gain > gain_idx) 477 - data->cur_resolution >>= data->cur_gain - gain_idx; 419 + veml6030_update_gain_res(data, gain_idx); 478 420 479 - data->cur_gain = gain_idx; 421 + return 0; 422 + } 480 423 481 - return ret; 424 + static int veml6035_set_als_gain(struct iio_dev *indio_dev, int val, int val2) 425 + { 426 + int ret, new_gain, gain_idx; 427 + struct veml6030_data *data = iio_priv(indio_dev); 428 + 429 + if (val == 0 && val2 == 125000) { 430 + new_gain = VEML6035_SENS; 431 + gain_idx = 5; 432 + } else if (val == 0 && val2 == 250000) { 433 + new_gain = VEML6035_SENS | VEML6035_GAIN; 434 + gain_idx = 4; 435 + } else if (val == 0 && val2 == 500000) { 436 + new_gain = VEML6035_SENS | VEML6035_GAIN | 437 + VEML6035_DG; 438 + gain_idx = 3; 439 + } else if (val == 1 && val2 == 0) { 440 + new_gain = 0x0000; 441 + gain_idx = 2; 442 + } else if (val == 2 && val2 == 0) { 443 + new_gain = VEML6035_GAIN; 444 + gain_idx = 1; 445 + } else if (val == 4 && val2 == 0) { 446 + new_gain = VEML6035_GAIN | VEML6035_DG; 447 + gain_idx = 0; 448 + } else { 449 + return -EINVAL; 450 + } 451 + 452 + ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_CONF, 453 + VEML6035_GAIN_M, new_gain); 454 + if (ret) { 455 + dev_err(&data->client->dev, "can't set als gain %d\n", ret); 456 + return ret; 457 + } 458 + 459 + veml6030_update_gain_res(data, gain_idx); 460 + 461 + return 0; 482 462 } 483 463 484 464 static int veml6030_get_als_gain(struct iio_dev *indio_dev, ··· 540 460 case 3: 541 461 *val = 0; 542 462 *val2 = 250000; 463 + break; 464 + default: 465 + return -EINVAL; 466 + } 467 + 468 + return IIO_VAL_INT_PLUS_MICRO; 469 + } 470 + 471 + static int veml6035_get_als_gain(struct iio_dev *indio_dev, int *val, int *val2) 472 + { 473 + int ret, reg; 474 + struct veml6030_data *data = iio_priv(indio_dev); 475 + 476 + ret = regmap_read(data->regmap, VEML6030_REG_ALS_CONF, &reg); 477 + if (ret) { 478 + dev_err(&data->client->dev, 479 + "can't read als conf register %d\n", ret); 480 + return ret; 481 + } 482 + 483 + switch (FIELD_GET(VEML6035_GAIN_M, reg)) { 484 + case 0: 485 + *val = 1; 486 + *val2 = 0; 487 + break; 488 + case 1: 489 + case 2: 490 + *val = 2; 491 + *val2 = 0; 492 + break; 493 + case 3: 494 + *val = 4; 495 + *val2 = 0; 496 + break; 497 + case 4: 498 + *val = 0; 499 + *val2 = 125000; 500 + break; 501 + case 5: 502 + case 6: 503 + *val = 0; 504 + *val2 = 250000; 505 + break; 506 + case 7: 507 + *val = 0; 508 + *val2 = 500000; 543 509 break; 544 510 default: 545 511 return -EINVAL; ··· 682 556 case IIO_CHAN_INFO_INT_TIME: 683 557 return veml6030_get_intgrn_tm(indio_dev, val, val2); 684 558 case IIO_CHAN_INFO_SCALE: 685 - return veml6030_get_als_gain(indio_dev, val, val2); 559 + return data->chip->get_als_gain(indio_dev, val, val2); 686 560 default: 687 561 return -EINVAL; 688 562 } ··· 693 567 const int **vals, int *type, int *length, 694 568 long mask) 695 569 { 570 + struct veml6030_data *data = iio_priv(indio_dev); 571 + 696 572 switch (mask) { 697 573 case IIO_CHAN_INFO_INT_TIME: 698 574 *vals = (int *)&veml6030_it_times; ··· 702 574 *type = IIO_VAL_INT_PLUS_MICRO; 703 575 return IIO_AVAIL_LIST; 704 576 case IIO_CHAN_INFO_SCALE: 705 - *vals = (int *)&veml6030_scale_vals; 706 - *length = 2 * ARRAY_SIZE(veml6030_scale_vals); 577 + *vals = (int *)*data->chip->scale_vals; 578 + *length = 2 * data->chip->num_scale_vals; 707 579 *type = IIO_VAL_INT_PLUS_MICRO; 708 580 return IIO_AVAIL_LIST; 709 581 } ··· 715 587 struct iio_chan_spec const *chan, 716 588 int val, int val2, long mask) 717 589 { 590 + struct veml6030_data *data = iio_priv(indio_dev); 591 + 718 592 switch (mask) { 719 593 case IIO_CHAN_INFO_INT_TIME: 720 594 return veml6030_set_intgrn_tm(indio_dev, val, val2); 721 595 case IIO_CHAN_INFO_SCALE: 722 - return veml6030_set_als_gain(indio_dev, val, val2); 596 + return data->chip->set_als_gain(indio_dev, val, val2); 723 597 default: 724 598 return -EINVAL; 725 599 } ··· 829 699 .event_attrs = &veml6030_event_attr_group, 830 700 }; 831 701 702 + static const struct iio_info veml6035_info = { 703 + .read_raw = veml6030_read_raw, 704 + .read_avail = veml6030_read_avail, 705 + .write_raw = veml6030_write_raw, 706 + .read_event_value = veml6030_read_event_val, 707 + .write_event_value = veml6030_write_event_val, 708 + .read_event_config = veml6030_read_interrupt_config, 709 + .write_event_config = veml6030_write_interrupt_config, 710 + .event_attrs = &veml6030_event_attr_group, 711 + }; 712 + 832 713 static const struct iio_info veml6030_info_no_irq = { 833 714 .read_raw = veml6030_read_raw, 834 715 .read_avail = veml6030_read_avail, 716 + .write_raw = veml6030_write_raw, 717 + }; 718 + 719 + static const struct iio_info veml6035_info_no_irq = { 720 + .read_raw = veml6030_read_raw, 835 721 .write_raw = veml6030_write_raw, 836 722 }; 837 723 ··· 934 788 return ret; 935 789 } 936 790 791 + /* 792 + * Set ALS gain to 1/8, integration time to 100 ms, ALS and WHITE 793 + * channel enabled, ALS channel interrupt, PSM enabled, 794 + * PSM_WAIT = 0.8 s, persistence to 1 x integration time and the 795 + * threshold interrupt disabled by default. First shutdown the sensor, 796 + * update registers and then power on the sensor. 797 + */ 798 + static int veml6035_hw_init(struct iio_dev *indio_dev, struct device *dev) 799 + { 800 + int ret, val; 801 + struct veml6030_data *data = iio_priv(indio_dev); 802 + 803 + ret = veml6030_als_shut_down(data); 804 + if (ret) 805 + return dev_err_probe(dev, ret, "can't shutdown als\n"); 806 + 807 + ret = regmap_write(data->regmap, VEML6030_REG_ALS_CONF, 808 + VEML6035_SENS | VEML6035_CHAN_EN | VEML6030_ALS_SD); 809 + if (ret) 810 + return dev_err_probe(dev, ret, "can't setup als configs\n"); 811 + 812 + ret = regmap_update_bits(data->regmap, VEML6030_REG_ALS_PSM, 813 + VEML6030_PSM | VEML6030_PSM_EN, 0x03); 814 + if (ret) 815 + return dev_err_probe(dev, ret, "can't setup default PSM\n"); 816 + 817 + ret = regmap_write(data->regmap, VEML6030_REG_ALS_WH, 0xFFFF); 818 + if (ret) 819 + return dev_err_probe(dev, ret, "can't setup high threshold\n"); 820 + 821 + ret = regmap_write(data->regmap, VEML6030_REG_ALS_WL, 0x0000); 822 + if (ret) 823 + return dev_err_probe(dev, ret, "can't setup low threshold\n"); 824 + 825 + ret = veml6030_als_pwr_on(data); 826 + if (ret) 827 + return dev_err_probe(dev, ret, "can't poweron als\n"); 828 + 829 + ret = devm_add_action_or_reset(dev, veml6030_als_shut_down_action, data); 830 + if (ret < 0) 831 + return ret; 832 + 833 + /* Clear stale interrupt status bits if any during start */ 834 + ret = regmap_read(data->regmap, VEML6030_REG_ALS_INT, &val); 835 + if (ret < 0) 836 + return dev_err_probe(dev, ret, 837 + "can't clear als interrupt status\n"); 838 + 839 + /* Cache currently active measurement parameters */ 840 + data->cur_gain = 5; 841 + data->cur_resolution = 1024; 842 + data->cur_integration_time = 3; 843 + 844 + return 0; 845 + } 846 + 937 847 static int veml6030_probe(struct i2c_client *client) 938 848 { 939 849 int ret; ··· 1020 818 return dev_err_probe(&client->dev, ret, 1021 819 "failed to enable regulator\n"); 1022 820 1023 - indio_dev->name = "veml6030"; 821 + data->chip = i2c_get_match_data(client); 822 + if (!data->chip) 823 + return -EINVAL; 824 + 825 + indio_dev->name = data->chip->name; 1024 826 indio_dev->channels = veml6030_channels; 1025 827 indio_dev->num_channels = ARRAY_SIZE(veml6030_channels); 1026 828 indio_dev->modes = INDIO_DIRECT_MODE; ··· 1033 827 ret = devm_request_threaded_irq(&client->dev, client->irq, 1034 828 NULL, veml6030_event_handler, 1035 829 IRQF_TRIGGER_LOW | IRQF_ONESHOT, 1036 - "veml6030", indio_dev); 830 + indio_dev->name, indio_dev); 1037 831 if (ret < 0) 1038 832 return dev_err_probe(&client->dev, ret, 1039 833 "irq %d request failed\n", 1040 834 client->irq); 1041 835 1042 - indio_dev->info = &veml6030_info; 836 + indio_dev->info = data->chip->info; 1043 837 } else { 1044 - indio_dev->info = &veml6030_info_no_irq; 838 + indio_dev->info = data->chip->info_no_irq; 1045 839 } 1046 840 1047 - ret = veml6030_hw_init(indio_dev, &client->dev); 841 + ret = data->chip->hw_init(indio_dev, &client->dev); 1048 842 if (ret < 0) 1049 843 return ret; 1050 844 ··· 1080 874 static DEFINE_RUNTIME_DEV_PM_OPS(veml6030_pm_ops, veml6030_runtime_suspend, 1081 875 veml6030_runtime_resume, NULL); 1082 876 877 + static const struct veml603x_chip veml6030_chip = { 878 + .name = "veml6030", 879 + .scale_vals = &veml6030_scale_vals, 880 + .num_scale_vals = ARRAY_SIZE(veml6030_scale_vals), 881 + .info = &veml6030_info, 882 + .info_no_irq = &veml6030_info_no_irq, 883 + .hw_init = veml6030_hw_init, 884 + .set_als_gain = veml6030_set_als_gain, 885 + .get_als_gain = veml6030_get_als_gain, 886 + }; 887 + 888 + static const struct veml603x_chip veml6035_chip = { 889 + .name = "veml6035", 890 + .scale_vals = &veml6035_scale_vals, 891 + .num_scale_vals = ARRAY_SIZE(veml6035_scale_vals), 892 + .info = &veml6035_info, 893 + .info_no_irq = &veml6035_info_no_irq, 894 + .hw_init = veml6035_hw_init, 895 + .set_als_gain = veml6035_set_als_gain, 896 + .get_als_gain = veml6035_get_als_gain, 897 + }; 898 + 1083 899 static const struct of_device_id veml6030_of_match[] = { 1084 - { .compatible = "vishay,veml6030" }, 900 + { 901 + .compatible = "vishay,veml6030", 902 + .data = &veml6030_chip, 903 + }, 904 + { 905 + .compatible = "vishay,veml6035", 906 + .data = &veml6035_chip, 907 + }, 1085 908 { } 1086 909 }; 1087 910 MODULE_DEVICE_TABLE(of, veml6030_of_match); 1088 911 1089 912 static const struct i2c_device_id veml6030_id[] = { 1090 - { "veml6030" }, 913 + { "veml6030", (kernel_ulong_t)&veml6030_chip}, 914 + { "veml6035", (kernel_ulong_t)&veml6035_chip}, 1091 915 { } 1092 916 }; 1093 917 MODULE_DEVICE_TABLE(i2c, veml6030_id);