From 9107b150b0d1fd5a2bdcc080b3493aefd8c56b46 Mon Sep 17 00:00:00 2001 From: flabbergast Date: Tue, 19 Apr 2016 21:51:34 +0100 Subject: [PATCH] [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(-) diff --git a/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c b/os/hal/ports/KINETIS/LLD/hal_i2c_lld.c index ce596274..c6b3d11a 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? */ + } - } else if (i2c->S & I2Cx_S_TCF) { +#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) { /* 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 2b25df25..3576b604 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 */ }; /*===========================================================================*/