diff options
author | flabbergast <s3+flabbergast@sdfeu.org> | 2016-04-19 21:51:34 +0100 |
---|---|---|
committer | flabbergast <s3+flabbergast@sdfeu.org> | 2016-04-25 11:53:55 +0100 |
commit | 9107b150b0d1fd5a2bdcc080b3493aefd8c56b46 (patch) | |
tree | 80f4134a89d7abc457d887d826124da4951d95d1 /os/hal/ports/KINETIS | |
parent | 2897589bf3b8fbbc2bfbda787bd42aa6b71b7bff (diff) | |
download | ChibiOS-Contrib-9107b150b0d1fd5a2bdcc080b3493aefd8c56b46.tar.gz ChibiOS-Contrib-9107b150b0d1fd5a2bdcc080b3493aefd8c56b46.tar.bz2 ChibiOS-Contrib-9107b150b0d1fd5a2bdcc080b3493aefd8c56b46.zip |
[KINETIS] Add I2C workaround for KL27Z.
Diffstat (limited to 'os/hal/ports/KINETIS')
-rw-r--r-- | os/hal/ports/KINETIS/LLD/hal_i2c_lld.c | 67 | ||||
-rw-r--r-- | 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 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 */
};
/*===========================================================================*/
|