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

jacobrosenthal 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 ce0d958  hw driver: drv2605 (#757)
ce0d958 is described below

commit ce0d9588be52097624c8bf25e2da6c40bcdf3626
Author: Jacob Rosenthal <jacobrosent...@gmail.com>
AuthorDate: Mon Feb 12 13:14:12 2018 -0700

    hw driver: drv2605 (#757)
    
    * hw driver: drv2605
    * drivers: _writelen check for buffer overflow
---
 apps/sensors_test/src/main.c                       |   8 +
 hw/drivers/drv2605/include/drv2605/drv2605.h       | 171 ++++
 .../creator/syscfg.yml => drivers/drv2605/pkg.yml} |  47 +-
 hw/drivers/drv2605/src/drv2605.c                   | 875 +++++++++++++++++++++
 hw/drivers/drv2605/src/drv2605_priv.h              | 328 ++++++++
 hw/drivers/drv2605/src/drv2605_shell.c             | 504 ++++++++++++
 hw/drivers/drv2605/syscfg.yml                      |  67 ++
 hw/drivers/sensors/bno055/src/bno055.c             |   7 +-
 hw/drivers/sensors/lis2dh12/src/lis2dh12.c         |   7 +-
 hw/drivers/sensors/tcs34725/src/tcs34725.c         |   7 +-
 hw/sensor/creator/pkg.yml                          |   2 +
 hw/sensor/creator/src/sensor_creator.c             |  52 ++
 hw/sensor/creator/syscfg.yml                       |   3 +
 13 files changed, 2046 insertions(+), 32 deletions(-)

diff --git a/apps/sensors_test/src/main.c b/apps/sensors_test/src/main.c
index 4238419..fe4a862 100755
--- a/apps/sensors_test/src/main.c
+++ b/apps/sensors_test/src/main.c
@@ -55,6 +55,10 @@
 #if MYNEWT_VAL(BMP280_CLI)
 #include <bmp280/bmp280.h>
 #endif
+#if MYNEWT_VAL(DRV2605_CLI)
+#include <drv2605/drv2605.h>
+#endif
+
 
 #if MYNEWT_VAL(SENSOR_OIC)
 #include <oic/oc_api.h>
@@ -430,6 +434,10 @@ sensors_dev_shell_init(void)
 #if MYNEWT_VAL(BMP280_CLI)
     bmp280_shell_init();
 #endif
+
+#if MYNEWT_VAL(DRV2605_CLI)
+    drv2605_shell_init();
+#endif
 }
 
 static void
diff --git a/hw/drivers/drv2605/include/drv2605/drv2605.h 
b/hw/drivers/drv2605/include/drv2605/drv2605.h
new file mode 100644
index 0000000..ce5e661
--- /dev/null
+++ b/hw/drivers/drv2605/include/drv2605/drv2605.h
@@ -0,0 +1,171 @@
+/*
+ * 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 __DRV2605_H__
+#define __DRV2605_H__
+
+#include <os/os.h>
+#include "os/os_dev.h"
+#include "sensor/sensor.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+struct drv2605_cal {
+    uint8_t brake_factor;
+    uint8_t loop_gain;
+    uint8_t lra_sample_time;
+    uint8_t lra_blanking_time;
+    uint8_t lra_idiss_time;
+    uint8_t auto_cal_time;
+    uint8_t lra_zc_det_time;
+};
+
+enum drv2605_power_mode {
+    DRV2605_POWER_STANDBY = 0x00, // en pin high, standby bit high
+    DRV2605_POWER_ACTIVE, //en pin high, standby bit low
+    DRV2605_POWER_OFF //en pin low
+};
+
+enum drv2605_op_mode {
+    DRV2605_OP_ROM = 0x00,
+    DRV2605_OP_PWM,
+    DRV2605_OP_ANALOG,
+    DRV2605_OP_RTP,
+    DRV2605_OP_DIAGNOSTIC,
+    DRV2605_OP_CALIBRATION,
+    DRV2605_OP_RESET
+};
+
+struct drv2605_cfg {
+    enum drv2605_op_mode op_mode;
+    struct drv2605_cal cal;
+};
+
+struct drv2605 {
+    struct os_dev dev;
+    struct sensor sensor;
+    struct drv2605_cfg cfg;
+};
+
+
+/**
+ * Initialize the drv2605. This function is normally called by sysinit.
+ *
+ * @param dev  Pointer to the drv2605_dev device descriptor
+ */
+int
+drv2605_init(struct os_dev *dev, void *arg);
+
+int
+drv2605_config(struct drv2605 *drv2605, struct drv2605_cfg *cfg);
+
+
+#if MYNEWT_VAL(DRV2605_CLI)
+int drv2605_shell_init(void);
+#endif
+
+
+/**
+ * Get a best effort defaults for the drv2605_cal
+ *
+ * @param The sensor interface
+ * @param Pointer to the drv2605_cal struct
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_default_cal(struct drv2605_cal *cal);
+
+/**
+ * Get chip ID from the sensor
+ *
+ * @param The sensor interface
+ * @param Pointer to the variable to fill up chip ID in
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_get_chip_id(struct sensor_itf *itf, uint8_t *id);
+
+/**
+ * Get chip ID from the sensor
+ *
+ * @param The sensor interface
+ * @param Pointer to the buffer of rom library selections
+ * @param Size of the rom buffer (max 8)
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_load_rom(struct sensor_itf *itf, uint8_t *buffer, size_t length);
+
+/**
+ * Load value for rtp playback to device
+ *
+ * @param The sensor interface
+ * @param Value to load
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_load_rtp(struct sensor_itf *itf, uint8_t value);
+
+/**
+ * Trigger preloaded rom selections
+ *
+ * @param The sensor interface
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_trigger_rom(struct sensor_itf *itf);
+
+/**
+ * Get rom playback status from device
+ *
+ * @param The sensor interface
+ * @param Pointer to the variable to fill up status in
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_rom_busy(struct sensor_itf *itf, bool *status);
+
+/**
+ * Set the current power mode from device.
+ *
+ * @param The sensor interface
+ * @param drv2605_power_mode to send to device
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_set_power_mode(struct sensor_itf *itf, enum drv2605_power_mode 
power_mode);
+
+/**
+ * Get the current power mode from device
+ *
+ * @param The sensor interface
+ * @param Pointer to drv2605_power_mode enum
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_get_power_mode(struct sensor_itf *itf, enum drv2605_power_mode 
*power_mode);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRV2605_H__ */
diff --git a/hw/sensor/creator/syscfg.yml b/hw/drivers/drv2605/pkg.yml
similarity index 51%
copy from hw/sensor/creator/syscfg.yml
copy to hw/drivers/drv2605/pkg.yml
index cd54cdf..8561f2b 100644
--- a/hw/sensor/creator/syscfg.yml
+++ b/hw/drivers/drv2605/pkg.yml
@@ -1,3 +1,4 @@
+#
 # 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
@@ -16,33 +17,21 @@
 # under the License.
 #
 
-# Package: hw/sensor/creator
+pkg.name: hw/drivers/drv2605
+pkg.description: i2c driver for the drv2605l in a Closed Loop LRA 
configuration based on SLOS854C REVISED SEPTEMBER 2014 datasheet
+pkg.author: Jacob Rosenthal
+pkg.homepage:
+pkg.keywords:
+    - drv2605
+    - drv2605l
+    - haptic
+    - lra
+    - i2c
+
+pkg.deps:
+    - "@apache-mynewt-core/kernel/os"
+    - "@apache-mynewt-core/hw/hal"
+    - "@apache-mynewt-core/hw/sensor"
 
-syscfg.defs:
-    TSL2561_OFB:
-        description: 'TSL2561 is present'
-        value : 0
-    LSM303DLHC_OFB:
-        description: 'LSM303 is present'
-        value : 0
-    MPU6050_OFB:
-        description: 'MPU6050 is present'
-        value : 0
-    BNO055_OFB:
-        description: 'BNO055 is present'
-        value : 0
-    BME280_OFB:
-        description: 'BME280 is present'
-        value : 0
-    MS5837_OFB:
-        description: 'MS5837 is present'
-        value : 0
-    BMP280_OFB:
-        description: 'BMP280 is present'
-        value : 0
-    TCS34725_OFB:
-        description: 'TCS34725 is present'
-        value : 0
-    BMA253_OFB:
-        description: 'BMA253 is present'
-        value : 0
+pkg.deps.DRV2605_CLI:
+    - "@apache-mynewt-core/util/parse"
diff --git a/hw/drivers/drv2605/src/drv2605.c b/hw/drivers/drv2605/src/drv2605.c
new file mode 100644
index 0000000..a724edc
--- /dev/null
+++ b/hw/drivers/drv2605/src/drv2605.c
@@ -0,0 +1,875 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "defs/error.h"
+#include "os/os.h"
+#include "sysinit/sysinit.h"
+#include "hal/hal_i2c.h"
+#include "hal/hal_gpio.h"
+#include "drv2605/drv2605.h"
+#include "drv2605_priv.h"
+
+#if MYNEWT_VAL(DRV2605_LOG)
+#include "log/log.h"
+#endif
+
+#if MYNEWT_VAL(DRV2605_STATS)
+#include "stats/stats.h"
+#endif
+
+#if MYNEWT_VAL(DRV2605_STATS)
+/* Define the stats section and records */
+STATS_SECT_START(drv2605_stat_section)
+    STATS_SECT_ENTRY(errors)
+STATS_SECT_END
+
+/* Define stat names for querying */
+STATS_NAME_START(drv2605_stat_section)
+    STATS_NAME(drv2605_stat_section, errors)
+STATS_NAME_END(drv2605_stat_section)
+
+/* Global variable used to hold stats data */
+STATS_SECT_DECL(drv2605_stat_section) g_drv2605stats;
+#endif
+
+#if MYNEWT_VAL(DRV2605_LOG)
+#define LOG_MODULE_DRV2605 (306)
+#define DRV2605_INFO(...)  LOG_INFO(&_log, LOG_MODULE_DRV2605, __VA_ARGS__)
+#define DRV2605_ERR(...)   LOG_ERROR(&_log, LOG_MODULE_DRV2605, __VA_ARGS__)
+static struct log _log;
+#else
+#define DRV2605_INFO(...)
+#define DRV2605_ERR(...)
+#endif
+
+
+/**
+ * Writes a single byte to the specified register
+ *
+ * @param The Sesnsor interface
+ * @param The register address to write to
+ * @param The value to write
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_write8(struct sensor_itf *itf, uint8_t reg, uint8_t value)
+{
+    int rc;
+    uint8_t payload[2] = { reg, value};
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 2,
+        .buffer = payload
+    };
+
+    rc = hal_i2c_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC, 1);
+    if (rc) {
+        DRV2605_ERR("Failed to write to 0x%02X:0x%02X with value 0x%02X\n",
+                       data_struct.address, reg, value);
+#if MYNEWT_VAL(DRV2605_STATS)
+        STATS_INC(g_drv2605stats, errors);
+#endif
+    }
+
+    return rc;
+}
+
+/**
+ * Writes a multiple bytes to the specified register (MAX: 8 bytes)
+ *
+ * @param The Sesnsor interface
+ * @param The register address to write to
+ * @param The data buffer to write from
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_writelen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
+                      uint8_t len)
+{
+    int rc;
+    uint8_t payload[9] = { reg, 0, 0, 0, 0, 0, 0, 0, 0};
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = len + 1,
+        .buffer = payload
+    };
+
+    if (len > (sizeof(payload) - 1)) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    memcpy(&payload[1], buffer, len);
+
+    /* Register write */
+    rc = hal_i2c_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 
10, 1);
+    if (rc) {
+        DRV2605_ERR("I2C access failed at address 0x%02X\n", 
data_struct.address);
+        STATS_INC(g_drv2605stats, errors);
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+/**
+ * Reads a single byte from the specified register
+ *
+ * @param The Sensor interface
+ * @param The register address to read from
+ * @param Pointer to where the register value should be written
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value)
+{
+    int rc;
+    uint8_t payload;
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 1,
+        .buffer = &payload
+    };
+
+    /* Register write */
+    payload = reg;
+    rc = hal_i2c_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 
10, 0);
+    if (rc) {
+        DRV2605_ERR("I2C register write failed at address 0x%02X:0x%02X\n",
+                   data_struct.address, reg);
+#if MYNEWT_VAL(DRV2605_STATS)
+        STATS_INC(g_drv2605stats, errors);
+#endif
+        goto err;
+    }
+
+    /* Read one byte back */
+    payload = 0;
+    rc = hal_i2c_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 
1);
+    *value = payload;
+    if (rc) {
+        DRV2605_ERR("Failed to read from 0x%02X:0x%02X\n", 
data_struct.address, reg);
+#if MYNEWT_VAL(DRV2605_STATS)
+        STATS_INC(g_drv2605stats, errors);
+#endif
+    }
+
+err:
+    return rc;
+}
+
+/**
+ * Read data from the sensor of variable length (MAX: 8 bytes)
+ *
+ * @param The Sensor interface
+ * @param Register to read from
+ * @param Bufer to read into
+ * @param Length of the buffer
+ *
+ * @return 0 on success and non-zero on failure
+ */
+int
+drv2605_readlen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
+               uint8_t len)
+{
+    int rc;
+    uint8_t payload[23] = { reg, 0, 0, 0, 0, 0, 0, 0,
+                              0, 0, 0, 0, 0, 0, 0, 0,
+                              0, 0, 0, 0, 0, 0, 0};
+
+    struct hal_i2c_master_data data_struct = {
+        .address = itf->si_addr,
+        .len = 1,
+        .buffer = payload
+    };
+
+    /* Clear the supplied buffer */
+    memset(buffer, 0, len);
+
+    /* Register write */
+    rc = hal_i2c_master_write(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 
10, 0);
+    if (rc) {
+        DRV2605_ERR("I2C access failed at address 0x%02X\n", 
data_struct.address);
+#if MYNEWT_VAL(DRV2605_STATS)
+        STATS_INC(g_drv2605stats, errors);
+#endif
+        goto err;
+    }
+
+    /* Read len bytes back */
+    memset(payload, 0, sizeof(payload));
+    data_struct.len = len;
+    rc = hal_i2c_master_read(itf->si_num, &data_struct, OS_TICKS_PER_SEC / 10, 
1);
+    if (rc) {
+        DRV2605_ERR("Failed to read from 0x%02X:0x%02X\n", 
data_struct.address, reg);
+#if MYNEWT_VAL(DRV2605_STATS)
+        STATS_INC(g_drv2605stats, errors);
+#endif
+        goto err;
+    }
+
+    /* Copy the I2C results into the supplied buffer */
+    memcpy(buffer, payload, len);
+
+    return 0;
+err:
+    return rc;
+}
+
+//  general best fit values from datasheet 8.5.6
+int
+drv2605_default_cal(struct drv2605_cal *cal)
+{
+    cal->brake_factor = 2;
+    cal->loop_gain = 2;
+    cal->lra_sample_time = 3;
+    cal->lra_blanking_time = 1;
+    cal->lra_idiss_time = 1;
+    cal->auto_cal_time = 3;
+    cal->lra_zc_det_time = 0;
+
+    return 0;
+}
+
+/**
+ * Expects to be called back through os_dev_create().
+ *
+ * @param The device object associated with this accellerometer
+ * @param Argument passed to OS device init, unused
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_init(struct os_dev *dev, void *arg)
+{
+    struct drv2605 *drv2605;
+    struct sensor *sensor;
+    uint8_t id;
+    int rc;
+
+    if (!arg || !dev) {
+        rc = SYS_ENODEV;
+        goto err;
+    }
+
+    drv2605 = (struct drv2605 *) dev;
+
+#if MYNEWT_VAL(DRV2605_LOG)
+    log_register(dev->od_name, &_log, &log_console_handler, NULL, 
LOG_SYSLEVEL);
+#endif
+
+    sensor = &drv2605->sensor;
+
+#if MYNEWT_VAL(DRV2605_STATS)
+    /* Initialise the stats entry */
+    rc = stats_init(
+        STATS_HDR(g_drv2605stats),
+        STATS_SIZE_INIT_PARMS(g_drv2605stats, STATS_SIZE_32),
+        STATS_NAME_INIT_PARMS(drv2605_stat_section));
+    SYSINIT_PANIC_ASSERT(rc == 0);
+    /* Register the entry with the stats registry */
+    rc = stats_register(dev->od_name, STATS_HDR(g_drv2605stats));
+    SYSINIT_PANIC_ASSERT(rc == 0);
+#endif
+
+    rc = sensor_init(sensor, dev);
+    if (rc != 0) {
+        goto err;
+    }
+
+    /* Set the interface */
+    rc = sensor_set_interface(sensor, arg);
+    if (rc) {
+        goto err;
+    }
+
+    /* Check if we can read the chip address */
+    rc = drv2605_get_chip_id(arg, &id);
+    if (rc) {
+        goto err;
+    }
+
+    if (id != DRV2605_STATUS_DEVICE_ID_2605 && id != 
DRV2605_STATUS_DEVICE_ID_2605L) {
+        os_time_delay((OS_TICKS_PER_SEC * 100)/1000 + 1);
+
+        rc = drv2605_get_chip_id(arg, &id);
+        if (rc) {
+            goto err;
+        }
+
+        if (id != DRV2605_STATUS_DEVICE_ID_2605 && id != 
DRV2605_STATUS_DEVICE_ID_2605L) {
+            rc = SYS_EINVAL;
+            goto err;
+        }
+    }
+
+    return (0);
+err:
+    return (rc);
+}
+
+/**
+ * Get chip ID from the sensor
+ *
+ * @param The sensor interface
+ * @param Pointer to the variable to fill up chip ID in
+ * @return 0 on success, non-zero on failure
+ */
+int
+drv2605_get_chip_id(struct sensor_itf *itf, uint8_t *id)
+{
+    int rc;
+    uint8_t idtmp;
+
+    /* Check if we can read the chip address */
+    rc = drv2605_read8(itf, DRV2605_STATUS_ADDR, &idtmp);
+    if (rc) {
+        goto err;
+    }
+
+    *id = (idtmp & DRV2605_STATUS_DEVICE_ID_MASK) >> 
DRV2605_STATUS_DEVICE_ID_POS;
+
+    return 0;
+err:
+    return rc;
+}
+
+
+// NOTE diagnistics (and frankly all operation) will in all likelyhood fail if 
your motor is not SECURED to a mass
+// it cant be floating on your desk even for prototyping
+int
+drv2605_mode_diagnostic(struct sensor_itf *itf)
+{
+    int rc;
+    uint8_t temp;
+    uint8_t interval = 255;
+    uint8_t last_mode;
+
+    rc = drv2605_read8(itf, DRV2605_MODE_ADDR, &last_mode);
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_DIAGNOSTICS | 
DRV2605_MODE_ACTIVE);
+    if (rc) {
+        goto err;
+    }
+
+    // 4. Set the GO bit (write 0x01 to register 0x0C) to start the 
auto-calibration process
+    rc = drv2605_write8(itf, DRV2605_GO_ADDR, DRV2605_GO_GO);
+    if (rc) {
+        goto err;
+    }
+
+    // When diagnostic is complete, the GO bit automatically clears. Timeout 
after 255 x 5ms or 1275ms
+    do{
+        os_time_delay((OS_TICKS_PER_SEC * 5)/1000 + 1);
+        rc = drv2605_read8(itf, DRV2605_GO_ADDR, &temp);
+    } while (!rc && interval-- && (temp & DRV2605_GO_GO));
+
+    // if we timed out
+    if (!interval) {
+        rc = OS_TIMEOUT;
+        goto err;
+    }
+
+    // 5. Check the status of the DIAG_RESULT bit (in register 0x00) to ensure 
that the diagnostic routine is complete without faults.
+    rc = drv2605_read8(itf, DRV2605_STATUS_ADDR, &temp);
+    if (rc || (temp & DRV2605_STATUS_DIAG_RESULT_FAIL)) {
+        rc = OS_ERROR;
+        goto err;
+    }
+
+    // put back into standby like all other successful mode ops
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, (last_mode & 
(~DRV2605_MODE_STANDBY_MASK)) | DRV2605_MODE_STANDBY);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+
+int
+drv2605_send_defaults(struct sensor_itf *itf)
+{
+    int rc;
+
+    rc = drv2605_write8(itf, DRV2605_RATED_VOLTAGE_ADDR, 
MYNEWT_VAL(DRV2605_RATED_VOLTAGE));
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_OVERDRIVE_CLAMP_VOLTAGE_ADDR, 
MYNEWT_VAL(DRV2605_OD_CLAMP));
+    if (rc) {
+        goto err;
+    }
+
+    // TODO: Support ERM?
+    rc = drv2605_write8(itf, DRV2605_FEEDBACK_CONTROL_ADDR, 
((MYNEWT_VAL(DRV2605_CALIBRATED_BEMF_GAIN) & 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_MAX) << 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS) | DRV2605_FEEDBACK_CONTROL_N_LRA );
+    if (rc) {
+        goto err;
+    }
+
+    // They seem to always enable startup boost in the dev kit so throw it in?
+    rc = drv2605_write8(itf, DRV2605_CONTROL1_ADDR, 
((MYNEWT_VAL(DRV2605_DRIVE_TIME) & DRV2605_CONTROL1_DRIVE_TIME_MAX) << 
DRV2605_CONTROL1_DRIVE_TIME_POS) | DRV2605_CONTROL1_STARTUP_BOOST_ENABLE);
+    if (rc) {
+        goto err;
+    }
+
+    // TODO: Support ERM?
+    rc = drv2605_write8(itf, DRV2605_CONTROL3_ADDR, 
DRV2605_CONTROL3_LRA_DRIVE_MODE_ONCE | DRV2605_CONTROL3_LRA_OPEN_LOOP_CLOSED);
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, 
DRV2605_AUTO_CALIBRATION_COMPENSATION_RESULT_ADDR, 
MYNEWT_VAL(DRV2605_CALIBRATED_COMP));
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_AUTO_CALIBRATION_BACK_EMF_RESULT_ADDR, 
MYNEWT_VAL(DRV2605_CALIBRATED_BEMF));
+    if (rc) {
+        goto err;
+    }
+
+    // TODO: Support ERM?
+    // Library 6 is a closed-loop library tuned for LRAs. The library 
selection occurs through register 0x03 (see the (Address: 0x03) section).
+    rc = drv2605_write8(itf, DRV2605_WAVEFORM_CONTROL_ADDR, 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_LRA);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_get_power_mode(struct sensor_itf *itf, enum drv2605_power_mode 
*power_mode)
+{
+    int rc;
+    uint8_t last_mode;
+    bool standby, en_pin;
+
+    rc = drv2605_read8(itf, DRV2605_MODE_ADDR, &last_mode);
+    if (rc) {
+        goto err;
+    }
+
+    standby = last_mode & DRV2605_MODE_STANDBY_MASK;
+    en_pin = hal_gpio_read(itf->si_cs_pin);
+
+    if(!en_pin){
+        *power_mode = DRV2605_POWER_OFF;
+    }else{
+        if(standby){
+            *power_mode = DRV2605_POWER_STANDBY;
+        }else{
+            *power_mode = DRV2605_POWER_ACTIVE;
+        }
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_set_standby(struct sensor_itf *itf, bool standby)
+{
+    int rc;
+    uint8_t last_mode;
+
+    rc = drv2605_read8(itf, DRV2605_MODE_ADDR, &last_mode);
+    if (rc) {
+        goto err;
+    }
+
+    uint8_t mode;
+    if(standby){
+        mode = DRV2605_MODE_STANDBY;
+    }else{
+        mode = DRV2605_MODE_ACTIVE;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, (last_mode & 
(~DRV2605_MODE_STANDBY_MASK)) | mode);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_set_power_mode(struct sensor_itf *itf, enum drv2605_power_mode 
power_mode)
+{
+    // TODO: any hiccup in writing enable if already active? dont like the 
idea of reading it first though..
+    switch(power_mode) {
+        case DRV2605_POWER_STANDBY:
+            hal_gpio_write(itf->si_cs_pin, 1);
+            return drv2605_set_standby(itf, 1);
+        case DRV2605_POWER_ACTIVE:
+            hal_gpio_write(itf->si_cs_pin, 1);
+            return drv2605_set_standby(itf, 0);
+        case DRV2605_POWER_OFF:
+            hal_gpio_write(itf->si_cs_pin, 0);
+            return 0;
+        default:
+            return SYS_EINVAL;
+    }
+}
+
+int
+drv2605_validate_cal(struct drv2605_cal *cal)
+{
+    int rc;
+
+    if (cal->brake_factor > DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->loop_gain > DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->lra_sample_time > DRV2605_CONTROL2_SAMPLE_TIME_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->lra_blanking_time > DRV2605_BLANKING_TIME_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->lra_idiss_time > DRV2605_IDISS_TIME_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->auto_cal_time > DRV2605_CONTROL4_AUTO_CAL_TIME_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    if (cal->lra_zc_det_time > DRV2605_CONTROL4_ZC_DET_TIME_MAX) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+// if succesful calibration overrites DRV2605_BEMF_GAIN, 
DRV2605_CALIBRATED_COMP and DRV2605_CALIBRATED_BEMF
+int
+drv2605_mode_calibrate(struct sensor_itf *itf, struct drv2605_cal *cal)
+{
+    int rc;
+    uint8_t temp;
+    uint8_t interval = 255;
+    uint8_t last_fb, last_mode;
+
+    rc = drv2605_read8(itf, DRV2605_MODE_ADDR, &last_mode);
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_validate_cal(cal);
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_read8(itf, DRV2605_FEEDBACK_CONTROL_ADDR, &last_fb);
+    if (rc) {
+        goto err;
+    }
+
+    // technically only need to protect the ERM_LRA bit as DRV2605_BEMF_GAIN 
will be altered anyway, but lets show em our fancy bit math
+    uint8_t mask = (DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_MASK | 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MASK);
+    uint8_t altered = (cal->brake_factor << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS) | (cal->loop_gain << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS);
+    uint8_t new = (last_fb & (~mask)) | altered;
+    rc = drv2605_write8(itf, DRV2605_FEEDBACK_CONTROL_ADDR, new);
+    if (rc) {
+        goto err;
+    }
+
+    int8_t idiss_lsb = cal->lra_idiss_time & 0x03;
+    int8_t blanking_lsb = cal->lra_blanking_time & 0x03;
+    int8_t ctrl2 = (cal->lra_sample_time << DRV2605_CONTROL2_SAMPLE_TIME_POS) 
| (blanking_lsb << DRV2605_CONTROL2_BLANKING_TIME_LSB_POS) | (idiss_lsb << 
DRV2605_CONTROL2_IDISS_TIME_LSB_POS);
+    rc = drv2605_write8(itf, DRV2605_CONTROL2_ADDR, ctrl2);
+    if (rc) {
+        goto err;
+    }
+
+    int8_t blanking_msb = cal->lra_blanking_time & 0x0C;
+    int8_t idiss_msb = cal->lra_idiss_time & 0x0C;
+    rc = drv2605_write8(itf, DRV2605_CONTROL5_ADDR, blanking_msb  << 
DRV2605_CONTROL5_BLANKING_TIME_MSB_POS | idiss_msb  << 
DRV2605_CONTROL5_IDISS_TIME_MSB_POS );
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_CONTROL4_ADDR, (cal->lra_zc_det_time << 
DRV2605_CONTROL4_ZC_DET_TIME_POS) | (cal->auto_cal_time << 
DRV2605_CONTROL4_AUTO_CAL_TIME_POS));
+    if (rc) {
+        goto err;
+    }
+
+    // 2. Write a value of 0x07 to register 0x01. This value moves the 
DRV2605L device out of STANDBY and places the MODE[2:0] bits in 
auto-calibration mode.
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_AUTO_CALIBRATION 
| DRV2605_MODE_ACTIVE);
+    if (rc) {
+        goto err;
+    }
+
+    // 4. Set the GO bit (write 0x01 to register 0x0C) to start the 
auto-calibration process.
+    rc = drv2605_write8(itf, DRV2605_GO_ADDR, DRV2605_GO_GO);
+    if (rc) {
+        goto err;
+    }
+
+    // When auto calibration is complete, the GO bit automatically clears. The 
auto-calibration results are written in the respective registers as shown in 
Figure 25.
+    do{
+        os_time_delay((OS_TICKS_PER_SEC * 5)/1000 + 1);
+        rc = drv2605_read8(itf, DRV2605_GO_ADDR, &temp);
+    } while (!rc && interval-- && (temp & DRV2605_GO_GO));
+
+    // if we timed out
+    if (!interval) {
+        rc = OS_TIMEOUT;
+        goto err;
+    }
+
+    // 5. Check the status of the DIAG_RESULT bit (in register 0x00) to ensure 
that the auto-calibration routine is complete without faults
+    rc = drv2605_read8(itf, DRV2605_STATUS_ADDR, &temp);
+    if (rc || (temp & DRV2605_STATUS_DIAG_RESULT_FAIL)) {
+        rc = OS_ERROR;
+        goto err;
+    }
+
+    // put back into standby like all other successful mode ops
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, (last_mode & 
(~DRV2605_MODE_STANDBY_MASK)) | DRV2605_MODE_STANDBY);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_mode_rom(struct sensor_itf *itf)
+{
+    int rc;
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_INTERNAL_TRIGGER 
| DRV2605_MODE_STANDBY);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_mode_rtp(struct sensor_itf *itf)
+{
+    int rc;
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_RTP | 
DRV2605_MODE_STANDBY);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_mode_pwm(struct sensor_itf *itf)
+{
+    int rc;
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_PWM_ANALOG_INPUT 
| DRV2605_MODE_STANDBY);
+    if (rc) {
+        goto err;
+    }
+
+    rc = drv2605_write8(itf, DRV2605_CONTROL3_ADDR, 
DRV2605_CONTROL3_N_PWM_ANALOG_MASK);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+// NOTE reset sets mode back to standby
+// obviously you must _configure again after a reset
+int
+drv2605_mode_reset(struct sensor_itf *itf)
+{
+    int rc;
+    uint8_t temp;
+    uint8_t interval = 255;
+
+    rc = drv2605_write8(itf, DRV2605_MODE_ADDR, DRV2605_MODE_RESET);
+    if (rc) {
+        goto err;
+    }
+
+    // When reset is complete, the reset bit automatically clears. Timeout 
after 255 x 5ms or 1275ms
+    do{
+        os_time_delay((OS_TICKS_PER_SEC * 5)/1000 + 1);
+        rc = drv2605_read8(itf, DRV2605_MODE_ADDR, &temp);
+    } while (!rc && interval-- && (temp & DRV2605_MODE_RESET));
+
+    // if we timed out
+    if (!interval) {
+        rc = OS_TIMEOUT;
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+// note device MSUT be reconfigured for an operational state after a an error 
or after succsessful diag and calibration and reset
+// upon success the device is always left in DRV2605_POWER_STANDBY state
+// no device state is guaranteed for error returns
+int
+drv2605_config(struct drv2605 *drv2605, struct drv2605_cfg *cfg)
+{
+    int rc;
+    struct sensor_itf *itf;
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = hal_gpio_init_out(itf->si_cs_pin, 1);
+    if (rc) {
+        return rc;
+    }
+
+    rc = drv2605_send_defaults(itf);
+    if (rc) {
+        return rc;
+    }
+
+    switch(cfg->op_mode) {
+        case DRV2605_OP_ROM:
+            return drv2605_mode_rom(itf);
+        case DRV2605_OP_PWM:
+            return drv2605_mode_pwm(itf);
+        case DRV2605_OP_ANALOG:
+            return drv2605_mode_pwm(itf);
+        case DRV2605_OP_RTP:
+            return drv2605_mode_rtp(itf);
+        case DRV2605_OP_DIAGNOSTIC:
+            return drv2605_mode_diagnostic(itf);
+        case DRV2605_OP_CALIBRATION:
+            return drv2605_mode_calibrate(itf, &cfg->cal);
+        case DRV2605_OP_RESET:
+            return drv2605_mode_reset(itf);
+        default:
+            return SYS_EINVAL;
+    }
+}
+
+int
+drv2605_load_rom(struct sensor_itf *itf, uint8_t* wav_ids, size_t len)
+{
+    int rc;
+
+    if (len > 8) {
+        rc = SYS_EINVAL;
+        goto err;
+    }
+
+    rc = drv2605_writelen(itf, DRV2605_WAVEFORM_SEQUENCER_ADDR, wav_ids, len);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_trigger_rom(struct sensor_itf *itf)
+{
+    return drv2605_write8(itf, DRV2605_GO_ADDR, DRV2605_GO_GO);
+}
+
+// theres sadly no interrupt for knowing when long running roms are finished, 
youll need to block on this or set a callout
+// to poll for completion
+int
+drv2605_rom_busy(struct sensor_itf *itf, bool *status)
+{
+    int rc;
+    uint8_t temp;
+
+    rc = drv2605_read8(itf, DRV2605_GO_ADDR, &temp);
+    if (rc) {
+        goto err;
+    }
+
+    *status = temp;
+    return 0;
+err:
+    return rc;
+}
+
+int
+drv2605_load_rtp(struct sensor_itf *itf, uint8_t value)
+{
+    int rc;
+
+    rc = drv2605_write8(itf, DRV2605_REAL_TIME_PLAYBACK_INPUT_ADDR, value);
+    if (rc) {
+        goto err;
+    }
+
+    return 0;
+err:
+    return rc;
+}
diff --git a/hw/drivers/drv2605/src/drv2605_priv.h 
b/hw/drivers/drv2605/src/drv2605_priv.h
new file mode 100644
index 0000000..9acc295
--- /dev/null
+++ b/hw/drivers/drv2605/src/drv2605_priv.h
@@ -0,0 +1,328 @@
+/*
+ * 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 __DRV2605_PRIV_H__
+#define __DRV2605_PRIV_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <math.h>
+
+
+#define DRV2605_STATUS_ADDR                                     0x00
+
+#define DRV2605_STATUS_DEVICE_ID_POS                            5
+#define DRV2605_STATUS_DEVICE_ID_MASK                           (0x7 << 
DRV2605_STATUS_DEVICE_ID_POS)
+
+#define DRV2605_STATUS_DEVICE_ID_2605                           (3)
+#define DRV2605_STATUS_DEVICE_ID_2604                           (4)
+#define DRV2605_STATUS_DEVICE_ID_2604L                          (6)
+#define DRV2605_STATUS_DEVICE_ID_2605L                          (7)
+
+#define DRV2605_LOW_VOLTAGE_ID                                  0x07
+
+
+#define DRV2605_STATUS_DIAG_RESULT_POS                          3
+#define DRV2605_STATUS_DIAG_RESULT_SUCCESS                      ((0) << 
DRV2605_STATUS_DIAG_RESULT_POS)
+#define DRV2605_STATUS_DIAG_RESULT_FAIL                         ((1) << 
DRV2605_STATUS_DIAG_RESULT_POS)
+
+// FB_STS is reserved on the drv2605l and therefore to be ingored
+#define DRV2605_STATUS_FB_STS_POS                               2
+#define DRV2605_STATUS_FB_STS                                   ((1) << 
DRV2605_STATUS_FB_STS_POS)
+
+#define DRV2605_STATUS_OVER_TEMP_POS                            1
+#define DRV2605_STATUS_OVER_TEMP                                ((1) << 
DRV2605_STATUS_OVER_TEMP_POS)
+
+#define DRV2605_STATUS_OC_DETECT_POS                            0
+#define DRV2605_STATUS_OC_DETECT                                ((1) << 
DRV2605_STATUS_OC_DETECT_POS)
+
+
+#define DRV2605_MODE_ADDR                                       0x01
+
+#define DRV2605_MODE_DEV_RESET_POS                              7
+#define DRV2605_MODE_RESET                                      ((1) << 
DRV2605_MODE_DEV_RESET_POS)
+
+#define DRV2605_MODE_STANDBY_POS                                6
+#define DRV2605_MODE_STANDBY_MASK                               ((1) << 
DRV2605_MODE_STANDBY_POS)
+#define DRV2605_MODE_ACTIVE                                     ((0) << 
DRV2605_MODE_STANDBY_POS)
+#define DRV2605_MODE_STANDBY                                    ((1) << 
DRV2605_MODE_STANDBY_POS)
+
+#define DRV2605_MODE_MODE_POS                                   0
+#define DRV2605_MODE_MODE_MASK                                   ((7) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_INTERNAL_TRIGGER                           ((0) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_EXTERNAL_TRIGGER_EDGE                      ((1) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_EXTERNAL_TRIGGER_LEVEL                     ((2) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_PWM_ANALOG_INPUT                           ((3) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_AUDIO_TO_VIBE                              ((4) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_RTP                                        ((5) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_DIAGNOSTICS                                ((6) << 
DRV2605_MODE_MODE_POS)
+#define DRV2605_MODE_AUTO_CALIBRATION                           ((7) << 
DRV2605_MODE_MODE_POS)
+
+#define DRV2605_REAL_TIME_PLAYBACK_INPUT_ADDR                   0x02
+
+#define DRV2605_WAVEFORM_CONTROL_ADDR                           0x03
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS                0
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_EMPTY              ((0x00) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_A                  ((0x01) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_B                  ((0x02) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_C                  ((0x03) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_D                  ((0x04) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_E                  ((0x05) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+#define DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_LRA                ((0x06) << 
DRV2605_WAVEFORM_CONTROL_LIBRARY_SEL_POS)
+
+#define DRV2605_WAVEFORM_CONTROL_HI_Z_POS                       4
+
+#define DRV2605_WAVEFORM_SEQUENCER_ADDR                         0x04
+
+#define DRV2605_GO_ADDR                                         0x0C
+#define DRV2605_GO_GO_POS                                       0
+#define DRV2605_GO_GO                                           ((1) << 
DRV2605_GO_GO_POS)
+
+
+#define DRV2605_OVERDRIVE_TIME_OFFSET_ADDR                      0x0D
+#define DRV2605_SUSTAIN_TIME_OFFSET_POSITIVE_ADDR               0x0E
+#define DRV2605_SUSTAIN_TIME_OFFSET_NEGATIVE_ADDR               0x0F
+#define DRV2605_BRAKE_TIME_OFFSET_ADDR                          0x10
+
+#define DRV2605_AUDIO_TO_VIBE_CONTROL_ADDR                      0x11
+#define DRV2605_AUDIO_TO_VIBE_CONTROL_ATH_PEAK_TIME_POS         2
+#define DRV2605_AUDIO_TO_VIBE_CONTROL_ATH_FILTER_POS            0
+
+#define DRV2605_AUDIO_TO_VIBE_MINIMUM_INPUT_LEVEL_ADDR          0x12
+#define DRV2605_AUDIO_TO_VIBE_MAXIMUM_INPUT_LEVEL_ADDR          0x13
+#define DRV2605_AUDIO_TO_DRIVE_MINIMUM_OUTPUT_DRIVE_ADDR        0x14
+#define DRV2605_AUDIO_TO_DRIVE_MAXIMUM_OUTPUT_DRIVE_ADDR        0x15
+#define DRV2605_RATED_VOLTAGE_ADDR                              0x16
+#define DRV2605_OVERDRIVE_CLAMP_VOLTAGE_ADDR                    0x17
+#define DRV2605_AUTO_CALIBRATION_COMPENSATION_RESULT_ADDR       0x18
+#define DRV2605_AUTO_CALIBRATION_BACK_EMF_RESULT_ADDR           0x19
+
+#define DRV2605_FEEDBACK_CONTROL_ADDR                           0x1A
+
+#define DRV2605_FEEDBACK_CONTROL_N_ERM_LRA_POS                  7
+#define DRV2605_FEEDBACK_CONTROL_N_ERM                          ((0) << 
DRV2605_FEEDBACK_CONTROL_N_ERM_LRA_POS)
+#define DRV2605_FEEDBACK_CONTROL_N_LRA                          ((1) << 
DRV2605_FEEDBACK_CONTROL_N_ERM_LRA_POS)
+
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS            4
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_MAX            7
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_MASK           
((DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_MAX) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_1X             ((0) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_2X             ((1) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_3X             ((2) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_4X             ((3) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_6X             ((4) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_8X             ((5) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_16X            ((6) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+#define DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_DISBLED        ((7) << 
DRV2605_FEEDBACK_CONTROL_FB_BRAKE_FACTOR_POS)
+
+
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS                  2
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MAX                  3
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MASK                 
((DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MAX) << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS)
+
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_LOW                  ((0) << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_MEDIUM               ((1) << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_HIGH                 ((2) << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_VERY_HIGH            ((3) << 
DRV2605_FEEDBACK_CONTROL_LOOP_GAIN_POS)
+
+
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS                  0
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_MAX                  3
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_3P75X            ((0) << 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_7P5X             ((1) << 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_15X              ((2) << 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_22X              ((3) << 
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_POS)
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_ERM_0P255X           
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_3P75X
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_ERM_0P7875X          
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_7P5X
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_ERM_1P365X           
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_15X
+#define DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_ERM_3P0X             
DRV2605_FEEDBACK_CONTROL_BEMF_GAIN_LRA_22X
+
+#define DRV2605_CONTROL1_ADDR                                   0x1B
+#define DRV2605_CONTROL1_STARTUP_BOOST_POS                      7
+#define DRV2605_CONTROL1_STARTUP_BOOST_DISABLE                  ((0) << 
DRV2605_CONTROL1_STARTUP_BOOST_POS)
+#define DRV2605_CONTROL1_STARTUP_BOOST_ENABLE                   ((1) << 
DRV2605_CONTROL1_STARTUP_BOOST_POS)
+
+#define DRV2605_CONTROL1_AC_COUPLE_POS                          5
+#define DRV2605_CONTROL1_DRIVE_TIME_POS                         0
+#define DRV2605_CONTROL1_DRIVE_TIME_MAX                         31
+
+#define DRV2605_CONTROL2_ADDR                                   0x1C
+#define DRV2605_CONTROL2_BIDIR_INPUT_POS                        7
+#define DRV2605_CONTROL2_BIDIR_INPUT_UNI                        ((0) << 
DRV2605_CONTROL2_BIDIR_INPUT_POS)
+#define DRV2605_CONTROL2_BIDIR_INPUT_BI                         ((1) << 
DRV2605_CONTROL2_BIDIR_INPUT_POS)
+
+#define DRV2605_CONTROL2_BRAKE_STABILIZER_POS                   6
+#define DRV2605_CONTROL2_BRAKE_STABILIZER_ENABLE                ((1) << 
DRV2605_CONTROL2_BRAKE_STABILIZER_POS)
+#define DRV2605_CONTROL2_BRAKE_STABILIZER_DISABLE               ((0) << 
DRV2605_CONTROL2_BRAKE_STABILIZER_POS)
+
+#define DRV2605_CONTROL2_SAMPLE_TIME_POS                        4
+#define DRV2605_CONTROL2_SAMPLE_TIME_MAX                        3
+#define DRV2605_CONTROL2_SAMPLE_TIME_MASK                       
((DRV2605_CONTROL2_SAMPLE_TIME_MAX) << DRV2605_CONTROL2_SAMPLE_TIME_POS)
+
+#define DRV2605_CONTROL2_SAMPLE_TIME_150                        ((0) << 
DRV2605_CONTROL2_SAMPLE_TIME_POS)
+#define DRV2605_CONTROL2_SAMPLE_TIME_200                        ((1) << 
DRV2605_CONTROL2_SAMPLE_TIME_POS)
+#define DRV2605_CONTROL2_SAMPLE_TIME_250                        ((2) << 
DRV2605_CONTROL2_SAMPLE_TIME_POS)
+#define DRV2605_CONTROL2_SAMPLE_TIME_300                        ((3) << 
DRV2605_CONTROL2_SAMPLE_TIME_POS)
+
+#define DRV2605_BLANKING_TIME_MAX                               15
+#define DRV2605_CONTROL2_BLANKING_TIME_LSB_POS                  2
+
+#define DRV2605_IDISS_TIME_MAX                                  15
+
+#define DRV2605_CONTROL2_IDISS_TIME_LSB_POS                     0
+
+
+#define DRV2605_CONTROL3_ADDR                                   0x1D
+#define DRV2605_CONTROL3_NG_THRESH_POS                          6
+#define DRV2605_CONTROL3_NG_THRESH_DISABLED                     ((0) << 
DRV2605_CONTROL3_NG_THRESH_POS)
+#define DRV2605_CONTROL3_NG_THRESH_2PERCENT                     ((1) << 
DRV2605_CONTROL3_NG_THRESH_POS)
+#define DRV2605_CONTROL3_NG_THRESH_4PERCENT                     ((2) << 
DRV2605_CONTROL3_NG_THRESH_POS)
+#define DRV2605_CONTROL3_NG_THRESH_8PERCENT                     ((3) << 
DRV2605_CONTROL3_NG_THRESH_POS)
+
+
+#define DRV2605_CONTROL3_ERM_OPEN_LOOP_POS                      5
+#define DRV2605_CONTROL3_ERM_OPEN_LOOP_DISABLED                 ((0) << 
DRV2605_CONTROL3_ERM_OPEN_LOOP_POS)
+#define DRV2605_CONTROL3_ERM_OPEN_LOOP_ENABLED                  ((1) << 
DRV2605_CONTROL3_ERM_OPEN_LOOP_POS)
+
+#define DRV2605_CONTROL3_SUPPLY_COMP_DIS_POS                    4
+#define DRV2605_CONTROL3_SUPPLY_COMP_DIS_ENABLED                ((0) << 
DRV2605_CONTROL3_SUPPLY_COMP_DIS_POS)
+#define DRV2605_CONTROL3_SUPPLY_COMP_DIS_DISABLED               ((1) << 
DRV2605_CONTROL3_SUPPLY_COMP_DIS_POS)
+
+#define DRV2605_CONTROL3_DATA_FORMAT_RTP_POS                    3
+#define DRV2605_CONTROL3_DATA_FORMAT_RTP_SIGNED                 ((0) << 
DRV2605_CONTROL3_DATA_FORMAT_RTP_POS)
+#define DRV2605_CONTROL3_DATA_FORMAT_RTP_UNSIGNED               ((1) << 
DRV2605_CONTROL3_DATA_FORMAT_RTP_POS)
+
+#define DRV2605_CONTROL3_LRA_DRIVE_MODE_POS                     2
+#define DRV2605_CONTROL3_LRA_DRIVE_MODE_ONCE                    ((0) << 
DRV2605_CONTROL3_LRA_DRIVE_MODE_POS)
+#define DRV2605_CONTROL3_LRA_DRIVE_MODE_TWICE                   ((1) << 
DRV2605_CONTROL3_LRA_DRIVE_MODE_POS)
+
+#define DRV2605_CONTROL3_N_PWM_ANALOG_POS                       1
+#define DRV2605_CONTROL3_N_PWM_ANALOG_MASK                      ((1) << 
DRV2605_CONTROL3_N_PWM_ANALOG_POS)
+#define DRV2605_CONTROL3_N_PWM_ANALOG_PWM                       ((0) << 
DRV2605_CONTROL3_N_PWM_ANALOG_POS)
+#define DRV2605_CONTROL3_N_PWM_ANALOG_ANALOG                    ((1) << 
DRV2605_CONTROL3_N_PWM_ANALOG_POS)
+
+#define DRV2605_CONTROL3_LRA_OPEN_LOOP_POS                      0
+#define DRV2605_CONTROL3_LRA_OPEN_LOOP_AUTO                     ((0) << 
DRV2605_CONTROL3_LRA_OPEN_LOOP_POS)
+#define DRV2605_CONTROL3_LRA_OPEN_LOOP_OPEN                     ((1) << 
DRV2605_CONTROL3_LRA_OPEN_LOOP_POS)
+#define DRV2605_CONTROL3_LRA_OPEN_LOOP_CLOSED                   
DRV2605_CONTROL3_LRA_OPEN_LOOP_AUTO
+
+#define DRV2605_CONTROL4_ADDR                                   0x1E
+
+#define DRV2605_CONTROL4_ZC_DET_TIME_POS                        6
+#define DRV2605_CONTROL4_ZC_DET_TIME_MAX                        3
+#define DRV2605_CONTROL4_ZC_DET_TIME_100                        ((0) << 
DRV2605_CONTROL4_ZC_DET_TIME_POS)
+#define DRV2605_CONTROL4_ZC_DET_TIME_200                        ((1) << 
DRV2605_CONTROL4_ZC_DET_TIME_POS)
+#define DRV2605_CONTROL4_ZC_DET_TIME_300                        ((2) << 
DRV2605_CONTROL4_ZC_DET_TIME_POS)
+#define DRV2605_CONTROL4_ZC_DET_TIME_390                        ((3) << 
DRV2605_CONTROL4_ZC_DET_TIME_POS)
+
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_POS                      4
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_MAX                      3
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_150                      ((0) << 
DRV2605_CONTROL4_AUTO_CAL_TIME_POS)
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_250                      ((1) << 
DRV2605_CONTROL4_AUTO_CAL_TIME_POS)
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_500                      ((2) << 
DRV2605_CONTROL4_AUTO_CAL_TIME_POS)
+#define DRV2605_CONTROL4_AUTO_CAL_TIME_1000                     ((3) << 
DRV2605_CONTROL4_AUTO_CAL_TIME_POS)
+
+#define DRV2605_CONTROL4_OTP_STATUS_POS                         2
+#define DRV2605_CONTROL4_OTP_PROGRAM_POS                        0
+
+#define DRV2605_CONTROL5_ADDR                                   0x1F
+#define DRV2605_CONTROL5_AUTO_OL_CNT_POS                        6
+#define DRV2605_CONTROL5_AUTO_OL_CNT_3                          ((0) << 
DRV2605_CONTROL5_AUTO_OL_CNT_POS)
+#define DRV2605_CONTROL5_AUTO_OL_CNT_4                          ((1) << 
DRV2605_CONTROL5_AUTO_OL_CNT_POS)
+#define DRV2605_CONTROL5_AUTO_OL_CNT_5                          ((2) << 
DRV2605_CONTROL5_AUTO_OL_CNT_POS)
+#define DRV2605_CONTROL5_AUTO_OL_CNT_6                          ((3) << 
DRV2605_CONTROL5_AUTO_OL_CNT_POS)
+
+#define DRV2605_CONTROL5_LRA_AUTO_OPEN_LOOP_POS                 5
+#define DRV2605_CONTROL5_LRA_AUTO_OPEN_LOOP_NEVER               ((0) << 
DRV2605_CONTROL5_LRA_AUTO_OPEN_LOOP_POS)
+#define DRV2605_CONTROL5_LRA_AUTO_OPEN_LOOP_AUTO                ((1) << 
DRV2605_CONTROL5_LRA_AUTO_OPEN_LOOP_POS)
+
+#define DRV2605_CONTROL5_PLAYBACK_INTERVAL_POS                  4
+#define DRV2605_CONTROL5_PLAYBACK_INTERVAL_5                    ((0) << 
DRV2605_CONTROL5_PLAYBACK_INTERVAL_POS)
+#define DRV2605_CONTROL5_PLAYBACK_INTERVAL_1                    ((1) << 
DRV2605_CONTROL5_PLAYBACK_INTERVAL_POS)
+
+#define DRV2605_CONTROL5_BLANKING_TIME_MSB_POS                  2
+#define DRV2605_CONTROL5_IDISS_TIME_MSB_POS                     0
+
+#define DRV2605_LRA_OPEN_LOOP_PERIOD_ADDR                       0x20
+
+#define DRV2605_VBAT_VOLTAGE_MONITOR_ADDR                       0x21
+#define DRV2605_LRA_RESONANCE_PERIOD_ADDR                       0x22
+
+struct sensor_itf;
+
+/**
+ * Reads a single byte from the specified register
+ *
+ * @param The sensor interface
+ * @param The register address to read from
+ * @param Pointer to where the register value should be written
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_read8(struct sensor_itf *itf, uint8_t reg, uint8_t *value);
+
+
+/**
+ * Read data from the sensor of variable length (MAX: 8 bytes)
+ *
+ * @param The Sensor interface
+ * @param Register to read from
+ * @param Bufer to read into
+ * @param Length of the buffer
+ *
+ * @return 0 on success and non-zero on failure
+ */
+int
+drv2605_readlen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
+               uint8_t len);
+
+/**
+ * Writes a single byte to the specified register
+ *
+ * @param The sensor interface
+ * @param The register address to write to
+ * @param The value to write
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_write8(struct sensor_itf *itf, uint8_t reg, uint8_t value);
+
+/**
+ * Writes a multiple bytes to the specified register
+ *
+ * @param The sensor interface
+ * @param The register address to write to
+ * @param The data buffer to write from
+ *
+ * @return 0 on success, non-zero error on failure.
+ */
+int
+drv2605_writelen(struct sensor_itf *itf, uint8_t reg, uint8_t *buffer,
+                uint8_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __DRV2605_PRIV_H__ */
+
diff --git a/hw/drivers/drv2605/src/drv2605_shell.c 
b/hw/drivers/drv2605/src/drv2605_shell.c
new file mode 100644
index 0000000..d56f694
--- /dev/null
+++ b/hw/drivers/drv2605/src/drv2605_shell.c
@@ -0,0 +1,504 @@
+/*
+ * 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 <string.h>
+#include <errno.h>
+#include "sysinit/sysinit.h"
+#include "console/console.h"
+#include "shell/shell.h"
+#include "drv2605_priv.h"
+#include "drv2605/drv2605.h"
+#include "sensor/sensor.h"
+#include "parse/parse.h"
+
+#if MYNEWT_VAL(DRV2605_CLI)
+
+static int drv2605_shell_cmd(int argc, char **argv);
+
+static struct shell_cmd drv2605_shell_cmd_struct = {
+    .sc_cmd = "drv2605",
+    .sc_cmd_func = drv2605_shell_cmd
+};
+
+static int
+drv2605_shell_err_too_many_args(char *cmd_name)
+{
+    console_printf("Error: too many arguments for command \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+drv2605_shell_err_too_few_args(char *cmd_name)
+{
+    console_printf("Error: too few arguments for command \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+drv2605_shell_err_unknown_arg(char *cmd_name)
+{
+    console_printf("Error: unknown argument \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+drv2605_shell_err_invalid_arg(char *cmd_name)
+{
+    console_printf("Error: invalid argument \"%s\"\n",
+                   cmd_name);
+    return EINVAL;
+}
+
+static int
+drv2605_shell_help(void)
+{
+    console_printf("%s cmd  [flags...]\n", drv2605_shell_cmd_struct.sc_cmd);
+    console_printf("cmd:\n");
+    console_printf("\tchip_id\n");
+    console_printf("\tload_cal [brake_factor loop_gain lra_sample_time 
lra_blanking_time lra_idiss_time auto_cal_time lra_zc_det_time]\n");
+    console_printf("\tload_rom [up to 8 uint8_t]\n");
+    console_printf("\tload_rtp [uint8_t]\n");
+    console_printf("\top_mode [reset | rom | pwm | analog | rtp | diag | 
cal]\n");
+    console_printf("\tpower_mode [deep | standby | active]\n");
+    console_printf("\ttrigger\n");
+    console_printf("\tpeek [reg]\n");
+    console_printf("\tpoke [reg value]\n");
+    console_printf("\tdump_cal\n");
+    console_printf("\tdump_all\n");
+    return 0;
+}
+
+static int
+drv2605_shell_cmd_load_rom(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    uint8_t waveform[8] = {0};
+    uint8_t len = argc-2;
+    uint8_t i;
+    struct sensor_itf *itf;
+
+    if (argc > 10) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    } else if (argc < 3) {
+        return drv2605_shell_err_too_few_args(argv[1]);
+    }
+
+    for (i = 0; i<len;i++) {
+        waveform[i] = parse_ll_bounds(argv[i+2], 0, 255, &rc);
+        if (rc != 0) {
+            goto err;
+        }
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_load_rom(itf, waveform, len);
+    if (rc) {
+        console_printf("load failed %d\n", rc);
+    }else{
+        console_printf("load succeeded\n");
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_cmd_trigger_rom(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    struct sensor_itf *itf;
+
+    if (argc > 3) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_trigger_rom(itf);
+    if (rc) {
+        console_printf("trigger failed %d\n", rc);
+    }else{
+        console_printf("trigger succeeded\n");
+    }
+
+    return rc;
+}
+
+static int
+drv2605_shell_cmd_get_chip_id(int argc, char **argv, struct drv2605 *drv2605)
+{
+    uint8_t id;
+    int rc;
+    struct sensor_itf *itf;
+
+    if (argc > 3) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    /* Display the chip id */
+    if (argc == 2) {
+        rc = drv2605_get_chip_id(itf, &id);
+        if (rc) {
+            console_printf("chipid failed %d\n", rc);
+        }else{
+            console_printf("0x%02X\n", id);
+        }
+    }
+
+    return 0;
+}
+
+static int
+drv2605_shell_cmd_dump_cal(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    uint8_t tmp[3];
+    struct sensor_itf *itf;
+
+    if (argc > 3) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_readlen(itf, 
DRV2605_AUTO_CALIBRATION_COMPENSATION_RESULT_ADDR, &tmp[0], sizeof(tmp));
+    if (rc) {
+        console_printf("dump failed %d\n", rc);
+        goto err;
+    }
+    console_printf("\nDRV2605_CALIBRATED_COMP: 
0x%02X\nDRV2605_CALIBRATED_BEMF: 0x%02X\nDRV2605_CALIBRATED_BEMF_GAIN: %0d\n", 
tmp[0], tmp[1], tmp[2] & 0x03);
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_cmd_peek(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    uint8_t value;
+    uint8_t reg;
+    struct sensor_itf *itf;
+
+    if (argc > 3) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    } else if (argc < 3) {
+        return drv2605_shell_err_too_few_args(argv[1]);
+    }
+
+    reg = parse_ll_bounds(argv[2], 0, 34, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_read8(itf, reg, &value);
+    if (rc) {
+        console_printf("peek failed %d\n", rc);
+    }else{
+        console_printf("value: 0x%02X\n", value);
+    }
+
+    return 0;
+}
+
+static int
+drv2605_shell_cmd_poke(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    uint8_t reg;
+    uint8_t value;
+    struct sensor_itf *itf;
+
+    if (argc > 4) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    } else if (argc < 4) {
+        return drv2605_shell_err_too_few_args(argv[1]);
+    }
+
+    reg = parse_ll_bounds(argv[2], 0, 34, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    value = parse_ll_bounds(argv[3], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[3]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_write8(itf, reg, value);
+    if (rc) {
+        console_printf("poke failed %d\n", rc);
+    }else{
+        console_printf("wrote: 0x%02X to 0x%02X\n", value, reg);
+    }
+
+    return 0;
+}
+
+static int
+drv2605_shell_cmd_dump_all(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    uint8_t value;
+    int i;
+    struct sensor_itf *itf;
+
+    if (argc > 3) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    for (i=0; i<=34; i++){
+        rc = drv2605_read8(itf, i, &value);
+        if (rc) {
+            console_printf("dump failed %d\n", rc);
+            goto err;
+        }else{
+            console_printf("reg 0x:%02X = 0x%02X\n", i, value);
+        }
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_load_cal(int argc, char **argv, struct drv2605_cal *cal)
+{
+    int rc;
+
+    cal->brake_factor = parse_ll_bounds(argv[2], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->loop_gain = parse_ll_bounds(argv[3], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->lra_sample_time = parse_ll_bounds(argv[4], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->lra_blanking_time = parse_ll_bounds(argv[5], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->lra_idiss_time = parse_ll_bounds(argv[6], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->auto_cal_time = parse_ll_bounds(argv[7], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    cal->lra_zc_det_time = parse_ll_bounds(argv[8], 0, 255, &rc);
+    if (rc != 0) {
+        return drv2605_shell_err_invalid_arg(argv[2]);
+    }
+
+    return 0;
+}
+
+static int
+drv2605_shell_cmd_load_cal(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+
+    if(argc == 2) {
+        return drv2605_default_cal(&drv2605->cfg.cal);
+    }else if (argc > 9) {
+        return drv2605_shell_err_too_many_args(argv[1]);
+    } else if (argc < 9) {
+        return drv2605_shell_err_too_few_args(argv[1]);
+    }
+
+    rc = drv2605_shell_load_cal(argc, argv, &drv2605->cfg.cal);
+    if (rc) {
+        console_printf("load_cal failed %d\n", rc);
+        goto err;
+    }else{
+        console_printf("load_cal succeeded\n");
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_cmd_power_mode(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+    enum drv2605_power_mode mode;
+    struct sensor_itf *itf;
+
+    if(strcmp(argv[2],"off") == 0) {
+        mode = DRV2605_POWER_OFF;
+    }else if(strcmp(argv[2],"standby") == 0) {
+        mode = DRV2605_POWER_STANDBY;
+    }else if(strcmp(argv[2],"active") == 0) {
+        mode = DRV2605_POWER_ACTIVE;
+    }else {
+        return drv2605_shell_err_unknown_arg(argv[2]);
+    }
+
+    itf = SENSOR_GET_ITF(&(drv2605->sensor));
+
+    rc = drv2605_set_power_mode(itf, mode);
+    if (rc) {
+        console_printf("power_mode failed %d\n", rc);
+        goto err;
+    }else{
+        console_printf("power_mode succeeded\n");
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_cmd_op_mode(int argc, char **argv, struct drv2605 *drv2605)
+{
+    int rc;
+
+    if(strcmp(argv[2],"rom") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_ROM;
+    }else if(strcmp(argv[2],"reset") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_RESET;
+    }else if(strcmp(argv[2],"pwm") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_PWM;
+    }else if(strcmp(argv[2],"analog") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_ANALOG;
+    }else if(strcmp(argv[2],"rtp") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_RTP;
+    }else if(strcmp(argv[2],"diag") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_DIAGNOSTIC;
+    }else if(strcmp(argv[2],"cal") == 0) {
+        drv2605->cfg.op_mode = DRV2605_OP_CALIBRATION;
+    }else {
+        return drv2605_shell_err_unknown_arg(argv[2]);
+    }
+
+    rc = drv2605_config(drv2605, &drv2605->cfg);
+    if (rc) {
+        console_printf("op_mode failed %d\n", rc);
+        goto err;
+    }else{
+        console_printf("op_mode succeeded\n");
+    }
+
+    return 0;
+err:
+    return rc;
+}
+
+static int
+drv2605_shell_cmd(int argc, char **argv)
+{
+    struct os_dev * dev;
+    struct drv2605 * drv2605;
+
+    dev = os_dev_open("drv2605_0", OS_TIMEOUT_NEVER, NULL);
+    if (dev == NULL) {
+        console_printf("failed to open drv2605_0 device\n");
+        return ENODEV;
+    }
+
+    drv2605 = (struct drv2605 *)dev;
+
+    if (argc == 1) {
+        return drv2605_shell_help();
+    }
+
+    if (argc > 1 && strcmp(argv[1], "load_cal") == 0) {
+        return drv2605_shell_cmd_load_cal(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "dump_cal") == 0) {
+        return drv2605_shell_cmd_dump_cal(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "dump_all") == 0) {
+        return drv2605_shell_cmd_dump_all(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "chip_id") == 0) {
+        return drv2605_shell_cmd_get_chip_id(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "op_mode") == 0) {
+        return drv2605_shell_cmd_op_mode(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "power_mode") == 0) {
+        return drv2605_shell_cmd_power_mode(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "load") == 0) {
+        return drv2605_shell_cmd_load_rom(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "trigger") == 0) {
+        return drv2605_shell_cmd_trigger_rom(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "peek") == 0) {
+        return drv2605_shell_cmd_peek(argc, argv, drv2605);
+    }
+
+    if (argc > 1 && strcmp(argv[1], "poke") == 0) {
+        return drv2605_shell_cmd_poke(argc, argv, drv2605);
+    }
+
+    return drv2605_shell_err_unknown_arg(argv[1]);
+}
+
+int
+drv2605_shell_init(void)
+{
+    int rc;
+
+    rc = shell_cmd_register(&drv2605_shell_cmd_struct);
+    SYSINIT_PANIC_ASSERT(rc == 0);
+
+    return rc;
+}
+
+#endif
diff --git a/hw/drivers/drv2605/syscfg.yml b/hw/drivers/drv2605/syscfg.yml
new file mode 100644
index 0000000..5946ea9
--- /dev/null
+++ b/hw/drivers/drv2605/syscfg.yml
@@ -0,0 +1,67 @@
+#
+# 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:
+    DRV2605_CLI:
+        description: 'Enable shell support for the DRV2605'
+        value: 0
+    DRV2605_LOG:
+        description: 'Enable DRV2605 logging'
+        value: 0
+    DRV2605_STATS:
+        description: 'Enable DRV2605 statistics'
+        value: 0
+    DRV2605_SHELL_ITF_TYPE:
+        description: 'DRV2605 interface type - I2C'
+        value: 1
+    DRV2605_SHELL_ITF_NUM:
+        description: 'DRV2605 interface number'
+        value: 0
+    DRV2605_SHELL_ITF_ADDR:
+        description: 'DRV2605 I2C Address'
+        value: 0x5A
+
+    DRV2605_EN_PIN:
+        description: 'EN pin'
+        value: -1
+
+    # these are settings taken from the drv2605 evm for the SEMCO1030 (Mplus)
+    # 205hz freq, nominal voltage 1.5V, max voltage 3.0V, 
DRV2605_LRA_SAMPLE_TIME: 3, DRV2605_LRA_BLANKING_TIME: 2, 
DRV2605_LRA_IDISS_TIME: 2
+    # REQUIRED, you need to change based on your motor
+    DRV2605_RATED_VOLTAGE:
+        description: '8.5.2.1 Rated Voltage Programming'
+        value: 0x3E
+    DRV2605_OD_CLAMP:
+        description: '8.5.2.2 Overdrive Voltage-Clamp Programming, Note: LRA 
and ERM (8) and (9) equations are swapped in the "SLOS854C REVISED SEPTEMBER 
2014" datasheet, confirmed 
https://e2e.ti.com/support/other_analog/haptics/f/927/t/655886'
+        value: 0x89
+    DRV2605_DRIVE_TIME:
+        description: '8.6.21 LRA DRIVE_TIME = (0.5 × (1/fLRA * 1000) - 0.5 ms) 
/  0.1 ms'
+        value: 19
+
+    # Now supply previously computed calibration values, specific to your 
motor, these are settings from the SEMCO1030 from drv2605 devkit
+    # If you don't have these youll need to run drv2605_auto_calibrate with 
the appropriate inputs to generate them
+    DRV2605_CALIBRATED_COMP:
+        description: 'Previously Computed Automatic Compensation for Resistive 
Losses for SEMCO1030'
+        value: 0x09
+    DRV2605_CALIBRATED_BEMF:
+        description: 'Previously Computed Auto-Calibration Back-EMF Result for 
SEMCO1030'
+        value: 0x79
+    DRV2605_CALIBRATED_BEMF_GAIN:
+        description: 'Previously Computed Auto-Calibration BEMF_GAIN Result 
for SEMCO1030'
+        value: 1
diff --git a/hw/drivers/sensors/bno055/src/bno055.c 
b/hw/drivers/sensors/bno055/src/bno055.c
index 9421b46..c34daeb 100644
--- a/hw/drivers/sensors/bno055/src/bno055.c
+++ b/hw/drivers/sensors/bno055/src/bno055.c
@@ -98,7 +98,7 @@ bno055_write8(struct sensor_itf *itf, uint8_t reg, uint8_t 
value)
 }
 
 /**
- * Writes a multiple bytes to the specified register
+ * Writes a multiple bytes to the specified register  (MAX: 22 bytes)
  *
  * @param The Sesnsor interface
  * @param The register address to write to
@@ -121,6 +121,11 @@ bno055_writelen(struct sensor_itf *itf, uint8_t reg, 
uint8_t *buffer,
         .buffer = payload
     };
 
+    if (len > (sizeof(payload) - 1)) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
     memcpy(&payload[1], buffer, len);
 
     /* Register write */
diff --git a/hw/drivers/sensors/lis2dh12/src/lis2dh12.c 
b/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
index 7a01b1d..e0849c8 100644
--- a/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
+++ b/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
@@ -199,7 +199,7 @@ err:
 
 
 /**
- * Write multiple length data to LIS2DH12 sensor over I2C
+ * Write multiple length data to LIS2DH12 sensor over I2C  (MAX: 19 bytes)
  *
  * @param The sensor interface
  * @param register address
@@ -223,6 +223,11 @@ lis2dh12_i2c_writelen(struct sensor_itf *itf, uint8_t 
addr, uint8_t *buffer,
         .buffer = payload
     };
 
+    if (len > (sizeof(payload) - 1)) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
     memcpy(&payload[1], buffer, len);
 
     /* Register write */
diff --git a/hw/drivers/sensors/tcs34725/src/tcs34725.c 
b/hw/drivers/sensors/tcs34725/src/tcs34725.c
index 3f58f62..37b8e37 100644
--- a/hw/drivers/sensors/tcs34725/src/tcs34725.c
+++ b/hw/drivers/sensors/tcs34725/src/tcs34725.c
@@ -204,7 +204,7 @@ err:
 }
 
 /**
- * Writes a multiple bytes to the specified register
+ * Writes a multiple bytes to the specified register (MAX: 8 bytes)
  *
  * @param The sensor interface
  * @param The register address to write to
@@ -224,6 +224,11 @@ tcs34725_writelen(struct sensor_itf *itf, uint8_t reg, 
uint8_t *buffer, uint8_t
         .buffer = payload
     };
 
+    if (len > (sizeof(payload) - 1)) {
+        rc = OS_EINVAL;
+        goto err;
+    }
+
     memcpy(&payload[1], buffer, len);
 
     /* Register write */
diff --git a/hw/sensor/creator/pkg.yml b/hw/sensor/creator/pkg.yml
index 3eedd57..39d30fa 100644
--- a/hw/sensor/creator/pkg.yml
+++ b/hw/sensor/creator/pkg.yml
@@ -26,6 +26,8 @@ pkg.keywords:
 
 pkg.deps.BME280_OFB:
     - hw/drivers/sensors/bme280
+pkg.deps.DRV2605_OFB:
+    - hw/drivers/drv2605
 pkg.deps.LSM303DLHC_OFB:
     - hw/drivers/sensors/lsm303dlhc
 pkg.deps.MPU6050_OFB:
diff --git a/hw/sensor/creator/src/sensor_creator.c 
b/hw/sensor/creator/src/sensor_creator.c
index aa21dee..e5b3be3 100644
--- a/hw/sensor/creator/src/sensor_creator.c
+++ b/hw/sensor/creator/src/sensor_creator.c
@@ -23,6 +23,10 @@
 #include <defs/error.h>
 #include <string.h>
 
+#if MYNEWT_VAL(DRV2605_OFB)
+#include "hal/hal_gpio.h"
+#include <drv2605/drv2605.h>
+#endif
 #if MYNEWT_VAL(LSM303DLHC_OFB)
 #include <lsm303dlhc/lsm303dlhc.h>
 #endif
@@ -55,6 +59,10 @@
 #endif
 
 /* Driver definitions */
+#if MYNEWT_VAL(DRV2605_OFB)
+static struct drv2605 drv2605;
+#endif
+
 #if MYNEWT_VAL(LSM303DLHC_OFB)
 static struct lsm303dlhc lsm303dlhc;
 #endif
@@ -126,6 +134,15 @@ static struct sensor_itf spi_0_itf_bme = {
 };
 #endif
 
+#if MYNEWT_VAL(I2C_0) && MYNEWT_VAL(DRV2605_OFB)
+static struct sensor_itf i2c_0_itf_drv = {
+    .si_type = SENSOR_ITF_I2C,
+    .si_num  = 0,
+    .si_addr = MYNEWT_VAL(DRV2605_SHELL_ITF_ADDR),
+    .si_cs_pin = MYNEWT_VAL(DRV2605_EN_PIN)
+};
+#endif
+
 #if MYNEWT_VAL(I2C_0) && MYNEWT_VAL(LSM303DLHC_OFB)
 static struct sensor_itf i2c_0_itf_lsm = {
     .si_type = SENSOR_ITF_I2C,
@@ -356,6 +373,29 @@ config_tsl2561_sensor(void)
 #endif
 
 /**
+ * DRV2605 Actuator default configuration used by the creator package
+ *
+ * @return 0 on success, non-zero on failure
+ */
+#if MYNEWT_VAL(DRV2605_OFB)
+static int
+config_drv2605_actuator(void)
+{
+    int rc;
+    struct os_dev *dev;
+    struct drv2605_cfg cfg = {0};
+
+    dev = (struct os_dev *) os_dev_open("drv2605_0", OS_TIMEOUT_NEVER, NULL);
+    assert(dev != NULL);
+
+    rc = drv2605_config((struct drv2605 *) dev, &cfg);
+
+    os_dev_close(dev);
+    return rc;
+}
+#endif
+
+/**
  * LSM303DLHC Sensor default configuration used by the creator package
  *
  * @return 0 on success, non-zero on failure
@@ -515,6 +555,18 @@ sensor_dev_create(void)
     int rc;
 
     (void)rc;
+#if MYNEWT_VAL(DRV2605_OFB)
+    rc = hal_gpio_init_out(MYNEWT_VAL(DRV2605_EN_PIN), 1);
+    assert(rc == 0);
+
+    rc = os_dev_create((struct os_dev *) &drv2605, "drv2605_0",
+      OS_DEV_INIT_PRIMARY, 0, drv2605_init, (void *)&i2c_0_itf_drv);
+    assert(rc == 0);
+
+    rc = config_drv2605_actuator();
+    assert(rc == 0);
+#endif
+
 #if MYNEWT_VAL(LSM303DLHC_OFB)
     /* Since this sensor has multiple I2C addreses,
      * 0x1E for accelerometer and 0x19 for magnetometer,
diff --git a/hw/sensor/creator/syscfg.yml b/hw/sensor/creator/syscfg.yml
index cd54cdf..e4cf5d9 100644
--- a/hw/sensor/creator/syscfg.yml
+++ b/hw/sensor/creator/syscfg.yml
@@ -22,6 +22,9 @@ syscfg.defs:
     TSL2561_OFB:
         description: 'TSL2561 is present'
         value : 0
+    DRV2605_OFB:
+        description: 'DRV2605 is present'
+        value : 0
     LSM303DLHC_OFB:
         description: 'LSM303 is present'
         value : 0

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

Reply via email to