devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/3] Add support for DLH pressure sensors
@ 2019-12-11  9:59 tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 1/3] iio: pressure: Add driver " tomislav.denis
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: tomislav.denis @ 2019-12-11  9:59 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree, tomislav.denis

From: Tomislav Denis <tomislav.denis@avl.com>

This patchset adds support for All Sensors DLH series low
voltage digital pressure sensors.

Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF

Changes in v4:
- unused includes removed
- fixed casting from big endian to cpu endian
- removed -i2c from all filenames
- removed _i2c from driver name and all functions 

Changes in v3:
- missing ack included in the commit message
- unneceseary iio_buffer_enabled check removed
- data ready trigger removed
- trigger handler and read raw functions refactored

Changes in v2:
- web page link in the MAINTAINERS file fixed
- adjust the units of the output to the IIO ABI
- unneceseary default case removed
- define the channel member of the iio_chan_spec
  struct for channels specification
- remove explicit cast for pointers of type void *
- add support for the EOC(data ready) pin
- drop the unneceseary return ret;
- rename dlh-i2c.yaml to asc,dlh-i2c.yaml
- change the bindings copyright to GPL-2.0-only OR BSD-2-Clause
- document EOC(data ready) pin

Tomislav Denis (3):
  iio: pressure: Add driver for DLH pressure sensors
  dt-bindings: Add asc vendor
  bindings: iio: pressure: Add documentation for dlh driver

 .../devicetree/bindings/iio/pressure/asc,dlh.yaml  |  51 +++
 .../devicetree/bindings/vendor-prefixes.yaml       |   2 +
 MAINTAINERS                                        |   8 +
 drivers/iio/pressure/Kconfig                       |  12 +
 drivers/iio/pressure/Makefile                      |   1 +
 drivers/iio/pressure/dlh.c                         | 375 +++++++++++++++++++++
 6 files changed, 449 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
 create mode 100644 drivers/iio/pressure/dlh.c

-- 
2.7.4


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH v4 1/3] iio: pressure: Add driver for DLH pressure sensors
  2019-12-11  9:59 [PATCH v4 0/3] Add support for DLH pressure sensors tomislav.denis
@ 2019-12-11  9:59 ` tomislav.denis
  2019-12-23 17:42   ` Jonathan Cameron
  2019-12-11  9:59 ` [PATCH v4 2/3] dt-bindings: Add asc vendor tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver tomislav.denis
  2 siblings, 1 reply; 7+ messages in thread
From: tomislav.denis @ 2019-12-11  9:59 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree, tomislav.denis

From: Tomislav Denis <tomislav.denis@avl.com>

All Sensors DLH is series of low voltage digital pressure sensors.
Additionally to pressure value sensors deliver a temperature value.
Sensors can be accessed over I2C and SPI, this driver supports
only I2C access.

Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
---
 MAINTAINERS                   |   7 +
 drivers/iio/pressure/Kconfig  |  12 ++
 drivers/iio/pressure/Makefile |   1 +
 drivers/iio/pressure/dlh.c    | 375 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/iio/pressure/dlh.c

diff --git a/MAINTAINERS b/MAINTAINERS
index d5ea4e4..0fa24df 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -668,6 +668,13 @@ S:	Maintained
 F:	Documentation/i2c/busses/i2c-ali1563.rst
 F:	drivers/i2c/busses/i2c-ali1563.c
 
+ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER
+M:	Tomislav Denis <tomislav.denis@avl.com>
+W:	http://www.allsensors.com/
+S:	Maintained
+L:	linux-iio@vger.kernel.org
+F:	drivers/iio/pressure/dlh.c
+
 ALLEGRO DVT VIDEO IP CORE DRIVER
 M:	Michael Tretter <m.tretter@pengutronix.de>
 R:	Pengutronix Kernel Team <kernel@pengutronix.de>
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index ba420e4..3d383d8 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -53,6 +53,18 @@ config IIO_CROS_EC_BARO
 	  To compile this driver as a module, choose M here: the module
 	  will be called cros_ec_baro.
 
+config DLH
+	tristate "All Sensors DLH series low voltage digital pressure sensors"
+	depends on I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for the All Sensors DLH series
+	  pressure sensors driver.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called dlh.
+
 config DPS310
 	tristate "Infineon DPS310 pressure and temperature sensor"
 	depends on I2C
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index d8f5ace..c609ad4 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
 bmp280-objs := bmp280-core.o bmp280-regmap.o
 obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
 obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
+obj-$(CONFIG_DLH) += dlh.o
 obj-$(CONFIG_DPS310) += dps310.o
 obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
 obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
diff --git a/drivers/iio/pressure/dlh.c b/drivers/iio/pressure/dlh.c
new file mode 100644
index 0000000..18fc4e1
--- /dev/null
+++ b/drivers/iio/pressure/dlh.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * All Sensors DLH series low voltage digital pressure sensors
+ *
+ * Copyright (c) 2019 AVL DiTEST GmbH
+ *   Tomislav Denis <tomislav.denis@avl.com>
+ *
+ * Datasheet: http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <asm/unaligned.h>
+
+/* Commands */
+#define DLH_START_SINGLE    0xAA
+
+/* Status bits */
+#define DLH_STATUS_OK       0x40
+
+/* DLH  data format */
+#define DLH_NUM_READ_BYTES  7
+#define DLH_NUM_DATA_BYTES  3
+#define DLH_NUM_PR_BITS     24
+#define DLH_NUM_TEMP_BITS   24
+
+/* DLH  timings */
+#define DLH_SINGLE_DUT_MS   5
+
+enum dhl_ids {
+	dlhl60d,
+	dlhl60g,
+};
+
+struct dlh_info {
+	u8 osdig;           /* digital offset factor */
+	unsigned int fss;   /* full scale span (inch H2O) */
+};
+
+struct dlh_state {
+	struct i2c_client *client;
+	struct dlh_info info;
+	bool use_interrupt;
+	struct completion completion;
+	u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned;
+};
+
+static struct dlh_info dlh_info_tbl[] = {
+	[dlhl60d] = {
+		.osdig = 2,
+		.fss = 120,
+	},
+	[dlhl60g] = {
+		.osdig = 10,
+		.fss = 60,
+	},
+};
+
+
+static int dlh_cmd_start_single(struct dlh_state *st)
+{
+	int ret;
+
+	ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
+	if (ret)
+		dev_err(&st->client->dev,
+			"%s: I2C write byte failed\n", __func__);
+
+	return ret;
+}
+
+static int dlh_cmd_read_data(struct dlh_state *st)
+{
+	int ret;
+
+	ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
+	if (ret < 0) {
+		dev_err(&st->client->dev,
+			"%s: I2C read block failed\n", __func__);
+		return ret;
+	}
+
+	if (st->rx_buf[0] != DLH_STATUS_OK) {
+		dev_err(&st->client->dev,
+			"%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int dlh_start_capture_and_read(struct dlh_state *st)
+{
+	int ret;
+
+	if (st->use_interrupt)
+		reinit_completion(&st->completion);
+
+	ret = dlh_cmd_start_single(st);
+	if (ret)
+		return ret;
+
+	if (st->use_interrupt) {
+		ret = wait_for_completion_timeout(&st->completion,
+			msecs_to_jiffies(DLH_SINGLE_DUT_MS));
+		if (!ret) {
+			dev_err(&st->client->dev,
+				"%s: conversion timed out\n", __func__);
+			return -ETIMEDOUT;
+		}
+	} else {
+		mdelay(DLH_SINGLE_DUT_MS);
+	}
+
+	return dlh_cmd_read_data(st);
+}
+
+static int dlh_read_direct(struct dlh_state *st,
+	unsigned int *pressure, unsigned int *temperature)
+{
+	int ret;
+
+	ret = dlh_start_capture_and_read(st);
+	if (ret)
+		return ret;
+
+	*pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
+	*temperature = get_unaligned_be32(&st->rx_buf[3]) &
+		GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
+
+	return 0;
+}
+
+static int dlh_read_raw(struct iio_dev *indio_dev,
+	struct iio_chan_spec const *channel, int *value,
+	int *value2, long mask)
+{
+	struct dlh_state *st = iio_priv(indio_dev);
+	unsigned int pressure, temperature;
+	int ret;
+	s64 tmp;
+	s32 rem;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		ret = dlh_read_direct(st, &pressure, &temperature);
+		iio_device_release_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+
+		switch (channel->type) {
+		case IIO_PRESSURE:
+			*value = pressure;
+			return IIO_VAL_INT;
+
+		case IIO_TEMP:
+			*value = temperature;
+			return IIO_VAL_INT;
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_SCALE:
+		switch (channel->type) {
+		case IIO_PRESSURE:
+			tmp = div_s64(125LL * st->info.fss * 24909 * 100,
+				1 << DLH_NUM_PR_BITS);
+			tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+			*value = tmp;
+			*value2 = rem;
+			return IIO_VAL_INT_PLUS_NANO;
+
+		case IIO_TEMP:
+			*value = 125 * 1000;
+			*value2 = DLH_NUM_TEMP_BITS;
+			return IIO_VAL_FRACTIONAL_LOG2;
+
+		default:
+			return -EINVAL;
+		}
+	case IIO_CHAN_INFO_OFFSET:
+		switch (channel->type) {
+		case IIO_PRESSURE:
+			*value = -125 * st->info.fss * 24909;
+			*value2 = 100 * st->info.osdig * 100000;
+			return IIO_VAL_FRACTIONAL;
+
+		case IIO_TEMP:
+			*value = -40 * 1000;
+			return IIO_VAL_INT;
+
+		default:
+			return -EINVAL;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info dlh_info = {
+	.read_raw = dlh_read_raw,
+};
+
+static const struct iio_chan_spec dlh_channels[] = {
+	{
+		.type = IIO_PRESSURE,
+		.indexed = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type =
+			BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_OFFSET),
+		.scan_index = 0,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = DLH_NUM_PR_BITS,
+			.storagebits = 32,
+			.shift = 8,
+			.endianness = IIO_BE,
+		},
+	}, {
+		.type = IIO_TEMP,
+		.indexed = 1,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type =
+			BIT(IIO_CHAN_INFO_SCALE) |
+			BIT(IIO_CHAN_INFO_OFFSET),
+		.scan_index = 1,
+		.scan_type = {
+			.sign = 'u',
+			.realbits = DLH_NUM_TEMP_BITS,
+			.storagebits = 32,
+			.shift = 8,
+			.endianness = IIO_BE,
+		},
+	}
+};
+
+static irqreturn_t dlh_trigger_handler(int irq, void *private)
+{
+	struct iio_poll_func *pf = private;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct dlh_state *st = iio_priv(indio_dev);
+	int ret;
+	unsigned int chn, i = 0;
+	__be32 tmp_buf[2];
+
+	ret = dlh_start_capture_and_read(st);
+	if (ret)
+		goto out;
+
+	for_each_set_bit(chn, indio_dev->active_scan_mask,
+		indio_dev->masklength) {
+		memcpy(tmp_buf + i,
+			&st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
+			DLH_NUM_DATA_BYTES);
+		i++;
+	}
+
+	iio_push_to_buffers(indio_dev, tmp_buf);
+
+out:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t dlh_interrupt(int irq, void *private)
+{
+	struct iio_dev *indio_dev = private;
+	struct dlh_state *st = iio_priv(indio_dev);
+
+	complete(&st->completion);
+
+	return IRQ_HANDLED;
+};
+
+static int dlh_probe(struct i2c_client *client,
+	const struct i2c_device_id *id)
+{
+	struct dlh_state *st;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
+		dev_err(&client->dev,
+			"adapter doesn't support required i2c functionality\n");
+		return -EOPNOTSUPP;
+	}
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+	if (!indio_dev) {
+		dev_err(&client->dev, "failed to allocate iio device\n");
+		return -ENOMEM;
+	}
+
+	i2c_set_clientdata(client, indio_dev);
+
+	st = iio_priv(indio_dev);
+	st->info = dlh_info_tbl[id->driver_data];
+	st->client = client;
+	st->use_interrupt = false;
+
+	indio_dev->name = id->name;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->dev.of_node = client->dev.of_node;
+	indio_dev->info = &dlh_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels =  dlh_channels;
+	indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
+
+	if (client->irq > 0) {
+		ret = devm_request_threaded_irq(&client->dev, client->irq,
+			dlh_interrupt, NULL,
+			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+			id->name, indio_dev);
+		if (ret) {
+			dev_err(&client->dev, "failed to allocate threaded irq");
+			return ret;
+		}
+
+		st->use_interrupt = true;
+		init_completion(&st->completion);
+	}
+
+	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+		NULL, &dlh_trigger_handler, NULL);
+	if (ret) {
+		dev_err(&client->dev, "failed to setup iio buffer\n");
+		return ret;
+	}
+
+	ret = devm_iio_device_register(&client->dev, indio_dev);
+	if (ret)
+		dev_err(&client->dev, "failed to register iio device\n");
+
+	return ret;
+}
+
+static const struct of_device_id dlh_of_match[] = {
+	{ .compatible = "asc,dlhl60d" },
+	{ .compatible = "asc,dlhl60g" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, dlh_of_match);
+
+static const struct i2c_device_id dlh_id[] = {
+	{ "dlhl60d",    dlhl60d },
+	{ "dlhl60g",    dlhl60g },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, dlh_id);
+
+static struct i2c_driver dlh_driver = {
+	.driver = {
+		.name = "dlh",
+		.of_match_table = dlh_of_match,
+	},
+	.probe = dlh_probe,
+	.id_table = dlh_id,
+};
+module_i2c_driver(dlh_driver);
+
+MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
+MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 2/3] dt-bindings: Add asc vendor
  2019-12-11  9:59 [PATCH v4 0/3] Add support for DLH pressure sensors tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 1/3] iio: pressure: Add driver " tomislav.denis
@ 2019-12-11  9:59 ` tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver tomislav.denis
  2 siblings, 0 replies; 7+ messages in thread
From: tomislav.denis @ 2019-12-11  9:59 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree, tomislav.denis

From: Tomislav Denis <tomislav.denis@avl.com>

All Sensors Corporation is a manufacturer of MEMS piezoresitive
ultra low pressure sensors and pressure transducers.

Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
Acked-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 967e78c..88247b3 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -109,6 +109,8 @@ patternProperties:
     description: Artesyn Embedded Technologies Inc.
   "^asahi-kasei,.*":
     description: Asahi Kasei Corp.
+  "^asc,.*":
+    description: All Sensors Corporation
   "^aspeed,.*":
     description: ASPEED Technology Inc.
   "^asus,.*":
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver
  2019-12-11  9:59 [PATCH v4 0/3] Add support for DLH pressure sensors tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 1/3] iio: pressure: Add driver " tomislav.denis
  2019-12-11  9:59 ` [PATCH v4 2/3] dt-bindings: Add asc vendor tomislav.denis
@ 2019-12-11  9:59 ` tomislav.denis
  2019-12-23 17:48   ` Jonathan Cameron
  2 siblings, 1 reply; 7+ messages in thread
From: tomislav.denis @ 2019-12-11  9:59 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree, tomislav.denis

From: Tomislav Denis <tomislav.denis@avl.com>

Add a device tree binding documentation for DLH series pressure
sensors.

Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
---
 .../devicetree/bindings/iio/pressure/asc,dlh.yaml  | 51 ++++++++++++++++++++++
 MAINTAINERS                                        |  1 +
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml

diff --git a/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml b/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
new file mode 100644
index 0000000..c2c1817
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
@@ -0,0 +1,51 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/pressure/dlh.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: All Sensors DLH series low voltage digital pressure sensors
+
+maintainers:
+  - Tomislav Denis <tomislav.denis@avl.com>
+
+description: |
+  Bindings for the All Sensors DLH series pressure sensors.
+
+  Specifications about the sensors can be found at:
+    http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
+
+properties:
+  compatible:
+    enum:
+      - asc,dlhl60d
+      - asc,dlhl60g
+
+  reg:
+    description: I2C device address
+    maxItems: 1
+
+  interrupts:
+    description: interrupt mapping for EOC(data ready) pin
+    maxItems: 1
+
+required:
+  - compatible
+  - reg
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    i2c0 {
+      #address-cells = <1>;
+      #size-cells = <0>;
+
+      pressure@29 {
+          compatible = "asc,dlhl60d";
+          reg = <0x29>;
+          interrupt-parent = <&gpio0>;
+          interrupts = <10 IRQ_TYPE_EDGE_RISING>;
+      };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index 0fa24df..7e181b9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -674,6 +674,7 @@ W:	http://www.allsensors.com/
 S:	Maintained
 L:	linux-iio@vger.kernel.org
 F:	drivers/iio/pressure/dlh.c
+F:	Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
 
 ALLEGRO DVT VIDEO IP CORE DRIVER
 M:	Michael Tretter <m.tretter@pengutronix.de>
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 1/3] iio: pressure: Add driver for DLH pressure sensors
  2019-12-11  9:59 ` [PATCH v4 1/3] iio: pressure: Add driver " tomislav.denis
@ 2019-12-23 17:42   ` Jonathan Cameron
  2019-12-25  8:09     ` Denis, Tomislav AVL DiTEST
  0 siblings, 1 reply; 7+ messages in thread
From: Jonathan Cameron @ 2019-12-23 17:42 UTC (permalink / raw)
  To: tomislav.denis; +Cc: linux-iio, devicetree

On Wed, 11 Dec 2019 10:59:44 +0100
<tomislav.denis@avl.com> wrote:

> From: Tomislav Denis <tomislav.denis@avl.com>
> 
> All Sensors DLH is series of low voltage digital pressure sensors.
> Additionally to pressure value sensors deliver a temperature value.
> Sensors can be accessed over I2C and SPI, this driver supports
> only I2C access.
> 
> Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>

Gah. My explanation on the wildcard naming thing was not clear before.
Sorry about that.  What I meant was this needs to have an explicit
name after a single part number.   I've renamed
as dlhl60d.c and all similar places.
I left the define prefix as DLH_ as unlikely we'll get any problems
with that.

Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to see if they can break it.

Thanks,

Jonathan

> ---
>  MAINTAINERS                   |   7 +
>  drivers/iio/pressure/Kconfig  |  12 ++
>  drivers/iio/pressure/Makefile |   1 +
>  drivers/iio/pressure/dlh.c    | 375 ++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 395 insertions(+)
>  create mode 100644 drivers/iio/pressure/dlh.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index d5ea4e4..0fa24df 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -668,6 +668,13 @@ S:	Maintained
>  F:	Documentation/i2c/busses/i2c-ali1563.rst
>  F:	drivers/i2c/busses/i2c-ali1563.c
>  
> +ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER
> +M:	Tomislav Denis <tomislav.denis@avl.com>
> +W:	http://www.allsensors.com/
> +S:	Maintained
> +L:	linux-iio@vger.kernel.org
> +F:	drivers/iio/pressure/dlh.c
> +
>  ALLEGRO DVT VIDEO IP CORE DRIVER
>  M:	Michael Tretter <m.tretter@pengutronix.de>
>  R:	Pengutronix Kernel Team <kernel@pengutronix.de>
> diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
> index ba420e4..3d383d8 100644
> --- a/drivers/iio/pressure/Kconfig
> +++ b/drivers/iio/pressure/Kconfig
> @@ -53,6 +53,18 @@ config IIO_CROS_EC_BARO
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called cros_ec_baro.
>  
> +config DLH
> +	tristate "All Sensors DLH series low voltage digital pressure sensors"
> +	depends on I2C
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build support for the All Sensors DLH series
> +	  pressure sensors driver.
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called dlh.
> +
>  config DPS310
>  	tristate "Infineon DPS310 pressure and temperature sensor"
>  	depends on I2C
> diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
> index d8f5ace..c609ad4 100644
> --- a/drivers/iio/pressure/Makefile
> +++ b/drivers/iio/pressure/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
>  bmp280-objs := bmp280-core.o bmp280-regmap.o
>  obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
>  obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
> +obj-$(CONFIG_DLH) += dlh.o
>  obj-$(CONFIG_DPS310) += dps310.o
>  obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
>  obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
> diff --git a/drivers/iio/pressure/dlh.c b/drivers/iio/pressure/dlh.c
> new file mode 100644
> index 0000000..18fc4e1
> --- /dev/null
> +++ b/drivers/iio/pressure/dlh.c
> @@ -0,0 +1,375 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * All Sensors DLH series low voltage digital pressure sensors
> + *
> + * Copyright (c) 2019 AVL DiTEST GmbH
> + *   Tomislav Denis <tomislav.denis@avl.com>
> + *
> + * Datasheet: http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/i2c.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <asm/unaligned.h>
> +
> +/* Commands */
> +#define DLH_START_SINGLE    0xAA
> +
> +/* Status bits */
> +#define DLH_STATUS_OK       0x40
> +
> +/* DLH  data format */
> +#define DLH_NUM_READ_BYTES  7
> +#define DLH_NUM_DATA_BYTES  3
> +#define DLH_NUM_PR_BITS     24
> +#define DLH_NUM_TEMP_BITS   24
> +
> +/* DLH  timings */
> +#define DLH_SINGLE_DUT_MS   5
> +
> +enum dhl_ids {
> +	dlhl60d,
> +	dlhl60g,
> +};
> +
> +struct dlh_info {
> +	u8 osdig;           /* digital offset factor */
> +	unsigned int fss;   /* full scale span (inch H2O) */
> +};
> +
> +struct dlh_state {
> +	struct i2c_client *client;
> +	struct dlh_info info;
> +	bool use_interrupt;
> +	struct completion completion;
> +	u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned;
> +};
> +
> +static struct dlh_info dlh_info_tbl[] = {
> +	[dlhl60d] = {
> +		.osdig = 2,
> +		.fss = 120,
> +	},
> +	[dlhl60g] = {
> +		.osdig = 10,
> +		.fss = 60,
> +	},
> +};
> +
> +
> +static int dlh_cmd_start_single(struct dlh_state *st)
> +{
> +	int ret;
> +
> +	ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
> +	if (ret)
> +		dev_err(&st->client->dev,
> +			"%s: I2C write byte failed\n", __func__);
> +
> +	return ret;
> +}
> +
> +static int dlh_cmd_read_data(struct dlh_state *st)
> +{
> +	int ret;
> +
> +	ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
> +	if (ret < 0) {
> +		dev_err(&st->client->dev,
> +			"%s: I2C read block failed\n", __func__);
> +		return ret;
> +	}
> +
> +	if (st->rx_buf[0] != DLH_STATUS_OK) {
> +		dev_err(&st->client->dev,
> +			"%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
> +		return -EBUSY;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dlh_start_capture_and_read(struct dlh_state *st)
> +{
> +	int ret;
> +
> +	if (st->use_interrupt)
> +		reinit_completion(&st->completion);
> +
> +	ret = dlh_cmd_start_single(st);
> +	if (ret)
> +		return ret;
> +
> +	if (st->use_interrupt) {
> +		ret = wait_for_completion_timeout(&st->completion,
> +			msecs_to_jiffies(DLH_SINGLE_DUT_MS));
> +		if (!ret) {
> +			dev_err(&st->client->dev,
> +				"%s: conversion timed out\n", __func__);
> +			return -ETIMEDOUT;
> +		}
> +	} else {
> +		mdelay(DLH_SINGLE_DUT_MS);
> +	}
> +
> +	return dlh_cmd_read_data(st);
> +}
> +
> +static int dlh_read_direct(struct dlh_state *st,
> +	unsigned int *pressure, unsigned int *temperature)
> +{
> +	int ret;
> +
> +	ret = dlh_start_capture_and_read(st);
> +	if (ret)
> +		return ret;
> +
> +	*pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
> +	*temperature = get_unaligned_be32(&st->rx_buf[3]) &
> +		GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
> +
> +	return 0;
> +}
> +
> +static int dlh_read_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *channel, int *value,
> +	int *value2, long mask)
> +{
> +	struct dlh_state *st = iio_priv(indio_dev);
> +	unsigned int pressure, temperature;
> +	int ret;
> +	s64 tmp;
> +	s32 rem;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +
> +		ret = dlh_read_direct(st, &pressure, &temperature);
> +		iio_device_release_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +
> +		switch (channel->type) {
> +		case IIO_PRESSURE:
> +			*value = pressure;
> +			return IIO_VAL_INT;
> +
> +		case IIO_TEMP:
> +			*value = temperature;
> +			return IIO_VAL_INT;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_SCALE:
> +		switch (channel->type) {
> +		case IIO_PRESSURE:
> +			tmp = div_s64(125LL * st->info.fss * 24909 * 100,
> +				1 << DLH_NUM_PR_BITS);
> +			tmp = div_s64_rem(tmp, 1000000000LL, &rem);
> +			*value = tmp;
> +			*value2 = rem;
> +			return IIO_VAL_INT_PLUS_NANO;
> +
> +		case IIO_TEMP:
> +			*value = 125 * 1000;
> +			*value2 = DLH_NUM_TEMP_BITS;
> +			return IIO_VAL_FRACTIONAL_LOG2;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +	case IIO_CHAN_INFO_OFFSET:
> +		switch (channel->type) {
> +		case IIO_PRESSURE:
> +			*value = -125 * st->info.fss * 24909;
> +			*value2 = 100 * st->info.osdig * 100000;
> +			return IIO_VAL_FRACTIONAL;
> +
> +		case IIO_TEMP:
> +			*value = -40 * 1000;
> +			return IIO_VAL_INT;
> +
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info dlh_info = {
> +	.read_raw = dlh_read_raw,
> +};
> +
> +static const struct iio_chan_spec dlh_channels[] = {
> +	{
> +		.type = IIO_PRESSURE,
> +		.indexed = 1,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type =
> +			BIT(IIO_CHAN_INFO_SCALE) |
> +			BIT(IIO_CHAN_INFO_OFFSET),
> +		.scan_index = 0,
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = DLH_NUM_PR_BITS,
> +			.storagebits = 32,
> +			.shift = 8,
> +			.endianness = IIO_BE,
> +		},
> +	}, {
> +		.type = IIO_TEMP,
> +		.indexed = 1,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type =
> +			BIT(IIO_CHAN_INFO_SCALE) |
> +			BIT(IIO_CHAN_INFO_OFFSET),
> +		.scan_index = 1,
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = DLH_NUM_TEMP_BITS,
> +			.storagebits = 32,
> +			.shift = 8,
> +			.endianness = IIO_BE,
> +		},
> +	}
> +};
> +
> +static irqreturn_t dlh_trigger_handler(int irq, void *private)
> +{
> +	struct iio_poll_func *pf = private;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct dlh_state *st = iio_priv(indio_dev);
> +	int ret;
> +	unsigned int chn, i = 0;
> +	__be32 tmp_buf[2];
> +
> +	ret = dlh_start_capture_and_read(st);
> +	if (ret)
> +		goto out;
> +
> +	for_each_set_bit(chn, indio_dev->active_scan_mask,
> +		indio_dev->masklength) {
> +		memcpy(tmp_buf + i,
> +			&st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
> +			DLH_NUM_DATA_BYTES);
> +		i++;
> +	}
> +
> +	iio_push_to_buffers(indio_dev, tmp_buf);
> +
> +out:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t dlh_interrupt(int irq, void *private)
> +{
> +	struct iio_dev *indio_dev = private;
> +	struct dlh_state *st = iio_priv(indio_dev);
> +
> +	complete(&st->completion);
> +
> +	return IRQ_HANDLED;
> +};
> +
> +static int dlh_probe(struct i2c_client *client,
> +	const struct i2c_device_id *id)
> +{
> +	struct dlh_state *st;
> +	struct iio_dev *indio_dev;
> +	int ret;
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
> +		dev_err(&client->dev,
> +			"adapter doesn't support required i2c functionality\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
> +	if (!indio_dev) {
> +		dev_err(&client->dev, "failed to allocate iio device\n");
> +		return -ENOMEM;
> +	}
> +
> +	i2c_set_clientdata(client, indio_dev);
> +
> +	st = iio_priv(indio_dev);
> +	st->info = dlh_info_tbl[id->driver_data];
> +	st->client = client;
> +	st->use_interrupt = false;
> +
> +	indio_dev->name = id->name;
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->dev.of_node = client->dev.of_node;
> +	indio_dev->info = &dlh_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels =  dlh_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
> +
> +	if (client->irq > 0) {
> +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> +			dlh_interrupt, NULL,
> +			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> +			id->name, indio_dev);
> +		if (ret) {
> +			dev_err(&client->dev, "failed to allocate threaded irq");
> +			return ret;
> +		}
> +
> +		st->use_interrupt = true;
> +		init_completion(&st->completion);
> +	}
> +
> +	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
> +		NULL, &dlh_trigger_handler, NULL);
> +	if (ret) {
> +		dev_err(&client->dev, "failed to setup iio buffer\n");
> +		return ret;
> +	}
> +
> +	ret = devm_iio_device_register(&client->dev, indio_dev);
> +	if (ret)
> +		dev_err(&client->dev, "failed to register iio device\n");
> +
> +	return ret;
> +}
> +
> +static const struct of_device_id dlh_of_match[] = {
> +	{ .compatible = "asc,dlhl60d" },
> +	{ .compatible = "asc,dlhl60g" },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(of, dlh_of_match);
> +
> +static const struct i2c_device_id dlh_id[] = {
> +	{ "dlhl60d",    dlhl60d },
> +	{ "dlhl60g",    dlhl60g },
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, dlh_id);
> +
> +static struct i2c_driver dlh_driver = {
> +	.driver = {
> +		.name = "dlh",
> +		.of_match_table = dlh_of_match,
> +	},
> +	.probe = dlh_probe,
> +	.id_table = dlh_id,
> +};
> +module_i2c_driver(dlh_driver);
> +
> +MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
> +MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
> +MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver
  2019-12-11  9:59 ` [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver tomislav.denis
@ 2019-12-23 17:48   ` Jonathan Cameron
  0 siblings, 0 replies; 7+ messages in thread
From: Jonathan Cameron @ 2019-12-23 17:48 UTC (permalink / raw)
  To: tomislav.denis; +Cc: linux-iio, devicetree

On Wed, 11 Dec 2019 10:59:46 +0100
<tomislav.denis@avl.com> wrote:

> From: Tomislav Denis <tomislav.denis@avl.com>
> 
> Add a device tree binding documentation for DLH series pressure
> sensors.
> 
> Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
A few things I fixed up whilst applying this.

Applied with these tweaks to the togreg branch of iio.git.

Please take a look and check I didn't mess this up.

Thanks,

Jonathan

> ---
>  .../devicetree/bindings/iio/pressure/asc,dlh.yaml  | 51 ++++++++++++++++++++++

Name is now asc,dlhl60d.yaml

>  MAINTAINERS                                        |  1 +
>  2 files changed, 52 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
> 
> diff --git a/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml b/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
> new file mode 100644
> index 0000000..c2c1817
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
> @@ -0,0 +1,51 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/iio/pressure/dlh.yaml#

This was already wrong, now has asc,dlhl60d.yaml

> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: All Sensors DLH series low voltage digital pressure sensors
> +
> +maintainers:
> +  - Tomislav Denis <tomislav.denis@avl.com>
> +
> +description: |
> +  Bindings for the All Sensors DLH series pressure sensors.
> +
> +  Specifications about the sensors can be found at:
> +    http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
> +
> +properties:
> +  compatible:
> +    enum:
> +      - asc,dlhl60d
> +      - asc,dlhl60g
> +
> +  reg:
> +    description: I2C device address
> +    maxItems: 1
> +
> +  interrupts:
> +    description: interrupt mapping for EOC(data ready) pin
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +
> +examples:
> +  - |
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +
> +    i2c0 {
Spacing is pretty random in here. Now consistent 4 space indent
throughout.


> +      #address-cells = <1>;
> +      #size-cells = <0>;
> +
> +      pressure@29 {
> +          compatible = "asc,dlhl60d";
> +          reg = <0x29>;
> +          interrupt-parent = <&gpio0>;
> +          interrupts = <10 IRQ_TYPE_EDGE_RISING>;
> +      };
> +    };
> +...
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 0fa24df..7e181b9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -674,6 +674,7 @@ W:	http://www.allsensors.com/
>  S:	Maintained
>  L:	linux-iio@vger.kernel.org
>  F:	drivers/iio/pressure/dlh.c
> +F:	Documentation/devicetree/bindings/iio/pressure/asc,dlh.yaml
>  
>  ALLEGRO DVT VIDEO IP CORE DRIVER
>  M:	Michael Tretter <m.tretter@pengutronix.de>


^ permalink raw reply	[flat|nested] 7+ messages in thread

* RE: [PATCH v4 1/3] iio: pressure: Add driver for DLH pressure sensors
  2019-12-23 17:42   ` Jonathan Cameron
@ 2019-12-25  8:09     ` Denis, Tomislav AVL DiTEST
  0 siblings, 0 replies; 7+ messages in thread
From: Denis, Tomislav AVL DiTEST @ 2019-12-25  8:09 UTC (permalink / raw)
  To: 'Jonathan Cameron'; +Cc: linux-iio, devicetree

> On Wed, 11 Dec 2019 10:59:44 +0100
> <tomislav.denis@avl.com> wrote:
> 
> > From: Tomislav Denis <tomislav.denis@avl.com>
> >
> > All Sensors DLH is series of low voltage digital pressure sensors.
> > Additionally to pressure value sensors deliver a temperature value.
> > Sensors can be accessed over I2C and SPI, this driver supports only
> > I2C access.
> >
> > Signed-off-by: Tomislav Denis <tomislav.denis@avl.com>
> 
> Gah. My explanation on the wildcard naming thing was not clear before.
> Sorry about that.  What I meant was this needs to have an explicit
> name after a single part number.   I've renamed
> as dlhl60d.c and all similar places.
> I left the define prefix as DLH_ as unlikely we'll get any problems with that.
> 
> Applied to the togreg branch of iio.git and pushed out as testing for the
> autobuilders to see if they can break it.
> 
> Thanks,
> 
> Jonathan
> 

Hi Jonathan,

Ok great. Thanks a lot for helping me with this, I've learned a lot from you.

Tomislav

> > ---
> >  MAINTAINERS                   |   7 +
> >  drivers/iio/pressure/Kconfig  |  12 ++
> >  drivers/iio/pressure/Makefile |   1 +
> >  drivers/iio/pressure/dlh.c    | 375
> ++++++++++++++++++++++++++++++++++++++++++
> >  4 files changed, 395 insertions(+)
> >  create mode 100644 drivers/iio/pressure/dlh.c
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index d5ea4e4..0fa24df 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -668,6 +668,13 @@ S:	Maintained
> >  F:	Documentation/i2c/busses/i2c-ali1563.rst
> >  F:	drivers/i2c/busses/i2c-ali1563.c
> >
> > +ALL SENSORS DLH SERIES PRESSURE SENSORS DRIVER
> > +M:	Tomislav Denis <tomislav.denis@avl.com>
> > +W:	http://www.allsensors.com/
> > +S:	Maintained
> > +L:	linux-iio@vger.kernel.org
> > +F:	drivers/iio/pressure/dlh.c
> > +
> >  ALLEGRO DVT VIDEO IP CORE DRIVER
> >  M:	Michael Tretter <m.tretter@pengutronix.de>
> >  R:	Pengutronix Kernel Team <kernel@pengutronix.de>
> > diff --git a/drivers/iio/pressure/Kconfig
> > b/drivers/iio/pressure/Kconfig index ba420e4..3d383d8 100644
> > --- a/drivers/iio/pressure/Kconfig
> > +++ b/drivers/iio/pressure/Kconfig
> > @@ -53,6 +53,18 @@ config IIO_CROS_EC_BARO
> >  	  To compile this driver as a module, choose M here: the module
> >  	  will be called cros_ec_baro.
> >
> > +config DLH
> > +	tristate "All Sensors DLH series low voltage digital pressure sensors"
> > +	depends on I2C
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> > +	help
> > +	  Say yes here to build support for the All Sensors DLH series
> > +	  pressure sensors driver.
> > +
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called dlh.
> > +
> >  config DPS310
> >  	tristate "Infineon DPS310 pressure and temperature sensor"
> >  	depends on I2C
> > diff --git a/drivers/iio/pressure/Makefile
> > b/drivers/iio/pressure/Makefile index d8f5ace..c609ad4 100644
> > --- a/drivers/iio/pressure/Makefile
> > +++ b/drivers/iio/pressure/Makefile
> > @@ -9,6 +9,7 @@ obj-$(CONFIG_BMP280) += bmp280.o  bmp280-objs :=
> > bmp280-core.o bmp280-regmap.o
> >  obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
> >  obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
> > +obj-$(CONFIG_DLH) += dlh.o
> >  obj-$(CONFIG_DPS310) += dps310.o
> >  obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
> >  obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
> > diff --git a/drivers/iio/pressure/dlh.c b/drivers/iio/pressure/dlh.c
> > new file mode 100644 index 0000000..18fc4e1
> > --- /dev/null
> > +++ b/drivers/iio/pressure/dlh.c
> > @@ -0,0 +1,375 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * All Sensors DLH series low voltage digital pressure sensors
> > + *
> > + * Copyright (c) 2019 AVL DiTEST GmbH
> > + *   Tomislav Denis <tomislav.denis@avl.com>
> > + *
> > + * Datasheet: http://www.allsensors.com/cad/DS-0355_Rev_B.PDF
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <linux/delay.h>
> > +#include <linux/i2c.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/trigger_consumer.h> #include
> > +<linux/iio/triggered_buffer.h> #include <asm/unaligned.h>
> > +
> > +/* Commands */
> > +#define DLH_START_SINGLE    0xAA
> > +
> > +/* Status bits */
> > +#define DLH_STATUS_OK       0x40
> > +
> > +/* DLH  data format */
> > +#define DLH_NUM_READ_BYTES  7
> > +#define DLH_NUM_DATA_BYTES  3
> > +#define DLH_NUM_PR_BITS     24
> > +#define DLH_NUM_TEMP_BITS   24
> > +
> > +/* DLH  timings */
> > +#define DLH_SINGLE_DUT_MS   5
> > +
> > +enum dhl_ids {
> > +	dlhl60d,
> > +	dlhl60g,
> > +};
> > +
> > +struct dlh_info {
> > +	u8 osdig;           /* digital offset factor */
> > +	unsigned int fss;   /* full scale span (inch H2O) */
> > +};
> > +
> > +struct dlh_state {
> > +	struct i2c_client *client;
> > +	struct dlh_info info;
> > +	bool use_interrupt;
> > +	struct completion completion;
> > +	u8 rx_buf[DLH_NUM_READ_BYTES] ____cacheline_aligned; };
> > +
> > +static struct dlh_info dlh_info_tbl[] = {
> > +	[dlhl60d] = {
> > +		.osdig = 2,
> > +		.fss = 120,
> > +	},
> > +	[dlhl60g] = {
> > +		.osdig = 10,
> > +		.fss = 60,
> > +	},
> > +};
> > +
> > +
> > +static int dlh_cmd_start_single(struct dlh_state *st) {
> > +	int ret;
> > +
> > +	ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
> > +	if (ret)
> > +		dev_err(&st->client->dev,
> > +			"%s: I2C write byte failed\n", __func__);
> > +
> > +	return ret;
> > +}
> > +
> > +static int dlh_cmd_read_data(struct dlh_state *st) {
> > +	int ret;
> > +
> > +	ret = i2c_master_recv(st->client, st->rx_buf,
> DLH_NUM_READ_BYTES);
> > +	if (ret < 0) {
> > +		dev_err(&st->client->dev,
> > +			"%s: I2C read block failed\n", __func__);
> > +		return ret;
> > +	}
> > +
> > +	if (st->rx_buf[0] != DLH_STATUS_OK) {
> > +		dev_err(&st->client->dev,
> > +			"%s: invalid status 0x%02x\n", __func__, st-
> >rx_buf[0]);
> > +		return -EBUSY;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int dlh_start_capture_and_read(struct dlh_state *st) {
> > +	int ret;
> > +
> > +	if (st->use_interrupt)
> > +		reinit_completion(&st->completion);
> > +
> > +	ret = dlh_cmd_start_single(st);
> > +	if (ret)
> > +		return ret;
> > +
> > +	if (st->use_interrupt) {
> > +		ret = wait_for_completion_timeout(&st->completion,
> > +			msecs_to_jiffies(DLH_SINGLE_DUT_MS));
> > +		if (!ret) {
> > +			dev_err(&st->client->dev,
> > +				"%s: conversion timed out\n", __func__);
> > +			return -ETIMEDOUT;
> > +		}
> > +	} else {
> > +		mdelay(DLH_SINGLE_DUT_MS);
> > +	}
> > +
> > +	return dlh_cmd_read_data(st);
> > +}
> > +
> > +static int dlh_read_direct(struct dlh_state *st,
> > +	unsigned int *pressure, unsigned int *temperature) {
> > +	int ret;
> > +
> > +	ret = dlh_start_capture_and_read(st);
> > +	if (ret)
> > +		return ret;
> > +
> > +	*pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
> > +	*temperature = get_unaligned_be32(&st->rx_buf[3]) &
> > +		GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
> > +
> > +	return 0;
> > +}
> > +
> > +static int dlh_read_raw(struct iio_dev *indio_dev,
> > +	struct iio_chan_spec const *channel, int *value,
> > +	int *value2, long mask)
> > +{
> > +	struct dlh_state *st = iio_priv(indio_dev);
> > +	unsigned int pressure, temperature;
> > +	int ret;
> > +	s64 tmp;
> > +	s32 rem;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		ret = iio_device_claim_direct_mode(indio_dev);
> > +		if (ret)
> > +			return ret;
> > +
> > +		ret = dlh_read_direct(st, &pressure, &temperature);
> > +		iio_device_release_direct_mode(indio_dev);
> > +		if (ret)
> > +			return ret;
> > +
> > +		switch (channel->type) {
> > +		case IIO_PRESSURE:
> > +			*value = pressure;
> > +			return IIO_VAL_INT;
> > +
> > +		case IIO_TEMP:
> > +			*value = temperature;
> > +			return IIO_VAL_INT;
> > +
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_SCALE:
> > +		switch (channel->type) {
> > +		case IIO_PRESSURE:
> > +			tmp = div_s64(125LL * st->info.fss * 24909 * 100,
> > +				1 << DLH_NUM_PR_BITS);
> > +			tmp = div_s64_rem(tmp, 1000000000LL, &rem);
> > +			*value = tmp;
> > +			*value2 = rem;
> > +			return IIO_VAL_INT_PLUS_NANO;
> > +
> > +		case IIO_TEMP:
> > +			*value = 125 * 1000;
> > +			*value2 = DLH_NUM_TEMP_BITS;
> > +			return IIO_VAL_FRACTIONAL_LOG2;
> > +
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	case IIO_CHAN_INFO_OFFSET:
> > +		switch (channel->type) {
> > +		case IIO_PRESSURE:
> > +			*value = -125 * st->info.fss * 24909;
> > +			*value2 = 100 * st->info.osdig * 100000;
> > +			return IIO_VAL_FRACTIONAL;
> > +
> > +		case IIO_TEMP:
> > +			*value = -40 * 1000;
> > +			return IIO_VAL_INT;
> > +
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	return -EINVAL;
> > +}
> > +
> > +static const struct iio_info dlh_info = {
> > +	.read_raw = dlh_read_raw,
> > +};
> > +
> > +static const struct iio_chan_spec dlh_channels[] = {
> > +	{
> > +		.type = IIO_PRESSURE,
> > +		.indexed = 1,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> > +		.info_mask_shared_by_type =
> > +			BIT(IIO_CHAN_INFO_SCALE) |
> > +			BIT(IIO_CHAN_INFO_OFFSET),
> > +		.scan_index = 0,
> > +		.scan_type = {
> > +			.sign = 'u',
> > +			.realbits = DLH_NUM_PR_BITS,
> > +			.storagebits = 32,
> > +			.shift = 8,
> > +			.endianness = IIO_BE,
> > +		},
> > +	}, {
> > +		.type = IIO_TEMP,
> > +		.indexed = 1,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> > +		.info_mask_shared_by_type =
> > +			BIT(IIO_CHAN_INFO_SCALE) |
> > +			BIT(IIO_CHAN_INFO_OFFSET),
> > +		.scan_index = 1,
> > +		.scan_type = {
> > +			.sign = 'u',
> > +			.realbits = DLH_NUM_TEMP_BITS,
> > +			.storagebits = 32,
> > +			.shift = 8,
> > +			.endianness = IIO_BE,
> > +		},
> > +	}
> > +};
> > +
> > +static irqreturn_t dlh_trigger_handler(int irq, void *private) {
> > +	struct iio_poll_func *pf = private;
> > +	struct iio_dev *indio_dev = pf->indio_dev;
> > +	struct dlh_state *st = iio_priv(indio_dev);
> > +	int ret;
> > +	unsigned int chn, i = 0;
> > +	__be32 tmp_buf[2];
> > +
> > +	ret = dlh_start_capture_and_read(st);
> > +	if (ret)
> > +		goto out;
> > +
> > +	for_each_set_bit(chn, indio_dev->active_scan_mask,
> > +		indio_dev->masklength) {
> > +		memcpy(tmp_buf + i,
> > +			&st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
> > +			DLH_NUM_DATA_BYTES);
> > +		i++;
> > +	}
> > +
> > +	iio_push_to_buffers(indio_dev, tmp_buf);
> > +
> > +out:
> > +	iio_trigger_notify_done(indio_dev->trig);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t dlh_interrupt(int irq, void *private) {
> > +	struct iio_dev *indio_dev = private;
> > +	struct dlh_state *st = iio_priv(indio_dev);
> > +
> > +	complete(&st->completion);
> > +
> > +	return IRQ_HANDLED;
> > +};
> > +
> > +static int dlh_probe(struct i2c_client *client,
> > +	const struct i2c_device_id *id)
> > +{
> > +	struct dlh_state *st;
> > +	struct iio_dev *indio_dev;
> > +	int ret;
> > +
> > +	if (!i2c_check_functionality(client->adapter,
> > +		I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
> > +		dev_err(&client->dev,
> > +			"adapter doesn't support required i2c
> functionality\n");
> > +		return -EOPNOTSUPP;
> > +	}
> > +
> > +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
> > +	if (!indio_dev) {
> > +		dev_err(&client->dev, "failed to allocate iio device\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	i2c_set_clientdata(client, indio_dev);
> > +
> > +	st = iio_priv(indio_dev);
> > +	st->info = dlh_info_tbl[id->driver_data];
> > +	st->client = client;
> > +	st->use_interrupt = false;
> > +
> > +	indio_dev->name = id->name;
> > +	indio_dev->dev.parent = &client->dev;
> > +	indio_dev->dev.of_node = client->dev.of_node;
> > +	indio_dev->info = &dlh_info;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->channels =  dlh_channels;
> > +	indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
> > +
> > +	if (client->irq > 0) {
> > +		ret = devm_request_threaded_irq(&client->dev, client->irq,
> > +			dlh_interrupt, NULL,
> > +			IRQF_TRIGGER_RISING | IRQF_ONESHOT,
> > +			id->name, indio_dev);
> > +		if (ret) {
> > +			dev_err(&client->dev, "failed to allocate threaded
> irq");
> > +			return ret;
> > +		}
> > +
> > +		st->use_interrupt = true;
> > +		init_completion(&st->completion);
> > +	}
> > +
> > +	ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
> > +		NULL, &dlh_trigger_handler, NULL);
> > +	if (ret) {
> > +		dev_err(&client->dev, "failed to setup iio buffer\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_iio_device_register(&client->dev, indio_dev);
> > +	if (ret)
> > +		dev_err(&client->dev, "failed to register iio device\n");
> > +
> > +	return ret;
> > +}
> > +
> > +static const struct of_device_id dlh_of_match[] = {
> > +	{ .compatible = "asc,dlhl60d" },
> > +	{ .compatible = "asc,dlhl60g" },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(of, dlh_of_match);
> > +
> > +static const struct i2c_device_id dlh_id[] = {
> > +	{ "dlhl60d",    dlhl60d },
> > +	{ "dlhl60g",    dlhl60g },
> > +	{}
> > +};
> > +MODULE_DEVICE_TABLE(i2c, dlh_id);
> > +
> > +static struct i2c_driver dlh_driver = {
> > +	.driver = {
> > +		.name = "dlh",
> > +		.of_match_table = dlh_of_match,
> > +	},
> > +	.probe = dlh_probe,
> > +	.id_table = dlh_id,
> > +};
> > +module_i2c_driver(dlh_driver);
> > +
> > +MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
> > +MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure
> > +sensors"); MODULE_LICENSE("GPL v2");


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-12-25  8:41 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-12-11  9:59 [PATCH v4 0/3] Add support for DLH pressure sensors tomislav.denis
2019-12-11  9:59 ` [PATCH v4 1/3] iio: pressure: Add driver " tomislav.denis
2019-12-23 17:42   ` Jonathan Cameron
2019-12-25  8:09     ` Denis, Tomislav AVL DiTEST
2019-12-11  9:59 ` [PATCH v4 2/3] dt-bindings: Add asc vendor tomislav.denis
2019-12-11  9:59 ` [PATCH v4 3/3] bindings: iio: pressure: Add documentation for dlh driver tomislav.denis
2019-12-23 17:48   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).