xref: /openbmc/linux/drivers/iio/proximity/vcnl3020.c (revision 35267cea)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Support for Vishay VCNL3020 proximity sensor on i2c bus.
4  * Based on Vishay VCNL4000 driver code.
5  *
6  * TODO: interrupts.
7  */
8 
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>
14 
15 #include <linux/iio/iio.h>
16 #include <linux/iio/sysfs.h>
17 
18 #define VCNL3020_PROD_ID	0x21
19 
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 */
33 
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
37 					* measurement
38 					*/
39 
40 #define VCNL_ON_DEMAND_TIMEOUT_US	100000
41 #define VCNL_POLL_US			20000
42 
43 static const int vcnl3020_prox_sampling_frequency[][2] = {
44 	{1, 950000},
45 	{3, 906250},
46 	{7, 812500},
47 	{16, 625000},
48 	{31, 250000},
49 	{62, 500000},
50 	{125, 0},
51 	{250, 0},
52 };
53 
54 /**
55  * struct vcnl3020_data - vcnl3020 specific data.
56  * @regmap:	device register map.
57  * @dev:	vcnl3020 device.
58  * @rev:	revision id.
59  * @lock:	lock for protecting access to device hardware registers.
60  */
61 struct vcnl3020_data {
62 	struct regmap *regmap;
63 	struct device *dev;
64 	u8 rev;
65 	struct mutex lock;
66 };
67 
68 /**
69  * struct vcnl3020_property - vcnl3020 property.
70  * @name:	property name.
71  * @reg:	i2c register offset.
72  * @conversion_func:	conversion function.
73  */
74 struct vcnl3020_property {
75 	const char *name;
76 	u32 reg;
77 	u32 (*conversion_func)(u32 *val);
78 };
79 
80 static u32 microamp_to_reg(u32 *val)
81 {
82 	/*
83 	 * An example of conversion from uA to reg val:
84 	 * 200000 uA == 200 mA == 20
85 	 */
86 	return *val /= 10000;
87 };
88 
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,
93 };
94 
95 static int vcnl3020_get_and_apply_property(struct vcnl3020_data *data,
96 					   struct vcnl3020_property prop)
97 {
98 	int rc;
99 	u32 val;
100 
101 	rc = device_property_read_u32(data->dev, prop.name, &val);
102 	if (rc)
103 		return 0;
104 
105 	if (prop.conversion_func)
106 		prop.conversion_func(&val);
107 
108 	rc = regmap_write(data->regmap, prop.reg, val);
109 	if (rc) {
110 		dev_err(data->dev, "Error (%d) setting property (%s)\n",
111 			rc, prop.name);
112 	}
113 
114 	return rc;
115 }
116 
117 static int vcnl3020_init(struct vcnl3020_data *data)
118 {
119 	int rc;
120 	unsigned int reg;
121 
122 	rc = regmap_read(data->regmap, VCNL_PROD_REV, &reg);
123 	if (rc) {
124 		dev_err(data->dev,
125 			"Error (%d) reading product revision\n", rc);
126 		return rc;
127 	}
128 
129 	if (reg != VCNL3020_PROD_ID) {
130 		dev_err(data->dev,
131 			"Product id (%x) did not match vcnl3020 (%x)\n", reg,
132 			VCNL3020_PROD_ID);
133 		return -ENODEV;
134 	}
135 
136 	data->rev = reg;
137 	mutex_init(&data->lock);
138 
139 	return vcnl3020_get_and_apply_property(data,
140 					       vcnl3020_led_current_property);
141 };
142 
143 static int vcnl3020_measure_proximity(struct vcnl3020_data *data, int *val)
144 {
145 	int rc;
146 	unsigned int reg;
147 	__be16 res;
148 
149 	mutex_lock(&data->lock);
150 
151 	rc = regmap_write(data->regmap, VCNL_COMMAND, VCNL_PS_OD);
152 	if (rc)
153 		goto err_unlock;
154 
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);
159 	if (rc) {
160 		dev_err(data->dev,
161 			"Error (%d) reading vcnl3020 command register\n", rc);
162 		goto err_unlock;
163 	}
164 
165 	/* high & low result bytes read */
166 	rc = regmap_bulk_read(data->regmap, VCNL_PS_RESULT_HI, &res,
167 			      sizeof(res));
168 	if (rc)
169 		goto err_unlock;
170 
171 	*val = be16_to_cpu(res);
172 
173 err_unlock:
174 	mutex_unlock(&data->lock);
175 
176 	return rc;
177 }
178 
179 static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val,
180 					 int *val2)
181 {
182 	int rc;
183 	unsigned int prox_rate;
184 
185 	rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate);
186 	if (rc)
187 		return rc;
188 
189 	if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency))
190 		return -EINVAL;
191 
192 	*val = vcnl3020_prox_sampling_frequency[prox_rate][0];
193 	*val2 = vcnl3020_prox_sampling_frequency[prox_rate][1];
194 
195 	return 0;
196 }
197 
198 static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val,
199 					  int val2)
200 {
201 	unsigned int i;
202 	int index = -1;
203 
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]) {
207 			index = i;
208 			break;
209 		}
210 	}
211 
212 	if (index < 0)
213 		return -EINVAL;
214 
215 	return regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index);
216 }
217 
218 static const struct iio_chan_spec vcnl3020_channels[] = {
219 	{
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),
224 	},
225 };
226 
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)
230 {
231 	int rc;
232 	struct vcnl3020_data *data = iio_priv(indio_dev);
233 
234 	switch (mask) {
235 	case IIO_CHAN_INFO_RAW:
236 		rc = vcnl3020_measure_proximity(data, val);
237 		if (rc)
238 			return rc;
239 		return IIO_VAL_INT;
240 	case IIO_CHAN_INFO_SAMP_FREQ:
241 		rc = vcnl3020_read_proxy_samp_freq(data, val, val2);
242 		if (rc < 0)
243 			return rc;
244 		return IIO_VAL_INT_PLUS_MICRO;
245 	default:
246 		return -EINVAL;
247 	}
248 }
249 
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)
253 {
254 	int rc;
255 	struct vcnl3020_data *data = iio_priv(indio_dev);
256 
257 	switch (mask) {
258 	case IIO_CHAN_INFO_SAMP_FREQ:
259 		rc = iio_device_claim_direct_mode(indio_dev);
260 		if (rc)
261 			return rc;
262 		rc = vcnl3020_write_proxy_samp_freq(data, val, val2);
263 		iio_device_release_direct_mode(indio_dev);
264 		return rc;
265 	default:
266 		return -EINVAL;
267 	}
268 }
269 
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,
273 			       long mask)
274 {
275 	switch (mask) {
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;
281 	default:
282 		return -EINVAL;
283 	}
284 }
285 
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,
290 };
291 
292 static const struct regmap_config vcnl3020_regmap_config = {
293 	.reg_bits	= 8,
294 	.val_bits	= 8,
295 	.max_register	= VCNL_PS_MOD_ADJ,
296 };
297 
298 static int vcnl3020_probe(struct i2c_client *client)
299 {
300 	struct vcnl3020_data *data;
301 	struct iio_dev *indio_dev;
302 	struct regmap *regmap;
303 	int rc;
304 
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);
309 	}
310 
311 	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
312 	if (!indio_dev)
313 		return -ENOMEM;
314 
315 	data = iio_priv(indio_dev);
316 	i2c_set_clientdata(client, indio_dev);
317 	data->regmap = regmap;
318 	data->dev = &client->dev;
319 
320 	rc = vcnl3020_init(data);
321 	if (rc)
322 		return rc;
323 
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;
329 
330 	return devm_iio_device_register(&client->dev, indio_dev);
331 }
332 
333 static const struct of_device_id vcnl3020_of_match[] = {
334 	{
335 		.compatible = "vishay,vcnl3020",
336 	},
337 	{}
338 };
339 MODULE_DEVICE_TABLE(of, vcnl3020_of_match);
340 
341 static struct i2c_driver vcnl3020_driver = {
342 	.driver = {
343 		.name   = "vcnl3020",
344 		.of_match_table = vcnl3020_of_match,
345 	},
346 	.probe_new  = vcnl3020_probe,
347 };
348 module_i2c_driver(vcnl3020_driver);
349 
350 MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
351 MODULE_DESCRIPTION("Vishay VCNL3020 proximity sensor driver");
352 MODULE_LICENSE("GPL");
353