18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient 48c2ecf20Sopenharmony_ci * light and proximity sensor 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 78c2ecf20Sopenharmony_ci * Copyright 2019 Pursim SPC 88c2ecf20Sopenharmony_ci * Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * IIO driver for: 118c2ecf20Sopenharmony_ci * VCNL4000/10/20 (7-bit I2C slave address 0x13) 128c2ecf20Sopenharmony_ci * VCNL4040 (7-bit I2C slave address 0x60) 138c2ecf20Sopenharmony_ci * VCNL4200 (7-bit I2C slave address 0x51) 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * TODO: 168c2ecf20Sopenharmony_ci * allow to adjust IR current 178c2ecf20Sopenharmony_ci * interrupts (VCNL4040, VCNL4200) 188c2ecf20Sopenharmony_ci */ 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <linux/module.h> 218c2ecf20Sopenharmony_ci#include <linux/i2c.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci#include <linux/delay.h> 248c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/iio/buffer.h> 288c2ecf20Sopenharmony_ci#include <linux/iio/events.h> 298c2ecf20Sopenharmony_ci#include <linux/iio/iio.h> 308c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h> 318c2ecf20Sopenharmony_ci#include <linux/iio/trigger.h> 328c2ecf20Sopenharmony_ci#include <linux/iio/trigger_consumer.h> 338c2ecf20Sopenharmony_ci#include <linux/iio/triggered_buffer.h> 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define VCNL4000_DRV_NAME "vcnl4000" 368c2ecf20Sopenharmony_ci#define VCNL4000_PROD_ID 0x01 378c2ecf20Sopenharmony_ci#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 388c2ecf20Sopenharmony_ci#define VCNL4040_PROD_ID 0x86 398c2ecf20Sopenharmony_ci#define VCNL4200_PROD_ID 0x58 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define VCNL4000_COMMAND 0x80 /* Command register */ 428c2ecf20Sopenharmony_ci#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 438c2ecf20Sopenharmony_ci#define VCNL4010_PROX_RATE 0x82 /* Proximity rate */ 448c2ecf20Sopenharmony_ci#define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 458c2ecf20Sopenharmony_ci#define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 468c2ecf20Sopenharmony_ci#define VCNL4010_ALS_PARAM 0x84 /* ALS rate */ 478c2ecf20Sopenharmony_ci#define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 488c2ecf20Sopenharmony_ci#define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 498c2ecf20Sopenharmony_ci#define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 508c2ecf20Sopenharmony_ci#define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 518c2ecf20Sopenharmony_ci#define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 528c2ecf20Sopenharmony_ci#define VCNL4010_INT_CTRL 0x89 /* Interrupt control */ 538c2ecf20Sopenharmony_ci#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 548c2ecf20Sopenharmony_ci#define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */ 558c2ecf20Sopenharmony_ci#define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */ 568c2ecf20Sopenharmony_ci#define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */ 578c2ecf20Sopenharmony_ci#define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */ 588c2ecf20Sopenharmony_ci#define VCNL4010_ISR 0x8e /* Interrupt status */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 618c2ecf20Sopenharmony_ci#define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 628c2ecf20Sopenharmony_ci#define VCNL4200_PS_DATA 0x08 /* Proximity data */ 638c2ecf20Sopenharmony_ci#define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 648c2ecf20Sopenharmony_ci#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci#define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* Bit masks for COMMAND register */ 698c2ecf20Sopenharmony_ci#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 708c2ecf20Sopenharmony_ci#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 718c2ecf20Sopenharmony_ci#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 728c2ecf20Sopenharmony_ci#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 738c2ecf20Sopenharmony_ci#define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 748c2ecf20Sopenharmony_ci#define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 758c2ecf20Sopenharmony_ci#define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Bit masks for interrupt registers. */ 788c2ecf20Sopenharmony_ci#define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 798c2ecf20Sopenharmony_ci#define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 808c2ecf20Sopenharmony_ci#define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 818c2ecf20Sopenharmony_ci#define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 848c2ecf20Sopenharmony_ci#define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 858c2ecf20Sopenharmony_ci#define VCNL4010_INT_ALS 2 /* ALS data ready */ 868c2ecf20Sopenharmony_ci#define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci#define VCNL4010_INT_THR \ 898c2ecf20Sopenharmony_ci (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 908c2ecf20Sopenharmony_ci#define VCNL4010_INT_DRDY \ 918c2ecf20Sopenharmony_ci (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic const int vcnl4010_prox_sampling_frequency[][2] = { 948c2ecf20Sopenharmony_ci {1, 950000}, 958c2ecf20Sopenharmony_ci {3, 906250}, 968c2ecf20Sopenharmony_ci {7, 812500}, 978c2ecf20Sopenharmony_ci {16, 625000}, 988c2ecf20Sopenharmony_ci {31, 250000}, 998c2ecf20Sopenharmony_ci {62, 500000}, 1008c2ecf20Sopenharmony_ci {125, 0}, 1018c2ecf20Sopenharmony_ci {250, 0}, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cienum vcnl4000_device_ids { 1078c2ecf20Sopenharmony_ci VCNL4000, 1088c2ecf20Sopenharmony_ci VCNL4010, 1098c2ecf20Sopenharmony_ci VCNL4040, 1108c2ecf20Sopenharmony_ci VCNL4200, 1118c2ecf20Sopenharmony_ci}; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistruct vcnl4200_channel { 1148c2ecf20Sopenharmony_ci u8 reg; 1158c2ecf20Sopenharmony_ci ktime_t last_measurement; 1168c2ecf20Sopenharmony_ci ktime_t sampling_rate; 1178c2ecf20Sopenharmony_ci struct mutex lock; 1188c2ecf20Sopenharmony_ci}; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistruct vcnl4000_data { 1218c2ecf20Sopenharmony_ci struct i2c_client *client; 1228c2ecf20Sopenharmony_ci enum vcnl4000_device_ids id; 1238c2ecf20Sopenharmony_ci int rev; 1248c2ecf20Sopenharmony_ci int al_scale; 1258c2ecf20Sopenharmony_ci const struct vcnl4000_chip_spec *chip_spec; 1268c2ecf20Sopenharmony_ci struct mutex vcnl4000_lock; 1278c2ecf20Sopenharmony_ci struct vcnl4200_channel vcnl4200_al; 1288c2ecf20Sopenharmony_ci struct vcnl4200_channel vcnl4200_ps; 1298c2ecf20Sopenharmony_ci uint32_t near_level; 1308c2ecf20Sopenharmony_ci}; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistruct vcnl4000_chip_spec { 1338c2ecf20Sopenharmony_ci const char *prod; 1348c2ecf20Sopenharmony_ci struct iio_chan_spec const *channels; 1358c2ecf20Sopenharmony_ci const int num_channels; 1368c2ecf20Sopenharmony_ci const struct iio_info *info; 1378c2ecf20Sopenharmony_ci bool irq_support; 1388c2ecf20Sopenharmony_ci int (*init)(struct vcnl4000_data *data); 1398c2ecf20Sopenharmony_ci int (*measure_light)(struct vcnl4000_data *data, int *val); 1408c2ecf20Sopenharmony_ci int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1418c2ecf20Sopenharmony_ci int (*set_power_state)(struct vcnl4000_data *data, bool on); 1428c2ecf20Sopenharmony_ci}; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic const struct i2c_device_id vcnl4000_id[] = { 1458c2ecf20Sopenharmony_ci { "vcnl4000", VCNL4000 }, 1468c2ecf20Sopenharmony_ci { "vcnl4010", VCNL4010 }, 1478c2ecf20Sopenharmony_ci { "vcnl4020", VCNL4010 }, 1488c2ecf20Sopenharmony_ci { "vcnl4040", VCNL4040 }, 1498c2ecf20Sopenharmony_ci { "vcnl4200", VCNL4200 }, 1508c2ecf20Sopenharmony_ci { } 1518c2ecf20Sopenharmony_ci}; 1528c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, vcnl4000_id); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci /* no suspend op */ 1578c2ecf20Sopenharmony_ci return 0; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic int vcnl4000_init(struct vcnl4000_data *data) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci int ret, prod_id; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1658c2ecf20Sopenharmony_ci if (ret < 0) 1668c2ecf20Sopenharmony_ci return ret; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci prod_id = ret >> 4; 1698c2ecf20Sopenharmony_ci switch (prod_id) { 1708c2ecf20Sopenharmony_ci case VCNL4000_PROD_ID: 1718c2ecf20Sopenharmony_ci if (data->id != VCNL4000) 1728c2ecf20Sopenharmony_ci dev_warn(&data->client->dev, 1738c2ecf20Sopenharmony_ci "wrong device id, use vcnl4000"); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case VCNL4010_PROD_ID: 1768c2ecf20Sopenharmony_ci if (data->id != VCNL4010) 1778c2ecf20Sopenharmony_ci dev_warn(&data->client->dev, 1788c2ecf20Sopenharmony_ci "wrong device id, use vcnl4010/4020"); 1798c2ecf20Sopenharmony_ci break; 1808c2ecf20Sopenharmony_ci default: 1818c2ecf20Sopenharmony_ci return -ENODEV; 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci data->rev = ret & 0xf; 1858c2ecf20Sopenharmony_ci data->al_scale = 250000; 1868c2ecf20Sopenharmony_ci mutex_init(&data->vcnl4000_lock); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci return data->chip_spec->set_power_state(data, true); 1898c2ecf20Sopenharmony_ci}; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci u16 val = on ? 0 /* power on */ : 1 /* shut down */; 1948c2ecf20Sopenharmony_ci int ret; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); 1978c2ecf20Sopenharmony_ci if (ret < 0) 1988c2ecf20Sopenharmony_ci return ret; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 2018c2ecf20Sopenharmony_ci if (ret < 0) 2028c2ecf20Sopenharmony_ci return ret; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (on) { 2058c2ecf20Sopenharmony_ci /* Wait at least one integration cycle before fetching data */ 2068c2ecf20Sopenharmony_ci data->vcnl4200_al.last_measurement = ktime_get(); 2078c2ecf20Sopenharmony_ci data->vcnl4200_ps.last_measurement = ktime_get(); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int vcnl4200_init(struct vcnl4000_data *data) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci int ret, id; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 2188c2ecf20Sopenharmony_ci if (ret < 0) 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci id = ret & 0xff; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci if (id != VCNL4200_PROD_ID) { 2248c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 2258c2ecf20Sopenharmony_ci if (ret < 0) 2268c2ecf20Sopenharmony_ci return ret; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci id = ret & 0xff; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (id != VCNL4040_PROD_ID) 2318c2ecf20Sopenharmony_ci return -ENODEV; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dev_dbg(&data->client->dev, "device id 0x%x", id); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci data->rev = (ret >> 8) & 0xf; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci data->vcnl4200_al.reg = VCNL4200_AL_DATA; 2398c2ecf20Sopenharmony_ci data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 2408c2ecf20Sopenharmony_ci switch (id) { 2418c2ecf20Sopenharmony_ci case VCNL4200_PROD_ID: 2428c2ecf20Sopenharmony_ci /* Default wait time is 50ms, add 20% tolerance. */ 2438c2ecf20Sopenharmony_ci data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 2448c2ecf20Sopenharmony_ci /* Default wait time is 4.8ms, add 20% tolerance. */ 2458c2ecf20Sopenharmony_ci data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 2468c2ecf20Sopenharmony_ci data->al_scale = 24000; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci case VCNL4040_PROD_ID: 2498c2ecf20Sopenharmony_ci /* Default wait time is 80ms, add 20% tolerance. */ 2508c2ecf20Sopenharmony_ci data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 2518c2ecf20Sopenharmony_ci /* Default wait time is 5ms, add 20% tolerance. */ 2528c2ecf20Sopenharmony_ci data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 2538c2ecf20Sopenharmony_ci data->al_scale = 120000; 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci mutex_init(&data->vcnl4200_al.lock); 2578c2ecf20Sopenharmony_ci mutex_init(&data->vcnl4200_ps.lock); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci ret = data->chip_spec->set_power_state(data, true); 2608c2ecf20Sopenharmony_ci if (ret < 0) 2618c2ecf20Sopenharmony_ci return ret; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return 0; 2648c2ecf20Sopenharmony_ci}; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci s32 ret; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(data->client, data_reg); 2718c2ecf20Sopenharmony_ci if (ret < 0) 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci *val = ret; 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 2798c2ecf20Sopenharmony_ci{ 2808c2ecf20Sopenharmony_ci if (val > U16_MAX) 2818c2ecf20Sopenharmony_ci return -ERANGE; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return i2c_smbus_write_word_swapped(data->client, data_reg, val); 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 2888c2ecf20Sopenharmony_ci u8 rdy_mask, u8 data_reg, int *val) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci int tries = 20; 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci mutex_lock(&data->vcnl4000_lock); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 2968c2ecf20Sopenharmony_ci req_mask); 2978c2ecf20Sopenharmony_ci if (ret < 0) 2988c2ecf20Sopenharmony_ci goto fail; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci /* wait for data to become ready */ 3018c2ecf20Sopenharmony_ci while (tries--) { 3028c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 3038c2ecf20Sopenharmony_ci if (ret < 0) 3048c2ecf20Sopenharmony_ci goto fail; 3058c2ecf20Sopenharmony_ci if (ret & rdy_mask) 3068c2ecf20Sopenharmony_ci break; 3078c2ecf20Sopenharmony_ci msleep(20); /* measurement takes up to 100 ms */ 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (tries < 0) { 3118c2ecf20Sopenharmony_ci dev_err(&data->client->dev, 3128c2ecf20Sopenharmony_ci "vcnl4000_measure() failed, data not ready\n"); 3138c2ecf20Sopenharmony_ci ret = -EIO; 3148c2ecf20Sopenharmony_ci goto fail; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci ret = vcnl4000_read_data(data, data_reg, val); 3188c2ecf20Sopenharmony_ci if (ret < 0) 3198c2ecf20Sopenharmony_ci goto fail; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci mutex_unlock(&data->vcnl4000_lock); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_cifail: 3268c2ecf20Sopenharmony_ci mutex_unlock(&data->vcnl4000_lock); 3278c2ecf20Sopenharmony_ci return ret; 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic int vcnl4200_measure(struct vcnl4000_data *data, 3318c2ecf20Sopenharmony_ci struct vcnl4200_channel *chan, int *val) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci int ret; 3348c2ecf20Sopenharmony_ci s64 delta; 3358c2ecf20Sopenharmony_ci ktime_t next_measurement; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci mutex_lock(&chan->lock); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci next_measurement = ktime_add(chan->last_measurement, 3408c2ecf20Sopenharmony_ci chan->sampling_rate); 3418c2ecf20Sopenharmony_ci delta = ktime_us_delta(next_measurement, ktime_get()); 3428c2ecf20Sopenharmony_ci if (delta > 0) 3438c2ecf20Sopenharmony_ci usleep_range(delta, delta + 500); 3448c2ecf20Sopenharmony_ci chan->last_measurement = ktime_get(); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci mutex_unlock(&chan->lock); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_data(data->client, chan->reg); 3498c2ecf20Sopenharmony_ci if (ret < 0) 3508c2ecf20Sopenharmony_ci return ret; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci *val = ret; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 3588c2ecf20Sopenharmony_ci{ 3598c2ecf20Sopenharmony_ci return vcnl4000_measure(data, 3608c2ecf20Sopenharmony_ci VCNL4000_AL_OD, VCNL4000_AL_RDY, 3618c2ecf20Sopenharmony_ci VCNL4000_AL_RESULT_HI, val); 3628c2ecf20Sopenharmony_ci} 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci return vcnl4200_measure(data, &data->vcnl4200_al, val); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cistatic int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 3708c2ecf20Sopenharmony_ci{ 3718c2ecf20Sopenharmony_ci return vcnl4000_measure(data, 3728c2ecf20Sopenharmony_ci VCNL4000_PS_OD, VCNL4000_PS_RDY, 3738c2ecf20Sopenharmony_ci VCNL4000_PS_RESULT_HI, val); 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci return vcnl4200_measure(data, &data->vcnl4200_ps, val); 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 3828c2ecf20Sopenharmony_ci int *val2) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci int ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 3878c2ecf20Sopenharmony_ci if (ret < 0) 3888c2ecf20Sopenharmony_ci return ret; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 3918c2ecf20Sopenharmony_ci return -EINVAL; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci *val = vcnl4010_prox_sampling_frequency[ret][0]; 3948c2ecf20Sopenharmony_ci *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci return 0; 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci int ret; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 4048c2ecf20Sopenharmony_ci if (ret < 0) 4058c2ecf20Sopenharmony_ci return false; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci return !!(ret & VCNL4000_SELF_TIMED_EN); 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct device *dev = &data->client->dev; 4138c2ecf20Sopenharmony_ci int ret; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (on) { 4168c2ecf20Sopenharmony_ci ret = pm_runtime_get_sync(dev); 4178c2ecf20Sopenharmony_ci if (ret < 0) 4188c2ecf20Sopenharmony_ci pm_runtime_put_noidle(dev); 4198c2ecf20Sopenharmony_ci } else { 4208c2ecf20Sopenharmony_ci pm_runtime_mark_last_busy(dev); 4218c2ecf20Sopenharmony_ci ret = pm_runtime_put_autosuspend(dev); 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci return ret; 4258c2ecf20Sopenharmony_ci} 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int vcnl4000_read_raw(struct iio_dev *indio_dev, 4288c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4298c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 4308c2ecf20Sopenharmony_ci{ 4318c2ecf20Sopenharmony_ci int ret; 4328c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci switch (mask) { 4358c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4368c2ecf20Sopenharmony_ci ret = vcnl4000_set_pm_runtime_state(data, true); 4378c2ecf20Sopenharmony_ci if (ret < 0) 4388c2ecf20Sopenharmony_ci return ret; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci switch (chan->type) { 4418c2ecf20Sopenharmony_ci case IIO_LIGHT: 4428c2ecf20Sopenharmony_ci ret = data->chip_spec->measure_light(data, val); 4438c2ecf20Sopenharmony_ci if (!ret) 4448c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 4458c2ecf20Sopenharmony_ci break; 4468c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 4478c2ecf20Sopenharmony_ci ret = data->chip_spec->measure_proximity(data, val); 4488c2ecf20Sopenharmony_ci if (!ret) 4498c2ecf20Sopenharmony_ci ret = IIO_VAL_INT; 4508c2ecf20Sopenharmony_ci break; 4518c2ecf20Sopenharmony_ci default: 4528c2ecf20Sopenharmony_ci ret = -EINVAL; 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci vcnl4000_set_pm_runtime_state(data, false); 4558c2ecf20Sopenharmony_ci return ret; 4568c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 4578c2ecf20Sopenharmony_ci if (chan->type != IIO_LIGHT) 4588c2ecf20Sopenharmony_ci return -EINVAL; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci *val = 0; 4618c2ecf20Sopenharmony_ci *val2 = data->al_scale; 4628c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 4638c2ecf20Sopenharmony_ci default: 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci} 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_cistatic int vcnl4010_read_raw(struct iio_dev *indio_dev, 4698c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 4708c2ecf20Sopenharmony_ci int *val, int *val2, long mask) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci int ret; 4738c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci switch (mask) { 4768c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_RAW: 4778c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SCALE: 4788c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 4798c2ecf20Sopenharmony_ci if (ret) 4808c2ecf20Sopenharmony_ci return ret; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci /* Protect against event capture. */ 4838c2ecf20Sopenharmony_ci if (vcnl4010_is_in_periodic_mode(data)) { 4848c2ecf20Sopenharmony_ci ret = -EBUSY; 4858c2ecf20Sopenharmony_ci } else { 4868c2ecf20Sopenharmony_ci ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 4878c2ecf20Sopenharmony_ci mask); 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 4918c2ecf20Sopenharmony_ci return ret; 4928c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 4938c2ecf20Sopenharmony_ci switch (chan->type) { 4948c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 4958c2ecf20Sopenharmony_ci ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 4968c2ecf20Sopenharmony_ci if (ret < 0) 4978c2ecf20Sopenharmony_ci return ret; 4988c2ecf20Sopenharmony_ci return IIO_VAL_INT_PLUS_MICRO; 4998c2ecf20Sopenharmony_ci default: 5008c2ecf20Sopenharmony_ci return -EINVAL; 5018c2ecf20Sopenharmony_ci } 5028c2ecf20Sopenharmony_ci default: 5038c2ecf20Sopenharmony_ci return -EINVAL; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int vcnl4010_read_avail(struct iio_dev *indio_dev, 5088c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 5098c2ecf20Sopenharmony_ci const int **vals, int *type, int *length, 5108c2ecf20Sopenharmony_ci long mask) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci switch (mask) { 5138c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 5148c2ecf20Sopenharmony_ci *vals = (int *)vcnl4010_prox_sampling_frequency; 5158c2ecf20Sopenharmony_ci *type = IIO_VAL_INT_PLUS_MICRO; 5168c2ecf20Sopenharmony_ci *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 5178c2ecf20Sopenharmony_ci return IIO_AVAIL_LIST; 5188c2ecf20Sopenharmony_ci default: 5198c2ecf20Sopenharmony_ci return -EINVAL; 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_cistatic int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 5248c2ecf20Sopenharmony_ci int val2) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci unsigned int i; 5278c2ecf20Sopenharmony_ci int index = -1; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 5308c2ecf20Sopenharmony_ci if (val == vcnl4010_prox_sampling_frequency[i][0] && 5318c2ecf20Sopenharmony_ci val2 == vcnl4010_prox_sampling_frequency[i][1]) { 5328c2ecf20Sopenharmony_ci index = i; 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (index < 0) 5388c2ecf20Sopenharmony_ci return -EINVAL; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 5418c2ecf20Sopenharmony_ci index); 5428c2ecf20Sopenharmony_ci} 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cistatic int vcnl4010_write_raw(struct iio_dev *indio_dev, 5458c2ecf20Sopenharmony_ci struct iio_chan_spec const *chan, 5468c2ecf20Sopenharmony_ci int val, int val2, long mask) 5478c2ecf20Sopenharmony_ci{ 5488c2ecf20Sopenharmony_ci int ret; 5498c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 5528c2ecf20Sopenharmony_ci if (ret) 5538c2ecf20Sopenharmony_ci return ret; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* Protect against event capture. */ 5568c2ecf20Sopenharmony_ci if (vcnl4010_is_in_periodic_mode(data)) { 5578c2ecf20Sopenharmony_ci ret = -EBUSY; 5588c2ecf20Sopenharmony_ci goto end; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci switch (mask) { 5628c2ecf20Sopenharmony_ci case IIO_CHAN_INFO_SAMP_FREQ: 5638c2ecf20Sopenharmony_ci switch (chan->type) { 5648c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 5658c2ecf20Sopenharmony_ci ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 5668c2ecf20Sopenharmony_ci goto end; 5678c2ecf20Sopenharmony_ci default: 5688c2ecf20Sopenharmony_ci ret = -EINVAL; 5698c2ecf20Sopenharmony_ci goto end; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci default: 5728c2ecf20Sopenharmony_ci ret = -EINVAL; 5738c2ecf20Sopenharmony_ci goto end; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ciend: 5778c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 5788c2ecf20Sopenharmony_ci return ret; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int vcnl4010_read_event(struct iio_dev *indio_dev, 5828c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 5838c2ecf20Sopenharmony_ci enum iio_event_type type, 5848c2ecf20Sopenharmony_ci enum iio_event_direction dir, 5858c2ecf20Sopenharmony_ci enum iio_event_info info, 5868c2ecf20Sopenharmony_ci int *val, int *val2) 5878c2ecf20Sopenharmony_ci{ 5888c2ecf20Sopenharmony_ci int ret; 5898c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci switch (info) { 5928c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 5938c2ecf20Sopenharmony_ci switch (dir) { 5948c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 5958c2ecf20Sopenharmony_ci ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 5968c2ecf20Sopenharmony_ci val); 5978c2ecf20Sopenharmony_ci if (ret < 0) 5988c2ecf20Sopenharmony_ci return ret; 5998c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6008c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 6018c2ecf20Sopenharmony_ci ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 6028c2ecf20Sopenharmony_ci val); 6038c2ecf20Sopenharmony_ci if (ret < 0) 6048c2ecf20Sopenharmony_ci return ret; 6058c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6068c2ecf20Sopenharmony_ci default: 6078c2ecf20Sopenharmony_ci return -EINVAL; 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci default: 6108c2ecf20Sopenharmony_ci return -EINVAL; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic int vcnl4010_write_event(struct iio_dev *indio_dev, 6158c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 6168c2ecf20Sopenharmony_ci enum iio_event_type type, 6178c2ecf20Sopenharmony_ci enum iio_event_direction dir, 6188c2ecf20Sopenharmony_ci enum iio_event_info info, 6198c2ecf20Sopenharmony_ci int val, int val2) 6208c2ecf20Sopenharmony_ci{ 6218c2ecf20Sopenharmony_ci int ret; 6228c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci switch (info) { 6258c2ecf20Sopenharmony_ci case IIO_EV_INFO_VALUE: 6268c2ecf20Sopenharmony_ci switch (dir) { 6278c2ecf20Sopenharmony_ci case IIO_EV_DIR_RISING: 6288c2ecf20Sopenharmony_ci ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 6298c2ecf20Sopenharmony_ci val); 6308c2ecf20Sopenharmony_ci if (ret < 0) 6318c2ecf20Sopenharmony_ci return ret; 6328c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6338c2ecf20Sopenharmony_ci case IIO_EV_DIR_FALLING: 6348c2ecf20Sopenharmony_ci ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 6358c2ecf20Sopenharmony_ci val); 6368c2ecf20Sopenharmony_ci if (ret < 0) 6378c2ecf20Sopenharmony_ci return ret; 6388c2ecf20Sopenharmony_ci return IIO_VAL_INT; 6398c2ecf20Sopenharmony_ci default: 6408c2ecf20Sopenharmony_ci return -EINVAL; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci default: 6438c2ecf20Sopenharmony_ci return -EINVAL; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci int ret; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 6528c2ecf20Sopenharmony_ci if (ret < 0) 6538c2ecf20Sopenharmony_ci return false; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return !!(ret & VCNL4010_INT_THR_EN); 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int vcnl4010_read_event_config(struct iio_dev *indio_dev, 6598c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 6608c2ecf20Sopenharmony_ci enum iio_event_type type, 6618c2ecf20Sopenharmony_ci enum iio_event_direction dir) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci switch (chan->type) { 6668c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 6678c2ecf20Sopenharmony_ci return vcnl4010_is_thr_enabled(data); 6688c2ecf20Sopenharmony_ci default: 6698c2ecf20Sopenharmony_ci return -EINVAL; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci} 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_cistatic int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 6768c2ecf20Sopenharmony_ci int ret; 6778c2ecf20Sopenharmony_ci int icr; 6788c2ecf20Sopenharmony_ci int command; 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci if (state) { 6818c2ecf20Sopenharmony_ci ret = iio_device_claim_direct_mode(indio_dev); 6828c2ecf20Sopenharmony_ci if (ret) 6838c2ecf20Sopenharmony_ci return ret; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci /* Enable periodic measurement of proximity data. */ 6868c2ecf20Sopenharmony_ci command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* 6898c2ecf20Sopenharmony_ci * Enable interrupts on threshold, for proximity data by 6908c2ecf20Sopenharmony_ci * default. 6918c2ecf20Sopenharmony_ci */ 6928c2ecf20Sopenharmony_ci icr = VCNL4010_INT_THR_EN; 6938c2ecf20Sopenharmony_ci } else { 6948c2ecf20Sopenharmony_ci if (!vcnl4010_is_thr_enabled(data)) 6958c2ecf20Sopenharmony_ci return 0; 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci command = 0; 6988c2ecf20Sopenharmony_ci icr = 0; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 7028c2ecf20Sopenharmony_ci command); 7038c2ecf20Sopenharmony_ci if (ret < 0) 7048c2ecf20Sopenharmony_ci goto end; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ciend: 7098c2ecf20Sopenharmony_ci if (state) 7108c2ecf20Sopenharmony_ci iio_device_release_direct_mode(indio_dev); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci return ret; 7138c2ecf20Sopenharmony_ci} 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_cistatic int vcnl4010_write_event_config(struct iio_dev *indio_dev, 7168c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 7178c2ecf20Sopenharmony_ci enum iio_event_type type, 7188c2ecf20Sopenharmony_ci enum iio_event_direction dir, 7198c2ecf20Sopenharmony_ci int state) 7208c2ecf20Sopenharmony_ci{ 7218c2ecf20Sopenharmony_ci switch (chan->type) { 7228c2ecf20Sopenharmony_ci case IIO_PROXIMITY: 7238c2ecf20Sopenharmony_ci return vcnl4010_config_threshold(indio_dev, state); 7248c2ecf20Sopenharmony_ci default: 7258c2ecf20Sopenharmony_ci return -EINVAL; 7268c2ecf20Sopenharmony_ci } 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_cistatic ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 7308c2ecf20Sopenharmony_ci uintptr_t priv, 7318c2ecf20Sopenharmony_ci const struct iio_chan_spec *chan, 7328c2ecf20Sopenharmony_ci char *buf) 7338c2ecf20Sopenharmony_ci{ 7348c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", data->near_level); 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 7408c2ecf20Sopenharmony_ci { 7418c2ecf20Sopenharmony_ci .name = "nearlevel", 7428c2ecf20Sopenharmony_ci .shared = IIO_SEPARATE, 7438c2ecf20Sopenharmony_ci .read = vcnl4000_read_near_level, 7448c2ecf20Sopenharmony_ci }, 7458c2ecf20Sopenharmony_ci { /* sentinel */ } 7468c2ecf20Sopenharmony_ci}; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_cistatic const struct iio_event_spec vcnl4000_event_spec[] = { 7498c2ecf20Sopenharmony_ci { 7508c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 7518c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_RISING, 7528c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 7538c2ecf20Sopenharmony_ci }, { 7548c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 7558c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_FALLING, 7568c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_VALUE), 7578c2ecf20Sopenharmony_ci }, { 7588c2ecf20Sopenharmony_ci .type = IIO_EV_TYPE_THRESH, 7598c2ecf20Sopenharmony_ci .dir = IIO_EV_DIR_EITHER, 7608c2ecf20Sopenharmony_ci .mask_separate = BIT(IIO_EV_INFO_ENABLE), 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci}; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic const struct iio_chan_spec vcnl4000_channels[] = { 7658c2ecf20Sopenharmony_ci { 7668c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 7678c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 7688c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 7698c2ecf20Sopenharmony_ci }, { 7708c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 7718c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 7728c2ecf20Sopenharmony_ci .ext_info = vcnl4000_ext_info, 7738c2ecf20Sopenharmony_ci } 7748c2ecf20Sopenharmony_ci}; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_cistatic const struct iio_chan_spec vcnl4010_channels[] = { 7778c2ecf20Sopenharmony_ci { 7788c2ecf20Sopenharmony_ci .type = IIO_LIGHT, 7798c2ecf20Sopenharmony_ci .scan_index = -1, 7808c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 7818c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SCALE), 7828c2ecf20Sopenharmony_ci }, { 7838c2ecf20Sopenharmony_ci .type = IIO_PROXIMITY, 7848c2ecf20Sopenharmony_ci .scan_index = 0, 7858c2ecf20Sopenharmony_ci .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 7868c2ecf20Sopenharmony_ci BIT(IIO_CHAN_INFO_SAMP_FREQ), 7878c2ecf20Sopenharmony_ci .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 7888c2ecf20Sopenharmony_ci .event_spec = vcnl4000_event_spec, 7898c2ecf20Sopenharmony_ci .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 7908c2ecf20Sopenharmony_ci .ext_info = vcnl4000_ext_info, 7918c2ecf20Sopenharmony_ci .scan_type = { 7928c2ecf20Sopenharmony_ci .sign = 'u', 7938c2ecf20Sopenharmony_ci .realbits = 16, 7948c2ecf20Sopenharmony_ci .storagebits = 16, 7958c2ecf20Sopenharmony_ci .endianness = IIO_CPU, 7968c2ecf20Sopenharmony_ci }, 7978c2ecf20Sopenharmony_ci }, 7988c2ecf20Sopenharmony_ci IIO_CHAN_SOFT_TIMESTAMP(1), 7998c2ecf20Sopenharmony_ci}; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic const struct iio_info vcnl4000_info = { 8028c2ecf20Sopenharmony_ci .read_raw = vcnl4000_read_raw, 8038c2ecf20Sopenharmony_ci}; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cistatic const struct iio_info vcnl4010_info = { 8068c2ecf20Sopenharmony_ci .read_raw = vcnl4010_read_raw, 8078c2ecf20Sopenharmony_ci .read_avail = vcnl4010_read_avail, 8088c2ecf20Sopenharmony_ci .write_raw = vcnl4010_write_raw, 8098c2ecf20Sopenharmony_ci .read_event_value = vcnl4010_read_event, 8108c2ecf20Sopenharmony_ci .write_event_value = vcnl4010_write_event, 8118c2ecf20Sopenharmony_ci .read_event_config = vcnl4010_read_event_config, 8128c2ecf20Sopenharmony_ci .write_event_config = vcnl4010_write_event_config, 8138c2ecf20Sopenharmony_ci}; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_cistatic const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 8168c2ecf20Sopenharmony_ci [VCNL4000] = { 8178c2ecf20Sopenharmony_ci .prod = "VCNL4000", 8188c2ecf20Sopenharmony_ci .init = vcnl4000_init, 8198c2ecf20Sopenharmony_ci .measure_light = vcnl4000_measure_light, 8208c2ecf20Sopenharmony_ci .measure_proximity = vcnl4000_measure_proximity, 8218c2ecf20Sopenharmony_ci .set_power_state = vcnl4000_set_power_state, 8228c2ecf20Sopenharmony_ci .channels = vcnl4000_channels, 8238c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(vcnl4000_channels), 8248c2ecf20Sopenharmony_ci .info = &vcnl4000_info, 8258c2ecf20Sopenharmony_ci .irq_support = false, 8268c2ecf20Sopenharmony_ci }, 8278c2ecf20Sopenharmony_ci [VCNL4010] = { 8288c2ecf20Sopenharmony_ci .prod = "VCNL4010/4020", 8298c2ecf20Sopenharmony_ci .init = vcnl4000_init, 8308c2ecf20Sopenharmony_ci .measure_light = vcnl4000_measure_light, 8318c2ecf20Sopenharmony_ci .measure_proximity = vcnl4000_measure_proximity, 8328c2ecf20Sopenharmony_ci .set_power_state = vcnl4000_set_power_state, 8338c2ecf20Sopenharmony_ci .channels = vcnl4010_channels, 8348c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(vcnl4010_channels), 8358c2ecf20Sopenharmony_ci .info = &vcnl4010_info, 8368c2ecf20Sopenharmony_ci .irq_support = true, 8378c2ecf20Sopenharmony_ci }, 8388c2ecf20Sopenharmony_ci [VCNL4040] = { 8398c2ecf20Sopenharmony_ci .prod = "VCNL4040", 8408c2ecf20Sopenharmony_ci .init = vcnl4200_init, 8418c2ecf20Sopenharmony_ci .measure_light = vcnl4200_measure_light, 8428c2ecf20Sopenharmony_ci .measure_proximity = vcnl4200_measure_proximity, 8438c2ecf20Sopenharmony_ci .set_power_state = vcnl4200_set_power_state, 8448c2ecf20Sopenharmony_ci .channels = vcnl4000_channels, 8458c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(vcnl4000_channels), 8468c2ecf20Sopenharmony_ci .info = &vcnl4000_info, 8478c2ecf20Sopenharmony_ci .irq_support = false, 8488c2ecf20Sopenharmony_ci }, 8498c2ecf20Sopenharmony_ci [VCNL4200] = { 8508c2ecf20Sopenharmony_ci .prod = "VCNL4200", 8518c2ecf20Sopenharmony_ci .init = vcnl4200_init, 8528c2ecf20Sopenharmony_ci .measure_light = vcnl4200_measure_light, 8538c2ecf20Sopenharmony_ci .measure_proximity = vcnl4200_measure_proximity, 8548c2ecf20Sopenharmony_ci .set_power_state = vcnl4200_set_power_state, 8558c2ecf20Sopenharmony_ci .channels = vcnl4000_channels, 8568c2ecf20Sopenharmony_ci .num_channels = ARRAY_SIZE(vcnl4000_channels), 8578c2ecf20Sopenharmony_ci .info = &vcnl4000_info, 8588c2ecf20Sopenharmony_ci .irq_support = false, 8598c2ecf20Sopenharmony_ci }, 8608c2ecf20Sopenharmony_ci}; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic irqreturn_t vcnl4010_irq_thread(int irq, void *p) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = p; 8658c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 8668c2ecf20Sopenharmony_ci unsigned long isr; 8678c2ecf20Sopenharmony_ci int ret; 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 8708c2ecf20Sopenharmony_ci if (ret < 0) 8718c2ecf20Sopenharmony_ci goto end; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci isr = ret; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (isr & VCNL4010_INT_THR) { 8768c2ecf20Sopenharmony_ci if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 8778c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 8788c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE( 8798c2ecf20Sopenharmony_ci IIO_PROXIMITY, 8808c2ecf20Sopenharmony_ci 1, 8818c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 8828c2ecf20Sopenharmony_ci IIO_EV_DIR_FALLING), 8838c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 8878c2ecf20Sopenharmony_ci iio_push_event(indio_dev, 8888c2ecf20Sopenharmony_ci IIO_UNMOD_EVENT_CODE( 8898c2ecf20Sopenharmony_ci IIO_PROXIMITY, 8908c2ecf20Sopenharmony_ci 1, 8918c2ecf20Sopenharmony_ci IIO_EV_TYPE_THRESH, 8928c2ecf20Sopenharmony_ci IIO_EV_DIR_RISING), 8938c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 8978c2ecf20Sopenharmony_ci isr & VCNL4010_INT_THR); 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 9018c2ecf20Sopenharmony_ci iio_trigger_poll_chained(indio_dev->trig); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ciend: 9048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_cistatic irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct iio_poll_func *pf = p; 9108c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = pf->indio_dev; 9118c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 9128c2ecf20Sopenharmony_ci const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 9138c2ecf20Sopenharmony_ci u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 9148c2ecf20Sopenharmony_ci bool data_read = false; 9158c2ecf20Sopenharmony_ci unsigned long isr; 9168c2ecf20Sopenharmony_ci int val = 0; 9178c2ecf20Sopenharmony_ci int ret; 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 9208c2ecf20Sopenharmony_ci if (ret < 0) 9218c2ecf20Sopenharmony_ci goto end; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci isr = ret; 9248c2ecf20Sopenharmony_ci 9258c2ecf20Sopenharmony_ci if (test_bit(0, active_scan_mask)) { 9268c2ecf20Sopenharmony_ci if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 9278c2ecf20Sopenharmony_ci ret = vcnl4000_read_data(data, 9288c2ecf20Sopenharmony_ci VCNL4000_PS_RESULT_HI, 9298c2ecf20Sopenharmony_ci &val); 9308c2ecf20Sopenharmony_ci if (ret < 0) 9318c2ecf20Sopenharmony_ci goto end; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci buffer[0] = val; 9348c2ecf20Sopenharmony_ci data_read = true; 9358c2ecf20Sopenharmony_ci } 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 9398c2ecf20Sopenharmony_ci isr & VCNL4010_INT_DRDY); 9408c2ecf20Sopenharmony_ci if (ret < 0) 9418c2ecf20Sopenharmony_ci goto end; 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci if (!data_read) 9448c2ecf20Sopenharmony_ci goto end; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci iio_push_to_buffers_with_timestamp(indio_dev, buffer, 9478c2ecf20Sopenharmony_ci iio_get_time_ns(indio_dev)); 9488c2ecf20Sopenharmony_ci 9498c2ecf20Sopenharmony_ciend: 9508c2ecf20Sopenharmony_ci iio_trigger_notify_done(indio_dev->trig); 9518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 9578c2ecf20Sopenharmony_ci int ret; 9588c2ecf20Sopenharmony_ci int cmd; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci /* Do not enable the buffer if we are already capturing events. */ 9618c2ecf20Sopenharmony_ci if (vcnl4010_is_in_periodic_mode(data)) 9628c2ecf20Sopenharmony_ci return -EBUSY; 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 9658c2ecf20Sopenharmony_ci VCNL4010_INT_PROX_EN); 9668c2ecf20Sopenharmony_ci if (ret < 0) 9678c2ecf20Sopenharmony_ci return ret; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 9708c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 9768c2ecf20Sopenharmony_ci int ret; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 9798c2ecf20Sopenharmony_ci if (ret < 0) 9808c2ecf20Sopenharmony_ci return ret; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 9838c2ecf20Sopenharmony_ci} 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_cistatic const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 9868c2ecf20Sopenharmony_ci .postenable = &vcnl4010_buffer_postenable, 9878c2ecf20Sopenharmony_ci .predisable = &vcnl4010_buffer_predisable, 9888c2ecf20Sopenharmony_ci}; 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_cistatic const struct iio_trigger_ops vcnl4010_trigger_ops = { 9918c2ecf20Sopenharmony_ci .validate_device = iio_trigger_validate_own_device, 9928c2ecf20Sopenharmony_ci}; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 9978c2ecf20Sopenharmony_ci struct i2c_client *client = data->client; 9988c2ecf20Sopenharmony_ci struct iio_trigger *trigger; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 10018c2ecf20Sopenharmony_ci indio_dev->name, indio_dev->id); 10028c2ecf20Sopenharmony_ci if (!trigger) 10038c2ecf20Sopenharmony_ci return -ENOMEM; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci trigger->dev.parent = &client->dev; 10068c2ecf20Sopenharmony_ci trigger->ops = &vcnl4010_trigger_ops; 10078c2ecf20Sopenharmony_ci iio_trigger_set_drvdata(trigger, indio_dev); 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci return devm_iio_trigger_register(&client->dev, trigger); 10108c2ecf20Sopenharmony_ci} 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_cistatic int vcnl4000_probe(struct i2c_client *client, 10138c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 10148c2ecf20Sopenharmony_ci{ 10158c2ecf20Sopenharmony_ci struct vcnl4000_data *data; 10168c2ecf20Sopenharmony_ci struct iio_dev *indio_dev; 10178c2ecf20Sopenharmony_ci int ret; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 10208c2ecf20Sopenharmony_ci if (!indio_dev) 10218c2ecf20Sopenharmony_ci return -ENOMEM; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci data = iio_priv(indio_dev); 10248c2ecf20Sopenharmony_ci i2c_set_clientdata(client, indio_dev); 10258c2ecf20Sopenharmony_ci data->client = client; 10268c2ecf20Sopenharmony_ci data->id = id->driver_data; 10278c2ecf20Sopenharmony_ci data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci ret = data->chip_spec->init(data); 10308c2ecf20Sopenharmony_ci if (ret < 0) 10318c2ecf20Sopenharmony_ci return ret; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 10348c2ecf20Sopenharmony_ci data->chip_spec->prod, data->rev); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci if (device_property_read_u32(&client->dev, "proximity-near-level", 10378c2ecf20Sopenharmony_ci &data->near_level)) 10388c2ecf20Sopenharmony_ci data->near_level = 0; 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci indio_dev->info = data->chip_spec->info; 10418c2ecf20Sopenharmony_ci indio_dev->channels = data->chip_spec->channels; 10428c2ecf20Sopenharmony_ci indio_dev->num_channels = data->chip_spec->num_channels; 10438c2ecf20Sopenharmony_ci indio_dev->name = VCNL4000_DRV_NAME; 10448c2ecf20Sopenharmony_ci indio_dev->modes = INDIO_DIRECT_MODE; 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci if (client->irq && data->chip_spec->irq_support) { 10478c2ecf20Sopenharmony_ci ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 10488c2ecf20Sopenharmony_ci NULL, 10498c2ecf20Sopenharmony_ci vcnl4010_trigger_handler, 10508c2ecf20Sopenharmony_ci &vcnl4010_buffer_ops); 10518c2ecf20Sopenharmony_ci if (ret < 0) { 10528c2ecf20Sopenharmony_ci dev_err(&client->dev, 10538c2ecf20Sopenharmony_ci "unable to setup iio triggered buffer\n"); 10548c2ecf20Sopenharmony_ci return ret; 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci ret = devm_request_threaded_irq(&client->dev, client->irq, 10588c2ecf20Sopenharmony_ci NULL, vcnl4010_irq_thread, 10598c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | 10608c2ecf20Sopenharmony_ci IRQF_ONESHOT, 10618c2ecf20Sopenharmony_ci "vcnl4010_irq", 10628c2ecf20Sopenharmony_ci indio_dev); 10638c2ecf20Sopenharmony_ci if (ret < 0) { 10648c2ecf20Sopenharmony_ci dev_err(&client->dev, "irq request failed\n"); 10658c2ecf20Sopenharmony_ci return ret; 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci ret = vcnl4010_probe_trigger(indio_dev); 10698c2ecf20Sopenharmony_ci if (ret < 0) 10708c2ecf20Sopenharmony_ci return ret; 10718c2ecf20Sopenharmony_ci } 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ret = pm_runtime_set_active(&client->dev); 10748c2ecf20Sopenharmony_ci if (ret < 0) 10758c2ecf20Sopenharmony_ci goto fail_poweroff; 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci ret = iio_device_register(indio_dev); 10788c2ecf20Sopenharmony_ci if (ret < 0) 10798c2ecf20Sopenharmony_ci goto fail_poweroff; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci pm_runtime_enable(&client->dev); 10828c2ecf20Sopenharmony_ci pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 10838c2ecf20Sopenharmony_ci pm_runtime_use_autosuspend(&client->dev); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci return 0; 10868c2ecf20Sopenharmony_cifail_poweroff: 10878c2ecf20Sopenharmony_ci data->chip_spec->set_power_state(data, false); 10888c2ecf20Sopenharmony_ci return ret; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic const struct of_device_id vcnl_4000_of_match[] = { 10928c2ecf20Sopenharmony_ci { 10938c2ecf20Sopenharmony_ci .compatible = "vishay,vcnl4000", 10948c2ecf20Sopenharmony_ci .data = (void *)VCNL4000, 10958c2ecf20Sopenharmony_ci }, 10968c2ecf20Sopenharmony_ci { 10978c2ecf20Sopenharmony_ci .compatible = "vishay,vcnl4010", 10988c2ecf20Sopenharmony_ci .data = (void *)VCNL4010, 10998c2ecf20Sopenharmony_ci }, 11008c2ecf20Sopenharmony_ci { 11018c2ecf20Sopenharmony_ci .compatible = "vishay,vcnl4020", 11028c2ecf20Sopenharmony_ci .data = (void *)VCNL4010, 11038c2ecf20Sopenharmony_ci }, 11048c2ecf20Sopenharmony_ci { 11058c2ecf20Sopenharmony_ci .compatible = "vishay,vcnl4040", 11068c2ecf20Sopenharmony_ci .data = (void *)VCNL4040, 11078c2ecf20Sopenharmony_ci }, 11088c2ecf20Sopenharmony_ci { 11098c2ecf20Sopenharmony_ci .compatible = "vishay,vcnl4200", 11108c2ecf20Sopenharmony_ci .data = (void *)VCNL4200, 11118c2ecf20Sopenharmony_ci }, 11128c2ecf20Sopenharmony_ci {}, 11138c2ecf20Sopenharmony_ci}; 11148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_cistatic int vcnl4000_remove(struct i2c_client *client) 11178c2ecf20Sopenharmony_ci{ 11188c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(client); 11198c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci pm_runtime_dont_use_autosuspend(&client->dev); 11228c2ecf20Sopenharmony_ci pm_runtime_disable(&client->dev); 11238c2ecf20Sopenharmony_ci iio_device_unregister(indio_dev); 11248c2ecf20Sopenharmony_ci pm_runtime_set_suspended(&client->dev); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci return data->chip_spec->set_power_state(data, false); 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_cistatic int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) 11308c2ecf20Sopenharmony_ci{ 11318c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11328c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci return data->chip_spec->set_power_state(data, false); 11358c2ecf20Sopenharmony_ci} 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_cistatic int __maybe_unused vcnl4000_runtime_resume(struct device *dev) 11388c2ecf20Sopenharmony_ci{ 11398c2ecf20Sopenharmony_ci struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11408c2ecf20Sopenharmony_ci struct vcnl4000_data *data = iio_priv(indio_dev); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci return data->chip_spec->set_power_state(data, true); 11438c2ecf20Sopenharmony_ci} 11448c2ecf20Sopenharmony_ci 11458c2ecf20Sopenharmony_cistatic const struct dev_pm_ops vcnl4000_pm_ops = { 11468c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 11478c2ecf20Sopenharmony_ci pm_runtime_force_resume) 11488c2ecf20Sopenharmony_ci SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, 11498c2ecf20Sopenharmony_ci vcnl4000_runtime_resume, NULL) 11508c2ecf20Sopenharmony_ci}; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cistatic struct i2c_driver vcnl4000_driver = { 11538c2ecf20Sopenharmony_ci .driver = { 11548c2ecf20Sopenharmony_ci .name = VCNL4000_DRV_NAME, 11558c2ecf20Sopenharmony_ci .pm = &vcnl4000_pm_ops, 11568c2ecf20Sopenharmony_ci .of_match_table = vcnl_4000_of_match, 11578c2ecf20Sopenharmony_ci }, 11588c2ecf20Sopenharmony_ci .probe = vcnl4000_probe, 11598c2ecf20Sopenharmony_ci .id_table = vcnl4000_id, 11608c2ecf20Sopenharmony_ci .remove = vcnl4000_remove, 11618c2ecf20Sopenharmony_ci}; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_cimodule_i2c_driver(vcnl4000_driver); 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ciMODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 11668c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 11678c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 11688c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1169