All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
@ 2014-11-06  9:46 Markezana, William
  2014-11-06 16:17 ` Daniel Baluta
  0 siblings, 1 reply; 15+ messages in thread
From: Markezana, William @ 2014-11-06  9:46 UTC (permalink / raw)
  To: jic23, linux-iio; +Cc: linux-kernel

Signed-off-by: Ludovic <ludovic.tancerel@maplehightech.com>
---
 drivers/iio/pressure/Kconfig                      |   10 +
 drivers/iio/pressure/Makefile                     |    1 +
 drivers/iio/pressure/ms5637.c                     |  488 +++++++++++++++++++++
 drivers/staging/iio/Documentation/pressure/ms5637 |   25 ++
 4 files changed, 524 insertions(+)
 create mode 100644 drivers/iio/pressure/ms5637.c
 create mode 100644 drivers/staging/iio/Documentation/pressure/ms5637

diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index ffac8ac..4ba9bd5 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -41,6 +41,16 @@ config MPL3115
           To compile this driver as a module, choose M here: the module
           will be called mpl3115.
 
+config MS5637
+        tristate "MS5637 pressure & temperature sensor"
+        depends on I2C
+        help
+          If you say yes here you get support for the Measurement Specialties
+          MS5637 pressure and temperature sensor.
+
+          This driver can also be built as a module. If so, the module will
+          be called ms5637.
+
 config IIO_ST_PRESS
 	tristate "STMicroelectronics pressure sensor Driver"
 	depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index c53d250..50fe66e 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -6,6 +6,7 @@
 obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
 obj-$(CONFIG_MPL115) += mpl115.o
 obj-$(CONFIG_MPL3115) += mpl3115.o
+obj-$(CONFIG_MS5637) += ms5637.o
 obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
 st_pressure-y := st_pressure_core.o
 st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c
new file mode 100644
index 0000000..ea74636
--- /dev/null
+++ b/drivers/iio/pressure/ms5637.c
@@ -0,0 +1,488 @@
+/*
+ * ms5637.c - Support for Measurement Specialties MS5637 temperature sensor
+ *
+ * Copyright (c) 2014 Measurement Specialties
+ *
+ * Licensed under the GPL-2.
+ *
+ * (7-bit I2C slave address 0x77)
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+/* MS5637 Commands */
+#define MS5637_RESET				(0x1E)
+#define MS5637_TEMPERATURE_CONVERSION_START	(0x50)
+#define MS5637_PRESSURE_CONVERSION_START	(0x40)
+#define MS5637_ADC_READ				(0x00)
+#define MS5637_PROM_READ			(0xA0)
+#define MS5637_PROM_ELEMENTS_NB                 (7)
+
+#define ATTR_RESET				0
+#define ATTR_RESOLUTION				1
+
+static const u16 conversion_time[] = { 1000, 2000, 3000, 5000, 9000, 17000 };
+
+struct ms5637_dev {
+	struct i2c_client *client;
+	struct mutex lock; /* mutex protecting this data structure */
+	u16 calibration_coeffs[MS5637_PROM_ELEMENTS_NB];
+	bool got_calibration_words;
+	unsigned long last_update;
+	bool valid;
+	int temperature;
+	unsigned int pressure;
+	u8 resolution_index;
+};
+
+static int ms5637_get_calibration_coeffs(struct ms5637_dev *dev_data,
+					 unsigned char index, u16 *word)
+{
+	int ret = 0;
+
+	ret = i2c_smbus_read_word_swapped(dev_data->client,
+					  MS5637_PROM_READ + (index << 1));
+	if (ret < 0)
+		return ret;
+	*word = (u16) ret & 0xFFFF;
+	return 0;
+}
+
+static bool ms5637_crc_check(u16 *n_prom, u8 crc)
+{
+	u8 cnt, n_bit;
+	u16 n_rem, crc_read;
+
+	n_rem = 0x00;
+	crc_read = n_prom[0];
+	n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
+	n_prom[0] = (0x0FFF & (n_prom[0]));	/* Clear the CRC byte */
+
+	for (cnt = 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {
+		if (cnt % 2 == 1)
+			n_rem ^= n_prom[cnt >> 1] & 0x00FF;
+		else
+			n_rem ^= n_prom[cnt >> 1] >> 8;
+
+		for (n_bit = 8; n_bit > 0; n_bit--) {
+			if (n_rem & 0x8000)
+				n_rem = (n_rem << 1) ^ 0x3000;
+			else
+				n_rem <<= 1;
+		}
+	}
+	n_rem >>= 12;
+	n_prom[0] = crc_read;
+	return (n_rem == crc);
+}
+
+static int ms5637_fill_calibration_coeffs(struct ms5637_dev *dev_data)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < MS5637_PROM_ELEMENTS_NB; i++) {
+		ret = ms5637_get_calibration_coeffs(dev_data, i,
+				&dev_data->calibration_coeffs
+				[i]);
+
+		dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
+			dev_data->calibration_coeffs[i]);
+
+		if (ret < 0) {
+			dev_err(&dev_data->client->dev,
+				"unable to get calibration coefficients at address %d\n",
+				i + 1);
+			return ret;
+		}
+	}
+
+	if (!ms5637_crc_check(dev_data->calibration_coeffs,
+			      (dev_data->
+			       calibration_coeffs[0] & 0xF000) >> 12)) {
+		dev_err(&dev_data->client->dev,
+			"Calibration coefficients crc check error\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int ms5637_read_adc_value(struct ms5637_dev *dev_data, u32 *adc_value)
+{
+	int ret = 0;
+	u8 buf[3];
+
+	ret =
+	    i2c_smbus_read_i2c_block_data(dev_data->client, MS5637_ADC_READ, 3,
+					  buf);
+	if (ret < 0)
+		return ret;
+	dev_dbg(&dev_data->client->dev, "ADC raw value : %x %x %x\n", buf[0],
+		buf[1], buf[2]);
+	*adc_value = (buf[0] << 16) + (buf[1] << 8) + buf[0];
+	return 0;
+}
+
+static int ms5637_conversion_and_read_adc(struct ms5637_dev *dev_data,
+					  u32 *t_adc, u32 *p_adc)
+{
+	int ret;
+
+	/* Trigger Temperature conversion */
+	ret = i2c_smbus_write_byte(dev_data->client,
+				   MS5637_TEMPERATURE_CONVERSION_START
+				   + dev_data->resolution_index * 2);
+	if (ret < 0)
+		return ret;
+	usleep_range(conversion_time[dev_data->resolution_index],
+		     conversion_time[dev_data->resolution_index]+3000);
+	/* Retrieve ADC value */
+	ret = ms5637_read_adc_value(dev_data, t_adc);
+
+	/* Trigger Pressure conversion */
+	ret = i2c_smbus_write_byte(dev_data->client,
+				   MS5637_PRESSURE_CONVERSION_START
+				   + dev_data->resolution_index * 2);
+	if (ret < 0)
+		return ret;
+	usleep_range(conversion_time[dev_data->resolution_index],
+		     conversion_time[dev_data->resolution_index]+3000);
+	/* Retrieve ADC value */
+	ret = ms5637_read_adc_value(dev_data, p_adc);
+
+	if (ret < 0)
+		return ret;
+	return 0;
+}
+
+static int ms5637_read_temperature_and_pressure(struct ms5637_dev *dev_data)
+{
+	int ret = 0;
+	u32 t_adc, p_adc;
+	s32 dt, temp;
+	s64 off, sens, t2, off2, sens2;
+
+	mutex_lock(&dev_data->lock);
+
+	if (time_after(jiffies, dev_data->last_update + HZ / 2) ||
+	    !dev_data->valid) {
+		if (!dev_data->got_calibration_words) {
+			ret = ms5637_fill_calibration_coeffs(dev_data);
+			if (ret < 0)
+				goto out;
+
+			dev_data->got_calibration_words = true;
+		}
+
+		ret = ms5637_conversion_and_read_adc(dev_data, &t_adc, &p_adc);
+
+		if (ret < 0) {
+			dev_data->got_calibration_words = false;
+			dev_err(&dev_data->client->dev,
+				"unable to make sensor adc conversion\n");
+			goto out;
+		}
+
+		dt = (s32) t_adc - (dev_data->calibration_coeffs[5] << 8);
+
+		/* Actual temperature = 2000 + dT * TEMPSENS */
+		temp =
+		    2000 + (((s64) dt * dev_data->calibration_coeffs[6]) >> 23);
+
+		/* Second order temperature compensation */
+		if (temp < 2000) {
+			s64 tmp = (s64) temp - 2000;
+
+			t2 = (3 * ((s64) dt * (s64) dt)) >> 33;
+			off2 = (61 * tmp * tmp) >> 4;
+			sens2 = (29 * tmp * tmp) >> 4;
+
+			if (temp < -1500) {
+				s64 tmp = (s64) temp + 1500;
+
+				off2 += 17 * tmp * tmp;
+				sens2 += 9 * tmp * tmp;
+			}
+		} else {
+			t2 = (5 * ((s64) dt * (s64) dt)) >> 38;
+			off2 = 0;
+			sens2 = 0;
+		}
+
+		/* OFF = OFF_T1 + TCO * dT */
+		off = (((s64) dev_data->calibration_coeffs[2]) << 17)
+		    +
+		    ((((s64) dev_data->calibration_coeffs[4]) * (s64) dt) >> 6);
+		off -= off2;
+
+		/* Sensitivity at actual temperature = SENS_T1 + TCS * dT */
+		sens = (((s64) dev_data->calibration_coeffs[1]) << 16)
+		    + (((s64) dev_data->calibration_coeffs[3] * dt) >> 7);
+		sens -= sens2;
+
+		/* Temperature compensated pressure = D1 * SENS - OFF */
+		dev_data->temperature = (temp - t2) * 10;
+		dev_data->pressure = (u32) (((((s64) p_adc * sens) >> 21)
+					     - off) >> 15) / 100;
+
+		dev_data->last_update = jiffies;
+		dev_data->valid = true;
+	}
+ out:
+	mutex_unlock(&dev_data->lock);
+
+	return ret >= 0 ? 0 : ret;
+}
+
+static int ms5637_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *channel, int *val,
+			   int *val2, long mask)
+{
+	int ret;
+	struct ms5637_dev *dev_data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		switch (channel->type) {
+		case IIO_TEMP:	/* in 0.001 °C */
+			ret = ms5637_read_temperature_and_pressure(dev_data);
+			if (ret)
+				goto t_err;
+			*val = dev_data->temperature;
+			break;
+		case IIO_PRESSURE:	/* in mB */
+			ret = ms5637_read_temperature_and_pressure(dev_data);
+			if (ret)
+				goto p_err;
+			*val = dev_data->pressure;
+			break;
+		default:
+			return -EINVAL;
+		}
+		ret = IIO_VAL_INT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+ t_err:
+	dev_err(&indio_dev->dev, "Temperature read error\n");
+	return ret;
+ p_err:
+	dev_err(&indio_dev->dev, "Pressure read error\n");
+	return ret;
+}
+
+static const struct iio_chan_spec ms5637_channels[] = {
+	{
+	 .type = IIO_TEMP,
+	 .output = 1,
+	 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	 .scan_index = 0,
+	 },
+	{
+	 .type = IIO_PRESSURE,
+	 .output = 1,
+	 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	 .scan_index = 0,
+	 }
+};
+
+static ssize_t ms5637_read_attr(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct ms5637_dev *dev_data = iio_priv(indio_dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	int ret = 0;
+
+	switch (this_attr->address) {
+	case ATTR_RESET:
+		ret = sprintf(buf, "0\n");
+		break;
+	case ATTR_RESOLUTION:
+		ret = sprintf(buf, "%d\n", dev_data->resolution_index + 8);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static ssize_t ms5637_write_attr(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t len)
+{
+	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+	struct ms5637_dev *dev_data = iio_priv(indio_dev);
+	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+	u8 val;
+	int ret;
+
+	ret = kstrtou8(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	mutex_lock(&dev_data->lock);
+	switch (this_attr->address) {
+	case ATTR_RESET:
+		if (val != 1)
+			goto einval;
+
+		ret = i2c_smbus_write_byte(dev_data->client, MS5637_RESET);
+		if (ret < 0)
+			goto out;
+		usleep_range(3000, 6000);
+
+		break;
+	case ATTR_RESOLUTION:
+		if ((val < 8) || (val > 13))
+			goto einval;
+		dev_data->resolution_index = val - 8;
+		break;
+	default:
+		goto einval;
+	}
+
+	dev_data->valid = false;
+	goto out;
+ einval:
+	ret = -EINVAL;
+ out:
+	mutex_unlock(&dev_data->lock);
+
+	return ret ? ret : len;
+}
+
+static IIO_DEVICE_ATTR(in_reset,
+		       S_IRUGO | S_IWUSR,
+		       ms5637_read_attr, ms5637_write_attr, ATTR_RESET);
+static IIO_DEVICE_ATTR(in_resolution,
+		       S_IRUGO | S_IWUSR,
+		       ms5637_read_attr, ms5637_write_attr, ATTR_RESOLUTION);
+static IIO_CONST_ATTR(resolution_available, "8 9 10 11 12 13");
+
+static struct attribute *ms5637_attributes[] = {
+	&iio_dev_attr_in_reset.dev_attr.attr,
+	&iio_dev_attr_in_resolution.dev_attr.attr,
+	&iio_const_attr_resolution_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ms5637_attribute_group = {
+	.attrs = ms5637_attributes,
+};
+
+static const struct iio_info ms5637_info = {
+	.read_raw = ms5637_read_raw,
+	.attrs = &ms5637_attribute_group,
+	.driver_module = THIS_MODULE,
+};
+
+static int ms5637_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct ms5637_dev *dev_data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev,
+			"adapter does not support SMBus read word transactions\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_WRITE_BYTE)) {
+		dev_err(&client->dev,
+			"adapter does not support SMBus write byte transactions\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+		dev_err(&client->dev,
+			"adapter does not support SMBus read block transactions\n");
+		return -ENODEV;
+	}
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	dev_data = iio_priv(indio_dev);
+	dev_data->client = client;
+	mutex_init(&dev_data->lock);
+
+	indio_dev->info = &ms5637_info;
+	indio_dev->name = id->name;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = ms5637_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
+
+	i2c_set_clientdata(client, indio_dev);
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_write_byte(client, MS5637_RESET);
+	if (ret < 0)
+		return ret;
+	usleep_range(3000, 6000);
+
+	ret = ms5637_fill_calibration_coeffs(dev_data);
+	if (ret == 0)
+		dev_data->got_calibration_words = true;
+
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(&client->dev, "Driver initialization done");
+	return 0;
+}
+
+static int ms5637_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id ms5637_id[] = {
+	{"ms5637", 0},
+	{}
+};
+
+static struct i2c_driver ms5637_driver = {
+	.probe = ms5637_probe,
+	.remove = ms5637_remove,
+	.id_table = ms5637_id,
+	.driver = {
+		   .name = "ms5637",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+module_i2c_driver(ms5637_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Measurement Specialties MS5637 temperature driver");
+MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
+MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
diff --git a/drivers/staging/iio/Documentation/pressure/ms5637 b/drivers/staging/iio/Documentation/pressure/ms5637
new file mode 100644
index 0000000..d0d64f3
--- /dev/null
+++ b/drivers/staging/iio/Documentation/pressure/ms5637
@@ -0,0 +1,25 @@
+Kernel driver ms5637
+====================
+
+Supported chips:
+  * Measurement Specialties MS5637
+    Prefix: 'ms5637'
+    Addresses scanned: I2C 0x76
+    Datasheet: Available for download on meas-spec.com
+
+Authors:
+    William Markezana (Meas-Spec) <william.markezana@meas-spec.com>
+    Ludovic Tancerel <ludovic.tancerel@maplehightech.com>
+
+Description
+-----------
+
+The MS5637 is a single chip pressure & temperature sensor.
+The driver returns a milli-bar pressure value and a milli-degre celius value using the iio framework
+
+Via the iio sysfs interface, there are several attributes available.
+in_reset - Reset ms5637 device by writing a 1 in it.
+in_resolution - Set the number of bits for ADC resolution.
+out_pressure_input - Current pressure from ms5637 sensor (milli-bar)
+out_temp_input - Current temperature from htu21 sensor (milli-°C value)
+resolution_available - List of resolutions supported
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 15+ messages in thread
* [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
@ 2015-06-16 12:33 Ludovic
  2015-06-16 13:56 ` Peter Meerwald
  2015-06-16 20:42 ` Tomasz Duszynski
  0 siblings, 2 replies; 15+ messages in thread
From: Ludovic @ 2015-06-16 12:33 UTC (permalink / raw)
  To: jic23, knaack.h, pmeerw, ludovic.tancerel, linux-iio

MS5637 spec : http://www.meas-spec.com/product/pressure/MS5637-02BA03.aspx

This patch has already been submitted some time ago by William Markezana, it was originally developped by me.
Thank you for all the comments done. Hopefully, everything is ok now.

Thank you in advance for reviewing again.

Regards,
Ludovic
 
Signed-off-by: Ludovic <ludovic.tancerel@maplehightech.com>
---
 drivers/iio/pressure/Kconfig  |  10 +
 drivers/iio/pressure/Makefile |   1 +
 drivers/iio/pressure/ms5637.c | 475 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 486 insertions(+)
 create mode 100644 drivers/iio/pressure/ms5637.c

diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index a3be537..6c7faa1 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -52,6 +52,16 @@ config MPL3115
           To compile this driver as a module, choose M here: the module
           will be called mpl3115.
 
+config MS5637
+        tristate "MS5637 pressure & temperature sensor"
+        depends on I2C
+        help
+          If you say yes here you get support for the Measurement Specialties
+          MS5637 pressure and temperature sensor.
+
+          This driver can also be built as a module. If so, the module will
+          be called ms5637.
+
 config IIO_ST_PRESS
 	tristate "STMicroelectronics pressure sensor Driver"
 	depends on (I2C || SPI_MASTER) && SYSFS
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index 88011f2..2455f8e 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_BMP280) += bmp280.o
 obj-$(CONFIG_HID_SENSOR_PRESS)   += hid-sensor-press.o
 obj-$(CONFIG_MPL115) += mpl115.o
 obj-$(CONFIG_MPL3115) += mpl3115.o
+obj-$(CONFIG_MS5637) += ms5637.o
 obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
 st_pressure-y := st_pressure_core.o
 st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c
new file mode 100644
index 0000000..9c647d7
--- /dev/null
+++ b/drivers/iio/pressure/ms5637.c
@@ -0,0 +1,475 @@
+/*
+ * ms5637.c - Support for Measurement-Specialties ms5637
+ *            pressure & temperature sensor
+ *
+ * Copyright (c) 2014 Measurement-Specialties
+ *
+ * Licensed under the GPL-2.
+ *
+ * (7-bit I2C slave address 0x76)
+ *
+ */
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/jiffies.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+
+/* MS5637 Commands */
+#define MS5637_RESET				0x1E
+#define MS5637_TEMPERATURE_CONVERSION_START	0x50
+#define MS5637_PRESSURE_CONVERSION_START	0x40
+#define MS5637_ADC_READ				0x00
+#define MS5637_PROM_READ			0xA0
+#define MS5637_PROM_ELEMENTS_NB                 7
+
+static const u16 ms5637_conversion_time[] = { 1000, 2000, 3000,
+					      5000, 9000, 17000 };
+static const int ms5637_samp_freq[6][2] = { {1800, 0}, {900, 0},
+					    {450, 0}, {225, 0},
+					    {112, 500000}, {56, 250000},
+					  };
+
+struct ms5637_dev {
+	struct i2c_client *client;
+	struct mutex lock; /* mutex protecting this data structure */
+	u16 calibration_coeffs[MS5637_PROM_ELEMENTS_NB];
+	bool got_calibration_words;
+	unsigned long last_update;
+	bool valid;
+	int temperature;
+	unsigned int pressure;
+	u8 resolution_index;
+};
+
+static int ms5637_get_calibration_coeffs(struct ms5637_dev *dev_data,
+					 unsigned char index, u16 *word)
+{
+	int ret = 0;
+
+	ret = i2c_smbus_read_word_swapped(dev_data->client,
+					  MS5637_PROM_READ + (index << 1));
+	if (ret < 0)
+		return ret;
+	*word = (u16)ret & 0xFFFF;
+	return 0;
+}
+
+static bool ms5637_crc_check(u16 *n_prom, u8 crc)
+{
+	unsigned int cnt, n_bit;
+	u16 n_rem, crc_read;
+
+	n_rem = 0x0000;
+	crc_read = n_prom[0];
+	n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
+	n_prom[0] &= 0x0FFF;	/* Clear the CRC byte */
+
+	for (cnt = 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {
+		if (cnt % 2 == 1)
+			n_rem ^= n_prom[cnt >> 1] & 0x00FF;
+		else
+			n_rem ^= n_prom[cnt >> 1] >> 8;
+
+		for (n_bit = 8; n_bit > 0; n_bit--) {
+			if (n_rem & 0x8000)
+				n_rem = (n_rem << 1) ^ 0x3000;
+			else
+				n_rem <<= 1;
+		}
+	}
+	n_rem >>= 12;
+	n_prom[0] = crc_read;
+	return (n_rem == crc);
+}
+
+static int ms5637_fill_calibration_coeffs(struct ms5637_dev *dev_data)
+{
+	int i, ret = 0;
+
+	for (i = 0; i < MS5637_PROM_ELEMENTS_NB; i++) {
+		ret = ms5637_get_calibration_coeffs(
+					dev_data, i,
+					&dev_data->calibration_coeffs[i]);
+
+		if (ret < 0) {
+			dev_err(&dev_data->client->dev,
+				"Unable to get calibration coefficients at address %d\n",
+				i + 1);
+			return ret;
+		}
+
+		dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
+			dev_data->calibration_coeffs[i]);
+	}
+
+	ret = ms5637_crc_check(
+			dev_data->calibration_coeffs,
+			(dev_data->calibration_coeffs[0] & 0xF000) >> 12);
+	if (ret == 0)
+		dev_err(&dev_data->client->dev,
+			"Calibration coefficients crc check error\n");
+
+	return 0;
+}
+
+static int ms5637_read_adc_value(struct ms5637_dev *dev_data, u32 *adc_value)
+{
+	int ret;
+	u8 buf[3];
+
+	ret =
+	    i2c_smbus_read_i2c_block_data(dev_data->client, MS5637_ADC_READ, 3,
+					  buf);
+	if (ret < 0)
+		return ret;
+	dev_dbg(&dev_data->client->dev, "ADC raw value : %x %x %x\n", buf[0],
+		buf[1], buf[2]);
+	*adc_value = (buf[0] << 16) + (buf[1] << 8) + buf[2];
+	return 0;
+}
+
+static int ms5637_conversion_and_read_adc(struct ms5637_dev *dev_data,
+					  u32 *t_adc, u32 *p_adc)
+{
+	int ret;
+
+	dev_dbg(&dev_data->client->dev, "ms5637_conversion_and_read_adc");
+	/* Trigger Temperature conversion */
+	ret = i2c_smbus_write_byte(dev_data->client,
+				   MS5637_TEMPERATURE_CONVERSION_START
+				   + dev_data->resolution_index * 2);
+	if (ret < 0)
+		return ret;
+	usleep_range(ms5637_conversion_time[dev_data->resolution_index],
+		     ms5637_conversion_time[dev_data->resolution_index] + 3000);
+	/* Retrieve ADC value */
+	ret = ms5637_read_adc_value(dev_data, t_adc);
+	if (ret < 0)
+		return ret;
+
+	/* Trigger Pressure conversion */
+	ret = i2c_smbus_write_byte(dev_data->client,
+				   MS5637_PRESSURE_CONVERSION_START
+				   + dev_data->resolution_index * 2);
+	if (ret < 0)
+		return ret;
+	usleep_range(ms5637_conversion_time[dev_data->resolution_index],
+		     ms5637_conversion_time[dev_data->resolution_index] + 3000);
+	/* Retrieve ADC value */
+	ret = ms5637_read_adc_value(dev_data, p_adc);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ms5637_read_temperature_and_pressure(struct ms5637_dev *dev_data)
+{
+	int ret = 0;
+	u32 t_adc, p_adc;
+	s32 dt, temp;
+	s64 off, sens, t2, off2, sens2;
+
+	mutex_lock(&dev_data->lock);
+
+	if (time_after(jiffies, dev_data->last_update + HZ / 2) ||
+	    !dev_data->valid) {
+		if (!dev_data->got_calibration_words) {
+			ret = ms5637_fill_calibration_coeffs(dev_data);
+			if (ret != 0)
+				goto out;
+
+			dev_data->got_calibration_words = true;
+		}
+
+		ret = ms5637_conversion_and_read_adc(dev_data, &t_adc, &p_adc);
+		if (ret < 0) {
+			dev_data->got_calibration_words = false;
+			dev_err(&dev_data->client->dev,
+				"Unable to make sensor adc conversion\n");
+			goto out;
+		}
+
+		dt = (s32)t_adc - (dev_data->calibration_coeffs[5] << 8);
+
+		/* Actual temperature = 2000 + dT * TEMPSENS */
+		temp =
+		    2000 + (((s64)dt * dev_data->calibration_coeffs[6]) >> 23);
+
+		/* Second order temperature compensation */
+		if (temp < 2000) {
+			s64 tmp = (s64)temp - 2000;
+
+			t2 = (3 * ((s64)dt * (s64)dt)) >> 33;
+			off2 = (61 * tmp * tmp) >> 4;
+			sens2 = (29 * tmp * tmp) >> 4;
+
+			if (temp < -1500) {
+				s64 tmp = (s64)temp + 1500;
+
+				off2 += 17 * tmp * tmp;
+				sens2 += 9 * tmp * tmp;
+			}
+		} else {
+			t2 = (5 * ((s64)dt * (s64)dt)) >> 38;
+			off2 = 0;
+			sens2 = 0;
+		}
+
+		/* OFF = OFF_T1 + TCO * dT */
+		off = (((s64)dev_data->calibration_coeffs[2]) << 17) +
+		    ((((s64)dev_data->calibration_coeffs[4]) * (s64)dt) >> 6);
+		off -= off2;
+
+		/* Sensitivity at actual temperature = SENS_T1 + TCS * dT */
+		sens = (((s64)dev_data->calibration_coeffs[1]) << 16)
+		    + (((s64)dev_data->calibration_coeffs[3] * dt) >> 7);
+		sens -= sens2;
+
+		/* Temperature compensated pressure = D1 * SENS - OFF */
+		dev_data->temperature = (temp - t2) * 10;
+		dev_data->pressure = (u32)(((((s64)p_adc * sens) >> 21)
+					     - off) >> 15);
+
+		dev_data->last_update = jiffies;
+		dev_data->valid = true;
+	}
+ out:
+	mutex_unlock(&dev_data->lock);
+
+	return ret >= 0 ? 0 : ret;
+}
+
+static int ms5637_get_int_plus_micros_index(const int (*vals)[2], int n,
+					    int val, int val2)
+{
+	while (n-- > 0)
+		if (val == vals[n][0] && val2 == vals[n][1])
+			return n;
+	return -EINVAL;
+}
+
+static int ms5637_get_samp_freq_index(struct ms5637_dev *dev_data,
+				      int val, int val2)
+{
+	return ms5637_get_int_plus_micros_index(ms5637_samp_freq,
+				ARRAY_SIZE(ms5637_samp_freq), val, val2);
+}
+
+static ssize_t ms5637_show_int_plus_micros(char *buf,
+					   const int (*vals)[2], int n)
+{
+	size_t len = 0;
+
+	while (n-- > 0)
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+			"%d.%06d ", vals[n][0], vals[n][1]);
+
+	/* replace trailing space by newline */
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t ms5637_show_samp_freq_avail(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	return ms5637_show_int_plus_micros(buf, ms5637_samp_freq,
+					   ARRAY_SIZE(ms5637_samp_freq));
+}
+
+static int ms5637_read_raw(struct iio_dev *indio_dev,
+			   struct iio_chan_spec const *channel, int *val,
+			   int *val2, long mask)
+{
+	int ret;
+	struct ms5637_dev *dev_data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		ret = ms5637_read_temperature_and_pressure(dev_data);
+		if (ret)
+			goto err;
+		switch (channel->type) {
+		case IIO_TEMP:	/* in °C */
+			*val = dev_data->temperature / 1000;
+			*val2 = (dev_data->temperature -
+					(dev_data->temperature / 1000) * 1000
+				) * 1000;
+			break;
+		case IIO_PRESSURE:	/* in kPa */
+			*val = dev_data->pressure / 1000;
+			*val2 = (dev_data->pressure -
+				 (dev_data->pressure / 1000) * 1000) * 1000;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = ms5637_samp_freq[dev_data->resolution_index][0];
+		*val2 = ms5637_samp_freq[dev_data->resolution_index][1];
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return IIO_VAL_INT_PLUS_MICRO;
+ err:
+	dev_err(&indio_dev->dev, "Device read error\n");
+	return ret;
+}
+
+static int ms5637_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct ms5637_dev *dev_data = iio_priv(indio_dev);
+	int i;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		i = ms5637_get_samp_freq_index(dev_data, val, val2);
+		if (i < 0)
+			return -EINVAL;
+
+		dev_data->resolution_index = i;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct iio_chan_spec ms5637_channels[] = {
+	{
+	 .type = IIO_TEMP,
+	 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+	 },
+	{
+	 .type = IIO_PRESSURE,
+	 .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+	 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+	 }
+};
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq_avail);
+
+static struct attribute *ms5637_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ms5637_attribute_group = {
+	.attrs = ms5637_attributes,
+};
+
+static const struct iio_info ms5637_info = {
+	.read_raw = ms5637_read_raw,
+	.write_raw = ms5637_write_raw,
+	.attrs = &ms5637_attribute_group,
+	.driver_module = THIS_MODULE,
+};
+
+static int ms5637_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct ms5637_dev *dev_data;
+	struct iio_dev *indio_dev;
+	int ret;
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+		dev_err(&client->dev,
+			"Adapter does not support SMBus read word transactions\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_WRITE_BYTE)) {
+		dev_err(&client->dev,
+			"Adapter does not support SMBus write byte transactions\n");
+		return -ENODEV;
+	}
+
+	if (!i2c_check_functionality(client->adapter,
+				     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+		dev_err(&client->dev,
+			"Adapter does not support SMBus read block transactions\n");
+		return -ENODEV;
+	}
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	dev_data = iio_priv(indio_dev);
+	dev_data->client = client;
+	mutex_init(&dev_data->lock);
+
+	indio_dev->info = &ms5637_info;
+	indio_dev->name = id->name;
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = ms5637_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
+
+	i2c_set_clientdata(client, indio_dev);
+	ret = i2c_smbus_write_byte(client, MS5637_RESET);
+	if (ret < 0)
+		return ret;
+	usleep_range(3000, 6000);
+
+	ret = ms5637_fill_calibration_coeffs(dev_data);
+	if (ret == 0)
+		dev_data->got_calibration_words = true;
+	else
+		return ret;
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0)
+		return ret;
+
+	dev_dbg(&client->dev, "Driver initialization done");
+	return 0;
+}
+
+static int ms5637_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	devm_iio_device_unregister(&client->dev, indio_dev);
+
+	return 0;
+}
+
+static const struct i2c_device_id ms5637_id[] = {
+	{"ms5637", 0},
+	{}
+};
+
+static struct i2c_driver ms5637_driver = {
+	.probe = ms5637_probe,
+	.remove = ms5637_remove,
+	.id_table = ms5637_id,
+	.driver = {
+		   .name = "ms5637",
+		   .owner = THIS_MODULE,
+		   },
+};
+
+module_i2c_driver(ms5637_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Measurement-Specialties ms5637 temperature driver");
+MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
+MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
-- 
2.3.7


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

end of thread, other threads:[~2015-06-24 17:59 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-06  9:46 [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support Markezana, William
2014-11-06 16:17 ` Daniel Baluta
2014-11-06 16:54   ` Peter Meerwald
2014-11-06 17:38     ` Markezana, William
2014-11-06 19:48       ` Hartmut Knaack
2014-11-06 22:43     ` Hartmut Knaack
2014-11-08 12:09     ` Jonathan Cameron
2015-06-16 12:33 [PATCH] iio: ms5637: " Ludovic
2015-06-16 13:56 ` Peter Meerwald
2015-06-17  8:18   ` ludovic.tancerel
2015-06-16 20:42 ` Tomasz Duszynski
2015-06-17  7:08   ` ludovic.tancerel
2015-06-21 13:36     ` Jonathan Cameron
2015-06-24 16:17       ` ludovic.tancerel
2015-06-24 17:59         ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.