18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Device driver for monitoring ambient light intensity in (lux) and proximity 48c2ecf20Sopenharmony_ci * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, 58c2ecf20Sopenharmony_ci * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (c) 2012, TAOS Corporation. 88c2ecf20Sopenharmony_ci * Copyright (c) 2017-2018 Brian Masney <masneyb@onstation.org> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/kernel.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/mutex.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 208c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 218c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 228c2ecf20Sopenharmony_ci#include <linux/platform_data/tsl2772.h> 238c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Cal defs */ 268c2ecf20Sopenharmony_ci#define PROX_STAT_CAL 0 278c2ecf20Sopenharmony_ci#define PROX_STAT_SAMP 1 288c2ecf20Sopenharmony_ci#define MAX_SAMPLES_CAL 200 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* TSL2772 Device ID */ 318c2ecf20Sopenharmony_ci#define TRITON_ID 0x00 328c2ecf20Sopenharmony_ci#define SWORDFISH_ID 0x30 338c2ecf20Sopenharmony_ci#define HALIBUT_ID 0x20 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Lux calculation constants */ 368c2ecf20Sopenharmony_ci#define TSL2772_LUX_CALC_OVER_FLOW 65535 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * TAOS Register definitions - Note: depending on device, some of these register 408c2ecf20Sopenharmony_ci * are not used and the register address is benign. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Register offsets */ 448c2ecf20Sopenharmony_ci#define TSL2772_MAX_CONFIG_REG 16 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* Device Registers and Masks */ 478c2ecf20Sopenharmony_ci#define TSL2772_CNTRL 0x00 488c2ecf20Sopenharmony_ci#define TSL2772_ALS_TIME 0X01 498c2ecf20Sopenharmony_ci#define TSL2772_PRX_TIME 0x02 508c2ecf20Sopenharmony_ci#define TSL2772_WAIT_TIME 0x03 518c2ecf20Sopenharmony_ci#define TSL2772_ALS_MINTHRESHLO 0X04 528c2ecf20Sopenharmony_ci#define TSL2772_ALS_MINTHRESHHI 0X05 538c2ecf20Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHLO 0X06 548c2ecf20Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHHI 0X07 558c2ecf20Sopenharmony_ci#define TSL2772_PRX_MINTHRESHLO 0X08 568c2ecf20Sopenharmony_ci#define TSL2772_PRX_MINTHRESHHI 0X09 578c2ecf20Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHLO 0X0A 588c2ecf20Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHHI 0X0B 598c2ecf20Sopenharmony_ci#define TSL2772_PERSISTENCE 0x0C 608c2ecf20Sopenharmony_ci#define TSL2772_ALS_PRX_CONFIG 0x0D 618c2ecf20Sopenharmony_ci#define TSL2772_PRX_COUNT 0x0E 628c2ecf20Sopenharmony_ci#define TSL2772_GAIN 0x0F 638c2ecf20Sopenharmony_ci#define TSL2772_NOTUSED 0x10 648c2ecf20Sopenharmony_ci#define TSL2772_REVID 0x11 658c2ecf20Sopenharmony_ci#define TSL2772_CHIPID 0x12 668c2ecf20Sopenharmony_ci#define TSL2772_STATUS 0x13 678c2ecf20Sopenharmony_ci#define TSL2772_ALS_CHAN0LO 0x14 688c2ecf20Sopenharmony_ci#define TSL2772_ALS_CHAN0HI 0x15 698c2ecf20Sopenharmony_ci#define TSL2772_ALS_CHAN1LO 0x16 708c2ecf20Sopenharmony_ci#define TSL2772_ALS_CHAN1HI 0x17 718c2ecf20Sopenharmony_ci#define TSL2772_PRX_LO 0x18 728c2ecf20Sopenharmony_ci#define TSL2772_PRX_HI 0x19 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* tsl2772 cmd reg masks */ 758c2ecf20Sopenharmony_ci#define TSL2772_CMD_REG 0x80 768c2ecf20Sopenharmony_ci#define TSL2772_CMD_SPL_FN 0x60 778c2ecf20Sopenharmony_ci#define TSL2772_CMD_REPEAT_PROTO 0x00 788c2ecf20Sopenharmony_ci#define TSL2772_CMD_AUTOINC_PROTO 0x20 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci#define TSL2772_CMD_PROX_INT_CLR 0X05 818c2ecf20Sopenharmony_ci#define TSL2772_CMD_ALS_INT_CLR 0x06 828c2ecf20Sopenharmony_ci#define TSL2772_CMD_PROXALS_INT_CLR 0X07 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* tsl2772 cntrl reg masks */ 858c2ecf20Sopenharmony_ci#define TSL2772_CNTL_ADC_ENBL 0x02 868c2ecf20Sopenharmony_ci#define TSL2772_CNTL_PWR_ON 0x01 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* tsl2772 status reg masks */ 898c2ecf20Sopenharmony_ci#define TSL2772_STA_ADC_VALID 0x01 908c2ecf20Sopenharmony_ci#define TSL2772_STA_PRX_VALID 0x02 918c2ecf20Sopenharmony_ci#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ 928c2ecf20Sopenharmony_ci TSL2772_STA_PRX_VALID) 938c2ecf20Sopenharmony_ci#define TSL2772_STA_ALS_INTR 0x10 948c2ecf20Sopenharmony_ci#define TSL2772_STA_PRX_INTR 0x20 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci/* tsl2772 cntrl reg masks */ 978c2ecf20Sopenharmony_ci#define TSL2772_CNTL_REG_CLEAR 0x00 988c2ecf20Sopenharmony_ci#define TSL2772_CNTL_PROX_INT_ENBL 0X20 998c2ecf20Sopenharmony_ci#define TSL2772_CNTL_ALS_INT_ENBL 0X10 1008c2ecf20Sopenharmony_ci#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 1018c2ecf20Sopenharmony_ci#define TSL2772_CNTL_PROX_DET_ENBL 0X04 1028c2ecf20Sopenharmony_ci#define TSL2772_CNTL_PWRON 0x01 1038c2ecf20Sopenharmony_ci#define TSL2772_CNTL_ALSPON_ENBL 0x03 1048c2ecf20Sopenharmony_ci#define TSL2772_CNTL_INTALSPON_ENBL 0x13 1058c2ecf20Sopenharmony_ci#define TSL2772_CNTL_PROXPON_ENBL 0x0F 1068c2ecf20Sopenharmony_ci#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MIN 250 1098c2ecf20Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MAX 4000 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define TSL2772_MAX_PROX_LEDS 2 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#define TSL2772_BOOT_MIN_SLEEP_TIME 10000 1148c2ecf20Sopenharmony_ci#define TSL2772_BOOT_MAX_SLEEP_TIME 28000 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* Device family members */ 1178c2ecf20Sopenharmony_cienum { 1188c2ecf20Sopenharmony_ci tsl2571, 1198c2ecf20Sopenharmony_ci tsl2671, 1208c2ecf20Sopenharmony_ci tmd2671, 1218c2ecf20Sopenharmony_ci tsl2771, 1228c2ecf20Sopenharmony_ci tmd2771, 1238c2ecf20Sopenharmony_ci tsl2572, 1248c2ecf20Sopenharmony_ci tsl2672, 1258c2ecf20Sopenharmony_ci tmd2672, 1268c2ecf20Sopenharmony_ci tsl2772, 1278c2ecf20Sopenharmony_ci tmd2772, 1288c2ecf20Sopenharmony_ci apds9930, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cienum { 1328c2ecf20Sopenharmony_ci TSL2772_CHIP_UNKNOWN = 0, 1338c2ecf20Sopenharmony_ci TSL2772_CHIP_WORKING = 1, 1348c2ecf20Sopenharmony_ci TSL2772_CHIP_SUSPENDED = 2 1358c2ecf20Sopenharmony_ci}; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cienum { 1388c2ecf20Sopenharmony_ci TSL2772_SUPPLY_VDD = 0, 1398c2ecf20Sopenharmony_ci TSL2772_SUPPLY_VDDIO = 1, 1408c2ecf20Sopenharmony_ci TSL2772_NUM_SUPPLIES = 2 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/* Per-device data */ 1448c2ecf20Sopenharmony_cistruct tsl2772_als_info { 1458c2ecf20Sopenharmony_ci u16 als_ch0; 1468c2ecf20Sopenharmony_ci u16 als_ch1; 1478c2ecf20Sopenharmony_ci u16 lux; 1488c2ecf20Sopenharmony_ci}; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct tsl2772_chip_info { 1518c2ecf20Sopenharmony_ci int chan_table_elements; 1528c2ecf20Sopenharmony_ci struct iio_chan_spec channel_with_events[4]; 1538c2ecf20Sopenharmony_ci struct iio_chan_spec channel_without_events[4]; 1548c2ecf20Sopenharmony_ci const struct iio_info *info; 1558c2ecf20Sopenharmony_ci}; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic const int tsl2772_led_currents[][2] = { 1588c2ecf20Sopenharmony_ci { 100000, TSL2772_100_mA }, 1598c2ecf20Sopenharmony_ci { 50000, TSL2772_50_mA }, 1608c2ecf20Sopenharmony_ci { 25000, TSL2772_25_mA }, 1618c2ecf20Sopenharmony_ci { 13000, TSL2772_13_mA }, 1628c2ecf20Sopenharmony_ci { 0, 0 } 1638c2ecf20Sopenharmony_ci}; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistruct tsl2772_chip { 1668c2ecf20Sopenharmony_ci kernel_ulong_t id; 1678c2ecf20Sopenharmony_ci struct mutex prox_mutex; 1688c2ecf20Sopenharmony_ci struct mutex als_mutex; 1698c2ecf20Sopenharmony_ci struct i2c_client *client; 1708c2ecf20Sopenharmony_ci struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES]; 1718c2ecf20Sopenharmony_ci u16 prox_data; 1728c2ecf20Sopenharmony_ci struct tsl2772_als_info als_cur_info; 1738c2ecf20Sopenharmony_ci struct tsl2772_settings settings; 1748c2ecf20Sopenharmony_ci struct tsl2772_platform_data *pdata; 1758c2ecf20Sopenharmony_ci int als_gain_time_scale; 1768c2ecf20Sopenharmony_ci int als_saturation; 1778c2ecf20Sopenharmony_ci int tsl2772_chip_status; 1788c2ecf20Sopenharmony_ci u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; 1798c2ecf20Sopenharmony_ci const struct tsl2772_chip_info *chip_info; 1808c2ecf20Sopenharmony_ci const struct iio_info *info; 1818c2ecf20Sopenharmony_ci s64 event_timestamp; 1828c2ecf20Sopenharmony_ci /* 1838c2ecf20Sopenharmony_ci * This structure is intentionally large to accommodate 1848c2ecf20Sopenharmony_ci * updates via sysfs. 1858c2ecf20Sopenharmony_ci * Sized to 9 = max 8 segments + 1 termination segment 1868c2ecf20Sopenharmony_ci */ 1878c2ecf20Sopenharmony_ci struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; 1888c2ecf20Sopenharmony_ci}; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci/* 1918c2ecf20Sopenharmony_ci * Different devices require different coefficents, and these numbers were 1928c2ecf20Sopenharmony_ci * derived from the 'Lux Equation' section of the various device datasheets. 1938c2ecf20Sopenharmony_ci * All of these coefficients assume a Glass Attenuation (GA) factor of 1. 1948c2ecf20Sopenharmony_ci * The coefficients are multiplied by 1000 to avoid floating point operations. 1958c2ecf20Sopenharmony_ci * The two rows in each table correspond to the Lux1 and Lux2 equations from 1968c2ecf20Sopenharmony_ci * the datasheets. 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistatic const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 1998c2ecf20Sopenharmony_ci { 53000, 106000 }, 2008c2ecf20Sopenharmony_ci { 31800, 53000 }, 2018c2ecf20Sopenharmony_ci { 0, 0 }, 2028c2ecf20Sopenharmony_ci}; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_cistatic const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 2058c2ecf20Sopenharmony_ci { 24000, 48000 }, 2068c2ecf20Sopenharmony_ci { 14400, 24000 }, 2078c2ecf20Sopenharmony_ci { 0, 0 }, 2088c2ecf20Sopenharmony_ci}; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 2118c2ecf20Sopenharmony_ci { 60000, 112200 }, 2128c2ecf20Sopenharmony_ci { 37800, 60000 }, 2138c2ecf20Sopenharmony_ci { 0, 0 }, 2148c2ecf20Sopenharmony_ci}; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_cistatic const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 2178c2ecf20Sopenharmony_ci { 20000, 35000 }, 2188c2ecf20Sopenharmony_ci { 12600, 20000 }, 2198c2ecf20Sopenharmony_ci { 0, 0 }, 2208c2ecf20Sopenharmony_ci}; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_cistatic const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 2238c2ecf20Sopenharmony_ci { 52000, 96824 }, 2248c2ecf20Sopenharmony_ci { 38792, 67132 }, 2258c2ecf20Sopenharmony_ci { 0, 0 }, 2268c2ecf20Sopenharmony_ci}; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { 2298c2ecf20Sopenharmony_ci [tsl2571] = tsl2x71_lux_table, 2308c2ecf20Sopenharmony_ci [tsl2671] = tsl2x71_lux_table, 2318c2ecf20Sopenharmony_ci [tmd2671] = tmd2x71_lux_table, 2328c2ecf20Sopenharmony_ci [tsl2771] = tsl2x71_lux_table, 2338c2ecf20Sopenharmony_ci [tmd2771] = tmd2x71_lux_table, 2348c2ecf20Sopenharmony_ci [tsl2572] = tsl2x72_lux_table, 2358c2ecf20Sopenharmony_ci [tsl2672] = tsl2x72_lux_table, 2368c2ecf20Sopenharmony_ci [tmd2672] = tmd2x72_lux_table, 2378c2ecf20Sopenharmony_ci [tsl2772] = tsl2x72_lux_table, 2388c2ecf20Sopenharmony_ci [tmd2772] = tmd2x72_lux_table, 2398c2ecf20Sopenharmony_ci [apds9930] = apds9930_lux_table, 2408c2ecf20Sopenharmony_ci}; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct tsl2772_settings tsl2772_default_settings = { 2438c2ecf20Sopenharmony_ci .als_time = 255, /* 2.72 / 2.73 ms */ 2448c2ecf20Sopenharmony_ci .als_gain = 0, 2458c2ecf20Sopenharmony_ci .prox_time = 255, /* 2.72 / 2.73 ms */ 2468c2ecf20Sopenharmony_ci .prox_gain = 0, 2478c2ecf20Sopenharmony_ci .wait_time = 255, 2488c2ecf20Sopenharmony_ci .als_prox_config = 0, 2498c2ecf20Sopenharmony_ci .als_gain_trim = 1000, 2508c2ecf20Sopenharmony_ci .als_cal_target = 150, 2518c2ecf20Sopenharmony_ci .als_persistence = 1, 2528c2ecf20Sopenharmony_ci .als_interrupt_en = false, 2538c2ecf20Sopenharmony_ci .als_thresh_low = 200, 2548c2ecf20Sopenharmony_ci .als_thresh_high = 256, 2558c2ecf20Sopenharmony_ci .prox_persistence = 1, 2568c2ecf20Sopenharmony_ci .prox_interrupt_en = false, 2578c2ecf20Sopenharmony_ci .prox_thres_low = 0, 2588c2ecf20Sopenharmony_ci .prox_thres_high = 512, 2598c2ecf20Sopenharmony_ci .prox_max_samples_cal = 30, 2608c2ecf20Sopenharmony_ci .prox_pulse_count = 8, 2618c2ecf20Sopenharmony_ci .prox_diode = TSL2772_DIODE1, 2628c2ecf20Sopenharmony_ci .prox_power = TSL2772_100_mA 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_cistatic const s16 tsl2772_als_gain[] = { 2668c2ecf20Sopenharmony_ci 1, 2678c2ecf20Sopenharmony_ci 8, 2688c2ecf20Sopenharmony_ci 16, 2698c2ecf20Sopenharmony_ci 120 2708c2ecf20Sopenharmony_ci}; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic const s16 tsl2772_prox_gain[] = { 2738c2ecf20Sopenharmony_ci 1, 2748c2ecf20Sopenharmony_ci 2, 2758c2ecf20Sopenharmony_ci 4, 2768c2ecf20Sopenharmony_ci 8 2778c2ecf20Sopenharmony_ci}; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic const int tsl2772_int_time_avail[][6] = { 2808c2ecf20Sopenharmony_ci [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, 2818c2ecf20Sopenharmony_ci [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, 2828c2ecf20Sopenharmony_ci [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, 2838c2ecf20Sopenharmony_ci [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, 2848c2ecf20Sopenharmony_ci [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, 2858c2ecf20Sopenharmony_ci [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, 2868c2ecf20Sopenharmony_ci [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, 2878c2ecf20Sopenharmony_ci [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, 2888c2ecf20Sopenharmony_ci [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, 2898c2ecf20Sopenharmony_ci [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, 2908c2ecf20Sopenharmony_ci [apds9930] = { 0, 2730, 0, 2730, 0, 699000 }, 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci/* Channel variations */ 2988c2ecf20Sopenharmony_cienum { 2998c2ecf20Sopenharmony_ci ALS, 3008c2ecf20Sopenharmony_ci PRX, 3018c2ecf20Sopenharmony_ci ALSPRX, 3028c2ecf20Sopenharmony_ci PRX2, 3038c2ecf20Sopenharmony_ci ALSPRX2, 3048c2ecf20Sopenharmony_ci}; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic const u8 device_channel_config[] = { 3078c2ecf20Sopenharmony_ci [tsl2571] = ALS, 3088c2ecf20Sopenharmony_ci [tsl2671] = PRX, 3098c2ecf20Sopenharmony_ci [tmd2671] = PRX, 3108c2ecf20Sopenharmony_ci [tsl2771] = ALSPRX, 3118c2ecf20Sopenharmony_ci [tmd2771] = ALSPRX, 3128c2ecf20Sopenharmony_ci [tsl2572] = ALS, 3138c2ecf20Sopenharmony_ci [tsl2672] = PRX2, 3148c2ecf20Sopenharmony_ci [tmd2672] = PRX2, 3158c2ecf20Sopenharmony_ci [tsl2772] = ALSPRX2, 3168c2ecf20Sopenharmony_ci [tmd2772] = ALSPRX2, 3178c2ecf20Sopenharmony_ci [apds9930] = ALSPRX2, 3188c2ecf20Sopenharmony_ci}; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int tsl2772_read_status(struct tsl2772_chip *chip) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci int ret; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 3258c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_STATUS); 3268c2ecf20Sopenharmony_ci if (ret < 0) 3278c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3288c2ecf20Sopenharmony_ci "%s: failed to read STATUS register: %d\n", __func__, 3298c2ecf20Sopenharmony_ci ret); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci return ret; 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_cistatic int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) 3358c2ecf20Sopenharmony_ci{ 3368c2ecf20Sopenharmony_ci int ret; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(chip->client, 3398c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CNTRL, data); 3408c2ecf20Sopenharmony_ci if (ret < 0) { 3418c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3428c2ecf20Sopenharmony_ci "%s: failed to write to control register %x: %d\n", 3438c2ecf20Sopenharmony_ci __func__, data, ret); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return ret; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, 3508c2ecf20Sopenharmony_ci int upper_reg) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci u8 buf[2]; 3538c2ecf20Sopenharmony_ci int ret; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 3568c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | 3578c2ecf20Sopenharmony_ci lower_reg); 3588c2ecf20Sopenharmony_ci if (ret < 0) { 3598c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3608c2ecf20Sopenharmony_ci "%s: failed to enable auto increment protocol: %d\n", 3618c2ecf20Sopenharmony_ci __func__, ret); 3628c2ecf20Sopenharmony_ci return ret; 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 3668c2ecf20Sopenharmony_ci TSL2772_CMD_REG | lower_reg); 3678c2ecf20Sopenharmony_ci if (ret < 0) { 3688c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3698c2ecf20Sopenharmony_ci "%s: failed to read from register %x: %d\n", __func__, 3708c2ecf20Sopenharmony_ci lower_reg, ret); 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci buf[0] = ret; 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 3768c2ecf20Sopenharmony_ci TSL2772_CMD_REG | upper_reg); 3778c2ecf20Sopenharmony_ci if (ret < 0) { 3788c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3798c2ecf20Sopenharmony_ci "%s: failed to read from register %x: %d\n", __func__, 3808c2ecf20Sopenharmony_ci upper_reg, ret); 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci } 3838c2ecf20Sopenharmony_ci buf[1] = ret; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 3868c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | 3878c2ecf20Sopenharmony_ci lower_reg); 3888c2ecf20Sopenharmony_ci if (ret < 0) { 3898c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 3908c2ecf20Sopenharmony_ci "%s: failed to enable repeated byte protocol: %d\n", 3918c2ecf20Sopenharmony_ci __func__, ret); 3928c2ecf20Sopenharmony_ci return ret; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return le16_to_cpup((const __le16 *)&buf[0]); 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/** 3998c2ecf20Sopenharmony_ci * tsl2772_get_lux() - Reads and calculates current lux value. 4008c2ecf20Sopenharmony_ci * @indio_dev: pointer to IIO device 4018c2ecf20Sopenharmony_ci * 4028c2ecf20Sopenharmony_ci * The raw ch0 and ch1 values of the ambient light sensed in the last 4038c2ecf20Sopenharmony_ci * integration cycle are read from the device. The raw values are multiplied 4048c2ecf20Sopenharmony_ci * by a device-specific scale factor, and divided by the integration time and 4058c2ecf20Sopenharmony_ci * device gain. The code supports multiple lux equations through the lux table 4068c2ecf20Sopenharmony_ci * coefficients. A lux gain trim is applied to each lux equation, and then the 4078c2ecf20Sopenharmony_ci * maximum lux within the interval 0..65535 is selected. 4088c2ecf20Sopenharmony_ci */ 4098c2ecf20Sopenharmony_cistatic int tsl2772_get_lux(struct iio_dev *indio_dev) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 4128c2ecf20Sopenharmony_ci struct tsl2772_lux *p; 4138c2ecf20Sopenharmony_ci int max_lux, ret; 4148c2ecf20Sopenharmony_ci bool overflow; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci mutex_lock(&chip->als_mutex); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { 4198c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "%s: device is not enabled\n", 4208c2ecf20Sopenharmony_ci __func__); 4218c2ecf20Sopenharmony_ci ret = -EBUSY; 4228c2ecf20Sopenharmony_ci goto out_unlock; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci ret = tsl2772_read_status(chip); 4268c2ecf20Sopenharmony_ci if (ret < 0) 4278c2ecf20Sopenharmony_ci goto out_unlock; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (!(ret & TSL2772_STA_ADC_VALID)) { 4308c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 4318c2ecf20Sopenharmony_ci "%s: data not valid yet\n", __func__); 4328c2ecf20Sopenharmony_ci ret = chip->als_cur_info.lux; /* return LAST VALUE */ 4338c2ecf20Sopenharmony_ci goto out_unlock; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, 4378c2ecf20Sopenharmony_ci TSL2772_ALS_CHAN0HI); 4388c2ecf20Sopenharmony_ci if (ret < 0) 4398c2ecf20Sopenharmony_ci goto out_unlock; 4408c2ecf20Sopenharmony_ci chip->als_cur_info.als_ch0 = ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, 4438c2ecf20Sopenharmony_ci TSL2772_ALS_CHAN1HI); 4448c2ecf20Sopenharmony_ci if (ret < 0) 4458c2ecf20Sopenharmony_ci goto out_unlock; 4468c2ecf20Sopenharmony_ci chip->als_cur_info.als_ch1 = ret; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { 4498c2ecf20Sopenharmony_ci max_lux = TSL2772_LUX_CALC_OVER_FLOW; 4508c2ecf20Sopenharmony_ci goto update_struct_with_max_lux; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (!chip->als_cur_info.als_ch0) { 4548c2ecf20Sopenharmony_ci /* have no data, so return LAST VALUE */ 4558c2ecf20Sopenharmony_ci ret = chip->als_cur_info.lux; 4568c2ecf20Sopenharmony_ci goto out_unlock; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci max_lux = 0; 4608c2ecf20Sopenharmony_ci overflow = false; 4618c2ecf20Sopenharmony_ci for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; 4628c2ecf20Sopenharmony_ci p++) { 4638c2ecf20Sopenharmony_ci int lux; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci lux = ((chip->als_cur_info.als_ch0 * p->ch0) - 4668c2ecf20Sopenharmony_ci (chip->als_cur_info.als_ch1 * p->ch1)) / 4678c2ecf20Sopenharmony_ci chip->als_gain_time_scale; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* 4708c2ecf20Sopenharmony_ci * The als_gain_trim can have a value within the range 250..4000 4718c2ecf20Sopenharmony_ci * and is a multiplier for the lux. A trim of 1000 makes no 4728c2ecf20Sopenharmony_ci * changes to the lux, less than 1000 scales it down, and 4738c2ecf20Sopenharmony_ci * greater than 1000 scales it up. 4748c2ecf20Sopenharmony_ci */ 4758c2ecf20Sopenharmony_ci lux = (lux * chip->settings.als_gain_trim) / 1000; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci if (lux > TSL2772_LUX_CALC_OVER_FLOW) { 4788c2ecf20Sopenharmony_ci overflow = true; 4798c2ecf20Sopenharmony_ci continue; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci max_lux = max(max_lux, lux); 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (overflow && max_lux == 0) 4868c2ecf20Sopenharmony_ci max_lux = TSL2772_LUX_CALC_OVER_FLOW; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ciupdate_struct_with_max_lux: 4898c2ecf20Sopenharmony_ci chip->als_cur_info.lux = max_lux; 4908c2ecf20Sopenharmony_ci ret = max_lux; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ciout_unlock: 4938c2ecf20Sopenharmony_ci mutex_unlock(&chip->als_mutex); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci return ret; 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci/** 4998c2ecf20Sopenharmony_ci * tsl2772_get_prox() - Reads proximity data registers and updates 5008c2ecf20Sopenharmony_ci * chip->prox_data. 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * @indio_dev: pointer to IIO device 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_cistatic int tsl2772_get_prox(struct iio_dev *indio_dev) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 5078c2ecf20Sopenharmony_ci int ret; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci mutex_lock(&chip->prox_mutex); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci ret = tsl2772_read_status(chip); 5128c2ecf20Sopenharmony_ci if (ret < 0) 5138c2ecf20Sopenharmony_ci goto prox_poll_err; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci switch (chip->id) { 5168c2ecf20Sopenharmony_ci case tsl2571: 5178c2ecf20Sopenharmony_ci case tsl2671: 5188c2ecf20Sopenharmony_ci case tmd2671: 5198c2ecf20Sopenharmony_ci case tsl2771: 5208c2ecf20Sopenharmony_ci case tmd2771: 5218c2ecf20Sopenharmony_ci if (!(ret & TSL2772_STA_ADC_VALID)) { 5228c2ecf20Sopenharmony_ci ret = -EINVAL; 5238c2ecf20Sopenharmony_ci goto prox_poll_err; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci break; 5268c2ecf20Sopenharmony_ci case tsl2572: 5278c2ecf20Sopenharmony_ci case tsl2672: 5288c2ecf20Sopenharmony_ci case tmd2672: 5298c2ecf20Sopenharmony_ci case tsl2772: 5308c2ecf20Sopenharmony_ci case tmd2772: 5318c2ecf20Sopenharmony_ci case apds9930: 5328c2ecf20Sopenharmony_ci if (!(ret & TSL2772_STA_PRX_VALID)) { 5338c2ecf20Sopenharmony_ci ret = -EINVAL; 5348c2ecf20Sopenharmony_ci goto prox_poll_err; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci break; 5378c2ecf20Sopenharmony_ci } 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); 5408c2ecf20Sopenharmony_ci if (ret < 0) 5418c2ecf20Sopenharmony_ci goto prox_poll_err; 5428c2ecf20Sopenharmony_ci chip->prox_data = ret; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ciprox_poll_err: 5458c2ecf20Sopenharmony_ci mutex_unlock(&chip->prox_mutex); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return ret; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int tsl2772_read_prox_led_current(struct tsl2772_chip *chip) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct device_node *of_node = chip->client->dev.of_node; 5538c2ecf20Sopenharmony_ci int ret, tmp, i; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci ret = of_property_read_u32(of_node, "led-max-microamp", &tmp); 5568c2ecf20Sopenharmony_ci if (ret < 0) 5578c2ecf20Sopenharmony_ci return ret; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci for (i = 0; tsl2772_led_currents[i][0] != 0; i++) { 5608c2ecf20Sopenharmony_ci if (tmp == tsl2772_led_currents[i][0]) { 5618c2ecf20Sopenharmony_ci chip->settings.prox_power = tsl2772_led_currents[i][1]; 5628c2ecf20Sopenharmony_ci return 0; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n", 5678c2ecf20Sopenharmony_ci tmp); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci return -EINVAL; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci} 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_cistatic int tsl2772_read_prox_diodes(struct tsl2772_chip *chip) 5748c2ecf20Sopenharmony_ci{ 5758c2ecf20Sopenharmony_ci struct device_node *of_node = chip->client->dev.of_node; 5768c2ecf20Sopenharmony_ci int i, ret, num_leds, prox_diode_mask; 5778c2ecf20Sopenharmony_ci u32 leds[TSL2772_MAX_PROX_LEDS]; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes"); 5808c2ecf20Sopenharmony_ci if (ret < 0) 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci num_leds = ret; 5848c2ecf20Sopenharmony_ci if (num_leds > TSL2772_MAX_PROX_LEDS) 5858c2ecf20Sopenharmony_ci num_leds = TSL2772_MAX_PROX_LEDS; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes", 5888c2ecf20Sopenharmony_ci leds, num_leds); 5898c2ecf20Sopenharmony_ci if (ret < 0) { 5908c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 5918c2ecf20Sopenharmony_ci "Invalid value for amstaos,proximity-diodes: %d.\n", 5928c2ecf20Sopenharmony_ci ret); 5938c2ecf20Sopenharmony_ci return ret; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci prox_diode_mask = 0; 5978c2ecf20Sopenharmony_ci for (i = 0; i < num_leds; i++) { 5988c2ecf20Sopenharmony_ci if (leds[i] == 0) 5998c2ecf20Sopenharmony_ci prox_diode_mask |= TSL2772_DIODE0; 6008c2ecf20Sopenharmony_ci else if (leds[i] == 1) 6018c2ecf20Sopenharmony_ci prox_diode_mask |= TSL2772_DIODE1; 6028c2ecf20Sopenharmony_ci else { 6038c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 6048c2ecf20Sopenharmony_ci "Invalid value %d in amstaos,proximity-diodes.\n", 6058c2ecf20Sopenharmony_ci leds[i]); 6068c2ecf20Sopenharmony_ci return -EINVAL; 6078c2ecf20Sopenharmony_ci } 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci chip->settings.prox_diode = prox_diode_mask; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void tsl2772_parse_dt(struct tsl2772_chip *chip) 6158c2ecf20Sopenharmony_ci{ 6168c2ecf20Sopenharmony_ci tsl2772_read_prox_led_current(chip); 6178c2ecf20Sopenharmony_ci tsl2772_read_prox_diodes(chip); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci/** 6218c2ecf20Sopenharmony_ci * tsl2772_defaults() - Populates the device nominal operating parameters 6228c2ecf20Sopenharmony_ci * with those provided by a 'platform' data struct or 6238c2ecf20Sopenharmony_ci * with prefined defaults. 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * @chip: pointer to device structure. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_cistatic void tsl2772_defaults(struct tsl2772_chip *chip) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci /* If Operational settings defined elsewhere.. */ 6308c2ecf20Sopenharmony_ci if (chip->pdata && chip->pdata->platform_default_settings) 6318c2ecf20Sopenharmony_ci memcpy(&chip->settings, chip->pdata->platform_default_settings, 6328c2ecf20Sopenharmony_ci sizeof(tsl2772_default_settings)); 6338c2ecf20Sopenharmony_ci else 6348c2ecf20Sopenharmony_ci memcpy(&chip->settings, &tsl2772_default_settings, 6358c2ecf20Sopenharmony_ci sizeof(tsl2772_default_settings)); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* Load up the proper lux table. */ 6388c2ecf20Sopenharmony_ci if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) 6398c2ecf20Sopenharmony_ci memcpy(chip->tsl2772_device_lux, 6408c2ecf20Sopenharmony_ci chip->pdata->platform_lux_table, 6418c2ecf20Sopenharmony_ci sizeof(chip->pdata->platform_lux_table)); 6428c2ecf20Sopenharmony_ci else 6438c2ecf20Sopenharmony_ci memcpy(chip->tsl2772_device_lux, 6448c2ecf20Sopenharmony_ci tsl2772_default_lux_table_group[chip->id], 6458c2ecf20Sopenharmony_ci TSL2772_DEFAULT_TABLE_BYTES); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci tsl2772_parse_dt(chip); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci/** 6518c2ecf20Sopenharmony_ci * tsl2772_als_calibrate() - Obtain single reading and calculate 6528c2ecf20Sopenharmony_ci * the als_gain_trim. 6538c2ecf20Sopenharmony_ci * 6548c2ecf20Sopenharmony_ci * @indio_dev: pointer to IIO device 6558c2ecf20Sopenharmony_ci */ 6568c2ecf20Sopenharmony_cistatic int tsl2772_als_calibrate(struct iio_dev *indio_dev) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 6598c2ecf20Sopenharmony_ci int ret, lux_val; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 6628c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CNTRL); 6638c2ecf20Sopenharmony_ci if (ret < 0) { 6648c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 6658c2ecf20Sopenharmony_ci "%s: failed to read from the CNTRL register\n", 6668c2ecf20Sopenharmony_ci __func__); 6678c2ecf20Sopenharmony_ci return ret; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) 6718c2ecf20Sopenharmony_ci != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { 6728c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 6738c2ecf20Sopenharmony_ci "%s: Device is not powered on and/or ADC is not enabled\n", 6748c2ecf20Sopenharmony_ci __func__); 6758c2ecf20Sopenharmony_ci return -EINVAL; 6768c2ecf20Sopenharmony_ci } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { 6778c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 6788c2ecf20Sopenharmony_ci "%s: The two ADC channels have not completed an integration cycle\n", 6798c2ecf20Sopenharmony_ci __func__); 6808c2ecf20Sopenharmony_ci return -ENODATA; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci lux_val = tsl2772_get_lux(indio_dev); 6848c2ecf20Sopenharmony_ci if (lux_val < 0) { 6858c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 6868c2ecf20Sopenharmony_ci "%s: failed to get lux\n", __func__); 6878c2ecf20Sopenharmony_ci return lux_val; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci if (lux_val == 0) 6908c2ecf20Sopenharmony_ci return -ERANGE; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / 6938c2ecf20Sopenharmony_ci lux_val; 6948c2ecf20Sopenharmony_ci if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) 6958c2ecf20Sopenharmony_ci return -ERANGE; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci chip->settings.als_gain_trim = ret; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return ret; 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic void tsl2772_disable_regulators_action(void *_data) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = _data; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int tsl2772_chip_on(struct iio_dev *indio_dev) 7108c2ecf20Sopenharmony_ci{ 7118c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 7128c2ecf20Sopenharmony_ci int ret, i, als_count, als_time_us; 7138c2ecf20Sopenharmony_ci u8 *dev_reg, reg_val; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci /* Non calculated parameters */ 7168c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; 7178c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; 7188c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; 7198c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = 7208c2ecf20Sopenharmony_ci chip->settings.als_prox_config; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = 7238c2ecf20Sopenharmony_ci (chip->settings.als_thresh_low) & 0xFF; 7248c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = 7258c2ecf20Sopenharmony_ci (chip->settings.als_thresh_low >> 8) & 0xFF; 7268c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = 7278c2ecf20Sopenharmony_ci (chip->settings.als_thresh_high) & 0xFF; 7288c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = 7298c2ecf20Sopenharmony_ci (chip->settings.als_thresh_high >> 8) & 0xFF; 7308c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PERSISTENCE] = 7318c2ecf20Sopenharmony_ci (chip->settings.prox_persistence & 0xFF) << 4 | 7328c2ecf20Sopenharmony_ci (chip->settings.als_persistence & 0xFF); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_COUNT] = 7358c2ecf20Sopenharmony_ci chip->settings.prox_pulse_count; 7368c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = 7378c2ecf20Sopenharmony_ci (chip->settings.prox_thres_low) & 0xFF; 7388c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = 7398c2ecf20Sopenharmony_ci (chip->settings.prox_thres_low >> 8) & 0xFF; 7408c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = 7418c2ecf20Sopenharmony_ci (chip->settings.prox_thres_high) & 0xFF; 7428c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = 7438c2ecf20Sopenharmony_ci (chip->settings.prox_thres_high >> 8) & 0xFF; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* and make sure we're not already on */ 7468c2ecf20Sopenharmony_ci if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { 7478c2ecf20Sopenharmony_ci /* if forcing a register update - turn off, then on */ 7488c2ecf20Sopenharmony_ci dev_info(&chip->client->dev, "device is already enabled\n"); 7498c2ecf20Sopenharmony_ci return -EINVAL; 7508c2ecf20Sopenharmony_ci } 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Set the gain based on tsl2772_settings struct */ 7538c2ecf20Sopenharmony_ci chip->tsl2772_config[TSL2772_GAIN] = 7548c2ecf20Sopenharmony_ci (chip->settings.als_gain & 0xFF) | 7558c2ecf20Sopenharmony_ci ((chip->settings.prox_gain & 0xFF) << 2) | 7568c2ecf20Sopenharmony_ci (chip->settings.prox_diode << 4) | 7578c2ecf20Sopenharmony_ci (chip->settings.prox_power << 6); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci /* set chip time scaling and saturation */ 7608c2ecf20Sopenharmony_ci als_count = 256 - chip->settings.als_time; 7618c2ecf20Sopenharmony_ci als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; 7628c2ecf20Sopenharmony_ci chip->als_saturation = als_count * 768; /* 75% of full scale */ 7638c2ecf20Sopenharmony_ci chip->als_gain_time_scale = als_time_us * 7648c2ecf20Sopenharmony_ci tsl2772_als_gain[chip->settings.als_gain]; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci /* 7678c2ecf20Sopenharmony_ci * TSL2772 Specific power-on / adc enable sequence 7688c2ecf20Sopenharmony_ci * Power on the device 1st. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_ci ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); 7718c2ecf20Sopenharmony_ci if (ret < 0) 7728c2ecf20Sopenharmony_ci return ret; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* 7758c2ecf20Sopenharmony_ci * Use the following shadow copy for our delay before enabling ADC. 7768c2ecf20Sopenharmony_ci * Write all the registers. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ci for (i = 0, dev_reg = chip->tsl2772_config; 7798c2ecf20Sopenharmony_ci i < TSL2772_MAX_CONFIG_REG; i++) { 7808c2ecf20Sopenharmony_ci int reg = TSL2772_CMD_REG + i; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(chip->client, reg, 7838c2ecf20Sopenharmony_ci *dev_reg++); 7848c2ecf20Sopenharmony_ci if (ret < 0) { 7858c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 7868c2ecf20Sopenharmony_ci "%s: failed to write to register %x: %d\n", 7878c2ecf20Sopenharmony_ci __func__, reg, ret); 7888c2ecf20Sopenharmony_ci return ret; 7898c2ecf20Sopenharmony_ci } 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci /* Power-on settling time */ 7938c2ecf20Sopenharmony_ci usleep_range(3000, 3500); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | 7968c2ecf20Sopenharmony_ci TSL2772_CNTL_PROX_DET_ENBL; 7978c2ecf20Sopenharmony_ci if (chip->settings.als_interrupt_en) 7988c2ecf20Sopenharmony_ci reg_val |= TSL2772_CNTL_ALS_INT_ENBL; 7998c2ecf20Sopenharmony_ci if (chip->settings.prox_interrupt_en) 8008c2ecf20Sopenharmony_ci reg_val |= TSL2772_CNTL_PROX_INT_ENBL; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci ret = tsl2772_write_control_reg(chip, reg_val); 8038c2ecf20Sopenharmony_ci if (ret < 0) 8048c2ecf20Sopenharmony_ci return ret; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 8078c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | 8088c2ecf20Sopenharmony_ci TSL2772_CMD_PROXALS_INT_CLR); 8098c2ecf20Sopenharmony_ci if (ret < 0) { 8108c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 8118c2ecf20Sopenharmony_ci "%s: failed to clear interrupt status: %d\n", 8128c2ecf20Sopenharmony_ci __func__, ret); 8138c2ecf20Sopenharmony_ci return ret; 8148c2ecf20Sopenharmony_ci } 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return ret; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic int tsl2772_chip_off(struct iio_dev *indio_dev) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci /* turn device off */ 8268c2ecf20Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; 8278c2ecf20Sopenharmony_ci return tsl2772_write_control_reg(chip, 0x00); 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic void tsl2772_chip_off_action(void *data) 8318c2ecf20Sopenharmony_ci{ 8328c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = data; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci tsl2772_chip_off(indio_dev); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/** 8388c2ecf20Sopenharmony_ci * tsl2772_invoke_change - power cycle the device to implement the user 8398c2ecf20Sopenharmony_ci * parameters 8408c2ecf20Sopenharmony_ci * @indio_dev: pointer to IIO device 8418c2ecf20Sopenharmony_ci * 8428c2ecf20Sopenharmony_ci * Obtain and lock both ALS and PROX resources, determine and save device state 8438c2ecf20Sopenharmony_ci * (On/Off), cycle device to implement updated parameter, put device back into 8448c2ecf20Sopenharmony_ci * proper state, and unlock resource. 8458c2ecf20Sopenharmony_ci */ 8468c2ecf20Sopenharmony_cistatic int tsl2772_invoke_change(struct iio_dev *indio_dev) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 8498c2ecf20Sopenharmony_ci int device_status = chip->tsl2772_chip_status; 8508c2ecf20Sopenharmony_ci int ret; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci mutex_lock(&chip->als_mutex); 8538c2ecf20Sopenharmony_ci mutex_lock(&chip->prox_mutex); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (device_status == TSL2772_CHIP_WORKING) { 8568c2ecf20Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 8578c2ecf20Sopenharmony_ci if (ret < 0) 8588c2ecf20Sopenharmony_ci goto unlock; 8598c2ecf20Sopenharmony_ci } 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci ret = tsl2772_chip_on(indio_dev); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ciunlock: 8648c2ecf20Sopenharmony_ci mutex_unlock(&chip->prox_mutex); 8658c2ecf20Sopenharmony_ci mutex_unlock(&chip->als_mutex); 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci return ret; 8688c2ecf20Sopenharmony_ci} 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_cistatic int tsl2772_prox_cal(struct iio_dev *indio_dev) 8718c2ecf20Sopenharmony_ci{ 8728c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 8738c2ecf20Sopenharmony_ci int prox_history[MAX_SAMPLES_CAL + 1]; 8748c2ecf20Sopenharmony_ci int i, ret, mean, max, sample_sum; 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci if (chip->settings.prox_max_samples_cal < 1 || 8778c2ecf20Sopenharmony_ci chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) 8788c2ecf20Sopenharmony_ci return -EINVAL; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { 8818c2ecf20Sopenharmony_ci usleep_range(15000, 17500); 8828c2ecf20Sopenharmony_ci ret = tsl2772_get_prox(indio_dev); 8838c2ecf20Sopenharmony_ci if (ret < 0) 8848c2ecf20Sopenharmony_ci return ret; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci prox_history[i] = chip->prox_data; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci sample_sum = 0; 8908c2ecf20Sopenharmony_ci max = INT_MIN; 8918c2ecf20Sopenharmony_ci for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { 8928c2ecf20Sopenharmony_ci sample_sum += prox_history[i]; 8938c2ecf20Sopenharmony_ci max = max(max, prox_history[i]); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci mean = sample_sum / chip->settings.prox_max_samples_cal; 8968c2ecf20Sopenharmony_ci 8978c2ecf20Sopenharmony_ci chip->settings.prox_thres_high = (max << 1) - mean; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int tsl2772_read_avail(struct iio_dev *indio_dev, 9038c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 9048c2ecf20Sopenharmony_ci const int **vals, int *type, int *length, 9058c2ecf20Sopenharmony_ci long mask) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci switch (mask) { 9108c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 9118c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 9128c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); 9138c2ecf20Sopenharmony_ci *vals = tsl2772_int_calibscale_avail; 9148c2ecf20Sopenharmony_ci } else { 9158c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); 9168c2ecf20Sopenharmony_ci *vals = tsl2772_prox_calibscale_avail; 9178c2ecf20Sopenharmony_ci } 9188c2ecf20Sopenharmony_ci *type = IIO_VAL_INT; 9198c2ecf20Sopenharmony_ci return IIO_AVAIL_LIST; 9208c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 9218c2ecf20Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); 9228c2ecf20Sopenharmony_ci *vals = tsl2772_int_time_avail[chip->id]; 9238c2ecf20Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 9248c2ecf20Sopenharmony_ci return IIO_AVAIL_RANGE; 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci return -EINVAL; 9288c2ecf20Sopenharmony_ci} 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_cistatic ssize_t in_illuminance0_target_input_show(struct device *dev, 9318c2ecf20Sopenharmony_ci struct device_attribute *attr, 9328c2ecf20Sopenharmony_ci char *buf) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic ssize_t in_illuminance0_target_input_store(struct device *dev, 9408c2ecf20Sopenharmony_ci struct device_attribute *attr, 9418c2ecf20Sopenharmony_ci const char *buf, size_t len) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 9448c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 9458c2ecf20Sopenharmony_ci u16 value; 9468c2ecf20Sopenharmony_ci int ret; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci if (kstrtou16(buf, 0, &value)) 9498c2ecf20Sopenharmony_ci return -EINVAL; 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci chip->settings.als_cal_target = value; 9528c2ecf20Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 9538c2ecf20Sopenharmony_ci if (ret < 0) 9548c2ecf20Sopenharmony_ci return ret; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci return len; 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic ssize_t in_illuminance0_calibrate_store(struct device *dev, 9608c2ecf20Sopenharmony_ci struct device_attribute *attr, 9618c2ecf20Sopenharmony_ci const char *buf, size_t len) 9628c2ecf20Sopenharmony_ci{ 9638c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 9648c2ecf20Sopenharmony_ci bool value; 9658c2ecf20Sopenharmony_ci int ret; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if (kstrtobool(buf, &value) || !value) 9688c2ecf20Sopenharmony_ci return -EINVAL; 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci ret = tsl2772_als_calibrate(indio_dev); 9718c2ecf20Sopenharmony_ci if (ret < 0) 9728c2ecf20Sopenharmony_ci return ret; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 9758c2ecf20Sopenharmony_ci if (ret < 0) 9768c2ecf20Sopenharmony_ci return ret; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci return len; 9798c2ecf20Sopenharmony_ci} 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_show(struct device *dev, 9828c2ecf20Sopenharmony_ci struct device_attribute *attr, 9838c2ecf20Sopenharmony_ci char *buf) 9848c2ecf20Sopenharmony_ci{ 9858c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); 9868c2ecf20Sopenharmony_ci int i = 0; 9878c2ecf20Sopenharmony_ci int offset = 0; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci while (i < TSL2772_MAX_LUX_TABLE_SIZE) { 9908c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%u,%u,", 9918c2ecf20Sopenharmony_ci chip->tsl2772_device_lux[i].ch0, 9928c2ecf20Sopenharmony_ci chip->tsl2772_device_lux[i].ch1); 9938c2ecf20Sopenharmony_ci if (chip->tsl2772_device_lux[i].ch0 == 0) { 9948c2ecf20Sopenharmony_ci /* 9958c2ecf20Sopenharmony_ci * We just printed the first "0" entry. 9968c2ecf20Sopenharmony_ci * Now get rid of the extra "," and break. 9978c2ecf20Sopenharmony_ci */ 9988c2ecf20Sopenharmony_ci offset--; 9998c2ecf20Sopenharmony_ci break; 10008c2ecf20Sopenharmony_ci } 10018c2ecf20Sopenharmony_ci i++; 10028c2ecf20Sopenharmony_ci } 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci offset += scnprintf(buf + offset, PAGE_SIZE - offset, "\n"); 10058c2ecf20Sopenharmony_ci return offset; 10068c2ecf20Sopenharmony_ci} 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_store(struct device *dev, 10098c2ecf20Sopenharmony_ci struct device_attribute *attr, 10108c2ecf20Sopenharmony_ci const char *buf, size_t len) 10118c2ecf20Sopenharmony_ci{ 10128c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 10138c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 10148c2ecf20Sopenharmony_ci int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; 10158c2ecf20Sopenharmony_ci int n, ret; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci get_options(buf, ARRAY_SIZE(value), value); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci /* 10208c2ecf20Sopenharmony_ci * We now have an array of ints starting at value[1], and 10218c2ecf20Sopenharmony_ci * enumerated by value[0]. 10228c2ecf20Sopenharmony_ci * We expect each group of two ints to be one table entry, 10238c2ecf20Sopenharmony_ci * and the last table entry is all 0. 10248c2ecf20Sopenharmony_ci */ 10258c2ecf20Sopenharmony_ci n = value[0]; 10268c2ecf20Sopenharmony_ci if ((n % 2) || n < 4 || 10278c2ecf20Sopenharmony_ci n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) 10288c2ecf20Sopenharmony_ci return -EINVAL; 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci if ((value[(n - 1)] | value[n]) != 0) 10318c2ecf20Sopenharmony_ci return -EINVAL; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { 10348c2ecf20Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 10358c2ecf20Sopenharmony_ci if (ret < 0) 10368c2ecf20Sopenharmony_ci return ret; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci /* Zero out the table */ 10408c2ecf20Sopenharmony_ci memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); 10418c2ecf20Sopenharmony_ci memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 10448c2ecf20Sopenharmony_ci if (ret < 0) 10458c2ecf20Sopenharmony_ci return ret; 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci return len; 10488c2ecf20Sopenharmony_ci} 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic ssize_t in_proximity0_calibrate_store(struct device *dev, 10518c2ecf20Sopenharmony_ci struct device_attribute *attr, 10528c2ecf20Sopenharmony_ci const char *buf, size_t len) 10538c2ecf20Sopenharmony_ci{ 10548c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 10558c2ecf20Sopenharmony_ci bool value; 10568c2ecf20Sopenharmony_ci int ret; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (kstrtobool(buf, &value) || !value) 10598c2ecf20Sopenharmony_ci return -EINVAL; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci ret = tsl2772_prox_cal(indio_dev); 10628c2ecf20Sopenharmony_ci if (ret < 0) 10638c2ecf20Sopenharmony_ci return ret; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 10668c2ecf20Sopenharmony_ci if (ret < 0) 10678c2ecf20Sopenharmony_ci return ret; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci return len; 10708c2ecf20Sopenharmony_ci} 10718c2ecf20Sopenharmony_ci 10728c2ecf20Sopenharmony_cistatic int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, 10738c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 10748c2ecf20Sopenharmony_ci enum iio_event_type type, 10758c2ecf20Sopenharmony_ci enum iio_event_direction dir) 10768c2ecf20Sopenharmony_ci{ 10778c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) 10808c2ecf20Sopenharmony_ci return chip->settings.als_interrupt_en; 10818c2ecf20Sopenharmony_ci else 10828c2ecf20Sopenharmony_ci return chip->settings.prox_interrupt_en; 10838c2ecf20Sopenharmony_ci} 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_cistatic int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, 10868c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 10878c2ecf20Sopenharmony_ci enum iio_event_type type, 10888c2ecf20Sopenharmony_ci enum iio_event_direction dir, 10898c2ecf20Sopenharmony_ci int val) 10908c2ecf20Sopenharmony_ci{ 10918c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 10928c2ecf20Sopenharmony_ci 10938c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) 10948c2ecf20Sopenharmony_ci chip->settings.als_interrupt_en = val ? true : false; 10958c2ecf20Sopenharmony_ci else 10968c2ecf20Sopenharmony_ci chip->settings.prox_interrupt_en = val ? true : false; 10978c2ecf20Sopenharmony_ci 10988c2ecf20Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 10998c2ecf20Sopenharmony_ci} 11008c2ecf20Sopenharmony_ci 11018c2ecf20Sopenharmony_cistatic int tsl2772_write_event_value(struct iio_dev *indio_dev, 11028c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 11038c2ecf20Sopenharmony_ci enum iio_event_type type, 11048c2ecf20Sopenharmony_ci enum iio_event_direction dir, 11058c2ecf20Sopenharmony_ci enum iio_event_info info, 11068c2ecf20Sopenharmony_ci int val, int val2) 11078c2ecf20Sopenharmony_ci{ 11088c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 11098c2ecf20Sopenharmony_ci int ret = -EINVAL, count, persistence; 11108c2ecf20Sopenharmony_ci u8 time; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci switch (info) { 11138c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 11148c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 11158c2ecf20Sopenharmony_ci switch (dir) { 11168c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 11178c2ecf20Sopenharmony_ci chip->settings.als_thresh_high = val; 11188c2ecf20Sopenharmony_ci ret = 0; 11198c2ecf20Sopenharmony_ci break; 11208c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 11218c2ecf20Sopenharmony_ci chip->settings.als_thresh_low = val; 11228c2ecf20Sopenharmony_ci ret = 0; 11238c2ecf20Sopenharmony_ci break; 11248c2ecf20Sopenharmony_ci default: 11258c2ecf20Sopenharmony_ci break; 11268c2ecf20Sopenharmony_ci } 11278c2ecf20Sopenharmony_ci } else { 11288c2ecf20Sopenharmony_ci switch (dir) { 11298c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 11308c2ecf20Sopenharmony_ci chip->settings.prox_thres_high = val; 11318c2ecf20Sopenharmony_ci ret = 0; 11328c2ecf20Sopenharmony_ci break; 11338c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 11348c2ecf20Sopenharmony_ci chip->settings.prox_thres_low = val; 11358c2ecf20Sopenharmony_ci ret = 0; 11368c2ecf20Sopenharmony_ci break; 11378c2ecf20Sopenharmony_ci default: 11388c2ecf20Sopenharmony_ci break; 11398c2ecf20Sopenharmony_ci } 11408c2ecf20Sopenharmony_ci } 11418c2ecf20Sopenharmony_ci break; 11428c2ecf20Sopenharmony_ci case IIO_EV_INFO_PERIOD: 11438c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) 11448c2ecf20Sopenharmony_ci time = chip->settings.als_time; 11458c2ecf20Sopenharmony_ci else 11468c2ecf20Sopenharmony_ci time = chip->settings.prox_time; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci count = 256 - time; 11498c2ecf20Sopenharmony_ci persistence = ((val * 1000000) + val2) / 11508c2ecf20Sopenharmony_ci (count * tsl2772_int_time_avail[chip->id][3]); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 11538c2ecf20Sopenharmony_ci /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ 11548c2ecf20Sopenharmony_ci if (persistence > 3) 11558c2ecf20Sopenharmony_ci persistence = (persistence / 5) + 3; 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_ci chip->settings.als_persistence = persistence; 11588c2ecf20Sopenharmony_ci } else { 11598c2ecf20Sopenharmony_ci chip->settings.prox_persistence = persistence; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci ret = 0; 11638c2ecf20Sopenharmony_ci break; 11648c2ecf20Sopenharmony_ci default: 11658c2ecf20Sopenharmony_ci break; 11668c2ecf20Sopenharmony_ci } 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci if (ret < 0) 11698c2ecf20Sopenharmony_ci return ret; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 11728c2ecf20Sopenharmony_ci} 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_cistatic int tsl2772_read_event_value(struct iio_dev *indio_dev, 11758c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 11768c2ecf20Sopenharmony_ci enum iio_event_type type, 11778c2ecf20Sopenharmony_ci enum iio_event_direction dir, 11788c2ecf20Sopenharmony_ci enum iio_event_info info, 11798c2ecf20Sopenharmony_ci int *val, int *val2) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 11828c2ecf20Sopenharmony_ci int filter_delay, persistence; 11838c2ecf20Sopenharmony_ci u8 time; 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci switch (info) { 11868c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 11878c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 11888c2ecf20Sopenharmony_ci switch (dir) { 11898c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 11908c2ecf20Sopenharmony_ci *val = chip->settings.als_thresh_high; 11918c2ecf20Sopenharmony_ci return IIO_VAL_INT; 11928c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 11938c2ecf20Sopenharmony_ci *val = chip->settings.als_thresh_low; 11948c2ecf20Sopenharmony_ci return IIO_VAL_INT; 11958c2ecf20Sopenharmony_ci default: 11968c2ecf20Sopenharmony_ci return -EINVAL; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci } else { 11998c2ecf20Sopenharmony_ci switch (dir) { 12008c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 12018c2ecf20Sopenharmony_ci *val = chip->settings.prox_thres_high; 12028c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12038c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 12048c2ecf20Sopenharmony_ci *val = chip->settings.prox_thres_low; 12058c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12068c2ecf20Sopenharmony_ci default: 12078c2ecf20Sopenharmony_ci return -EINVAL; 12088c2ecf20Sopenharmony_ci } 12098c2ecf20Sopenharmony_ci } 12108c2ecf20Sopenharmony_ci break; 12118c2ecf20Sopenharmony_ci case IIO_EV_INFO_PERIOD: 12128c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 12138c2ecf20Sopenharmony_ci time = chip->settings.als_time; 12148c2ecf20Sopenharmony_ci persistence = chip->settings.als_persistence; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ 12178c2ecf20Sopenharmony_ci if (persistence > 3) 12188c2ecf20Sopenharmony_ci persistence = (persistence - 3) * 5; 12198c2ecf20Sopenharmony_ci } else { 12208c2ecf20Sopenharmony_ci time = chip->settings.prox_time; 12218c2ecf20Sopenharmony_ci persistence = chip->settings.prox_persistence; 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci filter_delay = persistence * (256 - time) * 12258c2ecf20Sopenharmony_ci tsl2772_int_time_avail[chip->id][3]; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci *val = filter_delay / 1000000; 12288c2ecf20Sopenharmony_ci *val2 = filter_delay % 1000000; 12298c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 12308c2ecf20Sopenharmony_ci default: 12318c2ecf20Sopenharmony_ci return -EINVAL; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_cistatic int tsl2772_read_raw(struct iio_dev *indio_dev, 12368c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 12378c2ecf20Sopenharmony_ci int *val, 12388c2ecf20Sopenharmony_ci int *val2, 12398c2ecf20Sopenharmony_ci long mask) 12408c2ecf20Sopenharmony_ci{ 12418c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci switch (mask) { 12448c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 12458c2ecf20Sopenharmony_ci switch (chan->type) { 12468c2ecf20Sopenharmony_ci case IIO_LIGHT: 12478c2ecf20Sopenharmony_ci tsl2772_get_lux(indio_dev); 12488c2ecf20Sopenharmony_ci *val = chip->als_cur_info.lux; 12498c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12508c2ecf20Sopenharmony_ci default: 12518c2ecf20Sopenharmony_ci return -EINVAL; 12528c2ecf20Sopenharmony_ci } 12538c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 12548c2ecf20Sopenharmony_ci switch (chan->type) { 12558c2ecf20Sopenharmony_ci case IIO_INTENSITY: 12568c2ecf20Sopenharmony_ci tsl2772_get_lux(indio_dev); 12578c2ecf20Sopenharmony_ci if (chan->channel == 0) 12588c2ecf20Sopenharmony_ci *val = chip->als_cur_info.als_ch0; 12598c2ecf20Sopenharmony_ci else 12608c2ecf20Sopenharmony_ci *val = chip->als_cur_info.als_ch1; 12618c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12628c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 12638c2ecf20Sopenharmony_ci tsl2772_get_prox(indio_dev); 12648c2ecf20Sopenharmony_ci *val = chip->prox_data; 12658c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12668c2ecf20Sopenharmony_ci default: 12678c2ecf20Sopenharmony_ci return -EINVAL; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci break; 12708c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 12718c2ecf20Sopenharmony_ci if (chan->type == IIO_LIGHT) 12728c2ecf20Sopenharmony_ci *val = tsl2772_als_gain[chip->settings.als_gain]; 12738c2ecf20Sopenharmony_ci else 12748c2ecf20Sopenharmony_ci *val = tsl2772_prox_gain[chip->settings.prox_gain]; 12758c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12768c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 12778c2ecf20Sopenharmony_ci *val = chip->settings.als_gain_trim; 12788c2ecf20Sopenharmony_ci return IIO_VAL_INT; 12798c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 12808c2ecf20Sopenharmony_ci *val = 0; 12818c2ecf20Sopenharmony_ci *val2 = (256 - chip->settings.als_time) * 12828c2ecf20Sopenharmony_ci tsl2772_int_time_avail[chip->id][3]; 12838c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 12848c2ecf20Sopenharmony_ci default: 12858c2ecf20Sopenharmony_ci return -EINVAL; 12868c2ecf20Sopenharmony_ci } 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic int tsl2772_write_raw(struct iio_dev *indio_dev, 12908c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 12918c2ecf20Sopenharmony_ci int val, 12928c2ecf20Sopenharmony_ci int val2, 12938c2ecf20Sopenharmony_ci long mask) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci switch (mask) { 12988c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 12998c2ecf20Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 13008c2ecf20Sopenharmony_ci switch (val) { 13018c2ecf20Sopenharmony_ci case 1: 13028c2ecf20Sopenharmony_ci chip->settings.als_gain = 0; 13038c2ecf20Sopenharmony_ci break; 13048c2ecf20Sopenharmony_ci case 8: 13058c2ecf20Sopenharmony_ci chip->settings.als_gain = 1; 13068c2ecf20Sopenharmony_ci break; 13078c2ecf20Sopenharmony_ci case 16: 13088c2ecf20Sopenharmony_ci chip->settings.als_gain = 2; 13098c2ecf20Sopenharmony_ci break; 13108c2ecf20Sopenharmony_ci case 120: 13118c2ecf20Sopenharmony_ci chip->settings.als_gain = 3; 13128c2ecf20Sopenharmony_ci break; 13138c2ecf20Sopenharmony_ci default: 13148c2ecf20Sopenharmony_ci return -EINVAL; 13158c2ecf20Sopenharmony_ci } 13168c2ecf20Sopenharmony_ci } else { 13178c2ecf20Sopenharmony_ci switch (val) { 13188c2ecf20Sopenharmony_ci case 1: 13198c2ecf20Sopenharmony_ci chip->settings.prox_gain = 0; 13208c2ecf20Sopenharmony_ci break; 13218c2ecf20Sopenharmony_ci case 2: 13228c2ecf20Sopenharmony_ci chip->settings.prox_gain = 1; 13238c2ecf20Sopenharmony_ci break; 13248c2ecf20Sopenharmony_ci case 4: 13258c2ecf20Sopenharmony_ci chip->settings.prox_gain = 2; 13268c2ecf20Sopenharmony_ci break; 13278c2ecf20Sopenharmony_ci case 8: 13288c2ecf20Sopenharmony_ci chip->settings.prox_gain = 3; 13298c2ecf20Sopenharmony_ci break; 13308c2ecf20Sopenharmony_ci default: 13318c2ecf20Sopenharmony_ci return -EINVAL; 13328c2ecf20Sopenharmony_ci } 13338c2ecf20Sopenharmony_ci } 13348c2ecf20Sopenharmony_ci break; 13358c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 13368c2ecf20Sopenharmony_ci if (val < TSL2772_ALS_GAIN_TRIM_MIN || 13378c2ecf20Sopenharmony_ci val > TSL2772_ALS_GAIN_TRIM_MAX) 13388c2ecf20Sopenharmony_ci return -EINVAL; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_ci chip->settings.als_gain_trim = val; 13418c2ecf20Sopenharmony_ci break; 13428c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 13438c2ecf20Sopenharmony_ci if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || 13448c2ecf20Sopenharmony_ci val2 > tsl2772_int_time_avail[chip->id][5]) 13458c2ecf20Sopenharmony_ci return -EINVAL; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci chip->settings.als_time = 256 - 13488c2ecf20Sopenharmony_ci (val2 / tsl2772_int_time_avail[chip->id][3]); 13498c2ecf20Sopenharmony_ci break; 13508c2ecf20Sopenharmony_ci default: 13518c2ecf20Sopenharmony_ci return -EINVAL; 13528c2ecf20Sopenharmony_ci } 13538c2ecf20Sopenharmony_ci 13548c2ecf20Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 13558c2ecf20Sopenharmony_ci} 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_target_input); 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(in_illuminance0_calibrate); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(in_proximity0_calibrate); 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_lux_table); 13648c2ecf20Sopenharmony_ci 13658c2ecf20Sopenharmony_ci/* Use the default register values to identify the Taos device */ 13668c2ecf20Sopenharmony_cistatic int tsl2772_device_id_verif(int id, int target) 13678c2ecf20Sopenharmony_ci{ 13688c2ecf20Sopenharmony_ci switch (target) { 13698c2ecf20Sopenharmony_ci case tsl2571: 13708c2ecf20Sopenharmony_ci case tsl2671: 13718c2ecf20Sopenharmony_ci case tsl2771: 13728c2ecf20Sopenharmony_ci return (id & 0xf0) == TRITON_ID; 13738c2ecf20Sopenharmony_ci case tmd2671: 13748c2ecf20Sopenharmony_ci case tmd2771: 13758c2ecf20Sopenharmony_ci return (id & 0xf0) == HALIBUT_ID; 13768c2ecf20Sopenharmony_ci case tsl2572: 13778c2ecf20Sopenharmony_ci case tsl2672: 13788c2ecf20Sopenharmony_ci case tmd2672: 13798c2ecf20Sopenharmony_ci case tsl2772: 13808c2ecf20Sopenharmony_ci case tmd2772: 13818c2ecf20Sopenharmony_ci case apds9930: 13828c2ecf20Sopenharmony_ci return (id & 0xf0) == SWORDFISH_ID; 13838c2ecf20Sopenharmony_ci } 13848c2ecf20Sopenharmony_ci 13858c2ecf20Sopenharmony_ci return -EINVAL; 13868c2ecf20Sopenharmony_ci} 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_cistatic irqreturn_t tsl2772_event_handler(int irq, void *private) 13898c2ecf20Sopenharmony_ci{ 13908c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = private; 13918c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 13928c2ecf20Sopenharmony_ci s64 timestamp = iio_get_time_ns(indio_dev); 13938c2ecf20Sopenharmony_ci int ret; 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci ret = tsl2772_read_status(chip); 13968c2ecf20Sopenharmony_ci if (ret < 0) 13978c2ecf20Sopenharmony_ci return IRQ_HANDLED; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* What type of interrupt do we need to process */ 14008c2ecf20Sopenharmony_ci if (ret & TSL2772_STA_PRX_INTR) { 14018c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 14028c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 14038c2ecf20Sopenharmony_ci 0, 14048c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 14058c2ecf20Sopenharmony_ci IIO_EV_DIR_EITHER), 14068c2ecf20Sopenharmony_ci timestamp); 14078c2ecf20Sopenharmony_ci } 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (ret & TSL2772_STA_ALS_INTR) { 14108c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 14118c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 14128c2ecf20Sopenharmony_ci 0, 14138c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 14148c2ecf20Sopenharmony_ci IIO_EV_DIR_EITHER), 14158c2ecf20Sopenharmony_ci timestamp); 14168c2ecf20Sopenharmony_ci } 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 14198c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | 14208c2ecf20Sopenharmony_ci TSL2772_CMD_PROXALS_INT_CLR); 14218c2ecf20Sopenharmony_ci if (ret < 0) 14228c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 14238c2ecf20Sopenharmony_ci "%s: failed to clear interrupt status: %d\n", 14248c2ecf20Sopenharmony_ci __func__, ret); 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistatic struct attribute *tsl2772_ALS_device_attrs[] = { 14308c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 14318c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 14328c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 14338c2ecf20Sopenharmony_ci NULL 14348c2ecf20Sopenharmony_ci}; 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_cistatic struct attribute *tsl2772_PRX_device_attrs[] = { 14378c2ecf20Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 14388c2ecf20Sopenharmony_ci NULL 14398c2ecf20Sopenharmony_ci}; 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX_device_attrs[] = { 14428c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 14438c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 14448c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 14458c2ecf20Sopenharmony_ci NULL 14468c2ecf20Sopenharmony_ci}; 14478c2ecf20Sopenharmony_ci 14488c2ecf20Sopenharmony_cistatic struct attribute *tsl2772_PRX2_device_attrs[] = { 14498c2ecf20Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 14508c2ecf20Sopenharmony_ci NULL 14518c2ecf20Sopenharmony_ci}; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX2_device_attrs[] = { 14548c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 14558c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 14568c2ecf20Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 14578c2ecf20Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 14588c2ecf20Sopenharmony_ci NULL 14598c2ecf20Sopenharmony_ci}; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cistatic const struct attribute_group tsl2772_device_attr_group_tbl[] = { 14628c2ecf20Sopenharmony_ci [ALS] = { 14638c2ecf20Sopenharmony_ci .attrs = tsl2772_ALS_device_attrs, 14648c2ecf20Sopenharmony_ci }, 14658c2ecf20Sopenharmony_ci [PRX] = { 14668c2ecf20Sopenharmony_ci .attrs = tsl2772_PRX_device_attrs, 14678c2ecf20Sopenharmony_ci }, 14688c2ecf20Sopenharmony_ci [ALSPRX] = { 14698c2ecf20Sopenharmony_ci .attrs = tsl2772_ALSPRX_device_attrs, 14708c2ecf20Sopenharmony_ci }, 14718c2ecf20Sopenharmony_ci [PRX2] = { 14728c2ecf20Sopenharmony_ci .attrs = tsl2772_PRX2_device_attrs, 14738c2ecf20Sopenharmony_ci }, 14748c2ecf20Sopenharmony_ci [ALSPRX2] = { 14758c2ecf20Sopenharmony_ci .attrs = tsl2772_ALSPRX2_device_attrs, 14768c2ecf20Sopenharmony_ci }, 14778c2ecf20Sopenharmony_ci}; 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci#define TSL2772_DEVICE_INFO(type)[type] = \ 14808c2ecf20Sopenharmony_ci { \ 14818c2ecf20Sopenharmony_ci .attrs = &tsl2772_device_attr_group_tbl[type], \ 14828c2ecf20Sopenharmony_ci .read_raw = &tsl2772_read_raw, \ 14838c2ecf20Sopenharmony_ci .read_avail = &tsl2772_read_avail, \ 14848c2ecf20Sopenharmony_ci .write_raw = &tsl2772_write_raw, \ 14858c2ecf20Sopenharmony_ci .read_event_value = &tsl2772_read_event_value, \ 14868c2ecf20Sopenharmony_ci .write_event_value = &tsl2772_write_event_value, \ 14878c2ecf20Sopenharmony_ci .read_event_config = &tsl2772_read_interrupt_config, \ 14888c2ecf20Sopenharmony_ci .write_event_config = &tsl2772_write_interrupt_config, \ 14898c2ecf20Sopenharmony_ci } 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_cistatic const struct iio_info tsl2772_device_info[] = { 14928c2ecf20Sopenharmony_ci TSL2772_DEVICE_INFO(ALS), 14938c2ecf20Sopenharmony_ci TSL2772_DEVICE_INFO(PRX), 14948c2ecf20Sopenharmony_ci TSL2772_DEVICE_INFO(ALSPRX), 14958c2ecf20Sopenharmony_ci TSL2772_DEVICE_INFO(PRX2), 14968c2ecf20Sopenharmony_ci TSL2772_DEVICE_INFO(ALSPRX2), 14978c2ecf20Sopenharmony_ci}; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_cistatic const struct iio_event_spec tsl2772_events[] = { 15008c2ecf20Sopenharmony_ci { 15018c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 15028c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 15038c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 15048c2ecf20Sopenharmony_ci }, { 15058c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 15068c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 15078c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 15088c2ecf20Sopenharmony_ci }, { 15098c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 15108c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 15118c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_PERIOD) | 15128c2ecf20Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 15138c2ecf20Sopenharmony_ci }, 15148c2ecf20Sopenharmony_ci}; 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_cistatic const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { 15178c2ecf20Sopenharmony_ci [ALS] = { 15188c2ecf20Sopenharmony_ci .channel_with_events = { 15198c2ecf20Sopenharmony_ci { 15208c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 15218c2ecf20Sopenharmony_ci .indexed = 1, 15228c2ecf20Sopenharmony_ci .channel = 0, 15238c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 15248c2ecf20Sopenharmony_ci }, { 15258c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 15268c2ecf20Sopenharmony_ci .indexed = 1, 15278c2ecf20Sopenharmony_ci .channel = 0, 15288c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 15298c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 15308c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 15318c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 15328c2ecf20Sopenharmony_ci .info_mask_separate_available = 15338c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 15348c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 15358c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 15368c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 15378c2ecf20Sopenharmony_ci }, { 15388c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 15398c2ecf20Sopenharmony_ci .indexed = 1, 15408c2ecf20Sopenharmony_ci .channel = 1, 15418c2ecf20Sopenharmony_ci }, 15428c2ecf20Sopenharmony_ci }, 15438c2ecf20Sopenharmony_ci .channel_without_events = { 15448c2ecf20Sopenharmony_ci { 15458c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 15468c2ecf20Sopenharmony_ci .indexed = 1, 15478c2ecf20Sopenharmony_ci .channel = 0, 15488c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 15498c2ecf20Sopenharmony_ci }, { 15508c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 15518c2ecf20Sopenharmony_ci .indexed = 1, 15528c2ecf20Sopenharmony_ci .channel = 0, 15538c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 15548c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 15558c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 15568c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 15578c2ecf20Sopenharmony_ci .info_mask_separate_available = 15588c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 15598c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 15608c2ecf20Sopenharmony_ci }, { 15618c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 15628c2ecf20Sopenharmony_ci .indexed = 1, 15638c2ecf20Sopenharmony_ci .channel = 1, 15648c2ecf20Sopenharmony_ci }, 15658c2ecf20Sopenharmony_ci }, 15668c2ecf20Sopenharmony_ci .chan_table_elements = 3, 15678c2ecf20Sopenharmony_ci .info = &tsl2772_device_info[ALS], 15688c2ecf20Sopenharmony_ci }, 15698c2ecf20Sopenharmony_ci [PRX] = { 15708c2ecf20Sopenharmony_ci .channel_with_events = { 15718c2ecf20Sopenharmony_ci { 15728c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 15738c2ecf20Sopenharmony_ci .indexed = 1, 15748c2ecf20Sopenharmony_ci .channel = 0, 15758c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 15768c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 15778c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 15788c2ecf20Sopenharmony_ci }, 15798c2ecf20Sopenharmony_ci }, 15808c2ecf20Sopenharmony_ci .channel_without_events = { 15818c2ecf20Sopenharmony_ci { 15828c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 15838c2ecf20Sopenharmony_ci .indexed = 1, 15848c2ecf20Sopenharmony_ci .channel = 0, 15858c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 15868c2ecf20Sopenharmony_ci }, 15878c2ecf20Sopenharmony_ci }, 15888c2ecf20Sopenharmony_ci .chan_table_elements = 1, 15898c2ecf20Sopenharmony_ci .info = &tsl2772_device_info[PRX], 15908c2ecf20Sopenharmony_ci }, 15918c2ecf20Sopenharmony_ci [ALSPRX] = { 15928c2ecf20Sopenharmony_ci .channel_with_events = { 15938c2ecf20Sopenharmony_ci { 15948c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 15958c2ecf20Sopenharmony_ci .indexed = 1, 15968c2ecf20Sopenharmony_ci .channel = 0, 15978c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 15988c2ecf20Sopenharmony_ci }, { 15998c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 16008c2ecf20Sopenharmony_ci .indexed = 1, 16018c2ecf20Sopenharmony_ci .channel = 0, 16028c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 16038c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 16048c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 16058c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 16068c2ecf20Sopenharmony_ci .info_mask_separate_available = 16078c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 16088c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16098c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 16108c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 16118c2ecf20Sopenharmony_ci }, { 16128c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 16138c2ecf20Sopenharmony_ci .indexed = 1, 16148c2ecf20Sopenharmony_ci .channel = 1, 16158c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 16168c2ecf20Sopenharmony_ci }, { 16178c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 16188c2ecf20Sopenharmony_ci .indexed = 1, 16198c2ecf20Sopenharmony_ci .channel = 0, 16208c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 16218c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 16228c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 16238c2ecf20Sopenharmony_ci }, 16248c2ecf20Sopenharmony_ci }, 16258c2ecf20Sopenharmony_ci .channel_without_events = { 16268c2ecf20Sopenharmony_ci { 16278c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 16288c2ecf20Sopenharmony_ci .indexed = 1, 16298c2ecf20Sopenharmony_ci .channel = 0, 16308c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 16318c2ecf20Sopenharmony_ci }, { 16328c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 16338c2ecf20Sopenharmony_ci .indexed = 1, 16348c2ecf20Sopenharmony_ci .channel = 0, 16358c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 16368c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 16378c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 16388c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 16398c2ecf20Sopenharmony_ci .info_mask_separate_available = 16408c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 16418c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16428c2ecf20Sopenharmony_ci }, { 16438c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 16448c2ecf20Sopenharmony_ci .indexed = 1, 16458c2ecf20Sopenharmony_ci .channel = 1, 16468c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 16478c2ecf20Sopenharmony_ci }, { 16488c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 16498c2ecf20Sopenharmony_ci .indexed = 1, 16508c2ecf20Sopenharmony_ci .channel = 0, 16518c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 16528c2ecf20Sopenharmony_ci }, 16538c2ecf20Sopenharmony_ci }, 16548c2ecf20Sopenharmony_ci .chan_table_elements = 4, 16558c2ecf20Sopenharmony_ci .info = &tsl2772_device_info[ALSPRX], 16568c2ecf20Sopenharmony_ci }, 16578c2ecf20Sopenharmony_ci [PRX2] = { 16588c2ecf20Sopenharmony_ci .channel_with_events = { 16598c2ecf20Sopenharmony_ci { 16608c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 16618c2ecf20Sopenharmony_ci .indexed = 1, 16628c2ecf20Sopenharmony_ci .channel = 0, 16638c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 16648c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16658c2ecf20Sopenharmony_ci .info_mask_separate_available = 16668c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16678c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 16688c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 16698c2ecf20Sopenharmony_ci }, 16708c2ecf20Sopenharmony_ci }, 16718c2ecf20Sopenharmony_ci .channel_without_events = { 16728c2ecf20Sopenharmony_ci { 16738c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 16748c2ecf20Sopenharmony_ci .indexed = 1, 16758c2ecf20Sopenharmony_ci .channel = 0, 16768c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 16778c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16788c2ecf20Sopenharmony_ci .info_mask_separate_available = 16798c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 16808c2ecf20Sopenharmony_ci }, 16818c2ecf20Sopenharmony_ci }, 16828c2ecf20Sopenharmony_ci .chan_table_elements = 1, 16838c2ecf20Sopenharmony_ci .info = &tsl2772_device_info[PRX2], 16848c2ecf20Sopenharmony_ci }, 16858c2ecf20Sopenharmony_ci [ALSPRX2] = { 16868c2ecf20Sopenharmony_ci .channel_with_events = { 16878c2ecf20Sopenharmony_ci { 16888c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 16898c2ecf20Sopenharmony_ci .indexed = 1, 16908c2ecf20Sopenharmony_ci .channel = 0, 16918c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 16928c2ecf20Sopenharmony_ci }, { 16938c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 16948c2ecf20Sopenharmony_ci .indexed = 1, 16958c2ecf20Sopenharmony_ci .channel = 0, 16968c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 16978c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 16988c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 16998c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 17008c2ecf20Sopenharmony_ci .info_mask_separate_available = 17018c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 17028c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17038c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 17048c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 17058c2ecf20Sopenharmony_ci }, { 17068c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 17078c2ecf20Sopenharmony_ci .indexed = 1, 17088c2ecf20Sopenharmony_ci .channel = 1, 17098c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 17108c2ecf20Sopenharmony_ci }, { 17118c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 17128c2ecf20Sopenharmony_ci .indexed = 1, 17138c2ecf20Sopenharmony_ci .channel = 0, 17148c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 17158c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17168c2ecf20Sopenharmony_ci .info_mask_separate_available = 17178c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17188c2ecf20Sopenharmony_ci .event_spec = tsl2772_events, 17198c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 17208c2ecf20Sopenharmony_ci }, 17218c2ecf20Sopenharmony_ci }, 17228c2ecf20Sopenharmony_ci .channel_without_events = { 17238c2ecf20Sopenharmony_ci { 17248c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 17258c2ecf20Sopenharmony_ci .indexed = 1, 17268c2ecf20Sopenharmony_ci .channel = 0, 17278c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 17288c2ecf20Sopenharmony_ci }, { 17298c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 17308c2ecf20Sopenharmony_ci .indexed = 1, 17318c2ecf20Sopenharmony_ci .channel = 0, 17328c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 17338c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 17348c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 17358c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 17368c2ecf20Sopenharmony_ci .info_mask_separate_available = 17378c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 17388c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17398c2ecf20Sopenharmony_ci }, { 17408c2ecf20Sopenharmony_ci .type = IIO_INTENSITY, 17418c2ecf20Sopenharmony_ci .indexed = 1, 17428c2ecf20Sopenharmony_ci .channel = 1, 17438c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 17448c2ecf20Sopenharmony_ci }, { 17458c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 17468c2ecf20Sopenharmony_ci .indexed = 1, 17478c2ecf20Sopenharmony_ci .channel = 0, 17488c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 17498c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17508c2ecf20Sopenharmony_ci .info_mask_separate_available = 17518c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 17528c2ecf20Sopenharmony_ci }, 17538c2ecf20Sopenharmony_ci }, 17548c2ecf20Sopenharmony_ci .chan_table_elements = 4, 17558c2ecf20Sopenharmony_ci .info = &tsl2772_device_info[ALSPRX2], 17568c2ecf20Sopenharmony_ci }, 17578c2ecf20Sopenharmony_ci}; 17588c2ecf20Sopenharmony_ci 17598c2ecf20Sopenharmony_cistatic int tsl2772_probe(struct i2c_client *clientp, 17608c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 17618c2ecf20Sopenharmony_ci{ 17628c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 17638c2ecf20Sopenharmony_ci struct tsl2772_chip *chip; 17648c2ecf20Sopenharmony_ci int ret; 17658c2ecf20Sopenharmony_ci 17668c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); 17678c2ecf20Sopenharmony_ci if (!indio_dev) 17688c2ecf20Sopenharmony_ci return -ENOMEM; 17698c2ecf20Sopenharmony_ci 17708c2ecf20Sopenharmony_ci chip = iio_priv(indio_dev); 17718c2ecf20Sopenharmony_ci chip->client = clientp; 17728c2ecf20Sopenharmony_ci i2c_set_clientdata(clientp, indio_dev); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci chip->supplies[TSL2772_SUPPLY_VDD].supply = "vdd"; 17758c2ecf20Sopenharmony_ci chip->supplies[TSL2772_SUPPLY_VDDIO].supply = "vddio"; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci ret = devm_regulator_bulk_get(&clientp->dev, 17788c2ecf20Sopenharmony_ci ARRAY_SIZE(chip->supplies), 17798c2ecf20Sopenharmony_ci chip->supplies); 17808c2ecf20Sopenharmony_ci if (ret < 0) 17818c2ecf20Sopenharmony_ci return dev_err_probe(&clientp->dev, ret, "Failed to get regulators\n"); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies); 17848c2ecf20Sopenharmony_ci if (ret < 0) { 17858c2ecf20Sopenharmony_ci dev_err(&clientp->dev, "Failed to enable regulators: %d\n", 17868c2ecf20Sopenharmony_ci ret); 17878c2ecf20Sopenharmony_ci return ret; 17888c2ecf20Sopenharmony_ci } 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(&clientp->dev, 17918c2ecf20Sopenharmony_ci tsl2772_disable_regulators_action, 17928c2ecf20Sopenharmony_ci chip); 17938c2ecf20Sopenharmony_ci if (ret < 0) { 17948c2ecf20Sopenharmony_ci dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n", 17958c2ecf20Sopenharmony_ci ret); 17968c2ecf20Sopenharmony_ci return ret; 17978c2ecf20Sopenharmony_ci } 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ci usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); 18008c2ecf20Sopenharmony_ci 18018c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 18028c2ecf20Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CHIPID); 18038c2ecf20Sopenharmony_ci if (ret < 0) 18048c2ecf20Sopenharmony_ci return ret; 18058c2ecf20Sopenharmony_ci 18068c2ecf20Sopenharmony_ci if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { 18078c2ecf20Sopenharmony_ci dev_info(&chip->client->dev, 18088c2ecf20Sopenharmony_ci "%s: i2c device found does not match expected id\n", 18098c2ecf20Sopenharmony_ci __func__); 18108c2ecf20Sopenharmony_ci return -EINVAL; 18118c2ecf20Sopenharmony_ci } 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); 18148c2ecf20Sopenharmony_ci if (ret < 0) { 18158c2ecf20Sopenharmony_ci dev_err(&clientp->dev, 18168c2ecf20Sopenharmony_ci "%s: Failed to write to CMD register: %d\n", 18178c2ecf20Sopenharmony_ci __func__, ret); 18188c2ecf20Sopenharmony_ci return ret; 18198c2ecf20Sopenharmony_ci } 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci mutex_init(&chip->als_mutex); 18228c2ecf20Sopenharmony_ci mutex_init(&chip->prox_mutex); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; 18258c2ecf20Sopenharmony_ci chip->pdata = dev_get_platdata(&clientp->dev); 18268c2ecf20Sopenharmony_ci chip->id = id->driver_data; 18278c2ecf20Sopenharmony_ci chip->chip_info = 18288c2ecf20Sopenharmony_ci &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci indio_dev->info = chip->chip_info->info; 18318c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 18328c2ecf20Sopenharmony_ci indio_dev->name = chip->client->name; 18338c2ecf20Sopenharmony_ci indio_dev->num_channels = chip->chip_info->chan_table_elements; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci if (clientp->irq) { 18368c2ecf20Sopenharmony_ci indio_dev->channels = chip->chip_info->channel_with_events; 18378c2ecf20Sopenharmony_ci 18388c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, 18398c2ecf20Sopenharmony_ci NULL, 18408c2ecf20Sopenharmony_ci &tsl2772_event_handler, 18418c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | 18428c2ecf20Sopenharmony_ci IRQF_ONESHOT, 18438c2ecf20Sopenharmony_ci "TSL2772_event", 18448c2ecf20Sopenharmony_ci indio_dev); 18458c2ecf20Sopenharmony_ci if (ret) { 18468c2ecf20Sopenharmony_ci dev_err(&clientp->dev, 18478c2ecf20Sopenharmony_ci "%s: irq request failed\n", __func__); 18488c2ecf20Sopenharmony_ci return ret; 18498c2ecf20Sopenharmony_ci } 18508c2ecf20Sopenharmony_ci } else { 18518c2ecf20Sopenharmony_ci indio_dev->channels = chip->chip_info->channel_without_events; 18528c2ecf20Sopenharmony_ci } 18538c2ecf20Sopenharmony_ci 18548c2ecf20Sopenharmony_ci tsl2772_defaults(chip); 18558c2ecf20Sopenharmony_ci ret = tsl2772_chip_on(indio_dev); 18568c2ecf20Sopenharmony_ci if (ret < 0) 18578c2ecf20Sopenharmony_ci return ret; 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(&clientp->dev, 18608c2ecf20Sopenharmony_ci tsl2772_chip_off_action, 18618c2ecf20Sopenharmony_ci indio_dev); 18628c2ecf20Sopenharmony_ci if (ret < 0) 18638c2ecf20Sopenharmony_ci return ret; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci return devm_iio_device_register(&clientp->dev, indio_dev); 18668c2ecf20Sopenharmony_ci} 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_cistatic int tsl2772_suspend(struct device *dev) 18698c2ecf20Sopenharmony_ci{ 18708c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 18718c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 18728c2ecf20Sopenharmony_ci int ret; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 18758c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci return ret; 18788c2ecf20Sopenharmony_ci} 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_cistatic int tsl2772_resume(struct device *dev) 18818c2ecf20Sopenharmony_ci{ 18828c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 18838c2ecf20Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 18848c2ecf20Sopenharmony_ci int ret; 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies); 18878c2ecf20Sopenharmony_ci if (ret < 0) 18888c2ecf20Sopenharmony_ci return ret; 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci return tsl2772_chip_on(indio_dev); 18938c2ecf20Sopenharmony_ci} 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_cistatic const struct i2c_device_id tsl2772_idtable[] = { 18968c2ecf20Sopenharmony_ci { "tsl2571", tsl2571 }, 18978c2ecf20Sopenharmony_ci { "tsl2671", tsl2671 }, 18988c2ecf20Sopenharmony_ci { "tmd2671", tmd2671 }, 18998c2ecf20Sopenharmony_ci { "tsl2771", tsl2771 }, 19008c2ecf20Sopenharmony_ci { "tmd2771", tmd2771 }, 19018c2ecf20Sopenharmony_ci { "tsl2572", tsl2572 }, 19028c2ecf20Sopenharmony_ci { "tsl2672", tsl2672 }, 19038c2ecf20Sopenharmony_ci { "tmd2672", tmd2672 }, 19048c2ecf20Sopenharmony_ci { "tsl2772", tsl2772 }, 19058c2ecf20Sopenharmony_ci { "tmd2772", tmd2772 }, 19068c2ecf20Sopenharmony_ci { "apds9930", apds9930}, 19078c2ecf20Sopenharmony_ci {} 19088c2ecf20Sopenharmony_ci}; 19098c2ecf20Sopenharmony_ci 19108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tsl2772_idtable); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_cistatic const struct of_device_id tsl2772_of_match[] = { 19138c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2571" }, 19148c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2671" }, 19158c2ecf20Sopenharmony_ci { .compatible = "amstaos,tmd2671" }, 19168c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2771" }, 19178c2ecf20Sopenharmony_ci { .compatible = "amstaos,tmd2771" }, 19188c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2572" }, 19198c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2672" }, 19208c2ecf20Sopenharmony_ci { .compatible = "amstaos,tmd2672" }, 19218c2ecf20Sopenharmony_ci { .compatible = "amstaos,tsl2772" }, 19228c2ecf20Sopenharmony_ci { .compatible = "amstaos,tmd2772" }, 19238c2ecf20Sopenharmony_ci { .compatible = "avago,apds9930" }, 19248c2ecf20Sopenharmony_ci {} 19258c2ecf20Sopenharmony_ci}; 19268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsl2772_of_match); 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_cistatic const struct dev_pm_ops tsl2772_pm_ops = { 19298c2ecf20Sopenharmony_ci .suspend = tsl2772_suspend, 19308c2ecf20Sopenharmony_ci .resume = tsl2772_resume, 19318c2ecf20Sopenharmony_ci}; 19328c2ecf20Sopenharmony_ci 19338c2ecf20Sopenharmony_cistatic struct i2c_driver tsl2772_driver = { 19348c2ecf20Sopenharmony_ci .driver = { 19358c2ecf20Sopenharmony_ci .name = "tsl2772", 19368c2ecf20Sopenharmony_ci .of_match_table = tsl2772_of_match, 19378c2ecf20Sopenharmony_ci .pm = &tsl2772_pm_ops, 19388c2ecf20Sopenharmony_ci }, 19398c2ecf20Sopenharmony_ci .id_table = tsl2772_idtable, 19408c2ecf20Sopenharmony_ci .probe = tsl2772_probe, 19418c2ecf20Sopenharmony_ci}; 19428c2ecf20Sopenharmony_ci 19438c2ecf20Sopenharmony_cimodule_i2c_driver(tsl2772_driver); 19448c2ecf20Sopenharmony_ci 19458c2ecf20Sopenharmony_ciMODULE_AUTHOR("J. August Brenner <Jon.Brenner@ams.com>"); 19468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Brian Masney <masneyb@onstation.org>"); 19478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); 19488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1949