162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Device driver for monitoring ambient light intensity in (lux) and proximity 462306a36Sopenharmony_ci * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, 562306a36Sopenharmony_ci * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2012, TAOS Corporation. 862306a36Sopenharmony_ci * Copyright (c) 2017-2018 Brian Masney <masneyb@onstation.org> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/interrupt.h> 1562306a36Sopenharmony_ci#include <linux/kernel.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/mutex.h> 1862306a36Sopenharmony_ci#include <linux/property.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/iio/events.h> 2262306a36Sopenharmony_ci#include <linux/iio/iio.h> 2362306a36Sopenharmony_ci#include <linux/iio/sysfs.h> 2462306a36Sopenharmony_ci#include <linux/platform_data/tsl2772.h> 2562306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* Cal defs */ 2862306a36Sopenharmony_ci#define PROX_STAT_CAL 0 2962306a36Sopenharmony_ci#define PROX_STAT_SAMP 1 3062306a36Sopenharmony_ci#define MAX_SAMPLES_CAL 200 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* TSL2772 Device ID */ 3362306a36Sopenharmony_ci#define TRITON_ID 0x00 3462306a36Sopenharmony_ci#define SWORDFISH_ID 0x30 3562306a36Sopenharmony_ci#define HALIBUT_ID 0x20 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* Lux calculation constants */ 3862306a36Sopenharmony_ci#define TSL2772_LUX_CALC_OVER_FLOW 65535 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* 4162306a36Sopenharmony_ci * TAOS Register definitions - Note: depending on device, some of these register 4262306a36Sopenharmony_ci * are not used and the register address is benign. 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* Register offsets */ 4662306a36Sopenharmony_ci#define TSL2772_MAX_CONFIG_REG 16 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci/* Device Registers and Masks */ 4962306a36Sopenharmony_ci#define TSL2772_CNTRL 0x00 5062306a36Sopenharmony_ci#define TSL2772_ALS_TIME 0X01 5162306a36Sopenharmony_ci#define TSL2772_PRX_TIME 0x02 5262306a36Sopenharmony_ci#define TSL2772_WAIT_TIME 0x03 5362306a36Sopenharmony_ci#define TSL2772_ALS_MINTHRESHLO 0X04 5462306a36Sopenharmony_ci#define TSL2772_ALS_MINTHRESHHI 0X05 5562306a36Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHLO 0X06 5662306a36Sopenharmony_ci#define TSL2772_ALS_MAXTHRESHHI 0X07 5762306a36Sopenharmony_ci#define TSL2772_PRX_MINTHRESHLO 0X08 5862306a36Sopenharmony_ci#define TSL2772_PRX_MINTHRESHHI 0X09 5962306a36Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHLO 0X0A 6062306a36Sopenharmony_ci#define TSL2772_PRX_MAXTHRESHHI 0X0B 6162306a36Sopenharmony_ci#define TSL2772_PERSISTENCE 0x0C 6262306a36Sopenharmony_ci#define TSL2772_ALS_PRX_CONFIG 0x0D 6362306a36Sopenharmony_ci#define TSL2772_PRX_COUNT 0x0E 6462306a36Sopenharmony_ci#define TSL2772_GAIN 0x0F 6562306a36Sopenharmony_ci#define TSL2772_NOTUSED 0x10 6662306a36Sopenharmony_ci#define TSL2772_REVID 0x11 6762306a36Sopenharmony_ci#define TSL2772_CHIPID 0x12 6862306a36Sopenharmony_ci#define TSL2772_STATUS 0x13 6962306a36Sopenharmony_ci#define TSL2772_ALS_CHAN0LO 0x14 7062306a36Sopenharmony_ci#define TSL2772_ALS_CHAN0HI 0x15 7162306a36Sopenharmony_ci#define TSL2772_ALS_CHAN1LO 0x16 7262306a36Sopenharmony_ci#define TSL2772_ALS_CHAN1HI 0x17 7362306a36Sopenharmony_ci#define TSL2772_PRX_LO 0x18 7462306a36Sopenharmony_ci#define TSL2772_PRX_HI 0x19 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* tsl2772 cmd reg masks */ 7762306a36Sopenharmony_ci#define TSL2772_CMD_REG 0x80 7862306a36Sopenharmony_ci#define TSL2772_CMD_SPL_FN 0x60 7962306a36Sopenharmony_ci#define TSL2772_CMD_REPEAT_PROTO 0x00 8062306a36Sopenharmony_ci#define TSL2772_CMD_AUTOINC_PROTO 0x20 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define TSL2772_CMD_PROX_INT_CLR 0X05 8362306a36Sopenharmony_ci#define TSL2772_CMD_ALS_INT_CLR 0x06 8462306a36Sopenharmony_ci#define TSL2772_CMD_PROXALS_INT_CLR 0X07 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci/* tsl2772 cntrl reg masks */ 8762306a36Sopenharmony_ci#define TSL2772_CNTL_ADC_ENBL 0x02 8862306a36Sopenharmony_ci#define TSL2772_CNTL_PWR_ON 0x01 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* tsl2772 status reg masks */ 9162306a36Sopenharmony_ci#define TSL2772_STA_ADC_VALID 0x01 9262306a36Sopenharmony_ci#define TSL2772_STA_PRX_VALID 0x02 9362306a36Sopenharmony_ci#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ 9462306a36Sopenharmony_ci TSL2772_STA_PRX_VALID) 9562306a36Sopenharmony_ci#define TSL2772_STA_ALS_INTR 0x10 9662306a36Sopenharmony_ci#define TSL2772_STA_PRX_INTR 0x20 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* tsl2772 cntrl reg masks */ 9962306a36Sopenharmony_ci#define TSL2772_CNTL_REG_CLEAR 0x00 10062306a36Sopenharmony_ci#define TSL2772_CNTL_PROX_INT_ENBL 0X20 10162306a36Sopenharmony_ci#define TSL2772_CNTL_ALS_INT_ENBL 0X10 10262306a36Sopenharmony_ci#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 10362306a36Sopenharmony_ci#define TSL2772_CNTL_PROX_DET_ENBL 0X04 10462306a36Sopenharmony_ci#define TSL2772_CNTL_PWRON 0x01 10562306a36Sopenharmony_ci#define TSL2772_CNTL_ALSPON_ENBL 0x03 10662306a36Sopenharmony_ci#define TSL2772_CNTL_INTALSPON_ENBL 0x13 10762306a36Sopenharmony_ci#define TSL2772_CNTL_PROXPON_ENBL 0x0F 10862306a36Sopenharmony_ci#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MIN 250 11162306a36Sopenharmony_ci#define TSL2772_ALS_GAIN_TRIM_MAX 4000 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci#define TSL2772_MAX_PROX_LEDS 2 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci#define TSL2772_BOOT_MIN_SLEEP_TIME 10000 11662306a36Sopenharmony_ci#define TSL2772_BOOT_MAX_SLEEP_TIME 28000 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Device family members */ 11962306a36Sopenharmony_cienum { 12062306a36Sopenharmony_ci tsl2571, 12162306a36Sopenharmony_ci tsl2671, 12262306a36Sopenharmony_ci tmd2671, 12362306a36Sopenharmony_ci tsl2771, 12462306a36Sopenharmony_ci tmd2771, 12562306a36Sopenharmony_ci tsl2572, 12662306a36Sopenharmony_ci tsl2672, 12762306a36Sopenharmony_ci tmd2672, 12862306a36Sopenharmony_ci tsl2772, 12962306a36Sopenharmony_ci tmd2772, 13062306a36Sopenharmony_ci apds9930, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cienum { 13462306a36Sopenharmony_ci TSL2772_CHIP_UNKNOWN = 0, 13562306a36Sopenharmony_ci TSL2772_CHIP_WORKING = 1, 13662306a36Sopenharmony_ci TSL2772_CHIP_SUSPENDED = 2 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cienum { 14062306a36Sopenharmony_ci TSL2772_SUPPLY_VDD = 0, 14162306a36Sopenharmony_ci TSL2772_SUPPLY_VDDIO = 1, 14262306a36Sopenharmony_ci TSL2772_NUM_SUPPLIES = 2 14362306a36Sopenharmony_ci}; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci/* Per-device data */ 14662306a36Sopenharmony_cistruct tsl2772_als_info { 14762306a36Sopenharmony_ci u16 als_ch0; 14862306a36Sopenharmony_ci u16 als_ch1; 14962306a36Sopenharmony_ci u16 lux; 15062306a36Sopenharmony_ci}; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistruct tsl2772_chip_info { 15362306a36Sopenharmony_ci int chan_table_elements; 15462306a36Sopenharmony_ci struct iio_chan_spec channel_with_events[4]; 15562306a36Sopenharmony_ci struct iio_chan_spec channel_without_events[4]; 15662306a36Sopenharmony_ci const struct iio_info *info; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic const int tsl2772_led_currents[][2] = { 16062306a36Sopenharmony_ci { 100000, TSL2772_100_mA }, 16162306a36Sopenharmony_ci { 50000, TSL2772_50_mA }, 16262306a36Sopenharmony_ci { 25000, TSL2772_25_mA }, 16362306a36Sopenharmony_ci { 13000, TSL2772_13_mA }, 16462306a36Sopenharmony_ci { 0, 0 } 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistruct tsl2772_chip { 16862306a36Sopenharmony_ci kernel_ulong_t id; 16962306a36Sopenharmony_ci struct mutex prox_mutex; 17062306a36Sopenharmony_ci struct mutex als_mutex; 17162306a36Sopenharmony_ci struct i2c_client *client; 17262306a36Sopenharmony_ci struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES]; 17362306a36Sopenharmony_ci u16 prox_data; 17462306a36Sopenharmony_ci struct tsl2772_als_info als_cur_info; 17562306a36Sopenharmony_ci struct tsl2772_settings settings; 17662306a36Sopenharmony_ci struct tsl2772_platform_data *pdata; 17762306a36Sopenharmony_ci int als_gain_time_scale; 17862306a36Sopenharmony_ci int als_saturation; 17962306a36Sopenharmony_ci int tsl2772_chip_status; 18062306a36Sopenharmony_ci u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; 18162306a36Sopenharmony_ci const struct tsl2772_chip_info *chip_info; 18262306a36Sopenharmony_ci const struct iio_info *info; 18362306a36Sopenharmony_ci s64 event_timestamp; 18462306a36Sopenharmony_ci /* 18562306a36Sopenharmony_ci * This structure is intentionally large to accommodate 18662306a36Sopenharmony_ci * updates via sysfs. 18762306a36Sopenharmony_ci * Sized to 9 = max 8 segments + 1 termination segment 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_ci struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Different devices require different coefficents, and these numbers were 19462306a36Sopenharmony_ci * derived from the 'Lux Equation' section of the various device datasheets. 19562306a36Sopenharmony_ci * All of these coefficients assume a Glass Attenuation (GA) factor of 1. 19662306a36Sopenharmony_ci * The coefficients are multiplied by 1000 to avoid floating point operations. 19762306a36Sopenharmony_ci * The two rows in each table correspond to the Lux1 and Lux2 equations from 19862306a36Sopenharmony_ci * the datasheets. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 20162306a36Sopenharmony_ci { 53000, 106000 }, 20262306a36Sopenharmony_ci { 31800, 53000 }, 20362306a36Sopenharmony_ci { 0, 0 }, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 20762306a36Sopenharmony_ci { 24000, 48000 }, 20862306a36Sopenharmony_ci { 14400, 24000 }, 20962306a36Sopenharmony_ci { 0, 0 }, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 21362306a36Sopenharmony_ci { 60000, 112200 }, 21462306a36Sopenharmony_ci { 37800, 60000 }, 21562306a36Sopenharmony_ci { 0, 0 }, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 21962306a36Sopenharmony_ci { 20000, 35000 }, 22062306a36Sopenharmony_ci { 12600, 20000 }, 22162306a36Sopenharmony_ci { 0, 0 }, 22262306a36Sopenharmony_ci}; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { 22562306a36Sopenharmony_ci { 52000, 96824 }, 22662306a36Sopenharmony_ci { 38792, 67132 }, 22762306a36Sopenharmony_ci { 0, 0 }, 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { 23162306a36Sopenharmony_ci [tsl2571] = tsl2x71_lux_table, 23262306a36Sopenharmony_ci [tsl2671] = tsl2x71_lux_table, 23362306a36Sopenharmony_ci [tmd2671] = tmd2x71_lux_table, 23462306a36Sopenharmony_ci [tsl2771] = tsl2x71_lux_table, 23562306a36Sopenharmony_ci [tmd2771] = tmd2x71_lux_table, 23662306a36Sopenharmony_ci [tsl2572] = tsl2x72_lux_table, 23762306a36Sopenharmony_ci [tsl2672] = tsl2x72_lux_table, 23862306a36Sopenharmony_ci [tmd2672] = tmd2x72_lux_table, 23962306a36Sopenharmony_ci [tsl2772] = tsl2x72_lux_table, 24062306a36Sopenharmony_ci [tmd2772] = tmd2x72_lux_table, 24162306a36Sopenharmony_ci [apds9930] = apds9930_lux_table, 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cistatic const struct tsl2772_settings tsl2772_default_settings = { 24562306a36Sopenharmony_ci .als_time = 255, /* 2.72 / 2.73 ms */ 24662306a36Sopenharmony_ci .als_gain = 0, 24762306a36Sopenharmony_ci .prox_time = 255, /* 2.72 / 2.73 ms */ 24862306a36Sopenharmony_ci .prox_gain = 0, 24962306a36Sopenharmony_ci .wait_time = 255, 25062306a36Sopenharmony_ci .als_prox_config = 0, 25162306a36Sopenharmony_ci .als_gain_trim = 1000, 25262306a36Sopenharmony_ci .als_cal_target = 150, 25362306a36Sopenharmony_ci .als_persistence = 1, 25462306a36Sopenharmony_ci .als_interrupt_en = false, 25562306a36Sopenharmony_ci .als_thresh_low = 200, 25662306a36Sopenharmony_ci .als_thresh_high = 256, 25762306a36Sopenharmony_ci .prox_persistence = 1, 25862306a36Sopenharmony_ci .prox_interrupt_en = false, 25962306a36Sopenharmony_ci .prox_thres_low = 0, 26062306a36Sopenharmony_ci .prox_thres_high = 512, 26162306a36Sopenharmony_ci .prox_max_samples_cal = 30, 26262306a36Sopenharmony_ci .prox_pulse_count = 8, 26362306a36Sopenharmony_ci .prox_diode = TSL2772_DIODE1, 26462306a36Sopenharmony_ci .prox_power = TSL2772_100_mA 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const s16 tsl2772_als_gain[] = { 26862306a36Sopenharmony_ci 1, 26962306a36Sopenharmony_ci 8, 27062306a36Sopenharmony_ci 16, 27162306a36Sopenharmony_ci 120 27262306a36Sopenharmony_ci}; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic const s16 tsl2772_prox_gain[] = { 27562306a36Sopenharmony_ci 1, 27662306a36Sopenharmony_ci 2, 27762306a36Sopenharmony_ci 4, 27862306a36Sopenharmony_ci 8 27962306a36Sopenharmony_ci}; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic const int tsl2772_int_time_avail[][6] = { 28262306a36Sopenharmony_ci [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, 28362306a36Sopenharmony_ci [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, 28462306a36Sopenharmony_ci [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, 28562306a36Sopenharmony_ci [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, 28662306a36Sopenharmony_ci [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, 28762306a36Sopenharmony_ci [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, 28862306a36Sopenharmony_ci [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, 28962306a36Sopenharmony_ci [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, 29062306a36Sopenharmony_ci [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, 29162306a36Sopenharmony_ci [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, 29262306a36Sopenharmony_ci [apds9930] = { 0, 2730, 0, 2730, 0, 699000 }, 29362306a36Sopenharmony_ci}; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* Channel variations */ 30062306a36Sopenharmony_cienum { 30162306a36Sopenharmony_ci ALS, 30262306a36Sopenharmony_ci PRX, 30362306a36Sopenharmony_ci ALSPRX, 30462306a36Sopenharmony_ci PRX2, 30562306a36Sopenharmony_ci ALSPRX2, 30662306a36Sopenharmony_ci}; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic const u8 device_channel_config[] = { 30962306a36Sopenharmony_ci [tsl2571] = ALS, 31062306a36Sopenharmony_ci [tsl2671] = PRX, 31162306a36Sopenharmony_ci [tmd2671] = PRX, 31262306a36Sopenharmony_ci [tsl2771] = ALSPRX, 31362306a36Sopenharmony_ci [tmd2771] = ALSPRX, 31462306a36Sopenharmony_ci [tsl2572] = ALS, 31562306a36Sopenharmony_ci [tsl2672] = PRX2, 31662306a36Sopenharmony_ci [tmd2672] = PRX2, 31762306a36Sopenharmony_ci [tsl2772] = ALSPRX2, 31862306a36Sopenharmony_ci [tmd2772] = ALSPRX2, 31962306a36Sopenharmony_ci [apds9930] = ALSPRX2, 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_cistatic int tsl2772_read_status(struct tsl2772_chip *chip) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci int ret; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 32762306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_STATUS); 32862306a36Sopenharmony_ci if (ret < 0) 32962306a36Sopenharmony_ci dev_err(&chip->client->dev, 33062306a36Sopenharmony_ci "%s: failed to read STATUS register: %d\n", __func__, 33162306a36Sopenharmony_ci ret); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cistatic int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci int ret; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(chip->client, 34162306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CNTRL, data); 34262306a36Sopenharmony_ci if (ret < 0) { 34362306a36Sopenharmony_ci dev_err(&chip->client->dev, 34462306a36Sopenharmony_ci "%s: failed to write to control register %x: %d\n", 34562306a36Sopenharmony_ci __func__, data, ret); 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci return ret; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, 35262306a36Sopenharmony_ci int upper_reg) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u8 buf[2]; 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 35862306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | 35962306a36Sopenharmony_ci lower_reg); 36062306a36Sopenharmony_ci if (ret < 0) { 36162306a36Sopenharmony_ci dev_err(&chip->client->dev, 36262306a36Sopenharmony_ci "%s: failed to enable auto increment protocol: %d\n", 36362306a36Sopenharmony_ci __func__, ret); 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 36862306a36Sopenharmony_ci TSL2772_CMD_REG | lower_reg); 36962306a36Sopenharmony_ci if (ret < 0) { 37062306a36Sopenharmony_ci dev_err(&chip->client->dev, 37162306a36Sopenharmony_ci "%s: failed to read from register %x: %d\n", __func__, 37262306a36Sopenharmony_ci lower_reg, ret); 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci buf[0] = ret; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 37862306a36Sopenharmony_ci TSL2772_CMD_REG | upper_reg); 37962306a36Sopenharmony_ci if (ret < 0) { 38062306a36Sopenharmony_ci dev_err(&chip->client->dev, 38162306a36Sopenharmony_ci "%s: failed to read from register %x: %d\n", __func__, 38262306a36Sopenharmony_ci upper_reg, ret); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci buf[1] = ret; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 38862306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | 38962306a36Sopenharmony_ci lower_reg); 39062306a36Sopenharmony_ci if (ret < 0) { 39162306a36Sopenharmony_ci dev_err(&chip->client->dev, 39262306a36Sopenharmony_ci "%s: failed to enable repeated byte protocol: %d\n", 39362306a36Sopenharmony_ci __func__, ret); 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return le16_to_cpup((const __le16 *)&buf[0]); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/** 40162306a36Sopenharmony_ci * tsl2772_get_lux() - Reads and calculates current lux value. 40262306a36Sopenharmony_ci * @indio_dev: pointer to IIO device 40362306a36Sopenharmony_ci * 40462306a36Sopenharmony_ci * The raw ch0 and ch1 values of the ambient light sensed in the last 40562306a36Sopenharmony_ci * integration cycle are read from the device. The raw values are multiplied 40662306a36Sopenharmony_ci * by a device-specific scale factor, and divided by the integration time and 40762306a36Sopenharmony_ci * device gain. The code supports multiple lux equations through the lux table 40862306a36Sopenharmony_ci * coefficients. A lux gain trim is applied to each lux equation, and then the 40962306a36Sopenharmony_ci * maximum lux within the interval 0..65535 is selected. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_cistatic int tsl2772_get_lux(struct iio_dev *indio_dev) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 41462306a36Sopenharmony_ci struct tsl2772_lux *p; 41562306a36Sopenharmony_ci int max_lux, ret; 41662306a36Sopenharmony_ci bool overflow; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci mutex_lock(&chip->als_mutex); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { 42162306a36Sopenharmony_ci dev_err(&chip->client->dev, "%s: device is not enabled\n", 42262306a36Sopenharmony_ci __func__); 42362306a36Sopenharmony_ci ret = -EBUSY; 42462306a36Sopenharmony_ci goto out_unlock; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci ret = tsl2772_read_status(chip); 42862306a36Sopenharmony_ci if (ret < 0) 42962306a36Sopenharmony_ci goto out_unlock; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci if (!(ret & TSL2772_STA_ADC_VALID)) { 43262306a36Sopenharmony_ci dev_err(&chip->client->dev, 43362306a36Sopenharmony_ci "%s: data not valid yet\n", __func__); 43462306a36Sopenharmony_ci ret = chip->als_cur_info.lux; /* return LAST VALUE */ 43562306a36Sopenharmony_ci goto out_unlock; 43662306a36Sopenharmony_ci } 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, 43962306a36Sopenharmony_ci TSL2772_ALS_CHAN0HI); 44062306a36Sopenharmony_ci if (ret < 0) 44162306a36Sopenharmony_ci goto out_unlock; 44262306a36Sopenharmony_ci chip->als_cur_info.als_ch0 = ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, 44562306a36Sopenharmony_ci TSL2772_ALS_CHAN1HI); 44662306a36Sopenharmony_ci if (ret < 0) 44762306a36Sopenharmony_ci goto out_unlock; 44862306a36Sopenharmony_ci chip->als_cur_info.als_ch1 = ret; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { 45162306a36Sopenharmony_ci max_lux = TSL2772_LUX_CALC_OVER_FLOW; 45262306a36Sopenharmony_ci goto update_struct_with_max_lux; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci if (!chip->als_cur_info.als_ch0) { 45662306a36Sopenharmony_ci /* have no data, so return LAST VALUE */ 45762306a36Sopenharmony_ci ret = chip->als_cur_info.lux; 45862306a36Sopenharmony_ci goto out_unlock; 45962306a36Sopenharmony_ci } 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci max_lux = 0; 46262306a36Sopenharmony_ci overflow = false; 46362306a36Sopenharmony_ci for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; 46462306a36Sopenharmony_ci p++) { 46562306a36Sopenharmony_ci int lux; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci lux = ((chip->als_cur_info.als_ch0 * p->ch0) - 46862306a36Sopenharmony_ci (chip->als_cur_info.als_ch1 * p->ch1)) / 46962306a36Sopenharmony_ci chip->als_gain_time_scale; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* 47262306a36Sopenharmony_ci * The als_gain_trim can have a value within the range 250..4000 47362306a36Sopenharmony_ci * and is a multiplier for the lux. A trim of 1000 makes no 47462306a36Sopenharmony_ci * changes to the lux, less than 1000 scales it down, and 47562306a36Sopenharmony_ci * greater than 1000 scales it up. 47662306a36Sopenharmony_ci */ 47762306a36Sopenharmony_ci lux = (lux * chip->settings.als_gain_trim) / 1000; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (lux > TSL2772_LUX_CALC_OVER_FLOW) { 48062306a36Sopenharmony_ci overflow = true; 48162306a36Sopenharmony_ci continue; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci max_lux = max(max_lux, lux); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (overflow && max_lux == 0) 48862306a36Sopenharmony_ci max_lux = TSL2772_LUX_CALC_OVER_FLOW; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ciupdate_struct_with_max_lux: 49162306a36Sopenharmony_ci chip->als_cur_info.lux = max_lux; 49262306a36Sopenharmony_ci ret = max_lux; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ciout_unlock: 49562306a36Sopenharmony_ci mutex_unlock(&chip->als_mutex); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return ret; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/** 50162306a36Sopenharmony_ci * tsl2772_get_prox() - Reads proximity data registers and updates 50262306a36Sopenharmony_ci * chip->prox_data. 50362306a36Sopenharmony_ci * 50462306a36Sopenharmony_ci * @indio_dev: pointer to IIO device 50562306a36Sopenharmony_ci */ 50662306a36Sopenharmony_cistatic int tsl2772_get_prox(struct iio_dev *indio_dev) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 50962306a36Sopenharmony_ci int ret; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci mutex_lock(&chip->prox_mutex); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci ret = tsl2772_read_status(chip); 51462306a36Sopenharmony_ci if (ret < 0) 51562306a36Sopenharmony_ci goto prox_poll_err; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci switch (chip->id) { 51862306a36Sopenharmony_ci case tsl2571: 51962306a36Sopenharmony_ci case tsl2671: 52062306a36Sopenharmony_ci case tmd2671: 52162306a36Sopenharmony_ci case tsl2771: 52262306a36Sopenharmony_ci case tmd2771: 52362306a36Sopenharmony_ci if (!(ret & TSL2772_STA_ADC_VALID)) { 52462306a36Sopenharmony_ci ret = -EINVAL; 52562306a36Sopenharmony_ci goto prox_poll_err; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci case tsl2572: 52962306a36Sopenharmony_ci case tsl2672: 53062306a36Sopenharmony_ci case tmd2672: 53162306a36Sopenharmony_ci case tsl2772: 53262306a36Sopenharmony_ci case tmd2772: 53362306a36Sopenharmony_ci case apds9930: 53462306a36Sopenharmony_ci if (!(ret & TSL2772_STA_PRX_VALID)) { 53562306a36Sopenharmony_ci ret = -EINVAL; 53662306a36Sopenharmony_ci goto prox_poll_err; 53762306a36Sopenharmony_ci } 53862306a36Sopenharmony_ci break; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); 54262306a36Sopenharmony_ci if (ret < 0) 54362306a36Sopenharmony_ci goto prox_poll_err; 54462306a36Sopenharmony_ci chip->prox_data = ret; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ciprox_poll_err: 54762306a36Sopenharmony_ci mutex_unlock(&chip->prox_mutex); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci return ret; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic int tsl2772_read_prox_led_current(struct tsl2772_chip *chip) 55362306a36Sopenharmony_ci{ 55462306a36Sopenharmony_ci struct device *dev = &chip->client->dev; 55562306a36Sopenharmony_ci int ret, tmp, i; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci ret = device_property_read_u32(dev, "led-max-microamp", &tmp); 55862306a36Sopenharmony_ci if (ret < 0) 55962306a36Sopenharmony_ci return ret; 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci for (i = 0; tsl2772_led_currents[i][0] != 0; i++) { 56262306a36Sopenharmony_ci if (tmp == tsl2772_led_currents[i][0]) { 56362306a36Sopenharmony_ci chip->settings.prox_power = tsl2772_led_currents[i][1]; 56462306a36Sopenharmony_ci return 0; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci dev_err(dev, "Invalid value %d for led-max-microamp\n", tmp); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci} 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_cistatic int tsl2772_read_prox_diodes(struct tsl2772_chip *chip) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct device *dev = &chip->client->dev; 57662306a36Sopenharmony_ci int i, ret, num_leds, prox_diode_mask; 57762306a36Sopenharmony_ci u32 leds[TSL2772_MAX_PROX_LEDS]; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ret = device_property_count_u32(dev, "amstaos,proximity-diodes"); 58062306a36Sopenharmony_ci if (ret < 0) 58162306a36Sopenharmony_ci return ret; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci num_leds = ret; 58462306a36Sopenharmony_ci if (num_leds > TSL2772_MAX_PROX_LEDS) 58562306a36Sopenharmony_ci num_leds = TSL2772_MAX_PROX_LEDS; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci ret = device_property_read_u32_array(dev, "amstaos,proximity-diodes", leds, num_leds); 58862306a36Sopenharmony_ci if (ret < 0) { 58962306a36Sopenharmony_ci dev_err(dev, "Invalid value for amstaos,proximity-diodes: %d.\n", ret); 59062306a36Sopenharmony_ci return ret; 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci prox_diode_mask = 0; 59462306a36Sopenharmony_ci for (i = 0; i < num_leds; i++) { 59562306a36Sopenharmony_ci if (leds[i] == 0) 59662306a36Sopenharmony_ci prox_diode_mask |= TSL2772_DIODE0; 59762306a36Sopenharmony_ci else if (leds[i] == 1) 59862306a36Sopenharmony_ci prox_diode_mask |= TSL2772_DIODE1; 59962306a36Sopenharmony_ci else { 60062306a36Sopenharmony_ci dev_err(dev, "Invalid value %d in amstaos,proximity-diodes.\n", leds[i]); 60162306a36Sopenharmony_ci return -EINVAL; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci chip->settings.prox_diode = prox_diode_mask; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci return 0; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic void tsl2772_parse_dt(struct tsl2772_chip *chip) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci tsl2772_read_prox_led_current(chip); 61262306a36Sopenharmony_ci tsl2772_read_prox_diodes(chip); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci/** 61662306a36Sopenharmony_ci * tsl2772_defaults() - Populates the device nominal operating parameters 61762306a36Sopenharmony_ci * with those provided by a 'platform' data struct or 61862306a36Sopenharmony_ci * with prefined defaults. 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * @chip: pointer to device structure. 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_cistatic void tsl2772_defaults(struct tsl2772_chip *chip) 62362306a36Sopenharmony_ci{ 62462306a36Sopenharmony_ci /* If Operational settings defined elsewhere.. */ 62562306a36Sopenharmony_ci if (chip->pdata && chip->pdata->platform_default_settings) 62662306a36Sopenharmony_ci memcpy(&chip->settings, chip->pdata->platform_default_settings, 62762306a36Sopenharmony_ci sizeof(tsl2772_default_settings)); 62862306a36Sopenharmony_ci else 62962306a36Sopenharmony_ci memcpy(&chip->settings, &tsl2772_default_settings, 63062306a36Sopenharmony_ci sizeof(tsl2772_default_settings)); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* Load up the proper lux table. */ 63362306a36Sopenharmony_ci if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) 63462306a36Sopenharmony_ci memcpy(chip->tsl2772_device_lux, 63562306a36Sopenharmony_ci chip->pdata->platform_lux_table, 63662306a36Sopenharmony_ci sizeof(chip->pdata->platform_lux_table)); 63762306a36Sopenharmony_ci else 63862306a36Sopenharmony_ci memcpy(chip->tsl2772_device_lux, 63962306a36Sopenharmony_ci tsl2772_default_lux_table_group[chip->id], 64062306a36Sopenharmony_ci TSL2772_DEFAULT_TABLE_BYTES); 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci tsl2772_parse_dt(chip); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * tsl2772_als_calibrate() - Obtain single reading and calculate 64762306a36Sopenharmony_ci * the als_gain_trim. 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * @indio_dev: pointer to IIO device 65062306a36Sopenharmony_ci */ 65162306a36Sopenharmony_cistatic int tsl2772_als_calibrate(struct iio_dev *indio_dev) 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 65462306a36Sopenharmony_ci int ret, lux_val; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 65762306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CNTRL); 65862306a36Sopenharmony_ci if (ret < 0) { 65962306a36Sopenharmony_ci dev_err(&chip->client->dev, 66062306a36Sopenharmony_ci "%s: failed to read from the CNTRL register\n", 66162306a36Sopenharmony_ci __func__); 66262306a36Sopenharmony_ci return ret; 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) 66662306a36Sopenharmony_ci != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { 66762306a36Sopenharmony_ci dev_err(&chip->client->dev, 66862306a36Sopenharmony_ci "%s: Device is not powered on and/or ADC is not enabled\n", 66962306a36Sopenharmony_ci __func__); 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { 67262306a36Sopenharmony_ci dev_err(&chip->client->dev, 67362306a36Sopenharmony_ci "%s: The two ADC channels have not completed an integration cycle\n", 67462306a36Sopenharmony_ci __func__); 67562306a36Sopenharmony_ci return -ENODATA; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci lux_val = tsl2772_get_lux(indio_dev); 67962306a36Sopenharmony_ci if (lux_val < 0) { 68062306a36Sopenharmony_ci dev_err(&chip->client->dev, 68162306a36Sopenharmony_ci "%s: failed to get lux\n", __func__); 68262306a36Sopenharmony_ci return lux_val; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci if (lux_val == 0) 68562306a36Sopenharmony_ci return -ERANGE; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / 68862306a36Sopenharmony_ci lux_val; 68962306a36Sopenharmony_ci if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) 69062306a36Sopenharmony_ci return -ERANGE; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci chip->settings.als_gain_trim = ret; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci return ret; 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic void tsl2772_disable_regulators_action(void *_data) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct tsl2772_chip *chip = _data; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies); 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic int tsl2772_chip_on(struct iio_dev *indio_dev) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 70762306a36Sopenharmony_ci int ret, i, als_count, als_time_us; 70862306a36Sopenharmony_ci u8 *dev_reg, reg_val; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci /* Non calculated parameters */ 71162306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; 71262306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; 71362306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; 71462306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = 71562306a36Sopenharmony_ci chip->settings.als_prox_config; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = 71862306a36Sopenharmony_ci (chip->settings.als_thresh_low) & 0xFF; 71962306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = 72062306a36Sopenharmony_ci (chip->settings.als_thresh_low >> 8) & 0xFF; 72162306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = 72262306a36Sopenharmony_ci (chip->settings.als_thresh_high) & 0xFF; 72362306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = 72462306a36Sopenharmony_ci (chip->settings.als_thresh_high >> 8) & 0xFF; 72562306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PERSISTENCE] = 72662306a36Sopenharmony_ci (chip->settings.prox_persistence & 0xFF) << 4 | 72762306a36Sopenharmony_ci (chip->settings.als_persistence & 0xFF); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_COUNT] = 73062306a36Sopenharmony_ci chip->settings.prox_pulse_count; 73162306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = 73262306a36Sopenharmony_ci (chip->settings.prox_thres_low) & 0xFF; 73362306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = 73462306a36Sopenharmony_ci (chip->settings.prox_thres_low >> 8) & 0xFF; 73562306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = 73662306a36Sopenharmony_ci (chip->settings.prox_thres_high) & 0xFF; 73762306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = 73862306a36Sopenharmony_ci (chip->settings.prox_thres_high >> 8) & 0xFF; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* and make sure we're not already on */ 74162306a36Sopenharmony_ci if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { 74262306a36Sopenharmony_ci /* if forcing a register update - turn off, then on */ 74362306a36Sopenharmony_ci dev_info(&chip->client->dev, "device is already enabled\n"); 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci /* Set the gain based on tsl2772_settings struct */ 74862306a36Sopenharmony_ci chip->tsl2772_config[TSL2772_GAIN] = 74962306a36Sopenharmony_ci (chip->settings.als_gain & 0xFF) | 75062306a36Sopenharmony_ci ((chip->settings.prox_gain & 0xFF) << 2) | 75162306a36Sopenharmony_ci (chip->settings.prox_diode << 4) | 75262306a36Sopenharmony_ci (chip->settings.prox_power << 6); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci /* set chip time scaling and saturation */ 75562306a36Sopenharmony_ci als_count = 256 - chip->settings.als_time; 75662306a36Sopenharmony_ci als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; 75762306a36Sopenharmony_ci chip->als_saturation = als_count * 768; /* 75% of full scale */ 75862306a36Sopenharmony_ci chip->als_gain_time_scale = als_time_us * 75962306a36Sopenharmony_ci tsl2772_als_gain[chip->settings.als_gain]; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci /* 76262306a36Sopenharmony_ci * TSL2772 Specific power-on / adc enable sequence 76362306a36Sopenharmony_ci * Power on the device 1st. 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_ci ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); 76662306a36Sopenharmony_ci if (ret < 0) 76762306a36Sopenharmony_ci return ret; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* 77062306a36Sopenharmony_ci * Use the following shadow copy for our delay before enabling ADC. 77162306a36Sopenharmony_ci * Write all the registers. 77262306a36Sopenharmony_ci */ 77362306a36Sopenharmony_ci for (i = 0, dev_reg = chip->tsl2772_config; 77462306a36Sopenharmony_ci i < TSL2772_MAX_CONFIG_REG; i++) { 77562306a36Sopenharmony_ci int reg = TSL2772_CMD_REG + i; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci ret = i2c_smbus_write_byte_data(chip->client, reg, 77862306a36Sopenharmony_ci *dev_reg++); 77962306a36Sopenharmony_ci if (ret < 0) { 78062306a36Sopenharmony_ci dev_err(&chip->client->dev, 78162306a36Sopenharmony_ci "%s: failed to write to register %x: %d\n", 78262306a36Sopenharmony_ci __func__, reg, ret); 78362306a36Sopenharmony_ci return ret; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Power-on settling time */ 78862306a36Sopenharmony_ci usleep_range(3000, 3500); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | 79162306a36Sopenharmony_ci TSL2772_CNTL_PROX_DET_ENBL; 79262306a36Sopenharmony_ci if (chip->settings.als_interrupt_en) 79362306a36Sopenharmony_ci reg_val |= TSL2772_CNTL_ALS_INT_ENBL; 79462306a36Sopenharmony_ci if (chip->settings.prox_interrupt_en) 79562306a36Sopenharmony_ci reg_val |= TSL2772_CNTL_PROX_INT_ENBL; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci ret = tsl2772_write_control_reg(chip, reg_val); 79862306a36Sopenharmony_ci if (ret < 0) 79962306a36Sopenharmony_ci return ret; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 80262306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | 80362306a36Sopenharmony_ci TSL2772_CMD_PROXALS_INT_CLR); 80462306a36Sopenharmony_ci if (ret < 0) { 80562306a36Sopenharmony_ci dev_err(&chip->client->dev, 80662306a36Sopenharmony_ci "%s: failed to clear interrupt status: %d\n", 80762306a36Sopenharmony_ci __func__, ret); 80862306a36Sopenharmony_ci return ret; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return ret; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic int tsl2772_chip_off(struct iio_dev *indio_dev) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* turn device off */ 82162306a36Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; 82262306a36Sopenharmony_ci return tsl2772_write_control_reg(chip, 0x00); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void tsl2772_chip_off_action(void *data) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct iio_dev *indio_dev = data; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci tsl2772_chip_off(indio_dev); 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/** 83362306a36Sopenharmony_ci * tsl2772_invoke_change - power cycle the device to implement the user 83462306a36Sopenharmony_ci * parameters 83562306a36Sopenharmony_ci * @indio_dev: pointer to IIO device 83662306a36Sopenharmony_ci * 83762306a36Sopenharmony_ci * Obtain and lock both ALS and PROX resources, determine and save device state 83862306a36Sopenharmony_ci * (On/Off), cycle device to implement updated parameter, put device back into 83962306a36Sopenharmony_ci * proper state, and unlock resource. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_cistatic int tsl2772_invoke_change(struct iio_dev *indio_dev) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 84462306a36Sopenharmony_ci int device_status = chip->tsl2772_chip_status; 84562306a36Sopenharmony_ci int ret; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci mutex_lock(&chip->als_mutex); 84862306a36Sopenharmony_ci mutex_lock(&chip->prox_mutex); 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (device_status == TSL2772_CHIP_WORKING) { 85162306a36Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 85262306a36Sopenharmony_ci if (ret < 0) 85362306a36Sopenharmony_ci goto unlock; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci ret = tsl2772_chip_on(indio_dev); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ciunlock: 85962306a36Sopenharmony_ci mutex_unlock(&chip->prox_mutex); 86062306a36Sopenharmony_ci mutex_unlock(&chip->als_mutex); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci return ret; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic int tsl2772_prox_cal(struct iio_dev *indio_dev) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 86862306a36Sopenharmony_ci int prox_history[MAX_SAMPLES_CAL + 1]; 86962306a36Sopenharmony_ci int i, ret, mean, max, sample_sum; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (chip->settings.prox_max_samples_cal < 1 || 87262306a36Sopenharmony_ci chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) 87362306a36Sopenharmony_ci return -EINVAL; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { 87662306a36Sopenharmony_ci usleep_range(15000, 17500); 87762306a36Sopenharmony_ci ret = tsl2772_get_prox(indio_dev); 87862306a36Sopenharmony_ci if (ret < 0) 87962306a36Sopenharmony_ci return ret; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci prox_history[i] = chip->prox_data; 88262306a36Sopenharmony_ci } 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci sample_sum = 0; 88562306a36Sopenharmony_ci max = INT_MIN; 88662306a36Sopenharmony_ci for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { 88762306a36Sopenharmony_ci sample_sum += prox_history[i]; 88862306a36Sopenharmony_ci max = max(max, prox_history[i]); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci mean = sample_sum / chip->settings.prox_max_samples_cal; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci chip->settings.prox_thres_high = (max << 1) - mean; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_cistatic int tsl2772_read_avail(struct iio_dev *indio_dev, 89862306a36Sopenharmony_ci struct iio_chan_spec const *chan, 89962306a36Sopenharmony_ci const int **vals, int *type, int *length, 90062306a36Sopenharmony_ci long mask) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci switch (mask) { 90562306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 90662306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 90762306a36Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); 90862306a36Sopenharmony_ci *vals = tsl2772_int_calibscale_avail; 90962306a36Sopenharmony_ci } else { 91062306a36Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); 91162306a36Sopenharmony_ci *vals = tsl2772_prox_calibscale_avail; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci *type = IIO_VAL_INT; 91462306a36Sopenharmony_ci return IIO_AVAIL_LIST; 91562306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 91662306a36Sopenharmony_ci *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); 91762306a36Sopenharmony_ci *vals = tsl2772_int_time_avail[chip->id]; 91862306a36Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 91962306a36Sopenharmony_ci return IIO_AVAIL_RANGE; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return -EINVAL; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_cistatic ssize_t in_illuminance0_target_input_show(struct device *dev, 92662306a36Sopenharmony_ci struct device_attribute *attr, 92762306a36Sopenharmony_ci char *buf) 92862306a36Sopenharmony_ci{ 92962306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci return scnprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); 93262306a36Sopenharmony_ci} 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_cistatic ssize_t in_illuminance0_target_input_store(struct device *dev, 93562306a36Sopenharmony_ci struct device_attribute *attr, 93662306a36Sopenharmony_ci const char *buf, size_t len) 93762306a36Sopenharmony_ci{ 93862306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 93962306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 94062306a36Sopenharmony_ci u16 value; 94162306a36Sopenharmony_ci int ret; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci if (kstrtou16(buf, 0, &value)) 94462306a36Sopenharmony_ci return -EINVAL; 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci chip->settings.als_cal_target = value; 94762306a36Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 94862306a36Sopenharmony_ci if (ret < 0) 94962306a36Sopenharmony_ci return ret; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return len; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_cistatic ssize_t in_illuminance0_calibrate_store(struct device *dev, 95562306a36Sopenharmony_ci struct device_attribute *attr, 95662306a36Sopenharmony_ci const char *buf, size_t len) 95762306a36Sopenharmony_ci{ 95862306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 95962306a36Sopenharmony_ci bool value; 96062306a36Sopenharmony_ci int ret; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci if (kstrtobool(buf, &value) || !value) 96362306a36Sopenharmony_ci return -EINVAL; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci ret = tsl2772_als_calibrate(indio_dev); 96662306a36Sopenharmony_ci if (ret < 0) 96762306a36Sopenharmony_ci return ret; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 97062306a36Sopenharmony_ci if (ret < 0) 97162306a36Sopenharmony_ci return ret; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci return len; 97462306a36Sopenharmony_ci} 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_show(struct device *dev, 97762306a36Sopenharmony_ci struct device_attribute *attr, 97862306a36Sopenharmony_ci char *buf) 97962306a36Sopenharmony_ci{ 98062306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); 98162306a36Sopenharmony_ci int i = 0; 98262306a36Sopenharmony_ci int offset = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci while (i < TSL2772_MAX_LUX_TABLE_SIZE) { 98562306a36Sopenharmony_ci offset += scnprintf(buf + offset, PAGE_SIZE - offset, "%u,%u,", 98662306a36Sopenharmony_ci chip->tsl2772_device_lux[i].ch0, 98762306a36Sopenharmony_ci chip->tsl2772_device_lux[i].ch1); 98862306a36Sopenharmony_ci if (chip->tsl2772_device_lux[i].ch0 == 0) { 98962306a36Sopenharmony_ci /* 99062306a36Sopenharmony_ci * We just printed the first "0" entry. 99162306a36Sopenharmony_ci * Now get rid of the extra "," and break. 99262306a36Sopenharmony_ci */ 99362306a36Sopenharmony_ci offset--; 99462306a36Sopenharmony_ci break; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci i++; 99762306a36Sopenharmony_ci } 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci offset += scnprintf(buf + offset, PAGE_SIZE - offset, "\n"); 100062306a36Sopenharmony_ci return offset; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic ssize_t in_illuminance0_lux_table_store(struct device *dev, 100462306a36Sopenharmony_ci struct device_attribute *attr, 100562306a36Sopenharmony_ci const char *buf, size_t len) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 100862306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 100962306a36Sopenharmony_ci int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; 101062306a36Sopenharmony_ci int n, ret; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci get_options(buf, ARRAY_SIZE(value), value); 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci /* 101562306a36Sopenharmony_ci * We now have an array of ints starting at value[1], and 101662306a36Sopenharmony_ci * enumerated by value[0]. 101762306a36Sopenharmony_ci * We expect each group of two ints to be one table entry, 101862306a36Sopenharmony_ci * and the last table entry is all 0. 101962306a36Sopenharmony_ci */ 102062306a36Sopenharmony_ci n = value[0]; 102162306a36Sopenharmony_ci if ((n % 2) || n < 4 || 102262306a36Sopenharmony_ci n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if ((value[(n - 1)] | value[n]) != 0) 102662306a36Sopenharmony_ci return -EINVAL; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { 102962306a36Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 103062306a36Sopenharmony_ci if (ret < 0) 103162306a36Sopenharmony_ci return ret; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci /* Zero out the table */ 103562306a36Sopenharmony_ci memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); 103662306a36Sopenharmony_ci memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 103962306a36Sopenharmony_ci if (ret < 0) 104062306a36Sopenharmony_ci return ret; 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return len; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic ssize_t in_proximity0_calibrate_store(struct device *dev, 104662306a36Sopenharmony_ci struct device_attribute *attr, 104762306a36Sopenharmony_ci const char *buf, size_t len) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_to_iio_dev(dev); 105062306a36Sopenharmony_ci bool value; 105162306a36Sopenharmony_ci int ret; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (kstrtobool(buf, &value) || !value) 105462306a36Sopenharmony_ci return -EINVAL; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci ret = tsl2772_prox_cal(indio_dev); 105762306a36Sopenharmony_ci if (ret < 0) 105862306a36Sopenharmony_ci return ret; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci ret = tsl2772_invoke_change(indio_dev); 106162306a36Sopenharmony_ci if (ret < 0) 106262306a36Sopenharmony_ci return ret; 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci return len; 106562306a36Sopenharmony_ci} 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_cistatic int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, 106862306a36Sopenharmony_ci const struct iio_chan_spec *chan, 106962306a36Sopenharmony_ci enum iio_event_type type, 107062306a36Sopenharmony_ci enum iio_event_direction dir) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 107362306a36Sopenharmony_ci 107462306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) 107562306a36Sopenharmony_ci return chip->settings.als_interrupt_en; 107662306a36Sopenharmony_ci else 107762306a36Sopenharmony_ci return chip->settings.prox_interrupt_en; 107862306a36Sopenharmony_ci} 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_cistatic int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, 108162306a36Sopenharmony_ci const struct iio_chan_spec *chan, 108262306a36Sopenharmony_ci enum iio_event_type type, 108362306a36Sopenharmony_ci enum iio_event_direction dir, 108462306a36Sopenharmony_ci int val) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) 108962306a36Sopenharmony_ci chip->settings.als_interrupt_en = val ? true : false; 109062306a36Sopenharmony_ci else 109162306a36Sopenharmony_ci chip->settings.prox_interrupt_en = val ? true : false; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int tsl2772_write_event_value(struct iio_dev *indio_dev, 109762306a36Sopenharmony_ci const struct iio_chan_spec *chan, 109862306a36Sopenharmony_ci enum iio_event_type type, 109962306a36Sopenharmony_ci enum iio_event_direction dir, 110062306a36Sopenharmony_ci enum iio_event_info info, 110162306a36Sopenharmony_ci int val, int val2) 110262306a36Sopenharmony_ci{ 110362306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 110462306a36Sopenharmony_ci int ret = -EINVAL, count, persistence; 110562306a36Sopenharmony_ci u8 time; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci switch (info) { 110862306a36Sopenharmony_ci case IIO_EV_INFO_VALUE: 110962306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 111062306a36Sopenharmony_ci switch (dir) { 111162306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 111262306a36Sopenharmony_ci chip->settings.als_thresh_high = val; 111362306a36Sopenharmony_ci ret = 0; 111462306a36Sopenharmony_ci break; 111562306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 111662306a36Sopenharmony_ci chip->settings.als_thresh_low = val; 111762306a36Sopenharmony_ci ret = 0; 111862306a36Sopenharmony_ci break; 111962306a36Sopenharmony_ci default: 112062306a36Sopenharmony_ci break; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci } else { 112362306a36Sopenharmony_ci switch (dir) { 112462306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 112562306a36Sopenharmony_ci chip->settings.prox_thres_high = val; 112662306a36Sopenharmony_ci ret = 0; 112762306a36Sopenharmony_ci break; 112862306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 112962306a36Sopenharmony_ci chip->settings.prox_thres_low = val; 113062306a36Sopenharmony_ci ret = 0; 113162306a36Sopenharmony_ci break; 113262306a36Sopenharmony_ci default: 113362306a36Sopenharmony_ci break; 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci break; 113762306a36Sopenharmony_ci case IIO_EV_INFO_PERIOD: 113862306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) 113962306a36Sopenharmony_ci time = chip->settings.als_time; 114062306a36Sopenharmony_ci else 114162306a36Sopenharmony_ci time = chip->settings.prox_time; 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci count = 256 - time; 114462306a36Sopenharmony_ci persistence = ((val * 1000000) + val2) / 114562306a36Sopenharmony_ci (count * tsl2772_int_time_avail[chip->id][3]); 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 114862306a36Sopenharmony_ci /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ 114962306a36Sopenharmony_ci if (persistence > 3) 115062306a36Sopenharmony_ci persistence = (persistence / 5) + 3; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci chip->settings.als_persistence = persistence; 115362306a36Sopenharmony_ci } else { 115462306a36Sopenharmony_ci chip->settings.prox_persistence = persistence; 115562306a36Sopenharmony_ci } 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci ret = 0; 115862306a36Sopenharmony_ci break; 115962306a36Sopenharmony_ci default: 116062306a36Sopenharmony_ci break; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci if (ret < 0) 116462306a36Sopenharmony_ci return ret; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic int tsl2772_read_event_value(struct iio_dev *indio_dev, 117062306a36Sopenharmony_ci const struct iio_chan_spec *chan, 117162306a36Sopenharmony_ci enum iio_event_type type, 117262306a36Sopenharmony_ci enum iio_event_direction dir, 117362306a36Sopenharmony_ci enum iio_event_info info, 117462306a36Sopenharmony_ci int *val, int *val2) 117562306a36Sopenharmony_ci{ 117662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 117762306a36Sopenharmony_ci int filter_delay, persistence; 117862306a36Sopenharmony_ci u8 time; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci switch (info) { 118162306a36Sopenharmony_ci case IIO_EV_INFO_VALUE: 118262306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 118362306a36Sopenharmony_ci switch (dir) { 118462306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 118562306a36Sopenharmony_ci *val = chip->settings.als_thresh_high; 118662306a36Sopenharmony_ci return IIO_VAL_INT; 118762306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 118862306a36Sopenharmony_ci *val = chip->settings.als_thresh_low; 118962306a36Sopenharmony_ci return IIO_VAL_INT; 119062306a36Sopenharmony_ci default: 119162306a36Sopenharmony_ci return -EINVAL; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci } else { 119462306a36Sopenharmony_ci switch (dir) { 119562306a36Sopenharmony_ci case IIO_EV_DIR_RISING: 119662306a36Sopenharmony_ci *val = chip->settings.prox_thres_high; 119762306a36Sopenharmony_ci return IIO_VAL_INT; 119862306a36Sopenharmony_ci case IIO_EV_DIR_FALLING: 119962306a36Sopenharmony_ci *val = chip->settings.prox_thres_low; 120062306a36Sopenharmony_ci return IIO_VAL_INT; 120162306a36Sopenharmony_ci default: 120262306a36Sopenharmony_ci return -EINVAL; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci break; 120662306a36Sopenharmony_ci case IIO_EV_INFO_PERIOD: 120762306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 120862306a36Sopenharmony_ci time = chip->settings.als_time; 120962306a36Sopenharmony_ci persistence = chip->settings.als_persistence; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ 121262306a36Sopenharmony_ci if (persistence > 3) 121362306a36Sopenharmony_ci persistence = (persistence - 3) * 5; 121462306a36Sopenharmony_ci } else { 121562306a36Sopenharmony_ci time = chip->settings.prox_time; 121662306a36Sopenharmony_ci persistence = chip->settings.prox_persistence; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci filter_delay = persistence * (256 - time) * 122062306a36Sopenharmony_ci tsl2772_int_time_avail[chip->id][3]; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci *val = filter_delay / 1000000; 122362306a36Sopenharmony_ci *val2 = filter_delay % 1000000; 122462306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 122562306a36Sopenharmony_ci default: 122662306a36Sopenharmony_ci return -EINVAL; 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic int tsl2772_read_raw(struct iio_dev *indio_dev, 123162306a36Sopenharmony_ci struct iio_chan_spec const *chan, 123262306a36Sopenharmony_ci int *val, 123362306a36Sopenharmony_ci int *val2, 123462306a36Sopenharmony_ci long mask) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci switch (mask) { 123962306a36Sopenharmony_ci case IIO_CHAN_INFO_PROCESSED: 124062306a36Sopenharmony_ci switch (chan->type) { 124162306a36Sopenharmony_ci case IIO_LIGHT: 124262306a36Sopenharmony_ci tsl2772_get_lux(indio_dev); 124362306a36Sopenharmony_ci *val = chip->als_cur_info.lux; 124462306a36Sopenharmony_ci return IIO_VAL_INT; 124562306a36Sopenharmony_ci default: 124662306a36Sopenharmony_ci return -EINVAL; 124762306a36Sopenharmony_ci } 124862306a36Sopenharmony_ci case IIO_CHAN_INFO_RAW: 124962306a36Sopenharmony_ci switch (chan->type) { 125062306a36Sopenharmony_ci case IIO_INTENSITY: 125162306a36Sopenharmony_ci tsl2772_get_lux(indio_dev); 125262306a36Sopenharmony_ci if (chan->channel == 0) 125362306a36Sopenharmony_ci *val = chip->als_cur_info.als_ch0; 125462306a36Sopenharmony_ci else 125562306a36Sopenharmony_ci *val = chip->als_cur_info.als_ch1; 125662306a36Sopenharmony_ci return IIO_VAL_INT; 125762306a36Sopenharmony_ci case IIO_PROXIMITY: 125862306a36Sopenharmony_ci tsl2772_get_prox(indio_dev); 125962306a36Sopenharmony_ci *val = chip->prox_data; 126062306a36Sopenharmony_ci return IIO_VAL_INT; 126162306a36Sopenharmony_ci default: 126262306a36Sopenharmony_ci return -EINVAL; 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci break; 126562306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 126662306a36Sopenharmony_ci if (chan->type == IIO_LIGHT) 126762306a36Sopenharmony_ci *val = tsl2772_als_gain[chip->settings.als_gain]; 126862306a36Sopenharmony_ci else 126962306a36Sopenharmony_ci *val = tsl2772_prox_gain[chip->settings.prox_gain]; 127062306a36Sopenharmony_ci return IIO_VAL_INT; 127162306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 127262306a36Sopenharmony_ci *val = chip->settings.als_gain_trim; 127362306a36Sopenharmony_ci return IIO_VAL_INT; 127462306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 127562306a36Sopenharmony_ci *val = 0; 127662306a36Sopenharmony_ci *val2 = (256 - chip->settings.als_time) * 127762306a36Sopenharmony_ci tsl2772_int_time_avail[chip->id][3]; 127862306a36Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 127962306a36Sopenharmony_ci default: 128062306a36Sopenharmony_ci return -EINVAL; 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci} 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cistatic int tsl2772_write_raw(struct iio_dev *indio_dev, 128562306a36Sopenharmony_ci struct iio_chan_spec const *chan, 128662306a36Sopenharmony_ci int val, 128762306a36Sopenharmony_ci int val2, 128862306a36Sopenharmony_ci long mask) 128962306a36Sopenharmony_ci{ 129062306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci switch (mask) { 129362306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBSCALE: 129462306a36Sopenharmony_ci if (chan->type == IIO_INTENSITY) { 129562306a36Sopenharmony_ci switch (val) { 129662306a36Sopenharmony_ci case 1: 129762306a36Sopenharmony_ci chip->settings.als_gain = 0; 129862306a36Sopenharmony_ci break; 129962306a36Sopenharmony_ci case 8: 130062306a36Sopenharmony_ci chip->settings.als_gain = 1; 130162306a36Sopenharmony_ci break; 130262306a36Sopenharmony_ci case 16: 130362306a36Sopenharmony_ci chip->settings.als_gain = 2; 130462306a36Sopenharmony_ci break; 130562306a36Sopenharmony_ci case 120: 130662306a36Sopenharmony_ci chip->settings.als_gain = 3; 130762306a36Sopenharmony_ci break; 130862306a36Sopenharmony_ci default: 130962306a36Sopenharmony_ci return -EINVAL; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci } else { 131262306a36Sopenharmony_ci switch (val) { 131362306a36Sopenharmony_ci case 1: 131462306a36Sopenharmony_ci chip->settings.prox_gain = 0; 131562306a36Sopenharmony_ci break; 131662306a36Sopenharmony_ci case 2: 131762306a36Sopenharmony_ci chip->settings.prox_gain = 1; 131862306a36Sopenharmony_ci break; 131962306a36Sopenharmony_ci case 4: 132062306a36Sopenharmony_ci chip->settings.prox_gain = 2; 132162306a36Sopenharmony_ci break; 132262306a36Sopenharmony_ci case 8: 132362306a36Sopenharmony_ci chip->settings.prox_gain = 3; 132462306a36Sopenharmony_ci break; 132562306a36Sopenharmony_ci default: 132662306a36Sopenharmony_ci return -EINVAL; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci } 132962306a36Sopenharmony_ci break; 133062306a36Sopenharmony_ci case IIO_CHAN_INFO_CALIBBIAS: 133162306a36Sopenharmony_ci if (val < TSL2772_ALS_GAIN_TRIM_MIN || 133262306a36Sopenharmony_ci val > TSL2772_ALS_GAIN_TRIM_MAX) 133362306a36Sopenharmony_ci return -EINVAL; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci chip->settings.als_gain_trim = val; 133662306a36Sopenharmony_ci break; 133762306a36Sopenharmony_ci case IIO_CHAN_INFO_INT_TIME: 133862306a36Sopenharmony_ci if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || 133962306a36Sopenharmony_ci val2 > tsl2772_int_time_avail[chip->id][5]) 134062306a36Sopenharmony_ci return -EINVAL; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci chip->settings.als_time = 256 - 134362306a36Sopenharmony_ci (val2 / tsl2772_int_time_avail[chip->id][3]); 134462306a36Sopenharmony_ci break; 134562306a36Sopenharmony_ci default: 134662306a36Sopenharmony_ci return -EINVAL; 134762306a36Sopenharmony_ci } 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci return tsl2772_invoke_change(indio_dev); 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_target_input); 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_cistatic DEVICE_ATTR_WO(in_illuminance0_calibrate); 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic DEVICE_ATTR_WO(in_proximity0_calibrate); 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_cistatic DEVICE_ATTR_RW(in_illuminance0_lux_table); 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/* Use the default register values to identify the Taos device */ 136162306a36Sopenharmony_cistatic int tsl2772_device_id_verif(int id, int target) 136262306a36Sopenharmony_ci{ 136362306a36Sopenharmony_ci switch (target) { 136462306a36Sopenharmony_ci case tsl2571: 136562306a36Sopenharmony_ci case tsl2671: 136662306a36Sopenharmony_ci case tsl2771: 136762306a36Sopenharmony_ci return (id & 0xf0) == TRITON_ID; 136862306a36Sopenharmony_ci case tmd2671: 136962306a36Sopenharmony_ci case tmd2771: 137062306a36Sopenharmony_ci return (id & 0xf0) == HALIBUT_ID; 137162306a36Sopenharmony_ci case tsl2572: 137262306a36Sopenharmony_ci case tsl2672: 137362306a36Sopenharmony_ci case tmd2672: 137462306a36Sopenharmony_ci case tsl2772: 137562306a36Sopenharmony_ci case tmd2772: 137662306a36Sopenharmony_ci case apds9930: 137762306a36Sopenharmony_ci return (id & 0xf0) == SWORDFISH_ID; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci return -EINVAL; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_cistatic irqreturn_t tsl2772_event_handler(int irq, void *private) 138462306a36Sopenharmony_ci{ 138562306a36Sopenharmony_ci struct iio_dev *indio_dev = private; 138662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 138762306a36Sopenharmony_ci s64 timestamp = iio_get_time_ns(indio_dev); 138862306a36Sopenharmony_ci int ret; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci ret = tsl2772_read_status(chip); 139162306a36Sopenharmony_ci if (ret < 0) 139262306a36Sopenharmony_ci return IRQ_HANDLED; 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci /* What type of interrupt do we need to process */ 139562306a36Sopenharmony_ci if (ret & TSL2772_STA_PRX_INTR) { 139662306a36Sopenharmony_ci iio_push_event(indio_dev, 139762306a36Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 139862306a36Sopenharmony_ci 0, 139962306a36Sopenharmony_ci IIO_EV_TYPE_THRESH, 140062306a36Sopenharmony_ci IIO_EV_DIR_EITHER), 140162306a36Sopenharmony_ci timestamp); 140262306a36Sopenharmony_ci } 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci if (ret & TSL2772_STA_ALS_INTR) { 140562306a36Sopenharmony_ci iio_push_event(indio_dev, 140662306a36Sopenharmony_ci IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 140762306a36Sopenharmony_ci 0, 140862306a36Sopenharmony_ci IIO_EV_TYPE_THRESH, 140962306a36Sopenharmony_ci IIO_EV_DIR_EITHER), 141062306a36Sopenharmony_ci timestamp); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci ret = i2c_smbus_write_byte(chip->client, 141462306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | 141562306a36Sopenharmony_ci TSL2772_CMD_PROXALS_INT_CLR); 141662306a36Sopenharmony_ci if (ret < 0) 141762306a36Sopenharmony_ci dev_err(&chip->client->dev, 141862306a36Sopenharmony_ci "%s: failed to clear interrupt status: %d\n", 141962306a36Sopenharmony_ci __func__, ret); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci return IRQ_HANDLED; 142262306a36Sopenharmony_ci} 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_cistatic struct attribute *tsl2772_ALS_device_attrs[] = { 142562306a36Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 142662306a36Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 142762306a36Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 142862306a36Sopenharmony_ci NULL 142962306a36Sopenharmony_ci}; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic struct attribute *tsl2772_PRX_device_attrs[] = { 143262306a36Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 143362306a36Sopenharmony_ci NULL 143462306a36Sopenharmony_ci}; 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX_device_attrs[] = { 143762306a36Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 143862306a36Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 143962306a36Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 144062306a36Sopenharmony_ci NULL 144162306a36Sopenharmony_ci}; 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_cistatic struct attribute *tsl2772_PRX2_device_attrs[] = { 144462306a36Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 144562306a36Sopenharmony_ci NULL 144662306a36Sopenharmony_ci}; 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic struct attribute *tsl2772_ALSPRX2_device_attrs[] = { 144962306a36Sopenharmony_ci &dev_attr_in_illuminance0_target_input.attr, 145062306a36Sopenharmony_ci &dev_attr_in_illuminance0_calibrate.attr, 145162306a36Sopenharmony_ci &dev_attr_in_illuminance0_lux_table.attr, 145262306a36Sopenharmony_ci &dev_attr_in_proximity0_calibrate.attr, 145362306a36Sopenharmony_ci NULL 145462306a36Sopenharmony_ci}; 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_cistatic const struct attribute_group tsl2772_device_attr_group_tbl[] = { 145762306a36Sopenharmony_ci [ALS] = { 145862306a36Sopenharmony_ci .attrs = tsl2772_ALS_device_attrs, 145962306a36Sopenharmony_ci }, 146062306a36Sopenharmony_ci [PRX] = { 146162306a36Sopenharmony_ci .attrs = tsl2772_PRX_device_attrs, 146262306a36Sopenharmony_ci }, 146362306a36Sopenharmony_ci [ALSPRX] = { 146462306a36Sopenharmony_ci .attrs = tsl2772_ALSPRX_device_attrs, 146562306a36Sopenharmony_ci }, 146662306a36Sopenharmony_ci [PRX2] = { 146762306a36Sopenharmony_ci .attrs = tsl2772_PRX2_device_attrs, 146862306a36Sopenharmony_ci }, 146962306a36Sopenharmony_ci [ALSPRX2] = { 147062306a36Sopenharmony_ci .attrs = tsl2772_ALSPRX2_device_attrs, 147162306a36Sopenharmony_ci }, 147262306a36Sopenharmony_ci}; 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci#define TSL2772_DEVICE_INFO(type)[type] = \ 147562306a36Sopenharmony_ci { \ 147662306a36Sopenharmony_ci .attrs = &tsl2772_device_attr_group_tbl[type], \ 147762306a36Sopenharmony_ci .read_raw = &tsl2772_read_raw, \ 147862306a36Sopenharmony_ci .read_avail = &tsl2772_read_avail, \ 147962306a36Sopenharmony_ci .write_raw = &tsl2772_write_raw, \ 148062306a36Sopenharmony_ci .read_event_value = &tsl2772_read_event_value, \ 148162306a36Sopenharmony_ci .write_event_value = &tsl2772_write_event_value, \ 148262306a36Sopenharmony_ci .read_event_config = &tsl2772_read_interrupt_config, \ 148362306a36Sopenharmony_ci .write_event_config = &tsl2772_write_interrupt_config, \ 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic const struct iio_info tsl2772_device_info[] = { 148762306a36Sopenharmony_ci TSL2772_DEVICE_INFO(ALS), 148862306a36Sopenharmony_ci TSL2772_DEVICE_INFO(PRX), 148962306a36Sopenharmony_ci TSL2772_DEVICE_INFO(ALSPRX), 149062306a36Sopenharmony_ci TSL2772_DEVICE_INFO(PRX2), 149162306a36Sopenharmony_ci TSL2772_DEVICE_INFO(ALSPRX2), 149262306a36Sopenharmony_ci}; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_cistatic const struct iio_event_spec tsl2772_events[] = { 149562306a36Sopenharmony_ci { 149662306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 149762306a36Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 149862306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 149962306a36Sopenharmony_ci }, { 150062306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 150162306a36Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 150262306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 150362306a36Sopenharmony_ci }, { 150462306a36Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 150562306a36Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 150662306a36Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_PERIOD) | 150762306a36Sopenharmony_ci BIT(IIO_EV_INFO_ENABLE), 150862306a36Sopenharmony_ci }, 150962306a36Sopenharmony_ci}; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_cistatic const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { 151262306a36Sopenharmony_ci [ALS] = { 151362306a36Sopenharmony_ci .channel_with_events = { 151462306a36Sopenharmony_ci { 151562306a36Sopenharmony_ci .type = IIO_LIGHT, 151662306a36Sopenharmony_ci .indexed = 1, 151762306a36Sopenharmony_ci .channel = 0, 151862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 151962306a36Sopenharmony_ci }, { 152062306a36Sopenharmony_ci .type = IIO_INTENSITY, 152162306a36Sopenharmony_ci .indexed = 1, 152262306a36Sopenharmony_ci .channel = 0, 152362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 152462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 152562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 152662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 152762306a36Sopenharmony_ci .info_mask_separate_available = 152862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 152962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 153062306a36Sopenharmony_ci .event_spec = tsl2772_events, 153162306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 153262306a36Sopenharmony_ci }, { 153362306a36Sopenharmony_ci .type = IIO_INTENSITY, 153462306a36Sopenharmony_ci .indexed = 1, 153562306a36Sopenharmony_ci .channel = 1, 153662306a36Sopenharmony_ci }, 153762306a36Sopenharmony_ci }, 153862306a36Sopenharmony_ci .channel_without_events = { 153962306a36Sopenharmony_ci { 154062306a36Sopenharmony_ci .type = IIO_LIGHT, 154162306a36Sopenharmony_ci .indexed = 1, 154262306a36Sopenharmony_ci .channel = 0, 154362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 154462306a36Sopenharmony_ci }, { 154562306a36Sopenharmony_ci .type = IIO_INTENSITY, 154662306a36Sopenharmony_ci .indexed = 1, 154762306a36Sopenharmony_ci .channel = 0, 154862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 154962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 155062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 155162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 155262306a36Sopenharmony_ci .info_mask_separate_available = 155362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 155462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 155562306a36Sopenharmony_ci }, { 155662306a36Sopenharmony_ci .type = IIO_INTENSITY, 155762306a36Sopenharmony_ci .indexed = 1, 155862306a36Sopenharmony_ci .channel = 1, 155962306a36Sopenharmony_ci }, 156062306a36Sopenharmony_ci }, 156162306a36Sopenharmony_ci .chan_table_elements = 3, 156262306a36Sopenharmony_ci .info = &tsl2772_device_info[ALS], 156362306a36Sopenharmony_ci }, 156462306a36Sopenharmony_ci [PRX] = { 156562306a36Sopenharmony_ci .channel_with_events = { 156662306a36Sopenharmony_ci { 156762306a36Sopenharmony_ci .type = IIO_PROXIMITY, 156862306a36Sopenharmony_ci .indexed = 1, 156962306a36Sopenharmony_ci .channel = 0, 157062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 157162306a36Sopenharmony_ci .event_spec = tsl2772_events, 157262306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 157362306a36Sopenharmony_ci }, 157462306a36Sopenharmony_ci }, 157562306a36Sopenharmony_ci .channel_without_events = { 157662306a36Sopenharmony_ci { 157762306a36Sopenharmony_ci .type = IIO_PROXIMITY, 157862306a36Sopenharmony_ci .indexed = 1, 157962306a36Sopenharmony_ci .channel = 0, 158062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 158162306a36Sopenharmony_ci }, 158262306a36Sopenharmony_ci }, 158362306a36Sopenharmony_ci .chan_table_elements = 1, 158462306a36Sopenharmony_ci .info = &tsl2772_device_info[PRX], 158562306a36Sopenharmony_ci }, 158662306a36Sopenharmony_ci [ALSPRX] = { 158762306a36Sopenharmony_ci .channel_with_events = { 158862306a36Sopenharmony_ci { 158962306a36Sopenharmony_ci .type = IIO_LIGHT, 159062306a36Sopenharmony_ci .indexed = 1, 159162306a36Sopenharmony_ci .channel = 0, 159262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 159362306a36Sopenharmony_ci }, { 159462306a36Sopenharmony_ci .type = IIO_INTENSITY, 159562306a36Sopenharmony_ci .indexed = 1, 159662306a36Sopenharmony_ci .channel = 0, 159762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 159862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 159962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 160062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 160162306a36Sopenharmony_ci .info_mask_separate_available = 160262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 160362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 160462306a36Sopenharmony_ci .event_spec = tsl2772_events, 160562306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 160662306a36Sopenharmony_ci }, { 160762306a36Sopenharmony_ci .type = IIO_INTENSITY, 160862306a36Sopenharmony_ci .indexed = 1, 160962306a36Sopenharmony_ci .channel = 1, 161062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 161162306a36Sopenharmony_ci }, { 161262306a36Sopenharmony_ci .type = IIO_PROXIMITY, 161362306a36Sopenharmony_ci .indexed = 1, 161462306a36Sopenharmony_ci .channel = 0, 161562306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 161662306a36Sopenharmony_ci .event_spec = tsl2772_events, 161762306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 161862306a36Sopenharmony_ci }, 161962306a36Sopenharmony_ci }, 162062306a36Sopenharmony_ci .channel_without_events = { 162162306a36Sopenharmony_ci { 162262306a36Sopenharmony_ci .type = IIO_LIGHT, 162362306a36Sopenharmony_ci .indexed = 1, 162462306a36Sopenharmony_ci .channel = 0, 162562306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 162662306a36Sopenharmony_ci }, { 162762306a36Sopenharmony_ci .type = IIO_INTENSITY, 162862306a36Sopenharmony_ci .indexed = 1, 162962306a36Sopenharmony_ci .channel = 0, 163062306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 163162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 163262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 163362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 163462306a36Sopenharmony_ci .info_mask_separate_available = 163562306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 163662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 163762306a36Sopenharmony_ci }, { 163862306a36Sopenharmony_ci .type = IIO_INTENSITY, 163962306a36Sopenharmony_ci .indexed = 1, 164062306a36Sopenharmony_ci .channel = 1, 164162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 164262306a36Sopenharmony_ci }, { 164362306a36Sopenharmony_ci .type = IIO_PROXIMITY, 164462306a36Sopenharmony_ci .indexed = 1, 164562306a36Sopenharmony_ci .channel = 0, 164662306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 164762306a36Sopenharmony_ci }, 164862306a36Sopenharmony_ci }, 164962306a36Sopenharmony_ci .chan_table_elements = 4, 165062306a36Sopenharmony_ci .info = &tsl2772_device_info[ALSPRX], 165162306a36Sopenharmony_ci }, 165262306a36Sopenharmony_ci [PRX2] = { 165362306a36Sopenharmony_ci .channel_with_events = { 165462306a36Sopenharmony_ci { 165562306a36Sopenharmony_ci .type = IIO_PROXIMITY, 165662306a36Sopenharmony_ci .indexed = 1, 165762306a36Sopenharmony_ci .channel = 0, 165862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 165962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 166062306a36Sopenharmony_ci .info_mask_separate_available = 166162306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 166262306a36Sopenharmony_ci .event_spec = tsl2772_events, 166362306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 166462306a36Sopenharmony_ci }, 166562306a36Sopenharmony_ci }, 166662306a36Sopenharmony_ci .channel_without_events = { 166762306a36Sopenharmony_ci { 166862306a36Sopenharmony_ci .type = IIO_PROXIMITY, 166962306a36Sopenharmony_ci .indexed = 1, 167062306a36Sopenharmony_ci .channel = 0, 167162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 167262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 167362306a36Sopenharmony_ci .info_mask_separate_available = 167462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 167562306a36Sopenharmony_ci }, 167662306a36Sopenharmony_ci }, 167762306a36Sopenharmony_ci .chan_table_elements = 1, 167862306a36Sopenharmony_ci .info = &tsl2772_device_info[PRX2], 167962306a36Sopenharmony_ci }, 168062306a36Sopenharmony_ci [ALSPRX2] = { 168162306a36Sopenharmony_ci .channel_with_events = { 168262306a36Sopenharmony_ci { 168362306a36Sopenharmony_ci .type = IIO_LIGHT, 168462306a36Sopenharmony_ci .indexed = 1, 168562306a36Sopenharmony_ci .channel = 0, 168662306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 168762306a36Sopenharmony_ci }, { 168862306a36Sopenharmony_ci .type = IIO_INTENSITY, 168962306a36Sopenharmony_ci .indexed = 1, 169062306a36Sopenharmony_ci .channel = 0, 169162306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 169262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 169362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 169462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 169562306a36Sopenharmony_ci .info_mask_separate_available = 169662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 169762306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 169862306a36Sopenharmony_ci .event_spec = tsl2772_events, 169962306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 170062306a36Sopenharmony_ci }, { 170162306a36Sopenharmony_ci .type = IIO_INTENSITY, 170262306a36Sopenharmony_ci .indexed = 1, 170362306a36Sopenharmony_ci .channel = 1, 170462306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 170562306a36Sopenharmony_ci }, { 170662306a36Sopenharmony_ci .type = IIO_PROXIMITY, 170762306a36Sopenharmony_ci .indexed = 1, 170862306a36Sopenharmony_ci .channel = 0, 170962306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 171062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 171162306a36Sopenharmony_ci .info_mask_separate_available = 171262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 171362306a36Sopenharmony_ci .event_spec = tsl2772_events, 171462306a36Sopenharmony_ci .num_event_specs = ARRAY_SIZE(tsl2772_events), 171562306a36Sopenharmony_ci }, 171662306a36Sopenharmony_ci }, 171762306a36Sopenharmony_ci .channel_without_events = { 171862306a36Sopenharmony_ci { 171962306a36Sopenharmony_ci .type = IIO_LIGHT, 172062306a36Sopenharmony_ci .indexed = 1, 172162306a36Sopenharmony_ci .channel = 0, 172262306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), 172362306a36Sopenharmony_ci }, { 172462306a36Sopenharmony_ci .type = IIO_INTENSITY, 172562306a36Sopenharmony_ci .indexed = 1, 172662306a36Sopenharmony_ci .channel = 0, 172762306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 172862306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 172962306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE) | 173062306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBBIAS), 173162306a36Sopenharmony_ci .info_mask_separate_available = 173262306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_INT_TIME) | 173362306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 173462306a36Sopenharmony_ci }, { 173562306a36Sopenharmony_ci .type = IIO_INTENSITY, 173662306a36Sopenharmony_ci .indexed = 1, 173762306a36Sopenharmony_ci .channel = 1, 173862306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 173962306a36Sopenharmony_ci }, { 174062306a36Sopenharmony_ci .type = IIO_PROXIMITY, 174162306a36Sopenharmony_ci .indexed = 1, 174262306a36Sopenharmony_ci .channel = 0, 174362306a36Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 174462306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 174562306a36Sopenharmony_ci .info_mask_separate_available = 174662306a36Sopenharmony_ci BIT(IIO_CHAN_INFO_CALIBSCALE), 174762306a36Sopenharmony_ci }, 174862306a36Sopenharmony_ci }, 174962306a36Sopenharmony_ci .chan_table_elements = 4, 175062306a36Sopenharmony_ci .info = &tsl2772_device_info[ALSPRX2], 175162306a36Sopenharmony_ci }, 175262306a36Sopenharmony_ci}; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_cistatic int tsl2772_probe(struct i2c_client *clientp) 175562306a36Sopenharmony_ci{ 175662306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(clientp); 175762306a36Sopenharmony_ci struct iio_dev *indio_dev; 175862306a36Sopenharmony_ci struct tsl2772_chip *chip; 175962306a36Sopenharmony_ci int ret; 176062306a36Sopenharmony_ci 176162306a36Sopenharmony_ci indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); 176262306a36Sopenharmony_ci if (!indio_dev) 176362306a36Sopenharmony_ci return -ENOMEM; 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ci chip = iio_priv(indio_dev); 176662306a36Sopenharmony_ci chip->client = clientp; 176762306a36Sopenharmony_ci i2c_set_clientdata(clientp, indio_dev); 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci chip->supplies[TSL2772_SUPPLY_VDD].supply = "vdd"; 177062306a36Sopenharmony_ci chip->supplies[TSL2772_SUPPLY_VDDIO].supply = "vddio"; 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci ret = devm_regulator_bulk_get(&clientp->dev, 177362306a36Sopenharmony_ci ARRAY_SIZE(chip->supplies), 177462306a36Sopenharmony_ci chip->supplies); 177562306a36Sopenharmony_ci if (ret < 0) 177662306a36Sopenharmony_ci return dev_err_probe(&clientp->dev, ret, "Failed to get regulators\n"); 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies); 177962306a36Sopenharmony_ci if (ret < 0) { 178062306a36Sopenharmony_ci dev_err(&clientp->dev, "Failed to enable regulators: %d\n", 178162306a36Sopenharmony_ci ret); 178262306a36Sopenharmony_ci return ret; 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci ret = devm_add_action_or_reset(&clientp->dev, 178662306a36Sopenharmony_ci tsl2772_disable_regulators_action, 178762306a36Sopenharmony_ci chip); 178862306a36Sopenharmony_ci if (ret < 0) { 178962306a36Sopenharmony_ci dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n", 179062306a36Sopenharmony_ci ret); 179162306a36Sopenharmony_ci return ret; 179262306a36Sopenharmony_ci } 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci ret = i2c_smbus_read_byte_data(chip->client, 179762306a36Sopenharmony_ci TSL2772_CMD_REG | TSL2772_CHIPID); 179862306a36Sopenharmony_ci if (ret < 0) 179962306a36Sopenharmony_ci return ret; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { 180262306a36Sopenharmony_ci dev_info(&chip->client->dev, 180362306a36Sopenharmony_ci "%s: i2c device found does not match expected id\n", 180462306a36Sopenharmony_ci __func__); 180562306a36Sopenharmony_ci return -EINVAL; 180662306a36Sopenharmony_ci } 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); 180962306a36Sopenharmony_ci if (ret < 0) { 181062306a36Sopenharmony_ci dev_err(&clientp->dev, 181162306a36Sopenharmony_ci "%s: Failed to write to CMD register: %d\n", 181262306a36Sopenharmony_ci __func__, ret); 181362306a36Sopenharmony_ci return ret; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci mutex_init(&chip->als_mutex); 181762306a36Sopenharmony_ci mutex_init(&chip->prox_mutex); 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; 182062306a36Sopenharmony_ci chip->pdata = dev_get_platdata(&clientp->dev); 182162306a36Sopenharmony_ci chip->id = id->driver_data; 182262306a36Sopenharmony_ci chip->chip_info = 182362306a36Sopenharmony_ci &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci indio_dev->info = chip->chip_info->info; 182662306a36Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 182762306a36Sopenharmony_ci indio_dev->name = chip->client->name; 182862306a36Sopenharmony_ci indio_dev->num_channels = chip->chip_info->chan_table_elements; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci if (clientp->irq) { 183162306a36Sopenharmony_ci indio_dev->channels = chip->chip_info->channel_with_events; 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, 183462306a36Sopenharmony_ci NULL, 183562306a36Sopenharmony_ci &tsl2772_event_handler, 183662306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 183762306a36Sopenharmony_ci IRQF_ONESHOT, 183862306a36Sopenharmony_ci "TSL2772_event", 183962306a36Sopenharmony_ci indio_dev); 184062306a36Sopenharmony_ci if (ret) { 184162306a36Sopenharmony_ci dev_err(&clientp->dev, 184262306a36Sopenharmony_ci "%s: irq request failed\n", __func__); 184362306a36Sopenharmony_ci return ret; 184462306a36Sopenharmony_ci } 184562306a36Sopenharmony_ci } else { 184662306a36Sopenharmony_ci indio_dev->channels = chip->chip_info->channel_without_events; 184762306a36Sopenharmony_ci } 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_ci tsl2772_defaults(chip); 185062306a36Sopenharmony_ci ret = tsl2772_chip_on(indio_dev); 185162306a36Sopenharmony_ci if (ret < 0) 185262306a36Sopenharmony_ci return ret; 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci ret = devm_add_action_or_reset(&clientp->dev, 185562306a36Sopenharmony_ci tsl2772_chip_off_action, 185662306a36Sopenharmony_ci indio_dev); 185762306a36Sopenharmony_ci if (ret < 0) 185862306a36Sopenharmony_ci return ret; 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci return devm_iio_device_register(&clientp->dev, indio_dev); 186162306a36Sopenharmony_ci} 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_cistatic int tsl2772_suspend(struct device *dev) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 186662306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 186762306a36Sopenharmony_ci int ret; 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci ret = tsl2772_chip_off(indio_dev); 187062306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies); 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci return ret; 187362306a36Sopenharmony_ci} 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_cistatic int tsl2772_resume(struct device *dev) 187662306a36Sopenharmony_ci{ 187762306a36Sopenharmony_ci struct iio_dev *indio_dev = dev_get_drvdata(dev); 187862306a36Sopenharmony_ci struct tsl2772_chip *chip = iio_priv(indio_dev); 187962306a36Sopenharmony_ci int ret; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies); 188262306a36Sopenharmony_ci if (ret < 0) 188362306a36Sopenharmony_ci return ret; 188462306a36Sopenharmony_ci 188562306a36Sopenharmony_ci usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci return tsl2772_chip_on(indio_dev); 188862306a36Sopenharmony_ci} 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_cistatic const struct i2c_device_id tsl2772_idtable[] = { 189162306a36Sopenharmony_ci { "tsl2571", tsl2571 }, 189262306a36Sopenharmony_ci { "tsl2671", tsl2671 }, 189362306a36Sopenharmony_ci { "tmd2671", tmd2671 }, 189462306a36Sopenharmony_ci { "tsl2771", tsl2771 }, 189562306a36Sopenharmony_ci { "tmd2771", tmd2771 }, 189662306a36Sopenharmony_ci { "tsl2572", tsl2572 }, 189762306a36Sopenharmony_ci { "tsl2672", tsl2672 }, 189862306a36Sopenharmony_ci { "tmd2672", tmd2672 }, 189962306a36Sopenharmony_ci { "tsl2772", tsl2772 }, 190062306a36Sopenharmony_ci { "tmd2772", tmd2772 }, 190162306a36Sopenharmony_ci { "apds9930", apds9930 }, 190262306a36Sopenharmony_ci {} 190362306a36Sopenharmony_ci}; 190462306a36Sopenharmony_ci 190562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tsl2772_idtable); 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic const struct of_device_id tsl2772_of_match[] = { 190862306a36Sopenharmony_ci { .compatible = "amstaos,tsl2571" }, 190962306a36Sopenharmony_ci { .compatible = "amstaos,tsl2671" }, 191062306a36Sopenharmony_ci { .compatible = "amstaos,tmd2671" }, 191162306a36Sopenharmony_ci { .compatible = "amstaos,tsl2771" }, 191262306a36Sopenharmony_ci { .compatible = "amstaos,tmd2771" }, 191362306a36Sopenharmony_ci { .compatible = "amstaos,tsl2572" }, 191462306a36Sopenharmony_ci { .compatible = "amstaos,tsl2672" }, 191562306a36Sopenharmony_ci { .compatible = "amstaos,tmd2672" }, 191662306a36Sopenharmony_ci { .compatible = "amstaos,tsl2772" }, 191762306a36Sopenharmony_ci { .compatible = "amstaos,tmd2772" }, 191862306a36Sopenharmony_ci { .compatible = "avago,apds9930" }, 191962306a36Sopenharmony_ci {} 192062306a36Sopenharmony_ci}; 192162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tsl2772_of_match); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic const struct dev_pm_ops tsl2772_pm_ops = { 192462306a36Sopenharmony_ci .suspend = tsl2772_suspend, 192562306a36Sopenharmony_ci .resume = tsl2772_resume, 192662306a36Sopenharmony_ci}; 192762306a36Sopenharmony_ci 192862306a36Sopenharmony_cistatic struct i2c_driver tsl2772_driver = { 192962306a36Sopenharmony_ci .driver = { 193062306a36Sopenharmony_ci .name = "tsl2772", 193162306a36Sopenharmony_ci .of_match_table = tsl2772_of_match, 193262306a36Sopenharmony_ci .pm = &tsl2772_pm_ops, 193362306a36Sopenharmony_ci }, 193462306a36Sopenharmony_ci .id_table = tsl2772_idtable, 193562306a36Sopenharmony_ci .probe = tsl2772_probe, 193662306a36Sopenharmony_ci}; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_cimodule_i2c_driver(tsl2772_driver); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ciMODULE_AUTHOR("J. August Brenner <Jon.Brenner@ams.com>"); 194162306a36Sopenharmony_ciMODULE_AUTHOR("Brian Masney <masneyb@onstation.org>"); 194262306a36Sopenharmony_ciMODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); 194362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1944