You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by vi...@apache.org on 2018/04/18 21:43:05 UTC

[mynewt-core] branch master updated: Sensors/BMA2XX - New PR from PR #699 comments (#1018)

This is an automated email from the ASF dual-hosted git repository.

vipulrahane pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mynewt-core.git


The following commit(s) were added to refs/heads/master by this push:
     new bac5f88  Sensors/BMA2XX - New PR from PR #699 comments (#1018)
bac5f88 is described below

commit bac5f886532d83b1b73de62f6be917daf02f2c5b
Author: joshgrob <jo...@grobtech.com>
AuthorDate: Wed Apr 18 16:43:02 2018 -0500

    Sensors/BMA2XX - New PR from PR #699 comments (#1018)
    
    * sensors/bma2xx: Syscfg setup
    * sensors/bma2xx: first driver files
    * sensors/bma2xx: remove pins from configs, model cfg option
    * sensors/bma2xx: PR #697 fixes. typos, config for I2C
    * sensors/bma2xx: fixed method line and added todo comments for INT pins
---
 apps/sensors_test/src/main.c                      |    6 +
 apps/sensors_test/syscfg.yml                      |    1 +
 hw/bsp/bmd300eval/src/hal_bsp.c                   |   14 +
 hw/bsp/bmd300eval/syscfg.yml                      |    7 +
 hw/drivers/sensors/bma2xx/README.md               |  189 +
 hw/drivers/sensors/bma2xx/include/bma2xx/bma2xx.h |  464 ++
 hw/drivers/sensors/bma2xx/pkg.yml                 |   38 +
 hw/drivers/sensors/bma2xx/src/bma2xx.c            | 4941 +++++++++++++++++++++
 hw/drivers/sensors/bma2xx/src/bma2xx_priv.h       |  670 +++
 hw/drivers/sensors/bma2xx/src/bma2xx_shell.c      |  717 +++
 hw/drivers/sensors/bma2xx/syscfg.yml              |   47 +
 hw/sensor/creator/pkg.yml                         |    2 +
 hw/sensor/creator/src/sensor_creator.c            |  109 +-
 hw/sensor/creator/syscfg.yml                      |    3 +
 hw/sensor/include/sensor/sensor.h                 |    2 +-
 15 files changed, 7201 insertions(+), 9 deletions(-)

diff --git a/apps/sensors_test/src/main.c b/apps/sensors_test/src/main.c
index 87c08eb..0f270bd 100644
--- a/apps/sensors_test/src/main.c
+++ b/apps/sensors_test/src/main.c
@@ -44,6 +44,9 @@
 #if MYNEWT_VAL(BMA253_CLI)
 #include <bma253/bma253.h>
 #endif
+#if MYNEWT_VAL(BMA2XX_CLI)
+#include <bma2xx/bma2xx.h>
+#endif
 #if MYNEWT_VAL(BME280_CLI)
 #include <bme280/bme280.h>
 #endif
@@ -421,6 +424,9 @@ sensors_dev_shell_init(void)
 #if MYNEWT_VAL(BMA253_CLI)
     bma253_shell_init();
 #endif
+#if MYNEWT_VAL(BMA2XX_CLI)
+    bma2xx_shell_init();
+#endif
 
 #if MYNEWT_VAL(BME280_CLI)
     bme280_shell_init();
diff --git a/apps/sensors_test/syscfg.yml b/apps/sensors_test/syscfg.yml
index 997235b..87a449b 100644
--- a/apps/sensors_test/syscfg.yml
+++ b/apps/sensors_test/syscfg.yml
@@ -46,6 +46,7 @@ syscfg.vals:
     BNO055_CLI: 0
     TCS34725_CLI: 0
     BME280_CLI: 0
+    BMA2XX_CLI: 0
 
     # Setup Sensor BLE OIC GATT Server
     SENSOR_OIC: 1
diff --git a/hw/bsp/bmd300eval/src/hal_bsp.c b/hw/bsp/bmd300eval/src/hal_bsp.c
index cb9550d..af16b2b 100644
--- a/hw/bsp/bmd300eval/src/hal_bsp.c
+++ b/hw/bsp/bmd300eval/src/hal_bsp.c
@@ -27,6 +27,7 @@
 #include "hal/hal_system.h"
 #include "hal/hal_flash.h"
 #include "hal/hal_spi.h"
+#include "hal/hal_i2c.h"
 #include "mcu/nrf52_hal.h"
 #if MYNEWT_VAL(UART_0) || MYNEWT_VAL(UART_1)
 #include "uart/uart.h"
@@ -88,6 +89,14 @@ static const struct nrf52_hal_spi_cfg os_bsp_spi0s_cfg = {
 };
 #endif
 
+#if MYNEWT_VAL(I2C_0)
+static const struct nrf52_hal_i2c_cfg hal_i2c_cfg = {
+    .scl_pin = MYNEWT_VAL(I2C_0_PIN_CLK),
+    .sda_pin = MYNEWT_VAL(I2C_0_PIN_SDA),
+    .i2c_frequency = 400    /* 400 kHz */
+};
+#endif
+
 #if MYNEWT_VAL(ADC_0)
 static struct adc_dev os_bsp_adc0;
 static struct nrf52_adc_dev_cfg os_bsp_adc0_config = {
@@ -273,6 +282,11 @@ hal_bsp_init(void)
     assert(rc == 0);
 #endif
 
+#if MYNEWT_VAL(I2C_0)
+    rc = hal_i2c_init(0, (void *)&hal_i2c_cfg);
+    assert(rc == 0);
+#endif
+
 #if MYNEWT_VAL(UART_0)
     rc = os_dev_create((struct os_dev *) &os_bsp_uart0, "uart0",
       OS_DEV_INIT_PRIMARY, 0, uart_hal_init, (void *)&os_bsp_uart0_cfg);
diff --git a/hw/bsp/bmd300eval/syscfg.yml b/hw/bsp/bmd300eval/syscfg.yml
index ba2435d..4d5cead 100644
--- a/hw/bsp/bmd300eval/syscfg.yml
+++ b/hw/bsp/bmd300eval/syscfg.yml
@@ -73,6 +73,13 @@ syscfg.defs:
         description: 'SS pin for SPI_0_SLAVE'
         value:  22
 
+    I2C_0_PIN_CLK:
+        description: 'Pin Number for I2C Clock'
+        value: 15
+    I2C_0_PIN_SDA:
+        description: 'Pin Number for I2C SDA'
+        value: 14
+
     TIMER_0:
         description: 'NRF52 Timer 0'
         value:  1
diff --git a/hw/drivers/sensors/bma2xx/README.md b/hw/drivers/sensors/bma2xx/README.md
new file mode 100644
index 0000000..4fdd76e
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/README.md
@@ -0,0 +1,189 @@
+<!--
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+-->
+
+<img src="http://mynewt.apache.org/img/logo.svg" width="250" alt="Apache Mynewt">
+
+## Table of Contents
+- [Overview](#overview)
+- [Usage](#usage)
+- [Wrap-up](#wrap)
+
+## Overview<a name=overview></a>
+
+The goal of the mynewt BMA2XX sensor driver is to allow a common interface 
+across the family of [Bosch BMA200 series accelerometers](https://www.bosch-sensortec.com/bst/products/motion/accelerometers/overview_accelerometers).
+Fortunately, the line of sensors have very similar functionality, and share 
+the same registers mappings and interrupt engine.  The main differences are 
+that each sensor has different bit-depth (14, 12, 10bit) and chip ids. 
+
+The BMA2XX is based off the functionality of the mynewt BMA253 driver, but 
+now includes SPI and I2C support and will support the following models:
+
+* BMA280 - Supported
+* BMA255 - (Coming soon)
+* BMA253 - Supported
+* BMA250E - (Coming soon)
+* BMA22E - (Coming soon)
+* BMA220 - (Coming soon)
+
+
+Bosch-Sensortech has also published a [generic driver](https://github.com/BoschSensortec/BMA2x2_driver)
+for reference 
+
+## Usage<a name=usage></a>
+
+The BMA2XX driver is designed as an (offboard sensor) and is instantiated from
+ the sensor creator.  It also has shell support and defines a "bma2xx" command 
+ option.  To test it out let's walk through a simple sensor_test application.
+ 
+NOTE:  Stay tuned for 
+ 
+### Create Target
+First let's create a new target and base it off the sensor_test app.  We will
+ also enable the shell, the driver and indicate if we are SPI or I2C.
+~~~~
+> newt target create bma2xx_sensor_test
+Target targets/bma2xx_sensor_test successfully created 
+~~~~
+~~~~
+> newt target set bma2xx_sensor_test app=@apache-mynewt-core/apps/sensors_test bsp=@apache-mynewt-core/hw/bsp/bmd300eval build_profile=debug
+Target targets/bma2xx_sensor_test successfully set target.app to @apache-mynewt-core/apps/sensors_test
+Target targets/bma2xx_sensor_test successfully set target.bsp to @apache-mynewt-core/hw/bsp/bmd300eval
+Target targets/bma2xx_sensor_test successfully set target.build_profile to debug
+~~~~
+~~~~
+> newt target set bma2xx_sensor_test syscfg=BMA2XX_OFB=1:SPI_0_MASTER=1:BMA2XX_CLI=1:SHELL_TASK=1
+Target targets/bma2xx_sensor_test successfully set target.syscfg to BMA2XX_OFB=1:SPI_0_MASTER=1:BMA2XX_CLI=1:SHELL_TASK=1
+~~~~
+
+### Set Model in Config
+As mentioned earlier the BMA2XX driver is supported in the sensor creator, 
+and comes with a set of default settings.  The key attribute that needs to be
+ set is the specific model (eg. BMA280, BMA253, etc) so that chip_ids and 
+ resolution of accelerometer values are correct.  This is found in 
+ [sensor_creator.c](https://github.com/apache/mynewt-core/blob/master/hw/sensor/creator/src/sensor_creator.c)  
+in the config_bma2xx_sensor() function, and we will indicate that the BMA280 
+is present.  
+~~~~
+int
+config_bma2xx_sensor(void)
+{
+    struct os_dev * dev;
+    struct bma2xx_cfg cfg;
+    int rc;
+
+    dev = os_dev_open("bma2xx_0", OS_TIMEOUT_NEVER, NULL);
+    assert(dev != NULL);
+
+    // set the model attribute to one of the following enums
+    // enum bma2xx_model {
+    //   BMA2XX_BMA280 = 0,
+    //   BMA2XX_BMA253 = 1,
+    // };
+    //
+    cfg.model = BMA2XX_BMA280;
+    
+    
+    .
+    .
+    .
+}
+~~~~
+
+### Set Pins
+Don't forgot to update your SPI/I2C pins the appropriate BSP configs and 
+sensor_creator.c file.  We will not cover how to do that here. 
+
+### Load App
+Now it's time to build, and load the app to the target bsp.  We will assume 
+the bootloader is already built and installed.  See [this tutorial](https://mynewt.apache.org/latest/os/tutorials/sensors/sensor_thingy_lis2dh12_onb/) 
+for more instructions.
+Using the "newt run" command (build, load, gdb) we get the following:
+
+~~~~
+> newt run bma2xx_sensor_test 0.0.0.1
+    .
+    .
+    .
+at repos/apache-mynewt-core/hw/mcu/nordic/nrf52xxx/src/hal_os_tick.c:165
+165	    if (ticks > 0) {
+Resetting target
+0x000000dc in ?? ()
+(gdb) c
+Continuing.
+~~~~
+
+
+### Read Data
+Now that the app is loaded and running in a gdb session let's interact with the 
+shell to read some data. Below is output from uart session with the BSP. 
+Please note the command available under the "bma2xx" command option.
+~~~~
+help
+
+009897 help
+009897 stat                          
+009898 config                        
+009899 log                           
+009900 flash                         
+009901 i2c_scan                      
+009902 tasks                         
+009903 mpool                         
+009904 date                          
+009905 sensor                        
+009906 bma2xx                        
+009907                               
+009908 compat> bma2xx
+
+010864 self-test <default|strict>
+010864 offset-compensation <x={0g|-1g|+1g}> <y={0g|-1g|+1g}> <z={0g|-1g|+1g}>
+010866 query-offsets 
+010867 write-offsets 
+010868 stream-read <num-reads>
+010868 current-temp 
+010869 current-orient 
+010870 wait-for-orient 
+010870 wait-for-high-g 
+010871 wait-for-low-g 
+010872 wait-for-tap <double|single>
+010872 power-settings <normal|deep-suspend|suspend|standby|lpm1|lpm2>
+                      <0.5ms|1ms|2ms|4ms|6ms|10ms|25ms|50ms|100ms|500ms|1s>
+010876 compat> bma2xx stream-read 10
+
+012040 x = 0.009516000 y = 0.017324000 z = 1.004791975
+012041 x = 0.011224000 y = 0.021715998 z = 0.998936000
+012042 x = 0.012932000 y = 0.021472000 z = 0.997716032
+012044 x = 0.019032000 y = 0.019763998 z = 1.001863956
+012045 x = 0.016592000 y = 0.017811998 z = 0.996495936
+012046 x = 0.017080000 y = 0.016348001 z = 1.001132011
+012048 x = 0.016104000 y = 0.011224000 z = 0.993323968
+012049 x = 0.017324000 y = 0.015372000 z = 1.006255984
+012051 x = 0.015616000 y = 0.015616000 z = 0.994544000
+012052 x = 0.014884000 y = 0.018056000 z = 0.999668032
+012065 compat> 
+~~~~
+
+## Wrap-Up
+As shown in the example above, in one package your app can support the family
+ of Bosch BMA200 sensors with minimal configuration.  Also, using the "newt 
+ size" command the package runs ~13k in debug mode  In the coming weeks the
+  intent is complete support of the rest of the sensors, and provide more in 
+  depth tutorials that exercise the sensor API. 
diff --git a/hw/drivers/sensors/bma2xx/include/bma2xx/bma2xx.h b/hw/drivers/sensors/bma2xx/include/bma2xx/bma2xx.h
new file mode 100644
index 0000000..cac3959
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/include/bma2xx/bma2xx.h
@@ -0,0 +1,464 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * resarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef __BMA2XX_H__
+#define __BMA2XX_H__
+
+#include "os/os.h"
+#include "os/os_dev.h"
+#include "sensor/sensor.h"
+#include "sensor/accel.h"
+#include "sensor/temperature.h"
+
+#ifdef __cplusplus
+#extern "C" {
+#endif
+
+/* XXX use some better defaults. For now it is min */
+#define BMA2XX_LOW_G_DELAY_MS_DEFAULT       2
+#define BMA2XX_HIGH_G_DELAY_MS_DEFAULT      2
+
+/* Supported device models.  */
+enum bma2xx_model {
+    BMA2XX_BMA280 = 0,
+    BMA2XX_BMA253 = 1,
+};
+
+/* Range of acceleration measurements */
+enum bma2xx_g_range {
+    BMA2XX_G_RANGE_2  = 0,
+    BMA2XX_G_RANGE_4  = 1,
+    BMA2XX_G_RANGE_8  = 2,
+    BMA2XX_G_RANGE_16 = 3,
+};
+
+/* How often acceleration measurements are taken */
+enum bma2xx_filter_bandwidth {
+    BMA2XX_FILTER_BANDWIDTH_7_81_HZ  = 0,
+    BMA2XX_FILTER_BANDWIDTH_15_63_HZ = 1,
+    BMA2XX_FILTER_BANDWIDTH_31_25_HZ = 2,
+    BMA2XX_FILTER_BANDWIDTH_62_5_HZ  = 3,
+    BMA2XX_FILTER_BANDWIDTH_125_HZ   = 4,
+    BMA2XX_FILTER_BANDWIDTH_250_HZ   = 5,
+    BMA2XX_FILTER_BANDWIDTH_500_HZ   = 6,
+    BMA2XX_FILTER_BANDWIDTH_1000_HZ  = 7,
+    BMA2XX_FILTER_BANDWIDTH_ODR_MAX  = 8,
+};
+
+/* Quiet time after a double/single tap */
+enum bma2xx_tap_quiet {
+    BMA2XX_TAP_QUIET_20_MS = 0,
+    BMA2XX_TAP_QUIET_30_MS = 1,
+};
+
+/* Settling time after a double/single tap */
+enum bma2xx_tap_shock {
+    BMA2XX_TAP_SHOCK_50_MS = 0,
+    BMA2XX_TAP_SHOCK_75_MS = 1,
+};
+
+/* How long to wait for the next tap in a double tap scenario */
+enum bma2xx_d_tap_window {
+    BMA2XX_D_TAP_WINDOW_50_MS  = 0,
+    BMA2XX_D_TAP_WINDOW_100_MS = 1,
+    BMA2XX_D_TAP_WINDOW_150_MS = 2,
+    BMA2XX_D_TAP_WINDOW_200_MS = 3,
+    BMA2XX_D_TAP_WINDOW_250_MS = 4,
+    BMA2XX_D_TAP_WINDOW_375_MS = 5,
+    BMA2XX_D_TAP_WINDOW_500_MS = 6,
+    BMA2XX_D_TAP_WINDOW_700_MS = 7,
+};
+
+/* How many samples to use after a wake up from a low power mode to determine
+ * whether a tap occurred */
+enum bma2xx_tap_wake_samples {
+    BMA2XX_TAP_WAKE_SAMPLES_2  = 0,
+    BMA2XX_TAP_WAKE_SAMPLES_4  = 1,
+    BMA2XX_TAP_WAKE_SAMPLES_8  = 2,
+    BMA2XX_TAP_WAKE_SAMPLES_16 = 3,
+};
+
+/* Block generation of orientation events based on given criteria */
+enum bma2xx_orient_blocking {
+    BMA2XX_ORIENT_BLOCKING_NONE                       = 0,
+    BMA2XX_ORIENT_BLOCKING_ACCEL_ONLY                 = 1,
+    BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE            = 2,
+    BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE_AND_STABLE = 3,
+};
+
+/* Orientation mode configuration, used to determine thresholds for
+ * transitions between different orientations */
+enum bma2xx_orient_mode {
+    BMA2XX_ORIENT_MODE_SYMMETRICAL       = 0,
+    BMA2XX_ORIENT_MODE_HIGH_ASYMMETRICAL = 1,
+    BMA2XX_ORIENT_MODE_LOW_ASYMMETRICAL  = 2,
+};
+
+/* Power mode for the device */
+enum bma2xx_power_mode {
+    BMA2XX_POWER_MODE_NORMAL       = 0,
+    BMA2XX_POWER_MODE_DEEP_SUSPEND = 1,
+    BMA2XX_POWER_MODE_SUSPEND      = 2,
+    BMA2XX_POWER_MODE_STANDBY      = 3,
+    BMA2XX_POWER_MODE_LPM_1        = 4,
+    BMA2XX_POWER_MODE_LPM_2        = 5,
+};
+
+/* Duration of sleep whenever the device is in a power mode that alternates
+ * between wake and sleep (LPM 1 & 2) */
+enum bma2xx_sleep_duration {
+    BMA2XX_SLEEP_DURATION_0_5_MS = 0,
+    BMA2XX_SLEEP_DURATION_1_MS   = 1,
+    BMA2XX_SLEEP_DURATION_2_MS   = 2,
+    BMA2XX_SLEEP_DURATION_4_MS   = 3,
+    BMA2XX_SLEEP_DURATION_6_MS   = 4,
+    BMA2XX_SLEEP_DURATION_10_MS  = 5,
+    BMA2XX_SLEEP_DURATION_25_MS  = 6,
+    BMA2XX_SLEEP_DURATION_50_MS  = 7,
+    BMA2XX_SLEEP_DURATION_100_MS = 8,
+    BMA2XX_SLEEP_DURATION_500_MS = 9,
+    BMA2XX_SLEEP_DURATION_1_S    = 10,
+};
+
+/* Default configuration values to use with the device */
+struct bma2xx_cfg {
+    /* device model within BMA2XX family */
+    enum bma2xx_model model;
+    /* Accelerometer configuration */
+    enum bma2xx_g_range g_range;
+    enum bma2xx_filter_bandwidth filter_bandwidth;
+    /* Whether to use data that has not been filtered at all */
+    bool use_unfiltered_data;
+    /* Low-g event configuration */
+    uint16_t low_g_delay_ms;
+    float low_g_thresh_g;
+    float low_g_hyster_g;
+    /* High-g event configuration */
+    float high_g_hyster_g;
+    uint16_t high_g_delay_ms;
+    float high_g_thresh_g;
+    /* Tap (double & single) event configuration */
+    enum bma2xx_tap_quiet tap_quiet;
+    enum bma2xx_tap_shock tap_shock;
+    enum bma2xx_d_tap_window d_tap_window;
+    enum bma2xx_tap_wake_samples tap_wake_samples;
+    float tap_thresh_g;
+    /* Orientation event configuration */
+    float orient_hyster_g;
+    enum bma2xx_orient_blocking orient_blocking;
+    enum bma2xx_orient_mode orient_mode;
+    bool orient_signal_ud;
+    /* Offsets for acceleration measurements, by axis */
+    float offset_x_g;
+    float offset_y_g;
+    float offset_z_g;
+    /* Power management configuration */
+    enum bma2xx_power_mode power_mode;
+    enum bma2xx_sleep_duration sleep_duration;
+    /* Applicable sensor types supported */
+    sensor_type_t sensor_mask;
+};
+
+/* Used to track interrupt state to wake any present waiters */
+struct bma2xx_int {
+    /* Synchronize access to this structure */
+    os_sr_t lock;
+    /* Sleep waiting for an interrupt to occur */
+    struct os_sem wait;
+    /* Is the interrupt currently active */
+    bool active;
+    /* Is there a waiter currently sleeping */
+    bool asleep;
+    /* Configured interrupts */
+    struct sensor_int *ints;
+};
+
+/* Device private data */
+struct bma2xx_private_driver_data {
+    struct bma2xx_int * interrupt;
+    struct sensor_notify_ev_ctx notify_ctx;
+    struct sensor_read_ev_ctx read_ctx;
+    uint8_t registered_mask;
+
+    uint8_t int_num;
+    uint8_t int_route;
+    uint8_t int_ref_cnt;
+};
+
+/* The device itself */
+struct bma2xx {
+    /* Underlying OS device */
+    struct os_dev dev;
+    /* The sensor infrastructure */
+    struct sensor sensor;
+    /* Default configuration values */
+    struct bma2xx_cfg cfg;
+    /* Active interrupt state */
+    struct bma2xx_int intr;
+    /* Active power mode, could be different from default configured
+     * power mode if a function that requires a higher power mode is
+     * currently running. */
+    enum bma2xx_power_mode power;
+
+    /* Private driver data */
+    struct bma2xx_private_driver_data pdd;
+};
+
+/* Offset compensation is performed to target this given value, by axis */
+enum bma2xx_offset_comp_target {
+    BMA2XX_OFFSET_COMP_TARGET_0_G     = 0,
+    BMA2XX_OFFSET_COMP_TARGET_NEG_1_G = 1,
+    BMA2XX_OFFSET_COMP_TARGET_POS_1_G = 2,
+};
+
+/* The device's X/Y orientation, in form of rotation */
+enum bma2xx_orient_xy {
+    BMA2XX_ORIENT_XY_PORTRAIT_UPRIGHT     = 0,
+    BMA2XX_ORIENT_XY_PORTRAIT_UPSIDE_DOWN = 1,
+    BMA2XX_ORIENT_XY_LANDSCAPE_LEFT       = 2,
+    BMA2XX_ORIENT_XY_LANDSCAPE_RIGHT      = 3,
+};
+
+/* The device's full orientation */
+struct bma2xx_orient_xyz {
+    /* The X/Y orientation */
+    enum bma2xx_orient_xy orient_xy;
+    /* Is device facing upward or downward */
+    bool downward_z;
+};
+
+/* Type of tap event to look for */
+enum bma2xx_tap_type {
+    BMA2XX_TAP_TYPE_DOUBLE = 0,
+    BMA2XX_TAP_TYPE_SINGLE = 1,
+};
+
+/**
+ * Perform a self test of the device and report on its health.
+ *
+ * @param The device object.
+ * @param Multiplier on the high amplitude self test minimums; 1.0 for default.
+ * @param Multiplier on the low amplitude self test minimums; 0.0 for default.
+ * @param The result of the self-test: false if passed, true if failed.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_self_test(struct bma2xx * bma2xx,
+                 float delta_high_mult,
+                 float delta_low_mult,
+                 bool * self_test_fail);
+
+/**
+ * Perform an offset compensation and use the resulting offsets.
+ *
+ * @param The device object.
+ * @param The correct target value for the X axis.
+ * @param The correct target value for the Y axis.
+ * @param The correct target value for the Z axis.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_offset_compensation(struct bma2xx * bma2xx,
+                           enum bma2xx_offset_comp_target target_x,
+                           enum bma2xx_offset_comp_target target_y,
+                           enum bma2xx_offset_comp_target target_z);
+
+/**
+ * Return the current compensation offsets in use.
+ *
+ * @param The device object.
+ * @param The offset for the X axis, filled in by this function.
+ * @param The offset for the Y axis, filled in by this function.
+ * @param The offset for the Z axis, filled in by this function.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_query_offsets(struct bma2xx * bma2xx,
+                     float * offset_x_g,
+                     float * offset_y_g,
+                     float * offset_z_g);
+
+/**
+ * Store and use the provided compensation offsets.
+ *
+ * @param The device object.
+ * @param The offset for the X axis.
+ * @param The offset for the Y axis.
+ * @param the offset for the Z axis.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_write_offsets(struct bma2xx * bma2xx,
+                     float offset_x_g,
+                     float offset_y_g,
+                     float offset_z_g);
+
+/**
+ * Callback for handling accelerometer sensor data.
+ *
+ * @param The opaque pointer passed in to the stream read function.
+ * @param The accelerometer data provided by the sensor.
+ *
+ * @return true to stop streaming data, false to continue.
+ */
+typedef bool
+(*bma2xx_stream_read_func_t)(void *,
+                             struct sensor_accel_data *);
+
+/**
+ * Provide a continuous stream of accelerometer readings.
+ *
+ * @param The device object.
+ * @param The function pointer to invoke for each accelerometer reading.
+ * @param The opaque pointer that will be passed in to the function.
+ * @param If non-zero, how long the stream should run in milliseconds.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_stream_read(struct bma2xx * bma2xx,
+                   bma2xx_stream_read_func_t read_func,
+                   void * read_arg,
+                   uint32_t time_ms);
+
+/**
+ * Get the current temperature at the device.
+ *
+ * @param The device object.
+ * @param The current temperature in celsius, filled in by this function.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_current_temp(struct bma2xx * bma2xx,
+                    float * temp_c);
+
+/**
+ * Get the current device orientation.
+ *
+ * @param The device object.
+ * @param The current orientation, filled in by this function.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_current_orient(struct bma2xx * bma2xx,
+                      struct bma2xx_orient_xyz * orient_xyz);
+
+/**
+ * Block until the device orientation changes.
+ *
+ * @param The device object.
+ * @param The new orientation, filled in by this function.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_wait_for_orient(struct bma2xx * bma2xx,
+                       struct bma2xx_orient_xyz * orient_xyz);
+
+/**
+ * Block until a high-g event occurs.
+ *
+ * @param The device object.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_wait_for_high_g(struct bma2xx * bma2xx);
+
+/**
+ * Block until a low-g event occurs.
+ *
+ * @param The device object.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_wait_for_low_g(struct bma2xx * bma2xx);
+
+/**
+ * Block until a single or double tap event occurs.
+ *
+ * @param The device object.
+ * @param The type of tap event to look for.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_wait_for_tap(struct bma2xx * bma2xx,
+                    enum bma2xx_tap_type tap_type);
+
+/**
+ * Change the default power settings.
+ *
+ * @param The device object.
+ * @param The new power mode, which the device resides in unless a different
+ *        and more aggressive power mode is required to satisfy a running API
+ *        function.
+ * @param The new sleep duration, used whenever device is in LPM_1/2 mode
+ *        either due to default power mode or due to being in a temporarily
+ *        overridden power mode.
+ */
+int
+bma2xx_power_settings(struct bma2xx * bma2xx,
+                      enum bma2xx_power_mode power_mode,
+                      enum bma2xx_sleep_duration sleep_duration);
+
+/**
+ * Configure the sensor.
+ *
+ * @param ptr to sensor driver
+ * @param ptr to sensor driver config
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_config(struct bma2xx * bma2xx, struct bma2xx_cfg * cfg);
+
+/**
+ * Expects to be called back through os_dev_create().
+ *
+ * @param ptr to the device object associated with this accelerometer
+ * @param argument passed to OS device init
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_init(struct os_dev * dev, void * arg);
+
+#if MYNEWT_VAL(BMA2XX_CLI)
+/**
+ * Initialize the BMA2XX shell extensions.
+ *
+ * @return 0 on success, non-zero on failure.
+ */
+int
+bma2xx_shell_init(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/drivers/sensors/bma2xx/pkg.yml b/hw/drivers/sensors/bma2xx/pkg.yml
new file mode 100644
index 0000000..aaf26ad
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/pkg.yml
@@ -0,0 +1,38 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+pkg.name: hw/drivers/sensors/bma2xx
+pkg.description: Driver for the Bosch BMA2xx line of accelerometers.  Supports BMA253, BMA280
+pkg.keywords:
+    - bma2xx
+    - i2c
+    - spi
+    - sensor
+
+pkg.deps:
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/hw/sensor"
+
+pkg.req_apis:
+    - stats
+    - log
+
+pkg.deps.BMA2XX_CLI:
+    - "@apache-mynewt-core/util/parse"
diff --git a/hw/drivers/sensors/bma2xx/src/bma2xx.c b/hw/drivers/sensors/bma2xx/src/bma2xx.c
new file mode 100644
index 0000000..f8a1f1e
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/src/bma2xx.c
@@ -0,0 +1,4941 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * resarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <assert.h>
+#include <math.h>
+#include <string.h>
+#include <errno.h>
+
+
+#include "bma2xx/bma2xx.h"
+#include "bma2xx_priv.h"
+#include "defs/error.h"
+#include "hal/hal_gpio.h"
+#include "hal/hal_i2c.h"
+#include "hal/hal_spi.h"
+
+#if MYNEWT_VAL(BMA2XX_LOG)
+#include "log/log.h"
+#endif
+
+#if MYNEWT_VAL(BMA2XX_LOG)
+static struct log bma2xx_log;
+#define LOG_MODULE_BMA2XX (200)
+#define BMA2XX_ERROR(...) LOG_ERROR(&bma2xx_log, LOG_MODULE_BMA2XX, __VA_ARGS__)
+#define BMA2XX_INFO(...)  LOG_INFO(&bma2xx_log, LOG_MODULE_BMA2XX, __VA_ARGS__)
+#else
+#define BMA2XX_ERROR(...)
+#define BMA2XX_INFO(...)
+#endif
+
+#define BMA2XX_NOTIFY_MASK  0x01
+#define BMA2XX_READ_MASK    0x02
+
+#if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER)
+static struct hal_spi_settings spi_bma2xx_settings = {
+        .data_order = HAL_SPI_MSB_FIRST,
+        .data_mode  = HAL_SPI_MODE0,
+        .baudrate   = 4000,
+        .word_size  = HAL_SPI_WORD_SIZE_8BIT,
+};
+#endif
+
+static void
+delay_msec(uint32_t delay)
+{
+    delay = (delay * OS_TICKS_PER_SEC) / 1000 + 1;
+    os_time_delay(delay);
+}
+
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+static void
+init_interrupt(struct bma2xx_int * interrupt, struct sensor_int *ints)
+{
+    os_error_t error;
+
+    error = os_sem_init(&interrupt->wait, 0);
+    assert(error == OS_OK);
+
+    interrupt->active = false;
+    interrupt->asleep = false;
+    interrupt->ints = ints;
+}
+
+static void
+undo_interrupt(struct bma2xx_int * interrupt)
+{
+    OS_ENTER_CRITICAL(interrupt->lock);
+    interrupt->active = false;
+    interrupt->asleep = false;
+    OS_EXIT_CRITICAL(interrupt->lock);
+}
+
+static void
+wait_interrupt(struct bma2xx_int * interrupt, enum bma2xx_int_num int_num)
+{
+    bool wait;
+
+    OS_ENTER_CRITICAL(interrupt->lock);
+
+    /* Check if we did not missed interrupt */
+    if (hal_gpio_read(interrupt->ints[int_num].host_pin) ==
+                                            interrupt->ints[int_num].active) {
+        OS_EXIT_CRITICAL(interrupt->lock);
+        return;
+    }
+
+    if (interrupt->active) {
+        interrupt->active = false;
+        wait = false;
+    } else {
+        interrupt->asleep = true;
+        wait = true;
+    }
+    OS_EXIT_CRITICAL(interrupt->lock);
+
+    if (wait) {
+        os_error_t error;
+
+        error = os_sem_pend(&interrupt->wait, -1);
+        assert(error == OS_OK);
+    }
+}
+
+static void
+wake_interrupt(struct bma2xx_int * interrupt)
+{
+    bool wake;
+
+    OS_ENTER_CRITICAL(interrupt->lock);
+    if (interrupt->asleep) {
+        interrupt->asleep = false;
+        wake = true;
+    } else {
+        interrupt->active = true;
+        wake = false;
+    }
+    OS_EXIT_CRITICAL(interrupt->lock);
+
+    if (wake) {
+        os_error_t error;
+
+        error = os_sem_release(&interrupt->wait);
+        assert(error == OS_OK);
+    }
+}
+
+static void
+interrupt_handler(void * arg)
+{
+    struct sensor *sensor = arg;
+    struct bma2xx *bma2xx;
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+
+    if (bma2xx->pdd.interrupt) {
+        wake_interrupt(bma2xx->pdd.interrupt);
+    }
+
+    sensor_mgr_put_interrupt_evt(sensor);
+}
+#endif
+
+/**
+ * Read multiple length data over SPI
+ *
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to read
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+spi_readlen(const struct sensor_itf * itf, uint8_t addr, uint8_t *payload,
+            uint8_t len)
+{
+    int i;
+    uint16_t retval;
+    int rc;
+
+    rc = 0;
+
+    /* Select the device */
+    hal_gpio_write(itf->si_cs_pin, 0);
+
+    /* Send the address */
+    retval = hal_spi_tx_val(itf->si_num, addr | BMA2XX_SPI_READ_CMD_BIT);
+    if (retval == 0xFFFF) {
+        rc = SYS_EINVAL;
+        BMA2XX_ERROR("SPI_%u register write failed addr:0x%02X\n",
+                     itf->si_num, addr);
+        goto err;
+    }
+
+    for (i = 0; i < len; i++) {
+        /* Read data */
+        retval = hal_spi_tx_val(itf->si_num, 0);
+        if (retval == 0xFFFF) {
+            rc = SYS_EINVAL;
+            BMA2XX_ERROR("SPI_%u read failed addr:0x%02X\n",
+                         itf->si_num, addr);
+            goto err;
+        }
+        payload[i] = retval;
+    }
+
+    rc = 0;
+
+    err:
+    /* De-select the device */
+    hal_gpio_write(itf->si_cs_pin, 1);
+
+    return rc;
+}
+
+/**
+ * Write multiple length data SPI
+ *
+ * @param register address
+ * @param variable length payload
+ * @param length of the payload to write
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+spi_writereg(const struct sensor_itf * itf, uint8_t addr, uint8_t payload,
+             uint8_t len)
+{
+    int i;
+    int rc;
+
+    /* Select the device */
+    hal_gpio_write(itf->si_cs_pin, 0);
+
+    /* Send the address */
+    rc = hal_spi_tx_val(itf->si_num, addr);
+    if (rc == 0xFFFF) {
+        rc = SYS_EINVAL;
+        BMA2XX_ERROR("SPI_%u register write failed addr:0x%02X\n",
+                     itf->si_num, addr);
+        goto err;
+    }
+
+    for (i = 0; i < len; i++) {
+        /* Read data */
+        rc = hal_spi_tx_val(itf->si_num, payload);
+        if (rc == 0xFFFF) {
+            rc = SYS_EINVAL;
+            BMA2XX_ERROR("SPI_%u write failed addr:0x%02X:0x%02X\n",
+                         itf->si_num, addr);
+            goto err;
+        }
+    }
+
+
+    rc = 0;
+
+    err:
+    /* De-select the device */
+    hal_gpio_write(itf->si_cs_pin, 1);
+
+    os_time_delay((OS_TICKS_PER_SEC * 30)/1000 + 1);
+
+    return rc;
+}
+
+int
+i2c_readlen(const struct sensor_itf * itf, uint8_t addr, uint8_t *payload,
+            uint8_t len)
+{
+    struct hal_i2c_master_data oper;
+    int rc;
+
+    oper.address = itf->si_addr;
+    oper.len     = 1;
+    oper.buffer  = &addr;
+
+    rc = hal_i2c_master_write(itf->si_num, &oper,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc != 0) {
+        BMA2XX_ERROR("I2C access failed at address 0x%02X\n",
+                     addr);
+        return rc;
+    }
+
+    oper.address = itf->si_addr;
+    oper.len     = len;
+    oper.buffer  = payload;
+
+    rc = hal_i2c_master_read(itf->si_num, &oper,
+                             OS_TICKS_PER_SEC / 10, 1);
+    if (rc != 0) {
+        BMA2XX_ERROR("I2C read failed at address 0x%02X length %u\n",
+                     addr, len);
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+i2c_writereg(const struct sensor_itf * itf, uint8_t addr, uint8_t data)
+{
+    uint8_t tuple[2];
+    struct hal_i2c_master_data oper;
+    int rc;
+
+    tuple[0] = addr;
+    tuple[1] = data;
+
+    oper.address = itf->si_addr;
+    oper.len     = 2;
+    oper.buffer  = tuple;
+
+    rc = hal_i2c_master_write(itf->si_num, &oper,
+                              OS_TICKS_PER_SEC / 10, 1);
+    if (rc != 0) {
+        BMA2XX_ERROR("I2C write failed at address 0x%02X single byte\n",
+                     addr);
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+get_register(const struct bma2xx * bma2xx,
+             uint8_t addr,
+             uint8_t * data)
+{
+    int rc;
+    const struct sensor_itf * itf;
+    itf = SENSOR_GET_ITF(&bma2xx->sensor);
+
+    if (itf->si_type == SENSOR_ITF_SPI) {
+        rc = spi_readlen(itf, addr, data, 1);
+    } else if (itf->si_type == SENSOR_ITF_I2C){
+        rc = i2c_readlen(itf, addr, data, 1);
+    } else {
+        rc = SYS_EINVAL;
+    }
+
+    return rc;
+}
+
+static int
+get_registers(const struct bma2xx * bma2xx,
+              uint8_t addr,
+              uint8_t * data,
+              uint8_t size)
+{
+    int rc;
+    const struct sensor_itf * itf;
+    itf = SENSOR_GET_ITF(&bma2xx->sensor);
+
+    if (itf->si_type == SENSOR_ITF_SPI) {
+        rc = spi_readlen(itf, addr, data, size);
+    } else if (itf->si_type == SENSOR_ITF_I2C){
+        rc = i2c_readlen(itf, addr, data, size);
+    } else {
+        rc = SYS_EINVAL;
+    }
+
+    return rc;
+}
+
+static int
+set_register(const struct bma2xx * bma2xx,
+             uint8_t addr,
+             uint8_t data)
+{
+    int rc;
+    const struct sensor_itf * itf;
+
+    itf = SENSOR_GET_ITF(&bma2xx->sensor);
+
+    if (itf->si_type == SENSOR_ITF_SPI) {
+        rc = spi_writereg(itf, addr, data, 1);
+    } else if (itf->si_type == SENSOR_ITF_I2C){
+        rc = i2c_writereg(itf, addr, data);
+    } else {
+        rc = SYS_EINVAL;
+    }
+
+    switch (bma2xx->power) {
+    case BMA2XX_POWER_MODE_SUSPEND:
+    case BMA2XX_POWER_MODE_LPM_1:
+        delay_msec(1);
+        break;
+    default:
+        break;
+    }
+
+    return rc;
+}
+
+int
+bma2xx_get_chip_id(const struct bma2xx * bma2xx,
+                   uint8_t * chip_id)
+{
+    return get_register(bma2xx, REG_ADDR_BGW_CHIPID, chip_id);
+}
+
+static void
+compute_accel_data(struct accel_data * accel_data,
+                   enum bma2xx_model model,
+                   const uint8_t * raw_data,
+                   float accel_scale)
+{
+    int16_t raw_accel;
+    uint8_t model_shift = 0;
+
+    switch (model) {
+        case BMA2XX_BMA280:
+            model_shift = BMA280_ACCEL_BIT_SHIFT;
+            break;
+        case BMA2XX_BMA253:
+            model_shift = BMA253_ACCEL_BIT_SHIFT;
+            break;
+        default:
+            break;
+    }
+
+    raw_accel = (int16_t)(raw_data[0] & 0xFC) | (int16_t)(raw_data[1] << 8);
+    raw_accel >>= model_shift;
+
+    accel_data->accel_g = (float)raw_accel * accel_scale;
+    accel_data->new_data = raw_data[0] & 0x01;
+}
+
+static int
+get_accel_scale(enum bma2xx_model model,
+                enum bma2xx_g_range g_range,
+                float *accel_scale)
+{
+    switch (g_range) {
+        case BMA2XX_G_RANGE_2:
+            switch(model){
+                case BMA2XX_BMA280:
+                    *accel_scale = BMA280_G_SCALE_2;
+                    break;
+                case BMA2XX_BMA253:
+                    *accel_scale = BMA253_G_SCALE_2;
+                    break;
+                default:
+                    return SYS_EINVAL;
+            }
+            break;
+        case BMA2XX_G_RANGE_4:
+            switch(model){
+                case BMA2XX_BMA280:
+                    *accel_scale = BMA280_G_SCALE_4;
+                    break;
+                case BMA2XX_BMA253:
+                    *accel_scale = BMA253_G_SCALE_4;
+                    break;
+                default:
+                    return SYS_EINVAL;
+            }
+            break;
+        case BMA2XX_G_RANGE_8:
+            switch(model){
+                case BMA2XX_BMA280:
+                    *accel_scale = BMA280_G_SCALE_8;
+                    break;
+                case BMA2XX_BMA253:
+                    *accel_scale = BMA253_G_SCALE_8;
+                    break;
+                default:
+                    return SYS_EINVAL;
+            }
+            break;
+        case BMA2XX_G_RANGE_16:
+            switch(model){
+                case BMA2XX_BMA280:
+                    *accel_scale = BMA280_G_SCALE_16;
+                    break;
+                case BMA2XX_BMA253:
+                    *accel_scale = BMA253_G_SCALE_16;
+                    break;
+                default:
+                    return SYS_EINVAL;
+            }
+            break;
+        default:
+            return SYS_EINVAL;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_accel(const struct bma2xx * bma2xx,
+                 enum bma2xx_g_range g_range,
+                 enum axis axis,
+                 struct accel_data * accel_data)
+{
+    float accel_scale;
+    uint8_t base_addr;
+    uint8_t data[2];
+    int rc;
+
+    rc = get_accel_scale(bma2xx->cfg.model, g_range, &accel_scale);
+    if (rc != 0){
+        return rc;
+    }
+
+    switch (axis) {
+    case AXIS_X:
+        base_addr = REG_ADDR_ACCD_X_LSB;
+        break;
+    case AXIS_Y:
+        base_addr = REG_ADDR_ACCD_Y_LSB;
+        break;
+    case AXIS_Z:
+        base_addr = REG_ADDR_ACCD_Z_LSB;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_registers(bma2xx, base_addr,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    compute_accel_data(accel_data, bma2xx->cfg.model, data, accel_scale);
+
+    return 0;
+}
+
+int
+bma2xx_get_temp(const struct bma2xx * bma2xx,
+                float * temp_c)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_ACCD_TEMP, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *temp_c = (float)(int8_t)data * 0.5 + 23.0;
+
+    return 0;
+}
+
+static void
+quad_to_axis_trigger(struct axis_trigger * axis_trigger,
+                     uint8_t quad_bits,
+                     const char * name_bits)
+{
+    axis_trigger->sign = (quad_bits >> 3) & 0x01;
+    switch (quad_bits & 0x07) {
+    default:
+        BMA2XX_ERROR("unknown %s quad bits 0x%02X\n",
+                     name_bits, quad_bits);
+    case 0x00:
+        axis_trigger->axis = -1;
+        axis_trigger->axis_known = false;
+        break;
+    case 0x01:
+        axis_trigger->axis = AXIS_X;
+        axis_trigger->axis_known = true;
+        break;
+    case 0x02:
+        axis_trigger->axis = AXIS_Y;
+        axis_trigger->axis_known = true;
+        break;
+    case 0x03:
+        axis_trigger->axis = AXIS_Z;
+        axis_trigger->axis_known = true;
+        break;
+    }
+}
+
+int
+bma2xx_get_int_status(const struct bma2xx * bma2xx,
+                      struct int_status * int_status)
+{
+    uint8_t data[4];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_STATUS_0,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_status->flat_int_active = data[0] & 0x80;
+    int_status->orient_int_active = data[0] & 0x40;
+    int_status->s_tap_int_active = data[0] & 0x20;
+    int_status->d_tap_int_active = data[0] & 0x10;
+    int_status->slow_no_mot_int_active = data[0] & 0x08;
+    int_status->slope_int_active = data[0] & 0x04;
+    int_status->high_g_int_active = data[0] & 0x02;
+    int_status->low_g_int_active = data[0] & 0x01;
+    int_status->data_int_active = data[1] & 0x80;
+    int_status->fifo_wmark_int_active = data[1] & 0x40;
+    int_status->fifo_full_int_active = data[1] & 0x20;
+    quad_to_axis_trigger(&int_status->tap_trigger,
+                 (data[2] >> 4) & 0x0F, "tap");
+    quad_to_axis_trigger(&int_status->slope_trigger,
+                 (data[2] >> 0) & 0x0F, "slope");
+    int_status->device_is_flat = data[3] & 0x80;
+    int_status->device_is_down = data[3] & 0x40;
+    int_status->device_orientation = (data[3] >> 4) & 0x03;
+    quad_to_axis_trigger(&int_status->high_g_trigger,
+                 (data[3] >> 0) & 0x0F, "high_g");
+
+    return 0;
+}
+
+int
+bma2xx_get_fifo_status(const struct bma2xx * bma2xx,
+                       bool * overrun,
+                       uint8_t * frame_counter)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_FIFO_STATUS, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *overrun = data & 0x80;
+    *frame_counter = data & 0x7F;
+
+    return 0;
+}
+
+int
+bma2xx_get_g_range(const struct bma2xx * bma2xx,
+                   enum bma2xx_g_range * g_range)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_PMU_RANGE, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch (data & 0x0F) {
+    default:
+        BMA2XX_ERROR("unknown PMU_RANGE reg value 0x%02X\n", data);
+        *g_range = BMA2XX_G_RANGE_16;
+        break;
+    case 0x03:
+        *g_range = BMA2XX_G_RANGE_2;
+        break;
+    case 0x05:
+        *g_range = BMA2XX_G_RANGE_4;
+        break;
+    case 0x08:
+        *g_range = BMA2XX_G_RANGE_8;
+        break;
+    case 0x0C:
+        *g_range = BMA2XX_G_RANGE_16;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_g_range(const struct bma2xx * bma2xx,
+                   enum bma2xx_g_range g_range)
+{
+    uint8_t data;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        data = 0x03;
+        break;
+    case BMA2XX_G_RANGE_4:
+        data = 0x05;
+        break;
+    case BMA2XX_G_RANGE_8:
+        data = 0x08;
+        break;
+    case BMA2XX_G_RANGE_16:
+        data = 0x0C;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, REG_ADDR_PMU_RANGE, data);
+}
+
+int
+bma2xx_get_filter_bandwidth(const struct bma2xx * bma2xx,
+                            enum bma2xx_filter_bandwidth * filter_bandwidth)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_PMU_BW, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch (data & 0x1F) {
+    case 0x00 ... 0x08:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_7_81_HZ;
+        break;
+    case 0x09:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_15_63_HZ;
+        break;
+    case 0x0A:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_31_25_HZ;
+        break;
+    case 0x0B:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_62_5_HZ;
+        break;
+    case 0x0C:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_125_HZ;
+        break;
+    case 0x0D:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_250_HZ;
+        break;
+    case 0x0E:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_500_HZ;
+        break;
+    case 0x0F ... 0x1F:
+        *filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_1000_HZ;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_filter_bandwidth(const struct bma2xx * bma2xx,
+                            enum bma2xx_filter_bandwidth filter_bandwidth)
+{
+    uint8_t data;
+
+    switch (filter_bandwidth) {
+    case BMA2XX_FILTER_BANDWIDTH_7_81_HZ:
+        data = 0x08;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_15_63_HZ:
+        data = 0x09;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_31_25_HZ:
+        data = 0x0A;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_62_5_HZ:
+        data = 0x0B;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_125_HZ:
+        data = 0x0C;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_250_HZ:
+        data = 0x0D;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_500_HZ:
+        data = 0x0E;
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_1000_HZ:
+        switch( bma2xx->cfg.model) {
+            case BMA2XX_BMA253:
+                data = 0x0F;
+                break;
+            case BMA2XX_BMA280:
+                return SYS_EINVAL;
+            default:
+                return SYS_EINVAL;
+        }
+        break;
+    case BMA2XX_FILTER_BANDWIDTH_ODR_MAX:
+        switch( bma2xx->cfg.model) {
+            case BMA2XX_BMA253:
+                return SYS_EINVAL;
+            case BMA2XX_BMA280:
+                data = 0x0F;
+                break;
+            default:
+                return SYS_EINVAL;
+        }
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, REG_ADDR_PMU_BW, data);
+}
+
+int
+bma2xx_get_power_settings(const struct bma2xx * bma2xx,
+                          struct power_settings * power_settings)
+{
+    uint8_t data[2];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_PMU_LPW,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch ((data[0] >> 5) & 0x07) {
+    default:
+        BMA2XX_ERROR("unknown PMU_LPW reg value 0x%02X\n", data[0]);
+        power_settings->power_mode = BMA2XX_POWER_MODE_NORMAL;
+        break;
+    case 0x00:
+        power_settings->power_mode = BMA2XX_POWER_MODE_NORMAL;
+        break;
+    case 0x01:
+        power_settings->power_mode = BMA2XX_POWER_MODE_DEEP_SUSPEND;
+        break;
+    case 0x02:
+        if ((data[1] & 0x40) == 0) {
+            power_settings->power_mode = BMA2XX_POWER_MODE_LPM_1;
+        } else {
+            power_settings->power_mode = BMA2XX_POWER_MODE_LPM_2;
+        }
+        break;
+    case 0x04:
+        if ((data[1] & 0x40) == 0) {
+            power_settings->power_mode = BMA2XX_POWER_MODE_SUSPEND;
+        } else {
+            power_settings->power_mode = BMA2XX_POWER_MODE_STANDBY;
+        }
+        break;
+    }
+
+    switch ((data[0] >> 1) & 0x0F) {
+    case 0x00 ... 0x05:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_0_5_MS;
+        break;
+    case 0x06:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_1_MS;
+        break;
+    case 0x07:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_2_MS;
+        break;
+    case 0x08:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_4_MS;
+        break;
+    case 0x09:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_6_MS;
+        break;
+    case 0x0A:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_10_MS;
+        break;
+    case 0x0B:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_25_MS;
+        break;
+    case 0x0C:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_50_MS;
+        break;
+    case 0x0D:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_100_MS;
+        break;
+    case 0x0E:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_500_MS;
+        break;
+    case 0x0F:
+        power_settings->sleep_duration = BMA2XX_SLEEP_DURATION_1_S;
+        break;
+    }
+
+    if ((data[1] & 0x20) != 0) {
+        power_settings->sleep_timer = SLEEP_TIMER_EQUIDISTANT_SAMPLING;
+    } else {
+        power_settings->sleep_timer = SLEEP_TIMER_EVENT_DRIVEN;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_power_settings(const struct bma2xx * bma2xx,
+                          const struct power_settings * power_settings)
+{
+    uint8_t data[2];
+    int rc;
+
+    data[0] = 0;
+    data[1] = 0;
+
+    switch (power_settings->power_mode) {
+    case BMA2XX_POWER_MODE_NORMAL:
+        data[0] |= 0x00 << 5;
+        break;
+    case BMA2XX_POWER_MODE_DEEP_SUSPEND:
+        data[0] |= 0x01 << 5;
+        break;
+    case BMA2XX_POWER_MODE_SUSPEND:
+        data[0] |= 0x04 << 5;
+        data[1] |= 0x00 << 6;
+        break;
+    case BMA2XX_POWER_MODE_STANDBY:
+        data[0] |= 0x04 << 5;
+        data[1] |= 0x01 << 6;
+        break;
+    case BMA2XX_POWER_MODE_LPM_1:
+        data[0] |= 0x02 << 5;
+        data[1] |= 0x00 << 6;
+        break;
+    case BMA2XX_POWER_MODE_LPM_2:
+        data[0] |= 0x02 << 5;
+        data[1] |= 0x01 << 6;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (power_settings->sleep_duration) {
+    case BMA2XX_SLEEP_DURATION_0_5_MS:
+        data[0] |= 0x05 << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_1_MS:
+        data[0] |= 0x06 << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_2_MS:
+        data[0] |= 0x07 << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_4_MS:
+        data[0] |= 0x08 << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_6_MS:
+        data[0] |= 0x09 << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_10_MS:
+        data[0] |= 0x0A << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_25_MS:
+        data[0] |= 0x0B << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_50_MS:
+        data[0] |= 0x0C << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_100_MS:
+        data[0] |= 0x0D << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_500_MS:
+        data[0] |= 0x0E << 1;
+        break;
+    case BMA2XX_SLEEP_DURATION_1_S:
+        data[0] |= 0x0F << 1;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (power_settings->sleep_timer) {
+    case SLEEP_TIMER_EVENT_DRIVEN:
+        data[1] |= 0x00 << 5;
+        break;
+    case SLEEP_TIMER_EQUIDISTANT_SAMPLING:
+        data[1] |= 0x01 << 5;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = set_register(bma2xx, REG_ADDR_PMU_LOW_POWER, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_PMU_LPW, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_data_acquisition(const struct bma2xx * bma2xx,
+                            bool * unfiltered_reg_data,
+                            bool * disable_reg_shadow)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_ACCD_HBW, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *unfiltered_reg_data = data & 0x80;
+    *disable_reg_shadow = data & 0x40;
+
+    return 0;
+}
+
+int
+bma2xx_set_data_acquisition(const struct bma2xx * bma2xx,
+                            bool unfiltered_reg_data,
+                            bool disable_reg_shadow)
+{
+    uint8_t data;
+
+    data = (unfiltered_reg_data << 7) |
+           (disable_reg_shadow << 6);
+
+    return set_register(bma2xx, REG_ADDR_ACCD_HBW, data);
+}
+
+int
+bma2xx_set_softreset(const struct bma2xx * bma2xx)
+{
+    int rc;
+
+    rc = set_register(bma2xx, REG_ADDR_BGW_SOFTRESET, REG_VALUE_SOFT_RESET);
+    if (rc != 0) {
+        return rc;
+    }
+
+    delay_msec(2);
+
+    return 0;
+}
+
+int
+bma2xx_get_int_enable(const struct bma2xx * bma2xx,
+                      struct int_enable * int_enable)
+{
+    uint8_t data[3];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_EN_0,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_enable->flat_int_enable = data[0] & 0x80;
+    int_enable->orient_int_enable = data[0] & 0x40;
+    int_enable->s_tap_int_enable = data[0] & 0x20;
+    int_enable->d_tap_int_enable = data[0] & 0x10;
+    int_enable->slope_z_int_enable = data[0] & 0x04;
+    int_enable->slope_y_int_enable = data[0] & 0x02;
+    int_enable->slope_x_int_enable = data[0] & 0x01;
+    int_enable->fifo_wmark_int_enable = data[1] & 0x40;
+    int_enable->fifo_full_int_enable = data[1] & 0x20;
+    int_enable->data_int_enable = data[1] & 0x10;
+    int_enable->low_g_int_enable = data[1] & 0x08;
+    int_enable->high_g_z_int_enable = data[1] & 0x04;
+    int_enable->high_g_y_int_enable = data[1] & 0x02;
+    int_enable->high_g_x_int_enable = data[1] & 0x01;
+    int_enable->no_motion_select = data[2] & 0x08;
+    int_enable->slow_no_mot_z_int_enable = data[2] & 0x04;
+    int_enable->slow_no_mot_y_int_enable = data[2] & 0x02;
+    int_enable->slow_no_mot_x_int_enable = data[2] & 0x01;
+
+    return 0;
+}
+
+int
+bma2xx_set_int_enable(const struct bma2xx * bma2xx,
+                      const struct int_enable * int_enable)
+{
+    uint8_t data[3];
+    int rc;
+
+    data[0] = (int_enable->flat_int_enable << 7) |
+              (int_enable->orient_int_enable << 6) |
+              (int_enable->s_tap_int_enable << 5) |
+              (int_enable->d_tap_int_enable << 4) |
+              (int_enable->slope_z_int_enable << 2) |
+              (int_enable->slope_y_int_enable << 1) |
+              (int_enable->slope_x_int_enable << 0);
+
+    data[1] = (int_enable->fifo_wmark_int_enable << 6) |
+              (int_enable->fifo_full_int_enable << 5) |
+              (int_enable->data_int_enable << 4) |
+              (int_enable->low_g_int_enable << 3) |
+              (int_enable->high_g_z_int_enable << 2) |
+              (int_enable->high_g_y_int_enable << 1) |
+              (int_enable->high_g_x_int_enable << 0);
+
+    data[2] = (int_enable->no_motion_select << 3) |
+              (int_enable->slow_no_mot_z_int_enable << 2) |
+              (int_enable->slow_no_mot_y_int_enable << 1) |
+              (int_enable->slow_no_mot_x_int_enable << 0);
+
+    rc = set_register(bma2xx, REG_ADDR_INT_EN_0, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_EN_1, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_EN_2, data[2]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_int_routes(const struct bma2xx * bma2xx,
+                      struct int_routes * int_routes)
+{
+    uint8_t data[3];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_MAP_0,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_routes->flat_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x80) != 0) {
+        int_routes->flat_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x80) != 0) {
+        int_routes->flat_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->orient_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x40) != 0) {
+        int_routes->orient_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x40) != 0) {
+        int_routes->orient_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->s_tap_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x20) != 0) {
+        int_routes->s_tap_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x20) != 0) {
+        int_routes->s_tap_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->d_tap_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x10) != 0) {
+        int_routes->d_tap_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x10) != 0) {
+        int_routes->d_tap_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->slow_no_mot_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x08) != 0) {
+        int_routes->slow_no_mot_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x08) != 0) {
+        int_routes->slow_no_mot_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->slope_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x04) != 0) {
+        int_routes->slope_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x04) != 0) {
+        int_routes->slope_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->high_g_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x02) != 0) {
+        int_routes->high_g_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x02) != 0) {
+        int_routes->high_g_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->low_g_int_route = INT_ROUTE_NONE;
+    if ((data[0] & 0x01) != 0) {
+        int_routes->low_g_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[2] & 0x01) != 0) {
+        int_routes->low_g_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->fifo_wmark_int_route = INT_ROUTE_NONE;
+    if ((data[1] & 0x02) != 0) {
+        int_routes->fifo_wmark_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[1] & 0x40) != 0) {
+        int_routes->fifo_wmark_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->fifo_full_int_route = INT_ROUTE_NONE;
+    if ((data[1] & 0x04) != 0) {
+        int_routes->fifo_full_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[1] & 0x20) != 0) {
+        int_routes->fifo_full_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    int_routes->data_int_route = INT_ROUTE_NONE;
+    if ((data[1] & 0x01) != 0) {
+        int_routes->data_int_route |= INT_ROUTE_PIN_1;
+    }
+    if ((data[1] & 0x80) != 0) {
+        int_routes->data_int_route |= INT_ROUTE_PIN_2;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_int_routes(const struct bma2xx * bma2xx,
+                      const struct int_routes * int_routes)
+{
+    uint8_t data[3];
+    int rc;
+
+    data[0] = (((int_routes->flat_int_route & INT_ROUTE_PIN_1) != 0) << 7) |
+              (((int_routes->orient_int_route & INT_ROUTE_PIN_1) != 0) << 6) |
+              (((int_routes->s_tap_int_route & INT_ROUTE_PIN_1) != 0) << 5) |
+              (((int_routes->d_tap_int_route & INT_ROUTE_PIN_1) != 0) << 4) |
+              (((int_routes->slow_no_mot_int_route & INT_ROUTE_PIN_1) != 0) << 3) |
+              (((int_routes->slope_int_route & INT_ROUTE_PIN_1) != 0) << 2) |
+              (((int_routes->high_g_int_route & INT_ROUTE_PIN_1) != 0) << 1) |
+              (((int_routes->low_g_int_route & INT_ROUTE_PIN_1) != 0) << 0);
+
+    data[1] = (((int_routes->data_int_route & INT_ROUTE_PIN_2) != 0) << 7) |
+              (((int_routes->fifo_wmark_int_route & INT_ROUTE_PIN_2) != 0) << 6) |
+              (((int_routes->fifo_full_int_route & INT_ROUTE_PIN_2) != 0) << 5) |
+              (((int_routes->fifo_full_int_route & INT_ROUTE_PIN_1) != 0) << 2) |
+              (((int_routes->fifo_wmark_int_route & INT_ROUTE_PIN_1) != 0) << 1) |
+              (((int_routes->data_int_route & INT_ROUTE_PIN_1) != 0) << 0);
+
+    data[2] = (((int_routes->flat_int_route & INT_ROUTE_PIN_2) != 0) << 7) |
+              (((int_routes->orient_int_route & INT_ROUTE_PIN_2) != 0) << 6) |
+              (((int_routes->s_tap_int_route & INT_ROUTE_PIN_2) != 0) << 5) |
+              (((int_routes->d_tap_int_route & INT_ROUTE_PIN_2) != 0) << 4) |
+              (((int_routes->slow_no_mot_int_route & INT_ROUTE_PIN_2) != 0) << 3) |
+              (((int_routes->slope_int_route & INT_ROUTE_PIN_2) != 0) << 2) |
+              (((int_routes->high_g_int_route & INT_ROUTE_PIN_2) != 0) << 1) |
+              (((int_routes->low_g_int_route & INT_ROUTE_PIN_2) != 0) << 0);
+
+    rc = set_register(bma2xx, REG_ADDR_INT_MAP_0, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_MAP_1, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_MAP_2, data[2]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_int_filters(const struct bma2xx * bma2xx,
+                       struct int_filters * int_filters)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_INT_SRC, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_filters->unfiltered_data_int = data & 0x20;
+    int_filters->unfiltered_tap_int = data & 0x10;
+    int_filters->unfiltered_slow_no_mot_int = data & 0x08;
+    int_filters->unfiltered_slope_int = data & 0x04;
+    int_filters->unfiltered_high_g_int = data & 0x02;
+    int_filters->unfiltered_low_g_int = data & 0x01;
+
+    return 0;
+}
+
+int
+bma2xx_set_int_filters(const struct bma2xx * bma2xx,
+                       const struct int_filters * int_filters)
+{
+    uint8_t data;
+
+    data = (int_filters->unfiltered_data_int << 5) |
+           (int_filters->unfiltered_tap_int << 4) |
+           (int_filters->unfiltered_slow_no_mot_int << 3) |
+           (int_filters->unfiltered_slope_int << 2) |
+           (int_filters->unfiltered_high_g_int << 1) |
+           (int_filters->unfiltered_low_g_int << 0);
+
+    return set_register(bma2xx, REG_ADDR_INT_SRC, data);
+}
+
+int
+bma2xx_get_int_pin_electrical(const struct bma2xx * bma2xx,
+                              struct int_pin_electrical * electrical)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_INT_OUT_CTRL, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if ((data & 0x02) != 0) {
+        electrical->pin1_output = INT_PIN_OUTPUT_OPEN_DRAIN;
+    } else {
+        electrical->pin1_output = INT_PIN_OUTPUT_PUSH_PULL;
+    }
+    if ((data & 0x01) != 0) {
+        electrical->pin1_active = INT_PIN_ACTIVE_HIGH;
+    } else {
+        electrical->pin1_active = INT_PIN_ACTIVE_LOW;
+    }
+    if ((data & 0x08) != 0) {
+        electrical->pin2_output = INT_PIN_OUTPUT_OPEN_DRAIN;
+    } else {
+        electrical->pin2_output = INT_PIN_OUTPUT_PUSH_PULL;
+    }
+    if ((data & 0x04) != 0) {
+        electrical->pin2_active = INT_PIN_ACTIVE_HIGH;
+    } else {
+        electrical->pin2_active = INT_PIN_ACTIVE_LOW;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_int_pin_electrical(const struct bma2xx * bma2xx,
+                              const struct int_pin_electrical * electrical)
+{
+    uint8_t data;
+
+    data = 0;
+
+    switch (electrical->pin1_output) {
+    case INT_PIN_OUTPUT_OPEN_DRAIN:
+        data |= 0x02;
+        break;
+    case INT_PIN_OUTPUT_PUSH_PULL:
+        data |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (electrical->pin1_active) {
+    case INT_PIN_ACTIVE_HIGH:
+        data |= 0x01;
+        break;
+    case INT_PIN_ACTIVE_LOW:
+        data |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (electrical->pin2_output) {
+    case INT_PIN_OUTPUT_OPEN_DRAIN:
+        data |= 0x08;
+        break;
+    case INT_PIN_OUTPUT_PUSH_PULL:
+        data |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (electrical->pin2_active) {
+    case INT_PIN_ACTIVE_HIGH:
+        data |= 0x04;
+        break;
+    case INT_PIN_ACTIVE_LOW:
+        data |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, REG_ADDR_INT_OUT_CTRL, data);
+}
+
+int
+bma2xx_get_int_latch(const struct bma2xx * bma2xx,
+                     enum int_latch * int_latch)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_INT_RST_LATCH, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch (data & 0x0F) {
+    case 0x00:
+        *int_latch = INT_LATCH_NON_LATCHED;
+        break;
+    case 0x01:
+        *int_latch = INT_LATCH_TEMPORARY_250_MS;
+        break;
+    case 0x02:
+        *int_latch = INT_LATCH_TEMPORARY_500_MS;
+        break;
+    case 0x03:
+        *int_latch = INT_LATCH_TEMPORARY_1_S;
+        break;
+    case 0x04:
+        *int_latch = INT_LATCH_TEMPORARY_2_S;
+        break;
+    case 0x05:
+        *int_latch = INT_LATCH_TEMPORARY_4_S;
+        break;
+    case 0x06:
+        *int_latch = INT_LATCH_TEMPORARY_8_S;
+        break;
+    case 0x07:
+        *int_latch = INT_LATCH_LATCHED;
+        break;
+    case 0x08:
+        *int_latch = INT_LATCH_NON_LATCHED;
+        break;
+    case 0x09:
+        *int_latch = INT_LATCH_TEMPORARY_250_US;
+        break;
+    case 0x0A:
+        *int_latch = INT_LATCH_TEMPORARY_500_US;
+        break;
+    case 0x0B:
+        *int_latch = INT_LATCH_TEMPORARY_1_MS;
+        break;
+    case 0x0C:
+        *int_latch = INT_LATCH_TEMPORARY_12_5_MS;
+        break;
+    case 0x0D:
+        *int_latch = INT_LATCH_TEMPORARY_25_MS;
+        break;
+    case 0x0E:
+        *int_latch = INT_LATCH_TEMPORARY_50_MS;
+        break;
+    case 0x0F:
+        *int_latch = INT_LATCH_LATCHED;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_int_latch(const struct bma2xx * bma2xx,
+                     bool reset_ints,
+                     enum int_latch int_latch)
+{
+    uint8_t data;
+
+    data = 0;
+    data |= reset_ints << 7;
+
+    switch (int_latch) {
+    case INT_LATCH_NON_LATCHED:
+        data |= 0x00;
+        break;
+    case INT_LATCH_LATCHED:
+        data |= 0x0F;
+        break;
+    case INT_LATCH_TEMPORARY_250_US:
+        data |= 0x09;
+        break;
+    case INT_LATCH_TEMPORARY_500_US:
+        data |= 0x0A;
+        break;
+    case INT_LATCH_TEMPORARY_1_MS:
+        data |= 0x0B;
+        break;
+    case INT_LATCH_TEMPORARY_12_5_MS:
+        data |= 0x0C;
+        break;
+    case INT_LATCH_TEMPORARY_25_MS:
+        data |= 0x0D;
+        break;
+    case INT_LATCH_TEMPORARY_50_MS:
+        data |= 0x0E;
+        break;
+    case INT_LATCH_TEMPORARY_250_MS:
+        data |= 0x01;
+        break;
+    case INT_LATCH_TEMPORARY_500_MS:
+        data |= 0x02;
+        break;
+    case INT_LATCH_TEMPORARY_1_S:
+        data |= 0x03;
+        break;
+    case INT_LATCH_TEMPORARY_2_S:
+        data |= 0x04;
+        break;
+    case INT_LATCH_TEMPORARY_4_S:
+        data |= 0x05;
+        break;
+    case INT_LATCH_TEMPORARY_8_S:
+        data |= 0x06;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, REG_ADDR_INT_RST_LATCH, data);
+}
+
+int
+bma2xx_get_low_g_int_cfg(const struct bma2xx * bma2xx,
+                         struct low_g_int_cfg * low_g_int_cfg)
+{
+    uint8_t data[3];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_0,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    low_g_int_cfg->delay_ms = (data[0] + 1) << 1;
+    low_g_int_cfg->thresh_g = (float)data[1] * 0.00781;
+    low_g_int_cfg->hyster_g = (float)(data[2] & 0x03) * 0.125;
+    low_g_int_cfg->axis_summing = data[2] & 0x04;
+
+    return 0;
+}
+
+int
+bma2xx_set_low_g_int_cfg(const struct bma2xx * bma2xx,
+                         const struct low_g_int_cfg * low_g_int_cfg)
+{
+    uint8_t data[3];
+    int rc;
+
+    if (low_g_int_cfg->delay_ms < 2 ||
+        low_g_int_cfg->delay_ms > 512) {
+        return SYS_EINVAL;
+    }
+    if (low_g_int_cfg->thresh_g < 0.0 ||
+        low_g_int_cfg->thresh_g > 1.992) {
+        return SYS_EINVAL;
+    }
+    if (low_g_int_cfg->hyster_g < 0.0 ||
+        low_g_int_cfg->hyster_g > 0.375) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = (low_g_int_cfg->delay_ms >> 1) - 1;
+    data[1] = low_g_int_cfg->thresh_g / 0.00781;
+    data[2] = (low_g_int_cfg->axis_summing << 2) |
+              (((uint8_t)(low_g_int_cfg->hyster_g / 0.125) & 0x03) << 0);
+
+    rc = set_register(bma2xx, REG_ADDR_INT_0, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_1, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_2, data[2]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_high_g_int_cfg(const struct bma2xx * bma2xx,
+                          enum bma2xx_g_range g_range,
+                          struct high_g_int_cfg * high_g_int_cfg)
+{
+    float hyster_scale;
+    float thresh_scale;
+    uint8_t data[3];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        hyster_scale = 0.125;
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_4:
+        hyster_scale = 0.25;
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_8:
+        hyster_scale = 0.5;
+        thresh_scale = 0.03125;
+        break;
+    case BMA2XX_G_RANGE_16:
+        hyster_scale = 1.0;
+        thresh_scale = 0.0625;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_2,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    high_g_int_cfg->hyster_g = (float)((data[0] >> 6) & 0x03) * hyster_scale;
+    high_g_int_cfg->delay_ms = (data[1] + 1) << 1;
+    high_g_int_cfg->thresh_g = (float)data[2] * thresh_scale;
+
+    return 0;
+}
+
+int
+bma2xx_set_high_g_int_cfg(const struct bma2xx * bma2xx,
+                          enum bma2xx_g_range g_range,
+                          const struct high_g_int_cfg * high_g_int_cfg)
+{
+    float hyster_scale;
+    float thresh_scale;
+    uint8_t data[3];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        hyster_scale = 0.125;
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_4:
+        hyster_scale = 0.25;
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_8:
+        hyster_scale = 0.5;
+        thresh_scale = 0.03125;
+        break;
+    case BMA2XX_G_RANGE_16:
+        hyster_scale = 1.0;
+        thresh_scale = 0.0625;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (high_g_int_cfg->hyster_g < 0.0 ||
+        high_g_int_cfg->hyster_g > hyster_scale * 3.0) {
+        return SYS_EINVAL;
+    }
+    if (high_g_int_cfg->delay_ms < 2 ||
+        high_g_int_cfg->delay_ms > 512) {
+        return SYS_EINVAL;
+    }
+    if (high_g_int_cfg->thresh_g < 0.0 ||
+        high_g_int_cfg->thresh_g > thresh_scale * 255.0) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = ((uint8_t)(high_g_int_cfg->hyster_g / hyster_scale) & 0x03) << 6;
+    data[1] = (high_g_int_cfg->delay_ms >> 1) - 1;
+    data[2] = high_g_int_cfg->thresh_g / thresh_scale;
+
+    rc = set_register(bma2xx, REG_ADDR_INT_2, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_3, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_4, data[2]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_slow_no_mot_int_cfg(const struct bma2xx * bma2xx,
+                               bool no_motion_select,
+                               enum bma2xx_g_range g_range,
+                               struct slow_no_mot_int_cfg * slow_no_mot_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.00391;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.03125;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_register(bma2xx, REG_ADDR_INT_5, data + 0);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = get_register(bma2xx, REG_ADDR_INT_7, data + 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (no_motion_select) {
+        if ((data[0] & 0x80) == 0) {
+            if ((data[0] & 0x40) == 0) {
+                slow_no_mot_int_cfg->duration_p_or_s =
+                    ((data[0] >> 2) & 0x0F) + 1;
+            } else {
+                slow_no_mot_int_cfg->duration_p_or_s =
+                    (((data[0] >> 2) & 0x0F) << 2) + 20;
+            }
+        } else {
+            slow_no_mot_int_cfg->duration_p_or_s =
+                (((data[0] >> 2) & 0x1F) << 3) + 88;
+        }
+    } else {
+        slow_no_mot_int_cfg->duration_p_or_s =
+            ((data[0] >> 2) & 0x03) + 1;
+    }
+    slow_no_mot_int_cfg->thresh_g = (float)data[1] * thresh_scale;
+
+    return 0;
+}
+
+int
+bma2xx_set_slow_no_mot_int_cfg(const struct bma2xx * bma2xx,
+                               bool no_motion_select,
+                               enum bma2xx_g_range g_range,
+                               const struct slow_no_mot_int_cfg * slow_no_mot_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    uint16_t duration;
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.00391;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.03125;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (no_motion_select) {
+        if (slow_no_mot_int_cfg->duration_p_or_s < 1 ||
+            slow_no_mot_int_cfg->duration_p_or_s > 336) {
+            return SYS_EINVAL;
+        }
+    } else {
+        if (slow_no_mot_int_cfg->duration_p_or_s < 1 ||
+            slow_no_mot_int_cfg->duration_p_or_s > 4) {
+            return SYS_EINVAL;
+        }
+    }
+    if (slow_no_mot_int_cfg->thresh_g < 0.0 ||
+        slow_no_mot_int_cfg->thresh_g > thresh_scale * 255.0) {
+        return SYS_EINVAL;
+    }
+
+    duration = slow_no_mot_int_cfg->duration_p_or_s;
+    if (no_motion_select) {
+        if (duration > 80) {
+            if (duration < 88) {
+                duration = 88;
+            }
+            data[0] = (((duration - 88) >> 3) << 2) | 0x80;
+        } else if (duration > 16) {
+            if (duration < 20) {
+                duration = 20;
+            }
+            data[0] = (((duration - 20) >> 2) << 2) | 0x40;
+        } else {
+            data[0] = (duration - 1) << 2;
+        }
+    } else {
+        data[0] = (duration - 1) << 2;
+    }
+    data[1] = slow_no_mot_int_cfg->thresh_g / thresh_scale;
+
+    rc = set_register(bma2xx, REG_ADDR_INT_5, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_7, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_slope_int_cfg(const struct bma2xx * bma2xx,
+                         enum bma2xx_g_range g_range,
+                         struct slope_int_cfg * slope_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.00391;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.03125;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_5,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    slope_int_cfg->duration_p = (data[0] & 0x03) + 1;
+    slope_int_cfg->thresh_g = (float)data[1] * thresh_scale;
+
+    return 0;
+}
+
+int
+bma2xx_set_slope_int_cfg(const struct bma2xx * bma2xx,
+                         enum bma2xx_g_range g_range,
+                         const struct slope_int_cfg * slope_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.00391;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.00781;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.01563;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.03125;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (slope_int_cfg->duration_p < 1 ||
+        slope_int_cfg->duration_p > 4) {
+        return SYS_EINVAL;
+    }
+    if (slope_int_cfg->thresh_g < 0.0 ||
+        slope_int_cfg->thresh_g > thresh_scale * 255.0) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = (slope_int_cfg->duration_p - 1) & 0x03;
+    data[1] = slope_int_cfg->thresh_g / thresh_scale;
+
+    rc = set_register(bma2xx, REG_ADDR_INT_5, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_6, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_tap_int_cfg(const struct bma2xx * bma2xx,
+                       enum bma2xx_g_range g_range,
+                       struct tap_int_cfg * tap_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.0625;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.125;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.25;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.5;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_8,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    if ((data[0] & 0x80) == 0) {
+        tap_int_cfg->tap_quiet = BMA2XX_TAP_QUIET_30_MS;
+    } else {
+        tap_int_cfg->tap_quiet = BMA2XX_TAP_QUIET_20_MS;
+    }
+    if ((data[0] & 0x40) == 0) {
+        tap_int_cfg->tap_shock = BMA2XX_TAP_SHOCK_50_MS;
+    } else {
+        tap_int_cfg->tap_shock = BMA2XX_TAP_SHOCK_75_MS;
+    }
+
+    switch (data[0] & 0x07) {
+    case 0x00:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_50_MS;
+        break;
+    case 0x01:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_100_MS;
+        break;
+    case 0x02:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_150_MS;
+        break;
+    case 0x03:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_200_MS;
+        break;
+    case 0x04:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_250_MS;
+        break;
+    case 0x05:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_375_MS;
+        break;
+    case 0x06:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_500_MS;
+        break;
+    case 0x07:
+        tap_int_cfg->d_tap_window = BMA2XX_D_TAP_WINDOW_700_MS;
+        break;
+    }
+
+    switch ((data[1] >> 6) & 0x03) {
+    case 0x00:
+        tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_2;
+        break;
+    case 0x01:
+        tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_4;
+        break;
+    case 0x02:
+        tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_8;
+        break;
+    case 0x03:
+        tap_int_cfg->tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_16;
+        break;
+    }
+
+    tap_int_cfg->thresh_g = (float)(data[1] & 0x1F) * thresh_scale;
+
+    return 0;
+}
+
+int
+bma2xx_set_tap_int_cfg(const struct bma2xx * bma2xx,
+                       enum bma2xx_g_range g_range,
+                       const struct tap_int_cfg * tap_int_cfg)
+{
+    float thresh_scale;
+    uint8_t data[2];
+    int rc;
+
+    switch (g_range) {
+    case BMA2XX_G_RANGE_2:
+        thresh_scale = 0.0625;
+        break;
+    case BMA2XX_G_RANGE_4:
+        thresh_scale = 0.125;
+        break;
+    case BMA2XX_G_RANGE_8:
+        thresh_scale = 0.25;
+        break;
+    case BMA2XX_G_RANGE_16:
+        thresh_scale = 0.5;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (tap_int_cfg->thresh_g < 0.0 ||
+        tap_int_cfg->thresh_g > thresh_scale * 31.0) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = 0;
+    data[1] = 0;
+
+    switch (tap_int_cfg->tap_quiet) {
+    case BMA2XX_TAP_QUIET_20_MS:
+        data[0] |= 0x80;
+        break;
+    case BMA2XX_TAP_QUIET_30_MS:
+        data[0] |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (tap_int_cfg->tap_shock) {
+    case BMA2XX_TAP_SHOCK_50_MS:
+        data[0] |= 0x00;
+        break;
+    case BMA2XX_TAP_SHOCK_75_MS:
+        data[0] |= 0x40;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (tap_int_cfg->d_tap_window) {
+    case BMA2XX_D_TAP_WINDOW_50_MS:
+        data[0] |= 0x00;
+        break;
+    case BMA2XX_D_TAP_WINDOW_100_MS:
+        data[0] |= 0x01;
+        break;
+    case BMA2XX_D_TAP_WINDOW_150_MS:
+        data[0] |= 0x02;
+        break;
+    case BMA2XX_D_TAP_WINDOW_200_MS:
+        data[0] |= 0x03;
+        break;
+    case BMA2XX_D_TAP_WINDOW_250_MS:
+        data[0] |= 0x04;
+        break;
+    case BMA2XX_D_TAP_WINDOW_375_MS:
+        data[0] |= 0x05;
+        break;
+    case BMA2XX_D_TAP_WINDOW_500_MS:
+        data[0] |= 0x06;
+        break;
+    case BMA2XX_D_TAP_WINDOW_700_MS:
+        data[0] |= 0x07;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (tap_int_cfg->tap_wake_samples) {
+    case BMA2XX_TAP_WAKE_SAMPLES_2:
+        data[1] |= 0x00 << 6;
+        break;
+    case BMA2XX_TAP_WAKE_SAMPLES_4:
+        data[1] |= 0x01 << 6;
+        break;
+    case BMA2XX_TAP_WAKE_SAMPLES_8:
+        data[1] |= 0x02 << 6;
+        break;
+    case BMA2XX_TAP_WAKE_SAMPLES_16:
+        data[1] |= 0x03 << 6;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    data[1] |= (uint8_t)(tap_int_cfg->thresh_g / thresh_scale) & 0x1F;
+
+    rc = set_register(bma2xx, REG_ADDR_INT_8, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_9, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_orient_int_cfg(const struct bma2xx * bma2xx,
+                          struct orient_int_cfg * orient_int_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_A,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    orient_int_cfg->hyster_g = (float)((data[0] >> 4) & 0x07) * 0.0625;
+
+    switch ((data[0] >> 2) & 0x03) {
+    case 0x00:
+        orient_int_cfg->orient_blocking =
+            BMA2XX_ORIENT_BLOCKING_NONE;
+        break;
+    case 0x01:
+        orient_int_cfg->orient_blocking =
+            BMA2XX_ORIENT_BLOCKING_ACCEL_ONLY;
+        break;
+    case 0x02:
+        orient_int_cfg->orient_blocking =
+            BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE;
+        break;
+    case 0x03:
+        orient_int_cfg->orient_blocking =
+            BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE_AND_STABLE;
+        break;
+    }
+
+    switch (data[0] & 0x03) {
+    case 0x00:
+        orient_int_cfg->orient_mode =
+            BMA2XX_ORIENT_MODE_SYMMETRICAL;
+        break;
+    case 0x01:
+        orient_int_cfg->orient_mode =
+            BMA2XX_ORIENT_MODE_HIGH_ASYMMETRICAL;
+        break;
+    case 0x02:
+        orient_int_cfg->orient_mode =
+            BMA2XX_ORIENT_MODE_LOW_ASYMMETRICAL;
+        break;
+    case 0x03:
+        orient_int_cfg->orient_mode =
+            BMA2XX_ORIENT_MODE_SYMMETRICAL;
+        break;
+    }
+
+    orient_int_cfg->signal_up_dn = data[1] & 0x40;
+    orient_int_cfg->blocking_angle = data[1] & 0x3F;
+
+    return 0;
+}
+
+int
+bma2xx_set_orient_int_cfg(const struct bma2xx * bma2xx,
+                          const struct orient_int_cfg * orient_int_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    if (orient_int_cfg->hyster_g < 0.0 ||
+        orient_int_cfg->hyster_g > (0.0625 * 7.0)) {
+        return SYS_EINVAL;
+    }
+    if (orient_int_cfg->blocking_angle > 0x3F) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = (uint8_t)(orient_int_cfg->hyster_g / 0.0625) << 4;
+
+    switch (orient_int_cfg->orient_blocking) {
+    case BMA2XX_ORIENT_BLOCKING_NONE:
+        data[0] |= 0x00 << 2;
+        break;
+    case BMA2XX_ORIENT_BLOCKING_ACCEL_ONLY:
+        data[0] |= 0x01 << 2;
+        break;
+    case BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE:
+        data[0] |= 0x02 << 2;
+        break;
+    case BMA2XX_ORIENT_BLOCKING_ACCEL_AND_SLOPE_AND_STABLE:
+        data[0] |= 0x03 << 2;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (orient_int_cfg->orient_mode) {
+    case BMA2XX_ORIENT_MODE_SYMMETRICAL:
+        data[0] |= 0x00;
+        break;
+    case BMA2XX_ORIENT_MODE_HIGH_ASYMMETRICAL:
+        data[0] |= 0x01;
+        break;
+    case BMA2XX_ORIENT_MODE_LOW_ASYMMETRICAL:
+        data[0] |= 0x02;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    data[1] = (orient_int_cfg->signal_up_dn << 6) |
+              ((orient_int_cfg->blocking_angle & 0x3F) << 0);
+
+    rc = set_register(bma2xx, REG_ADDR_INT_A, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_B, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_flat_int_cfg(const struct bma2xx * bma2xx,
+                        struct flat_int_cfg * flat_int_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_INT_C,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    flat_int_cfg->flat_angle = data[0] & 0x3F;
+
+    switch ((data[1] >> 4) & 0x03) {
+    case 0x00:
+        flat_int_cfg->flat_hold = FLAT_HOLD_0_MS;
+        break;
+    case 0x01:
+        flat_int_cfg->flat_hold = FLAT_HOLD_512_MS;
+        break;
+    case 0x02:
+        flat_int_cfg->flat_hold = FLAT_HOLD_1024_MS;
+        break;
+    case 0x03:
+        flat_int_cfg->flat_hold = FLAT_HOLD_2048_MS;
+        break;
+    }
+
+    flat_int_cfg->flat_hyster = data[1] & 0x07;
+    flat_int_cfg->hyster_enable = (data[1] & 0x07) != 0x00;
+
+    return 0;
+}
+
+int
+bma2xx_set_flat_int_cfg(const struct bma2xx * bma2xx,
+                        const struct flat_int_cfg * flat_int_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    if (flat_int_cfg->flat_angle > 0x3F) {
+        return SYS_EINVAL;
+    }
+    if (flat_int_cfg->flat_hyster == 0x00 &&
+        flat_int_cfg->hyster_enable) {
+        return SYS_EINVAL;
+    }
+
+    data[0] = flat_int_cfg->flat_angle & 0x3F;
+    data[1] = 0;
+
+    switch (flat_int_cfg->flat_hold) {
+    case FLAT_HOLD_0_MS:
+        data[1] |= 0x00 << 4;
+        break;
+    case FLAT_HOLD_512_MS:
+        data[1] |= 0x01 << 4;
+        break;
+    case FLAT_HOLD_1024_MS:
+        data[1] |= 0x02 << 4;
+        break;
+    case FLAT_HOLD_2048_MS:
+        data[1] |= 0x03 << 4;
+        break;
+    }
+
+    if (flat_int_cfg->hyster_enable) {
+        data[1] |= flat_int_cfg->flat_hyster & 0x07;
+    }
+
+    rc = set_register(bma2xx, REG_ADDR_INT_C, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_INT_D, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_fifo_wmark_level(const struct bma2xx * bma2xx,
+                            uint8_t * wmark_level)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_FIFO_CONFIG_0, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *wmark_level = data & 0x3F;
+
+    return 0;
+}
+
+int
+bma2xx_set_fifo_wmark_level(const struct bma2xx * bma2xx,
+                            uint8_t wmark_level)
+{
+    uint8_t data;
+
+    if (wmark_level > 32) {
+        return SYS_EINVAL;
+    }
+
+    data = wmark_level & 0x3F;
+
+    return set_register(bma2xx, REG_ADDR_FIFO_CONFIG_0, data);
+}
+
+int
+bma2xx_get_self_test_cfg(const struct bma2xx * bma2xx,
+                         struct self_test_cfg * self_test_cfg)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_PMU_SELF_TEST, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if ((data & 0x10) == 0) {
+        self_test_cfg->self_test_ampl = SELF_TEST_AMPL_LOW;
+    } else {
+        self_test_cfg->self_test_ampl = SELF_TEST_AMPL_HIGH;
+    }
+    if ((data & 0x04) == 0) {
+        self_test_cfg->self_test_sign = SELF_TEST_SIGN_NEGATIVE;
+    } else {
+        self_test_cfg->self_test_sign = SELF_TEST_SIGN_POSITIVE;
+    }
+
+    switch (data & 0x03) {
+    case 0x00:
+        self_test_cfg->self_test_axis = -1;
+        self_test_cfg->self_test_enabled = false;
+        break;
+    case 0x01:
+        self_test_cfg->self_test_axis = AXIS_X;
+        self_test_cfg->self_test_enabled = true;
+        break;
+    case 0x02:
+        self_test_cfg->self_test_axis = AXIS_Y;
+        self_test_cfg->self_test_enabled = true;
+        break;
+    case 0x03:
+        self_test_cfg->self_test_axis = AXIS_Z;
+        self_test_cfg->self_test_enabled = true;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_self_test_cfg(const struct bma2xx * bma2xx,
+                         const struct self_test_cfg * self_test_cfg)
+{
+    uint8_t data;
+
+    data = 0;
+
+    switch (self_test_cfg->self_test_ampl) {
+    case SELF_TEST_AMPL_HIGH:
+        data |= 0x10;
+        break;
+    case SELF_TEST_AMPL_LOW:
+        data |= 0x00;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (self_test_cfg->self_test_sign) {
+    case SELF_TEST_SIGN_NEGATIVE:
+        data |= 0x00;
+        break;
+    case SELF_TEST_SIGN_POSITIVE:
+        data |= 0x04;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (self_test_cfg->self_test_enabled) {
+        switch (self_test_cfg->self_test_axis) {
+        case AXIS_X:
+            data |= 0x01;
+            break;
+        case AXIS_Y:
+            data |= 0x02;
+            break;
+        case AXIS_Z:
+            data |= 0x03;
+            break;
+        default:
+            return SYS_EINVAL;
+        }
+    }
+
+    return set_register(bma2xx, REG_ADDR_PMU_SELF_TEST, data);
+}
+
+int
+bma2xx_get_nvm_control(const struct bma2xx * bma2xx,
+                       uint8_t * remaining_cycles,
+                       bool * load_from_nvm,
+                       bool * nvm_is_ready,
+                       bool * nvm_unlocked)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_TRIM_NVM_CTRL, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *remaining_cycles = (data >> 4) & 0x0F;
+    *load_from_nvm = data & 0x08;
+    *nvm_is_ready = data & 0x04;
+    *nvm_unlocked = data & 0x01;
+
+    return 0;
+}
+
+int
+bma2xx_set_nvm_control(const struct bma2xx * bma2xx,
+                       bool load_from_nvm,
+                       bool store_into_nvm,
+                       bool nvm_unlocked)
+{
+    uint8_t data;
+
+    data = (load_from_nvm << 3) |
+           (store_into_nvm << 1) |
+           (nvm_unlocked << 0);
+
+    return set_register(bma2xx, REG_ADDR_TRIM_NVM_CTRL, data);
+}
+
+int
+bma2xx_get_i2c_watchdog(const struct bma2xx * bma2xx,
+                        enum i2c_watchdog * i2c_watchdog)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_BGW_SPI3_WDT, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if ((data & 0x04) != 0) {
+        if ((data & 0x02) != 0) {
+            *i2c_watchdog = I2C_WATCHDOG_50_MS;
+        } else {
+            *i2c_watchdog = I2C_WATCHDOG_1_MS;
+        }
+    } else {
+        *i2c_watchdog = I2C_WATCHDOG_DISABLED;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_i2c_watchdog(const struct bma2xx * bma2xx,
+                        enum i2c_watchdog i2c_watchdog)
+{
+    uint8_t data;
+
+    switch (i2c_watchdog) {
+    case I2C_WATCHDOG_DISABLED:
+        data = 0x00;
+        break;
+    case I2C_WATCHDOG_1_MS:
+        data = 0x04;
+        break;
+    case I2C_WATCHDOG_50_MS:
+        data = 0x06;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, REG_ADDR_BGW_SPI3_WDT, data);
+}
+
+int
+bma2xx_get_fast_ofc_cfg(const struct bma2xx * bma2xx,
+                        bool * fast_ofc_ready,
+                        enum bma2xx_offset_comp_target * ofc_target_z,
+                        enum bma2xx_offset_comp_target * ofc_target_y,
+                        enum bma2xx_offset_comp_target * ofc_target_x)
+{
+    uint8_t data[2];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_OFC_CTRL,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    *fast_ofc_ready = data[0] & 0x10;
+
+    switch ((data[1] >> 5) & 0x03) {
+    case 0x00:
+        *ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    case 0x01:
+        *ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
+        break;
+    case 0x02:
+        *ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
+        break;
+    case 0x03:
+        *ofc_target_z = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    }
+
+    switch ((data[1] >> 3) & 0x03) {
+    case 0x00:
+        *ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    case 0x01:
+        *ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
+        break;
+    case 0x02:
+        *ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
+        break;
+    case 0x03:
+        *ofc_target_y = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    }
+
+    switch ((data[1] >> 1) & 0x03) {
+    case 0x00:
+        *ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    case 0x01:
+        *ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_POS_1_G;
+        break;
+    case 0x02:
+        *ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G;
+        break;
+    case 0x03:
+        *ofc_target_x = BMA2XX_OFFSET_COMP_TARGET_0_G;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_fast_ofc_cfg(const struct bma2xx * bma2xx,
+                        enum axis fast_ofc_axis,
+                        enum bma2xx_offset_comp_target fast_ofc_target,
+                        bool trigger_fast_ofc)
+{
+    uint8_t data[2];
+    uint8_t axis_value;
+    uint8_t axis_shift;
+    int rc;
+
+    data[0] = 0;
+    data[1] = 0;
+
+    switch (fast_ofc_axis) {
+    case AXIS_X:
+        axis_value = 0x01;
+        axis_shift = 1;
+        break;
+    case AXIS_Y:
+        axis_value = 0x02;
+        axis_shift = 3;
+        break;
+    case AXIS_Z:
+        axis_value = 0x03;
+        axis_shift = 5;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (fast_ofc_target) {
+    case BMA2XX_OFFSET_COMP_TARGET_0_G:
+        data[1] |= 0x00 << axis_shift;
+        break;
+    case BMA2XX_OFFSET_COMP_TARGET_NEG_1_G:
+        data[1] |= 0x02 << axis_shift;
+        break;
+    case BMA2XX_OFFSET_COMP_TARGET_POS_1_G:
+        data[1] |= 0x01 << axis_shift;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    if (trigger_fast_ofc) {
+        data[0] |= axis_value << 5;
+    }
+
+    rc = set_register(bma2xx, REG_ADDR_OFC_SETTING, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_OFC_CTRL, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_get_slow_ofc_cfg(const struct bma2xx * bma2xx,
+                        struct slow_ofc_cfg * slow_ofc_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    rc = get_registers(bma2xx, REG_ADDR_OFC_CTRL,
+                       data, sizeof(data) / sizeof(*data));
+    if (rc != 0) {
+        return rc;
+    }
+
+    slow_ofc_cfg->ofc_z_enabled = data[0] & 0x04;
+    slow_ofc_cfg->ofc_y_enabled = data[0] & 0x02;
+    slow_ofc_cfg->ofc_x_enabled = data[0] & 0x01;
+    slow_ofc_cfg->high_bw_cut_off = data[1] & 0x01;
+
+    return 0;
+}
+
+int
+bma2xx_set_slow_ofc_cfg(const struct bma2xx * bma2xx,
+                        const struct slow_ofc_cfg * slow_ofc_cfg)
+{
+    uint8_t data[2];
+    int rc;
+
+    data[0] = (slow_ofc_cfg->ofc_z_enabled << 2) |
+              (slow_ofc_cfg->ofc_y_enabled << 1) |
+              (slow_ofc_cfg->ofc_x_enabled << 0);
+    data[1] = slow_ofc_cfg->high_bw_cut_off << 0;
+
+    rc = set_register(bma2xx, REG_ADDR_OFC_SETTING, data[1]);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = set_register(bma2xx, REG_ADDR_OFC_CTRL, data[0]);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_ofc_reset(const struct bma2xx * bma2xx)
+{
+    return set_register(bma2xx, REG_ADDR_OFC_CTRL, 0x80);
+}
+
+int
+bma2xx_get_ofc_offset(const struct bma2xx * bma2xx,
+                      enum axis axis,
+                      float * offset_g)
+{
+    uint8_t reg_addr;
+    uint8_t data;
+    int rc;
+
+    switch (axis) {
+    case AXIS_X:
+        reg_addr = REG_ADDR_OFC_OFFSET_X;
+        break;
+    case AXIS_Y:
+        reg_addr = REG_ADDR_OFC_OFFSET_Y;
+        break;
+    case AXIS_Z:
+        reg_addr = REG_ADDR_OFC_OFFSET_Z;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_register(bma2xx, reg_addr, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *offset_g = (float)(int8_t)data * 0.00781;
+
+    return 0;
+}
+
+int
+bma2xx_set_ofc_offset(const struct bma2xx * bma2xx,
+                      enum axis axis,
+                      float offset_g)
+{
+    uint8_t reg_addr;
+    uint8_t data;
+
+    switch (axis) {
+    case AXIS_X:
+        reg_addr = REG_ADDR_OFC_OFFSET_X;
+        break;
+    case AXIS_Y:
+        reg_addr = REG_ADDR_OFC_OFFSET_Y;
+        break;
+    case AXIS_Z:
+        reg_addr = REG_ADDR_OFC_OFFSET_Z;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    data = (int8_t)(offset_g / 0.00781);
+
+    return set_register(bma2xx, reg_addr, data);
+}
+
+int
+bma2xx_get_saved_data(const struct bma2xx * bma2xx,
+                      enum saved_data_addr saved_data_addr,
+                      uint8_t * saved_data_val)
+{
+    uint8_t reg_addr;
+
+    switch (saved_data_addr) {
+    case SAVED_DATA_ADDR_0:
+        reg_addr = REG_ADDR_TRIM_GP0;
+        break;
+    case SAVED_DATA_ADDR_1:
+        reg_addr = REG_ADDR_TRIM_GP1;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return get_register(bma2xx, reg_addr, saved_data_val);
+}
+
+int
+bma2xx_set_saved_data(const struct bma2xx * bma2xx,
+                      enum saved_data_addr saved_data_addr,
+                      uint8_t saved_data_val)
+{
+    uint8_t reg_addr;
+
+    switch (saved_data_addr) {
+    case SAVED_DATA_ADDR_0:
+        reg_addr = REG_ADDR_TRIM_GP0;
+        break;
+    case SAVED_DATA_ADDR_1:
+        reg_addr = REG_ADDR_TRIM_GP1;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    return set_register(bma2xx, reg_addr, saved_data_val);
+}
+
+int
+bma2xx_get_fifo_cfg(const struct bma2xx * bma2xx,
+                    struct fifo_cfg * fifo_cfg)
+{
+    uint8_t data;
+    int rc;
+
+    rc = get_register(bma2xx, REG_ADDR_FIFO_CONFIG_1, &data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    switch ((data >> 6) & 0x03) {
+    case 0x03:
+        BMA2XX_ERROR("unknown FIFO_CONFIG_1 reg value 0x%02X\n", data);
+    case 0x00:
+        fifo_cfg->fifo_mode = FIFO_MODE_BYPASS;
+        break;
+    case 0x01:
+        fifo_cfg->fifo_mode = FIFO_MODE_FIFO;
+        break;
+    case 0x02:
+        fifo_cfg->fifo_mode = FIFO_MODE_STREAM;
+        break;
+    }
+
+    switch ((data >> 0) & 0x03) {
+    case 0x00:
+        fifo_cfg->fifo_data = FIFO_DATA_X_AND_Y_AND_Z;
+        break;
+    case 0x01:
+        fifo_cfg->fifo_data = FIFO_DATA_X_ONLY;
+        break;
+    case 0x02:
+        fifo_cfg->fifo_data = FIFO_DATA_Y_ONLY;
+        break;
+    case 0x03:
+        fifo_cfg->fifo_data = FIFO_DATA_Z_ONLY;
+        break;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_set_fifo_cfg(const struct bma2xx * bma2xx,
+                    const struct fifo_cfg * fifo_cfg)
+{
+    uint8_t data;
+
+    data = 0;
+
+    switch (fifo_cfg->fifo_mode) {
+    case FIFO_MODE_BYPASS:
+        data |= 0x00 << 6;
+        break;
+    case FIFO_MODE_FIFO:
+        data |= 0x01 << 6;
+        break;
+    case FIFO_MODE_STREAM:
+        data |= 0x02 << 6;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    switch (fifo_cfg->fifo_data) {
+    case FIFO_DATA_X_AND_Y_AND_Z:
+        data |= 0x00 << 0;
+        break;
+    case FIFO_DATA_X_ONLY:
+        data |= 0x01 << 0;
+        break;
+    case FIFO_DATA_Y_ONLY:
+        data |= 0x02 << 0;
+        break;
+    case FIFO_DATA_Z_ONLY:
+        data |= 0x03 << 0;
+        break;
+    }
+
+    return set_register(bma2xx, REG_ADDR_FIFO_CONFIG_1, data);
+}
+
+int
+bma2xx_get_fifo(const struct bma2xx * bma2xx,
+                enum bma2xx_g_range g_range,
+                enum fifo_data fifo_data,
+                struct accel_data * accel_data)
+{
+    float accel_scale;
+    uint8_t size, iter;
+    uint8_t data[AXIS_ALL << 1];
+    int rc;
+
+    rc = get_accel_scale(bma2xx->cfg.model, g_range, &accel_scale);
+    if (rc != 0){
+        return SYS_EINVAL;
+    }
+
+    switch (fifo_data) {
+    case FIFO_DATA_X_AND_Y_AND_Z:
+        size = AXIS_ALL << 1;
+        break;
+    case FIFO_DATA_X_ONLY:
+    case FIFO_DATA_Y_ONLY:
+    case FIFO_DATA_Z_ONLY:
+        size = 1 << 1;
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = get_registers(bma2xx, REG_ADDR_FIFO_DATA, data, size);
+    if (rc != 0) {
+        return rc;
+    }
+
+    for (iter = 0; iter < size; iter += 2) {
+        compute_accel_data(accel_data + (iter >> 1),
+                           bma2xx->cfg.model,
+                           data + iter,
+                           accel_scale);
+    }
+
+    return 0;
+}
+
+static int
+reset_and_recfg(struct bma2xx * bma2xx)
+{
+    const struct bma2xx_cfg * cfg;
+    int rc;
+    enum int_route int_route;
+    struct int_routes int_routes;
+    struct int_filters int_filters;
+    struct int_pin_electrical int_pin_electrical;
+    struct low_g_int_cfg low_g_int_cfg;
+    struct high_g_int_cfg high_g_int_cfg;
+    struct tap_int_cfg tap_int_cfg;
+    struct orient_int_cfg orient_int_cfg;
+    enum i2c_watchdog i2c_watchdog;
+    struct fifo_cfg fifo_cfg;
+    struct bma2xx_private_driver_data *pdd;
+
+    cfg = &bma2xx->cfg;
+    pdd = &bma2xx->pdd;
+
+    bma2xx->power = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = bma2xx_set_softreset(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_filter_bandwidth(bma2xx, cfg->filter_bandwidth);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_data_acquisition(bma2xx,
+                     cfg->use_unfiltered_data,
+                     false);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_route = INT_ROUTE_NONE;
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    int_route = pdd->int_route;
+#endif
+
+    int_routes.flat_int_route        = INT_ROUTE_NONE;
+    int_routes.orient_int_route      = int_route;
+    int_routes.s_tap_int_route       = INT_ROUTE_NONE;
+    int_routes.d_tap_int_route       = INT_ROUTE_NONE;
+    int_routes.slow_no_mot_int_route = INT_ROUTE_NONE;
+    int_routes.slope_int_route       = INT_ROUTE_NONE;
+    int_routes.high_g_int_route      = int_route;
+    int_routes.low_g_int_route       = int_route;
+    int_routes.fifo_wmark_int_route  = INT_ROUTE_NONE;
+    int_routes.fifo_full_int_route   = INT_ROUTE_NONE;
+    int_routes.data_int_route        = int_route;
+
+    rc = bma2xx_set_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_filters.unfiltered_data_int        = cfg->use_unfiltered_data;
+    int_filters.unfiltered_tap_int         = cfg->use_unfiltered_data;
+    int_filters.unfiltered_slow_no_mot_int = cfg->use_unfiltered_data;
+    int_filters.unfiltered_slope_int       = cfg->use_unfiltered_data;
+    int_filters.unfiltered_high_g_int      = cfg->use_unfiltered_data;
+    int_filters.unfiltered_low_g_int       = cfg->use_unfiltered_data;
+
+    rc = bma2xx_set_int_filters(bma2xx, &int_filters);
+    if (rc != 0) {
+        return rc;
+    }
+
+#if MYNEWT_VAL(BMA2XX_INT_CFG_OUTPUT)
+    int_pin_electrical.pin1_output = INT_PIN_OUTPUT_OPEN_DRAIN;
+    int_pin_electrical.pin2_output = INT_PIN_OUTPUT_OPEN_DRAIN;
+#else
+    int_pin_electrical.pin1_output = INT_PIN_OUTPUT_PUSH_PULL;
+    int_pin_electrical.pin2_output = INT_PIN_OUTPUT_PUSH_PULL;
+#endif
+#if MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)
+    int_pin_electrical.pin1_active = INT_PIN_ACTIVE_HIGH;
+    int_pin_electrical.pin2_active = INT_PIN_ACTIVE_HIGH;
+#else
+    int_pin_electrical.pin1_active = INT_PIN_ACTIVE_LOW;
+    int_pin_electrical.pin2_active = INT_PIN_ACTIVE_LOW;
+#endif
+
+    rc = bma2xx_set_int_pin_electrical(bma2xx, &int_pin_electrical);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_int_latch(bma2xx, false, INT_LATCH_NON_LATCHED);
+    if (rc != 0) {
+        return rc;
+    }
+
+    low_g_int_cfg.delay_ms     = cfg->low_g_delay_ms;
+    low_g_int_cfg.thresh_g     = cfg->low_g_thresh_g;
+    low_g_int_cfg.hyster_g     = cfg->low_g_hyster_g;
+    low_g_int_cfg.axis_summing = false;
+
+    rc = bma2xx_set_low_g_int_cfg(bma2xx, &low_g_int_cfg);
+    if (rc != 0) {
+        return rc;
+    }
+
+    high_g_int_cfg.hyster_g = cfg->high_g_hyster_g;
+    high_g_int_cfg.delay_ms = cfg->high_g_delay_ms;
+    high_g_int_cfg.thresh_g = cfg->high_g_thresh_g;
+
+    rc = bma2xx_set_high_g_int_cfg(bma2xx, cfg->g_range, &high_g_int_cfg);
+    if (rc != 0) {
+        return rc;
+    }
+
+    tap_int_cfg.tap_quiet        = cfg->tap_quiet;
+    tap_int_cfg.tap_shock        = cfg->tap_shock;
+    tap_int_cfg.d_tap_window     = cfg->d_tap_window;
+    tap_int_cfg.tap_wake_samples = cfg->tap_wake_samples;
+    tap_int_cfg.thresh_g         = cfg->tap_thresh_g;
+
+    rc = bma2xx_set_tap_int_cfg(bma2xx, cfg->g_range, &tap_int_cfg);
+    if (rc != 0) {
+        return rc;
+    }
+
+    orient_int_cfg.hyster_g        = cfg->orient_hyster_g;
+    orient_int_cfg.orient_blocking = cfg->orient_blocking;
+    orient_int_cfg.orient_mode     = cfg->orient_mode;
+    orient_int_cfg.signal_up_dn    = cfg->orient_signal_ud;
+    orient_int_cfg.blocking_angle  = 0x08;
+
+    rc = bma2xx_set_orient_int_cfg(bma2xx, &orient_int_cfg);
+    if (rc != 0) {
+        return rc;
+    }
+
+#if MYNEWT_VAL(BMA2XX_I2C_WDT)
+    i2c_watchdog = I2C_WATCHDOG_50_MS;
+#else
+    i2c_watchdog = I2C_WATCHDOG_DISABLED;
+#endif
+
+    rc = bma2xx_set_i2c_watchdog(bma2xx, i2c_watchdog);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_X, cfg->offset_x_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Y, cfg->offset_y_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Z, cfg->offset_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    fifo_cfg.fifo_mode = FIFO_MODE_BYPASS;
+    fifo_cfg.fifo_data = FIFO_DATA_X_AND_Y_AND_Z;
+
+    rc = bma2xx_set_fifo_cfg(bma2xx, &fifo_cfg);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+change_power(struct bma2xx * bma2xx,
+             enum bma2xx_power_mode target)
+{
+    const struct bma2xx_cfg * cfg;
+    int rc;
+    bool step1_move;
+    bool step2_move;
+    enum bma2xx_power_mode step1_mode;
+    enum bma2xx_power_mode step2_mode;
+    struct power_settings power_settings;
+
+    cfg = &bma2xx->cfg;
+
+    if (bma2xx->power == BMA2XX_POWER_MODE_DEEP_SUSPEND) {
+        rc = reset_and_recfg(bma2xx);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    step1_move = false;
+    switch (bma2xx->power) {
+    case BMA2XX_POWER_MODE_SUSPEND:
+    case BMA2XX_POWER_MODE_LPM_1:
+        switch (target) {
+        case BMA2XX_POWER_MODE_STANDBY:
+        case BMA2XX_POWER_MODE_LPM_2:
+            step1_mode = BMA2XX_POWER_MODE_NORMAL;
+            step1_move = true;
+            break;
+        default:
+            break;
+        }
+        break;
+    case BMA2XX_POWER_MODE_STANDBY:
+    case BMA2XX_POWER_MODE_LPM_2:
+        switch (target) {
+        case BMA2XX_POWER_MODE_SUSPEND:
+        case BMA2XX_POWER_MODE_LPM_1:
+            step1_mode = BMA2XX_POWER_MODE_NORMAL;
+            step1_move = true;
+            break;
+        default:
+            break;
+        }
+        break;
+    default:
+        break;
+    }
+
+    if (bma2xx->power != target) {
+        step2_mode = target;
+        step2_move = true;
+    } else {
+        step2_move = false;
+    }
+
+    if (step1_move) {
+        power_settings.power_mode     = step1_mode;
+        power_settings.sleep_duration = cfg->sleep_duration;
+        power_settings.sleep_timer    = SLEEP_TIMER_EVENT_DRIVEN;
+
+        rc = bma2xx_set_power_settings(bma2xx, &power_settings);
+        if (rc != 0) {
+            return rc;
+        }
+
+        bma2xx->power = step1_mode;
+    }
+
+    if (step2_move) {
+        power_settings.power_mode     = step2_mode;
+        power_settings.sleep_duration = cfg->sleep_duration;
+        power_settings.sleep_timer    = SLEEP_TIMER_EVENT_DRIVEN;
+
+        rc = bma2xx_set_power_settings(bma2xx, &power_settings);
+        if (rc != 0) {
+            return rc;
+        }
+
+        bma2xx->power = step2_mode;
+    }
+
+    return 0;
+}
+
+static int
+interim_power(struct bma2xx * bma2xx,
+              const enum bma2xx_power_mode * reqs,
+              uint8_t size)
+{
+    uint8_t i;
+
+    if (size == 0) {
+        return SYS_EINVAL;
+    }
+
+    for (i = 0; i < size; i++) {
+        if (reqs[i] == bma2xx->power) {
+            return 0;
+        }
+    }
+
+    return change_power(bma2xx, reqs[0]);
+}
+
+static int
+default_power(struct bma2xx * bma2xx)
+{
+    const struct bma2xx_cfg * cfg;
+
+    cfg = &bma2xx->cfg;
+
+    if (cfg->power_mode == bma2xx->power) {
+        return 0;
+    }
+
+    return change_power(bma2xx, cfg->power_mode);
+}
+
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+static int
+init_intpin(struct bma2xx * bma2xx,
+              hal_gpio_irq_handler_t handler,
+              void * arg)
+{
+    struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
+    hal_gpio_irq_trig_t trig;
+    int pin = -1;
+    int rc;
+    int i;
+
+    for (i = 0; i < MYNEWT_VAL(SENSOR_MAX_INTERRUPTS_PINS); i++){
+        pin = bma2xx->sensor.s_itf.si_ints[i].host_pin;
+        if (pin > 0) {
+            break;
+        }
+    }
+
+    if (pin < 0) {
+        BMA2XX_ERROR("Interrupt pin not configured\n");
+        return SYS_EINVAL;
+    }
+
+    pdd->int_num = i;
+    if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].active) {
+        trig = HAL_GPIO_TRIG_RISING;
+    } else {
+        trig = HAL_GPIO_TRIG_FALLING;
+    }
+
+    if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].device_pin == 1) {
+        pdd->int_route = INT_ROUTE_PIN_1;
+    } else if (bma2xx->sensor.s_itf.si_ints[pdd->int_num].device_pin == 2) {
+        pdd->int_route = INT_ROUTE_PIN_2;
+    } else {
+        BMA2XX_ERROR("Route not configured\n");
+        return SYS_EINVAL;
+    }
+
+    rc = hal_gpio_irq_init(pin,
+                           handler,
+                           arg,
+                           trig,
+                           HAL_GPIO_PULL_NONE);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static void
+enable_intpin(struct bma2xx * bma2xx)
+{
+    struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
+    enum bma2xx_int_num int_num = pdd->int_num;
+
+    pdd->int_ref_cnt++;
+
+    if (pdd->int_ref_cnt == 1) {
+        hal_gpio_irq_enable(bma2xx->sensor.s_itf.si_ints[int_num].host_pin);
+    }
+}
+
+static void
+disable_intpin(struct bma2xx * bma2xx)
+{
+    struct bma2xx_private_driver_data *pdd = &bma2xx->pdd;
+    enum bma2xx_int_num int_num = pdd->int_num;
+
+    if (pdd->int_ref_cnt == 0) {
+        return;
+    }
+
+    pdd->int_ref_cnt--;
+
+    if (pdd->int_ref_cnt == 0) {
+        hal_gpio_irq_disable(bma2xx->sensor.s_itf.si_ints[int_num].host_pin);
+    }
+}
+#endif
+
+static int
+self_test_enable(const struct bma2xx * bma2xx,
+                 enum self_test_ampl ampl,
+                 enum self_test_sign sign,
+                 enum axis axis)
+{
+    struct self_test_cfg self_test_cfg;
+
+    self_test_cfg.self_test_ampl    = ampl;
+    self_test_cfg.self_test_sign    = sign;
+    self_test_cfg.self_test_axis    = axis;
+    self_test_cfg.self_test_enabled = true;
+
+    return bma2xx_set_self_test_cfg(bma2xx, &self_test_cfg);
+}
+
+static int
+self_test_disable(const struct bma2xx * bma2xx)
+{
+    struct self_test_cfg self_test_cfg;
+
+    self_test_cfg.self_test_ampl    = SELF_TEST_AMPL_LOW;
+    self_test_cfg.self_test_sign    = SELF_TEST_SIGN_NEGATIVE;
+    self_test_cfg.self_test_axis    = -1;
+    self_test_cfg.self_test_enabled = false;
+
+    return bma2xx_set_self_test_cfg(bma2xx, &self_test_cfg);
+}
+
+static int
+self_test_nudge(const struct bma2xx * bma2xx,
+                enum self_test_ampl ampl,
+                enum self_test_sign sign,
+                enum axis axis,
+                enum bma2xx_g_range g_range,
+                struct accel_data * accel_data)
+{
+    int rc;
+
+    rc = self_test_enable(bma2xx, ampl, sign, axis);
+    if (rc != 0) {
+        return rc;
+    }
+
+    delay_msec(50);
+
+    rc = bma2xx_get_accel(bma2xx, g_range, axis, accel_data);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = self_test_disable(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    delay_msec(50);
+
+    return 0;
+}
+
+static int
+self_test_axis(const struct bma2xx * bma2xx,
+               enum axis axis,
+               enum bma2xx_g_range g_range,
+               float * delta_hi_g,
+               float * delta_lo_g)
+{
+    struct accel_data accel_neg_hi;
+    struct accel_data accel_neg_lo;
+    struct accel_data accel_pos_hi;
+    struct accel_data accel_pos_lo;
+    int rc;
+
+    rc = self_test_nudge(bma2xx,
+                         SELF_TEST_AMPL_HIGH,
+                         SELF_TEST_SIGN_NEGATIVE,
+                         axis,
+                         g_range,
+                         &accel_neg_hi);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = self_test_nudge(bma2xx,
+                         SELF_TEST_AMPL_LOW,
+                         SELF_TEST_SIGN_NEGATIVE,
+                         axis,
+                         g_range,
+                         &accel_neg_lo);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = self_test_nudge(bma2xx,
+                         SELF_TEST_AMPL_HIGH,
+                         SELF_TEST_SIGN_POSITIVE,
+                         axis,
+                         g_range,
+                         &accel_pos_hi);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = self_test_nudge(bma2xx,
+                         SELF_TEST_AMPL_LOW,
+                         SELF_TEST_SIGN_POSITIVE,
+                         axis,
+                         g_range,
+                         &accel_pos_lo);
+    if (rc != 0) {
+        return rc;
+    }
+
+    *delta_hi_g = accel_pos_hi.accel_g - accel_neg_hi.accel_g;
+    *delta_lo_g = accel_pos_lo.accel_g - accel_neg_lo.accel_g;
+
+    return 0;
+}
+
+int
+bma2xx_self_test(struct bma2xx * bma2xx,
+                 float delta_high_mult,
+                 float delta_low_mult,
+                 bool * self_test_fail)
+{
+    const struct bma2xx_cfg * cfg;
+    enum bma2xx_power_mode request_power;
+    int rc;
+    float delta_hi_x_g;
+    float delta_lo_x_g;
+    float delta_hi_y_g;
+    float delta_lo_y_g;
+    float delta_hi_z_g;
+    float delta_lo_z_g;
+    bool fail;
+
+    cfg = &bma2xx->cfg;
+
+    request_power = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx, &request_power, 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_g_range(bma2xx, BMA2XX_G_RANGE_8);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = self_test_axis(bma2xx,
+                        AXIS_X,
+                        BMA2XX_G_RANGE_8,
+                        &delta_hi_x_g,
+                        &delta_lo_x_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = self_test_axis(bma2xx,
+                        AXIS_Y,
+                        BMA2XX_G_RANGE_8,
+                        &delta_hi_y_g,
+                        &delta_lo_y_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = self_test_axis(bma2xx,
+                        AXIS_Z,
+                        BMA2XX_G_RANGE_8,
+                        &delta_hi_z_g,
+                        &delta_lo_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = self_test_disable(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
+    if (rc != 0) {
+        return rc;
+    }
+
+    delay_msec(50);
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    fail = false;
+    if (delta_hi_x_g < delta_high_mult * 0.8) {
+        fail = true;
+    }
+    if (delta_lo_x_g < delta_low_mult * 0.8) {
+        fail = true;
+    }
+    if (delta_hi_y_g < delta_high_mult * 0.8) {
+        fail = true;
+    }
+    if (delta_lo_y_g < delta_low_mult * 0.8) {
+        fail = true;
+    }
+    if (delta_hi_z_g < delta_high_mult * 0.4) {
+        fail = true;
+    }
+    if (delta_lo_z_g < delta_low_mult * 0.4) {
+        fail = true;
+    }
+
+    *self_test_fail = fail;
+
+    return 0;
+}
+
+static int
+axis_offset_compensation(const struct bma2xx * bma2xx,
+                         enum axis axis,
+                         enum bma2xx_offset_comp_target target)
+{
+    int rc;
+    bool ready;
+    enum bma2xx_offset_comp_target target_z;
+    enum bma2xx_offset_comp_target target_y;
+    enum bma2xx_offset_comp_target target_x;
+    uint32_t count;
+
+    rc = bma2xx_get_fast_ofc_cfg(bma2xx,
+                                 &ready,
+                                 &target_z,
+                                 &target_y,
+                                 &target_x);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (!ready) {
+        BMA2XX_ERROR("offset compensation already in progress\n");
+        return SYS_ETIMEOUT;
+    }
+
+    rc = bma2xx_set_fast_ofc_cfg(bma2xx, axis, target, true);
+    if (rc != 0) {
+        return rc;
+    }
+
+    for (count = 1000; count != 0; count--) {
+        rc = bma2xx_get_fast_ofc_cfg(bma2xx,
+                                     &ready,
+                                     &target_z,
+                                     &target_y,
+                                     &target_x);
+        if (rc != 0) {
+            return rc;
+        }
+
+        if (ready) {
+            break;
+        }
+    }
+
+    if (count == 0) {
+        BMA2XX_ERROR("offset compensation did not complete\n");
+        return SYS_ETIMEOUT;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_offset_compensation(struct bma2xx * bma2xx,
+                           enum bma2xx_offset_comp_target target_x,
+                           enum bma2xx_offset_comp_target target_y,
+                           enum bma2xx_offset_comp_target target_z)
+{
+    const struct bma2xx_cfg * cfg;
+    enum bma2xx_power_mode request_power;
+    int rc;
+
+    cfg = &bma2xx->cfg;
+
+    request_power = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx, &request_power, 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_g_range(bma2xx, BMA2XX_G_RANGE_2);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = axis_offset_compensation(bma2xx, AXIS_X, target_x);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = axis_offset_compensation(bma2xx, AXIS_Y, target_y);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = axis_offset_compensation(bma2xx, AXIS_Z, target_z);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_X, &bma2xx->cfg.offset_x_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Y, &bma2xx->cfg.offset_y_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Z, &bma2xx->cfg.offset_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_g_range(bma2xx, cfg->g_range);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_query_offsets(struct bma2xx * bma2xx,
+                     float * offset_x_g,
+                     float * offset_y_g,
+                     float * offset_z_g)
+{
+    const struct bma2xx_cfg * cfg;
+    enum bma2xx_power_mode request_power[5];
+    int rc;
+    float val_offset_x_g;
+    float val_offset_y_g;
+    float val_offset_z_g;
+    bool mismatch;
+
+    cfg = &bma2xx->cfg;
+
+    request_power[0] = BMA2XX_POWER_MODE_SUSPEND;
+    request_power[1] = BMA2XX_POWER_MODE_STANDBY;
+    request_power[2] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[3] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[4] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_X, &val_offset_x_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Y, &val_offset_y_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_get_ofc_offset(bma2xx, AXIS_Z, &val_offset_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    mismatch = false;
+    if (cfg->offset_x_g != val_offset_x_g) {
+        BMA2XX_ERROR("X compensation offset value mismatch\n");
+        mismatch = true;
+    }
+    if (cfg->offset_y_g != val_offset_y_g) {
+        BMA2XX_ERROR("Y compensation offset value mismatch\n");
+        mismatch = true;
+    }
+    if (cfg->offset_z_g != val_offset_z_g) {
+        BMA2XX_ERROR("Z compensation offset value mismatch\n");
+        mismatch = true;
+    }
+
+    if (mismatch) {
+        return SYS_EINVAL;
+    }
+
+    *offset_x_g = val_offset_x_g;
+    *offset_y_g = val_offset_y_g;
+    *offset_z_g = val_offset_z_g;
+
+    return 0;
+}
+
+int
+bma2xx_write_offsets(struct bma2xx * bma2xx,
+                     float offset_x_g,
+                     float offset_y_g,
+                     float offset_z_g)
+{
+    struct bma2xx_cfg * cfg;
+    enum bma2xx_power_mode request_power[5];
+    int rc;
+
+    cfg = &bma2xx->cfg;
+
+    request_power[0] = BMA2XX_POWER_MODE_SUSPEND;
+    request_power[1] = BMA2XX_POWER_MODE_STANDBY;
+    request_power[2] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[3] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[4] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_X, offset_x_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Y, offset_y_g);
+    if (rc != 0) {
+        return rc;
+    }
+    rc = bma2xx_set_ofc_offset(bma2xx, AXIS_Z, offset_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    cfg->offset_x_g = offset_x_g;
+    cfg->offset_y_g = offset_y_g;
+    cfg->offset_z_g = offset_z_g;
+
+    return 0;
+}
+
+int
+bma2xx_stream_read(struct bma2xx * bma2xx,
+                   bma2xx_stream_read_func_t read_func,
+                   void * read_arg,
+                   uint32_t time_ms)
+{
+    const struct bma2xx_cfg * cfg;
+    int rc;
+    enum bma2xx_power_mode request_power;
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = { 0 };
+    os_time_t time_ticks;
+    os_time_t stop_ticks;
+    struct accel_data accel_data[AXIS_ALL];
+    struct sensor_accel_data sad;
+    struct bma2xx_private_driver_data *pdd;
+
+    cfg = &bma2xx->cfg;
+    pdd = &bma2xx->pdd;
+
+    stop_ticks = 0;
+
+    request_power = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx, &request_power, 1);
+    if (rc != 0) {
+        return rc;
+    }
+
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    undo_interrupt(&bma2xx->intr);
+
+    if (pdd->interrupt) {
+        return SYS_EBUSY;
+    }
+    pdd->interrupt = &bma2xx->intr;
+    enable_intpin(bma2xx);
+#endif
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    /* Leave tap configured as it is since it is on int2*/
+    int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
+    int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
+    int_enable.data_int_enable = true;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    if (time_ms != 0) {
+        rc = os_time_ms_to_ticks(time_ms, &time_ticks);
+        if (rc != 0) {
+            goto done;
+        }
+        stop_ticks = os_time_get() + time_ticks;
+    }
+
+    for (;;) {
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+        wait_interrupt(&bma2xx->intr, pdd->int_num);
+#else
+        switch (cfg->filter_bandwidth) {
+        case BMA2XX_FILTER_BANDWIDTH_7_81_HZ:
+            delay_msec(128);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_15_63_HZ:
+            delay_msec(64);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_31_25_HZ:
+            delay_msec(32);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_62_5_HZ:
+            delay_msec(16);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_125_HZ:
+            delay_msec(8);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_250_HZ:
+            delay_msec(4);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_500_HZ:
+            delay_msec(2);
+            break;
+        case BMA2XX_FILTER_BANDWIDTH_1000_HZ:
+        case BMA2XX_FILTER_BANDWIDTH_ODR_MAX:
+            delay_msec(1);
+            break;
+        default:
+            delay_msec(1000);
+            break;
+        }
+#endif
+
+        rc = bma2xx_get_fifo(bma2xx,
+                             cfg->g_range,
+                             FIFO_DATA_X_AND_Y_AND_Z,
+                             accel_data);
+        if (rc != 0) {
+            goto done;
+        }
+
+        sad.sad_x = accel_data[AXIS_X].accel_g;
+        sad.sad_y = accel_data[AXIS_Y].accel_g;
+        sad.sad_z = accel_data[AXIS_Z].accel_g;
+        sad.sad_x_is_valid = 1;
+        sad.sad_y_is_valid = 1;
+        sad.sad_z_is_valid = 1;
+
+        if (read_func(read_arg, &sad)) {
+            break;
+        }
+
+        if (time_ms != 0 && OS_TIME_TICK_GT(os_time_get(), stop_ticks)) {
+                break;
+        }
+    }
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        goto done;
+    }
+
+done:
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    pdd->interrupt = NULL;
+    disable_intpin(bma2xx);
+#endif
+
+    return rc;
+}
+
+int
+bma2xx_current_temp(struct bma2xx * bma2xx,
+                    float * temp_c)
+{
+    enum bma2xx_power_mode request_power[3];
+    int rc;
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_temp(bma2xx, temp_c);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_current_orient(struct bma2xx * bma2xx,
+                      struct bma2xx_orient_xyz * orient_xyz)
+{
+    enum bma2xx_power_mode request_power[3];
+    int rc;
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = { 0 };
+    struct int_status int_status;
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+   /* Leave tap configured as it is since it is on int2*/
+    int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
+    int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
+
+    int_enable.orient_int_enable = true;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_int_status(bma2xx, &int_status);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Back to original interrupts */
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    orient_xyz->orient_xy  = int_status.device_orientation;
+    orient_xyz->downward_z = int_status.device_is_down;
+
+    return 0;
+}
+
+int
+bma2xx_wait_for_orient(struct bma2xx * bma2xx,
+                       struct bma2xx_orient_xyz * orient_xyz)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    int rc;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = {0};
+    struct int_status int_status;
+    struct bma2xx_private_driver_data *pdd;
+
+    pdd = &bma2xx->pdd;
+
+    if (pdd->interrupt) {
+        BMA2XX_ERROR("Interrupt used\n");
+        return SYS_EINVAL;
+    }
+
+    pdd->interrupt = &bma2xx->intr;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    undo_interrupt(&bma2xx->intr);
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+   /* Leave tap configured as it is since it is on int2*/
+    int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
+    int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
+    int_enable.orient_int_enable = true;
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    wait_interrupt(&bma2xx->intr, pdd->int_num);
+
+    rc = bma2xx_get_int_status(bma2xx, &int_status);
+    if (rc != 0) {
+        goto done;
+    }
+
+    /* Back to original interrupts */
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        goto done;
+    }
+
+    orient_xyz->orient_xy  = int_status.device_orientation;
+    orient_xyz->downward_z = int_status.device_is_down;
+
+done:
+    pdd->interrupt = NULL;
+    disable_intpin(bma2xx);
+    return rc;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+int
+bma2xx_wait_for_high_g(struct bma2xx * bma2xx)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    int rc;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = { 0 };
+    struct bma2xx_private_driver_data *pdd;
+
+    pdd = &bma2xx->pdd;
+
+    if (pdd->interrupt) {
+        BMA2XX_ERROR("Interrupt used\n");
+        return SYS_EINVAL;
+    }
+
+    pdd->interrupt = &bma2xx->intr;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    undo_interrupt(&bma2xx->intr);
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+   /* Leave tap configured as it is since it is on int2*/
+    int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
+    int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
+
+    int_enable.high_g_z_int_enable = true;
+    int_enable.high_g_y_int_enable = true;
+    int_enable.high_g_x_int_enable = true;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    wait_interrupt(&bma2xx->intr, pdd->int_num);
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        goto done;
+    }
+
+done:
+    pdd->interrupt = NULL;
+    disable_intpin(bma2xx);
+    return rc;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+int
+bma2xx_wait_for_low_g(struct bma2xx * bma2xx)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    int rc;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = { 0 };
+    struct bma2xx_private_driver_data *pdd;
+
+    pdd = &bma2xx->pdd;
+
+    if (pdd->interrupt) {
+        BMA2XX_ERROR("Interrupt used\n");
+        return SYS_EINVAL;
+    }
+
+    pdd->interrupt = &bma2xx->intr;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    undo_interrupt(&bma2xx->intr);
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+   /* Leave tap configured as it is since it is on int2 */
+    int_enable.s_tap_int_enable = int_enable_org.s_tap_int_enable;
+    int_enable.d_tap_int_enable = int_enable_org.d_tap_int_enable;
+
+    int_enable.low_g_int_enable         = true;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    wait_interrupt(&bma2xx->intr, pdd->int_num);
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        goto done;
+    }
+
+done:
+    pdd->interrupt = NULL;
+    disable_intpin(bma2xx);
+    return 0;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+int
+bma2xx_wait_for_tap(struct bma2xx * bma2xx,
+                    enum bma2xx_tap_type tap_type)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    int rc = 0;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable_org;
+    struct int_enable int_enable = { 0 };
+    struct int_routes int_routes;
+    struct int_routes int_routes_org;
+    struct bma2xx_private_driver_data *pdd;
+
+    pdd = &bma2xx->pdd;
+
+    switch (tap_type) {
+    case BMA2XX_TAP_TYPE_DOUBLE:
+    case BMA2XX_TAP_TYPE_SINGLE:
+        break;
+    default:
+        return SYS_EINVAL;
+    }
+
+    rc = bma2xx_get_int_routes(bma2xx, &int_routes_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_routes = int_routes_org;
+    if (tap_type == BMA2XX_TAP_TYPE_DOUBLE) {
+        /* According to BMA2XX when single tap shall not be used we should not
+         * route it to any INTX
+         */
+        int_routes.d_tap_int_route = pdd->int_route;
+        int_routes.s_tap_int_route = INT_ROUTE_NONE;
+    } else {
+        int_routes.d_tap_int_route = INT_ROUTE_NONE;
+        int_routes.s_tap_int_route = pdd->int_route;
+    }
+
+    rc = bma2xx_set_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (pdd->interrupt) {
+        BMA2XX_ERROR("Interrupt used\n");
+        return SYS_EINVAL;
+    }
+
+    pdd->interrupt = &bma2xx->intr;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    undo_interrupt(&bma2xx->intr);
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_enable.s_tap_int_enable         = tap_type == BMA2XX_TAP_TYPE_SINGLE;
+    int_enable.d_tap_int_enable         = tap_type == BMA2XX_TAP_TYPE_DOUBLE;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    wait_interrupt(&bma2xx->intr, pdd->int_num);
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable_org);
+    if (rc != 0) {
+        goto done;
+    }
+
+    rc = default_power(bma2xx);
+
+done:
+    pdd->interrupt = NULL;
+    disable_intpin(bma2xx);
+    /* Restore previous routing */
+    rc = bma2xx_set_int_routes(bma2xx, &int_routes_org);
+
+    return rc;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+int
+bma2xx_power_settings(struct bma2xx * bma2xx,
+                      enum bma2xx_power_mode power_mode,
+                      enum bma2xx_sleep_duration sleep_duration)
+{
+    struct bma2xx_cfg * cfg;
+
+    cfg = &bma2xx->cfg;
+
+    cfg->power_mode     = power_mode;
+    cfg->sleep_duration = sleep_duration;
+
+    return default_power(bma2xx);
+}
+
+static int
+sensor_driver_read(struct sensor * sensor,
+                   sensor_type_t sensor_type,
+                   sensor_data_func_t data_func,
+                   void * data_arg,
+                   uint32_t timeout)
+{
+    struct bma2xx * bma2xx;
+    const struct bma2xx_cfg * cfg;
+    enum bma2xx_power_mode request_power[3];
+    int rc;
+    struct accel_data accel_data[AXIS_ALL];
+    struct sensor_accel_data sad;
+    float temp_c;
+    struct sensor_temp_data std;
+
+    if ((sensor_type & ~(SENSOR_TYPE_ACCELEROMETER |
+                         SENSOR_TYPE_AMBIENT_TEMPERATURE)) != 0) {
+        return SYS_EINVAL;
+    }
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+    cfg = &bma2xx->cfg;
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    if ((sensor_type & SENSOR_TYPE_ACCELEROMETER) != 0) {
+        rc = bma2xx_get_accel(bma2xx,
+                              cfg->g_range,
+                              AXIS_X,
+                              accel_data + AXIS_X);
+        if (rc != 0) {
+            return rc;
+        }
+        rc = bma2xx_get_accel(bma2xx,
+                              cfg->g_range,
+                              AXIS_Y,
+                              accel_data + AXIS_Y);
+        if (rc != 0) {
+            return rc;
+        }
+        rc = bma2xx_get_accel(bma2xx,
+                              cfg->g_range,
+                              AXIS_Z,
+                              accel_data + AXIS_Z);
+        if (rc != 0) {
+            return rc;
+        }
+
+        sad.sad_x = accel_data[AXIS_X].accel_g;
+        sad.sad_y = accel_data[AXIS_Y].accel_g;
+        sad.sad_z = accel_data[AXIS_Z].accel_g;
+        sad.sad_x_is_valid = 1;
+        sad.sad_y_is_valid = 1;
+        sad.sad_z_is_valid = 1;
+
+        rc = data_func(sensor,
+                       data_arg,
+                       &sad,
+                       SENSOR_TYPE_ACCELEROMETER);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    if ((sensor_type & SENSOR_TYPE_AMBIENT_TEMPERATURE) != 0) {
+        rc = bma2xx_get_temp(bma2xx, &temp_c);
+        if (rc != 0) {
+            return rc;
+        }
+
+        std.std_temp = temp_c;
+        std.std_temp_is_valid = 1;
+
+        rc = data_func(sensor,
+                       data_arg,
+                       &std,
+                       SENSOR_TYPE_AMBIENT_TEMPERATURE);
+        if (rc != 0) {
+            return rc;
+        }
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+static int
+sensor_driver_get_config(struct sensor * sensor,
+                         sensor_type_t sensor_type,
+                         struct sensor_cfg * cfg)
+{
+    if ((sensor_type & ~(SENSOR_TYPE_ACCELEROMETER |
+                         SENSOR_TYPE_AMBIENT_TEMPERATURE)) != 0) {
+        return SYS_EINVAL;
+    }
+    if ((sensor_type & (sensor_type - 1)) != 0) {
+        return SYS_EINVAL;
+    }
+
+    if ((sensor_type & SENSOR_TYPE_ACCELEROMETER) != 0) {
+        cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT_TRIPLET;
+    }
+    if ((sensor_type & SENSOR_TYPE_AMBIENT_TEMPERATURE) != 0) {
+        cfg->sc_valtype = SENSOR_VALUE_TYPE_FLOAT;
+    }
+
+    return 0;
+}
+
+static int
+sensor_driver_set_trigger_thresh(struct sensor * sensor,
+                                 sensor_type_t sensor_type,
+                                 struct sensor_type_traits * stt)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    struct bma2xx * bma2xx;
+    const struct bma2xx_cfg * cfg;
+    int rc;
+    enum bma2xx_power_mode request_power[3];
+    const struct sensor_accel_data * low_thresh;
+    const struct sensor_accel_data * high_thresh;
+    struct int_enable int_enable;
+    float thresh;
+    struct low_g_int_cfg low_g_int_cfg;
+    struct high_g_int_cfg high_g_int_cfg;
+    struct bma2xx_private_driver_data *pdd;
+
+    if (sensor_type != SENSOR_TYPE_ACCELEROMETER) {
+        return SYS_EINVAL;
+    }
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+    cfg = &bma2xx->cfg;
+    pdd = &bma2xx->pdd;
+
+    pdd->read_ctx.srec_type |= sensor_type;
+    pdd->registered_mask |= BMA2XX_READ_MASK;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    low_thresh  = stt->stt_low_thresh.sad;
+    high_thresh = stt->stt_high_thresh.sad;
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    if (low_thresh->sad_x_is_valid |
+        low_thresh->sad_y_is_valid |
+        low_thresh->sad_z_is_valid) {
+        thresh = INFINITY;
+
+        if (low_thresh->sad_x_is_valid) {
+            if (thresh > low_thresh->sad_x) {
+                thresh = low_thresh->sad_x;
+            }
+        }
+        if (low_thresh->sad_y_is_valid) {
+            if (thresh > low_thresh->sad_y) {
+                thresh = low_thresh->sad_y;
+            }
+        }
+        if (low_thresh->sad_z_is_valid) {
+            if (thresh > low_thresh->sad_z) {
+                thresh = low_thresh->sad_z;
+            }
+        }
+
+        low_g_int_cfg.delay_ms     = 20;
+        low_g_int_cfg.thresh_g     = thresh;
+        low_g_int_cfg.hyster_g     = 0.125;
+        low_g_int_cfg.axis_summing = false;
+
+        rc = bma2xx_set_low_g_int_cfg(bma2xx,
+                                      &low_g_int_cfg);
+        if (rc != 0) {
+            goto done;
+        }
+
+        int_enable.low_g_int_enable = true;
+    }
+
+    if (high_thresh->sad_x_is_valid |
+        high_thresh->sad_y_is_valid |
+        high_thresh->sad_z_is_valid) {
+        thresh = 0.0;
+
+        if (high_thresh->sad_x_is_valid) {
+            if (thresh < high_thresh->sad_x) {
+                thresh = high_thresh->sad_x;
+            }
+        }
+        if (high_thresh->sad_y_is_valid) {
+            if (thresh < high_thresh->sad_y) {
+                thresh = high_thresh->sad_y;
+            }
+        }
+        if (high_thresh->sad_z_is_valid) {
+            if (thresh < high_thresh->sad_z) {
+                thresh = high_thresh->sad_z;
+            }
+        }
+
+        high_g_int_cfg.hyster_g = 0.25;
+        high_g_int_cfg.delay_ms = 32;
+        high_g_int_cfg.thresh_g = thresh;
+
+        rc = bma2xx_set_high_g_int_cfg(bma2xx,
+                                       cfg->g_range,
+                                       &high_g_int_cfg);
+        if (rc != 0) {
+            goto done;
+        }
+
+        int_enable.high_g_z_int_enable = high_thresh->sad_z_is_valid;
+        int_enable.high_g_y_int_enable = high_thresh->sad_y_is_valid;
+        int_enable.high_g_x_int_enable = high_thresh->sad_x_is_valid;
+    }
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+
+done:
+    if (rc != 0) {
+        /* Something went wrong, unregister from interrupt */
+        pdd->read_ctx.srec_type &= ~sensor_type;
+        pdd->registered_mask &= ~BMA2XX_READ_MASK;
+        disable_intpin(bma2xx);
+    }
+
+    return rc;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+static int
+sensor_driver_unset_notification(struct sensor * sensor,
+                               sensor_event_type_t sensor_event_type)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    struct bma2xx * bma2xx;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable;
+    struct int_routes int_routes;
+    struct bma2xx_private_driver_data *pdd;
+    int rc;
+
+    if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
+                               SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
+        return SYS_EINVAL;
+    }
+
+    /*XXX for now we do not support registering for both events */
+    if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
+                                        SENSOR_EVENT_TYPE_SINGLE_TAP)) {
+        return SYS_EINVAL;
+    }
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+    pdd = &bma2xx->pdd;
+
+    pdd->notify_ctx.snec_evtype &= ~sensor_event_type;
+    pdd->registered_mask &= ~BMA2XX_NOTIFY_MASK;
+    disable_intpin(bma2xx);
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Clear route and interrupts. We can do it for single and double as driver
+     * supports notification only for one of them at the time
+     */
+    rc = bma2xx_get_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (sensor_event_type & SENSOR_EVENT_TYPE_SINGLE_TAP) {
+        int_routes.s_tap_int_route = INT_ROUTE_NONE;
+    }
+
+    if (sensor_event_type & SENSOR_EVENT_TYPE_DOUBLE_TAP) {
+        int_routes.d_tap_int_route = INT_ROUTE_NONE;
+    }
+
+    rc = bma2xx_set_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        return rc;
+    }
+
+    int_enable.d_tap_int_enable = false;
+    int_enable.s_tap_int_enable = false;
+
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+static int
+sensor_driver_set_notification(struct sensor * sensor,
+                               sensor_event_type_t sensor_event_type)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    struct bma2xx * bma2xx;
+    int rc;
+    enum bma2xx_power_mode request_power[3];
+    struct int_enable int_enable;
+    struct int_routes int_routes;
+    struct bma2xx_private_driver_data *pdd;
+
+    if ((sensor_event_type & ~(SENSOR_EVENT_TYPE_DOUBLE_TAP |
+                               SENSOR_EVENT_TYPE_SINGLE_TAP)) != 0) {
+        return SYS_EINVAL;
+    }
+
+    /*XXX for now we do not support registering for both events */
+    if (sensor_event_type == (SENSOR_EVENT_TYPE_DOUBLE_TAP |
+                                        SENSOR_EVENT_TYPE_SINGLE_TAP)) {
+        return SYS_EINVAL;
+    }
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+    pdd = &bma2xx->pdd;
+
+    if (pdd->registered_mask & BMA2XX_NOTIFY_MASK) {
+        return SYS_EBUSY;
+    }
+
+    pdd->notify_ctx.snec_evtype |= sensor_event_type;
+    pdd->registered_mask |= BMA2XX_NOTIFY_MASK;
+    enable_intpin(bma2xx);
+
+    request_power[0] = BMA2XX_POWER_MODE_LPM_1;
+    request_power[1] = BMA2XX_POWER_MODE_LPM_2;
+    request_power[2] = BMA2XX_POWER_MODE_NORMAL;
+
+    rc = interim_power(bma2xx,
+                       request_power,
+                       sizeof(request_power) / sizeof(*request_power));
+    if (rc != 0) {
+        goto done;
+    }
+
+    /* Configure route */
+    rc = bma2xx_get_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (sensor_event_type & SENSOR_EVENT_TYPE_DOUBLE_TAP) {
+        int_routes.d_tap_int_route = pdd->int_route;
+    }
+
+    if (sensor_event_type & SENSOR_EVENT_TYPE_SINGLE_TAP) {
+        int_routes.s_tap_int_route = pdd->int_route;
+    }
+
+    rc = bma2xx_set_int_routes(bma2xx, &int_routes);
+    if (rc != 0) {
+        return rc;
+    }
+
+    /* Configure enable event*/
+    rc = bma2xx_get_int_enable(bma2xx, &int_enable);
+    if (rc != 0) {
+        goto done;
+    }
+
+    /* Enable tap event*/
+    int_enable.s_tap_int_enable         = sensor_event_type &
+                                          SENSOR_EVENT_TYPE_SINGLE_TAP;
+    int_enable.d_tap_int_enable         = sensor_event_type &
+                                          SENSOR_EVENT_TYPE_DOUBLE_TAP;
+    rc = bma2xx_set_int_enable(bma2xx, &int_enable);
+
+done:
+    if (rc != 0) {
+        pdd->notify_ctx.snec_evtype &= ~sensor_event_type;
+        pdd->registered_mask &= ~BMA2XX_NOTIFY_MASK;
+        disable_intpin(bma2xx);
+    }
+
+    return rc;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+static int
+sensor_driver_handle_interrupt(struct sensor * sensor)
+{
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    struct bma2xx * bma2xx;
+    struct bma2xx_private_driver_data *pdd;
+    struct int_status int_status;
+    int rc;
+
+    bma2xx = (struct bma2xx *)SENSOR_GET_DEVICE(sensor);
+    pdd = &bma2xx->pdd;
+
+    rc = bma2xx_get_int_status(bma2xx, &int_status);
+    if (rc != 0) {
+        BMA2XX_ERROR("Cound not read int status err=0x%02x\n", rc);
+        return rc;
+    }
+
+    if ((pdd->registered_mask & BMA2XX_NOTIFY_MASK) &&
+            (int_status.s_tap_int_active || int_status.d_tap_int_active)) {
+        sensor_mgr_put_notify_evt(&pdd->notify_ctx);
+    }
+
+    if ((pdd->registered_mask & BMA2XX_READ_MASK) &&
+            (int_status.high_g_int_active || int_status.low_g_int_active)) {
+        sensor_mgr_put_read_evt(&pdd->read_ctx);
+    }
+
+    return 0;
+#else
+    return SYS_ENODEV;
+#endif
+}
+
+static struct sensor_driver bma2xx_sensor_driver = {
+    .sd_read               = sensor_driver_read,
+    .sd_get_config         = sensor_driver_get_config,
+    .sd_set_trigger_thresh = sensor_driver_set_trigger_thresh,
+    .sd_set_notification   = sensor_driver_set_notification,
+    .sd_unset_notification = sensor_driver_unset_notification,
+    .sd_handle_interrupt   = sensor_driver_handle_interrupt,
+};
+
+int
+bma2xx_config(struct bma2xx * bma2xx, struct bma2xx_cfg * cfg)
+{
+    struct sensor * sensor;
+    int rc;
+    uint8_t chip_id;
+    uint8_t model_chip_id;
+
+    bma2xx->cfg = *cfg;
+
+    sensor = &bma2xx->sensor;
+
+    rc = bma2xx_get_chip_id(bma2xx, &chip_id);
+    if (rc != 0) {
+        return rc;
+    }
+    switch (cfg->model) {
+        case BMA2XX_BMA280:
+            model_chip_id = BMA280_REG_VALUE_CHIP_ID;
+            break;
+        case BMA2XX_BMA253:
+            model_chip_id = BMA253_REG_VALUE_CHIP_ID;
+            break;
+        default:
+            return SYS_EINVAL;
+    }
+
+    if (chip_id != model_chip_id) {
+        BMA2XX_ERROR("received incorrect chip ID 0x%02X\n", chip_id);
+        return SYS_EINVAL;
+    }
+
+    rc = reset_and_recfg(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = default_power(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = sensor_set_type_mask(sensor, cfg->sensor_mask);
+    if (rc != 0) {
+        return rc;
+    }
+
+    return 0;
+}
+
+int
+bma2xx_init(struct os_dev * dev, void * arg)
+{
+    struct bma2xx * bma2xx;
+    struct sensor * sensor;
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    struct bma2xx_private_driver_data *pdd;
+#endif
+    int rc;
+
+    if (!dev || !arg) {
+        return SYS_ENODEV;
+    }
+
+#if MYNEWT_VAL(BMA2XX_LOG)
+    rc = log_register(dev->od_name,
+                      &bma2xx_log,
+                      &log_console_handler,
+                      NULL,
+                      LOG_SYSLEVEL);
+    if (rc != 0) {
+        return rc;
+    }
+#endif
+
+    bma2xx = (struct bma2xx *)dev;
+    sensor = &bma2xx->sensor;
+
+    rc = sensor_init(sensor, dev);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = sensor_set_driver(sensor,
+                           SENSOR_TYPE_ACCELEROMETER |
+                           SENSOR_TYPE_AMBIENT_TEMPERATURE,
+                           &bma2xx_sensor_driver);
+    if (rc != 0) {
+        return rc;
+    }
+
+    rc = sensor_set_interface(sensor, arg);
+    if (rc != 0) {
+        return rc;
+    }
+
+    sensor->s_next_run = OS_TIMEOUT_NEVER;
+
+    rc = sensor_mgr_register(sensor);
+    if (rc != 0) {
+        return rc;
+    }
+
+#if MYNEWT_VAL(SPI_0_MASTER) || MYNEWT_VAL(SPI_1_MASTER)
+    rc = hal_spi_config(sensor->s_itf.si_num, &spi_bma2xx_settings);
+	if (rc == EINVAL) {
+		/* If spi is already enabled, for nrf52, it returns -1, We should not
+			* fail if the spi is already enabled
+			*/
+		return rc;
+	}
+
+	rc = hal_spi_enable(sensor->s_itf.si_num);
+	if (rc) {
+		return rc;
+	}
+
+	rc = hal_gpio_init_out(sensor->s_itf.si_cs_pin, 1);
+	if (rc) {
+		return rc;
+	}
+#endif
+
+#if MYNEWT_VAL(BMA2XX_INT_ENABLE)
+    init_interrupt(&bma2xx->intr, bma2xx->sensor.s_itf.si_ints);
+
+    pdd = &bma2xx->pdd;
+
+    pdd->read_ctx.srec_sensor = sensor;
+    pdd->notify_ctx.snec_sensor = sensor;
+
+    rc = init_intpin(bma2xx, interrupt_handler, sensor);
+    if (rc != 0) {
+        return rc;
+    }
+#endif
+
+    bma2xx->power = BMA2XX_POWER_MODE_NORMAL;
+
+    return 0;
+}
diff --git a/hw/drivers/sensors/bma2xx/src/bma2xx_priv.h b/hw/drivers/sensors/bma2xx/src/bma2xx_priv.h
new file mode 100644
index 0000000..3910c96
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/src/bma2xx_priv.h
@@ -0,0 +1,670 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef __BMA2XX_PRIV_H__
+#define __BMA2XX_PRIV_H__
+
+#include "bma2xx/bma2xx.h"
+
+#ifdef __cplusplus
+#extern "C" {
+#endif
+
+/*
+ * Full register map:
+ */
+
+#define BMA2XX_SPI_READ_CMD_BIT 0x80
+
+#define REG_ADDR_BGW_CHIPID    0x00    /*    r            */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_ACCD_X_LSB    0x02    /*    r            */
+#define REG_ADDR_ACCD_X_MSB    0x03    /*    r            */
+#define REG_ADDR_ACCD_Y_LSB    0x04    /*    r            */
+#define REG_ADDR_ACCD_Y_MSB    0x05    /*    r            */
+#define REG_ADDR_ACCD_Z_LSB    0x06    /*    r            */
+#define REG_ADDR_ACCD_Z_MSB    0x07    /*    r            */
+#define REG_ADDR_ACCD_TEMP     0x08    /*    r            */
+#define REG_ADDR_INT_STATUS_0  0x09    /*    r            */
+#define REG_ADDR_INT_STATUS_1  0x0A    /*    r            */
+#define REG_ADDR_INT_STATUS_2  0x0B    /*    r            */
+#define REG_ADDR_INT_STATUS_3  0x0C    /*    r            */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_FIFO_STATUS   0x0E    /*    r            */
+#define REG_ADDR_PMU_RANGE     0x0F    /*    rw           */
+#define REG_ADDR_PMU_BW        0x10    /*    rw           */
+#define REG_ADDR_PMU_LPW       0x11    /*    rw           */
+#define REG_ADDR_PMU_LOW_POWER 0x12    /*    rw           */
+#define REG_ADDR_ACCD_HBW      0x13    /*    rw           */
+#define REG_ADDR_BGW_SOFTRESET 0x14    /*     w           */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_INT_EN_0      0x16    /*    rw           */
+#define REG_ADDR_INT_EN_1      0x17    /*    rw           */
+#define REG_ADDR_INT_EN_2      0x18    /*    rw           */
+#define REG_ADDR_INT_MAP_0     0x19    /*    rw           */
+#define REG_ADDR_INT_MAP_1     0x1A    /*    rw           */
+#define REG_ADDR_INT_MAP_2     0x1B    /*    rw           */
+/* RESERVED */                         /*                 */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_INT_SRC       0x1E    /*    rw           */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_INT_OUT_CTRL  0x20    /*    rw           */
+#define REG_ADDR_INT_RST_LATCH 0x21    /*    rw           */
+#define REG_ADDR_INT_0         0x22    /*    rw           */
+#define REG_ADDR_INT_1         0x23    /*    rw           */
+#define REG_ADDR_INT_2         0x24    /*    rw           */
+#define REG_ADDR_INT_3         0x25    /*    rw           */
+#define REG_ADDR_INT_4         0x26    /*    rw           */
+#define REG_ADDR_INT_5         0x27    /*    rw           */
+#define REG_ADDR_INT_6         0x28    /*    rw           */
+#define REG_ADDR_INT_7         0x29    /*    rw           */
+#define REG_ADDR_INT_8         0x2A    /*    rw           */
+#define REG_ADDR_INT_9         0x2B    /*    rw           */
+#define REG_ADDR_INT_A         0x2C    /*    rw           */
+#define REG_ADDR_INT_B         0x2D    /*    rw           */
+#define REG_ADDR_INT_C         0x2E    /*    rw           */
+#define REG_ADDR_INT_D         0x2F    /*    rw           */
+#define REG_ADDR_FIFO_CONFIG_0 0x30    /*    rw           */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_PMU_SELF_TEST 0x32    /*    rw           */
+#define REG_ADDR_TRIM_NVM_CTRL 0x33    /*    rw           */
+#define REG_ADDR_BGW_SPI3_WDT  0x34    /*    rw           */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_OFC_CTRL      0x36    /*    rw           */
+#define REG_ADDR_OFC_SETTING   0x37    /*    rw           */
+#define REG_ADDR_OFC_OFFSET_X  0x38    /*    rw    nvm    */
+#define REG_ADDR_OFC_OFFSET_Y  0x39    /*    rw    nvm    */
+#define REG_ADDR_OFC_OFFSET_Z  0x3A    /*    rw    nvm    */
+#define REG_ADDR_TRIM_GP0      0x3B    /*    rw    nvm    */
+#define REG_ADDR_TRIM_GP1      0x3C    /*    rw    nvm    */
+/* RESERVED */                         /*                 */
+#define REG_ADDR_FIFO_CONFIG_1 0x3E    /*    rw           */
+#define REG_ADDR_FIFO_DATA     0x3F    /*    r            */
+
+/* BMA253, BMA280 unique settings */
+#define BMA253_REG_VALUE_CHIP_ID    0xFA
+#define BMA253_G_SCALE_2     (0.00098)
+#define BMA253_G_SCALE_4     (0.00195)
+#define BMA253_G_SCALE_8     (0.00391)
+#define BMA253_G_SCALE_16     (0.00781)
+#define BMA253_ACCEL_BIT_SHIFT 4
+
+#define BMA280_REG_VALUE_CHIP_ID    0xFB
+#define BMA280_G_SCALE_2     (0.000244)
+#define BMA280_G_SCALE_4     (0.000488)
+#define BMA280_G_SCALE_8     (0.000977)
+#define BMA280_G_SCALE_16     (0.001953)
+#define BMA280_ACCEL_BIT_SHIFT 2
+/* Magical value that is used to initiate a full reset */
+#define REG_VALUE_SOFT_RESET 0xB6
+
+/* Get the chip ID */
+int
+bma2xx_get_chip_id(const struct bma2xx * bma2xx,
+                   uint8_t * chip_id);
+
+/* All three axis types */
+enum axis {
+    AXIS_X   = 0,
+    AXIS_Y   = 1,
+    AXIS_Z   = 2,
+    AXIS_ALL = 3,
+};
+
+/* A single accelerometer measurement for one axis */
+struct accel_data {
+    float accel_g;
+    bool new_data;
+};
+
+/* Get an accelerometer measurement for a single axis */
+int
+bma2xx_get_accel(const struct bma2xx * bma2xx,
+                 enum bma2xx_g_range g_range,
+                 enum axis axis,
+                 struct accel_data * accel_data);
+
+/* Get a temperature measurement */
+int
+bma2xx_get_temp(const struct bma2xx * bma2xx,
+                float * temp_c);
+
+/* Which direction in an axis was this interrupt triggered on */
+enum axis_trigger_sign {
+    AXIS_TRIGGER_SIGN_POS = 0,
+    AXIS_TRIGGER_SIGN_NEG = 1,
+};
+
+/* Which axis was this interrupt triggered on */
+struct axis_trigger {
+    enum axis_trigger_sign sign;
+    enum axis axis;
+    bool axis_known;
+};
+
+/* Active status of all interrupts */
+struct int_status {
+    bool flat_int_active;
+    bool orient_int_active;
+    bool s_tap_int_active;
+    bool d_tap_int_active;
+    bool slow_no_mot_int_active;
+    bool slope_int_active;
+    bool high_g_int_active;
+    bool low_g_int_active;
+    bool data_int_active;
+    bool fifo_wmark_int_active;
+    bool fifo_full_int_active;
+    struct axis_trigger tap_trigger;
+    struct axis_trigger slope_trigger;
+    bool device_is_flat;
+    bool device_is_down;
+    enum bma2xx_orient_xy device_orientation;
+    struct axis_trigger high_g_trigger;
+};
+
+/* Get the active status of all interrupts */
+int
+bma2xx_get_int_status(const struct bma2xx * bma2xx,
+                      struct int_status * int_status);
+
+/* Get the status and size of the FIFO */
+int
+bma2xx_get_fifo_status(const struct bma2xx * bma2xx,
+                       bool * overrun,
+                       uint8_t * frame_counter);
+
+/* Get/Set the accelerometer range */
+int
+bma2xx_get_g_range(const struct bma2xx * bma2xx,
+                   enum bma2xx_g_range * g_range);
+int
+bma2xx_set_g_range(const struct bma2xx * bma2xx,
+                   enum bma2xx_g_range g_range);
+
+/* Get/Set the filter output bandwidth */
+int
+bma2xx_get_filter_bandwidth(const struct bma2xx * bma2xx,
+                            enum bma2xx_filter_bandwidth * filter_bandwidth);
+int
+bma2xx_set_filter_bandwidth(const struct bma2xx * bma2xx,
+                            enum bma2xx_filter_bandwidth filter_bandwidth);
+
+/* Whether the sleep timer is locked to events or to time */
+enum sleep_timer {
+    SLEEP_TIMER_EVENT_DRIVEN         = 0,
+    SLEEP_TIMER_EQUIDISTANT_SAMPLING = 1,
+};
+
+/* Power settings of the device */
+struct power_settings {
+    enum bma2xx_power_mode power_mode;
+    enum bma2xx_sleep_duration sleep_duration;
+    enum sleep_timer sleep_timer;
+};
+
+/* Get/Set the power settings of the device */
+int
+bma2xx_get_power_settings(const struct bma2xx * bma2xx,
+                          struct power_settings * power_settings);
+int
+bma2xx_set_power_settings(const struct bma2xx * bma2xx,
+                          const struct power_settings * power_settings);
+
+/* Get/Set the data register settings */
+int
+bma2xx_get_data_acquisition(const struct bma2xx * bma2xx,
+                            bool * unfiltered_reg_data,
+                            bool * disable_reg_shadow);
+int
+bma2xx_set_data_acquisition(const struct bma2xx * bma2xx,
+                            bool unfiltered_reg_data,
+                            bool disable_reg_shadow);
+
+/* Kick off a full soft reset of the device */
+int
+bma2xx_set_softreset(const struct bma2xx * bma2xx);
+
+/* Enable settings of all interupts */
+struct int_enable {
+    bool flat_int_enable;
+    bool orient_int_enable;
+    bool s_tap_int_enable;
+    bool d_tap_int_enable;
+    bool slope_z_int_enable;
+    bool slope_y_int_enable;
+    bool slope_x_int_enable;
+    bool fifo_wmark_int_enable;
+    bool fifo_full_int_enable;
+    bool data_int_enable;
+    bool low_g_int_enable;
+    bool high_g_z_int_enable;
+    bool high_g_y_int_enable;
+    bool high_g_x_int_enable;
+    bool no_motion_select;
+    bool slow_no_mot_z_int_enable;
+    bool slow_no_mot_y_int_enable;
+    bool slow_no_mot_x_int_enable;
+};
+
+/* Get/Set the enable settings of all interrupts */
+int
+bma2xx_get_int_enable(const struct bma2xx * bma2xx,
+                      struct int_enable * int_enable);
+int
+bma2xx_set_int_enable(const struct bma2xx * bma2xx,
+                      const struct int_enable * int_enable);
+
+/* Which physical device pin is a given interrupt routed to */
+enum int_route {
+    INT_ROUTE_NONE  = 0,
+    INT_ROUTE_PIN_1 = 1,
+    INT_ROUTE_PIN_2 = 2,
+    INT_ROUTE_BOTH  = 3,
+};
+
+enum bma2xx_int_num {
+    INT1_PIN,
+    INT2_PIN
+};
+
+/* Pin routing settings of all interrupts */
+struct int_routes {
+    enum int_route flat_int_route;
+    enum int_route orient_int_route;
+    enum int_route s_tap_int_route;
+    enum int_route d_tap_int_route;
+    enum int_route slow_no_mot_int_route;
+    enum int_route slope_int_route;
+    enum int_route high_g_int_route;
+    enum int_route low_g_int_route;
+    enum int_route fifo_wmark_int_route;
+    enum int_route fifo_full_int_route;
+    enum int_route data_int_route;
+};
+
+/* Get/Set the pin routing settings of all interrupts */
+int
+bma2xx_get_int_routes(const struct bma2xx * bma2xx,
+                      struct int_routes * int_routes);
+int
+bma2xx_set_int_routes(const struct bma2xx * bma2xx,
+                      const struct int_routes * int_routes);
+
+/* Whether each interrupt uses filtered or unfiltered data */
+struct int_filters {
+    bool unfiltered_data_int;
+    bool unfiltered_tap_int;
+    bool unfiltered_slow_no_mot_int;
+    bool unfiltered_slope_int;
+    bool unfiltered_high_g_int;
+    bool unfiltered_low_g_int;
+};
+
+/* Get/Set the filtered data settings of all interrupts */
+int
+bma2xx_get_int_filters(const struct bma2xx * bma2xx,
+                       struct int_filters * int_filters);
+int
+bma2xx_set_int_filters(const struct bma2xx * bma2xx,
+                       const struct int_filters * int_filters);
+
+/* Drive mode of the interrupt pins */
+enum int_pin_output {
+    INT_PIN_OUTPUT_PUSH_PULL  = 0,
+    INT_PIN_OUTPUT_OPEN_DRAIN = 1,
+};
+
+/* Active mode of the interrupt pins */
+enum int_pin_active {
+    INT_PIN_ACTIVE_LOW  = 0,
+    INT_PIN_ACTIVE_HIGH = 1,
+};
+
+/* Electrical settings of both interrupt pins */
+struct int_pin_electrical {
+    enum int_pin_output pin1_output;
+    enum int_pin_active pin1_active;
+    enum int_pin_output pin2_output;
+    enum int_pin_active pin2_active;
+};
+
+/* Get/Set the electrical settings of both interrupt pins */
+int
+bma2xx_get_int_pin_electrical(const struct bma2xx * bma2xx,
+                              struct int_pin_electrical * electrical);
+int
+bma2xx_set_int_pin_electrical(const struct bma2xx * bma2xx,
+                              const struct int_pin_electrical * electrical);
+
+/* Length of time that an interrupt condition should be latched active */
+enum int_latch {
+    INT_LATCH_NON_LATCHED       = 0,
+    INT_LATCH_LATCHED           = 1,
+    INT_LATCH_TEMPORARY_250_US  = 2,
+    INT_LATCH_TEMPORARY_500_US  = 3,
+    INT_LATCH_TEMPORARY_1_MS    = 4,
+    INT_LATCH_TEMPORARY_12_5_MS = 5,
+    INT_LATCH_TEMPORARY_25_MS   = 6,
+    INT_LATCH_TEMPORARY_50_MS   = 7,
+    INT_LATCH_TEMPORARY_250_MS  = 8,
+    INT_LATCH_TEMPORARY_500_MS  = 9,
+    INT_LATCH_TEMPORARY_1_S     = 10,
+    INT_LATCH_TEMPORARY_2_S     = 11,
+    INT_LATCH_TEMPORARY_4_S     = 12,
+    INT_LATCH_TEMPORARY_8_S     = 13,
+};
+
+/* Get/Set the interrupt condition latch time */
+int
+bma2xx_get_int_latch(const struct bma2xx * bma2xx,
+                     enum int_latch * int_latch);
+int
+bma2xx_set_int_latch(const struct bma2xx * bma2xx,
+                     bool reset_ints,
+                     enum int_latch int_latch);
+
+/* Settings for the low-g interrupt */
+struct low_g_int_cfg {
+    uint16_t delay_ms;
+    float thresh_g;
+    float hyster_g;
+    bool axis_summing;
+};
+
+/* Get/Set the low-g interrupt settings */
+int
+bma2xx_get_low_g_int_cfg(const struct bma2xx * bma2xx,
+                         struct low_g_int_cfg * low_g_int_cfg);
+int
+bma2xx_set_low_g_int_cfg(const struct bma2xx * bma2xx,
+                         const struct low_g_int_cfg * low_g_int_cfg);
+
+/* Settings for the high-g interrupt */
+struct high_g_int_cfg {
+    float hyster_g;
+    uint16_t delay_ms;
+    float thresh_g;
+};
+
+/* Get/Set the high-g interrupt settings */
+int
+bma2xx_get_high_g_int_cfg(const struct bma2xx * bma2xx,
+                          enum bma2xx_g_range g_range,
+                          struct high_g_int_cfg * high_g_int_cfg);
+int
+bma2xx_set_high_g_int_cfg(const struct bma2xx * bma2xx,
+                          enum bma2xx_g_range g_range,
+                          const struct high_g_int_cfg * high_g_int_cfg);
+
+/* Settings for the slow/no-motion interrupt */
+struct slow_no_mot_int_cfg {
+    uint16_t duration_p_or_s;
+    float thresh_g;
+};
+
+/* Get/Set the slow/no-motion interrupt settings */
+int
+bma2xx_get_slow_no_mot_int_cfg(const struct bma2xx * bma2xx,
+                               bool no_motion_select,
+                               enum bma2xx_g_range g_range,
+                               struct slow_no_mot_int_cfg * slow_no_mot_int_cfg);
+int
+bma2xx_set_slow_no_mot_int_cfg(const struct bma2xx * bma2xx,
+                               bool no_motion_select,
+                               enum bma2xx_g_range g_range,
+                               const struct slow_no_mot_int_cfg * slow_no_mot_int_cfg);
+
+/* Settings for the slope interrupt */
+struct slope_int_cfg {
+    uint8_t duration_p;
+    float thresh_g;
+};
+
+/* Get/Set the slope interrupt settings */
+int
+bma2xx_get_slope_int_cfg(const struct bma2xx * bma2xx,
+                         enum bma2xx_g_range g_range,
+                         struct slope_int_cfg * slope_int_cfg);
+int
+bma2xx_set_slope_int_cfg(const struct bma2xx * bma2xx,
+                         enum bma2xx_g_range g_range,
+                         const struct slope_int_cfg * slope_int_cfg);
+
+/* Settings for the double/single tap interrupt */
+struct tap_int_cfg {
+    enum bma2xx_tap_quiet tap_quiet;
+    enum bma2xx_tap_shock tap_shock;
+    enum bma2xx_d_tap_window d_tap_window;
+    enum bma2xx_tap_wake_samples tap_wake_samples;
+    float thresh_g;
+};
+
+/* Get/Set the double/single tap interrupt settings */
+int
+bma2xx_get_tap_int_cfg(const struct bma2xx * bma2xx,
+                       enum bma2xx_g_range g_range,
+                       struct tap_int_cfg * tap_int_cfg);
+int
+bma2xx_set_tap_int_cfg(const struct bma2xx * bma2xx,
+                       enum bma2xx_g_range g_range,
+                       const struct tap_int_cfg * tap_int_cfg);
+
+/* Settings for the orientation interrupt */
+struct orient_int_cfg {
+    float hyster_g;
+    enum bma2xx_orient_blocking orient_blocking;
+    enum bma2xx_orient_mode orient_mode;
+    bool signal_up_dn;
+    uint8_t blocking_angle;
+};
+
+/* Get/Set the orientation interrupt settings */
+int
+bma2xx_get_orient_int_cfg(const struct bma2xx * bma2xx,
+                          struct orient_int_cfg * orient_int_cfg);
+int
+bma2xx_set_orient_int_cfg(const struct bma2xx * bma2xx,
+                          const struct orient_int_cfg * orient_int_cfg);
+
+/* Hold time for flat condition */
+enum flat_hold {
+    FLAT_HOLD_0_MS    = 0,
+    FLAT_HOLD_512_MS  = 1,
+    FLAT_HOLD_1024_MS = 2,
+    FLAT_HOLD_2048_MS = 3,
+};
+
+/* Settings for the flat interrupt */
+struct flat_int_cfg {
+    uint8_t flat_angle;
+    enum flat_hold flat_hold;
+    uint8_t flat_hyster;
+    bool hyster_enable;
+};
+
+/* Get/Set the flat interrupt settings */
+int
+bma2xx_get_flat_int_cfg(const struct bma2xx * bma2xx,
+                        struct flat_int_cfg * flat_int_cfg);
+int
+bma2xx_set_flat_int_cfg(const struct bma2xx * bma2xx,
+                        const struct flat_int_cfg * flat_int_cfg);
+
+/* Get/Set the FIFO watermark level */
+int
+bma2xx_get_fifo_wmark_level(const struct bma2xx * bma2xx,
+                            uint8_t * wmark_level);
+int
+bma2xx_set_fifo_wmark_level(const struct bma2xx * bma2xx,
+                            uint8_t wmark_level);
+
+/* Amplitude of a self-test induced acceleration */
+enum self_test_ampl {
+    SELF_TEST_AMPL_HIGH = 0,
+    SELF_TEST_AMPL_LOW  = 1,
+};
+
+/* Direction of a self-test induced acceleration */
+enum self_test_sign {
+    SELF_TEST_SIGN_NEGATIVE = 0,
+    SELF_TEST_SIGN_POSITIVE = 1,
+};
+
+/* Settings for the self-test functionality */
+struct self_test_cfg {
+    enum self_test_ampl self_test_ampl;
+    enum self_test_sign self_test_sign;
+    enum axis self_test_axis;
+    bool self_test_enabled;
+};
+
+/* Get/Set the self-test settings */
+int
+bma2xx_get_self_test_cfg(const struct bma2xx * bma2xx,
+                         struct self_test_cfg * self_test_cfg);
+int
+bma2xx_set_self_test_cfg(const struct bma2xx * bma2xx,
+                         const struct self_test_cfg * self_test_cfg);
+
+/* Get/Set the NVM reset/write control values */
+int
+bma2xx_get_nvm_control(const struct bma2xx * bma2xx,
+                       uint8_t * remaining_cycles,
+                       bool * load_from_nvm,
+                       bool * nvm_is_ready,
+                       bool * nvm_unlocked);
+int
+bma2xx_set_nvm_control(const struct bma2xx * bma2xx,
+                       bool load_from_nvm,
+                       bool store_into_nvm,
+                       bool nvm_unlocked);
+
+/* Length of time before the I2C watchdog fires */
+enum i2c_watchdog {
+    I2C_WATCHDOG_DISABLED = 0,
+    I2C_WATCHDOG_1_MS     = 1,
+    I2C_WATCHDOG_50_MS    = 2,
+};
+
+/* Get/Set the I2C watchdog settings */
+int
+bma2xx_get_i2c_watchdog(const struct bma2xx * bma2xx,
+                        enum i2c_watchdog * i2c_watchdog);
+int
+bma2xx_set_i2c_watchdog(const struct bma2xx * bma2xx,
+                        enum i2c_watchdog i2c_watchdog);
+
+/* Offset compensation settings used in slow compensation mode */
+struct slow_ofc_cfg {
+    bool ofc_z_enabled;
+    bool ofc_y_enabled;
+    bool ofc_x_enabled;
+    bool high_bw_cut_off;
+};
+
+/* Get/Set the fast & slow offset compensation mode settings, and reset all
+ * offset compensation values back to NVM defaults */
+int
+bma2xx_get_fast_ofc_cfg(const struct bma2xx * bma2xx,
+                        bool * fast_ofc_ready,
+                        enum bma2xx_offset_comp_target * ofc_target_z,
+                        enum bma2xx_offset_comp_target * ofc_target_y,
+                        enum bma2xx_offset_comp_target * ofc_target_x);
+int
+bma2xx_set_fast_ofc_cfg(const struct bma2xx * bma2xx,
+                        enum axis fast_ofc_axis,
+                        enum bma2xx_offset_comp_target fast_ofc_target,
+                        bool trigger_fast_ofc);
+int
+bma2xx_get_slow_ofc_cfg(const struct bma2xx * bma2xx,
+                        struct slow_ofc_cfg * slow_ofc_cfg);
+int
+bma2xx_set_slow_ofc_cfg(const struct bma2xx * bma2xx,
+                        const struct slow_ofc_cfg * slow_ofc_cfg);
+int
+bma2xx_set_ofc_reset(const struct bma2xx * bma2xx);
+
+/* Get/Set the offset compensation value for a specific axis */
+int
+bma2xx_get_ofc_offset(const struct bma2xx * bma2xx,
+                      enum axis axis,
+                      float * offset_g);
+int
+bma2xx_set_ofc_offset(const struct bma2xx * bma2xx,
+                      enum axis axis,
+                      float offset_g);
+
+/* General purpose non-volatile data registers */
+enum saved_data_addr {
+    SAVED_DATA_ADDR_0 = 0,
+    SAVED_DATA_ADDR_1 = 1,
+};
+
+/* Get/Set the data stored in general purpose non-volatile registers */
+int
+bma2xx_get_saved_data(const struct bma2xx * bma2xx,
+                      enum saved_data_addr saved_data_addr,
+                      uint8_t * saved_data_val);
+int
+bma2xx_set_saved_data(const struct bma2xx * bma2xx,
+                      enum saved_data_addr saved_data_addr,
+                      uint8_t saved_data_val);
+
+/* Mode that the FIFO is running in */
+enum fifo_mode {
+    FIFO_MODE_BYPASS = 0,
+    FIFO_MODE_FIFO   = 1,
+    FIFO_MODE_STREAM = 2,
+};
+
+/* Measurements for which axis to capture into the FIFO */
+enum fifo_data {
+    FIFO_DATA_X_AND_Y_AND_Z = 0,
+    FIFO_DATA_X_ONLY        = 1,
+    FIFO_DATA_Y_ONLY        = 2,
+    FIFO_DATA_Z_ONLY        = 3,
+};
+
+/* FIFO capture and behavior settings */
+struct fifo_cfg {
+    enum fifo_mode fifo_mode;
+    enum fifo_data fifo_data;
+};
+
+/* Get/Set the FIFO capture and behavior settings */
+int
+bma2xx_get_fifo_cfg(const struct bma2xx * bma2xx,
+                    struct fifo_cfg * fifo_cfg);
+int
+bma2xx_set_fifo_cfg(const struct bma2xx * bma2xx,
+                    const struct fifo_cfg * fifo_cfg);
+
+/* Read a single multi-axis data frame from the FIFO */
+int
+bma2xx_get_fifo(const struct bma2xx * bma2xx,
+                enum bma2xx_g_range g_range,
+                enum fifo_data fifo_data,
+                struct accel_data * accel_data);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/drivers/sensors/bma2xx/src/bma2xx_shell.c b/hw/drivers/sensors/bma2xx/src/bma2xx_shell.c
new file mode 100644
index 0000000..d2b6899
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/src/bma2xx_shell.c
@@ -0,0 +1,717 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include <errno.h>
+
+#include "syscfg/syscfg.h"
+#include "bma2xx/bma2xx.h"
+#include "bma2xx_priv.h"
+#include "console/console.h"
+#include "shell/shell.h"
+
+#if MYNEWT_VAL(BMA2XX_CLI)
+
+struct self_test_mode {
+    const char * name;
+    float hmult;
+    float lmult;
+};
+
+static const struct self_test_mode self_test_modes[] = {
+    {
+        .name  = "default",
+        .hmult = 1.0,
+        .lmult = 0.0,
+    },
+    {
+        .name  = "strict",
+        .hmult = 2.0,
+        .lmult = 0.5,
+    },
+};
+
+static int
+bma2xx_self_test_cmd(struct bma2xx * bma2xx,
+                     int argc,
+                     char * argv[])
+{
+    const struct self_test_mode * self_test_mode;
+    uint8_t i;
+    bool self_test_fail;
+    int rc;
+
+    if (argc != 1) {
+        return EINVAL;
+    }
+
+    self_test_mode = NULL;
+    for (i = 0; i < sizeof(self_test_modes) /
+                    sizeof(*self_test_modes); i++) {
+        if (strcmp(self_test_modes[i].name, argv[0]) == 0) {
+            self_test_mode = self_test_modes + i;
+        }
+    }
+
+    if (self_test_mode == NULL) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_self_test(bma2xx,
+                          self_test_mode->hmult,
+                          self_test_mode->lmult,
+                          &self_test_fail);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (self_test_fail) {
+        console_printf("self test failed\n");
+    } else {
+        console_printf("self test passed\n");
+    }
+
+    return 0;
+}
+
+struct offset_comp_target {
+    const char * name;
+    enum bma2xx_offset_comp_target target;
+};
+
+static const struct offset_comp_target offset_comp_targets[] = {
+    {
+        .name   = "0g",
+        .target = BMA2XX_OFFSET_COMP_TARGET_0_G,
+    },
+    {
+        .name   = "-1g",
+        .target = BMA2XX_OFFSET_COMP_TARGET_NEG_1_G,
+    },
+    {
+        .name   = "+1g",
+        .target = BMA2XX_OFFSET_COMP_TARGET_POS_1_G,
+    },
+};
+
+static int
+bma2xx_offset_compensation_cmd(struct bma2xx * bma2xx,
+                               int argc,
+                               char * argv[])
+{
+    const struct offset_comp_target * target_x;
+    const struct offset_comp_target * target_y;
+    const struct offset_comp_target * target_z;
+    uint8_t i;
+
+    if (argc != 3) {
+        return EINVAL;
+    }
+
+    target_x = NULL;
+    for (i = 0; i < sizeof(offset_comp_targets) /
+                    sizeof(*offset_comp_targets); i++) {
+        if (strcmp(offset_comp_targets[i].name, argv[0]) == 0) {
+            target_x = offset_comp_targets + i;
+        }
+    }
+    target_y = NULL;
+    for (i = 0; i < sizeof(offset_comp_targets) /
+                    sizeof(*offset_comp_targets); i++) {
+        if (strcmp(offset_comp_targets[i].name, argv[1]) == 0) {
+            target_y = offset_comp_targets + i;
+        }
+    }
+    target_z = NULL;
+    for (i = 0; i < sizeof(offset_comp_targets) /
+                    sizeof(*offset_comp_targets); i++) {
+        if (strcmp(offset_comp_targets[i].name, argv[2]) == 0) {
+            target_z = offset_comp_targets + i;
+        }
+    }
+
+    if (target_x == NULL) {
+        return EINVAL;
+    }
+    if (target_y == NULL) {
+        return EINVAL;
+    }
+    if (target_z == NULL) {
+        return EINVAL;
+    }
+
+    return bma2xx_offset_compensation(bma2xx,
+                                      target_x->target,
+                                      target_y->target,
+                                      target_z->target);
+}
+
+static int
+bma2xx_query_offsets_cmd(struct bma2xx * bma2xx,
+                         int argc,
+                         char * argv[])
+{
+    int rc;
+    float offset_x_g;
+    float offset_y_g;
+    float offset_z_g;
+    char buffer_x[20];
+    char buffer_y[20];
+    char buffer_z[20];
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_query_offsets(bma2xx,
+                              &offset_x_g,
+                              &offset_y_g,
+                              &offset_z_g);
+    if (rc != 0) {
+        return rc;
+    }
+
+    sensor_ftostr(offset_x_g, buffer_x, sizeof(buffer_x));
+    sensor_ftostr(offset_y_g, buffer_y, sizeof(buffer_y));
+    sensor_ftostr(offset_z_g, buffer_z, sizeof(buffer_z));
+
+    console_printf("offset x = %s offset y = %s offset z = %s\n",
+                   buffer_x,
+                   buffer_y,
+                   buffer_z);
+
+    return 0;
+}
+
+static int
+bma2xx_write_offsets_cmd(struct bma2xx * bma2xx,
+                         int argc,
+                         char * argv[])
+{
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    return bma2xx_write_offsets(bma2xx, 0.0, 0.0, 0.0);
+}
+
+struct stream_read_context {
+    uint32_t count;
+};
+
+static bool
+bma2xx_stream_read_cb(void * arg,
+                      struct sensor_accel_data * sad)
+{
+    char buffer_x[20];
+    char buffer_y[20];
+    char buffer_z[20];
+    struct stream_read_context * ctx;
+
+    sensor_ftostr(sad->sad_x, buffer_x, sizeof(buffer_x));
+    sensor_ftostr(sad->sad_y, buffer_y, sizeof(buffer_y));
+    sensor_ftostr(sad->sad_z, buffer_z, sizeof(buffer_z));
+
+    console_printf("x = %s y = %s z = %s\n",
+                   buffer_x,
+                   buffer_y,
+                   buffer_z);
+
+    ctx = (struct stream_read_context *)arg;
+    ctx->count--;
+
+    return ctx->count == 0;
+}
+
+static int
+bma2xx_stream_read_cmd(struct bma2xx * bma2xx,
+                       int argc,
+                       char * argv[])
+{
+    char * end;
+    struct stream_read_context ctx;
+
+    if (argc != 1) {
+        return EINVAL;
+    }
+
+    ctx.count = strtoul(argv[0], &end, 10);
+    if (*end != '\0') {
+        return EINVAL;
+    }
+
+    return bma2xx_stream_read(bma2xx,
+                              bma2xx_stream_read_cb,
+                              &ctx,
+                              0);
+}
+
+static int
+bma2xx_current_temp_cmd(struct bma2xx * bma2xx,
+                        int argc,
+                        char * argv[])
+{
+    float temp_c;
+    int rc;
+    char buffer[20];
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_current_temp(bma2xx, &temp_c);
+    if (rc != 0) {
+        return rc;
+    }
+
+    sensor_ftostr(temp_c, buffer, sizeof(buffer));
+
+    console_printf("temp = %s C\n", buffer);
+
+    return 0;
+}
+
+static void
+console_print_orient(const struct bma2xx_orient_xyz * orient_xyz)
+{
+    const char * xy_desc;
+    const char * z_desc;
+
+    switch (orient_xyz->orient_xy) {
+    case BMA2XX_ORIENT_XY_PORTRAIT_UPRIGHT:
+        xy_desc = "portrait-upright";
+        break;
+    case BMA2XX_ORIENT_XY_PORTRAIT_UPSIDE_DOWN:
+        xy_desc = "portrait-upside-down";
+        break;
+    case BMA2XX_ORIENT_XY_LANDSCAPE_LEFT:
+        xy_desc = "landscape-left";
+        break;
+    case BMA2XX_ORIENT_XY_LANDSCAPE_RIGHT:
+        xy_desc = "landscape-right";
+        break;
+    default:
+        xy_desc = "unknown-enum";
+        break;
+    }
+
+    if (orient_xyz->downward_z) {
+        z_desc = "facing-downward";
+    } else {
+        z_desc = "facing-upward";
+    }
+
+    console_printf("xy = %s z = %s\n", xy_desc, z_desc);
+}
+
+static int
+bma2xx_current_orient_cmd(struct bma2xx * bma2xx,
+                          int argc,
+                          char * argv[])
+{
+    struct bma2xx_orient_xyz orient_xyz;
+    int rc;
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_current_orient(bma2xx, &orient_xyz);
+    if (rc != 0) {
+        return rc;
+    }
+
+    console_print_orient(&orient_xyz);
+
+    return 0;
+}
+
+static int
+bma2xx_wait_for_orient_cmd(struct bma2xx * bma2xx,
+                           int argc,
+                           char * argv[])
+{
+    struct bma2xx_orient_xyz orient_xyz;
+    int rc;
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_wait_for_orient(bma2xx, &orient_xyz);
+    if (rc != 0) {
+        return rc;
+    }
+
+    console_print_orient(&orient_xyz);
+    console_printf("done!\n");
+
+    return 0;
+}
+
+static int
+bma2xx_wait_for_high_g_cmd(struct bma2xx * bma2xx,
+                           int argc,
+                           char * argv[])
+{
+    int rc;
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_wait_for_high_g(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    console_printf("done!\n");
+
+    return 0;
+}
+
+static int
+bma2xx_wait_for_low_g_cmd(struct bma2xx * bma2xx,
+                          int argc,
+                          char * argv[])
+{
+    int rc;
+
+    if (argc != 0) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_wait_for_low_g(bma2xx);
+    if (rc != 0) {
+        return rc;
+    }
+
+    console_printf("done!\n");
+
+    return 0;
+}
+
+struct tap_type {
+    const char * name;
+    enum bma2xx_tap_type type;
+};
+
+static const struct tap_type tap_types[] = {
+    {
+        .name = "double",
+        .type = BMA2XX_TAP_TYPE_DOUBLE,
+    },
+    {
+        .name = "single",
+        .type = BMA2XX_TAP_TYPE_SINGLE,
+    },
+};
+
+static int
+bma2xx_wait_for_tap_cmd(struct bma2xx * bma2xx,
+                        int argc,
+                        char * argv[])
+{
+    const struct tap_type * tap_type;
+    uint8_t i;
+    int rc;
+
+    if (argc != 1) {
+        return EINVAL;
+    }
+
+    tap_type = NULL;
+    for (i = 0; i < sizeof(tap_types) /
+                    sizeof(*tap_types); i++) {
+        if (strcmp(tap_types[i].name, argv[0]) == 0) {
+            tap_type = tap_types + i;
+        }
+    }
+
+    if (tap_type == NULL) {
+        return EINVAL;
+    }
+
+    rc = bma2xx_wait_for_tap(bma2xx, tap_type->type);
+    if (rc != 0) {
+        return rc;
+    }
+
+    console_printf("done!\n");
+
+    return 0;
+}
+
+struct power_mode {
+    const char * name;
+    enum bma2xx_power_mode power;
+};
+
+static const struct power_mode power_modes[] = {
+    {
+        .name  = "normal",
+        .power = BMA2XX_POWER_MODE_NORMAL,
+    },
+    {
+        .name  = "deep-suspend",
+        .power = BMA2XX_POWER_MODE_DEEP_SUSPEND,
+    },
+    {
+        .name  = "suspend",
+        .power = BMA2XX_POWER_MODE_SUSPEND,
+    },
+    {
+        .name  = "standby",
+        .power = BMA2XX_POWER_MODE_STANDBY,
+    },
+    {
+        .name  = "lpm1",
+        .power = BMA2XX_POWER_MODE_LPM_1,
+    },
+    {
+        .name  = "lpm2",
+        .power = BMA2XX_POWER_MODE_LPM_2,
+    },
+};
+
+struct sleep_duration {
+    const char * name;
+    enum bma2xx_sleep_duration sleep;
+};
+
+static const struct sleep_duration sleep_durations[] = {
+    {
+        .name  = "0.5ms",
+        .sleep = BMA2XX_SLEEP_DURATION_0_5_MS,
+    },
+    {
+        .name  = "1ms",
+        .sleep = BMA2XX_SLEEP_DURATION_1_MS,
+    },
+    {
+        .name  = "2ms",
+        .sleep = BMA2XX_SLEEP_DURATION_2_MS,
+    },
+    {
+        .name  = "4ms",
+        .sleep = BMA2XX_SLEEP_DURATION_4_MS,
+    },
+    {
+        .name  = "6ms",
+        .sleep = BMA2XX_SLEEP_DURATION_6_MS,
+    },
+    {
+        .name  = "10ms",
+        .sleep = BMA2XX_SLEEP_DURATION_10_MS,
+    },
+    {
+        .name  = "25ms",
+        .sleep = BMA2XX_SLEEP_DURATION_25_MS,
+    },
+    {
+        .name  = "50ms",
+        .sleep = BMA2XX_SLEEP_DURATION_50_MS,
+    },
+    {
+        .name  = "100ms",
+        .sleep = BMA2XX_SLEEP_DURATION_100_MS,
+    },
+    {
+        .name  = "500ms",
+        .sleep = BMA2XX_SLEEP_DURATION_500_MS,
+    },
+    {
+        .name  = "1s",
+        .sleep = BMA2XX_SLEEP_DURATION_1_S,
+    },
+};
+
+static int
+bma2xx_power_settings_cmd(struct bma2xx * bma2xx,
+                          int argc,
+                          char * argv[])
+{
+    const struct power_mode * power_mode;
+    const struct sleep_duration * sleep_duration;
+    uint8_t i;
+
+    if (argc != 2) {
+        return EINVAL;
+    }
+
+    power_mode = NULL;
+    for (i = 0; i < sizeof(power_modes) /
+                    sizeof(*power_modes); i++) {
+        if (strcmp(power_modes[i].name, argv[0]) == 0) {
+            power_mode = power_modes + i;
+        }
+    }
+
+    sleep_duration = NULL;
+    for (i = 0; i < sizeof(sleep_durations) /
+                    sizeof(*sleep_durations); i++) {
+        if (strcmp(sleep_durations[i].name, argv[1]) == 0) {
+            sleep_duration = sleep_durations + i;
+        }
+    }
+
+    if (power_mode == NULL) {
+        return EINVAL;
+    }
+    if (sleep_duration == NULL) {
+        return EINVAL;
+    }
+
+    return bma2xx_power_settings(bma2xx,
+                                 power_mode->power,
+                                 sleep_duration->sleep);
+}
+
+struct subcmd {
+    const char * name;
+    const char * help;
+    int (*func)(struct bma2xx * bma2xx,
+                int argc,
+                char * argv[]);
+};
+
+static const struct subcmd supported_subcmds[] = {
+    {
+        .name = "self-test",
+        .help = "<default|strict>",
+        .func = bma2xx_self_test_cmd,
+    },
+    {
+        .name = "offset-compensation",
+        .help = "<x={0g|-1g|+1g}> <y={0g|-1g|+1g}> <z={0g|-1g|+1g}>",
+        .func = bma2xx_offset_compensation_cmd,
+    },
+    {
+        .name = "query-offsets",
+        .help = "",
+        .func = bma2xx_query_offsets_cmd,
+    },
+    {
+        .name = "write-offsets",
+        .help = "",
+        .func = bma2xx_write_offsets_cmd,
+    },
+    {
+        .name = "stream-read",
+        .help = "<num-reads>",
+        .func = bma2xx_stream_read_cmd,
+    },
+    {
+        .name = "current-temp",
+        .help = "",
+        .func = bma2xx_current_temp_cmd,
+    },
+    {
+        .name = "current-orient",
+        .help = "",
+        .func = bma2xx_current_orient_cmd,
+    },
+    {
+        .name = "wait-for-orient",
+        .help = "",
+        .func = bma2xx_wait_for_orient_cmd,
+    },
+    {
+        .name = "wait-for-high-g",
+        .help = "",
+        .func = bma2xx_wait_for_high_g_cmd,
+    },
+    {
+        .name = "wait-for-low-g",
+        .help = "",
+        .func = bma2xx_wait_for_low_g_cmd,
+    },
+    {
+        .name = "wait-for-tap",
+        .help = "<double|single>",
+        .func = bma2xx_wait_for_tap_cmd,
+    },
+    {
+        .name = "power-settings",
+        .help = "<normal|deep-suspend|suspend|standby|lpm1|lpm2>" \
+                "\n                      "                        \
+                "<0.5ms|1ms|2ms|4ms|6ms|10ms|25ms|50ms|100ms|500ms|1s>",
+        .func = bma2xx_power_settings_cmd,
+    },
+};
+
+static int
+bma2xx_shell_cmd(int argc, char * argv[])
+{
+    struct os_dev * dev;
+    struct bma2xx * bma2xx;
+    const struct subcmd * subcmd;
+    uint8_t i;
+
+    dev = os_dev_open(MYNEWT_VAL(BMA2XX_SHELL_DEV_NAME), OS_TIMEOUT_NEVER, NULL);
+    if (dev == NULL) {
+        console_printf("failed to open bma2xx_0 device\n");
+        return ENODEV;
+    }
+
+    bma2xx = (struct bma2xx *)dev;
+
+    subcmd = NULL;
+    if (argc > 1) {
+        for (i = 0; i < sizeof(supported_subcmds) /
+                        sizeof(*supported_subcmds); i++) {
+            if (strcmp(supported_subcmds[i].name, argv[1]) == 0) {
+                subcmd = supported_subcmds + i;
+            }
+        }
+
+        if (subcmd == NULL) {
+            console_printf("unknown %s subcommand\n", argv[1]);
+        }
+    }
+
+    if (subcmd != NULL) {
+        if (subcmd->func(bma2xx, argc - 2, argv + 2) != 0) {
+            console_printf("could not run %s subcommand\n", argv[1]);
+            console_printf("%s %s\n", subcmd->name, subcmd->help);
+        }
+    } else {
+        for (i = 0; i < sizeof(supported_subcmds) /
+                        sizeof(*supported_subcmds); i++) {
+            subcmd = supported_subcmds + i;
+            console_printf("%s %s\n", subcmd->name, subcmd->help);
+        }
+    }
+
+    os_dev_close(dev);
+
+    return 0;
+}
+
+static const struct shell_cmd bma2xx_shell_cmd_desc = {
+    .sc_cmd      = "bma2xx",
+    .sc_cmd_func = bma2xx_shell_cmd,
+};
+
+int
+bma2xx_shell_init(void)
+{
+    return shell_cmd_register(&bma2xx_shell_cmd_desc);
+}
+
+#endif
diff --git a/hw/drivers/sensors/bma2xx/syscfg.yml b/hw/drivers/sensors/bma2xx/syscfg.yml
new file mode 100644
index 0000000..42c95de
--- /dev/null
+++ b/hw/drivers/sensors/bma2xx/syscfg.yml
@@ -0,0 +1,47 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+syscfg.defs:
+    BMA2XX_CLI:
+        description: 'Enable shell support for BMA2XX'
+        value: 0
+    BMA2XX_I2C_WDT:
+        description: 'Enable I2C watchdog functionality'
+        value: 0
+    BMA2XX_INT_ENABLE:
+        description: 'Enable interrupt support, necessary for events'
+        value: 1
+    BMA2XX_INT_CFG_ACTIVE:
+        description: 'Set 0 for active-low, 1 for active-high'
+        value: 1
+    BMA2XX_INT_CFG_OUTPUT:
+        description: 'Set 0 for push-pull, 1 for open-drain'
+        value: 1
+    BMA2XX_INT_PIN_DEVICE:
+        description: 'Interrupt pin number 1 or 2 on accelerometer device'
+        value: 1
+    BMA2XX_INT2_PIN_DEVICE:
+        description: 'Interrupt pin number 1 or 2 on accelerometer device'
+        value: 2
+    BMA2XX_LOG:
+        description: 'Enable BMA2XX logging'
+        value: 0
+    BMA2XX_SHELL_DEV_NAME:
+        description: 'BMA2XX Shell device name'
+        value: "\"bma2xx_0\""
diff --git a/hw/sensor/creator/pkg.yml b/hw/sensor/creator/pkg.yml
index 1c6a2ec..d9f4ec0 100644
--- a/hw/sensor/creator/pkg.yml
+++ b/hw/sensor/creator/pkg.yml
@@ -48,6 +48,8 @@ pkg.deps.BMP280_OFB:
     - hw/drivers/sensors/bmp280
 pkg.deps.BMA253_OFB:
     - hw/drivers/sensors/bma253
+pkg.deps.BMA2XX_OFB:
+    - hw/drivers/sensors/bma2xx
 pkg.deps.LIS2DW12_OFB:
     - hw/drivers/sensors/lis2dw12
 pkg.init:
diff --git a/hw/sensor/creator/src/sensor_creator.c b/hw/sensor/creator/src/sensor_creator.c
index 14d3e55..e2ee8d8 100644
--- a/hw/sensor/creator/src/sensor_creator.c
+++ b/hw/sensor/creator/src/sensor_creator.c
@@ -60,6 +60,10 @@
 #include <bma253/bma253.h>
 #endif
 
+#if MYNEWT_VAL(BMA2XX_OFB)
+#include <bma2xx/bma2xx.h>
+#endif
+
 
 #if MYNEWT_VAL(ADXL345_OFB)
 #include <adxl345/adxl345.h>
@@ -118,6 +122,10 @@ static struct bmp280 bmp280;
 static struct bma253 bma253;
 #endif
 
+#if MYNEWT_VAL(BMA2XX_OFB)
+static struct bma2xx bma2xx;
+#endif
+
 #if MYNEWT_VAL(ADXL345_OFB)
 static struct adxl345 adxl345;
 #endif
@@ -242,10 +250,39 @@ static struct sensor_itf i2c_0_itf_lis = {
     .si_num  = 0,
     .si_addr = 0x18,
     .si_ints = {
-       { MYNEWT_VAL(BMA253_INT_PIN_HOST), MYNEWT_VAL(BMA253_INT_PIN_DEVICE),
-                                         MYNEWT_VAL(BMA253_INT_CFG_ACTIVE)},
-       { MYNEWT_VAL(BMA253_INT2_PIN_HOST), MYNEWT_VAL(BMA253_INT2_PIN_DEVICE),
-                                       MYNEWT_VAL(BMA253_INT_CFG_ACTIVE)}
+        { 26, MYNEWT_VAL(BMA2XX_INT_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)},
+        { 25, MYNEWT_VAL(BMA2XX_INT2_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)}
+    },
+};
+#endif
+
+#if MYNEWT_VAL(I2C_0) && MYNEWT_VAL(BMA2XX_OFB)
+static struct sensor_itf spi2c_0_itf_bma2xx = {
+    .si_type = SENSOR_ITF_I2C,
+    .si_num  = 0,
+    .si_addr = 0x18,
+    .si_ints = {
+        { 26, MYNEWT_VAL(BMA2XX_INT_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)},
+        { 25, MYNEWT_VAL(BMA2XX_INT2_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)}
+    },
+};
+#endif
+#if MYNEWT_VAL(SPI_0_MASTER) && MYNEWT_VAL(BMA2XX_OFB)
+//TODO:  Make INT pin nums configurable.  Leaving hardcoded
+//to handle multiple bma2xx sensor interface examples
+static struct sensor_itf spi2c_0_itf_bma2xx = {
+    .si_type = SENSOR_ITF_SPI,
+    .si_num = 0,
+    .si_cs_pin = 21,
+    .si_ints = {
+        { 26, MYNEWT_VAL(BMA2XX_INT_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)},
+        { 25, MYNEWT_VAL(BMA2XX_INT2_PIN_DEVICE),
+            MYNEWT_VAL(BMA2XX_INT_CFG_ACTIVE)}
     },
 };
 #endif
@@ -769,11 +806,12 @@ config_lis2dw12_sensor(void)
     cfg.tap.latency = 8; /* 640ms */
     cfg.tap.quiet = 0; /* 10ms */
     cfg.tap.shock = 3; /* 120ms */
+
     cfg.double_tap_event_enable = 0;
 
-    cfg.freefall_dur = 6; 
+    cfg.freefall_dur = 6;
     cfg.freefall_ths = 3; /* ~312mg */
-    
+
     cfg.int1_pin_cfg = 0;
     cfg.int2_pin_cfg = 0;
     cfg.int_enable = 0;
@@ -783,7 +821,7 @@ config_lis2dw12_sensor(void)
     cfg.int_active_low = 0;
     cfg.slp_mode = 0;
     cfg.self_test_mode = LIS2DW12_ST_MODE_DISABLE;
-    
+
     cfg.fifo_mode = LIS2DW12_FIFO_M_BYPASS;
     cfg.fifo_threshold = 32;
 
@@ -798,16 +836,62 @@ config_lis2dw12_sensor(void)
     cfg.low_noise_enable = 1;
     
     cfg.read_mode.mode = LIS2DW12_READ_M_POLL;
+
     cfg.mask = SENSOR_TYPE_ACCELEROMETER;
 
     rc = lis2dw12_config((struct lis2dw12 *) dev, &cfg);
     assert(rc == 0);
-    
+
     os_dev_close(dev);
     return rc;
 }
 #endif
 
+#if MYNEWT_VAL(BMA2XX_OFB)
+/**
+ * BMA2XX sensor default configuration
+ *
+ * @return 0 on success, non-zero on failure
+ */
+int
+config_bma2xx_sensor(void)
+{
+    struct os_dev * dev;
+    struct bma2xx_cfg cfg;
+    int rc;
+
+    dev = os_dev_open("bma2xx_0", OS_TIMEOUT_NEVER, NULL);
+    assert(dev != NULL);
+
+    cfg.model = BMA2XX_BMA280;
+    cfg.low_g_delay_ms = BMA2XX_LOW_G_DELAY_MS_DEFAULT;
+    cfg.high_g_delay_ms = BMA2XX_HIGH_G_DELAY_MS_DEFAULT;
+    cfg.g_range = BMA2XX_G_RANGE_2;
+    cfg.filter_bandwidth = BMA2XX_FILTER_BANDWIDTH_500_HZ;
+    cfg.use_unfiltered_data = false;
+    cfg.tap_quiet = BMA2XX_TAP_QUIET_30_MS;
+    cfg.tap_shock = BMA2XX_TAP_SHOCK_50_MS;
+    cfg.d_tap_window = BMA2XX_D_TAP_WINDOW_250_MS;
+    cfg.tap_wake_samples = BMA2XX_TAP_WAKE_SAMPLES_16;
+    cfg.tap_thresh_g = 1.0;
+    cfg.offset_x_g = 0.0;
+    cfg.offset_y_g = 0.0;
+    cfg.offset_z_g = 0.0;
+    cfg.orient_blocking = BMA2XX_ORIENT_BLOCKING_NONE;
+    cfg.orient_mode = BMA2XX_ORIENT_MODE_SYMMETRICAL;
+    cfg.power_mode = BMA2XX_POWER_MODE_NORMAL;
+    cfg.sleep_duration = BMA2XX_SLEEP_DURATION_0_5_MS;
+    cfg.sensor_mask = SENSOR_TYPE_ACCELEROMETER;
+
+    rc = bma2xx_config((struct bma2xx *)dev, &cfg);
+    assert(rc == 0);
+
+    os_dev_close(dev);
+
+    return 0;
+}
+#endif
+
 /* Sensor device creation */
 void
 sensor_dev_create(void)
@@ -923,6 +1007,15 @@ sensor_dev_create(void)
     assert(rc == 0);
 #endif
 
+#if MYNEWT_VAL(BMA2XX_OFB)
+rc = os_dev_create((struct os_dev *)&bma2xx, "bma2xx_0",
+  OS_DEV_INIT_PRIMARY, 0, bma2xx_init, &spi2c_0_itf_bma2xx);
+assert(rc == 0);
+
+rc = config_bma2xx_sensor();
+assert(rc == 0);
+#endif
+
 #if MYNEWT_VAL(ADXL345_OFB)
     rc = os_dev_create((struct os_dev *) &adxl345, "adxl345_0",
       OS_DEV_INIT_PRIMARY, 0, adxl345_init, (void *)&i2c_0_itf_adxl);
diff --git a/hw/sensor/creator/syscfg.yml b/hw/sensor/creator/syscfg.yml
index 2d750c8..48954d0 100644
--- a/hw/sensor/creator/syscfg.yml
+++ b/hw/sensor/creator/syscfg.yml
@@ -61,4 +61,7 @@ syscfg.defs:
     LIS2DW12_OFB:
         description: 'LIS2DW12 is present'
         value : 0
+    BMA2XX_OFB:
+        description: 'A sensor in the BMA2XX family is present'
+        value : 0
 
diff --git a/hw/sensor/include/sensor/sensor.h b/hw/sensor/include/sensor/sensor.h
index 9e758e5..fccf1cb 100644
--- a/hw/sensor/include/sensor/sensor.h
+++ b/hw/sensor/include/sensor/sensor.h
@@ -365,7 +365,7 @@ typedef int (*sensor_read_func_t)(struct sensor *, sensor_type_t,
 typedef int (*sensor_get_config_func_t)(struct sensor *, sensor_type_t,
         struct sensor_cfg *);
 
-/** 
+/**
  * Send a new configuration register set to the sensor.
  *
  * @param ptr to the sensor-specific stucture

-- 
To stop receiving notification emails like this one, please contact
vipulrahane@apache.org.