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 */ 68*854965b7SAstrid Rost #define VCNL4200_INT_FLAGS 0x0d /* Interrupt register */ 69be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 70be38866fSTomas Novotny 715a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 725a441aadSAngus Ainslie (Purism) 7362a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 74ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 75ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 76ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 77ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 78d35567fcSMathieu Othacehe #define VCNL4000_ALS_EN BIT(2) /* start ALS measurement */ 79d35567fcSMathieu Othacehe #define VCNL4000_PROX_EN BIT(1) /* start proximity measurement */ 80d35567fcSMathieu Othacehe #define VCNL4000_SELF_TIMED_EN BIT(0) /* start self-timed measurement */ 81d35567fcSMathieu Othacehe 82e21b5b1fSMårten Lindahl #define VCNL4040_ALS_CONF_ALS_SHUTDOWN BIT(0) 83e21b5b1fSMårten Lindahl #define VCNL4040_PS_CONF1_PS_SHUTDOWN BIT(0) 8485e2c6a2SMårten Lindahl #define VCNL4040_PS_CONF2_PS_IT GENMASK(3, 1) /* Proximity integration time */ 8554667612SMårten Lindahl #define VCNL4040_PS_CONF2_PS_INT GENMASK(9, 8) /* Proximity interrupt mode */ 8654667612SMårten Lindahl #define VCNL4040_PS_IF_AWAY BIT(8) /* Proximity event cross low threshold */ 8754667612SMårten Lindahl #define VCNL4040_PS_IF_CLOSE BIT(9) /* Proximity event cross high threshold */ 88e21b5b1fSMårten Lindahl 89d35567fcSMathieu Othacehe /* Bit masks for interrupt registers. */ 90d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_SEL BIT(0) /* Select threshold interrupt source */ 91d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_EN BIT(1) /* Threshold interrupt type */ 92d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS_EN BIT(2) /* Enable on ALS data ready */ 93d35567fcSMathieu Othacehe #define VCNL4010_INT_PROX_EN BIT(3) /* Enable on proximity data ready */ 94d35567fcSMathieu Othacehe 95d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_HIGH 0 /* High threshold exceeded */ 96d35567fcSMathieu Othacehe #define VCNL4010_INT_THR_LOW 1 /* Low threshold exceeded */ 97d35567fcSMathieu Othacehe #define VCNL4010_INT_ALS 2 /* ALS data ready */ 98d35567fcSMathieu Othacehe #define VCNL4010_INT_PROXIMITY 3 /* Proximity data ready */ 99d35567fcSMathieu Othacehe 100d35567fcSMathieu Othacehe #define VCNL4010_INT_THR \ 101d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_THR_LOW) | BIT(VCNL4010_INT_THR_HIGH)) 102d35567fcSMathieu Othacehe #define VCNL4010_INT_DRDY \ 103d35567fcSMathieu Othacehe (BIT(VCNL4010_INT_PROXIMITY) | BIT(VCNL4010_INT_ALS)) 104d35567fcSMathieu Othacehe 105f6889c1bSMathieu Othacehe static const int vcnl4010_prox_sampling_frequency[][2] = { 106f6889c1bSMathieu Othacehe {1, 950000}, 107f6889c1bSMathieu Othacehe {3, 906250}, 108f6889c1bSMathieu Othacehe {7, 812500}, 109f6889c1bSMathieu Othacehe {16, 625000}, 110f6889c1bSMathieu Othacehe {31, 250000}, 111f6889c1bSMathieu Othacehe {62, 500000}, 112f6889c1bSMathieu Othacehe {125, 0}, 113f6889c1bSMathieu Othacehe {250, 0}, 114f6889c1bSMathieu Othacehe }; 11562a1efb9SPeter Meerwald 11685e2c6a2SMårten Lindahl static const int vcnl4040_ps_it_times[][2] = { 11785e2c6a2SMårten Lindahl {0, 100}, 11885e2c6a2SMårten Lindahl {0, 150}, 11985e2c6a2SMårten Lindahl {0, 200}, 12085e2c6a2SMårten Lindahl {0, 250}, 12185e2c6a2SMårten Lindahl {0, 300}, 12285e2c6a2SMårten Lindahl {0, 350}, 12385e2c6a2SMårten Lindahl {0, 400}, 12485e2c6a2SMårten Lindahl {0, 800}, 12585e2c6a2SMårten Lindahl }; 12685e2c6a2SMårten Lindahl 1275e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 1285e00708dSGuido Günther 1291ebc787aSTomas Novotny enum vcnl4000_device_ids { 1301ebc787aSTomas Novotny VCNL4000, 13150c50b97STomas Novotny VCNL4010, 1325a441aadSAngus Ainslie (Purism) VCNL4040, 133be38866fSTomas Novotny VCNL4200, 134be38866fSTomas Novotny }; 135be38866fSTomas Novotny 136be38866fSTomas Novotny struct vcnl4200_channel { 137be38866fSTomas Novotny u8 reg; 138be38866fSTomas Novotny ktime_t last_measurement; 139be38866fSTomas Novotny ktime_t sampling_rate; 140be38866fSTomas Novotny struct mutex lock; 1411ebc787aSTomas Novotny }; 1421ebc787aSTomas Novotny 14362a1efb9SPeter Meerwald struct vcnl4000_data { 14462a1efb9SPeter Meerwald struct i2c_client *client; 1451ebc787aSTomas Novotny enum vcnl4000_device_ids id; 1461ebc787aSTomas Novotny int rev; 1471ebc787aSTomas Novotny int al_scale; 14854667612SMårten Lindahl u8 ps_int; /* proximity interrupt mode */ 1491ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 150be38866fSTomas Novotny struct mutex vcnl4000_lock; 151be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 152be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 153f5a98e1fSGuido Günther uint32_t near_level; 15462a1efb9SPeter Meerwald }; 15562a1efb9SPeter Meerwald 1561ebc787aSTomas Novotny struct vcnl4000_chip_spec { 1571ebc787aSTomas Novotny const char *prod; 158d35567fcSMathieu Othacehe struct iio_chan_spec const *channels; 159d35567fcSMathieu Othacehe const int num_channels; 160d35567fcSMathieu Othacehe const struct iio_info *info; 161bfb6cfeeSMårten Lindahl const struct iio_buffer_setup_ops *buffer_setup_ops; 1621ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 1631ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 1641ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 1655e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 166bfb6cfeeSMårten Lindahl irqreturn_t (*irq_thread)(int irq, void *priv); 167bfb6cfeeSMårten Lindahl irqreturn_t (*trig_buffer_func)(int irq, void *priv); 168*854965b7SAstrid Rost 169*854965b7SAstrid Rost u8 int_reg; 1701ebc787aSTomas Novotny }; 1711ebc787aSTomas Novotny 17262a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 1731ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 17450c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 17550c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1765a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 177be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 17862a1efb9SPeter Meerwald { } 17962a1efb9SPeter Meerwald }; 18062a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 18162a1efb9SPeter Meerwald 1825e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 1835e00708dSGuido Günther { 1845e00708dSGuido Günther /* no suspend op */ 1855e00708dSGuido Günther return 0; 1865e00708dSGuido Günther } 1875e00708dSGuido Günther 1881ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1891ebc787aSTomas Novotny { 1901ebc787aSTomas Novotny int ret, prod_id; 1911ebc787aSTomas Novotny 1921ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1931ebc787aSTomas Novotny if (ret < 0) 1941ebc787aSTomas Novotny return ret; 1951ebc787aSTomas Novotny 1961ebc787aSTomas Novotny prod_id = ret >> 4; 19758bf9aceSTomas Novotny switch (prod_id) { 19858bf9aceSTomas Novotny case VCNL4000_PROD_ID: 19958bf9aceSTomas Novotny if (data->id != VCNL4000) 20058bf9aceSTomas Novotny dev_warn(&data->client->dev, 20158bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 20258bf9aceSTomas Novotny break; 20358bf9aceSTomas Novotny case VCNL4010_PROD_ID: 20458bf9aceSTomas Novotny if (data->id != VCNL4010) 20558bf9aceSTomas Novotny dev_warn(&data->client->dev, 20658bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 20758bf9aceSTomas Novotny break; 20858bf9aceSTomas Novotny default: 2091ebc787aSTomas Novotny return -ENODEV; 21058bf9aceSTomas Novotny } 2111ebc787aSTomas Novotny 2121ebc787aSTomas Novotny data->rev = ret & 0xf; 2131ebc787aSTomas Novotny data->al_scale = 250000; 214be38866fSTomas Novotny 2155e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 216be38866fSTomas Novotny }; 217be38866fSTomas Novotny 218e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_als_enable(struct vcnl4000_data *data, bool en) 2195e00708dSGuido Günther { 2205e00708dSGuido Günther int ret; 2215e00708dSGuido Günther 222e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 223e21b5b1fSMårten Lindahl 224e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_AL_CONF); 225e21b5b1fSMårten Lindahl if (ret < 0) 226e21b5b1fSMårten Lindahl goto out; 227e21b5b1fSMårten Lindahl 228e21b5b1fSMårten Lindahl if (en) 229e21b5b1fSMårten Lindahl ret &= ~VCNL4040_ALS_CONF_ALS_SHUTDOWN; 230e21b5b1fSMårten Lindahl else 231e21b5b1fSMårten Lindahl ret |= VCNL4040_ALS_CONF_ALS_SHUTDOWN; 232e21b5b1fSMårten Lindahl 233e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, ret); 234e21b5b1fSMårten Lindahl 235e21b5b1fSMårten Lindahl out: 236e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 237e21b5b1fSMårten Lindahl 238e21b5b1fSMårten Lindahl return ret; 239e21b5b1fSMårten Lindahl } 240e21b5b1fSMårten Lindahl 241e21b5b1fSMårten Lindahl static ssize_t vcnl4000_write_ps_enable(struct vcnl4000_data *data, bool en) 242e21b5b1fSMårten Lindahl { 243e21b5b1fSMårten Lindahl int ret; 244e21b5b1fSMårten Lindahl 245e21b5b1fSMårten Lindahl mutex_lock(&data->vcnl4000_lock); 246e21b5b1fSMårten Lindahl 247e21b5b1fSMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 248e21b5b1fSMårten Lindahl if (ret < 0) 249e21b5b1fSMårten Lindahl goto out; 250e21b5b1fSMårten Lindahl 251e21b5b1fSMårten Lindahl if (en) 252e21b5b1fSMårten Lindahl ret &= ~VCNL4040_PS_CONF1_PS_SHUTDOWN; 253e21b5b1fSMårten Lindahl else 254e21b5b1fSMårten Lindahl ret |= VCNL4040_PS_CONF1_PS_SHUTDOWN; 255e21b5b1fSMårten Lindahl 256e21b5b1fSMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, ret); 257e21b5b1fSMårten Lindahl 258e21b5b1fSMårten Lindahl out: 259e21b5b1fSMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 260e21b5b1fSMårten Lindahl 261e21b5b1fSMårten Lindahl return ret; 262e21b5b1fSMårten Lindahl } 263e21b5b1fSMårten Lindahl 264e21b5b1fSMårten Lindahl static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 265e21b5b1fSMårten Lindahl { 266e21b5b1fSMårten Lindahl int ret; 267e21b5b1fSMårten Lindahl 26854667612SMårten Lindahl /* Do not power down if interrupts are enabled */ 26954667612SMårten Lindahl if (!on && data->ps_int) 27054667612SMårten Lindahl return 0; 27154667612SMårten Lindahl 272e21b5b1fSMårten Lindahl ret = vcnl4000_write_als_enable(data, on); 2735e00708dSGuido Günther if (ret < 0) 2745e00708dSGuido Günther return ret; 2755e00708dSGuido Günther 276e21b5b1fSMårten Lindahl ret = vcnl4000_write_ps_enable(data, on); 2775e00708dSGuido Günther if (ret < 0) 2785e00708dSGuido Günther return ret; 2795e00708dSGuido Günther 2805e00708dSGuido Günther if (on) { 2815e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 2825e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 2835e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 2845e00708dSGuido Günther } 2855e00708dSGuido Günther 2865e00708dSGuido Günther return 0; 2875e00708dSGuido Günther } 2885e00708dSGuido Günther 289be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 290be38866fSTomas Novotny { 2915a441aadSAngus Ainslie (Purism) int ret, id; 292be38866fSTomas Novotny 293be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 294be38866fSTomas Novotny if (ret < 0) 295be38866fSTomas Novotny return ret; 296be38866fSTomas Novotny 2975a441aadSAngus Ainslie (Purism) id = ret & 0xff; 2985a441aadSAngus Ainslie (Purism) 2995a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 3005a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 3015a441aadSAngus Ainslie (Purism) if (ret < 0) 3025a441aadSAngus Ainslie (Purism) return ret; 3035a441aadSAngus Ainslie (Purism) 3045a441aadSAngus Ainslie (Purism) id = ret & 0xff; 3055a441aadSAngus Ainslie (Purism) 3065a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 307be38866fSTomas Novotny return -ENODEV; 3085a441aadSAngus Ainslie (Purism) } 3095a441aadSAngus Ainslie (Purism) 3105a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 311be38866fSTomas Novotny 312be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 31354667612SMårten Lindahl data->ps_int = 0; 314be38866fSTomas Novotny 315be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 316be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 3175a441aadSAngus Ainslie (Purism) switch (id) { 3185a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 319b42aa97eSTomas Novotny /* Default wait time is 50ms, add 20% tolerance. */ 320b42aa97eSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000); 321b42aa97eSTomas Novotny /* Default wait time is 4.8ms, add 20% tolerance. */ 322b42aa97eSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000); 323bc80573eSGuido Günther data->al_scale = 24000; 3245a441aadSAngus Ainslie (Purism) break; 3255a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 3262ca5a879STomas Novotny /* Default wait time is 80ms, add 20% tolerance. */ 3272ca5a879STomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 96000 * 1000); 3282ca5a879STomas Novotny /* Default wait time is 5ms, add 20% tolerance. */ 3292ca5a879STomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 6000 * 1000); 330bc80573eSGuido Günther data->al_scale = 120000; 3315a441aadSAngus Ainslie (Purism) break; 3325a441aadSAngus Ainslie (Purism) } 333be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 334be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 3351ebc787aSTomas Novotny 3365e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 3375e00708dSGuido Günther if (ret < 0) 3385e00708dSGuido Günther return ret; 3395e00708dSGuido Günther 3401ebc787aSTomas Novotny return 0; 3411ebc787aSTomas Novotny }; 3421ebc787aSTomas Novotny 343816956c3SMathieu Othacehe static int vcnl4000_read_data(struct vcnl4000_data *data, u8 data_reg, int *val) 344816956c3SMathieu Othacehe { 345816956c3SMathieu Othacehe s32 ret; 346816956c3SMathieu Othacehe 347816956c3SMathieu Othacehe ret = i2c_smbus_read_word_swapped(data->client, data_reg); 348816956c3SMathieu Othacehe if (ret < 0) 349816956c3SMathieu Othacehe return ret; 350816956c3SMathieu Othacehe 351816956c3SMathieu Othacehe *val = ret; 352816956c3SMathieu Othacehe return 0; 353816956c3SMathieu Othacehe } 354816956c3SMathieu Othacehe 355816956c3SMathieu Othacehe static int vcnl4000_write_data(struct vcnl4000_data *data, u8 data_reg, int val) 356816956c3SMathieu Othacehe { 357816956c3SMathieu Othacehe if (val > U16_MAX) 358816956c3SMathieu Othacehe return -ERANGE; 359816956c3SMathieu Othacehe 360816956c3SMathieu Othacehe return i2c_smbus_write_word_swapped(data->client, data_reg, val); 361816956c3SMathieu Othacehe } 362816956c3SMathieu Othacehe 363816956c3SMathieu Othacehe 36462a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 36562a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 36662a1efb9SPeter Meerwald { 36762a1efb9SPeter Meerwald int tries = 20; 36862a1efb9SPeter Meerwald int ret; 36962a1efb9SPeter Meerwald 370be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 371ff34ed6dSPeter Meerwald-Stadler 37262a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 37362a1efb9SPeter Meerwald req_mask); 37462a1efb9SPeter Meerwald if (ret < 0) 375ff34ed6dSPeter Meerwald-Stadler goto fail; 37662a1efb9SPeter Meerwald 37762a1efb9SPeter Meerwald /* wait for data to become ready */ 37862a1efb9SPeter Meerwald while (tries--) { 37962a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 38062a1efb9SPeter Meerwald if (ret < 0) 381ff34ed6dSPeter Meerwald-Stadler goto fail; 38262a1efb9SPeter Meerwald if (ret & rdy_mask) 38362a1efb9SPeter Meerwald break; 38462a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 38562a1efb9SPeter Meerwald } 38662a1efb9SPeter Meerwald 38762a1efb9SPeter Meerwald if (tries < 0) { 38862a1efb9SPeter Meerwald dev_err(&data->client->dev, 38962a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 390ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 391ff34ed6dSPeter Meerwald-Stadler goto fail; 39262a1efb9SPeter Meerwald } 39362a1efb9SPeter Meerwald 394816956c3SMathieu Othacehe ret = vcnl4000_read_data(data, data_reg, val); 39562a1efb9SPeter Meerwald if (ret < 0) 396ff34ed6dSPeter Meerwald-Stadler goto fail; 39762a1efb9SPeter Meerwald 398be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 39962a1efb9SPeter Meerwald 40062a1efb9SPeter Meerwald return 0; 401ff34ed6dSPeter Meerwald-Stadler 402ff34ed6dSPeter Meerwald-Stadler fail: 403be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 404ff34ed6dSPeter Meerwald-Stadler return ret; 40562a1efb9SPeter Meerwald } 40662a1efb9SPeter Meerwald 407be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 408be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 409be38866fSTomas Novotny { 410be38866fSTomas Novotny int ret; 411be38866fSTomas Novotny s64 delta; 412be38866fSTomas Novotny ktime_t next_measurement; 413be38866fSTomas Novotny 414be38866fSTomas Novotny mutex_lock(&chan->lock); 415be38866fSTomas Novotny 416be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 417be38866fSTomas Novotny chan->sampling_rate); 418be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 419be38866fSTomas Novotny if (delta > 0) 420be38866fSTomas Novotny usleep_range(delta, delta + 500); 421be38866fSTomas Novotny chan->last_measurement = ktime_get(); 422be38866fSTomas Novotny 423be38866fSTomas Novotny mutex_unlock(&chan->lock); 424be38866fSTomas Novotny 425be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 426be38866fSTomas Novotny if (ret < 0) 427be38866fSTomas Novotny return ret; 428be38866fSTomas Novotny 429be38866fSTomas Novotny *val = ret; 430be38866fSTomas Novotny 431be38866fSTomas Novotny return 0; 432be38866fSTomas Novotny } 433be38866fSTomas Novotny 4341ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 4351ebc787aSTomas Novotny { 4361ebc787aSTomas Novotny return vcnl4000_measure(data, 4371ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 4381ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 4391ebc787aSTomas Novotny } 4401ebc787aSTomas Novotny 441be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 442be38866fSTomas Novotny { 443be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 444be38866fSTomas Novotny } 445be38866fSTomas Novotny 4461ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 4471ebc787aSTomas Novotny { 4481ebc787aSTomas Novotny return vcnl4000_measure(data, 4491ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 4501ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 4511ebc787aSTomas Novotny } 4521ebc787aSTomas Novotny 453be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 454be38866fSTomas Novotny { 455be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 456be38866fSTomas Novotny } 457be38866fSTomas Novotny 458f6889c1bSMathieu Othacehe static int vcnl4010_read_proxy_samp_freq(struct vcnl4000_data *data, int *val, 459f6889c1bSMathieu Othacehe int *val2) 460f6889c1bSMathieu Othacehe { 461f6889c1bSMathieu Othacehe int ret; 462f6889c1bSMathieu Othacehe 463f6889c1bSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_PROX_RATE); 464f6889c1bSMathieu Othacehe if (ret < 0) 465f6889c1bSMathieu Othacehe return ret; 466f6889c1bSMathieu Othacehe 467f6889c1bSMathieu Othacehe if (ret >= ARRAY_SIZE(vcnl4010_prox_sampling_frequency)) 468f6889c1bSMathieu Othacehe return -EINVAL; 469f6889c1bSMathieu Othacehe 470f6889c1bSMathieu Othacehe *val = vcnl4010_prox_sampling_frequency[ret][0]; 471f6889c1bSMathieu Othacehe *val2 = vcnl4010_prox_sampling_frequency[ret][1]; 472f6889c1bSMathieu Othacehe 473f6889c1bSMathieu Othacehe return 0; 474f6889c1bSMathieu Othacehe } 475f6889c1bSMathieu Othacehe 476d35567fcSMathieu Othacehe static bool vcnl4010_is_in_periodic_mode(struct vcnl4000_data *data) 477f5a98e1fSGuido Günther { 478d35567fcSMathieu Othacehe int ret; 479f5a98e1fSGuido Günther 480d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 481d35567fcSMathieu Othacehe if (ret < 0) 482d35567fcSMathieu Othacehe return false; 483d35567fcSMathieu Othacehe 484d35567fcSMathieu Othacehe return !!(ret & VCNL4000_SELF_TIMED_EN); 485f5a98e1fSGuido Günther } 486f5a98e1fSGuido Günther 4875e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 4885e00708dSGuido Günther { 4895e00708dSGuido Günther struct device *dev = &data->client->dev; 4905e00708dSGuido Günther int ret; 4915e00708dSGuido Günther 4925e00708dSGuido Günther if (on) { 493db27fdb3SJonathan Cameron ret = pm_runtime_resume_and_get(dev); 4945e00708dSGuido Günther } else { 4955e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 4965e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 4975e00708dSGuido Günther } 4985e00708dSGuido Günther 4995e00708dSGuido Günther return ret; 5005e00708dSGuido Günther } 5015e00708dSGuido Günther 50285e2c6a2SMårten Lindahl static int vcnl4040_read_ps_it(struct vcnl4000_data *data, int *val, int *val2) 50385e2c6a2SMårten Lindahl { 50485e2c6a2SMårten Lindahl int ret; 50585e2c6a2SMårten Lindahl 50685e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 50785e2c6a2SMårten Lindahl if (ret < 0) 50885e2c6a2SMårten Lindahl return ret; 50985e2c6a2SMårten Lindahl 51085e2c6a2SMårten Lindahl ret = FIELD_GET(VCNL4040_PS_CONF2_PS_IT, ret); 51185e2c6a2SMårten Lindahl 51285e2c6a2SMårten Lindahl if (ret >= ARRAY_SIZE(vcnl4040_ps_it_times)) 51385e2c6a2SMårten Lindahl return -EINVAL; 51485e2c6a2SMårten Lindahl 51585e2c6a2SMårten Lindahl *val = vcnl4040_ps_it_times[ret][0]; 51685e2c6a2SMårten Lindahl *val2 = vcnl4040_ps_it_times[ret][1]; 51785e2c6a2SMårten Lindahl 51885e2c6a2SMårten Lindahl return 0; 51985e2c6a2SMårten Lindahl } 52085e2c6a2SMårten Lindahl 52185e2c6a2SMårten Lindahl static ssize_t vcnl4040_write_ps_it(struct vcnl4000_data *data, int val) 52285e2c6a2SMårten Lindahl { 52385e2c6a2SMårten Lindahl unsigned int i; 52485e2c6a2SMårten Lindahl int ret, index = -1; 52585e2c6a2SMårten Lindahl u16 regval; 52685e2c6a2SMårten Lindahl 52785e2c6a2SMårten Lindahl for (i = 0; i < ARRAY_SIZE(vcnl4040_ps_it_times); i++) { 52885e2c6a2SMårten Lindahl if (val == vcnl4040_ps_it_times[i][1]) { 52985e2c6a2SMårten Lindahl index = i; 53085e2c6a2SMårten Lindahl break; 53185e2c6a2SMårten Lindahl } 53285e2c6a2SMårten Lindahl } 53385e2c6a2SMårten Lindahl 53485e2c6a2SMårten Lindahl if (index < 0) 53585e2c6a2SMårten Lindahl return -EINVAL; 53685e2c6a2SMårten Lindahl 53785e2c6a2SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 53885e2c6a2SMårten Lindahl 53985e2c6a2SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 54085e2c6a2SMårten Lindahl if (ret < 0) 54185e2c6a2SMårten Lindahl goto out; 54285e2c6a2SMårten Lindahl 54385e2c6a2SMårten Lindahl regval = (ret & ~VCNL4040_PS_CONF2_PS_IT) | 54485e2c6a2SMårten Lindahl FIELD_PREP(VCNL4040_PS_CONF2_PS_IT, index); 54585e2c6a2SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 54685e2c6a2SMårten Lindahl regval); 54785e2c6a2SMårten Lindahl 54885e2c6a2SMårten Lindahl out: 54985e2c6a2SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 55085e2c6a2SMårten Lindahl return ret; 55185e2c6a2SMårten Lindahl } 55285e2c6a2SMårten Lindahl 55362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 55462a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 55562a1efb9SPeter Meerwald int *val, int *val2, long mask) 55662a1efb9SPeter Meerwald { 5575d693139SPeter Meerwald-Stadler int ret; 55862a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 55962a1efb9SPeter Meerwald 56062a1efb9SPeter Meerwald switch (mask) { 56162a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 5625e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 5635e00708dSGuido Günther if (ret < 0) 5645e00708dSGuido Günther return ret; 5655e00708dSGuido Günther 56662a1efb9SPeter Meerwald switch (chan->type) { 56762a1efb9SPeter Meerwald case IIO_LIGHT: 5681ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 5694a818643SGuido Günther if (!ret) 5704a818643SGuido Günther ret = IIO_VAL_INT; 5714a818643SGuido Günther break; 57262a1efb9SPeter Meerwald case IIO_PROXIMITY: 5731ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 5744a818643SGuido Günther if (!ret) 5754a818643SGuido Günther ret = IIO_VAL_INT; 5764a818643SGuido Günther break; 57762a1efb9SPeter Meerwald default: 5784a818643SGuido Günther ret = -EINVAL; 57962a1efb9SPeter Meerwald } 5805e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 5814a818643SGuido Günther return ret; 58262a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 5835d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 5845d693139SPeter Meerwald-Stadler return -EINVAL; 5855d693139SPeter Meerwald-Stadler 58662a1efb9SPeter Meerwald *val = 0; 5871ebc787aSTomas Novotny *val2 = data->al_scale; 5885d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 58985e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 59085e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 59185e2c6a2SMårten Lindahl return -EINVAL; 59285e2c6a2SMårten Lindahl ret = vcnl4040_read_ps_it(data, val, val2); 59385e2c6a2SMårten Lindahl if (ret < 0) 59485e2c6a2SMårten Lindahl return ret; 59585e2c6a2SMårten Lindahl return IIO_VAL_INT_PLUS_MICRO; 59685e2c6a2SMårten Lindahl default: 59785e2c6a2SMårten Lindahl return -EINVAL; 59885e2c6a2SMårten Lindahl } 59985e2c6a2SMårten Lindahl } 60085e2c6a2SMårten Lindahl 60185e2c6a2SMårten Lindahl static int vcnl4040_write_raw(struct iio_dev *indio_dev, 60285e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 60385e2c6a2SMårten Lindahl int val, int val2, long mask) 60485e2c6a2SMårten Lindahl { 60585e2c6a2SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 60685e2c6a2SMårten Lindahl 60785e2c6a2SMårten Lindahl switch (mask) { 60885e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 60985e2c6a2SMårten Lindahl if (val != 0) 61085e2c6a2SMårten Lindahl return -EINVAL; 61185e2c6a2SMårten Lindahl if (chan->type != IIO_PROXIMITY) 61285e2c6a2SMårten Lindahl return -EINVAL; 61385e2c6a2SMårten Lindahl return vcnl4040_write_ps_it(data, val2); 61485e2c6a2SMårten Lindahl default: 61585e2c6a2SMårten Lindahl return -EINVAL; 61685e2c6a2SMårten Lindahl } 61785e2c6a2SMårten Lindahl } 61885e2c6a2SMårten Lindahl 61985e2c6a2SMårten Lindahl static int vcnl4040_read_avail(struct iio_dev *indio_dev, 62085e2c6a2SMårten Lindahl struct iio_chan_spec const *chan, 62185e2c6a2SMårten Lindahl const int **vals, int *type, int *length, 62285e2c6a2SMårten Lindahl long mask) 62385e2c6a2SMårten Lindahl { 62485e2c6a2SMårten Lindahl switch (mask) { 62585e2c6a2SMårten Lindahl case IIO_CHAN_INFO_INT_TIME: 62685e2c6a2SMårten Lindahl *vals = (int *)vcnl4040_ps_it_times; 62785e2c6a2SMårten Lindahl *type = IIO_VAL_INT_PLUS_MICRO; 62885e2c6a2SMårten Lindahl *length = 2 * ARRAY_SIZE(vcnl4040_ps_it_times); 62985e2c6a2SMårten Lindahl return IIO_AVAIL_LIST; 63062a1efb9SPeter Meerwald default: 6315d693139SPeter Meerwald-Stadler return -EINVAL; 63262a1efb9SPeter Meerwald } 63362a1efb9SPeter Meerwald } 63462a1efb9SPeter Meerwald 635d35567fcSMathieu Othacehe static int vcnl4010_read_raw(struct iio_dev *indio_dev, 636d35567fcSMathieu Othacehe struct iio_chan_spec const *chan, 637d35567fcSMathieu Othacehe int *val, int *val2, long mask) 638d35567fcSMathieu Othacehe { 639d35567fcSMathieu Othacehe int ret; 640d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 641d35567fcSMathieu Othacehe 642d35567fcSMathieu Othacehe switch (mask) { 643d35567fcSMathieu Othacehe case IIO_CHAN_INFO_RAW: 644d35567fcSMathieu Othacehe case IIO_CHAN_INFO_SCALE: 645d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 646d35567fcSMathieu Othacehe if (ret) 647d35567fcSMathieu Othacehe return ret; 648d35567fcSMathieu Othacehe 649d35567fcSMathieu Othacehe /* Protect against event capture. */ 650d35567fcSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 651d35567fcSMathieu Othacehe ret = -EBUSY; 652d35567fcSMathieu Othacehe } else { 653d35567fcSMathieu Othacehe ret = vcnl4000_read_raw(indio_dev, chan, val, val2, 654d35567fcSMathieu Othacehe mask); 655d35567fcSMathieu Othacehe } 656d35567fcSMathieu Othacehe 657d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 658d35567fcSMathieu Othacehe return ret; 659f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 660f6889c1bSMathieu Othacehe switch (chan->type) { 661f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 662f6889c1bSMathieu Othacehe ret = vcnl4010_read_proxy_samp_freq(data, val, val2); 663f6889c1bSMathieu Othacehe if (ret < 0) 664f6889c1bSMathieu Othacehe return ret; 665f6889c1bSMathieu Othacehe return IIO_VAL_INT_PLUS_MICRO; 666d35567fcSMathieu Othacehe default: 667d35567fcSMathieu Othacehe return -EINVAL; 668d35567fcSMathieu Othacehe } 669f6889c1bSMathieu Othacehe default: 670f6889c1bSMathieu Othacehe return -EINVAL; 671f6889c1bSMathieu Othacehe } 672f6889c1bSMathieu Othacehe } 673f6889c1bSMathieu Othacehe 674f6889c1bSMathieu Othacehe static int vcnl4010_read_avail(struct iio_dev *indio_dev, 675f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 676f6889c1bSMathieu Othacehe const int **vals, int *type, int *length, 677f6889c1bSMathieu Othacehe long mask) 678f6889c1bSMathieu Othacehe { 679f6889c1bSMathieu Othacehe switch (mask) { 680f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 681f6889c1bSMathieu Othacehe *vals = (int *)vcnl4010_prox_sampling_frequency; 682f6889c1bSMathieu Othacehe *type = IIO_VAL_INT_PLUS_MICRO; 683f6889c1bSMathieu Othacehe *length = 2 * ARRAY_SIZE(vcnl4010_prox_sampling_frequency); 684f6889c1bSMathieu Othacehe return IIO_AVAIL_LIST; 685f6889c1bSMathieu Othacehe default: 686f6889c1bSMathieu Othacehe return -EINVAL; 687f6889c1bSMathieu Othacehe } 688f6889c1bSMathieu Othacehe } 689f6889c1bSMathieu Othacehe 690f6889c1bSMathieu Othacehe static int vcnl4010_write_proxy_samp_freq(struct vcnl4000_data *data, int val, 691f6889c1bSMathieu Othacehe int val2) 692f6889c1bSMathieu Othacehe { 693f6889c1bSMathieu Othacehe unsigned int i; 694f6889c1bSMathieu Othacehe int index = -1; 695f6889c1bSMathieu Othacehe 696f6889c1bSMathieu Othacehe for (i = 0; i < ARRAY_SIZE(vcnl4010_prox_sampling_frequency); i++) { 697f6889c1bSMathieu Othacehe if (val == vcnl4010_prox_sampling_frequency[i][0] && 698f6889c1bSMathieu Othacehe val2 == vcnl4010_prox_sampling_frequency[i][1]) { 699f6889c1bSMathieu Othacehe index = i; 700f6889c1bSMathieu Othacehe break; 701f6889c1bSMathieu Othacehe } 702f6889c1bSMathieu Othacehe } 703f6889c1bSMathieu Othacehe 704f6889c1bSMathieu Othacehe if (index < 0) 705f6889c1bSMathieu Othacehe return -EINVAL; 706f6889c1bSMathieu Othacehe 707f6889c1bSMathieu Othacehe return i2c_smbus_write_byte_data(data->client, VCNL4010_PROX_RATE, 708f6889c1bSMathieu Othacehe index); 709f6889c1bSMathieu Othacehe } 710f6889c1bSMathieu Othacehe 711f6889c1bSMathieu Othacehe static int vcnl4010_write_raw(struct iio_dev *indio_dev, 712f6889c1bSMathieu Othacehe struct iio_chan_spec const *chan, 713f6889c1bSMathieu Othacehe int val, int val2, long mask) 714f6889c1bSMathieu Othacehe { 715f6889c1bSMathieu Othacehe int ret; 716f6889c1bSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 717f6889c1bSMathieu Othacehe 718f6889c1bSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 719f6889c1bSMathieu Othacehe if (ret) 720f6889c1bSMathieu Othacehe return ret; 721f6889c1bSMathieu Othacehe 722f6889c1bSMathieu Othacehe /* Protect against event capture. */ 723f6889c1bSMathieu Othacehe if (vcnl4010_is_in_periodic_mode(data)) { 724f6889c1bSMathieu Othacehe ret = -EBUSY; 725f6889c1bSMathieu Othacehe goto end; 726f6889c1bSMathieu Othacehe } 727f6889c1bSMathieu Othacehe 728f6889c1bSMathieu Othacehe switch (mask) { 729f6889c1bSMathieu Othacehe case IIO_CHAN_INFO_SAMP_FREQ: 730f6889c1bSMathieu Othacehe switch (chan->type) { 731f6889c1bSMathieu Othacehe case IIO_PROXIMITY: 732f6889c1bSMathieu Othacehe ret = vcnl4010_write_proxy_samp_freq(data, val, val2); 733f6889c1bSMathieu Othacehe goto end; 734f6889c1bSMathieu Othacehe default: 735f6889c1bSMathieu Othacehe ret = -EINVAL; 736f6889c1bSMathieu Othacehe goto end; 737f6889c1bSMathieu Othacehe } 738f6889c1bSMathieu Othacehe default: 739f6889c1bSMathieu Othacehe ret = -EINVAL; 740f6889c1bSMathieu Othacehe goto end; 741f6889c1bSMathieu Othacehe } 742f6889c1bSMathieu Othacehe 743f6889c1bSMathieu Othacehe end: 744f6889c1bSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 745f6889c1bSMathieu Othacehe return ret; 746d35567fcSMathieu Othacehe } 747d35567fcSMathieu Othacehe 748d35567fcSMathieu Othacehe static int vcnl4010_read_event(struct iio_dev *indio_dev, 749d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 750d35567fcSMathieu Othacehe enum iio_event_type type, 751d35567fcSMathieu Othacehe enum iio_event_direction dir, 752d35567fcSMathieu Othacehe enum iio_event_info info, 753d35567fcSMathieu Othacehe int *val, int *val2) 754d35567fcSMathieu Othacehe { 755d35567fcSMathieu Othacehe int ret; 756d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 757d35567fcSMathieu Othacehe 758d35567fcSMathieu Othacehe switch (info) { 759d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 760d35567fcSMathieu Othacehe switch (dir) { 761d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 762d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_HIGH_THR_HI, 763d35567fcSMathieu Othacehe val); 764d35567fcSMathieu Othacehe if (ret < 0) 765d35567fcSMathieu Othacehe return ret; 766d35567fcSMathieu Othacehe return IIO_VAL_INT; 767d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 768d35567fcSMathieu Othacehe ret = vcnl4000_read_data(data, VCNL4010_LOW_THR_HI, 769d35567fcSMathieu Othacehe val); 770d35567fcSMathieu Othacehe if (ret < 0) 771d35567fcSMathieu Othacehe return ret; 772d35567fcSMathieu Othacehe return IIO_VAL_INT; 773d35567fcSMathieu Othacehe default: 774d35567fcSMathieu Othacehe return -EINVAL; 775d35567fcSMathieu Othacehe } 776d35567fcSMathieu Othacehe default: 777d35567fcSMathieu Othacehe return -EINVAL; 778d35567fcSMathieu Othacehe } 779d35567fcSMathieu Othacehe } 780d35567fcSMathieu Othacehe 781d35567fcSMathieu Othacehe static int vcnl4010_write_event(struct iio_dev *indio_dev, 782d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 783d35567fcSMathieu Othacehe enum iio_event_type type, 784d35567fcSMathieu Othacehe enum iio_event_direction dir, 785d35567fcSMathieu Othacehe enum iio_event_info info, 786d35567fcSMathieu Othacehe int val, int val2) 787d35567fcSMathieu Othacehe { 788d35567fcSMathieu Othacehe int ret; 789d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 790d35567fcSMathieu Othacehe 791d35567fcSMathieu Othacehe switch (info) { 792d35567fcSMathieu Othacehe case IIO_EV_INFO_VALUE: 793d35567fcSMathieu Othacehe switch (dir) { 794d35567fcSMathieu Othacehe case IIO_EV_DIR_RISING: 795d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_HIGH_THR_HI, 796d35567fcSMathieu Othacehe val); 797d35567fcSMathieu Othacehe if (ret < 0) 798d35567fcSMathieu Othacehe return ret; 799d35567fcSMathieu Othacehe return IIO_VAL_INT; 800d35567fcSMathieu Othacehe case IIO_EV_DIR_FALLING: 801d35567fcSMathieu Othacehe ret = vcnl4000_write_data(data, VCNL4010_LOW_THR_HI, 802d35567fcSMathieu Othacehe val); 803d35567fcSMathieu Othacehe if (ret < 0) 804d35567fcSMathieu Othacehe return ret; 805d35567fcSMathieu Othacehe return IIO_VAL_INT; 806d35567fcSMathieu Othacehe default: 807d35567fcSMathieu Othacehe return -EINVAL; 808d35567fcSMathieu Othacehe } 809d35567fcSMathieu Othacehe default: 810d35567fcSMathieu Othacehe return -EINVAL; 811d35567fcSMathieu Othacehe } 812d35567fcSMathieu Othacehe } 813d35567fcSMathieu Othacehe 81454667612SMårten Lindahl static int vcnl4040_read_event(struct iio_dev *indio_dev, 81554667612SMårten Lindahl const struct iio_chan_spec *chan, 81654667612SMårten Lindahl enum iio_event_type type, 81754667612SMårten Lindahl enum iio_event_direction dir, 81854667612SMårten Lindahl enum iio_event_info info, 81954667612SMårten Lindahl int *val, int *val2) 82054667612SMårten Lindahl { 82154667612SMårten Lindahl int ret; 82254667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 82354667612SMårten Lindahl 82454667612SMårten Lindahl switch (dir) { 82554667612SMårten Lindahl case IIO_EV_DIR_RISING: 82654667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 82754667612SMårten Lindahl VCNL4040_PS_THDH_LM); 82854667612SMårten Lindahl if (ret < 0) 82954667612SMårten Lindahl return ret; 83054667612SMårten Lindahl *val = ret; 83154667612SMårten Lindahl return IIO_VAL_INT; 83254667612SMårten Lindahl case IIO_EV_DIR_FALLING: 83354667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, 83454667612SMårten Lindahl VCNL4040_PS_THDL_LM); 83554667612SMårten Lindahl if (ret < 0) 83654667612SMårten Lindahl return ret; 83754667612SMårten Lindahl *val = ret; 83854667612SMårten Lindahl return IIO_VAL_INT; 83954667612SMårten Lindahl default: 84054667612SMårten Lindahl return -EINVAL; 84154667612SMårten Lindahl } 84254667612SMårten Lindahl } 84354667612SMårten Lindahl 84454667612SMårten Lindahl static int vcnl4040_write_event(struct iio_dev *indio_dev, 84554667612SMårten Lindahl const struct iio_chan_spec *chan, 84654667612SMårten Lindahl enum iio_event_type type, 84754667612SMårten Lindahl enum iio_event_direction dir, 84854667612SMårten Lindahl enum iio_event_info info, 84954667612SMårten Lindahl int val, int val2) 85054667612SMårten Lindahl { 85154667612SMårten Lindahl int ret; 85254667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 85354667612SMårten Lindahl 85454667612SMårten Lindahl switch (dir) { 85554667612SMårten Lindahl case IIO_EV_DIR_RISING: 85654667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 85754667612SMårten Lindahl VCNL4040_PS_THDH_LM, val); 85854667612SMårten Lindahl if (ret < 0) 85954667612SMårten Lindahl return ret; 86054667612SMårten Lindahl return IIO_VAL_INT; 86154667612SMårten Lindahl case IIO_EV_DIR_FALLING: 86254667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, 86354667612SMårten Lindahl VCNL4040_PS_THDL_LM, val); 86454667612SMårten Lindahl if (ret < 0) 86554667612SMårten Lindahl return ret; 86654667612SMårten Lindahl return IIO_VAL_INT; 86754667612SMårten Lindahl default: 86854667612SMårten Lindahl return -EINVAL; 86954667612SMårten Lindahl } 87054667612SMårten Lindahl } 87154667612SMårten Lindahl 872d35567fcSMathieu Othacehe static bool vcnl4010_is_thr_enabled(struct vcnl4000_data *data) 873d35567fcSMathieu Othacehe { 874d35567fcSMathieu Othacehe int ret; 875d35567fcSMathieu Othacehe 876d35567fcSMathieu Othacehe ret = i2c_smbus_read_byte_data(data->client, VCNL4010_INT_CTRL); 877d35567fcSMathieu Othacehe if (ret < 0) 878d35567fcSMathieu Othacehe return false; 879d35567fcSMathieu Othacehe 880d35567fcSMathieu Othacehe return !!(ret & VCNL4010_INT_THR_EN); 881d35567fcSMathieu Othacehe } 882d35567fcSMathieu Othacehe 883d35567fcSMathieu Othacehe static int vcnl4010_read_event_config(struct iio_dev *indio_dev, 884d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 885d35567fcSMathieu Othacehe enum iio_event_type type, 886d35567fcSMathieu Othacehe enum iio_event_direction dir) 887d35567fcSMathieu Othacehe { 888d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 889d35567fcSMathieu Othacehe 890d35567fcSMathieu Othacehe switch (chan->type) { 891d35567fcSMathieu Othacehe case IIO_PROXIMITY: 892d35567fcSMathieu Othacehe return vcnl4010_is_thr_enabled(data); 893d35567fcSMathieu Othacehe default: 894d35567fcSMathieu Othacehe return -EINVAL; 895d35567fcSMathieu Othacehe } 896d35567fcSMathieu Othacehe } 897d35567fcSMathieu Othacehe 898d35567fcSMathieu Othacehe static int vcnl4010_config_threshold(struct iio_dev *indio_dev, bool state) 899d35567fcSMathieu Othacehe { 900d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 901d35567fcSMathieu Othacehe int ret; 902d35567fcSMathieu Othacehe int icr; 903d35567fcSMathieu Othacehe int command; 904d35567fcSMathieu Othacehe 905d35567fcSMathieu Othacehe if (state) { 906d35567fcSMathieu Othacehe ret = iio_device_claim_direct_mode(indio_dev); 907d35567fcSMathieu Othacehe if (ret) 908d35567fcSMathieu Othacehe return ret; 909d35567fcSMathieu Othacehe 910d35567fcSMathieu Othacehe /* Enable periodic measurement of proximity data. */ 911d35567fcSMathieu Othacehe command = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 912d35567fcSMathieu Othacehe 913d35567fcSMathieu Othacehe /* 914d35567fcSMathieu Othacehe * Enable interrupts on threshold, for proximity data by 915d35567fcSMathieu Othacehe * default. 916d35567fcSMathieu Othacehe */ 917d35567fcSMathieu Othacehe icr = VCNL4010_INT_THR_EN; 918d35567fcSMathieu Othacehe } else { 919d35567fcSMathieu Othacehe if (!vcnl4010_is_thr_enabled(data)) 920d35567fcSMathieu Othacehe return 0; 921d35567fcSMathieu Othacehe 922d35567fcSMathieu Othacehe command = 0; 923d35567fcSMathieu Othacehe icr = 0; 924d35567fcSMathieu Othacehe } 925d35567fcSMathieu Othacehe 926d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 927d35567fcSMathieu Othacehe command); 928d35567fcSMathieu Othacehe if (ret < 0) 929d35567fcSMathieu Othacehe goto end; 930d35567fcSMathieu Othacehe 931d35567fcSMathieu Othacehe ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, icr); 932d35567fcSMathieu Othacehe 933d35567fcSMathieu Othacehe end: 934d35567fcSMathieu Othacehe if (state) 935d35567fcSMathieu Othacehe iio_device_release_direct_mode(indio_dev); 936d35567fcSMathieu Othacehe 937d35567fcSMathieu Othacehe return ret; 938d35567fcSMathieu Othacehe } 939d35567fcSMathieu Othacehe 940d35567fcSMathieu Othacehe static int vcnl4010_write_event_config(struct iio_dev *indio_dev, 941d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 942d35567fcSMathieu Othacehe enum iio_event_type type, 943d35567fcSMathieu Othacehe enum iio_event_direction dir, 944d35567fcSMathieu Othacehe int state) 945d35567fcSMathieu Othacehe { 946d35567fcSMathieu Othacehe switch (chan->type) { 947d35567fcSMathieu Othacehe case IIO_PROXIMITY: 948d35567fcSMathieu Othacehe return vcnl4010_config_threshold(indio_dev, state); 949d35567fcSMathieu Othacehe default: 950d35567fcSMathieu Othacehe return -EINVAL; 951d35567fcSMathieu Othacehe } 952d35567fcSMathieu Othacehe } 953d35567fcSMathieu Othacehe 95454667612SMårten Lindahl static int vcnl4040_read_event_config(struct iio_dev *indio_dev, 95554667612SMårten Lindahl const struct iio_chan_spec *chan, 95654667612SMårten Lindahl enum iio_event_type type, 95754667612SMårten Lindahl enum iio_event_direction dir) 95854667612SMårten Lindahl { 95954667612SMårten Lindahl int ret; 96054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 96154667612SMårten Lindahl 96254667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 96354667612SMårten Lindahl if (ret < 0) 96454667612SMårten Lindahl return ret; 96554667612SMårten Lindahl 96654667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, ret); 96754667612SMårten Lindahl 96854667612SMårten Lindahl return (dir == IIO_EV_DIR_RISING) ? 96954667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_AWAY, ret) : 97054667612SMårten Lindahl FIELD_GET(VCNL4040_PS_IF_CLOSE, ret); 97154667612SMårten Lindahl } 97254667612SMårten Lindahl 97354667612SMårten Lindahl static int vcnl4040_write_event_config(struct iio_dev *indio_dev, 97454667612SMårten Lindahl const struct iio_chan_spec *chan, 97554667612SMårten Lindahl enum iio_event_type type, 97654667612SMårten Lindahl enum iio_event_direction dir, int state) 97754667612SMårten Lindahl { 97854667612SMårten Lindahl int ret; 97954667612SMårten Lindahl u16 val, mask; 98054667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 98154667612SMårten Lindahl 98254667612SMårten Lindahl mutex_lock(&data->vcnl4000_lock); 98354667612SMårten Lindahl 98454667612SMårten Lindahl ret = i2c_smbus_read_word_data(data->client, VCNL4200_PS_CONF1); 98554667612SMårten Lindahl if (ret < 0) 98654667612SMårten Lindahl goto out; 98754667612SMårten Lindahl 98854667612SMårten Lindahl if (dir == IIO_EV_DIR_RISING) 98954667612SMårten Lindahl mask = VCNL4040_PS_IF_AWAY; 99054667612SMårten Lindahl else 99154667612SMårten Lindahl mask = VCNL4040_PS_IF_CLOSE; 99254667612SMårten Lindahl 99354667612SMårten Lindahl val = state ? (ret | mask) : (ret & ~mask); 99454667612SMårten Lindahl 99554667612SMårten Lindahl data->ps_int = FIELD_GET(VCNL4040_PS_CONF2_PS_INT, val); 99654667612SMårten Lindahl ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 99754667612SMårten Lindahl 99854667612SMårten Lindahl out: 99954667612SMårten Lindahl mutex_unlock(&data->vcnl4000_lock); 100054667612SMårten Lindahl data->chip_spec->set_power_state(data, data->ps_int != 0); 100154667612SMårten Lindahl 100254667612SMårten Lindahl return ret; 100354667612SMårten Lindahl } 100454667612SMårten Lindahl 100554667612SMårten Lindahl static irqreturn_t vcnl4040_irq_thread(int irq, void *p) 100654667612SMårten Lindahl { 100754667612SMårten Lindahl struct iio_dev *indio_dev = p; 100854667612SMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 100954667612SMårten Lindahl int ret; 101054667612SMårten Lindahl 1011*854965b7SAstrid Rost ret = i2c_smbus_read_word_data(data->client, data->chip_spec->int_reg); 101254667612SMårten Lindahl if (ret < 0) 101354667612SMårten Lindahl return IRQ_HANDLED; 101454667612SMårten Lindahl 101554667612SMårten Lindahl if (ret & VCNL4040_PS_IF_CLOSE) { 101654667612SMårten Lindahl iio_push_event(indio_dev, 101754667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 101854667612SMårten Lindahl IIO_EV_TYPE_THRESH, 101954667612SMårten Lindahl IIO_EV_DIR_RISING), 102054667612SMårten Lindahl iio_get_time_ns(indio_dev)); 102154667612SMårten Lindahl } 102254667612SMårten Lindahl 102354667612SMårten Lindahl if (ret & VCNL4040_PS_IF_AWAY) { 102454667612SMårten Lindahl iio_push_event(indio_dev, 102554667612SMårten Lindahl IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, 102654667612SMårten Lindahl IIO_EV_TYPE_THRESH, 102754667612SMårten Lindahl IIO_EV_DIR_FALLING), 102854667612SMårten Lindahl iio_get_time_ns(indio_dev)); 102954667612SMårten Lindahl } 103054667612SMårten Lindahl 103154667612SMårten Lindahl return IRQ_HANDLED; 103254667612SMårten Lindahl } 103354667612SMårten Lindahl 1034d35567fcSMathieu Othacehe static ssize_t vcnl4000_read_near_level(struct iio_dev *indio_dev, 1035d35567fcSMathieu Othacehe uintptr_t priv, 1036d35567fcSMathieu Othacehe const struct iio_chan_spec *chan, 1037d35567fcSMathieu Othacehe char *buf) 1038d35567fcSMathieu Othacehe { 1039d35567fcSMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 1040d35567fcSMathieu Othacehe 1041d35567fcSMathieu Othacehe return sprintf(buf, "%u\n", data->near_level); 1042d35567fcSMathieu Othacehe } 1043d35567fcSMathieu Othacehe 10443a52d32aSMårten Lindahl static irqreturn_t vcnl4010_irq_thread(int irq, void *p) 10453a52d32aSMårten Lindahl { 10463a52d32aSMårten Lindahl struct iio_dev *indio_dev = p; 10473a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 10483a52d32aSMårten Lindahl unsigned long isr; 10493a52d32aSMårten Lindahl int ret; 10503a52d32aSMårten Lindahl 10513a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 10523a52d32aSMårten Lindahl if (ret < 0) 10533a52d32aSMårten Lindahl goto end; 10543a52d32aSMårten Lindahl 10553a52d32aSMårten Lindahl isr = ret; 10563a52d32aSMårten Lindahl 10573a52d32aSMårten Lindahl if (isr & VCNL4010_INT_THR) { 10583a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_LOW, &isr)) { 10593a52d32aSMårten Lindahl iio_push_event(indio_dev, 10603a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 10613a52d32aSMårten Lindahl IIO_PROXIMITY, 10623a52d32aSMårten Lindahl 1, 10633a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 10643a52d32aSMårten Lindahl IIO_EV_DIR_FALLING), 10653a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 10663a52d32aSMårten Lindahl } 10673a52d32aSMårten Lindahl 10683a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_THR_HIGH, &isr)) { 10693a52d32aSMårten Lindahl iio_push_event(indio_dev, 10703a52d32aSMårten Lindahl IIO_UNMOD_EVENT_CODE( 10713a52d32aSMårten Lindahl IIO_PROXIMITY, 10723a52d32aSMårten Lindahl 1, 10733a52d32aSMårten Lindahl IIO_EV_TYPE_THRESH, 10743a52d32aSMårten Lindahl IIO_EV_DIR_RISING), 10753a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 10763a52d32aSMårten Lindahl } 10773a52d32aSMårten Lindahl 10783a52d32aSMårten Lindahl i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 10793a52d32aSMårten Lindahl isr & VCNL4010_INT_THR); 10803a52d32aSMårten Lindahl } 10813a52d32aSMårten Lindahl 10823a52d32aSMårten Lindahl if (isr & VCNL4010_INT_DRDY && iio_buffer_enabled(indio_dev)) 1083f700e55eSMehdi Djait iio_trigger_poll_nested(indio_dev->trig); 10843a52d32aSMårten Lindahl 10853a52d32aSMårten Lindahl end: 10863a52d32aSMårten Lindahl return IRQ_HANDLED; 10873a52d32aSMårten Lindahl } 10883a52d32aSMårten Lindahl 10893a52d32aSMårten Lindahl static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) 10903a52d32aSMårten Lindahl { 10913a52d32aSMårten Lindahl struct iio_poll_func *pf = p; 10923a52d32aSMårten Lindahl struct iio_dev *indio_dev = pf->indio_dev; 10933a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 10943a52d32aSMårten Lindahl const unsigned long *active_scan_mask = indio_dev->active_scan_mask; 10953a52d32aSMårten Lindahl u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ 10963a52d32aSMårten Lindahl bool data_read = false; 10973a52d32aSMårten Lindahl unsigned long isr; 10983a52d32aSMårten Lindahl int val = 0; 10993a52d32aSMårten Lindahl int ret; 11003a52d32aSMårten Lindahl 11013a52d32aSMårten Lindahl ret = i2c_smbus_read_byte_data(data->client, VCNL4010_ISR); 11023a52d32aSMårten Lindahl if (ret < 0) 11033a52d32aSMårten Lindahl goto end; 11043a52d32aSMårten Lindahl 11053a52d32aSMårten Lindahl isr = ret; 11063a52d32aSMårten Lindahl 11073a52d32aSMårten Lindahl if (test_bit(0, active_scan_mask)) { 11083a52d32aSMårten Lindahl if (test_bit(VCNL4010_INT_PROXIMITY, &isr)) { 11093a52d32aSMårten Lindahl ret = vcnl4000_read_data(data, 11103a52d32aSMårten Lindahl VCNL4000_PS_RESULT_HI, 11113a52d32aSMårten Lindahl &val); 11123a52d32aSMårten Lindahl if (ret < 0) 11133a52d32aSMårten Lindahl goto end; 11143a52d32aSMårten Lindahl 11153a52d32aSMårten Lindahl buffer[0] = val; 11163a52d32aSMårten Lindahl data_read = true; 11173a52d32aSMårten Lindahl } 11183a52d32aSMårten Lindahl } 11193a52d32aSMårten Lindahl 11203a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_ISR, 11213a52d32aSMårten Lindahl isr & VCNL4010_INT_DRDY); 11223a52d32aSMårten Lindahl if (ret < 0) 11233a52d32aSMårten Lindahl goto end; 11243a52d32aSMårten Lindahl 11253a52d32aSMårten Lindahl if (!data_read) 11263a52d32aSMårten Lindahl goto end; 11273a52d32aSMårten Lindahl 11283a52d32aSMårten Lindahl iio_push_to_buffers_with_timestamp(indio_dev, buffer, 11293a52d32aSMårten Lindahl iio_get_time_ns(indio_dev)); 11303a52d32aSMårten Lindahl 11313a52d32aSMårten Lindahl end: 11323a52d32aSMårten Lindahl iio_trigger_notify_done(indio_dev->trig); 11333a52d32aSMårten Lindahl return IRQ_HANDLED; 11343a52d32aSMårten Lindahl } 11353a52d32aSMårten Lindahl 11363a52d32aSMårten Lindahl static int vcnl4010_buffer_postenable(struct iio_dev *indio_dev) 11373a52d32aSMårten Lindahl { 11383a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11393a52d32aSMårten Lindahl int ret; 11403a52d32aSMårten Lindahl int cmd; 11413a52d32aSMårten Lindahl 11423a52d32aSMårten Lindahl /* Do not enable the buffer if we are already capturing events. */ 11433a52d32aSMårten Lindahl if (vcnl4010_is_in_periodic_mode(data)) 11443a52d32aSMårten Lindahl return -EBUSY; 11453a52d32aSMårten Lindahl 11463a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 11473a52d32aSMårten Lindahl VCNL4010_INT_PROX_EN); 11483a52d32aSMårten Lindahl if (ret < 0) 11493a52d32aSMårten Lindahl return ret; 11503a52d32aSMårten Lindahl 11513a52d32aSMårten Lindahl cmd = VCNL4000_SELF_TIMED_EN | VCNL4000_PROX_EN; 11523a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, cmd); 11533a52d32aSMårten Lindahl } 11543a52d32aSMårten Lindahl 11553a52d32aSMårten Lindahl static int vcnl4010_buffer_predisable(struct iio_dev *indio_dev) 11563a52d32aSMårten Lindahl { 11573a52d32aSMårten Lindahl struct vcnl4000_data *data = iio_priv(indio_dev); 11583a52d32aSMårten Lindahl int ret; 11593a52d32aSMårten Lindahl 11603a52d32aSMårten Lindahl ret = i2c_smbus_write_byte_data(data->client, VCNL4010_INT_CTRL, 0); 11613a52d32aSMårten Lindahl if (ret < 0) 11623a52d32aSMårten Lindahl return ret; 11633a52d32aSMårten Lindahl 11643a52d32aSMårten Lindahl return i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 0); 11653a52d32aSMårten Lindahl } 11663a52d32aSMårten Lindahl 11673a52d32aSMårten Lindahl static const struct iio_buffer_setup_ops vcnl4010_buffer_ops = { 11683a52d32aSMårten Lindahl .postenable = &vcnl4010_buffer_postenable, 11693a52d32aSMårten Lindahl .predisable = &vcnl4010_buffer_predisable, 11703a52d32aSMårten Lindahl }; 11713a52d32aSMårten Lindahl 1172d35567fcSMathieu Othacehe static const struct iio_chan_spec_ext_info vcnl4000_ext_info[] = { 1173d35567fcSMathieu Othacehe { 1174d35567fcSMathieu Othacehe .name = "nearlevel", 1175d35567fcSMathieu Othacehe .shared = IIO_SEPARATE, 1176d35567fcSMathieu Othacehe .read = vcnl4000_read_near_level, 1177d35567fcSMathieu Othacehe }, 1178d35567fcSMathieu Othacehe { /* sentinel */ } 1179d35567fcSMathieu Othacehe }; 1180d35567fcSMathieu Othacehe 1181d35567fcSMathieu Othacehe static const struct iio_event_spec vcnl4000_event_spec[] = { 1182d35567fcSMathieu Othacehe { 1183d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1184d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_RISING, 1185d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1186d35567fcSMathieu Othacehe }, { 1187d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1188d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_FALLING, 1189d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_VALUE), 1190d35567fcSMathieu Othacehe }, { 1191d35567fcSMathieu Othacehe .type = IIO_EV_TYPE_THRESH, 1192d35567fcSMathieu Othacehe .dir = IIO_EV_DIR_EITHER, 1193d35567fcSMathieu Othacehe .mask_separate = BIT(IIO_EV_INFO_ENABLE), 1194d35567fcSMathieu Othacehe } 1195d35567fcSMathieu Othacehe }; 1196d35567fcSMathieu Othacehe 119754667612SMårten Lindahl static const struct iio_event_spec vcnl4040_event_spec[] = { 119854667612SMårten Lindahl { 119954667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 120054667612SMårten Lindahl .dir = IIO_EV_DIR_RISING, 120154667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 120254667612SMårten Lindahl }, { 120354667612SMårten Lindahl .type = IIO_EV_TYPE_THRESH, 120454667612SMårten Lindahl .dir = IIO_EV_DIR_FALLING, 120554667612SMårten Lindahl .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE), 120654667612SMårten Lindahl }, 120754667612SMårten Lindahl }; 120854667612SMårten Lindahl 1209d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4000_channels[] = { 1210d35567fcSMathieu Othacehe { 1211d35567fcSMathieu Othacehe .type = IIO_LIGHT, 1212d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1213d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1214d35567fcSMathieu Othacehe }, { 1215d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 1216d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 1217d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 1218d35567fcSMathieu Othacehe } 1219d35567fcSMathieu Othacehe }; 1220d35567fcSMathieu Othacehe 1221d35567fcSMathieu Othacehe static const struct iio_chan_spec vcnl4010_channels[] = { 1222d35567fcSMathieu Othacehe { 1223d35567fcSMathieu Othacehe .type = IIO_LIGHT, 12248fe78d52SMathieu Othacehe .scan_index = -1, 1225d35567fcSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1226d35567fcSMathieu Othacehe BIT(IIO_CHAN_INFO_SCALE), 1227d35567fcSMathieu Othacehe }, { 1228d35567fcSMathieu Othacehe .type = IIO_PROXIMITY, 12298fe78d52SMathieu Othacehe .scan_index = 0, 1230f6889c1bSMathieu Othacehe .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 1231f6889c1bSMathieu Othacehe BIT(IIO_CHAN_INFO_SAMP_FREQ), 1232f6889c1bSMathieu Othacehe .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), 1233d35567fcSMathieu Othacehe .event_spec = vcnl4000_event_spec, 1234d35567fcSMathieu Othacehe .num_event_specs = ARRAY_SIZE(vcnl4000_event_spec), 1235d35567fcSMathieu Othacehe .ext_info = vcnl4000_ext_info, 12368fe78d52SMathieu Othacehe .scan_type = { 12378fe78d52SMathieu Othacehe .sign = 'u', 12388fe78d52SMathieu Othacehe .realbits = 16, 12398fe78d52SMathieu Othacehe .storagebits = 16, 12408fe78d52SMathieu Othacehe .endianness = IIO_CPU, 1241d35567fcSMathieu Othacehe }, 12428fe78d52SMathieu Othacehe }, 12438fe78d52SMathieu Othacehe IIO_CHAN_SOFT_TIMESTAMP(1), 1244d35567fcSMathieu Othacehe }; 1245d35567fcSMathieu Othacehe 124685e2c6a2SMårten Lindahl static const struct iio_chan_spec vcnl4040_channels[] = { 124785e2c6a2SMårten Lindahl { 124885e2c6a2SMårten Lindahl .type = IIO_LIGHT, 124985e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 125085e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_SCALE), 125185e2c6a2SMårten Lindahl }, { 125285e2c6a2SMårten Lindahl .type = IIO_PROXIMITY, 125385e2c6a2SMårten Lindahl .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 125485e2c6a2SMårten Lindahl BIT(IIO_CHAN_INFO_INT_TIME), 125585e2c6a2SMårten Lindahl .info_mask_separate_available = BIT(IIO_CHAN_INFO_INT_TIME), 125685e2c6a2SMårten Lindahl .ext_info = vcnl4000_ext_info, 125754667612SMårten Lindahl .event_spec = vcnl4040_event_spec, 125854667612SMårten Lindahl .num_event_specs = ARRAY_SIZE(vcnl4040_event_spec), 125985e2c6a2SMårten Lindahl } 126085e2c6a2SMårten Lindahl }; 126185e2c6a2SMårten Lindahl 126262a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 126362a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 126462a1efb9SPeter Meerwald }; 126562a1efb9SPeter Meerwald 1266d35567fcSMathieu Othacehe static const struct iio_info vcnl4010_info = { 1267d35567fcSMathieu Othacehe .read_raw = vcnl4010_read_raw, 1268f6889c1bSMathieu Othacehe .read_avail = vcnl4010_read_avail, 1269f6889c1bSMathieu Othacehe .write_raw = vcnl4010_write_raw, 1270d35567fcSMathieu Othacehe .read_event_value = vcnl4010_read_event, 1271d35567fcSMathieu Othacehe .write_event_value = vcnl4010_write_event, 1272d35567fcSMathieu Othacehe .read_event_config = vcnl4010_read_event_config, 1273d35567fcSMathieu Othacehe .write_event_config = vcnl4010_write_event_config, 1274d35567fcSMathieu Othacehe }; 1275d35567fcSMathieu Othacehe 127685e2c6a2SMårten Lindahl static const struct iio_info vcnl4040_info = { 127785e2c6a2SMårten Lindahl .read_raw = vcnl4000_read_raw, 127885e2c6a2SMårten Lindahl .write_raw = vcnl4040_write_raw, 127954667612SMårten Lindahl .read_event_value = vcnl4040_read_event, 128054667612SMårten Lindahl .write_event_value = vcnl4040_write_event, 128154667612SMårten Lindahl .read_event_config = vcnl4040_read_event_config, 128254667612SMårten Lindahl .write_event_config = vcnl4040_write_event_config, 128385e2c6a2SMårten Lindahl .read_avail = vcnl4040_read_avail, 128485e2c6a2SMårten Lindahl }; 128585e2c6a2SMårten Lindahl 1286d35567fcSMathieu Othacehe static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 1287d35567fcSMathieu Othacehe [VCNL4000] = { 1288d35567fcSMathieu Othacehe .prod = "VCNL4000", 1289d35567fcSMathieu Othacehe .init = vcnl4000_init, 1290d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1291d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1292d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1293d35567fcSMathieu Othacehe .channels = vcnl4000_channels, 1294d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1295d35567fcSMathieu Othacehe .info = &vcnl4000_info, 1296d35567fcSMathieu Othacehe }, 1297d35567fcSMathieu Othacehe [VCNL4010] = { 1298d35567fcSMathieu Othacehe .prod = "VCNL4010/4020", 1299d35567fcSMathieu Othacehe .init = vcnl4000_init, 1300d35567fcSMathieu Othacehe .measure_light = vcnl4000_measure_light, 1301d35567fcSMathieu Othacehe .measure_proximity = vcnl4000_measure_proximity, 1302d35567fcSMathieu Othacehe .set_power_state = vcnl4000_set_power_state, 1303d35567fcSMathieu Othacehe .channels = vcnl4010_channels, 1304d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4010_channels), 1305d35567fcSMathieu Othacehe .info = &vcnl4010_info, 1306bfb6cfeeSMårten Lindahl .irq_thread = vcnl4010_irq_thread, 1307bfb6cfeeSMårten Lindahl .trig_buffer_func = vcnl4010_trigger_handler, 1308bfb6cfeeSMårten Lindahl .buffer_setup_ops = &vcnl4010_buffer_ops, 1309d35567fcSMathieu Othacehe }, 1310d35567fcSMathieu Othacehe [VCNL4040] = { 1311d35567fcSMathieu Othacehe .prod = "VCNL4040", 1312d35567fcSMathieu Othacehe .init = vcnl4200_init, 1313d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1314d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1315d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 131685e2c6a2SMårten Lindahl .channels = vcnl4040_channels, 131785e2c6a2SMårten Lindahl .num_channels = ARRAY_SIZE(vcnl4040_channels), 131885e2c6a2SMårten Lindahl .info = &vcnl4040_info, 131954667612SMårten Lindahl .irq_thread = vcnl4040_irq_thread, 1320*854965b7SAstrid Rost .int_reg = VCNL4040_INT_FLAGS, 1321d35567fcSMathieu Othacehe }, 1322d35567fcSMathieu Othacehe [VCNL4200] = { 1323d35567fcSMathieu Othacehe .prod = "VCNL4200", 1324d35567fcSMathieu Othacehe .init = vcnl4200_init, 1325d35567fcSMathieu Othacehe .measure_light = vcnl4200_measure_light, 1326d35567fcSMathieu Othacehe .measure_proximity = vcnl4200_measure_proximity, 1327d35567fcSMathieu Othacehe .set_power_state = vcnl4200_set_power_state, 1328*854965b7SAstrid Rost .channels = vcnl4040_channels, 1329d35567fcSMathieu Othacehe .num_channels = ARRAY_SIZE(vcnl4000_channels), 1330*854965b7SAstrid Rost .info = &vcnl4040_info, 1331*854965b7SAstrid Rost .irq_thread = vcnl4040_irq_thread, 1332*854965b7SAstrid Rost .int_reg = VCNL4200_INT_FLAGS, 1333d35567fcSMathieu Othacehe }, 1334d35567fcSMathieu Othacehe }; 1335d35567fcSMathieu Othacehe 13368fe78d52SMathieu Othacehe static const struct iio_trigger_ops vcnl4010_trigger_ops = { 13378fe78d52SMathieu Othacehe .validate_device = iio_trigger_validate_own_device, 13388fe78d52SMathieu Othacehe }; 13398fe78d52SMathieu Othacehe 13408fe78d52SMathieu Othacehe static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) 13418fe78d52SMathieu Othacehe { 13428fe78d52SMathieu Othacehe struct vcnl4000_data *data = iio_priv(indio_dev); 13438fe78d52SMathieu Othacehe struct i2c_client *client = data->client; 13448fe78d52SMathieu Othacehe struct iio_trigger *trigger; 13458fe78d52SMathieu Othacehe 13468fe78d52SMathieu Othacehe trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", 134715ea2878SJonathan Cameron indio_dev->name, 134815ea2878SJonathan Cameron iio_device_id(indio_dev)); 13498fe78d52SMathieu Othacehe if (!trigger) 13508fe78d52SMathieu Othacehe return -ENOMEM; 13518fe78d52SMathieu Othacehe 13528fe78d52SMathieu Othacehe trigger->ops = &vcnl4010_trigger_ops; 13538fe78d52SMathieu Othacehe iio_trigger_set_drvdata(trigger, indio_dev); 13548fe78d52SMathieu Othacehe 13558fe78d52SMathieu Othacehe return devm_iio_trigger_register(&client->dev, trigger); 13568fe78d52SMathieu Othacehe } 13578fe78d52SMathieu Othacehe 1358e61295e0SUwe Kleine-König static int vcnl4000_probe(struct i2c_client *client) 135962a1efb9SPeter Meerwald { 1360e61295e0SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client); 136162a1efb9SPeter Meerwald struct vcnl4000_data *data; 136262a1efb9SPeter Meerwald struct iio_dev *indio_dev; 13631ebc787aSTomas Novotny int ret; 136462a1efb9SPeter Meerwald 13652669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 136662a1efb9SPeter Meerwald if (!indio_dev) 136762a1efb9SPeter Meerwald return -ENOMEM; 136862a1efb9SPeter Meerwald 136962a1efb9SPeter Meerwald data = iio_priv(indio_dev); 137062a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 137162a1efb9SPeter Meerwald data->client = client; 13721ebc787aSTomas Novotny data->id = id->driver_data; 13731ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 137462a1efb9SPeter Meerwald 137542ec40b0SMårten Lindahl mutex_init(&data->vcnl4000_lock); 137642ec40b0SMårten Lindahl 13771ebc787aSTomas Novotny ret = data->chip_spec->init(data); 137862a1efb9SPeter Meerwald if (ret < 0) 13792669d723SPeter Meerwald return ret; 138062a1efb9SPeter Meerwald 1381d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 13821ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 138362a1efb9SPeter Meerwald 1384f5a98e1fSGuido Günther if (device_property_read_u32(&client->dev, "proximity-near-level", 1385f5a98e1fSGuido Günther &data->near_level)) 1386f5a98e1fSGuido Günther data->near_level = 0; 1387f5a98e1fSGuido Günther 1388d35567fcSMathieu Othacehe indio_dev->info = data->chip_spec->info; 1389d35567fcSMathieu Othacehe indio_dev->channels = data->chip_spec->channels; 1390d35567fcSMathieu Othacehe indio_dev->num_channels = data->chip_spec->num_channels; 139162a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 139262a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 139362a1efb9SPeter Meerwald 1394bfb6cfeeSMårten Lindahl if (data->chip_spec->trig_buffer_func && 1395bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops) { 13968fe78d52SMathieu Othacehe ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, 13978fe78d52SMathieu Othacehe NULL, 1398bfb6cfeeSMårten Lindahl data->chip_spec->trig_buffer_func, 1399bfb6cfeeSMårten Lindahl data->chip_spec->buffer_setup_ops); 14008fe78d52SMathieu Othacehe if (ret < 0) { 14018fe78d52SMathieu Othacehe dev_err(&client->dev, 14028fe78d52SMathieu Othacehe "unable to setup iio triggered buffer\n"); 14038fe78d52SMathieu Othacehe return ret; 14048fe78d52SMathieu Othacehe } 1405bfb6cfeeSMårten Lindahl } 14068fe78d52SMathieu Othacehe 1407bfb6cfeeSMårten Lindahl if (client->irq && data->chip_spec->irq_thread) { 1408d35567fcSMathieu Othacehe ret = devm_request_threaded_irq(&client->dev, client->irq, 1409bfb6cfeeSMårten Lindahl NULL, data->chip_spec->irq_thread, 1410d35567fcSMathieu Othacehe IRQF_TRIGGER_FALLING | 1411d35567fcSMathieu Othacehe IRQF_ONESHOT, 1412bfb6cfeeSMårten Lindahl "vcnl4000_irq", 1413d35567fcSMathieu Othacehe indio_dev); 1414d35567fcSMathieu Othacehe if (ret < 0) { 1415d35567fcSMathieu Othacehe dev_err(&client->dev, "irq request failed\n"); 1416d35567fcSMathieu Othacehe return ret; 1417d35567fcSMathieu Othacehe } 14188fe78d52SMathieu Othacehe 14198fe78d52SMathieu Othacehe ret = vcnl4010_probe_trigger(indio_dev); 14208fe78d52SMathieu Othacehe if (ret < 0) 14218fe78d52SMathieu Othacehe return ret; 1422d35567fcSMathieu Othacehe } 1423d35567fcSMathieu Othacehe 14245e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 14255e00708dSGuido Günther if (ret < 0) 14265e00708dSGuido Günther goto fail_poweroff; 14275e00708dSGuido Günther 14285e00708dSGuido Günther ret = iio_device_register(indio_dev); 14295e00708dSGuido Günther if (ret < 0) 14305e00708dSGuido Günther goto fail_poweroff; 14315e00708dSGuido Günther 14325e00708dSGuido Günther pm_runtime_enable(&client->dev); 14335e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 14345e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 14355e00708dSGuido Günther 14365e00708dSGuido Günther return 0; 14375e00708dSGuido Günther fail_poweroff: 14385e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 14395e00708dSGuido Günther return ret; 144062a1efb9SPeter Meerwald } 144162a1efb9SPeter Meerwald 1442ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 1443ebd457d5SAngus Ainslie (Purism) { 1444ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 14451436a78cSMarco Felsch .data = (void *)VCNL4000, 1446ebd457d5SAngus Ainslie (Purism) }, 1447ebd457d5SAngus Ainslie (Purism) { 1448ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 14491436a78cSMarco Felsch .data = (void *)VCNL4010, 1450ebd457d5SAngus Ainslie (Purism) }, 1451ebd457d5SAngus Ainslie (Purism) { 14521436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 14531436a78cSMarco Felsch .data = (void *)VCNL4010, 1454ebd457d5SAngus Ainslie (Purism) }, 1455ebd457d5SAngus Ainslie (Purism) { 14567fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 14577fd1c260SMarco Felsch .data = (void *)VCNL4040, 14587fd1c260SMarco Felsch }, 14597fd1c260SMarco Felsch { 1460ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 14611436a78cSMarco Felsch .data = (void *)VCNL4200, 1462ebd457d5SAngus Ainslie (Purism) }, 1463ebd457d5SAngus Ainslie (Purism) {}, 1464ebd457d5SAngus Ainslie (Purism) }; 1465ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 1466ebd457d5SAngus Ainslie (Purism) 1467ed5c2f5fSUwe Kleine-König static void vcnl4000_remove(struct i2c_client *client) 14685e00708dSGuido Günther { 14695e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 14705e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 1471ab91da2fSUwe Kleine-König int ret; 14725e00708dSGuido Günther 14735e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 14745e00708dSGuido Günther pm_runtime_disable(&client->dev); 14755e00708dSGuido Günther iio_device_unregister(indio_dev); 14765e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 14775e00708dSGuido Günther 1478ab91da2fSUwe Kleine-König ret = data->chip_spec->set_power_state(data, false); 1479ab91da2fSUwe Kleine-König if (ret) 1480ab91da2fSUwe Kleine-König dev_warn(&client->dev, "Failed to power down (%pe)\n", 1481ab91da2fSUwe Kleine-König ERR_PTR(ret)); 14825e00708dSGuido Günther } 14835e00708dSGuido Günther 1484cd4d10b1SJonathan Cameron static int vcnl4000_runtime_suspend(struct device *dev) 14855e00708dSGuido Günther { 14865e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 14875e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 14885e00708dSGuido Günther 14895e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 14905e00708dSGuido Günther } 14915e00708dSGuido Günther 1492cd4d10b1SJonathan Cameron static int vcnl4000_runtime_resume(struct device *dev) 14935e00708dSGuido Günther { 14945e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 14955e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 14965e00708dSGuido Günther 14975e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 14985e00708dSGuido Günther } 14995e00708dSGuido Günther 1500cd4d10b1SJonathan Cameron static DEFINE_RUNTIME_DEV_PM_OPS(vcnl4000_pm_ops, vcnl4000_runtime_suspend, 1501cd4d10b1SJonathan Cameron vcnl4000_runtime_resume, NULL); 15025e00708dSGuido Günther 150362a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 150462a1efb9SPeter Meerwald .driver = { 150562a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 1506cd4d10b1SJonathan Cameron .pm = pm_ptr(&vcnl4000_pm_ops), 1507ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 150862a1efb9SPeter Meerwald }, 15097cf15f42SUwe Kleine-König .probe = vcnl4000_probe, 151062a1efb9SPeter Meerwald .id_table = vcnl4000_id, 15115e00708dSGuido Günther .remove = vcnl4000_remove, 151262a1efb9SPeter Meerwald }; 151362a1efb9SPeter Meerwald 151462a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 151562a1efb9SPeter Meerwald 151662a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 15178fe78d52SMathieu Othacehe MODULE_AUTHOR("Mathieu Othacehe <m.othacehe@gmail.com>"); 151862a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 151962a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 1520