diff options
-rw-r--r-- | libmaple/i2c.c | 378 |
1 files changed, 190 insertions, 188 deletions
diff --git a/libmaple/i2c.c b/libmaple/i2c.c index 15bcbe0..2e11f54 100644 --- a/libmaple/i2c.c +++ b/libmaple/i2c.c @@ -102,194 +102,6 @@ enum { }; /** - * @brief IRQ handler for I2C master. Handles transmission/reception. - * @param dev I2C device - */ -void _i2c_irq_handler(i2c_dev *dev) { - /* WTFs: - * - Where is I2C_MSG_10BIT_ADDR handled? - */ - i2c_msg *msg = dev->msg; - - uint8 read = msg->flags & I2C_MSG_READ; - - uint32 sr1 = dev->regs->SR1; - uint32 sr2 = dev->regs->SR2; - I2C_CRUMB(IRQ_ENTRY, sr1, sr2); - - /* - * Reset timeout counter - */ - dev->timestamp = systick_uptime(); - - /* - * EV5: Start condition sent - */ - if (sr1 & I2C_SR1_SB) { - msg->xferred = 0; - i2c_enable_irq(dev, I2C_IRQ_BUFFER); - - /* - * Master receiver - */ - if (read) { - i2c_enable_ack(dev); - } - - i2c_send_slave_addr(dev, msg->addr, read); - sr1 = sr2 = 0; - } - - /* - * EV6: Slave address sent - */ - if (sr1 & I2C_SR1_ADDR) { - /* - * Special case event EV6_1 for master receiver. - * Generate NACK and restart/stop condition after ADDR - * is cleared. - */ - if (read) { - if (msg->length == 1) { - i2c_disable_ack(dev); - if (dev->msgs_left > 1) { - i2c_start_condition(dev); - I2C_CRUMB(RX_ADDR_START, 0, 0); - } else { - i2c_stop_condition(dev); - I2C_CRUMB(RX_ADDR_STOP, 0, 0); - } - } - } else { - /* - * Master transmitter: write first byte to fill shift - * register. We should get another TXE interrupt - * immediately to fill DR again. - */ - if (msg->length != 1) { - i2c_write(dev, msg->data[msg->xferred++]); - } - } - sr1 = sr2 = 0; - } - - /* - * EV8: Master transmitter - * Transmit buffer empty, but we haven't finished transmitting the last - * byte written. - */ - if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) { - I2C_CRUMB(TXE_ONLY, 0, 0); - if (dev->msgs_left) { - i2c_write(dev, msg->data[msg->xferred++]); - if (msg->xferred == msg->length) { - /* - * End of this message. Turn off TXE/RXNE and wait for - * BTF to send repeated start or stop condition. - */ - i2c_disable_irq(dev, I2C_IRQ_BUFFER); - dev->msgs_left--; - } - } else { - /* - * This should be impossible... - */ - ASSERT(0); - } - sr1 = sr2 = 0; - } - - /* - * EV8_2: Master transmitter - * Last byte sent, program repeated start/stop - */ - if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) { - I2C_CRUMB(TXE_BTF, 0, 0); - if (dev->msgs_left) { - I2C_CRUMB(TEST, 0, 0); - /* - * Repeated start insanity: We can't disable ITEVTEN or else SB - * won't interrupt, but if we don't disable ITEVTEN, BTF will - * continually interrupt us. What the fuck ST? - */ - i2c_start_condition(dev); - while (!(dev->regs->SR1 & I2C_SR1_SB)) - ; - dev->msg++; - } else { - i2c_stop_condition(dev); - - /* - * Turn off event interrupts to keep BTF from firing until - * the end of the stop condition. Why on earth they didn't - * have a start/stop condition request clear BTF is beyond - * me. - */ - i2c_disable_irq(dev, I2C_IRQ_EVENT); - I2C_CRUMB(STOP_SENT, 0, 0); - dev->state = I2C_STATE_XFER_DONE; - } - sr1 = sr2 = 0; - } - - /* - * EV7: Master Receiver - */ - if (sr1 & I2C_SR1_RXNE) { - I2C_CRUMB(RXNE_ONLY, 0, 0); - msg->data[msg->xferred++] = dev->regs->DR; - - /* - * EV7_1: Second to last byte in the reception? Set NACK and generate - * stop/restart condition in time for the last byte. We'll get one more - * RXNE interrupt before shutting things down. - */ - if (msg->xferred == (msg->length - 1)) { - i2c_disable_ack(dev); - if (dev->msgs_left > 2) { - i2c_start_condition(dev); - I2C_CRUMB(RXNE_START_SENT, 0, 0); - } else { - i2c_stop_condition(dev); - I2C_CRUMB(RXNE_STOP_SENT, 0, 0); - } - } else if (msg->xferred == msg->length) { - dev->msgs_left--; - if (dev->msgs_left == 0) { - /* - * We're done. - */ - I2C_CRUMB(RXNE_DONE, 0, 0); - dev->state = I2C_STATE_XFER_DONE; - } else { - dev->msg++; - } - } - } -} - -/** - * @brief Interrupt handler for I2C error conditions - * @param dev I2C device - * @sideeffect Aborts any pending I2C transactions - */ -void _i2c_irq_error_handler(i2c_dev *dev) { - I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2); - - dev->error_flags = dev->regs->SR2 & (I2C_SR1_BERR | - I2C_SR1_ARLO | - I2C_SR1_AF | - I2C_SR1_OVR); - /* Clear flags */ - dev->regs->SR1 = 0; - dev->regs->SR2 = 0; - - i2c_stop_condition(dev); - i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR); - dev->state = I2C_STATE_ERROR; -} - -/** * @brief Reset an I2C bus. * * Reset is accomplished by clocking out pulses until any hung slaves @@ -521,3 +333,193 @@ static inline int32 wait_for_state_change(i2c_dev *dev, } } } + +/* + * Private API + */ + +/* + * IRQ handler for I2C master. Handles transmission/reception. + */ +void _i2c_irq_handler(i2c_dev *dev) { + /* WTFs: + * - Where is I2C_MSG_10BIT_ADDR handled? + */ + i2c_msg *msg = dev->msg; + + uint8 read = msg->flags & I2C_MSG_READ; + + uint32 sr1 = dev->regs->SR1; + uint32 sr2 = dev->regs->SR2; + I2C_CRUMB(IRQ_ENTRY, sr1, sr2); + + /* + * Reset timeout counter + */ + dev->timestamp = systick_uptime(); + + /* + * EV5: Start condition sent + */ + if (sr1 & I2C_SR1_SB) { + msg->xferred = 0; + i2c_enable_irq(dev, I2C_IRQ_BUFFER); + + /* + * Master receiver + */ + if (read) { + i2c_enable_ack(dev); + } + + i2c_send_slave_addr(dev, msg->addr, read); + sr1 = sr2 = 0; + } + + /* + * EV6: Slave address sent + */ + if (sr1 & I2C_SR1_ADDR) { + /* + * Special case event EV6_1 for master receiver. + * Generate NACK and restart/stop condition after ADDR + * is cleared. + */ + if (read) { + if (msg->length == 1) { + i2c_disable_ack(dev); + if (dev->msgs_left > 1) { + i2c_start_condition(dev); + I2C_CRUMB(RX_ADDR_START, 0, 0); + } else { + i2c_stop_condition(dev); + I2C_CRUMB(RX_ADDR_STOP, 0, 0); + } + } + } else { + /* + * Master transmitter: write first byte to fill shift + * register. We should get another TXE interrupt + * immediately to fill DR again. + */ + if (msg->length != 1) { + i2c_write(dev, msg->data[msg->xferred++]); + } + } + sr1 = sr2 = 0; + } + + /* + * EV8: Master transmitter + * Transmit buffer empty, but we haven't finished transmitting the last + * byte written. + */ + if ((sr1 & I2C_SR1_TXE) && !(sr1 & I2C_SR1_BTF)) { + I2C_CRUMB(TXE_ONLY, 0, 0); + if (dev->msgs_left) { + i2c_write(dev, msg->data[msg->xferred++]); + if (msg->xferred == msg->length) { + /* + * End of this message. Turn off TXE/RXNE and wait for + * BTF to send repeated start or stop condition. + */ + i2c_disable_irq(dev, I2C_IRQ_BUFFER); + dev->msgs_left--; + } + } else { + /* + * This should be impossible... + */ + ASSERT(0); + } + sr1 = sr2 = 0; + } + + /* + * EV8_2: Master transmitter + * Last byte sent, program repeated start/stop + */ + if ((sr1 & I2C_SR1_TXE) && (sr1 & I2C_SR1_BTF)) { + I2C_CRUMB(TXE_BTF, 0, 0); + if (dev->msgs_left) { + I2C_CRUMB(TEST, 0, 0); + /* + * Repeated start insanity: We can't disable ITEVTEN or else SB + * won't interrupt, but if we don't disable ITEVTEN, BTF will + * continually interrupt us. What the fuck ST? + */ + i2c_start_condition(dev); + while (!(dev->regs->SR1 & I2C_SR1_SB)) + ; + dev->msg++; + } else { + i2c_stop_condition(dev); + + /* + * Turn off event interrupts to keep BTF from firing until + * the end of the stop condition. Why on earth they didn't + * have a start/stop condition request clear BTF is beyond + * me. + */ + i2c_disable_irq(dev, I2C_IRQ_EVENT); + I2C_CRUMB(STOP_SENT, 0, 0); + dev->state = I2C_STATE_XFER_DONE; + } + sr1 = sr2 = 0; + } + + /* + * EV7: Master Receiver + */ + if (sr1 & I2C_SR1_RXNE) { + I2C_CRUMB(RXNE_ONLY, 0, 0); + msg->data[msg->xferred++] = dev->regs->DR; + + /* + * EV7_1: Second to last byte in the reception? Set NACK and generate + * stop/restart condition in time for the last byte. We'll get one more + * RXNE interrupt before shutting things down. + */ + if (msg->xferred == (msg->length - 1)) { + i2c_disable_ack(dev); + if (dev->msgs_left > 2) { + i2c_start_condition(dev); + I2C_CRUMB(RXNE_START_SENT, 0, 0); + } else { + i2c_stop_condition(dev); + I2C_CRUMB(RXNE_STOP_SENT, 0, 0); + } + } else if (msg->xferred == msg->length) { + dev->msgs_left--; + if (dev->msgs_left == 0) { + /* + * We're done. + */ + I2C_CRUMB(RXNE_DONE, 0, 0); + dev->state = I2C_STATE_XFER_DONE; + } else { + dev->msg++; + } + } + } +} + +/* + * Interrupt handler for I2C error conditions. Aborts any pending I2C + * transactions. + */ +void _i2c_irq_error_handler(i2c_dev *dev) { + I2C_CRUMB(ERROR_ENTRY, dev->regs->SR1, dev->regs->SR2); + + dev->error_flags = dev->regs->SR2 & (I2C_SR1_BERR | + I2C_SR1_ARLO | + I2C_SR1_AF | + I2C_SR1_OVR); + /* Clear flags */ + dev->regs->SR1 = 0; + dev->regs->SR2 = 0; + + i2c_stop_condition(dev); + i2c_disable_irq(dev, I2C_IRQ_BUFFER | I2C_IRQ_EVENT | I2C_IRQ_ERROR); + dev->state = I2C_STATE_ERROR; +} |