1234353Sdim// SPDX-License-Identifier: GPL-2.0
2204642Srdivacky/*
3204642Srdivacky * All Sensors DLH series low voltage digital pressure sensors
4204642Srdivacky *
5204642Srdivacky * Copyright (c) 2019 AVL DiTEST GmbH
6204642Srdivacky *   Tomislav Denis <tomislav.denis@avl.com>
7204642Srdivacky *
8204642Srdivacky * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF
9204642Srdivacky */
10204642Srdivacky
11204642Srdivacky#include <linux/module.h>
12204642Srdivacky#include <linux/delay.h>
13204642Srdivacky#include <linux/i2c.h>
14204642Srdivacky#include <linux/iio/iio.h>
15204642Srdivacky#include <linux/iio/buffer.h>
16204642Srdivacky#include <linux/iio/trigger_consumer.h>
17204642Srdivacky#include <linux/iio/triggered_buffer.h>
18234353Sdim#include <asm/unaligned.h>
19204642Srdivacky
20204642Srdivacky/* Commands */
21204642Srdivacky#define DLH_START_SINGLE    0xAA
22224145Sdim
23224145Sdim/* Status bits */
24224145Sdim#define DLH_STATUS_OK       0x40
25204642Srdivacky
26204642Srdivacky/* DLH  data format */
27204642Srdivacky#define DLH_NUM_READ_BYTES  7
28204642Srdivacky#define DLH_NUM_DATA_BYTES  3
29204642Srdivacky#define DLH_NUM_PR_BITS     24
30204642Srdivacky#define DLH_NUM_TEMP_BITS   24
31204642Srdivacky
32204642Srdivacky/* DLH  timings */
33204642Srdivacky#define DLH_SINGLE_DUT_MS   5
34204642Srdivacky
35204642Srdivackyenum dhl_ids {
36204642Srdivacky	dlhl60d,
37204642Srdivacky	dlhl60g,
38204642Srdivacky};
39204642Srdivacky
40204642Srdivackystruct dlh_info {
41204642Srdivacky	u8 osdig;           /* digital offset factor */
42204642Srdivacky	unsigned int fss;   /* full scale span (inch H2O) */
43204642Srdivacky};
44204642Srdivacky
45204642Srdivackystruct dlh_state {
46204642Srdivacky	struct i2c_client *client;
47204642Srdivacky	struct dlh_info info;
48204642Srdivacky	bool use_interrupt;
49204642Srdivacky	struct completion completion;
50204642Srdivacky	u8 rx_buf[DLH_NUM_READ_BYTES];
51204642Srdivacky};
52204642Srdivacky
53204642Srdivackystatic struct dlh_info dlh_info_tbl[] = {
54204642Srdivacky	[dlhl60d] = {
55204642Srdivacky		.osdig = 2,
56204642Srdivacky		.fss = 120,
57204642Srdivacky	},
58204642Srdivacky	[dlhl60g] = {
59204642Srdivacky		.osdig = 10,
60204642Srdivacky		.fss = 60,
61204642Srdivacky	},
62204642Srdivacky};
63204642Srdivacky
64204642Srdivacky
65204642Srdivackystatic int dlh_cmd_start_single(struct dlh_state *st)
66204642Srdivacky{
67204642Srdivacky	int ret;
68204642Srdivacky
69204642Srdivacky	ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
70204642Srdivacky	if (ret)
71204642Srdivacky		dev_err(&st->client->dev,
72204642Srdivacky			"%s: I2C write byte failed\n", __func__);
73204642Srdivacky
74204642Srdivacky	return ret;
75204642Srdivacky}
76204642Srdivacky
77204642Srdivackystatic int dlh_cmd_read_data(struct dlh_state *st)
78204642Srdivacky{
79218893Sdim	int ret;
80218893Sdim
81218893Sdim	ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
82218893Sdim	if (ret < 0) {
83218893Sdim		dev_err(&st->client->dev,
84204642Srdivacky			"%s: I2C read block failed\n", __func__);
85204642Srdivacky		return ret;
86204642Srdivacky	}
87204642Srdivacky
88204642Srdivacky	if (st->rx_buf[0] != DLH_STATUS_OK) {
89218893Sdim		dev_err(&st->client->dev,
90218893Sdim			"%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
91218893Sdim		return -EBUSY;
92218893Sdim	}
93218893Sdim
94218893Sdim	return 0;
95218893Sdim}
96218893Sdim
97218893Sdimstatic int dlh_start_capture_and_read(struct dlh_state *st)
98218893Sdim{
99218893Sdim	int ret;
100204642Srdivacky
101204642Srdivacky	if (st->use_interrupt)
102204642Srdivacky		reinit_completion(&st->completion);
103218893Sdim
104204642Srdivacky	ret = dlh_cmd_start_single(st);
105204642Srdivacky	if (ret)
106218893Sdim		return ret;
107204642Srdivacky
108218893Sdim	if (st->use_interrupt) {
109218893Sdim		ret = wait_for_completion_timeout(&st->completion,
110218893Sdim			msecs_to_jiffies(DLH_SINGLE_DUT_MS));
111218893Sdim		if (!ret) {
112218893Sdim			dev_err(&st->client->dev,
113218893Sdim				"%s: conversion timed out\n", __func__);
114218893Sdim			return -ETIMEDOUT;
115218893Sdim		}
116218893Sdim	} else {
117218893Sdim		mdelay(DLH_SINGLE_DUT_MS);
118218893Sdim	}
119218893Sdim
120218893Sdim	return dlh_cmd_read_data(st);
121218893Sdim}
122218893Sdim
123218893Sdimstatic int dlh_read_direct(struct dlh_state *st,
124218893Sdim	unsigned int *pressure, unsigned int *temperature)
125218893Sdim{
126218893Sdim	int ret;
127218893Sdim
128218893Sdim	ret = dlh_start_capture_and_read(st);
129218893Sdim	if (ret)
130218893Sdim		return ret;
131218893Sdim
132218893Sdim	*pressure = get_unaligned_be24(&st->rx_buf[1]);
133218893Sdim	*temperature = get_unaligned_be24(&st->rx_buf[4]);
134218893Sdim
135218893Sdim	return 0;
136218893Sdim}
137218893Sdim
138218893Sdimstatic int dlh_read_raw(struct iio_dev *indio_dev,
139218893Sdim	struct iio_chan_spec const *channel, int *value,
140218893Sdim	int *value2, long mask)
141204642Srdivacky{
142204642Srdivacky	struct dlh_state *st = iio_priv(indio_dev);
143218893Sdim	unsigned int pressure, temperature;
144218893Sdim	int ret;
145218893Sdim	s64 tmp;
146218893Sdim	s32 rem;
147218893Sdim
148218893Sdim	switch (mask) {
149218893Sdim	case IIO_CHAN_INFO_RAW:
150218893Sdim		ret = iio_device_claim_direct_mode(indio_dev);
151218893Sdim		if (ret)
152218893Sdim			return ret;
153218893Sdim
154218893Sdim		ret = dlh_read_direct(st, &pressure, &temperature);
155218893Sdim		iio_device_release_direct_mode(indio_dev);
156218893Sdim		if (ret)
157218893Sdim			return ret;
158218893Sdim
159218893Sdim		switch (channel->type) {
160218893Sdim		case IIO_PRESSURE:
161218893Sdim			*value = pressure;
162218893Sdim			return IIO_VAL_INT;
163218893Sdim
164218893Sdim		case IIO_TEMP:
165218893Sdim			*value = temperature;
166218893Sdim			return IIO_VAL_INT;
167204642Srdivacky
168204642Srdivacky		default:
169224145Sdim			return -EINVAL;
170204642Srdivacky		}
171204642Srdivacky	case IIO_CHAN_INFO_SCALE:
172204642Srdivacky		switch (channel->type) {
173204642Srdivacky		case IIO_PRESSURE:
174204642Srdivacky			tmp = div_s64(125LL * st->info.fss * 24909 * 100,
175204642Srdivacky				1 << DLH_NUM_PR_BITS);
176204642Srdivacky			tmp = div_s64_rem(tmp, 1000000000LL, &rem);
177204642Srdivacky			*value = tmp;
178204642Srdivacky			*value2 = rem;
179204642Srdivacky			return IIO_VAL_INT_PLUS_NANO;
180204642Srdivacky
181204642Srdivacky		case IIO_TEMP:
182204642Srdivacky			*value = 125 * 1000;
183204642Srdivacky			*value2 = DLH_NUM_TEMP_BITS;
184204642Srdivacky			return IIO_VAL_FRACTIONAL_LOG2;
185204642Srdivacky
186204642Srdivacky		default:
187204642Srdivacky			return -EINVAL;
188204642Srdivacky		}
189204642Srdivacky	case IIO_CHAN_INFO_OFFSET:
190204642Srdivacky		switch (channel->type) {
191204642Srdivacky		case IIO_PRESSURE:
192204642Srdivacky			*value = -125 * st->info.fss * 24909;
193204642Srdivacky			*value2 = 100 * st->info.osdig * 100000;
194204642Srdivacky			return IIO_VAL_FRACTIONAL;
195204642Srdivacky
196204642Srdivacky		case IIO_TEMP:
197204642Srdivacky			*value = -40 * 1000;
198218893Sdim			return IIO_VAL_INT;
199218893Sdim
200218893Sdim		default:
201218893Sdim			return -EINVAL;
202204642Srdivacky		}
203204642Srdivacky	}
204210299Sed
205210299Sed	return -EINVAL;
206218893Sdim}
207218893Sdim
208218893Sdimstatic const struct iio_info dlh_info = {
209218893Sdim	.read_raw = dlh_read_raw,
210218893Sdim};
211210299Sed
212210299Sedstatic const struct iio_chan_spec dlh_channels[] = {
213210299Sed	{
214210299Sed		.type = IIO_PRESSURE,
215204642Srdivacky		.indexed = 1,
216204642Srdivacky		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
217204642Srdivacky		.info_mask_shared_by_type =
218208599Srdivacky			BIT(IIO_CHAN_INFO_SCALE) |
219208599Srdivacky			BIT(IIO_CHAN_INFO_OFFSET),
220204642Srdivacky		.scan_index = 0,
221204642Srdivacky		.scan_type = {
222204642Srdivacky			.sign = 'u',
223204642Srdivacky			.realbits = DLH_NUM_PR_BITS,
224208599Srdivacky			.storagebits = 32,
225208599Srdivacky			.shift = 8,
226204642Srdivacky			.endianness = IIO_BE,
227204642Srdivacky		},
228204642Srdivacky	}, {
229204642Srdivacky		.type = IIO_TEMP,
230204642Srdivacky		.indexed = 1,
231204642Srdivacky		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
232204642Srdivacky		.info_mask_shared_by_type =
233204642Srdivacky			BIT(IIO_CHAN_INFO_SCALE) |
234204642Srdivacky			BIT(IIO_CHAN_INFO_OFFSET),
235204642Srdivacky		.scan_index = 1,
236204642Srdivacky		.scan_type = {
237204642Srdivacky			.sign = 'u',
238204642Srdivacky			.realbits = DLH_NUM_TEMP_BITS,
239204642Srdivacky			.storagebits = 32,
240204642Srdivacky			.shift = 8,
241			.endianness = IIO_BE,
242		},
243	}
244};
245
246static irqreturn_t dlh_trigger_handler(int irq, void *private)
247{
248	struct iio_poll_func *pf = private;
249	struct iio_dev *indio_dev = pf->indio_dev;
250	struct dlh_state *st = iio_priv(indio_dev);
251	int ret;
252	unsigned int chn, i = 0;
253	__be32 tmp_buf[2] = { };
254
255	ret = dlh_start_capture_and_read(st);
256	if (ret)
257		goto out;
258
259	for_each_set_bit(chn, indio_dev->active_scan_mask,
260			 indio_dev->masklength) {
261		memcpy(&tmp_buf[i++],
262			&st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
263			DLH_NUM_DATA_BYTES);
264	}
265
266	iio_push_to_buffers(indio_dev, tmp_buf);
267
268out:
269	iio_trigger_notify_done(indio_dev->trig);
270
271	return IRQ_HANDLED;
272}
273
274static irqreturn_t dlh_interrupt(int irq, void *private)
275{
276	struct iio_dev *indio_dev = private;
277	struct dlh_state *st = iio_priv(indio_dev);
278
279	complete(&st->completion);
280
281	return IRQ_HANDLED;
282};
283
284static int dlh_probe(struct i2c_client *client)
285{
286	const struct i2c_device_id *id = i2c_client_get_device_id(client);
287	struct dlh_state *st;
288	struct iio_dev *indio_dev;
289	int ret;
290
291	if (!i2c_check_functionality(client->adapter,
292		I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
293		dev_err(&client->dev,
294			"adapter doesn't support required i2c functionality\n");
295		return -EOPNOTSUPP;
296	}
297
298	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
299	if (!indio_dev) {
300		dev_err(&client->dev, "failed to allocate iio device\n");
301		return -ENOMEM;
302	}
303
304	i2c_set_clientdata(client, indio_dev);
305
306	st = iio_priv(indio_dev);
307	st->info = dlh_info_tbl[id->driver_data];
308	st->client = client;
309	st->use_interrupt = false;
310
311	indio_dev->name = id->name;
312	indio_dev->info = &dlh_info;
313	indio_dev->modes = INDIO_DIRECT_MODE;
314	indio_dev->channels =  dlh_channels;
315	indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
316
317	if (client->irq > 0) {
318		ret = devm_request_threaded_irq(&client->dev, client->irq,
319			dlh_interrupt, NULL,
320			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
321			id->name, indio_dev);
322		if (ret) {
323			dev_err(&client->dev, "failed to allocate threaded irq");
324			return ret;
325		}
326
327		st->use_interrupt = true;
328		init_completion(&st->completion);
329	}
330
331	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
332		NULL, &dlh_trigger_handler, NULL);
333	if (ret) {
334		dev_err(&client->dev, "failed to setup iio buffer\n");
335		return ret;
336	}
337
338	ret = devm_iio_device_register(&client->dev, indio_dev);
339	if (ret)
340		dev_err(&client->dev, "failed to register iio device\n");
341
342	return ret;
343}
344
345static const struct of_device_id dlh_of_match[] = {
346	{ .compatible = "asc,dlhl60d" },
347	{ .compatible = "asc,dlhl60g" },
348	{}
349};
350MODULE_DEVICE_TABLE(of, dlh_of_match);
351
352static const struct i2c_device_id dlh_id[] = {
353	{ "dlhl60d",    dlhl60d },
354	{ "dlhl60g",    dlhl60g },
355	{}
356};
357MODULE_DEVICE_TABLE(i2c, dlh_id);
358
359static struct i2c_driver dlh_driver = {
360	.driver = {
361		.name = "dlhl60d",
362		.of_match_table = dlh_of_match,
363	},
364	.probe = dlh_probe,
365	.id_table = dlh_id,
366};
367module_i2c_driver(dlh_driver);
368
369MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
370MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
371MODULE_LICENSE("GPL v2");
372