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 */ 65be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 66be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 6754667612SMårten Lindahl #define VCNL4040_INT_FLAGS 0x0b /* Interrupt register */ 68854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 69be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 70be38866fSTomas Novotny 715a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 725a441aadSAngus Ainslie (Purism) 7362a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 74ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 75ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 76ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 77ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 78d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 79d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 80d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 81d35567fcSMathieu Othacehe 82e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 83*fea2c97dSAstrid Rost #define VCNL4040_ALS_CONF_IT GENMASK(7, 6) /* Ambient integration time */ 84e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8585e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 8654667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 8754667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 8854667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 89e21b5b1fSMårten Lindahl 90d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 92d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 93d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 94d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 95d35567fcSMathieu Othacehe 96d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 97d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 98d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 99d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 100d35567fcSMathieu Othacehe 101d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 102d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 103d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 104d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 105d35567fcSMathieu Othacehe 106f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 107f6889c1bSMathieu Othacehe {1, 950000}, 108f6889c1bSMathieu Othacehe {3, 906250}, 109f6889c1bSMathieu Othacehe {7, 812500}, 110f6889c1bSMathieu Othacehe {16, 625000}, 111f6889c1bSMathieu Othacehe {31, 250000}, 112f6889c1bSMathieu Othacehe {62, 500000}, 113f6889c1bSMathieu Othacehe {125, 0}, 114f6889c1bSMathieu Othacehe {250, 0}, 115f6889c1bSMathieu Othacehe }; 11662a1efb9SPeter Meerwald 11785e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 11885e2c6a2SMårten Lindahl {0, 100}, 11985e2c6a2SMårten Lindahl {0, 150}, 12085e2c6a2SMårten Lindahl {0, 200}, 12185e2c6a2SMårten Lindahl {0, 250}, 12285e2c6a2SMårten Lindahl {0, 300}, 12385e2c6a2SMårten Lindahl {0, 350}, 12485e2c6a2SMårten Lindahl {0, 400}, 12585e2c6a2SMårten Lindahl {0, 800}, 12685e2c6a2SMårten Lindahl }; 12785e2c6a2SMårten Lindahl 128e55c96daSAstrid Rost static const int vcnl4200_ps_it_times[][2] = { 129e55c96daSAstrid Rost {0, 96}, 130e55c96daSAstrid Rost {0, 144}, 131e55c96daSAstrid Rost {0, 192}, 132e55c96daSAstrid Rost {0, 384}, 133e55c96daSAstrid Rost {0, 768}, 134e55c96daSAstrid Rost {0, 864}, 135e55c96daSAstrid Rost }; 136e55c96daSAstrid Rost 137*fea2c97dSAstrid Rost static const int vcnl4040_als_it_times[][2] = { 138*fea2c97dSAstrid Rost {0, 80000}, 139*fea2c97dSAstrid Rost {0, 160000}, 140*fea2c97dSAstrid Rost {0, 320000}, 141*fea2c97dSAstrid Rost {0, 640000}, 142*fea2c97dSAstrid Rost }; 143*fea2c97dSAstrid Rost 144*fea2c97dSAstrid Rost static const int vcnl4200_als_it_times[][2] = { 145*fea2c97dSAstrid Rost {0, 50000}, 146*fea2c97dSAstrid Rost {0, 100000}, 147*fea2c97dSAstrid Rost {0, 200000}, 148*fea2c97dSAstrid Rost {0, 400000}, 149*fea2c97dSAstrid Rost }; 150*fea2c97dSAstrid Rost 1515e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1525e00708dSGuido Günther 1531ebc787aSTomas Novotny enum vcnl4000_device_ids { 1541ebc787aSTomas Novotny VCNL4000, 15550c50b97STomas Novotny VCNL4010, 1565a441aadSAngus Ainslie (Purism) VCNL4040, 157be38866fSTomas Novotny VCNL4200, 158be38866fSTomas Novotny }; 159be38866fSTomas Novotny 160be38866fSTomas Novotny struct vcnl4200_channel { 161be38866fSTomas Novotny u8 reg; 162be38866fSTomas Novotny ktime_t last_measurement; 163be38866fSTomas Novotny ktime_t sampling_rate; 164be38866fSTomas Novotny struct mutex lock; 1651ebc787aSTomas Novotny }; 1661ebc787aSTomas Novotny 16762a1efb9SPeter Meerwald struct vcnl4000_data { 16862a1efb9SPeter Meerwald struct i2c_client *client; 1691ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1701ebc787aSTomas Novotny int rev; 1711ebc787aSTomas Novotny int al_scale; 17254667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 1731ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 174be38866fSTomas Novotny struct mutex vcnl4000_lock; 175be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 176be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 177f5a98e1fSGuido Günther uint32_t near_level; 17862a1efb9SPeter Meerwald }; 17962a1efb9SPeter Meerwald 1801ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1811ebc787aSTomas Novotny const char *prod; 182d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 183d35567fcSMathieu Othacehe const int num_channels; 184d35567fcSMathieu Othacehe const struct iio_info *info; 185bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1861ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1871ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1881ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1895e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 190bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 191bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 192854965b7SAstrid Rost 193854965b7SAstrid Rost u8 int_reg; 194e55c96daSAstrid Rost const int(*ps_it_times)[][2]; 195e55c96daSAstrid Rost const int num_ps_it_times; 196*fea2c97dSAstrid Rost const int(*als_it_times)[][2]; 197*fea2c97dSAstrid Rost const int num_als_it_times; 198*fea2c97dSAstrid Rost const unsigned int ulux_step; 1991ebc787aSTomas Novotny }; 2001ebc787aSTomas Novotny 20162a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 2021ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 20350c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 20450c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 2055a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 206be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 20762a1efb9SPeter Meerwald { } 20862a1efb9SPeter Meerwald }; 20962a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 21062a1efb9SPeter Meerwald 2115e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 2125e00708dSGuido Günther { 2135e00708dSGuido Günther /* no suspend op */ 2145e00708dSGuido Günther return 0; 2155e00708dSGuido Günther } 2165e00708dSGuido Günther 2171ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 2181ebc787aSTomas Novotny { 2191ebc787aSTomas Novotny int ret, prod_id; 2201ebc787aSTomas Novotny 2211ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 2221ebc787aSTomas Novotny if (ret < 0) 2231ebc787aSTomas Novotny return ret; 2241ebc787aSTomas Novotny 2251ebc787aSTomas Novotny prod_id = ret >> 4; 22658bf9aceSTomas Novotny switch (prod_id) { 22758bf9aceSTomas Novotny case VCNL4000_PROD_ID: 22858bf9aceSTomas Novotny if (data->id != VCNL4000) 22958bf9aceSTomas Novotny dev_warn(&data->client->dev, 23058bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 23158bf9aceSTomas Novotny break; 23258bf9aceSTomas Novotny case VCNL4010_PROD_ID: 23358bf9aceSTomas Novotny if (data->id != VCNL4010) 23458bf9aceSTomas Novotny dev_warn(&data->client->dev, 23558bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 23658bf9aceSTomas Novotny break; 23758bf9aceSTomas Novotny default: 2381ebc787aSTomas Novotny return -ENODEV; 23958bf9aceSTomas Novotny } 2401ebc787aSTomas Novotny 2411ebc787aSTomas Novotny data->rev = ret & 0xf; 2421ebc787aSTomas Novotny data->al_scale = 250000; 243be38866fSTomas Novotny 2445e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 245be38866fSTomas Novotny }; 246be38866fSTomas Novotny 247e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2485e00708dSGuido Günther { 2495e00708dSGuido Günther int ret; 2505e00708dSGuido Günther 251e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 252e21b5b1fSMårten Lindahl 253e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 254e21b5b1fSMårten Lindahl if (ret < 0) 255e21b5b1fSMårten Lindahl goto out; 256e21b5b1fSMårten Lindahl 257e21b5b1fSMårten Lindahl if (en) 258e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 259e21b5b1fSMårten Lindahl else 260e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 261e21b5b1fSMårten Lindahl 262e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 263e21b5b1fSMårten Lindahl 264e21b5b1fSMårten Lindahl out: 265e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 266e21b5b1fSMårten Lindahl 267e21b5b1fSMårten Lindahl return ret; 268e21b5b1fSMårten Lindahl } 269e21b5b1fSMårten Lindahl 270e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 271e21b5b1fSMårten Lindahl { 272e21b5b1fSMårten Lindahl int ret; 273e21b5b1fSMårten Lindahl 274e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 275e21b5b1fSMårten Lindahl 276e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 277e21b5b1fSMårten Lindahl if (ret < 0) 278e21b5b1fSMårten Lindahl goto out; 279e21b5b1fSMårten Lindahl 280e21b5b1fSMårten Lindahl if (en) 281e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 282e21b5b1fSMårten Lindahl else 283e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 284e21b5b1fSMårten Lindahl 285e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 286e21b5b1fSMårten Lindahl 287e21b5b1fSMårten Lindahl out: 288e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 289e21b5b1fSMårten Lindahl 290e21b5b1fSMårten Lindahl return ret; 291e21b5b1fSMårten Lindahl } 292e21b5b1fSMårten Lindahl 293e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 294e21b5b1fSMårten Lindahl { 295e21b5b1fSMårten Lindahl int ret; 296e21b5b1fSMårten Lindahl 29754667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 29854667612SMårten Lindahl if (!on && data->ps_int) 29954667612SMårten Lindahl return 0; 30054667612SMårten Lindahl 301e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 3025e00708dSGuido Günther if (ret < 0) 3035e00708dSGuido Günther return ret; 3045e00708dSGuido Günther 305e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 3065e00708dSGuido Günther if (ret < 0) 3075e00708dSGuido Günther return ret; 3085e00708dSGuido Günther 3095e00708dSGuido Günther if (on) { 3105e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 3115e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 3125e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 3135e00708dSGuido Günther } 3145e00708dSGuido Günther 3155e00708dSGuido Günther return 0; 3165e00708dSGuido Günther } 3175e00708dSGuido Günther 318be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 319be38866fSTomas Novotny { 3205a441aadSAngus Ainslie (Purism) int ret, id; 321be38866fSTomas Novotny 322be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 323be38866fSTomas Novotny if (ret < 0) 324be38866fSTomas Novotny return ret; 325be38866fSTomas Novotny 3265a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3275a441aadSAngus Ainslie (Purism) 3285a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3295a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3305a441aadSAngus Ainslie (Purism) if (ret < 0) 3315a441aadSAngus Ainslie (Purism) return ret; 3325a441aadSAngus Ainslie (Purism) 3335a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3345a441aadSAngus Ainslie (Purism) 3355a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 336be38866fSTomas Novotny return -ENODEV; 3375a441aadSAngus Ainslie (Purism) } 3385a441aadSAngus Ainslie (Purism) 3395a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 340be38866fSTomas Novotny 341be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 34254667612SMårten Lindahl data->ps_int = 0; 343be38866fSTomas Novotny 344be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 345be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3465a441aadSAngus Ainslie (Purism) switch (id) { 3475a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 348b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 349b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 350b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 351b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 3525a441aadSAngus Ainslie (Purism) break; 3535a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3542ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3552ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3562ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3572ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 3585a441aadSAngus Ainslie (Purism) break; 3595a441aadSAngus Ainslie (Purism) } 360*fea2c97dSAstrid Rost data->al_scale = data->chip_spec->ulux_step; 361be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 362be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3631ebc787aSTomas Novotny 3645e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3655e00708dSGuido Günther if (ret < 0) 3665e00708dSGuido Günther return ret; 3675e00708dSGuido Günther 3681ebc787aSTomas Novotny return 0; 3691ebc787aSTomas Novotny }; 3701ebc787aSTomas Novotny 371816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 372816956c3SMathieu Othacehe { 373816956c3SMathieu Othacehe s32 ret; 374816956c3SMathieu Othacehe 375816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 376816956c3SMathieu Othacehe if (ret < 0) 377816956c3SMathieu Othacehe return ret; 378816956c3SMathieu Othacehe 379816956c3SMathieu Othacehe *val = ret; 380816956c3SMathieu Othacehe return 0; 381816956c3SMathieu Othacehe } 382816956c3SMathieu Othacehe 383816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 384816956c3SMathieu Othacehe { 385816956c3SMathieu Othacehe if (val > U16_MAX) 386816956c3SMathieu Othacehe return -ERANGE; 387816956c3SMathieu Othacehe 388816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 389816956c3SMathieu Othacehe } 390816956c3SMathieu Othacehe 391816956c3SMathieu Othacehe 39262a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 39362a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 39462a1efb9SPeter Meerwald { 39562a1efb9SPeter Meerwald int tries = 20; 39662a1efb9SPeter Meerwald int ret; 39762a1efb9SPeter Meerwald 398be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 399ff34ed6dSPeter Meerwald-Stadler 40062a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 40162a1efb9SPeter Meerwald req_mask); 40262a1efb9SPeter Meerwald if (ret < 0) 403ff34ed6dSPeter Meerwald-Stadler goto fail; 40462a1efb9SPeter Meerwald 40562a1efb9SPeter Meerwald /* wait for data to become ready */ 40662a1efb9SPeter Meerwald while (tries--) { 40762a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 40862a1efb9SPeter Meerwald if (ret < 0) 409ff34ed6dSPeter Meerwald-Stadler goto fail; 41062a1efb9SPeter Meerwald if (ret & rdy_mask) 41162a1efb9SPeter Meerwald break; 41262a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 41362a1efb9SPeter Meerwald } 41462a1efb9SPeter Meerwald 41562a1efb9SPeter Meerwald if (tries < 0) { 41662a1efb9SPeter Meerwald dev_err(&data->client->dev, 41762a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 418ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 419ff34ed6dSPeter Meerwald-Stadler goto fail; 42062a1efb9SPeter Meerwald } 42162a1efb9SPeter Meerwald 422816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 42362a1efb9SPeter Meerwald if (ret < 0) 424ff34ed6dSPeter Meerwald-Stadler goto fail; 42562a1efb9SPeter Meerwald 426be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 42762a1efb9SPeter Meerwald 42862a1efb9SPeter Meerwald return 0; 429ff34ed6dSPeter Meerwald-Stadler 430ff34ed6dSPeter Meerwald-Stadler fail: 431be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 432ff34ed6dSPeter Meerwald-Stadler return ret; 43362a1efb9SPeter Meerwald } 43462a1efb9SPeter Meerwald 435be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 436be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 437be38866fSTomas Novotny { 438be38866fSTomas Novotny int ret; 439be38866fSTomas Novotny s64 delta; 440be38866fSTomas Novotny ktime_t next_measurement; 441be38866fSTomas Novotny 442be38866fSTomas Novotny mutex_lock(&chan->lock); 443be38866fSTomas Novotny 444be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 445be38866fSTomas Novotny chan->sampling_rate); 446be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 447be38866fSTomas Novotny if (delta > 0) 448be38866fSTomas Novotny usleep_range(delta, delta + 500); 449be38866fSTomas Novotny chan->last_measurement = ktime_get(); 450be38866fSTomas Novotny 451be38866fSTomas Novotny mutex_unlock(&chan->lock); 452be38866fSTomas Novotny 453be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 454be38866fSTomas Novotny if (ret < 0) 455be38866fSTomas Novotny return ret; 456be38866fSTomas Novotny 457be38866fSTomas Novotny *val = ret; 458be38866fSTomas Novotny 459be38866fSTomas Novotny return 0; 460be38866fSTomas Novotny } 461be38866fSTomas Novotny 4621ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4631ebc787aSTomas Novotny { 4641ebc787aSTomas Novotny return vcnl4000_measure(data, 4651ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4661ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4671ebc787aSTomas Novotny } 4681ebc787aSTomas Novotny 469be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 470be38866fSTomas Novotny { 471be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 472be38866fSTomas Novotny } 473be38866fSTomas Novotny 4741ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4751ebc787aSTomas Novotny { 4761ebc787aSTomas Novotny return vcnl4000_measure(data, 4771ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4781ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4791ebc787aSTomas Novotny } 4801ebc787aSTomas Novotny 481be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 482be38866fSTomas Novotny { 483be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 484be38866fSTomas Novotny } 485be38866fSTomas Novotny 486f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 487f6889c1bSMathieu Othacehe int *val2) 488f6889c1bSMathieu Othacehe { 489f6889c1bSMathieu Othacehe int ret; 490f6889c1bSMathieu Othacehe 491f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 492f6889c1bSMathieu Othacehe if (ret < 0) 493f6889c1bSMathieu Othacehe return ret; 494f6889c1bSMathieu Othacehe 495f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 496f6889c1bSMathieu Othacehe return -EINVAL; 497f6889c1bSMathieu Othacehe 498f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 499f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 500f6889c1bSMathieu Othacehe 501f6889c1bSMathieu Othacehe return 0; 502f6889c1bSMathieu Othacehe } 503f6889c1bSMathieu Othacehe 504d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 505f5a98e1fSGuido Günther { 506d35567fcSMathieu Othacehe int ret; 507f5a98e1fSGuido Günther 508d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 509d35567fcSMathieu Othacehe if (ret < 0) 510d35567fcSMathieu Othacehe return false; 511d35567fcSMathieu Othacehe 512d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 513f5a98e1fSGuido Günther } 514f5a98e1fSGuido Günther 5155e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 5165e00708dSGuido Günther { 5175e00708dSGuido Günther struct device *dev = &data->client->dev; 5185e00708dSGuido Günther int ret; 5195e00708dSGuido Günther 5205e00708dSGuido Günther if (on) { 521db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 5225e00708dSGuido Günther } else { 5235e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 5245e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 5255e00708dSGuido Günther } 5265e00708dSGuido Günther 5275e00708dSGuido Günther return ret; 5285e00708dSGuido Günther } 5295e00708dSGuido Günther 530*fea2c97dSAstrid Rost static int vcnl4040_read_als_it(struct vcnl4000_data *data, int *val, int *val2) 531*fea2c97dSAstrid Rost { 532*fea2c97dSAstrid Rost int ret; 533*fea2c97dSAstrid Rost 534*fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 535*fea2c97dSAstrid Rost if (ret < 0) 536*fea2c97dSAstrid Rost return ret; 537*fea2c97dSAstrid Rost 538*fea2c97dSAstrid Rost ret = FIELD_GET(VCNL4040_ALS_CONF_IT, ret); 539*fea2c97dSAstrid Rost if (ret >= data->chip_spec->num_als_it_times) 540*fea2c97dSAstrid Rost return -EINVAL; 541*fea2c97dSAstrid Rost 542*fea2c97dSAstrid Rost *val = (*data->chip_spec->als_it_times)[ret][0]; 543*fea2c97dSAstrid Rost *val2 = (*data->chip_spec->als_it_times)[ret][1]; 544*fea2c97dSAstrid Rost 545*fea2c97dSAstrid Rost return 0; 546*fea2c97dSAstrid Rost } 547*fea2c97dSAstrid Rost 548*fea2c97dSAstrid Rost static ssize_t vcnl4040_write_als_it(struct vcnl4000_data *data, int val) 549*fea2c97dSAstrid Rost { 550*fea2c97dSAstrid Rost unsigned int i; 551*fea2c97dSAstrid Rost int ret; 552*fea2c97dSAstrid Rost u16 regval; 553*fea2c97dSAstrid Rost 554*fea2c97dSAstrid Rost for (i = 0; i < data->chip_spec->num_als_it_times; i++) { 555*fea2c97dSAstrid Rost if (val == (*data->chip_spec->als_it_times)[i][1]) 556*fea2c97dSAstrid Rost break; 557*fea2c97dSAstrid Rost } 558*fea2c97dSAstrid Rost 559*fea2c97dSAstrid Rost if (i == data->chip_spec->num_als_it_times) 560*fea2c97dSAstrid Rost return -EINVAL; 561*fea2c97dSAstrid Rost 562*fea2c97dSAstrid Rost data->vcnl4200_al.sampling_rate = ktime_set(0, val * 1200); 563*fea2c97dSAstrid Rost data->al_scale = div_u64(mul_u32_u32(data->chip_spec->ulux_step, 564*fea2c97dSAstrid Rost (*data->chip_spec->als_it_times)[0][1]), 565*fea2c97dSAstrid Rost val); 566*fea2c97dSAstrid Rost 567*fea2c97dSAstrid Rost mutex_lock(&data->vcnl4000_lock); 568*fea2c97dSAstrid Rost 569*fea2c97dSAstrid Rost ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 570*fea2c97dSAstrid Rost if (ret < 0) 571*fea2c97dSAstrid Rost goto out_unlock; 572*fea2c97dSAstrid Rost 573*fea2c97dSAstrid Rost regval = FIELD_PREP(VCNL4040_ALS_CONF_IT, i); 574*fea2c97dSAstrid Rost regval |= (ret & ~VCNL4040_ALS_CONF_IT); 575*fea2c97dSAstrid Rost ret = i2c_smbus_write_word_data(data->client, 576*fea2c97dSAstrid Rost VCNL4200_AL_CONF, 577*fea2c97dSAstrid Rost regval); 578*fea2c97dSAstrid Rost 579*fea2c97dSAstrid Rost out_unlock: 580*fea2c97dSAstrid Rost mutex_unlock(&data->vcnl4000_lock); 581*fea2c97dSAstrid Rost return ret; 582*fea2c97dSAstrid Rost } 583*fea2c97dSAstrid Rost 58485e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 58585e2c6a2SMårten Lindahl { 58685e2c6a2SMårten Lindahl int ret; 58785e2c6a2SMårten Lindahl 58885e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 58985e2c6a2SMårten Lindahl if (ret < 0) 59085e2c6a2SMårten Lindahl return ret; 59185e2c6a2SMårten Lindahl 59285e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 59385e2c6a2SMårten Lindahl 594e55c96daSAstrid Rost if (ret >= data->chip_spec->num_ps_it_times) 59585e2c6a2SMårten Lindahl return -EINVAL; 59685e2c6a2SMårten Lindahl 597e55c96daSAstrid Rost *val = (*data->chip_spec->ps_it_times)[ret][0]; 598e55c96daSAstrid Rost *val2 = (*data->chip_spec->ps_it_times)[ret][1]; 59985e2c6a2SMårten Lindahl 60085e2c6a2SMårten Lindahl return 0; 60185e2c6a2SMårten Lindahl } 60285e2c6a2SMårten Lindahl 60385e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 60485e2c6a2SMårten Lindahl { 60585e2c6a2SMårten Lindahl unsigned int i; 60685e2c6a2SMårten Lindahl int ret, index = -1; 60785e2c6a2SMårten Lindahl u16 regval; 60885e2c6a2SMårten Lindahl 609e55c96daSAstrid Rost for (i = 0; i < data->chip_spec->num_ps_it_times; i++) { 610e55c96daSAstrid Rost if (val == (*data->chip_spec->ps_it_times)[i][1]) { 61185e2c6a2SMårten Lindahl index = i; 61285e2c6a2SMårten Lindahl break; 61385e2c6a2SMårten Lindahl } 61485e2c6a2SMårten Lindahl } 61585e2c6a2SMårten Lindahl 61685e2c6a2SMårten Lindahl if (index < 0) 61785e2c6a2SMårten Lindahl return -EINVAL; 61885e2c6a2SMårten Lindahl 619e55c96daSAstrid Rost data->vcnl4200_ps.sampling_rate = ktime_set(0, val * 60 * NSEC_PER_USEC); 620e55c96daSAstrid Rost 62185e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 62285e2c6a2SMårten Lindahl 62385e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 62485e2c6a2SMårten Lindahl if (ret < 0) 62585e2c6a2SMårten Lindahl goto out; 62685e2c6a2SMårten Lindahl 62785e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 62885e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 62985e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 63085e2c6a2SMårten Lindahl regval); 63185e2c6a2SMårten Lindahl 63285e2c6a2SMårten Lindahl out: 63385e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 63485e2c6a2SMårten Lindahl return ret; 63585e2c6a2SMårten Lindahl } 63685e2c6a2SMårten Lindahl 63762a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 63862a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 63962a1efb9SPeter Meerwald int *val, int *val2, long mask) 64062a1efb9SPeter Meerwald { 6415d693139SPeter Meerwald-Stadler int ret; 64262a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 64362a1efb9SPeter Meerwald 64462a1efb9SPeter Meerwald switch (mask) { 64562a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 6465e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 6475e00708dSGuido Günther if (ret < 0) 6485e00708dSGuido Günther return ret; 6495e00708dSGuido Günther 65062a1efb9SPeter Meerwald switch (chan->type) { 65162a1efb9SPeter Meerwald case IIO_LIGHT: 6521ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 6534a818643SGuido Günther if (!ret) 6544a818643SGuido Günther ret = IIO_VAL_INT; 6554a818643SGuido Günther break; 65662a1efb9SPeter Meerwald case IIO_PROXIMITY: 6571ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 6584a818643SGuido Günther if (!ret) 6594a818643SGuido Günther ret = IIO_VAL_INT; 6604a818643SGuido Günther break; 66162a1efb9SPeter Meerwald default: 6624a818643SGuido Günther ret = -EINVAL; 66362a1efb9SPeter Meerwald } 6645e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 6654a818643SGuido Günther return ret; 66662a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 6675d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 6685d693139SPeter Meerwald-Stadler return -EINVAL; 6695d693139SPeter Meerwald-Stadler 67062a1efb9SPeter Meerwald *val = 0; 6711ebc787aSTomas Novotny *val2 = data->al_scale; 6725d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 67385e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 6742be17b68SAstrid Rost switch (chan->type) { 675*fea2c97dSAstrid Rost case IIO_LIGHT: 676*fea2c97dSAstrid Rost ret = vcnl4040_read_als_it(data, val, val2); 677*fea2c97dSAstrid Rost break; 6782be17b68SAstrid Rost case IIO_PROXIMITY: 67985e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 6802be17b68SAstrid Rost break; 6812be17b68SAstrid Rost default: 6822be17b68SAstrid Rost return -EINVAL; 6832be17b68SAstrid Rost } 68485e2c6a2SMårten Lindahl if (ret < 0) 68585e2c6a2SMårten Lindahl return ret; 68685e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 68785e2c6a2SMårten Lindahl default: 68885e2c6a2SMårten Lindahl return -EINVAL; 68985e2c6a2SMårten Lindahl } 69085e2c6a2SMårten Lindahl } 69185e2c6a2SMårten Lindahl 69285e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 69385e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 69485e2c6a2SMårten Lindahl int val, int val2, long mask) 69585e2c6a2SMårten Lindahl { 69685e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 69785e2c6a2SMårten Lindahl 69885e2c6a2SMårten Lindahl switch (mask) { 69985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 70085e2c6a2SMårten Lindahl if (val != 0) 70185e2c6a2SMårten Lindahl return -EINVAL; 7022be17b68SAstrid Rost switch (chan->type) { 703*fea2c97dSAstrid Rost case IIO_LIGHT: 704*fea2c97dSAstrid Rost return vcnl4040_write_als_it(data, val2); 7052be17b68SAstrid Rost case IIO_PROXIMITY: 70685e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 70785e2c6a2SMårten Lindahl default: 70885e2c6a2SMårten Lindahl return -EINVAL; 70985e2c6a2SMårten Lindahl } 7102be17b68SAstrid Rost default: 7112be17b68SAstrid Rost return -EINVAL; 7122be17b68SAstrid Rost } 71385e2c6a2SMårten Lindahl } 71485e2c6a2SMårten Lindahl 71585e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 71685e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 71785e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 71885e2c6a2SMårten Lindahl long mask) 71985e2c6a2SMårten Lindahl { 720e55c96daSAstrid Rost struct vcnl4000_data *data = iio_priv(indio_dev); 721e55c96daSAstrid Rost 72285e2c6a2SMårten Lindahl switch (mask) { 72385e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 7242be17b68SAstrid Rost switch (chan->type) { 725*fea2c97dSAstrid Rost case IIO_LIGHT: 726*fea2c97dSAstrid Rost *vals = (int *)(*data->chip_spec->als_it_times); 727*fea2c97dSAstrid Rost *length = 2 * data->chip_spec->num_als_it_times; 728*fea2c97dSAstrid Rost break; 7292be17b68SAstrid Rost case IIO_PROXIMITY: 730e55c96daSAstrid Rost *vals = (int *)(*data->chip_spec->ps_it_times); 731e55c96daSAstrid Rost *length = 2 * data->chip_spec->num_ps_it_times; 7322be17b68SAstrid Rost break; 7332be17b68SAstrid Rost default: 7342be17b68SAstrid Rost return -EINVAL; 7352be17b68SAstrid Rost } 7362be17b68SAstrid Rost *type = IIO_VAL_INT_PLUS_MICRO; 73785e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 73862a1efb9SPeter Meerwald default: 7395d693139SPeter Meerwald-Stadler return -EINVAL; 74062a1efb9SPeter Meerwald } 74162a1efb9SPeter Meerwald } 74262a1efb9SPeter Meerwald 743d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 744d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 745d35567fcSMathieu Othacehe int *val, int *val2, long mask) 746d35567fcSMathieu Othacehe { 747d35567fcSMathieu Othacehe int ret; 748d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 749d35567fcSMathieu Othacehe 750d35567fcSMathieu Othacehe switch (mask) { 751d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 752d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 753d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 754d35567fcSMathieu Othacehe if (ret) 755d35567fcSMathieu Othacehe return ret; 756d35567fcSMathieu Othacehe 757d35567fcSMathieu Othacehe /* Protect against event capture. */ 758d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 759d35567fcSMathieu Othacehe ret = -EBUSY; 760d35567fcSMathieu Othacehe } else { 761d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 762d35567fcSMathieu Othacehe mask); 763d35567fcSMathieu Othacehe } 764d35567fcSMathieu Othacehe 765d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 766d35567fcSMathieu Othacehe return ret; 767f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 768f6889c1bSMathieu Othacehe switch (chan->type) { 769f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 770f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 771f6889c1bSMathieu Othacehe if (ret < 0) 772f6889c1bSMathieu Othacehe return ret; 773f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 774d35567fcSMathieu Othacehe default: 775d35567fcSMathieu Othacehe return -EINVAL; 776d35567fcSMathieu Othacehe } 777f6889c1bSMathieu Othacehe default: 778f6889c1bSMathieu Othacehe return -EINVAL; 779f6889c1bSMathieu Othacehe } 780f6889c1bSMathieu Othacehe } 781f6889c1bSMathieu Othacehe 782f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 783f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 784f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 785f6889c1bSMathieu Othacehe long mask) 786f6889c1bSMathieu Othacehe { 787f6889c1bSMathieu Othacehe switch (mask) { 788f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 789f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 790f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 791f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 792f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 793f6889c1bSMathieu Othacehe default: 794f6889c1bSMathieu Othacehe return -EINVAL; 795f6889c1bSMathieu Othacehe } 796f6889c1bSMathieu Othacehe } 797f6889c1bSMathieu Othacehe 798f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 799f6889c1bSMathieu Othacehe int val2) 800f6889c1bSMathieu Othacehe { 801f6889c1bSMathieu Othacehe unsigned int i; 802f6889c1bSMathieu Othacehe int index = -1; 803f6889c1bSMathieu Othacehe 804f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 805f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 806f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 807f6889c1bSMathieu Othacehe index = i; 808f6889c1bSMathieu Othacehe break; 809f6889c1bSMathieu Othacehe } 810f6889c1bSMathieu Othacehe } 811f6889c1bSMathieu Othacehe 812f6889c1bSMathieu Othacehe if (index < 0) 813f6889c1bSMathieu Othacehe return -EINVAL; 814f6889c1bSMathieu Othacehe 815f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 816f6889c1bSMathieu Othacehe index); 817f6889c1bSMathieu Othacehe } 818f6889c1bSMathieu Othacehe 819f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 820f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 821f6889c1bSMathieu Othacehe int val, int val2, long mask) 822f6889c1bSMathieu Othacehe { 823f6889c1bSMathieu Othacehe int ret; 824f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 825f6889c1bSMathieu Othacehe 826f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 827f6889c1bSMathieu Othacehe if (ret) 828f6889c1bSMathieu Othacehe return ret; 829f6889c1bSMathieu Othacehe 830f6889c1bSMathieu Othacehe /* Protect against event capture. */ 831f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 832f6889c1bSMathieu Othacehe ret = -EBUSY; 833f6889c1bSMathieu Othacehe goto end; 834f6889c1bSMathieu Othacehe } 835f6889c1bSMathieu Othacehe 836f6889c1bSMathieu Othacehe switch (mask) { 837f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 838f6889c1bSMathieu Othacehe switch (chan->type) { 839f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 840f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 841f6889c1bSMathieu Othacehe goto end; 842f6889c1bSMathieu Othacehe default: 843f6889c1bSMathieu Othacehe ret = -EINVAL; 844f6889c1bSMathieu Othacehe goto end; 845f6889c1bSMathieu Othacehe } 846f6889c1bSMathieu Othacehe default: 847f6889c1bSMathieu Othacehe ret = -EINVAL; 848f6889c1bSMathieu Othacehe goto end; 849f6889c1bSMathieu Othacehe } 850f6889c1bSMathieu Othacehe 851f6889c1bSMathieu Othacehe end: 852f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 853f6889c1bSMathieu Othacehe return ret; 854d35567fcSMathieu Othacehe } 855d35567fcSMathieu Othacehe 856d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 857d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 858d35567fcSMathieu Othacehe enum iio_event_type type, 859d35567fcSMathieu Othacehe enum iio_event_direction dir, 860d35567fcSMathieu Othacehe enum iio_event_info info, 861d35567fcSMathieu Othacehe int *val, int *val2) 862d35567fcSMathieu Othacehe { 863d35567fcSMathieu Othacehe int ret; 864d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 865d35567fcSMathieu Othacehe 866d35567fcSMathieu Othacehe switch (info) { 867d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 868d35567fcSMathieu Othacehe switch (dir) { 869d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 870d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 871d35567fcSMathieu Othacehe val); 872d35567fcSMathieu Othacehe if (ret < 0) 873d35567fcSMathieu Othacehe return ret; 874d35567fcSMathieu Othacehe return IIO_VAL_INT; 875d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 876d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 877d35567fcSMathieu Othacehe val); 878d35567fcSMathieu Othacehe if (ret < 0) 879d35567fcSMathieu Othacehe return ret; 880d35567fcSMathieu Othacehe return IIO_VAL_INT; 881d35567fcSMathieu Othacehe default: 882d35567fcSMathieu Othacehe return -EINVAL; 883d35567fcSMathieu Othacehe } 884d35567fcSMathieu Othacehe default: 885d35567fcSMathieu Othacehe return -EINVAL; 886d35567fcSMathieu Othacehe } 887d35567fcSMathieu Othacehe } 888d35567fcSMathieu Othacehe 889d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 890d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 891d35567fcSMathieu Othacehe enum iio_event_type type, 892d35567fcSMathieu Othacehe enum iio_event_direction dir, 893d35567fcSMathieu Othacehe enum iio_event_info info, 894d35567fcSMathieu Othacehe int val, int val2) 895d35567fcSMathieu Othacehe { 896d35567fcSMathieu Othacehe int ret; 897d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 898d35567fcSMathieu Othacehe 899d35567fcSMathieu Othacehe switch (info) { 900d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 901d35567fcSMathieu Othacehe switch (dir) { 902d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 903d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 904d35567fcSMathieu Othacehe val); 905d35567fcSMathieu Othacehe if (ret < 0) 906d35567fcSMathieu Othacehe return ret; 907d35567fcSMathieu Othacehe return IIO_VAL_INT; 908d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 909d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 910d35567fcSMathieu Othacehe val); 911d35567fcSMathieu Othacehe if (ret < 0) 912d35567fcSMathieu Othacehe return ret; 913d35567fcSMathieu Othacehe return IIO_VAL_INT; 914d35567fcSMathieu Othacehe default: 915d35567fcSMathieu Othacehe return -EINVAL; 916d35567fcSMathieu Othacehe } 917d35567fcSMathieu Othacehe default: 918d35567fcSMathieu Othacehe return -EINVAL; 919d35567fcSMathieu Othacehe } 920d35567fcSMathieu Othacehe } 921d35567fcSMathieu Othacehe 92254667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 92354667612SMårten Lindahl const struct iio_chan_spec *chan, 92454667612SMårten Lindahl enum iio_event_type type, 92554667612SMårten Lindahl enum iio_event_direction dir, 92654667612SMårten Lindahl enum iio_event_info info, 92754667612SMårten Lindahl int *val, int *val2) 92854667612SMårten Lindahl { 92954667612SMårten Lindahl int ret; 93054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 93154667612SMårten Lindahl 9322be17b68SAstrid Rost switch (chan->type) { 9332be17b68SAstrid Rost case IIO_PROXIMITY: 9342be17b68SAstrid Rost switch (info) { 9352be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 93654667612SMårten Lindahl switch (dir) { 93754667612SMårten Lindahl case IIO_EV_DIR_RISING: 93854667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 93954667612SMårten Lindahl VCNL4040_PS_THDH_LM); 9402be17b68SAstrid Rost break; 94154667612SMårten Lindahl case IIO_EV_DIR_FALLING: 94254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 94354667612SMårten Lindahl VCNL4040_PS_THDL_LM); 9442be17b68SAstrid Rost break; 9452be17b68SAstrid Rost default: 9462be17b68SAstrid Rost return -EINVAL; 9472be17b68SAstrid Rost } 9482be17b68SAstrid Rost break; 9492be17b68SAstrid Rost default: 9502be17b68SAstrid Rost return -EINVAL; 9512be17b68SAstrid Rost } 9522be17b68SAstrid Rost break; 9532be17b68SAstrid Rost default: 9542be17b68SAstrid Rost return -EINVAL; 9552be17b68SAstrid Rost } 95654667612SMårten Lindahl if (ret < 0) 95754667612SMårten Lindahl return ret; 95854667612SMårten Lindahl *val = ret; 95954667612SMårten Lindahl return IIO_VAL_INT; 96054667612SMårten Lindahl } 96154667612SMårten Lindahl 96254667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 96354667612SMårten Lindahl const struct iio_chan_spec *chan, 96454667612SMårten Lindahl enum iio_event_type type, 96554667612SMårten Lindahl enum iio_event_direction dir, 96654667612SMårten Lindahl enum iio_event_info info, 96754667612SMårten Lindahl int val, int val2) 96854667612SMårten Lindahl { 96954667612SMårten Lindahl int ret; 97054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 97154667612SMårten Lindahl 9722be17b68SAstrid Rost switch (chan->type) { 9732be17b68SAstrid Rost case IIO_PROXIMITY: 9742be17b68SAstrid Rost switch (info) { 9752be17b68SAstrid Rost case IIO_EV_INFO_VALUE: 97654667612SMårten Lindahl switch (dir) { 97754667612SMårten Lindahl case IIO_EV_DIR_RISING: 97854667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 9792be17b68SAstrid Rost VCNL4040_PS_THDH_LM, 9802be17b68SAstrid Rost val); 9812be17b68SAstrid Rost break; 98254667612SMårten Lindahl case IIO_EV_DIR_FALLING: 98354667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 9842be17b68SAstrid Rost VCNL4040_PS_THDL_LM, 9852be17b68SAstrid Rost val); 9862be17b68SAstrid Rost break; 98754667612SMårten Lindahl default: 98854667612SMårten Lindahl return -EINVAL; 98954667612SMårten Lindahl } 9902be17b68SAstrid Rost break; 9912be17b68SAstrid Rost default: 9922be17b68SAstrid Rost return -EINVAL; 9932be17b68SAstrid Rost } 9942be17b68SAstrid Rost break; 9952be17b68SAstrid Rost default: 9962be17b68SAstrid Rost return -EINVAL; 9972be17b68SAstrid Rost } 9982be17b68SAstrid Rost if (ret < 0) 9992be17b68SAstrid Rost return ret; 10002be17b68SAstrid Rost return IIO_VAL_INT; 100154667612SMårten Lindahl } 100254667612SMårten Lindahl 1003d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 1004d35567fcSMathieu Othacehe { 1005d35567fcSMathieu Othacehe int ret; 1006d35567fcSMathieu Othacehe 1007d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 1008d35567fcSMathieu Othacehe if (ret < 0) 1009d35567fcSMathieu Othacehe return false; 1010d35567fcSMathieu Othacehe 1011d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 1012d35567fcSMathieu Othacehe } 1013d35567fcSMathieu Othacehe 1014d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 1015d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1016d35567fcSMathieu Othacehe enum iio_event_type type, 1017d35567fcSMathieu Othacehe enum iio_event_direction dir) 1018d35567fcSMathieu Othacehe { 1019d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1020d35567fcSMathieu Othacehe 1021d35567fcSMathieu Othacehe switch (chan->type) { 1022d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1023d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 1024d35567fcSMathieu Othacehe default: 1025d35567fcSMathieu Othacehe return -EINVAL; 1026d35567fcSMathieu Othacehe } 1027d35567fcSMathieu Othacehe } 1028d35567fcSMathieu Othacehe 1029d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 1030d35567fcSMathieu Othacehe { 1031d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1032d35567fcSMathieu Othacehe int ret; 1033d35567fcSMathieu Othacehe int icr; 1034d35567fcSMathieu Othacehe int command; 1035d35567fcSMathieu Othacehe 1036d35567fcSMathieu Othacehe if (state) { 1037d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 1038d35567fcSMathieu Othacehe if (ret) 1039d35567fcSMathieu Othacehe return ret; 1040d35567fcSMathieu Othacehe 1041d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 1042d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1043d35567fcSMathieu Othacehe 1044d35567fcSMathieu Othacehe /* 1045d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 1046d35567fcSMathieu Othacehe * default. 1047d35567fcSMathieu Othacehe */ 1048d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 1049d35567fcSMathieu Othacehe } else { 1050d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 1051d35567fcSMathieu Othacehe return 0; 1052d35567fcSMathieu Othacehe 1053d35567fcSMathieu Othacehe command = 0; 1054d35567fcSMathieu Othacehe icr = 0; 1055d35567fcSMathieu Othacehe } 1056d35567fcSMathieu Othacehe 1057d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 1058d35567fcSMathieu Othacehe command); 1059d35567fcSMathieu Othacehe if (ret < 0) 1060d35567fcSMathieu Othacehe goto end; 1061d35567fcSMathieu Othacehe 1062d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 1063d35567fcSMathieu Othacehe 1064d35567fcSMathieu Othacehe end: 1065d35567fcSMathieu Othacehe if (state) 1066d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 1067d35567fcSMathieu Othacehe 1068d35567fcSMathieu Othacehe return ret; 1069d35567fcSMathieu Othacehe } 1070d35567fcSMathieu Othacehe 1071d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 1072d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1073d35567fcSMathieu Othacehe enum iio_event_type type, 1074d35567fcSMathieu Othacehe enum iio_event_direction dir, 1075d35567fcSMathieu Othacehe int state) 1076d35567fcSMathieu Othacehe { 1077d35567fcSMathieu Othacehe switch (chan->type) { 1078d35567fcSMathieu Othacehe case IIO_PROXIMITY: 1079d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 1080d35567fcSMathieu Othacehe default: 1081d35567fcSMathieu Othacehe return -EINVAL; 1082d35567fcSMathieu Othacehe } 1083d35567fcSMathieu Othacehe } 1084d35567fcSMathieu Othacehe 108554667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 108654667612SMårten Lindahl const struct iio_chan_spec *chan, 108754667612SMårten Lindahl enum iio_event_type type, 108854667612SMårten Lindahl enum iio_event_direction dir) 108954667612SMårten Lindahl { 109054667612SMårten Lindahl int ret; 109154667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 109254667612SMårten Lindahl 10932be17b68SAstrid Rost switch (chan->type) { 10942be17b68SAstrid Rost case IIO_PROXIMITY: 109554667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 109654667612SMårten Lindahl if (ret < 0) 109754667612SMårten Lindahl return ret; 109854667612SMårten Lindahl 109954667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 110054667612SMårten Lindahl 110154667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 110254667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 110354667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 11042be17b68SAstrid Rost default: 11052be17b68SAstrid Rost return -EINVAL; 11062be17b68SAstrid Rost } 110754667612SMårten Lindahl } 110854667612SMårten Lindahl 110954667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 111054667612SMårten Lindahl const struct iio_chan_spec *chan, 111154667612SMårten Lindahl enum iio_event_type type, 111254667612SMårten Lindahl enum iio_event_direction dir, int state) 111354667612SMårten Lindahl { 11142be17b68SAstrid Rost int ret = -EINVAL; 111554667612SMårten Lindahl u16 val, mask; 111654667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 111754667612SMårten Lindahl 111854667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 111954667612SMårten Lindahl 11202be17b68SAstrid Rost switch (chan->type) { 11212be17b68SAstrid Rost case IIO_PROXIMITY: 112254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 112354667612SMårten Lindahl if (ret < 0) 112454667612SMårten Lindahl goto out; 112554667612SMårten Lindahl 112654667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 112754667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 112854667612SMårten Lindahl else 112954667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 113054667612SMårten Lindahl 113154667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 113254667612SMårten Lindahl 113354667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 11342be17b68SAstrid Rost ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 11352be17b68SAstrid Rost val); 11362be17b68SAstrid Rost break; 11372be17b68SAstrid Rost default: 11382be17b68SAstrid Rost break; 11392be17b68SAstrid Rost } 114054667612SMårten Lindahl 114154667612SMårten Lindahl out: 114254667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 114354667612SMårten Lindahl data->chip_spec->set_power_state(data, data->ps_int != 0); 114454667612SMårten Lindahl 114554667612SMårten Lindahl return ret; 114654667612SMårten Lindahl } 114754667612SMårten Lindahl 114854667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 114954667612SMårten Lindahl { 115054667612SMårten Lindahl struct iio_dev *indio_dev = p; 115154667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 115254667612SMårten Lindahl int ret; 115354667612SMårten Lindahl 1154854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 115554667612SMårten Lindahl if (ret < 0) 115654667612SMårten Lindahl return IRQ_HANDLED; 115754667612SMårten Lindahl 115854667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 115954667612SMårten Lindahl iio_push_event(indio_dev, 116054667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 116154667612SMårten Lindahl IIO_EV_TYPE_THRESH, 116254667612SMårten Lindahl IIO_EV_DIR_RISING), 116354667612SMårten Lindahl iio_get_time_ns(indio_dev)); 116454667612SMårten Lindahl } 116554667612SMårten Lindahl 116654667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 116754667612SMårten Lindahl iio_push_event(indio_dev, 116854667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 116954667612SMårten Lindahl IIO_EV_TYPE_THRESH, 117054667612SMårten Lindahl IIO_EV_DIR_FALLING), 117154667612SMårten Lindahl iio_get_time_ns(indio_dev)); 117254667612SMårten Lindahl } 117354667612SMårten Lindahl 117454667612SMårten Lindahl return IRQ_HANDLED; 117554667612SMårten Lindahl } 117654667612SMårten Lindahl 1177d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1178d35567fcSMathieu Othacehe uintptr_t priv, 1179d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1180d35567fcSMathieu Othacehe char *buf) 1181d35567fcSMathieu Othacehe { 1182d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1183d35567fcSMathieu Othacehe 1184d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1185d35567fcSMathieu Othacehe } 1186d35567fcSMathieu Othacehe 11873a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 11883a52d32aSMårten Lindahl { 11893a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 11903a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11913a52d32aSMårten Lindahl unsigned long isr; 11923a52d32aSMårten Lindahl int ret; 11933a52d32aSMårten Lindahl 11943a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 11953a52d32aSMårten Lindahl if (ret < 0) 11963a52d32aSMårten Lindahl goto end; 11973a52d32aSMårten Lindahl 11983a52d32aSMårten Lindahl isr = ret; 11993a52d32aSMårten Lindahl 12003a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 12013a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 12023a52d32aSMårten Lindahl iio_push_event(indio_dev, 12033a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 12043a52d32aSMårten Lindahl IIO_PROXIMITY, 12053a52d32aSMårten Lindahl 1, 12063a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 12073a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 12083a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 12093a52d32aSMårten Lindahl } 12103a52d32aSMårten Lindahl 12113a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 12123a52d32aSMårten Lindahl iio_push_event(indio_dev, 12133a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 12143a52d32aSMårten Lindahl IIO_PROXIMITY, 12153a52d32aSMårten Lindahl 1, 12163a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 12173a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 12183a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 12193a52d32aSMårten Lindahl } 12203a52d32aSMårten Lindahl 12213a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 12223a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 12233a52d32aSMårten Lindahl } 12243a52d32aSMårten Lindahl 12253a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1226f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 12273a52d32aSMårten Lindahl 12283a52d32aSMårten Lindahl end: 12293a52d32aSMårten Lindahl return IRQ_HANDLED; 12303a52d32aSMårten Lindahl } 12313a52d32aSMårten Lindahl 12323a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 12333a52d32aSMårten Lindahl { 12343a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 12353a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 12363a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 12373a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 12383a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 12393a52d32aSMårten Lindahl bool data_read = false; 12403a52d32aSMårten Lindahl unsigned long isr; 12413a52d32aSMårten Lindahl int val = 0; 12423a52d32aSMårten Lindahl int ret; 12433a52d32aSMårten Lindahl 12443a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 12453a52d32aSMårten Lindahl if (ret < 0) 12463a52d32aSMårten Lindahl goto end; 12473a52d32aSMårten Lindahl 12483a52d32aSMårten Lindahl isr = ret; 12493a52d32aSMårten Lindahl 12503a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 12513a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 12523a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 12533a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 12543a52d32aSMårten Lindahl &val); 12553a52d32aSMårten Lindahl if (ret < 0) 12563a52d32aSMårten Lindahl goto end; 12573a52d32aSMårten Lindahl 12583a52d32aSMårten Lindahl buffer[0] = val; 12593a52d32aSMårten Lindahl data_read = true; 12603a52d32aSMårten Lindahl } 12613a52d32aSMårten Lindahl } 12623a52d32aSMårten Lindahl 12633a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 12643a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 12653a52d32aSMårten Lindahl if (ret < 0) 12663a52d32aSMårten Lindahl goto end; 12673a52d32aSMårten Lindahl 12683a52d32aSMårten Lindahl if (!data_read) 12693a52d32aSMårten Lindahl goto end; 12703a52d32aSMårten Lindahl 12713a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 12723a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 12733a52d32aSMårten Lindahl 12743a52d32aSMårten Lindahl end: 12753a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 12763a52d32aSMårten Lindahl return IRQ_HANDLED; 12773a52d32aSMårten Lindahl } 12783a52d32aSMårten Lindahl 12793a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 12803a52d32aSMårten Lindahl { 12813a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 12823a52d32aSMårten Lindahl int ret; 12833a52d32aSMårten Lindahl int cmd; 12843a52d32aSMårten Lindahl 12853a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 12863a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 12873a52d32aSMårten Lindahl return -EBUSY; 12883a52d32aSMårten Lindahl 12893a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 12903a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 12913a52d32aSMårten Lindahl if (ret < 0) 12923a52d32aSMårten Lindahl return ret; 12933a52d32aSMårten Lindahl 12943a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 12953a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 12963a52d32aSMårten Lindahl } 12973a52d32aSMårten Lindahl 12983a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 12993a52d32aSMårten Lindahl { 13003a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 13013a52d32aSMårten Lindahl int ret; 13023a52d32aSMårten Lindahl 13033a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 13043a52d32aSMårten Lindahl if (ret < 0) 13053a52d32aSMårten Lindahl return ret; 13063a52d32aSMårten Lindahl 13073a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 13083a52d32aSMårten Lindahl } 13093a52d32aSMårten Lindahl 13103a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 13113a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 13123a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 13133a52d32aSMårten Lindahl }; 13143a52d32aSMårten Lindahl 1315d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1316d35567fcSMathieu Othacehe { 1317d35567fcSMathieu Othacehe .name = "nearlevel", 1318d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1319d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1320d35567fcSMathieu Othacehe }, 1321d35567fcSMathieu Othacehe { /* sentinel */ } 1322d35567fcSMathieu Othacehe }; 1323d35567fcSMathieu Othacehe 1324d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1325d35567fcSMathieu Othacehe { 1326d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1327d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1328d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1329d35567fcSMathieu Othacehe }, { 1330d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1331d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1332d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1333d35567fcSMathieu Othacehe }, { 1334d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1335d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1336d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1337d35567fcSMathieu Othacehe } 1338d35567fcSMathieu Othacehe }; 1339d35567fcSMathieu Othacehe 134054667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 134154667612SMårten Lindahl { 134254667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 134354667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 134454667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 134554667612SMårten Lindahl }, { 134654667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 134754667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 134854667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 134954667612SMårten Lindahl }, 135054667612SMårten Lindahl }; 135154667612SMårten Lindahl 1352d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1353d35567fcSMathieu Othacehe { 1354d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1355d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1356d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1357d35567fcSMathieu Othacehe }, { 1358d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1359d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1360d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1361d35567fcSMathieu Othacehe } 1362d35567fcSMathieu Othacehe }; 1363d35567fcSMathieu Othacehe 1364d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1365d35567fcSMathieu Othacehe { 1366d35567fcSMathieu Othacehe .type = IIO_LIGHT, 13678fe78d52SMathieu Othacehe .scan_index = -1, 1368d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1369d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1370d35567fcSMathieu Othacehe }, { 1371d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 13728fe78d52SMathieu Othacehe .scan_index = 0, 1373f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1374f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1375f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1376d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1377d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1378d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 13798fe78d52SMathieu Othacehe .scan_type = { 13808fe78d52SMathieu Othacehe .sign = 'u', 13818fe78d52SMathieu Othacehe .realbits = 16, 13828fe78d52SMathieu Othacehe .storagebits = 16, 13838fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1384d35567fcSMathieu Othacehe }, 13858fe78d52SMathieu Othacehe }, 13868fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1387d35567fcSMathieu Othacehe }; 1388d35567fcSMathieu Othacehe 138985e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 139085e2c6a2SMårten Lindahl { 139185e2c6a2SMårten Lindahl .type = IIO_LIGHT, 139285e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1393*fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_SCALE) | 1394*fea2c97dSAstrid Rost BIT(IIO_CHAN_INFO_INT_TIME), 1395*fea2c97dSAstrid Rost .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 139685e2c6a2SMårten Lindahl }, { 139785e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 139885e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 139985e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 140085e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 140185e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 140254667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 140354667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 140485e2c6a2SMårten Lindahl } 140585e2c6a2SMårten Lindahl }; 140685e2c6a2SMårten Lindahl 140762a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 140862a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 140962a1efb9SPeter Meerwald }; 141062a1efb9SPeter Meerwald 1411d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1412d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1413f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1414f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1415d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1416d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1417d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1418d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1419d35567fcSMathieu Othacehe }; 1420d35567fcSMathieu Othacehe 142185e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 142285e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 142385e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 142454667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 142554667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 142654667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 142754667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 142885e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 142985e2c6a2SMårten Lindahl }; 143085e2c6a2SMårten Lindahl 1431d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1432d35567fcSMathieu Othacehe [VCNL4000] = { 1433d35567fcSMathieu Othacehe .prod = "VCNL4000", 1434d35567fcSMathieu Othacehe .init = vcnl4000_init, 1435d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1436d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1437d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1438d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1439d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1440d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1441d35567fcSMathieu Othacehe }, 1442d35567fcSMathieu Othacehe [VCNL4010] = { 1443d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1444d35567fcSMathieu Othacehe .init = vcnl4000_init, 1445d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1446d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1447d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1448d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1449d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1450d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1451bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1452bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1453bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1454d35567fcSMathieu Othacehe }, 1455d35567fcSMathieu Othacehe [VCNL4040] = { 1456d35567fcSMathieu Othacehe .prod = "VCNL4040", 1457d35567fcSMathieu Othacehe .init = vcnl4200_init, 1458d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1459d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1460d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 146185e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 146285e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 146385e2c6a2SMårten Lindahl .info = &vcnl4040_info, 146454667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1465854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1466e55c96daSAstrid Rost .ps_it_times = &vcnl4040_ps_it_times, 1467e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4040_ps_it_times), 1468*fea2c97dSAstrid Rost .als_it_times = &vcnl4040_als_it_times, 1469*fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4040_als_it_times), 1470*fea2c97dSAstrid Rost .ulux_step = 100000, 1471d35567fcSMathieu Othacehe }, 1472d35567fcSMathieu Othacehe [VCNL4200] = { 1473d35567fcSMathieu Othacehe .prod = "VCNL4200", 1474d35567fcSMathieu Othacehe .init = vcnl4200_init, 1475d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1476d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1477d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1478854965b7SAstrid Rost .channels = vcnl4040_channels, 1479d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1480854965b7SAstrid Rost .info = &vcnl4040_info, 1481854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1482854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1483e55c96daSAstrid Rost .ps_it_times = &vcnl4200_ps_it_times, 1484e55c96daSAstrid Rost .num_ps_it_times = ARRAY_SIZE(vcnl4200_ps_it_times), 1485*fea2c97dSAstrid Rost .als_it_times = &vcnl4200_als_it_times, 1486*fea2c97dSAstrid Rost .num_als_it_times = ARRAY_SIZE(vcnl4200_als_it_times), 1487*fea2c97dSAstrid Rost .ulux_step = 24000, 1488d35567fcSMathieu Othacehe }, 1489d35567fcSMathieu Othacehe }; 1490d35567fcSMathieu Othacehe 14918fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 14928fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 14938fe78d52SMathieu Othacehe }; 14948fe78d52SMathieu Othacehe 14958fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 14968fe78d52SMathieu Othacehe { 14978fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 14988fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 14998fe78d52SMathieu Othacehe struct iio_trigger *trigger; 15008fe78d52SMathieu Othacehe 15018fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 150215ea2878SJonathan Cameron indio_dev->name, 150315ea2878SJonathan Cameron iio_device_id(indio_dev)); 15048fe78d52SMathieu Othacehe if (!trigger) 15058fe78d52SMathieu Othacehe return -ENOMEM; 15068fe78d52SMathieu Othacehe 15078fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 15088fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 15098fe78d52SMathieu Othacehe 15108fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 15118fe78d52SMathieu Othacehe } 15128fe78d52SMathieu Othacehe 1513e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 151462a1efb9SPeter Meerwald { 1515e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 151662a1efb9SPeter Meerwald struct vcnl4000_data *data; 151762a1efb9SPeter Meerwald struct iio_dev *indio_dev; 15181ebc787aSTomas Novotny int ret; 151962a1efb9SPeter Meerwald 15202669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 152162a1efb9SPeter Meerwald if (!indio_dev) 152262a1efb9SPeter Meerwald return -ENOMEM; 152362a1efb9SPeter Meerwald 152462a1efb9SPeter Meerwald data = iio_priv(indio_dev); 152562a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 152662a1efb9SPeter Meerwald data->client = client; 15271ebc787aSTomas Novotny data->id = id->driver_data; 15281ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 152962a1efb9SPeter Meerwald 153042ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 153142ec40b0SMårten Lindahl 15321ebc787aSTomas Novotny ret = data->chip_spec->init(data); 153362a1efb9SPeter Meerwald if (ret < 0) 15342669d723SPeter Meerwald return ret; 153562a1efb9SPeter Meerwald 1536d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 15371ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 153862a1efb9SPeter Meerwald 1539f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1540f5a98e1fSGuido Günther &data->near_level)) 1541f5a98e1fSGuido Günther data->near_level = 0; 1542f5a98e1fSGuido Günther 1543d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1544d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1545d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 154662a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 154762a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 154862a1efb9SPeter Meerwald 1549bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1550bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 15518fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 15528fe78d52SMathieu Othacehe NULL, 1553bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1554bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 15558fe78d52SMathieu Othacehe if (ret < 0) { 15568fe78d52SMathieu Othacehe dev_err(&client->dev, 15578fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 15588fe78d52SMathieu Othacehe return ret; 15598fe78d52SMathieu Othacehe } 1560bfb6cfeeSMårten Lindahl } 15618fe78d52SMathieu Othacehe 1562bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1563d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1564bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1565d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1566d35567fcSMathieu Othacehe IRQF_ONESHOT, 1567bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1568d35567fcSMathieu Othacehe indio_dev); 1569d35567fcSMathieu Othacehe if (ret < 0) { 1570d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1571d35567fcSMathieu Othacehe return ret; 1572d35567fcSMathieu Othacehe } 15738fe78d52SMathieu Othacehe 15748fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 15758fe78d52SMathieu Othacehe if (ret < 0) 15768fe78d52SMathieu Othacehe return ret; 1577d35567fcSMathieu Othacehe } 1578d35567fcSMathieu Othacehe 15795e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 15805e00708dSGuido Günther if (ret < 0) 15815e00708dSGuido Günther goto fail_poweroff; 15825e00708dSGuido Günther 15835e00708dSGuido Günther ret = iio_device_register(indio_dev); 15845e00708dSGuido Günther if (ret < 0) 15855e00708dSGuido Günther goto fail_poweroff; 15865e00708dSGuido Günther 15875e00708dSGuido Günther pm_runtime_enable(&client->dev); 15885e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 15895e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 15905e00708dSGuido Günther 15915e00708dSGuido Günther return 0; 15925e00708dSGuido Günther fail_poweroff: 15935e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 15945e00708dSGuido Günther return ret; 159562a1efb9SPeter Meerwald } 159662a1efb9SPeter Meerwald 1597ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1598ebd457d5SAngus Ainslie (Purism) { 1599ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 16001436a78cSMarco Felsch .data = (void *)VCNL4000, 1601ebd457d5SAngus Ainslie (Purism) }, 1602ebd457d5SAngus Ainslie (Purism) { 1603ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 16041436a78cSMarco Felsch .data = (void *)VCNL4010, 1605ebd457d5SAngus Ainslie (Purism) }, 1606ebd457d5SAngus Ainslie (Purism) { 16071436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 16081436a78cSMarco Felsch .data = (void *)VCNL4010, 1609ebd457d5SAngus Ainslie (Purism) }, 1610ebd457d5SAngus Ainslie (Purism) { 16117fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 16127fd1c260SMarco Felsch .data = (void *)VCNL4040, 16137fd1c260SMarco Felsch }, 16147fd1c260SMarco Felsch { 1615ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 16161436a78cSMarco Felsch .data = (void *)VCNL4200, 1617ebd457d5SAngus Ainslie (Purism) }, 1618ebd457d5SAngus Ainslie (Purism) {}, 1619ebd457d5SAngus Ainslie (Purism) }; 1620ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1621ebd457d5SAngus Ainslie (Purism) 1622ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 16235e00708dSGuido Günther { 16245e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 16255e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1626ab91da2fSUwe Kleine-König int ret; 16275e00708dSGuido Günther 16285e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 16295e00708dSGuido Günther pm_runtime_disable(&client->dev); 16305e00708dSGuido Günther iio_device_unregister(indio_dev); 16315e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 16325e00708dSGuido Günther 1633ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1634ab91da2fSUwe Kleine-König if (ret) 1635ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1636ab91da2fSUwe Kleine-König ERR_PTR(ret)); 16375e00708dSGuido Günther } 16385e00708dSGuido Günther 1639cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 16405e00708dSGuido Günther { 16415e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 16425e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 16435e00708dSGuido Günther 16445e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 16455e00708dSGuido Günther } 16465e00708dSGuido Günther 1647cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 16485e00708dSGuido Günther { 16495e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 16505e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 16515e00708dSGuido Günther 16525e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 16535e00708dSGuido Günther } 16545e00708dSGuido Günther 1655cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1656cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 16575e00708dSGuido Günther 165862a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 165962a1efb9SPeter Meerwald .driver = { 166062a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1661cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1662ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 166362a1efb9SPeter Meerwald }, 16647cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 166562a1efb9SPeter Meerwald .id_table = vcnl4000_id, 16665e00708dSGuido Günther .remove = vcnl4000_remove, 166762a1efb9SPeter Meerwald }; 166862a1efb9SPeter Meerwald 166962a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 167062a1efb9SPeter Meerwald 167162a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 16728fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 167362a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 167462a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1675