1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Support for Vishay VCNL3020 proximity sensor on i2c bus.
4*4882a593Smuzhiyun * Based on Vishay VCNL4000 driver code.
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * TODO: interrupts.
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <linux/module.h>
10*4882a593Smuzhiyun #include <linux/i2c.h>
11*4882a593Smuzhiyun #include <linux/err.h>
12*4882a593Smuzhiyun #include <linux/delay.h>
13*4882a593Smuzhiyun #include <linux/regmap.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/iio/iio.h>
16*4882a593Smuzhiyun #include <linux/iio/sysfs.h>
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define VCNL3020_PROD_ID 0x21
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun #define VCNL_COMMAND 0x80 /* Command register */
21*4882a593Smuzhiyun #define VCNL_PROD_REV 0x81 /* Product ID and Revision ID */
22*4882a593Smuzhiyun #define VCNL_PROXIMITY_RATE 0x82 /* Rate of Proximity Measurement */
23*4882a593Smuzhiyun #define VCNL_LED_CURRENT 0x83 /* IR LED current for proximity mode */
24*4882a593Smuzhiyun #define VCNL_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
25*4882a593Smuzhiyun #define VCNL_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
26*4882a593Smuzhiyun #define VCNL_PS_ICR 0x89 /* Interrupt Control Register */
27*4882a593Smuzhiyun #define VCNL_PS_LO_THR_HI 0x8a /* High byte of low threshold value */
28*4882a593Smuzhiyun #define VCNL_PS_LO_THR_LO 0x8b /* Low byte of low threshold value */
29*4882a593Smuzhiyun #define VCNL_PS_HI_THR_HI 0x8c /* High byte of high threshold value */
30*4882a593Smuzhiyun #define VCNL_PS_HI_THR_LO 0x8d /* Low byte of high threshold value */
31*4882a593Smuzhiyun #define VCNL_ISR 0x8e /* Interrupt Status Register */
32*4882a593Smuzhiyun #define VCNL_PS_MOD_ADJ 0x8f /* Proximity Modulator Timing Adjustment */
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* Bit masks for COMMAND register */
35*4882a593Smuzhiyun #define VCNL_PS_RDY BIT(5) /* proximity data ready? */
36*4882a593Smuzhiyun #define VCNL_PS_OD BIT(3) /* start on-demand proximity
37*4882a593Smuzhiyun * measurement
38*4882a593Smuzhiyun */
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #define VCNL_ON_DEMAND_TIMEOUT_US 100000
41*4882a593Smuzhiyun #define VCNL_POLL_US 20000
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /**
44*4882a593Smuzhiyun * struct vcnl3020_data - vcnl3020 specific data.
45*4882a593Smuzhiyun * @regmap: device register map.
46*4882a593Smuzhiyun * @dev: vcnl3020 device.
47*4882a593Smuzhiyun * @rev: revision id.
48*4882a593Smuzhiyun * @lock: lock for protecting access to device hardware registers.
49*4882a593Smuzhiyun */
50*4882a593Smuzhiyun struct vcnl3020_data {
51*4882a593Smuzhiyun struct regmap *regmap;
52*4882a593Smuzhiyun struct device *dev;
53*4882a593Smuzhiyun u8 rev;
54*4882a593Smuzhiyun struct mutex lock;
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun /**
58*4882a593Smuzhiyun * struct vcnl3020_property - vcnl3020 property.
59*4882a593Smuzhiyun * @name: property name.
60*4882a593Smuzhiyun * @reg: i2c register offset.
61*4882a593Smuzhiyun * @conversion_func: conversion function.
62*4882a593Smuzhiyun */
63*4882a593Smuzhiyun struct vcnl3020_property {
64*4882a593Smuzhiyun const char *name;
65*4882a593Smuzhiyun u32 reg;
66*4882a593Smuzhiyun u32 (*conversion_func)(u32 *val);
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun
microamp_to_reg(u32 * val)69*4882a593Smuzhiyun static u32 microamp_to_reg(u32 *val)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun /*
72*4882a593Smuzhiyun * An example of conversion from uA to reg val:
73*4882a593Smuzhiyun * 200000 uA == 200 mA == 20
74*4882a593Smuzhiyun */
75*4882a593Smuzhiyun return *val /= 10000;
76*4882a593Smuzhiyun };
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun static struct vcnl3020_property vcnl3020_led_current_property = {
79*4882a593Smuzhiyun .name = "vishay,led-current-microamp",
80*4882a593Smuzhiyun .reg = VCNL_LED_CURRENT,
81*4882a593Smuzhiyun .conversion_func = microamp_to_reg,
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun
vcnl3020_get_and_apply_property(struct vcnl3020_data * data,struct vcnl3020_property prop)84*4882a593Smuzhiyun static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
85*4882a593Smuzhiyun struct vcnl3020_property prop)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun int rc;
88*4882a593Smuzhiyun u32 val;
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun rc = device_property_read_u32(data->dev, prop.name, &val);
91*4882a593Smuzhiyun if (rc)
92*4882a593Smuzhiyun return 0;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun if (prop.conversion_func)
95*4882a593Smuzhiyun prop.conversion_func(&val);
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun rc = regmap_write(data->regmap, prop.reg, val);
98*4882a593Smuzhiyun if (rc) {
99*4882a593Smuzhiyun dev_err(data->dev, "Error (%d) setting property (%s)\n",
100*4882a593Smuzhiyun rc, prop.name);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return rc;
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
vcnl3020_init(struct vcnl3020_data * data)106*4882a593Smuzhiyun static int vcnl3020_init(struct vcnl3020_data *data)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun int rc;
109*4882a593Smuzhiyun unsigned int reg;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun rc = regmap_read(data->regmap, VCNL_PROD_REV, ®);
112*4882a593Smuzhiyun if (rc) {
113*4882a593Smuzhiyun dev_err(data->dev,
114*4882a593Smuzhiyun "Error (%d) reading product revision\n", rc);
115*4882a593Smuzhiyun return rc;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (reg != VCNL3020_PROD_ID) {
119*4882a593Smuzhiyun dev_err(data->dev,
120*4882a593Smuzhiyun "Product id (%x) did not match vcnl3020 (%x)\n", reg,
121*4882a593Smuzhiyun VCNL3020_PROD_ID);
122*4882a593Smuzhiyun return -ENODEV;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun data->rev = reg;
126*4882a593Smuzhiyun mutex_init(&data->lock);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun return vcnl3020_get_and_apply_property(data,
129*4882a593Smuzhiyun vcnl3020_led_current_property);
130*4882a593Smuzhiyun };
131*4882a593Smuzhiyun
vcnl3020_measure_proximity(struct vcnl3020_data * data,int * val)132*4882a593Smuzhiyun static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun int rc;
135*4882a593Smuzhiyun unsigned int reg;
136*4882a593Smuzhiyun __be16 res;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun mutex_lock(&data->lock);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
141*4882a593Smuzhiyun if (rc)
142*4882a593Smuzhiyun goto err_unlock;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun /* wait for data to become ready */
145*4882a593Smuzhiyun rc = regmap_read_poll_timeout(data->regmap, VCNL_COMMAND, reg,
146*4882a593Smuzhiyun reg & VCNL_PS_RDY, VCNL_POLL_US,
147*4882a593Smuzhiyun VCNL_ON_DEMAND_TIMEOUT_US);
148*4882a593Smuzhiyun if (rc) {
149*4882a593Smuzhiyun dev_err(data->dev,
150*4882a593Smuzhiyun "Error (%d) reading vcnl3020 command register\n", rc);
151*4882a593Smuzhiyun goto err_unlock;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun /* high & low result bytes read */
155*4882a593Smuzhiyun rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &res,
156*4882a593Smuzhiyun sizeof(res));
157*4882a593Smuzhiyun if (rc)
158*4882a593Smuzhiyun goto err_unlock;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun *val = be16_to_cpu(res);
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun err_unlock:
163*4882a593Smuzhiyun mutex_unlock(&data->lock);
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun return rc;
166*4882a593Smuzhiyun }
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun static const struct iio_chan_spec vcnl3020_channels[] = {
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun .type = IIO_PROXIMITY,
171*4882a593Smuzhiyun .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
172*4882a593Smuzhiyun },
173*4882a593Smuzhiyun };
174*4882a593Smuzhiyun
vcnl3020_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)175*4882a593Smuzhiyun static int vcnl3020_read_raw(struct iio_dev *indio_dev,
176*4882a593Smuzhiyun struct iio_chan_spec const *chan, int *val,
177*4882a593Smuzhiyun int *val2, long mask)
178*4882a593Smuzhiyun {
179*4882a593Smuzhiyun int rc;
180*4882a593Smuzhiyun struct vcnl3020_data *data = iio_priv(indio_dev);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun switch (mask) {
183*4882a593Smuzhiyun case IIO_CHAN_INFO_RAW:
184*4882a593Smuzhiyun rc = vcnl3020_measure_proximity(data, val);
185*4882a593Smuzhiyun if (rc)
186*4882a593Smuzhiyun return rc;
187*4882a593Smuzhiyun return IIO_VAL_INT;
188*4882a593Smuzhiyun default:
189*4882a593Smuzhiyun return -EINVAL;
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun static const struct iio_info vcnl3020_info = {
194*4882a593Smuzhiyun .read_raw = vcnl3020_read_raw,
195*4882a593Smuzhiyun };
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun static const struct regmap_config vcnl3020_regmap_config = {
198*4882a593Smuzhiyun .reg_bits = 8,
199*4882a593Smuzhiyun .val_bits = 8,
200*4882a593Smuzhiyun .max_register = VCNL_PS_MOD_ADJ,
201*4882a593Smuzhiyun };
202*4882a593Smuzhiyun
vcnl3020_probe(struct i2c_client * client)203*4882a593Smuzhiyun static int vcnl3020_probe(struct i2c_client *client)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun struct vcnl3020_data *data;
206*4882a593Smuzhiyun struct iio_dev *indio_dev;
207*4882a593Smuzhiyun struct regmap *regmap;
208*4882a593Smuzhiyun int rc;
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun regmap = devm_regmap_init_i2c(client, &vcnl3020_regmap_config);
211*4882a593Smuzhiyun if (IS_ERR(regmap)) {
212*4882a593Smuzhiyun dev_err(&client->dev, "regmap_init failed\n");
213*4882a593Smuzhiyun return PTR_ERR(regmap);
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
217*4882a593Smuzhiyun if (!indio_dev)
218*4882a593Smuzhiyun return -ENOMEM;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun data = iio_priv(indio_dev);
221*4882a593Smuzhiyun i2c_set_clientdata(client, indio_dev);
222*4882a593Smuzhiyun data->regmap = regmap;
223*4882a593Smuzhiyun data->dev = &client->dev;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun rc = vcnl3020_init(data);
226*4882a593Smuzhiyun if (rc)
227*4882a593Smuzhiyun return rc;
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun indio_dev->info = &vcnl3020_info;
230*4882a593Smuzhiyun indio_dev->channels = vcnl3020_channels;
231*4882a593Smuzhiyun indio_dev->num_channels = ARRAY_SIZE(vcnl3020_channels);
232*4882a593Smuzhiyun indio_dev->name = "vcnl3020";
233*4882a593Smuzhiyun indio_dev->modes = INDIO_DIRECT_MODE;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun return devm_iio_device_register(&client->dev, indio_dev);
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun static const struct of_device_id vcnl3020_of_match[] = {
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun .compatible = "vishay,vcnl3020",
241*4882a593Smuzhiyun },
242*4882a593Smuzhiyun {}
243*4882a593Smuzhiyun };
244*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun static struct i2c_driver vcnl3020_driver = {
247*4882a593Smuzhiyun .driver = {
248*4882a593Smuzhiyun .name = "vcnl3020",
249*4882a593Smuzhiyun .of_match_table = vcnl3020_of_match,
250*4882a593Smuzhiyun },
251*4882a593Smuzhiyun .probe_new = vcnl3020_probe,
252*4882a593Smuzhiyun };
253*4882a593Smuzhiyun module_i2c_driver(vcnl3020_driver);
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
256*4882a593Smuzhiyun MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
257*4882a593Smuzhiyun MODULE_LICENSE("GPL");
258