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

* Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  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
  0 siblings, 1 reply; 15+ messages in thread
From: Daniel Baluta @ 2014-11-06 16:17 UTC (permalink / raw)
  To: Markezana, William; +Cc: jic23, linux-iio, linux-kernel

Quick review:

Use  iio: ms5637: Add Measurement Specialties MS5637 support instead of
iio: (ms5637) Add Measurement Specialties MS5637 support for subject.

If available please add a link to the datasheet here.

On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William
<William.Markezana@meas-spec.com> wrote:
> 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)

I would prefer to remove "()" around hexa/decimal values.

> +
> +#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);
> +
No check for ret here? If this is not needed then you can remove
the above assignment.

> +       /* 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);
> +
No new line here.
> +       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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

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

* Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  2014-11-06 16:17 ` Daniel Baluta
@ 2014-11-06 16:54   ` Peter Meerwald
  2014-11-06 17:38     ` Markezana, William
                       ` (2 more replies)
  0 siblings, 3 replies; 15+ messages in thread
From: Peter Meerwald @ 2014-11-06 16:54 UTC (permalink / raw)
  To: Daniel Baluta, Markezana, William; +Cc: jic23, linux-iio, linux-kernel

[-- Attachment #1: Type: TEXT/PLAIN, Size: 23809 bytes --]

> Use  iio: ms5637: Add Measurement Specialties MS5637 support instead of
> iio: (ms5637) Add Measurement Specialties MS5637 support for subject.
> 
> If available please add a link to the datasheet here.

some more comments below...
 
> On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William
> <William.Markezana@meas-spec.com> wrote:
> > 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 ++

don't add documentation to staging

> >  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

pressure & temperature

> > + *
> > + * 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)
> 
> I would prefer to remove "()" around hexa/decimal values.
> 
> > +
> > +#define ATTR_RESET                             0
> > +#define ATTR_RESOLUTION                                1

MS5637_ prefix please

> > +
> > +static const u16 conversion_time[] = { 1000, 2000, 3000, 5000, 9000, 17000 };

ms5637_ prefix please

> > +
> > +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;

cnt should be unsigned int, it's just a counter should have the CPUs 
native size

> > +       u16 n_rem, crc_read;
> > +
> > +       n_rem = 0x00;

should be 0 or 0x0000 as n_rem is u16

> > +       crc_read = n_prom[0];
> > +       n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
> > +       n_prom[0] = (0x0FFF & (n_prom[0]));     /* Clear the CRC byte */

remove extra parenthesis

> > +
> > +       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]);

linebreak looks weird before [i]

> > +
> > +               dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
> > +                       dev_data->calibration_coeffs[i]);

debug output should be after validity check

> > +
> > +               if (ret < 0) {
> > +                       dev_err(&dev_data->client->dev,
> > +                               "unable to get calibration coefficients at address %d\n",

Unable probably (uppercase all all other messages

> > +                               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;

no need to initialize 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[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);
> > +
> No check for ret here? If this is not needed then you can remove
> the above assignment.
> 
> > +       /* 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);
> > +
> No new line here.
> > +       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);
> > +
remove blank line

> > +               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 */

IIO wants kilopoascal

> > +                       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;

should simplify control flow a bit, just return instead of breaking, 
jumping

is it really necesary to have two different error messages?
could just code the call to ms5637_read_temperature_and_pressure() once

> > + 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,

output is for output channels, I guess the ms5637 cannot change the 
weather :)

> > +        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> > +        .scan_index = 0,

no need set the scan_index

> > +        }
> > +};
> > +
> > +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:

uhuh, private ABI
maybe this could be modelled by IIO's integration time?

> > +               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);

I'd rather do this before iio_device_register() -- what happens when it 
fails? there is no unregister()

> > +       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

0x77 is claimed at the top of ms5637.c

> > +    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

degree; the information so far is redundant

> > +
> > +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

in_, not out_

regular IIO channels (integration time?) could/should be used

I don't know about reset, needed?

private ABI should go to documentation/ABI/testing/sysfs-bus-iio-*

> > --
> > 1.7.9.5
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at  http://www.tux.org/lkml/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)

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

* RE: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  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
  2 siblings, 1 reply; 15+ messages in thread
From: Markezana, William @ 2014-11-06 17:38 UTC (permalink / raw)
  To: Peter Meerwald, Daniel Baluta; +Cc: jic23, linux-iio, linux-kernel

Hi all, 

Thank you for your feedbacks, I will take all those remarks in account and come back to you soon.
Just one question, is the kilopascals output mandatory for pressure sensors?
The benefit of our sensor is its accuracy of 1 millibars which is 0.1 kilopascals. It does not makes sense to have such accuracy if the final output is ten times less accurate...

William Markezana 
+33 582 082 286
william.markezana@meas-spec.com


-----Message d'origine-----
De : Peter Meerwald [mailto:pmeerw@pmeerw.net] 
Envoyé : jeudi 6 novembre 2014 17:54
À : Daniel Baluta; Markezana, William
Cc : jic23@kernel.org; linux-iio@vger.kernel.org; linux-kernel@vger.kernel.org
Objet : Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support

> Use  iio: ms5637: Add Measurement Specialties MS5637 support instead 
> of
> iio: (ms5637) Add Measurement Specialties MS5637 support for subject.
> 
> If available please add a link to the datasheet here.

some more comments below...
 
> On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William 
> <William.Markezana@meas-spec.com> wrote:
> > 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 ++

don't add documentation to staging

> >  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

pressure & temperature

> > + *
> > + * 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)
> 
> I would prefer to remove "()" around hexa/decimal values.
> 
> > +
> > +#define ATTR_RESET                             0
> > +#define ATTR_RESOLUTION                                1

MS5637_ prefix please

> > +
> > +static const u16 conversion_time[] = { 1000, 2000, 3000, 5000, 
> > +9000, 17000 };

ms5637_ prefix please

> > +
> > +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;

cnt should be unsigned int, it's just a counter should have the CPUs native size

> > +       u16 n_rem, crc_read;
> > +
> > +       n_rem = 0x00;

should be 0 or 0x0000 as n_rem is u16

> > +       crc_read = n_prom[0];
> > +       n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
> > +       n_prom[0] = (0x0FFF & (n_prom[0]));     /* Clear the CRC byte */

remove extra parenthesis

> > +
> > +       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]);

linebreak looks weird before [i]

> > +
> > +               dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
> > +                       dev_data->calibration_coeffs[i]);

debug output should be after validity check

> > +
> > +               if (ret < 0) {
> > +                       dev_err(&dev_data->client->dev,
> > +                               "unable to get calibration 
> > + coefficients at address %d\n",

Unable probably (uppercase all all other messages

> > +                               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;

no need to initialize 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[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);
> > +
> No check for ret here? If this is not needed then you can remove the 
> above assignment.
> 
> > +       /* 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);
> > +
> No new line here.
> > +       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);
> > +
remove blank line

> > +               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 */

IIO wants kilopoascal

> > +                       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;

should simplify control flow a bit, just return instead of breaking, jumping

is it really necesary to have two different error messages?
could just code the call to ms5637_read_temperature_and_pressure() once

> > + 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,

output is for output channels, I guess the ms5637 cannot change the weather :)

> > +        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
> > +        .scan_index = 0,

no need set the scan_index

> > +        }
> > +};
> > +
> > +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:

uhuh, private ABI
maybe this could be modelled by IIO's integration time?

> > +               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);

I'd rather do this before iio_device_register() -- what happens when it fails? there is no unregister()

> > +       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

0x77 is claimed at the top of ms5637.c

> > +    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

degree; the information so far is redundant

> > +
> > +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

in_, not out_

regular IIO channels (integration time?) could/should be used

I don't know about reset, needed?

private ABI should go to documentation/ABI/testing/sysfs-bus-iio-*

> > --
> > 1.7.9.5
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe 
> > linux-kernel" in the body of a message to majordomo@vger.kernel.org 
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> > Please read the FAQ at  http://www.tux.org/lkml/
> --
> To unsubscribe from this list: send the line "unsubscribe 
> linux-kernel" in the body of a message to majordomo@vger.kernel.org 
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)

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

* Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  2014-11-06 17:38     ` Markezana, William
@ 2014-11-06 19:48       ` Hartmut Knaack
  0 siblings, 0 replies; 15+ messages in thread
From: Hartmut Knaack @ 2014-11-06 19:48 UTC (permalink / raw)
  To: Markezana, William, Peter Meerwald, Daniel Baluta
  Cc: jic23, linux-iio, linux-kernel

Markezana, William schrieb am 06.11.2014 18:38:
> Hi all, 
> 
> Thank you for your feedbacks, I will take all those remarks in account and come back to you soon.
> Just one question, is the kilopascals output mandatory for pressure sensors?
> The benefit of our sensor is its accuracy of 1 millibars which is 0.1 kilopascals. It does not makes sense to have such accuracy if the final output is ten times less accurate...
> 
Using IIO_VAL_INT_PLUS_MICRO, you will get a resolution of 1E-6 kPa. That should be sufficient for your purpose, but if you need more, have a look at IIO_VAL_INT_PLUS_NANO.
> William Markezana 
> +33 582 082 286
> william.markezana@meas-spec.com
> 
> 
> -----Message d'origine-----
> De : Peter Meerwald [mailto:pmeerw@pmeerw.net] 
> Envoyé : jeudi 6 novembre 2014 17:54
> À : Daniel Baluta; Markezana, William
> Cc : jic23@kernel.org; linux-iio@vger.kernel.org; linux-kernel@vger.kernel.org
> Objet : Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
> 
>> Use  iio: ms5637: Add Measurement Specialties MS5637 support instead 
>> of
>> iio: (ms5637) Add Measurement Specialties MS5637 support for subject.
>>
>> If available please add a link to the datasheet here.
> 
> some more comments below...
>  
>> On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William 
>> <William.Markezana@meas-spec.com> wrote:
>>> 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 ++
> 
> don't add documentation to staging
> 
>>>  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)   +=id-sensor-press.o
>>>  obj-$(CONFIG_MPL115) +=pl115.o
>>>  obj-$(CONFIG_MPL3115) +=pl3115.o
>>> +obj-$(CONFIG_MS5637) +=s5637.o
>>>  obj-$(CONFIG_IIO_ST_PRESS) +=t_pressure.o  st_pressure-y := 
>>> st_pressure_core.o
>>>  st_pressure-$(CONFIG_IIO_BUFFER) +=t_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
> 
> pressure & temperature
> 
>>> + *
>>> + * 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)
>>
>> I would prefer to remove "()" around hexa/decimal values.
>>
>>> +
>>> +#define ATTR_RESET                             0
>>> +#define ATTR_RESOLUTION                                1
> 
> MS5637_ prefix please
> 
>>> +
>>> +static const u16 conversion_time[] = 1000, 2000, 3000, 5000, 
>>> +9000, 17000 };
> 
> ms5637_ prefix please
> 
>>> +
>>> +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 =;
>>> +
>>> +       ret =2c_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;
> 
> cnt should be unsigned int, it's just a counter should have the CPUs native size
> 
>>> +       u16 n_rem, crc_read;
>>> +
>>> +       n_rem =x00;
> 
> should be 0 or 0x0000 as n_rem is u16
> 
>>> +       crc_read =_prom[0];
>>> +       n_prom[MS5637_PROM_ELEMENTS_NB] =;
>>> +       n_prom[0] =0x0FFF & (n_prom[0]));     /* Clear the CRC byte */
> 
> remove extra parenthesis
> 
>>> +
>>> +       for (cnt =; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {
>>> +               if (cnt % 2 =1)
>>> +                       n_rem ^=_prom[cnt >> 1] & 0x00FF;
>>> +               else
>>> +                       n_rem ^=_prom[cnt >> 1] >> 8;
>>> +
>>> +               for (n_bit =; n_bit > 0; n_bit--) {
>>> +                       if (n_rem & 0x8000)
>>> +                               n_rem =n_rem << 1) ^ 0x3000;
>>> +                       else
>>> +                               n_rem <<=;
>>> +               }
>>> +       }
>>> +       n_rem >>=2;
>>> +       n_prom[0] =rc_read;
>>> +       return (n_rem =crc);
>>> +}
>>> +
>>> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev 
>>> +*dev_data) {
>>> +       int i, ret =;
>>> +
>>> +       for (i =; i < MS5637_PROM_ELEMENTS_NB; i++) {
>>> +               ret =s5637_get_calibration_coeffs(dev_data, i,
>>> +                               &dev_data->calibration_coeffs
>>> +                               [i]);
> 
> linebreak looks weird before [i]
> 
>>> +
>>> +               dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
>>> +                       dev_data->calibration_coeffs[i]);
> 
> debug output should be after validity check
> 
>>> +
>>> +               if (ret < 0) {
>>> +                       dev_err(&dev_data->client->dev,
>>> +                               "unable to get calibration 
>>> + coefficients at address %d\n",
> 
> Unable probably (uppercase all all other messages
> 
>>> +                               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 =;
> 
> no need to initialize 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[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 =2c_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 =s5637_read_adc_value(dev_data, t_adc);
>>> +
>> No check for ret here? If this is not needed then you can remove the 
>> above assignment.
>>
>>> +       /* Trigger Pressure conversion */
>>> +       ret =2c_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 =s5637_read_adc_value(dev_data, p_adc);
>>> +
>> No new line here.
>>> +       if (ret < 0)
>>> +               return ret;
>>> +       return 0;
>>> +}
>>> +
>>> +static int ms5637_read_temperature_and_pressure(struct ms5637_dev 
>>> +*dev_data) {
>>> +       int ret =;
>>> +       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 =s5637_fill_calibration_coeffs(dev_data);
>>> +                       if (ret < 0)
>>> +                               goto out;
>>> +
>>> +                       dev_data->got_calibration_words =rue;
>>> +               }
>>> +
>>> +               ret =s5637_conversion_and_read_adc(dev_data, 
>>> + &t_adc, &p_adc);
>>> +
> remove blank line
> 
>>> +               if (ret < 0) {
>>> +                       dev_data->got_calibration_words =alse;
>>> +                       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 =000 + 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 +=7 * tmp * tmp;
>>> +                               sens2 += * tmp * tmp;
>>> +                       }
>>> +               } else {
>>> +                       t2 =5 * ((s64) dt * (s64) dt)) >> 38;
>>> +                       off2 =;
>>> +                       sens2 =;
>>> +               }
>>> +
>>> +               /* OFF =FF_T1 + TCO * dT */
>>> +               off =((s64) dev_data->calibration_coeffs[2]) << 17)
>>> +                   +
>>> +                   ((((s64) dev_data->calibration_coeffs[4]) * (s64) dt) >> 6);
>>> +               off -=ff2;
>>> +
>>> +               /* Sensitivity at actual temperature =ENS_T1 + TCS * dT */
>>> +               sens =((s64) dev_data->calibration_coeffs[1]) << 16)
>>> +                   + (((s64) dev_data->calibration_coeffs[3] * dt) >> 7);
>>> +               sens -=ens2;
>>> +
>>> +               /* Temperature compensated pressure =1 * SENS - OFF */
>>> +               dev_data->temperature =temp - t2) * 10;
>>> +               dev_data->pressure =u32) (((((s64) p_adc * sens) >> 21)
>>> +                                            - off) >> 15) / 100;
>>> +
>>> +               dev_data->last_update =iffies;
>>> +               dev_data->valid =rue;
>>> +       }
>>> + out:
>>> +       mutex_unlock(&dev_data->lock);
>>> +
>>> +       return ret >= ? 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 =io_priv(indio_dev);
>>> +
>>> +       switch (mask) {
>>> +       case IIO_CHAN_INFO_PROCESSED:
>>> +               switch (channel->type) {
>>> +               case IIO_TEMP:  /* in 0.001 °C */
>>> +                       ret =s5637_read_temperature_and_pressure(dev_data);
>>> +                       if (ret)
>>> +                               goto t_err;
>>> +                       *val =ev_data->temperature;
>>> +                       break;
>>> +               case IIO_PRESSURE:      /* in mB */
> 
> IIO wants kilopoascal
> 
>>> +                       ret =s5637_read_temperature_and_pressure(dev_data);
>>> +                       if (ret)
>>> +                               goto p_err;
>>> +                       *val =ev_data->pressure;
>>> +                       break;
>>> +               default:
>>> +                       return -EINVAL;
>>> +               }
>>> +               ret =IO_VAL_INT;
>>> +               break;
>>> +       default:
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       return ret;
> 
> should simplify control flow a bit, just return instead of breaking, jumping
> 
> is it really necesary to have two different error messages?
> could just code the call to ms5637_read_temperature_and_pressure() once
> 
>>> + 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 =IO_TEMP,
>>> +        .output =,
>>> +        .info_mask_separate =IT(IIO_CHAN_INFO_PROCESSED),
>>> +        .scan_index =,
>>> +        },
>>> +       {
>>> +        .type =IO_PRESSURE,
>>> +        .output =,
> 
> output is for output channels, I guess the ms5637 cannot change the weather :)
> 
>>> +        .info_mask_separate =IT(IIO_CHAN_INFO_PROCESSED),
>>> +        .scan_index =,
> 
> no need set the scan_index
> 
>>> +        }
>>> +};
>>> +
>>> +static ssize_t ms5637_read_attr(struct device *dev,
>>> +                               struct device_attribute *attr, char 
>>> +*buf) {
>>> +       struct iio_dev *indio_dev =ev_to_iio_dev(dev);
>>> +       struct ms5637_dev *dev_data =io_priv(indio_dev);
>>> +       struct iio_dev_attr *this_attr =o_iio_dev_attr(attr);
>>> +       int ret =;
>>> +
>>> +       switch (this_attr->address) {
>>> +       case ATTR_RESET:
>>> +               ret =printf(buf, "0\n");
>>> +               break;
>>> +       case ATTR_RESOLUTION:
> 
> uhuh, private ABI
> maybe this could be modelled by IIO's integration time?
> 
>>> +               ret =printf(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 =ev_to_iio_dev(dev);
>>> +       struct ms5637_dev *dev_data =io_priv(indio_dev);
>>> +       struct iio_dev_attr *this_attr =o_iio_dev_attr(attr);
>>> +       u8 val;
>>> +       int ret;
>>> +
>>> +       ret =strtou8(buf, 10, &val);
>>> +       if (ret)
>>> +               return ret;
>>> +
>>> +       mutex_lock(&dev_data->lock);
>>> +       switch (this_attr->address) {
>>> +       case ATTR_RESET:
>>> +               if (val !=)
>>> +                       goto einval;
>>> +
>>> +               ret =2c_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 =al - 8;
>>> +               break;
>>> +       default:
>>> +               goto einval;
>>> +       }
>>> +
>>> +       dev_data->valid =alse;
>>> +       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 = s5637_attributes,
>>> +};
>>> +
>>> +static const struct iio_info ms5637_info =
>>> +       .read_raw = s5637_read_raw,
>>> +       .attrs =ms5637_attribute_group,
>>> +       .driver_module =HIS_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 =evm_iio_device_alloc(&client->dev, sizeof(*dev_data));
>>> +       if (!indio_dev)
>>> +               return -ENOMEM;
>>> +
>>> +       dev_data =io_priv(indio_dev);
>>> +       dev_data->client =lient;
>>> +       mutex_init(&dev_data->lock);
>>> +
>>> +       indio_dev->info =ms5637_info;
>>> +       indio_dev->name =d->name;
>>> +       indio_dev->dev.parent =client->dev;
>>> +       indio_dev->modes =NDIO_DIRECT_MODE;
>>> +       indio_dev->channels =s5637_channels;
>>> +       indio_dev->num_channels =RRAY_SIZE(ms5637_channels);
>>> +
>>> +       i2c_set_clientdata(client, indio_dev);
>>> +       ret =io_device_register(indio_dev);
>>> +       if (ret < 0)
>>> +               return ret;
>>> +
>>> +       ret =2c_smbus_write_byte(client, MS5637_RESET);
> 
> I'd rather do this before iio_device_register() -- what happens when it fails? there is no unregister()
> 
>>> +       if (ret < 0)
>>> +               return ret;
>>> +       usleep_range(3000, 6000);
>>> +
>>> +       ret =s5637_fill_calibration_coeffs(dev_data);
>>> +       if (ret =0)
>>> +               dev_data->got_calibration_words =rue;
>>> +
>>> +       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 =2c_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 = s5637_probe,
>>> +       .remove =s5637_remove,
>>> +       .id_table =s5637_id,
>>> +       .driver =
>>> +                  .name = ms5637",
>>> +                  .owner =HIS_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
> 
> 0x77 is claimed at the top of ms5637.c
> 
>>> +    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
> 
> degree; the information so far is redundant
> 
>>> +
>>> +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
> 
> in_, not out_
> 
> regular IIO channels (integration time?) could/should be used
> 
> I don't know about reset, needed?
> 
> private ABI should go to documentation/ABI/testing/sysfs-bus-iio-*
> 
>>> --
>>> 1.7.9.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe 
>>> linux-kernel" in the body of a message to majordomo@vger.kernel.org 
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at  http://www.tux.org/lkml/
>> --
>> To unsubscribe from this list: send the line "unsubscribe 
>> linux-kernel" in the body of a message to majordomo@vger.kernel.org 
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>>
> 


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

* Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  2014-11-06 16:54   ` Peter Meerwald
  2014-11-06 17:38     ` Markezana, William
@ 2014-11-06 22:43     ` Hartmut Knaack
  2014-11-08 12:09     ` Jonathan Cameron
  2 siblings, 0 replies; 15+ messages in thread
From: Hartmut Knaack @ 2014-11-06 22:43 UTC (permalink / raw)
  To: Peter Meerwald, Daniel Baluta, Markezana, William
  Cc: jic23, linux-iio, linux-kernel

Peter Meerwald schrieb am 06.11.2014 17:54:
>> Use  iio: ms5637: Add Measurement Specialties MS5637 support instead of
>> iio: (ms5637) Add Measurement Specialties MS5637 support for subject.
>>
>> If available please add a link to the datasheet here.
> 
> some more comments below...
Just one comment for now.
>  
>> On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William
>> <William.Markezana@meas-spec.com> wrote:
>>> 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 ++
> 
> don't add documentation to staging
> 
>>>  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
> 
> pressure & temperature
> 
>>> + *
>>> + * 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)
>>
>> I would prefer to remove "()" around hexa/decimal values.
>>
>>> +
>>> +#define ATTR_RESET                             0
>>> +#define ATTR_RESOLUTION                                1
> 
> MS5637_ prefix please
> 
>>> +
>>> +static const u16 conversion_time[] = { 1000, 2000, 3000, 5000, 9000, 17000 };
> 
> ms5637_ prefix please
> 
>>> +
>>> +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;
> 
> cnt should be unsigned int, it's just a counter should have the CPUs 
> native size
> 
>>> +       u16 n_rem, crc_read;
>>> +
>>> +       n_rem = 0x00;
> 
> should be 0 or 0x0000 as n_rem is u16
> 
>>> +       crc_read = n_prom[0];
>>> +       n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
>>> +       n_prom[0] = (0x0FFF & (n_prom[0]));     /* Clear the CRC byte */
> 
> remove extra parenthesis
Actually, it should be as simple as this:
	n_prom[0] &= 0x0FFF;
> 
>>> +
>>> +       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]);
> 
> linebreak looks weird before [i]
> 
>>> +
>>> +               dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
>>> +                       dev_data->calibration_coeffs[i]);
> 
> debug output should be after validity check
> 
>>> +
>>> +               if (ret < 0) {
>>> +                       dev_err(&dev_data->client->dev,
>>> +                               "unable to get calibration coefficients at address %d\n",
> 
> Unable probably (uppercase all all other messages
> 
>>> +                               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;
> 
> no need to initialize 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[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);
>>> +
>> No check for ret here? If this is not needed then you can remove
>> the above assignment.
>>
>>> +       /* 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);
>>> +
>> No new line here.
>>> +       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);
>>> +
> remove blank line
> 
>>> +               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 */
> 
> IIO wants kilopoascal
> 
>>> +                       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;
> 
> should simplify control flow a bit, just return instead of breaking, 
> jumping
> 
> is it really necesary to have two different error messages?
> could just code the call to ms5637_read_temperature_and_pressure() once
> 
>>> + 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,
> 
> output is for output channels, I guess the ms5637 cannot change the 
> weather :)
> 
>>> +        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
>>> +        .scan_index = 0,
> 
> no need set the scan_index
> 
>>> +        }
>>> +};
>>> +
>>> +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:
> 
> uhuh, private ABI
> maybe this could be modelled by IIO's integration time?
> 
>>> +               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);
> 
> I'd rather do this before iio_device_register() -- what happens when it 
> fails? there is no unregister()
> 
>>> +       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
> 
> 0x77 is claimed at the top of ms5637.c
> 
>>> +    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
> 
> degree; the information so far is redundant
> 
>>> +
>>> +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
> 
> in_, not out_
> 
> regular IIO channels (integration time?) could/should be used
> 
> I don't know about reset, needed?
> 
> private ABI should go to documentation/ABI/testing/sysfs-bus-iio-*
> 
>>> --
>>> 1.7.9.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at  http://www.tux.org/lkml/
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>>
> 


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

* Re: [PATCH] iio: (ms5637) Add Measurement Specialties MS5637 support
  2014-11-06 16:54   ` Peter Meerwald
  2014-11-06 17:38     ` Markezana, William
  2014-11-06 22:43     ` Hartmut Knaack
@ 2014-11-08 12:09     ` Jonathan Cameron
  2 siblings, 0 replies; 15+ messages in thread
From: Jonathan Cameron @ 2014-11-08 12:09 UTC (permalink / raw)
  To: Peter Meerwald, Daniel Baluta, Markezana, William; +Cc: linux-iio, linux-kernel

On 06/11/14 16:54, Peter Meerwald wrote:
>> Use  iio: ms5637: Add Measurement Specialties MS5637 support instead of
>> iio: (ms5637) Add Measurement Specialties MS5637 support for subject.
>>
>> If available please add a link to the datasheet here.
> 
> some more comments below...
And a few more from me...
>  
>> On Thu, Nov 6, 2014 at 11:46 AM, Markezana, William
>> <William.Markezana@meas-spec.com> wrote:
>>> 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 ++
> 
> don't add documentation to staging
> 
>>>  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
> 
> pressure & temperature
> 
>>> + *
>>> + * 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)
>>
>> I would prefer to remove "()" around hexa/decimal values.
>>
>>> +
>>> +#define ATTR_RESET                             0
>>> +#define ATTR_RESOLUTION                                1
> 
> MS5637_ prefix please
> 
>>> +
>>> +static const u16 conversion_time[] = { 1000, 2000, 3000, 5000, 9000, 17000 };
> 
> ms5637_ prefix please
> 
>>> +
>>> +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;
> 
> cnt should be unsigned int, it's just a counter should have the CPUs 
> native size
> 
>>> +       u16 n_rem, crc_read;
>>> +
>>> +       n_rem = 0x00;
> 
> should be 0 or 0x0000 as n_rem is u16
> 
>>> +       crc_read = n_prom[0];
>>> +       n_prom[MS5637_PROM_ELEMENTS_NB] = 0;
>>> +       n_prom[0] = (0x0FFF & (n_prom[0]));     /* Clear the CRC byte */
> 
> remove extra parenthesis
> 
>>> +
>>> +       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]);
> 
> linebreak looks weird before [i]
> 
>>> +
>>> +               dev_dbg(&dev_data->client->dev, "Coeff %d : %d", i,
>>> +                       dev_data->calibration_coeffs[i]);
> 
> debug output should be after validity check
> 
>>> +
>>> +               if (ret < 0) {
>>> +                       dev_err(&dev_data->client->dev,
>>> +                               "unable to get calibration coefficients at address %d\n",
> 
> Unable probably (uppercase all all other messages
> 
>>> +                               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;
Proper error codes.
>>> +       }
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static int ms5637_read_adc_value(struct ms5637_dev *dev_data, u32 *adc_value)
>>> +{
>>> +       int ret = 0;
> 
> no need to initialize 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[0];
Looks suspect... Using buf[0] twice?
>>> +       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);
>>> +
>> No check for ret here? If this is not needed then you can remove
>> the above assignment.
>>
>>> +       /* 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);
>>> +
>> No new line here.
>>> +       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);
>>> +
> remove blank line
> 
>>> +               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 */
> 
> IIO wants kilopoascal
> 
>>> +                       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;
> 
> should simplify control flow a bit, just return instead of breaking, 
> jumping
> 
> is it really necesary to have two different error messages?
> could just code the call to ms5637_read_temperature_and_pressure() once
> 
>>> + 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,
> 
> output is for output channels, I guess the ms5637 cannot change the 
> weather :)
> 
>>> +        .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
>>> +        .scan_index = 0,
> 
> no need set the scan_index
> 
>>> +        }
>>> +};
>>> +
>>> +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:
> 
> uhuh, private ABI
> maybe this could be modelled by IIO's integration time?
Or sampling_frequency (not always the same ;)  
> 
>>> +               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;

Not nice.  Set ret when the error occurs and have the good path
drop straight through.

Also check the request is valid before taking the lock and your flow
becomes quite a bit simpler.

>>> + 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");
This one is always interesting.

Do any applications care?
Would we be better off just providing one case?
Does it effect anything else? (data rate?)
If so are we better off using the sampling_frequency
interface to expose it?  Most of the time that is the
knob that userspace is going to want to turn.


>>> +
>>> +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);
> 
> I'd rather do this before iio_device_register() -- what happens when it 
> fails? there is no unregister()

Just to reinforce this.  When iio_device_register is called, the
device is exposed to userspace.  Hence it must be ready to respond
coherently to any interactions.  That's why it is almost always the
last thing called in the probe funciton.
> 
>>> +       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);
As all you have is iio_device_unregister in here you have
a rare case where we could use devm_iio_device_register
and drop the remove functional altogether.
(which is why we have a devm version of that function)
>>> +
>>> +       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
> 
> 0x77 is claimed at the top of ms5637.c
Also doesn't scan.  Address that is valid is fair enough though if correct
> 
>>> +    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
> 
> degree; the information so far is redundant
Line is way to long as well ;)
> 
>>> +
>>> +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
> 
> in_, not out_
> 
> regular IIO channels (integration time?) could/should be used
> 
> I don't know about reset, needed?
Explicit userspace reset is unusual.  Normally either the driver
can detect a problem (device not responding etc) and reset or
it is done on driver load to get the device into a sane state in the
first place.

Tends to be useful during driver debugging, but adds nothing
after the driver is written... 
 
> 
> private ABI should go to documentation/ABI/testing/sysfs-bus-iio-*
Absolutely.
> 
>>> --
>>> 1.7.9.5
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at  http://www.tux.org/lkml/
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/
>>
> 


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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-24 16:17       ` ludovic.tancerel
@ 2015-06-24 17:59         ` Jonathan Cameron
  0 siblings, 0 replies; 15+ messages in thread
From: Jonathan Cameron @ 2015-06-24 17:59 UTC (permalink / raw)
  To: ludovic.tancerel
  Cc: Tomasz Duszynski, knaack.h, pmeerw, linux-iio, Markezana,
	William, Lars-Peter Clausen

On 24/06/15 17:17, ludovic.tancerel@maplehightech.com wrote:
> Hi Jonathan, Thomasz
> 
> thank you for your feedback and recommendations.
> 
> I am willing to do things as clean as possible in a timeframe that I can sustain myself.
> I will try to give more details on what is being done, and the proposal to comply to your request.
> Please read below. Thank you for reviewing and commenting.
> 
> I use this channel to ask a few questions on upcoming drivers as well.
> 
> Regards,
> Ludovic
> 
> Le 21 juin 2015 à 15:36, Jonathan Cameron <jic23@kernel.org> a écrit :
> 
>> On 17/06/15 08:08, ludovic.tancerel@maplehightech.com wrote:
>>> Hello Tomasz,
>>>
>>> Thank you for notifying me and your feedback, there is a wish from
>>> Measurement Specialties to have a separate driver for MS5637 and
>>> MS5611. There are a few differences in HW, moreover the driver you
>>> pushed does not support resolution change.
>> Hmm.  If I get a proposal from someone to actually do the work
>> to unify the resulting drivers without any loss of functionality, I
>> will be in favour.
>>
>> If there is any chance of pushing back on this if there is a strong
>> case for a more unified driver then please do what you can.  I
>> appreciate the constraints that can get applied from above!
> 
> I am willing to commonalize as well as much as possible. However,
> there is different need, and request coming from Measurement
> Specialties, I have to fulfill their requirements. The approach I
> prefer is to have different files for different HW, and common
> functions that would fall into common/meas_sensors/.
That can work well. Just depends on the particular parts to whether it makes sense
to do it that way or big multipart drivers.
> 
> My main concern is about SPI vs I2C.
> I did not plan to develop the drivers supporting both SPI and I2C. I developed just for I2C support.
> The drivers for below HW are all ready, I just have to apply the rework for ms5637 that is applicable to them.
Cool.  One option to make life easier for whoever might one
day implement spi support would be to use regmap for all of them.  The cost
is low even if you only do i2c support, but then the cost to add SPI is a matter
of a few lines.  One of the biggest reason regmap exists is because lots
of parts support both spi and i2c (does lots of other cool stuff as well!)
> 
>>>
>>> Note additional drivers from Measurement Specialties will be added
>>> soon.
>> If the parts are also similar in function and could be covered by
>> a single driver I might well start pushing back harder.  In the long
>> run, lots of duplicated code is bad for maintenance so I get grumpy
>> fairly quickly and very happy when I get big patches that unify
>> lots of drivers (as long as they aren’t too complex!)
> 
> I will commonalize functions whereas it can be, but will still have separate drivers.
> The upcoming drivers are for following parts:
> TSYS01 (Temperature)
> TSYS02D (Temperature)
> HTU21D (Temperature & Humidity)
> MS8607 (Temperature, Pressure & Humidity)
> KMA36 (magnetic encoder)
> Depending on the sensors, there is possible reuse between them. They
> do not all share same protocol. That means there should be at least 3
> patches.
> 
> Regarding Humidity, there is one thing that is typical on humidity
> sensor, it is heater function. I don’t see anything similar in
> existing drivers (It is marked as TODO for si7005) Is this ok to have
> an attribute that enables the heater ? I presume that falls within
> private-ABI area, unless a new channel shall be added to control
> this.
It's not really a general purpose channel so probably makes sense to
have it as private ABI.  Note there are a couple of humidity sensors
in HWMON - it's always been an unclear question wrt to where they should
go.

It would be good to share ABI with them where possible (looks like it
is called heater_enable in sht15 for example).


> Last thing I want to ask is about kma36. I prefer asking before going further
> This chipset is an angle magnetical encoder. There is no existing IIO
> driver of that kind for now as far as I can see. My original intent
> was to create a new directory (me ? magnetic_encoder ? mag_encoder ?)
encoder would probably work best.
> and reuse the IIO_ANGL channel that is originally meant for gyros. 
> Does that sound proper to you ?
Sounds fine.  We do have quite a few resolver drivers that are still in 
staging (for a reason!) which are probably similar is some ways.

> 
>>
>> Jonathan
>>>
>>> I will include your comments in corrections.
>>> Thank you,
>>> Ludovic
>>>
>>>
>>> Le 16 juin 2015 à 22:42, Tomasz Duszynski <tduszyns@gmail.com> a écrit :
>>>
>>>> On Tue, Jun 16, 2015 at 02:33:29PM +0200, Ludovic wrote:
>>>>> 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.
>>>>>
>>>> Hi Ludovic,
>>>>
>>>> Not so long ago I added support for ms5611. Comparing both chips datasheets
>>>> didn't reveal much difference, so maybe two patches can somehow be combined to
>>>> avoid code duplication.
>>>>
>>>> A few additional comments below.
>>>>> 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 */
>>>> probably iio wants milli C
>>>>> +			*val = dev_data->temperature / 1000;
>>>>> +			*val2 = (dev_data->temperature -
>>>>> +					(dev_data->temperature / 1000) * 1000
>>>>> +				) * 1000;
>>>> (dev->data->temperature % 1000) * 1000 has same effect but is much shorter
>>>>> +			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;
>>>>> +}
>>>> seems that nothing special happens on removal, so maybe use
>>>> devm_iio_device_register and drop *_remove().
>>>>> +
>>>>> +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
>>>>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> 


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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-21 13:36     ` Jonathan Cameron
@ 2015-06-24 16:17       ` ludovic.tancerel
  2015-06-24 17:59         ` Jonathan Cameron
  0 siblings, 1 reply; 15+ messages in thread
From: ludovic.tancerel @ 2015-06-24 16:17 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Tomasz Duszynski, knaack.h, pmeerw, linux-iio, Markezana, William

Hi Jonathan, Thomasz

thank you for your feedback and recommendations.

I am willing to do things as clean as possible in a timeframe that I can =
sustain myself.
I will try to give more details on what is being done, and the proposal =
to comply to your request.
Please read below. Thank you for reviewing and commenting.

I use this channel to ask a few questions on upcoming drivers as well.

Regards,
Ludovic

Le 21 juin 2015 =E0 15:36, Jonathan Cameron <jic23@kernel.org> a =E9crit =
:

> On 17/06/15 08:08, ludovic.tancerel@maplehightech.com wrote:
>> Hello Tomasz,
>>=20
>> Thank you for notifying me and your feedback, there is a wish from
>> Measurement Specialties to have a separate driver for MS5637 and
>> MS5611. There are a few differences in HW, moreover the driver you
>> pushed does not support resolution change.
> Hmm.  If I get a proposal from someone to actually do the work
> to unify the resulting drivers without any loss of functionality, I
> will be in favour.
>=20
> If there is any chance of pushing back on this if there is a strong
> case for a more unified driver then please do what you can.  I
> appreciate the constraints that can get applied from above!

I am willing to commonalize as well as much as possible.
However, there is different need, and request coming from Measurement =
Specialties, I have to fulfill their requirements.
The approach I prefer is to have different files for different HW, and =
common functions that would fall into common/meas_sensors/.

My main concern is about SPI vs I2C.
I did not plan to develop the drivers supporting both SPI and I2C. I =
developed just for I2C support.
The drivers for below HW are all ready, I just have to apply the rework =
for ms5637 that is applicable to them.

>>=20
>> Note additional drivers from Measurement Specialties will be added
>> soon.
> If the parts are also similar in function and could be covered by
> a single driver I might well start pushing back harder.  In the long
> run, lots of duplicated code is bad for maintenance so I get grumpy
> fairly quickly and very happy when I get big patches that unify
> lots of drivers (as long as they aren=92t too complex!)

I will commonalize functions whereas it can be, but will still have =
separate drivers.
The upcoming drivers are for following parts:
TSYS01 (Temperature)
TSYS02D (Temperature)
HTU21D (Temperature & Humidity)
MS8607 (Temperature, Pressure & Humidity)
KMA36 (magnetic encoder)
Depending on the sensors, there is possible reuse between them. They do =
not all share same protocol. That means there should be at least 3 =
patches.

Regarding Humidity, there is one thing that is typical on humidity =
sensor, it is heater function.
I don=92t see anything similar in existing drivers (It is marked as TODO =
for si7005)
Is this ok to have an attribute that enables the heater ? I presume that =
falls within private-ABI area, unless a new channel shall be added to =
control this.

Last thing I want to ask is about kma36. I prefer asking before going =
further
This chipset is an angle magnetical encoder.
There is no existing IIO driver of that kind for now as far as I can =
see.
My original intent was to create a new directory (me ? magnetic_encoder =
? mag_encoder ?) and reuse the IIO_ANGL channel that is originally meant =
for gyros.
Does that sound proper to you ?


>=20
> Jonathan
>>=20
>> I will include your comments in corrections.
>> Thank you,
>> Ludovic
>>=20
>>=20
>> Le 16 juin 2015 =E0 22:42, Tomasz Duszynski <tduszyns@gmail.com> a =
=E9crit :
>>=20
>>> On Tue, Jun 16, 2015 at 02:33:29PM +0200, Ludovic wrote:
>>>> MS5637 spec : =
http://www.meas-spec.com/product/pressure/MS5637-02BA03.aspx
>>>>=20
>>>> 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.
>>>>=20
>>>> Thank you in advance for reviewing again.
>>>>=20
>>> Hi Ludovic,
>>>=20
>>> Not so long ago I added support for ms5611. Comparing both chips =
datasheets
>>> didn't reveal much difference, so maybe two patches can somehow be =
combined to
>>> avoid code duplication.
>>>=20
>>> A few additional comments below.
>>>> Regards,
>>>> Ludovic
>>>>=20
>>>> 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
>>>>=20
>>>> 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.
>>>>=20
>>>> +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) +=3D bmp280.o
>>>> obj-$(CONFIG_HID_SENSOR_PRESS)   +=3D hid-sensor-press.o
>>>> obj-$(CONFIG_MPL115) +=3D mpl115.o
>>>> obj-$(CONFIG_MPL3115) +=3D mpl3115.o
>>>> +obj-$(CONFIG_MS5637) +=3D ms5637.o
>>>> obj-$(CONFIG_IIO_ST_PRESS) +=3D st_pressure.o
>>>> st_pressure-y :=3D st_pressure_core.o
>>>> st_pressure-$(CONFIG_IIO_BUFFER) +=3D 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[] =3D { 1000, 2000, 3000,
>>>> +					      5000, 9000, 17000 };
>>>> +static const int ms5637_samp_freq[6][2] =3D { {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 =3D 0;
>>>> +
>>>> +	ret =3D i2c_smbus_read_word_swapped(dev_data->client,
>>>> +					  MS5637_PROM_READ + (index << =
1));
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +	*word =3D (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 =3D 0x0000;
>>>> +	crc_read =3D n_prom[0];
>>>> +	n_prom[MS5637_PROM_ELEMENTS_NB] =3D 0;
>>>> +	n_prom[0] &=3D 0x0FFF;	/* Clear the CRC byte */
>>>> +
>>>> +	for (cnt =3D 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) =
{
>>>> +		if (cnt % 2 =3D=3D 1)
>>>> +			n_rem ^=3D n_prom[cnt >> 1] & 0x00FF;
>>>> +		else
>>>> +			n_rem ^=3D n_prom[cnt >> 1] >> 8;
>>>> +
>>>> +		for (n_bit =3D 8; n_bit > 0; n_bit--) {
>>>> +			if (n_rem & 0x8000)
>>>> +				n_rem =3D (n_rem << 1) ^ 0x3000;
>>>> +			else
>>>> +				n_rem <<=3D 1;
>>>> +		}
>>>> +	}
>>>> +	n_rem >>=3D 12;
>>>> +	n_prom[0] =3D crc_read;
>>>> +	return (n_rem =3D=3D crc);
>>>> +}
>>>> +
>>>> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev =
*dev_data)
>>>> +{
>>>> +	int i, ret =3D 0;
>>>> +
>>>> +	for (i =3D 0; i < MS5637_PROM_ELEMENTS_NB; i++) {
>>>> +		ret =3D 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 =3D ms5637_crc_check(
>>>> +			dev_data->calibration_coeffs,
>>>> +			(dev_data->calibration_coeffs[0] & 0xF000) >> =
12);
>>>> +	if (ret =3D=3D 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 =3D
>>>> +	    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 =3D (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 =3D 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 =3D ms5637_read_adc_value(dev_data, t_adc);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +
>>>> +	/* Trigger Pressure conversion */
>>>> +	ret =3D 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 =3D 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 =3D 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 =3D =
ms5637_fill_calibration_coeffs(dev_data);
>>>> +			if (ret !=3D 0)
>>>> +				goto out;
>>>> +
>>>> +			dev_data->got_calibration_words =3D true;
>>>> +		}
>>>> +
>>>> +		ret =3D ms5637_conversion_and_read_adc(dev_data, &t_adc, =
&p_adc);
>>>> +		if (ret < 0) {
>>>> +			dev_data->got_calibration_words =3D false;
>>>> +			dev_err(&dev_data->client->dev,
>>>> +				"Unable to make sensor adc =
conversion\n");
>>>> +			goto out;
>>>> +		}
>>>> +
>>>> +		dt =3D (s32)t_adc - (dev_data->calibration_coeffs[5] << =
8);
>>>> +
>>>> +		/* Actual temperature =3D 2000 + dT * TEMPSENS */
>>>> +		temp =3D
>>>> +		    2000 + (((s64)dt * dev_data->calibration_coeffs[6]) =
>> 23);
>>>> +
>>>> +		/* Second order temperature compensation */
>>>> +		if (temp < 2000) {
>>>> +			s64 tmp =3D (s64)temp - 2000;
>>>> +
>>>> +			t2 =3D (3 * ((s64)dt * (s64)dt)) >> 33;
>>>> +			off2 =3D (61 * tmp * tmp) >> 4;
>>>> +			sens2 =3D (29 * tmp * tmp) >> 4;
>>>> +
>>>> +			if (temp < -1500) {
>>>> +				s64 tmp =3D (s64)temp + 1500;
>>>> +
>>>> +				off2 +=3D 17 * tmp * tmp;
>>>> +				sens2 +=3D 9 * tmp * tmp;
>>>> +			}
>>>> +		} else {
>>>> +			t2 =3D (5 * ((s64)dt * (s64)dt)) >> 38;
>>>> +			off2 =3D 0;
>>>> +			sens2 =3D 0;
>>>> +		}
>>>> +
>>>> +		/* OFF =3D OFF_T1 + TCO * dT */
>>>> +		off =3D (((s64)dev_data->calibration_coeffs[2]) << 17) +
>>>> +		    ((((s64)dev_data->calibration_coeffs[4]) * (s64)dt) =
>> 6);
>>>> +		off -=3D off2;
>>>> +
>>>> +		/* Sensitivity at actual temperature =3D SENS_T1 + TCS * =
dT */
>>>> +		sens =3D (((s64)dev_data->calibration_coeffs[1]) << 16)
>>>> +		    + (((s64)dev_data->calibration_coeffs[3] * dt) >> =
7);
>>>> +		sens -=3D sens2;
>>>> +
>>>> +		/* Temperature compensated pressure =3D D1 * SENS - OFF =
*/
>>>> +		dev_data->temperature =3D (temp - t2) * 10;
>>>> +		dev_data->pressure =3D (u32)(((((s64)p_adc * sens) >> =
21)
>>>> +					     - off) >> 15);
>>>> +
>>>> +		dev_data->last_update =3D jiffies;
>>>> +		dev_data->valid =3D true;
>>>> +	}
>>>> + out:
>>>> +	mutex_unlock(&dev_data->lock);
>>>> +
>>>> +	return ret >=3D 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 =3D=3D vals[n][0] && val2 =3D=3D 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 =3D 0;
>>>> +
>>>> +	while (n-- > 0)
>>>> +		len +=3D scnprintf(buf + len, PAGE_SIZE - len,
>>>> +			"%d.%06d ", vals[n][0], vals[n][1]);
>>>> +
>>>> +	/* replace trailing space by newline */
>>>> +	buf[len - 1] =3D '\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 =3D iio_priv(indio_dev);
>>>> +
>>>> +	switch (mask) {
>>>> +	case IIO_CHAN_INFO_PROCESSED:
>>>> +		ret =3D ms5637_read_temperature_and_pressure(dev_data);
>>>> +		if (ret)
>>>> +			goto err;
>>>> +		switch (channel->type) {
>>>> +		case IIO_TEMP:	/* in =B0C */
>>> probably iio wants milli C
>>>> +			*val =3D dev_data->temperature / 1000;
>>>> +			*val2 =3D (dev_data->temperature -
>>>> +					(dev_data->temperature / 1000) * =
1000
>>>> +				) * 1000;
>>> (dev->data->temperature % 1000) * 1000 has same effect but is much =
shorter
>>>> +			break;
>>>> +		case IIO_PRESSURE:	/* in kPa */
>>>> +			*val =3D dev_data->pressure / 1000;
>>>> +			*val2 =3D (dev_data->pressure -
>>>> +				 (dev_data->pressure / 1000) * 1000) * =
1000;
>>>> +			break;
>>>> +		default:
>>>> +			return -EINVAL;
>>>> +		}
>>>> +		break;
>>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>>> +		*val =3D =
ms5637_samp_freq[dev_data->resolution_index][0];
>>>> +		*val2 =3D =
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 =3D iio_priv(indio_dev);
>>>> +	int i;
>>>> +
>>>> +	switch (mask) {
>>>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>>>> +		i =3D ms5637_get_samp_freq_index(dev_data, val, val2);
>>>> +		if (i < 0)
>>>> +			return -EINVAL;
>>>> +
>>>> +		dev_data->resolution_index =3D i;
>>>> +		break;
>>>> +	default:
>>>> +		return -EINVAL;
>>>> +	}
>>>> +	return 0;
>>>> +}
>>>> +
>>>> +static const struct iio_chan_spec ms5637_channels[] =3D {
>>>> +	{
>>>> +	 .type =3D IIO_TEMP,
>>>> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
>>>> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
>>>> +	 },
>>>> +	{
>>>> +	 .type =3D IIO_PRESSURE,
>>>> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
>>>> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
>>>> +	 }
>>>> +};
>>>> +
>>>> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq_avail);
>>>> +
>>>> +static struct attribute *ms5637_attributes[] =3D {
>>>> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
>>>> +	NULL,
>>>> +};
>>>> +
>>>> +static const struct attribute_group ms5637_attribute_group =3D {
>>>> +	.attrs =3D ms5637_attributes,
>>>> +};
>>>> +
>>>> +static const struct iio_info ms5637_info =3D {
>>>> +	.read_raw =3D ms5637_read_raw,
>>>> +	.write_raw =3D ms5637_write_raw,
>>>> +	.attrs =3D &ms5637_attribute_group,
>>>> +	.driver_module =3D 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 =3D devm_iio_device_alloc(&client->dev, =
sizeof(*dev_data));
>>>> +	if (!indio_dev)
>>>> +		return -ENOMEM;
>>>> +
>>>> +	dev_data =3D iio_priv(indio_dev);
>>>> +	dev_data->client =3D client;
>>>> +	mutex_init(&dev_data->lock);
>>>> +
>>>> +	indio_dev->info =3D &ms5637_info;
>>>> +	indio_dev->name =3D id->name;
>>>> +	indio_dev->dev.parent =3D &client->dev;
>>>> +	indio_dev->modes =3D INDIO_DIRECT_MODE;
>>>> +	indio_dev->channels =3D ms5637_channels;
>>>> +	indio_dev->num_channels =3D ARRAY_SIZE(ms5637_channels);
>>>> +
>>>> +	i2c_set_clientdata(client, indio_dev);
>>>> +	ret =3D i2c_smbus_write_byte(client, MS5637_RESET);
>>>> +	if (ret < 0)
>>>> +		return ret;
>>>> +	usleep_range(3000, 6000);
>>>> +
>>>> +	ret =3D ms5637_fill_calibration_coeffs(dev_data);
>>>> +	if (ret =3D=3D 0)
>>>> +		dev_data->got_calibration_words =3D true;
>>>> +	else
>>>> +		return ret;
>>>> +
>>>> +	ret =3D 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 =3D i2c_get_clientdata(client);
>>>> +
>>>> +	devm_iio_device_unregister(&client->dev, indio_dev);
>>>> +
>>>> +	return 0;
>>>> +}
>>> seems that nothing special happens on removal, so maybe use
>>> devm_iio_device_register and drop *_remove().
>>>> +
>>>> +static const struct i2c_device_id ms5637_id[] =3D {
>>>> +	{"ms5637", 0},
>>>> +	{}
>>>> +};
>>>> +
>>>> +static struct i2c_driver ms5637_driver =3D {
>>>> +	.probe =3D ms5637_probe,
>>>> +	.remove =3D ms5637_remove,
>>>> +	.id_table =3D ms5637_id,
>>>> +	.driver =3D {
>>>> +		   .name =3D "ms5637",
>>>> +		   .owner =3D 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
>>>>=20
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe =
linux-iio" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" =
in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>=20
>=20
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" =
in

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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-17  7:08   ` ludovic.tancerel
@ 2015-06-21 13:36     ` Jonathan Cameron
  2015-06-24 16:17       ` ludovic.tancerel
  0 siblings, 1 reply; 15+ messages in thread
From: Jonathan Cameron @ 2015-06-21 13:36 UTC (permalink / raw)
  To: ludovic.tancerel, Tomasz Duszynski
  Cc: knaack.h, pmeerw, linux-iio, Markezana, William

On 17/06/15 08:08, ludovic.tancerel@maplehightech.com wrote:
> Hello Tomasz,
> 
> Thank you for notifying me and your feedback, there is a wish from
> Measurement Specialties to have a separate driver for MS5637 and
> MS5611. There are a few differences in HW, moreover the driver you
> pushed does not support resolution change.
Hmm.  If I get a proposal from someone to actually do the work
to unify the resulting drivers without any loss of functionality, I
will be in favour.

If there is any chance of pushing back on this if there is a strong
case for a more unified driver then please do what you can.  I
appreciate the constraints that can get applied from above!
> 
> Note additional drivers from Measurement Specialties will be added
> soon.
If the parts are also similar in function and could be covered by
a single driver I might well start pushing back harder.  In the long
run, lots of duplicated code is bad for maintenance so I get grumpy
fairly quickly and very happy when I get big patches that unify
lots of drivers (as long as they aren't too complex!)

Jonathan
> 
> I will include your comments in corrections.
> Thank you,
> Ludovic
> 
> 
> Le 16 juin 2015 à 22:42, Tomasz Duszynski <tduszyns@gmail.com> a écrit :
> 
>> On Tue, Jun 16, 2015 at 02:33:29PM +0200, Ludovic wrote:
>>> 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.
>>>
>> Hi Ludovic,
>>
>> Not so long ago I added support for ms5611. Comparing both chips datasheets
>> didn't reveal much difference, so maybe two patches can somehow be combined to
>> avoid code duplication.
>>
>> A few additional comments below.
>>> 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 */
>> probably iio wants milli C
>>> +			*val = dev_data->temperature / 1000;
>>> +			*val2 = (dev_data->temperature -
>>> +					(dev_data->temperature / 1000) * 1000
>>> +				) * 1000;
>> (dev->data->temperature % 1000) * 1000 has same effect but is much shorter
>>> +			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;
>>> +}
>> seems that nothing special happens on removal, so maybe use
>> devm_iio_device_register and drop *_remove().
>>> +
>>> +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
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in

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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-16 13:56 ` Peter Meerwald
@ 2015-06-17  8:18   ` ludovic.tancerel
  0 siblings, 0 replies; 15+ messages in thread
From: ludovic.tancerel @ 2015-06-17  8:18 UTC (permalink / raw)
  To: Peter Meerwald; +Cc: jic23, knaack.h, linux-iio

Hi Peter,

thank you very much for your comments,
Please, see below for comments other than minor comments that I will take into account.

Regards,
Ludovic


Le 16 juin 2015 à 15:56, Peter Meerwald <pmeerw@pmeerw.net> a écrit :

> 
>> MS5637 spec : http://www.meas-spec.com/product/pressure/MS5637-02BA03.aspx
> 
>> Thank you in advance for reviewing again.
> 
> some comments below
> 
>> 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)
> 
> u8 index probably
> 
>> +{
>> +	int ret = 0;
> 
> initialization not necessary
> 
>> +
>> +	ret = i2c_smbus_read_word_swapped(dev_data->client,
>> +					  MS5637_PROM_READ + (index << 1));
>> +	if (ret < 0)
>> +		return ret;
>> +	*word = (u16)ret & 0xFFFF;
> 
> *word = ret; should do as well
> 
>> +	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;
> 
> this looks like an out-of-bound access, one beyond 
> calibration_coeffs[MS5637_PROM_ELEMENTS_NB]
Indeed …
That is embarrassing for me to leave such error before submission, my apologizes.
Is there a lint or equivalent script for drivers before submission ?

> 
>> +	n_prom[0] &= 0x0FFF;	/* Clear the CRC byte */
> 
> better a space instead of a tab to separate comment;
> 0x0fff is not a byte, the comment is misleading
> 
>> +
>> +	for (cnt = 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {
> 
> probably also out-of-bound
> 
>> +		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);
> 
> () is not necessary
> 
>> +}
>> +
>> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev *dev_data)
>> +{
>> +	int i, ret = 0;
> 
> init of ret not necessary
>> +
>> +	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);
> 
> _crc_check() returns bool, so check for == false
> 
>> +	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]);
> 
> probably some whitespace here...
> 
>> +	*adc_value = (buf[0] << 16) + (buf[1] << 8) + buf[2];
> 
> .. and here
> 
>> +	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);
> 
> maybe a MACRO() for this monster?
> 
>> +	/* 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;
> 
> maybe a function/macro for splitting up the data?
> 
>> +			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;
> 
> perhaps return 0 here
> 
>> +	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)
> 
> probably return ret on error 
> 
>> +		dev_data->got_calibration_words = true;
> 
> and move 
> dev_data->got_calibration_words = true
> to fill_calib_coeffs(), would save another assignment elsewhere also
Indeed, the removal of reset from previous round makes this got_calibration_words param unnecessary.
I should remove it.

> 
>> +	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);
> 
> iio_device_register() vs. devm_iio_device_unregister -- really?
> 
> perhaps this could be one of the cases where we can 
> devm_iio_device_register() and just drop the _remove()?
I misunderstood first comments from Jonathan on this. I think I am clear now on what I should be doing,
sorry about that.

> 
>> +
>> +	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");
> 
> and pressure
> 
>> +MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
>> +MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
>> 
> 
> -- 
> 
> Peter Meerwald
> +43-664-2444418 (mobile)


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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-16 20:42 ` Tomasz Duszynski
@ 2015-06-17  7:08   ` ludovic.tancerel
  2015-06-21 13:36     ` Jonathan Cameron
  0 siblings, 1 reply; 15+ messages in thread
From: ludovic.tancerel @ 2015-06-17  7:08 UTC (permalink / raw)
  To: Tomasz Duszynski; +Cc: jic23, knaack.h, pmeerw, linux-iio, Markezana, William

Hello Tomasz,

Thank you for notifying me and  your feedback, there is a wish from =
Measurement Specialties to have a separate driver for MS5637 and MS5611.
There are a few differences in HW, moreover the driver you pushed does =
not support resolution change.

Note additional drivers from Measurement Specialties will be added soon.

I will include your comments in corrections.

Thank you,
Ludovic


Le 16 juin 2015 =E0 22:42, Tomasz Duszynski <tduszyns@gmail.com> a =E9crit=
 :

> On Tue, Jun 16, 2015 at 02:33:29PM +0200, Ludovic wrote:
>> MS5637 spec : =
http://www.meas-spec.com/product/pressure/MS5637-02BA03.aspx
>>=20
>> 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.
>>=20
>> Thank you in advance for reviewing again.
>>=20
> Hi Ludovic,
>=20
> Not so long ago I added support for ms5611. Comparing both chips =
datasheets
> didn't reveal much difference, so maybe two patches can somehow be =
combined to
> avoid code duplication.
>=20
> A few additional comments below.
>> Regards,
>> Ludovic
>>=20
>> 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
>>=20
>> 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.
>>=20
>> +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) +=3D bmp280.o
>> obj-$(CONFIG_HID_SENSOR_PRESS)   +=3D hid-sensor-press.o
>> obj-$(CONFIG_MPL115) +=3D mpl115.o
>> obj-$(CONFIG_MPL3115) +=3D mpl3115.o
>> +obj-$(CONFIG_MS5637) +=3D ms5637.o
>> obj-$(CONFIG_IIO_ST_PRESS) +=3D st_pressure.o
>> st_pressure-y :=3D st_pressure_core.o
>> st_pressure-$(CONFIG_IIO_BUFFER) +=3D 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[] =3D { 1000, 2000, 3000,
>> +					      5000, 9000, 17000 };
>> +static const int ms5637_samp_freq[6][2] =3D { {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 =3D 0;
>> +
>> +	ret =3D i2c_smbus_read_word_swapped(dev_data->client,
>> +					  MS5637_PROM_READ + (index << =
1));
>> +	if (ret < 0)
>> +		return ret;
>> +	*word =3D (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 =3D 0x0000;
>> +	crc_read =3D n_prom[0];
>> +	n_prom[MS5637_PROM_ELEMENTS_NB] =3D 0;
>> +	n_prom[0] &=3D 0x0FFF;	/* Clear the CRC byte */
>> +
>> +	for (cnt =3D 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) =
{
>> +		if (cnt % 2 =3D=3D 1)
>> +			n_rem ^=3D n_prom[cnt >> 1] & 0x00FF;
>> +		else
>> +			n_rem ^=3D n_prom[cnt >> 1] >> 8;
>> +
>> +		for (n_bit =3D 8; n_bit > 0; n_bit--) {
>> +			if (n_rem & 0x8000)
>> +				n_rem =3D (n_rem << 1) ^ 0x3000;
>> +			else
>> +				n_rem <<=3D 1;
>> +		}
>> +	}
>> +	n_rem >>=3D 12;
>> +	n_prom[0] =3D crc_read;
>> +	return (n_rem =3D=3D crc);
>> +}
>> +
>> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev =
*dev_data)
>> +{
>> +	int i, ret =3D 0;
>> +
>> +	for (i =3D 0; i < MS5637_PROM_ELEMENTS_NB; i++) {
>> +		ret =3D 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 =3D ms5637_crc_check(
>> +			dev_data->calibration_coeffs,
>> +			(dev_data->calibration_coeffs[0] & 0xF000) >> =
12);
>> +	if (ret =3D=3D 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 =3D
>> +	    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 =3D (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 =3D 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 =3D ms5637_read_adc_value(dev_data, t_adc);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* Trigger Pressure conversion */
>> +	ret =3D 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 =3D 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 =3D 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 =3D =
ms5637_fill_calibration_coeffs(dev_data);
>> +			if (ret !=3D 0)
>> +				goto out;
>> +
>> +			dev_data->got_calibration_words =3D true;
>> +		}
>> +
>> +		ret =3D ms5637_conversion_and_read_adc(dev_data, &t_adc, =
&p_adc);
>> +		if (ret < 0) {
>> +			dev_data->got_calibration_words =3D false;
>> +			dev_err(&dev_data->client->dev,
>> +				"Unable to make sensor adc =
conversion\n");
>> +			goto out;
>> +		}
>> +
>> +		dt =3D (s32)t_adc - (dev_data->calibration_coeffs[5] << =
8);
>> +
>> +		/* Actual temperature =3D 2000 + dT * TEMPSENS */
>> +		temp =3D
>> +		    2000 + (((s64)dt * dev_data->calibration_coeffs[6]) =
>> 23);
>> +
>> +		/* Second order temperature compensation */
>> +		if (temp < 2000) {
>> +			s64 tmp =3D (s64)temp - 2000;
>> +
>> +			t2 =3D (3 * ((s64)dt * (s64)dt)) >> 33;
>> +			off2 =3D (61 * tmp * tmp) >> 4;
>> +			sens2 =3D (29 * tmp * tmp) >> 4;
>> +
>> +			if (temp < -1500) {
>> +				s64 tmp =3D (s64)temp + 1500;
>> +
>> +				off2 +=3D 17 * tmp * tmp;
>> +				sens2 +=3D 9 * tmp * tmp;
>> +			}
>> +		} else {
>> +			t2 =3D (5 * ((s64)dt * (s64)dt)) >> 38;
>> +			off2 =3D 0;
>> +			sens2 =3D 0;
>> +		}
>> +
>> +		/* OFF =3D OFF_T1 + TCO * dT */
>> +		off =3D (((s64)dev_data->calibration_coeffs[2]) << 17) +
>> +		    ((((s64)dev_data->calibration_coeffs[4]) * (s64)dt) =
>> 6);
>> +		off -=3D off2;
>> +
>> +		/* Sensitivity at actual temperature =3D SENS_T1 + TCS * =
dT */
>> +		sens =3D (((s64)dev_data->calibration_coeffs[1]) << 16)
>> +		    + (((s64)dev_data->calibration_coeffs[3] * dt) >> =
7);
>> +		sens -=3D sens2;
>> +
>> +		/* Temperature compensated pressure =3D D1 * SENS - OFF =
*/
>> +		dev_data->temperature =3D (temp - t2) * 10;
>> +		dev_data->pressure =3D (u32)(((((s64)p_adc * sens) >> =
21)
>> +					     - off) >> 15);
>> +
>> +		dev_data->last_update =3D jiffies;
>> +		dev_data->valid =3D true;
>> +	}
>> + out:
>> +	mutex_unlock(&dev_data->lock);
>> +
>> +	return ret >=3D 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 =3D=3D vals[n][0] && val2 =3D=3D 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 =3D 0;
>> +
>> +	while (n-- > 0)
>> +		len +=3D scnprintf(buf + len, PAGE_SIZE - len,
>> +			"%d.%06d ", vals[n][0], vals[n][1]);
>> +
>> +	/* replace trailing space by newline */
>> +	buf[len - 1] =3D '\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 =3D iio_priv(indio_dev);
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_PROCESSED:
>> +		ret =3D ms5637_read_temperature_and_pressure(dev_data);
>> +		if (ret)
>> +			goto err;
>> +		switch (channel->type) {
>> +		case IIO_TEMP:	/* in =B0C */
> probably iio wants milli C
>> +			*val =3D dev_data->temperature / 1000;
>> +			*val2 =3D (dev_data->temperature -
>> +					(dev_data->temperature / 1000) * =
1000
>> +				) * 1000;
> (dev->data->temperature % 1000) * 1000 has same effect but is much =
shorter
>> +			break;
>> +		case IIO_PRESSURE:	/* in kPa */
>> +			*val =3D dev_data->pressure / 1000;
>> +			*val2 =3D (dev_data->pressure -
>> +				 (dev_data->pressure / 1000) * 1000) * =
1000;
>> +			break;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +		break;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		*val =3D =
ms5637_samp_freq[dev_data->resolution_index][0];
>> +		*val2 =3D =
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 =3D iio_priv(indio_dev);
>> +	int i;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		i =3D ms5637_get_samp_freq_index(dev_data, val, val2);
>> +		if (i < 0)
>> +			return -EINVAL;
>> +
>> +		dev_data->resolution_index =3D i;
>> +		break;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +	return 0;
>> +}
>> +
>> +static const struct iio_chan_spec ms5637_channels[] =3D {
>> +	{
>> +	 .type =3D IIO_TEMP,
>> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
>> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
>> +	 },
>> +	{
>> +	 .type =3D IIO_PRESSURE,
>> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
>> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
>> +	 }
>> +};
>> +
>> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq_avail);
>> +
>> +static struct attribute *ms5637_attributes[] =3D {
>> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
>> +	NULL,
>> +};
>> +
>> +static const struct attribute_group ms5637_attribute_group =3D {
>> +	.attrs =3D ms5637_attributes,
>> +};
>> +
>> +static const struct iio_info ms5637_info =3D {
>> +	.read_raw =3D ms5637_read_raw,
>> +	.write_raw =3D ms5637_write_raw,
>> +	.attrs =3D &ms5637_attribute_group,
>> +	.driver_module =3D 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 =3D devm_iio_device_alloc(&client->dev, =
sizeof(*dev_data));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	dev_data =3D iio_priv(indio_dev);
>> +	dev_data->client =3D client;
>> +	mutex_init(&dev_data->lock);
>> +
>> +	indio_dev->info =3D &ms5637_info;
>> +	indio_dev->name =3D id->name;
>> +	indio_dev->dev.parent =3D &client->dev;
>> +	indio_dev->modes =3D INDIO_DIRECT_MODE;
>> +	indio_dev->channels =3D ms5637_channels;
>> +	indio_dev->num_channels =3D ARRAY_SIZE(ms5637_channels);
>> +
>> +	i2c_set_clientdata(client, indio_dev);
>> +	ret =3D i2c_smbus_write_byte(client, MS5637_RESET);
>> +	if (ret < 0)
>> +		return ret;
>> +	usleep_range(3000, 6000);
>> +
>> +	ret =3D ms5637_fill_calibration_coeffs(dev_data);
>> +	if (ret =3D=3D 0)
>> +		dev_data->got_calibration_words =3D true;
>> +	else
>> +		return ret;
>> +
>> +	ret =3D 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 =3D i2c_get_clientdata(client);
>> +
>> +	devm_iio_device_unregister(&client->dev, indio_dev);
>> +
>> +	return 0;
>> +}
> seems that nothing special happens on removal, so maybe use
> devm_iio_device_register and drop *_remove().
>> +
>> +static const struct i2c_device_id ms5637_id[] =3D {
>> +	{"ms5637", 0},
>> +	{}
>> +};
>> +
>> +static struct i2c_driver ms5637_driver =3D {
>> +	.probe =3D ms5637_probe,
>> +	.remove =3D ms5637_remove,
>> +	.id_table =3D ms5637_id,
>> +	.driver =3D {
>> +		   .name =3D "ms5637",
>> +		   .owner =3D 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
>>=20
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" =
in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" =
in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  2015-06-16 12:33 [PATCH] iio: ms5637: " Ludovic
  2015-06-16 13:56 ` Peter Meerwald
@ 2015-06-16 20:42 ` Tomasz Duszynski
  2015-06-17  7:08   ` ludovic.tancerel
  1 sibling, 1 reply; 15+ messages in thread
From: Tomasz Duszynski @ 2015-06-16 20:42 UTC (permalink / raw)
  To: Ludovic; +Cc: jic23, knaack.h, pmeerw, linux-iio

On Tue, Jun 16, 2015 at 02:33:29PM +0200, Ludovic wrote:
> 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.
>
Hi Ludovic,

Not so long ago I added support for ms5611. Comparing both chips datasheets
didn't reveal much difference, so maybe two patches can somehow be combined=
 to
avoid code duplication.

A few additional comments below.
> 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 Specia=
lties
> +          MS5637 pressure and temperature sensor.
> +
> +          This driver can also be built as a module. If so, the module w=
ill
> +          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) +=3D bmp280.o
>  obj-$(CONFIG_HID_SENSOR_PRESS)   +=3D hid-sensor-press.o
>  obj-$(CONFIG_MPL115) +=3D mpl115.o
>  obj-$(CONFIG_MPL3115) +=3D mpl3115.o
> +obj-$(CONFIG_MS5637) +=3D ms5637.o
>  obj-$(CONFIG_IIO_ST_PRESS) +=3D st_pressure.o
>  st_pressure-y :=3D st_pressure_core.o
>  st_pressure-$(CONFIG_IIO_BUFFER) +=3D 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[] =3D { 1000, 2000, 3000,
> +					      5000, 9000, 17000 };
> +static const int ms5637_samp_freq[6][2] =3D { {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 =3D 0;
> +
> +	ret =3D i2c_smbus_read_word_swapped(dev_data->client,
> +					  MS5637_PROM_READ + (index << 1));
> +	if (ret < 0)
> +		return ret;
> +	*word =3D (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 =3D 0x0000;
> +	crc_read =3D n_prom[0];
> +	n_prom[MS5637_PROM_ELEMENTS_NB] =3D 0;
> +	n_prom[0] &=3D 0x0FFF;	/* Clear the CRC byte */
> +
> +	for (cnt =3D 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {
> +		if (cnt % 2 =3D=3D 1)
> +			n_rem ^=3D n_prom[cnt >> 1] & 0x00FF;
> +		else
> +			n_rem ^=3D n_prom[cnt >> 1] >> 8;
> +
> +		for (n_bit =3D 8; n_bit > 0; n_bit--) {
> +			if (n_rem & 0x8000)
> +				n_rem =3D (n_rem << 1) ^ 0x3000;
> +			else
> +				n_rem <<=3D 1;
> +		}
> +	}
> +	n_rem >>=3D 12;
> +	n_prom[0] =3D crc_read;
> +	return (n_rem =3D=3D crc);
> +}
> +
> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev *dev_data)
> +{
> +	int i, ret =3D 0;
> +
> +	for (i =3D 0; i < MS5637_PROM_ELEMENTS_NB; i++) {
> +		ret =3D 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 =3D ms5637_crc_check(
> +			dev_data->calibration_coeffs,
> +			(dev_data->calibration_coeffs[0] & 0xF000) >> 12);
> +	if (ret =3D=3D 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_v=
alue)
> +{
> +	int ret;
> +	u8 buf[3];
> +
> +	ret =3D
> +	    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 =3D (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 =3D 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 =3D ms5637_read_adc_value(dev_data, t_adc);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* Trigger Pressure conversion */
> +	ret =3D 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 =3D 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_d=
ata)
> +{
> +	int ret =3D 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 =3D ms5637_fill_calibration_coeffs(dev_data);
> +			if (ret !=3D 0)
> +				goto out;
> +
> +			dev_data->got_calibration_words =3D true;
> +		}
> +
> +		ret =3D ms5637_conversion_and_read_adc(dev_data, &t_adc, &p_adc);
> +		if (ret < 0) {
> +			dev_data->got_calibration_words =3D false;
> +			dev_err(&dev_data->client->dev,
> +				"Unable to make sensor adc conversion\n");
> +			goto out;
> +		}
> +
> +		dt =3D (s32)t_adc - (dev_data->calibration_coeffs[5] << 8);
> +
> +		/* Actual temperature =3D 2000 + dT * TEMPSENS */
> +		temp =3D
> +		    2000 + (((s64)dt * dev_data->calibration_coeffs[6]) >> 23);
> +
> +		/* Second order temperature compensation */
> +		if (temp < 2000) {
> +			s64 tmp =3D (s64)temp - 2000;
> +
> +			t2 =3D (3 * ((s64)dt * (s64)dt)) >> 33;
> +			off2 =3D (61 * tmp * tmp) >> 4;
> +			sens2 =3D (29 * tmp * tmp) >> 4;
> +
> +			if (temp < -1500) {
> +				s64 tmp =3D (s64)temp + 1500;
> +
> +				off2 +=3D 17 * tmp * tmp;
> +				sens2 +=3D 9 * tmp * tmp;
> +			}
> +		} else {
> +			t2 =3D (5 * ((s64)dt * (s64)dt)) >> 38;
> +			off2 =3D 0;
> +			sens2 =3D 0;
> +		}
> +
> +		/* OFF =3D OFF_T1 + TCO * dT */
> +		off =3D (((s64)dev_data->calibration_coeffs[2]) << 17) +
> +		    ((((s64)dev_data->calibration_coeffs[4]) * (s64)dt) >> 6);
> +		off -=3D off2;
> +
> +		/* Sensitivity at actual temperature =3D SENS_T1 + TCS * dT */
> +		sens =3D (((s64)dev_data->calibration_coeffs[1]) << 16)
> +		    + (((s64)dev_data->calibration_coeffs[3] * dt) >> 7);
> +		sens -=3D sens2;
> +
> +		/* Temperature compensated pressure =3D D1 * SENS - OFF */
> +		dev_data->temperature =3D (temp - t2) * 10;
> +		dev_data->pressure =3D (u32)(((((s64)p_adc * sens) >> 21)
> +					     - off) >> 15);
> +
> +		dev_data->last_update =3D jiffies;
> +		dev_data->valid =3D true;
> +	}
> + out:
> +	mutex_unlock(&dev_data->lock);
> +
> +	return ret >=3D 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 =3D=3D vals[n][0] && val2 =3D=3D 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 =3D 0;
> +
> +	while (n-- > 0)
> +		len +=3D scnprintf(buf + len, PAGE_SIZE - len,
> +			"%d.%06d ", vals[n][0], vals[n][1]);
> +
> +	/* replace trailing space by newline */
> +	buf[len - 1] =3D '\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 =3D iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_PROCESSED:
> +		ret =3D ms5637_read_temperature_and_pressure(dev_data);
> +		if (ret)
> +			goto err;
> +		switch (channel->type) {
> +		case IIO_TEMP:	/* in =C2=B0C */
probably iio wants milli C
> +			*val =3D dev_data->temperature / 1000;
> +			*val2 =3D (dev_data->temperature -
> +					(dev_data->temperature / 1000) * 1000
> +				) * 1000;
(dev->data->temperature % 1000) * 1000 has same effect but is much shorter
> +			break;
> +		case IIO_PRESSURE:	/* in kPa */
> +			*val =3D dev_data->pressure / 1000;
> +			*val2 =3D (dev_data->pressure -
> +				 (dev_data->pressure / 1000) * 1000) * 1000;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val =3D ms5637_samp_freq[dev_data->resolution_index][0];
> +		*val2 =3D 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 =3D iio_priv(indio_dev);
> +	int i;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		i =3D ms5637_get_samp_freq_index(dev_data, val, val2);
> +		if (i < 0)
> +			return -EINVAL;
> +
> +		dev_data->resolution_index =3D i;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static const struct iio_chan_spec ms5637_channels[] =3D {
> +	{
> +	 .type =3D IIO_TEMP,
> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +	 },
> +	{
> +	 .type =3D IIO_PRESSURE,
> +	 .info_mask_separate =3D BIT(IIO_CHAN_INFO_PROCESSED),
> +	 .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +	 }
> +};
> +
> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq_avail);
> +
> +static struct attribute *ms5637_attributes[] =3D {
> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ms5637_attribute_group =3D {
> +	.attrs =3D ms5637_attributes,
> +};
> +
> +static const struct iio_info ms5637_info =3D {
> +	.read_raw =3D ms5637_read_raw,
> +	.write_raw =3D ms5637_write_raw,
> +	.attrs =3D &ms5637_attribute_group,
> +	.driver_module =3D 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 =3D devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	dev_data =3D iio_priv(indio_dev);
> +	dev_data->client =3D client;
> +	mutex_init(&dev_data->lock);
> +
> +	indio_dev->info =3D &ms5637_info;
> +	indio_dev->name =3D id->name;
> +	indio_dev->dev.parent =3D &client->dev;
> +	indio_dev->modes =3D INDIO_DIRECT_MODE;
> +	indio_dev->channels =3D ms5637_channels;
> +	indio_dev->num_channels =3D ARRAY_SIZE(ms5637_channels);
> +
> +	i2c_set_clientdata(client, indio_dev);
> +	ret =3D i2c_smbus_write_byte(client, MS5637_RESET);
> +	if (ret < 0)
> +		return ret;
> +	usleep_range(3000, 6000);
> +
> +	ret =3D ms5637_fill_calibration_coeffs(dev_data);
> +	if (ret =3D=3D 0)
> +		dev_data->got_calibration_words =3D true;
> +	else
> +		return ret;
> +
> +	ret =3D 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 =3D i2c_get_clientdata(client);
> +
> +	devm_iio_device_unregister(&client->dev, indio_dev);
> +
> +	return 0;
> +}
seems that nothing special happens on removal, so maybe use
devm_iio_device_register and drop *_remove().
> +
> +static const struct i2c_device_id ms5637_id[] =3D {
> +	{"ms5637", 0},
> +	{}
> +};
> +
> +static struct i2c_driver ms5637_driver =3D {
> +	.probe =3D ms5637_probe,
> +	.remove =3D ms5637_remove,
> +	.id_table =3D ms5637_id,
> +	.driver =3D {
> +		   .name =3D "ms5637",
> +		   .owner =3D 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
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [PATCH] iio: ms5637: Add Measurement Specialties MS5637 support
  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
  1 sibling, 1 reply; 15+ messages in thread
From: Peter Meerwald @ 2015-06-16 13:56 UTC (permalink / raw)
  To: Ludovic; +Cc: jic23, knaack.h, linux-iio

[-- Attachment #1: Type: TEXT/PLAIN, Size: 16224 bytes --]


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

> Thank you in advance for reviewing again.

some comments below

> 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)

u8 index probably

> +{
> +	int ret = 0;

initialization not necessary

> +
> +	ret = i2c_smbus_read_word_swapped(dev_data->client,
> +					  MS5637_PROM_READ + (index << 1));
> +	if (ret < 0)
> +		return ret;
> +	*word = (u16)ret & 0xFFFF;

*word = ret; should do as well

> +	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;

this looks like an out-of-bound access, one beyond 
calibration_coeffs[MS5637_PROM_ELEMENTS_NB]

> +	n_prom[0] &= 0x0FFF;	/* Clear the CRC byte */

better a space instead of a tab to separate comment;
0x0fff is not a byte, the comment is misleading

> +
> +	for (cnt = 0; cnt < (MS5637_PROM_ELEMENTS_NB + 1) * 2; cnt++) {

probably also out-of-bound

> +		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);

() is not necessary

> +}
> +
> +static int ms5637_fill_calibration_coeffs(struct ms5637_dev *dev_data)
> +{
> +	int i, ret = 0;

init of ret not necessary
> +
> +	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);

_crc_check() returns bool, so check for == false

> +	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]);

probably some whitespace here...

> +	*adc_value = (buf[0] << 16) + (buf[1] << 8) + buf[2];

.. and here

> +	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);

maybe a MACRO() for this monster?

> +	/* 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;

maybe a function/macro for splitting up the data?

> +			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;

perhaps return 0 here

> +	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)

probably return ret on error 

> +		dev_data->got_calibration_words = true;

and move 
dev_data->got_calibration_words = true
to fill_calib_coeffs(), would save another assignment elsewhere also

> +	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);

iio_device_register() vs. devm_iio_device_unregister -- really?

perhaps this could be one of the cases where we can 
devm_iio_device_register() and just drop the _remove()?

> +
> +	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");

and pressure

> +MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
> +MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)

^ permalink raw reply	[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.