[PATCH] dev/i2c: Add I2C device support for FPGA Slave, LM25066A, TMP112, ADS1113 and ADS1115

Chris Johns chrisj at rtems.org
Wed Aug 16 05:11:40 UTC 2017


Closes #3101.
---
 cpukit/dev/Makefile.am                        |   8 +
 cpukit/dev/i2c/fpga-i2c-slave.c               | 132 +++++++++
 cpukit/dev/i2c/ti-ads-16bit-adc.c             | 239 ++++++++++++++++
 cpukit/dev/i2c/ti-lm25066a.c                  | 376 ++++++++++++++++++++++++++
 cpukit/dev/i2c/ti-tmp112.c                    | 229 ++++++++++++++++
 cpukit/dev/include/dev/i2c/fpga-i2c-slave.h   |  25 ++
 cpukit/dev/include/dev/i2c/ti-ads-16bit-adc.h | 203 ++++++++++++++
 cpukit/dev/include/dev/i2c/ti-lm25066a.h      | 190 +++++++++++++
 cpukit/dev/include/dev/i2c/ti-tmp112.h        | 109 ++++++++
 cpukit/dev/preinstall.am                      |  16 ++
 10 files changed, 1527 insertions(+)
 create mode 100644 cpukit/dev/i2c/fpga-i2c-slave.c
 create mode 100644 cpukit/dev/i2c/ti-ads-16bit-adc.c
 create mode 100644 cpukit/dev/i2c/ti-lm25066a.c
 create mode 100644 cpukit/dev/i2c/ti-tmp112.c
 create mode 100644 cpukit/dev/include/dev/i2c/fpga-i2c-slave.h
 create mode 100644 cpukit/dev/include/dev/i2c/ti-ads-16bit-adc.h
 create mode 100644 cpukit/dev/include/dev/i2c/ti-lm25066a.h
 create mode 100644 cpukit/dev/include/dev/i2c/ti-tmp112.h

diff --git a/cpukit/dev/Makefile.am b/cpukit/dev/Makefile.am
index fbd84fd639..3cb3413b78 100644
--- a/cpukit/dev/Makefile.am
+++ b/cpukit/dev/Makefile.am
@@ -7,9 +7,13 @@ include_dev_HEADERS =
 include_dev_i2cdir = $(includedir)/dev/i2c
 include_dev_i2c_HEADERS =
 include_dev_i2c_HEADERS += include/dev/i2c/eeprom.h
+include_dev_i2c_HEADERS += include/dev/i2c/fpga-i2c-slave.h
 include_dev_i2c_HEADERS += include/dev/i2c/gpio-nxp-pca9535.h
 include_dev_i2c_HEADERS += include/dev/i2c/i2c.h
 include_dev_i2c_HEADERS += include/dev/i2c/switch-nxp-pca9548a.h
+include_dev_i2c_HEADERS += include/dev/i2c/ti-ads-16bit-adc.h
+include_dev_i2c_HEADERS += include/dev/i2c/ti-lm25066a.h
+include_dev_i2c_HEADERS += include/dev/i2c/ti-tmp112.h
 
 include_dev_spidir = $(includedir)/dev/spi
 include_dev_spi_HEADERS =
@@ -32,10 +36,14 @@ noinst_LIBRARIES = libdev.a
 
 libdev_a_SOURCES =
 libdev_a_SOURCES += i2c/eeprom.c
+libdev_a_SOURCES += i2c/fpga-i2c-slave.c
 libdev_a_SOURCES += i2c/gpio-nxp-pca9535.c
 libdev_a_SOURCES += i2c/i2c-bus.c
 libdev_a_SOURCES += i2c/i2c-dev.c
 libdev_a_SOURCES += i2c/switch-nxp-pca9548a.c
+libdev_a_SOURCES += i2c/ti-ads-16bit-adc.c
+libdev_a_SOURCES += i2c/ti-lm25066a.c
+libdev_a_SOURCES += i2c/ti-tmp112.c
 libdev_a_SOURCES += spi/spi-bus.c
 libdev_a_SOURCES += serial/sc16is752.c
 libdev_a_SOURCES += serial/sc16is752-spi.c
diff --git a/cpukit/dev/i2c/fpga-i2c-slave.c b/cpukit/dev/i2c/fpga-i2c-slave.c
new file mode 100644
index 0000000000..cf00a57b41
--- /dev/null
+++ b/cpukit/dev/i2c/fpga-i2c-slave.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+/*
+ * I2C slave for testing:
+ *       https://github.com/oetr/FPGA-I2C-Slave
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <dev/i2c/i2c.h>
+#include <dev/i2c/fpga-i2c-slave.h>
+
+typedef struct {
+  i2c_dev  base;
+  uint16_t address;
+  size_t   size;
+} fpga_i2c_slave;
+
+static ssize_t
+fpga_i2c_slave_read(i2c_dev* base, void* buf, size_t n, off_t offset)
+{
+  fpga_i2c_slave* dev = (fpga_i2c_slave*) base;
+  off_t           avail = dev->size - offset;
+  uint8_t*        in = buf;
+  size_t          todo;
+
+  if (avail <= 0) {
+    return 0;
+  }
+
+  if (n > avail) {
+    n = (size_t) avail;
+  }
+
+  todo = n;
+
+  while (todo > 0) {
+    /*
+     * Limit the transfer size so that it can be stored in 8-bits.  This may
+     * help some bus controllers.
+     */
+    uint16_t cur = (uint16_t) (todo < 255 ?  todo : 255);
+    i2c_msg msgs = {
+      .addr = dev->base.address,
+      .flags = I2C_M_RD,
+      .buf = in,
+      .len = cur
+    };
+    int err;
+    err = i2c_bus_transfer(dev->base.bus, &msgs, 1);
+    if (err != 0) {
+      return err;
+    }
+    todo -= cur;
+    in += cur;
+  }
+
+  return (ssize_t) n;
+}
+
+static ssize_t
+fpga_i2c_slave_write(i2c_dev* base, const void* buf, size_t n, off_t offset)
+{
+  fpga_i2c_slave* dev = (fpga_i2c_slave*) base;
+  off_t           avail = dev->size - offset;
+  const uint8_t*  out = buf;
+  size_t          todo;
+
+  if (avail <= 0) {
+    return 0;
+  }
+
+  if (n > avail) {
+    n = (size_t) avail;
+  }
+
+  todo = n;
+
+  while (todo > 0) {
+    /*
+     * Limit the transfer size so that it can be stored in 8-bits.  This may
+     * help some bus controllers.
+     */
+    uint16_t cur = (uint16_t) (todo < 255 ?  todo : 255);
+    i2c_msg msgs = {
+      .addr = dev->base.address,
+      .flags = 0,
+      .buf = RTEMS_DECONST(uint8_t*, out),
+      .len = cur
+    };
+    int err;
+    err = i2c_bus_transfer(dev->base.bus, &msgs, 1);
+    if (err != 0) {
+      return err;
+    }
+    todo -= cur;
+    out += cur;
+  }
+
+  return (ssize_t) n;
+}
+
+int
+i2c_dev_register_fpga_i2c_slave(const char* bus_path,
+                                const char* dev_path,
+                                uint16_t    address,
+                                size_t      size)
+
+{
+  fpga_i2c_slave* dev;
+
+  dev = (fpga_i2c_slave*)
+    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
+  if (dev == NULL) {
+    return -1;
+  }
+
+  dev->base.read = fpga_i2c_slave_read;
+  dev->base.write = fpga_i2c_slave_write;
+  dev->size = size;
+
+  return i2c_dev_register(&dev->base, dev_path);
+}
diff --git a/cpukit/dev/i2c/ti-ads-16bit-adc.c b/cpukit/dev/i2c/ti-ads-16bit-adc.c
new file mode 100644
index 0000000000..35e6a7eb63
--- /dev/null
+++ b/cpukit/dev/i2c/ti-ads-16bit-adc.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2016-2016 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <unistd.h>
+
+#include <dev/i2c/i2c.h>
+#include <dev/i2c/ti-ads-16bit-adc.h>
+
+/*
+ * Registers.
+ */
+#define ADS_CONVERSION (0)
+#define ADS_CONFIG     (1)
+#define ADS_LO_THRESH  (2)
+#define ADS_HI_THRESH  (3)
+
+/*
+ * Configuration register.
+ */
+#define CFG_OS_NOT_CONVERTING     (1 << 15)   /* read */
+#define CFG_OS_START_SSHOT        (1 << 15)   /* write */
+#define CFG_MUX_BASE              (12)
+#define CFG_MUX_MASK              (7)
+#define CFG_PGA_BASE              (9)
+#define CFG_PGA_MASK              (7)
+#define CFG_MODE_BASE             (8)
+#define CFG_MODE_MASK             (1)
+#define CFG_DATA_RATE_BASE        (5)
+#define CFG_DATA_RATE_MASK        (7)
+#define CFG_COMP_BASE             (0)
+#define CFG_COMP_MASK             (0x1f)
+#define CFG_COMP_MODE_ACTIVE_LOW  (0 << 4)
+#define CFG_COMP_MODE_ACTIVE_HIGH (1 << 4)
+
+#define CFG_MODE_CONT             (0 << CFG_MODE_BASE)
+#define CFG_MODE_SSHOT            (1 << CFG_MODE_BASE)
+
+typedef struct {
+  i2c_dev    base;
+  ti_ads_adc device;
+  uint32_t   poll_wait_period;
+  int        reg;
+  uint16_t   config_shadow;
+} ti_ads;
+
+static int
+ti_ads_reg_write(ti_ads* dev, int reg, uint16_t value)
+{
+  uint8_t out[3];
+  i2c_msg msgs[1] = {
+    {
+      .addr = dev->base.address,
+      .flags = 0,
+      .len = (uint16_t) sizeof(out),
+      .buf = &out[0]
+    }
+  };
+  out[0] = (uint8_t) reg;
+  out[1] = (uint8_t) (value >> 8);
+  out[2] = (uint8_t) value;
+  return i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+}
+
+static int
+ti_ads_reg_read(ti_ads* dev, int reg, uint16_t* value)
+{
+  uint8_t in[2] = { 0, 0 };
+  uint8_t out[1] = { (uint8_t) reg };
+  i2c_msg msgs[2] = {
+    {
+      .addr = dev->base.address,
+      .flags = 0,
+      .len = (uint16_t) sizeof(out),
+      .buf = &out[0]
+    }, {
+      .addr = dev->base.address,
+      .flags = I2C_M_RD,
+      .len = (uint16_t) sizeof(in),
+      .buf = &in[0]
+    }
+  };
+  int err;
+  err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+  *value = (((uint16_t) in[0]) << 8) | in[1];
+  return err;
+}
+
+static int
+ti_ads_set_config(ti_ads* dev, uint16_t value, int base, uint16_t mask)
+{
+  int err;
+  dev->config_shadow &= ~(mask << base);
+  dev->config_shadow |= (value & mask) << base;
+  err = ti_ads_reg_write(dev, ADS_CONFIG, dev->config_shadow);
+  return err;
+}
+
+static int
+ti_ads_sample(ti_ads* dev, uint16_t* value)
+{
+  int err;
+  if ((dev->config_shadow & CFG_MODE_SSHOT) == CFG_MODE_SSHOT) {
+    i2c_bus_obtain(dev->base.bus);
+    err = ti_ads_reg_write(dev,
+                           ADS_CONFIG,
+                           dev->config_shadow | CFG_OS_START_SSHOT);
+    if (err == 0) {
+      uint16_t config = 0;
+      while (err == 0 && (config & CFG_OS_NOT_CONVERTING) == 0) {
+        err = ti_ads_reg_read(dev, ADS_CONFIG, &config);
+        if (dev->poll_wait_period && (config & CFG_OS_NOT_CONVERTING) == 0)
+          usleep(dev->poll_wait_period);
+      }
+      err = ti_ads_reg_read(dev, ADS_CONVERSION, value);
+    }
+    i2c_bus_release(dev->base.bus);
+  }
+  else {
+    err = ti_ads_reg_read(dev, ADS_CONVERSION, value);
+  }
+  return err;
+}
+
+static int
+ti_ads_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg)
+{
+  ti_ads*  dev = (ti_ads*) iic_dev;
+  uint16_t value;
+  int      err;
+
+  switch (command) {
+    case TI_ADS_ADC_GET_CONVERSION:
+      value = 0;
+      err = ti_ads_sample(dev, &value);
+      if (err == 0)
+        *((uint16_t*) arg) = value;
+      break;
+    case TI_ADS_ADC_SET_MUX:
+      if (dev->device == TI_ADS1115) {
+        value = (uint16_t)(uintptr_t) arg;
+        err = ti_ads_set_config(dev, value, CFG_MUX_BASE, CFG_MUX_MASK);
+      }
+      else {
+        err = -ENOTTY;
+      }
+      break;
+    case TI_ADS_ADC_SET_MODE:
+      value = (uint16_t)(uintptr_t) arg;
+      err = ti_ads_set_config(dev, value, CFG_MODE_BASE, CFG_MODE_MASK);
+      break;
+    case TI_ADS_ADC_SET_PGA:
+      if (dev->device == TI_ADS1114 || dev->device == TI_ADS1115) {
+        value = (uint16_t)(uintptr_t) arg;
+        err = ti_ads_set_config(dev, value, CFG_PGA_BASE, CFG_PGA_MASK);
+      }
+      else {
+        err = -ENOTTY;
+      }
+      break;
+    case TI_ADS_ADC_SET_COMP:
+      if (dev->device == TI_ADS1114 || dev->device == TI_ADS1115) {
+        value = (uint16_t)(uintptr_t) arg;
+        err = ti_ads_set_config(dev, value, CFG_COMP_BASE, CFG_COMP_MASK);
+      }
+      else {
+        err = -ENOTTY;
+      }
+      break;
+    case TI_ADS_ADC_SET_LO_THRESH:
+      value = (uint16_t)(uintptr_t) arg;
+      err = ti_ads_reg_write(dev, ADS_LO_THRESH, value);
+      break;
+    case TI_ADS_ADC_SET_HI_THRESH:
+      value = (uint16_t)(uintptr_t) arg;
+      err = ti_ads_reg_write(dev, ADS_HI_THRESH, value);
+      break;
+    case TI_ADS_ADC_SET_CONV_WAIT:
+      dev->poll_wait_period = (uint32_t)(uintptr_t) arg;
+      err = 0;
+      break;
+    default:
+      err = -ENOTTY;
+      break;
+  }
+
+  return err;
+}
+
+int
+i2c_dev_register_ti_ads_adc(const char* bus_path,
+                            const char* dev_path,
+                            uint16_t    address,
+                            ti_ads_adc  device)
+{
+  ti_ads* dev;
+
+  dev = (ti_ads*)
+    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
+  if (dev == NULL) {
+    return -1;
+  }
+
+  dev->base.ioctl = ti_ads_ioctl;
+  dev->device = device;
+  dev->config_shadow = 0;
+
+  switch (device) {
+    default:
+      errno = EIO;
+      return -1;
+    case TI_ADS1115:
+      dev->config_shadow |= ((TI_ADS_MUX_ApA0_AnA1 << CFG_MUX_BASE) |
+                             (TI_ADS_PGA_FS_2_048V << CFG_PGA_BASE) |
+                             ((TI_ADS_COMP_MODE_HYSTERESIS |
+                               TI_ADS_COMP_POL_ACTIVE_LOW |
+                               TI_ADS_COMP_LAT_NON_LATCHING |
+                               TI_ADS_COMP_QUE_DISABLE_COMP) << CFG_COMP_BASE));
+      /* FALLTHRU */
+    case TI_ADS1114:
+      dev->config_shadow |= TI_ADS_PGA_FS_2_048V << CFG_PGA_BASE;
+      /* FALLTHRU */
+    case TI_ADS1113:
+      dev->config_shadow |= (CFG_MODE_SSHOT |
+                             (TI_ADS_DATARATE_8SPS  << CFG_DATA_RATE_BASE));
+      break;
+  }
+
+  return i2c_dev_register(&dev->base, dev_path);
+}
diff --git a/cpukit/dev/i2c/ti-lm25066a.c b/cpukit/dev/i2c/ti-lm25066a.c
new file mode 100644
index 0000000000..7f479a537d
--- /dev/null
+++ b/cpukit/dev/i2c/ti-lm25066a.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <dev/i2c/i2c.h>
+#include <dev/i2c/ti-lm25066a.h>
+
+/*
+ * Commands.
+ *
+ * The commands are listed in Table 1 in the datasheet. These are the mapped to
+ * command ids in the intreface. Commands have different data size, a mix of
+ * read and write amd formats.
+ *
+ * The formats for values is listed in Table 41 in the datasheet.
+ */
+
+#define CMD_RD         (1 << 0)
+#define CMD_WR         (1 << 1)
+#define CMD_RW         (CMD_RD | CMD_WR)
+#define CMD_STR        (1 << 2)
+#define CMD_VAL_       (1 << 3)
+#define CMD_BLK_       (1 << 4)
+#define CMD_INDEX_BASE (16)
+#define CMD_INDEX_MASK (0xf)
+#define CMD_VAL(l)     (CMD_VAL_ | ((l) << CMD_INDEX_BASE))
+#define CMD_BLK(l)     (CMD_BLK_ | ((l) << CMD_INDEX_BASE))
+#define CMD_INDEX(f)   ((f >> CMD_INDEX_BASE) & CMD_INDEX_MASK)
+
+/*
+ * Number of bits in the ADC.
+ */
+#define ADC_BITS (12)
+
+/*
+ * Blocks of values that can be read.
+ */
+static const ti_lm25066a_cmd block_read[] =
+{
+  TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ,
+  TI_LM25066A_MFR_READ_IIN,
+  TI_LM25066A_READ_VOUT,
+  TI_LM25066A_READ_VIN,
+  TI_LM25066A_MFR_READ_PIN,
+  TI_LM25066A_READ_TEMPERATURE_1
+};
+
+static const ti_lm25066a_cmd avg_block_read[] =
+{
+  TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ,
+  TI_LM25066A_MFR_READ_AVG_IIN,
+  TI_LM25066A_MFR_READ_AVG_VOUT,
+  TI_LM25066A_MFR_READ_AVG_VIN,
+  TI_LM25066A_MFR_READ_AVG_PIN,
+  TI_LM25066A_READ_TEMPERATURE_1
+};
+
+static const ti_lm25066a_cmd* blocks[] =
+{
+  block_read,
+  avg_block_read
+};
+
+typedef struct
+{
+  uint8_t  cmd;
+  uint16_t size;
+  uint32_t flags;
+} ti_lm25066a_i2c_cmd;
+
+/*
+ * Table 1.
+ */
+static const ti_lm25066a_i2c_cmd commands[] =
+{
+  { 0x01,  1, CMD_RW              }, /* OPERATION */
+  { 0x03,  0, CMD_WR              }, /* CLEAR_FAULTS */
+  { 0x19,  1, CMD_RD              }, /* CAPABILITY */
+  { 0x43,  2, CMD_RW | CMD_VAL(1) }, /* VOUT_UV_WARN_LIMIT */
+  { 0x4f,  2, CMD_RW | CMD_VAL(5) }, /* OT_FAULT_LIMIT */
+  { 0x51,  2, CMD_RW | CMD_VAL(5) }, /* OT_WARN_LIMIT */
+  { 0x57,  2, CMD_RD | CMD_VAL(0) }, /* VIN_OV_WARN_LIMIT */
+  { 0x58,  2, CMD_RW | CMD_VAL(0) }, /* VIN_UV_WARN_LIMIT */
+  { 0x78,  1, CMD_RD              }, /* STATUS_BYTE */
+  { 0x79,  2, CMD_RD              }, /* STATUS_WORD */
+  { 0x7a,  1, CMD_RD              }, /* STATUS_VOUT */
+  { 0x7c,  1, CMD_RD              }, /* STATUS_INPUT */
+  { 0x7d,  1, CMD_RD              }, /* STATUS_TEMPERATURE */
+  { 0x7e,  1, CMD_RD              }, /* STATUS_CML */
+  { 0x80,  1, CMD_RD              }, /* STATUS_MFR_SPECIFIC */
+  { 0x88,  2, CMD_RD | CMD_VAL(0) }, /* READ_VIN */
+  { 0x8b,  2, CMD_RD | CMD_VAL(1) }, /* READ_VOUT */
+  { 0x8d,  2, CMD_RD | CMD_VAL(5) }, /* READ_TEMPERATURE_1 */
+  { 0x99,  4, CMD_RD | CMD_STR    }, /* MFR_ID */
+  { 0x9a,  9, CMD_RD | CMD_STR    }, /* MFR_MODEL */
+  { 0x9b,  2, CMD_RD | CMD_STR    }, /* MFR_REVISION */
+  { 0xd0,  2, CMD_RD | CMD_VAL(2) }, /* MFR_READ_VAUX */
+  { 0xd1,  2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_IIN */
+  { 0xd2,  2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_PIN */
+  { 0xd3,  2, CMD_RW | CMD_VAL(3) }, /* MFR_IIN_OC_WARN_LIMIT */
+  { 0xd4,  2, CMD_RW | CMD_VAL(4) }, /* MFR_PIN_OP_WARN_LIMIT */
+  { 0xd5,  2, CMD_RD | CMD_VAL(4) }, /* MFR_PIN_PEAK */
+  { 0xd6,  0, CMD_WR              }, /* MFR_CLEAR_PIN_PEAK */
+  { 0xd7,  1, CMD_RW              }, /* MFR_GATE_MASK */
+  { 0xd8,  2, CMD_RW              }, /* MFR_ALERT_MASK */
+  { 0xd9,  1, CMD_RD              }, /* MFR_DEVICE_SETUP */
+  { 0xda, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLOCK_READ */
+  { 0xdb,  1, CMD_RW              }, /* MFR_SAMPLES_FOR_AVG */
+  { 0xdc,  2, CMD_RD | CMD_VAL(0) }, /* MFR_READ_AVG_VIN */
+  { 0xdd,  2, CMD_RD | CMD_VAL(1) }, /* MFR_READ_AVG_VOUT */
+  { 0xde,  2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_AVG_IIN */
+  { 0xdf,  2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_AVG_PIN */
+  { 0xe0, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLACK_BOX_READ */
+  { 0xe1,  2, CMD_RD              }, /* MFR_DIAGNOSTIC_WORD_READ */
+  { 0xe2, 13, CMD_RD | CMD_BLK(1) }, /* MFR_AVG_BLOCK_READ */
+};
+
+#define IO_CMDS          (sizeof(commands) / sizeof(commands[0]))
+#define IO_INTS          (6)
+#define IO_MESSAGE_SIZE  ((sizeof(uint16_t) * IO_INTS) + 2)
+
+typedef struct {
+  i2c_dev                       base;
+  uint8_t                       pointer;
+  uint16_t                      config_shadow;
+  const ti_lm25066a_conversion* conversions;
+  int                           scale;
+  uint8_t                       buffer[IO_MESSAGE_SIZE];
+} ti_lm25066a;
+
+/*
+ * Convert a value using table 41 in the data sheet.
+ */
+static int
+ti_lm25066a_io_convert_to_real(ti_lm25066a* dev, uint16_t word, int conversion)
+{
+  const ti_lm25066a_conversion* const con = &dev->conversions[conversion];
+  uint32_t                            u = word;
+  int                                 value;
+  value = ((int) (u & ((1 << ADC_BITS) - 1))) * dev->scale;
+  value = ((value * con->R) - con->b) / con->m;
+  return value;
+}
+
+static void
+ti_lm25066a_io_convert_block(ti_lm25066a*    dev,
+                             const uint16_t* words,
+                             int*            values,
+                             int             block)
+{
+  const ti_lm25066a_cmd* cmds = blocks[block];
+  int                    c;
+  /*
+   * A block is always 6 values.
+   */
+  values[0] = words[0];
+  for (c = 1; c < 6; ++c) {
+    const ti_lm25066a_i2c_cmd* cmd = &commands[cmds[c]];
+    if ((cmd->flags & CMD_VAL_) != 0) {
+      values[c] =
+        ti_lm25066a_io_convert_to_real(dev, words[c], CMD_INDEX(cmd->flags));
+    } else {
+      values[c] = words[c];
+    }
+  }
+}
+
+static int
+ti_lm25066a_io_read(ti_lm25066a* dev, ti_lm25066a_io* io)
+{
+  const ti_lm25066a_i2c_cmd* cmd;
+  uint8_t                    out[1];
+  uint8_t*                   in = dev->buffer;
+  i2c_msg                    msgs[2];
+  int                        err;
+
+  if (io->cmd >= IO_CMDS)
+    return -EIO;
+
+  cmd = &commands[io->cmd];
+
+  if ((cmd->flags & CMD_RD) == 0)
+    return -EIO;
+
+  out[0] = cmd->cmd;
+  msgs[0].addr = dev->base.address;
+  msgs[0].flags = 0;
+  msgs[0].len = (uint16_t) sizeof(out);
+  msgs[0].buf = &out[0];
+  msgs[1].addr = dev->base.address;
+  msgs[1].flags = I2C_M_RD;
+  msgs[1].len = (uint16_t) cmd->size;
+  msgs[1].buf = &in[0];
+
+  err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+  if (err != 0)
+    return err;
+
+  err = -EIO;
+
+  switch (io->type) {
+    case TI_LM25066A_8BIT:
+      io->data.u8 = in[0];
+      err = 0;
+      break;
+    case TI_LM25066A_16BIT:
+      io->data.u16 = (((uint16_t) in[0]) << 8) | in[1];
+      err = 0;
+      break;
+    case TI_LM25066A_VALUE:
+      if ((cmd->flags & CMD_VAL_) != 0) {
+        uint16_t data = ((uint16_t) in[1]) << 8 | in[0];
+        io->data.value =
+          ti_lm25066a_io_convert_to_real(dev, data, CMD_INDEX(cmd->flags));
+      }
+      else {
+        io->data.value = ((uint16_t) in[1]) << 8 | in[0];;
+      }
+      err = 0;
+      break;
+    case TI_LM25066A_VALUES:
+      if ((cmd->flags & CMD_BLK_) != 0) {
+        uint16_t  data[in[0] / 2];
+        uint16_t* d = &data[0];
+        uint8_t*  i = &in[1];
+        int       w;
+        for (w = 0; w < (in[0] / 2); ++w, ++d, i += 2) {
+          *d = (((uint16_t) i[1]) << 8) | i[0] ;
+        }
+        ti_lm25066a_io_convert_block(dev,
+                                     &data[0],
+                                     &io->data.values[0],
+                                     CMD_INDEX(cmd->flags));
+        err = 0;
+      }
+      break;
+    case TI_LM25066A_STRING:
+      memcpy(&io->data.string[0], &in[0], cmd->size);
+      err = 0;
+      break;
+    case TI_LM25066A_RAW:
+      memcpy(io->data.raw, &in[0], cmd->size);
+      err = 0;
+      break;
+    default:
+      break;
+  }
+
+  return err;
+}
+
+static int
+ti_lm25066a_io_write(ti_lm25066a* dev, ti_lm25066a_io* io)
+{
+  const ti_lm25066a_i2c_cmd* cmd;
+  uint8_t*                   out = dev->buffer;
+  uint16_t                   length = 0;
+  int                        err;
+
+  if (io->cmd >= IO_CMDS)
+    return -EIO;
+
+  cmd = &commands[io->cmd];
+
+  if ((cmd->flags & CMD_WR) == 0)
+    return -EIO;
+
+  out[0] = cmd->cmd;
+
+  if (cmd->size == 0) {
+    out[1] = 0;
+    length = 1;
+  }
+  else {
+    switch (io->type) {
+      case TI_LM25066A_8BIT:
+        out[1] = io->data.u8;
+        length = 1 + 1;
+        break;
+      case TI_LM25066A_16BIT:
+        out[1] = io->data.u16 >> 8;
+        out[2] = io->data.u16;
+        length = 2 + 1;
+        break;
+      case TI_LM25066A_VALUE:
+        break;
+      case TI_LM25066A_VALUES:
+        break;
+      case TI_LM25066A_STRING:
+        break;
+      case TI_LM25066A_RAW:
+        memcpy(&out[1], io->data.raw, cmd->size);
+        length = cmd->size + 1;
+        err = 0;
+      break;
+      default:
+        break;
+    }
+  }
+
+  if (length == 0)
+    err = -EIO;
+  else {
+    i2c_msg msgs[1] = {
+      {
+        .addr = dev->base.address,
+        .flags = 0,
+        .len = length,
+        .buf = &out[0]
+      }
+    };
+    err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+  }
+
+  return err;
+}
+
+static int
+ti_lm25066a_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg)
+{
+  ti_lm25066a*    dev = (ti_lm25066a*) iic_dev;
+  ti_lm25066a_io* io = (ti_lm25066a_io*) arg;
+  int        err;
+
+  switch (command) {
+    case TI_LM25066A_GET:
+      err = ti_lm25066a_io_read(dev, io);
+      break;
+    case TI_LM25066A_SET:
+      err = ti_lm25066a_io_write(dev, io);
+      break;
+    default:
+      err = -ENOTTY;
+      break;
+  }
+
+  return err;
+}
+
+int
+i2c_dev_register_ti_lm25066a(const char*                         bus_path,
+                             const char*                         dev_path,
+                             uint16_t                            address,
+                             const ti_lm25066a_conversion* const conversions,
+                             const int                           decimal_points)
+{
+  ti_lm25066a* dev;
+  int          i;
+
+  dev = (ti_lm25066a*)
+    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
+  if (dev == NULL) {
+    return -1;
+  }
+
+  dev->base.ioctl = ti_lm25066a_ioctl;
+  dev->pointer = -1;
+  dev->config_shadow = 0;
+  dev->conversions = conversions;
+  dev->scale = 1;
+  for (i = 0; i < decimal_points; ++i)
+    dev->scale *= 10;
+
+  return i2c_dev_register(&dev->base, dev_path);
+}
diff --git a/cpukit/dev/i2c/ti-tmp112.c b/cpukit/dev/i2c/ti-tmp112.c
new file mode 100644
index 0000000000..0886a4de9c
--- /dev/null
+++ b/cpukit/dev/i2c/ti-tmp112.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#include <stdio.h>
+
+#include <dev/i2c/i2c.h>
+#include <dev/i2c/ti-tmp112.h>
+
+/*
+ * Registers.
+ */
+#define TMP_TEMPERATURE (0)
+#define TMP_CONFIG      (1)
+#define TMP_LOW_TEMP    (2)
+#define TMP_HIGH_TEMP   (3)
+
+/*
+ * Configuration register.
+ */
+#define CFG_ONE_SHOT_BASE         (15)
+#define CFG_SHUTDOWN_BASE         (8)
+#define CFG_EXTENDED_MODE_BASE    (4)
+
+#define CFG_ONE_SHOT              (1 << CFG_ONE_SHOT_BASE)
+#define CFG_SHUTDOWN              (1 << CFG_SHUTDOWN_BASE)
+#define CFG_EXTENDED_MODE         (1 << CFG_EXTENDED_MODE_BASE)
+
+typedef struct {
+  i2c_dev  base;
+  uint8_t  pointer;
+  uint16_t config_shadow;
+} ti_tmp112;
+
+static int
+ti_tmp112_reg_write(ti_tmp112* dev, int reg, uint16_t value)
+{
+  uint8_t out[3];
+  i2c_msg msgs[1] = {
+    {
+      .addr = dev->base.address,
+      .flags = 0,
+      .len = (uint16_t) sizeof(out),
+      .buf = &out[0]
+    }
+  };
+  int err;
+  if (dev->pointer == reg) {
+    out[0] = (uint8_t) (value >> 8);
+    out[1] = (uint8_t) value;
+    msgs[0].len = 2;
+  }
+  else {
+    out[0] = reg;
+    out[1] = (uint8_t) (value >> 8);
+    out[2] = (uint8_t) value;
+  }
+  err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+  if (err == 0)
+    dev->pointer = reg;
+  else
+    dev->pointer = -1;
+  return err;
+}
+
+static int
+ti_tmp112_reg_read(ti_tmp112* dev, int reg, uint16_t* value)
+{
+  uint8_t in[2] = { 0, 0 };
+  int     err;
+  if (dev->pointer == reg) {
+    i2c_msg msgs[1] = {
+      {
+        .addr = dev->base.address,
+        .flags = I2C_M_RD,
+        .len = (uint16_t) sizeof(in),
+        .buf = &in[0]
+      }
+    };
+    err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+  }
+  else {
+    uint8_t out[1] = { (uint8_t) reg };
+    i2c_msg msgs[2] = {
+      {
+        .addr = dev->base.address,
+        .flags = 0,
+        .len = (uint16_t) sizeof(out),
+        .buf = &out[0]
+      }, {
+        .addr = dev->base.address,
+        .flags = I2C_M_RD,
+        .len = (uint16_t) sizeof(in),
+        .buf = &in[0]
+      }
+    };
+    err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
+    if (err == 0)
+      dev->pointer = reg;
+    else
+      dev->pointer = -1;
+  }
+  *value = (((uint16_t) in[0]) << 8) | in[1];
+  return err;
+}
+
+static int
+ti_tmp112_set_config(ti_tmp112* dev, uint16_t value)
+{
+  int err;
+  dev->config_shadow = value;
+  err = ti_tmp112_reg_write(dev, TMP_CONFIG, dev->config_shadow);
+  return err;
+}
+
+static int
+ti_tmp112_get_temp(ti_tmp112* dev, int* temp, bool raw)
+{
+  uint16_t value = 0;
+  int      err;
+
+  *temp = 0;
+
+  if ((dev->config_shadow & CFG_SHUTDOWN) == CFG_SHUTDOWN) {
+    i2c_bus_obtain(dev->base.bus);
+    err = ti_tmp112_reg_write(dev,
+                           TMP_CONFIG,
+                           dev->config_shadow | CFG_ONE_SHOT);
+    if (err == 0) {
+      uint16_t config = 0;
+      while (err == 0 && (config & CFG_ONE_SHOT) == 0)
+        err = ti_tmp112_reg_read(dev, TMP_CONFIG, &config);
+      err = ti_tmp112_reg_read(dev, TMP_TEMPERATURE, &value);
+    }
+    i2c_bus_release(dev->base.bus);
+  }
+  else {
+    err = ti_tmp112_reg_read(dev, TMP_TEMPERATURE, &value);
+  }
+
+  if (err == 0) {
+    if (raw) {
+      *temp = (int) value;
+    }
+    else {
+      int      bits = 12;
+      uint32_t u;
+      if ((dev->config_shadow & CFG_EXTENDED_MODE) != 0)
+        bits = 13;
+      u = value >> ((sizeof(value) * 8) - bits);
+      *temp = (int) (u  & ((1 << bits) - 1));
+      if ((u & (1 << (bits - 1))) != 0)
+        *temp |= ~((1 << bits) - 1);
+      *temp = *temp * 625;
+    }
+  }
+
+  return err;
+}
+
+static int
+ti_tmp112_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg)
+{
+  ti_tmp112* dev = (ti_tmp112*) iic_dev;
+  uint16_t   v16;
+  int        vint;
+  int        err;
+
+  switch (command) {
+    case TI_TMP112_GET_TEMP:
+      vint = 0;
+      err = ti_tmp112_get_temp(dev, &vint, false);
+      if (err == 0)
+        *((int*) arg) = vint;
+      break;
+    case TI_TMP112_GET_TEMP_RAW:
+      vint = 0;
+      err = ti_tmp112_get_temp(dev, &vint, true);
+      if (err == 0)
+        *((uint16_t*) arg) = (uint16_t) vint;
+      break;
+    case TI_TMP112_SET_CONFIG:
+      v16 = (uint16_t)(uintptr_t) arg;
+      err = ti_tmp112_set_config(dev, v16);
+      break;
+    case TI_TMP112_SET_LOW_TEMP:
+      v16 = (uint16_t)(uintptr_t) arg;
+      err = ti_tmp112_reg_write(dev, TMP_LOW_TEMP, v16);
+      break;
+    case TI_TMP112_SET_HIGH_TEMP:
+      v16 = (uint16_t)(uintptr_t) arg;
+      err = ti_tmp112_reg_write(dev, TMP_HIGH_TEMP, v16);
+      break;
+    default:
+      err = -ENOTTY;
+      break;
+  }
+
+  return err;
+}
+
+int
+i2c_dev_register_ti_tmp112(const char* bus_path,
+                           const char* dev_path,
+                           uint16_t    address)
+{
+  ti_tmp112* dev;
+
+  dev = (ti_tmp112*)
+    i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
+  if (dev == NULL) {
+    return -1;
+  }
+
+  dev->base.ioctl = ti_tmp112_ioctl;
+  dev->pointer = -1;
+  dev->config_shadow = 0x60a0;
+
+  return i2c_dev_register(&dev->base, dev_path);
+}
diff --git a/cpukit/dev/include/dev/i2c/fpga-i2c-slave.h b/cpukit/dev/include/dev/i2c/fpga-i2c-slave.h
new file mode 100644
index 0000000000..c10b26ac5b
--- /dev/null
+++ b/cpukit/dev/include/dev/i2c/fpga-i2c-slave.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+/*
+ * I2C slave for testing:
+ *       https://github.com/oetr/FPGA-I2C-Slave
+ */
+
+#ifndef FPGA_I2C_SLAVE_H
+#define FPGA_I2C_SLAVE_H
+
+#include <dev/i2c/i2c.h>
+
+int i2c_dev_register_fpga_i2c_slave(const char* bus_path,
+                                    const char* dev_path,
+                                    uint16_t   address,
+                                    size_t     size);
+
+#endif
diff --git a/cpukit/dev/include/dev/i2c/ti-ads-16bit-adc.h b/cpukit/dev/include/dev/i2c/ti-ads-16bit-adc.h
new file mode 100644
index 0000000000..40d680da88
--- /dev/null
+++ b/cpukit/dev/include/dev/i2c/ti-ads-16bit-adc.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+/*
+ * TI ADS1113 and ADS1115.
+ *   http://www.ti.com/product/ads1113/description
+ */
+
+#ifndef TI_ADS1113_ADS1115_H
+#define TI_ADS1113_ADS1115_H
+
+#include <dev/i2c/i2c.h>
+
+/*
+ * Supported devices. Please others once tested.
+ */
+typedef enum {
+  TI_ADS1113,
+  TI_ADS1114,
+  TI_ADS1115
+} ti_ads_adc;
+
+/*
+ * Multiplexer interface. Avalable on ADS1115.
+ */
+typedef enum {
+  TI_ADS_MUX_ApA0_AnA1  = 0,    /* default */
+  TI_ADS_MUX_ApA0_AnA3  = 1,
+  TI_ADS_MUX_ApA1_AnA3  = 2,
+  TI_ADS_MUX_ApA2_AnA3  = 3,
+  TI_ADS_MUX_ApA0_AnGND = 4,
+  TI_ADS_MUX_ApA1_AnGND = 5,
+  TI_ADS_MUX_ApA2_AnGND = 6,
+  TI_ADS_MUX_ApA3_AnGND = 7
+} ti_ads_adc_mux;
+
+/*
+ * Programmable Gain Amplifier. Avalable on ADS1114 and ADS1115.
+ */
+typedef enum {
+  TI_ADS_PGA_FS_6_144V   = 0,
+  TI_ADS_PGA_FS_4_096V   = 1,
+  TI_ADS_PGA_FS_2_048V   = 2,    /* default */
+  TI_ADS_PGA_FS_1_024V   = 3,
+  TI_ADS_PGA_FS_0_512V   = 4,
+  TI_ADS_PGA_FS_0_256V   = 5,
+  TI_ADS_PGA_FS_0_256V_2 = 6,
+  TI_ADS_PGA_FS_0_256V_3 = 7,
+} ti_ads_adc_pga;
+
+/*
+ * Mode.
+ */
+typedef enum {
+  TI_ADS_MODE_CONTINUOUS  = 0,
+  TI_ADS_MODE_SINGLE_SHOT = 1,    /* default */
+} ti_ads_adc_mode;
+
+/*
+ * Data rate.
+ */
+typedef enum {
+  TI_ADS_DATARATE_8SPS   = 0,
+  TI_ADS_DATARATE_16SPS  = 1,
+  TI_ADS_DATARATE_32SPS  = 2,
+  TI_ADS_DATARATE_64SPS  = 3,
+  TI_ADS_DATARATE_128SPS = 4,    /* default */
+  TI_ADS_DATARATE_250SPS = 5,
+  TI_ADS_DATARATE_475SPS = 6,
+  TI_ADS_DATARATE_860SPS = 7,
+} ti_ads_adc_data_rate;
+
+/*
+ * Comparitor interface. Avalable on ADS1114 and ADS1115.
+ *
+ * Create a value to write.
+ */
+#define TI_ADS_COMP_MODE_HYSTERESIS  (0 << 4)    /* default */
+#define TI_ADS_COMP_MODE_WINDOW      (1 << 4)
+#define TI_ADS_COMP_POL_ACTIVE_LOW   (0 << 3)    /* default */
+#define TI_ADS_COMP_POL_ACTIVE_HIGH  (1 << 3)
+#define TI_ADS_COMP_LAT_NON_LATCHING (0 << 2)    /* default */
+#define TI_ADS_COMP_LAT_LATCHING     (1 << 2)
+#define TI_ADS_COMP_QUE_DISABLE_COMP (3 << 0)    /* default */
+#define TI_ADS_COMP_QUE_AFTER_4      (2 << 0)
+#define TI_ADS_COMP_QUE_AFTER_2      (1 << 0)
+#define TI_ADS_COMP_QUE_AFTER_1      (0 << 0)
+
+/*
+ * IO control interface.
+ *
+ * Note: if in Power-down single-shot mode (default) a conversion requires the
+ *       device to power up and perform a conversion and this can take
+ *       while. The TI_ADS_ADC_GET_CONV_WAIT call sets the micro-seconds to
+ *       wait between polls. The actual rate will depend on the system tick.
+ */
+#define TI_ADS_ADC_GET_CONVERSION  (I2C_DEV_IO_CONTROL + 0)
+#define TI_ADS_ADC_SET_MUX         (I2C_DEV_IO_CONTROL + 1)
+#define TI_ADS_ADC_SET_PGA         (I2C_DEV_IO_CONTROL + 2)
+#define TI_ADS_ADC_SET_MODE        (I2C_DEV_IO_CONTROL + 3)
+#define TI_ADS_ADC_SET_DATA_RATE   (I2C_DEV_IO_CONTROL + 4)
+#define TI_ADS_ADC_SET_COMP        (I2C_DEV_IO_CONTROL + 5)
+#define TI_ADS_ADC_SET_LO_THRESH   (I2C_DEV_IO_CONTROL + 6)
+#define TI_ADS_ADC_SET_HI_THRESH   (I2C_DEV_IO_CONTROL + 7)
+#define TI_ADS_ADC_SET_CONV_WAIT   (I2C_DEV_IO_CONTROL + 8)
+
+/*
+ * Register the device.
+ */
+int i2c_dev_register_ti_ads_adc(const char* bus_path,
+                                const char* dev_path,
+                                uint16_t    address,
+                                ti_ads_adc  device);
+
+/*
+ * Perform a conversion. If the mode is single shot a single shot conversion is
+ * started and the call waits for the conversion to complete.
+ */
+static inline int
+ti_ads_adc_convert(int fd, uint16_t* sample)
+{
+  return ioctl(fd, TI_ADS_ADC_GET_CONVERSION, sample);
+}
+
+/*
+ * Set the multipler.
+ */
+static inline int
+ti_ads_adc_set_mux(int fd, ti_ads_adc_mux mux)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_MUX, (void *)(uintptr_t) mux);
+}
+
+/*
+ * Set the PGA.
+ */
+static inline int
+ti_ads_adc_set_pga(int fd, ti_ads_adc_pga pga)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_PGA, (void *)(uintptr_t) pga);
+}
+
+/*
+ * Set the mode.
+ */
+static inline int
+ti_ads_adc_set_mode(int fd, ti_ads_adc_mode mode)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_MODE, (void *)(uintptr_t) mode);
+}
+
+/*
+ * Set the data rate.
+ */
+static inline int
+ti_ads_adc_set_data_rate(int fd, ti_ads_adc_data_rate rate)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_DATA_RATE, (void *)(uintptr_t) rate);
+}
+
+/*
+ * Configure the comparator.
+ */
+static inline int
+ti_ads_adc_set_comparator(int fd, uint16_t comp)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_COMP, (void *)(uintptr_t) comp);
+}
+
+/*
+ * Set the lower threshold.
+ */
+static inline int
+ti_ads_adc_set_low_threshold(int fd, uint16_t level)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_LO_THRESH, (void *)(uintptr_t) level);
+}
+
+/*
+ * Set the upper threshold.
+ */
+static inline int
+ti_ads_adc_set_high_threshold(int fd, uint16_t level)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_HI_THRESH, (void *)(uintptr_t) level);
+}
+
+/*
+ * Set the conversion poll wait period.
+ */
+static inline int
+ti_ads_adc_set_conversion_poll_wait(int fd, uint32_t micro_seconds)
+{
+  return ioctl(fd, TI_ADS_ADC_SET_CONV_WAIT, (void *)(uintptr_t) micro_seconds);
+}
+
+#endif
diff --git a/cpukit/dev/include/dev/i2c/ti-lm25066a.h b/cpukit/dev/include/dev/i2c/ti-lm25066a.h
new file mode 100644
index 0000000000..4429f0fc70
--- /dev/null
+++ b/cpukit/dev/include/dev/i2c/ti-lm25066a.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+/*
+ * TI LM25066A
+ *   http://www.ti.com/product/LM25066A
+ */
+
+#ifndef TI_LM25066A_H
+#define TI_LM25066A_H
+
+#include <dev/i2c/i2c.h>
+
+/*
+ * PM Bus Command interface.
+ *
+ * The keys are:
+ *   IO : IO direction R=read W=write
+ *   SZ : Size in bytes.
+ */
+typedef enum
+{                                            /* IO SZ Name  */
+  TI_LM25066A_OPERATION                = 0,  /* R   1 PMBus */
+  TI_LM25066A_CLEAR_FAULTS             = 1,  /*  W  0 PMBus */
+  TI_LM25066A_CAPABILITY               = 2,  /* R   1 PMBus */
+  TI_LM25066A_VOUT_UV_WARN_LIMIT       = 3,  /* RW  2 PMBus */
+  TI_LM25066A_OT_FAULT_LIMIT           = 4,  /* RW  2 PMBus */
+  TI_LM25066A_OT_WARN_LIMIT            = 5,  /* RW  2 PMBus */
+  TI_LM25066A_VIN_OV_WARN_LIMIT        = 6,  /* RW  2 PMBus */
+  TI_LM25066A_VIN_UV_WARN_LIMIT        = 7,  /* RW  2 PMBus */
+  TI_LM25066A_STATUS_BYTE              = 8,  /* R   1 PMBus */
+  TI_LM25066A_STATUS_WORD              = 9,  /* R   2 PMBus */
+  TI_LM25066A_STATUS_VOUT              = 10, /* R   1 PMBus */
+  TI_LM25066A_STATUS_INPUT             = 11, /* R   1 PMBus */
+  TI_LM25066A_STATUS_TEMPERATURE       = 12, /* R   1 PMBus */
+  TI_LM25066A_STATUS_CML               = 13, /* R   1 PMBus */
+  TI_LM25066A_STATUS_MFR_SPECIFIC      = 14, /* R   1 PMBus */
+  TI_LM25066A_READ_VIN                 = 15, /* R   2 PMBus */
+  TI_LM25066A_READ_VOUT                = 16, /* R   2 PMBus */
+  TI_LM25066A_READ_TEMPERATURE_1       = 17, /* R   2 PMBus */
+  TI_LM25066A_MFR_ID                   = 18, /* R   3 PMBus */
+  TI_LM25066A_MFR_MODEL                = 19, /* R   8 PMBus */
+  TI_LM25066A_MFR_REVISION             = 20, /* R   2 PMBus */
+  TI_LM25066A_MFR_READ_VAUX            = 21, /* R   2 MFR_SPECIFIC_00 */
+  TI_LM25066A_MFR_READ_IIN             = 22, /* R   2 MFR_SPECIFIC_01 */
+  TI_LM25066A_MFR_READ_PIN             = 23, /* R   2 MFR_SPECIFIC_02 */
+  TI_LM25066A_MFR_IIN_OC_WARN_LIMIT    = 24, /* RW  2 MFR_SPECIFIC_03 */
+  TI_LM25066A_MFR_PIN_OP_WARN_LIMIT    = 25, /* RW  2 MFR_SPECIFIC_04 */
+  TI_LM25066A_MFR_PIN_PEAK             = 26, /* R   2 MFR_SPECIFIC_05 */
+  TI_LM25066A_MFR_CLEAR_PIN_PEAK       = 27, /*  W  0 MFR_SPECIFIC_06 */
+  TI_LM25066A_MFR_GATE_MASK            = 28, /* RW  1 MFR_SPECIFIC_07 */
+  TI_LM25066A_MFR_ALERT_MASK           = 29, /* RW  2 MFR_SPECIFIC_08 */
+  TI_LM25066A_MFR_DEVICE_SETUP         = 30, /* RW  1 MFR_SPECIFIC_09 */
+  TI_LM25066A_MFR_BLOCK_READ           = 31, /* R  12 MFR_SPECIFIC_10 */
+  TI_LM25066A_MFR_SAMPLES_FOR_AVG      = 32, /* RW  1 MFR_SPECIFIC_11 */
+  TI_LM25066A_MFR_READ_AVG_VIN         = 33, /* R   2 MFR_SPECIFIC_12 */
+  TI_LM25066A_MFR_READ_AVG_VOUT        = 34, /* R   2 MFR_SPECIFIC_13 */
+  TI_LM25066A_MFR_READ_AVG_IIN         = 35, /* R   2 MFR_SPECIFIC_14 */
+  TI_LM25066A_MFR_READ_AVG_PIN         = 36, /* R   2 MFR_SPECIFIC_15 */
+  TI_LM25066A_MFR_BLACK_BOX_READ       = 37, /* R  12 MFR_SPECIFIC_16 */
+  TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ = 38, /* R   2 MFR_SPECIFIC_17 */
+  TI_LM25066A_MFR_AVG_BLOCK_READ       = 39, /* R  12 MFR_SPECIFIC_18 */
+} ti_lm25066a_cmd;
+
+/*
+ * Real world converters. Page 46 of the datasheet discusses reading and
+ * writing telemtry data and obtaining the real world values. There are 8
+ * separate conversions using the same formula.
+ *
+ * The formula is:
+ *
+ *      1        -R
+ *  X = - (Y x 10  - b)
+ *      m
+ *
+ *  X: the calculated "real world" value (volts, amps, watt, etc.)
+ *  m: the slope coefficient
+ *  Y: a two byte two's complement integer received from device
+ *  b: the offset, a two byte two's complement integer
+ *  R: the exponent, a one byte two's complement integer
+ *
+ * R in the table is inverted because we cannot store 0.01 in an int. This
+ * makes the equation:
+ *
+ *      1  Y
+ *  X = - (- - b)
+ *      m  R
+ *
+ * The R value lets the integer result have decimal places.
+ *
+ * There are 8 conversion table entries listed in Table 41 of the
+ * data sheet. They are:
+ *
+ * 1.    READ_VIN, READ_AVG_VIN, VIN_OV_WARN_LIMIT, VIN_UV_WARN_LIMIT
+ * 2.    READ_VOUT, READ_AVG_VOUT, VOUT_UV_WARN_LIMIT
+ * 3.    READ_VAUX
+ * 4.GND READ_IIN, READ_AVG_IIN, MFR_IIN_OC_WARN_LIMIT
+ * 4.VDD READ_IIN, READ_AVG_IIN, MFR_IIN_OC_WARN_LIMIT
+ * 5.GND READ_PIN, READ_AVG_PIN, READ_PIN_PEAK, MFR_PIN_OP_WARN_LIMIT
+ * 5.VCC READ_PIN, READ_AVG_PIN, READ_PIN_PEAK, MFR_PIN_OP_WARN_LIMIT
+ * 6.    READ_TEMPERATURE_1, OT_WARN_LIMIT, OT_FAULT_LIMIT
+ *
+ * You need to provide 6 sets of conversion factors. Row 4 and 5 depend on how
+ * the device is wired. The driver will use the matching table row to convert
+ * the 2-complement 12 bit to a real world value.
+ */
+#define TI_LM25066A_CONVERSION_SIZE (6)
+typedef struct
+{
+  int         m;    /* The slope coefficient */
+  int         b;    /* The offset */
+  int         R;    /* The inverted power of 10 of -R */
+} ti_lm25066a_conversion;
+
+/*
+ * IO control interface.
+ */
+#define TI_LM25066A_GET (I2C_DEV_IO_CONTROL + 0)
+#define TI_LM25066A_SET (I2C_DEV_IO_CONTROL + 1)
+
+/*
+ * IO data types.
+ */
+typedef enum
+{
+  TI_LM25066A_8BIT   = 0,
+  TI_LM25066A_16BIT  = 1,
+  TI_LM25066A_VALUE  = 2,
+  TI_LM25066A_VALUES = 3,
+  TI_LM25066A_STRING = 4,
+  TI_LM25066A_RAW    = 5
+} ti_lm25066a_data;
+
+/*
+ * Struct to move data into and out of the driver.
+ */
+typedef struct
+{
+  ti_lm25066a_cmd  cmd;
+  ti_lm25066a_data type;
+  union {
+    uint8_t  u8;
+    uint16_t u16;
+    int      value;
+    int      values[6];
+    char     string[9];
+    uint8_t* raw;
+  } data;
+}  ti_lm25066a_io;
+
+/*
+ * Register the device.
+ *
+ * The conversions table has 6 columns.
+ *
+ * The values are an integer so the decimal_point value scales the value so it
+ * can fit in an integer with the required number of decimal points.
+ */
+int i2c_dev_register_ti_lm25066a(const char*                         bus_path,
+                                 const char*                         dev_path,
+                                 uint16_t                            address,
+                                 const ti_lm25066a_conversion* const conversions,
+                                 const int                           decimal_points);
+
+/*
+ * Get.
+ */
+static inline int
+ti_lm25066a_get(int fd, ti_lm25066a_io* io)
+{
+  return ioctl(fd, TI_LM25066A_GET, io);
+}
+
+/*
+ * Set.
+ */
+static inline int
+ti_lm25066a_set(int fd, ti_lm25066a_io* io)
+{
+  return ioctl(fd, TI_LM25066A_SET, io);
+}
+
+
+#endif
diff --git a/cpukit/dev/include/dev/i2c/ti-tmp112.h b/cpukit/dev/include/dev/i2c/ti-tmp112.h
new file mode 100644
index 0000000000..58efbef7d8
--- /dev/null
+++ b/cpukit/dev/include/dev/i2c/ti-tmp112.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016-2017 Chris Johns <chrisj at rtems.org>
+ * All rights reserved.
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+/*
+ * TI TMP112
+ *   http://www.ti.com/product/TMP112
+ */
+
+#ifndef TI_TMP112_H
+#define TI_TMP112_H
+
+#include <dev/i2c/i2c.h>
+
+/*
+ * Confirguration.
+ */
+#define TI_TMP112_CR_0_25Hz      (0 << 6)
+#define TI_TMP112_CR_1Hz         (1 << 6)
+#define TI_TMP112_CR_4Hz         (2 << 6)    /* default */
+#define TI_TMP112_CR_8Hz         (3 << 6)
+#define TI_TMP112_EM_NORMAL      (0 << 4)    /* default */
+#define TI_TMP112_EM_EXTENDED    (1 << 4)
+#define TI_TMP112_SD_ON          (0 << 8)    /* default */
+#define TI_TMP112_SD_SHUTDOWN    (1 << 8)
+#define TI_TMP112_TM_COMPARATOR  (0 << 9)    /* default */
+#define TI_TMP112_TM_INTERRUPT   (1 << 9)
+#define TI_TMP112_POL_ALERT_LOW  (0 << 10)   /* default */
+#define TI_TMP112_POL_ALERT_HIGH (1 << 10)
+#define TI_TMP112_FQL_1          (0 << 11)   /* default */
+#define TI_TMP112_FQL_2          (1 << 11)
+#define TI_TMP112_FQL_4          (2 << 11)
+#define TI_TMP112_FQL_6          (3 << 11)
+
+/*
+ * IO control interface.
+ */
+#define TI_TMP112_GET_TEMP      (I2C_DEV_IO_CONTROL + 0)
+#define TI_TMP112_GET_TEMP_RAW  (I2C_DEV_IO_CONTROL + 1)
+#define TI_TMP112_SET_CONFIG    (I2C_DEV_IO_CONTROL + 2)
+#define TI_TMP112_SET_LOW_TEMP  (I2C_DEV_IO_CONTROL + 3)
+#define TI_TMP112_SET_HIGH_TEMP (I2C_DEV_IO_CONTROL + 4)
+
+/*
+ * Register the device.
+ */
+int i2c_dev_register_ti_tmp112(const char* bus_path,
+                               const char* dev_path,
+                               uint16_t    address);
+
+/*
+ * Get the temperature in degrees C x 10000. To print you would so:
+ *
+ *   printf("Temperature is %3d.%04d\n", temp / 10000, temp % 10000);
+ *
+ * If the device is shutdown a single conversion is made waiting for the
+ * conversion to complete.
+ */
+static inline int
+ti_tmp112_get_temperature(int fd, int* temp)
+{
+  return ioctl(fd, TI_TMP112_GET_TEMP, temp);
+}
+
+/*
+ * Get the temperature as the raw register value.
+ *
+ * If the device is shutdown a single conversion is made waiting for the
+ * conversion to complete.
+ */
+static inline int
+ti_tmp112_get_temperature_raw(int fd, unsigned int* temp)
+{
+  return ioctl(fd, TI_TMP112_GET_TEMP_RAW, temp);
+}
+
+/*
+ * Set the configuration.
+ */
+static inline int
+ti_tmp112_adc_set_mux(int fd, uint16_t config)
+{
+  return ioctl(fd, TI_TMP112_SET_CONFIG, (void *)(uintptr_t) config);
+}
+
+/*
+ * Set the low temperature.
+ */
+static inline int
+ti_tmp112_set_low_temperator(int fd, uint16_t temp)
+{
+  return ioctl(fd, TI_TMP112_SET_LOW_TEMP, (void *)(uintptr_t) temp);
+}
+
+/*
+ * Set the high temperature.
+ */
+static inline int
+ti_tmp112_adc_set_high_threshold(int fd, uint16_t level)
+{
+  return ioctl(fd, TI_TMP112_SET_HIGH_TEMP, (void *)(uintptr_t) level);
+}
+
+#endif
diff --git a/cpukit/dev/preinstall.am b/cpukit/dev/preinstall.am
index 307d0a4a6e..ecdf50a624 100644
--- a/cpukit/dev/preinstall.am
+++ b/cpukit/dev/preinstall.am
@@ -27,6 +27,10 @@ $(PROJECT_INCLUDE)/dev/i2c/eeprom.h: include/dev/i2c/eeprom.h $(PROJECT_INCLUDE)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/eeprom.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/eeprom.h
 
+$(PROJECT_INCLUDE)/dev/i2c/fpga-i2c-slave.h: include/dev/i2c/fpga-i2c-slave.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/fpga-i2c-slave.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/fpga-i2c-slave.h
+
 $(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h: include/dev/i2c/gpio-nxp-pca9535.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/gpio-nxp-pca9535.h
@@ -39,6 +43,18 @@ $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h: include/dev/i2c/switch-nxp-pca
 	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
 PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/switch-nxp-pca9548a.h
 
+$(PROJECT_INCLUDE)/dev/i2c/ti-ads-16bit-adc.h: include/dev/i2c/ti-ads-16bit-adc.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/ti-ads-16bit-adc.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/ti-ads-16bit-adc.h
+
+$(PROJECT_INCLUDE)/dev/i2c/ti-lm25066a.h: include/dev/i2c/ti-lm25066a.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/ti-lm25066a.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/ti-lm25066a.h
+
+$(PROJECT_INCLUDE)/dev/i2c/ti-tmp112.h: include/dev/i2c/ti-tmp112.h $(PROJECT_INCLUDE)/dev/i2c/$(dirstamp)
+	$(INSTALL_DATA) $< $(PROJECT_INCLUDE)/dev/i2c/ti-tmp112.h
+PREINSTALL_FILES += $(PROJECT_INCLUDE)/dev/i2c/ti-tmp112.h
+
 $(PROJECT_INCLUDE)/dev/spi/$(dirstamp):
 	@$(MKDIR_P) $(PROJECT_INCLUDE)/dev/spi
 	@: > $(PROJECT_INCLUDE)/dev/spi/$(dirstamp)
-- 
2.13.2



More information about the devel mailing list