vcnl4000.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient
  3. * light and proximity sensor
  4. *
  5. * Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
  6. *
  7. * This file is subject to the terms and conditions of version 2 of
  8. * the GNU General Public License. See the file COPYING in the main
  9. * directory of this archive for more details.
  10. *
  11. * IIO driver for:
  12. * VCNL4000/10/20 (7-bit I2C slave address 0x13)
  13. * VCNL4200 (7-bit I2C slave address 0x51)
  14. *
  15. * TODO:
  16. * allow to adjust IR current
  17. * proximity threshold and event handling
  18. * periodic ALS/proximity measurement (VCNL4010/20)
  19. * interrupts (VCNL4010/20, VCNL4200)
  20. */
  21. #include <linux/module.h>
  22. #include <linux/i2c.h>
  23. #include <linux/err.h>
  24. #include <linux/delay.h>
  25. #include <linux/iio/iio.h>
  26. #include <linux/iio/sysfs.h>
  27. #define VCNL4000_DRV_NAME "vcnl4000"
  28. #define VCNL4000_PROD_ID 0x01
  29. #define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
  30. #define VCNL4200_PROD_ID 0x58
  31. #define VCNL4000_COMMAND 0x80 /* Command register */
  32. #define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
  33. #define VCNL4000_LED_CURRENT 0x83 /* IR LED current for proximity mode */
  34. #define VCNL4000_AL_PARAM 0x84 /* Ambient light parameter register */
  35. #define VCNL4000_AL_RESULT_HI 0x85 /* Ambient light result register, MSB */
  36. #define VCNL4000_AL_RESULT_LO 0x86 /* Ambient light result register, LSB */
  37. #define VCNL4000_PS_RESULT_HI 0x87 /* Proximity result register, MSB */
  38. #define VCNL4000_PS_RESULT_LO 0x88 /* Proximity result register, LSB */
  39. #define VCNL4000_PS_MEAS_FREQ 0x89 /* Proximity test signal frequency */
  40. #define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
  41. #define VCNL4200_AL_CONF 0x00 /* Ambient light configuration */
  42. #define VCNL4200_PS_CONF1 0x03 /* Proximity configuration */
  43. #define VCNL4200_PS_DATA 0x08 /* Proximity data */
  44. #define VCNL4200_AL_DATA 0x09 /* Ambient light data */
  45. #define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
  46. /* Bit masks for COMMAND register */
  47. #define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
  48. #define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
  49. #define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
  50. #define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
  51. enum vcnl4000_device_ids {
  52. VCNL4000,
  53. VCNL4010,
  54. VCNL4200,
  55. };
  56. struct vcnl4200_channel {
  57. u8 reg;
  58. ktime_t last_measurement;
  59. ktime_t sampling_rate;
  60. struct mutex lock;
  61. };
  62. struct vcnl4000_data {
  63. struct i2c_client *client;
  64. enum vcnl4000_device_ids id;
  65. int rev;
  66. int al_scale;
  67. const struct vcnl4000_chip_spec *chip_spec;
  68. struct mutex vcnl4000_lock;
  69. struct vcnl4200_channel vcnl4200_al;
  70. struct vcnl4200_channel vcnl4200_ps;
  71. };
  72. struct vcnl4000_chip_spec {
  73. const char *prod;
  74. int (*init)(struct vcnl4000_data *data);
  75. int (*measure_light)(struct vcnl4000_data *data, int *val);
  76. int (*measure_proximity)(struct vcnl4000_data *data, int *val);
  77. };
  78. static const struct i2c_device_id vcnl4000_id[] = {
  79. { "vcnl4000", VCNL4000 },
  80. { "vcnl4010", VCNL4010 },
  81. { "vcnl4020", VCNL4010 },
  82. { "vcnl4200", VCNL4200 },
  83. { }
  84. };
  85. MODULE_DEVICE_TABLE(i2c, vcnl4000_id);
  86. static int vcnl4000_init(struct vcnl4000_data *data)
  87. {
  88. int ret, prod_id;
  89. ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
  90. if (ret < 0)
  91. return ret;
  92. prod_id = ret >> 4;
  93. switch (prod_id) {
  94. case VCNL4000_PROD_ID:
  95. if (data->id != VCNL4000)
  96. dev_warn(&data->client->dev,
  97. "wrong device id, use vcnl4000");
  98. break;
  99. case VCNL4010_PROD_ID:
  100. if (data->id != VCNL4010)
  101. dev_warn(&data->client->dev,
  102. "wrong device id, use vcnl4010/4020");
  103. break;
  104. default:
  105. return -ENODEV;
  106. }
  107. data->rev = ret & 0xf;
  108. data->al_scale = 250000;
  109. mutex_init(&data->vcnl4000_lock);
  110. return 0;
  111. };
  112. static int vcnl4200_init(struct vcnl4000_data *data)
  113. {
  114. int ret;
  115. ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
  116. if (ret < 0)
  117. return ret;
  118. if ((ret & 0xff) != VCNL4200_PROD_ID)
  119. return -ENODEV;
  120. data->rev = (ret >> 8) & 0xf;
  121. /* Set defaults and enable both channels */
  122. ret = i2c_smbus_write_byte_data(data->client, VCNL4200_AL_CONF, 0x00);
  123. if (ret < 0)
  124. return ret;
  125. ret = i2c_smbus_write_byte_data(data->client, VCNL4200_PS_CONF1, 0x00);
  126. if (ret < 0)
  127. return ret;
  128. data->al_scale = 24000;
  129. data->vcnl4200_al.reg = VCNL4200_AL_DATA;
  130. data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
  131. /* Default wait time is 50ms, add 20% tolerance. */
  132. data->vcnl4200_al.sampling_rate = ktime_set(0, 60000 * 1000);
  133. /* Default wait time is 4.8ms, add 20% tolerance. */
  134. data->vcnl4200_ps.sampling_rate = ktime_set(0, 5760 * 1000);
  135. data->vcnl4200_al.last_measurement = ktime_set(0, 0);
  136. data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
  137. mutex_init(&data->vcnl4200_al.lock);
  138. mutex_init(&data->vcnl4200_ps.lock);
  139. return 0;
  140. };
  141. static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
  142. u8 rdy_mask, u8 data_reg, int *val)
  143. {
  144. int tries = 20;
  145. __be16 buf;
  146. int ret;
  147. mutex_lock(&data->vcnl4000_lock);
  148. ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
  149. req_mask);
  150. if (ret < 0)
  151. goto fail;
  152. /* wait for data to become ready */
  153. while (tries--) {
  154. ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
  155. if (ret < 0)
  156. goto fail;
  157. if (ret & rdy_mask)
  158. break;
  159. msleep(20); /* measurement takes up to 100 ms */
  160. }
  161. if (tries < 0) {
  162. dev_err(&data->client->dev,
  163. "vcnl4000_measure() failed, data not ready\n");
  164. ret = -EIO;
  165. goto fail;
  166. }
  167. ret = i2c_smbus_read_i2c_block_data(data->client,
  168. data_reg, sizeof(buf), (u8 *) &buf);
  169. if (ret < 0)
  170. goto fail;
  171. mutex_unlock(&data->vcnl4000_lock);
  172. *val = be16_to_cpu(buf);
  173. return 0;
  174. fail:
  175. mutex_unlock(&data->vcnl4000_lock);
  176. return ret;
  177. }
  178. static int vcnl4200_measure(struct vcnl4000_data *data,
  179. struct vcnl4200_channel *chan, int *val)
  180. {
  181. int ret;
  182. s64 delta;
  183. ktime_t next_measurement;
  184. mutex_lock(&chan->lock);
  185. next_measurement = ktime_add(chan->last_measurement,
  186. chan->sampling_rate);
  187. delta = ktime_us_delta(next_measurement, ktime_get());
  188. if (delta > 0)
  189. usleep_range(delta, delta + 500);
  190. chan->last_measurement = ktime_get();
  191. mutex_unlock(&chan->lock);
  192. ret = i2c_smbus_read_word_data(data->client, chan->reg);
  193. if (ret < 0)
  194. return ret;
  195. *val = ret;
  196. return 0;
  197. }
  198. static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
  199. {
  200. return vcnl4000_measure(data,
  201. VCNL4000_AL_OD, VCNL4000_AL_RDY,
  202. VCNL4000_AL_RESULT_HI, val);
  203. }
  204. static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
  205. {
  206. return vcnl4200_measure(data, &data->vcnl4200_al, val);
  207. }
  208. static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
  209. {
  210. return vcnl4000_measure(data,
  211. VCNL4000_PS_OD, VCNL4000_PS_RDY,
  212. VCNL4000_PS_RESULT_HI, val);
  213. }
  214. static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
  215. {
  216. return vcnl4200_measure(data, &data->vcnl4200_ps, val);
  217. }
  218. static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
  219. [VCNL4000] = {
  220. .prod = "VCNL4000",
  221. .init = vcnl4000_init,
  222. .measure_light = vcnl4000_measure_light,
  223. .measure_proximity = vcnl4000_measure_proximity,
  224. },
  225. [VCNL4010] = {
  226. .prod = "VCNL4010/4020",
  227. .init = vcnl4000_init,
  228. .measure_light = vcnl4000_measure_light,
  229. .measure_proximity = vcnl4000_measure_proximity,
  230. },
  231. [VCNL4200] = {
  232. .prod = "VCNL4200",
  233. .init = vcnl4200_init,
  234. .measure_light = vcnl4200_measure_light,
  235. .measure_proximity = vcnl4200_measure_proximity,
  236. },
  237. };
  238. static const struct iio_chan_spec vcnl4000_channels[] = {
  239. {
  240. .type = IIO_LIGHT,
  241. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  242. BIT(IIO_CHAN_INFO_SCALE),
  243. }, {
  244. .type = IIO_PROXIMITY,
  245. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  246. }
  247. };
  248. static int vcnl4000_read_raw(struct iio_dev *indio_dev,
  249. struct iio_chan_spec const *chan,
  250. int *val, int *val2, long mask)
  251. {
  252. int ret;
  253. struct vcnl4000_data *data = iio_priv(indio_dev);
  254. switch (mask) {
  255. case IIO_CHAN_INFO_RAW:
  256. switch (chan->type) {
  257. case IIO_LIGHT:
  258. ret = data->chip_spec->measure_light(data, val);
  259. if (ret < 0)
  260. return ret;
  261. return IIO_VAL_INT;
  262. case IIO_PROXIMITY:
  263. ret = data->chip_spec->measure_proximity(data, val);
  264. if (ret < 0)
  265. return ret;
  266. return IIO_VAL_INT;
  267. default:
  268. return -EINVAL;
  269. }
  270. case IIO_CHAN_INFO_SCALE:
  271. if (chan->type != IIO_LIGHT)
  272. return -EINVAL;
  273. *val = 0;
  274. *val2 = data->al_scale;
  275. return IIO_VAL_INT_PLUS_MICRO;
  276. default:
  277. return -EINVAL;
  278. }
  279. }
  280. static const struct iio_info vcnl4000_info = {
  281. .read_raw = vcnl4000_read_raw,
  282. };
  283. static int vcnl4000_probe(struct i2c_client *client,
  284. const struct i2c_device_id *id)
  285. {
  286. struct vcnl4000_data *data;
  287. struct iio_dev *indio_dev;
  288. int ret;
  289. indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
  290. if (!indio_dev)
  291. return -ENOMEM;
  292. data = iio_priv(indio_dev);
  293. i2c_set_clientdata(client, indio_dev);
  294. data->client = client;
  295. data->id = id->driver_data;
  296. data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
  297. ret = data->chip_spec->init(data);
  298. if (ret < 0)
  299. return ret;
  300. dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
  301. data->chip_spec->prod, data->rev);
  302. indio_dev->dev.parent = &client->dev;
  303. indio_dev->info = &vcnl4000_info;
  304. indio_dev->channels = vcnl4000_channels;
  305. indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
  306. indio_dev->name = VCNL4000_DRV_NAME;
  307. indio_dev->modes = INDIO_DIRECT_MODE;
  308. return devm_iio_device_register(&client->dev, indio_dev);
  309. }
  310. static struct i2c_driver vcnl4000_driver = {
  311. .driver = {
  312. .name = VCNL4000_DRV_NAME,
  313. },
  314. .probe = vcnl4000_probe,
  315. .id_table = vcnl4000_id,
  316. };
  317. module_i2c_driver(vcnl4000_driver);
  318. MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
  319. MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
  320. MODULE_LICENSE("GPL");