All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] iio: accel: Add support for Sensortek STK8312
@ 2015-05-05 13:20 Tiberiu Breana
  2015-05-05 13:43 ` Jonathan Cameron
  2015-05-05 13:45 ` Peter Meerwald
  0 siblings, 2 replies; 7+ messages in thread
From: Tiberiu Breana @ 2015-05-05 13:20 UTC (permalink / raw)
  To: linux-iio; +Cc: Jonathan Cameron

Minimal implementation of an IIO driver for the Sensortek
STK8312 3-axis accelerometer.
Datasheet:
http://www.syi-group.com/uploadpic/data/201361817562681623.pdf

Includes:
- ACPI support;
- read_raw for x,y,z axes;
- reading and setting the scale (range) parameter.
- power management

Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
---
 drivers/iio/accel/Kconfig   |  11 ++
 drivers/iio/accel/Makefile  |   2 +
 drivers/iio/accel/stk8312.c | 375 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 388 insertions(+)
 create mode 100644 drivers/iio/accel/stk8312.c

diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 7c9a9a9..8bd8ccb 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -136,4 +136,15 @@ config MMA9553
 
 	  To compile this driver as a module, choose M here: the module
 	  will be called mma9553.
+
+config STK8312
+	tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
+	depends on I2C
+	help
+	  Say yes here to get support for the Sensortek STK8312 3-axis
+	  accelerometer.
+
+	  Choosing M will build the driver as a module. If so, the module
+	  will be called stk8312.
+
 endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 99d89e4..8b327c1 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
 obj-$(CONFIG_MMA9551)		+= mma9551.o
 obj-$(CONFIG_MMA9553)		+= mma9553.o
 
+obj-$(CONFIG_STK8312)		+= stk8312.o
+
 obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
 
 obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
new file mode 100644
index 0000000..ff27e6b
--- /dev/null
+++ b/drivers/iio/accel/stk8312.c
@@ -0,0 +1,375 @@
+/**
+ * Sensortek STK8312 3-Axis Accelerometer
+ *
+ * Copyright (c) 2015, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define STK8312_REG_XOUT		0x00
+#define STK8312_REG_YOUT		0x01
+#define STK8312_REG_ZOUT		0x02
+#define STK8312_REG_MODE		0x07
+#define STK8312_REG_STH			0x13
+#define STK8312_REG_RESET		0x20
+
+#define STK8312_MODE_ACTIVE		1
+#define STK8312_MODE_STANDBY		0
+#define STK8312_MODE_MASK		0x01
+#define STK8312_RNG_MASK		0xC0
+#define STK8312_ALERT_MASK		0x40
+#define STK8312_DATA6_MASK		0x3F
+#define STK8312_RNG_SHIFT		6
+#define STK8312_READ_RETRIES		16
+
+#define STK8312_DRIVER_NAME		"stk8312"
+
+#define STK8312_SCALE_AVAIL		"0.4747 0.4632 1.2354"
+
+static const int stk8312_scale_table[][2] = {
+	{0, 474700}, {0, 463200}, {1, 235400}
+};
+
+#define STK8312_ACCEL_CHANNEL(axis) {				\
+	.type = IIO_ACCEL,					\
+	.modified = 1,						\
+	.channel2 = IIO_MOD_##axis,				\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
+}
+
+static const struct iio_chan_spec stk8312_channels[] = {
+	STK8312_ACCEL_CHANNEL(X),
+	STK8312_ACCEL_CHANNEL(Y),
+	STK8312_ACCEL_CHANNEL(Z),
+};
+
+struct stk8312_data {
+	struct i2c_client *client;
+	struct mutex lock;
+	int range;
+	u8 mode;
+};
+
+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
+
+static struct attribute *stk8312_attributes[] = {
+	&iio_const_attr_in_accel_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group stk8312_attribute_group = {
+	.attrs = stk8312_attributes
+};
+
+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
+{
+	int ret;
+	u8 masked_reg;
+	struct i2c_client *client = data->client;
+
+	if (mode > 1)
+		return -EINVAL;
+	else if (mode == data->mode)
+		return 0;
+
+	ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
+	if (ret < 0) {
+		dev_err(&client->dev, "register read failed\n");
+		return ret;
+	}
+	masked_reg = ret & (~STK8312_MODE_MASK);
+	masked_reg |= mode;
+
+	ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE,
+					masked_reg);
+	if (ret < 0)
+		dev_err(&client->dev, "failed to change sensor mode\n");
+	else
+		data->mode = mode;
+
+	return ret;
+}
+
+static int stk8312_set_range(struct stk8312_data *data, u8 range)
+{
+	int ret;
+	int masked_reg;
+	u8 mode;
+	struct i2c_client *client = data->client;
+
+	if (range < 0 || range > 2)
+		return -EINVAL;
+
+	if (range == data->range)
+		return 0;
+
+	mode = data->mode;
+	/* We need to go in standby mode to modify registers */
+	ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
+	if (ret < 0)
+		return ret;
+
+	ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
+	if (ret < 0) {
+		dev_err(&client->dev, "register read failed\n");
+		return ret;
+	}
+
+	masked_reg = ret & (~STK8312_RNG_MASK);
+	masked_reg |= range << STK8312_RNG_SHIFT;
+
+	ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
+	if (ret < 0)
+		dev_err(&client->dev, "failed to change sensor range\n");
+	else
+		data->range = range;
+
+	return stk8312_set_mode(data, mode);
+}
+
+static int stk8312_read_accel(struct stk8312_data *data, int axis)
+{
+	int ret;
+	u8 reg;
+	int retries = STK8312_READ_RETRIES;
+	struct i2c_client *client = data->client;
+
+	switch (axis) {
+	case IIO_MOD_X:
+		reg = STK8312_REG_XOUT;
+		break;
+	case IIO_MOD_Y:
+		reg = STK8312_REG_YOUT;
+		break;
+	case IIO_MOD_Z:
+		reg = STK8312_REG_ZOUT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, reg);
+	if (ret < 0) {
+		dev_err(&client->dev, "register read failed\n");
+		return ret;
+	}
+	if (data->range == 0) {
+		/* 6bit mode */
+		while (retries--) {
+			if (ret & STK8312_ALERT_MASK) {
+				/* The register is being updated. Try again. */
+				msleep(20);
+				ret = i2c_smbus_read_byte_data(client, reg);
+				if (ret < 0) {
+					dev_err(&data->client->dev,
+						"register read failed\n");
+					return ret;
+				}
+			} else {
+				ret &= STK8312_DATA6_MASK;
+				return sign_extend32(ret, 5);
+			}
+		}
+		dev_err(&data->client->dev,
+				"register read failed, data not ready\n");
+		return -EIO;
+	}
+	/* 8bit mode */
+	return sign_extend32(ret, 7);
+}
+
+static int stk8312_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int *val, int *val2, long mask)
+{
+	struct stk8312_data *data = iio_priv(indio_dev);
+
+	if (chan->type != IIO_ACCEL)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		mutex_lock(&data->lock);
+		*val = stk8312_read_accel(data, chan->channel2);
+		mutex_unlock(&data->lock);
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		*val = stk8312_scale_table[data->range][0];
+		*val2 = stk8312_scale_table[data->range][1];
+		return IIO_VAL_INT_PLUS_MICRO;
+	}
+
+	return -EINVAL;
+}
+
+static int stk8312_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     int val, int val2, long mask)
+{
+	int i;
+	int index = -1;
+	int ret;
+	struct stk8312_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
+			if (val == stk8312_scale_table[i][0] &&
+			    val2 == stk8312_scale_table[i][1]) {
+				index = i;
+				break;
+			}
+		if (index < 0)
+			return -EINVAL;
+
+		mutex_lock(&data->lock);
+		ret = stk8312_set_range(data, index);
+		mutex_unlock(&data->lock);
+
+		return ret;
+	}
+
+	return -EINVAL;
+}
+
+static const struct iio_info stk8312_info = {
+	.driver_module		= THIS_MODULE,
+	.read_raw		= stk8312_read_raw,
+	.write_raw		= stk8312_write_raw,
+	.attrs			= &stk8312_attribute_group,
+};
+
+static int stk8312_init(struct iio_dev *indio_dev)
+{
+	int ret;
+	struct stk8312_data *data = iio_priv(indio_dev);
+	struct i2c_client *client = data->client;
+
+	/* A software reset is recommended at power-on */
+	ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
+	if (ret < 0)
+		dev_err(&client->dev, "failed to reset sensor");
+
+	ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
+	if (ret < 0)
+		dev_err(&client->dev, "failed to enable sensor");
+
+	return ret;
+}
+
+static int stk8312_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	int ret;
+	struct iio_dev *indio_dev;
+	struct stk8312_data *data;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev) {
+		dev_err(&client->dev, "iio allocation failed!\n");
+		return -ENOMEM;
+	}
+
+	data = iio_priv(indio_dev);
+	data->client = client;
+	i2c_set_clientdata(client, indio_dev);
+	mutex_init(&data->lock);
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &stk8312_info;
+	indio_dev->name = STK8312_DRIVER_NAME;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->channels = stk8312_channels;
+	indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
+
+	ret = stk8312_init(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "sensor initialization failed\n");
+		return ret;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret < 0) {
+		dev_err(&client->dev, "device_register failed\n");
+		stk8312_set_mode(data, STK8312_MODE_STANDBY);
+	}
+
+	return ret;
+}
+
+static int stk8312_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+	iio_device_unregister(indio_dev);
+
+	return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int stk8312_suspend(struct device *dev)
+{
+	struct stk8312_data *data;
+
+	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+	return stk8312_set_mode(data, STK8312_MODE_STANDBY);
+}
+
+static int stk8312_resume(struct device *dev)
+{
+	struct stk8312_data *data;
+
+	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+	return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
+}
+
+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
+
+#define STK8312_PM_OPS (&stk8312_pm_ops)
+#else
+#define STK8312_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id stk8312_i2c_id[] = {
+	{"STK8312", 0},
+	{}
+};
+
+static const struct acpi_device_id stk8312_acpi_id[] = {
+	{"STK8312", 0},
+	{}
+};
+
+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
+
+static struct i2c_driver stk8312_driver = {
+	.driver = {
+		.name = "stk8312",
+		.pm = STK8312_PM_OPS,
+		.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
+	},
+	.probe =            stk8312_probe,
+	.remove =           stk8312_remove,
+	.id_table =         stk8312_i2c_id,
+};
+
+module_i2c_driver(stk8312_driver);
+
+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

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

* Re: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-05 13:20 [PATCH] iio: accel: Add support for Sensortek STK8312 Tiberiu Breana
@ 2015-05-05 13:43 ` Jonathan Cameron
  2015-05-06 13:12   ` Breana, Tiberiu A
  2015-05-05 13:45 ` Peter Meerwald
  1 sibling, 1 reply; 7+ messages in thread
From: Jonathan Cameron @ 2015-05-05 13:43 UTC (permalink / raw)
  To: Tiberiu Breana, linux-iio; +Cc: Jonathan Cameron



On 5 May 2015 14:20:38 GMT+01:00, Tiberiu Breana <tiberiu.a.breana@intel.com> wrote:
>Minimal implementation of an IIO driver for the Sensortek
>STK8312 3-axis accelerometer.
>Datasheet:
>http://www.syi-group.com/uploadpic/data/201361817562681623.pdf
>
>Includes:
>- ACPI support;
>- read_raw for x,y,z axes;
>- reading and setting the scale (range) parameter.
>- power management
>
>Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>

Looks good. Will take another look when not stuck with only my phone (and a case of broken circuit boards) whilst my laptop is in the hold of the plane...
>---
> drivers/iio/accel/Kconfig   |  11 ++
> drivers/iio/accel/Makefile  |   2 +
>drivers/iio/accel/stk8312.c | 375
>++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 388 insertions(+)
> create mode 100644 drivers/iio/accel/stk8312.c
>
>diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
>index 7c9a9a9..8bd8ccb 100644
>--- a/drivers/iio/accel/Kconfig
>+++ b/drivers/iio/accel/Kconfig
>@@ -136,4 +136,15 @@ config MMA9553
> 
> 	  To compile this driver as a module, choose M here: the module
> 	  will be called mma9553.
>+
>+config STK8312
>+	tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
>+	depends on I2C
>+	help
>+	  Say yes here to get support for the Sensortek STK8312 3-axis
>+	  accelerometer.
>+
>+	  Choosing M will build the driver as a module. If so, the module
>+	  will be called stk8312.
>+
> endmenu
>diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
>index 99d89e4..8b327c1 100644
>--- a/drivers/iio/accel/Makefile
>+++ b/drivers/iio/accel/Makefile
>@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
> obj-$(CONFIG_MMA9551)		+= mma9551.o
> obj-$(CONFIG_MMA9553)		+= mma9553.o
> 
>+obj-$(CONFIG_STK8312)		+= stk8312.o
>+
> obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
> 
> obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
>new file mode 100644
>index 0000000..ff27e6b
>--- /dev/null
>+++ b/drivers/iio/accel/stk8312.c
>@@ -0,0 +1,375 @@
>+/**
>+ * Sensortek STK8312 3-Axis Accelerometer
>+ *
>+ * Copyright (c) 2015, Intel Corporation.
>+ *
>+ * This file is subject to the terms and conditions of version 2 of
>+ * the GNU General Public License. See the file COPYING in the main
>+ * directory of this archive for more details.
>+ *
>+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.
>+ */
>+
>+#include <linux/acpi.h>
>+#include <linux/i2c.h>
>+#include <linux/kernel.h>
>+#include <linux/module.h>
>+#include <linux/delay.h>
>+#include <linux/iio/iio.h>
>+#include <linux/iio/sysfs.h>
>+
>+#define STK8312_REG_XOUT		0x00
>+#define STK8312_REG_YOUT		0x01
>+#define STK8312_REG_ZOUT		0x02
>+#define STK8312_REG_MODE		0x07
>+#define STK8312_REG_STH			0x13
>+#define STK8312_REG_RESET		0x20
>+
>+#define STK8312_MODE_ACTIVE		1
>+#define STK8312_MODE_STANDBY		0
>+#define STK8312_MODE_MASK		0x01
>+#define STK8312_RNG_MASK		0xC0
>+#define STK8312_ALERT_MASK		0x40
>+#define STK8312_DATA6_MASK		0x3F
>+#define STK8312_RNG_SHIFT		6
>+#define STK8312_READ_RETRIES		16
>+
>+#define STK8312_DRIVER_NAME		"stk8312"
>+
>+#define STK8312_SCALE_AVAIL		"0.4747 0.4632 1.2354"
>+
>+static const int stk8312_scale_table[][2] = {
>+	{0, 474700}, {0, 463200}, {1, 235400}
>+};
>+
>+#define STK8312_ACCEL_CHANNEL(axis) {				\
>+	.type = IIO_ACCEL,					\
>+	.modified = 1,						\
>+	.channel2 = IIO_MOD_##axis,				\
>+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
>+	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
>+}
>+
>+static const struct iio_chan_spec stk8312_channels[] = {
>+	STK8312_ACCEL_CHANNEL(X),
>+	STK8312_ACCEL_CHANNEL(Y),
>+	STK8312_ACCEL_CHANNEL(Z),
>+};
>+
>+struct stk8312_data {
>+	struct i2c_client *client;
>+	struct mutex lock;
>+	int range;
>+	u8 mode;
>+};
>+
>+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
>+
>+static struct attribute *stk8312_attributes[] = {
>+	&iio_const_attr_in_accel_scale_available.dev_attr.attr,
>+	NULL,
>+};
>+
>+static const struct attribute_group stk8312_attribute_group = {
>+	.attrs = stk8312_attributes
>+};
>+
>+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
>+{
>+	int ret;
>+	u8 masked_reg;
>+	struct i2c_client *client = data->client;
>+
>+	if (mode > 1)
>+		return -EINVAL;
>+	else if (mode == data->mode)
>+		return 0;
>+
>+	ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
>+	if (ret < 0) {
>+		dev_err(&client->dev, "register read failed\n");
>+		return ret;
>+	}
>+	masked_reg = ret & (~STK8312_MODE_MASK);
>+	masked_reg |= mode;
>+
>+	ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE,
>+					masked_reg);
>+	if (ret < 0)
>+		dev_err(&client->dev, "failed to change sensor mode\n");
>+	else
>+		data->mode = mode;
>+
>+	return ret;
>+}
>+
>+static int stk8312_set_range(struct stk8312_data *data, u8 range)
>+{
>+	int ret;
>+	int masked_reg;
>+	u8 mode;
>+	struct i2c_client *client = data->client;
>+
>+	if (range < 0 || range > 2)
>+		return -EINVAL;
>+
>+	if (range == data->range)
>+		return 0;
>+
>+	mode = data->mode;
>+	/* We need to go in standby mode to modify registers */
>+	ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
>+	if (ret < 0)
>+		return ret;
>+
>+	ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
>+	if (ret < 0) {
>+		dev_err(&client->dev, "register read failed\n");
>+		return ret;
>+	}
>+
>+	masked_reg = ret & (~STK8312_RNG_MASK);
>+	masked_reg |= range << STK8312_RNG_SHIFT;
>+
>+	ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
>+	if (ret < 0)
>+		dev_err(&client->dev, "failed to change sensor range\n");
>+	else
>+		data->range = range;
>+
>+	return stk8312_set_mode(data, mode);
>+}
>+
>+static int stk8312_read_accel(struct stk8312_data *data, int axis)
>+{
>+	int ret;
>+	u8 reg;
>+	int retries = STK8312_READ_RETRIES;
>+	struct i2c_client *client = data->client;
>+
>+	switch (axis) {
>+	case IIO_MOD_X:
>+		reg = STK8312_REG_XOUT;
>+		break;
>+	case IIO_MOD_Y:
>+		reg = STK8312_REG_YOUT;
>+		break;
>+	case IIO_MOD_Z:
>+		reg = STK8312_REG_ZOUT;
>+		break;
>+	default:
>+		return -EINVAL;
>+	}
>+
>+	ret = i2c_smbus_read_byte_data(client, reg);
>+	if (ret < 0) {
>+		dev_err(&client->dev, "register read failed\n");
>+		return ret;
>+	}
>+	if (data->range == 0) {
>+		/* 6bit mode */
>+		while (retries--) {
>+			if (ret & STK8312_ALERT_MASK) {
>+				/* The register is being updated. Try again. */
>+				msleep(20);
>+				ret = i2c_smbus_read_byte_data(client, reg);
>+				if (ret < 0) {
>+					dev_err(&data->client->dev,
>+						"register read failed\n");
>+					return ret;
>+				}
>+			} else {
>+				ret &= STK8312_DATA6_MASK;
>+				return sign_extend32(ret, 5);
>+			}
>+		}
>+		dev_err(&data->client->dev,
>+				"register read failed, data not ready\n");
>+		return -EIO;
>+	}
>+	/* 8bit mode */
>+	return sign_extend32(ret, 7);
>+}
>+
>+static int stk8312_read_raw(struct iio_dev *indio_dev,
>+			    struct iio_chan_spec const *chan,
>+			    int *val, int *val2, long mask)
>+{
>+	struct stk8312_data *data = iio_priv(indio_dev);
>+
>+	if (chan->type != IIO_ACCEL)
>+		return -EINVAL;
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_RAW:
>+		mutex_lock(&data->lock);
>+		*val = stk8312_read_accel(data, chan->channel2);
>+		mutex_unlock(&data->lock);
>+		return IIO_VAL_INT;
>+	case IIO_CHAN_INFO_SCALE:
>+		*val = stk8312_scale_table[data->range][0];
>+		*val2 = stk8312_scale_table[data->range][1];
>+		return IIO_VAL_INT_PLUS_MICRO;
>+	}
>+
>+	return -EINVAL;
>+}
>+
>+static int stk8312_write_raw(struct iio_dev *indio_dev,
>+			     struct iio_chan_spec const *chan,
>+			     int val, int val2, long mask)
>+{
>+	int i;
>+	int index = -1;
>+	int ret;
>+	struct stk8312_data *data = iio_priv(indio_dev);
>+
>+	switch (mask) {
>+	case IIO_CHAN_INFO_SCALE:
>+		for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
>+			if (val == stk8312_scale_table[i][0] &&
>+			    val2 == stk8312_scale_table[i][1]) {
>+				index = i;
>+				break;
>+			}
>+		if (index < 0)
>+			return -EINVAL;
>+
>+		mutex_lock(&data->lock);
>+		ret = stk8312_set_range(data, index);
>+		mutex_unlock(&data->lock);
>+
>+		return ret;
>+	}
>+
>+	return -EINVAL;
>+}
>+
>+static const struct iio_info stk8312_info = {
>+	.driver_module		= THIS_MODULE,
>+	.read_raw		= stk8312_read_raw,
>+	.write_raw		= stk8312_write_raw,
>+	.attrs			= &stk8312_attribute_group,
>+};
>+
>+static int stk8312_init(struct iio_dev *indio_dev)
>+{
>+	int ret;
>+	struct stk8312_data *data = iio_priv(indio_dev);
>+	struct i2c_client *client = data->client;
>+
>+	/* A software reset is recommended at power-on */
>+	ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET,
>0x00);
>+	if (ret < 0)
>+		dev_err(&client->dev, "failed to reset sensor");
>+
>+	ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
>+	if (ret < 0)
>+		dev_err(&client->dev, "failed to enable sensor");
>+
>+	return ret;
>+}
>+
>+static int stk8312_probe(struct i2c_client *client,
>+			 const struct i2c_device_id *id)
>+{
>+	int ret;
>+	struct iio_dev *indio_dev;
>+	struct stk8312_data *data;
>+
>+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>+	if (!indio_dev) {
>+		dev_err(&client->dev, "iio allocation failed!\n");
>+		return -ENOMEM;
>+	}
>+
>+	data = iio_priv(indio_dev);
>+	data->client = client;
>+	i2c_set_clientdata(client, indio_dev);
>+	mutex_init(&data->lock);
>+
>+	indio_dev->dev.parent = &client->dev;
>+	indio_dev->info = &stk8312_info;
>+	indio_dev->name = STK8312_DRIVER_NAME;
>+	indio_dev->modes = INDIO_DIRECT_MODE;
>+	indio_dev->channels = stk8312_channels;
>+	indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
>+
>+	ret = stk8312_init(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&client->dev, "sensor initialization failed\n");
>+		return ret;
>+	}
>+
>+	ret = iio_device_register(indio_dev);
>+	if (ret < 0) {
>+		dev_err(&client->dev, "device_register failed\n");
>+		stk8312_set_mode(data, STK8312_MODE_STANDBY);
>+	}
>+
>+	return ret;
>+}
>+
>+static int stk8312_remove(struct i2c_client *client)
>+{
>+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
>+
>+	iio_device_unregister(indio_dev);
>+
>+	return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
>+}
>+
>+#ifdef CONFIG_PM_SLEEP
>+static int stk8312_suspend(struct device *dev)
>+{
>+	struct stk8312_data *data;
>+
>+	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
>+
>+	return stk8312_set_mode(data, STK8312_MODE_STANDBY);
>+}
>+
>+static int stk8312_resume(struct device *dev)
>+{
>+	struct stk8312_data *data;
>+
>+	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
>+
>+	return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
>+}
>+
>+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,
>stk8312_resume);
>+
>+#define STK8312_PM_OPS (&stk8312_pm_ops)
>+#else
>+#define STK8312_PM_OPS NULL
>+#endif
>+
>+static const struct i2c_device_id stk8312_i2c_id[] = {
>+	{"STK8312", 0},
>+	{}
>+};
>+
>+static const struct acpi_device_id stk8312_acpi_id[] = {
>+	{"STK8312", 0},
>+	{}
>+};
>+
>+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
>+
>+static struct i2c_driver stk8312_driver = {
>+	.driver = {
>+		.name = "stk8312",
>+		.pm = STK8312_PM_OPS,
>+		.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
>+	},
>+	.probe =            stk8312_probe,
>+	.remove =           stk8312_remove,
>+	.id_table =         stk8312_i2c_id,
>+};
>+
>+module_i2c_driver(stk8312_driver);
>+
>+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
>+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
>+MODULE_LICENSE("GPL v2");

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

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

* Re: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-05 13:20 [PATCH] iio: accel: Add support for Sensortek STK8312 Tiberiu Breana
  2015-05-05 13:43 ` Jonathan Cameron
@ 2015-05-05 13:45 ` Peter Meerwald
  1 sibling, 0 replies; 7+ messages in thread
From: Peter Meerwald @ 2015-05-05 13:45 UTC (permalink / raw)
  To: Tiberiu Breana; +Cc: linux-iio, Jonathan Cameron

On Tue, 5 May 2015, Tiberiu Breana wrote:

> Minimal implementation of an IIO driver for the Sensortek
> STK8312 3-axis accelerometer.
> Datasheet:
> http://www.syi-group.com/uploadpic/data/201361817562681623.pdf

some comments below
 
> Includes:
> - ACPI support;
> - read_raw for x,y,z axes;
> - reading and setting the scale (range) parameter.
> - power management
> 
> Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
> ---
>  drivers/iio/accel/Kconfig   |  11 ++
>  drivers/iio/accel/Makefile  |   2 +
>  drivers/iio/accel/stk8312.c | 375 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 388 insertions(+)
>  create mode 100644 drivers/iio/accel/stk8312.c
> 
> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> index 7c9a9a9..8bd8ccb 100644
> --- a/drivers/iio/accel/Kconfig
> +++ b/drivers/iio/accel/Kconfig
> @@ -136,4 +136,15 @@ config MMA9553
>  
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called mma9553.
> +
> +config STK8312
> +	tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
> +	depends on I2C
> +	help
> +	  Say yes here to get support for the Sensortek STK8312 3-axis
> +	  accelerometer.
> +
> +	  Choosing M will build the driver as a module. If so, the module
> +	  will be called stk8312.
> +
>  endmenu
> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> index 99d89e4..8b327c1 100644
> --- a/drivers/iio/accel/Makefile
> +++ b/drivers/iio/accel/Makefile
> @@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)	+= mma9551_core.o
>  obj-$(CONFIG_MMA9551)		+= mma9551.o
>  obj-$(CONFIG_MMA9553)		+= mma9553.o
>  
> +obj-$(CONFIG_STK8312)		+= stk8312.o
> +
>  obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
>  
>  obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
> diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
> new file mode 100644
> index 0000000..ff27e6b
> --- /dev/null
> +++ b/drivers/iio/accel/stk8312.c
> @@ -0,0 +1,375 @@
> +/**
> + * Sensortek STK8312 3-Axis Accelerometer
> + *
> + * Copyright (c) 2015, Intel Corporation.
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License. See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * IIO driver for STK8312; 7-bit I2C address: 0x3D.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +
> +#define STK8312_REG_XOUT		0x00
> +#define STK8312_REG_YOUT		0x01
> +#define STK8312_REG_ZOUT		0x02
> +#define STK8312_REG_MODE		0x07
> +#define STK8312_REG_STH			0x13
> +#define STK8312_REG_RESET		0x20
> +
> +#define STK8312_MODE_ACTIVE		1
> +#define STK8312_MODE_STANDBY		0
> +#define STK8312_MODE_MASK		0x01
> +#define STK8312_RNG_MASK		0xC0
> +#define STK8312_ALERT_MASK		0x40
> +#define STK8312_DATA6_MASK		0x3F
> +#define STK8312_RNG_SHIFT		6
> +#define STK8312_READ_RETRIES		16
> +
> +#define STK8312_DRIVER_NAME		"stk8312"
> +
> +#define STK8312_SCALE_AVAIL		"0.4747 0.4632 1.2354"

the scale values look strange: not monotonic, 0.47 ~= 0.46 ?!

> +
> +static const int stk8312_scale_table[][2] = {
> +	{0, 474700}, {0, 463200}, {1, 235400}
> +};
> +
> +#define STK8312_ACCEL_CHANNEL(axis) {				\
> +	.type = IIO_ACCEL,					\
> +	.modified = 1,						\
> +	.channel2 = IIO_MOD_##axis,				\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
> +}
> +
> +static const struct iio_chan_spec stk8312_channels[] = {
> +	STK8312_ACCEL_CHANNEL(X),
> +	STK8312_ACCEL_CHANNEL(Y),
> +	STK8312_ACCEL_CHANNEL(Z),
> +};
> +
> +struct stk8312_data {
> +	struct i2c_client *client;
> +	struct mutex lock;
> +	int range;
> +	u8 mode;
> +};
> +
> +static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
> +
> +static struct attribute *stk8312_attributes[] = {
> +	&iio_const_attr_in_accel_scale_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group stk8312_attribute_group = {
> +	.attrs = stk8312_attributes
> +};
> +
> +static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
> +{
> +	int ret;
> +	u8 masked_reg;
> +	struct i2c_client *client = data->client;
> +
> +	if (mode > 1)
> +		return -EINVAL;
> +	else if (mode == data->mode)
> +		return 0;
> +
> +	ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "register read failed\n");

the message doesn't explain much, maybe mention register name or drop the 
thing

> +		return ret;
> +	}
> +	masked_reg = ret & (~STK8312_MODE_MASK);
> +	masked_reg |= mode;
> +
> +	ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE,
> +					masked_reg);
> +	if (ret < 0)
> +		dev_err(&client->dev, "failed to change sensor mode\n");
> +	else
> +		data->mode = mode;
> +
> +	return ret;
> +}
> +
> +static int stk8312_set_range(struct stk8312_data *data, u8 range)
> +{
> +	int ret;
> +	int masked_reg;

u8 probably

> +	u8 mode;
> +	struct i2c_client *client = data->client;
> +
> +	if (range < 0 || range > 2)
> +		return -EINVAL;
> +
> +	if (range == data->range)
> +		return 0;
> +
> +	mode = data->mode;
> +	/* We need to go in standby mode to modify registers */
> +	ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "register read failed\n");
> +		return ret;
> +	}
> +
> +	masked_reg = ret & (~STK8312_RNG_MASK);
> +	masked_reg |= range << STK8312_RNG_SHIFT;
> +
> +	ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
> +	if (ret < 0)
> +		dev_err(&client->dev, "failed to change sensor range\n");
> +	else
> +		data->range = range;
> +
> +	return stk8312_set_mode(data, mode);
> +}
> +
> +static int stk8312_read_accel(struct stk8312_data *data, int axis)
> +{
> +	int ret;
> +	u8 reg;
> +	int retries = STK8312_READ_RETRIES;
> +	struct i2c_client *client = data->client;
> +
> +	switch (axis) {

you could use the iio channel's .address field to store the register to 
read and save this switch

> +	case IIO_MOD_X:
> +		reg = STK8312_REG_XOUT;
> +		break;
> +	case IIO_MOD_Y:
> +		reg = STK8312_REG_YOUT;
> +		break;
> +	case IIO_MOD_Z:
> +		reg = STK8312_REG_ZOUT;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = i2c_smbus_read_byte_data(client, reg);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "register read failed\n");
> +		return ret;
> +	}
> +	if (data->range == 0) {
> +		/* 6bit mode */
> +		while (retries--) {
> +			if (ret & STK8312_ALERT_MASK) {
> +				/* The register is being updated. Try again. */
> +				msleep(20);
> +				ret = i2c_smbus_read_byte_data(client, reg);
> +				if (ret < 0) {
> +					dev_err(&data->client->dev,
> +						"register read failed\n");
> +					return ret;
> +				}
> +			} else {
> +				ret &= STK8312_DATA6_MASK;
> +				return sign_extend32(ret, 5);
> +			}
> +		}
> +		dev_err(&data->client->dev,
> +				"register read failed, data not ready\n");
> +		return -EIO;
> +	}
> +	/* 8bit mode */
> +	return sign_extend32(ret, 7);
> +}
> +
> +static int stk8312_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct stk8312_data *data = iio_priv(indio_dev);
> +
> +	if (chan->type != IIO_ACCEL)
> +		return -EINVAL;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		mutex_lock(&data->lock);
> +		*val = stk8312_read_accel(data, chan->channel2);
> +		mutex_unlock(&data->lock);
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = stk8312_scale_table[data->range][0];
> +		*val2 = stk8312_scale_table[data->range][1];
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int stk8312_write_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int val, int val2, long mask)
> +{
> +	int i;
> +	int index = -1;
> +	int ret;
> +	struct stk8312_data *data = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
> +			if (val == stk8312_scale_table[i][0] &&
> +			    val2 == stk8312_scale_table[i][1]) {
> +				index = i;
> +				break;
> +			}
> +		if (index < 0)
> +			return -EINVAL;
> +
> +		mutex_lock(&data->lock);
> +		ret = stk8312_set_range(data, index);
> +		mutex_unlock(&data->lock);
> +
> +		return ret;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static const struct iio_info stk8312_info = {
> +	.driver_module		= THIS_MODULE,
> +	.read_raw		= stk8312_read_raw,
> +	.write_raw		= stk8312_write_raw,
> +	.attrs			= &stk8312_attribute_group,
> +};
> +
> +static int stk8312_init(struct iio_dev *indio_dev)
> +{
> +	int ret;
> +	struct stk8312_data *data = iio_priv(indio_dev);
> +	struct i2c_client *client = data->client;
> +
> +	/* A software reset is recommended at power-on */
> +	ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET, 0x00);
> +	if (ret < 0)
> +		dev_err(&client->dev, "failed to reset sensor");

return ret;

> +
> +	ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
> +	if (ret < 0)
> +		dev_err(&client->dev, "failed to enable sensor");
> +
> +	return ret;
> +}
> +
> +static int stk8312_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	int ret;
> +	struct iio_dev *indio_dev;
> +	struct stk8312_data *data;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev) {
> +		dev_err(&client->dev, "iio allocation failed!\n");
> +		return -ENOMEM;
> +	}
> +
> +	data = iio_priv(indio_dev);
> +	data->client = client;
> +	i2c_set_clientdata(client, indio_dev);
> +	mutex_init(&data->lock);
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->info = &stk8312_info;
> +	indio_dev->name = STK8312_DRIVER_NAME;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->channels = stk8312_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
> +
> +	ret = stk8312_init(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "sensor initialization failed\n");

we already had an error message in _init, not needed here again

> +		return ret;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "device_register failed\n");
> +		stk8312_set_mode(data, STK8312_MODE_STANDBY);
> +	}
> +
> +	return ret;
> +}
> +
> +static int stk8312_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +
> +	iio_device_unregister(indio_dev);
> +
> +	return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int stk8312_suspend(struct device *dev)
> +{
> +	struct stk8312_data *data;
> +
> +	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> +
> +	return stk8312_set_mode(data, STK8312_MODE_STANDBY);
> +}
> +
> +static int stk8312_resume(struct device *dev)
> +{
> +	struct stk8312_data *data;
> +
> +	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> +
> +	return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume);
> +
> +#define STK8312_PM_OPS (&stk8312_pm_ops)
> +#else
> +#define STK8312_PM_OPS NULL
> +#endif
> +
> +static const struct i2c_device_id stk8312_i2c_id[] = {
> +	{"STK8312", 0},
> +	{}
> +};
> +
> +static const struct acpi_device_id stk8312_acpi_id[] = {
> +	{"STK8312", 0},
> +	{}
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
> +
> +static struct i2c_driver stk8312_driver = {
> +	.driver = {
> +		.name = "stk8312",
> +		.pm = STK8312_PM_OPS,
> +		.acpi_match_table = ACPI_PTR(stk8312_acpi_id),
> +	},
> +	.probe =            stk8312_probe,
> +	.remove =           stk8312_remove,
> +	.id_table =         stk8312_i2c_id,
> +};
> +
> +module_i2c_driver(stk8312_driver);
> +
> +MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
> +MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
> +MODULE_LICENSE("GPL v2");
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)

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

* RE: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-05 13:43 ` Jonathan Cameron
@ 2015-05-06 13:12   ` Breana, Tiberiu A
  2015-05-06 14:42     ` Peter Meerwald
  2015-05-06 17:35     ` Jonathan Cameron
  0 siblings, 2 replies; 7+ messages in thread
From: Breana, Tiberiu A @ 2015-05-06 13:12 UTC (permalink / raw)
  To: linux-iio, pmeerw, Jonathan Cameron

Thanks for the reviews.=0A=
=0A=
The accelerometer has 3 measurement ranges:=0A=
-1.5g - +1.5g (6 bit, signed)=0A=
-6g - +6g (8 bit, signed)=0A=
-16g - +16g (8 bit, signed)=0A=
=0A=
scale1 =3D 1.5 * 9.81 / 31 ~=3D 0.4747=0A=
scale2 =3D 6 * 9.81 / 127 ~=3D 0.4634=0A=
scale3 =3D 16 * 9.81 / 127 ~=3D 1.2359=0A=
=0A=
Thus the resulting, non-monotonic scales.=0A=
I'll upload v2 with the rest of the suggested changes.=0A=
=0A=
Regards,=0A=
Tiberiu=0A=
________________________________________=0A=
From: linux-iio-owner@vger.kernel.org [linux-iio-owner@vger.kernel.org] on =
behalf of Jonathan Cameron [jic23@jic23.retrosnub.co.uk]=0A=
Sent: Tuesday, May 05, 2015 4:43 PM=0A=
To: Breana, Tiberiu A; linux-iio@vger.kernel.org=0A=
Cc: Jonathan Cameron=0A=
Subject: Re: [PATCH] iio: accel: Add support for Sensortek STK8312=0A=
=0A=
On 5 May 2015 14:20:38 GMT+01:00, Tiberiu Breana <tiberiu.a.breana@intel.co=
m> wrote:=0A=
>Minimal implementation of an IIO driver for the Sensortek=0A=
>STK8312 3-axis accelerometer.=0A=
>Datasheet:=0A=
>http://www.syi-group.com/uploadpic/data/201361817562681623.pdf=0A=
>=0A=
>Includes:=0A=
>- ACPI support;=0A=
>- read_raw for x,y,z axes;=0A=
>- reading and setting the scale (range) parameter.=0A=
>- power management=0A=
>=0A=
>Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>=0A=
=0A=
Looks good. Will take another look when not stuck with only my phone (and a=
 case of broken circuit boards) whilst my laptop is in the hold of the plan=
e...=0A=
>---=0A=
> drivers/iio/accel/Kconfig   |  11 ++=0A=
> drivers/iio/accel/Makefile  |   2 +=0A=
>drivers/iio/accel/stk8312.c | 375=0A=
>++++++++++++++++++++++++++++++++++++++++++++=0A=
> 3 files changed, 388 insertions(+)=0A=
> create mode 100644 drivers/iio/accel/stk8312.c=0A=
>=0A=
>diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig=0A=
>index 7c9a9a9..8bd8ccb 100644=0A=
>--- a/drivers/iio/accel/Kconfig=0A=
>+++ b/drivers/iio/accel/Kconfig=0A=
>@@ -136,4 +136,15 @@ config MMA9553=0A=
>=0A=
>         To compile this driver as a module, choose M here: the module=0A=
>         will be called mma9553.=0A=
>+=0A=
>+config STK8312=0A=
>+      tristate "Sensortek STK8312 3-Axis Accelerometer Driver"=0A=
>+      depends on I2C=0A=
>+      help=0A=
>+        Say yes here to get support for the Sensortek STK8312 3-axis=0A=
>+        accelerometer.=0A=
>+=0A=
>+        Choosing M will build the driver as a module. If so, the module=
=0A=
>+        will be called stk8312.=0A=
>+=0A=
> endmenu=0A=
>diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile=0A=
>index 99d89e4..8b327c1 100644=0A=
>--- a/drivers/iio/accel/Makefile=0A=
>+++ b/drivers/iio/accel/Makefile=0A=
>@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)   +=3D mma9551_core.o=0A=
> obj-$(CONFIG_MMA9551)         +=3D mma9551.o=0A=
> obj-$(CONFIG_MMA9553)         +=3D mma9553.o=0A=
>=0A=
>+obj-$(CONFIG_STK8312)         +=3D stk8312.o=0A=
>+=0A=
> obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) +=3D ssp_accel_sensor.o=0A=
>=0A=
> obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) +=3D st_accel.o=0A=
>diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c=0A=
>new file mode 100644=0A=
>index 0000000..ff27e6b=0A=
>--- /dev/null=0A=
>+++ b/drivers/iio/accel/stk8312.c=0A=
>@@ -0,0 +1,375 @@=0A=
>+/**=0A=
>+ * Sensortek STK8312 3-Axis Accelerometer=0A=
>+ *=0A=
>+ * Copyright (c) 2015, Intel Corporation.=0A=
>+ *=0A=
>+ * This file is subject to the terms and conditions of version 2 of=0A=
>+ * the GNU General Public License. See the file COPYING in the main=0A=
>+ * directory of this archive for more details.=0A=
>+ *=0A=
>+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.=0A=
>+ */=0A=
>+=0A=
>+#include <linux/acpi.h>=0A=
>+#include <linux/i2c.h>=0A=
>+#include <linux/kernel.h>=0A=
>+#include <linux/module.h>=0A=
>+#include <linux/delay.h>=0A=
>+#include <linux/iio/iio.h>=0A=
>+#include <linux/iio/sysfs.h>=0A=
>+=0A=
>+#define STK8312_REG_XOUT              0x00=0A=
>+#define STK8312_REG_YOUT              0x01=0A=
>+#define STK8312_REG_ZOUT              0x02=0A=
>+#define STK8312_REG_MODE              0x07=0A=
>+#define STK8312_REG_STH                       0x13=0A=
>+#define STK8312_REG_RESET             0x20=0A=
>+=0A=
>+#define STK8312_MODE_ACTIVE           1=0A=
>+#define STK8312_MODE_STANDBY          0=0A=
>+#define STK8312_MODE_MASK             0x01=0A=
>+#define STK8312_RNG_MASK              0xC0=0A=
>+#define STK8312_ALERT_MASK            0x40=0A=
>+#define STK8312_DATA6_MASK            0x3F=0A=
>+#define STK8312_RNG_SHIFT             6=0A=
>+#define STK8312_READ_RETRIES          16=0A=
>+=0A=
>+#define STK8312_DRIVER_NAME           "stk8312"=0A=
>+=0A=
>+#define STK8312_SCALE_AVAIL           "0.4747 0.4632 1.2354"=0A=
>+=0A=
>+static const int stk8312_scale_table[][2] =3D {=0A=
>+      {0, 474700}, {0, 463200}, {1, 235400}=0A=
>+};=0A=
>+=0A=
>+#define STK8312_ACCEL_CHANNEL(axis) {                         \=0A=
>+      .type =3D IIO_ACCEL,                                      \=0A=
>+      .modified =3D 1,                                          \=0A=
>+      .channel2 =3D IIO_MOD_##axis,                             \=0A=
>+      .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW),           \=0A=
>+      .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE),   \=0A=
>+}=0A=
>+=0A=
>+static const struct iio_chan_spec stk8312_channels[] =3D {=0A=
>+      STK8312_ACCEL_CHANNEL(X),=0A=
>+      STK8312_ACCEL_CHANNEL(Y),=0A=
>+      STK8312_ACCEL_CHANNEL(Z),=0A=
>+};=0A=
>+=0A=
>+struct stk8312_data {=0A=
>+      struct i2c_client *client;=0A=
>+      struct mutex lock;=0A=
>+      int range;=0A=
>+      u8 mode;=0A=
>+};=0A=
>+=0A=
>+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);=0A=
>+=0A=
>+static struct attribute *stk8312_attributes[] =3D {=0A=
>+      &iio_const_attr_in_accel_scale_available.dev_attr.attr,=0A=
>+      NULL,=0A=
>+};=0A=
>+=0A=
>+static const struct attribute_group stk8312_attribute_group =3D {=0A=
>+      .attrs =3D stk8312_attributes=0A=
>+};=0A=
>+=0A=
>+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)=0A=
>+{=0A=
>+      int ret;=0A=
>+      u8 masked_reg;=0A=
>+      struct i2c_client *client =3D data->client;=0A=
>+=0A=
>+      if (mode > 1)=0A=
>+              return -EINVAL;=0A=
>+      else if (mode =3D=3D data->mode)=0A=
>+              return 0;=0A=
>+=0A=
>+      ret =3D i2c_smbus_read_byte_data(client, STK8312_REG_MODE);=0A=
>+      if (ret < 0) {=0A=
>+              dev_err(&client->dev, "register read failed\n");=0A=
>+              return ret;=0A=
>+      }=0A=
>+      masked_reg =3D ret & (~STK8312_MODE_MASK);=0A=
>+      masked_reg |=3D mode;=0A=
>+=0A=
>+      ret =3D i2c_smbus_write_byte_data(client, STK8312_REG_MODE,=0A=
>+                                      masked_reg);=0A=
>+      if (ret < 0)=0A=
>+              dev_err(&client->dev, "failed to change sensor mode\n");=0A=
>+      else=0A=
>+              data->mode =3D mode;=0A=
>+=0A=
>+      return ret;=0A=
>+}=0A=
>+=0A=
>+static int stk8312_set_range(struct stk8312_data *data, u8 range)=0A=
>+{=0A=
>+      int ret;=0A=
>+      int masked_reg;=0A=
>+      u8 mode;=0A=
>+      struct i2c_client *client =3D data->client;=0A=
>+=0A=
>+      if (range < 0 || range > 2)=0A=
>+              return -EINVAL;=0A=
>+=0A=
>+      if (range =3D=3D data->range)=0A=
>+              return 0;=0A=
>+=0A=
>+      mode =3D data->mode;=0A=
>+      /* We need to go in standby mode to modify registers */=0A=
>+      ret =3D stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>+      if (ret < 0)=0A=
>+              return ret;=0A=
>+=0A=
>+      ret =3D i2c_smbus_read_byte_data(client, STK8312_REG_STH);=0A=
>+      if (ret < 0) {=0A=
>+              dev_err(&client->dev, "register read failed\n");=0A=
>+              return ret;=0A=
>+      }=0A=
>+=0A=
>+      masked_reg =3D ret & (~STK8312_RNG_MASK);=0A=
>+      masked_reg |=3D range << STK8312_RNG_SHIFT;=0A=
>+=0A=
>+      ret =3D i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_r=
eg);=0A=
>+      if (ret < 0)=0A=
>+              dev_err(&client->dev, "failed to change sensor range\n");=
=0A=
>+      else=0A=
>+              data->range =3D range;=0A=
>+=0A=
>+      return stk8312_set_mode(data, mode);=0A=
>+}=0A=
>+=0A=
>+static int stk8312_read_accel(struct stk8312_data *data, int axis)=0A=
>+{=0A=
>+      int ret;=0A=
>+      u8 reg;=0A=
>+      int retries =3D STK8312_READ_RETRIES;=0A=
>+      struct i2c_client *client =3D data->client;=0A=
>+=0A=
>+      switch (axis) {=0A=
>+      case IIO_MOD_X:=0A=
>+              reg =3D STK8312_REG_XOUT;=0A=
>+              break;=0A=
>+      case IIO_MOD_Y:=0A=
>+              reg =3D STK8312_REG_YOUT;=0A=
>+              break;=0A=
>+      case IIO_MOD_Z:=0A=
>+              reg =3D STK8312_REG_ZOUT;=0A=
>+              break;=0A=
>+      default:=0A=
>+              return -EINVAL;=0A=
>+      }=0A=
>+=0A=
>+      ret =3D i2c_smbus_read_byte_data(client, reg);=0A=
>+      if (ret < 0) {=0A=
>+              dev_err(&client->dev, "register read failed\n");=0A=
>+              return ret;=0A=
>+      }=0A=
>+      if (data->range =3D=3D 0) {=0A=
>+              /* 6bit mode */=0A=
>+              while (retries--) {=0A=
>+                      if (ret & STK8312_ALERT_MASK) {=0A=
>+                              /* The register is being updated. Try again=
. */=0A=
>+                              msleep(20);=0A=
>+                              ret =3D i2c_smbus_read_byte_data(client, re=
g);=0A=
>+                              if (ret < 0) {=0A=
>+                                      dev_err(&data->client->dev,=0A=
>+                                              "register read failed\n");=
=0A=
>+                                      return ret;=0A=
>+                              }=0A=
>+                      } else {=0A=
>+                              ret &=3D STK8312_DATA6_MASK;=0A=
>+                              return sign_extend32(ret, 5);=0A=
>+                      }=0A=
>+              }=0A=
>+              dev_err(&data->client->dev,=0A=
>+                              "register read failed, data not ready\n");=
=0A=
>+              return -EIO;=0A=
>+      }=0A=
>+      /* 8bit mode */=0A=
>+      return sign_extend32(ret, 7);=0A=
>+}=0A=
>+=0A=
>+static int stk8312_read_raw(struct iio_dev *indio_dev,=0A=
>+                          struct iio_chan_spec const *chan,=0A=
>+                          int *val, int *val2, long mask)=0A=
>+{=0A=
>+      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>+=0A=
>+      if (chan->type !=3D IIO_ACCEL)=0A=
>+              return -EINVAL;=0A=
>+=0A=
>+      switch (mask) {=0A=
>+      case IIO_CHAN_INFO_RAW:=0A=
>+              mutex_lock(&data->lock);=0A=
>+              *val =3D stk8312_read_accel(data, chan->channel2);=0A=
>+              mutex_unlock(&data->lock);=0A=
>+              return IIO_VAL_INT;=0A=
>+      case IIO_CHAN_INFO_SCALE:=0A=
>+              *val =3D stk8312_scale_table[data->range][0];=0A=
>+              *val2 =3D stk8312_scale_table[data->range][1];=0A=
>+              return IIO_VAL_INT_PLUS_MICRO;=0A=
>+      }=0A=
>+=0A=
>+      return -EINVAL;=0A=
>+}=0A=
>+=0A=
>+static int stk8312_write_raw(struct iio_dev *indio_dev,=0A=
>+                           struct iio_chan_spec const *chan,=0A=
>+                           int val, int val2, long mask)=0A=
>+{=0A=
>+      int i;=0A=
>+      int index =3D -1;=0A=
>+      int ret;=0A=
>+      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>+=0A=
>+      switch (mask) {=0A=
>+      case IIO_CHAN_INFO_SCALE:=0A=
>+              for (i =3D 0; i < ARRAY_SIZE(stk8312_scale_table); i++)=0A=
>+                      if (val =3D=3D stk8312_scale_table[i][0] &&=0A=
>+                          val2 =3D=3D stk8312_scale_table[i][1]) {=0A=
>+                              index =3D i;=0A=
>+                              break;=0A=
>+                      }=0A=
>+              if (index < 0)=0A=
>+                      return -EINVAL;=0A=
>+=0A=
>+              mutex_lock(&data->lock);=0A=
>+              ret =3D stk8312_set_range(data, index);=0A=
>+              mutex_unlock(&data->lock);=0A=
>+=0A=
>+              return ret;=0A=
>+      }=0A=
>+=0A=
>+      return -EINVAL;=0A=
>+}=0A=
>+=0A=
>+static const struct iio_info stk8312_info =3D {=0A=
>+      .driver_module          =3D THIS_MODULE,=0A=
>+      .read_raw               =3D stk8312_read_raw,=0A=
>+      .write_raw              =3D stk8312_write_raw,=0A=
>+      .attrs                  =3D &stk8312_attribute_group,=0A=
>+};=0A=
>+=0A=
>+static int stk8312_init(struct iio_dev *indio_dev)=0A=
>+{=0A=
>+      int ret;=0A=
>+      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>+      struct i2c_client *client =3D data->client;=0A=
>+=0A=
>+      /* A software reset is recommended at power-on */=0A=
>+      ret =3D i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET,=
=0A=
>0x00);=0A=
>+      if (ret < 0)=0A=
>+              dev_err(&client->dev, "failed to reset sensor");=0A=
>+=0A=
>+      ret =3D stk8312_set_mode(data, STK8312_MODE_ACTIVE);=0A=
>+      if (ret < 0)=0A=
>+              dev_err(&client->dev, "failed to enable sensor");=0A=
>+=0A=
>+      return ret;=0A=
>+}=0A=
>+=0A=
>+static int stk8312_probe(struct i2c_client *client,=0A=
>+                       const struct i2c_device_id *id)=0A=
>+{=0A=
>+      int ret;=0A=
>+      struct iio_dev *indio_dev;=0A=
>+      struct stk8312_data *data;=0A=
>+=0A=
>+      indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*data));=
=0A=
>+      if (!indio_dev) {=0A=
>+              dev_err(&client->dev, "iio allocation failed!\n");=0A=
>+              return -ENOMEM;=0A=
>+      }=0A=
>+=0A=
>+      data =3D iio_priv(indio_dev);=0A=
>+      data->client =3D client;=0A=
>+      i2c_set_clientdata(client, indio_dev);=0A=
>+      mutex_init(&data->lock);=0A=
>+=0A=
>+      indio_dev->dev.parent =3D &client->dev;=0A=
>+      indio_dev->info =3D &stk8312_info;=0A=
>+      indio_dev->name =3D STK8312_DRIVER_NAME;=0A=
>+      indio_dev->modes =3D INDIO_DIRECT_MODE;=0A=
>+      indio_dev->channels =3D stk8312_channels;=0A=
>+      indio_dev->num_channels =3D ARRAY_SIZE(stk8312_channels);=0A=
>+=0A=
>+      ret =3D stk8312_init(indio_dev);=0A=
>+      if (ret < 0) {=0A=
>+              dev_err(&client->dev, "sensor initialization failed\n");=0A=
>+              return ret;=0A=
>+      }=0A=
>+=0A=
>+      ret =3D iio_device_register(indio_dev);=0A=
>+      if (ret < 0) {=0A=
>+              dev_err(&client->dev, "device_register failed\n");=0A=
>+              stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>+      }=0A=
>+=0A=
>+      return ret;=0A=
>+}=0A=
>+=0A=
>+static int stk8312_remove(struct i2c_client *client)=0A=
>+{=0A=
>+      struct iio_dev *indio_dev =3D i2c_get_clientdata(client);=0A=
>+=0A=
>+      iio_device_unregister(indio_dev);=0A=
>+=0A=
>+      return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);=
=0A=
>+}=0A=
>+=0A=
>+#ifdef CONFIG_PM_SLEEP=0A=
>+static int stk8312_suspend(struct device *dev)=0A=
>+{=0A=
>+      struct stk8312_data *data;=0A=
>+=0A=
>+      data =3D iio_priv(i2c_get_clientdata(to_i2c_client(dev)));=0A=
>+=0A=
>+      return stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>+}=0A=
>+=0A=
>+static int stk8312_resume(struct device *dev)=0A=
>+{=0A=
>+      struct stk8312_data *data;=0A=
>+=0A=
>+      data =3D iio_priv(i2c_get_clientdata(to_i2c_client(dev)));=0A=
>+=0A=
>+      return stk8312_set_mode(data, STK8312_MODE_ACTIVE);=0A=
>+}=0A=
>+=0A=
>+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,=0A=
>stk8312_resume);=0A=
>+=0A=
>+#define STK8312_PM_OPS (&stk8312_pm_ops)=0A=
>+#else=0A=
>+#define STK8312_PM_OPS NULL=0A=
>+#endif=0A=
>+=0A=
>+static const struct i2c_device_id stk8312_i2c_id[] =3D {=0A=
>+      {"STK8312", 0},=0A=
>+      {}=0A=
>+};=0A=
>+=0A=
>+static const struct acpi_device_id stk8312_acpi_id[] =3D {=0A=
>+      {"STK8312", 0},=0A=
>+      {}=0A=
>+};=0A=
>+=0A=
>+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);=0A=
>+=0A=
>+static struct i2c_driver stk8312_driver =3D {=0A=
>+      .driver =3D {=0A=
>+              .name =3D "stk8312",=0A=
>+              .pm =3D STK8312_PM_OPS,=0A=
>+              .acpi_match_table =3D ACPI_PTR(stk8312_acpi_id),=0A=
>+      },=0A=
>+      .probe =3D            stk8312_probe,=0A=
>+      .remove =3D           stk8312_remove,=0A=
>+      .id_table =3D         stk8312_i2c_id,=0A=
>+};=0A=
>+=0A=
>+module_i2c_driver(stk8312_driver);=0A=
>+=0A=
>+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");=0A=
>+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");=0A=
>+MODULE_LICENSE("GPL v2");=0A=
=0A=
--=0A=
Sent from my Android device with K-9 Mail. Please excuse my brevity.=0A=
--=0A=
To unsubscribe from this list: send the line "unsubscribe linux-iio" in=0A=
the body of a message to majordomo@vger.kernel.org=0A=
More majordomo info at  http://vger.kernel.org/majordomo-info.html=0A=

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

* RE: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-06 13:12   ` Breana, Tiberiu A
@ 2015-05-06 14:42     ` Peter Meerwald
  2015-05-06 17:35     ` Jonathan Cameron
  1 sibling, 0 replies; 7+ messages in thread
From: Peter Meerwald @ 2015-05-06 14:42 UTC (permalink / raw)
  To: Breana, Tiberiu A; +Cc: linux-iio, Jonathan Cameron

> The accelerometer has 3 measurement ranges:
> -1.5g - +1.5g (6 bit, signed)
> -6g - +6g (8 bit, signed)
> -16g - +16g (8 bit, signed)
> 
> scale1 = 1.5 * 9.81 / 31 ~= 0.4747
> scale2 = 6 * 9.81 / 127 ~= 0.4634
> scale3 = 16 * 9.81 / 127 ~= 1.2359

that explains things :)
probably worth a comment

thanks, p.
 
> Thus the resulting, non-monotonic scales.
> I'll upload v2 with the rest of the suggested changes.
> 
> Regards,
> Tiberiu
> ________________________________________
> From: linux-iio-owner@vger.kernel.org [linux-iio-owner@vger.kernel.org] on behalf of Jonathan Cameron [jic23@jic23.retrosnub.co.uk]
> Sent: Tuesday, May 05, 2015 4:43 PM
> To: Breana, Tiberiu A; linux-iio@vger.kernel.org
> Cc: Jonathan Cameron
> Subject: Re: [PATCH] iio: accel: Add support for Sensortek STK8312
> 
> On 5 May 2015 14:20:38 GMT+01:00, Tiberiu Breana <tiberiu.a.breana@intel.com> wrote:
> >Minimal implementation of an IIO driver for the Sensortek
> >STK8312 3-axis accelerometer.
> >Datasheet:
> >http://www.syi-group.com/uploadpic/data/201361817562681623.pdf
> >
> >Includes:
> >- ACPI support;
> >- read_raw for x,y,z axes;
> >- reading and setting the scale (range) parameter.
> >- power management
> >
> >Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
> 
> Looks good. Will take another look when not stuck with only my phone (and a case of broken circuit boards) whilst my laptop is in the hold of the plane...
> >---
> > drivers/iio/accel/Kconfig   |  11 ++
> > drivers/iio/accel/Makefile  |   2 +
> >drivers/iio/accel/stk8312.c | 375
> >++++++++++++++++++++++++++++++++++++++++++++
> > 3 files changed, 388 insertions(+)
> > create mode 100644 drivers/iio/accel/stk8312.c
> >
> >diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
> >index 7c9a9a9..8bd8ccb 100644
> >--- a/drivers/iio/accel/Kconfig
> >+++ b/drivers/iio/accel/Kconfig
> >@@ -136,4 +136,15 @@ config MMA9553
> >
> >         To compile this driver as a module, choose M here: the module
> >         will be called mma9553.
> >+
> >+config STK8312
> >+      tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
> >+      depends on I2C
> >+      help
> >+        Say yes here to get support for the Sensortek STK8312 3-axis
> >+        accelerometer.
> >+
> >+        Choosing M will build the driver as a module. If so, the module
> >+        will be called stk8312.
> >+
> > endmenu
> >diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
> >index 99d89e4..8b327c1 100644
> >--- a/drivers/iio/accel/Makefile
> >+++ b/drivers/iio/accel/Makefile
> >@@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)   += mma9551_core.o
> > obj-$(CONFIG_MMA9551)         += mma9551.o
> > obj-$(CONFIG_MMA9553)         += mma9553.o
> >
> >+obj-$(CONFIG_STK8312)         += stk8312.o
> >+
> > obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
> >
> > obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
> >diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
> >new file mode 100644
> >index 0000000..ff27e6b
> >--- /dev/null
> >+++ b/drivers/iio/accel/stk8312.c
> >@@ -0,0 +1,375 @@
> >+/**
> >+ * Sensortek STK8312 3-Axis Accelerometer
> >+ *
> >+ * Copyright (c) 2015, Intel Corporation.
> >+ *
> >+ * This file is subject to the terms and conditions of version 2 of
> >+ * the GNU General Public License. See the file COPYING in the main
> >+ * directory of this archive for more details.
> >+ *
> >+ * IIO driver for STK8312; 7-bit I2C address: 0x3D.
> >+ */
> >+
> >+#include <linux/acpi.h>
> >+#include <linux/i2c.h>
> >+#include <linux/kernel.h>
> >+#include <linux/module.h>
> >+#include <linux/delay.h>
> >+#include <linux/iio/iio.h>
> >+#include <linux/iio/sysfs.h>
> >+
> >+#define STK8312_REG_XOUT              0x00
> >+#define STK8312_REG_YOUT              0x01
> >+#define STK8312_REG_ZOUT              0x02
> >+#define STK8312_REG_MODE              0x07
> >+#define STK8312_REG_STH                       0x13
> >+#define STK8312_REG_RESET             0x20
> >+
> >+#define STK8312_MODE_ACTIVE           1
> >+#define STK8312_MODE_STANDBY          0
> >+#define STK8312_MODE_MASK             0x01
> >+#define STK8312_RNG_MASK              0xC0
> >+#define STK8312_ALERT_MASK            0x40
> >+#define STK8312_DATA6_MASK            0x3F
> >+#define STK8312_RNG_SHIFT             6
> >+#define STK8312_READ_RETRIES          16
> >+
> >+#define STK8312_DRIVER_NAME           "stk8312"
> >+
> >+#define STK8312_SCALE_AVAIL           "0.4747 0.4632 1.2354"
> >+
> >+static const int stk8312_scale_table[][2] = {
> >+      {0, 474700}, {0, 463200}, {1, 235400}
> >+};
> >+
> >+#define STK8312_ACCEL_CHANNEL(axis) {                         \
> >+      .type = IIO_ACCEL,                                      \
> >+      .modified = 1,                                          \
> >+      .channel2 = IIO_MOD_##axis,                             \
> >+      .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
> >+      .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
> >+}
> >+
> >+static const struct iio_chan_spec stk8312_channels[] = {
> >+      STK8312_ACCEL_CHANNEL(X),
> >+      STK8312_ACCEL_CHANNEL(Y),
> >+      STK8312_ACCEL_CHANNEL(Z),
> >+};
> >+
> >+struct stk8312_data {
> >+      struct i2c_client *client;
> >+      struct mutex lock;
> >+      int range;
> >+      u8 mode;
> >+};
> >+
> >+static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
> >+
> >+static struct attribute *stk8312_attributes[] = {
> >+      &iio_const_attr_in_accel_scale_available.dev_attr.attr,
> >+      NULL,
> >+};
> >+
> >+static const struct attribute_group stk8312_attribute_group = {
> >+      .attrs = stk8312_attributes
> >+};
> >+
> >+static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
> >+{
> >+      int ret;
> >+      u8 masked_reg;
> >+      struct i2c_client *client = data->client;
> >+
> >+      if (mode > 1)
> >+              return -EINVAL;
> >+      else if (mode == data->mode)
> >+              return 0;
> >+
> >+      ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
> >+      if (ret < 0) {
> >+              dev_err(&client->dev, "register read failed\n");
> >+              return ret;
> >+      }
> >+      masked_reg = ret & (~STK8312_MODE_MASK);
> >+      masked_reg |= mode;
> >+
> >+      ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE,
> >+                                      masked_reg);
> >+      if (ret < 0)
> >+              dev_err(&client->dev, "failed to change sensor mode\n");
> >+      else
> >+              data->mode = mode;
> >+
> >+      return ret;
> >+}
> >+
> >+static int stk8312_set_range(struct stk8312_data *data, u8 range)
> >+{
> >+      int ret;
> >+      int masked_reg;
> >+      u8 mode;
> >+      struct i2c_client *client = data->client;
> >+
> >+      if (range < 0 || range > 2)
> >+              return -EINVAL;
> >+
> >+      if (range == data->range)
> >+              return 0;
> >+
> >+      mode = data->mode;
> >+      /* We need to go in standby mode to modify registers */
> >+      ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
> >+      if (ret < 0)
> >+              return ret;
> >+
> >+      ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
> >+      if (ret < 0) {
> >+              dev_err(&client->dev, "register read failed\n");
> >+              return ret;
> >+      }
> >+
> >+      masked_reg = ret & (~STK8312_RNG_MASK);
> >+      masked_reg |= range << STK8312_RNG_SHIFT;
> >+
> >+      ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
> >+      if (ret < 0)
> >+              dev_err(&client->dev, "failed to change sensor range\n");
> >+      else
> >+              data->range = range;
> >+
> >+      return stk8312_set_mode(data, mode);
> >+}
> >+
> >+static int stk8312_read_accel(struct stk8312_data *data, int axis)
> >+{
> >+      int ret;
> >+      u8 reg;
> >+      int retries = STK8312_READ_RETRIES;
> >+      struct i2c_client *client = data->client;
> >+
> >+      switch (axis) {
> >+      case IIO_MOD_X:
> >+              reg = STK8312_REG_XOUT;
> >+              break;
> >+      case IIO_MOD_Y:
> >+              reg = STK8312_REG_YOUT;
> >+              break;
> >+      case IIO_MOD_Z:
> >+              reg = STK8312_REG_ZOUT;
> >+              break;
> >+      default:
> >+              return -EINVAL;
> >+      }
> >+
> >+      ret = i2c_smbus_read_byte_data(client, reg);
> >+      if (ret < 0) {
> >+              dev_err(&client->dev, "register read failed\n");
> >+              return ret;
> >+      }
> >+      if (data->range == 0) {
> >+              /* 6bit mode */
> >+              while (retries--) {
> >+                      if (ret & STK8312_ALERT_MASK) {
> >+                              /* The register is being updated. Try again. */
> >+                              msleep(20);
> >+                              ret = i2c_smbus_read_byte_data(client, reg);
> >+                              if (ret < 0) {
> >+                                      dev_err(&data->client->dev,
> >+                                              "register read failed\n");
> >+                                      return ret;
> >+                              }
> >+                      } else {
> >+                              ret &= STK8312_DATA6_MASK;
> >+                              return sign_extend32(ret, 5);
> >+                      }
> >+              }
> >+              dev_err(&data->client->dev,
> >+                              "register read failed, data not ready\n");
> >+              return -EIO;
> >+      }
> >+      /* 8bit mode */
> >+      return sign_extend32(ret, 7);
> >+}
> >+
> >+static int stk8312_read_raw(struct iio_dev *indio_dev,
> >+                          struct iio_chan_spec const *chan,
> >+                          int *val, int *val2, long mask)
> >+{
> >+      struct stk8312_data *data = iio_priv(indio_dev);
> >+
> >+      if (chan->type != IIO_ACCEL)
> >+              return -EINVAL;
> >+
> >+      switch (mask) {
> >+      case IIO_CHAN_INFO_RAW:
> >+              mutex_lock(&data->lock);
> >+              *val = stk8312_read_accel(data, chan->channel2);
> >+              mutex_unlock(&data->lock);
> >+              return IIO_VAL_INT;
> >+      case IIO_CHAN_INFO_SCALE:
> >+              *val = stk8312_scale_table[data->range][0];
> >+              *val2 = stk8312_scale_table[data->range][1];
> >+              return IIO_VAL_INT_PLUS_MICRO;
> >+      }
> >+
> >+      return -EINVAL;
> >+}
> >+
> >+static int stk8312_write_raw(struct iio_dev *indio_dev,
> >+                           struct iio_chan_spec const *chan,
> >+                           int val, int val2, long mask)
> >+{
> >+      int i;
> >+      int index = -1;
> >+      int ret;
> >+      struct stk8312_data *data = iio_priv(indio_dev);
> >+
> >+      switch (mask) {
> >+      case IIO_CHAN_INFO_SCALE:
> >+              for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
> >+                      if (val == stk8312_scale_table[i][0] &&
> >+                          val2 == stk8312_scale_table[i][1]) {
> >+                              index = i;
> >+                              break;
> >+                      }
> >+              if (index < 0)
> >+                      return -EINVAL;
> >+
> >+              mutex_lock(&data->lock);
> >+              ret = stk8312_set_range(data, index);
> >+              mutex_unlock(&data->lock);
> >+
> >+              return ret;
> >+      }
> >+
> >+      return -EINVAL;
> >+}
> >+
> >+static const struct iio_info stk8312_info = {
> >+      .driver_module          = THIS_MODULE,
> >+      .read_raw               = stk8312_read_raw,
> >+      .write_raw              = stk8312_write_raw,
> >+      .attrs                  = &stk8312_attribute_group,
> >+};
> >+
> >+static int stk8312_init(struct iio_dev *indio_dev)
> >+{
> >+      int ret;
> >+      struct stk8312_data *data = iio_priv(indio_dev);
> >+      struct i2c_client *client = data->client;
> >+
> >+      /* A software reset is recommended at power-on */
> >+      ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET,
> >0x00);
> >+      if (ret < 0)
> >+              dev_err(&client->dev, "failed to reset sensor");
> >+
> >+      ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
> >+      if (ret < 0)
> >+              dev_err(&client->dev, "failed to enable sensor");
> >+
> >+      return ret;
> >+}
> >+
> >+static int stk8312_probe(struct i2c_client *client,
> >+                       const struct i2c_device_id *id)
> >+{
> >+      int ret;
> >+      struct iio_dev *indio_dev;
> >+      struct stk8312_data *data;
> >+
> >+      indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> >+      if (!indio_dev) {
> >+              dev_err(&client->dev, "iio allocation failed!\n");
> >+              return -ENOMEM;
> >+      }
> >+
> >+      data = iio_priv(indio_dev);
> >+      data->client = client;
> >+      i2c_set_clientdata(client, indio_dev);
> >+      mutex_init(&data->lock);
> >+
> >+      indio_dev->dev.parent = &client->dev;
> >+      indio_dev->info = &stk8312_info;
> >+      indio_dev->name = STK8312_DRIVER_NAME;
> >+      indio_dev->modes = INDIO_DIRECT_MODE;
> >+      indio_dev->channels = stk8312_channels;
> >+      indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
> >+
> >+      ret = stk8312_init(indio_dev);
> >+      if (ret < 0) {
> >+              dev_err(&client->dev, "sensor initialization failed\n");
> >+              return ret;
> >+      }
> >+
> >+      ret = iio_device_register(indio_dev);
> >+      if (ret < 0) {
> >+              dev_err(&client->dev, "device_register failed\n");
> >+              stk8312_set_mode(data, STK8312_MODE_STANDBY);
> >+      }
> >+
> >+      return ret;
> >+}
> >+
> >+static int stk8312_remove(struct i2c_client *client)
> >+{
> >+      struct iio_dev *indio_dev = i2c_get_clientdata(client);
> >+
> >+      iio_device_unregister(indio_dev);
> >+
> >+      return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
> >+}
> >+
> >+#ifdef CONFIG_PM_SLEEP
> >+static int stk8312_suspend(struct device *dev)
> >+{
> >+      struct stk8312_data *data;
> >+
> >+      data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> >+
> >+      return stk8312_set_mode(data, STK8312_MODE_STANDBY);
> >+}
> >+
> >+static int stk8312_resume(struct device *dev)
> >+{
> >+      struct stk8312_data *data;
> >+
> >+      data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
> >+
> >+      return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
> >+}
> >+
> >+static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,
> >stk8312_resume);
> >+
> >+#define STK8312_PM_OPS (&stk8312_pm_ops)
> >+#else
> >+#define STK8312_PM_OPS NULL
> >+#endif
> >+
> >+static const struct i2c_device_id stk8312_i2c_id[] = {
> >+      {"STK8312", 0},
> >+      {}
> >+};
> >+
> >+static const struct acpi_device_id stk8312_acpi_id[] = {
> >+      {"STK8312", 0},
> >+      {}
> >+};
> >+
> >+MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
> >+
> >+static struct i2c_driver stk8312_driver = {
> >+      .driver = {
> >+              .name = "stk8312",
> >+              .pm = STK8312_PM_OPS,
> >+              .acpi_match_table = ACPI_PTR(stk8312_acpi_id),
> >+      },
> >+      .probe =            stk8312_probe,
> >+      .remove =           stk8312_remove,
> >+      .id_table =         stk8312_i2c_id,
> >+};
> >+
> >+module_i2c_driver(stk8312_driver);
> >+
> >+MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
> >+MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
> >+MODULE_LICENSE("GPL v2");
> 
> --
> Sent from my Android device with K-9 Mail. Please excuse my brevity.
> --
> 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
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)

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

* Re: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-06 13:12   ` Breana, Tiberiu A
  2015-05-06 14:42     ` Peter Meerwald
@ 2015-05-06 17:35     ` Jonathan Cameron
  2015-05-07 11:26       ` Breana, Tiberiu A
  1 sibling, 1 reply; 7+ messages in thread
From: Jonathan Cameron @ 2015-05-06 17:35 UTC (permalink / raw)
  To: Breana, Tiberiu A, linux-iio, pmeerw, Jonathan Cameron

On 06/05/15 14:12, Breana, Tiberiu A wrote:
> Thanks for the reviews.
> 
> The accelerometer has 3 measurement ranges:
> -1.5g - +1.5g (6 bit, signed)
> -6g - +6g (8 bit, signed)
> -16g - +16g (8 bit, signed)
> 
> scale1 = 1.5 * 9.81 / 31 ~= 0.4747
> scale2 = 6 * 9.81 / 127 ~= 0.4634
> scale3 = 16 * 9.81 / 127 ~= 1.2359
Um.. Given these are two's complement, you are going
to be asymmetric so question is, are you correct that
the maximum is +6G etc or is it that the minimum is -6G?
If the -6G option then this all looks a bit more sensible...
scale1 = 1.5*9.81/32 = 0.4598
scale2 = 6*9.81/128 = 0.4598
scale3 = whatever.

I guess either you need to chase someone to clarity this
or get some steady state measurements on the floor of the two
scales and perform a lot of averaging to see what how they actually
line up.

I'm particularly suspicious of your interpretation as the
sensitivities are the same for the two scales...

Now conjecturing further.  Why does the 1.5G version exist seeing
as you don't seem to gain anything from using it?  Same accuracy over
a reduced range,.

The answer is that it exposes the rev and alert bits allowing you to
know if you read at a 'dangerous time' and need to reread.

The rev bit appears to be undocumented (at least a search doesn't find
the docs for me).

So what we actually have is a device that really has two resolutions.
The 6 bit one allows you to read just 3 registers to know if you got
good data, the 8 bit reads exactly the same (+ 2 more adc pipeline stages
to get the range) but requires you to also check the tilt status register
(I think...) to find out if you got a good read.  

Next question is whether anyone will ever care about 1 more register read...
If not we could just not support the 6bit mode at all.

Jonathan

> 
> Thus the resulting, non-monotonic scales.
> I'll upload v2 with the rest of the suggested changes.
> 
> Regards,
> Tiberiu
> ________________________________________
> From: linux-iio-owner@vger.kernel.org [linux-iio-owner@vger.kernel.org] on behalf of Jonathan Cameron [jic23@jic23.retrosnub.co.uk]
> Sent: Tuesday, May 05, 2015 4:43 PM
> To: Breana, Tiberiu A; linux-iio@vger.kernel.org
> Cc: Jonathan Cameron
> Subject: Re: [PATCH] iio: accel: Add support for Sensortek STK8312
> 
> On 5 May 2015 14:20:38 GMT+01:00, Tiberiu Breana <tiberiu.a.breana@intel.com> wrote:
>> Minimal implementation of an IIO driver for the Sensortek
>> STK8312 3-axis accelerometer.
>> Datasheet:
>> http://www.syi-group.com/uploadpic/data/201361817562681623.pdf
>>
>> Includes:
>> - ACPI support;
>> - read_raw for x,y,z axes;
>> - reading and setting the scale (range) parameter.
>> - power management
>>
>> Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>
> 
> Looks good. Will take another look when not stuck with only my phone (and a case of broken circuit boards) whilst my laptop is in the hold of the plane...
>> ---
>> drivers/iio/accel/Kconfig   |  11 ++
>> drivers/iio/accel/Makefile  |   2 +
>> drivers/iio/accel/stk8312.c | 375
>> ++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 388 insertions(+)
>> create mode 100644 drivers/iio/accel/stk8312.c
>>
>> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
>> index 7c9a9a9..8bd8ccb 100644
>> --- a/drivers/iio/accel/Kconfig
>> +++ b/drivers/iio/accel/Kconfig
>> @@ -136,4 +136,15 @@ config MMA9553
>>
>>         To compile this driver as a module, choose M here: the module
>>         will be called mma9553.
>> +
>> +config STK8312
>> +      tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
>> +      depends on I2C
>> +      help
>> +        Say yes here to get support for the Sensortek STK8312 3-axis
>> +        accelerometer.
>> +
>> +        Choosing M will build the driver as a module. If so, the module
>> +        will be called stk8312.
>> +
>> endmenu
>> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
>> index 99d89e4..8b327c1 100644
>> --- a/drivers/iio/accel/Makefile
>> +++ b/drivers/iio/accel/Makefile
>> @@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)   += mma9551_core.o
>> obj-$(CONFIG_MMA9551)         += mma9551.o
>> obj-$(CONFIG_MMA9553)         += mma9553.o
>>
>> +obj-$(CONFIG_STK8312)         += stk8312.o
>> +
>> obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) += ssp_accel_sensor.o
>>
>> obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
>> diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c
>> new file mode 100644
>> index 0000000..ff27e6b
>> --- /dev/null
>> +++ b/drivers/iio/accel/stk8312.c
>> @@ -0,0 +1,375 @@
>> +/**
>> + * Sensortek STK8312 3-Axis Accelerometer
>> + *
>> + * Copyright (c) 2015, Intel Corporation.
>> + *
>> + * This file is subject to the terms and conditions of version 2 of
>> + * the GNU General Public License. See the file COPYING in the main
>> + * directory of this archive for more details.
>> + *
>> + * IIO driver for STK8312; 7-bit I2C address: 0x3D.
>> + */
>> +
>> +#include <linux/acpi.h>
>> +#include <linux/i2c.h>
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/delay.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/sysfs.h>
>> +
>> +#define STK8312_REG_XOUT              0x00
>> +#define STK8312_REG_YOUT              0x01
>> +#define STK8312_REG_ZOUT              0x02
>> +#define STK8312_REG_MODE              0x07
>> +#define STK8312_REG_STH                       0x13
>> +#define STK8312_REG_RESET             0x20
>> +
>> +#define STK8312_MODE_ACTIVE           1
>> +#define STK8312_MODE_STANDBY          0
>> +#define STK8312_MODE_MASK             0x01
>> +#define STK8312_RNG_MASK              0xC0
>> +#define STK8312_ALERT_MASK            0x40
>> +#define STK8312_DATA6_MASK            0x3F
>> +#define STK8312_RNG_SHIFT             6
>> +#define STK8312_READ_RETRIES          16
>> +
>> +#define STK8312_DRIVER_NAME           "stk8312"
>> +
>> +#define STK8312_SCALE_AVAIL           "0.4747 0.4632 1.2354"
>> +
>> +static const int stk8312_scale_table[][2] = {
>> +      {0, 474700}, {0, 463200}, {1, 235400}
>> +};
>> +
>> +#define STK8312_ACCEL_CHANNEL(axis) {                         \
>> +      .type = IIO_ACCEL,                                      \
>> +      .modified = 1,                                          \
>> +      .channel2 = IIO_MOD_##axis,                             \
>> +      .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),           \
>> +      .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),   \
>> +}
>> +
>> +static const struct iio_chan_spec stk8312_channels[] = {
>> +      STK8312_ACCEL_CHANNEL(X),
>> +      STK8312_ACCEL_CHANNEL(Y),
>> +      STK8312_ACCEL_CHANNEL(Z),
>> +};
>> +
>> +struct stk8312_data {
>> +      struct i2c_client *client;
>> +      struct mutex lock;
>> +      int range;
>> +      u8 mode;
>> +};
>> +
>> +static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);
>> +
>> +static struct attribute *stk8312_attributes[] = {
>> +      &iio_const_attr_in_accel_scale_available.dev_attr.attr,
>> +      NULL,
>> +};
>> +
>> +static const struct attribute_group stk8312_attribute_group = {
>> +      .attrs = stk8312_attributes
>> +};
>> +
>> +static int stk8312_set_mode(struct stk8312_data *data, u8 mode)
>> +{
>> +      int ret;
>> +      u8 masked_reg;
>> +      struct i2c_client *client = data->client;
>> +
>> +      if (mode > 1)
>> +              return -EINVAL;
>> +      else if (mode == data->mode)
>> +              return 0;
>> +
>> +      ret = i2c_smbus_read_byte_data(client, STK8312_REG_MODE);
>> +      if (ret < 0) {
>> +              dev_err(&client->dev, "register read failed\n");
>> +              return ret;
>> +      }
>> +      masked_reg = ret & (~STK8312_MODE_MASK);
>> +      masked_reg |= mode;
>> +
>> +      ret = i2c_smbus_write_byte_data(client, STK8312_REG_MODE,
>> +                                      masked_reg);
>> +      if (ret < 0)
>> +              dev_err(&client->dev, "failed to change sensor mode\n");
>> +      else
>> +              data->mode = mode;
>> +
>> +      return ret;
>> +}
>> +
>> +static int stk8312_set_range(struct stk8312_data *data, u8 range)
>> +{
>> +      int ret;
>> +      int masked_reg;
>> +      u8 mode;
>> +      struct i2c_client *client = data->client;
>> +
>> +      if (range < 0 || range > 2)
>> +              return -EINVAL;
>> +
>> +      if (range == data->range)
>> +              return 0;
>> +
>> +      mode = data->mode;
>> +      /* We need to go in standby mode to modify registers */
>> +      ret = stk8312_set_mode(data, STK8312_MODE_STANDBY);
>> +      if (ret < 0)
>> +              return ret;
>> +
>> +      ret = i2c_smbus_read_byte_data(client, STK8312_REG_STH);
>> +      if (ret < 0) {
>> +              dev_err(&client->dev, "register read failed\n");
>> +              return ret;
>> +      }
>> +
>> +      masked_reg = ret & (~STK8312_RNG_MASK);
>> +      masked_reg |= range << STK8312_RNG_SHIFT;
>> +
>> +      ret = i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked_reg);
>> +      if (ret < 0)
>> +              dev_err(&client->dev, "failed to change sensor range\n");
>> +      else
>> +              data->range = range;
>> +
>> +      return stk8312_set_mode(data, mode);
>> +}
>> +
>> +static int stk8312_read_accel(struct stk8312_data *data, int axis)
>> +{
>> +      int ret;
>> +      u8 reg;
>> +      int retries = STK8312_READ_RETRIES;
>> +      struct i2c_client *client = data->client;
>> +
>> +      switch (axis) {
>> +      case IIO_MOD_X:
>> +              reg = STK8312_REG_XOUT;
>> +              break;
>> +      case IIO_MOD_Y:
>> +              reg = STK8312_REG_YOUT;
>> +              break;
>> +      case IIO_MOD_Z:
>> +              reg = STK8312_REG_ZOUT;
>> +              break;
>> +      default:
>> +              return -EINVAL;
>> +      }
>> +
>> +      ret = i2c_smbus_read_byte_data(client, reg);
>> +      if (ret < 0) {
>> +              dev_err(&client->dev, "register read failed\n");
>> +              return ret;
>> +      }
>> +      if (data->range == 0) {
>> +              /* 6bit mode */
>> +              while (retries--) {
>> +                      if (ret & STK8312_ALERT_MASK) {
>> +                              /* The register is being updated. Try again. */
>> +                              msleep(20);
>> +                              ret = i2c_smbus_read_byte_data(client, reg);
>> +                              if (ret < 0) {
>> +                                      dev_err(&data->client->dev,
>> +                                              "register read failed\n");
>> +                                      return ret;
>> +                              }
>> +                      } else {
>> +                              ret &= STK8312_DATA6_MASK;
>> +                              return sign_extend32(ret, 5);
>> +                      }
>> +              }
>> +              dev_err(&data->client->dev,
>> +                              "register read failed, data not ready\n");
>> +              return -EIO;
>> +      }
>> +      /* 8bit mode */
>> +      return sign_extend32(ret, 7);
>> +}
>> +
>> +static int stk8312_read_raw(struct iio_dev *indio_dev,
>> +                          struct iio_chan_spec const *chan,
>> +                          int *val, int *val2, long mask)
>> +{
>> +      struct stk8312_data *data = iio_priv(indio_dev);
>> +
>> +      if (chan->type != IIO_ACCEL)
>> +              return -EINVAL;
>> +
>> +      switch (mask) {
>> +      case IIO_CHAN_INFO_RAW:
>> +              mutex_lock(&data->lock);
>> +              *val = stk8312_read_accel(data, chan->channel2);
>> +              mutex_unlock(&data->lock);
>> +              return IIO_VAL_INT;
>> +      case IIO_CHAN_INFO_SCALE:
>> +              *val = stk8312_scale_table[data->range][0];
>> +              *val2 = stk8312_scale_table[data->range][1];
>> +              return IIO_VAL_INT_PLUS_MICRO;
>> +      }
>> +
>> +      return -EINVAL;
>> +}
>> +
>> +static int stk8312_write_raw(struct iio_dev *indio_dev,
>> +                           struct iio_chan_spec const *chan,
>> +                           int val, int val2, long mask)
>> +{
>> +      int i;
>> +      int index = -1;
>> +      int ret;
>> +      struct stk8312_data *data = iio_priv(indio_dev);
>> +
>> +      switch (mask) {
>> +      case IIO_CHAN_INFO_SCALE:
>> +              for (i = 0; i < ARRAY_SIZE(stk8312_scale_table); i++)
>> +                      if (val == stk8312_scale_table[i][0] &&
>> +                          val2 == stk8312_scale_table[i][1]) {
>> +                              index = i;
>> +                              break;
>> +                      }
>> +              if (index < 0)
>> +                      return -EINVAL;
>> +
>> +              mutex_lock(&data->lock);
>> +              ret = stk8312_set_range(data, index);
>> +              mutex_unlock(&data->lock);
>> +
>> +              return ret;
>> +      }
>> +
>> +      return -EINVAL;
>> +}
>> +
>> +static const struct iio_info stk8312_info = {
>> +      .driver_module          = THIS_MODULE,
>> +      .read_raw               = stk8312_read_raw,
>> +      .write_raw              = stk8312_write_raw,
>> +      .attrs                  = &stk8312_attribute_group,
>> +};
>> +
>> +static int stk8312_init(struct iio_dev *indio_dev)
>> +{
>> +      int ret;
>> +      struct stk8312_data *data = iio_priv(indio_dev);
>> +      struct i2c_client *client = data->client;
>> +
>> +      /* A software reset is recommended at power-on */
>> +      ret = i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET,
>> 0x00);
>> +      if (ret < 0)
>> +              dev_err(&client->dev, "failed to reset sensor");
>> +
>> +      ret = stk8312_set_mode(data, STK8312_MODE_ACTIVE);
>> +      if (ret < 0)
>> +              dev_err(&client->dev, "failed to enable sensor");
>> +
>> +      return ret;
>> +}
>> +
>> +static int stk8312_probe(struct i2c_client *client,
>> +                       const struct i2c_device_id *id)
>> +{
>> +      int ret;
>> +      struct iio_dev *indio_dev;
>> +      struct stk8312_data *data;
>> +
>> +      indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
>> +      if (!indio_dev) {
>> +              dev_err(&client->dev, "iio allocation failed!\n");
>> +              return -ENOMEM;
>> +      }
>> +
>> +      data = iio_priv(indio_dev);
>> +      data->client = client;
>> +      i2c_set_clientdata(client, indio_dev);
>> +      mutex_init(&data->lock);
>> +
>> +      indio_dev->dev.parent = &client->dev;
>> +      indio_dev->info = &stk8312_info;
>> +      indio_dev->name = STK8312_DRIVER_NAME;
>> +      indio_dev->modes = INDIO_DIRECT_MODE;
>> +      indio_dev->channels = stk8312_channels;
>> +      indio_dev->num_channels = ARRAY_SIZE(stk8312_channels);
>> +
>> +      ret = stk8312_init(indio_dev);
>> +      if (ret < 0) {
>> +              dev_err(&client->dev, "sensor initialization failed\n");
>> +              return ret;
>> +      }
>> +
>> +      ret = iio_device_register(indio_dev);
>> +      if (ret < 0) {
>> +              dev_err(&client->dev, "device_register failed\n");
>> +              stk8312_set_mode(data, STK8312_MODE_STANDBY);
>> +      }
>> +
>> +      return ret;
>> +}
>> +
>> +static int stk8312_remove(struct i2c_client *client)
>> +{
>> +      struct iio_dev *indio_dev = i2c_get_clientdata(client);
>> +
>> +      iio_device_unregister(indio_dev);
>> +
>> +      return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY);
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stk8312_suspend(struct device *dev)
>> +{
>> +      struct stk8312_data *data;
>> +
>> +      data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
>> +
>> +      return stk8312_set_mode(data, STK8312_MODE_STANDBY);
>> +}
>> +
>> +static int stk8312_resume(struct device *dev)
>> +{
>> +      struct stk8312_data *data;
>> +
>> +      data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
>> +
>> +      return stk8312_set_mode(data, STK8312_MODE_ACTIVE);
>> +}
>> +
>> +static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,
>> stk8312_resume);
>> +
>> +#define STK8312_PM_OPS (&stk8312_pm_ops)
>> +#else
>> +#define STK8312_PM_OPS NULL
>> +#endif
>> +
>> +static const struct i2c_device_id stk8312_i2c_id[] = {
>> +      {"STK8312", 0},
>> +      {}
>> +};
>> +
>> +static const struct acpi_device_id stk8312_acpi_id[] = {
>> +      {"STK8312", 0},
>> +      {}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);
>> +
>> +static struct i2c_driver stk8312_driver = {
>> +      .driver = {
>> +              .name = "stk8312",
>> +              .pm = STK8312_PM_OPS,
>> +              .acpi_match_table = ACPI_PTR(stk8312_acpi_id),
>> +      },
>> +      .probe =            stk8312_probe,
>> +      .remove =           stk8312_remove,
>> +      .id_table =         stk8312_i2c_id,
>> +};
>> +
>> +module_i2c_driver(stk8312_driver);
>> +
>> +MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
>> +MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");
>> +MODULE_LICENSE("GPL v2");
> 
> --
> Sent from my Android device with K-9 Mail. Please excuse my brevity.
> --
> 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] 7+ messages in thread

* RE: [PATCH] iio: accel: Add support for Sensortek STK8312
  2015-05-06 17:35     ` Jonathan Cameron
@ 2015-05-07 11:26       ` Breana, Tiberiu A
  0 siblings, 0 replies; 7+ messages in thread
From: Breana, Tiberiu A @ 2015-05-07 11:26 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, pmeerw, Jonathan Cameron

I'm currently discussing this with a Sensortek engineer. Quote:=0A=
=0A=
"scale0 =3D (1.5+1.5)*9.81/(2^6-1)        =3D 0.4671 m/s^2=0A=
scale1 =3D (6+6)*9.81/(2^8-1)                 =3D 0.4616 m/s^2=0A=
scale2 =3D (16+16)*9.81/(2^8-1)                =3D 1.2311 m/s^2=0A=
=0A=
but you may interprete scale0 =3D scale1 to simplify this problem. "=0A=
=0A=
So I was pretty close.=0A=
=0A=
The 'rev' bit found in the data registers, according to the datasheet (see =
page 11)=0A=
is 'reserved', so it should be ignored. Regarding the 6bit mode, perhaps it=
 consumes=0A=
less power than the 8bit modes? Otherwise I can't see any other advantages.=
=0A=
I don't think the 'tilt status' register is related to raw accel readings.=
=0A=
=0A=
They've just sent me a new version of the datasheet which also describes an=
=0A=
initialization procedure. I'll send a new version of the driver once it's r=
eady.=0A=
=0A=
Regards,=0A=
Tiberiu=0A=
________________________________________=0A=
From: Jonathan Cameron [jic23@kernel.org]=0A=
Sent: Wednesday, May 06, 2015 8:35 PM=0A=
To: Breana, Tiberiu A; linux-iio@vger.kernel.org; pmeerw@pmeerw.net; Jonath=
an Cameron=0A=
Subject: Re: [PATCH] iio: accel: Add support for Sensortek STK8312=0A=
=0A=
On 06/05/15 14:12, Breana, Tiberiu A wrote:=0A=
> Thanks for the reviews.=0A=
>=0A=
> The accelerometer has 3 measurement ranges:=0A=
> -1.5g - +1.5g (6 bit, signed)=0A=
> -6g - +6g (8 bit, signed)=0A=
> -16g - +16g (8 bit, signed)=0A=
>=0A=
> scale1 =3D 1.5 * 9.81 / 31 ~=3D 0.4747=0A=
> scale2 =3D 6 * 9.81 / 127 ~=3D 0.4634=0A=
> scale3 =3D 16 * 9.81 / 127 ~=3D 1.2359=0A=
Um.. Given these are two's complement, you are going=0A=
to be asymmetric so question is, are you correct that=0A=
the maximum is +6G etc or is it that the minimum is -6G?=0A=
If the -6G option then this all looks a bit more sensible...=0A=
scale1 =3D 1.5*9.81/32 =3D 0.4598=0A=
scale2 =3D 6*9.81/128 =3D 0.4598=0A=
scale3 =3D whatever.=0A=
=0A=
I guess either you need to chase someone to clarity this=0A=
or get some steady state measurements on the floor of the two=0A=
scales and perform a lot of averaging to see what how they actually=0A=
line up.=0A=
=0A=
I'm particularly suspicious of your interpretation as the=0A=
sensitivities are the same for the two scales...=0A=
=0A=
Now conjecturing further.  Why does the 1.5G version exist seeing=0A=
as you don't seem to gain anything from using it?  Same accuracy over=0A=
a reduced range,.=0A=
=0A=
The answer is that it exposes the rev and alert bits allowing you to=0A=
know if you read at a 'dangerous time' and need to reread.=0A=
=0A=
The rev bit appears to be undocumented (at least a search doesn't find=0A=
the docs for me).=0A=
=0A=
So what we actually have is a device that really has two resolutions.=0A=
The 6 bit one allows you to read just 3 registers to know if you got=0A=
good data, the 8 bit reads exactly the same (+ 2 more adc pipeline stages=
=0A=
to get the range) but requires you to also check the tilt status register=
=0A=
(I think...) to find out if you got a good read.=0A=
=0A=
Next question is whether anyone will ever care about 1 more register read..=
.=0A=
If not we could just not support the 6bit mode at all.=0A=
=0A=
Jonathan=0A=
=0A=
>=0A=
> Thus the resulting, non-monotonic scales.=0A=
> I'll upload v2 with the rest of the suggested changes.=0A=
>=0A=
> Regards,=0A=
> Tiberiu=0A=
> ________________________________________=0A=
> From: linux-iio-owner@vger.kernel.org [linux-iio-owner@vger.kernel.org] o=
n behalf of Jonathan Cameron [jic23@jic23.retrosnub.co.uk]=0A=
> Sent: Tuesday, May 05, 2015 4:43 PM=0A=
> To: Breana, Tiberiu A; linux-iio@vger.kernel.org=0A=
> Cc: Jonathan Cameron=0A=
> Subject: Re: [PATCH] iio: accel: Add support for Sensortek STK8312=0A=
>=0A=
> On 5 May 2015 14:20:38 GMT+01:00, Tiberiu Breana <tiberiu.a.breana@intel.=
com> wrote:=0A=
>> Minimal implementation of an IIO driver for the Sensortek=0A=
>> STK8312 3-axis accelerometer.=0A=
>> Datasheet:=0A=
>> http://www.syi-group.com/uploadpic/data/201361817562681623.pdf=0A=
>>=0A=
>> Includes:=0A=
>> - ACPI support;=0A=
>> - read_raw for x,y,z axes;=0A=
>> - reading and setting the scale (range) parameter.=0A=
>> - power management=0A=
>>=0A=
>> Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com>=0A=
>=0A=
> Looks good. Will take another look when not stuck with only my phone (and=
 a case of broken circuit boards) whilst my laptop is in the hold of the pl=
ane...=0A=
>> ---=0A=
>> drivers/iio/accel/Kconfig   |  11 ++=0A=
>> drivers/iio/accel/Makefile  |   2 +=0A=
>> drivers/iio/accel/stk8312.c | 375=0A=
>> ++++++++++++++++++++++++++++++++++++++++++++=0A=
>> 3 files changed, 388 insertions(+)=0A=
>> create mode 100644 drivers/iio/accel/stk8312.c=0A=
>>=0A=
>> diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig=0A=
>> index 7c9a9a9..8bd8ccb 100644=0A=
>> --- a/drivers/iio/accel/Kconfig=0A=
>> +++ b/drivers/iio/accel/Kconfig=0A=
>> @@ -136,4 +136,15 @@ config MMA9553=0A=
>>=0A=
>>         To compile this driver as a module, choose M here: the module=0A=
>>         will be called mma9553.=0A=
>> +=0A=
>> +config STK8312=0A=
>> +      tristate "Sensortek STK8312 3-Axis Accelerometer Driver"=0A=
>> +      depends on I2C=0A=
>> +      help=0A=
>> +        Say yes here to get support for the Sensortek STK8312 3-axis=0A=
>> +        accelerometer.=0A=
>> +=0A=
>> +        Choosing M will build the driver as a module. If so, the module=
=0A=
>> +        will be called stk8312.=0A=
>> +=0A=
>> endmenu=0A=
>> diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile=0A=
>> index 99d89e4..8b327c1 100644=0A=
>> --- a/drivers/iio/accel/Makefile=0A=
>> +++ b/drivers/iio/accel/Makefile=0A=
>> @@ -14,6 +14,8 @@ obj-$(CONFIG_MMA9551_CORE)   +=3D mma9551_core.o=0A=
>> obj-$(CONFIG_MMA9551)         +=3D mma9551.o=0A=
>> obj-$(CONFIG_MMA9553)         +=3D mma9553.o=0A=
>>=0A=
>> +obj-$(CONFIG_STK8312)         +=3D stk8312.o=0A=
>> +=0A=
>> obj-$(CONFIG_IIO_SSP_SENSORS_COMMONS) +=3D ssp_accel_sensor.o=0A=
>>=0A=
>> obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) +=3D st_accel.o=0A=
>> diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c=
=0A=
>> new file mode 100644=0A=
>> index 0000000..ff27e6b=0A=
>> --- /dev/null=0A=
>> +++ b/drivers/iio/accel/stk8312.c=0A=
>> @@ -0,0 +1,375 @@=0A=
>> +/**=0A=
>> + * Sensortek STK8312 3-Axis Accelerometer=0A=
>> + *=0A=
>> + * Copyright (c) 2015, Intel Corporation.=0A=
>> + *=0A=
>> + * This file is subject to the terms and conditions of version 2 of=0A=
>> + * the GNU General Public License. See the file COPYING in the main=0A=
>> + * directory of this archive for more details.=0A=
>> + *=0A=
>> + * IIO driver for STK8312; 7-bit I2C address: 0x3D.=0A=
>> + */=0A=
>> +=0A=
>> +#include <linux/acpi.h>=0A=
>> +#include <linux/i2c.h>=0A=
>> +#include <linux/kernel.h>=0A=
>> +#include <linux/module.h>=0A=
>> +#include <linux/delay.h>=0A=
>> +#include <linux/iio/iio.h>=0A=
>> +#include <linux/iio/sysfs.h>=0A=
>> +=0A=
>> +#define STK8312_REG_XOUT              0x00=0A=
>> +#define STK8312_REG_YOUT              0x01=0A=
>> +#define STK8312_REG_ZOUT              0x02=0A=
>> +#define STK8312_REG_MODE              0x07=0A=
>> +#define STK8312_REG_STH                       0x13=0A=
>> +#define STK8312_REG_RESET             0x20=0A=
>> +=0A=
>> +#define STK8312_MODE_ACTIVE           1=0A=
>> +#define STK8312_MODE_STANDBY          0=0A=
>> +#define STK8312_MODE_MASK             0x01=0A=
>> +#define STK8312_RNG_MASK              0xC0=0A=
>> +#define STK8312_ALERT_MASK            0x40=0A=
>> +#define STK8312_DATA6_MASK            0x3F=0A=
>> +#define STK8312_RNG_SHIFT             6=0A=
>> +#define STK8312_READ_RETRIES          16=0A=
>> +=0A=
>> +#define STK8312_DRIVER_NAME           "stk8312"=0A=
>> +=0A=
>> +#define STK8312_SCALE_AVAIL           "0.4747 0.4632 1.2354"=0A=
>> +=0A=
>> +static const int stk8312_scale_table[][2] =3D {=0A=
>> +      {0, 474700}, {0, 463200}, {1, 235400}=0A=
>> +};=0A=
>> +=0A=
>> +#define STK8312_ACCEL_CHANNEL(axis) {                         \=0A=
>> +      .type =3D IIO_ACCEL,                                      \=0A=
>> +      .modified =3D 1,                                          \=0A=
>> +      .channel2 =3D IIO_MOD_##axis,                             \=0A=
>> +      .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW),           \=0A=
>> +      .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE),   \=0A=
>> +}=0A=
>> +=0A=
>> +static const struct iio_chan_spec stk8312_channels[] =3D {=0A=
>> +      STK8312_ACCEL_CHANNEL(X),=0A=
>> +      STK8312_ACCEL_CHANNEL(Y),=0A=
>> +      STK8312_ACCEL_CHANNEL(Z),=0A=
>> +};=0A=
>> +=0A=
>> +struct stk8312_data {=0A=
>> +      struct i2c_client *client;=0A=
>> +      struct mutex lock;=0A=
>> +      int range;=0A=
>> +      u8 mode;=0A=
>> +};=0A=
>> +=0A=
>> +static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL);=
=0A=
>> +=0A=
>> +static struct attribute *stk8312_attributes[] =3D {=0A=
>> +      &iio_const_attr_in_accel_scale_available.dev_attr.attr,=0A=
>> +      NULL,=0A=
>> +};=0A=
>> +=0A=
>> +static const struct attribute_group stk8312_attribute_group =3D {=0A=
>> +      .attrs =3D stk8312_attributes=0A=
>> +};=0A=
>> +=0A=
>> +static int stk8312_set_mode(struct stk8312_data *data, u8 mode)=0A=
>> +{=0A=
>> +      int ret;=0A=
>> +      u8 masked_reg;=0A=
>> +      struct i2c_client *client =3D data->client;=0A=
>> +=0A=
>> +      if (mode > 1)=0A=
>> +              return -EINVAL;=0A=
>> +      else if (mode =3D=3D data->mode)=0A=
>> +              return 0;=0A=
>> +=0A=
>> +      ret =3D i2c_smbus_read_byte_data(client, STK8312_REG_MODE);=0A=
>> +      if (ret < 0) {=0A=
>> +              dev_err(&client->dev, "register read failed\n");=0A=
>> +              return ret;=0A=
>> +      }=0A=
>> +      masked_reg =3D ret & (~STK8312_MODE_MASK);=0A=
>> +      masked_reg |=3D mode;=0A=
>> +=0A=
>> +      ret =3D i2c_smbus_write_byte_data(client, STK8312_REG_MODE,=0A=
>> +                                      masked_reg);=0A=
>> +      if (ret < 0)=0A=
>> +              dev_err(&client->dev, "failed to change sensor mode\n");=
=0A=
>> +      else=0A=
>> +              data->mode =3D mode;=0A=
>> +=0A=
>> +      return ret;=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_set_range(struct stk8312_data *data, u8 range)=0A=
>> +{=0A=
>> +      int ret;=0A=
>> +      int masked_reg;=0A=
>> +      u8 mode;=0A=
>> +      struct i2c_client *client =3D data->client;=0A=
>> +=0A=
>> +      if (range < 0 || range > 2)=0A=
>> +              return -EINVAL;=0A=
>> +=0A=
>> +      if (range =3D=3D data->range)=0A=
>> +              return 0;=0A=
>> +=0A=
>> +      mode =3D data->mode;=0A=
>> +      /* We need to go in standby mode to modify registers */=0A=
>> +      ret =3D stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>> +      if (ret < 0)=0A=
>> +              return ret;=0A=
>> +=0A=
>> +      ret =3D i2c_smbus_read_byte_data(client, STK8312_REG_STH);=0A=
>> +      if (ret < 0) {=0A=
>> +              dev_err(&client->dev, "register read failed\n");=0A=
>> +              return ret;=0A=
>> +      }=0A=
>> +=0A=
>> +      masked_reg =3D ret & (~STK8312_RNG_MASK);=0A=
>> +      masked_reg |=3D range << STK8312_RNG_SHIFT;=0A=
>> +=0A=
>> +      ret =3D i2c_smbus_write_byte_data(client, STK8312_REG_STH, masked=
_reg);=0A=
>> +      if (ret < 0)=0A=
>> +              dev_err(&client->dev, "failed to change sensor range\n");=
=0A=
>> +      else=0A=
>> +              data->range =3D range;=0A=
>> +=0A=
>> +      return stk8312_set_mode(data, mode);=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_read_accel(struct stk8312_data *data, int axis)=0A=
>> +{=0A=
>> +      int ret;=0A=
>> +      u8 reg;=0A=
>> +      int retries =3D STK8312_READ_RETRIES;=0A=
>> +      struct i2c_client *client =3D data->client;=0A=
>> +=0A=
>> +      switch (axis) {=0A=
>> +      case IIO_MOD_X:=0A=
>> +              reg =3D STK8312_REG_XOUT;=0A=
>> +              break;=0A=
>> +      case IIO_MOD_Y:=0A=
>> +              reg =3D STK8312_REG_YOUT;=0A=
>> +              break;=0A=
>> +      case IIO_MOD_Z:=0A=
>> +              reg =3D STK8312_REG_ZOUT;=0A=
>> +              break;=0A=
>> +      default:=0A=
>> +              return -EINVAL;=0A=
>> +      }=0A=
>> +=0A=
>> +      ret =3D i2c_smbus_read_byte_data(client, reg);=0A=
>> +      if (ret < 0) {=0A=
>> +              dev_err(&client->dev, "register read failed\n");=0A=
>> +              return ret;=0A=
>> +      }=0A=
>> +      if (data->range =3D=3D 0) {=0A=
>> +              /* 6bit mode */=0A=
>> +              while (retries--) {=0A=
>> +                      if (ret & STK8312_ALERT_MASK) {=0A=
>> +                              /* The register is being updated. Try aga=
in. */=0A=
>> +                              msleep(20);=0A=
>> +                              ret =3D i2c_smbus_read_byte_data(client, =
reg);=0A=
>> +                              if (ret < 0) {=0A=
>> +                                      dev_err(&data->client->dev,=0A=
>> +                                              "register read failed\n")=
;=0A=
>> +                                      return ret;=0A=
>> +                              }=0A=
>> +                      } else {=0A=
>> +                              ret &=3D STK8312_DATA6_MASK;=0A=
>> +                              return sign_extend32(ret, 5);=0A=
>> +                      }=0A=
>> +              }=0A=
>> +              dev_err(&data->client->dev,=0A=
>> +                              "register read failed, data not ready\n")=
;=0A=
>> +              return -EIO;=0A=
>> +      }=0A=
>> +      /* 8bit mode */=0A=
>> +      return sign_extend32(ret, 7);=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_read_raw(struct iio_dev *indio_dev,=0A=
>> +                          struct iio_chan_spec const *chan,=0A=
>> +                          int *val, int *val2, long mask)=0A=
>> +{=0A=
>> +      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +      if (chan->type !=3D IIO_ACCEL)=0A=
>> +              return -EINVAL;=0A=
>> +=0A=
>> +      switch (mask) {=0A=
>> +      case IIO_CHAN_INFO_RAW:=0A=
>> +              mutex_lock(&data->lock);=0A=
>> +              *val =3D stk8312_read_accel(data, chan->channel2);=0A=
>> +              mutex_unlock(&data->lock);=0A=
>> +              return IIO_VAL_INT;=0A=
>> +      case IIO_CHAN_INFO_SCALE:=0A=
>> +              *val =3D stk8312_scale_table[data->range][0];=0A=
>> +              *val2 =3D stk8312_scale_table[data->range][1];=0A=
>> +              return IIO_VAL_INT_PLUS_MICRO;=0A=
>> +      }=0A=
>> +=0A=
>> +      return -EINVAL;=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_write_raw(struct iio_dev *indio_dev,=0A=
>> +                           struct iio_chan_spec const *chan,=0A=
>> +                           int val, int val2, long mask)=0A=
>> +{=0A=
>> +      int i;=0A=
>> +      int index =3D -1;=0A=
>> +      int ret;=0A=
>> +      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>> +=0A=
>> +      switch (mask) {=0A=
>> +      case IIO_CHAN_INFO_SCALE:=0A=
>> +              for (i =3D 0; i < ARRAY_SIZE(stk8312_scale_table); i++)=
=0A=
>> +                      if (val =3D=3D stk8312_scale_table[i][0] &&=0A=
>> +                          val2 =3D=3D stk8312_scale_table[i][1]) {=0A=
>> +                              index =3D i;=0A=
>> +                              break;=0A=
>> +                      }=0A=
>> +              if (index < 0)=0A=
>> +                      return -EINVAL;=0A=
>> +=0A=
>> +              mutex_lock(&data->lock);=0A=
>> +              ret =3D stk8312_set_range(data, index);=0A=
>> +              mutex_unlock(&data->lock);=0A=
>> +=0A=
>> +              return ret;=0A=
>> +      }=0A=
>> +=0A=
>> +      return -EINVAL;=0A=
>> +}=0A=
>> +=0A=
>> +static const struct iio_info stk8312_info =3D {=0A=
>> +      .driver_module          =3D THIS_MODULE,=0A=
>> +      .read_raw               =3D stk8312_read_raw,=0A=
>> +      .write_raw              =3D stk8312_write_raw,=0A=
>> +      .attrs                  =3D &stk8312_attribute_group,=0A=
>> +};=0A=
>> +=0A=
>> +static int stk8312_init(struct iio_dev *indio_dev)=0A=
>> +{=0A=
>> +      int ret;=0A=
>> +      struct stk8312_data *data =3D iio_priv(indio_dev);=0A=
>> +      struct i2c_client *client =3D data->client;=0A=
>> +=0A=
>> +      /* A software reset is recommended at power-on */=0A=
>> +      ret =3D i2c_smbus_write_byte_data(data->client, STK8312_REG_RESET=
,=0A=
>> 0x00);=0A=
>> +      if (ret < 0)=0A=
>> +              dev_err(&client->dev, "failed to reset sensor");=0A=
>> +=0A=
>> +      ret =3D stk8312_set_mode(data, STK8312_MODE_ACTIVE);=0A=
>> +      if (ret < 0)=0A=
>> +              dev_err(&client->dev, "failed to enable sensor");=0A=
>> +=0A=
>> +      return ret;=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_probe(struct i2c_client *client,=0A=
>> +                       const struct i2c_device_id *id)=0A=
>> +{=0A=
>> +      int ret;=0A=
>> +      struct iio_dev *indio_dev;=0A=
>> +      struct stk8312_data *data;=0A=
>> +=0A=
>> +      indio_dev =3D devm_iio_device_alloc(&client->dev, sizeof(*data));=
=0A=
>> +      if (!indio_dev) {=0A=
>> +              dev_err(&client->dev, "iio allocation failed!\n");=0A=
>> +              return -ENOMEM;=0A=
>> +      }=0A=
>> +=0A=
>> +      data =3D iio_priv(indio_dev);=0A=
>> +      data->client =3D client;=0A=
>> +      i2c_set_clientdata(client, indio_dev);=0A=
>> +      mutex_init(&data->lock);=0A=
>> +=0A=
>> +      indio_dev->dev.parent =3D &client->dev;=0A=
>> +      indio_dev->info =3D &stk8312_info;=0A=
>> +      indio_dev->name =3D STK8312_DRIVER_NAME;=0A=
>> +      indio_dev->modes =3D INDIO_DIRECT_MODE;=0A=
>> +      indio_dev->channels =3D stk8312_channels;=0A=
>> +      indio_dev->num_channels =3D ARRAY_SIZE(stk8312_channels);=0A=
>> +=0A=
>> +      ret =3D stk8312_init(indio_dev);=0A=
>> +      if (ret < 0) {=0A=
>> +              dev_err(&client->dev, "sensor initialization failed\n");=
=0A=
>> +              return ret;=0A=
>> +      }=0A=
>> +=0A=
>> +      ret =3D iio_device_register(indio_dev);=0A=
>> +      if (ret < 0) {=0A=
>> +              dev_err(&client->dev, "device_register failed\n");=0A=
>> +              stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>> +      }=0A=
>> +=0A=
>> +      return ret;=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_remove(struct i2c_client *client)=0A=
>> +{=0A=
>> +      struct iio_dev *indio_dev =3D i2c_get_clientdata(client);=0A=
>> +=0A=
>> +      iio_device_unregister(indio_dev);=0A=
>> +=0A=
>> +      return stk8312_set_mode(iio_priv(indio_dev), STK8312_MODE_STANDBY=
);=0A=
>> +}=0A=
>> +=0A=
>> +#ifdef CONFIG_PM_SLEEP=0A=
>> +static int stk8312_suspend(struct device *dev)=0A=
>> +{=0A=
>> +      struct stk8312_data *data;=0A=
>> +=0A=
>> +      data =3D iio_priv(i2c_get_clientdata(to_i2c_client(dev)));=0A=
>> +=0A=
>> +      return stk8312_set_mode(data, STK8312_MODE_STANDBY);=0A=
>> +}=0A=
>> +=0A=
>> +static int stk8312_resume(struct device *dev)=0A=
>> +{=0A=
>> +      struct stk8312_data *data;=0A=
>> +=0A=
>> +      data =3D iio_priv(i2c_get_clientdata(to_i2c_client(dev)));=0A=
>> +=0A=
>> +      return stk8312_set_mode(data, STK8312_MODE_ACTIVE);=0A=
>> +}=0A=
>> +=0A=
>> +static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend,=0A=
>> stk8312_resume);=0A=
>> +=0A=
>> +#define STK8312_PM_OPS (&stk8312_pm_ops)=0A=
>> +#else=0A=
>> +#define STK8312_PM_OPS NULL=0A=
>> +#endif=0A=
>> +=0A=
>> +static const struct i2c_device_id stk8312_i2c_id[] =3D {=0A=
>> +      {"STK8312", 0},=0A=
>> +      {}=0A=
>> +};=0A=
>> +=0A=
>> +static const struct acpi_device_id stk8312_acpi_id[] =3D {=0A=
>> +      {"STK8312", 0},=0A=
>> +      {}=0A=
>> +};=0A=
>> +=0A=
>> +MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id);=0A=
>> +=0A=
>> +static struct i2c_driver stk8312_driver =3D {=0A=
>> +      .driver =3D {=0A=
>> +              .name =3D "stk8312",=0A=
>> +              .pm =3D STK8312_PM_OPS,=0A=
>> +              .acpi_match_table =3D ACPI_PTR(stk8312_acpi_id),=0A=
>> +      },=0A=
>> +      .probe =3D            stk8312_probe,=0A=
>> +      .remove =3D           stk8312_remove,=0A=
>> +      .id_table =3D         stk8312_i2c_id,=0A=
>> +};=0A=
>> +=0A=
>> +module_i2c_driver(stk8312_driver);=0A=
>> +=0A=
>> +MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");=0A=
>> +MODULE_DESCRIPTION("STK8312 3-Axis Accelerometer driver");=0A=
>> +MODULE_LICENSE("GPL v2");=0A=
>=0A=
> --=0A=
> Sent from my Android device with K-9 Mail. Please excuse my brevity.=0A=
> --=0A=
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in=
=0A=
> the body of a message to majordomo@vger.kernel.org=0A=
> More majordomo info at  http://vger.kernel.org/majordomo-info.html=0A=
>=0A=
=0A=

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

end of thread, other threads:[~2015-05-07 11:26 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-05-05 13:20 [PATCH] iio: accel: Add support for Sensortek STK8312 Tiberiu Breana
2015-05-05 13:43 ` Jonathan Cameron
2015-05-06 13:12   ` Breana, Tiberiu A
2015-05-06 14:42     ` Peter Meerwald
2015-05-06 17:35     ` Jonathan Cameron
2015-05-07 11:26       ` Breana, Tiberiu A
2015-05-05 13:45 ` Peter Meerwald

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.