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