diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 5fec852dae559f309c1df56029ec5e11c243e160..a53cda6b541d27896b1045c187056fce02482772 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -133,6 +133,7 @@ config ARCH_CHIP_SAMA5
 	select ARCH_CORTEXA5
 	select ARCH_HAVE_FPU
 	select ARCH_HAVE_LOWVECTORS
+	select ARCH_HAVE_I2CRESET
 	---help---
 		Atmel SAMA5 (ARM Cortex-A5)
 
diff --git a/arch/arm/src/sama5/sam_pio.c b/arch/arm/src/sama5/sam_pio.c
index 459b6ffee046e1c5970f845b80d1bfead61e4d11..29b2c52eafe63b9f90901374ad5144b5224935bb 100644
--- a/arch/arm/src/sama5/sam_pio.c
+++ b/arch/arm/src/sama5/sam_pio.c
@@ -670,6 +670,11 @@ void sam_piowrite(pio_pinset_t pinset, bool value)
 
   if (base != 0)
     {
+      /* Set or clear the output as requested.  NOTE: that there is no
+       * check if the pin is actually configured as an output so this could,
+       * potentially, do nothing.
+       */
+
       if (value)
         {
           putreg32(pin, base + SAM_PIO_SODR_OFFSET);
@@ -697,17 +702,21 @@ bool sam_pioread(pio_pinset_t pinset)
 
   if (base != 0)
     {
-      pin  = sam_piopin(pinset);
+      pin = sam_piopin(pinset);
 
-      if ((pinset & PIO_MODE_MASK) == PIO_OUTPUT)
-        {
-          regval = getreg32(base + SAM_PIO_ODSR_OFFSET);
-        }
-      else
-        {
-          regval = getreg32(base + SAM_PIO_PDSR_OFFSET);
-        }
+      /* For output PIOs, the ODSR register provides the output value to
+       * drive the pin.  The PDSR register, on the the other hand, provides
+       * the current sensed value on a pin, whether the pin is configured
+       * as an input, an output or as a peripheral.
+       *
+       * There is small delay between the setting in ODSR and PDSR but
+       * otherwise the they should be the same unless something external
+       * is driving the pin.
+       *
+       * Let's assume that PDSR is what the caller wants.
+       */
 
+      regval = getreg32(base + SAM_PIO_PDSR_OFFSET);
       return (regval & pin) != 0;
     }
 
diff --git a/arch/arm/src/sama5/sam_twi.c b/arch/arm/src/sama5/sam_twi.c
index 0d4cffeb268bf43857917b9c3a0c813af8941359..7f07e530ad6f49d563c3c769271bb9f24fee50ec 100644
--- a/arch/arm/src/sama5/sam_twi.c
+++ b/arch/arm/src/sama5/sam_twi.c
@@ -104,6 +104,14 @@
 
 #define TWI_MAX_FREQUENCY 66000000   /* Maximum TWI frequency */
 
+/* Macros to convert a I2C pin to a PIO open-drain output */
+
+#define I2C_INPUT       (PIO_INPUT | PIO_CFG_PULLUP)
+#define I2C_OUTPUT      (PIO_OUTPUT | PIO_CFG_OPENDRAIN | PIO_OUTPUT_SET)
+
+#define MKI2C_INPUT(p)  (((p) & (PIO_PORT_MASK | PIO_PIN_MASK)) | I2C_INPUT)
+#define MKI2C_OUTPUT(p) (((p) & (PIO_PORT_MASK | PIO_PIN_MASK)) | I2C_OUTPUT)
+
 /* Debug ***********************************************************************/
 /* CONFIG_DEBUG_I2C + CONFIG_DEBUG enables general I2C debug output. */
 
@@ -122,18 +130,30 @@
 /*******************************************************************************
  * Private Types
  *******************************************************************************/
+/* Invariant attributes of a TWI bus */
+
+struct twi_attr_s
+{
+  uint8_t             twi;        /* TWI device number (for debug output) */
+  uint8_t             pid;        /* TWI peripheral ID */
+  uint16_t            irq;        /* IRQ number for this TWI bus */
+  pio_pinset_t        sclcfg;     /* TWI CK pin configuration (SCL in I2C-ese) */
+  pio_pinset_t        sdacfg;     /* TWI D pin configuration (SDA in I2C-ese) */
+  uintptr_t           base;       /* Base address of TWI registers */
+  xcpt_t              handler;    /* TWI interrupt handler */
+};
+
+/* State of a TWI bus */
 
 struct twi_dev_s
 {
   struct i2c_dev_s    dev;        /* Generic I2C device */
+  const struct twi_attr_s *attr;  /* Invariant attributes of TWI device */
   struct i2c_msg_s    *msg;       /* Message list */
-  uintptr_t           base;       /* Base address of registers */
-  uint32_t            frequency;  /* TWO clock frequency */
-  uint16_t            irq;        /* IRQ number for this device */
+  uint32_t            frequency;  /* TWI clock frequency */
   uint16_t            address;    /* Slave address */
   uint16_t            flags;      /* Transfer flags */
   uint8_t             msgc;       /* Number of message in the message list */
-  uint8_t             twi;        /* TWI peripheral number (for debug output) */
 
   sem_t               exclsem;    /* Only one thread can access at a time */
   sem_t               waitsem;    /* Wait for TWI transfer completion */
@@ -226,26 +246,70 @@ static int twi_registercallback(FAR struct i2c_dev_s *dev,
 
 static uint32_t twi_hw_setfrequency(struct twi_dev_s *priv,
           uint32_t frequency);
-static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid,
-          uint32_t frequency);
+static void twi_hw_initialize(struct twi_dev_s *priv, uint32_t frequency);
+static void twi_initialize(struct twi_dev_s *priv, uint32_t frequency);
 
 /*******************************************************************************
  * Private Data
  *******************************************************************************/
 
 #ifdef CONFIG_SAMA5_TWI0
+static const struct twi_attr_s g_twi0attr =
+{
+  .twi     = 0,
+  .pid     = SAM_PID_TWI0,
+  .irq     = SAM_IRQ_TWI0,
+  .sclcfg  = PIO_TWI0_CK,
+  .sdacfg  = PIO_TWI0_D,
+  .base    = SAM_TWI0_VBASE,
+  .handler = twi0_interrupt,
+};
+
 static struct twi_dev_s g_twi0;
 #endif
 
 #ifdef CONFIG_SAMA5_TWI1
+static const struct twi_attr_s g_twi1attr =
+{
+  .twi     = 1,
+  .pid     = SAM_PID_TWI1,
+  .irq     = SAM_IRQ_TWI1,
+  .sclcfg  = PIO_TWI1_CK,
+  .sdacfg  = PIO_TWI1_D,
+  .base    = SAM_TWI1_VBASE,
+  .handler = twi1_interrupt,
+};
+
 static struct twi_dev_s g_twi1;
 #endif
 
 #ifdef CONFIG_SAMA5_TWI2
+static const struct twi_attr_s g_twi2attr =
+{
+  .twi     = 2,
+  .pid     = SAM_PID_TWI2,
+  .irq     = SAM_IRQ_TWI2,
+  .sclcfg  = PIO_TWI2_CK,
+  .sdacfg  = PIO_TWI2_D,
+  .base    = SAM_TWI2_VBASE,
+  .handler = twi2_interrupt,
+};
+
 static struct twi_dev_s g_twi2;
 #endif
 
 #ifdef CONFIG_SAMA5_TWI3
+static const struct twi_attr_s g_twi3attr =
+{
+  .twi     = 3,
+  .pid     = SAM_PID_TWI3,
+  .irq     = SAM_IRQ_TWI3,
+  .sclcfg  = PIO_TWI3_CK,
+  .sdacfg  = PIO_TWI3_D,
+  .base    = SAM_TWI3_VBASE,
+  .handler = twi3_interrupt,
+};
+
 static struct twi_dev_s g_twi3;
 #endif
 
@@ -406,7 +470,7 @@ static void twi_putabs(struct twi_dev_s *priv, uintptr_t address,
 
 static inline uint32_t twi_getrel(struct twi_dev_s *priv, unsigned int offset)
 {
-  return twi_getabs(priv, priv->base + offset);
+  return twi_getabs(priv, priv->attr->base + offset);
 }
 
 /****************************************************************************
@@ -421,7 +485,7 @@ static inline uint32_t twi_getrel(struct twi_dev_s *priv, unsigned int offset)
 static inline void twi_putrel(struct twi_dev_s *priv, unsigned int offset,
                               uint32_t value)
 {
-  twi_putabs(priv, priv->base + offset, value);
+  twi_putabs(priv, priv->attr->base + offset, value);
 }
 
 /****************************************************************************
@@ -449,9 +513,10 @@ static int twi_wait(struct twi_dev_s *priv)
 
   do
     {
-      i2cvdbg("TWI%d Waiting...\n", priv->twi);
+      i2cvdbg("TWI%d Waiting...\n", priv->attr->twi);
       twi_takesem(&priv->waitsem);
-      i2cvdbg("TWI%d Awakened with result: %d\n", priv->twi, priv->result);
+      i2cvdbg("TWI%d Awakened with result: %d\n",
+              priv->attr->twi, priv->result);
     }
   while (priv->result == -EBUSY);
 
@@ -508,7 +573,7 @@ static int twi_interrupt(struct twi_dev_s *priv)
   imr     = twi_getrel(priv, SAM_TWI_IMR_OFFSET);
   pending = sr & imr;
 
-  i2cllvdbg("TWI%d pending: %08x\n", priv->twi, pending);
+  i2cllvdbg("TWI%d pending: %08x\n", priv->attr->twi, pending);
 
   /* Byte received */
 
@@ -604,7 +669,7 @@ static int twi_interrupt(struct twi_dev_s *priv)
     {
       /* Wake up the thread with an I/O error indication */
 
-      i2clldbg("ERROR: TWI%d pending: %08x\n", priv->twi, pending);
+      i2clldbg("ERROR: TWI%d pending: %08x\n", priv->attr->twi, pending);
       twi_wakeup(priv, -EIO);
     }
 
@@ -654,7 +719,7 @@ static void twi_timeout(int argc, uint32_t arg, ...)
 {
   struct twi_dev_s *priv = (struct twi_dev_s *)arg;
 
-  i2clldbg("TWI%d Timeout!\n", priv->twi);
+  i2clldbg("TWI%d Timeout!\n", priv->attr->twi);
   twi_wakeup(priv, -ETIMEDOUT);
 }
 
@@ -791,7 +856,7 @@ static int twi_setaddress(FAR struct i2c_dev_s *dev, int addr, int nbits)
 {
   struct twi_dev_s *priv = (struct twi_dev_s *) dev;
 
-  i2cvdbg("TWI%d address: %02x nbits: %d\n", priv->twi, addr, nbits);
+  i2cvdbg("TWI%d address: %02x nbits: %d\n", priv->attr->twi, addr, nbits);
   DEBUGASSERT(dev != NULL && nbits == 7);
 
   /* Get exclusive access to the device */
@@ -830,7 +895,7 @@ static int twi_write(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer, int wbuf
     .length = wbuflen
   };
 
-  i2cvdbg("TWI%d buflen: %d\n", priv->twi, wbuflen);
+  i2cvdbg("TWI%d buflen: %d\n", priv->attr->twi, wbuflen);
   DEBUGASSERT(dev != NULL);
 
   /* Get exclusive access to the device */
@@ -889,7 +954,7 @@ static int twi_read(FAR struct i2c_dev_s *dev, uint8_t *rbuffer, int rbuflen)
   };
 
   DEBUGASSERT(dev != NULL);
-  i2cvdbg("TWI%d rbuflen: %d\n", priv->twi, rbuflen);
+  i2cvdbg("TWI%d rbuflen: %d\n", priv->attr->twi, rbuflen);
 
   /* Get exclusive access to the device */
 
@@ -955,7 +1020,7 @@ static int twi_writeread(FAR struct i2c_dev_s *dev, const uint8_t *wbuffer,
   };
 
   DEBUGASSERT(dev != NULL);
-  i2cvdbg("TWI%d wbuflen: %d rbuflen: %d\n", priv->twi, wbuflen, rbuflen);
+  i2cvdbg("TWI%d wbuflen: %d rbuflen: %d\n", priv->attr->twi, wbuflen, rbuflen);
 
   /* Get exclusive access to the device */
 
@@ -1042,7 +1107,7 @@ static int twi_transfer(FAR struct i2c_dev_s *dev,
   int ret;
 
   DEBUGASSERT(dev != NULL);
-  i2cvdbg("TWI%d count: %d\n", priv->twi, count);
+  i2cvdbg("TWI%d count: %d\n", priv->attr->twi, count);
 
   /* Get exclusive access to the device */
 
@@ -1129,7 +1194,7 @@ static uint32_t twi_hw_setfrequency(struct twi_dev_s *priv, uint32_t frequency)
 
   actual = (priv->frequency / 2) / (((1 << ckdiv) * cldiv) + 2);
   i2cvdbg("TWI%d frequency: %d ckdiv: %d cldiv: %d actual: %d\n",
-          priv->twi, frequency, ckdiv, cldiv, actual);
+          priv->attr->twi, frequency, ckdiv, cldiv, actual);
 
   return actual;
 }
@@ -1142,13 +1207,12 @@ static uint32_t twi_hw_setfrequency(struct twi_dev_s *priv, uint32_t frequency)
  *
  *******************************************************************************/
 
-static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid,
-                              uint32_t frequency)
+static void twi_hw_initialize(struct twi_dev_s *priv, uint32_t frequency)
 {
   uint32_t regval;
   uint32_t mck;
 
-  i2cvdbg("TWI%d Initializing\n", priv->twi);
+  i2cvdbg("TWI%d Initializing\n", priv->attr->twi);
 
   /* SVEN: TWI Slave Mode Enabled */
 
@@ -1207,7 +1271,7 @@ static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid,
 
   /* Set the TWI peripheral input clock to the maximum, valid frequency */
 
-  regval |= PMC_PCR_PID(pid) | PMC_PCR_CMD | PMC_PCR_EN;
+  regval |= PMC_PCR_PID(priv->attr->pid) | PMC_PCR_CMD | PMC_PCR_EN;
   twi_putabs(priv, SAM_PMC_PCR, regval);
 
   /* Set the initial TWI data transfer frequency */
@@ -1215,6 +1279,51 @@ static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid,
   (void)twi_hw_setfrequency(priv, frequency);
 }
 
+/*******************************************************************************
+ * Name: twi_sw_initialize
+ *
+ * Description:
+ *   Initialize/Re-initialize one TWI state structure
+ *
+ *******************************************************************************/
+
+static void twi_initialize(struct twi_dev_s *priv, uint32_t frequency)
+{
+  irqstate_t flags = irqsave();
+
+  /* Configure PIO pins */
+
+  sam_configpio(priv->attr->sclcfg);
+  sam_configpio(priv->attr->sdacfg);
+
+  /* Initialize the device structure */
+
+  priv->dev.ops = &g_twiops;
+  priv->address = 0;
+  priv->flags   = 0;
+
+  sem_init(&priv->exclsem, 0, 1);
+  sem_init(&priv->waitsem, 0, 0);
+
+  /* Allocate a watchdog timer */
+
+  priv->timeout = wd_create();
+  DEBUGASSERT(priv->timeout != 0);
+
+  /* Configure and enable the TWI hardware */
+
+  twi_hw_initialize(priv, frequency);
+
+  /* Attach Interrupt Handler */
+
+  irq_attach(priv->attr->irq, priv->attr->handler);
+
+  /* Enable Interrupts */
+
+  up_enable_irq(priv->attr->irq);
+  irqrestore(flags);
+}
+
 /*******************************************************************************
  * Public Functions
  *******************************************************************************/
@@ -1230,155 +1339,90 @@ static void twi_hw_initialize(struct twi_dev_s *priv, unsigned int pid,
 struct i2c_dev_s *up_i2cinitialize(int bus)
 {
   struct twi_dev_s *priv;
-  xcpt_t handler;
-  irqstate_t flags;
   uint32_t frequency;
-  unsigned int pid;
 
   i2cvdbg("Initializing TWI%d\n", bus);
 
-  flags = irqsave();
-
 #ifdef CONFIG_SAMA5_TWI0
   if (bus == 0)
     {
-      /* Set up TWI0 register base address and IRQ number */
+      /* Select up TWI0 and setup invariant attributes */
 
       priv       = &g_twi0;
-      priv->base = SAM_TWI0_VBASE;
-      priv->irq  = SAM_IRQ_TWI0;
-      priv->twi  = 0;
+      priv->attr = &g_twi0attr;
 
       /* Enable peripheral clocking */
 
       sam_twi0_enableclk();
 
-      /* Configure PIO pins */
+      /* Select the (initial) TWI frequency */
 
-      sam_configpio(PIO_TWI0_CK);
-      sam_configpio(PIO_TWI0_D);
-
-      /* Select the interrupt handler, TWI frequency, and peripheral ID */
-
-      handler    = twi0_interrupt;
-      frequency  = CONFIG_SAMA5_TWI0_FREQUENCY;
-      pid        = SAM_PID_TWI0;
+      frequency = CONFIG_SAMA5_TWI0_FREQUENCY;
     }
   else
 #endif
 #ifdef CONFIG_SAMA5_TWI1
   if (bus == 1)
     {
-      /* Set up TWI1 register base address and IRQ number */
+      /* Select up TWI1 and setup invariant attributes */
 
       priv       = &g_twi1;
-      priv->base = SAM_TWI1_VBASE;
-      priv->irq  = SAM_IRQ_TWI1;
-      priv->twi  = 1;
+      priv->attr = &g_twi1attr;
 
       /* Enable peripheral clocking */
 
       sam_twi1_enableclk();
 
-      /* Configure PIO pins */
-
-      sam_configpio(PIO_TWI1_CK);
-      sam_configpio(PIO_TWI1_D);
-
-      /* Select the interrupt handler, TWI frequency, and peripheral ID */
+      /* Select the (initial) TWI frequency */
 
-      handler    = twi1_interrupt;
-      frequency  = CONFIG_SAMA5_TWI1_FREQUENCY;
-      pid        = SAM_PID_TWI1;
+      frequency = CONFIG_SAMA5_TWI1_FREQUENCY;
     }
   else
 #endif
 #ifdef CONFIG_SAMA5_TWI2
   if (bus == 2)
     {
-      /* Set up TWI2 register base address and IRQ number */
+      /* Select up TWI2 and setup invariant attributes */
 
       priv       = &g_twi2;
-      priv->base = SAM_TWI2_VBASE;
-      priv->irq  = SAM_IRQ_TWI2;
-      priv->twi  = 2;
-
-      /* Configure PIO pins */
-
-      sam_configpio(PIO_TWI2_CK);
-      sam_configpio(PIO_TWI2_D);
+      priv->attr = &g_twi2attr;
 
       /* Enable peripheral clocking */
 
       sam_twi2_enableclk();
 
-      /* Select the interrupt handler, TWI frequency, and peripheral ID */
+      /* Select the (initial) TWI frequency */
 
-      handler    = twi2_interrupt;
-      frequency  = CONFIG_SAMA5_TWI2_FREQUENCY;
-      pid        = SAM_PID_TWI2;
+      frequency = CONFIG_SAMA5_TWI2_FREQUENCY;
     }
   else
 #endif
 #ifdef CONFIG_SAMA5_TWI3
   if (bus == 3)
     {
-      /* Set up TWI3 register base address and IRQ number */
+      /* Select up TWI3 and setup invariant attributes */
 
       priv       = &g_twi3;
-      priv->base = SAM_TWI3_VBASE;
-      priv->irq  = SAM_IRQ_TWI3;
-      priv->twi  = 3;
-
-      /* Configure PIO pins */
-
-      sam_configpio(PIO_TWI3_CK);
-      sam_configpio(PIO_TWI3_D);
+      priv->attr = &g_twi2attr;
 
       /* Enable peripheral clocking */
 
       sam_twi3_enableclk();
 
-      /* Select the interrupt handler, TWI frequency, and peripheral ID */
+      /* Select the (initial) TWI frequency */
 
-      handler    = twi3_interrupt;
-      frequency  = CONFIG_SAMA5_TWI3_FREQUENCY;
-      pid        = SAM_PID_TWI3;
+      frequency = CONFIG_SAMA5_TWI3_FREQUENCY;
     }
   else
 #endif
     {
-      irqrestore(flags);
       i2cdbg("ERROR: Unsupported bus: TWI%d\n", bus);
       return NULL;
     }
 
-  /* Initialize the device structure */
-
-  priv->dev.ops = &g_twiops;
-  priv->address = 0;
-  priv->flags   = 0;
-
-  sem_init(&priv->exclsem, 0, 1);
-  sem_init(&priv->waitsem, 0, 0);
-
-  /* Allocate a watchdog timer */
-
-  priv->timeout = wd_create();
-  DEBUGASSERT(priv->timeout != 0);
-
-  /* Configure and enable the TWI hardware */
-
-  twi_hw_initialize(priv, pid, frequency);
-
-  /* Attach Interrupt Handler */
+  /* Initialize the TWI peripheral */
 
-  irq_attach(priv->irq, handler);
-
-  /* Enable Interrupts */
-
-  up_enable_irq(priv->irq);
-  irqrestore(flags);
+  twi_initialize(priv, frequency);
   return &priv->dev;
 }
 
@@ -1390,15 +1434,15 @@ struct i2c_dev_s *up_i2cinitialize(int bus)
  *
  *******************************************************************************/
 
-int up_i2cuninitialize(FAR struct i2c_dev_s * dev)
+int up_i2cuninitialize(FAR struct i2c_dev_s *dev)
 {
   struct twi_dev_s *priv = (struct twi_dev_s *) dev;
 
-  i2cvdbg("TWI%d Un-initializing\n", priv->twi);
+  i2cvdbg("TWI%d Un-initializing\n", priv->attr->twi);
 
   /* Disable interrupts */
 
-  up_disable_irq(priv->irq);
+  up_disable_irq(priv->attr->irq);
 
   /* Reset data structures */
 
@@ -1412,8 +1456,127 @@ int up_i2cuninitialize(FAR struct i2c_dev_s * dev)
 
   /* Detach Interrupt Handler */
 
-  irq_detach(priv->irq);
+  irq_detach(priv->attr->irq);
   return OK;
 }
 
-#endif
+/************************************************************************************
+ * Name: up_i2creset
+ *
+ * Description:
+ *   Reset an I2C bus
+ *
+ ************************************************************************************/
+
+#ifdef CONFIG_I2C_RESET
+int up_i2creset(FAR struct i2c_dev_s *dev)
+{
+  struct twi_dev_s *priv = (struct twi_dev_s *)dev;
+  unsigned int clockcnt;
+  unsigned int stretchcnt;
+  uint32_t frequency;
+  uint32_t sclpin;
+  uint32_t sdapin;
+  int ret;
+
+  ASSERT(priv);
+
+  /* Get exclusive access to the TWI device */
+
+  twi_takesem(&priv->exclsem);
+
+  /* Uninitialize the port, saving the currently selected frequency.  Other
+   * dynamically configured data (address and transfer flags) will be
+   * preserved.
+   */
+
+  frequency = priv->frequency;
+  up_i2cuninitialize(&priv->dev);
+
+  /* Use GPIO configuration to un-wedge the bus */
+
+  sclpin = MKI2C_OUTPUT(priv->attr->sclcfg);
+  sdapin = MKI2C_OUTPUT(priv->attr->sdacfg);
+
+  sam_configpio(sclpin);
+  sam_configpio(sdapin);
+
+  /* Let SDA go high (i.e., floating since this is an open-drain output). */
+
+  sam_piowrite(sdapin, true);
+
+  /* Clock the bus until any slaves currently driving it low let it float.
+   * Reading from the output will return the actual sensed level on the
+   * SDA pin (not the level that we wrote).
+   */
+
+  clockcnt = 0;
+  while (sam_pioread(sdapin) == false)
+    {
+      /* Give up if we have tried too hard */
+
+      if (clockcnt++ > 10)
+        {
+          ret = -ETIMEDOUT;
+          goto out;
+        }
+
+      /* Sniff to make sure that clock stretching has finished.  SCL should
+       * be floating high here unless something is driving it low.
+       *
+       * If the bus never relaxes, the reset has failed.
+       */
+
+      stretchcnt = 0;
+      while (sam_pioread(sclpin) == false)
+        {
+          /* Give up if we have tried too hard */
+
+          if (stretchcnt++ > 10)
+            {
+              ret = -EAGAIN;
+              goto out;
+            }
+
+          up_udelay(10);
+        }
+
+      /* Drive SCL low */
+
+      sam_piowrite(sclpin, false);
+      up_udelay(10);
+
+      /* Drive SCL high (floating) again */
+
+      sam_piowrite(sclpin, true);
+      up_udelay(10);
+    }
+
+  /* Generate a start followed by a stop to reset slave
+   * state machines.
+   */
+
+  sam_piowrite(sdapin, false);
+  up_udelay(10);
+  sam_piowrite(sclpin, false);
+  up_udelay(10);
+
+  sam_piowrite(sclpin, true);
+  up_udelay(10);
+  sam_piowrite(sdapin, true);
+  up_udelay(10);
+
+  /* Re-initialize the port (using the saved frequency) */
+
+  twi_initialize(priv, frequency);
+  ret = OK;
+
+out:
+
+  /* Release our lock on the bus */
+
+  twi_givesem(&priv->exclsem);
+  return ret;
+}
+#endif /* CONFIG_I2C_RESET */
+#endif /* CONFIG_SAMA5_TWI0 || ... || CONFIG_SAMA5_TWI3 */
diff --git a/drivers/input/mxt.c b/drivers/input/mxt.c
index aac6ee6135aa210e11988e68f6423248e029961a..fb771b3616421a81e3c79a27ff85aebb7bbfa627 100644
--- a/drivers/input/mxt.c
+++ b/drivers/input/mxt.c
@@ -192,10 +192,13 @@ static int  mxt_getreg(FAR struct mxt_dev_s *priv, uint16_t regaddr,
 static int  mxt_putreg(FAR struct mxt_dev_s *priv, uint16_t regaddr,
               FAR const uint8_t *buffer, size_t buflen);
 
-/* MXT object access */
+/* MXT object/message access */
 
 static FAR struct mxt_object_s *mxt_object(FAR struct mxt_dev_s *priv,
               uint8_t type);
+#ifdef CONFIG_I2C_RESET
+static inline bool mxt_nullmsg(FAR struct mxt_msg_s *msg);
+#endif
 
 /* Poll support */
 
@@ -254,6 +257,14 @@ static const struct file_operations mxt_fops =
 #endif
 };
 
+#ifdef CONFIG_I2C_RESET
+static const struct mxt_msg_s g_nullmsg =
+{
+  .id = 0,
+  .body = {0, 0, 0, 0, 0, 0, 0},
+};
+#endif
+
 /****************************************************************************
  * Private Functions
  ****************************************************************************/
@@ -413,6 +424,17 @@ static int mxt_putobject(FAR struct mxt_dev_s *priv, uint8_t type,
   return mxt_putreg(priv, regaddr + offset, (FAR const uint8_t *)&value, 1);
 }
 
+/****************************************************************************
+ * Name: mxt_nullmsg
+ ****************************************************************************/
+
+#ifdef CONFIG_I2C_RESET
+static inline bool mxt_nullmsg(FAR struct mxt_msg_s *msg)
+{
+  return (memcmp(msg, &g_nullmsg, sizeof(struct mxt_msg_s)) == 0);
+}
+#endif
+
 /****************************************************************************
  * Name: mxt_notify
  ****************************************************************************/
@@ -745,6 +767,7 @@ static void mxt_worker(FAR void *arg)
   FAR const struct mxt_lower_s *lower;
   struct mxt_msg_s msg;
   uint8_t id;
+  int retries;
   int ret;
 
   ASSERT(priv != NULL);
@@ -767,6 +790,7 @@ static void mxt_worker(FAR void *arg)
 
   /* Loop, processing each message from the maXTouch */
 
+  retries = 0;
   do
     {
       /* Retrieve the next message from the maXTouch */
@@ -795,6 +819,8 @@ static void mxt_worker(FAR void *arg)
 
           ivdbg("T6: status: %02x checksum: %06lx\n",
                 status, (unsigned long)chksum);
+
+          retries = 0;
         }
       else
 #endif
@@ -804,6 +830,7 @@ static void mxt_worker(FAR void *arg)
       if (id >= priv->t9idmin && id <= priv->t9idmax)
         {
           mxt_touch_event(priv, &msg, id - priv->t9idmin);
+          retries = 0;
         }
 
 #ifdef CONFIG_MXT_BUTTONS
@@ -812,18 +839,42 @@ static void mxt_worker(FAR void *arg)
       else if (msg.id == priv->t19id)
         {
           mxt_button_event(priv, &msg);
+          retries = 0;
         }
 #endif
-      /* Any others ignored */
+
+#ifdef CONFIG_I2C_RESET
+      /* A message of all zeroes probably means that the bus is hung */
+
+      else if (mxt_nullmsg(&msg))
+        {
+          /* Try to shake the bus free */
+
+          ivdbg("WARNING: Null message... resetting I2C\n");
+
+          ret = up_i2creset(priv->i2c);
+          if (ret < 0)
+            {
+              idbg("ERROR: up_i2creset failed: %d\n", ret);
+              break;
+            }
+
+          retries++;
+        }
+#endif
+
+      /* Any other message IDs are ignored */
 
       else
         {
           ivdbg("Ignored: id=%u message={%02x %02x %02x %02x %02x %02x %02x}\n",
                 msg.id, msg.body[0], msg.body[1], msg.body[2], msg.body[3],
                 msg.body[4], msg.body[5], msg.body[6]);
+
+          retries++;
         }
     }
-  while (id != 0x00 && id != 0xff);
+  while (id != 0xff && retries < 16);
 
 errout_with_semaphore:
   /* Release our lock on the MXT device */
@@ -1482,7 +1533,7 @@ static int mxt_getobjtab(FAR struct mxt_dev_s *priv)
 static int mxt_clrpending(FAR struct mxt_dev_s *priv)
 {
   struct mxt_msg_s msg;
-  int retries = 10;
+  int retries = 16;
   int ret;
 
   /* Read dummy message until there are no more to read (or until we have
@@ -1498,14 +1549,32 @@ static int mxt_clrpending(FAR struct mxt_dev_s *priv)
           idbg("ERROR: mxt_getmessage failed: %d\n", ret);
           return ret;
         }
+
+#ifdef CONFIG_I2C_RESET
+      /* A message of all zeroes probably means that the bus is hung */
+
+      if (mxt_nullmsg(&msg))
+        {
+          /* Try to shake the bus free */
+
+          ivdbg("WARNING: Null message... resetting I2C\n");
+
+          ret = up_i2creset(priv->i2c);
+          if (ret < 0)
+            {
+              idbg("ERROR: up_i2creset failed: %d\n", ret);
+              return ret;
+            }
+        }
+#endif
     }
-  while (msg.id != 0x00 && msg.id != 0xff && --retries > 0);
+  while (msg.id != 0xff && --retries > 0);
 
   /* Complain if we exceed the retry limit */
 
   if (retries <= 0)
     {
-      idbg("ERROR: CHG pin did not clear: ID=%02x\n", msg.id);
+      idbg("ERROR: Failed to clear messages: ID=%02x\n", msg.id);
       return -EBUSY;
     }