From fa9644e655ca8304a27a85a82a457bbc5785fdcc Mon Sep 17 00:00:00 2001
From: flabbergast <s3+flabbergast@sdfeu.org>
Date: Mon, 18 Apr 2016 21:09:29 +0100
Subject: [KINETIS] Slightly rewrite and comment i2c driver.

---
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 199 +++++++++++++++++++++++----------
 1 file changed, 143 insertions(+), 56 deletions(-)

(limited to 'os/hal/ports/KINETIS')

diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
index 1095737..0831eac 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
@@ -63,7 +63,7 @@ void config_frequency(I2CDriver *i2cp) {
    * divider used to generate the SCL clock from the main
    * system clock.
    */
-  uint16_t icr_table[] = {
+  const uint16_t icr_table[] = {
     /* 0x00 - 0x0F */
     20,22,24,26,28,30,34,40,28,32,36,40,44,48,56,68,
     /* 0x10 - 0x1F */
@@ -117,53 +117,116 @@ static void serve_interrupt(I2CDriver *i2cp) {
   I2C_TypeDef *i2c = i2cp->i2c;
   intstate_t state = i2cp->intstate;
 
-  if (i2c->S & I2Cx_S_ARBL) {
-
-    i2cp->errors |= I2C_ARBITRATION_LOST;
-    i2c->S |= I2Cx_S_ARBL;
-
-  } else if (state == STATE_SEND) {
-
-    if (i2c->S & I2Cx_S_RXAK)
-      i2cp->errors |= I2C_ACK_FAILURE;
-    else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes)
-      i2c->D = i2cp->txbuf[i2cp->txidx++];
-    else
-      i2cp->intstate = STATE_STOP;
-
-  } else if (state == STATE_DUMMY) {
-
-    if (i2c->S & I2Cx_S_RXAK)
-      i2cp->errors |= I2C_ACK_FAILURE;
-    else {
-      i2c->C1 &= ~I2Cx_C1_TX;
-
-      if (i2cp->rxbytes > 1)
-        i2c->C1 &= ~I2Cx_C1_TXAK;
-      else
-        i2c->C1 |= I2Cx_C1_TXAK;
-      (void) i2c->D;
-      i2cp->intstate = STATE_RECV;
-    }
-
-  } else if (state == STATE_RECV) {
-
-    if (i2cp->rxbytes > 1) {
-      if (i2cp->rxidx == (i2cp->rxbytes - 2))
-        i2c->C1 |= I2Cx_C1_TXAK;
-      else
-        i2c->C1 &= ~I2Cx_C1_TXAK;
-    }
+  /* check if we're master or slave */
+  if (i2c->C1 & I2Cx_C1_MST) {
+    /* master */
+
+    if (i2c->S & I2Cx_S_ARBL) {
+      /* check if we lost arbitration */
+      i2cp->errors |= I2C_ARBITRATION_LOST;
+      i2c->S |= I2Cx_S_ARBL;
+      /* TODO: may need to do more here, reset bus? */
+      /* Perhaps clear MST? */
+
+    } else if (i2c->S & I2Cx_S_TCF) {
+      /* just completed byte transfer */
+      if (i2c->C1 & I2Cx_C1_TX) {
+        /* the byte was transmitted */
+
+        if (state == STATE_SEND) {
+          /* currently sending stuff */
+
+          if (i2c->S & I2Cx_S_RXAK) {
+            /* slave did not ACK */
+            i2cp->errors |= I2C_ACK_FAILURE;
+            /* the thread will be woken up at the end of ISR and release the bus */
+
+          } else if (i2cp->txbuf != NULL && i2cp->txidx < i2cp->txbytes) {
+            /* slave ACK'd and we want to send more */
+            i2c->D = i2cp->txbuf[i2cp->txidx++];
+          } else {
+            /* slave ACK'd and we are done sending */
+            i2cp->intstate = STATE_STOP;
+            /* this wakes up the waiting thread at the end of ISR */
+          }
+
+        } else if (state == STATE_RECV) {
+          /* should be receiving stuff, so we've just sent the address */
+
+          if (i2c->S & I2Cx_S_RXAK) {
+            /* slave did not ACK */
+            i2cp->errors |= I2C_ACK_FAILURE;
+            /* the thread will be woken up and release the bus */
+
+          } else {
+            /* slave ACK'd, we should be receiving next */
+            i2c->C1 &= ~I2Cx_C1_TX;
+
+            if (i2cp->rxbytes > 1) {
+              /* multi-byte read, send ACK after next transfer */
+              i2c->C1 &= ~I2Cx_C1_TXAK;
+            } else {
+              /* only 1 byte remaining, send NAK */
+              i2c->C1 |= I2Cx_C1_TXAK;
+            }
+
+            (void) i2c->D; /* dummy read; triggers next receive */
+          }
+
+        } /* possibly check other states here - should not happen! */
+
+      } else {
+        /* the byte was received */
+
+        if (state == STATE_RECV) {
+          /* currently receiving stuff */
+          /* the received byte is now in D */
+
+          if (i2cp->rxbytes > 1) {
+            /* expecting at least one byte after this one */
+            if (i2cp->rxidx == (i2cp->rxbytes - 2)) {
+              /* expecting exactly one byte after this one, NAK that one */
+              i2c->C1 |= I2Cx_C1_TXAK;
+            } else {
+              /* expecting more than one after this one, respond with ACK */
+              i2c->C1 &= ~I2Cx_C1_TXAK;
+            }
+          }
+
+          if (i2cp->rxidx == i2cp->rxbytes - 1) {
+            /* D is the last byte we're expecting */
+            /* release bus: switch to RX mode, send STOP */
+            /* need to do it now otherwise the I2C module will wait for another byte */
+            // delayMicroseconds(1);
+            i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
+            i2cp->intstate = STATE_STOP;
+            /* this wakes up the waiting thread at the end of ISR */
+          }
+
+          /* get the data from D; this triggers the next receive */
+          i2cp->rxbuf[i2cp->rxidx++] = i2c->D;
+
+          // if (i2cp->rxidx == i2cp->rxbytes) {
+            /* done receiving */
+          // }
+        } /* possibly check other states here - should not happen! */
+      }
 
-    if (i2cp->rxidx == i2cp->rxbytes - 1)
-      i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
+    } /* possibly check other interrupt flags here */
 
-    i2cp->rxbuf[i2cp->rxidx++] = i2c->D;
+  } else {
+    /* slave */
 
-    if (i2cp->rxidx == i2cp->rxbytes)
-      i2cp->intstate = STATE_STOP;
+    /* Not implemented yet */
   }
 
+  /* Reset other interrupt sources */
+#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */
+  i2cp->i2c->FLT |= I2Cx_FLT_STOPF;
+#endif
+#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */
+  i2cp->i2c->FLT |= I2Cx_FLT_STARTF;
+#endif
   /* Reset interrupt flag */
   i2c->S |= I2Cx_S_IICIF;
 
@@ -260,8 +323,9 @@ void i2c_lld_start(I2CDriver *i2cp) {
   }
 
   config_frequency(i2cp);
-  i2cp->i2c->C1 |= I2Cx_C1_IICEN | I2Cx_C1_IICIE;
-  i2cp->intstate = STATE_STOP;
+  i2cp->i2c->C1 = I2Cx_C1_IICEN | I2Cx_C1_IICIE; // reset I2C, enable interrupts
+  i2cp->i2c->S = I2Cx_S_IICIF | I2Cx_S_ARBL; // clear status flags just in case
+  i2cp->intstate = STATE_STOP; // internal state
 }
 
 /**
@@ -315,35 +379,58 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
   i2cp->rxbytes = rxbytes;
   i2cp->rxidx = 0;
 
-  /* send START */
-  i2cp->i2c->C1 |= I2Cx_C1_MST;
-  i2cp->i2c->C1 |= I2Cx_C1_TX;
-
-  /* FIXME: should not use busy waiting! */
-  while (!(i2cp->i2c->S & I2Cx_S_BUSY));
+  /* clear status flags */
+#if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */
+  i2cp->i2c->FLT |= I2Cx_FLT_STOPF;
+#endif
+#if defined(I2Cx_FLT_STARTF) /* extra flags on KL27Z */
+  i2cp->i2c->FLT |= I2Cx_FLT_STARTF;
+#endif
+  i2cp->i2c->S = I2Cx_S_IICIF|I2Cx_S_ARBL;
+
+  /* acquire the bus */
+  /* check to see if we already have the bus */
+  if(i2cp->i2c->C1 & I2Cx_C1_MST) {
+    /* send repeated start */
+    i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX;
+  } else {
+    /* wait until the bus is released */
+    /* TODO: implement timeout here */
+    /* FIXME: should not use busy waiting! */
+    while (i2cp->i2c->S & I2Cx_S_BUSY);
+    /* send START */
+    i2cp->i2c->C1 |= I2Cx_C1_MST|I2Cx_C1_TX;
+  }
 
+  /* send slave address */
   i2cp->i2c->D = addr << 1 | op;
 
+  /* wait for the ISR to signal that the transmission (or receive if no transmission) phase is complete */
   msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
 
   /* FIXME */
   //if (i2cp->i2c->S & I2Cx_S_RXAK)
   //  i2cp->errors |= I2C_ACK_FAILURE;
 
-  if (msg == MSG_OK && txbuf != NULL && rxbuf != NULL && rxbytes > 0) {
+  /* the transmitting (or receiving if no transmission) phase has finished,
+   * do we expect to receive something? */
+  if (msg == MSG_OK && rxbuf != NULL && rxbytes > 0 && i2cp->rxidx < rxbytes) {
+    /* send repeated start */
     i2cp->i2c->C1 |= I2Cx_C1_RSTA;
     /* FIXME */
-    while (!(i2cp->i2c->S & I2Cx_S_BUSY));
+    // while (!(i2cp->i2c->S & I2Cx_S_BUSY));
 
-    i2cp->intstate = STATE_DUMMY;
+    i2cp->intstate = STATE_RECV;
     i2cp->i2c->D = i2cp->addr << 1 | 1;
 
     msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
   }
 
+  /* release bus - RX mode, send STOP */
+  // other kinetis I2C drivers wait here for 1us. is this needed?
   i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
   /* FIXME */
-  while (i2cp->i2c->S & I2Cx_S_BUSY);
+  // while (i2cp->i2c->S & I2Cx_S_BUSY);
 
   return msg;
 }
@@ -373,7 +460,7 @@ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
                                      uint8_t *rxbuf, size_t rxbytes,
                                      systime_t timeout) {
 
-  i2cp->intstate = STATE_DUMMY;
+  i2cp->intstate = STATE_RECV;
   return _i2c_txrx_timeout(i2cp, addr, NULL, 0, rxbuf, rxbytes, timeout);
 }
 
-- 
cgit v1.2.3


From 0a37322265bb0e109d8f4a840ccc1ccbb19ffdfa Mon Sep 17 00:00:00 2001
From: flabbergast <s3+flabbergast@sdfeu.org>
Date: Mon, 18 Apr 2016 22:53:11 +0100
Subject: [KINETIS] I2C driver: implement timeout.

---
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 31 +++++++++++++++++++++++++------
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.h |  7 +++++++
 2 files changed, 32 insertions(+), 6 deletions(-)

(limited to 'os/hal/ports/KINETIS')

diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
index 0831eac..6085941 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
@@ -363,8 +363,8 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
                                       uint8_t *rxbuf, size_t rxbytes,
                                       systime_t timeout) {
 
-  (void)timeout;
   msg_t msg;
+  systime_t start, end;
 
   uint8_t op = (i2cp->intstate == STATE_SEND) ? 0 : 1;
 
@@ -394,10 +394,29 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
     /* send repeated start */
     i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX;
   } else {
+    /* unlock during the wait, so that tasks with
+     * higher priority can get attention */
+    osalSysUnlock();
+
     /* wait until the bus is released */
-    /* TODO: implement timeout here */
-    /* FIXME: should not use busy waiting! */
-    while (i2cp->i2c->S & I2Cx_S_BUSY);
+    /* Calculating the time window for the timeout on the busy bus condition.*/
+    start = osalOsGetSystemTimeX();
+    end = start + OSAL_MS2ST(KINETIS_I2C_BUSY_TIMEOUT);
+
+    while(true) {
+      osalSysLock();
+      /* If the bus is not busy then the operation can continue, note, the
+         loop is exited in the locked state.*/
+      if(!(i2cp->i2c->S & I2Cx_S_BUSY))
+        break;
+      /* If the system time went outside the allowed window then a timeout
+         condition is returned.*/
+      if (!osalOsIsTimeWithinX(osalOsGetSystemTimeX(), start, end)) {
+        return MSG_TIMEOUT;
+      }
+      osalSysUnlock();
+    }
+
     /* send START */
     i2cp->i2c->C1 |= I2Cx_C1_MST|I2Cx_C1_TX;
   }
@@ -406,7 +425,7 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
   i2cp->i2c->D = addr << 1 | op;
 
   /* wait for the ISR to signal that the transmission (or receive if no transmission) phase is complete */
-  msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
+  msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
 
   /* FIXME */
   //if (i2cp->i2c->S & I2Cx_S_RXAK)
@@ -423,7 +442,7 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
     i2cp->intstate = STATE_RECV;
     i2cp->i2c->D = i2cp->addr << 1 | 1;
 
-    msg = osalThreadSuspendTimeoutS(&i2cp->thread, TIME_INFINITE);
+    msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
   }
 
   /* release bus - RX mode, send STOP */
diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
index a7214c5..2b25df2 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
@@ -77,6 +77,13 @@
 #define KINETIS_I2C_I2C1_PRIORITY        12
 #endif
 
+/**
+ * @brief   Timeout for external clearing BUSY bus (in ms).
+ */
+#if !defined(KINETIS_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__)
+#define KINETIS_I2C_BUSY_TIMEOUT 50
+#endif
+
 /*===========================================================================*/
 /* Derived constants and error checks.                                       */
 /*===========================================================================*/
-- 
cgit v1.2.3


From 2897589bf3b8fbbc2bfbda787bd42aa6b71b7bff Mon Sep 17 00:00:00 2001
From: flabbergast <s3+flabbergast@sdfeu.org>
Date: Tue, 19 Apr 2016 09:57:00 +0100
Subject: [KINETIS] Fix I2C clock divisor computation.

---
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

(limited to 'os/hal/ports/KINETIS')

diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
index 6085941..ce59627 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
@@ -80,9 +80,9 @@ void config_frequency(I2CDriver *i2cp) {
   uint16_t best, diff;
 
   if (i2cp->config != NULL)
-    divisor = KINETIS_SYSCLK_FREQUENCY / i2cp->config->clock;
+    divisor = KINETIS_BUSCLK_FREQUENCY / i2cp->config->clock;
   else
-    divisor = KINETIS_SYSCLK_FREQUENCY / 100000;
+    divisor = KINETIS_BUSCLK_FREQUENCY / 100000;
 
   best = ~0;
   index = 0;
-- 
cgit v1.2.3


From 9107b150b0d1fd5a2bdcc080b3493aefd8c56b46 Mon Sep 17 00:00:00 2001
From: flabbergast <s3+flabbergast@sdfeu.org>
Date: Tue, 19 Apr 2016 21:51:34 +0100
Subject: [KINETIS] Add I2C workaround for KL27Z.

---
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 67 +++++++++++++++++++++++++++++++++-
 os/hal/ports/KINETIS/LLD/hal_i2c_lld.h | 10 ++++-
 2 files changed, 74 insertions(+), 3 deletions(-)

(limited to 'os/hal/ports/KINETIS')

diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
index ce59627..c6b3d11 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c
@@ -127,8 +127,20 @@ static void serve_interrupt(I2CDriver *i2cp) {
       i2c->S |= I2Cx_S_ARBL;
       /* TODO: may need to do more here, reset bus? */
       /* Perhaps clear MST? */
+    }
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+    else if ((i2cp->rsta_workaround == RSTA_WORKAROUND_ON) && (i2cp->i2c->FLT & I2Cx_FLT_STARTF)) {
+      i2cp->rsta_workaround = RSTA_WORKAROUND_OFF;
+      /* clear+disable STARTF/STOPF interrupts and wake up the thread */
+      i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF;
+      i2cp->i2c->FLT &= ~I2Cx_FLT_SSIE;
+      i2c->S |= I2Cx_S_IICIF;
+      _i2c_wakeup_isr(i2cp);
+    }
+#endif /* KL27Z RST workaround */
 
-    } else if (i2c->S & I2Cx_S_TCF) {
+    else if (i2c->S & I2Cx_S_TCF) {
       /* just completed byte transfer */
       if (i2c->C1 & I2Cx_C1_TX) {
         /* the byte was transmitted */
@@ -213,7 +225,6 @@ static void serve_interrupt(I2CDriver *i2cp) {
       }
 
     } /* possibly check other interrupt flags here */
-
   } else {
     /* slave */
 
@@ -379,6 +390,10 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
   i2cp->rxbytes = rxbytes;
   i2cp->rxidx = 0;
 
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+  i2cp->rsta_workaround = RSTA_WORKAROUND_OFF;
+#endif /* KL27Z RST workaround */
+
   /* clear status flags */
 #if defined(I2Cx_FLT_STOPF) /* extra flags on KL26Z and KL27Z */
   i2cp->i2c->FLT |= I2Cx_FLT_STOPF;
@@ -391,8 +406,34 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
   /* acquire the bus */
   /* check to see if we already have the bus */
   if(i2cp->i2c->C1 & I2Cx_C1_MST) {
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+    /* need to wait for STARTF interrupt after issuing repeated start,
+     * otherwise the double buffering mechanism sends the last sent byte
+     * instead of the slave address.
+     * https://community.freescale.com/thread/377611
+     */
+    i2cp->rsta_workaround = RSTA_WORKAROUND_ON;
+    /* clear any interrupt bits and enable STARTF/STOPF interrupts */
+    i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF;
+    i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL;
+    i2cp->i2c->FLT |= I2Cx_FLT_SSIE;
+#endif /* KL27Z RST workaround */
+
     /* send repeated start */
     i2cp->i2c->C1 |= I2Cx_C1_RSTA | I2Cx_C1_TX;
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+    /* wait for the STARTF interrupt */
+    msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
+    /* abort if this didn't go well (timed out) */
+    if (msg != MSG_OK) {
+      /* release bus - RX mode, send STOP */
+      i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
+      return msg;
+    }
+#endif /* KL27Z RST workaround */
+
   } else {
     /* unlock during the wait, so that tasks with
      * higher priority can get attention */
@@ -434,8 +475,30 @@ static inline msg_t _i2c_txrx_timeout(I2CDriver *i2cp, i2caddr_t addr,
   /* the transmitting (or receiving if no transmission) phase has finished,
    * do we expect to receive something? */
   if (msg == MSG_OK && rxbuf != NULL && rxbytes > 0 && i2cp->rxidx < rxbytes) {
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+    /* the same KL27Z RST workaround as above */
+    i2cp->rsta_workaround = RSTA_WORKAROUND_ON;
+    /* clear any interrupt bits and enable STARTF/STOPF interrupts */
+    i2cp->i2c->FLT |= I2Cx_FLT_STOPF|I2Cx_FLT_STARTF;
+    i2cp->i2c->S |= I2Cx_S_IICIF|I2Cx_S_ARBL;
+    i2cp->i2c->FLT |= I2Cx_FLT_SSIE;
+#endif /* KL27Z RST workaround */
+
     /* send repeated start */
     i2cp->i2c->C1 |= I2Cx_C1_RSTA;
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+    /* wait for the STARTF interrupt */
+    msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
+    /* abort if this didn't go well (timed out) */
+    if (msg != MSG_OK) {
+      /* release bus - RX mode, send STOP */
+      i2cp->i2c->C1 &= ~(I2Cx_C1_TX | I2Cx_C1_MST);
+      return msg;
+    }
+#endif /* KL27Z RST workaround */
+
     /* FIXME */
     // while (!(i2cp->i2c->S & I2Cx_S_BUSY));
 
diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
index 2b25df2..3576b60 100644
--- a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
+++ b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.h
@@ -34,7 +34,11 @@
 #define STATE_STOP    0x00
 #define STATE_SEND    0x01
 #define STATE_RECV    0x02
-#define STATE_DUMMY   0x03
+
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+#define RSTA_WORKAROUND_OFF    0x00
+#define RSTA_WORKAROUND_ON     0x01
+#endif /* KL27Z RST workaround */
 
 /*===========================================================================*/
 /* Driver pre-compile time settings.                                         */
@@ -188,6 +192,10 @@ struct I2CDriver {
   intstate_t                intstate;
   /* @brief Low-level register access. */
   I2C_TypeDef               *i2c;
+#if defined(KL27Zxxx) || defined(KL27Zxx) /* KL27Z RST workaround */
+  /* @brief Auxiliary variable for KL27Z repeated start workaround. */
+  intstate_t                rsta_workaround;
+#endif /* KL27Z RST workaround */
 };
 
 /*===========================================================================*/
-- 
cgit v1.2.3