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 */ 68be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 69be38866fSTomas Novotny 705a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 715a441aadSAngus Ainslie (Purism) 7262a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 73ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 74ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 75ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 76ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 77d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 78d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 79d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 80d35567fcSMathieu Othacehe 81e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 82e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8385e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 8454667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 8554667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 8654667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 87e21b5b1fSMårten Lindahl 88d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 89d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 90d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 92d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 93d35567fcSMathieu Othacehe 94d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 95d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 96d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 97d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 98d35567fcSMathieu Othacehe 99d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 100d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 101d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 102d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 103d35567fcSMathieu Othacehe 104f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 105f6889c1bSMathieu Othacehe {1, 950000}, 106f6889c1bSMathieu Othacehe {3, 906250}, 107f6889c1bSMathieu Othacehe {7, 812500}, 108f6889c1bSMathieu Othacehe {16, 625000}, 109f6889c1bSMathieu Othacehe {31, 250000}, 110f6889c1bSMathieu Othacehe {62, 500000}, 111f6889c1bSMathieu Othacehe {125, 0}, 112f6889c1bSMathieu Othacehe {250, 0}, 113f6889c1bSMathieu Othacehe }; 11462a1efb9SPeter Meerwald 11585e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 11685e2c6a2SMårten Lindahl {0, 100}, 11785e2c6a2SMårten Lindahl {0, 150}, 11885e2c6a2SMårten Lindahl {0, 200}, 11985e2c6a2SMårten Lindahl {0, 250}, 12085e2c6a2SMårten Lindahl {0, 300}, 12185e2c6a2SMårten Lindahl {0, 350}, 12285e2c6a2SMårten Lindahl {0, 400}, 12385e2c6a2SMårten Lindahl {0, 800}, 12485e2c6a2SMårten Lindahl }; 12585e2c6a2SMårten Lindahl 1265e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1275e00708dSGuido Günther 1281ebc787aSTomas Novotny enum vcnl4000_device_ids { 1291ebc787aSTomas Novotny VCNL4000, 13050c50b97STomas Novotny VCNL4010, 1315a441aadSAngus Ainslie (Purism) VCNL4040, 132be38866fSTomas Novotny VCNL4200, 133be38866fSTomas Novotny }; 134be38866fSTomas Novotny 135be38866fSTomas Novotny struct vcnl4200_channel { 136be38866fSTomas Novotny u8 reg; 137be38866fSTomas Novotny ktime_t last_measurement; 138be38866fSTomas Novotny ktime_t sampling_rate; 139be38866fSTomas Novotny struct mutex lock; 1401ebc787aSTomas Novotny }; 1411ebc787aSTomas Novotny 14262a1efb9SPeter Meerwald struct vcnl4000_data { 14362a1efb9SPeter Meerwald struct i2c_client *client; 1441ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1451ebc787aSTomas Novotny int rev; 1461ebc787aSTomas Novotny int al_scale; 14754667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 1481ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 149be38866fSTomas Novotny struct mutex vcnl4000_lock; 150be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 151be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 152f5a98e1fSGuido Günther uint32_t near_level; 15362a1efb9SPeter Meerwald }; 15462a1efb9SPeter Meerwald 1551ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1561ebc787aSTomas Novotny const char *prod; 157d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 158d35567fcSMathieu Othacehe const int num_channels; 159d35567fcSMathieu Othacehe const struct iio_info *info; 160bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1611ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1621ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1631ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1645e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 165bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 166bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 1671ebc787aSTomas Novotny }; 1681ebc787aSTomas Novotny 16962a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1701ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 17150c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 17250c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1735a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 174be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 17562a1efb9SPeter Meerwald { } 17662a1efb9SPeter Meerwald }; 17762a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 17862a1efb9SPeter Meerwald 1795e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1805e00708dSGuido Günther { 1815e00708dSGuido Günther /* no suspend op */ 1825e00708dSGuido Günther return 0; 1835e00708dSGuido Günther } 1845e00708dSGuido Günther 1851ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1861ebc787aSTomas Novotny { 1871ebc787aSTomas Novotny int ret, prod_id; 1881ebc787aSTomas Novotny 1891ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1901ebc787aSTomas Novotny if (ret < 0) 1911ebc787aSTomas Novotny return ret; 1921ebc787aSTomas Novotny 1931ebc787aSTomas Novotny prod_id = ret >> 4; 19458bf9aceSTomas Novotny switch (prod_id) { 19558bf9aceSTomas Novotny case VCNL4000_PROD_ID: 19658bf9aceSTomas Novotny if (data->id != VCNL4000) 19758bf9aceSTomas Novotny dev_warn(&data->client->dev, 19858bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 19958bf9aceSTomas Novotny break; 20058bf9aceSTomas Novotny case VCNL4010_PROD_ID: 20158bf9aceSTomas Novotny if (data->id != VCNL4010) 20258bf9aceSTomas Novotny dev_warn(&data->client->dev, 20358bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 20458bf9aceSTomas Novotny break; 20558bf9aceSTomas Novotny default: 2061ebc787aSTomas Novotny return -ENODEV; 20758bf9aceSTomas Novotny } 2081ebc787aSTomas Novotny 2091ebc787aSTomas Novotny data->rev = ret & 0xf; 2101ebc787aSTomas Novotny data->al_scale = 250000; 211be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 212be38866fSTomas Novotny 2135e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 214be38866fSTomas Novotny }; 215be38866fSTomas Novotny 216e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2175e00708dSGuido Günther { 2185e00708dSGuido Günther int ret; 2195e00708dSGuido Günther 220e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 221e21b5b1fSMårten Lindahl 222e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 223e21b5b1fSMårten Lindahl if (ret < 0) 224e21b5b1fSMårten Lindahl goto out; 225e21b5b1fSMårten Lindahl 226e21b5b1fSMårten Lindahl if (en) 227e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 228e21b5b1fSMårten Lindahl else 229e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 230e21b5b1fSMårten Lindahl 231e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 232e21b5b1fSMårten Lindahl 233e21b5b1fSMårten Lindahl out: 234e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 235e21b5b1fSMårten Lindahl 236e21b5b1fSMårten Lindahl return ret; 237e21b5b1fSMårten Lindahl } 238e21b5b1fSMårten Lindahl 239e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 240e21b5b1fSMårten Lindahl { 241e21b5b1fSMårten Lindahl int ret; 242e21b5b1fSMårten Lindahl 243e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 244e21b5b1fSMårten Lindahl 245e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 246e21b5b1fSMårten Lindahl if (ret < 0) 247e21b5b1fSMårten Lindahl goto out; 248e21b5b1fSMårten Lindahl 249e21b5b1fSMårten Lindahl if (en) 250e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 251e21b5b1fSMårten Lindahl else 252e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 253e21b5b1fSMårten Lindahl 254e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 255e21b5b1fSMårten Lindahl 256e21b5b1fSMårten Lindahl out: 257e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 258e21b5b1fSMårten Lindahl 259e21b5b1fSMårten Lindahl return ret; 260e21b5b1fSMårten Lindahl } 261e21b5b1fSMårten Lindahl 262e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 263e21b5b1fSMårten Lindahl { 264e21b5b1fSMårten Lindahl int ret; 265e21b5b1fSMårten Lindahl 26654667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 26754667612SMårten Lindahl if (!on && data->ps_int) 26854667612SMårten Lindahl return 0; 26954667612SMårten Lindahl 270e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 2715e00708dSGuido Günther if (ret < 0) 2725e00708dSGuido Günther return ret; 2735e00708dSGuido Günther 274e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 2755e00708dSGuido Günther if (ret < 0) 2765e00708dSGuido Günther return ret; 2775e00708dSGuido Günther 2785e00708dSGuido Günther if (on) { 2795e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2805e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2815e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2825e00708dSGuido Günther } 2835e00708dSGuido Günther 2845e00708dSGuido Günther return 0; 2855e00708dSGuido Günther } 2865e00708dSGuido Günther 287be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 288be38866fSTomas Novotny { 2895a441aadSAngus Ainslie (Purism) int ret, id; 290be38866fSTomas Novotny 291be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 292be38866fSTomas Novotny if (ret < 0) 293be38866fSTomas Novotny return ret; 294be38866fSTomas Novotny 2955a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2965a441aadSAngus Ainslie (Purism) 2975a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 2985a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 2995a441aadSAngus Ainslie (Purism) if (ret < 0) 3005a441aadSAngus Ainslie (Purism) return ret; 3015a441aadSAngus Ainslie (Purism) 3025a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3035a441aadSAngus Ainslie (Purism) 3045a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 305be38866fSTomas Novotny return -ENODEV; 3065a441aadSAngus Ainslie (Purism) } 3075a441aadSAngus Ainslie (Purism) 3085a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 309be38866fSTomas Novotny 310be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 31154667612SMårten Lindahl data->ps_int = 0; 312be38866fSTomas Novotny 313be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 314be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3155a441aadSAngus Ainslie (Purism) switch (id) { 3165a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 317b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 318b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 319b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 320b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 321bc80573eSGuido Günther data->al_scale = 24000; 3225a441aadSAngus Ainslie (Purism) break; 3235a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3242ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3252ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3262ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3272ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 328bc80573eSGuido Günther data->al_scale = 120000; 3295a441aadSAngus Ainslie (Purism) break; 3305a441aadSAngus Ainslie (Purism) } 331be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 332be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3331ebc787aSTomas Novotny 3345e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3355e00708dSGuido Günther if (ret < 0) 3365e00708dSGuido Günther return ret; 3375e00708dSGuido Günther 3381ebc787aSTomas Novotny return 0; 3391ebc787aSTomas Novotny }; 3401ebc787aSTomas Novotny 341816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 342816956c3SMathieu Othacehe { 343816956c3SMathieu Othacehe s32 ret; 344816956c3SMathieu Othacehe 345816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 346816956c3SMathieu Othacehe if (ret < 0) 347816956c3SMathieu Othacehe return ret; 348816956c3SMathieu Othacehe 349816956c3SMathieu Othacehe *val = ret; 350816956c3SMathieu Othacehe return 0; 351816956c3SMathieu Othacehe } 352816956c3SMathieu Othacehe 353816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 354816956c3SMathieu Othacehe { 355816956c3SMathieu Othacehe if (val > U16_MAX) 356816956c3SMathieu Othacehe return -ERANGE; 357816956c3SMathieu Othacehe 358816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 359816956c3SMathieu Othacehe } 360816956c3SMathieu Othacehe 361816956c3SMathieu Othacehe 36262a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 36362a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 36462a1efb9SPeter Meerwald { 36562a1efb9SPeter Meerwald int tries = 20; 36662a1efb9SPeter Meerwald int ret; 36762a1efb9SPeter Meerwald 368be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 369ff34ed6dSPeter Meerwald-Stadler 37062a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 37162a1efb9SPeter Meerwald req_mask); 37262a1efb9SPeter Meerwald if (ret < 0) 373ff34ed6dSPeter Meerwald-Stadler goto fail; 37462a1efb9SPeter Meerwald 37562a1efb9SPeter Meerwald /* wait for data to become ready */ 37662a1efb9SPeter Meerwald while (tries--) { 37762a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 37862a1efb9SPeter Meerwald if (ret < 0) 379ff34ed6dSPeter Meerwald-Stadler goto fail; 38062a1efb9SPeter Meerwald if (ret & rdy_mask) 38162a1efb9SPeter Meerwald break; 38262a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 38362a1efb9SPeter Meerwald } 38462a1efb9SPeter Meerwald 38562a1efb9SPeter Meerwald if (tries < 0) { 38662a1efb9SPeter Meerwald dev_err(&data->client->dev, 38762a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 388ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 389ff34ed6dSPeter Meerwald-Stadler goto fail; 39062a1efb9SPeter Meerwald } 39162a1efb9SPeter Meerwald 392816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 39362a1efb9SPeter Meerwald if (ret < 0) 394ff34ed6dSPeter Meerwald-Stadler goto fail; 39562a1efb9SPeter Meerwald 396be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 39762a1efb9SPeter Meerwald 39862a1efb9SPeter Meerwald return 0; 399ff34ed6dSPeter Meerwald-Stadler 400ff34ed6dSPeter Meerwald-Stadler fail: 401be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 402ff34ed6dSPeter Meerwald-Stadler return ret; 40362a1efb9SPeter Meerwald } 40462a1efb9SPeter Meerwald 405be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 406be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 407be38866fSTomas Novotny { 408be38866fSTomas Novotny int ret; 409be38866fSTomas Novotny s64 delta; 410be38866fSTomas Novotny ktime_t next_measurement; 411be38866fSTomas Novotny 412be38866fSTomas Novotny mutex_lock(&chan->lock); 413be38866fSTomas Novotny 414be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 415be38866fSTomas Novotny chan->sampling_rate); 416be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 417be38866fSTomas Novotny if (delta > 0) 418be38866fSTomas Novotny usleep_range(delta, delta + 500); 419be38866fSTomas Novotny chan->last_measurement = ktime_get(); 420be38866fSTomas Novotny 421be38866fSTomas Novotny mutex_unlock(&chan->lock); 422be38866fSTomas Novotny 423be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 424be38866fSTomas Novotny if (ret < 0) 425be38866fSTomas Novotny return ret; 426be38866fSTomas Novotny 427be38866fSTomas Novotny *val = ret; 428be38866fSTomas Novotny 429be38866fSTomas Novotny return 0; 430be38866fSTomas Novotny } 431be38866fSTomas Novotny 4321ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4331ebc787aSTomas Novotny { 4341ebc787aSTomas Novotny return vcnl4000_measure(data, 4351ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4361ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4371ebc787aSTomas Novotny } 4381ebc787aSTomas Novotny 439be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 440be38866fSTomas Novotny { 441be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 442be38866fSTomas Novotny } 443be38866fSTomas Novotny 4441ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4451ebc787aSTomas Novotny { 4461ebc787aSTomas Novotny return vcnl4000_measure(data, 4471ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4481ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4491ebc787aSTomas Novotny } 4501ebc787aSTomas Novotny 451be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 452be38866fSTomas Novotny { 453be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 454be38866fSTomas Novotny } 455be38866fSTomas Novotny 456f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 457f6889c1bSMathieu Othacehe int *val2) 458f6889c1bSMathieu Othacehe { 459f6889c1bSMathieu Othacehe int ret; 460f6889c1bSMathieu Othacehe 461f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 462f6889c1bSMathieu Othacehe if (ret < 0) 463f6889c1bSMathieu Othacehe return ret; 464f6889c1bSMathieu Othacehe 465f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 466f6889c1bSMathieu Othacehe return -EINVAL; 467f6889c1bSMathieu Othacehe 468f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 469f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 470f6889c1bSMathieu Othacehe 471f6889c1bSMathieu Othacehe return 0; 472f6889c1bSMathieu Othacehe } 473f6889c1bSMathieu Othacehe 474d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 475f5a98e1fSGuido Günther { 476d35567fcSMathieu Othacehe int ret; 477f5a98e1fSGuido Günther 478d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 479d35567fcSMathieu Othacehe if (ret < 0) 480d35567fcSMathieu Othacehe return false; 481d35567fcSMathieu Othacehe 482d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 483f5a98e1fSGuido Günther } 484f5a98e1fSGuido Günther 4855e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4865e00708dSGuido Günther { 4875e00708dSGuido Günther struct device *dev = &data->client->dev; 4885e00708dSGuido Günther int ret; 4895e00708dSGuido Günther 4905e00708dSGuido Günther if (on) { 491db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4925e00708dSGuido Günther } else { 4935e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4945e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4955e00708dSGuido Günther } 4965e00708dSGuido Günther 4975e00708dSGuido Günther return ret; 4985e00708dSGuido Günther } 4995e00708dSGuido Günther 50085e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 50185e2c6a2SMårten Lindahl { 50285e2c6a2SMårten Lindahl int ret; 50385e2c6a2SMårten Lindahl 50485e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 50585e2c6a2SMårten Lindahl if (ret < 0) 50685e2c6a2SMårten Lindahl return ret; 50785e2c6a2SMårten Lindahl 50885e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 50985e2c6a2SMårten Lindahl 51085e2c6a2SMårten Lindahl if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times)) 51185e2c6a2SMårten Lindahl return -EINVAL; 51285e2c6a2SMårten Lindahl 51385e2c6a2SMårten Lindahl *val = vcnl4040_ps_it_times[ret][0]; 51485e2c6a2SMårten Lindahl *val2 = vcnl4040_ps_it_times[ret][1]; 51585e2c6a2SMårten Lindahl 51685e2c6a2SMårten Lindahl return 0; 51785e2c6a2SMårten Lindahl } 51885e2c6a2SMårten Lindahl 51985e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 52085e2c6a2SMårten Lindahl { 52185e2c6a2SMårten Lindahl unsigned int i; 52285e2c6a2SMårten Lindahl int ret, index = -1; 52385e2c6a2SMårten Lindahl u16 regval; 52485e2c6a2SMårten Lindahl 52585e2c6a2SMårten Lindahl for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) { 52685e2c6a2SMårten Lindahl if (val == vcnl4040_ps_it_times[i][1]) { 52785e2c6a2SMårten Lindahl index = i; 52885e2c6a2SMårten Lindahl break; 52985e2c6a2SMårten Lindahl } 53085e2c6a2SMårten Lindahl } 53185e2c6a2SMårten Lindahl 53285e2c6a2SMårten Lindahl if (index < 0) 53385e2c6a2SMårten Lindahl return -EINVAL; 53485e2c6a2SMårten Lindahl 53585e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 53685e2c6a2SMårten Lindahl 53785e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 53885e2c6a2SMårten Lindahl if (ret < 0) 53985e2c6a2SMårten Lindahl goto out; 54085e2c6a2SMårten Lindahl 54185e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 54285e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 54385e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 54485e2c6a2SMårten Lindahl regval); 54585e2c6a2SMårten Lindahl 54685e2c6a2SMårten Lindahl out: 54785e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 54885e2c6a2SMårten Lindahl return ret; 54985e2c6a2SMårten Lindahl } 55085e2c6a2SMårten Lindahl 55162a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 55262a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 55362a1efb9SPeter Meerwald int *val, int *val2, long mask) 55462a1efb9SPeter Meerwald { 5555d693139SPeter Meerwald-Stadler int ret; 55662a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 55762a1efb9SPeter Meerwald 55862a1efb9SPeter Meerwald switch (mask) { 55962a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 5605e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 5615e00708dSGuido Günther if (ret < 0) 5625e00708dSGuido Günther return ret; 5635e00708dSGuido Günther 56462a1efb9SPeter Meerwald switch (chan->type) { 56562a1efb9SPeter Meerwald case IIO_LIGHT: 5661ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 5674a818643SGuido Günther if (!ret) 5684a818643SGuido Günther ret = IIO_VAL_INT; 5694a818643SGuido Günther break; 57062a1efb9SPeter Meerwald case IIO_PROXIMITY: 5711ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 5724a818643SGuido Günther if (!ret) 5734a818643SGuido Günther ret = IIO_VAL_INT; 5744a818643SGuido Günther break; 57562a1efb9SPeter Meerwald default: 5764a818643SGuido Günther ret = -EINVAL; 57762a1efb9SPeter Meerwald } 5785e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 5794a818643SGuido Günther return ret; 58062a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 5815d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 5825d693139SPeter Meerwald-Stadler return -EINVAL; 5835d693139SPeter Meerwald-Stadler 58462a1efb9SPeter Meerwald *val = 0; 5851ebc787aSTomas Novotny *val2 = data->al_scale; 5865d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 58785e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 58885e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 58985e2c6a2SMårten Lindahl return -EINVAL; 59085e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 59185e2c6a2SMårten Lindahl if (ret < 0) 59285e2c6a2SMårten Lindahl return ret; 59385e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 59485e2c6a2SMårten Lindahl default: 59585e2c6a2SMårten Lindahl return -EINVAL; 59685e2c6a2SMårten Lindahl } 59785e2c6a2SMårten Lindahl } 59885e2c6a2SMårten Lindahl 59985e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 60085e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 60185e2c6a2SMårten Lindahl int val, int val2, long mask) 60285e2c6a2SMårten Lindahl { 60385e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 60485e2c6a2SMårten Lindahl 60585e2c6a2SMårten Lindahl switch (mask) { 60685e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 60785e2c6a2SMårten Lindahl if (val != 0) 60885e2c6a2SMårten Lindahl return -EINVAL; 60985e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 61085e2c6a2SMårten Lindahl return -EINVAL; 61185e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 61285e2c6a2SMårten Lindahl default: 61385e2c6a2SMårten Lindahl return -EINVAL; 61485e2c6a2SMårten Lindahl } 61585e2c6a2SMårten Lindahl } 61685e2c6a2SMårten Lindahl 61785e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 61885e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 61985e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 62085e2c6a2SMårten Lindahl long mask) 62185e2c6a2SMårten Lindahl { 62285e2c6a2SMårten Lindahl switch (mask) { 62385e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 62485e2c6a2SMårten Lindahl *vals = (int *)vcnl4040_ps_it_times; 62585e2c6a2SMårten Lindahl *type = IIO_VAL_INT_PLUS_MICRO; 62685e2c6a2SMårten Lindahl *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times); 62785e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 62862a1efb9SPeter Meerwald default: 6295d693139SPeter Meerwald-Stadler return -EINVAL; 63062a1efb9SPeter Meerwald } 63162a1efb9SPeter Meerwald } 63262a1efb9SPeter Meerwald 633d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 634d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 635d35567fcSMathieu Othacehe int *val, int *val2, long mask) 636d35567fcSMathieu Othacehe { 637d35567fcSMathieu Othacehe int ret; 638d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 639d35567fcSMathieu Othacehe 640d35567fcSMathieu Othacehe switch (mask) { 641d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 642d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 643d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 644d35567fcSMathieu Othacehe if (ret) 645d35567fcSMathieu Othacehe return ret; 646d35567fcSMathieu Othacehe 647d35567fcSMathieu Othacehe /* Protect against event capture. */ 648d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 649d35567fcSMathieu Othacehe ret = -EBUSY; 650d35567fcSMathieu Othacehe } else { 651d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 652d35567fcSMathieu Othacehe mask); 653d35567fcSMathieu Othacehe } 654d35567fcSMathieu Othacehe 655d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 656d35567fcSMathieu Othacehe return ret; 657f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 658f6889c1bSMathieu Othacehe switch (chan->type) { 659f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 660f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 661f6889c1bSMathieu Othacehe if (ret < 0) 662f6889c1bSMathieu Othacehe return ret; 663f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 664d35567fcSMathieu Othacehe default: 665d35567fcSMathieu Othacehe return -EINVAL; 666d35567fcSMathieu Othacehe } 667f6889c1bSMathieu Othacehe default: 668f6889c1bSMathieu Othacehe return -EINVAL; 669f6889c1bSMathieu Othacehe } 670f6889c1bSMathieu Othacehe } 671f6889c1bSMathieu Othacehe 672f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 673f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 674f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 675f6889c1bSMathieu Othacehe long mask) 676f6889c1bSMathieu Othacehe { 677f6889c1bSMathieu Othacehe switch (mask) { 678f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 679f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 680f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 681f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 682f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 683f6889c1bSMathieu Othacehe default: 684f6889c1bSMathieu Othacehe return -EINVAL; 685f6889c1bSMathieu Othacehe } 686f6889c1bSMathieu Othacehe } 687f6889c1bSMathieu Othacehe 688f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 689f6889c1bSMathieu Othacehe int val2) 690f6889c1bSMathieu Othacehe { 691f6889c1bSMathieu Othacehe unsigned int i; 692f6889c1bSMathieu Othacehe int index = -1; 693f6889c1bSMathieu Othacehe 694f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 695f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 696f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 697f6889c1bSMathieu Othacehe index = i; 698f6889c1bSMathieu Othacehe break; 699f6889c1bSMathieu Othacehe } 700f6889c1bSMathieu Othacehe } 701f6889c1bSMathieu Othacehe 702f6889c1bSMathieu Othacehe if (index < 0) 703f6889c1bSMathieu Othacehe return -EINVAL; 704f6889c1bSMathieu Othacehe 705f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 706f6889c1bSMathieu Othacehe index); 707f6889c1bSMathieu Othacehe } 708f6889c1bSMathieu Othacehe 709f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 710f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 711f6889c1bSMathieu Othacehe int val, int val2, long mask) 712f6889c1bSMathieu Othacehe { 713f6889c1bSMathieu Othacehe int ret; 714f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 715f6889c1bSMathieu Othacehe 716f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 717f6889c1bSMathieu Othacehe if (ret) 718f6889c1bSMathieu Othacehe return ret; 719f6889c1bSMathieu Othacehe 720f6889c1bSMathieu Othacehe /* Protect against event capture. */ 721f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 722f6889c1bSMathieu Othacehe ret = -EBUSY; 723f6889c1bSMathieu Othacehe goto end; 724f6889c1bSMathieu Othacehe } 725f6889c1bSMathieu Othacehe 726f6889c1bSMathieu Othacehe switch (mask) { 727f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 728f6889c1bSMathieu Othacehe switch (chan->type) { 729f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 730f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 731f6889c1bSMathieu Othacehe goto end; 732f6889c1bSMathieu Othacehe default: 733f6889c1bSMathieu Othacehe ret = -EINVAL; 734f6889c1bSMathieu Othacehe goto end; 735f6889c1bSMathieu Othacehe } 736f6889c1bSMathieu Othacehe default: 737f6889c1bSMathieu Othacehe ret = -EINVAL; 738f6889c1bSMathieu Othacehe goto end; 739f6889c1bSMathieu Othacehe } 740f6889c1bSMathieu Othacehe 741f6889c1bSMathieu Othacehe end: 742f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 743f6889c1bSMathieu Othacehe return ret; 744d35567fcSMathieu Othacehe } 745d35567fcSMathieu Othacehe 746d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 747d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 748d35567fcSMathieu Othacehe enum iio_event_type type, 749d35567fcSMathieu Othacehe enum iio_event_direction dir, 750d35567fcSMathieu Othacehe enum iio_event_info info, 751d35567fcSMathieu Othacehe int *val, int *val2) 752d35567fcSMathieu Othacehe { 753d35567fcSMathieu Othacehe int ret; 754d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 755d35567fcSMathieu Othacehe 756d35567fcSMathieu Othacehe switch (info) { 757d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 758d35567fcSMathieu Othacehe switch (dir) { 759d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 760d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 761d35567fcSMathieu Othacehe val); 762d35567fcSMathieu Othacehe if (ret < 0) 763d35567fcSMathieu Othacehe return ret; 764d35567fcSMathieu Othacehe return IIO_VAL_INT; 765d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 766d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 767d35567fcSMathieu Othacehe val); 768d35567fcSMathieu Othacehe if (ret < 0) 769d35567fcSMathieu Othacehe return ret; 770d35567fcSMathieu Othacehe return IIO_VAL_INT; 771d35567fcSMathieu Othacehe default: 772d35567fcSMathieu Othacehe return -EINVAL; 773d35567fcSMathieu Othacehe } 774d35567fcSMathieu Othacehe default: 775d35567fcSMathieu Othacehe return -EINVAL; 776d35567fcSMathieu Othacehe } 777d35567fcSMathieu Othacehe } 778d35567fcSMathieu Othacehe 779d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 780d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 781d35567fcSMathieu Othacehe enum iio_event_type type, 782d35567fcSMathieu Othacehe enum iio_event_direction dir, 783d35567fcSMathieu Othacehe enum iio_event_info info, 784d35567fcSMathieu Othacehe int val, int val2) 785d35567fcSMathieu Othacehe { 786d35567fcSMathieu Othacehe int ret; 787d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 788d35567fcSMathieu Othacehe 789d35567fcSMathieu Othacehe switch (info) { 790d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 791d35567fcSMathieu Othacehe switch (dir) { 792d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 793d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 794d35567fcSMathieu Othacehe val); 795d35567fcSMathieu Othacehe if (ret < 0) 796d35567fcSMathieu Othacehe return ret; 797d35567fcSMathieu Othacehe return IIO_VAL_INT; 798d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 799d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 800d35567fcSMathieu Othacehe val); 801d35567fcSMathieu Othacehe if (ret < 0) 802d35567fcSMathieu Othacehe return ret; 803d35567fcSMathieu Othacehe return IIO_VAL_INT; 804d35567fcSMathieu Othacehe default: 805d35567fcSMathieu Othacehe return -EINVAL; 806d35567fcSMathieu Othacehe } 807d35567fcSMathieu Othacehe default: 808d35567fcSMathieu Othacehe return -EINVAL; 809d35567fcSMathieu Othacehe } 810d35567fcSMathieu Othacehe } 811d35567fcSMathieu Othacehe 81254667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 81354667612SMårten Lindahl const struct iio_chan_spec *chan, 81454667612SMårten Lindahl enum iio_event_type type, 81554667612SMårten Lindahl enum iio_event_direction dir, 81654667612SMårten Lindahl enum iio_event_info info, 81754667612SMårten Lindahl int *val, int *val2) 81854667612SMårten Lindahl { 81954667612SMårten Lindahl int ret; 82054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 82154667612SMårten Lindahl 82254667612SMårten Lindahl switch (dir) { 82354667612SMårten Lindahl case IIO_EV_DIR_RISING: 82454667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 82554667612SMårten Lindahl VCNL4040_PS_THDH_LM); 82654667612SMårten Lindahl if (ret < 0) 82754667612SMårten Lindahl return ret; 82854667612SMårten Lindahl *val = ret; 82954667612SMårten Lindahl return IIO_VAL_INT; 83054667612SMårten Lindahl case IIO_EV_DIR_FALLING: 83154667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 83254667612SMårten Lindahl VCNL4040_PS_THDL_LM); 83354667612SMårten Lindahl if (ret < 0) 83454667612SMårten Lindahl return ret; 83554667612SMårten Lindahl *val = ret; 83654667612SMårten Lindahl return IIO_VAL_INT; 83754667612SMårten Lindahl default: 83854667612SMårten Lindahl return -EINVAL; 83954667612SMårten Lindahl } 84054667612SMårten Lindahl } 84154667612SMårten Lindahl 84254667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 84354667612SMårten Lindahl const struct iio_chan_spec *chan, 84454667612SMårten Lindahl enum iio_event_type type, 84554667612SMårten Lindahl enum iio_event_direction dir, 84654667612SMårten Lindahl enum iio_event_info info, 84754667612SMårten Lindahl int val, int val2) 84854667612SMårten Lindahl { 84954667612SMårten Lindahl int ret; 85054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 85154667612SMårten Lindahl 85254667612SMårten Lindahl switch (dir) { 85354667612SMårten Lindahl case IIO_EV_DIR_RISING: 85454667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 85554667612SMårten Lindahl VCNL4040_PS_THDH_LM, val); 85654667612SMårten Lindahl if (ret < 0) 85754667612SMårten Lindahl return ret; 85854667612SMårten Lindahl return IIO_VAL_INT; 85954667612SMårten Lindahl case IIO_EV_DIR_FALLING: 86054667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 86154667612SMårten Lindahl VCNL4040_PS_THDL_LM, val); 86254667612SMårten Lindahl if (ret < 0) 86354667612SMårten Lindahl return ret; 86454667612SMårten Lindahl return IIO_VAL_INT; 86554667612SMårten Lindahl default: 86654667612SMårten Lindahl return -EINVAL; 86754667612SMårten Lindahl } 86854667612SMårten Lindahl } 86954667612SMårten Lindahl 870d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 871d35567fcSMathieu Othacehe { 872d35567fcSMathieu Othacehe int ret; 873d35567fcSMathieu Othacehe 874d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 875d35567fcSMathieu Othacehe if (ret < 0) 876d35567fcSMathieu Othacehe return false; 877d35567fcSMathieu Othacehe 878d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 879d35567fcSMathieu Othacehe } 880d35567fcSMathieu Othacehe 881d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 882d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 883d35567fcSMathieu Othacehe enum iio_event_type type, 884d35567fcSMathieu Othacehe enum iio_event_direction dir) 885d35567fcSMathieu Othacehe { 886d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 887d35567fcSMathieu Othacehe 888d35567fcSMathieu Othacehe switch (chan->type) { 889d35567fcSMathieu Othacehe case IIO_PROXIMITY: 890d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 891d35567fcSMathieu Othacehe default: 892d35567fcSMathieu Othacehe return -EINVAL; 893d35567fcSMathieu Othacehe } 894d35567fcSMathieu Othacehe } 895d35567fcSMathieu Othacehe 896d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 897d35567fcSMathieu Othacehe { 898d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 899d35567fcSMathieu Othacehe int ret; 900d35567fcSMathieu Othacehe int icr; 901d35567fcSMathieu Othacehe int command; 902d35567fcSMathieu Othacehe 903d35567fcSMathieu Othacehe if (state) { 904d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 905d35567fcSMathieu Othacehe if (ret) 906d35567fcSMathieu Othacehe return ret; 907d35567fcSMathieu Othacehe 908d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 909d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 910d35567fcSMathieu Othacehe 911d35567fcSMathieu Othacehe /* 912d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 913d35567fcSMathieu Othacehe * default. 914d35567fcSMathieu Othacehe */ 915d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 916d35567fcSMathieu Othacehe } else { 917d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 918d35567fcSMathieu Othacehe return 0; 919d35567fcSMathieu Othacehe 920d35567fcSMathieu Othacehe command = 0; 921d35567fcSMathieu Othacehe icr = 0; 922d35567fcSMathieu Othacehe } 923d35567fcSMathieu Othacehe 924d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 925d35567fcSMathieu Othacehe command); 926d35567fcSMathieu Othacehe if (ret < 0) 927d35567fcSMathieu Othacehe goto end; 928d35567fcSMathieu Othacehe 929d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 930d35567fcSMathieu Othacehe 931d35567fcSMathieu Othacehe end: 932d35567fcSMathieu Othacehe if (state) 933d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 934d35567fcSMathieu Othacehe 935d35567fcSMathieu Othacehe return ret; 936d35567fcSMathieu Othacehe } 937d35567fcSMathieu Othacehe 938d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 939d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 940d35567fcSMathieu Othacehe enum iio_event_type type, 941d35567fcSMathieu Othacehe enum iio_event_direction dir, 942d35567fcSMathieu Othacehe int state) 943d35567fcSMathieu Othacehe { 944d35567fcSMathieu Othacehe switch (chan->type) { 945d35567fcSMathieu Othacehe case IIO_PROXIMITY: 946d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 947d35567fcSMathieu Othacehe default: 948d35567fcSMathieu Othacehe return -EINVAL; 949d35567fcSMathieu Othacehe } 950d35567fcSMathieu Othacehe } 951d35567fcSMathieu Othacehe 95254667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 95354667612SMårten Lindahl const struct iio_chan_spec *chan, 95454667612SMårten Lindahl enum iio_event_type type, 95554667612SMårten Lindahl enum iio_event_direction dir) 95654667612SMårten Lindahl { 95754667612SMårten Lindahl int ret; 95854667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 95954667612SMårten Lindahl 96054667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 96154667612SMårten Lindahl if (ret < 0) 96254667612SMårten Lindahl return ret; 96354667612SMårten Lindahl 96454667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 96554667612SMårten Lindahl 96654667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 96754667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 96854667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 96954667612SMårten Lindahl } 97054667612SMårten Lindahl 97154667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 97254667612SMårten Lindahl const struct iio_chan_spec *chan, 97354667612SMårten Lindahl enum iio_event_type type, 97454667612SMårten Lindahl enum iio_event_direction dir, int state) 97554667612SMårten Lindahl { 97654667612SMårten Lindahl int ret; 97754667612SMårten Lindahl u16 val, mask; 97854667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 97954667612SMårten Lindahl 98054667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 98154667612SMårten Lindahl 98254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 98354667612SMårten Lindahl if (ret < 0) 98454667612SMårten Lindahl goto out; 98554667612SMårten Lindahl 98654667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 98754667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 98854667612SMårten Lindahl else 98954667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 99054667612SMårten Lindahl 99154667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 99254667612SMårten Lindahl 99354667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 99454667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 99554667612SMårten Lindahl 99654667612SMårten Lindahl out: 99754667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 99854667612SMårten Lindahl data->chip_spec->set_power_state(data, data->ps_int != 0); 99954667612SMårten Lindahl 100054667612SMårten Lindahl return ret; 100154667612SMårten Lindahl } 100254667612SMårten Lindahl 100354667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 100454667612SMårten Lindahl { 100554667612SMårten Lindahl struct iio_dev *indio_dev = p; 100654667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 100754667612SMårten Lindahl int ret; 100854667612SMårten Lindahl 100954667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4040_INT_FLAGS); 101054667612SMårten Lindahl if (ret < 0) 101154667612SMårten Lindahl return IRQ_HANDLED; 101254667612SMårten Lindahl 101354667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 101454667612SMårten Lindahl iio_push_event(indio_dev, 101554667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 101654667612SMårten Lindahl IIO_EV_TYPE_THRESH, 101754667612SMårten Lindahl IIO_EV_DIR_RISING), 101854667612SMårten Lindahl iio_get_time_ns(indio_dev)); 101954667612SMårten Lindahl } 102054667612SMårten Lindahl 102154667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 102254667612SMårten Lindahl iio_push_event(indio_dev, 102354667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 102454667612SMårten Lindahl IIO_EV_TYPE_THRESH, 102554667612SMårten Lindahl IIO_EV_DIR_FALLING), 102654667612SMårten Lindahl iio_get_time_ns(indio_dev)); 102754667612SMårten Lindahl } 102854667612SMårten Lindahl 102954667612SMårten Lindahl return IRQ_HANDLED; 103054667612SMårten Lindahl } 103154667612SMårten Lindahl 1032d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1033d35567fcSMathieu Othacehe uintptr_t priv, 1034d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1035d35567fcSMathieu Othacehe char *buf) 1036d35567fcSMathieu Othacehe { 1037d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1038d35567fcSMathieu Othacehe 1039d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1040d35567fcSMathieu Othacehe } 1041d35567fcSMathieu Othacehe 10423a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 10433a52d32aSMårten Lindahl { 10443a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 10453a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 10463a52d32aSMårten Lindahl unsigned long isr; 10473a52d32aSMårten Lindahl int ret; 10483a52d32aSMårten Lindahl 10493a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 10503a52d32aSMårten Lindahl if (ret < 0) 10513a52d32aSMårten Lindahl goto end; 10523a52d32aSMårten Lindahl 10533a52d32aSMårten Lindahl isr = ret; 10543a52d32aSMårten Lindahl 10553a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 10563a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 10573a52d32aSMårten Lindahl iio_push_event(indio_dev, 10583a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 10593a52d32aSMårten Lindahl IIO_PROXIMITY, 10603a52d32aSMårten Lindahl 1, 10613a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 10623a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 10633a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 10643a52d32aSMårten Lindahl } 10653a52d32aSMårten Lindahl 10663a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 10673a52d32aSMårten Lindahl iio_push_event(indio_dev, 10683a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 10693a52d32aSMårten Lindahl IIO_PROXIMITY, 10703a52d32aSMårten Lindahl 1, 10713a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 10723a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 10733a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 10743a52d32aSMårten Lindahl } 10753a52d32aSMårten Lindahl 10763a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 10773a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 10783a52d32aSMårten Lindahl } 10793a52d32aSMårten Lindahl 10803a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1081*f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 10823a52d32aSMårten Lindahl 10833a52d32aSMårten Lindahl end: 10843a52d32aSMårten Lindahl return IRQ_HANDLED; 10853a52d32aSMårten Lindahl } 10863a52d32aSMårten Lindahl 10873a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 10883a52d32aSMårten Lindahl { 10893a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 10903a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 10913a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 10923a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 10933a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 10943a52d32aSMårten Lindahl bool data_read = false; 10953a52d32aSMårten Lindahl unsigned long isr; 10963a52d32aSMårten Lindahl int val = 0; 10973a52d32aSMårten Lindahl int ret; 10983a52d32aSMårten Lindahl 10993a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 11003a52d32aSMårten Lindahl if (ret < 0) 11013a52d32aSMårten Lindahl goto end; 11023a52d32aSMårten Lindahl 11033a52d32aSMårten Lindahl isr = ret; 11043a52d32aSMårten Lindahl 11053a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 11063a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 11073a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 11083a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 11093a52d32aSMårten Lindahl &val); 11103a52d32aSMårten Lindahl if (ret < 0) 11113a52d32aSMårten Lindahl goto end; 11123a52d32aSMårten Lindahl 11133a52d32aSMårten Lindahl buffer[0] = val; 11143a52d32aSMårten Lindahl data_read = true; 11153a52d32aSMårten Lindahl } 11163a52d32aSMårten Lindahl } 11173a52d32aSMårten Lindahl 11183a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 11193a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 11203a52d32aSMårten Lindahl if (ret < 0) 11213a52d32aSMårten Lindahl goto end; 11223a52d32aSMårten Lindahl 11233a52d32aSMårten Lindahl if (!data_read) 11243a52d32aSMårten Lindahl goto end; 11253a52d32aSMårten Lindahl 11263a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 11273a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 11283a52d32aSMårten Lindahl 11293a52d32aSMårten Lindahl end: 11303a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 11313a52d32aSMårten Lindahl return IRQ_HANDLED; 11323a52d32aSMårten Lindahl } 11333a52d32aSMårten Lindahl 11343a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 11353a52d32aSMårten Lindahl { 11363a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11373a52d32aSMårten Lindahl int ret; 11383a52d32aSMårten Lindahl int cmd; 11393a52d32aSMårten Lindahl 11403a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 11413a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 11423a52d32aSMårten Lindahl return -EBUSY; 11433a52d32aSMårten Lindahl 11443a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 11453a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 11463a52d32aSMårten Lindahl if (ret < 0) 11473a52d32aSMårten Lindahl return ret; 11483a52d32aSMårten Lindahl 11493a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 11503a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 11513a52d32aSMårten Lindahl } 11523a52d32aSMårten Lindahl 11533a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 11543a52d32aSMårten Lindahl { 11553a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11563a52d32aSMårten Lindahl int ret; 11573a52d32aSMårten Lindahl 11583a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 11593a52d32aSMårten Lindahl if (ret < 0) 11603a52d32aSMårten Lindahl return ret; 11613a52d32aSMårten Lindahl 11623a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 11633a52d32aSMårten Lindahl } 11643a52d32aSMårten Lindahl 11653a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 11663a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 11673a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 11683a52d32aSMårten Lindahl }; 11693a52d32aSMårten Lindahl 1170d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1171d35567fcSMathieu Othacehe { 1172d35567fcSMathieu Othacehe .name = "nearlevel", 1173d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1174d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1175d35567fcSMathieu Othacehe }, 1176d35567fcSMathieu Othacehe { /* sentinel */ } 1177d35567fcSMathieu Othacehe }; 1178d35567fcSMathieu Othacehe 1179d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1180d35567fcSMathieu Othacehe { 1181d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1182d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1183d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1184d35567fcSMathieu Othacehe }, { 1185d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1186d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1187d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1188d35567fcSMathieu Othacehe }, { 1189d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1190d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1191d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1192d35567fcSMathieu Othacehe } 1193d35567fcSMathieu Othacehe }; 1194d35567fcSMathieu Othacehe 119554667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 119654667612SMårten Lindahl { 119754667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 119854667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 119954667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 120054667612SMårten Lindahl }, { 120154667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 120254667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 120354667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 120454667612SMårten Lindahl }, 120554667612SMårten Lindahl }; 120654667612SMårten Lindahl 1207d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1208d35567fcSMathieu Othacehe { 1209d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1210d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1211d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1212d35567fcSMathieu Othacehe }, { 1213d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1214d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1215d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1216d35567fcSMathieu Othacehe } 1217d35567fcSMathieu Othacehe }; 1218d35567fcSMathieu Othacehe 1219d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1220d35567fcSMathieu Othacehe { 1221d35567fcSMathieu Othacehe .type = IIO_LIGHT, 12228fe78d52SMathieu Othacehe .scan_index = -1, 1223d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1224d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1225d35567fcSMathieu Othacehe }, { 1226d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 12278fe78d52SMathieu Othacehe .scan_index = 0, 1228f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1229f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1230f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1231d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1232d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1233d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 12348fe78d52SMathieu Othacehe .scan_type = { 12358fe78d52SMathieu Othacehe .sign = 'u', 12368fe78d52SMathieu Othacehe .realbits = 16, 12378fe78d52SMathieu Othacehe .storagebits = 16, 12388fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1239d35567fcSMathieu Othacehe }, 12408fe78d52SMathieu Othacehe }, 12418fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1242d35567fcSMathieu Othacehe }; 1243d35567fcSMathieu Othacehe 124485e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 124585e2c6a2SMårten Lindahl { 124685e2c6a2SMårten Lindahl .type = IIO_LIGHT, 124785e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 124885e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_SCALE), 124985e2c6a2SMårten Lindahl }, { 125085e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 125185e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 125285e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 125385e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 125485e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 125554667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 125654667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 125785e2c6a2SMårten Lindahl } 125885e2c6a2SMårten Lindahl }; 125985e2c6a2SMårten Lindahl 126062a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 126162a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 126262a1efb9SPeter Meerwald }; 126362a1efb9SPeter Meerwald 1264d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1265d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1266f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1267f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1268d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1269d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1270d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1271d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1272d35567fcSMathieu Othacehe }; 1273d35567fcSMathieu Othacehe 127485e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 127585e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 127685e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 127754667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 127854667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 127954667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 128054667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 128185e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 128285e2c6a2SMårten Lindahl }; 128385e2c6a2SMårten Lindahl 1284d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1285d35567fcSMathieu Othacehe [VCNL4000] = { 1286d35567fcSMathieu Othacehe .prod = "VCNL4000", 1287d35567fcSMathieu Othacehe .init = vcnl4000_init, 1288d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1289d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1290d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1291d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1292d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1293d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1294d35567fcSMathieu Othacehe }, 1295d35567fcSMathieu Othacehe [VCNL4010] = { 1296d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1297d35567fcSMathieu Othacehe .init = vcnl4000_init, 1298d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1299d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1300d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1301d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1302d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1303d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1304bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1305bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1306bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1307d35567fcSMathieu Othacehe }, 1308d35567fcSMathieu Othacehe [VCNL4040] = { 1309d35567fcSMathieu Othacehe .prod = "VCNL4040", 1310d35567fcSMathieu Othacehe .init = vcnl4200_init, 1311d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1312d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1313d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 131485e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 131585e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 131685e2c6a2SMårten Lindahl .info = &vcnl4040_info, 131754667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1318d35567fcSMathieu Othacehe }, 1319d35567fcSMathieu Othacehe [VCNL4200] = { 1320d35567fcSMathieu Othacehe .prod = "VCNL4200", 1321d35567fcSMathieu Othacehe .init = vcnl4200_init, 1322d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1323d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1324d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1325d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1326d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1327d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1328d35567fcSMathieu Othacehe }, 1329d35567fcSMathieu Othacehe }; 1330d35567fcSMathieu Othacehe 13318fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 13328fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 13338fe78d52SMathieu Othacehe }; 13348fe78d52SMathieu Othacehe 13358fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 13368fe78d52SMathieu Othacehe { 13378fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 13388fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 13398fe78d52SMathieu Othacehe struct iio_trigger *trigger; 13408fe78d52SMathieu Othacehe 13418fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 134215ea2878SJonathan Cameron indio_dev->name, 134315ea2878SJonathan Cameron iio_device_id(indio_dev)); 13448fe78d52SMathieu Othacehe if (!trigger) 13458fe78d52SMathieu Othacehe return -ENOMEM; 13468fe78d52SMathieu Othacehe 13478fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 13488fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 13498fe78d52SMathieu Othacehe 13508fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 13518fe78d52SMathieu Othacehe } 13528fe78d52SMathieu Othacehe 1353e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 135462a1efb9SPeter Meerwald { 1355e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 135662a1efb9SPeter Meerwald struct vcnl4000_data *data; 135762a1efb9SPeter Meerwald struct iio_dev *indio_dev; 13581ebc787aSTomas Novotny int ret; 135962a1efb9SPeter Meerwald 13602669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 136162a1efb9SPeter Meerwald if (!indio_dev) 136262a1efb9SPeter Meerwald return -ENOMEM; 136362a1efb9SPeter Meerwald 136462a1efb9SPeter Meerwald data = iio_priv(indio_dev); 136562a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 136662a1efb9SPeter Meerwald data->client = client; 13671ebc787aSTomas Novotny data->id = id->driver_data; 13681ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 136962a1efb9SPeter Meerwald 13701ebc787aSTomas Novotny ret = data->chip_spec->init(data); 137162a1efb9SPeter Meerwald if (ret < 0) 13722669d723SPeter Meerwald return ret; 137362a1efb9SPeter Meerwald 1374d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 13751ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 137662a1efb9SPeter Meerwald 1377f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1378f5a98e1fSGuido Günther &data->near_level)) 1379f5a98e1fSGuido Günther data->near_level = 0; 1380f5a98e1fSGuido Günther 1381d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1382d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1383d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 138462a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 138562a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 138662a1efb9SPeter Meerwald 1387bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1388bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 13898fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 13908fe78d52SMathieu Othacehe NULL, 1391bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1392bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 13938fe78d52SMathieu Othacehe if (ret < 0) { 13948fe78d52SMathieu Othacehe dev_err(&client->dev, 13958fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 13968fe78d52SMathieu Othacehe return ret; 13978fe78d52SMathieu Othacehe } 1398bfb6cfeeSMårten Lindahl } 13998fe78d52SMathieu Othacehe 1400bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1401d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1402bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1403d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1404d35567fcSMathieu Othacehe IRQF_ONESHOT, 1405bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1406d35567fcSMathieu Othacehe indio_dev); 1407d35567fcSMathieu Othacehe if (ret < 0) { 1408d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1409d35567fcSMathieu Othacehe return ret; 1410d35567fcSMathieu Othacehe } 14118fe78d52SMathieu Othacehe 14128fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 14138fe78d52SMathieu Othacehe if (ret < 0) 14148fe78d52SMathieu Othacehe return ret; 1415d35567fcSMathieu Othacehe } 1416d35567fcSMathieu Othacehe 14175e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 14185e00708dSGuido Günther if (ret < 0) 14195e00708dSGuido Günther goto fail_poweroff; 14205e00708dSGuido Günther 14215e00708dSGuido Günther ret = iio_device_register(indio_dev); 14225e00708dSGuido Günther if (ret < 0) 14235e00708dSGuido Günther goto fail_poweroff; 14245e00708dSGuido Günther 14255e00708dSGuido Günther pm_runtime_enable(&client->dev); 14265e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 14275e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 14285e00708dSGuido Günther 14295e00708dSGuido Günther return 0; 14305e00708dSGuido Günther fail_poweroff: 14315e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 14325e00708dSGuido Günther return ret; 143362a1efb9SPeter Meerwald } 143462a1efb9SPeter Meerwald 1435ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1436ebd457d5SAngus Ainslie (Purism) { 1437ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 14381436a78cSMarco Felsch .data = (void *)VCNL4000, 1439ebd457d5SAngus Ainslie (Purism) }, 1440ebd457d5SAngus Ainslie (Purism) { 1441ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 14421436a78cSMarco Felsch .data = (void *)VCNL4010, 1443ebd457d5SAngus Ainslie (Purism) }, 1444ebd457d5SAngus Ainslie (Purism) { 14451436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 14461436a78cSMarco Felsch .data = (void *)VCNL4010, 1447ebd457d5SAngus Ainslie (Purism) }, 1448ebd457d5SAngus Ainslie (Purism) { 14497fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 14507fd1c260SMarco Felsch .data = (void *)VCNL4040, 14517fd1c260SMarco Felsch }, 14527fd1c260SMarco Felsch { 1453ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 14541436a78cSMarco Felsch .data = (void *)VCNL4200, 1455ebd457d5SAngus Ainslie (Purism) }, 1456ebd457d5SAngus Ainslie (Purism) {}, 1457ebd457d5SAngus Ainslie (Purism) }; 1458ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1459ebd457d5SAngus Ainslie (Purism) 1460ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 14615e00708dSGuido Günther { 14625e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 14635e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1464ab91da2fSUwe Kleine-König int ret; 14655e00708dSGuido Günther 14665e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 14675e00708dSGuido Günther pm_runtime_disable(&client->dev); 14685e00708dSGuido Günther iio_device_unregister(indio_dev); 14695e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 14705e00708dSGuido Günther 1471ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1472ab91da2fSUwe Kleine-König if (ret) 1473ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1474ab91da2fSUwe Kleine-König ERR_PTR(ret)); 14755e00708dSGuido Günther } 14765e00708dSGuido Günther 1477cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 14785e00708dSGuido Günther { 14795e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 14805e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 14815e00708dSGuido Günther 14825e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 14835e00708dSGuido Günther } 14845e00708dSGuido Günther 1485cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 14865e00708dSGuido Günther { 14875e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 14885e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 14895e00708dSGuido Günther 14905e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 14915e00708dSGuido Günther } 14925e00708dSGuido Günther 1493cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1494cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 14955e00708dSGuido Günther 149662a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 149762a1efb9SPeter Meerwald .driver = { 149862a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1499cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1500ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 150162a1efb9SPeter Meerwald }, 1502e61295e0SUwe Kleine-König .probe_new = vcnl4000_probe, 150362a1efb9SPeter Meerwald .id_table = vcnl4000_id, 15045e00708dSGuido Günther .remove = vcnl4000_remove, 150562a1efb9SPeter Meerwald }; 150662a1efb9SPeter Meerwald 150762a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 150862a1efb9SPeter Meerwald 150962a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 15108fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 151162a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 151262a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1513