RE: [PATCH] iio: accel: Add support for Sensortek STK8312

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> 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@xxxxxxxxxxxxxxx [linux-iio-owner@xxxxxxxxxxxxxxx] on behalf of Jonathan Cameron [jic23@xxxxxxxxxxxxxxxxxxxxx]
> Sent: Tuesday, May 05, 2015 4:43 PM
> To: Breana, Tiberiu A; linux-iio@xxxxxxxxxxxxxxx
> 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@xxxxxxxxx> 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@xxxxxxxxx>
> 
> 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@xxxxxxxxx>");
> >+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@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

-- 

Peter Meerwald
+43-664-2444418 (mobile)
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux