jacobrosenthal closed pull request #757: HW driver: drv2605l
URL: https://github.com/apache/mynewt-core/pull/757
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/apps/sensors_test/src/main.c b/apps/sensors_test/src/main.c
index 423841958..fe4a862d8 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 000000000..ce5e661e2
--- /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/drivers/drv2605/pkg.yml b/hw/drivers/drv2605/pkg.yml
new file mode 100644
index 000000000..8561f2b52
--- /dev/null
+++ b/hw/drivers/drv2605/pkg.yml
@@ -0,0 +1,37 @@
+#
+# 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/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"
+
+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 000000000..a724edc2e
--- /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 000000000..9acc295d4
--- /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 000000000..d56f694ae
--- /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 000000000..5946ea9bc
--- /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 9421b4640..c34daeb04 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 7a01b1dd0..e0849c838 100644
--- a/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
+++ b/hw/drivers/sensors/lis2dh12/src/lis2dh12.c
@@ -199,7 +199,7 @@ lis2dh12_spi_readlen(struct sensor_itf *itf, uint8_t addr, 
uint8_t *payload,
 
 
 /**
- * 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 3f58f6209..37b8e379e 100644
--- a/hw/drivers/sensors/tcs34725/src/tcs34725.c
+++ b/hw/drivers/sensors/tcs34725/src/tcs34725.c
@@ -204,7 +204,7 @@ tcs34725_readlen(struct sensor_itf *itf, uint8_t reg, 
uint8_t *buffer, uint8_t l
 }
 
 /**
- * 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 3eedd57c3..39d30fa6a 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 aa21deede..e5b3be3b1 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,
@@ -355,6 +372,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
  *
@@ -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 cd54cdfeb..e4cf5d974 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


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
us...@infra.apache.org


With regards,
Apache Git Services

Reply via email to