ltr390.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. // SPDX-License-Identifier: GPL-2.0-or-later
  2. /*
  3. * IIO driver for Lite-On LTR390 ALS and UV sensor
  4. * (7-bit I2C slave address 0x53)
  5. *
  6. * Based on the work of:
  7. * Shreeya Patel and Shi Zhigang (LTRF216 Driver)
  8. *
  9. * Copyright (C) 2023 Anshul Dalal <anshulusr@gmail.com>
  10. *
  11. * Datasheet:
  12. * https://optoelectronics.liteon.com/upload/download/DS86-2015-0004/LTR-390UV_Final_%20DS_V1%201.pdf
  13. *
  14. * TODO:
  15. * - Support for configurable gain and resolution
  16. * - Sensor suspend/resume support
  17. * - Add support for reading the ALS
  18. * - Interrupt support
  19. */
  20. #include <linux/i2c.h>
  21. #include <linux/math.h>
  22. #include <linux/module.h>
  23. #include <linux/mutex.h>
  24. #include <linux/regmap.h>
  25. #include <linux/bitfield.h>
  26. #include <linux/iio/iio.h>
  27. #include <linux/unaligned.h>
  28. #define LTR390_MAIN_CTRL 0x00
  29. #define LTR390_ALS_UVS_MEAS_RATE 0x04
  30. #define LTR390_ALS_UVS_GAIN 0x05
  31. #define LTR390_PART_ID 0x06
  32. #define LTR390_ALS_DATA 0x0D
  33. #define LTR390_UVS_DATA 0x10
  34. #define LTR390_INT_CFG 0x19
  35. #define LTR390_PART_NUMBER_ID 0xb
  36. #define LTR390_ALS_UVS_GAIN_MASK 0x07
  37. #define LTR390_ALS_UVS_INT_TIME_MASK 0x70
  38. #define LTR390_ALS_UVS_INT_TIME(x) FIELD_PREP(LTR390_ALS_UVS_INT_TIME_MASK, (x))
  39. #define LTR390_SW_RESET BIT(4)
  40. #define LTR390_UVS_MODE BIT(3)
  41. #define LTR390_SENSOR_ENABLE BIT(1)
  42. #define LTR390_FRACTIONAL_PRECISION 100
  43. /*
  44. * At 20-bit resolution (integration time: 400ms) and 18x gain, 2300 counts of
  45. * the sensor are equal to 1 UV Index [Datasheet Page#8].
  46. *
  47. * For the default resolution of 18-bit (integration time: 100ms) and default
  48. * gain of 3x, the counts/uvi are calculated as follows:
  49. * 2300 / ((3/18) * (100/400)) = 95.83
  50. */
  51. #define LTR390_COUNTS_PER_UVI 96
  52. /*
  53. * Window Factor is needed when the device is under Window glass with coated
  54. * tinted ink. This is to compensate for the light loss due to the lower
  55. * transmission rate of the window glass and helps * in calculating lux.
  56. */
  57. #define LTR390_WINDOW_FACTOR 1
  58. enum ltr390_mode {
  59. LTR390_SET_ALS_MODE,
  60. LTR390_SET_UVS_MODE,
  61. };
  62. struct ltr390_data {
  63. struct regmap *regmap;
  64. struct i2c_client *client;
  65. /* Protects device from simulataneous reads */
  66. struct mutex lock;
  67. enum ltr390_mode mode;
  68. int gain;
  69. int int_time_us;
  70. };
  71. static const struct regmap_config ltr390_regmap_config = {
  72. .name = "ltr390",
  73. .reg_bits = 8,
  74. .reg_stride = 1,
  75. .val_bits = 8,
  76. };
  77. static int ltr390_register_read(struct ltr390_data *data, u8 register_address)
  78. {
  79. struct device *dev = &data->client->dev;
  80. int ret;
  81. u8 recieve_buffer[3];
  82. ret = regmap_bulk_read(data->regmap, register_address, recieve_buffer,
  83. sizeof(recieve_buffer));
  84. if (ret) {
  85. dev_err(dev, "failed to read measurement data");
  86. return ret;
  87. }
  88. return get_unaligned_le24(recieve_buffer);
  89. }
  90. static int ltr390_set_mode(struct ltr390_data *data, enum ltr390_mode mode)
  91. {
  92. int ret;
  93. if (data->mode == mode)
  94. return 0;
  95. switch (mode) {
  96. case LTR390_SET_ALS_MODE:
  97. ret = regmap_clear_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
  98. break;
  99. case LTR390_SET_UVS_MODE:
  100. ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_UVS_MODE);
  101. break;
  102. }
  103. if (ret)
  104. return ret;
  105. data->mode = mode;
  106. return 0;
  107. }
  108. static int ltr390_counts_per_uvi(struct ltr390_data *data)
  109. {
  110. const int orig_gain = 18;
  111. const int orig_int_time = 400;
  112. return DIV_ROUND_CLOSEST(23 * data->gain * data->int_time_us, 10 * orig_gain * orig_int_time);
  113. }
  114. static int ltr390_read_raw(struct iio_dev *iio_device,
  115. struct iio_chan_spec const *chan, int *val,
  116. int *val2, long mask)
  117. {
  118. int ret;
  119. struct ltr390_data *data = iio_priv(iio_device);
  120. guard(mutex)(&data->lock);
  121. switch (mask) {
  122. case IIO_CHAN_INFO_RAW:
  123. switch (chan->type) {
  124. case IIO_UVINDEX:
  125. ret = ltr390_set_mode(data, LTR390_SET_UVS_MODE);
  126. if (ret < 0)
  127. return ret;
  128. ret = ltr390_register_read(data, LTR390_UVS_DATA);
  129. if (ret < 0)
  130. return ret;
  131. break;
  132. case IIO_LIGHT:
  133. ret = ltr390_set_mode(data, LTR390_SET_ALS_MODE);
  134. if (ret < 0)
  135. return ret;
  136. ret = ltr390_register_read(data, LTR390_ALS_DATA);
  137. if (ret < 0)
  138. return ret;
  139. break;
  140. default:
  141. return -EINVAL;
  142. }
  143. *val = ret;
  144. return IIO_VAL_INT;
  145. case IIO_CHAN_INFO_SCALE:
  146. switch (chan->type) {
  147. case IIO_UVINDEX:
  148. *val = LTR390_WINDOW_FACTOR * LTR390_FRACTIONAL_PRECISION;
  149. *val2 = ltr390_counts_per_uvi(data);
  150. return IIO_VAL_FRACTIONAL;
  151. case IIO_LIGHT:
  152. *val = LTR390_WINDOW_FACTOR * 6 * 100;
  153. *val2 = data->gain * data->int_time_us;
  154. return IIO_VAL_FRACTIONAL;
  155. default:
  156. return -EINVAL;
  157. }
  158. case IIO_CHAN_INFO_INT_TIME:
  159. *val = data->int_time_us;
  160. return IIO_VAL_INT;
  161. default:
  162. return -EINVAL;
  163. }
  164. }
  165. /* integration time in us */
  166. static const int ltr390_int_time_map_us[] = { 400000, 200000, 100000, 50000, 25000, 12500 };
  167. static const int ltr390_gain_map[] = { 1, 3, 6, 9, 18 };
  168. static const struct iio_chan_spec ltr390_channels[] = {
  169. /* UV sensor */
  170. {
  171. .type = IIO_UVINDEX,
  172. .scan_index = 0,
  173. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
  174. .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
  175. .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
  176. },
  177. /* ALS sensor */
  178. {
  179. .type = IIO_LIGHT,
  180. .scan_index = 1,
  181. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
  182. .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME),
  183. .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | BIT(IIO_CHAN_INFO_SCALE)
  184. },
  185. };
  186. static int ltr390_set_gain(struct ltr390_data *data, int val)
  187. {
  188. int ret, idx;
  189. for (idx = 0; idx < ARRAY_SIZE(ltr390_gain_map); idx++) {
  190. if (ltr390_gain_map[idx] != val)
  191. continue;
  192. guard(mutex)(&data->lock);
  193. ret = regmap_update_bits(data->regmap,
  194. LTR390_ALS_UVS_GAIN,
  195. LTR390_ALS_UVS_GAIN_MASK, idx);
  196. if (ret)
  197. return ret;
  198. data->gain = ltr390_gain_map[idx];
  199. return 0;
  200. }
  201. return -EINVAL;
  202. }
  203. static int ltr390_set_int_time(struct ltr390_data *data, int val)
  204. {
  205. int ret, idx;
  206. for (idx = 0; idx < ARRAY_SIZE(ltr390_int_time_map_us); idx++) {
  207. if (ltr390_int_time_map_us[idx] != val)
  208. continue;
  209. guard(mutex)(&data->lock);
  210. ret = regmap_update_bits(data->regmap,
  211. LTR390_ALS_UVS_MEAS_RATE,
  212. LTR390_ALS_UVS_INT_TIME_MASK,
  213. LTR390_ALS_UVS_INT_TIME(idx));
  214. if (ret)
  215. return ret;
  216. data->int_time_us = ltr390_int_time_map_us[idx];
  217. return 0;
  218. }
  219. return -EINVAL;
  220. }
  221. static int ltr390_read_avail(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
  222. const int **vals, int *type, int *length, long mask)
  223. {
  224. switch (mask) {
  225. case IIO_CHAN_INFO_SCALE:
  226. *length = ARRAY_SIZE(ltr390_gain_map);
  227. *type = IIO_VAL_INT;
  228. *vals = ltr390_gain_map;
  229. return IIO_AVAIL_LIST;
  230. case IIO_CHAN_INFO_INT_TIME:
  231. *length = ARRAY_SIZE(ltr390_int_time_map_us);
  232. *type = IIO_VAL_INT;
  233. *vals = ltr390_int_time_map_us;
  234. return IIO_AVAIL_LIST;
  235. default:
  236. return -EINVAL;
  237. }
  238. }
  239. static int ltr390_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
  240. int val, int val2, long mask)
  241. {
  242. struct ltr390_data *data = iio_priv(indio_dev);
  243. switch (mask) {
  244. case IIO_CHAN_INFO_SCALE:
  245. if (val2 != 0)
  246. return -EINVAL;
  247. return ltr390_set_gain(data, val);
  248. case IIO_CHAN_INFO_INT_TIME:
  249. if (val2 != 0)
  250. return -EINVAL;
  251. return ltr390_set_int_time(data, val);
  252. default:
  253. return -EINVAL;
  254. }
  255. }
  256. static const struct iio_info ltr390_info = {
  257. .read_raw = ltr390_read_raw,
  258. .write_raw = ltr390_write_raw,
  259. .read_avail = ltr390_read_avail,
  260. };
  261. static int ltr390_probe(struct i2c_client *client)
  262. {
  263. struct ltr390_data *data;
  264. struct iio_dev *indio_dev;
  265. struct device *dev;
  266. int ret, part_number;
  267. dev = &client->dev;
  268. indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
  269. if (!indio_dev)
  270. return -ENOMEM;
  271. data = iio_priv(indio_dev);
  272. data->regmap = devm_regmap_init_i2c(client, &ltr390_regmap_config);
  273. if (IS_ERR(data->regmap))
  274. return dev_err_probe(dev, PTR_ERR(data->regmap),
  275. "regmap initialization failed\n");
  276. data->client = client;
  277. /* default value of integration time from pg: 15 of the datasheet */
  278. data->int_time_us = 100000;
  279. /* default value of gain from pg: 16 of the datasheet */
  280. data->gain = 3;
  281. /* default mode for ltr390 is ALS mode */
  282. data->mode = LTR390_SET_ALS_MODE;
  283. mutex_init(&data->lock);
  284. indio_dev->info = &ltr390_info;
  285. indio_dev->channels = ltr390_channels;
  286. indio_dev->num_channels = ARRAY_SIZE(ltr390_channels);
  287. indio_dev->name = "ltr390";
  288. ret = regmap_read(data->regmap, LTR390_PART_ID, &part_number);
  289. if (ret)
  290. return dev_err_probe(dev, ret,
  291. "failed to get sensor's part id\n");
  292. /* Lower 4 bits of `part_number` change with hardware revisions */
  293. if (part_number >> 4 != LTR390_PART_NUMBER_ID)
  294. dev_info(dev, "received invalid product id: 0x%x", part_number);
  295. dev_dbg(dev, "LTR390, product id: 0x%x\n", part_number);
  296. /* reset sensor, chip fails to respond to this, so ignore any errors */
  297. regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SW_RESET);
  298. /* Wait for the registers to reset before proceeding */
  299. usleep_range(1000, 2000);
  300. ret = regmap_set_bits(data->regmap, LTR390_MAIN_CTRL, LTR390_SENSOR_ENABLE);
  301. if (ret)
  302. return dev_err_probe(dev, ret, "failed to enable the sensor\n");
  303. return devm_iio_device_register(dev, indio_dev);
  304. }
  305. static const struct i2c_device_id ltr390_id[] = {
  306. { "ltr390" },
  307. { /* Sentinel */ }
  308. };
  309. MODULE_DEVICE_TABLE(i2c, ltr390_id);
  310. static const struct of_device_id ltr390_of_table[] = {
  311. { .compatible = "liteon,ltr390" },
  312. { /* Sentinel */ }
  313. };
  314. MODULE_DEVICE_TABLE(of, ltr390_of_table);
  315. static struct i2c_driver ltr390_driver = {
  316. .driver = {
  317. .name = "ltr390",
  318. .of_match_table = ltr390_of_table,
  319. },
  320. .probe = ltr390_probe,
  321. .id_table = ltr390_id,
  322. };
  323. module_i2c_driver(ltr390_driver);
  324. MODULE_AUTHOR("Anshul Dalal <anshulusr@gmail.com>");
  325. MODULE_DESCRIPTION("Lite-On LTR390 ALS and UV sensor Driver");
  326. MODULE_LICENSE("GPL");