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 862a1efb9SPeter Meerwald * 9be38866fSTomas Novotny * IIO driver for: 10be38866fSTomas Novotny * VCNL4000/10/20 (7-bit I2C slave address 0x13) 115a441aadSAngus Ainslie (Purism) * VCNL4040 (7-bit I2C slave address 0x60) 12be38866fSTomas Novotny * VCNL4200 (7-bit I2C slave address 0x51) 1362a1efb9SPeter Meerwald * 1462a1efb9SPeter Meerwald * TODO: 1562a1efb9SPeter Meerwald * allow to adjust IR current 1662a1efb9SPeter Meerwald * proximity threshold and event handling 17d978bfddSPeter Meerwald-Stadler * periodic ALS/proximity measurement (VCNL4010/20) 185a441aadSAngus Ainslie (Purism) * interrupts (VCNL4010/20/40, VCNL4200) 1962a1efb9SPeter Meerwald */ 2062a1efb9SPeter Meerwald 2162a1efb9SPeter Meerwald #include <linux/module.h> 2262a1efb9SPeter Meerwald #include <linux/i2c.h> 2362a1efb9SPeter Meerwald #include <linux/err.h> 2462a1efb9SPeter Meerwald #include <linux/delay.h> 25*5e00708dSGuido Günther #include <linux/pm_runtime.h> 2662a1efb9SPeter Meerwald 2762a1efb9SPeter Meerwald #include <linux/iio/iio.h> 2862a1efb9SPeter Meerwald #include <linux/iio/sysfs.h> 2962a1efb9SPeter Meerwald 3062a1efb9SPeter Meerwald #define VCNL4000_DRV_NAME "vcnl4000" 311ebc787aSTomas Novotny #define VCNL4000_PROD_ID 0x01 321ebc787aSTomas Novotny #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */ 335a441aadSAngus Ainslie (Purism) #define VCNL4040_PROD_ID 0x86 34be38866fSTomas Novotny #define VCNL4200_PROD_ID 0x58 3562a1efb9SPeter Meerwald 3662a1efb9SPeter Meerwald #define VCNL4000_COMMAND 0x80 /* Command register */ 3762a1efb9SPeter Meerwald #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */ 3862a1efb9SPeter Meerwald #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */ 3962a1efb9SPeter Meerwald #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */ 4062a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */ 4162a1efb9SPeter Meerwald #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */ 4262a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */ 4362a1efb9SPeter Meerwald #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */ 4462a1efb9SPeter Meerwald #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */ 4562a1efb9SPeter Meerwald #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */ 4662a1efb9SPeter Meerwald 47be38866fSTomas Novotny #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */ 48be38866fSTomas Novotny #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */ 49be38866fSTomas Novotny #define VCNL4200_PS_DATA 0x08 /* Proximity data */ 50be38866fSTomas Novotny #define VCNL4200_AL_DATA 0x09 /* Ambient light data */ 51be38866fSTomas Novotny #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */ 52be38866fSTomas Novotny 535a441aadSAngus Ainslie (Purism) #define VCNL4040_DEV_ID 0x0c /* Device ID and version */ 545a441aadSAngus Ainslie (Purism) 5562a1efb9SPeter Meerwald /* Bit masks for COMMAND register */ 56ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */ 57ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */ 58ff6a5259SPeter Meerwald-Stadler #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */ 59ff6a5259SPeter Meerwald-Stadler #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */ 6062a1efb9SPeter Meerwald 61*5e00708dSGuido Günther #define VCNL4000_SLEEP_DELAY_MS 2000 /* before we enter pm_runtime_suspend */ 62*5e00708dSGuido Günther 631ebc787aSTomas Novotny enum vcnl4000_device_ids { 641ebc787aSTomas Novotny VCNL4000, 6550c50b97STomas Novotny VCNL4010, 665a441aadSAngus Ainslie (Purism) VCNL4040, 67be38866fSTomas Novotny VCNL4200, 68be38866fSTomas Novotny }; 69be38866fSTomas Novotny 70be38866fSTomas Novotny struct vcnl4200_channel { 71be38866fSTomas Novotny u8 reg; 72be38866fSTomas Novotny ktime_t last_measurement; 73be38866fSTomas Novotny ktime_t sampling_rate; 74be38866fSTomas Novotny struct mutex lock; 751ebc787aSTomas Novotny }; 761ebc787aSTomas Novotny 7762a1efb9SPeter Meerwald struct vcnl4000_data { 7862a1efb9SPeter Meerwald struct i2c_client *client; 791ebc787aSTomas Novotny enum vcnl4000_device_ids id; 801ebc787aSTomas Novotny int rev; 811ebc787aSTomas Novotny int al_scale; 821ebc787aSTomas Novotny const struct vcnl4000_chip_spec *chip_spec; 83be38866fSTomas Novotny struct mutex vcnl4000_lock; 84be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_al; 85be38866fSTomas Novotny struct vcnl4200_channel vcnl4200_ps; 8662a1efb9SPeter Meerwald }; 8762a1efb9SPeter Meerwald 881ebc787aSTomas Novotny struct vcnl4000_chip_spec { 891ebc787aSTomas Novotny const char *prod; 901ebc787aSTomas Novotny int (*init)(struct vcnl4000_data *data); 911ebc787aSTomas Novotny int (*measure_light)(struct vcnl4000_data *data, int *val); 921ebc787aSTomas Novotny int (*measure_proximity)(struct vcnl4000_data *data, int *val); 93*5e00708dSGuido Günther int (*set_power_state)(struct vcnl4000_data *data, bool on); 941ebc787aSTomas Novotny }; 951ebc787aSTomas Novotny 9662a1efb9SPeter Meerwald static const struct i2c_device_id vcnl4000_id[] = { 971ebc787aSTomas Novotny { "vcnl4000", VCNL4000 }, 9850c50b97STomas Novotny { "vcnl4010", VCNL4010 }, 9950c50b97STomas Novotny { "vcnl4020", VCNL4010 }, 1005a441aadSAngus Ainslie (Purism) { "vcnl4040", VCNL4040 }, 101be38866fSTomas Novotny { "vcnl4200", VCNL4200 }, 10262a1efb9SPeter Meerwald { } 10362a1efb9SPeter Meerwald }; 10462a1efb9SPeter Meerwald MODULE_DEVICE_TABLE(i2c, vcnl4000_id); 10562a1efb9SPeter Meerwald 106*5e00708dSGuido Günther static int vcnl4000_set_power_state(struct vcnl4000_data *data, bool on) 107*5e00708dSGuido Günther { 108*5e00708dSGuido Günther /* no suspend op */ 109*5e00708dSGuido Günther return 0; 110*5e00708dSGuido Günther } 111*5e00708dSGuido Günther 1121ebc787aSTomas Novotny static int vcnl4000_init(struct vcnl4000_data *data) 1131ebc787aSTomas Novotny { 1141ebc787aSTomas Novotny int ret, prod_id; 1151ebc787aSTomas Novotny 1161ebc787aSTomas Novotny ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV); 1171ebc787aSTomas Novotny if (ret < 0) 1181ebc787aSTomas Novotny return ret; 1191ebc787aSTomas Novotny 1201ebc787aSTomas Novotny prod_id = ret >> 4; 12158bf9aceSTomas Novotny switch (prod_id) { 12258bf9aceSTomas Novotny case VCNL4000_PROD_ID: 12358bf9aceSTomas Novotny if (data->id != VCNL4000) 12458bf9aceSTomas Novotny dev_warn(&data->client->dev, 12558bf9aceSTomas Novotny "wrong device id, use vcnl4000"); 12658bf9aceSTomas Novotny break; 12758bf9aceSTomas Novotny case VCNL4010_PROD_ID: 12858bf9aceSTomas Novotny if (data->id != VCNL4010) 12958bf9aceSTomas Novotny dev_warn(&data->client->dev, 13058bf9aceSTomas Novotny "wrong device id, use vcnl4010/4020"); 13158bf9aceSTomas Novotny break; 13258bf9aceSTomas Novotny default: 1331ebc787aSTomas Novotny return -ENODEV; 13458bf9aceSTomas Novotny } 1351ebc787aSTomas Novotny 1361ebc787aSTomas Novotny data->rev = ret & 0xf; 1371ebc787aSTomas Novotny data->al_scale = 250000; 138be38866fSTomas Novotny mutex_init(&data->vcnl4000_lock); 139be38866fSTomas Novotny 140*5e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 141be38866fSTomas Novotny }; 142be38866fSTomas Novotny 143*5e00708dSGuido Günther static int vcnl4200_set_power_state(struct vcnl4000_data *data, bool on) 144*5e00708dSGuido Günther { 145*5e00708dSGuido Günther u16 val = on ? 0 /* power on */ : 1 /* shut down */; 146*5e00708dSGuido Günther int ret; 147*5e00708dSGuido Günther 148*5e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, val); 149*5e00708dSGuido Günther if (ret < 0) 150*5e00708dSGuido Günther return ret; 151*5e00708dSGuido Günther 152*5e00708dSGuido Günther ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, val); 153*5e00708dSGuido Günther if (ret < 0) 154*5e00708dSGuido Günther return ret; 155*5e00708dSGuido Günther 156*5e00708dSGuido Günther if (on) { 157*5e00708dSGuido Günther /* Wait at least one integration cycle before fetching data */ 158*5e00708dSGuido Günther data->vcnl4200_al.last_measurement = ktime_get(); 159*5e00708dSGuido Günther data->vcnl4200_ps.last_measurement = ktime_get(); 160*5e00708dSGuido Günther } 161*5e00708dSGuido Günther 162*5e00708dSGuido Günther return 0; 163*5e00708dSGuido Günther } 164*5e00708dSGuido Günther 165be38866fSTomas Novotny static int vcnl4200_init(struct vcnl4000_data *data) 166be38866fSTomas Novotny { 1675a441aadSAngus Ainslie (Purism) int ret, id; 168be38866fSTomas Novotny 169be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID); 170be38866fSTomas Novotny if (ret < 0) 171be38866fSTomas Novotny return ret; 172be38866fSTomas Novotny 1735a441aadSAngus Ainslie (Purism) id = ret & 0xff; 1745a441aadSAngus Ainslie (Purism) 1755a441aadSAngus Ainslie (Purism) if (id != VCNL4200_PROD_ID) { 1765a441aadSAngus Ainslie (Purism) ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID); 1775a441aadSAngus Ainslie (Purism) if (ret < 0) 1785a441aadSAngus Ainslie (Purism) return ret; 1795a441aadSAngus Ainslie (Purism) 1805a441aadSAngus Ainslie (Purism) id = ret & 0xff; 1815a441aadSAngus Ainslie (Purism) 1825a441aadSAngus Ainslie (Purism) if (id != VCNL4040_PROD_ID) 183be38866fSTomas Novotny return -ENODEV; 1845a441aadSAngus Ainslie (Purism) } 1855a441aadSAngus Ainslie (Purism) 1865a441aadSAngus Ainslie (Purism) dev_dbg(&data->client->dev, "device id 0x%x", id); 187be38866fSTomas Novotny 188be38866fSTomas Novotny data->rev = (ret >> 8) & 0xf; 189be38866fSTomas Novotny 190be38866fSTomas Novotny data->vcnl4200_al.reg = VCNL4200_AL_DATA; 191be38866fSTomas Novotny data->vcnl4200_ps.reg = VCNL4200_PS_DATA; 1925a441aadSAngus Ainslie (Purism) switch (id) { 1935a441aadSAngus Ainslie (Purism) case VCNL4200_PROD_ID: 1945a441aadSAngus Ainslie (Purism) /* Integration time is 50ms, but the experiments */ 1955a441aadSAngus Ainslie (Purism) /* show 54ms in total. */ 196be38866fSTomas Novotny data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000); 197be38866fSTomas Novotny data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000); 198bc80573eSGuido Günther data->al_scale = 24000; 1995a441aadSAngus Ainslie (Purism) break; 2005a441aadSAngus Ainslie (Purism) case VCNL4040_PROD_ID: 2015a441aadSAngus Ainslie (Purism) /* Integration time is 80ms, add 10ms. */ 2025a441aadSAngus Ainslie (Purism) data->vcnl4200_al.sampling_rate = ktime_set(0, 100000 * 1000); 2035a441aadSAngus Ainslie (Purism) data->vcnl4200_ps.sampling_rate = ktime_set(0, 100000 * 1000); 204bc80573eSGuido Günther data->al_scale = 120000; 2055a441aadSAngus Ainslie (Purism) break; 2065a441aadSAngus Ainslie (Purism) } 207be38866fSTomas Novotny mutex_init(&data->vcnl4200_al.lock); 208be38866fSTomas Novotny mutex_init(&data->vcnl4200_ps.lock); 2091ebc787aSTomas Novotny 210*5e00708dSGuido Günther ret = data->chip_spec->set_power_state(data, true); 211*5e00708dSGuido Günther if (ret < 0) 212*5e00708dSGuido Günther return ret; 213*5e00708dSGuido Günther 2141ebc787aSTomas Novotny return 0; 2151ebc787aSTomas Novotny }; 2161ebc787aSTomas Novotny 21762a1efb9SPeter Meerwald static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask, 21862a1efb9SPeter Meerwald u8 rdy_mask, u8 data_reg, int *val) 21962a1efb9SPeter Meerwald { 22062a1efb9SPeter Meerwald int tries = 20; 22191f197e0SLars-Peter Clausen __be16 buf; 22262a1efb9SPeter Meerwald int ret; 22362a1efb9SPeter Meerwald 224be38866fSTomas Novotny mutex_lock(&data->vcnl4000_lock); 225ff34ed6dSPeter Meerwald-Stadler 22662a1efb9SPeter Meerwald ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND, 22762a1efb9SPeter Meerwald req_mask); 22862a1efb9SPeter Meerwald if (ret < 0) 229ff34ed6dSPeter Meerwald-Stadler goto fail; 23062a1efb9SPeter Meerwald 23162a1efb9SPeter Meerwald /* wait for data to become ready */ 23262a1efb9SPeter Meerwald while (tries--) { 23362a1efb9SPeter Meerwald ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND); 23462a1efb9SPeter Meerwald if (ret < 0) 235ff34ed6dSPeter Meerwald-Stadler goto fail; 23662a1efb9SPeter Meerwald if (ret & rdy_mask) 23762a1efb9SPeter Meerwald break; 23862a1efb9SPeter Meerwald msleep(20); /* measurement takes up to 100 ms */ 23962a1efb9SPeter Meerwald } 24062a1efb9SPeter Meerwald 24162a1efb9SPeter Meerwald if (tries < 0) { 24262a1efb9SPeter Meerwald dev_err(&data->client->dev, 24362a1efb9SPeter Meerwald "vcnl4000_measure() failed, data not ready\n"); 244ff34ed6dSPeter Meerwald-Stadler ret = -EIO; 245ff34ed6dSPeter Meerwald-Stadler goto fail; 24662a1efb9SPeter Meerwald } 24762a1efb9SPeter Meerwald 24862a1efb9SPeter Meerwald ret = i2c_smbus_read_i2c_block_data(data->client, 24962a1efb9SPeter Meerwald data_reg, sizeof(buf), (u8 *) &buf); 25062a1efb9SPeter Meerwald if (ret < 0) 251ff34ed6dSPeter Meerwald-Stadler goto fail; 25262a1efb9SPeter Meerwald 253be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 25462a1efb9SPeter Meerwald *val = be16_to_cpu(buf); 25562a1efb9SPeter Meerwald 25662a1efb9SPeter Meerwald return 0; 257ff34ed6dSPeter Meerwald-Stadler 258ff34ed6dSPeter Meerwald-Stadler fail: 259be38866fSTomas Novotny mutex_unlock(&data->vcnl4000_lock); 260ff34ed6dSPeter Meerwald-Stadler return ret; 26162a1efb9SPeter Meerwald } 26262a1efb9SPeter Meerwald 263be38866fSTomas Novotny static int vcnl4200_measure(struct vcnl4000_data *data, 264be38866fSTomas Novotny struct vcnl4200_channel *chan, int *val) 265be38866fSTomas Novotny { 266be38866fSTomas Novotny int ret; 267be38866fSTomas Novotny s64 delta; 268be38866fSTomas Novotny ktime_t next_measurement; 269be38866fSTomas Novotny 270be38866fSTomas Novotny mutex_lock(&chan->lock); 271be38866fSTomas Novotny 272be38866fSTomas Novotny next_measurement = ktime_add(chan->last_measurement, 273be38866fSTomas Novotny chan->sampling_rate); 274be38866fSTomas Novotny delta = ktime_us_delta(next_measurement, ktime_get()); 275be38866fSTomas Novotny if (delta > 0) 276be38866fSTomas Novotny usleep_range(delta, delta + 500); 277be38866fSTomas Novotny chan->last_measurement = ktime_get(); 278be38866fSTomas Novotny 279be38866fSTomas Novotny mutex_unlock(&chan->lock); 280be38866fSTomas Novotny 281be38866fSTomas Novotny ret = i2c_smbus_read_word_data(data->client, chan->reg); 282be38866fSTomas Novotny if (ret < 0) 283be38866fSTomas Novotny return ret; 284be38866fSTomas Novotny 285be38866fSTomas Novotny *val = ret; 286be38866fSTomas Novotny 287be38866fSTomas Novotny return 0; 288be38866fSTomas Novotny } 289be38866fSTomas Novotny 2901ebc787aSTomas Novotny static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val) 2911ebc787aSTomas Novotny { 2921ebc787aSTomas Novotny return vcnl4000_measure(data, 2931ebc787aSTomas Novotny VCNL4000_AL_OD, VCNL4000_AL_RDY, 2941ebc787aSTomas Novotny VCNL4000_AL_RESULT_HI, val); 2951ebc787aSTomas Novotny } 2961ebc787aSTomas Novotny 297be38866fSTomas Novotny static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val) 298be38866fSTomas Novotny { 299be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_al, val); 300be38866fSTomas Novotny } 301be38866fSTomas Novotny 3021ebc787aSTomas Novotny static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val) 3031ebc787aSTomas Novotny { 3041ebc787aSTomas Novotny return vcnl4000_measure(data, 3051ebc787aSTomas Novotny VCNL4000_PS_OD, VCNL4000_PS_RDY, 3061ebc787aSTomas Novotny VCNL4000_PS_RESULT_HI, val); 3071ebc787aSTomas Novotny } 3081ebc787aSTomas Novotny 309be38866fSTomas Novotny static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val) 310be38866fSTomas Novotny { 311be38866fSTomas Novotny return vcnl4200_measure(data, &data->vcnl4200_ps, val); 312be38866fSTomas Novotny } 313be38866fSTomas Novotny 3141ebc787aSTomas Novotny static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = { 3151ebc787aSTomas Novotny [VCNL4000] = { 3161ebc787aSTomas Novotny .prod = "VCNL4000", 3171ebc787aSTomas Novotny .init = vcnl4000_init, 3181ebc787aSTomas Novotny .measure_light = vcnl4000_measure_light, 3191ebc787aSTomas Novotny .measure_proximity = vcnl4000_measure_proximity, 320*5e00708dSGuido Günther .set_power_state = vcnl4000_set_power_state, 3211ebc787aSTomas Novotny }, 32250c50b97STomas Novotny [VCNL4010] = { 32350c50b97STomas Novotny .prod = "VCNL4010/4020", 32450c50b97STomas Novotny .init = vcnl4000_init, 32550c50b97STomas Novotny .measure_light = vcnl4000_measure_light, 32650c50b97STomas Novotny .measure_proximity = vcnl4000_measure_proximity, 327*5e00708dSGuido Günther .set_power_state = vcnl4000_set_power_state, 32850c50b97STomas Novotny }, 3295a441aadSAngus Ainslie (Purism) [VCNL4040] = { 3305a441aadSAngus Ainslie (Purism) .prod = "VCNL4040", 3315a441aadSAngus Ainslie (Purism) .init = vcnl4200_init, 3325a441aadSAngus Ainslie (Purism) .measure_light = vcnl4200_measure_light, 3335a441aadSAngus Ainslie (Purism) .measure_proximity = vcnl4200_measure_proximity, 334*5e00708dSGuido Günther .set_power_state = vcnl4200_set_power_state, 3355a441aadSAngus Ainslie (Purism) }, 336be38866fSTomas Novotny [VCNL4200] = { 337be38866fSTomas Novotny .prod = "VCNL4200", 338be38866fSTomas Novotny .init = vcnl4200_init, 339be38866fSTomas Novotny .measure_light = vcnl4200_measure_light, 340be38866fSTomas Novotny .measure_proximity = vcnl4200_measure_proximity, 341*5e00708dSGuido Günther .set_power_state = vcnl4200_set_power_state, 342be38866fSTomas Novotny }, 3431ebc787aSTomas Novotny }; 3441ebc787aSTomas Novotny 34562a1efb9SPeter Meerwald static const struct iio_chan_spec vcnl4000_channels[] = { 34662a1efb9SPeter Meerwald { 34762a1efb9SPeter Meerwald .type = IIO_LIGHT, 348bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | 349bb7c5940SJonathan Cameron BIT(IIO_CHAN_INFO_SCALE), 35062a1efb9SPeter Meerwald }, { 35162a1efb9SPeter Meerwald .type = IIO_PROXIMITY, 352bb7c5940SJonathan Cameron .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), 35362a1efb9SPeter Meerwald } 35462a1efb9SPeter Meerwald }; 35562a1efb9SPeter Meerwald 356*5e00708dSGuido Günther static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) 357*5e00708dSGuido Günther { 358*5e00708dSGuido Günther struct device *dev = &data->client->dev; 359*5e00708dSGuido Günther int ret; 360*5e00708dSGuido Günther 361*5e00708dSGuido Günther if (on) { 362*5e00708dSGuido Günther ret = pm_runtime_get_sync(dev); 363*5e00708dSGuido Günther if (ret < 0) 364*5e00708dSGuido Günther pm_runtime_put_noidle(dev); 365*5e00708dSGuido Günther } else { 366*5e00708dSGuido Günther pm_runtime_mark_last_busy(dev); 367*5e00708dSGuido Günther ret = pm_runtime_put_autosuspend(dev); 368*5e00708dSGuido Günther } 369*5e00708dSGuido Günther 370*5e00708dSGuido Günther return ret; 371*5e00708dSGuido Günther } 372*5e00708dSGuido Günther 37362a1efb9SPeter Meerwald static int vcnl4000_read_raw(struct iio_dev *indio_dev, 37462a1efb9SPeter Meerwald struct iio_chan_spec const *chan, 37562a1efb9SPeter Meerwald int *val, int *val2, long mask) 37662a1efb9SPeter Meerwald { 3775d693139SPeter Meerwald-Stadler int ret; 37862a1efb9SPeter Meerwald struct vcnl4000_data *data = iio_priv(indio_dev); 37962a1efb9SPeter Meerwald 38062a1efb9SPeter Meerwald switch (mask) { 38162a1efb9SPeter Meerwald case IIO_CHAN_INFO_RAW: 382*5e00708dSGuido Günther ret = vcnl4000_set_pm_runtime_state(data, true); 383*5e00708dSGuido Günther if (ret < 0) 384*5e00708dSGuido Günther return ret; 385*5e00708dSGuido Günther 38662a1efb9SPeter Meerwald switch (chan->type) { 38762a1efb9SPeter Meerwald case IIO_LIGHT: 3881ebc787aSTomas Novotny ret = data->chip_spec->measure_light(data, val); 3894a818643SGuido Günther if (!ret) 3904a818643SGuido Günther ret = IIO_VAL_INT; 3914a818643SGuido Günther break; 39262a1efb9SPeter Meerwald case IIO_PROXIMITY: 3931ebc787aSTomas Novotny ret = data->chip_spec->measure_proximity(data, val); 3944a818643SGuido Günther if (!ret) 3954a818643SGuido Günther ret = IIO_VAL_INT; 3964a818643SGuido Günther break; 39762a1efb9SPeter Meerwald default: 3984a818643SGuido Günther ret = -EINVAL; 39962a1efb9SPeter Meerwald } 400*5e00708dSGuido Günther vcnl4000_set_pm_runtime_state(data, false); 4014a818643SGuido Günther return ret; 40262a1efb9SPeter Meerwald case IIO_CHAN_INFO_SCALE: 4035d693139SPeter Meerwald-Stadler if (chan->type != IIO_LIGHT) 4045d693139SPeter Meerwald-Stadler return -EINVAL; 4055d693139SPeter Meerwald-Stadler 40662a1efb9SPeter Meerwald *val = 0; 4071ebc787aSTomas Novotny *val2 = data->al_scale; 4085d693139SPeter Meerwald-Stadler return IIO_VAL_INT_PLUS_MICRO; 40962a1efb9SPeter Meerwald default: 4105d693139SPeter Meerwald-Stadler return -EINVAL; 41162a1efb9SPeter Meerwald } 41262a1efb9SPeter Meerwald } 41362a1efb9SPeter Meerwald 41462a1efb9SPeter Meerwald static const struct iio_info vcnl4000_info = { 41562a1efb9SPeter Meerwald .read_raw = vcnl4000_read_raw, 41662a1efb9SPeter Meerwald }; 41762a1efb9SPeter Meerwald 418fc52692cSGreg Kroah-Hartman static int vcnl4000_probe(struct i2c_client *client, 41962a1efb9SPeter Meerwald const struct i2c_device_id *id) 42062a1efb9SPeter Meerwald { 42162a1efb9SPeter Meerwald struct vcnl4000_data *data; 42262a1efb9SPeter Meerwald struct iio_dev *indio_dev; 4231ebc787aSTomas Novotny int ret; 42462a1efb9SPeter Meerwald 4252669d723SPeter Meerwald indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 42662a1efb9SPeter Meerwald if (!indio_dev) 42762a1efb9SPeter Meerwald return -ENOMEM; 42862a1efb9SPeter Meerwald 42962a1efb9SPeter Meerwald data = iio_priv(indio_dev); 43062a1efb9SPeter Meerwald i2c_set_clientdata(client, indio_dev); 43162a1efb9SPeter Meerwald data->client = client; 4321ebc787aSTomas Novotny data->id = id->driver_data; 4331ebc787aSTomas Novotny data->chip_spec = &vcnl4000_chip_spec_cfg[data->id]; 43462a1efb9SPeter Meerwald 4351ebc787aSTomas Novotny ret = data->chip_spec->init(data); 43662a1efb9SPeter Meerwald if (ret < 0) 4372669d723SPeter Meerwald return ret; 43862a1efb9SPeter Meerwald 439d978bfddSPeter Meerwald-Stadler dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n", 4401ebc787aSTomas Novotny data->chip_spec->prod, data->rev); 44162a1efb9SPeter Meerwald 44262a1efb9SPeter Meerwald indio_dev->dev.parent = &client->dev; 44362a1efb9SPeter Meerwald indio_dev->info = &vcnl4000_info; 44462a1efb9SPeter Meerwald indio_dev->channels = vcnl4000_channels; 44562a1efb9SPeter Meerwald indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels); 44662a1efb9SPeter Meerwald indio_dev->name = VCNL4000_DRV_NAME; 44762a1efb9SPeter Meerwald indio_dev->modes = INDIO_DIRECT_MODE; 44862a1efb9SPeter Meerwald 449*5e00708dSGuido Günther ret = pm_runtime_set_active(&client->dev); 450*5e00708dSGuido Günther if (ret < 0) 451*5e00708dSGuido Günther goto fail_poweroff; 452*5e00708dSGuido Günther 453*5e00708dSGuido Günther ret = iio_device_register(indio_dev); 454*5e00708dSGuido Günther if (ret < 0) 455*5e00708dSGuido Günther goto fail_poweroff; 456*5e00708dSGuido Günther 457*5e00708dSGuido Günther pm_runtime_enable(&client->dev); 458*5e00708dSGuido Günther pm_runtime_set_autosuspend_delay(&client->dev, VCNL4000_SLEEP_DELAY_MS); 459*5e00708dSGuido Günther pm_runtime_use_autosuspend(&client->dev); 460*5e00708dSGuido Günther 461*5e00708dSGuido Günther return 0; 462*5e00708dSGuido Günther fail_poweroff: 463*5e00708dSGuido Günther data->chip_spec->set_power_state(data, false); 464*5e00708dSGuido Günther return ret; 46562a1efb9SPeter Meerwald } 46662a1efb9SPeter Meerwald 467ebd457d5SAngus Ainslie (Purism) static const struct of_device_id vcnl_4000_of_match[] = { 468ebd457d5SAngus Ainslie (Purism) { 469ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4000", 4701436a78cSMarco Felsch .data = (void *)VCNL4000, 471ebd457d5SAngus Ainslie (Purism) }, 472ebd457d5SAngus Ainslie (Purism) { 473ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4010", 4741436a78cSMarco Felsch .data = (void *)VCNL4010, 475ebd457d5SAngus Ainslie (Purism) }, 476ebd457d5SAngus Ainslie (Purism) { 4771436a78cSMarco Felsch .compatible = "vishay,vcnl4020", 4781436a78cSMarco Felsch .data = (void *)VCNL4010, 479ebd457d5SAngus Ainslie (Purism) }, 480ebd457d5SAngus Ainslie (Purism) { 4817fd1c260SMarco Felsch .compatible = "vishay,vcnl4040", 4827fd1c260SMarco Felsch .data = (void *)VCNL4040, 4837fd1c260SMarco Felsch }, 4847fd1c260SMarco Felsch { 485ebd457d5SAngus Ainslie (Purism) .compatible = "vishay,vcnl4200", 4861436a78cSMarco Felsch .data = (void *)VCNL4200, 487ebd457d5SAngus Ainslie (Purism) }, 488ebd457d5SAngus Ainslie (Purism) {}, 489ebd457d5SAngus Ainslie (Purism) }; 490ebd457d5SAngus Ainslie (Purism) MODULE_DEVICE_TABLE(of, vcnl_4000_of_match); 491ebd457d5SAngus Ainslie (Purism) 492*5e00708dSGuido Günther static int vcnl4000_remove(struct i2c_client *client) 493*5e00708dSGuido Günther { 494*5e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(client); 495*5e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 496*5e00708dSGuido Günther 497*5e00708dSGuido Günther pm_runtime_dont_use_autosuspend(&client->dev); 498*5e00708dSGuido Günther pm_runtime_disable(&client->dev); 499*5e00708dSGuido Günther iio_device_unregister(indio_dev); 500*5e00708dSGuido Günther pm_runtime_set_suspended(&client->dev); 501*5e00708dSGuido Günther 502*5e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 503*5e00708dSGuido Günther } 504*5e00708dSGuido Günther 505*5e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_suspend(struct device *dev) 506*5e00708dSGuido Günther { 507*5e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 508*5e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 509*5e00708dSGuido Günther 510*5e00708dSGuido Günther return data->chip_spec->set_power_state(data, false); 511*5e00708dSGuido Günther } 512*5e00708dSGuido Günther 513*5e00708dSGuido Günther static int __maybe_unused vcnl4000_runtime_resume(struct device *dev) 514*5e00708dSGuido Günther { 515*5e00708dSGuido Günther struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); 516*5e00708dSGuido Günther struct vcnl4000_data *data = iio_priv(indio_dev); 517*5e00708dSGuido Günther 518*5e00708dSGuido Günther return data->chip_spec->set_power_state(data, true); 519*5e00708dSGuido Günther } 520*5e00708dSGuido Günther 521*5e00708dSGuido Günther static const struct dev_pm_ops vcnl4000_pm_ops = { 522*5e00708dSGuido Günther SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, 523*5e00708dSGuido Günther pm_runtime_force_resume) 524*5e00708dSGuido Günther SET_RUNTIME_PM_OPS(vcnl4000_runtime_suspend, 525*5e00708dSGuido Günther vcnl4000_runtime_resume, NULL) 526*5e00708dSGuido Günther }; 527*5e00708dSGuido Günther 52862a1efb9SPeter Meerwald static struct i2c_driver vcnl4000_driver = { 52962a1efb9SPeter Meerwald .driver = { 53062a1efb9SPeter Meerwald .name = VCNL4000_DRV_NAME, 531*5e00708dSGuido Günther .pm = &vcnl4000_pm_ops, 532ebd457d5SAngus Ainslie (Purism) .of_match_table = vcnl_4000_of_match, 53362a1efb9SPeter Meerwald }, 53462a1efb9SPeter Meerwald .probe = vcnl4000_probe, 53562a1efb9SPeter Meerwald .id_table = vcnl4000_id, 536*5e00708dSGuido Günther .remove = vcnl4000_remove, 53762a1efb9SPeter Meerwald }; 53862a1efb9SPeter Meerwald 53962a1efb9SPeter Meerwald module_i2c_driver(vcnl4000_driver); 54062a1efb9SPeter Meerwald 54162a1efb9SPeter Meerwald MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>"); 54262a1efb9SPeter Meerwald MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver"); 54362a1efb9SPeter Meerwald MODULE_LICENSE("GPL"); 544