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, ®); 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