136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 262a1efb9SPeter Meerwald /* 35a441aadSAngus Ainslie (Purism) * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient 4d978bfddSPeter Meerwald-Stadler * light and proximity sensor 562a1efb9SPeter Meerwald * 662a1efb9SPeter Meerwald * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net> 75a441aadSAngus Ainslie (Purism) * Copyright 2019 Pursim SPC 8*8fe78d52SMathieu Othacehe * Copyright 2020 Mathieu Othacehe <m.othacehe@gmail.com> 962a1efb9SPeter Meerwald * 10be38866fSTomas Novotny * IIO driver for: 11be38866fSTomas Novotny * VCNL4000/10/20 (7-bit I2C slave address 0x13) 125a441aadSAngus Ainslie (Purism) * VCNL4040 (7-bit I2C slave address 0x60) 13be38866fSTomas Novotny * VCNL4200 (7-bit I2C slave address 0x51) 1462a1efb9SPeter Meerwald * 1562a1efb9SPeter Meerwald * TODO: 1662a1efb9SPeter Meerwald * allow to adjust IR current 17*8fe78d52SMathieu Othacehe * interrupts (VCNL4040, VCNL4200) 1862a1efb9SPeter Meerwald */ 1962a1efb9SPeter Meerwald 2062a1efb9SPeter Meerwald #include <linux/module.h> 2162a1efb9SPeter Meerwald #include <linux/i2c.h> 2262a1efb9SPeter Meerwald #include <linux/err.h> 2362a1efb9SPeter Meerwald #include <linux/delay.h> 245e00708dSGuido Günther #include <linux/pm_runtime.h> 25d35567fcSMathieu Othacehe #include <linux/interrupt.h> 2662a1efb9SPeter Meerwald 27*8fe78d52SMathieu Othacehe #include <linux/iio/buffer.h> 28d35567fcSMathieu Othacehe #include <linux/iio/events.h> 2962a1efb9SPeter Meerwald #include <linux/iio/iio.h> 3062a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 31*8fe78d52SMathieu Othacehe #include <linux/iio/trigger.h> 32*8fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 33*8fe78d52SMathieu Othacehe #include <linux/iio/triggered_buffer.h> 3462a1efb9SPeter Meerwald 3562a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 361ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 371ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 385a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 39be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 4062a1efb9SPeter Meerwald 4162a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 4262a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 43d35567fcSMathieu Othacehe #define VCNL4010_PROX_RATE 0x82 /* Proximity rate */ 4462a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 4562a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 46d35567fcSMathieu Othacehe #define VCNL4010_ALS_PARAM 0x84 /* ALS rate */ 4762a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 4862a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 4962a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 5062a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 5162a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 52d35567fcSMathieu Othacehe #define VCNL4010_INT_CTRL 0x89 /* Interrupt control */ 5362a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 54d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */ 55d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */ 56d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */ 57d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */ 58d35567fcSMathieu Othacehe #define VCNL4010_ISR 0x8e /* Interrupt status */ 5962a1efb9SPeter Meerwald 60be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 61be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 62be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 63be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 64be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 65be38866fSTomas Novotny 665a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 675a441aadSAngus Ainslie (Purism) 6862a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 69ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 70ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 71ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 72ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 73d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 74d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 75d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 76d35567fcSMathieu Othacehe 77d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 78d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 79d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 80d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 81d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 82d35567fcSMathieu Othacehe 83d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 84d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 85d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 86d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 87d35567fcSMathieu Othacehe 88d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 89d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 90d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 91d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 92d35567fcSMathieu Othacehe 93f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 94f6889c1bSMathieu Othacehe {1, 950000}, 95f6889c1bSMathieu Othacehe {3, 906250}, 96f6889c1bSMathieu Othacehe {7, 812500}, 97f6889c1bSMathieu Othacehe {16, 625000}, 98f6889c1bSMathieu Othacehe {31, 250000}, 99f6889c1bSMathieu Othacehe {62, 500000}, 100f6889c1bSMathieu Othacehe {125, 0}, 101f6889c1bSMathieu Othacehe {250, 0}, 102f6889c1bSMathieu Othacehe }; 10362a1efb9SPeter Meerwald 1045e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1055e00708dSGuido Günther 1061ebc787aSTomas Novotny enum vcnl4000_device_ids { 1071ebc787aSTomas Novotny VCNL4000, 10850c50b97STomas Novotny VCNL4010, 1095a441aadSAngus Ainslie (Purism) VCNL4040, 110be38866fSTomas Novotny VCNL4200, 111be38866fSTomas Novotny }; 112be38866fSTomas Novotny 113be38866fSTomas Novotny struct vcnl4200_channel { 114be38866fSTomas Novotny u8 reg; 115be38866fSTomas Novotny ktime_t last_measurement; 116be38866fSTomas Novotny ktime_t sampling_rate; 117be38866fSTomas Novotny struct mutex lock; 1181ebc787aSTomas Novotny }; 1191ebc787aSTomas Novotny 12062a1efb9SPeter Meerwald struct vcnl4000_data { 12162a1efb9SPeter Meerwald struct i2c_client *client; 1221ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1231ebc787aSTomas Novotny int rev; 1241ebc787aSTomas Novotny int al_scale; 1251ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 126be38866fSTomas Novotny struct mutex vcnl4000_lock; 127be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 128be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 129f5a98e1fSGuido Günther uint32_t near_level; 13062a1efb9SPeter Meerwald }; 13162a1efb9SPeter Meerwald 1321ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1331ebc787aSTomas Novotny const char *prod; 134d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 135d35567fcSMathieu Othacehe const int num_channels; 136d35567fcSMathieu Othacehe const struct iio_info *info; 137d35567fcSMathieu Othacehe bool irq_support; 1381ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1391ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1401ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1415e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 1421ebc787aSTomas Novotny }; 1431ebc787aSTomas Novotny 14462a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1451ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 14650c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 14750c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1485a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 149be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 15062a1efb9SPeter Meerwald { } 15162a1efb9SPeter Meerwald }; 15262a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 15362a1efb9SPeter Meerwald 1545e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1555e00708dSGuido Günther { 1565e00708dSGuido Günther /* no suspend op */ 1575e00708dSGuido Günther return 0; 1585e00708dSGuido Günther } 1595e00708dSGuido Günther 1601ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1611ebc787aSTomas Novotny { 1621ebc787aSTomas Novotny int ret, prod_id; 1631ebc787aSTomas Novotny 1641ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1651ebc787aSTomas Novotny if (ret < 0) 1661ebc787aSTomas Novotny return ret; 1671ebc787aSTomas Novotny 1681ebc787aSTomas Novotny prod_id = ret >> 4; 16958bf9aceSTomas Novotny switch (prod_id) { 17058bf9aceSTomas Novotny case VCNL4000_PROD_ID: 17158bf9aceSTomas Novotny if (data->id != VCNL4000) 17258bf9aceSTomas Novotny dev_warn(&data->client->dev, 17358bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 17458bf9aceSTomas Novotny break; 17558bf9aceSTomas Novotny case VCNL4010_PROD_ID: 17658bf9aceSTomas Novotny if (data->id != VCNL4010) 17758bf9aceSTomas Novotny dev_warn(&data->client->dev, 17858bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 17958bf9aceSTomas Novotny break; 18058bf9aceSTomas Novotny default: 1811ebc787aSTomas Novotny return -ENODEV; 18258bf9aceSTomas Novotny } 1831ebc787aSTomas Novotny 1841ebc787aSTomas Novotny data->rev = ret & 0xf; 1851ebc787aSTomas Novotny data->al_scale = 250000; 186be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 187be38866fSTomas Novotny 1885e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 189be38866fSTomas Novotny }; 190be38866fSTomas Novotny 1915e00708dSGuido Günther static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 1925e00708dSGuido Günther { 1935e00708dSGuido Günther u16 val = on ? 0 /* power on */ : 1 /* shut down */; 1945e00708dSGuido Günther int ret; 1955e00708dSGuido Günther 1965e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); 1975e00708dSGuido Günther if (ret < 0) 1985e00708dSGuido Günther return ret; 1995e00708dSGuido Günther 2005e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 2015e00708dSGuido Günther if (ret < 0) 2025e00708dSGuido Günther return ret; 2035e00708dSGuido Günther 2045e00708dSGuido Günther if (on) { 2055e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2065e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2075e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2085e00708dSGuido Günther } 2095e00708dSGuido Günther 2105e00708dSGuido Günther return 0; 2115e00708dSGuido Günther } 2125e00708dSGuido Günther 213be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 214be38866fSTomas Novotny { 2155a441aadSAngus Ainslie (Purism) int ret, id; 216be38866fSTomas Novotny 217be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 218be38866fSTomas Novotny if (ret < 0) 219be38866fSTomas Novotny return ret; 220be38866fSTomas Novotny 2215a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2225a441aadSAngus Ainslie (Purism) 2235a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 2245a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 2255a441aadSAngus Ainslie (Purism) if (ret < 0) 2265a441aadSAngus Ainslie (Purism) return ret; 2275a441aadSAngus Ainslie (Purism) 2285a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2295a441aadSAngus Ainslie (Purism) 2305a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 231be38866fSTomas Novotny return -ENODEV; 2325a441aadSAngus Ainslie (Purism) } 2335a441aadSAngus Ainslie (Purism) 2345a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 235be38866fSTomas Novotny 236be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 237be38866fSTomas Novotny 238be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 239be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 2405a441aadSAngus Ainslie (Purism) switch (id) { 2415a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 242b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 243b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 244b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 245b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 246bc80573eSGuido Günther data->al_scale = 24000; 2475a441aadSAngus Ainslie (Purism) break; 2485a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 2492ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 2502ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 2512ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 2522ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 253bc80573eSGuido Günther data->al_scale = 120000; 2545a441aadSAngus Ainslie (Purism) break; 2555a441aadSAngus Ainslie (Purism) } 256be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 257be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 2581ebc787aSTomas Novotny 2595e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 2605e00708dSGuido Günther if (ret < 0) 2615e00708dSGuido Günther return ret; 2625e00708dSGuido Günther 2631ebc787aSTomas Novotny return 0; 2641ebc787aSTomas Novotny }; 2651ebc787aSTomas Novotny 266816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 267816956c3SMathieu Othacehe { 268816956c3SMathieu Othacehe s32 ret; 269816956c3SMathieu Othacehe 270816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 271816956c3SMathieu Othacehe if (ret < 0) 272816956c3SMathieu Othacehe return ret; 273816956c3SMathieu Othacehe 274816956c3SMathieu Othacehe *val = ret; 275816956c3SMathieu Othacehe return 0; 276816956c3SMathieu Othacehe } 277816956c3SMathieu Othacehe 278816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 279816956c3SMathieu Othacehe { 280816956c3SMathieu Othacehe if (val > U16_MAX) 281816956c3SMathieu Othacehe return -ERANGE; 282816956c3SMathieu Othacehe 283816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 284816956c3SMathieu Othacehe } 285816956c3SMathieu Othacehe 286816956c3SMathieu Othacehe 28762a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 28862a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 28962a1efb9SPeter Meerwald { 29062a1efb9SPeter Meerwald int tries = 20; 29162a1efb9SPeter Meerwald int ret; 29262a1efb9SPeter Meerwald 293be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 294ff34ed6dSPeter Meerwald-Stadler 29562a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 29662a1efb9SPeter Meerwald req_mask); 29762a1efb9SPeter Meerwald if (ret < 0) 298ff34ed6dSPeter Meerwald-Stadler goto fail; 29962a1efb9SPeter Meerwald 30062a1efb9SPeter Meerwald /* wait for data to become ready */ 30162a1efb9SPeter Meerwald while (tries--) { 30262a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 30362a1efb9SPeter Meerwald if (ret < 0) 304ff34ed6dSPeter Meerwald-Stadler goto fail; 30562a1efb9SPeter Meerwald if (ret & rdy_mask) 30662a1efb9SPeter Meerwald break; 30762a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 30862a1efb9SPeter Meerwald } 30962a1efb9SPeter Meerwald 31062a1efb9SPeter Meerwald if (tries < 0) { 31162a1efb9SPeter Meerwald dev_err(&data->client->dev, 31262a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 313ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 314ff34ed6dSPeter Meerwald-Stadler goto fail; 31562a1efb9SPeter Meerwald } 31662a1efb9SPeter Meerwald 317816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 31862a1efb9SPeter Meerwald if (ret < 0) 319ff34ed6dSPeter Meerwald-Stadler goto fail; 32062a1efb9SPeter Meerwald 321be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 32262a1efb9SPeter Meerwald 32362a1efb9SPeter Meerwald return 0; 324ff34ed6dSPeter Meerwald-Stadler 325ff34ed6dSPeter Meerwald-Stadler fail: 326be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 327ff34ed6dSPeter Meerwald-Stadler return ret; 32862a1efb9SPeter Meerwald } 32962a1efb9SPeter Meerwald 330be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 331be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 332be38866fSTomas Novotny { 333be38866fSTomas Novotny int ret; 334be38866fSTomas Novotny s64 delta; 335be38866fSTomas Novotny ktime_t next_measurement; 336be38866fSTomas Novotny 337be38866fSTomas Novotny mutex_lock(&chan->lock); 338be38866fSTomas Novotny 339be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 340be38866fSTomas Novotny chan->sampling_rate); 341be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 342be38866fSTomas Novotny if (delta > 0) 343be38866fSTomas Novotny usleep_range(delta, delta + 500); 344be38866fSTomas Novotny chan->last_measurement = ktime_get(); 345be38866fSTomas Novotny 346be38866fSTomas Novotny mutex_unlock(&chan->lock); 347be38866fSTomas Novotny 348be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 349be38866fSTomas Novotny if (ret < 0) 350be38866fSTomas Novotny return ret; 351be38866fSTomas Novotny 352be38866fSTomas Novotny *val = ret; 353be38866fSTomas Novotny 354be38866fSTomas Novotny return 0; 355be38866fSTomas Novotny } 356be38866fSTomas Novotny 3571ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 3581ebc787aSTomas Novotny { 3591ebc787aSTomas Novotny return vcnl4000_measure(data, 3601ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 3611ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 3621ebc787aSTomas Novotny } 3631ebc787aSTomas Novotny 364be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 365be38866fSTomas Novotny { 366be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 367be38866fSTomas Novotny } 368be38866fSTomas Novotny 3691ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 3701ebc787aSTomas Novotny { 3711ebc787aSTomas Novotny return vcnl4000_measure(data, 3721ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 3731ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 3741ebc787aSTomas Novotny } 3751ebc787aSTomas Novotny 376be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 377be38866fSTomas Novotny { 378be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 379be38866fSTomas Novotny } 380be38866fSTomas Novotny 381f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 382f6889c1bSMathieu Othacehe int *val2) 383f6889c1bSMathieu Othacehe { 384f6889c1bSMathieu Othacehe int ret; 385f6889c1bSMathieu Othacehe 386f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 387f6889c1bSMathieu Othacehe if (ret < 0) 388f6889c1bSMathieu Othacehe return ret; 389f6889c1bSMathieu Othacehe 390f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 391f6889c1bSMathieu Othacehe return -EINVAL; 392f6889c1bSMathieu Othacehe 393f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 394f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 395f6889c1bSMathieu Othacehe 396f6889c1bSMathieu Othacehe return 0; 397f6889c1bSMathieu Othacehe } 398f6889c1bSMathieu Othacehe 399d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 400f5a98e1fSGuido Günther { 401d35567fcSMathieu Othacehe int ret; 402f5a98e1fSGuido Günther 403d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 404d35567fcSMathieu Othacehe if (ret < 0) 405d35567fcSMathieu Othacehe return false; 406d35567fcSMathieu Othacehe 407d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 408f5a98e1fSGuido Günther } 409f5a98e1fSGuido Günther 4105e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4115e00708dSGuido Günther { 4125e00708dSGuido Günther struct device *dev = &data->client->dev; 4135e00708dSGuido Günther int ret; 4145e00708dSGuido Günther 4155e00708dSGuido Günther if (on) { 4165e00708dSGuido Günther ret = pm_runtime_get_sync(dev); 4175e00708dSGuido Günther if (ret < 0) 4185e00708dSGuido Günther pm_runtime_put_noidle(dev); 4195e00708dSGuido Günther } else { 4205e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4215e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4225e00708dSGuido Günther } 4235e00708dSGuido Günther 4245e00708dSGuido Günther return ret; 4255e00708dSGuido Günther } 4265e00708dSGuido Günther 42762a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 42862a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 42962a1efb9SPeter Meerwald int *val, int *val2, long mask) 43062a1efb9SPeter Meerwald { 4315d693139SPeter Meerwald-Stadler int ret; 43262a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 43362a1efb9SPeter Meerwald 43462a1efb9SPeter Meerwald switch (mask) { 43562a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 4365e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 4375e00708dSGuido Günther if (ret < 0) 4385e00708dSGuido Günther return ret; 4395e00708dSGuido Günther 44062a1efb9SPeter Meerwald switch (chan->type) { 44162a1efb9SPeter Meerwald case IIO_LIGHT: 4421ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 4434a818643SGuido Günther if (!ret) 4444a818643SGuido Günther ret = IIO_VAL_INT; 4454a818643SGuido Günther break; 44662a1efb9SPeter Meerwald case IIO_PROXIMITY: 4471ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 4484a818643SGuido Günther if (!ret) 4494a818643SGuido Günther ret = IIO_VAL_INT; 4504a818643SGuido Günther break; 45162a1efb9SPeter Meerwald default: 4524a818643SGuido Günther ret = -EINVAL; 45362a1efb9SPeter Meerwald } 4545e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 4554a818643SGuido Günther return ret; 45662a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 4575d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 4585d693139SPeter Meerwald-Stadler return -EINVAL; 4595d693139SPeter Meerwald-Stadler 46062a1efb9SPeter Meerwald *val = 0; 4611ebc787aSTomas Novotny *val2 = data->al_scale; 4625d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 46362a1efb9SPeter Meerwald default: 4645d693139SPeter Meerwald-Stadler return -EINVAL; 46562a1efb9SPeter Meerwald } 46662a1efb9SPeter Meerwald } 46762a1efb9SPeter Meerwald 468d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 469d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 470d35567fcSMathieu Othacehe int *val, int *val2, long mask) 471d35567fcSMathieu Othacehe { 472d35567fcSMathieu Othacehe int ret; 473d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 474d35567fcSMathieu Othacehe 475d35567fcSMathieu Othacehe switch (mask) { 476d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 477d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 478d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 479d35567fcSMathieu Othacehe if (ret) 480d35567fcSMathieu Othacehe return ret; 481d35567fcSMathieu Othacehe 482d35567fcSMathieu Othacehe /* Protect against event capture. */ 483d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 484d35567fcSMathieu Othacehe ret = -EBUSY; 485d35567fcSMathieu Othacehe } else { 486d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 487d35567fcSMathieu Othacehe mask); 488d35567fcSMathieu Othacehe } 489d35567fcSMathieu Othacehe 490d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 491d35567fcSMathieu Othacehe return ret; 492f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 493f6889c1bSMathieu Othacehe switch (chan->type) { 494f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 495f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 496f6889c1bSMathieu Othacehe if (ret < 0) 497f6889c1bSMathieu Othacehe return ret; 498f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 499d35567fcSMathieu Othacehe default: 500d35567fcSMathieu Othacehe return -EINVAL; 501d35567fcSMathieu Othacehe } 502f6889c1bSMathieu Othacehe default: 503f6889c1bSMathieu Othacehe return -EINVAL; 504f6889c1bSMathieu Othacehe } 505f6889c1bSMathieu Othacehe } 506f6889c1bSMathieu Othacehe 507f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 508f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 509f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 510f6889c1bSMathieu Othacehe long mask) 511f6889c1bSMathieu Othacehe { 512f6889c1bSMathieu Othacehe switch (mask) { 513f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 514f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 515f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 516f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 517f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 518f6889c1bSMathieu Othacehe default: 519f6889c1bSMathieu Othacehe return -EINVAL; 520f6889c1bSMathieu Othacehe } 521f6889c1bSMathieu Othacehe } 522f6889c1bSMathieu Othacehe 523f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 524f6889c1bSMathieu Othacehe int val2) 525f6889c1bSMathieu Othacehe { 526f6889c1bSMathieu Othacehe unsigned int i; 527f6889c1bSMathieu Othacehe int index = -1; 528f6889c1bSMathieu Othacehe 529f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 530f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 531f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 532f6889c1bSMathieu Othacehe index = i; 533f6889c1bSMathieu Othacehe break; 534f6889c1bSMathieu Othacehe } 535f6889c1bSMathieu Othacehe } 536f6889c1bSMathieu Othacehe 537f6889c1bSMathieu Othacehe if (index < 0) 538f6889c1bSMathieu Othacehe return -EINVAL; 539f6889c1bSMathieu Othacehe 540f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 541f6889c1bSMathieu Othacehe index); 542f6889c1bSMathieu Othacehe } 543f6889c1bSMathieu Othacehe 544f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 545f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 546f6889c1bSMathieu Othacehe int val, int val2, long mask) 547f6889c1bSMathieu Othacehe { 548f6889c1bSMathieu Othacehe int ret; 549f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 550f6889c1bSMathieu Othacehe 551f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 552f6889c1bSMathieu Othacehe if (ret) 553f6889c1bSMathieu Othacehe return ret; 554f6889c1bSMathieu Othacehe 555f6889c1bSMathieu Othacehe /* Protect against event capture. */ 556f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 557f6889c1bSMathieu Othacehe ret = -EBUSY; 558f6889c1bSMathieu Othacehe goto end; 559f6889c1bSMathieu Othacehe } 560f6889c1bSMathieu Othacehe 561f6889c1bSMathieu Othacehe switch (mask) { 562f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 563f6889c1bSMathieu Othacehe switch (chan->type) { 564f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 565f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 566f6889c1bSMathieu Othacehe goto end; 567f6889c1bSMathieu Othacehe default: 568f6889c1bSMathieu Othacehe ret = -EINVAL; 569f6889c1bSMathieu Othacehe goto end; 570f6889c1bSMathieu Othacehe } 571f6889c1bSMathieu Othacehe default: 572f6889c1bSMathieu Othacehe ret = -EINVAL; 573f6889c1bSMathieu Othacehe goto end; 574f6889c1bSMathieu Othacehe } 575f6889c1bSMathieu Othacehe 576f6889c1bSMathieu Othacehe end: 577f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 578f6889c1bSMathieu Othacehe return ret; 579d35567fcSMathieu Othacehe } 580d35567fcSMathieu Othacehe 581d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 582d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 583d35567fcSMathieu Othacehe enum iio_event_type type, 584d35567fcSMathieu Othacehe enum iio_event_direction dir, 585d35567fcSMathieu Othacehe enum iio_event_info info, 586d35567fcSMathieu Othacehe int *val, int *val2) 587d35567fcSMathieu Othacehe { 588d35567fcSMathieu Othacehe int ret; 589d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 590d35567fcSMathieu Othacehe 591d35567fcSMathieu Othacehe switch (info) { 592d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 593d35567fcSMathieu Othacehe switch (dir) { 594d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 595d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 596d35567fcSMathieu Othacehe val); 597d35567fcSMathieu Othacehe if (ret < 0) 598d35567fcSMathieu Othacehe return ret; 599d35567fcSMathieu Othacehe return IIO_VAL_INT; 600d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 601d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 602d35567fcSMathieu Othacehe val); 603d35567fcSMathieu Othacehe if (ret < 0) 604d35567fcSMathieu Othacehe return ret; 605d35567fcSMathieu Othacehe return IIO_VAL_INT; 606d35567fcSMathieu Othacehe default: 607d35567fcSMathieu Othacehe return -EINVAL; 608d35567fcSMathieu Othacehe } 609d35567fcSMathieu Othacehe default: 610d35567fcSMathieu Othacehe return -EINVAL; 611d35567fcSMathieu Othacehe } 612d35567fcSMathieu Othacehe } 613d35567fcSMathieu Othacehe 614d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 615d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 616d35567fcSMathieu Othacehe enum iio_event_type type, 617d35567fcSMathieu Othacehe enum iio_event_direction dir, 618d35567fcSMathieu Othacehe enum iio_event_info info, 619d35567fcSMathieu Othacehe int val, int val2) 620d35567fcSMathieu Othacehe { 621d35567fcSMathieu Othacehe int ret; 622d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 623d35567fcSMathieu Othacehe 624d35567fcSMathieu Othacehe switch (info) { 625d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 626d35567fcSMathieu Othacehe switch (dir) { 627d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 628d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 629d35567fcSMathieu Othacehe val); 630d35567fcSMathieu Othacehe if (ret < 0) 631d35567fcSMathieu Othacehe return ret; 632d35567fcSMathieu Othacehe return IIO_VAL_INT; 633d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 634d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 635d35567fcSMathieu Othacehe val); 636d35567fcSMathieu Othacehe if (ret < 0) 637d35567fcSMathieu Othacehe return ret; 638d35567fcSMathieu Othacehe return IIO_VAL_INT; 639d35567fcSMathieu Othacehe default: 640d35567fcSMathieu Othacehe return -EINVAL; 641d35567fcSMathieu Othacehe } 642d35567fcSMathieu Othacehe default: 643d35567fcSMathieu Othacehe return -EINVAL; 644d35567fcSMathieu Othacehe } 645d35567fcSMathieu Othacehe } 646d35567fcSMathieu Othacehe 647d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 648d35567fcSMathieu Othacehe { 649d35567fcSMathieu Othacehe int ret; 650d35567fcSMathieu Othacehe 651d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 652d35567fcSMathieu Othacehe if (ret < 0) 653d35567fcSMathieu Othacehe return false; 654d35567fcSMathieu Othacehe 655d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 656d35567fcSMathieu Othacehe } 657d35567fcSMathieu Othacehe 658d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 659d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 660d35567fcSMathieu Othacehe enum iio_event_type type, 661d35567fcSMathieu Othacehe enum iio_event_direction dir) 662d35567fcSMathieu Othacehe { 663d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 664d35567fcSMathieu Othacehe 665d35567fcSMathieu Othacehe switch (chan->type) { 666d35567fcSMathieu Othacehe case IIO_PROXIMITY: 667d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 668d35567fcSMathieu Othacehe default: 669d35567fcSMathieu Othacehe return -EINVAL; 670d35567fcSMathieu Othacehe } 671d35567fcSMathieu Othacehe } 672d35567fcSMathieu Othacehe 673d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 674d35567fcSMathieu Othacehe { 675d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 676d35567fcSMathieu Othacehe int ret; 677d35567fcSMathieu Othacehe int icr; 678d35567fcSMathieu Othacehe int command; 679d35567fcSMathieu Othacehe 680d35567fcSMathieu Othacehe if (state) { 681d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 682d35567fcSMathieu Othacehe if (ret) 683d35567fcSMathieu Othacehe return ret; 684d35567fcSMathieu Othacehe 685d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 686d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 687d35567fcSMathieu Othacehe 688d35567fcSMathieu Othacehe /* 689d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 690d35567fcSMathieu Othacehe * default. 691d35567fcSMathieu Othacehe */ 692d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 693d35567fcSMathieu Othacehe } else { 694d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 695d35567fcSMathieu Othacehe return 0; 696d35567fcSMathieu Othacehe 697d35567fcSMathieu Othacehe command = 0; 698d35567fcSMathieu Othacehe icr = 0; 699d35567fcSMathieu Othacehe } 700d35567fcSMathieu Othacehe 701d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 702d35567fcSMathieu Othacehe command); 703d35567fcSMathieu Othacehe if (ret < 0) 704d35567fcSMathieu Othacehe goto end; 705d35567fcSMathieu Othacehe 706d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 707d35567fcSMathieu Othacehe 708d35567fcSMathieu Othacehe end: 709d35567fcSMathieu Othacehe if (state) 710d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 711d35567fcSMathieu Othacehe 712d35567fcSMathieu Othacehe return ret; 713d35567fcSMathieu Othacehe } 714d35567fcSMathieu Othacehe 715d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 716d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 717d35567fcSMathieu Othacehe enum iio_event_type type, 718d35567fcSMathieu Othacehe enum iio_event_direction dir, 719d35567fcSMathieu Othacehe int state) 720d35567fcSMathieu Othacehe { 721d35567fcSMathieu Othacehe switch (chan->type) { 722d35567fcSMathieu Othacehe case IIO_PROXIMITY: 723d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 724d35567fcSMathieu Othacehe default: 725d35567fcSMathieu Othacehe return -EINVAL; 726d35567fcSMathieu Othacehe } 727d35567fcSMathieu Othacehe } 728d35567fcSMathieu Othacehe 729d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 730d35567fcSMathieu Othacehe uintptr_t priv, 731d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 732d35567fcSMathieu Othacehe char *buf) 733d35567fcSMathieu Othacehe { 734d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 735d35567fcSMathieu Othacehe 736d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 737d35567fcSMathieu Othacehe } 738d35567fcSMathieu Othacehe 739d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 740d35567fcSMathieu Othacehe { 741d35567fcSMathieu Othacehe .name = "nearlevel", 742d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 743d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 744d35567fcSMathieu Othacehe }, 745d35567fcSMathieu Othacehe { /* sentinel */ } 746d35567fcSMathieu Othacehe }; 747d35567fcSMathieu Othacehe 748d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 749d35567fcSMathieu Othacehe { 750d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 751d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 752d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 753d35567fcSMathieu Othacehe }, { 754d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 755d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 756d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 757d35567fcSMathieu Othacehe }, { 758d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 759d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 760d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 761d35567fcSMathieu Othacehe } 762d35567fcSMathieu Othacehe }; 763d35567fcSMathieu Othacehe 764d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 765d35567fcSMathieu Othacehe { 766d35567fcSMathieu Othacehe .type = IIO_LIGHT, 767d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 768d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 769d35567fcSMathieu Othacehe }, { 770d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 771d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 772d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 773d35567fcSMathieu Othacehe } 774d35567fcSMathieu Othacehe }; 775d35567fcSMathieu Othacehe 776d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 777d35567fcSMathieu Othacehe { 778d35567fcSMathieu Othacehe .type = IIO_LIGHT, 779*8fe78d52SMathieu Othacehe .scan_index = -1, 780d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 781d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 782d35567fcSMathieu Othacehe }, { 783d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 784*8fe78d52SMathieu Othacehe .scan_index = 0, 785f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 786f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 787f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 788d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 789d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 790d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 791*8fe78d52SMathieu Othacehe .scan_type = { 792*8fe78d52SMathieu Othacehe .sign = 'u', 793*8fe78d52SMathieu Othacehe .realbits = 16, 794*8fe78d52SMathieu Othacehe .storagebits = 16, 795*8fe78d52SMathieu Othacehe .endianness = IIO_CPU, 796d35567fcSMathieu Othacehe }, 797*8fe78d52SMathieu Othacehe }, 798*8fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 799d35567fcSMathieu Othacehe }; 800d35567fcSMathieu Othacehe 80162a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 80262a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 80362a1efb9SPeter Meerwald }; 80462a1efb9SPeter Meerwald 805d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 806d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 807f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 808f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 809d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 810d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 811d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 812d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 813d35567fcSMathieu Othacehe }; 814d35567fcSMathieu Othacehe 815d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 816d35567fcSMathieu Othacehe [VCNL4000] = { 817d35567fcSMathieu Othacehe .prod = "VCNL4000", 818d35567fcSMathieu Othacehe .init = vcnl4000_init, 819d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 820d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 821d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 822d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 823d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 824d35567fcSMathieu Othacehe .info = &vcnl4000_info, 825d35567fcSMathieu Othacehe .irq_support = false, 826d35567fcSMathieu Othacehe }, 827d35567fcSMathieu Othacehe [VCNL4010] = { 828d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 829d35567fcSMathieu Othacehe .init = vcnl4000_init, 830d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 831d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 832d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 833d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 834d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 835d35567fcSMathieu Othacehe .info = &vcnl4010_info, 836d35567fcSMathieu Othacehe .irq_support = true, 837d35567fcSMathieu Othacehe }, 838d35567fcSMathieu Othacehe [VCNL4040] = { 839d35567fcSMathieu Othacehe .prod = "VCNL4040", 840d35567fcSMathieu Othacehe .init = vcnl4200_init, 841d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 842d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 843d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 844d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 845d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 846d35567fcSMathieu Othacehe .info = &vcnl4000_info, 847d35567fcSMathieu Othacehe .irq_support = false, 848d35567fcSMathieu Othacehe }, 849d35567fcSMathieu Othacehe [VCNL4200] = { 850d35567fcSMathieu Othacehe .prod = "VCNL4200", 851d35567fcSMathieu Othacehe .init = vcnl4200_init, 852d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 853d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 854d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 855d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 856d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 857d35567fcSMathieu Othacehe .info = &vcnl4000_info, 858d35567fcSMathieu Othacehe .irq_support = false, 859d35567fcSMathieu Othacehe }, 860d35567fcSMathieu Othacehe }; 861d35567fcSMathieu Othacehe 862d35567fcSMathieu Othacehe static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 863d35567fcSMathieu Othacehe { 864d35567fcSMathieu Othacehe struct iio_dev *indio_dev = p; 865d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 866d35567fcSMathieu Othacehe unsigned long isr; 867d35567fcSMathieu Othacehe int ret; 868d35567fcSMathieu Othacehe 869d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 870d35567fcSMathieu Othacehe if (ret < 0) 871d35567fcSMathieu Othacehe goto end; 872d35567fcSMathieu Othacehe 873d35567fcSMathieu Othacehe isr = ret; 874d35567fcSMathieu Othacehe 875d35567fcSMathieu Othacehe if (isr & VCNL4010_INT_THR) { 876d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 877d35567fcSMathieu Othacehe iio_push_event(indio_dev, 878d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 879d35567fcSMathieu Othacehe IIO_PROXIMITY, 880d35567fcSMathieu Othacehe 1, 881d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 882d35567fcSMathieu Othacehe IIO_EV_DIR_FALLING), 883d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 884d35567fcSMathieu Othacehe } 885d35567fcSMathieu Othacehe 886d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 887d35567fcSMathieu Othacehe iio_push_event(indio_dev, 888d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 889d35567fcSMathieu Othacehe IIO_PROXIMITY, 890d35567fcSMathieu Othacehe 1, 891d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 892d35567fcSMathieu Othacehe IIO_EV_DIR_RISING), 893d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 894d35567fcSMathieu Othacehe } 895d35567fcSMathieu Othacehe 896d35567fcSMathieu Othacehe i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 897d35567fcSMathieu Othacehe isr & VCNL4010_INT_THR); 898d35567fcSMathieu Othacehe } 899d35567fcSMathieu Othacehe 900*8fe78d52SMathieu Othacehe if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 901*8fe78d52SMathieu Othacehe iio_trigger_poll_chained(indio_dev->trig); 902*8fe78d52SMathieu Othacehe 903d35567fcSMathieu Othacehe end: 904d35567fcSMathieu Othacehe return IRQ_HANDLED; 905d35567fcSMathieu Othacehe } 906d35567fcSMathieu Othacehe 907*8fe78d52SMathieu Othacehe static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 908*8fe78d52SMathieu Othacehe { 909*8fe78d52SMathieu Othacehe struct iio_poll_func *pf = p; 910*8fe78d52SMathieu Othacehe struct iio_dev *indio_dev = pf->indio_dev; 911*8fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 912*8fe78d52SMathieu Othacehe const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 913*8fe78d52SMathieu Othacehe u16 buffer[8] = {0}; /* 1x16-bit + ts */ 914*8fe78d52SMathieu Othacehe bool data_read = false; 915*8fe78d52SMathieu Othacehe unsigned long isr; 916*8fe78d52SMathieu Othacehe int val = 0; 917*8fe78d52SMathieu Othacehe int ret; 918*8fe78d52SMathieu Othacehe 919*8fe78d52SMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 920*8fe78d52SMathieu Othacehe if (ret < 0) 921*8fe78d52SMathieu Othacehe goto end; 922*8fe78d52SMathieu Othacehe 923*8fe78d52SMathieu Othacehe isr = ret; 924*8fe78d52SMathieu Othacehe 925*8fe78d52SMathieu Othacehe if (test_bit(0, active_scan_mask)) { 926*8fe78d52SMathieu Othacehe if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 927*8fe78d52SMathieu Othacehe ret = vcnl4000_read_data(data, 928*8fe78d52SMathieu Othacehe VCNL4000_PS_RESULT_HI, 929*8fe78d52SMathieu Othacehe &val); 930*8fe78d52SMathieu Othacehe if (ret < 0) 931*8fe78d52SMathieu Othacehe goto end; 932*8fe78d52SMathieu Othacehe 933*8fe78d52SMathieu Othacehe buffer[0] = val; 934*8fe78d52SMathieu Othacehe data_read = true; 935*8fe78d52SMathieu Othacehe } 936*8fe78d52SMathieu Othacehe } 937*8fe78d52SMathieu Othacehe 938*8fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 939*8fe78d52SMathieu Othacehe isr & VCNL4010_INT_DRDY); 940*8fe78d52SMathieu Othacehe if (ret < 0) 941*8fe78d52SMathieu Othacehe goto end; 942*8fe78d52SMathieu Othacehe 943*8fe78d52SMathieu Othacehe if (!data_read) 944*8fe78d52SMathieu Othacehe goto end; 945*8fe78d52SMathieu Othacehe 946*8fe78d52SMathieu Othacehe iio_push_to_buffers_with_timestamp(indio_dev, buffer, 947*8fe78d52SMathieu Othacehe iio_get_time_ns(indio_dev)); 948*8fe78d52SMathieu Othacehe 949*8fe78d52SMathieu Othacehe end: 950*8fe78d52SMathieu Othacehe iio_trigger_notify_done(indio_dev->trig); 951*8fe78d52SMathieu Othacehe return IRQ_HANDLED; 952*8fe78d52SMathieu Othacehe } 953*8fe78d52SMathieu Othacehe 954*8fe78d52SMathieu Othacehe static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 955*8fe78d52SMathieu Othacehe { 956*8fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 957*8fe78d52SMathieu Othacehe int ret; 958*8fe78d52SMathieu Othacehe int cmd; 959*8fe78d52SMathieu Othacehe 960*8fe78d52SMathieu Othacehe ret = iio_triggered_buffer_postenable(indio_dev); 961*8fe78d52SMathieu Othacehe if (ret) 962*8fe78d52SMathieu Othacehe return ret; 963*8fe78d52SMathieu Othacehe 964*8fe78d52SMathieu Othacehe /* Do not enable the buffer if we are already capturing events. */ 965*8fe78d52SMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 966*8fe78d52SMathieu Othacehe ret = -EBUSY; 967*8fe78d52SMathieu Othacehe goto end; 968*8fe78d52SMathieu Othacehe } 969*8fe78d52SMathieu Othacehe 970*8fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 971*8fe78d52SMathieu Othacehe VCNL4010_INT_PROX_EN); 972*8fe78d52SMathieu Othacehe if (ret < 0) 973*8fe78d52SMathieu Othacehe goto end; 974*8fe78d52SMathieu Othacehe 975*8fe78d52SMathieu Othacehe cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 976*8fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 977*8fe78d52SMathieu Othacehe if (ret < 0) 978*8fe78d52SMathieu Othacehe goto end; 979*8fe78d52SMathieu Othacehe 980*8fe78d52SMathieu Othacehe return 0; 981*8fe78d52SMathieu Othacehe end: 982*8fe78d52SMathieu Othacehe iio_triggered_buffer_predisable(indio_dev); 983*8fe78d52SMathieu Othacehe 984*8fe78d52SMathieu Othacehe return ret; 985*8fe78d52SMathieu Othacehe } 986*8fe78d52SMathieu Othacehe 987*8fe78d52SMathieu Othacehe static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 988*8fe78d52SMathieu Othacehe { 989*8fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 990*8fe78d52SMathieu Othacehe int ret, ret_disable; 991*8fe78d52SMathieu Othacehe 992*8fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 993*8fe78d52SMathieu Othacehe if (ret < 0) 994*8fe78d52SMathieu Othacehe goto end; 995*8fe78d52SMathieu Othacehe 996*8fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 997*8fe78d52SMathieu Othacehe 998*8fe78d52SMathieu Othacehe end: 999*8fe78d52SMathieu Othacehe ret_disable = iio_triggered_buffer_predisable(indio_dev); 1000*8fe78d52SMathieu Othacehe if (ret == 0) 1001*8fe78d52SMathieu Othacehe ret = ret_disable; 1002*8fe78d52SMathieu Othacehe 1003*8fe78d52SMathieu Othacehe return ret; 1004*8fe78d52SMathieu Othacehe } 1005*8fe78d52SMathieu Othacehe 1006*8fe78d52SMathieu Othacehe static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 1007*8fe78d52SMathieu Othacehe .postenable = &vcnl4010_buffer_postenable, 1008*8fe78d52SMathieu Othacehe .predisable = &vcnl4010_buffer_predisable, 1009*8fe78d52SMathieu Othacehe }; 1010*8fe78d52SMathieu Othacehe 1011*8fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 1012*8fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 1013*8fe78d52SMathieu Othacehe }; 1014*8fe78d52SMathieu Othacehe 1015*8fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 1016*8fe78d52SMathieu Othacehe { 1017*8fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1018*8fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 1019*8fe78d52SMathieu Othacehe struct iio_trigger *trigger; 1020*8fe78d52SMathieu Othacehe 1021*8fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 1022*8fe78d52SMathieu Othacehe indio_dev->name, indio_dev->id); 1023*8fe78d52SMathieu Othacehe if (!trigger) 1024*8fe78d52SMathieu Othacehe return -ENOMEM; 1025*8fe78d52SMathieu Othacehe 1026*8fe78d52SMathieu Othacehe trigger->dev.parent = &client->dev; 1027*8fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 1028*8fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 1029*8fe78d52SMathieu Othacehe 1030*8fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 1031*8fe78d52SMathieu Othacehe } 1032*8fe78d52SMathieu Othacehe 1033fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 103462a1efb9SPeter Meerwald const struct i2c_device_id *id) 103562a1efb9SPeter Meerwald { 103662a1efb9SPeter Meerwald struct vcnl4000_data *data; 103762a1efb9SPeter Meerwald struct iio_dev *indio_dev; 10381ebc787aSTomas Novotny int ret; 103962a1efb9SPeter Meerwald 10402669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 104162a1efb9SPeter Meerwald if (!indio_dev) 104262a1efb9SPeter Meerwald return -ENOMEM; 104362a1efb9SPeter Meerwald 104462a1efb9SPeter Meerwald data = iio_priv(indio_dev); 104562a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 104662a1efb9SPeter Meerwald data->client = client; 10471ebc787aSTomas Novotny data->id = id->driver_data; 10481ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 104962a1efb9SPeter Meerwald 10501ebc787aSTomas Novotny ret = data->chip_spec->init(data); 105162a1efb9SPeter Meerwald if (ret < 0) 10522669d723SPeter Meerwald return ret; 105362a1efb9SPeter Meerwald 1054d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 10551ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 105662a1efb9SPeter Meerwald 1057f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1058f5a98e1fSGuido Günther &data->near_level)) 1059f5a98e1fSGuido Günther data->near_level = 0; 1060f5a98e1fSGuido Günther 106162a1efb9SPeter Meerwald indio_dev->dev.parent = &client->dev; 1062d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1063d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1064d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 106562a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 106662a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 106762a1efb9SPeter Meerwald 1068d35567fcSMathieu Othacehe if (client->irq && data->chip_spec->irq_support) { 1069*8fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 1070*8fe78d52SMathieu Othacehe NULL, 1071*8fe78d52SMathieu Othacehe vcnl4010_trigger_handler, 1072*8fe78d52SMathieu Othacehe &vcnl4010_buffer_ops); 1073*8fe78d52SMathieu Othacehe if (ret < 0) { 1074*8fe78d52SMathieu Othacehe dev_err(&client->dev, 1075*8fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 1076*8fe78d52SMathieu Othacehe return ret; 1077*8fe78d52SMathieu Othacehe } 1078*8fe78d52SMathieu Othacehe 1079d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1080d35567fcSMathieu Othacehe NULL, vcnl4010_irq_thread, 1081d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1082d35567fcSMathieu Othacehe IRQF_ONESHOT, 1083d35567fcSMathieu Othacehe "vcnl4010_irq", 1084d35567fcSMathieu Othacehe indio_dev); 1085d35567fcSMathieu Othacehe if (ret < 0) { 1086d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1087d35567fcSMathieu Othacehe return ret; 1088d35567fcSMathieu Othacehe } 1089*8fe78d52SMathieu Othacehe 1090*8fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 1091*8fe78d52SMathieu Othacehe if (ret < 0) 1092*8fe78d52SMathieu Othacehe return ret; 1093d35567fcSMathieu Othacehe } 1094d35567fcSMathieu Othacehe 10955e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 10965e00708dSGuido Günther if (ret < 0) 10975e00708dSGuido Günther goto fail_poweroff; 10985e00708dSGuido Günther 10995e00708dSGuido Günther ret = iio_device_register(indio_dev); 11005e00708dSGuido Günther if (ret < 0) 11015e00708dSGuido Günther goto fail_poweroff; 11025e00708dSGuido Günther 11035e00708dSGuido Günther pm_runtime_enable(&client->dev); 11045e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 11055e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 11065e00708dSGuido Günther 11075e00708dSGuido Günther return 0; 11085e00708dSGuido Günther fail_poweroff: 11095e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 11105e00708dSGuido Günther return ret; 111162a1efb9SPeter Meerwald } 111262a1efb9SPeter Meerwald 1113ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1114ebd457d5SAngus Ainslie (Purism) { 1115ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 11161436a78cSMarco Felsch .data = (void *)VCNL4000, 1117ebd457d5SAngus Ainslie (Purism) }, 1118ebd457d5SAngus Ainslie (Purism) { 1119ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 11201436a78cSMarco Felsch .data = (void *)VCNL4010, 1121ebd457d5SAngus Ainslie (Purism) }, 1122ebd457d5SAngus Ainslie (Purism) { 11231436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 11241436a78cSMarco Felsch .data = (void *)VCNL4010, 1125ebd457d5SAngus Ainslie (Purism) }, 1126ebd457d5SAngus Ainslie (Purism) { 11277fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 11287fd1c260SMarco Felsch .data = (void *)VCNL4040, 11297fd1c260SMarco Felsch }, 11307fd1c260SMarco Felsch { 1131ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 11321436a78cSMarco Felsch .data = (void *)VCNL4200, 1133ebd457d5SAngus Ainslie (Purism) }, 1134ebd457d5SAngus Ainslie (Purism) {}, 1135ebd457d5SAngus Ainslie (Purism) }; 1136ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1137ebd457d5SAngus Ainslie (Purism) 11385e00708dSGuido Günther static int vcnl4000_remove(struct i2c_client *client) 11395e00708dSGuido Günther { 11405e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 11415e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 11425e00708dSGuido Günther 11435e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 11445e00708dSGuido Günther pm_runtime_disable(&client->dev); 11455e00708dSGuido Günther iio_device_unregister(indio_dev); 11465e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 11475e00708dSGuido Günther 11485e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 11495e00708dSGuido Günther } 11505e00708dSGuido Günther 11515e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) 11525e00708dSGuido Günther { 11535e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11545e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 11555e00708dSGuido Günther 11565e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 11575e00708dSGuido Günther } 11585e00708dSGuido Günther 11595e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) 11605e00708dSGuido Günther { 11615e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11625e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 11635e00708dSGuido Günther 11645e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 11655e00708dSGuido Günther } 11665e00708dSGuido Günther 11675e00708dSGuido Günther static const struct dev_pm_ops vcnl4000_pm_ops = { 11685e00708dSGuido Günther SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 11695e00708dSGuido Günther pm_runtime_force_resume) 11705e00708dSGuido Günther SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, 11715e00708dSGuido Günther vcnl4000_runtime_resume, NULL) 11725e00708dSGuido Günther }; 11735e00708dSGuido Günther 117462a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 117562a1efb9SPeter Meerwald .driver = { 117662a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 11775e00708dSGuido Günther .pm = &vcnl4000_pm_ops, 1178ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 117962a1efb9SPeter Meerwald }, 118062a1efb9SPeter Meerwald .probe = vcnl4000_probe, 118162a1efb9SPeter Meerwald .id_table = vcnl4000_id, 11825e00708dSGuido Günther .remove = vcnl4000_remove, 118362a1efb9SPeter Meerwald }; 118462a1efb9SPeter Meerwald 118562a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 118662a1efb9SPeter Meerwald 118762a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 1188*8fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 118962a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 119062a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1191