vcnl4000.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  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. int ret;
  146. mutex_lock(&data->vcnl4000_lock);
  147. ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
  148. req_mask);
  149. if (ret < 0)
  150. goto fail;
  151. /* wait for data to become ready */
  152. while (tries--) {
  153. ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
  154. if (ret < 0)
  155. goto fail;
  156. if (ret & rdy_mask)
  157. break;
  158. msleep(20); /* measurement takes up to 100 ms */
  159. }
  160. if (tries < 0) {
  161. dev_err(&data->client->dev,
  162. "vcnl4000_measure() failed, data not ready\n");
  163. ret = -EIO;
  164. goto fail;
  165. }
  166. ret = i2c_smbus_read_word_swapped(data->client, data_reg);
  167. if (ret < 0)
  168. goto fail;
  169. mutex_unlock(&data->vcnl4000_lock);
  170. *val = ret;
  171. return 0;
  172. fail:
  173. mutex_unlock(&data->vcnl4000_lock);
  174. return ret;
  175. }
  176. static int vcnl4200_measure(struct vcnl4000_data *data,
  177. struct vcnl4200_channel *chan, int *val)
  178. {
  179. int ret;
  180. s64 delta;
  181. ktime_t next_measurement;
  182. mutex_lock(&chan->lock);
  183. next_measurement = ktime_add(chan->last_measurement,
  184. chan->sampling_rate);
  185. delta = ktime_us_delta(next_measurement, ktime_get());
  186. if (delta > 0)
  187. usleep_range(delta, delta + 500);
  188. chan->last_measurement = ktime_get();
  189. mutex_unlock(&chan->lock);
  190. ret = i2c_smbus_read_word_data(data->client, chan->reg);
  191. if (ret < 0)
  192. return ret;
  193. *val = ret;
  194. return 0;
  195. }
  196. static int vcnl4000_measure_light(struct vcnl4000_data *data, int *val)
  197. {
  198. return vcnl4000_measure(data,
  199. VCNL4000_AL_OD, VCNL4000_AL_RDY,
  200. VCNL4000_AL_RESULT_HI, val);
  201. }
  202. static int vcnl4200_measure_light(struct vcnl4000_data *data, int *val)
  203. {
  204. return vcnl4200_measure(data, &data->vcnl4200_al, val);
  205. }
  206. static int vcnl4000_measure_proximity(struct vcnl4000_data *data, int *val)
  207. {
  208. return vcnl4000_measure(data,
  209. VCNL4000_PS_OD, VCNL4000_PS_RDY,
  210. VCNL4000_PS_RESULT_HI, val);
  211. }
  212. static int vcnl4200_measure_proximity(struct vcnl4000_data *data, int *val)
  213. {
  214. return vcnl4200_measure(data, &data->vcnl4200_ps, val);
  215. }
  216. static const struct vcnl4000_chip_spec vcnl4000_chip_spec_cfg[] = {
  217. [VCNL4000] = {
  218. .prod = "VCNL4000",
  219. .init = vcnl4000_init,
  220. .measure_light = vcnl4000_measure_light,
  221. .measure_proximity = vcnl4000_measure_proximity,
  222. },
  223. [VCNL4010] = {
  224. .prod = "VCNL4010/4020",
  225. .init = vcnl4000_init,
  226. .measure_light = vcnl4000_measure_light,
  227. .measure_proximity = vcnl4000_measure_proximity,
  228. },
  229. [VCNL4200] = {
  230. .prod = "VCNL4200",
  231. .init = vcnl4200_init,
  232. .measure_light = vcnl4200_measure_light,
  233. .measure_proximity = vcnl4200_measure_proximity,
  234. },
  235. };
  236. static const struct iio_chan_spec vcnl4000_channels[] = {
  237. {
  238. .type = IIO_LIGHT,
  239. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
  240. BIT(IIO_CHAN_INFO_SCALE),
  241. }, {
  242. .type = IIO_PROXIMITY,
  243. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  244. }
  245. };
  246. static int vcnl4000_read_raw(struct iio_dev *indio_dev,
  247. struct iio_chan_spec const *chan,
  248. int *val, int *val2, long mask)
  249. {
  250. int ret;
  251. struct vcnl4000_data *data = iio_priv(indio_dev);
  252. switch (mask) {
  253. case IIO_CHAN_INFO_RAW:
  254. switch (chan->type) {
  255. case IIO_LIGHT:
  256. ret = data->chip_spec->measure_light(data, val);
  257. if (ret < 0)
  258. return ret;
  259. return IIO_VAL_INT;
  260. case IIO_PROXIMITY:
  261. ret = data->chip_spec->measure_proximity(data, val);
  262. if (ret < 0)
  263. return ret;
  264. return IIO_VAL_INT;
  265. default:
  266. return -EINVAL;
  267. }
  268. case IIO_CHAN_INFO_SCALE:
  269. if (chan->type != IIO_LIGHT)
  270. return -EINVAL;
  271. *val = 0;
  272. *val2 = data->al_scale;
  273. return IIO_VAL_INT_PLUS_MICRO;
  274. default:
  275. return -EINVAL;
  276. }
  277. }
  278. static const struct iio_info vcnl4000_info = {
  279. .read_raw = vcnl4000_read_raw,
  280. };
  281. static int vcnl4000_probe(struct i2c_client *client,
  282. const struct i2c_device_id *id)
  283. {
  284. struct vcnl4000_data *data;
  285. struct iio_dev *indio_dev;
  286. int ret;
  287. indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
  288. if (!indio_dev)
  289. return -ENOMEM;
  290. data = iio_priv(indio_dev);
  291. i2c_set_clientdata(client, indio_dev);
  292. data->client = client;
  293. data->id = id->driver_data;
  294. data->chip_spec = &vcnl4000_chip_spec_cfg[data->id];
  295. ret = data->chip_spec->init(data);
  296. if (ret < 0)
  297. return ret;
  298. dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
  299. data->chip_spec->prod, data->rev);
  300. indio_dev->dev.parent = &client->dev;
  301. indio_dev->info = &vcnl4000_info;
  302. indio_dev->channels = vcnl4000_channels;
  303. indio_dev->num_channels = ARRAY_SIZE(vcnl4000_channels);
  304. indio_dev->name = VCNL4000_DRV_NAME;
  305. indio_dev->modes = INDIO_DIRECT_MODE;
  306. return devm_iio_device_register(&client->dev, indio_dev);
  307. }
  308. static struct i2c_driver vcnl4000_driver = {
  309. .driver = {
  310. .name = VCNL4000_DRV_NAME,
  311. },
  312. .probe = vcnl4000_probe,
  313. .id_table = vcnl4000_id,
  314. };
  315. module_i2c_driver(vcnl4000_driver);
  316. MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
  317. MODULE_DESCRIPTION("Vishay VCNL4000 proximity/ambient light sensor driver");
  318. MODULE_LICENSE("GPL");