1 // SPDX-License-Identifier: GPL-2.0-only
3 * Support for Vishay VCNL3020 proximity sensor on i2c bus.
4 * Based on Vishay VCNL4000 driver code.
9 #include <linux/module.h>
10 #include <linux/i2c.h>
11 #include <linux/err.h>
12 #include <linux/delay.h>
13 #include <linux/regmap.h>
15 #include <linux/iio/iio.h>
16 #include <linux/iio/sysfs.h>
18 #define VCNL3020_PROD_ID 0x21
20 #define VCNL_COMMAND 0x80 /* Command register */
21 #define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */
22 #define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */
23 #define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */
24 #define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
25 #define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
26 #define VCNL_PS_ICR 0x89 /* Interrupt Control Register */
27 #define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */
28 #define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */
29 #define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */
30 #define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */
31 #define VCNL_ISR 0x8e /* Interrupt Status Register */
32 #define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */
34 /* Bit masks for COMMAND register */
35 #define VCNL_PS_RDY BIT(5) /* proximity data ready? */
36 #define VCNL_PS_OD BIT(3) /* start on-demand proximity
40 #define VCNL_ON_DEMAND_TIMEOUT_US 100000
41 #define VCNL_POLL_US 20000
43 static const int vcnl3020_prox_sampling_frequency[][2] = {
55 * struct vcnl3020_data - vcnl3020 specific data.
56 * @regmap: device register map.
57 * @dev: vcnl3020 device.
59 * @lock: lock for protecting access to device hardware registers.
61 struct vcnl3020_data {
62 struct regmap *regmap;
69 * struct vcnl3020_property - vcnl3020 property.
70 * @name: property name.
71 * @reg: i2c register offset.
72 * @conversion_func: conversion function.
74 struct vcnl3020_property {
77 u32 (*conversion_func)(u32 *val);
80 static u32 microamp_to_reg(u32 *val)
83 * An example of conversion from uA to reg val:
84 * 200000 uA == 200 mA == 20
89 static struct vcnl3020_property vcnl3020_led_current_property = {
90 .name = "vishay,led-current-microamp",
91 .reg = VCNL_LED_CURRENT,
92 .conversion_func = microamp_to_reg,
95 static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
96 struct vcnl3020_property prop)
101 rc = device_property_read_u32(data->dev, prop.name, &val);
105 if (prop.conversion_func)
106 prop.conversion_func(&val);
108 rc = regmap_write(data->regmap, prop.reg, val);
110 dev_err(data->dev, "Error (%d) setting property (%s)\n",
117 static int vcnl3020_init(struct vcnl3020_data *data)
122 rc = regmap_read(data->regmap, VCNL_PROD_REV, ®);
125 "Error (%d) reading product revision\n", rc);
129 if (reg != VCNL3020_PROD_ID) {
131 "Product id (%x) did not match vcnl3020 (%x)\n", reg,
137 mutex_init(&data->lock);
139 return vcnl3020_get_and_apply_property(data,
140 vcnl3020_led_current_property);
143 static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
149 mutex_lock(&data->lock);
151 rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
155 /* wait for data to become ready */
156 rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg,
157 reg & VCNL_PS_RDY, VCNL_POLL_US,
158 VCNL_ON_DEMAND_TIMEOUT_US);
161 "Error (%d) reading vcnl3020 command register\n", rc);
165 /* high & low result bytes read */
166 rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &res,
171 *val = be16_to_cpu(res);
174 mutex_unlock(&data->lock);
179 static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val,
183 unsigned int prox_rate;
185 rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate);
189 if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency))
192 *val = vcnl3020_prox_sampling_frequency[prox_rate][0];
193 *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1];
198 static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val,
204 for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) {
205 if (val == vcnl3020_prox_sampling_frequency[i][0] &&
206 val2 == vcnl3020_prox_sampling_frequency[i][1]) {
215 return regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index);
218 static const struct iio_chan_spec vcnl3020_channels[] = {
220 .type = IIO_PROXIMITY,
221 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
222 BIT(IIO_CHAN_INFO_SAMP_FREQ),
223 .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
227 static int vcnl3020_read_raw(struct iio_dev *indio_dev,
228 struct iio_chan_spec const *chan, int *val,
229 int *val2, long mask)
232 struct vcnl3020_data *data = iio_priv(indio_dev);
235 case IIO_CHAN_INFO_RAW:
236 rc = vcnl3020_measure_proximity(data, val);
240 case IIO_CHAN_INFO_SAMP_FREQ:
241 rc = vcnl3020_read_proxy_samp_freq(data, val, val2);
244 return IIO_VAL_INT_PLUS_MICRO;
250 static int vcnl3020_write_raw(struct iio_dev *indio_dev,
251 struct iio_chan_spec const *chan,
252 int val, int val2, long mask)
255 struct vcnl3020_data *data = iio_priv(indio_dev);
258 case IIO_CHAN_INFO_SAMP_FREQ:
259 rc = iio_device_claim_direct_mode(indio_dev);
262 rc = vcnl3020_write_proxy_samp_freq(data, val, val2);
263 iio_device_release_direct_mode(indio_dev);
270 static int vcnl3020_read_avail(struct iio_dev *indio_dev,
271 struct iio_chan_spec const *chan,
272 const int **vals, int *type, int *length,
276 case IIO_CHAN_INFO_SAMP_FREQ:
277 *vals = (int *)vcnl3020_prox_sampling_frequency;
278 *type = IIO_VAL_INT_PLUS_MICRO;
279 *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency);
280 return IIO_AVAIL_LIST;
286 static const struct iio_info vcnl3020_info = {
287 .read_raw = vcnl3020_read_raw,
288 .write_raw = vcnl3020_write_raw,
289 .read_avail = vcnl3020_read_avail,
292 static const struct regmap_config vcnl3020_regmap_config = {
295 .max_register = VCNL_PS_MOD_ADJ,
298 static int vcnl3020_probe(struct i2c_client *client)
300 struct vcnl3020_data *data;
301 struct iio_dev *indio_dev;
302 struct regmap *regmap;
305 regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config);
306 if (IS_ERR(regmap)) {
307 dev_err(&client->dev, "regmap_init failed\n");
308 return PTR_ERR(regmap);
311 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
315 data = iio_priv(indio_dev);
316 i2c_set_clientdata(client, indio_dev);
317 data->regmap = regmap;
318 data->dev = &client->dev;
320 rc = vcnl3020_init(data);
324 indio_dev->info = &vcnl3020_info;
325 indio_dev->channels = vcnl3020_channels;
326 indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels);
327 indio_dev->name = "vcnl3020";
328 indio_dev->modes = INDIO_DIRECT_MODE;
330 return devm_iio_device_register(&client->dev, indio_dev);
333 static const struct of_device_id vcnl3020_of_match[] = {
335 .compatible = "vishay,vcnl3020",
339 MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
341 static struct i2c_driver vcnl3020_driver = {
344 .of_match_table = vcnl3020_of_match,
346 .probe_new = vcnl3020_probe,
348 module_i2c_driver(vcnl3020_driver);
351 MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
352 MODULE_LICENSE("GPL");