18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * This file is part of the APDS990x sensor driver. 48c2ecf20Sopenharmony_ci * Chip is combined proximity and ambient light sensor. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Contact: Samu Onkalo <samu.p.onkalo@nokia.com> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/i2c.h> 148c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 158c2ecf20Sopenharmony_ci#include <linux/mutex.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h> 178c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/wait.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_data/apds990x.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Register map */ 248c2ecf20Sopenharmony_ci#define APDS990X_ENABLE 0x00 /* Enable of states and interrupts */ 258c2ecf20Sopenharmony_ci#define APDS990X_ATIME 0x01 /* ALS ADC time */ 268c2ecf20Sopenharmony_ci#define APDS990X_PTIME 0x02 /* Proximity ADC time */ 278c2ecf20Sopenharmony_ci#define APDS990X_WTIME 0x03 /* Wait time */ 288c2ecf20Sopenharmony_ci#define APDS990X_AILTL 0x04 /* ALS interrupt low threshold low byte */ 298c2ecf20Sopenharmony_ci#define APDS990X_AILTH 0x05 /* ALS interrupt low threshold hi byte */ 308c2ecf20Sopenharmony_ci#define APDS990X_AIHTL 0x06 /* ALS interrupt hi threshold low byte */ 318c2ecf20Sopenharmony_ci#define APDS990X_AIHTH 0x07 /* ALS interrupt hi threshold hi byte */ 328c2ecf20Sopenharmony_ci#define APDS990X_PILTL 0x08 /* Proximity interrupt low threshold low byte */ 338c2ecf20Sopenharmony_ci#define APDS990X_PILTH 0x09 /* Proximity interrupt low threshold hi byte */ 348c2ecf20Sopenharmony_ci#define APDS990X_PIHTL 0x0a /* Proximity interrupt hi threshold low byte */ 358c2ecf20Sopenharmony_ci#define APDS990X_PIHTH 0x0b /* Proximity interrupt hi threshold hi byte */ 368c2ecf20Sopenharmony_ci#define APDS990X_PERS 0x0c /* Interrupt persistence filters */ 378c2ecf20Sopenharmony_ci#define APDS990X_CONFIG 0x0d /* Configuration */ 388c2ecf20Sopenharmony_ci#define APDS990X_PPCOUNT 0x0e /* Proximity pulse count */ 398c2ecf20Sopenharmony_ci#define APDS990X_CONTROL 0x0f /* Gain control register */ 408c2ecf20Sopenharmony_ci#define APDS990X_REV 0x11 /* Revision Number */ 418c2ecf20Sopenharmony_ci#define APDS990X_ID 0x12 /* Device ID */ 428c2ecf20Sopenharmony_ci#define APDS990X_STATUS 0x13 /* Device status */ 438c2ecf20Sopenharmony_ci#define APDS990X_CDATAL 0x14 /* Clear ADC low data register */ 448c2ecf20Sopenharmony_ci#define APDS990X_CDATAH 0x15 /* Clear ADC high data register */ 458c2ecf20Sopenharmony_ci#define APDS990X_IRDATAL 0x16 /* IR ADC low data register */ 468c2ecf20Sopenharmony_ci#define APDS990X_IRDATAH 0x17 /* IR ADC high data register */ 478c2ecf20Sopenharmony_ci#define APDS990X_PDATAL 0x18 /* Proximity ADC low data register */ 488c2ecf20Sopenharmony_ci#define APDS990X_PDATAH 0x19 /* Proximity ADC high data register */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Control */ 518c2ecf20Sopenharmony_ci#define APDS990X_MAX_AGAIN 3 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Enable register */ 548c2ecf20Sopenharmony_ci#define APDS990X_EN_PIEN (0x1 << 5) 558c2ecf20Sopenharmony_ci#define APDS990X_EN_AIEN (0x1 << 4) 568c2ecf20Sopenharmony_ci#define APDS990X_EN_WEN (0x1 << 3) 578c2ecf20Sopenharmony_ci#define APDS990X_EN_PEN (0x1 << 2) 588c2ecf20Sopenharmony_ci#define APDS990X_EN_AEN (0x1 << 1) 598c2ecf20Sopenharmony_ci#define APDS990X_EN_PON (0x1 << 0) 608c2ecf20Sopenharmony_ci#define APDS990X_EN_DISABLE_ALL 0 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Status register */ 638c2ecf20Sopenharmony_ci#define APDS990X_ST_PINT (0x1 << 5) 648c2ecf20Sopenharmony_ci#define APDS990X_ST_AINT (0x1 << 4) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* I2C access types */ 678c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_MASK (0x03 << 5) 688c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_RB (0x00 << 5) /* Repeated byte */ 698c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_INC (0x01 << 5) /* Auto increment */ 708c2ecf20Sopenharmony_ci#define APDS990x_CMD_TYPE_SPE (0x03 << 5) /* Special function */ 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define APDS990x_ADDR_SHIFT 0 738c2ecf20Sopenharmony_ci#define APDS990x_CMD 0x80 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* Interrupt ack commands */ 768c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_ALS 0x6 778c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_PS 0x5 788c2ecf20Sopenharmony_ci#define APDS990X_INT_ACK_BOTH 0x7 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* ptime */ 818c2ecf20Sopenharmony_ci#define APDS990X_PTIME_DEFAULT 0xff /* Recommended conversion time 2.7ms*/ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci/* wtime */ 848c2ecf20Sopenharmony_ci#define APDS990X_WTIME_DEFAULT 0xee /* ~50ms wait time */ 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define APDS990X_TIME_TO_ADC 1024 /* One timetick as ADC count value */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci/* Persistence */ 898c2ecf20Sopenharmony_ci#define APDS990X_APERS_SHIFT 0 908c2ecf20Sopenharmony_ci#define APDS990X_PPERS_SHIFT 4 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* Supported ID:s */ 938c2ecf20Sopenharmony_ci#define APDS990X_ID_0 0x0 948c2ecf20Sopenharmony_ci#define APDS990X_ID_4 0x4 958c2ecf20Sopenharmony_ci#define APDS990X_ID_29 0x29 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/* pgain and pdiode settings */ 988c2ecf20Sopenharmony_ci#define APDS_PGAIN_1X 0x0 998c2ecf20Sopenharmony_ci#define APDS_PDIODE_IR 0x2 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define APDS990X_LUX_OUTPUT_SCALE 10 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Reverse chip factors for threshold calculation */ 1048c2ecf20Sopenharmony_cistruct reverse_factors { 1058c2ecf20Sopenharmony_ci u32 afactor; 1068c2ecf20Sopenharmony_ci int cf1; 1078c2ecf20Sopenharmony_ci int irf1; 1088c2ecf20Sopenharmony_ci int cf2; 1098c2ecf20Sopenharmony_ci int irf2; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistruct apds990x_chip { 1138c2ecf20Sopenharmony_ci struct apds990x_platform_data *pdata; 1148c2ecf20Sopenharmony_ci struct i2c_client *client; 1158c2ecf20Sopenharmony_ci struct mutex mutex; /* avoid parallel access */ 1168c2ecf20Sopenharmony_ci struct regulator_bulk_data regs[2]; 1178c2ecf20Sopenharmony_ci wait_queue_head_t wait; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci int prox_en; 1208c2ecf20Sopenharmony_ci bool prox_continuous_mode; 1218c2ecf20Sopenharmony_ci bool lux_wait_fresh_res; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* Chip parameters */ 1248c2ecf20Sopenharmony_ci struct apds990x_chip_factors cf; 1258c2ecf20Sopenharmony_ci struct reverse_factors rcf; 1268c2ecf20Sopenharmony_ci u16 atime; /* als integration time */ 1278c2ecf20Sopenharmony_ci u16 arate; /* als reporting rate */ 1288c2ecf20Sopenharmony_ci u16 a_max_result; /* Max possible ADC value with current atime */ 1298c2ecf20Sopenharmony_ci u8 again_meas; /* Gain used in last measurement */ 1308c2ecf20Sopenharmony_ci u8 again_next; /* Next calculated gain */ 1318c2ecf20Sopenharmony_ci u8 pgain; 1328c2ecf20Sopenharmony_ci u8 pdiode; 1338c2ecf20Sopenharmony_ci u8 pdrive; 1348c2ecf20Sopenharmony_ci u8 lux_persistence; 1358c2ecf20Sopenharmony_ci u8 prox_persistence; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci u32 lux_raw; 1388c2ecf20Sopenharmony_ci u32 lux; 1398c2ecf20Sopenharmony_ci u16 lux_clear; 1408c2ecf20Sopenharmony_ci u16 lux_ir; 1418c2ecf20Sopenharmony_ci u16 lux_calib; 1428c2ecf20Sopenharmony_ci u32 lux_thres_hi; 1438c2ecf20Sopenharmony_ci u32 lux_thres_lo; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci u32 prox_thres; 1468c2ecf20Sopenharmony_ci u16 prox_data; 1478c2ecf20Sopenharmony_ci u16 prox_calib; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci char chipname[10]; 1508c2ecf20Sopenharmony_ci u8 revision; 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci#define APDS_CALIB_SCALER 8192 1548c2ecf20Sopenharmony_ci#define APDS_LUX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) 1558c2ecf20Sopenharmony_ci#define APDS_PROX_NEUTRAL_CALIB_VALUE (1 * APDS_CALIB_SCALER) 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci#define APDS_PROX_DEF_THRES 600 1588c2ecf20Sopenharmony_ci#define APDS_PROX_HYSTERESIS 50 1598c2ecf20Sopenharmony_ci#define APDS_LUX_DEF_THRES_HI 101 1608c2ecf20Sopenharmony_ci#define APDS_LUX_DEF_THRES_LO 100 1618c2ecf20Sopenharmony_ci#define APDS_DEFAULT_PROX_PERS 1 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#define APDS_TIMEOUT 2000 1648c2ecf20Sopenharmony_ci#define APDS_STARTUP_DELAY 25000 /* us */ 1658c2ecf20Sopenharmony_ci#define APDS_RANGE 65535 1668c2ecf20Sopenharmony_ci#define APDS_PROX_RANGE 1023 1678c2ecf20Sopenharmony_ci#define APDS_LUX_GAIN_LO_LIMIT 100 1688c2ecf20Sopenharmony_ci#define APDS_LUX_GAIN_LO_LIMIT_STRICT 25 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define TIMESTEP 87 /* 2.7ms is about 87 / 32 */ 1718c2ecf20Sopenharmony_ci#define TIME_STEP_SCALER 32 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci#define APDS_LUX_AVERAGING_TIME 50 /* tolerates 50/60Hz ripple */ 1748c2ecf20Sopenharmony_ci#define APDS_LUX_DEFAULT_RATE 200 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic const u8 again[] = {1, 8, 16, 120}; /* ALS gain steps */ 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* Following two tables must match i.e 10Hz rate means 1 as persistence value */ 1798c2ecf20Sopenharmony_cistatic const u16 arates_hz[] = {10, 5, 2, 1}; 1808c2ecf20Sopenharmony_cistatic const u8 apersis[] = {1, 2, 4, 5}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci/* Regulators */ 1838c2ecf20Sopenharmony_cistatic const char reg_vcc[] = "Vdd"; 1848c2ecf20Sopenharmony_cistatic const char reg_vled[] = "Vled"; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int apds990x_read_byte(struct apds990x_chip *chip, u8 reg, u8 *data) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 1898c2ecf20Sopenharmony_ci s32 ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci reg &= ~APDS990x_CMD_TYPE_MASK; 1928c2ecf20Sopenharmony_ci reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 1958c2ecf20Sopenharmony_ci *data = ret; 1968c2ecf20Sopenharmony_ci return (int)ret; 1978c2ecf20Sopenharmony_ci} 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic int apds990x_read_word(struct apds990x_chip *chip, u8 reg, u16 *data) 2008c2ecf20Sopenharmony_ci{ 2018c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 2028c2ecf20Sopenharmony_ci s32 ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci reg &= ~APDS990x_CMD_TYPE_MASK; 2058c2ecf20Sopenharmony_ci reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(client, reg); 2088c2ecf20Sopenharmony_ci *data = ret; 2098c2ecf20Sopenharmony_ci return (int)ret; 2108c2ecf20Sopenharmony_ci} 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int apds990x_write_byte(struct apds990x_chip *chip, u8 reg, u8 data) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 2158c2ecf20Sopenharmony_ci s32 ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci reg &= ~APDS990x_CMD_TYPE_MASK; 2188c2ecf20Sopenharmony_ci reg |= APDS990x_CMD | APDS990x_CMD_TYPE_RB; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(client, reg, data); 2218c2ecf20Sopenharmony_ci return (int)ret; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic int apds990x_write_word(struct apds990x_chip *chip, u8 reg, u16 data) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 2278c2ecf20Sopenharmony_ci s32 ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci reg &= ~APDS990x_CMD_TYPE_MASK; 2308c2ecf20Sopenharmony_ci reg |= APDS990x_CMD | APDS990x_CMD_TYPE_INC; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_data(client, reg, data); 2338c2ecf20Sopenharmony_ci return (int)ret; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int apds990x_mode_on(struct apds990x_chip *chip) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci /* ALS is mandatory, proximity optional */ 2398c2ecf20Sopenharmony_ci u8 reg = APDS990X_EN_AIEN | APDS990X_EN_PON | APDS990X_EN_AEN | 2408c2ecf20Sopenharmony_ci APDS990X_EN_WEN; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci if (chip->prox_en) 2438c2ecf20Sopenharmony_ci reg |= APDS990X_EN_PIEN | APDS990X_EN_PEN; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return apds990x_write_byte(chip, APDS990X_ENABLE, reg); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic u16 apds990x_lux_to_threshold(struct apds990x_chip *chip, u32 lux) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci u32 thres; 2518c2ecf20Sopenharmony_ci u32 cpl; 2528c2ecf20Sopenharmony_ci u32 ir; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (lux == 0) 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci else if (lux == APDS_RANGE) 2578c2ecf20Sopenharmony_ci return APDS_RANGE; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* 2608c2ecf20Sopenharmony_ci * Reported LUX value is a combination of the IR and CLEAR channel 2618c2ecf20Sopenharmony_ci * values. However, interrupt threshold is only for clear channel. 2628c2ecf20Sopenharmony_ci * This function approximates needed HW threshold value for a given 2638c2ecf20Sopenharmony_ci * LUX value in the current lightning type. 2648c2ecf20Sopenharmony_ci * IR level compared to visible light varies heavily depending on the 2658c2ecf20Sopenharmony_ci * source of the light 2668c2ecf20Sopenharmony_ci * 2678c2ecf20Sopenharmony_ci * Calculate threshold value for the next measurement period. 2688c2ecf20Sopenharmony_ci * Math: threshold = lux * cpl where 2698c2ecf20Sopenharmony_ci * cpl = atime * again / (glass_attenuation * device_factor) 2708c2ecf20Sopenharmony_ci * (count-per-lux) 2718c2ecf20Sopenharmony_ci * 2728c2ecf20Sopenharmony_ci * First remove calibration. Division by four is to avoid overflow 2738c2ecf20Sopenharmony_ci */ 2748c2ecf20Sopenharmony_ci lux = lux * (APDS_CALIB_SCALER / 4) / (chip->lux_calib / 4); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci /* Multiplication by 64 is to increase accuracy */ 2778c2ecf20Sopenharmony_ci cpl = ((u32)chip->atime * (u32)again[chip->again_next] * 2788c2ecf20Sopenharmony_ci APDS_PARAM_SCALE * 64) / (chip->cf.ga * chip->cf.df); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci thres = lux * cpl / 64; 2818c2ecf20Sopenharmony_ci /* 2828c2ecf20Sopenharmony_ci * Convert IR light from the latest result to match with 2838c2ecf20Sopenharmony_ci * new gain step. This helps to adapt with the current 2848c2ecf20Sopenharmony_ci * source of light. 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_ci ir = (u32)chip->lux_ir * (u32)again[chip->again_next] / 2878c2ecf20Sopenharmony_ci (u32)again[chip->again_meas]; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* 2908c2ecf20Sopenharmony_ci * Compensate count with IR light impact 2918c2ecf20Sopenharmony_ci * IAC1 > IAC2 (see apds990x_get_lux for formulas) 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_ci if (chip->lux_clear * APDS_PARAM_SCALE >= 2948c2ecf20Sopenharmony_ci chip->rcf.afactor * chip->lux_ir) 2958c2ecf20Sopenharmony_ci thres = (chip->rcf.cf1 * thres + chip->rcf.irf1 * ir) / 2968c2ecf20Sopenharmony_ci APDS_PARAM_SCALE; 2978c2ecf20Sopenharmony_ci else 2988c2ecf20Sopenharmony_ci thres = (chip->rcf.cf2 * thres + chip->rcf.irf2 * ir) / 2998c2ecf20Sopenharmony_ci APDS_PARAM_SCALE; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (thres >= chip->a_max_result) 3028c2ecf20Sopenharmony_ci thres = chip->a_max_result - 1; 3038c2ecf20Sopenharmony_ci return thres; 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic inline int apds990x_set_atime(struct apds990x_chip *chip, u32 time_ms) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci u8 reg_value; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci chip->atime = time_ms; 3118c2ecf20Sopenharmony_ci /* Formula is specified in the data sheet */ 3128c2ecf20Sopenharmony_ci reg_value = 256 - ((time_ms * TIME_STEP_SCALER) / TIMESTEP); 3138c2ecf20Sopenharmony_ci /* Calculate max ADC value for given integration time */ 3148c2ecf20Sopenharmony_ci chip->a_max_result = (u16)(256 - reg_value) * APDS990X_TIME_TO_ADC; 3158c2ecf20Sopenharmony_ci return apds990x_write_byte(chip, APDS990X_ATIME, reg_value); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 3198c2ecf20Sopenharmony_cistatic int apds990x_refresh_pthres(struct apds990x_chip *chip, int data) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci int ret, lo, hi; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* If the chip is not in use, don't try to access it */ 3248c2ecf20Sopenharmony_ci if (pm_runtime_suspended(&chip->client->dev)) 3258c2ecf20Sopenharmony_ci return 0; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci if (data < chip->prox_thres) { 3288c2ecf20Sopenharmony_ci lo = 0; 3298c2ecf20Sopenharmony_ci hi = chip->prox_thres; 3308c2ecf20Sopenharmony_ci } else { 3318c2ecf20Sopenharmony_ci lo = chip->prox_thres - APDS_PROX_HYSTERESIS; 3328c2ecf20Sopenharmony_ci if (chip->prox_continuous_mode) 3338c2ecf20Sopenharmony_ci hi = chip->prox_thres; 3348c2ecf20Sopenharmony_ci else 3358c2ecf20Sopenharmony_ci hi = APDS_RANGE; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci ret = apds990x_write_word(chip, APDS990X_PILTL, lo); 3398c2ecf20Sopenharmony_ci ret |= apds990x_write_word(chip, APDS990X_PIHTL, hi); 3408c2ecf20Sopenharmony_ci return ret; 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 3448c2ecf20Sopenharmony_cistatic int apds990x_refresh_athres(struct apds990x_chip *chip) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci int ret; 3478c2ecf20Sopenharmony_ci /* If the chip is not in use, don't try to access it */ 3488c2ecf20Sopenharmony_ci if (pm_runtime_suspended(&chip->client->dev)) 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci ret = apds990x_write_word(chip, APDS990X_AILTL, 3528c2ecf20Sopenharmony_ci apds990x_lux_to_threshold(chip, chip->lux_thres_lo)); 3538c2ecf20Sopenharmony_ci ret |= apds990x_write_word(chip, APDS990X_AIHTL, 3548c2ecf20Sopenharmony_ci apds990x_lux_to_threshold(chip, chip->lux_thres_hi)); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return ret; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 3608c2ecf20Sopenharmony_cistatic void apds990x_force_a_refresh(struct apds990x_chip *chip) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci /* This will force ALS interrupt after the next measurement. */ 3638c2ecf20Sopenharmony_ci apds990x_write_word(chip, APDS990X_AILTL, APDS_LUX_DEF_THRES_LO); 3648c2ecf20Sopenharmony_ci apds990x_write_word(chip, APDS990X_AIHTL, APDS_LUX_DEF_THRES_HI); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 3688c2ecf20Sopenharmony_cistatic void apds990x_force_p_refresh(struct apds990x_chip *chip) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci /* This will force proximity interrupt after the next measurement. */ 3718c2ecf20Sopenharmony_ci apds990x_write_word(chip, APDS990X_PILTL, APDS_PROX_DEF_THRES - 1); 3728c2ecf20Sopenharmony_ci apds990x_write_word(chip, APDS990X_PIHTL, APDS_PROX_DEF_THRES); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 3768c2ecf20Sopenharmony_cistatic int apds990x_calc_again(struct apds990x_chip *chip) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci int curr_again = chip->again_meas; 3798c2ecf20Sopenharmony_ci int next_again = chip->again_meas; 3808c2ecf20Sopenharmony_ci int ret = 0; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci /* Calculate suitable als gain */ 3838c2ecf20Sopenharmony_ci if (chip->lux_clear == chip->a_max_result) 3848c2ecf20Sopenharmony_ci next_again -= 2; /* ALS saturated. Decrease gain by 2 steps */ 3858c2ecf20Sopenharmony_ci else if (chip->lux_clear > chip->a_max_result / 2) 3868c2ecf20Sopenharmony_ci next_again--; 3878c2ecf20Sopenharmony_ci else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) 3888c2ecf20Sopenharmony_ci next_again += 2; /* Too dark. Increase gain by 2 steps */ 3898c2ecf20Sopenharmony_ci else if (chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT) 3908c2ecf20Sopenharmony_ci next_again++; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* Limit gain to available range */ 3938c2ecf20Sopenharmony_ci if (next_again < 0) 3948c2ecf20Sopenharmony_ci next_again = 0; 3958c2ecf20Sopenharmony_ci else if (next_again > APDS990X_MAX_AGAIN) 3968c2ecf20Sopenharmony_ci next_again = APDS990X_MAX_AGAIN; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci /* Let's check can we trust the measured result */ 3998c2ecf20Sopenharmony_ci if (chip->lux_clear == chip->a_max_result) 4008c2ecf20Sopenharmony_ci /* Result can be totally garbage due to saturation */ 4018c2ecf20Sopenharmony_ci ret = -ERANGE; 4028c2ecf20Sopenharmony_ci else if (next_again != curr_again && 4038c2ecf20Sopenharmony_ci chip->lux_clear < APDS_LUX_GAIN_LO_LIMIT_STRICT) 4048c2ecf20Sopenharmony_ci /* 4058c2ecf20Sopenharmony_ci * Gain is changed and measurement result is very small. 4068c2ecf20Sopenharmony_ci * Result can be totally garbage due to underflow 4078c2ecf20Sopenharmony_ci */ 4088c2ecf20Sopenharmony_ci ret = -ERANGE; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci chip->again_next = next_again; 4118c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_CONTROL, 4128c2ecf20Sopenharmony_ci (chip->pdrive << 6) | 4138c2ecf20Sopenharmony_ci (chip->pdiode << 4) | 4148c2ecf20Sopenharmony_ci (chip->pgain << 2) | 4158c2ecf20Sopenharmony_ci (chip->again_next << 0)); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* 4188c2ecf20Sopenharmony_ci * Error means bad result -> re-measurement is needed. The forced 4198c2ecf20Sopenharmony_ci * refresh uses fastest possible persistence setting to get result 4208c2ecf20Sopenharmony_ci * as soon as possible. 4218c2ecf20Sopenharmony_ci */ 4228c2ecf20Sopenharmony_ci if (ret < 0) 4238c2ecf20Sopenharmony_ci apds990x_force_a_refresh(chip); 4248c2ecf20Sopenharmony_ci else 4258c2ecf20Sopenharmony_ci apds990x_refresh_athres(chip); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci return ret; 4288c2ecf20Sopenharmony_ci} 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/* Called always with mutex locked */ 4318c2ecf20Sopenharmony_cistatic int apds990x_get_lux(struct apds990x_chip *chip, int clear, int ir) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci int iac, iac1, iac2; /* IR adjusted counts */ 4348c2ecf20Sopenharmony_ci u32 lpc; /* Lux per count */ 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Formulas: 4378c2ecf20Sopenharmony_ci * iac1 = CF1 * CLEAR_CH - IRF1 * IR_CH 4388c2ecf20Sopenharmony_ci * iac2 = CF2 * CLEAR_CH - IRF2 * IR_CH 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci iac1 = (chip->cf.cf1 * clear - chip->cf.irf1 * ir) / APDS_PARAM_SCALE; 4418c2ecf20Sopenharmony_ci iac2 = (chip->cf.cf2 * clear - chip->cf.irf2 * ir) / APDS_PARAM_SCALE; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci iac = max(iac1, iac2); 4448c2ecf20Sopenharmony_ci iac = max(iac, 0); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci lpc = APDS990X_LUX_OUTPUT_SCALE * (chip->cf.df * chip->cf.ga) / 4478c2ecf20Sopenharmony_ci (u32)(again[chip->again_meas] * (u32)chip->atime); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return (iac * lpc) / APDS_PARAM_SCALE; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic int apds990x_ack_int(struct apds990x_chip *chip, u8 mode) 4538c2ecf20Sopenharmony_ci{ 4548c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 4558c2ecf20Sopenharmony_ci s32 ret; 4568c2ecf20Sopenharmony_ci u8 reg = APDS990x_CMD | APDS990x_CMD_TYPE_SPE; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci switch (mode & (APDS990X_ST_AINT | APDS990X_ST_PINT)) { 4598c2ecf20Sopenharmony_ci case APDS990X_ST_AINT: 4608c2ecf20Sopenharmony_ci reg |= APDS990X_INT_ACK_ALS; 4618c2ecf20Sopenharmony_ci break; 4628c2ecf20Sopenharmony_ci case APDS990X_ST_PINT: 4638c2ecf20Sopenharmony_ci reg |= APDS990X_INT_ACK_PS; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci default: 4668c2ecf20Sopenharmony_ci reg |= APDS990X_INT_ACK_BOTH; 4678c2ecf20Sopenharmony_ci break; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(client, reg); 4718c2ecf20Sopenharmony_ci return (int)ret; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic irqreturn_t apds990x_irq(int irq, void *data) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct apds990x_chip *chip = data; 4778c2ecf20Sopenharmony_ci u8 status; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci apds990x_read_byte(chip, APDS990X_STATUS, &status); 4808c2ecf20Sopenharmony_ci apds990x_ack_int(chip, status); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 4838c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(&chip->client->dev)) { 4848c2ecf20Sopenharmony_ci if (status & APDS990X_ST_AINT) { 4858c2ecf20Sopenharmony_ci apds990x_read_word(chip, APDS990X_CDATAL, 4868c2ecf20Sopenharmony_ci &chip->lux_clear); 4878c2ecf20Sopenharmony_ci apds990x_read_word(chip, APDS990X_IRDATAL, 4888c2ecf20Sopenharmony_ci &chip->lux_ir); 4898c2ecf20Sopenharmony_ci /* Store used gain for calculations */ 4908c2ecf20Sopenharmony_ci chip->again_meas = chip->again_next; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci chip->lux_raw = apds990x_get_lux(chip, 4938c2ecf20Sopenharmony_ci chip->lux_clear, 4948c2ecf20Sopenharmony_ci chip->lux_ir); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (apds990x_calc_again(chip) == 0) { 4978c2ecf20Sopenharmony_ci /* Result is valid */ 4988c2ecf20Sopenharmony_ci chip->lux = chip->lux_raw; 4998c2ecf20Sopenharmony_ci chip->lux_wait_fresh_res = false; 5008c2ecf20Sopenharmony_ci wake_up(&chip->wait); 5018c2ecf20Sopenharmony_ci sysfs_notify(&chip->client->dev.kobj, 5028c2ecf20Sopenharmony_ci NULL, "lux0_input"); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci if ((status & APDS990X_ST_PINT) && chip->prox_en) { 5078c2ecf20Sopenharmony_ci u16 clr_ch; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci apds990x_read_word(chip, APDS990X_CDATAL, &clr_ch); 5108c2ecf20Sopenharmony_ci /* 5118c2ecf20Sopenharmony_ci * If ALS channel is saturated at min gain, 5128c2ecf20Sopenharmony_ci * proximity gives false posivite values. 5138c2ecf20Sopenharmony_ci * Just ignore them. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci if (chip->again_meas == 0 && 5168c2ecf20Sopenharmony_ci clr_ch == chip->a_max_result) 5178c2ecf20Sopenharmony_ci chip->prox_data = 0; 5188c2ecf20Sopenharmony_ci else 5198c2ecf20Sopenharmony_ci apds990x_read_word(chip, 5208c2ecf20Sopenharmony_ci APDS990X_PDATAL, 5218c2ecf20Sopenharmony_ci &chip->prox_data); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci apds990x_refresh_pthres(chip, chip->prox_data); 5248c2ecf20Sopenharmony_ci if (chip->prox_data < chip->prox_thres) 5258c2ecf20Sopenharmony_ci chip->prox_data = 0; 5268c2ecf20Sopenharmony_ci else if (!chip->prox_continuous_mode) 5278c2ecf20Sopenharmony_ci chip->prox_data = APDS_PROX_RANGE; 5288c2ecf20Sopenharmony_ci sysfs_notify(&chip->client->dev.kobj, 5298c2ecf20Sopenharmony_ci NULL, "prox0_raw"); 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 5338c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int apds990x_configure(struct apds990x_chip *chip) 5378c2ecf20Sopenharmony_ci{ 5388c2ecf20Sopenharmony_ci /* It is recommended to use disabled mode during these operations */ 5398c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci /* conversion and wait times for different state machince states */ 5428c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_PTIME, APDS990X_PTIME_DEFAULT); 5438c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_WTIME, APDS990X_WTIME_DEFAULT); 5448c2ecf20Sopenharmony_ci apds990x_set_atime(chip, APDS_LUX_AVERAGING_TIME); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_CONFIG, 0); 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci /* Persistence levels */ 5498c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_PERS, 5508c2ecf20Sopenharmony_ci (chip->lux_persistence << APDS990X_APERS_SHIFT) | 5518c2ecf20Sopenharmony_ci (chip->prox_persistence << APDS990X_PPERS_SHIFT)); 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_PPCOUNT, chip->pdata->ppcount); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* Start with relatively small gain */ 5568c2ecf20Sopenharmony_ci chip->again_meas = 1; 5578c2ecf20Sopenharmony_ci chip->again_next = 1; 5588c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_CONTROL, 5598c2ecf20Sopenharmony_ci (chip->pdrive << 6) | 5608c2ecf20Sopenharmony_ci (chip->pdiode << 4) | 5618c2ecf20Sopenharmony_ci (chip->pgain << 2) | 5628c2ecf20Sopenharmony_ci (chip->again_next << 0)); 5638c2ecf20Sopenharmony_ci return 0; 5648c2ecf20Sopenharmony_ci} 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_cistatic int apds990x_detect(struct apds990x_chip *chip) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct i2c_client *client = chip->client; 5698c2ecf20Sopenharmony_ci int ret; 5708c2ecf20Sopenharmony_ci u8 id; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ret = apds990x_read_byte(chip, APDS990X_ID, &id); 5738c2ecf20Sopenharmony_ci if (ret < 0) { 5748c2ecf20Sopenharmony_ci dev_err(&client->dev, "ID read failed\n"); 5758c2ecf20Sopenharmony_ci return ret; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci ret = apds990x_read_byte(chip, APDS990X_REV, &chip->revision); 5798c2ecf20Sopenharmony_ci if (ret < 0) { 5808c2ecf20Sopenharmony_ci dev_err(&client->dev, "REV read failed\n"); 5818c2ecf20Sopenharmony_ci return ret; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci switch (id) { 5858c2ecf20Sopenharmony_ci case APDS990X_ID_0: 5868c2ecf20Sopenharmony_ci case APDS990X_ID_4: 5878c2ecf20Sopenharmony_ci case APDS990X_ID_29: 5888c2ecf20Sopenharmony_ci snprintf(chip->chipname, sizeof(chip->chipname), "APDS-990x"); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci default: 5918c2ecf20Sopenharmony_ci ret = -ENODEV; 5928c2ecf20Sopenharmony_ci break; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci return ret; 5958c2ecf20Sopenharmony_ci} 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5988c2ecf20Sopenharmony_cistatic int apds990x_chip_on(struct apds990x_chip *chip) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci int err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), 6018c2ecf20Sopenharmony_ci chip->regs); 6028c2ecf20Sopenharmony_ci if (err < 0) 6038c2ecf20Sopenharmony_ci return err; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci /* Refresh all configs in case of regulators were off */ 6088c2ecf20Sopenharmony_ci chip->prox_data = 0; 6098c2ecf20Sopenharmony_ci apds990x_configure(chip); 6108c2ecf20Sopenharmony_ci apds990x_mode_on(chip); 6118c2ecf20Sopenharmony_ci return 0; 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci#endif 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int apds990x_chip_off(struct apds990x_chip *chip) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci apds990x_write_byte(chip, APDS990X_ENABLE, APDS990X_EN_DISABLE_ALL); 6188c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); 6198c2ecf20Sopenharmony_ci return 0; 6208c2ecf20Sopenharmony_ci} 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_show(struct device *dev, 6238c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6248c2ecf20Sopenharmony_ci{ 6258c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 6268c2ecf20Sopenharmony_ci ssize_t ret; 6278c2ecf20Sopenharmony_ci u32 result; 6288c2ecf20Sopenharmony_ci long timeout; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev)) 6318c2ecf20Sopenharmony_ci return -EIO; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci timeout = wait_event_interruptible_timeout(chip->wait, 6348c2ecf20Sopenharmony_ci !chip->lux_wait_fresh_res, 6358c2ecf20Sopenharmony_ci msecs_to_jiffies(APDS_TIMEOUT)); 6368c2ecf20Sopenharmony_ci if (!timeout) 6378c2ecf20Sopenharmony_ci return -EIO; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 6408c2ecf20Sopenharmony_ci result = (chip->lux * chip->lux_calib) / APDS_CALIB_SCALER; 6418c2ecf20Sopenharmony_ci if (result > (APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE)) 6428c2ecf20Sopenharmony_ci result = APDS_RANGE * APDS990X_LUX_OUTPUT_SCALE; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ret = sprintf(buf, "%d.%d\n", 6458c2ecf20Sopenharmony_ci result / APDS990X_LUX_OUTPUT_SCALE, 6468c2ecf20Sopenharmony_ci result % APDS990X_LUX_OUTPUT_SCALE); 6478c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 6488c2ecf20Sopenharmony_ci return ret; 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_input, S_IRUGO, apds990x_lux_show, NULL); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_range_show(struct device *dev, 6548c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", APDS_RANGE); 6578c2ecf20Sopenharmony_ci} 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_sensor_range, S_IRUGO, apds990x_lux_range_show, NULL); 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_format_show(struct device *dev, 6628c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", APDS_CALIB_SCALER); 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_calibscale_default, S_IRUGO, 6688c2ecf20Sopenharmony_ci apds990x_lux_calib_format_show, NULL); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_show(struct device *dev, 6718c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 6728c2ecf20Sopenharmony_ci{ 6738c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", chip->lux_calib); 6768c2ecf20Sopenharmony_ci} 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_calib_store(struct device *dev, 6798c2ecf20Sopenharmony_ci struct device_attribute *attr, 6808c2ecf20Sopenharmony_ci const char *buf, size_t len) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 6838c2ecf20Sopenharmony_ci unsigned long value; 6848c2ecf20Sopenharmony_ci int ret; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 6878c2ecf20Sopenharmony_ci if (ret) 6888c2ecf20Sopenharmony_ci return ret; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci chip->lux_calib = value; 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return len; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_calibscale, S_IRUGO | S_IWUSR, apds990x_lux_calib_show, 6968c2ecf20Sopenharmony_ci apds990x_lux_calib_store); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_avail(struct device *dev, 6998c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci int i; 7028c2ecf20Sopenharmony_ci int pos = 0; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(arates_hz); i++) 7058c2ecf20Sopenharmony_ci pos += sprintf(buf + pos, "%d ", arates_hz[i]); 7068c2ecf20Sopenharmony_ci sprintf(buf + pos - 1, "\n"); 7078c2ecf20Sopenharmony_ci return pos; 7088c2ecf20Sopenharmony_ci} 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_show(struct device *dev, 7118c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->arate); 7168c2ecf20Sopenharmony_ci} 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic int apds990x_set_arate(struct apds990x_chip *chip, int rate) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci int i; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(arates_hz); i++) 7238c2ecf20Sopenharmony_ci if (rate >= arates_hz[i]) 7248c2ecf20Sopenharmony_ci break; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (i == ARRAY_SIZE(arates_hz)) 7278c2ecf20Sopenharmony_ci return -EINVAL; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci /* Pick up corresponding persistence value */ 7308c2ecf20Sopenharmony_ci chip->lux_persistence = apersis[i]; 7318c2ecf20Sopenharmony_ci chip->arate = arates_hz[i]; 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ci /* If the chip is not in use, don't try to access it */ 7348c2ecf20Sopenharmony_ci if (pm_runtime_suspended(&chip->client->dev)) 7358c2ecf20Sopenharmony_ci return 0; 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci /* Persistence levels */ 7388c2ecf20Sopenharmony_ci return apds990x_write_byte(chip, APDS990X_PERS, 7398c2ecf20Sopenharmony_ci (chip->lux_persistence << APDS990X_APERS_SHIFT) | 7408c2ecf20Sopenharmony_ci (chip->prox_persistence << APDS990X_PPERS_SHIFT)); 7418c2ecf20Sopenharmony_ci} 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic ssize_t apds990x_rate_store(struct device *dev, 7448c2ecf20Sopenharmony_ci struct device_attribute *attr, 7458c2ecf20Sopenharmony_ci const char *buf, size_t len) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 7488c2ecf20Sopenharmony_ci unsigned long value; 7498c2ecf20Sopenharmony_ci int ret; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 7528c2ecf20Sopenharmony_ci if (ret) 7538c2ecf20Sopenharmony_ci return ret; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 7568c2ecf20Sopenharmony_ci ret = apds990x_set_arate(chip, value); 7578c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (ret < 0) 7608c2ecf20Sopenharmony_ci return ret; 7618c2ecf20Sopenharmony_ci return len; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_rate_avail, S_IRUGO, apds990x_rate_avail, NULL); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_rate, S_IRUGO | S_IWUSR, apds990x_rate_show, 7678c2ecf20Sopenharmony_ci apds990x_rate_store); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_show(struct device *dev, 7708c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci ssize_t ret; 7738c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (pm_runtime_suspended(dev) || !chip->prox_en) 7768c2ecf20Sopenharmony_ci return -EIO; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 7798c2ecf20Sopenharmony_ci ret = sprintf(buf, "%d\n", chip->prox_data); 7808c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 7818c2ecf20Sopenharmony_ci return ret; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_raw, S_IRUGO, apds990x_prox_show, NULL); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_range_show(struct device *dev, 7878c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7888c2ecf20Sopenharmony_ci{ 7898c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", APDS_PROX_RANGE); 7908c2ecf20Sopenharmony_ci} 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_sensor_range, S_IRUGO, apds990x_prox_range_show, NULL); 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_enable_show(struct device *dev, 7958c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 7968c2ecf20Sopenharmony_ci{ 7978c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->prox_en); 8008c2ecf20Sopenharmony_ci} 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_enable_store(struct device *dev, 8038c2ecf20Sopenharmony_ci struct device_attribute *attr, 8048c2ecf20Sopenharmony_ci const char *buf, size_t len) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 8078c2ecf20Sopenharmony_ci unsigned long value; 8088c2ecf20Sopenharmony_ci int ret; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 8118c2ecf20Sopenharmony_ci if (ret) 8128c2ecf20Sopenharmony_ci return ret; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci if (!chip->prox_en) 8178c2ecf20Sopenharmony_ci chip->prox_data = 0; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci if (value) 8208c2ecf20Sopenharmony_ci chip->prox_en++; 8218c2ecf20Sopenharmony_ci else if (chip->prox_en > 0) 8228c2ecf20Sopenharmony_ci chip->prox_en--; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 8258c2ecf20Sopenharmony_ci apds990x_mode_on(chip); 8268c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 8278c2ecf20Sopenharmony_ci return len; 8288c2ecf20Sopenharmony_ci} 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_raw_en, S_IRUGO | S_IWUSR, apds990x_prox_enable_show, 8318c2ecf20Sopenharmony_ci apds990x_prox_enable_store); 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_cistatic const char *reporting_modes[] = {"trigger", "periodic"}; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_mode_show(struct device *dev, 8368c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8378c2ecf20Sopenharmony_ci{ 8388c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return sprintf(buf, "%s\n", 8418c2ecf20Sopenharmony_ci reporting_modes[!!chip->prox_continuous_mode]); 8428c2ecf20Sopenharmony_ci} 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_mode_store(struct device *dev, 8458c2ecf20Sopenharmony_ci struct device_attribute *attr, 8468c2ecf20Sopenharmony_ci const char *buf, size_t len) 8478c2ecf20Sopenharmony_ci{ 8488c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 8498c2ecf20Sopenharmony_ci int ret; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci ret = sysfs_match_string(reporting_modes, buf); 8528c2ecf20Sopenharmony_ci if (ret < 0) 8538c2ecf20Sopenharmony_ci return ret; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci chip->prox_continuous_mode = ret; 8568c2ecf20Sopenharmony_ci return len; 8578c2ecf20Sopenharmony_ci} 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_reporting_mode, S_IRUGO | S_IWUSR, 8608c2ecf20Sopenharmony_ci apds990x_prox_reporting_mode_show, 8618c2ecf20Sopenharmony_ci apds990x_prox_reporting_mode_store); 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_reporting_avail_show(struct device *dev, 8648c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8658c2ecf20Sopenharmony_ci{ 8668c2ecf20Sopenharmony_ci return sprintf(buf, "%s %s\n", reporting_modes[0], reporting_modes[1]); 8678c2ecf20Sopenharmony_ci} 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_reporting_mode_avail, S_IRUGO | S_IWUSR, 8708c2ecf20Sopenharmony_ci apds990x_prox_reporting_avail_show, NULL); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_above_show(struct device *dev, 8748c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8758c2ecf20Sopenharmony_ci{ 8768c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 8778c2ecf20Sopenharmony_ci 8788c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->lux_thres_hi); 8798c2ecf20Sopenharmony_ci} 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_below_show(struct device *dev, 8828c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->lux_thres_lo); 8878c2ecf20Sopenharmony_ci} 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_cistatic ssize_t apds990x_set_lux_thresh(struct apds990x_chip *chip, u32 *target, 8908c2ecf20Sopenharmony_ci const char *buf) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci unsigned long thresh; 8938c2ecf20Sopenharmony_ci int ret; 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &thresh); 8968c2ecf20Sopenharmony_ci if (ret) 8978c2ecf20Sopenharmony_ci return ret; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (thresh > APDS_RANGE) 9008c2ecf20Sopenharmony_ci return -EINVAL; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9038c2ecf20Sopenharmony_ci *target = thresh; 9048c2ecf20Sopenharmony_ci /* 9058c2ecf20Sopenharmony_ci * Don't update values in HW if we are still waiting for 9068c2ecf20Sopenharmony_ci * first interrupt to come after device handle open call. 9078c2ecf20Sopenharmony_ci */ 9088c2ecf20Sopenharmony_ci if (!chip->lux_wait_fresh_res) 9098c2ecf20Sopenharmony_ci apds990x_refresh_athres(chip); 9108c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9118c2ecf20Sopenharmony_ci return ret; 9128c2ecf20Sopenharmony_ci 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_above_store(struct device *dev, 9168c2ecf20Sopenharmony_ci struct device_attribute *attr, 9178c2ecf20Sopenharmony_ci const char *buf, size_t len) 9188c2ecf20Sopenharmony_ci{ 9198c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 9208c2ecf20Sopenharmony_ci int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_hi, buf); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci if (ret < 0) 9238c2ecf20Sopenharmony_ci return ret; 9248c2ecf20Sopenharmony_ci return len; 9258c2ecf20Sopenharmony_ci} 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_cistatic ssize_t apds990x_lux_thresh_below_store(struct device *dev, 9288c2ecf20Sopenharmony_ci struct device_attribute *attr, 9298c2ecf20Sopenharmony_ci const char *buf, size_t len) 9308c2ecf20Sopenharmony_ci{ 9318c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 9328c2ecf20Sopenharmony_ci int ret = apds990x_set_lux_thresh(chip, &chip->lux_thres_lo, buf); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (ret < 0) 9358c2ecf20Sopenharmony_ci return ret; 9368c2ecf20Sopenharmony_ci return len; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_thresh_above_value, S_IRUGO | S_IWUSR, 9408c2ecf20Sopenharmony_ci apds990x_lux_thresh_above_show, 9418c2ecf20Sopenharmony_ci apds990x_lux_thresh_above_store); 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_cistatic DEVICE_ATTR(lux0_thresh_below_value, S_IRUGO | S_IWUSR, 9448c2ecf20Sopenharmony_ci apds990x_lux_thresh_below_show, 9458c2ecf20Sopenharmony_ci apds990x_lux_thresh_below_store); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_threshold_show(struct device *dev, 9488c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 9518c2ecf20Sopenharmony_ci 9528c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", chip->prox_thres); 9538c2ecf20Sopenharmony_ci} 9548c2ecf20Sopenharmony_ci 9558c2ecf20Sopenharmony_cistatic ssize_t apds990x_prox_threshold_store(struct device *dev, 9568c2ecf20Sopenharmony_ci struct device_attribute *attr, 9578c2ecf20Sopenharmony_ci const char *buf, size_t len) 9588c2ecf20Sopenharmony_ci{ 9598c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 9608c2ecf20Sopenharmony_ci unsigned long value; 9618c2ecf20Sopenharmony_ci int ret; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 9648c2ecf20Sopenharmony_ci if (ret) 9658c2ecf20Sopenharmony_ci return ret; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci if ((value > APDS_RANGE) || (value == 0) || 9688c2ecf20Sopenharmony_ci (value < APDS_PROX_HYSTERESIS)) 9698c2ecf20Sopenharmony_ci return -EINVAL; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 9728c2ecf20Sopenharmony_ci chip->prox_thres = value; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci apds990x_force_p_refresh(chip); 9758c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 9768c2ecf20Sopenharmony_ci return len; 9778c2ecf20Sopenharmony_ci} 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_cistatic DEVICE_ATTR(prox0_thresh_above_value, S_IRUGO | S_IWUSR, 9808c2ecf20Sopenharmony_ci apds990x_prox_threshold_show, 9818c2ecf20Sopenharmony_ci apds990x_prox_threshold_store); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_cistatic ssize_t apds990x_power_state_show(struct device *dev, 9848c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 9858c2ecf20Sopenharmony_ci{ 9868c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", !pm_runtime_suspended(dev)); 9878c2ecf20Sopenharmony_ci return 0; 9888c2ecf20Sopenharmony_ci} 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic ssize_t apds990x_power_state_store(struct device *dev, 9918c2ecf20Sopenharmony_ci struct device_attribute *attr, 9928c2ecf20Sopenharmony_ci const char *buf, size_t len) 9938c2ecf20Sopenharmony_ci{ 9948c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 9958c2ecf20Sopenharmony_ci unsigned long value; 9968c2ecf20Sopenharmony_ci int ret; 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci ret = kstrtoul(buf, 0, &value); 9998c2ecf20Sopenharmony_ci if (ret) 10008c2ecf20Sopenharmony_ci return ret; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci if (value) { 10038c2ecf20Sopenharmony_ci pm_runtime_get_sync(dev); 10048c2ecf20Sopenharmony_ci mutex_lock(&chip->mutex); 10058c2ecf20Sopenharmony_ci chip->lux_wait_fresh_res = true; 10068c2ecf20Sopenharmony_ci apds990x_force_a_refresh(chip); 10078c2ecf20Sopenharmony_ci apds990x_force_p_refresh(chip); 10088c2ecf20Sopenharmony_ci mutex_unlock(&chip->mutex); 10098c2ecf20Sopenharmony_ci } else { 10108c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(dev)) 10118c2ecf20Sopenharmony_ci pm_runtime_put(dev); 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci return len; 10148c2ecf20Sopenharmony_ci} 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_cistatic DEVICE_ATTR(power_state, S_IRUGO | S_IWUSR, 10178c2ecf20Sopenharmony_ci apds990x_power_state_show, 10188c2ecf20Sopenharmony_ci apds990x_power_state_store); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_cistatic ssize_t apds990x_chip_id_show(struct device *dev, 10218c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 10228c2ecf20Sopenharmony_ci{ 10238c2ecf20Sopenharmony_ci struct apds990x_chip *chip = dev_get_drvdata(dev); 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return sprintf(buf, "%s %d\n", chip->chipname, chip->revision); 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic DEVICE_ATTR(chip_id, S_IRUGO, apds990x_chip_id_show, NULL); 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_cistatic struct attribute *sysfs_attrs_ctrl[] = { 10318c2ecf20Sopenharmony_ci &dev_attr_lux0_calibscale.attr, 10328c2ecf20Sopenharmony_ci &dev_attr_lux0_calibscale_default.attr, 10338c2ecf20Sopenharmony_ci &dev_attr_lux0_input.attr, 10348c2ecf20Sopenharmony_ci &dev_attr_lux0_sensor_range.attr, 10358c2ecf20Sopenharmony_ci &dev_attr_lux0_rate.attr, 10368c2ecf20Sopenharmony_ci &dev_attr_lux0_rate_avail.attr, 10378c2ecf20Sopenharmony_ci &dev_attr_lux0_thresh_above_value.attr, 10388c2ecf20Sopenharmony_ci &dev_attr_lux0_thresh_below_value.attr, 10398c2ecf20Sopenharmony_ci &dev_attr_prox0_raw_en.attr, 10408c2ecf20Sopenharmony_ci &dev_attr_prox0_raw.attr, 10418c2ecf20Sopenharmony_ci &dev_attr_prox0_sensor_range.attr, 10428c2ecf20Sopenharmony_ci &dev_attr_prox0_thresh_above_value.attr, 10438c2ecf20Sopenharmony_ci &dev_attr_prox0_reporting_mode.attr, 10448c2ecf20Sopenharmony_ci &dev_attr_prox0_reporting_mode_avail.attr, 10458c2ecf20Sopenharmony_ci &dev_attr_chip_id.attr, 10468c2ecf20Sopenharmony_ci &dev_attr_power_state.attr, 10478c2ecf20Sopenharmony_ci NULL 10488c2ecf20Sopenharmony_ci}; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_cistatic const struct attribute_group apds990x_attribute_group[] = { 10518c2ecf20Sopenharmony_ci {.attrs = sysfs_attrs_ctrl }, 10528c2ecf20Sopenharmony_ci}; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_cistatic int apds990x_probe(struct i2c_client *client, 10558c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 10568c2ecf20Sopenharmony_ci{ 10578c2ecf20Sopenharmony_ci struct apds990x_chip *chip; 10588c2ecf20Sopenharmony_ci int err; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci chip = kzalloc(sizeof *chip, GFP_KERNEL); 10618c2ecf20Sopenharmony_ci if (!chip) 10628c2ecf20Sopenharmony_ci return -ENOMEM; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci i2c_set_clientdata(client, chip); 10658c2ecf20Sopenharmony_ci chip->client = client; 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci init_waitqueue_head(&chip->wait); 10688c2ecf20Sopenharmony_ci mutex_init(&chip->mutex); 10698c2ecf20Sopenharmony_ci chip->pdata = client->dev.platform_data; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci if (chip->pdata == NULL) { 10728c2ecf20Sopenharmony_ci dev_err(&client->dev, "platform data is mandatory\n"); 10738c2ecf20Sopenharmony_ci err = -EINVAL; 10748c2ecf20Sopenharmony_ci goto fail1; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (chip->pdata->cf.ga == 0) { 10788c2ecf20Sopenharmony_ci /* set uncovered sensor default parameters */ 10798c2ecf20Sopenharmony_ci chip->cf.ga = 1966; /* 0.48 * APDS_PARAM_SCALE */ 10808c2ecf20Sopenharmony_ci chip->cf.cf1 = 4096; /* 1.00 * APDS_PARAM_SCALE */ 10818c2ecf20Sopenharmony_ci chip->cf.irf1 = 9134; /* 2.23 * APDS_PARAM_SCALE */ 10828c2ecf20Sopenharmony_ci chip->cf.cf2 = 2867; /* 0.70 * APDS_PARAM_SCALE */ 10838c2ecf20Sopenharmony_ci chip->cf.irf2 = 5816; /* 1.42 * APDS_PARAM_SCALE */ 10848c2ecf20Sopenharmony_ci chip->cf.df = 52; 10858c2ecf20Sopenharmony_ci } else { 10868c2ecf20Sopenharmony_ci chip->cf = chip->pdata->cf; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci /* precalculate inverse chip factors for threshold control */ 10908c2ecf20Sopenharmony_ci chip->rcf.afactor = 10918c2ecf20Sopenharmony_ci (chip->cf.irf1 - chip->cf.irf2) * APDS_PARAM_SCALE / 10928c2ecf20Sopenharmony_ci (chip->cf.cf1 - chip->cf.cf2); 10938c2ecf20Sopenharmony_ci chip->rcf.cf1 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / 10948c2ecf20Sopenharmony_ci chip->cf.cf1; 10958c2ecf20Sopenharmony_ci chip->rcf.irf1 = chip->cf.irf1 * APDS_PARAM_SCALE / 10968c2ecf20Sopenharmony_ci chip->cf.cf1; 10978c2ecf20Sopenharmony_ci chip->rcf.cf2 = APDS_PARAM_SCALE * APDS_PARAM_SCALE / 10988c2ecf20Sopenharmony_ci chip->cf.cf2; 10998c2ecf20Sopenharmony_ci chip->rcf.irf2 = chip->cf.irf2 * APDS_PARAM_SCALE / 11008c2ecf20Sopenharmony_ci chip->cf.cf2; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* Set something to start with */ 11038c2ecf20Sopenharmony_ci chip->lux_thres_hi = APDS_LUX_DEF_THRES_HI; 11048c2ecf20Sopenharmony_ci chip->lux_thres_lo = APDS_LUX_DEF_THRES_LO; 11058c2ecf20Sopenharmony_ci chip->lux_calib = APDS_LUX_NEUTRAL_CALIB_VALUE; 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci chip->prox_thres = APDS_PROX_DEF_THRES; 11088c2ecf20Sopenharmony_ci chip->pdrive = chip->pdata->pdrive; 11098c2ecf20Sopenharmony_ci chip->pdiode = APDS_PDIODE_IR; 11108c2ecf20Sopenharmony_ci chip->pgain = APDS_PGAIN_1X; 11118c2ecf20Sopenharmony_ci chip->prox_calib = APDS_PROX_NEUTRAL_CALIB_VALUE; 11128c2ecf20Sopenharmony_ci chip->prox_persistence = APDS_DEFAULT_PROX_PERS; 11138c2ecf20Sopenharmony_ci chip->prox_continuous_mode = false; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci chip->regs[0].supply = reg_vcc; 11168c2ecf20Sopenharmony_ci chip->regs[1].supply = reg_vled; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci err = regulator_bulk_get(&client->dev, 11198c2ecf20Sopenharmony_ci ARRAY_SIZE(chip->regs), chip->regs); 11208c2ecf20Sopenharmony_ci if (err < 0) { 11218c2ecf20Sopenharmony_ci dev_err(&client->dev, "Cannot get regulators\n"); 11228c2ecf20Sopenharmony_ci goto fail1; 11238c2ecf20Sopenharmony_ci } 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci err = regulator_bulk_enable(ARRAY_SIZE(chip->regs), chip->regs); 11268c2ecf20Sopenharmony_ci if (err < 0) { 11278c2ecf20Sopenharmony_ci dev_err(&client->dev, "Cannot enable regulators\n"); 11288c2ecf20Sopenharmony_ci goto fail2; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci usleep_range(APDS_STARTUP_DELAY, 2 * APDS_STARTUP_DELAY); 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci err = apds990x_detect(chip); 11348c2ecf20Sopenharmony_ci if (err < 0) { 11358c2ecf20Sopenharmony_ci dev_err(&client->dev, "APDS990X not found\n"); 11368c2ecf20Sopenharmony_ci goto fail3; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci pm_runtime_set_active(&client->dev); 11408c2ecf20Sopenharmony_ci 11418c2ecf20Sopenharmony_ci apds990x_configure(chip); 11428c2ecf20Sopenharmony_ci apds990x_set_arate(chip, APDS_LUX_DEFAULT_RATE); 11438c2ecf20Sopenharmony_ci apds990x_mode_on(chip); 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (chip->pdata->setup_resources) { 11488c2ecf20Sopenharmony_ci err = chip->pdata->setup_resources(); 11498c2ecf20Sopenharmony_ci if (err) { 11508c2ecf20Sopenharmony_ci err = -EINVAL; 11518c2ecf20Sopenharmony_ci goto fail3; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci } 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci err = sysfs_create_group(&chip->client->dev.kobj, 11568c2ecf20Sopenharmony_ci apds990x_attribute_group); 11578c2ecf20Sopenharmony_ci if (err < 0) { 11588c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "Sysfs registration failed\n"); 11598c2ecf20Sopenharmony_ci goto fail4; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci err = request_threaded_irq(client->irq, NULL, 11638c2ecf20Sopenharmony_ci apds990x_irq, 11648c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | IRQF_TRIGGER_LOW | 11658c2ecf20Sopenharmony_ci IRQF_ONESHOT, 11668c2ecf20Sopenharmony_ci "apds990x", chip); 11678c2ecf20Sopenharmony_ci if (err) { 11688c2ecf20Sopenharmony_ci dev_err(&client->dev, "could not get IRQ %d\n", 11698c2ecf20Sopenharmony_ci client->irq); 11708c2ecf20Sopenharmony_ci goto fail5; 11718c2ecf20Sopenharmony_ci } 11728c2ecf20Sopenharmony_ci return err; 11738c2ecf20Sopenharmony_cifail5: 11748c2ecf20Sopenharmony_ci sysfs_remove_group(&chip->client->dev.kobj, 11758c2ecf20Sopenharmony_ci &apds990x_attribute_group[0]); 11768c2ecf20Sopenharmony_cifail4: 11778c2ecf20Sopenharmony_ci if (chip->pdata && chip->pdata->release_resources) 11788c2ecf20Sopenharmony_ci chip->pdata->release_resources(); 11798c2ecf20Sopenharmony_cifail3: 11808c2ecf20Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(chip->regs), chip->regs); 11818c2ecf20Sopenharmony_cifail2: 11828c2ecf20Sopenharmony_ci regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); 11838c2ecf20Sopenharmony_cifail1: 11848c2ecf20Sopenharmony_ci kfree(chip); 11858c2ecf20Sopenharmony_ci return err; 11868c2ecf20Sopenharmony_ci} 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_cistatic int apds990x_remove(struct i2c_client *client) 11898c2ecf20Sopenharmony_ci{ 11908c2ecf20Sopenharmony_ci struct apds990x_chip *chip = i2c_get_clientdata(client); 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci free_irq(client->irq, chip); 11938c2ecf20Sopenharmony_ci sysfs_remove_group(&chip->client->dev.kobj, 11948c2ecf20Sopenharmony_ci apds990x_attribute_group); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci if (chip->pdata && chip->pdata->release_resources) 11978c2ecf20Sopenharmony_ci chip->pdata->release_resources(); 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci if (!pm_runtime_suspended(&client->dev)) 12008c2ecf20Sopenharmony_ci apds990x_chip_off(chip); 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 12038c2ecf20Sopenharmony_ci pm_runtime_set_suspended(&client->dev); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci regulator_bulk_free(ARRAY_SIZE(chip->regs), chip->regs); 12068c2ecf20Sopenharmony_ci 12078c2ecf20Sopenharmony_ci kfree(chip); 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci} 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12128c2ecf20Sopenharmony_cistatic int apds990x_suspend(struct device *dev) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 12158c2ecf20Sopenharmony_ci struct apds990x_chip *chip = i2c_get_clientdata(client); 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci apds990x_chip_off(chip); 12188c2ecf20Sopenharmony_ci return 0; 12198c2ecf20Sopenharmony_ci} 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_cistatic int apds990x_resume(struct device *dev) 12228c2ecf20Sopenharmony_ci{ 12238c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 12248c2ecf20Sopenharmony_ci struct apds990x_chip *chip = i2c_get_clientdata(client); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* 12278c2ecf20Sopenharmony_ci * If we were enabled at suspend time, it is expected 12288c2ecf20Sopenharmony_ci * everything works nice and smoothly. Chip_on is enough 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci apds990x_chip_on(chip); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci return 0; 12338c2ecf20Sopenharmony_ci} 12348c2ecf20Sopenharmony_ci#endif 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12378c2ecf20Sopenharmony_cistatic int apds990x_runtime_suspend(struct device *dev) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 12408c2ecf20Sopenharmony_ci struct apds990x_chip *chip = i2c_get_clientdata(client); 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci apds990x_chip_off(chip); 12438c2ecf20Sopenharmony_ci return 0; 12448c2ecf20Sopenharmony_ci} 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_cistatic int apds990x_runtime_resume(struct device *dev) 12478c2ecf20Sopenharmony_ci{ 12488c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 12498c2ecf20Sopenharmony_ci struct apds990x_chip *chip = i2c_get_clientdata(client); 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci apds990x_chip_on(chip); 12528c2ecf20Sopenharmony_ci return 0; 12538c2ecf20Sopenharmony_ci} 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci#endif 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_cistatic const struct i2c_device_id apds990x_id[] = { 12588c2ecf20Sopenharmony_ci {"apds990x", 0 }, 12598c2ecf20Sopenharmony_ci {} 12608c2ecf20Sopenharmony_ci}; 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, apds990x_id); 12638c2ecf20Sopenharmony_ci 12648c2ecf20Sopenharmony_cistatic const struct dev_pm_ops apds990x_pm_ops = { 12658c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(apds990x_suspend, apds990x_resume) 12668c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(apds990x_runtime_suspend, 12678c2ecf20Sopenharmony_ci apds990x_runtime_resume, 12688c2ecf20Sopenharmony_ci NULL) 12698c2ecf20Sopenharmony_ci}; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_cistatic struct i2c_driver apds990x_driver = { 12728c2ecf20Sopenharmony_ci .driver = { 12738c2ecf20Sopenharmony_ci .name = "apds990x", 12748c2ecf20Sopenharmony_ci .pm = &apds990x_pm_ops, 12758c2ecf20Sopenharmony_ci }, 12768c2ecf20Sopenharmony_ci .probe = apds990x_probe, 12778c2ecf20Sopenharmony_ci .remove = apds990x_remove, 12788c2ecf20Sopenharmony_ci .id_table = apds990x_id, 12798c2ecf20Sopenharmony_ci}; 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_cimodule_i2c_driver(apds990x_driver); 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("APDS990X combined ALS and proximity sensor"); 12848c2ecf20Sopenharmony_ciMODULE_AUTHOR("Samu Onkalo, Nokia Corporation"); 12858c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1286