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 2085e2c6a2SMårten Lindahl #include <linux/bitfield.h> 2162a1efb9SPeter Meerwald #include <linux/module.h> 2262a1efb9SPeter Meerwald #include <linux/i2c.h> 2362a1efb9SPeter Meerwald #include <linux/err.h> 2462a1efb9SPeter Meerwald #include <linux/delay.h> 255e00708dSGuido Günther #include <linux/pm_runtime.h> 26d35567fcSMathieu Othacehe #include <linux/interrupt.h> 2762a1efb9SPeter Meerwald 288fe78d52SMathieu Othacehe #include <linux/iio/buffer.h> 29d35567fcSMathieu Othacehe #include <linux/iio/events.h> 3062a1efb9SPeter Meerwald #include <linux/iio/iio.h> 3162a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 328fe78d52SMathieu Othacehe #include <linux/iio/trigger.h> 338fe78d52SMathieu Othacehe #include <linux/iio/trigger_consumer.h> 348fe78d52SMathieu Othacehe #include <linux/iio/triggered_buffer.h> 3562a1efb9SPeter Meerwald 3662a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 371ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 381ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 395a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 40be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 4162a1efb9SPeter Meerwald 4262a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 4362a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 44d35567fcSMathieu Othacehe #define VCNL4010_PROX_RATE 0x82 /* Proximity rate */ 4562a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 4662a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 47d35567fcSMathieu Othacehe #define VCNL4010_ALS_PARAM 0x84 /* ALS rate */ 4862a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 4962a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 5062a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 5162a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 5262a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 53d35567fcSMathieu Othacehe #define VCNL4010_INT_CTRL 0x89 /* Interrupt control */ 5462a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 55d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_HI 0x8a /* Low threshold, MSB */ 56d35567fcSMathieu Othacehe #define VCNL4010_LOW_THR_LO 0x8b /* Low threshold, LSB */ 57d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_HI 0x8c /* High threshold, MSB */ 58d35567fcSMathieu Othacehe #define VCNL4010_HIGH_THR_LO 0x8d /* High threshold, LSB */ 59d35567fcSMathieu Othacehe #define VCNL4010_ISR 0x8e /* Interrupt status */ 6062a1efb9SPeter Meerwald 61be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 62be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 6354667612SMårten Lindahl #define VCNL4040_PS_THDL_LM 0x06 /* Proximity threshold low */ 6454667612SMårten Lindahl #define VCNL4040_PS_THDH_LM 0x07 /* Proximity threshold high */ 65*bc292aafSAstrid Rost #define VCNL4040_ALS_THDL_LM 0x02 /* Ambient light threshold low */ 66*bc292aafSAstrid Rost #define VCNL4040_ALS_THDH_LM 0x01 /* Ambient light threshold high */ 67be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 68be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 6954667612SMårten Lindahl #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 70854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 71be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 72be38866fSTomas Novotny 735a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 745a441aadSAngus Ainslie (Purism) 7562a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 76ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 77ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 78ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 79ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 80d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 81d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 82d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 83d35567fcSMathieu Othacehe 84e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 85fea2c97dSAstrid Rost #define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ 86*bc292aafSAstrid Rost #define VCNL4040_ALS_CONF_INT_EN BIT(1) /* Ambient light Interrupt enable */ 87e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8885e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 8954667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 9054667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 9154667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 92*bc292aafSAstrid Rost #define VCNL4040_ALS_RISING BIT(12) /* Ambient Light cross high threshold */ 93*bc292aafSAstrid Rost #define VCNL4040_ALS_FALLING BIT(13) /* Ambient Light cross low threshold */ 94e21b5b1fSMårten Lindahl 95d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 96d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 97d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 98d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 99d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 100d35567fcSMathieu Othacehe 101d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 102d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 103d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 104d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 105d35567fcSMathieu Othacehe 106d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 107d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 108d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 109d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 110d35567fcSMathieu Othacehe 111f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 112f6889c1bSMathieu Othacehe {1, 950000}, 113f6889c1bSMathieu Othacehe {3, 906250}, 114f6889c1bSMathieu Othacehe {7, 812500}, 115f6889c1bSMathieu Othacehe {16, 625000}, 116f6889c1bSMathieu Othacehe {31, 250000}, 117f6889c1bSMathieu Othacehe {62, 500000}, 118f6889c1bSMathieu Othacehe {125, 0}, 119f6889c1bSMathieu Othacehe {250, 0}, 120f6889c1bSMathieu Othacehe }; 12162a1efb9SPeter Meerwald 12285e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 12385e2c6a2SMårten Lindahl {0, 100}, 12485e2c6a2SMårten Lindahl {0, 150}, 12585e2c6a2SMårten Lindahl {0, 200}, 12685e2c6a2SMårten Lindahl {0, 250}, 12785e2c6a2SMårten Lindahl {0, 300}, 12885e2c6a2SMårten Lindahl {0, 350}, 12985e2c6a2SMårten Lindahl {0, 400}, 13085e2c6a2SMårten Lindahl {0, 800}, 13185e2c6a2SMårten Lindahl }; 13285e2c6a2SMårten Lindahl 133e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 134e55c96daSAstrid Rost {0, 96}, 135e55c96daSAstrid Rost {0, 144}, 136e55c96daSAstrid Rost {0, 192}, 137e55c96daSAstrid Rost {0, 384}, 138e55c96daSAstrid Rost {0, 768}, 139e55c96daSAstrid Rost {0, 864}, 140e55c96daSAstrid Rost }; 141e55c96daSAstrid Rost 142fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = { 143fea2c97dSAstrid Rost {0, 80000}, 144fea2c97dSAstrid Rost {0, 160000}, 145fea2c97dSAstrid Rost {0, 320000}, 146fea2c97dSAstrid Rost {0, 640000}, 147fea2c97dSAstrid Rost }; 148fea2c97dSAstrid Rost 149fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = { 150fea2c97dSAstrid Rost {0, 50000}, 151fea2c97dSAstrid Rost {0, 100000}, 152fea2c97dSAstrid Rost {0, 200000}, 153fea2c97dSAstrid Rost {0, 400000}, 154fea2c97dSAstrid Rost }; 155fea2c97dSAstrid Rost 1565e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1575e00708dSGuido Günther 1581ebc787aSTomas Novotny enum vcnl4000_device_ids { 1591ebc787aSTomas Novotny VCNL4000, 16050c50b97STomas Novotny VCNL4010, 1615a441aadSAngus Ainslie (Purism) VCNL4040, 162be38866fSTomas Novotny VCNL4200, 163be38866fSTomas Novotny }; 164be38866fSTomas Novotny 165be38866fSTomas Novotny struct vcnl4200_channel { 166be38866fSTomas Novotny u8 reg; 167be38866fSTomas Novotny ktime_t last_measurement; 168be38866fSTomas Novotny ktime_t sampling_rate; 169be38866fSTomas Novotny struct mutex lock; 1701ebc787aSTomas Novotny }; 1711ebc787aSTomas Novotny 17262a1efb9SPeter Meerwald struct vcnl4000_data { 17362a1efb9SPeter Meerwald struct i2c_client *client; 1741ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1751ebc787aSTomas Novotny int rev; 1761ebc787aSTomas Novotny int al_scale; 17754667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 178*bc292aafSAstrid Rost u8 als_int; /* ambient light interrupt mode*/ 1791ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 180be38866fSTomas Novotny struct mutex vcnl4000_lock; 181be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 182be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 183f5a98e1fSGuido Günther uint32_t near_level; 18462a1efb9SPeter Meerwald }; 18562a1efb9SPeter Meerwald 1861ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1871ebc787aSTomas Novotny const char *prod; 188d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 189d35567fcSMathieu Othacehe const int num_channels; 190d35567fcSMathieu Othacehe const struct iio_info *info; 191bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1921ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1931ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1941ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1955e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 196bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 197bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 198854965b7SAstrid Rost 199854965b7SAstrid Rost u8 int_reg; 200e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 201e55c96daSAstrid Rost const int num_ps_it_times; 202fea2c97dSAstrid Rost const int(*als_it_times)[][2]; 203fea2c97dSAstrid Rost const int num_als_it_times; 204fea2c97dSAstrid Rost const unsigned int ulux_step; 2051ebc787aSTomas Novotny }; 2061ebc787aSTomas Novotny 20762a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 2081ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 20950c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 21050c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 2115a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 212be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 21362a1efb9SPeter Meerwald { } 21462a1efb9SPeter Meerwald }; 21562a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 21662a1efb9SPeter Meerwald 2175e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 2185e00708dSGuido Günther { 2195e00708dSGuido Günther /* no suspend op */ 2205e00708dSGuido Günther return 0; 2215e00708dSGuido Günther } 2225e00708dSGuido Günther 2231ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2241ebc787aSTomas Novotny { 2251ebc787aSTomas Novotny int ret, prod_id; 2261ebc787aSTomas Novotny 2271ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2281ebc787aSTomas Novotny if (ret < 0) 2291ebc787aSTomas Novotny return ret; 2301ebc787aSTomas Novotny 2311ebc787aSTomas Novotny prod_id = ret >> 4; 23258bf9aceSTomas Novotny switch (prod_id) { 23358bf9aceSTomas Novotny case VCNL4000_PROD_ID: 23458bf9aceSTomas Novotny if (data->id != VCNL4000) 23558bf9aceSTomas Novotny dev_warn(&data->client->dev, 23658bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 23758bf9aceSTomas Novotny break; 23858bf9aceSTomas Novotny case VCNL4010_PROD_ID: 23958bf9aceSTomas Novotny if (data->id != VCNL4010) 24058bf9aceSTomas Novotny dev_warn(&data->client->dev, 24158bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 24258bf9aceSTomas Novotny break; 24358bf9aceSTomas Novotny default: 2441ebc787aSTomas Novotny return -ENODEV; 24558bf9aceSTomas Novotny } 2461ebc787aSTomas Novotny 2471ebc787aSTomas Novotny data->rev = ret & 0xf; 2481ebc787aSTomas Novotny data->al_scale = 250000; 249be38866fSTomas Novotny 2505e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 251be38866fSTomas Novotny }; 252be38866fSTomas Novotny 253e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2545e00708dSGuido Günther { 2555e00708dSGuido Günther int ret; 2565e00708dSGuido Günther 257e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 258e21b5b1fSMårten Lindahl 259e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 260e21b5b1fSMårten Lindahl if (ret < 0) 261e21b5b1fSMårten Lindahl goto out; 262e21b5b1fSMårten Lindahl 263e21b5b1fSMårten Lindahl if (en) 264e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 265e21b5b1fSMårten Lindahl else 266e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 267e21b5b1fSMårten Lindahl 268e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 269e21b5b1fSMårten Lindahl 270e21b5b1fSMårten Lindahl out: 271e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 272e21b5b1fSMårten Lindahl 273e21b5b1fSMårten Lindahl return ret; 274e21b5b1fSMårten Lindahl } 275e21b5b1fSMårten Lindahl 276e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 277e21b5b1fSMårten Lindahl { 278e21b5b1fSMårten Lindahl int ret; 279e21b5b1fSMårten Lindahl 280e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 281e21b5b1fSMårten Lindahl 282e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 283e21b5b1fSMårten Lindahl if (ret < 0) 284e21b5b1fSMårten Lindahl goto out; 285e21b5b1fSMårten Lindahl 286e21b5b1fSMårten Lindahl if (en) 287e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 288e21b5b1fSMårten Lindahl else 289e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 290e21b5b1fSMårten Lindahl 291e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 292e21b5b1fSMårten Lindahl 293e21b5b1fSMårten Lindahl out: 294e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 295e21b5b1fSMårten Lindahl 296e21b5b1fSMårten Lindahl return ret; 297e21b5b1fSMårten Lindahl } 298e21b5b1fSMårten Lindahl 299e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 300e21b5b1fSMårten Lindahl { 301e21b5b1fSMårten Lindahl int ret; 302e21b5b1fSMårten Lindahl 30354667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 304*bc292aafSAstrid Rost if (!on && (data->ps_int || data->als_int)) 30554667612SMårten Lindahl return 0; 30654667612SMårten Lindahl 307e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 3085e00708dSGuido Günther if (ret < 0) 3095e00708dSGuido Günther return ret; 3105e00708dSGuido Günther 311e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 3125e00708dSGuido Günther if (ret < 0) 3135e00708dSGuido Günther return ret; 3145e00708dSGuido Günther 3155e00708dSGuido Günther if (on) { 3165e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 3175e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 3185e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 3195e00708dSGuido Günther } 3205e00708dSGuido Günther 3215e00708dSGuido Günther return 0; 3225e00708dSGuido Günther } 3235e00708dSGuido Günther 324be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 325be38866fSTomas Novotny { 3265a441aadSAngus Ainslie (Purism) int ret, id; 327be38866fSTomas Novotny 328be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 329be38866fSTomas Novotny if (ret < 0) 330be38866fSTomas Novotny return ret; 331be38866fSTomas Novotny 3325a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3335a441aadSAngus Ainslie (Purism) 3345a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3355a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3365a441aadSAngus Ainslie (Purism) if (ret < 0) 3375a441aadSAngus Ainslie (Purism) return ret; 3385a441aadSAngus Ainslie (Purism) 3395a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3405a441aadSAngus Ainslie (Purism) 3415a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 342be38866fSTomas Novotny return -ENODEV; 3435a441aadSAngus Ainslie (Purism) } 3445a441aadSAngus Ainslie (Purism) 3455a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 346be38866fSTomas Novotny 347be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 34854667612SMårten Lindahl data->ps_int = 0; 349*bc292aafSAstrid Rost data->als_int = 0; 350be38866fSTomas Novotny 351be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 352be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3535a441aadSAngus Ainslie (Purism) switch (id) { 3545a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 355b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 356b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 357b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 358b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 3595a441aadSAngus Ainslie (Purism) break; 3605a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3612ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3622ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3632ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3642ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 3655a441aadSAngus Ainslie (Purism) break; 3665a441aadSAngus Ainslie (Purism) } 367fea2c97dSAstrid Rost data->al_scale = data->chip_spec->ulux_step; 368be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 369be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3701ebc787aSTomas Novotny 3715e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3725e00708dSGuido Günther if (ret < 0) 3735e00708dSGuido Günther return ret; 3745e00708dSGuido Günther 3751ebc787aSTomas Novotny return 0; 3761ebc787aSTomas Novotny }; 3771ebc787aSTomas Novotny 378816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 379816956c3SMathieu Othacehe { 380816956c3SMathieu Othacehe s32 ret; 381816956c3SMathieu Othacehe 382816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 383816956c3SMathieu Othacehe if (ret < 0) 384816956c3SMathieu Othacehe return ret; 385816956c3SMathieu Othacehe 386816956c3SMathieu Othacehe *val = ret; 387816956c3SMathieu Othacehe return 0; 388816956c3SMathieu Othacehe } 389816956c3SMathieu Othacehe 390816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 391816956c3SMathieu Othacehe { 392816956c3SMathieu Othacehe if (val > U16_MAX) 393816956c3SMathieu Othacehe return -ERANGE; 394816956c3SMathieu Othacehe 395816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 396816956c3SMathieu Othacehe } 397816956c3SMathieu Othacehe 398816956c3SMathieu Othacehe 39962a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 40062a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 40162a1efb9SPeter Meerwald { 40262a1efb9SPeter Meerwald int tries = 20; 40362a1efb9SPeter Meerwald int ret; 40462a1efb9SPeter Meerwald 405be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 406ff34ed6dSPeter Meerwald-Stadler 40762a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 40862a1efb9SPeter Meerwald req_mask); 40962a1efb9SPeter Meerwald if (ret < 0) 410ff34ed6dSPeter Meerwald-Stadler goto fail; 41162a1efb9SPeter Meerwald 41262a1efb9SPeter Meerwald /* wait for data to become ready */ 41362a1efb9SPeter Meerwald while (tries--) { 41462a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 41562a1efb9SPeter Meerwald if (ret < 0) 416ff34ed6dSPeter Meerwald-Stadler goto fail; 41762a1efb9SPeter Meerwald if (ret & rdy_mask) 41862a1efb9SPeter Meerwald break; 41962a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 42062a1efb9SPeter Meerwald } 42162a1efb9SPeter Meerwald 42262a1efb9SPeter Meerwald if (tries < 0) { 42362a1efb9SPeter Meerwald dev_err(&data->client->dev, 42462a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 425ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 426ff34ed6dSPeter Meerwald-Stadler goto fail; 42762a1efb9SPeter Meerwald } 42862a1efb9SPeter Meerwald 429816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 43062a1efb9SPeter Meerwald if (ret < 0) 431ff34ed6dSPeter Meerwald-Stadler goto fail; 43262a1efb9SPeter Meerwald 433be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 43462a1efb9SPeter Meerwald 43562a1efb9SPeter Meerwald return 0; 436ff34ed6dSPeter Meerwald-Stadler 437ff34ed6dSPeter Meerwald-Stadler fail: 438be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 439ff34ed6dSPeter Meerwald-Stadler return ret; 44062a1efb9SPeter Meerwald } 44162a1efb9SPeter Meerwald 442be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 443be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 444be38866fSTomas Novotny { 445be38866fSTomas Novotny int ret; 446be38866fSTomas Novotny s64 delta; 447be38866fSTomas Novotny ktime_t next_measurement; 448be38866fSTomas Novotny 449be38866fSTomas Novotny mutex_lock(&chan->lock); 450be38866fSTomas Novotny 451be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 452be38866fSTomas Novotny chan->sampling_rate); 453be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 454be38866fSTomas Novotny if (delta > 0) 455be38866fSTomas Novotny usleep_range(delta, delta + 500); 456be38866fSTomas Novotny chan->last_measurement = ktime_get(); 457be38866fSTomas Novotny 458be38866fSTomas Novotny mutex_unlock(&chan->lock); 459be38866fSTomas Novotny 460be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 461be38866fSTomas Novotny if (ret < 0) 462be38866fSTomas Novotny return ret; 463be38866fSTomas Novotny 464be38866fSTomas Novotny *val = ret; 465be38866fSTomas Novotny 466be38866fSTomas Novotny return 0; 467be38866fSTomas Novotny } 468be38866fSTomas Novotny 4691ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4701ebc787aSTomas Novotny { 4711ebc787aSTomas Novotny return vcnl4000_measure(data, 4721ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4731ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4741ebc787aSTomas Novotny } 4751ebc787aSTomas Novotny 476be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 477be38866fSTomas Novotny { 478be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 479be38866fSTomas Novotny } 480be38866fSTomas Novotny 4811ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4821ebc787aSTomas Novotny { 4831ebc787aSTomas Novotny return vcnl4000_measure(data, 4841ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4851ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4861ebc787aSTomas Novotny } 4871ebc787aSTomas Novotny 488be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 489be38866fSTomas Novotny { 490be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 491be38866fSTomas Novotny } 492be38866fSTomas Novotny 493f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 494f6889c1bSMathieu Othacehe int *val2) 495f6889c1bSMathieu Othacehe { 496f6889c1bSMathieu Othacehe int ret; 497f6889c1bSMathieu Othacehe 498f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 499f6889c1bSMathieu Othacehe if (ret < 0) 500f6889c1bSMathieu Othacehe return ret; 501f6889c1bSMathieu Othacehe 502f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 503f6889c1bSMathieu Othacehe return -EINVAL; 504f6889c1bSMathieu Othacehe 505f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 506f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 507f6889c1bSMathieu Othacehe 508f6889c1bSMathieu Othacehe return 0; 509f6889c1bSMathieu Othacehe } 510f6889c1bSMathieu Othacehe 511d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 512f5a98e1fSGuido Günther { 513d35567fcSMathieu Othacehe int ret; 514f5a98e1fSGuido Günther 515d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 516d35567fcSMathieu Othacehe if (ret < 0) 517d35567fcSMathieu Othacehe return false; 518d35567fcSMathieu Othacehe 519d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 520f5a98e1fSGuido Günther } 521f5a98e1fSGuido Günther 5225e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 5235e00708dSGuido Günther { 5245e00708dSGuido Günther struct device *dev = &data->client->dev; 5255e00708dSGuido Günther int ret; 5265e00708dSGuido Günther 5275e00708dSGuido Günther if (on) { 528db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5295e00708dSGuido Günther } else { 5305e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5315e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5325e00708dSGuido Günther } 5335e00708dSGuido Günther 5345e00708dSGuido Günther return ret; 5355e00708dSGuido Günther } 5365e00708dSGuido Günther 537fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) 538fea2c97dSAstrid Rost { 539fea2c97dSAstrid Rost int ret; 540fea2c97dSAstrid Rost 541fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 542fea2c97dSAstrid Rost if (ret < 0) 543fea2c97dSAstrid Rost return ret; 544fea2c97dSAstrid Rost 545fea2c97dSAstrid Rost ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 546fea2c97dSAstrid Rost if (ret >= data->chip_spec->num_als_it_times) 547fea2c97dSAstrid Rost return -EINVAL; 548fea2c97dSAstrid Rost 549fea2c97dSAstrid Rost *val = (*data->chip_spec->als_it_times)[ret][0]; 550fea2c97dSAstrid Rost *val2 = (*data->chip_spec->als_it_times)[ret][1]; 551fea2c97dSAstrid Rost 552fea2c97dSAstrid Rost return 0; 553fea2c97dSAstrid Rost } 554fea2c97dSAstrid Rost 555fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) 556fea2c97dSAstrid Rost { 557fea2c97dSAstrid Rost unsigned int i; 558fea2c97dSAstrid Rost int ret; 559fea2c97dSAstrid Rost u16 regval; 560fea2c97dSAstrid Rost 561fea2c97dSAstrid Rost for (i = 0; i < data->chip_spec->num_als_it_times; i++) { 562fea2c97dSAstrid Rost if (val == (*data->chip_spec->als_it_times)[i][1]) 563fea2c97dSAstrid Rost break; 564fea2c97dSAstrid Rost } 565fea2c97dSAstrid Rost 566fea2c97dSAstrid Rost if (i == data->chip_spec->num_als_it_times) 567fea2c97dSAstrid Rost return -EINVAL; 568fea2c97dSAstrid Rost 569fea2c97dSAstrid Rost data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); 570fea2c97dSAstrid Rost data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, 571fea2c97dSAstrid Rost (*data->chip_spec->als_it_times)[0][1]), 572fea2c97dSAstrid Rost val); 573fea2c97dSAstrid Rost 574fea2c97dSAstrid Rost mutex_lock(&data->vcnl4000_lock); 575fea2c97dSAstrid Rost 576fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 577fea2c97dSAstrid Rost if (ret < 0) 578fea2c97dSAstrid Rost goto out_unlock; 579fea2c97dSAstrid Rost 580fea2c97dSAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); 581fea2c97dSAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_IT); 582fea2c97dSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 583fea2c97dSAstrid Rost VCNL4200_AL_CONF, 584fea2c97dSAstrid Rost regval); 585fea2c97dSAstrid Rost 586fea2c97dSAstrid Rost out_unlock: 587fea2c97dSAstrid Rost mutex_unlock(&data->vcnl4000_lock); 588fea2c97dSAstrid Rost return ret; 589fea2c97dSAstrid Rost } 590fea2c97dSAstrid Rost 59185e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 59285e2c6a2SMårten Lindahl { 59385e2c6a2SMårten Lindahl int ret; 59485e2c6a2SMårten Lindahl 59585e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 59685e2c6a2SMårten Lindahl if (ret < 0) 59785e2c6a2SMårten Lindahl return ret; 59885e2c6a2SMårten Lindahl 59985e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 60085e2c6a2SMårten Lindahl 601e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 60285e2c6a2SMårten Lindahl return -EINVAL; 60385e2c6a2SMårten Lindahl 604e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 605e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 60685e2c6a2SMårten Lindahl 60785e2c6a2SMårten Lindahl return 0; 60885e2c6a2SMårten Lindahl } 60985e2c6a2SMårten Lindahl 61085e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 61185e2c6a2SMårten Lindahl { 61285e2c6a2SMårten Lindahl unsigned int i; 61385e2c6a2SMårten Lindahl int ret, index = -1; 61485e2c6a2SMårten Lindahl u16 regval; 61585e2c6a2SMårten Lindahl 616e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 617e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 61885e2c6a2SMårten Lindahl index = i; 61985e2c6a2SMårten Lindahl break; 62085e2c6a2SMårten Lindahl } 62185e2c6a2SMårten Lindahl } 62285e2c6a2SMårten Lindahl 62385e2c6a2SMårten Lindahl if (index < 0) 62485e2c6a2SMårten Lindahl return -EINVAL; 62585e2c6a2SMårten Lindahl 626e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 627e55c96daSAstrid Rost 62885e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 62985e2c6a2SMårten Lindahl 63085e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 63185e2c6a2SMårten Lindahl if (ret < 0) 63285e2c6a2SMårten Lindahl goto out; 63385e2c6a2SMårten Lindahl 63485e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 63585e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 63685e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 63785e2c6a2SMårten Lindahl regval); 63885e2c6a2SMårten Lindahl 63985e2c6a2SMårten Lindahl out: 64085e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 64185e2c6a2SMårten Lindahl return ret; 64285e2c6a2SMårten Lindahl } 64385e2c6a2SMårten Lindahl 64462a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 64562a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 64662a1efb9SPeter Meerwald int *val, int *val2, long mask) 64762a1efb9SPeter Meerwald { 6485d693139SPeter Meerwald-Stadler int ret; 64962a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 65062a1efb9SPeter Meerwald 65162a1efb9SPeter Meerwald switch (mask) { 65262a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 6535e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 6545e00708dSGuido Günther if (ret < 0) 6555e00708dSGuido Günther return ret; 6565e00708dSGuido Günther 65762a1efb9SPeter Meerwald switch (chan->type) { 65862a1efb9SPeter Meerwald case IIO_LIGHT: 6591ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 6604a818643SGuido Günther if (!ret) 6614a818643SGuido Günther ret = IIO_VAL_INT; 6624a818643SGuido Günther break; 66362a1efb9SPeter Meerwald case IIO_PROXIMITY: 6641ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 6654a818643SGuido Günther if (!ret) 6664a818643SGuido Günther ret = IIO_VAL_INT; 6674a818643SGuido Günther break; 66862a1efb9SPeter Meerwald default: 6694a818643SGuido Günther ret = -EINVAL; 67062a1efb9SPeter Meerwald } 6715e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 6724a818643SGuido Günther return ret; 67362a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 6745d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 6755d693139SPeter Meerwald-Stadler return -EINVAL; 6765d693139SPeter Meerwald-Stadler 67762a1efb9SPeter Meerwald *val = 0; 6781ebc787aSTomas Novotny *val2 = data->al_scale; 6795d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 68085e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 6812be17b68SAstrid Rost switch (chan->type) { 682fea2c97dSAstrid Rost case IIO_LIGHT: 683fea2c97dSAstrid Rost ret = vcnl4040_read_als_it(data, val, val2); 684fea2c97dSAstrid Rost break; 6852be17b68SAstrid Rost case IIO_PROXIMITY: 68685e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 6872be17b68SAstrid Rost break; 6882be17b68SAstrid Rost default: 6892be17b68SAstrid Rost return -EINVAL; 6902be17b68SAstrid Rost } 69185e2c6a2SMårten Lindahl if (ret < 0) 69285e2c6a2SMårten Lindahl return ret; 69385e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 69485e2c6a2SMårten Lindahl default: 69585e2c6a2SMårten Lindahl return -EINVAL; 69685e2c6a2SMårten Lindahl } 69785e2c6a2SMårten Lindahl } 69885e2c6a2SMårten Lindahl 69985e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 70085e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 70185e2c6a2SMårten Lindahl int val, int val2, long mask) 70285e2c6a2SMårten Lindahl { 70385e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 70485e2c6a2SMårten Lindahl 70585e2c6a2SMårten Lindahl switch (mask) { 70685e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 70785e2c6a2SMårten Lindahl if (val != 0) 70885e2c6a2SMårten Lindahl return -EINVAL; 7092be17b68SAstrid Rost switch (chan->type) { 710fea2c97dSAstrid Rost case IIO_LIGHT: 711fea2c97dSAstrid Rost return vcnl4040_write_als_it(data, val2); 7122be17b68SAstrid Rost case IIO_PROXIMITY: 71385e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 71485e2c6a2SMårten Lindahl default: 71585e2c6a2SMårten Lindahl return -EINVAL; 71685e2c6a2SMårten Lindahl } 7172be17b68SAstrid Rost default: 7182be17b68SAstrid Rost return -EINVAL; 7192be17b68SAstrid Rost } 72085e2c6a2SMårten Lindahl } 72185e2c6a2SMårten Lindahl 72285e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 72385e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 72485e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 72585e2c6a2SMårten Lindahl long mask) 72685e2c6a2SMårten Lindahl { 727e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 728e55c96daSAstrid Rost 72985e2c6a2SMårten Lindahl switch (mask) { 73085e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 7312be17b68SAstrid Rost switch (chan->type) { 732fea2c97dSAstrid Rost case IIO_LIGHT: 733fea2c97dSAstrid Rost *vals = (int *)(*data->chip_spec->als_it_times); 734fea2c97dSAstrid Rost *length = 2 * data->chip_spec->num_als_it_times; 735fea2c97dSAstrid Rost break; 7362be17b68SAstrid Rost case IIO_PROXIMITY: 737e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 738e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 7392be17b68SAstrid Rost break; 7402be17b68SAstrid Rost default: 7412be17b68SAstrid Rost return -EINVAL; 7422be17b68SAstrid Rost } 7432be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 74485e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 74562a1efb9SPeter Meerwald default: 7465d693139SPeter Meerwald-Stadler return -EINVAL; 74762a1efb9SPeter Meerwald } 74862a1efb9SPeter Meerwald } 74962a1efb9SPeter Meerwald 750d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 751d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 752d35567fcSMathieu Othacehe int *val, int *val2, long mask) 753d35567fcSMathieu Othacehe { 754d35567fcSMathieu Othacehe int ret; 755d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 756d35567fcSMathieu Othacehe 757d35567fcSMathieu Othacehe switch (mask) { 758d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 759d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 760d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 761d35567fcSMathieu Othacehe if (ret) 762d35567fcSMathieu Othacehe return ret; 763d35567fcSMathieu Othacehe 764d35567fcSMathieu Othacehe /* Protect against event capture. */ 765d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 766d35567fcSMathieu Othacehe ret = -EBUSY; 767d35567fcSMathieu Othacehe } else { 768d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 769d35567fcSMathieu Othacehe mask); 770d35567fcSMathieu Othacehe } 771d35567fcSMathieu Othacehe 772d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 773d35567fcSMathieu Othacehe return ret; 774f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 775f6889c1bSMathieu Othacehe switch (chan->type) { 776f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 777f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 778f6889c1bSMathieu Othacehe if (ret < 0) 779f6889c1bSMathieu Othacehe return ret; 780f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 781d35567fcSMathieu Othacehe default: 782d35567fcSMathieu Othacehe return -EINVAL; 783d35567fcSMathieu Othacehe } 784f6889c1bSMathieu Othacehe default: 785f6889c1bSMathieu Othacehe return -EINVAL; 786f6889c1bSMathieu Othacehe } 787f6889c1bSMathieu Othacehe } 788f6889c1bSMathieu Othacehe 789f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 790f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 791f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 792f6889c1bSMathieu Othacehe long mask) 793f6889c1bSMathieu Othacehe { 794f6889c1bSMathieu Othacehe switch (mask) { 795f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 796f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 797f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 798f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 799f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 800f6889c1bSMathieu Othacehe default: 801f6889c1bSMathieu Othacehe return -EINVAL; 802f6889c1bSMathieu Othacehe } 803f6889c1bSMathieu Othacehe } 804f6889c1bSMathieu Othacehe 805f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 806f6889c1bSMathieu Othacehe int val2) 807f6889c1bSMathieu Othacehe { 808f6889c1bSMathieu Othacehe unsigned int i; 809f6889c1bSMathieu Othacehe int index = -1; 810f6889c1bSMathieu Othacehe 811f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 812f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 813f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 814f6889c1bSMathieu Othacehe index = i; 815f6889c1bSMathieu Othacehe break; 816f6889c1bSMathieu Othacehe } 817f6889c1bSMathieu Othacehe } 818f6889c1bSMathieu Othacehe 819f6889c1bSMathieu Othacehe if (index < 0) 820f6889c1bSMathieu Othacehe return -EINVAL; 821f6889c1bSMathieu Othacehe 822f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 823f6889c1bSMathieu Othacehe index); 824f6889c1bSMathieu Othacehe } 825f6889c1bSMathieu Othacehe 826f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 827f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 828f6889c1bSMathieu Othacehe int val, int val2, long mask) 829f6889c1bSMathieu Othacehe { 830f6889c1bSMathieu Othacehe int ret; 831f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 832f6889c1bSMathieu Othacehe 833f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 834f6889c1bSMathieu Othacehe if (ret) 835f6889c1bSMathieu Othacehe return ret; 836f6889c1bSMathieu Othacehe 837f6889c1bSMathieu Othacehe /* Protect against event capture. */ 838f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 839f6889c1bSMathieu Othacehe ret = -EBUSY; 840f6889c1bSMathieu Othacehe goto end; 841f6889c1bSMathieu Othacehe } 842f6889c1bSMathieu Othacehe 843f6889c1bSMathieu Othacehe switch (mask) { 844f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 845f6889c1bSMathieu Othacehe switch (chan->type) { 846f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 847f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 848f6889c1bSMathieu Othacehe goto end; 849f6889c1bSMathieu Othacehe default: 850f6889c1bSMathieu Othacehe ret = -EINVAL; 851f6889c1bSMathieu Othacehe goto end; 852f6889c1bSMathieu Othacehe } 853f6889c1bSMathieu Othacehe default: 854f6889c1bSMathieu Othacehe ret = -EINVAL; 855f6889c1bSMathieu Othacehe goto end; 856f6889c1bSMathieu Othacehe } 857f6889c1bSMathieu Othacehe 858f6889c1bSMathieu Othacehe end: 859f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 860f6889c1bSMathieu Othacehe return ret; 861d35567fcSMathieu Othacehe } 862d35567fcSMathieu Othacehe 863d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 864d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 865d35567fcSMathieu Othacehe enum iio_event_type type, 866d35567fcSMathieu Othacehe enum iio_event_direction dir, 867d35567fcSMathieu Othacehe enum iio_event_info info, 868d35567fcSMathieu Othacehe int *val, int *val2) 869d35567fcSMathieu Othacehe { 870d35567fcSMathieu Othacehe int ret; 871d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 872d35567fcSMathieu Othacehe 873d35567fcSMathieu Othacehe switch (info) { 874d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 875d35567fcSMathieu Othacehe switch (dir) { 876d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 877d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 878d35567fcSMathieu Othacehe val); 879d35567fcSMathieu Othacehe if (ret < 0) 880d35567fcSMathieu Othacehe return ret; 881d35567fcSMathieu Othacehe return IIO_VAL_INT; 882d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 883d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 884d35567fcSMathieu Othacehe val); 885d35567fcSMathieu Othacehe if (ret < 0) 886d35567fcSMathieu Othacehe return ret; 887d35567fcSMathieu Othacehe return IIO_VAL_INT; 888d35567fcSMathieu Othacehe default: 889d35567fcSMathieu Othacehe return -EINVAL; 890d35567fcSMathieu Othacehe } 891d35567fcSMathieu Othacehe default: 892d35567fcSMathieu Othacehe return -EINVAL; 893d35567fcSMathieu Othacehe } 894d35567fcSMathieu Othacehe } 895d35567fcSMathieu Othacehe 896d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 897d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 898d35567fcSMathieu Othacehe enum iio_event_type type, 899d35567fcSMathieu Othacehe enum iio_event_direction dir, 900d35567fcSMathieu Othacehe enum iio_event_info info, 901d35567fcSMathieu Othacehe int val, int val2) 902d35567fcSMathieu Othacehe { 903d35567fcSMathieu Othacehe int ret; 904d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 905d35567fcSMathieu Othacehe 906d35567fcSMathieu Othacehe switch (info) { 907d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 908d35567fcSMathieu Othacehe switch (dir) { 909d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 910d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 911d35567fcSMathieu Othacehe val); 912d35567fcSMathieu Othacehe if (ret < 0) 913d35567fcSMathieu Othacehe return ret; 914d35567fcSMathieu Othacehe return IIO_VAL_INT; 915d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 916d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 917d35567fcSMathieu Othacehe val); 918d35567fcSMathieu Othacehe if (ret < 0) 919d35567fcSMathieu Othacehe return ret; 920d35567fcSMathieu Othacehe return IIO_VAL_INT; 921d35567fcSMathieu Othacehe default: 922d35567fcSMathieu Othacehe return -EINVAL; 923d35567fcSMathieu Othacehe } 924d35567fcSMathieu Othacehe default: 925d35567fcSMathieu Othacehe return -EINVAL; 926d35567fcSMathieu Othacehe } 927d35567fcSMathieu Othacehe } 928d35567fcSMathieu Othacehe 92954667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 93054667612SMårten Lindahl const struct iio_chan_spec *chan, 93154667612SMårten Lindahl enum iio_event_type type, 93254667612SMårten Lindahl enum iio_event_direction dir, 93354667612SMårten Lindahl enum iio_event_info info, 93454667612SMårten Lindahl int *val, int *val2) 93554667612SMårten Lindahl { 93654667612SMårten Lindahl int ret; 93754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 93854667612SMårten Lindahl 9392be17b68SAstrid Rost switch (chan->type) { 940*bc292aafSAstrid Rost case IIO_LIGHT: 941*bc292aafSAstrid Rost switch (info) { 942*bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 943*bc292aafSAstrid Rost switch (dir) { 944*bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 945*bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 946*bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM); 947*bc292aafSAstrid Rost break; 948*bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 949*bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, 950*bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM); 951*bc292aafSAstrid Rost break; 952*bc292aafSAstrid Rost default: 953*bc292aafSAstrid Rost return -EINVAL; 954*bc292aafSAstrid Rost } 955*bc292aafSAstrid Rost break; 956*bc292aafSAstrid Rost default: 957*bc292aafSAstrid Rost return -EINVAL; 958*bc292aafSAstrid Rost } 959*bc292aafSAstrid Rost break; 9602be17b68SAstrid Rost case IIO_PROXIMITY: 9612be17b68SAstrid Rost switch (info) { 9622be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 96354667612SMårten Lindahl switch (dir) { 96454667612SMårten Lindahl case IIO_EV_DIR_RISING: 96554667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 96654667612SMårten Lindahl VCNL4040_PS_THDH_LM); 9672be17b68SAstrid Rost break; 96854667612SMårten Lindahl case IIO_EV_DIR_FALLING: 96954667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 97054667612SMårten Lindahl VCNL4040_PS_THDL_LM); 9712be17b68SAstrid Rost break; 9722be17b68SAstrid Rost default: 9732be17b68SAstrid Rost return -EINVAL; 9742be17b68SAstrid Rost } 9752be17b68SAstrid Rost break; 9762be17b68SAstrid Rost default: 9772be17b68SAstrid Rost return -EINVAL; 9782be17b68SAstrid Rost } 9792be17b68SAstrid Rost break; 9802be17b68SAstrid Rost default: 9812be17b68SAstrid Rost return -EINVAL; 9822be17b68SAstrid Rost } 98354667612SMårten Lindahl if (ret < 0) 98454667612SMårten Lindahl return ret; 98554667612SMårten Lindahl *val = ret; 98654667612SMårten Lindahl return IIO_VAL_INT; 98754667612SMårten Lindahl } 98854667612SMårten Lindahl 98954667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 99054667612SMårten Lindahl const struct iio_chan_spec *chan, 99154667612SMårten Lindahl enum iio_event_type type, 99254667612SMårten Lindahl enum iio_event_direction dir, 99354667612SMårten Lindahl enum iio_event_info info, 99454667612SMårten Lindahl int val, int val2) 99554667612SMårten Lindahl { 99654667612SMårten Lindahl int ret; 99754667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 99854667612SMårten Lindahl 9992be17b68SAstrid Rost switch (chan->type) { 1000*bc292aafSAstrid Rost case IIO_LIGHT: 1001*bc292aafSAstrid Rost switch (info) { 1002*bc292aafSAstrid Rost case IIO_EV_INFO_VALUE: 1003*bc292aafSAstrid Rost switch (dir) { 1004*bc292aafSAstrid Rost case IIO_EV_DIR_RISING: 1005*bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1006*bc292aafSAstrid Rost VCNL4040_ALS_THDH_LM, 1007*bc292aafSAstrid Rost val); 1008*bc292aafSAstrid Rost break; 1009*bc292aafSAstrid Rost case IIO_EV_DIR_FALLING: 1010*bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 1011*bc292aafSAstrid Rost VCNL4040_ALS_THDL_LM, 1012*bc292aafSAstrid Rost val); 1013*bc292aafSAstrid Rost break; 1014*bc292aafSAstrid Rost default: 1015*bc292aafSAstrid Rost return -EINVAL; 1016*bc292aafSAstrid Rost } 1017*bc292aafSAstrid Rost break; 1018*bc292aafSAstrid Rost default: 1019*bc292aafSAstrid Rost return -EINVAL; 1020*bc292aafSAstrid Rost } 1021*bc292aafSAstrid Rost break; 10222be17b68SAstrid Rost case IIO_PROXIMITY: 10232be17b68SAstrid Rost switch (info) { 10242be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 102554667612SMårten Lindahl switch (dir) { 102654667612SMårten Lindahl case IIO_EV_DIR_RISING: 102754667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 10282be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 10292be17b68SAstrid Rost val); 10302be17b68SAstrid Rost break; 103154667612SMårten Lindahl case IIO_EV_DIR_FALLING: 103254667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 10332be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 10342be17b68SAstrid Rost val); 10352be17b68SAstrid Rost break; 103654667612SMårten Lindahl default: 103754667612SMårten Lindahl return -EINVAL; 103854667612SMårten Lindahl } 10392be17b68SAstrid Rost break; 10402be17b68SAstrid Rost default: 10412be17b68SAstrid Rost return -EINVAL; 10422be17b68SAstrid Rost } 10432be17b68SAstrid Rost break; 10442be17b68SAstrid Rost default: 10452be17b68SAstrid Rost return -EINVAL; 10462be17b68SAstrid Rost } 10472be17b68SAstrid Rost if (ret < 0) 10482be17b68SAstrid Rost return ret; 10492be17b68SAstrid Rost return IIO_VAL_INT; 105054667612SMårten Lindahl } 105154667612SMårten Lindahl 1052d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 1053d35567fcSMathieu Othacehe { 1054d35567fcSMathieu Othacehe int ret; 1055d35567fcSMathieu Othacehe 1056d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 1057d35567fcSMathieu Othacehe if (ret < 0) 1058d35567fcSMathieu Othacehe return false; 1059d35567fcSMathieu Othacehe 1060d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 1061d35567fcSMathieu Othacehe } 1062d35567fcSMathieu Othacehe 1063d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 1064d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1065d35567fcSMathieu Othacehe enum iio_event_type type, 1066d35567fcSMathieu Othacehe enum iio_event_direction dir) 1067d35567fcSMathieu Othacehe { 1068d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1069d35567fcSMathieu Othacehe 1070d35567fcSMathieu Othacehe switch (chan->type) { 1071d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1072d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 1073d35567fcSMathieu Othacehe default: 1074d35567fcSMathieu Othacehe return -EINVAL; 1075d35567fcSMathieu Othacehe } 1076d35567fcSMathieu Othacehe } 1077d35567fcSMathieu Othacehe 1078d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 1079d35567fcSMathieu Othacehe { 1080d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1081d35567fcSMathieu Othacehe int ret; 1082d35567fcSMathieu Othacehe int icr; 1083d35567fcSMathieu Othacehe int command; 1084d35567fcSMathieu Othacehe 1085d35567fcSMathieu Othacehe if (state) { 1086d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1087d35567fcSMathieu Othacehe if (ret) 1088d35567fcSMathieu Othacehe return ret; 1089d35567fcSMathieu Othacehe 1090d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 1091d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1092d35567fcSMathieu Othacehe 1093d35567fcSMathieu Othacehe /* 1094d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 1095d35567fcSMathieu Othacehe * default. 1096d35567fcSMathieu Othacehe */ 1097d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 1098d35567fcSMathieu Othacehe } else { 1099d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 1100d35567fcSMathieu Othacehe return 0; 1101d35567fcSMathieu Othacehe 1102d35567fcSMathieu Othacehe command = 0; 1103d35567fcSMathieu Othacehe icr = 0; 1104d35567fcSMathieu Othacehe } 1105d35567fcSMathieu Othacehe 1106d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 1107d35567fcSMathieu Othacehe command); 1108d35567fcSMathieu Othacehe if (ret < 0) 1109d35567fcSMathieu Othacehe goto end; 1110d35567fcSMathieu Othacehe 1111d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 1112d35567fcSMathieu Othacehe 1113d35567fcSMathieu Othacehe end: 1114d35567fcSMathieu Othacehe if (state) 1115d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1116d35567fcSMathieu Othacehe 1117d35567fcSMathieu Othacehe return ret; 1118d35567fcSMathieu Othacehe } 1119d35567fcSMathieu Othacehe 1120d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 1121d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1122d35567fcSMathieu Othacehe enum iio_event_type type, 1123d35567fcSMathieu Othacehe enum iio_event_direction dir, 1124d35567fcSMathieu Othacehe int state) 1125d35567fcSMathieu Othacehe { 1126d35567fcSMathieu Othacehe switch (chan->type) { 1127d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1128d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1129d35567fcSMathieu Othacehe default: 1130d35567fcSMathieu Othacehe return -EINVAL; 1131d35567fcSMathieu Othacehe } 1132d35567fcSMathieu Othacehe } 1133d35567fcSMathieu Othacehe 113454667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 113554667612SMårten Lindahl const struct iio_chan_spec *chan, 113654667612SMårten Lindahl enum iio_event_type type, 113754667612SMårten Lindahl enum iio_event_direction dir) 113854667612SMårten Lindahl { 113954667612SMårten Lindahl int ret; 114054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 114154667612SMårten Lindahl 11422be17b68SAstrid Rost switch (chan->type) { 1143*bc292aafSAstrid Rost case IIO_LIGHT: 1144*bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1145*bc292aafSAstrid Rost if (ret < 0) 1146*bc292aafSAstrid Rost return ret; 1147*bc292aafSAstrid Rost 1148*bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, ret); 1149*bc292aafSAstrid Rost 1150*bc292aafSAstrid Rost return data->als_int; 11512be17b68SAstrid Rost case IIO_PROXIMITY: 115254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 115354667612SMårten Lindahl if (ret < 0) 115454667612SMårten Lindahl return ret; 115554667612SMårten Lindahl 115654667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 115754667612SMårten Lindahl 115854667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 115954667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 116054667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 11612be17b68SAstrid Rost default: 11622be17b68SAstrid Rost return -EINVAL; 11632be17b68SAstrid Rost } 116454667612SMårten Lindahl } 116554667612SMårten Lindahl 116654667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 116754667612SMårten Lindahl const struct iio_chan_spec *chan, 116854667612SMårten Lindahl enum iio_event_type type, 116954667612SMårten Lindahl enum iio_event_direction dir, int state) 117054667612SMårten Lindahl { 11712be17b68SAstrid Rost int ret = -EINVAL; 117254667612SMårten Lindahl u16 val, mask; 117354667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 117454667612SMårten Lindahl 117554667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 117654667612SMårten Lindahl 11772be17b68SAstrid Rost switch (chan->type) { 1178*bc292aafSAstrid Rost case IIO_LIGHT: 1179*bc292aafSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 1180*bc292aafSAstrid Rost if (ret < 0) 1181*bc292aafSAstrid Rost goto out; 1182*bc292aafSAstrid Rost 1183*bc292aafSAstrid Rost mask = VCNL4040_ALS_CONF_INT_EN; 1184*bc292aafSAstrid Rost if (state) 1185*bc292aafSAstrid Rost val = (ret | mask); 1186*bc292aafSAstrid Rost else 1187*bc292aafSAstrid Rost val = (ret & ~mask); 1188*bc292aafSAstrid Rost 1189*bc292aafSAstrid Rost data->als_int = FIELD_GET(VCNL4040_ALS_CONF_INT_EN, val); 1190*bc292aafSAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 1191*bc292aafSAstrid Rost val); 1192*bc292aafSAstrid Rost break; 11932be17b68SAstrid Rost case IIO_PROXIMITY: 119454667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 119554667612SMårten Lindahl if (ret < 0) 119654667612SMårten Lindahl goto out; 119754667612SMårten Lindahl 119854667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 119954667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 120054667612SMårten Lindahl else 120154667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 120254667612SMårten Lindahl 120354667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 120454667612SMårten Lindahl 120554667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 12062be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 12072be17b68SAstrid Rost val); 12082be17b68SAstrid Rost break; 12092be17b68SAstrid Rost default: 12102be17b68SAstrid Rost break; 12112be17b68SAstrid Rost } 121254667612SMårten Lindahl 121354667612SMårten Lindahl out: 121454667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 1215*bc292aafSAstrid Rost data->chip_spec->set_power_state(data, data->ps_int || data->als_int); 121654667612SMårten Lindahl 121754667612SMårten Lindahl return ret; 121854667612SMårten Lindahl } 121954667612SMårten Lindahl 122054667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 122154667612SMårten Lindahl { 122254667612SMårten Lindahl struct iio_dev *indio_dev = p; 122354667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 122454667612SMårten Lindahl int ret; 122554667612SMårten Lindahl 1226854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 122754667612SMårten Lindahl if (ret < 0) 122854667612SMårten Lindahl return IRQ_HANDLED; 122954667612SMårten Lindahl 123054667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 123154667612SMårten Lindahl iio_push_event(indio_dev, 123254667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 123354667612SMårten Lindahl IIO_EV_TYPE_THRESH, 123454667612SMårten Lindahl IIO_EV_DIR_RISING), 123554667612SMårten Lindahl iio_get_time_ns(indio_dev)); 123654667612SMårten Lindahl } 123754667612SMårten Lindahl 123854667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 123954667612SMårten Lindahl iio_push_event(indio_dev, 124054667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 124154667612SMårten Lindahl IIO_EV_TYPE_THRESH, 124254667612SMårten Lindahl IIO_EV_DIR_FALLING), 124354667612SMårten Lindahl iio_get_time_ns(indio_dev)); 124454667612SMårten Lindahl } 124554667612SMårten Lindahl 1246*bc292aafSAstrid Rost if (ret & VCNL4040_ALS_FALLING) { 1247*bc292aafSAstrid Rost iio_push_event(indio_dev, 1248*bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1249*bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1250*bc292aafSAstrid Rost IIO_EV_DIR_FALLING), 1251*bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1252*bc292aafSAstrid Rost } 1253*bc292aafSAstrid Rost 1254*bc292aafSAstrid Rost if (ret & VCNL4040_ALS_RISING) { 1255*bc292aafSAstrid Rost iio_push_event(indio_dev, 1256*bc292aafSAstrid Rost IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, 1257*bc292aafSAstrid Rost IIO_EV_TYPE_THRESH, 1258*bc292aafSAstrid Rost IIO_EV_DIR_RISING), 1259*bc292aafSAstrid Rost iio_get_time_ns(indio_dev)); 1260*bc292aafSAstrid Rost } 1261*bc292aafSAstrid Rost 126254667612SMårten Lindahl return IRQ_HANDLED; 126354667612SMårten Lindahl } 126454667612SMårten Lindahl 1265d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1266d35567fcSMathieu Othacehe uintptr_t priv, 1267d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1268d35567fcSMathieu Othacehe char *buf) 1269d35567fcSMathieu Othacehe { 1270d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1271d35567fcSMathieu Othacehe 1272d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1273d35567fcSMathieu Othacehe } 1274d35567fcSMathieu Othacehe 12753a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 12763a52d32aSMårten Lindahl { 12773a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 12783a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 12793a52d32aSMårten Lindahl unsigned long isr; 12803a52d32aSMårten Lindahl int ret; 12813a52d32aSMårten Lindahl 12823a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 12833a52d32aSMårten Lindahl if (ret < 0) 12843a52d32aSMårten Lindahl goto end; 12853a52d32aSMårten Lindahl 12863a52d32aSMårten Lindahl isr = ret; 12873a52d32aSMårten Lindahl 12883a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 12893a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 12903a52d32aSMårten Lindahl iio_push_event(indio_dev, 12913a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 12923a52d32aSMårten Lindahl IIO_PROXIMITY, 12933a52d32aSMårten Lindahl 1, 12943a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 12953a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 12963a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 12973a52d32aSMårten Lindahl } 12983a52d32aSMårten Lindahl 12993a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 13003a52d32aSMårten Lindahl iio_push_event(indio_dev, 13013a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 13023a52d32aSMårten Lindahl IIO_PROXIMITY, 13033a52d32aSMårten Lindahl 1, 13043a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 13053a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 13063a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 13073a52d32aSMårten Lindahl } 13083a52d32aSMårten Lindahl 13093a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 13103a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 13113a52d32aSMårten Lindahl } 13123a52d32aSMårten Lindahl 13133a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1314f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 13153a52d32aSMårten Lindahl 13163a52d32aSMårten Lindahl end: 13173a52d32aSMårten Lindahl return IRQ_HANDLED; 13183a52d32aSMårten Lindahl } 13193a52d32aSMårten Lindahl 13203a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 13213a52d32aSMårten Lindahl { 13223a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 13233a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 13243a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 13253a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 13263a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 13273a52d32aSMårten Lindahl bool data_read = false; 13283a52d32aSMårten Lindahl unsigned long isr; 13293a52d32aSMårten Lindahl int val = 0; 13303a52d32aSMårten Lindahl int ret; 13313a52d32aSMårten Lindahl 13323a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 13333a52d32aSMårten Lindahl if (ret < 0) 13343a52d32aSMårten Lindahl goto end; 13353a52d32aSMårten Lindahl 13363a52d32aSMårten Lindahl isr = ret; 13373a52d32aSMårten Lindahl 13383a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 13393a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 13403a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 13413a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 13423a52d32aSMårten Lindahl &val); 13433a52d32aSMårten Lindahl if (ret < 0) 13443a52d32aSMårten Lindahl goto end; 13453a52d32aSMårten Lindahl 13463a52d32aSMårten Lindahl buffer[0] = val; 13473a52d32aSMårten Lindahl data_read = true; 13483a52d32aSMårten Lindahl } 13493a52d32aSMårten Lindahl } 13503a52d32aSMårten Lindahl 13513a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 13523a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 13533a52d32aSMårten Lindahl if (ret < 0) 13543a52d32aSMårten Lindahl goto end; 13553a52d32aSMårten Lindahl 13563a52d32aSMårten Lindahl if (!data_read) 13573a52d32aSMårten Lindahl goto end; 13583a52d32aSMårten Lindahl 13593a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 13603a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 13613a52d32aSMårten Lindahl 13623a52d32aSMårten Lindahl end: 13633a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 13643a52d32aSMårten Lindahl return IRQ_HANDLED; 13653a52d32aSMårten Lindahl } 13663a52d32aSMårten Lindahl 13673a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 13683a52d32aSMårten Lindahl { 13693a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 13703a52d32aSMårten Lindahl int ret; 13713a52d32aSMårten Lindahl int cmd; 13723a52d32aSMårten Lindahl 13733a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 13743a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 13753a52d32aSMårten Lindahl return -EBUSY; 13763a52d32aSMårten Lindahl 13773a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 13783a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 13793a52d32aSMårten Lindahl if (ret < 0) 13803a52d32aSMårten Lindahl return ret; 13813a52d32aSMårten Lindahl 13823a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 13833a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 13843a52d32aSMårten Lindahl } 13853a52d32aSMårten Lindahl 13863a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 13873a52d32aSMårten Lindahl { 13883a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 13893a52d32aSMårten Lindahl int ret; 13903a52d32aSMårten Lindahl 13913a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 13923a52d32aSMårten Lindahl if (ret < 0) 13933a52d32aSMårten Lindahl return ret; 13943a52d32aSMårten Lindahl 13953a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 13963a52d32aSMårten Lindahl } 13973a52d32aSMårten Lindahl 13983a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 13993a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 14003a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 14013a52d32aSMårten Lindahl }; 14023a52d32aSMårten Lindahl 1403d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1404d35567fcSMathieu Othacehe { 1405d35567fcSMathieu Othacehe .name = "nearlevel", 1406d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1407d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1408d35567fcSMathieu Othacehe }, 1409d35567fcSMathieu Othacehe { /* sentinel */ } 1410d35567fcSMathieu Othacehe }; 1411d35567fcSMathieu Othacehe 1412d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1413d35567fcSMathieu Othacehe { 1414d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1415d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1416d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1417d35567fcSMathieu Othacehe }, { 1418d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1419d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1420d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1421d35567fcSMathieu Othacehe }, { 1422d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1423d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1424d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1425d35567fcSMathieu Othacehe } 1426d35567fcSMathieu Othacehe }; 1427d35567fcSMathieu Othacehe 142854667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 142954667612SMårten Lindahl { 143054667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 143154667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 143254667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 143354667612SMårten Lindahl }, { 143454667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 143554667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 143654667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 143754667612SMårten Lindahl }, 143854667612SMårten Lindahl }; 143954667612SMårten Lindahl 1440d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1441d35567fcSMathieu Othacehe { 1442d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1443d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1444d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1445d35567fcSMathieu Othacehe }, { 1446d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1447d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1448d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1449d35567fcSMathieu Othacehe } 1450d35567fcSMathieu Othacehe }; 1451d35567fcSMathieu Othacehe 1452d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1453d35567fcSMathieu Othacehe { 1454d35567fcSMathieu Othacehe .type = IIO_LIGHT, 14558fe78d52SMathieu Othacehe .scan_index = -1, 1456d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1457d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1458d35567fcSMathieu Othacehe }, { 1459d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 14608fe78d52SMathieu Othacehe .scan_index = 0, 1461f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1462f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1463f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1464d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1465d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1466d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 14678fe78d52SMathieu Othacehe .scan_type = { 14688fe78d52SMathieu Othacehe .sign = 'u', 14698fe78d52SMathieu Othacehe .realbits = 16, 14708fe78d52SMathieu Othacehe .storagebits = 16, 14718fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1472d35567fcSMathieu Othacehe }, 14738fe78d52SMathieu Othacehe }, 14748fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1475d35567fcSMathieu Othacehe }; 1476d35567fcSMathieu Othacehe 147785e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 147885e2c6a2SMårten Lindahl { 147985e2c6a2SMårten Lindahl .type = IIO_LIGHT, 148085e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1481fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_SCALE) | 1482fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME), 1483fea2c97dSAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 1484*bc292aafSAstrid Rost .event_spec = vcnl4000_event_spec, 1485*bc292aafSAstrid Rost .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 148685e2c6a2SMårten Lindahl }, { 148785e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 148885e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 148985e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 149085e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 149185e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 149254667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 149354667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 149485e2c6a2SMårten Lindahl } 149585e2c6a2SMårten Lindahl }; 149685e2c6a2SMårten Lindahl 149762a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 149862a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 149962a1efb9SPeter Meerwald }; 150062a1efb9SPeter Meerwald 1501d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1502d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1503f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1504f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1505d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1506d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1507d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1508d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1509d35567fcSMathieu Othacehe }; 1510d35567fcSMathieu Othacehe 151185e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 151285e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 151385e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 151454667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 151554667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 151654667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 151754667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 151885e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 151985e2c6a2SMårten Lindahl }; 152085e2c6a2SMårten Lindahl 1521d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1522d35567fcSMathieu Othacehe [VCNL4000] = { 1523d35567fcSMathieu Othacehe .prod = "VCNL4000", 1524d35567fcSMathieu Othacehe .init = vcnl4000_init, 1525d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1526d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1527d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1528d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1529d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1530d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1531d35567fcSMathieu Othacehe }, 1532d35567fcSMathieu Othacehe [VCNL4010] = { 1533d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1534d35567fcSMathieu Othacehe .init = vcnl4000_init, 1535d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1536d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1537d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1538d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1539d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1540d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1541bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1542bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1543bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1544d35567fcSMathieu Othacehe }, 1545d35567fcSMathieu Othacehe [VCNL4040] = { 1546d35567fcSMathieu Othacehe .prod = "VCNL4040", 1547d35567fcSMathieu Othacehe .init = vcnl4200_init, 1548d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1549d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1550d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 155185e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 155285e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 155385e2c6a2SMårten Lindahl .info = &vcnl4040_info, 155454667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1555854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1556e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1557e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1558fea2c97dSAstrid Rost .als_it_times = &vcnl4040_als_it_times, 1559fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), 1560fea2c97dSAstrid Rost .ulux_step = 100000, 1561d35567fcSMathieu Othacehe }, 1562d35567fcSMathieu Othacehe [VCNL4200] = { 1563d35567fcSMathieu Othacehe .prod = "VCNL4200", 1564d35567fcSMathieu Othacehe .init = vcnl4200_init, 1565d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1566d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1567d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1568854965b7SAstrid Rost .channels = vcnl4040_channels, 1569d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1570854965b7SAstrid Rost .info = &vcnl4040_info, 1571854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1572854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1573e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1574e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1575fea2c97dSAstrid Rost .als_it_times = &vcnl4200_als_it_times, 1576fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), 1577fea2c97dSAstrid Rost .ulux_step = 24000, 1578d35567fcSMathieu Othacehe }, 1579d35567fcSMathieu Othacehe }; 1580d35567fcSMathieu Othacehe 15818fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 15828fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 15838fe78d52SMathieu Othacehe }; 15848fe78d52SMathieu Othacehe 15858fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 15868fe78d52SMathieu Othacehe { 15878fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 15888fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 15898fe78d52SMathieu Othacehe struct iio_trigger *trigger; 15908fe78d52SMathieu Othacehe 15918fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 159215ea2878SJonathan Cameron indio_dev->name, 159315ea2878SJonathan Cameron iio_device_id(indio_dev)); 15948fe78d52SMathieu Othacehe if (!trigger) 15958fe78d52SMathieu Othacehe return -ENOMEM; 15968fe78d52SMathieu Othacehe 15978fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 15988fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 15998fe78d52SMathieu Othacehe 16008fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 16018fe78d52SMathieu Othacehe } 16028fe78d52SMathieu Othacehe 1603e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 160462a1efb9SPeter Meerwald { 1605e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 160662a1efb9SPeter Meerwald struct vcnl4000_data *data; 160762a1efb9SPeter Meerwald struct iio_dev *indio_dev; 16081ebc787aSTomas Novotny int ret; 160962a1efb9SPeter Meerwald 16102669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 161162a1efb9SPeter Meerwald if (!indio_dev) 161262a1efb9SPeter Meerwald return -ENOMEM; 161362a1efb9SPeter Meerwald 161462a1efb9SPeter Meerwald data = iio_priv(indio_dev); 161562a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 161662a1efb9SPeter Meerwald data->client = client; 16171ebc787aSTomas Novotny data->id = id->driver_data; 16181ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 161962a1efb9SPeter Meerwald 162042ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 162142ec40b0SMårten Lindahl 16221ebc787aSTomas Novotny ret = data->chip_spec->init(data); 162362a1efb9SPeter Meerwald if (ret < 0) 16242669d723SPeter Meerwald return ret; 162562a1efb9SPeter Meerwald 1626d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 16271ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 162862a1efb9SPeter Meerwald 1629f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1630f5a98e1fSGuido Günther &data->near_level)) 1631f5a98e1fSGuido Günther data->near_level = 0; 1632f5a98e1fSGuido Günther 1633d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1634d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1635d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 163662a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 163762a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 163862a1efb9SPeter Meerwald 1639bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1640bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 16418fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 16428fe78d52SMathieu Othacehe NULL, 1643bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1644bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 16458fe78d52SMathieu Othacehe if (ret < 0) { 16468fe78d52SMathieu Othacehe dev_err(&client->dev, 16478fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 16488fe78d52SMathieu Othacehe return ret; 16498fe78d52SMathieu Othacehe } 1650bfb6cfeeSMårten Lindahl } 16518fe78d52SMathieu Othacehe 1652bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1653d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1654bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1655d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1656d35567fcSMathieu Othacehe IRQF_ONESHOT, 1657bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1658d35567fcSMathieu Othacehe indio_dev); 1659d35567fcSMathieu Othacehe if (ret < 0) { 1660d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1661d35567fcSMathieu Othacehe return ret; 1662d35567fcSMathieu Othacehe } 16638fe78d52SMathieu Othacehe 16648fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 16658fe78d52SMathieu Othacehe if (ret < 0) 16668fe78d52SMathieu Othacehe return ret; 1667d35567fcSMathieu Othacehe } 1668d35567fcSMathieu Othacehe 16695e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 16705e00708dSGuido Günther if (ret < 0) 16715e00708dSGuido Günther goto fail_poweroff; 16725e00708dSGuido Günther 16735e00708dSGuido Günther ret = iio_device_register(indio_dev); 16745e00708dSGuido Günther if (ret < 0) 16755e00708dSGuido Günther goto fail_poweroff; 16765e00708dSGuido Günther 16775e00708dSGuido Günther pm_runtime_enable(&client->dev); 16785e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 16795e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 16805e00708dSGuido Günther 16815e00708dSGuido Günther return 0; 16825e00708dSGuido Günther fail_poweroff: 16835e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 16845e00708dSGuido Günther return ret; 168562a1efb9SPeter Meerwald } 168662a1efb9SPeter Meerwald 1687ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1688ebd457d5SAngus Ainslie (Purism) { 1689ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 16901436a78cSMarco Felsch .data = (void *)VCNL4000, 1691ebd457d5SAngus Ainslie (Purism) }, 1692ebd457d5SAngus Ainslie (Purism) { 1693ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 16941436a78cSMarco Felsch .data = (void *)VCNL4010, 1695ebd457d5SAngus Ainslie (Purism) }, 1696ebd457d5SAngus Ainslie (Purism) { 16971436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 16981436a78cSMarco Felsch .data = (void *)VCNL4010, 1699ebd457d5SAngus Ainslie (Purism) }, 1700ebd457d5SAngus Ainslie (Purism) { 17017fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 17027fd1c260SMarco Felsch .data = (void *)VCNL4040, 17037fd1c260SMarco Felsch }, 17047fd1c260SMarco Felsch { 1705ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 17061436a78cSMarco Felsch .data = (void *)VCNL4200, 1707ebd457d5SAngus Ainslie (Purism) }, 1708ebd457d5SAngus Ainslie (Purism) {}, 1709ebd457d5SAngus Ainslie (Purism) }; 1710ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1711ebd457d5SAngus Ainslie (Purism) 1712ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 17135e00708dSGuido Günther { 17145e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 17155e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1716ab91da2fSUwe Kleine-König int ret; 17175e00708dSGuido Günther 17185e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 17195e00708dSGuido Günther pm_runtime_disable(&client->dev); 17205e00708dSGuido Günther iio_device_unregister(indio_dev); 17215e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 17225e00708dSGuido Günther 1723ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1724ab91da2fSUwe Kleine-König if (ret) 1725ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1726ab91da2fSUwe Kleine-König ERR_PTR(ret)); 17275e00708dSGuido Günther } 17285e00708dSGuido Günther 1729cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 17305e00708dSGuido Günther { 17315e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 17325e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 17335e00708dSGuido Günther 17345e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 17355e00708dSGuido Günther } 17365e00708dSGuido Günther 1737cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 17385e00708dSGuido Günther { 17395e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 17405e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 17415e00708dSGuido Günther 17425e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 17435e00708dSGuido Günther } 17445e00708dSGuido Günther 1745cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1746cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 17475e00708dSGuido Günther 174862a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 174962a1efb9SPeter Meerwald .driver = { 175062a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1751cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1752ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 175362a1efb9SPeter Meerwald }, 17547cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 175562a1efb9SPeter Meerwald .id_table = vcnl4000_id, 17565e00708dSGuido Günther .remove = vcnl4000_remove, 175762a1efb9SPeter Meerwald }; 175862a1efb9SPeter Meerwald 175962a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 176062a1efb9SPeter Meerwald 176162a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 17628fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 176362a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 176462a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1765