dlhl60d.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * All Sensors DLH series low voltage digital pressure sensors
  4. *
  5. * Copyright (c) 2019 AVL DiTEST GmbH
  6. * Tomislav Denis <[email protected]>
  7. *
  8. * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF
  9. */
  10. #include <linux/module.h>
  11. #include <linux/delay.h>
  12. #include <linux/i2c.h>
  13. #include <linux/iio/iio.h>
  14. #include <linux/iio/buffer.h>
  15. #include <linux/iio/trigger_consumer.h>
  16. #include <linux/iio/triggered_buffer.h>
  17. #include <asm/unaligned.h>
  18. /* Commands */
  19. #define DLH_START_SINGLE 0xAA
  20. /* Status bits */
  21. #define DLH_STATUS_OK 0x40
  22. /* DLH data format */
  23. #define DLH_NUM_READ_BYTES 7
  24. #define DLH_NUM_DATA_BYTES 3
  25. #define DLH_NUM_PR_BITS 24
  26. #define DLH_NUM_TEMP_BITS 24
  27. /* DLH timings */
  28. #define DLH_SINGLE_DUT_MS 5
  29. enum dhl_ids {
  30. dlhl60d,
  31. dlhl60g,
  32. };
  33. struct dlh_info {
  34. u8 osdig; /* digital offset factor */
  35. unsigned int fss; /* full scale span (inch H2O) */
  36. };
  37. struct dlh_state {
  38. struct i2c_client *client;
  39. struct dlh_info info;
  40. bool use_interrupt;
  41. struct completion completion;
  42. u8 rx_buf[DLH_NUM_READ_BYTES];
  43. };
  44. static struct dlh_info dlh_info_tbl[] = {
  45. [dlhl60d] = {
  46. .osdig = 2,
  47. .fss = 120,
  48. },
  49. [dlhl60g] = {
  50. .osdig = 10,
  51. .fss = 60,
  52. },
  53. };
  54. static int dlh_cmd_start_single(struct dlh_state *st)
  55. {
  56. int ret;
  57. ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
  58. if (ret)
  59. dev_err(&st->client->dev,
  60. "%s: I2C write byte failed\n", __func__);
  61. return ret;
  62. }
  63. static int dlh_cmd_read_data(struct dlh_state *st)
  64. {
  65. int ret;
  66. ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
  67. if (ret < 0) {
  68. dev_err(&st->client->dev,
  69. "%s: I2C read block failed\n", __func__);
  70. return ret;
  71. }
  72. if (st->rx_buf[0] != DLH_STATUS_OK) {
  73. dev_err(&st->client->dev,
  74. "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
  75. return -EBUSY;
  76. }
  77. return 0;
  78. }
  79. static int dlh_start_capture_and_read(struct dlh_state *st)
  80. {
  81. int ret;
  82. if (st->use_interrupt)
  83. reinit_completion(&st->completion);
  84. ret = dlh_cmd_start_single(st);
  85. if (ret)
  86. return ret;
  87. if (st->use_interrupt) {
  88. ret = wait_for_completion_timeout(&st->completion,
  89. msecs_to_jiffies(DLH_SINGLE_DUT_MS));
  90. if (!ret) {
  91. dev_err(&st->client->dev,
  92. "%s: conversion timed out\n", __func__);
  93. return -ETIMEDOUT;
  94. }
  95. } else {
  96. mdelay(DLH_SINGLE_DUT_MS);
  97. }
  98. return dlh_cmd_read_data(st);
  99. }
  100. static int dlh_read_direct(struct dlh_state *st,
  101. unsigned int *pressure, unsigned int *temperature)
  102. {
  103. int ret;
  104. ret = dlh_start_capture_and_read(st);
  105. if (ret)
  106. return ret;
  107. *pressure = get_unaligned_be24(&st->rx_buf[1]);
  108. *temperature = get_unaligned_be24(&st->rx_buf[4]);
  109. return 0;
  110. }
  111. static int dlh_read_raw(struct iio_dev *indio_dev,
  112. struct iio_chan_spec const *channel, int *value,
  113. int *value2, long mask)
  114. {
  115. struct dlh_state *st = iio_priv(indio_dev);
  116. unsigned int pressure, temperature;
  117. int ret;
  118. s64 tmp;
  119. s32 rem;
  120. switch (mask) {
  121. case IIO_CHAN_INFO_RAW:
  122. ret = iio_device_claim_direct_mode(indio_dev);
  123. if (ret)
  124. return ret;
  125. ret = dlh_read_direct(st, &pressure, &temperature);
  126. iio_device_release_direct_mode(indio_dev);
  127. if (ret)
  128. return ret;
  129. switch (channel->type) {
  130. case IIO_PRESSURE:
  131. *value = pressure;
  132. return IIO_VAL_INT;
  133. case IIO_TEMP:
  134. *value = temperature;
  135. return IIO_VAL_INT;
  136. default:
  137. return -EINVAL;
  138. }
  139. case IIO_CHAN_INFO_SCALE:
  140. switch (channel->type) {
  141. case IIO_PRESSURE:
  142. tmp = div_s64(125LL * st->info.fss * 24909 * 100,
  143. 1 << DLH_NUM_PR_BITS);
  144. tmp = div_s64_rem(tmp, 1000000000LL, &rem);
  145. *value = tmp;
  146. *value2 = rem;
  147. return IIO_VAL_INT_PLUS_NANO;
  148. case IIO_TEMP:
  149. *value = 125 * 1000;
  150. *value2 = DLH_NUM_TEMP_BITS;
  151. return IIO_VAL_FRACTIONAL_LOG2;
  152. default:
  153. return -EINVAL;
  154. }
  155. case IIO_CHAN_INFO_OFFSET:
  156. switch (channel->type) {
  157. case IIO_PRESSURE:
  158. *value = -125 * st->info.fss * 24909;
  159. *value2 = 100 * st->info.osdig * 100000;
  160. return IIO_VAL_FRACTIONAL;
  161. case IIO_TEMP:
  162. *value = -40 * 1000;
  163. return IIO_VAL_INT;
  164. default:
  165. return -EINVAL;
  166. }
  167. }
  168. return -EINVAL;
  169. }
  170. static const struct iio_info dlh_info = {
  171. .read_raw = dlh_read_raw,
  172. };
  173. static const struct iio_chan_spec dlh_channels[] = {
  174. {
  175. .type = IIO_PRESSURE,
  176. .indexed = 1,
  177. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  178. .info_mask_shared_by_type =
  179. BIT(IIO_CHAN_INFO_SCALE) |
  180. BIT(IIO_CHAN_INFO_OFFSET),
  181. .scan_index = 0,
  182. .scan_type = {
  183. .sign = 'u',
  184. .realbits = DLH_NUM_PR_BITS,
  185. .storagebits = 32,
  186. .shift = 8,
  187. .endianness = IIO_BE,
  188. },
  189. }, {
  190. .type = IIO_TEMP,
  191. .indexed = 1,
  192. .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
  193. .info_mask_shared_by_type =
  194. BIT(IIO_CHAN_INFO_SCALE) |
  195. BIT(IIO_CHAN_INFO_OFFSET),
  196. .scan_index = 1,
  197. .scan_type = {
  198. .sign = 'u',
  199. .realbits = DLH_NUM_TEMP_BITS,
  200. .storagebits = 32,
  201. .shift = 8,
  202. .endianness = IIO_BE,
  203. },
  204. }
  205. };
  206. static irqreturn_t dlh_trigger_handler(int irq, void *private)
  207. {
  208. struct iio_poll_func *pf = private;
  209. struct iio_dev *indio_dev = pf->indio_dev;
  210. struct dlh_state *st = iio_priv(indio_dev);
  211. int ret;
  212. unsigned int chn, i = 0;
  213. __be32 tmp_buf[2];
  214. ret = dlh_start_capture_and_read(st);
  215. if (ret)
  216. goto out;
  217. for_each_set_bit(chn, indio_dev->active_scan_mask,
  218. indio_dev->masklength) {
  219. memcpy(tmp_buf + i,
  220. &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
  221. DLH_NUM_DATA_BYTES);
  222. i++;
  223. }
  224. iio_push_to_buffers(indio_dev, tmp_buf);
  225. out:
  226. iio_trigger_notify_done(indio_dev->trig);
  227. return IRQ_HANDLED;
  228. }
  229. static irqreturn_t dlh_interrupt(int irq, void *private)
  230. {
  231. struct iio_dev *indio_dev = private;
  232. struct dlh_state *st = iio_priv(indio_dev);
  233. complete(&st->completion);
  234. return IRQ_HANDLED;
  235. };
  236. static int dlh_probe(struct i2c_client *client,
  237. const struct i2c_device_id *id)
  238. {
  239. struct dlh_state *st;
  240. struct iio_dev *indio_dev;
  241. int ret;
  242. if (!i2c_check_functionality(client->adapter,
  243. I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
  244. dev_err(&client->dev,
  245. "adapter doesn't support required i2c functionality\n");
  246. return -EOPNOTSUPP;
  247. }
  248. indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
  249. if (!indio_dev) {
  250. dev_err(&client->dev, "failed to allocate iio device\n");
  251. return -ENOMEM;
  252. }
  253. i2c_set_clientdata(client, indio_dev);
  254. st = iio_priv(indio_dev);
  255. st->info = dlh_info_tbl[id->driver_data];
  256. st->client = client;
  257. st->use_interrupt = false;
  258. indio_dev->name = id->name;
  259. indio_dev->info = &dlh_info;
  260. indio_dev->modes = INDIO_DIRECT_MODE;
  261. indio_dev->channels = dlh_channels;
  262. indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
  263. if (client->irq > 0) {
  264. ret = devm_request_threaded_irq(&client->dev, client->irq,
  265. dlh_interrupt, NULL,
  266. IRQF_TRIGGER_RISING | IRQF_ONESHOT,
  267. id->name, indio_dev);
  268. if (ret) {
  269. dev_err(&client->dev, "failed to allocate threaded irq");
  270. return ret;
  271. }
  272. st->use_interrupt = true;
  273. init_completion(&st->completion);
  274. }
  275. ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
  276. NULL, &dlh_trigger_handler, NULL);
  277. if (ret) {
  278. dev_err(&client->dev, "failed to setup iio buffer\n");
  279. return ret;
  280. }
  281. ret = devm_iio_device_register(&client->dev, indio_dev);
  282. if (ret)
  283. dev_err(&client->dev, "failed to register iio device\n");
  284. return ret;
  285. }
  286. static const struct of_device_id dlh_of_match[] = {
  287. { .compatible = "asc,dlhl60d" },
  288. { .compatible = "asc,dlhl60g" },
  289. {}
  290. };
  291. MODULE_DEVICE_TABLE(of, dlh_of_match);
  292. static const struct i2c_device_id dlh_id[] = {
  293. { "dlhl60d", dlhl60d },
  294. { "dlhl60g", dlhl60g },
  295. {}
  296. };
  297. MODULE_DEVICE_TABLE(i2c, dlh_id);
  298. static struct i2c_driver dlh_driver = {
  299. .driver = {
  300. .name = "dlhl60d",
  301. .of_match_table = dlh_of_match,
  302. },
  303. .probe = dlh_probe,
  304. .id_table = dlh_id,
  305. };
  306. module_i2c_driver(dlh_driver);
  307. MODULE_AUTHOR("Tomislav Denis <[email protected]>");
  308. MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
  309. MODULE_LICENSE("GPL v2");