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 20*85e2c6a2SMå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 */ 63be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 64be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 65be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 66be38866fSTomas Novotny 675a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 685a441aadSAngus Ainslie (Purism) 6962a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 70ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 71ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 72ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 73ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 74d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 75d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 76d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 77d35567fcSMathieu Othacehe 78e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 79e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 80*85e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 81e21b5b1fSMårten Lindahl 82d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 83d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 84d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 85d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 86d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 87d35567fcSMathieu Othacehe 88d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 89d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 90d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 92d35567fcSMathieu Othacehe 93d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 94d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 95d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 96d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 97d35567fcSMathieu Othacehe 98f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 99f6889c1bSMathieu Othacehe {1, 950000}, 100f6889c1bSMathieu Othacehe {3, 906250}, 101f6889c1bSMathieu Othacehe {7, 812500}, 102f6889c1bSMathieu Othacehe {16, 625000}, 103f6889c1bSMathieu Othacehe {31, 250000}, 104f6889c1bSMathieu Othacehe {62, 500000}, 105f6889c1bSMathieu Othacehe {125, 0}, 106f6889c1bSMathieu Othacehe {250, 0}, 107f6889c1bSMathieu Othacehe }; 10862a1efb9SPeter Meerwald 109*85e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 110*85e2c6a2SMårten Lindahl {0, 100}, 111*85e2c6a2SMårten Lindahl {0, 150}, 112*85e2c6a2SMårten Lindahl {0, 200}, 113*85e2c6a2SMårten Lindahl {0, 250}, 114*85e2c6a2SMårten Lindahl {0, 300}, 115*85e2c6a2SMårten Lindahl {0, 350}, 116*85e2c6a2SMårten Lindahl {0, 400}, 117*85e2c6a2SMårten Lindahl {0, 800}, 118*85e2c6a2SMårten Lindahl }; 119*85e2c6a2SMårten Lindahl 1205e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1215e00708dSGuido Günther 1221ebc787aSTomas Novotny enum vcnl4000_device_ids { 1231ebc787aSTomas Novotny VCNL4000, 12450c50b97STomas Novotny VCNL4010, 1255a441aadSAngus Ainslie (Purism) VCNL4040, 126be38866fSTomas Novotny VCNL4200, 127be38866fSTomas Novotny }; 128be38866fSTomas Novotny 129be38866fSTomas Novotny struct vcnl4200_channel { 130be38866fSTomas Novotny u8 reg; 131be38866fSTomas Novotny ktime_t last_measurement; 132be38866fSTomas Novotny ktime_t sampling_rate; 133be38866fSTomas Novotny struct mutex lock; 1341ebc787aSTomas Novotny }; 1351ebc787aSTomas Novotny 13662a1efb9SPeter Meerwald struct vcnl4000_data { 13762a1efb9SPeter Meerwald struct i2c_client *client; 1381ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1391ebc787aSTomas Novotny int rev; 1401ebc787aSTomas Novotny int al_scale; 1411ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 142be38866fSTomas Novotny struct mutex vcnl4000_lock; 143be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 144be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 145f5a98e1fSGuido Günther uint32_t near_level; 14662a1efb9SPeter Meerwald }; 14762a1efb9SPeter Meerwald 1481ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1491ebc787aSTomas Novotny const char *prod; 150d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 151d35567fcSMathieu Othacehe const int num_channels; 152d35567fcSMathieu Othacehe const struct iio_info *info; 153d35567fcSMathieu Othacehe bool irq_support; 1541ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1551ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1561ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1575e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 1581ebc787aSTomas Novotny }; 1591ebc787aSTomas Novotny 16062a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1611ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 16250c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 16350c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1645a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 165be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 16662a1efb9SPeter Meerwald { } 16762a1efb9SPeter Meerwald }; 16862a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 16962a1efb9SPeter Meerwald 1705e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1715e00708dSGuido Günther { 1725e00708dSGuido Günther /* no suspend op */ 1735e00708dSGuido Günther return 0; 1745e00708dSGuido Günther } 1755e00708dSGuido Günther 1761ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1771ebc787aSTomas Novotny { 1781ebc787aSTomas Novotny int ret, prod_id; 1791ebc787aSTomas Novotny 1801ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1811ebc787aSTomas Novotny if (ret < 0) 1821ebc787aSTomas Novotny return ret; 1831ebc787aSTomas Novotny 1841ebc787aSTomas Novotny prod_id = ret >> 4; 18558bf9aceSTomas Novotny switch (prod_id) { 18658bf9aceSTomas Novotny case VCNL4000_PROD_ID: 18758bf9aceSTomas Novotny if (data->id != VCNL4000) 18858bf9aceSTomas Novotny dev_warn(&data->client->dev, 18958bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 19058bf9aceSTomas Novotny break; 19158bf9aceSTomas Novotny case VCNL4010_PROD_ID: 19258bf9aceSTomas Novotny if (data->id != VCNL4010) 19358bf9aceSTomas Novotny dev_warn(&data->client->dev, 19458bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 19558bf9aceSTomas Novotny break; 19658bf9aceSTomas Novotny default: 1971ebc787aSTomas Novotny return -ENODEV; 19858bf9aceSTomas Novotny } 1991ebc787aSTomas Novotny 2001ebc787aSTomas Novotny data->rev = ret & 0xf; 2011ebc787aSTomas Novotny data->al_scale = 250000; 202be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 203be38866fSTomas Novotny 2045e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 205be38866fSTomas Novotny }; 206be38866fSTomas Novotny 207e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2085e00708dSGuido Günther { 2095e00708dSGuido Günther int ret; 2105e00708dSGuido Günther 211e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 212e21b5b1fSMårten Lindahl 213e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 214e21b5b1fSMårten Lindahl if (ret < 0) 215e21b5b1fSMårten Lindahl goto out; 216e21b5b1fSMårten Lindahl 217e21b5b1fSMårten Lindahl if (en) 218e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 219e21b5b1fSMårten Lindahl else 220e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 221e21b5b1fSMårten Lindahl 222e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 223e21b5b1fSMårten Lindahl 224e21b5b1fSMårten Lindahl out: 225e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 226e21b5b1fSMårten Lindahl 227e21b5b1fSMårten Lindahl return ret; 228e21b5b1fSMårten Lindahl } 229e21b5b1fSMårten Lindahl 230e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 231e21b5b1fSMårten Lindahl { 232e21b5b1fSMårten Lindahl int ret; 233e21b5b1fSMårten Lindahl 234e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 235e21b5b1fSMårten Lindahl 236e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 237e21b5b1fSMårten Lindahl if (ret < 0) 238e21b5b1fSMårten Lindahl goto out; 239e21b5b1fSMårten Lindahl 240e21b5b1fSMårten Lindahl if (en) 241e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 242e21b5b1fSMårten Lindahl else 243e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 244e21b5b1fSMårten Lindahl 245e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 246e21b5b1fSMårten Lindahl 247e21b5b1fSMårten Lindahl out: 248e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 249e21b5b1fSMårten Lindahl 250e21b5b1fSMårten Lindahl return ret; 251e21b5b1fSMårten Lindahl } 252e21b5b1fSMårten Lindahl 253e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 254e21b5b1fSMårten Lindahl { 255e21b5b1fSMårten Lindahl int ret; 256e21b5b1fSMårten Lindahl 257e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 2585e00708dSGuido Günther if (ret < 0) 2595e00708dSGuido Günther return ret; 2605e00708dSGuido Günther 261e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 2625e00708dSGuido Günther if (ret < 0) 2635e00708dSGuido Günther return ret; 2645e00708dSGuido Günther 2655e00708dSGuido Günther if (on) { 2665e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2675e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2685e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2695e00708dSGuido Günther } 2705e00708dSGuido Günther 2715e00708dSGuido Günther return 0; 2725e00708dSGuido Günther } 2735e00708dSGuido Günther 274be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 275be38866fSTomas Novotny { 2765a441aadSAngus Ainslie (Purism) int ret, id; 277be38866fSTomas Novotny 278be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 279be38866fSTomas Novotny if (ret < 0) 280be38866fSTomas Novotny return ret; 281be38866fSTomas Novotny 2825a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2835a441aadSAngus Ainslie (Purism) 2845a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 2855a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 2865a441aadSAngus Ainslie (Purism) if (ret < 0) 2875a441aadSAngus Ainslie (Purism) return ret; 2885a441aadSAngus Ainslie (Purism) 2895a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2905a441aadSAngus Ainslie (Purism) 2915a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 292be38866fSTomas Novotny return -ENODEV; 2935a441aadSAngus Ainslie (Purism) } 2945a441aadSAngus Ainslie (Purism) 2955a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 296be38866fSTomas Novotny 297be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 298be38866fSTomas Novotny 299be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 300be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3015a441aadSAngus Ainslie (Purism) switch (id) { 3025a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 303b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 304b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 305b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 306b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 307bc80573eSGuido Günther data->al_scale = 24000; 3085a441aadSAngus Ainslie (Purism) break; 3095a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3102ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3112ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3122ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3132ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 314bc80573eSGuido Günther data->al_scale = 120000; 3155a441aadSAngus Ainslie (Purism) break; 3165a441aadSAngus Ainslie (Purism) } 317be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 318be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3191ebc787aSTomas Novotny 3205e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3215e00708dSGuido Günther if (ret < 0) 3225e00708dSGuido Günther return ret; 3235e00708dSGuido Günther 3241ebc787aSTomas Novotny return 0; 3251ebc787aSTomas Novotny }; 3261ebc787aSTomas Novotny 327816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 328816956c3SMathieu Othacehe { 329816956c3SMathieu Othacehe s32 ret; 330816956c3SMathieu Othacehe 331816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 332816956c3SMathieu Othacehe if (ret < 0) 333816956c3SMathieu Othacehe return ret; 334816956c3SMathieu Othacehe 335816956c3SMathieu Othacehe *val = ret; 336816956c3SMathieu Othacehe return 0; 337816956c3SMathieu Othacehe } 338816956c3SMathieu Othacehe 339816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 340816956c3SMathieu Othacehe { 341816956c3SMathieu Othacehe if (val > U16_MAX) 342816956c3SMathieu Othacehe return -ERANGE; 343816956c3SMathieu Othacehe 344816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 345816956c3SMathieu Othacehe } 346816956c3SMathieu Othacehe 347816956c3SMathieu Othacehe 34862a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 34962a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 35062a1efb9SPeter Meerwald { 35162a1efb9SPeter Meerwald int tries = 20; 35262a1efb9SPeter Meerwald int ret; 35362a1efb9SPeter Meerwald 354be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 355ff34ed6dSPeter Meerwald-Stadler 35662a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 35762a1efb9SPeter Meerwald req_mask); 35862a1efb9SPeter Meerwald if (ret < 0) 359ff34ed6dSPeter Meerwald-Stadler goto fail; 36062a1efb9SPeter Meerwald 36162a1efb9SPeter Meerwald /* wait for data to become ready */ 36262a1efb9SPeter Meerwald while (tries--) { 36362a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 36462a1efb9SPeter Meerwald if (ret < 0) 365ff34ed6dSPeter Meerwald-Stadler goto fail; 36662a1efb9SPeter Meerwald if (ret & rdy_mask) 36762a1efb9SPeter Meerwald break; 36862a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 36962a1efb9SPeter Meerwald } 37062a1efb9SPeter Meerwald 37162a1efb9SPeter Meerwald if (tries < 0) { 37262a1efb9SPeter Meerwald dev_err(&data->client->dev, 37362a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 374ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 375ff34ed6dSPeter Meerwald-Stadler goto fail; 37662a1efb9SPeter Meerwald } 37762a1efb9SPeter Meerwald 378816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 37962a1efb9SPeter Meerwald if (ret < 0) 380ff34ed6dSPeter Meerwald-Stadler goto fail; 38162a1efb9SPeter Meerwald 382be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 38362a1efb9SPeter Meerwald 38462a1efb9SPeter Meerwald return 0; 385ff34ed6dSPeter Meerwald-Stadler 386ff34ed6dSPeter Meerwald-Stadler fail: 387be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 388ff34ed6dSPeter Meerwald-Stadler return ret; 38962a1efb9SPeter Meerwald } 39062a1efb9SPeter Meerwald 391be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 392be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 393be38866fSTomas Novotny { 394be38866fSTomas Novotny int ret; 395be38866fSTomas Novotny s64 delta; 396be38866fSTomas Novotny ktime_t next_measurement; 397be38866fSTomas Novotny 398be38866fSTomas Novotny mutex_lock(&chan->lock); 399be38866fSTomas Novotny 400be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 401be38866fSTomas Novotny chan->sampling_rate); 402be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 403be38866fSTomas Novotny if (delta > 0) 404be38866fSTomas Novotny usleep_range(delta, delta + 500); 405be38866fSTomas Novotny chan->last_measurement = ktime_get(); 406be38866fSTomas Novotny 407be38866fSTomas Novotny mutex_unlock(&chan->lock); 408be38866fSTomas Novotny 409be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 410be38866fSTomas Novotny if (ret < 0) 411be38866fSTomas Novotny return ret; 412be38866fSTomas Novotny 413be38866fSTomas Novotny *val = ret; 414be38866fSTomas Novotny 415be38866fSTomas Novotny return 0; 416be38866fSTomas Novotny } 417be38866fSTomas Novotny 4181ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4191ebc787aSTomas Novotny { 4201ebc787aSTomas Novotny return vcnl4000_measure(data, 4211ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4221ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4231ebc787aSTomas Novotny } 4241ebc787aSTomas Novotny 425be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 426be38866fSTomas Novotny { 427be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 428be38866fSTomas Novotny } 429be38866fSTomas Novotny 4301ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4311ebc787aSTomas Novotny { 4321ebc787aSTomas Novotny return vcnl4000_measure(data, 4331ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4341ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4351ebc787aSTomas Novotny } 4361ebc787aSTomas Novotny 437be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 438be38866fSTomas Novotny { 439be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 440be38866fSTomas Novotny } 441be38866fSTomas Novotny 442f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 443f6889c1bSMathieu Othacehe int *val2) 444f6889c1bSMathieu Othacehe { 445f6889c1bSMathieu Othacehe int ret; 446f6889c1bSMathieu Othacehe 447f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 448f6889c1bSMathieu Othacehe if (ret < 0) 449f6889c1bSMathieu Othacehe return ret; 450f6889c1bSMathieu Othacehe 451f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 452f6889c1bSMathieu Othacehe return -EINVAL; 453f6889c1bSMathieu Othacehe 454f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 455f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 456f6889c1bSMathieu Othacehe 457f6889c1bSMathieu Othacehe return 0; 458f6889c1bSMathieu Othacehe } 459f6889c1bSMathieu Othacehe 460d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 461f5a98e1fSGuido Günther { 462d35567fcSMathieu Othacehe int ret; 463f5a98e1fSGuido Günther 464d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 465d35567fcSMathieu Othacehe if (ret < 0) 466d35567fcSMathieu Othacehe return false; 467d35567fcSMathieu Othacehe 468d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 469f5a98e1fSGuido Günther } 470f5a98e1fSGuido Günther 4715e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4725e00708dSGuido Günther { 4735e00708dSGuido Günther struct device *dev = &data->client->dev; 4745e00708dSGuido Günther int ret; 4755e00708dSGuido Günther 4765e00708dSGuido Günther if (on) { 477db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4785e00708dSGuido Günther } else { 4795e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4805e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4815e00708dSGuido Günther } 4825e00708dSGuido Günther 4835e00708dSGuido Günther return ret; 4845e00708dSGuido Günther } 4855e00708dSGuido Günther 486*85e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 487*85e2c6a2SMårten Lindahl { 488*85e2c6a2SMårten Lindahl int ret; 489*85e2c6a2SMårten Lindahl 490*85e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 491*85e2c6a2SMårten Lindahl if (ret < 0) 492*85e2c6a2SMårten Lindahl return ret; 493*85e2c6a2SMårten Lindahl 494*85e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 495*85e2c6a2SMårten Lindahl 496*85e2c6a2SMårten Lindahl if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times)) 497*85e2c6a2SMårten Lindahl return -EINVAL; 498*85e2c6a2SMårten Lindahl 499*85e2c6a2SMårten Lindahl *val = vcnl4040_ps_it_times[ret][0]; 500*85e2c6a2SMårten Lindahl *val2 = vcnl4040_ps_it_times[ret][1]; 501*85e2c6a2SMårten Lindahl 502*85e2c6a2SMårten Lindahl return 0; 503*85e2c6a2SMårten Lindahl } 504*85e2c6a2SMårten Lindahl 505*85e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 506*85e2c6a2SMårten Lindahl { 507*85e2c6a2SMårten Lindahl unsigned int i; 508*85e2c6a2SMårten Lindahl int ret, index = -1; 509*85e2c6a2SMårten Lindahl u16 regval; 510*85e2c6a2SMårten Lindahl 511*85e2c6a2SMårten Lindahl for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) { 512*85e2c6a2SMårten Lindahl if (val == vcnl4040_ps_it_times[i][1]) { 513*85e2c6a2SMårten Lindahl index = i; 514*85e2c6a2SMårten Lindahl break; 515*85e2c6a2SMårten Lindahl } 516*85e2c6a2SMårten Lindahl } 517*85e2c6a2SMårten Lindahl 518*85e2c6a2SMårten Lindahl if (index < 0) 519*85e2c6a2SMårten Lindahl return -EINVAL; 520*85e2c6a2SMårten Lindahl 521*85e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 522*85e2c6a2SMårten Lindahl 523*85e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 524*85e2c6a2SMårten Lindahl if (ret < 0) 525*85e2c6a2SMårten Lindahl goto out; 526*85e2c6a2SMårten Lindahl 527*85e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 528*85e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 529*85e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 530*85e2c6a2SMårten Lindahl regval); 531*85e2c6a2SMårten Lindahl 532*85e2c6a2SMårten Lindahl out: 533*85e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 534*85e2c6a2SMårten Lindahl return ret; 535*85e2c6a2SMårten Lindahl } 536*85e2c6a2SMårten Lindahl 53762a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 53862a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 53962a1efb9SPeter Meerwald int *val, int *val2, long mask) 54062a1efb9SPeter Meerwald { 5415d693139SPeter Meerwald-Stadler int ret; 54262a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 54362a1efb9SPeter Meerwald 54462a1efb9SPeter Meerwald switch (mask) { 54562a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 5465e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 5475e00708dSGuido Günther if (ret < 0) 5485e00708dSGuido Günther return ret; 5495e00708dSGuido Günther 55062a1efb9SPeter Meerwald switch (chan->type) { 55162a1efb9SPeter Meerwald case IIO_LIGHT: 5521ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 5534a818643SGuido Günther if (!ret) 5544a818643SGuido Günther ret = IIO_VAL_INT; 5554a818643SGuido Günther break; 55662a1efb9SPeter Meerwald case IIO_PROXIMITY: 5571ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 5584a818643SGuido Günther if (!ret) 5594a818643SGuido Günther ret = IIO_VAL_INT; 5604a818643SGuido Günther break; 56162a1efb9SPeter Meerwald default: 5624a818643SGuido Günther ret = -EINVAL; 56362a1efb9SPeter Meerwald } 5645e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 5654a818643SGuido Günther return ret; 56662a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 5675d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 5685d693139SPeter Meerwald-Stadler return -EINVAL; 5695d693139SPeter Meerwald-Stadler 57062a1efb9SPeter Meerwald *val = 0; 5711ebc787aSTomas Novotny *val2 = data->al_scale; 5725d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 573*85e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 574*85e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 575*85e2c6a2SMårten Lindahl return -EINVAL; 576*85e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 577*85e2c6a2SMårten Lindahl if (ret < 0) 578*85e2c6a2SMårten Lindahl return ret; 579*85e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 580*85e2c6a2SMårten Lindahl default: 581*85e2c6a2SMårten Lindahl return -EINVAL; 582*85e2c6a2SMårten Lindahl } 583*85e2c6a2SMårten Lindahl } 584*85e2c6a2SMårten Lindahl 585*85e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 586*85e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 587*85e2c6a2SMårten Lindahl int val, int val2, long mask) 588*85e2c6a2SMårten Lindahl { 589*85e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 590*85e2c6a2SMårten Lindahl 591*85e2c6a2SMårten Lindahl switch (mask) { 592*85e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 593*85e2c6a2SMårten Lindahl if (val != 0) 594*85e2c6a2SMårten Lindahl return -EINVAL; 595*85e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 596*85e2c6a2SMårten Lindahl return -EINVAL; 597*85e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 598*85e2c6a2SMårten Lindahl default: 599*85e2c6a2SMårten Lindahl return -EINVAL; 600*85e2c6a2SMårten Lindahl } 601*85e2c6a2SMårten Lindahl } 602*85e2c6a2SMårten Lindahl 603*85e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 604*85e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 605*85e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 606*85e2c6a2SMårten Lindahl long mask) 607*85e2c6a2SMårten Lindahl { 608*85e2c6a2SMårten Lindahl switch (mask) { 609*85e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 610*85e2c6a2SMårten Lindahl *vals = (int *)vcnl4040_ps_it_times; 611*85e2c6a2SMårten Lindahl *type = IIO_VAL_INT_PLUS_MICRO; 612*85e2c6a2SMårten Lindahl *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times); 613*85e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 61462a1efb9SPeter Meerwald default: 6155d693139SPeter Meerwald-Stadler return -EINVAL; 61662a1efb9SPeter Meerwald } 61762a1efb9SPeter Meerwald } 61862a1efb9SPeter Meerwald 619d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 620d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 621d35567fcSMathieu Othacehe int *val, int *val2, long mask) 622d35567fcSMathieu Othacehe { 623d35567fcSMathieu Othacehe int ret; 624d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 625d35567fcSMathieu Othacehe 626d35567fcSMathieu Othacehe switch (mask) { 627d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 628d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 629d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 630d35567fcSMathieu Othacehe if (ret) 631d35567fcSMathieu Othacehe return ret; 632d35567fcSMathieu Othacehe 633d35567fcSMathieu Othacehe /* Protect against event capture. */ 634d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 635d35567fcSMathieu Othacehe ret = -EBUSY; 636d35567fcSMathieu Othacehe } else { 637d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 638d35567fcSMathieu Othacehe mask); 639d35567fcSMathieu Othacehe } 640d35567fcSMathieu Othacehe 641d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 642d35567fcSMathieu Othacehe return ret; 643f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 644f6889c1bSMathieu Othacehe switch (chan->type) { 645f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 646f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 647f6889c1bSMathieu Othacehe if (ret < 0) 648f6889c1bSMathieu Othacehe return ret; 649f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 650d35567fcSMathieu Othacehe default: 651d35567fcSMathieu Othacehe return -EINVAL; 652d35567fcSMathieu Othacehe } 653f6889c1bSMathieu Othacehe default: 654f6889c1bSMathieu Othacehe return -EINVAL; 655f6889c1bSMathieu Othacehe } 656f6889c1bSMathieu Othacehe } 657f6889c1bSMathieu Othacehe 658f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 659f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 660f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 661f6889c1bSMathieu Othacehe long mask) 662f6889c1bSMathieu Othacehe { 663f6889c1bSMathieu Othacehe switch (mask) { 664f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 665f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 666f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 667f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 668f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 669f6889c1bSMathieu Othacehe default: 670f6889c1bSMathieu Othacehe return -EINVAL; 671f6889c1bSMathieu Othacehe } 672f6889c1bSMathieu Othacehe } 673f6889c1bSMathieu Othacehe 674f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 675f6889c1bSMathieu Othacehe int val2) 676f6889c1bSMathieu Othacehe { 677f6889c1bSMathieu Othacehe unsigned int i; 678f6889c1bSMathieu Othacehe int index = -1; 679f6889c1bSMathieu Othacehe 680f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 681f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 682f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 683f6889c1bSMathieu Othacehe index = i; 684f6889c1bSMathieu Othacehe break; 685f6889c1bSMathieu Othacehe } 686f6889c1bSMathieu Othacehe } 687f6889c1bSMathieu Othacehe 688f6889c1bSMathieu Othacehe if (index < 0) 689f6889c1bSMathieu Othacehe return -EINVAL; 690f6889c1bSMathieu Othacehe 691f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 692f6889c1bSMathieu Othacehe index); 693f6889c1bSMathieu Othacehe } 694f6889c1bSMathieu Othacehe 695f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 696f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 697f6889c1bSMathieu Othacehe int val, int val2, long mask) 698f6889c1bSMathieu Othacehe { 699f6889c1bSMathieu Othacehe int ret; 700f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 701f6889c1bSMathieu Othacehe 702f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 703f6889c1bSMathieu Othacehe if (ret) 704f6889c1bSMathieu Othacehe return ret; 705f6889c1bSMathieu Othacehe 706f6889c1bSMathieu Othacehe /* Protect against event capture. */ 707f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 708f6889c1bSMathieu Othacehe ret = -EBUSY; 709f6889c1bSMathieu Othacehe goto end; 710f6889c1bSMathieu Othacehe } 711f6889c1bSMathieu Othacehe 712f6889c1bSMathieu Othacehe switch (mask) { 713f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 714f6889c1bSMathieu Othacehe switch (chan->type) { 715f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 716f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 717f6889c1bSMathieu Othacehe goto end; 718f6889c1bSMathieu Othacehe default: 719f6889c1bSMathieu Othacehe ret = -EINVAL; 720f6889c1bSMathieu Othacehe goto end; 721f6889c1bSMathieu Othacehe } 722f6889c1bSMathieu Othacehe default: 723f6889c1bSMathieu Othacehe ret = -EINVAL; 724f6889c1bSMathieu Othacehe goto end; 725f6889c1bSMathieu Othacehe } 726f6889c1bSMathieu Othacehe 727f6889c1bSMathieu Othacehe end: 728f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 729f6889c1bSMathieu Othacehe return ret; 730d35567fcSMathieu Othacehe } 731d35567fcSMathieu Othacehe 732d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 733d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 734d35567fcSMathieu Othacehe enum iio_event_type type, 735d35567fcSMathieu Othacehe enum iio_event_direction dir, 736d35567fcSMathieu Othacehe enum iio_event_info info, 737d35567fcSMathieu Othacehe int *val, int *val2) 738d35567fcSMathieu Othacehe { 739d35567fcSMathieu Othacehe int ret; 740d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 741d35567fcSMathieu Othacehe 742d35567fcSMathieu Othacehe switch (info) { 743d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 744d35567fcSMathieu Othacehe switch (dir) { 745d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 746d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 747d35567fcSMathieu Othacehe val); 748d35567fcSMathieu Othacehe if (ret < 0) 749d35567fcSMathieu Othacehe return ret; 750d35567fcSMathieu Othacehe return IIO_VAL_INT; 751d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 752d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 753d35567fcSMathieu Othacehe val); 754d35567fcSMathieu Othacehe if (ret < 0) 755d35567fcSMathieu Othacehe return ret; 756d35567fcSMathieu Othacehe return IIO_VAL_INT; 757d35567fcSMathieu Othacehe default: 758d35567fcSMathieu Othacehe return -EINVAL; 759d35567fcSMathieu Othacehe } 760d35567fcSMathieu Othacehe default: 761d35567fcSMathieu Othacehe return -EINVAL; 762d35567fcSMathieu Othacehe } 763d35567fcSMathieu Othacehe } 764d35567fcSMathieu Othacehe 765d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 766d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 767d35567fcSMathieu Othacehe enum iio_event_type type, 768d35567fcSMathieu Othacehe enum iio_event_direction dir, 769d35567fcSMathieu Othacehe enum iio_event_info info, 770d35567fcSMathieu Othacehe int val, int val2) 771d35567fcSMathieu Othacehe { 772d35567fcSMathieu Othacehe int ret; 773d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 774d35567fcSMathieu Othacehe 775d35567fcSMathieu Othacehe switch (info) { 776d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 777d35567fcSMathieu Othacehe switch (dir) { 778d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 779d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 780d35567fcSMathieu Othacehe val); 781d35567fcSMathieu Othacehe if (ret < 0) 782d35567fcSMathieu Othacehe return ret; 783d35567fcSMathieu Othacehe return IIO_VAL_INT; 784d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 785d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 786d35567fcSMathieu Othacehe val); 787d35567fcSMathieu Othacehe if (ret < 0) 788d35567fcSMathieu Othacehe return ret; 789d35567fcSMathieu Othacehe return IIO_VAL_INT; 790d35567fcSMathieu Othacehe default: 791d35567fcSMathieu Othacehe return -EINVAL; 792d35567fcSMathieu Othacehe } 793d35567fcSMathieu Othacehe default: 794d35567fcSMathieu Othacehe return -EINVAL; 795d35567fcSMathieu Othacehe } 796d35567fcSMathieu Othacehe } 797d35567fcSMathieu Othacehe 798d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 799d35567fcSMathieu Othacehe { 800d35567fcSMathieu Othacehe int ret; 801d35567fcSMathieu Othacehe 802d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 803d35567fcSMathieu Othacehe if (ret < 0) 804d35567fcSMathieu Othacehe return false; 805d35567fcSMathieu Othacehe 806d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 807d35567fcSMathieu Othacehe } 808d35567fcSMathieu Othacehe 809d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 810d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 811d35567fcSMathieu Othacehe enum iio_event_type type, 812d35567fcSMathieu Othacehe enum iio_event_direction dir) 813d35567fcSMathieu Othacehe { 814d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 815d35567fcSMathieu Othacehe 816d35567fcSMathieu Othacehe switch (chan->type) { 817d35567fcSMathieu Othacehe case IIO_PROXIMITY: 818d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 819d35567fcSMathieu Othacehe default: 820d35567fcSMathieu Othacehe return -EINVAL; 821d35567fcSMathieu Othacehe } 822d35567fcSMathieu Othacehe } 823d35567fcSMathieu Othacehe 824d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 825d35567fcSMathieu Othacehe { 826d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 827d35567fcSMathieu Othacehe int ret; 828d35567fcSMathieu Othacehe int icr; 829d35567fcSMathieu Othacehe int command; 830d35567fcSMathieu Othacehe 831d35567fcSMathieu Othacehe if (state) { 832d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 833d35567fcSMathieu Othacehe if (ret) 834d35567fcSMathieu Othacehe return ret; 835d35567fcSMathieu Othacehe 836d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 837d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 838d35567fcSMathieu Othacehe 839d35567fcSMathieu Othacehe /* 840d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 841d35567fcSMathieu Othacehe * default. 842d35567fcSMathieu Othacehe */ 843d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 844d35567fcSMathieu Othacehe } else { 845d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 846d35567fcSMathieu Othacehe return 0; 847d35567fcSMathieu Othacehe 848d35567fcSMathieu Othacehe command = 0; 849d35567fcSMathieu Othacehe icr = 0; 850d35567fcSMathieu Othacehe } 851d35567fcSMathieu Othacehe 852d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 853d35567fcSMathieu Othacehe command); 854d35567fcSMathieu Othacehe if (ret < 0) 855d35567fcSMathieu Othacehe goto end; 856d35567fcSMathieu Othacehe 857d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 858d35567fcSMathieu Othacehe 859d35567fcSMathieu Othacehe end: 860d35567fcSMathieu Othacehe if (state) 861d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 862d35567fcSMathieu Othacehe 863d35567fcSMathieu Othacehe return ret; 864d35567fcSMathieu Othacehe } 865d35567fcSMathieu Othacehe 866d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 867d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 868d35567fcSMathieu Othacehe enum iio_event_type type, 869d35567fcSMathieu Othacehe enum iio_event_direction dir, 870d35567fcSMathieu Othacehe int state) 871d35567fcSMathieu Othacehe { 872d35567fcSMathieu Othacehe switch (chan->type) { 873d35567fcSMathieu Othacehe case IIO_PROXIMITY: 874d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 875d35567fcSMathieu Othacehe default: 876d35567fcSMathieu Othacehe return -EINVAL; 877d35567fcSMathieu Othacehe } 878d35567fcSMathieu Othacehe } 879d35567fcSMathieu Othacehe 880d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 881d35567fcSMathieu Othacehe uintptr_t priv, 882d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 883d35567fcSMathieu Othacehe char *buf) 884d35567fcSMathieu Othacehe { 885d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 886d35567fcSMathieu Othacehe 887d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 888d35567fcSMathieu Othacehe } 889d35567fcSMathieu Othacehe 890d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 891d35567fcSMathieu Othacehe { 892d35567fcSMathieu Othacehe .name = "nearlevel", 893d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 894d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 895d35567fcSMathieu Othacehe }, 896d35567fcSMathieu Othacehe { /* sentinel */ } 897d35567fcSMathieu Othacehe }; 898d35567fcSMathieu Othacehe 899d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 900d35567fcSMathieu Othacehe { 901d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 902d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 903d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 904d35567fcSMathieu Othacehe }, { 905d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 906d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 907d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 908d35567fcSMathieu Othacehe }, { 909d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 910d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 911d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 912d35567fcSMathieu Othacehe } 913d35567fcSMathieu Othacehe }; 914d35567fcSMathieu Othacehe 915d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 916d35567fcSMathieu Othacehe { 917d35567fcSMathieu Othacehe .type = IIO_LIGHT, 918d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 919d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 920d35567fcSMathieu Othacehe }, { 921d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 922d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 923d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 924d35567fcSMathieu Othacehe } 925d35567fcSMathieu Othacehe }; 926d35567fcSMathieu Othacehe 927d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 928d35567fcSMathieu Othacehe { 929d35567fcSMathieu Othacehe .type = IIO_LIGHT, 9308fe78d52SMathieu Othacehe .scan_index = -1, 931d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 932d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 933d35567fcSMathieu Othacehe }, { 934d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 9358fe78d52SMathieu Othacehe .scan_index = 0, 936f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 937f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 938f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 939d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 940d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 941d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 9428fe78d52SMathieu Othacehe .scan_type = { 9438fe78d52SMathieu Othacehe .sign = 'u', 9448fe78d52SMathieu Othacehe .realbits = 16, 9458fe78d52SMathieu Othacehe .storagebits = 16, 9468fe78d52SMathieu Othacehe .endianness = IIO_CPU, 947d35567fcSMathieu Othacehe }, 9488fe78d52SMathieu Othacehe }, 9498fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 950d35567fcSMathieu Othacehe }; 951d35567fcSMathieu Othacehe 952*85e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 953*85e2c6a2SMårten Lindahl { 954*85e2c6a2SMårten Lindahl .type = IIO_LIGHT, 955*85e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 956*85e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_SCALE), 957*85e2c6a2SMårten Lindahl }, { 958*85e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 959*85e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 960*85e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 961*85e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 962*85e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 963*85e2c6a2SMårten Lindahl } 964*85e2c6a2SMårten Lindahl }; 965*85e2c6a2SMårten Lindahl 96662a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 96762a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 96862a1efb9SPeter Meerwald }; 96962a1efb9SPeter Meerwald 970d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 971d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 972f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 973f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 974d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 975d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 976d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 977d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 978d35567fcSMathieu Othacehe }; 979d35567fcSMathieu Othacehe 980*85e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 981*85e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 982*85e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 983*85e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 984*85e2c6a2SMårten Lindahl }; 985*85e2c6a2SMårten Lindahl 986d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 987d35567fcSMathieu Othacehe [VCNL4000] = { 988d35567fcSMathieu Othacehe .prod = "VCNL4000", 989d35567fcSMathieu Othacehe .init = vcnl4000_init, 990d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 991d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 992d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 993d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 994d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 995d35567fcSMathieu Othacehe .info = &vcnl4000_info, 996d35567fcSMathieu Othacehe .irq_support = false, 997d35567fcSMathieu Othacehe }, 998d35567fcSMathieu Othacehe [VCNL4010] = { 999d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1000d35567fcSMathieu Othacehe .init = vcnl4000_init, 1001d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1002d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1003d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1004d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1005d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1006d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1007d35567fcSMathieu Othacehe .irq_support = true, 1008d35567fcSMathieu Othacehe }, 1009d35567fcSMathieu Othacehe [VCNL4040] = { 1010d35567fcSMathieu Othacehe .prod = "VCNL4040", 1011d35567fcSMathieu Othacehe .init = vcnl4200_init, 1012d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1013d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1014d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1015*85e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 1016*85e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 1017*85e2c6a2SMårten Lindahl .info = &vcnl4040_info, 1018d35567fcSMathieu Othacehe .irq_support = false, 1019d35567fcSMathieu Othacehe }, 1020d35567fcSMathieu Othacehe [VCNL4200] = { 1021d35567fcSMathieu Othacehe .prod = "VCNL4200", 1022d35567fcSMathieu Othacehe .init = vcnl4200_init, 1023d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1024d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1025d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1026d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1027d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1028d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1029d35567fcSMathieu Othacehe .irq_support = false, 1030d35567fcSMathieu Othacehe }, 1031d35567fcSMathieu Othacehe }; 1032d35567fcSMathieu Othacehe 1033d35567fcSMathieu Othacehe static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 1034d35567fcSMathieu Othacehe { 1035d35567fcSMathieu Othacehe struct iio_dev *indio_dev = p; 1036d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1037d35567fcSMathieu Othacehe unsigned long isr; 1038d35567fcSMathieu Othacehe int ret; 1039d35567fcSMathieu Othacehe 1040d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 1041d35567fcSMathieu Othacehe if (ret < 0) 1042d35567fcSMathieu Othacehe goto end; 1043d35567fcSMathieu Othacehe 1044d35567fcSMathieu Othacehe isr = ret; 1045d35567fcSMathieu Othacehe 1046d35567fcSMathieu Othacehe if (isr & VCNL4010_INT_THR) { 1047d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 1048d35567fcSMathieu Othacehe iio_push_event(indio_dev, 1049d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 1050d35567fcSMathieu Othacehe IIO_PROXIMITY, 1051d35567fcSMathieu Othacehe 1, 1052d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 1053d35567fcSMathieu Othacehe IIO_EV_DIR_FALLING), 1054d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 1055d35567fcSMathieu Othacehe } 1056d35567fcSMathieu Othacehe 1057d35567fcSMathieu Othacehe if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 1058d35567fcSMathieu Othacehe iio_push_event(indio_dev, 1059d35567fcSMathieu Othacehe IIO_UNMOD_EVENT_CODE( 1060d35567fcSMathieu Othacehe IIO_PROXIMITY, 1061d35567fcSMathieu Othacehe 1, 1062d35567fcSMathieu Othacehe IIO_EV_TYPE_THRESH, 1063d35567fcSMathieu Othacehe IIO_EV_DIR_RISING), 1064d35567fcSMathieu Othacehe iio_get_time_ns(indio_dev)); 1065d35567fcSMathieu Othacehe } 1066d35567fcSMathieu Othacehe 1067d35567fcSMathieu Othacehe i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 1068d35567fcSMathieu Othacehe isr & VCNL4010_INT_THR); 1069d35567fcSMathieu Othacehe } 1070d35567fcSMathieu Othacehe 10718fe78d52SMathieu Othacehe if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 10728fe78d52SMathieu Othacehe iio_trigger_poll_chained(indio_dev->trig); 10738fe78d52SMathieu Othacehe 1074d35567fcSMathieu Othacehe end: 1075d35567fcSMathieu Othacehe return IRQ_HANDLED; 1076d35567fcSMathieu Othacehe } 1077d35567fcSMathieu Othacehe 10788fe78d52SMathieu Othacehe static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 10798fe78d52SMathieu Othacehe { 10808fe78d52SMathieu Othacehe struct iio_poll_func *pf = p; 10818fe78d52SMathieu Othacehe struct iio_dev *indio_dev = pf->indio_dev; 10828fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 10838fe78d52SMathieu Othacehe const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 1084dce793c0SJonathan Cameron u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 10858fe78d52SMathieu Othacehe bool data_read = false; 10868fe78d52SMathieu Othacehe unsigned long isr; 10878fe78d52SMathieu Othacehe int val = 0; 10888fe78d52SMathieu Othacehe int ret; 10898fe78d52SMathieu Othacehe 10908fe78d52SMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 10918fe78d52SMathieu Othacehe if (ret < 0) 10928fe78d52SMathieu Othacehe goto end; 10938fe78d52SMathieu Othacehe 10948fe78d52SMathieu Othacehe isr = ret; 10958fe78d52SMathieu Othacehe 10968fe78d52SMathieu Othacehe if (test_bit(0, active_scan_mask)) { 10978fe78d52SMathieu Othacehe if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 10988fe78d52SMathieu Othacehe ret = vcnl4000_read_data(data, 10998fe78d52SMathieu Othacehe VCNL4000_PS_RESULT_HI, 11008fe78d52SMathieu Othacehe &val); 11018fe78d52SMathieu Othacehe if (ret < 0) 11028fe78d52SMathieu Othacehe goto end; 11038fe78d52SMathieu Othacehe 11048fe78d52SMathieu Othacehe buffer[0] = val; 11058fe78d52SMathieu Othacehe data_read = true; 11068fe78d52SMathieu Othacehe } 11078fe78d52SMathieu Othacehe } 11088fe78d52SMathieu Othacehe 11098fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 11108fe78d52SMathieu Othacehe isr & VCNL4010_INT_DRDY); 11118fe78d52SMathieu Othacehe if (ret < 0) 11128fe78d52SMathieu Othacehe goto end; 11138fe78d52SMathieu Othacehe 11148fe78d52SMathieu Othacehe if (!data_read) 11158fe78d52SMathieu Othacehe goto end; 11168fe78d52SMathieu Othacehe 11178fe78d52SMathieu Othacehe iio_push_to_buffers_with_timestamp(indio_dev, buffer, 11188fe78d52SMathieu Othacehe iio_get_time_ns(indio_dev)); 11198fe78d52SMathieu Othacehe 11208fe78d52SMathieu Othacehe end: 11218fe78d52SMathieu Othacehe iio_trigger_notify_done(indio_dev->trig); 11228fe78d52SMathieu Othacehe return IRQ_HANDLED; 11238fe78d52SMathieu Othacehe } 11248fe78d52SMathieu Othacehe 11258fe78d52SMathieu Othacehe static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 11268fe78d52SMathieu Othacehe { 11278fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 11288fe78d52SMathieu Othacehe int ret; 11298fe78d52SMathieu Othacehe int cmd; 11308fe78d52SMathieu Othacehe 11318fe78d52SMathieu Othacehe /* Do not enable the buffer if we are already capturing events. */ 1132f11d59d8SLars-Peter Clausen if (vcnl4010_is_in_periodic_mode(data)) 1133f11d59d8SLars-Peter Clausen return -EBUSY; 11348fe78d52SMathieu Othacehe 11358fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 11368fe78d52SMathieu Othacehe VCNL4010_INT_PROX_EN); 11378fe78d52SMathieu Othacehe if (ret < 0) 1138f11d59d8SLars-Peter Clausen return ret; 11398fe78d52SMathieu Othacehe 11408fe78d52SMathieu Othacehe cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 1141f11d59d8SLars-Peter Clausen return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 11428fe78d52SMathieu Othacehe } 11438fe78d52SMathieu Othacehe 11448fe78d52SMathieu Othacehe static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 11458fe78d52SMathieu Othacehe { 11468fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1147f11d59d8SLars-Peter Clausen int ret; 11488fe78d52SMathieu Othacehe 11498fe78d52SMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 11508fe78d52SMathieu Othacehe if (ret < 0) 11518fe78d52SMathieu Othacehe return ret; 1152f11d59d8SLars-Peter Clausen 1153f11d59d8SLars-Peter Clausen return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 11548fe78d52SMathieu Othacehe } 11558fe78d52SMathieu Othacehe 11568fe78d52SMathieu Othacehe static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 11578fe78d52SMathieu Othacehe .postenable = &vcnl4010_buffer_postenable, 11588fe78d52SMathieu Othacehe .predisable = &vcnl4010_buffer_predisable, 11598fe78d52SMathieu Othacehe }; 11608fe78d52SMathieu Othacehe 11618fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 11628fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 11638fe78d52SMathieu Othacehe }; 11648fe78d52SMathieu Othacehe 11658fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 11668fe78d52SMathieu Othacehe { 11678fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 11688fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 11698fe78d52SMathieu Othacehe struct iio_trigger *trigger; 11708fe78d52SMathieu Othacehe 11718fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 117215ea2878SJonathan Cameron indio_dev->name, 117315ea2878SJonathan Cameron iio_device_id(indio_dev)); 11748fe78d52SMathieu Othacehe if (!trigger) 11758fe78d52SMathieu Othacehe return -ENOMEM; 11768fe78d52SMathieu Othacehe 11778fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 11788fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 11798fe78d52SMathieu Othacehe 11808fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 11818fe78d52SMathieu Othacehe } 11828fe78d52SMathieu Othacehe 1183fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 118462a1efb9SPeter Meerwald const struct i2c_device_id *id) 118562a1efb9SPeter Meerwald { 118662a1efb9SPeter Meerwald struct vcnl4000_data *data; 118762a1efb9SPeter Meerwald struct iio_dev *indio_dev; 11881ebc787aSTomas Novotny int ret; 118962a1efb9SPeter Meerwald 11902669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 119162a1efb9SPeter Meerwald if (!indio_dev) 119262a1efb9SPeter Meerwald return -ENOMEM; 119362a1efb9SPeter Meerwald 119462a1efb9SPeter Meerwald data = iio_priv(indio_dev); 119562a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 119662a1efb9SPeter Meerwald data->client = client; 11971ebc787aSTomas Novotny data->id = id->driver_data; 11981ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 119962a1efb9SPeter Meerwald 12001ebc787aSTomas Novotny ret = data->chip_spec->init(data); 120162a1efb9SPeter Meerwald if (ret < 0) 12022669d723SPeter Meerwald return ret; 120362a1efb9SPeter Meerwald 1204d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 12051ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 120662a1efb9SPeter Meerwald 1207f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1208f5a98e1fSGuido Günther &data->near_level)) 1209f5a98e1fSGuido Günther data->near_level = 0; 1210f5a98e1fSGuido Günther 1211d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1212d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1213d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 121462a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 121562a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 121662a1efb9SPeter Meerwald 1217d35567fcSMathieu Othacehe if (client->irq && data->chip_spec->irq_support) { 12188fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 12198fe78d52SMathieu Othacehe NULL, 12208fe78d52SMathieu Othacehe vcnl4010_trigger_handler, 12218fe78d52SMathieu Othacehe &vcnl4010_buffer_ops); 12228fe78d52SMathieu Othacehe if (ret < 0) { 12238fe78d52SMathieu Othacehe dev_err(&client->dev, 12248fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 12258fe78d52SMathieu Othacehe return ret; 12268fe78d52SMathieu Othacehe } 12278fe78d52SMathieu Othacehe 1228d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1229d35567fcSMathieu Othacehe NULL, vcnl4010_irq_thread, 1230d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1231d35567fcSMathieu Othacehe IRQF_ONESHOT, 1232d35567fcSMathieu Othacehe "vcnl4010_irq", 1233d35567fcSMathieu Othacehe indio_dev); 1234d35567fcSMathieu Othacehe if (ret < 0) { 1235d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1236d35567fcSMathieu Othacehe return ret; 1237d35567fcSMathieu Othacehe } 12388fe78d52SMathieu Othacehe 12398fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 12408fe78d52SMathieu Othacehe if (ret < 0) 12418fe78d52SMathieu Othacehe return ret; 1242d35567fcSMathieu Othacehe } 1243d35567fcSMathieu Othacehe 12445e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 12455e00708dSGuido Günther if (ret < 0) 12465e00708dSGuido Günther goto fail_poweroff; 12475e00708dSGuido Günther 12485e00708dSGuido Günther ret = iio_device_register(indio_dev); 12495e00708dSGuido Günther if (ret < 0) 12505e00708dSGuido Günther goto fail_poweroff; 12515e00708dSGuido Günther 12525e00708dSGuido Günther pm_runtime_enable(&client->dev); 12535e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 12545e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 12555e00708dSGuido Günther 12565e00708dSGuido Günther return 0; 12575e00708dSGuido Günther fail_poweroff: 12585e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 12595e00708dSGuido Günther return ret; 126062a1efb9SPeter Meerwald } 126162a1efb9SPeter Meerwald 1262ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1263ebd457d5SAngus Ainslie (Purism) { 1264ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 12651436a78cSMarco Felsch .data = (void *)VCNL4000, 1266ebd457d5SAngus Ainslie (Purism) }, 1267ebd457d5SAngus Ainslie (Purism) { 1268ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 12691436a78cSMarco Felsch .data = (void *)VCNL4010, 1270ebd457d5SAngus Ainslie (Purism) }, 1271ebd457d5SAngus Ainslie (Purism) { 12721436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 12731436a78cSMarco Felsch .data = (void *)VCNL4010, 1274ebd457d5SAngus Ainslie (Purism) }, 1275ebd457d5SAngus Ainslie (Purism) { 12767fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 12777fd1c260SMarco Felsch .data = (void *)VCNL4040, 12787fd1c260SMarco Felsch }, 12797fd1c260SMarco Felsch { 1280ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 12811436a78cSMarco Felsch .data = (void *)VCNL4200, 1282ebd457d5SAngus Ainslie (Purism) }, 1283ebd457d5SAngus Ainslie (Purism) {}, 1284ebd457d5SAngus Ainslie (Purism) }; 1285ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1286ebd457d5SAngus Ainslie (Purism) 1287ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 12885e00708dSGuido Günther { 12895e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 12905e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1291ab91da2fSUwe Kleine-König int ret; 12925e00708dSGuido Günther 12935e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 12945e00708dSGuido Günther pm_runtime_disable(&client->dev); 12955e00708dSGuido Günther iio_device_unregister(indio_dev); 12965e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 12975e00708dSGuido Günther 1298ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1299ab91da2fSUwe Kleine-König if (ret) 1300ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1301ab91da2fSUwe Kleine-König ERR_PTR(ret)); 13025e00708dSGuido Günther } 13035e00708dSGuido Günther 1304cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 13055e00708dSGuido Günther { 13065e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 13075e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 13085e00708dSGuido Günther 13095e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 13105e00708dSGuido Günther } 13115e00708dSGuido Günther 1312cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 13135e00708dSGuido Günther { 13145e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 13155e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 13165e00708dSGuido Günther 13175e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 13185e00708dSGuido Günther } 13195e00708dSGuido Günther 1320cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1321cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 13225e00708dSGuido Günther 132362a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 132462a1efb9SPeter Meerwald .driver = { 132562a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1326cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1327ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 132862a1efb9SPeter Meerwald }, 132962a1efb9SPeter Meerwald .probe = vcnl4000_probe, 133062a1efb9SPeter Meerwald .id_table = vcnl4000_id, 13315e00708dSGuido Günther .remove = vcnl4000_remove, 133262a1efb9SPeter Meerwald }; 133362a1efb9SPeter Meerwald 133462a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 133562a1efb9SPeter Meerwald 133662a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 13378fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 133862a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 133962a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1340