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 88fe78d52SMathieu 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 178fe78d52SMathieu 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 278fe78d52SMathieu 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> 318fe78d52SMathieu Othacehe #include <linux/iio/trigger.h> 328fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 338fe78d52SMathieu 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) { 416db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4175e00708dSGuido Günther } else { 4185e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4195e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4205e00708dSGuido Günther } 4215e00708dSGuido Günther 4225e00708dSGuido Günther return ret; 4235e00708dSGuido Günther } 4245e00708dSGuido Günther 42562a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 42662a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 42762a1efb9SPeter Meerwald int *val, int *val2, long mask) 42862a1efb9SPeter Meerwald { 4295d693139SPeter Meerwald-Stadler int ret; 43062a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 43162a1efb9SPeter Meerwald 43262a1efb9SPeter Meerwald switch (mask) { 43362a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 4345e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 4355e00708dSGuido Günther if (ret < 0) 4365e00708dSGuido Günther return ret; 4375e00708dSGuido Günther 43862a1efb9SPeter Meerwald switch (chan->type) { 43962a1efb9SPeter Meerwald case IIO_LIGHT: 4401ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 4414a818643SGuido Günther if (!ret) 4424a818643SGuido Günther ret = IIO_VAL_INT; 4434a818643SGuido Günther break; 44462a1efb9SPeter Meerwald case IIO_PROXIMITY: 4451ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 4464a818643SGuido Günther if (!ret) 4474a818643SGuido Günther ret = IIO_VAL_INT; 4484a818643SGuido Günther break; 44962a1efb9SPeter Meerwald default: 4504a818643SGuido Günther ret = -EINVAL; 45162a1efb9SPeter Meerwald } 4525e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 4534a818643SGuido Günther return ret; 45462a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 4555d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 4565d693139SPeter Meerwald-Stadler return -EINVAL; 4575d693139SPeter Meerwald-Stadler 45862a1efb9SPeter Meerwald *val = 0; 4591ebc787aSTomas Novotny *val2 = data->al_scale; 4605d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 46162a1efb9SPeter Meerwald default: 4625d693139SPeter Meerwald-Stadler return -EINVAL; 46362a1efb9SPeter Meerwald } 46462a1efb9SPeter Meerwald } 46562a1efb9SPeter Meerwald 466d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 467d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 468d35567fcSMathieu Othacehe int *val, int *val2, long mask) 469d35567fcSMathieu Othacehe { 470d35567fcSMathieu Othacehe int ret; 471d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 472d35567fcSMathieu Othacehe 473d35567fcSMathieu Othacehe switch (mask) { 474d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 475d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 476d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 477d35567fcSMathieu Othacehe if (ret) 478d35567fcSMathieu Othacehe return ret; 479d35567fcSMathieu Othacehe 480d35567fcSMathieu Othacehe /* Protect against event capture. */ 481d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 482d35567fcSMathieu Othacehe ret = -EBUSY; 483d35567fcSMathieu Othacehe } else { 484d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 485d35567fcSMathieu Othacehe mask); 486d35567fcSMathieu Othacehe } 487d35567fcSMathieu Othacehe 488d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 489d35567fcSMathieu Othacehe return ret; 490f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 491f6889c1bSMathieu Othacehe switch (chan->type) { 492f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 493f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 494f6889c1bSMathieu Othacehe if (ret < 0) 495f6889c1bSMathieu Othacehe return ret; 496f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 497d35567fcSMathieu Othacehe default: 498d35567fcSMathieu Othacehe return -EINVAL; 499d35567fcSMathieu Othacehe } 500f6889c1bSMathieu Othacehe default: 501f6889c1bSMathieu Othacehe return -EINVAL; 502f6889c1bSMathieu Othacehe } 503f6889c1bSMathieu Othacehe } 504f6889c1bSMathieu Othacehe 505f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 506f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 507f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 508f6889c1bSMathieu Othacehe long mask) 509f6889c1bSMathieu Othacehe { 510f6889c1bSMathieu Othacehe switch (mask) { 511f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 512f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 513f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 514f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 515f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 516f6889c1bSMathieu Othacehe default: 517f6889c1bSMathieu Othacehe return -EINVAL; 518f6889c1bSMathieu Othacehe } 519f6889c1bSMathieu Othacehe } 520f6889c1bSMathieu Othacehe 521f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 522f6889c1bSMathieu Othacehe int val2) 523f6889c1bSMathieu Othacehe { 524f6889c1bSMathieu Othacehe unsigned int i; 525f6889c1bSMathieu Othacehe int index = -1; 526f6889c1bSMathieu Othacehe 527f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 528f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 529f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 530f6889c1bSMathieu Othacehe index = i; 531f6889c1bSMathieu Othacehe break; 532f6889c1bSMathieu Othacehe } 533f6889c1bSMathieu Othacehe } 534f6889c1bSMathieu Othacehe 535f6889c1bSMathieu Othacehe if (index < 0) 536f6889c1bSMathieu Othacehe return -EINVAL; 537f6889c1bSMathieu Othacehe 538f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 539f6889c1bSMathieu Othacehe index); 540f6889c1bSMathieu Othacehe } 541f6889c1bSMathieu Othacehe 542f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 543f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 544f6889c1bSMathieu Othacehe int val, int val2, long mask) 545f6889c1bSMathieu Othacehe { 546f6889c1bSMathieu Othacehe int ret; 547f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 548f6889c1bSMathieu Othacehe 549f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 550f6889c1bSMathieu Othacehe if (ret) 551f6889c1bSMathieu Othacehe return ret; 552f6889c1bSMathieu Othacehe 553f6889c1bSMathieu Othacehe /* Protect against event capture. */ 554f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 555f6889c1bSMathieu Othacehe ret = -EBUSY; 556f6889c1bSMathieu Othacehe goto end; 557f6889c1bSMathieu Othacehe } 558f6889c1bSMathieu Othacehe 559f6889c1bSMathieu Othacehe switch (mask) { 560f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 561f6889c1bSMathieu Othacehe switch (chan->type) { 562f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 563f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 564f6889c1bSMathieu Othacehe goto end; 565f6889c1bSMathieu Othacehe default: 566f6889c1bSMathieu Othacehe ret = -EINVAL; 567f6889c1bSMathieu Othacehe goto end; 568f6889c1bSMathieu Othacehe } 569f6889c1bSMathieu Othacehe default: 570f6889c1bSMathieu Othacehe ret = -EINVAL; 571f6889c1bSMathieu Othacehe goto end; 572f6889c1bSMathieu Othacehe } 573f6889c1bSMathieu Othacehe 574f6889c1bSMathieu Othacehe end: 575f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 576f6889c1bSMathieu Othacehe return ret; 577d35567fcSMathieu Othacehe } 578d35567fcSMathieu Othacehe 579d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 580d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 581d35567fcSMathieu Othacehe enum iio_event_type type, 582d35567fcSMathieu Othacehe enum iio_event_direction dir, 583d35567fcSMathieu Othacehe enum iio_event_info info, 584d35567fcSMathieu Othacehe int *val, int *val2) 585d35567fcSMathieu Othacehe { 586d35567fcSMathieu Othacehe int ret; 587d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 588d35567fcSMathieu Othacehe 589d35567fcSMathieu Othacehe switch (info) { 590d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 591d35567fcSMathieu Othacehe switch (dir) { 592d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 593d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 594d35567fcSMathieu Othacehe val); 595d35567fcSMathieu Othacehe if (ret < 0) 596d35567fcSMathieu Othacehe return ret; 597d35567fcSMathieu Othacehe return IIO_VAL_INT; 598d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 599d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 600d35567fcSMathieu Othacehe val); 601d35567fcSMathieu Othacehe if (ret < 0) 602d35567fcSMathieu Othacehe return ret; 603d35567fcSMathieu Othacehe return IIO_VAL_INT; 604d35567fcSMathieu Othacehe default: 605d35567fcSMathieu Othacehe return -EINVAL; 606d35567fcSMathieu Othacehe } 607d35567fcSMathieu Othacehe default: 608d35567fcSMathieu Othacehe return -EINVAL; 609d35567fcSMathieu Othacehe } 610d35567fcSMathieu Othacehe } 611d35567fcSMathieu Othacehe 612d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 613d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 614d35567fcSMathieu Othacehe enum iio_event_type type, 615d35567fcSMathieu Othacehe enum iio_event_direction dir, 616d35567fcSMathieu Othacehe enum iio_event_info info, 617d35567fcSMathieu Othacehe int val, int val2) 618d35567fcSMathieu Othacehe { 619d35567fcSMathieu Othacehe int ret; 620d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 621d35567fcSMathieu Othacehe 622d35567fcSMathieu Othacehe switch (info) { 623d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 624d35567fcSMathieu Othacehe switch (dir) { 625d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 626d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 627d35567fcSMathieu Othacehe val); 628d35567fcSMathieu Othacehe if (ret < 0) 629d35567fcSMathieu Othacehe return ret; 630d35567fcSMathieu Othacehe return IIO_VAL_INT; 631d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 632d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 633d35567fcSMathieu Othacehe val); 634d35567fcSMathieu Othacehe if (ret < 0) 635d35567fcSMathieu Othacehe return ret; 636d35567fcSMathieu Othacehe return IIO_VAL_INT; 637d35567fcSMathieu Othacehe default: 638d35567fcSMathieu Othacehe return -EINVAL; 639d35567fcSMathieu Othacehe } 640d35567fcSMathieu Othacehe default: 641d35567fcSMathieu Othacehe return -EINVAL; 642d35567fcSMathieu Othacehe } 643d35567fcSMathieu Othacehe } 644d35567fcSMathieu Othacehe 645d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 646d35567fcSMathieu Othacehe { 647d35567fcSMathieu Othacehe int ret; 648d35567fcSMathieu Othacehe 649d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 650d35567fcSMathieu Othacehe if (ret < 0) 651d35567fcSMathieu Othacehe return false; 652d35567fcSMathieu Othacehe 653d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 654d35567fcSMathieu Othacehe } 655d35567fcSMathieu Othacehe 656d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 657d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 658d35567fcSMathieu Othacehe enum iio_event_type type, 659d35567fcSMathieu Othacehe enum iio_event_direction dir) 660d35567fcSMathieu Othacehe { 661d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 662d35567fcSMathieu Othacehe 663d35567fcSMathieu Othacehe switch (chan->type) { 664d35567fcSMathieu Othacehe case IIO_PROXIMITY: 665d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 666d35567fcSMathieu Othacehe default: 667d35567fcSMathieu Othacehe return -EINVAL; 668d35567fcSMathieu Othacehe } 669d35567fcSMathieu Othacehe } 670d35567fcSMathieu Othacehe 671d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 672d35567fcSMathieu Othacehe { 673d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 674d35567fcSMathieu Othacehe int ret; 675d35567fcSMathieu Othacehe int icr; 676d35567fcSMathieu Othacehe int command; 677d35567fcSMathieu Othacehe 678d35567fcSMathieu Othacehe if (state) { 679d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 680d35567fcSMathieu Othacehe if (ret) 681d35567fcSMathieu Othacehe return ret; 682d35567fcSMathieu Othacehe 683d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 684d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 685d35567fcSMathieu Othacehe 686d35567fcSMathieu Othacehe /* 687d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 688d35567fcSMathieu Othacehe * default. 689d35567fcSMathieu Othacehe */ 690d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 691d35567fcSMathieu Othacehe } else { 692d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 693d35567fcSMathieu Othacehe return 0; 694d35567fcSMathieu Othacehe 695d35567fcSMathieu Othacehe command = 0; 696d35567fcSMathieu Othacehe icr = 0; 697d35567fcSMathieu Othacehe } 698d35567fcSMathieu Othacehe 699d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 700d35567fcSMathieu Othacehe command); 701d35567fcSMathieu Othacehe if (ret < 0) 702d35567fcSMathieu Othacehe goto end; 703d35567fcSMathieu Othacehe 704d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 705d35567fcSMathieu Othacehe 706d35567fcSMathieu Othacehe end: 707d35567fcSMathieu Othacehe if (state) 708d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 709d35567fcSMathieu Othacehe 710d35567fcSMathieu Othacehe return ret; 711d35567fcSMathieu Othacehe } 712d35567fcSMathieu Othacehe 713d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 714d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 715d35567fcSMathieu Othacehe enum iio_event_type type, 716d35567fcSMathieu Othacehe enum iio_event_direction dir, 717d35567fcSMathieu Othacehe int state) 718d35567fcSMathieu Othacehe { 719d35567fcSMathieu Othacehe switch (chan->type) { 720d35567fcSMathieu Othacehe case IIO_PROXIMITY: 721d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 722d35567fcSMathieu Othacehe default: 723d35567fcSMathieu Othacehe return -EINVAL; 724d35567fcSMathieu Othacehe } 725d35567fcSMathieu Othacehe } 726d35567fcSMathieu Othacehe 727d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 728d35567fcSMathieu Othacehe uintptr_t priv, 729d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 730d35567fcSMathieu Othacehe char *buf) 731d35567fcSMathieu Othacehe { 732d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 733d35567fcSMathieu Othacehe 734d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 735d35567fcSMathieu Othacehe } 736d35567fcSMathieu Othacehe 737d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 738d35567fcSMathieu Othacehe { 739d35567fcSMathieu Othacehe .name = "nearlevel", 740d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 741d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 742d35567fcSMathieu Othacehe }, 743d35567fcSMathieu Othacehe { /* sentinel */ } 744d35567fcSMathieu Othacehe }; 745d35567fcSMathieu Othacehe 746d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 747d35567fcSMathieu Othacehe { 748d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 749d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 750d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 751d35567fcSMathieu Othacehe }, { 752d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 753d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 754d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 755d35567fcSMathieu Othacehe }, { 756d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 757d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 758d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 759d35567fcSMathieu Othacehe } 760d35567fcSMathieu Othacehe }; 761d35567fcSMathieu Othacehe 762d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 763d35567fcSMathieu Othacehe { 764d35567fcSMathieu Othacehe .type = IIO_LIGHT, 765d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 766d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 767d35567fcSMathieu Othacehe }, { 768d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 769d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 770d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 771d35567fcSMathieu Othacehe } 772d35567fcSMathieu Othacehe }; 773d35567fcSMathieu Othacehe 774d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 775d35567fcSMathieu Othacehe { 776d35567fcSMathieu Othacehe .type = IIO_LIGHT, 7778fe78d52SMathieu Othacehe .scan_index = -1, 778d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 779d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 780d35567fcSMathieu Othacehe }, { 781d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 7828fe78d52SMathieu Othacehe .scan_index = 0, 783f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 784f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 785f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 786d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 787d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 788d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 7898fe78d52SMathieu Othacehe .scan_type = { 7908fe78d52SMathieu Othacehe .sign = 'u', 7918fe78d52SMathieu Othacehe .realbits = 16, 7928fe78d52SMathieu Othacehe .storagebits = 16, 7938fe78d52SMathieu Othacehe .endianness = IIO_CPU, 794d35567fcSMathieu Othacehe }, 7958fe78d52SMathieu Othacehe }, 7968fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 797d35567fcSMathieu Othacehe }; 798d35567fcSMathieu Othacehe 79962a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 80062a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 80162a1efb9SPeter Meerwald }; 80262a1efb9SPeter Meerwald 803d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 804d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 805f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 806f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 807d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 808d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 809d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 810d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 811d35567fcSMathieu Othacehe }; 812d35567fcSMathieu Othacehe 813d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 814d35567fcSMathieu Othacehe [VCNL4000] = { 815d35567fcSMathieu Othacehe .prod = "VCNL4000", 816d35567fcSMathieu Othacehe .init = vcnl4000_init, 817d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 818d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 819d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 820d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 821d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 822d35567fcSMathieu Othacehe .info = &vcnl4000_info, 823d35567fcSMathieu Othacehe .irq_support = false, 824d35567fcSMathieu Othacehe }, 825d35567fcSMathieu Othacehe [VCNL4010] = { 826d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 827d35567fcSMathieu Othacehe .init = vcnl4000_init, 828d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 829d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 830d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 831d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 832d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 833d35567fcSMathieu Othacehe .info = &vcnl4010_info, 834d35567fcSMathieu Othacehe .irq_support = true, 835d35567fcSMathieu Othacehe }, 836d35567fcSMathieu Othacehe [VCNL4040] = { 837d35567fcSMathieu Othacehe .prod = "VCNL4040", 838d35567fcSMathieu Othacehe .init = vcnl4200_init, 839d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 840d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 841d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 842d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 843d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 844d35567fcSMathieu Othacehe .info = &vcnl4000_info, 845d35567fcSMathieu Othacehe .irq_support = false, 846d35567fcSMathieu Othacehe }, 847d35567fcSMathieu Othacehe [VCNL4200] = { 848d35567fcSMathieu Othacehe .prod = "VCNL4200", 849d35567fcSMathieu Othacehe .init = vcnl4200_init, 850d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 851d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 852d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 853d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 854d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 855d35567fcSMathieu Othacehe .info = &vcnl4000_info, 856d35567fcSMathieu Othacehe .irq_support = false, 857d35567fcSMathieu Othacehe }, 858d35567fcSMathieu Othacehe }; 859d35567fcSMathieu Othacehe 860d35567fcSMathieu Othacehe static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 861d35567fcSMathieu Othacehe { 862d35567fcSMathieu Othacehe struct iio_dev *indio_dev = p; 863d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 864d35567fcSMathieu Othacehe unsigned long isr; 865d35567fcSMathieu Othacehe int ret; 866d35567fcSMathieu Othacehe 867d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 868d35567fcSMathieu Othacehe if (ret < 0) 869d35567fcSMathieu Othacehe goto end; 870d35567fcSMathieu Othacehe 871d35567fcSMathieu Othacehe isr = ret; 872d35567fcSMathieu Othacehe 873d35567fcSMathieu Othacehe if (isr & VCNL4010_INT_THR) { 874d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 875d35567fcSMathieu Othacehe iio_push_event(indio_dev, 876d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 877d35567fcSMathieu Othacehe IIO_PROXIMITY, 878d35567fcSMathieu Othacehe 1, 879d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 880d35567fcSMathieu Othacehe IIO_EV_DIR_FALLING), 881d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 882d35567fcSMathieu Othacehe } 883d35567fcSMathieu Othacehe 884d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 885d35567fcSMathieu Othacehe iio_push_event(indio_dev, 886d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 887d35567fcSMathieu Othacehe IIO_PROXIMITY, 888d35567fcSMathieu Othacehe 1, 889d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 890d35567fcSMathieu Othacehe IIO_EV_DIR_RISING), 891d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 892d35567fcSMathieu Othacehe } 893d35567fcSMathieu Othacehe 894d35567fcSMathieu Othacehe i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 895d35567fcSMathieu Othacehe isr & VCNL4010_INT_THR); 896d35567fcSMathieu Othacehe } 897d35567fcSMathieu Othacehe 8988fe78d52SMathieu Othacehe if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 8998fe78d52SMathieu Othacehe iio_trigger_poll_chained(indio_dev->trig); 9008fe78d52SMathieu Othacehe 901d35567fcSMathieu Othacehe end: 902d35567fcSMathieu Othacehe return IRQ_HANDLED; 903d35567fcSMathieu Othacehe } 904d35567fcSMathieu Othacehe 9058fe78d52SMathieu Othacehe static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 9068fe78d52SMathieu Othacehe { 9078fe78d52SMathieu Othacehe struct iio_poll_func *pf = p; 9088fe78d52SMathieu Othacehe struct iio_dev *indio_dev = pf->indio_dev; 9098fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 9108fe78d52SMathieu Othacehe const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 911dce793c0SJonathan Cameron u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 9128fe78d52SMathieu Othacehe bool data_read = false; 9138fe78d52SMathieu Othacehe unsigned long isr; 9148fe78d52SMathieu Othacehe int val = 0; 9158fe78d52SMathieu Othacehe int ret; 9168fe78d52SMathieu Othacehe 9178fe78d52SMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 9188fe78d52SMathieu Othacehe if (ret < 0) 9198fe78d52SMathieu Othacehe goto end; 9208fe78d52SMathieu Othacehe 9218fe78d52SMathieu Othacehe isr = ret; 9228fe78d52SMathieu Othacehe 9238fe78d52SMathieu Othacehe if (test_bit(0, active_scan_mask)) { 9248fe78d52SMathieu Othacehe if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 9258fe78d52SMathieu Othacehe ret = vcnl4000_read_data(data, 9268fe78d52SMathieu Othacehe VCNL4000_PS_RESULT_HI, 9278fe78d52SMathieu Othacehe &val); 9288fe78d52SMathieu Othacehe if (ret < 0) 9298fe78d52SMathieu Othacehe goto end; 9308fe78d52SMathieu Othacehe 9318fe78d52SMathieu Othacehe buffer[0] = val; 9328fe78d52SMathieu Othacehe data_read = true; 9338fe78d52SMathieu Othacehe } 9348fe78d52SMathieu Othacehe } 9358fe78d52SMathieu Othacehe 9368fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 9378fe78d52SMathieu Othacehe isr & VCNL4010_INT_DRDY); 9388fe78d52SMathieu Othacehe if (ret < 0) 9398fe78d52SMathieu Othacehe goto end; 9408fe78d52SMathieu Othacehe 9418fe78d52SMathieu Othacehe if (!data_read) 9428fe78d52SMathieu Othacehe goto end; 9438fe78d52SMathieu Othacehe 9448fe78d52SMathieu Othacehe iio_push_to_buffers_with_timestamp(indio_dev, buffer, 9458fe78d52SMathieu Othacehe iio_get_time_ns(indio_dev)); 9468fe78d52SMathieu Othacehe 9478fe78d52SMathieu Othacehe end: 9488fe78d52SMathieu Othacehe iio_trigger_notify_done(indio_dev->trig); 9498fe78d52SMathieu Othacehe return IRQ_HANDLED; 9508fe78d52SMathieu Othacehe } 9518fe78d52SMathieu Othacehe 9528fe78d52SMathieu Othacehe static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 9538fe78d52SMathieu Othacehe { 9548fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 9558fe78d52SMathieu Othacehe int ret; 9568fe78d52SMathieu Othacehe int cmd; 9578fe78d52SMathieu Othacehe 9588fe78d52SMathieu Othacehe /* Do not enable the buffer if we are already capturing events. */ 959f11d59d8SLars-Peter Clausen if (vcnl4010_is_in_periodic_mode(data)) 960f11d59d8SLars-Peter Clausen return -EBUSY; 9618fe78d52SMathieu Othacehe 9628fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 9638fe78d52SMathieu Othacehe VCNL4010_INT_PROX_EN); 9648fe78d52SMathieu Othacehe if (ret < 0) 965f11d59d8SLars-Peter Clausen return ret; 9668fe78d52SMathieu Othacehe 9678fe78d52SMathieu Othacehe cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 968f11d59d8SLars-Peter Clausen return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 9698fe78d52SMathieu Othacehe } 9708fe78d52SMathieu Othacehe 9718fe78d52SMathieu Othacehe static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 9728fe78d52SMathieu Othacehe { 9738fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 974f11d59d8SLars-Peter Clausen int ret; 9758fe78d52SMathieu Othacehe 9768fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 9778fe78d52SMathieu Othacehe if (ret < 0) 9788fe78d52SMathieu Othacehe return ret; 979f11d59d8SLars-Peter Clausen 980f11d59d8SLars-Peter Clausen return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 9818fe78d52SMathieu Othacehe } 9828fe78d52SMathieu Othacehe 9838fe78d52SMathieu Othacehe static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 9848fe78d52SMathieu Othacehe .postenable = &vcnl4010_buffer_postenable, 9858fe78d52SMathieu Othacehe .predisable = &vcnl4010_buffer_predisable, 9868fe78d52SMathieu Othacehe }; 9878fe78d52SMathieu Othacehe 9888fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 9898fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 9908fe78d52SMathieu Othacehe }; 9918fe78d52SMathieu Othacehe 9928fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 9938fe78d52SMathieu Othacehe { 9948fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 9958fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 9968fe78d52SMathieu Othacehe struct iio_trigger *trigger; 9978fe78d52SMathieu Othacehe 9988fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 99915ea2878SJonathan Cameron indio_dev->name, 100015ea2878SJonathan Cameron iio_device_id(indio_dev)); 10018fe78d52SMathieu Othacehe if (!trigger) 10028fe78d52SMathieu Othacehe return -ENOMEM; 10038fe78d52SMathieu Othacehe 10048fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 10058fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 10068fe78d52SMathieu Othacehe 10078fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 10088fe78d52SMathieu Othacehe } 10098fe78d52SMathieu Othacehe 1010fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 101162a1efb9SPeter Meerwald const struct i2c_device_id *id) 101262a1efb9SPeter Meerwald { 101362a1efb9SPeter Meerwald struct vcnl4000_data *data; 101462a1efb9SPeter Meerwald struct iio_dev *indio_dev; 10151ebc787aSTomas Novotny int ret; 101662a1efb9SPeter Meerwald 10172669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 101862a1efb9SPeter Meerwald if (!indio_dev) 101962a1efb9SPeter Meerwald return -ENOMEM; 102062a1efb9SPeter Meerwald 102162a1efb9SPeter Meerwald data = iio_priv(indio_dev); 102262a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 102362a1efb9SPeter Meerwald data->client = client; 10241ebc787aSTomas Novotny data->id = id->driver_data; 10251ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 102662a1efb9SPeter Meerwald 10271ebc787aSTomas Novotny ret = data->chip_spec->init(data); 102862a1efb9SPeter Meerwald if (ret < 0) 10292669d723SPeter Meerwald return ret; 103062a1efb9SPeter Meerwald 1031d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 10321ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 103362a1efb9SPeter Meerwald 1034f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1035f5a98e1fSGuido Günther &data->near_level)) 1036f5a98e1fSGuido Günther data->near_level = 0; 1037f5a98e1fSGuido Günther 1038d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1039d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1040d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 104162a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 104262a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 104362a1efb9SPeter Meerwald 1044d35567fcSMathieu Othacehe if (client->irq && data->chip_spec->irq_support) { 10458fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 10468fe78d52SMathieu Othacehe NULL, 10478fe78d52SMathieu Othacehe vcnl4010_trigger_handler, 10488fe78d52SMathieu Othacehe &vcnl4010_buffer_ops); 10498fe78d52SMathieu Othacehe if (ret < 0) { 10508fe78d52SMathieu Othacehe dev_err(&client->dev, 10518fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 10528fe78d52SMathieu Othacehe return ret; 10538fe78d52SMathieu Othacehe } 10548fe78d52SMathieu Othacehe 1055d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1056d35567fcSMathieu Othacehe NULL, vcnl4010_irq_thread, 1057d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1058d35567fcSMathieu Othacehe IRQF_ONESHOT, 1059d35567fcSMathieu Othacehe "vcnl4010_irq", 1060d35567fcSMathieu Othacehe indio_dev); 1061d35567fcSMathieu Othacehe if (ret < 0) { 1062d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1063d35567fcSMathieu Othacehe return ret; 1064d35567fcSMathieu Othacehe } 10658fe78d52SMathieu Othacehe 10668fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 10678fe78d52SMathieu Othacehe if (ret < 0) 10688fe78d52SMathieu Othacehe return ret; 1069d35567fcSMathieu Othacehe } 1070d35567fcSMathieu Othacehe 10715e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 10725e00708dSGuido Günther if (ret < 0) 10735e00708dSGuido Günther goto fail_poweroff; 10745e00708dSGuido Günther 10755e00708dSGuido Günther ret = iio_device_register(indio_dev); 10765e00708dSGuido Günther if (ret < 0) 10775e00708dSGuido Günther goto fail_poweroff; 10785e00708dSGuido Günther 10795e00708dSGuido Günther pm_runtime_enable(&client->dev); 10805e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 10815e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 10825e00708dSGuido Günther 10835e00708dSGuido Günther return 0; 10845e00708dSGuido Günther fail_poweroff: 10855e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 10865e00708dSGuido Günther return ret; 108762a1efb9SPeter Meerwald } 108862a1efb9SPeter Meerwald 1089ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1090ebd457d5SAngus Ainslie (Purism) { 1091ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 10921436a78cSMarco Felsch .data = (void *)VCNL4000, 1093ebd457d5SAngus Ainslie (Purism) }, 1094ebd457d5SAngus Ainslie (Purism) { 1095ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 10961436a78cSMarco Felsch .data = (void *)VCNL4010, 1097ebd457d5SAngus Ainslie (Purism) }, 1098ebd457d5SAngus Ainslie (Purism) { 10991436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 11001436a78cSMarco Felsch .data = (void *)VCNL4010, 1101ebd457d5SAngus Ainslie (Purism) }, 1102ebd457d5SAngus Ainslie (Purism) { 11037fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 11047fd1c260SMarco Felsch .data = (void *)VCNL4040, 11057fd1c260SMarco Felsch }, 11067fd1c260SMarco Felsch { 1107ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 11081436a78cSMarco Felsch .data = (void *)VCNL4200, 1109ebd457d5SAngus Ainslie (Purism) }, 1110ebd457d5SAngus Ainslie (Purism) {}, 1111ebd457d5SAngus Ainslie (Purism) }; 1112ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1113ebd457d5SAngus Ainslie (Purism) 1114*ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 11155e00708dSGuido Günther { 11165e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 11175e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1118ab91da2fSUwe Kleine-König int ret; 11195e00708dSGuido Günther 11205e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 11215e00708dSGuido Günther pm_runtime_disable(&client->dev); 11225e00708dSGuido Günther iio_device_unregister(indio_dev); 11235e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 11245e00708dSGuido Günther 1125ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1126ab91da2fSUwe Kleine-König if (ret) 1127ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1128ab91da2fSUwe Kleine-König ERR_PTR(ret)); 11295e00708dSGuido Günther } 11305e00708dSGuido Günther 1131cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 11325e00708dSGuido Günther { 11335e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11345e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 11355e00708dSGuido Günther 11365e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 11375e00708dSGuido Günther } 11385e00708dSGuido Günther 1139cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 11405e00708dSGuido Günther { 11415e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 11425e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 11435e00708dSGuido Günther 11445e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 11455e00708dSGuido Günther } 11465e00708dSGuido Günther 1147cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1148cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 11495e00708dSGuido Günther 115062a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 115162a1efb9SPeter Meerwald .driver = { 115262a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1153cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1154ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 115562a1efb9SPeter Meerwald }, 115662a1efb9SPeter Meerwald .probe = vcnl4000_probe, 115762a1efb9SPeter Meerwald .id_table = vcnl4000_id, 11585e00708dSGuido Günther .remove = vcnl4000_remove, 115962a1efb9SPeter Meerwald }; 116062a1efb9SPeter Meerwald 116162a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 116262a1efb9SPeter Meerwald 116362a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 11648fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 116562a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 116662a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1167