diff options
Diffstat (limited to 'bsps/arm/atsam/i2c/atsam_i2c_bus.c')
-rw-r--r-- | bsps/arm/atsam/i2c/atsam_i2c_bus.c | 146 |
1 files changed, 61 insertions, 85 deletions
diff --git a/bsps/arm/atsam/i2c/atsam_i2c_bus.c b/bsps/arm/atsam/i2c/atsam_i2c_bus.c index 4425975de8..3451d15bed 100644 --- a/bsps/arm/atsam/i2c/atsam_i2c_bus.c +++ b/bsps/arm/atsam/i2c/atsam_i2c_bus.c @@ -33,72 +33,65 @@ static void atsam_i2c_disable_interrupts(Twihs *regs) { - regs->TWIHS_IDR = 0xFFFFFFFF; + TWI_DisableIt(regs, 0xFFFFFFFF); } -static void -atsam_i2c_set_transfer_status(transfer_desc *trans_desc, transfer_state state) +/* + * Return true if the message is done right after this. That is the case if all + * bytes are received but no stop is requested. + */ +static bool +atsam_i2c_continue_read(Twihs *regs, atsam_i2c_bus *bus) { - trans_desc->trans_state = state; -} + bool done = false; -static void -atsam_i2c_continue_read(Twihs *regs, transfer_desc *trans_desc) -{ - trans_desc->data[trans_desc->already_transferred] = TWI_ReadByte(regs); - trans_desc->already_transferred++; + *bus->current_msg_byte = TWI_ReadByte(regs); + ++bus->current_msg_byte; + --bus->current_msg_todo; /* check for transfer finish */ - if (trans_desc->already_transferred == trans_desc->data_size) { - if (trans_desc->stop_request){ + if (bus->current_msg_todo == 0) { + if (bus->stop_request){ TWI_DisableIt(regs, TWIHS_IDR_RXRDY); TWI_EnableIt(regs, TWIHS_IER_TXCOMP); - atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT); } else { - atsam_i2c_set_transfer_status(trans_desc, RX_CONT_MESSAGE_NEEDED); + done = true; } } /* Last byte? */ - else if ((trans_desc->already_transferred == (trans_desc->data_size - 1)) - && (trans_desc->stop_request)){ + else if (bus->current_msg_todo == 1 && bus->stop_request) { TWI_Stop(regs); } + + return done; } +/* + * Return true if the message is done right after this. That is the case if all + * bytes are sent but no stop is requested. + */ static bool -atsam_i2c_is_state(transfer_desc *trans_desc, transfer_state state) +atsam_i2c_continue_write(Twihs *regs, atsam_i2c_bus *bus) { - return (trans_desc->trans_state == state); -} + bool done = false; -static void -atsam_i2c_continue_write(Twihs *regs, transfer_desc *trans_desc) -{ /* Transfer finished ? */ - if (trans_desc->already_transferred == trans_desc->data_size) { + if (bus->current_msg_todo == 0) { TWI_DisableIt(regs, TWIHS_IDR_TXRDY); - if (trans_desc->stop_request){ + if (bus->stop_request){ TWI_EnableIt(regs, TWIHS_IER_TXCOMP); TWI_SendSTOPCondition(regs); - atsam_i2c_set_transfer_status(trans_desc, TX_RX_STOP_SENT); } else { - atsam_i2c_set_transfer_status(trans_desc, TX_CONT_MESSAGE_NEEDED); + done = true; } } /* Bytes remaining */ else { - TWI_WriteByte(regs, - trans_desc->data[trans_desc->already_transferred]); - trans_desc->already_transferred++; + TWI_WriteByte(regs, *bus->current_msg_byte); + ++bus->current_msg_byte; + --bus->current_msg_todo; } -} - -static void -atsam_i2c_finish_write_transfer(Twihs *regs, transfer_desc *trans_desc) -{ - TWI_ReadByte(regs); - TWI_DisableIt(regs, TWIHS_IDR_TXCOMP); - trans_desc->status = 0; + return done; } static void @@ -115,19 +108,6 @@ atsam_i2c_next_packet(atsam_i2c_bus *bus) bus->current_msg_byte = msg->buf; } -static void -atsam_i2c_set_td(atsam_i2c_bus *bus, uint32_t already_transferred, - bool stop_needed) -{ - transfer_desc *trans_desc = &bus->trans_desc; - - trans_desc->status = ASYNC_STATUS_PENDING; - trans_desc->data = bus->current_msg_byte; - trans_desc->data_size = bus->current_msg_todo; - trans_desc->already_transferred = already_transferred; - trans_desc->stop_request = stop_needed; -} - static bool atsam_i2c_set_address_size(const i2c_msg *msg) { @@ -186,6 +166,8 @@ atsam_i2c_setup_write_transfer(atsam_i2c_bus *bus, Twihs *regs, bool ctrl, { atsam_i2c_set_address_regs(regs, slave_addr, ctrl, false); TWI_WriteByte(regs, *bus->current_msg_byte); + ++bus->current_msg_byte; + --bus->current_msg_todo; TWI_EnableIt(regs, TWIHS_IER_TXRDY); } @@ -197,8 +179,8 @@ atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs) uint32_t msg_todo = bus->msg_todo; uint16_t slave_addr; bool ten_bit_addr = false; - uint32_t already_transferred; bool stop_needed = true; + bool read; ten_bit_addr = atsam_i2c_set_address_size(msgs); @@ -206,22 +188,17 @@ atsam_i2c_setup_transfer(atsam_i2c_bus *bus, Twihs *regs) stop_needed = false; } - bus->read = (msgs->flags & I2C_M_RD) != 0; + read = (msgs->flags & I2C_M_RD) != 0; slave_addr = msgs->addr; - already_transferred = (bus->read == true) ? 0 : 1; - atsam_i2c_set_td(bus, already_transferred, stop_needed); - - transfer_desc *trans_desc = &bus->trans_desc; + bus->stop_request = stop_needed; - if (bus->read){ + if (read){ if (bus->current_msg_todo == 1){ send_stop = true; } - atsam_i2c_set_transfer_status(trans_desc, RX_SEND_DATA); atsam_i2c_setup_read_transfer(regs, ten_bit_addr, slave_addr, send_stop); } else { - atsam_i2c_set_transfer_status(trans_desc, TX_SEND_DATA); atsam_i2c_setup_write_transfer(bus, regs, ten_bit_addr, slave_addr); } @@ -233,40 +210,28 @@ atsam_i2c_interrupt(void *arg) atsam_i2c_bus *bus = arg; uint32_t irqstatus; bool done = false; - transfer_desc *trans_desc; Twihs *regs = bus->regs; /* read interrupts */ - irqstatus = regs->TWIHS_SR; + irqstatus = TWI_GetMaskedStatus(regs); - if((irqstatus & (TWIHS_SR_ARBLST | TWIHS_SR_NACK)) != 0) { + if((irqstatus & ATSAMV_I2C_IRQ_ERROR) != 0) { done = true; - } - - trans_desc = &bus->trans_desc; - - if (((irqstatus & TWIHS_SR_RXRDY) != 0) && - (atsam_i2c_is_state(trans_desc, RX_SEND_DATA))){ - /* carry on read transfer */ - atsam_i2c_continue_read(regs, trans_desc); - } else if (((irqstatus & TWIHS_SR_TXCOMP) != 0) && - (atsam_i2c_is_state(trans_desc, TX_RX_STOP_SENT))){ - atsam_i2c_finish_write_transfer(regs, trans_desc); + } else if ((irqstatus & TWIHS_SR_RXRDY) != 0) { + done = atsam_i2c_continue_read(regs, bus); + } else if ((irqstatus & TWIHS_SR_TXCOMP) != 0) { + TWI_DisableIt(regs, TWIHS_IDR_TXCOMP); done = true; - } else if (((irqstatus & TWIHS_SR_TXRDY) != 0) && - (atsam_i2c_is_state(trans_desc, TX_SEND_DATA))){ - atsam_i2c_continue_write(regs, trans_desc); - if (trans_desc->trans_state == TX_CONT_MESSAGE_NEEDED){ - done = true; - } + } else if ((irqstatus & TWIHS_SR_TXRDY) != 0) { + done = atsam_i2c_continue_write(regs, bus); } if(done){ - uint32_t err = irqstatus & ATSAMV_I2C_IRQ_ERROR; + bus->err = irqstatus & ATSAMV_I2C_IRQ_ERROR; atsam_i2c_next_packet(bus); - if (bus->msg_todo == 0 || err != 0) { + if (bus->msg_todo == 0 || bus->err != 0) { atsam_i2c_disable_interrupts(regs); rtems_binary_semaphore_post(&bus->sem); } else { @@ -291,27 +256,38 @@ atsam_i2c_transfer(i2c_bus *base, i2c_msg *msgs, uint32_t msg_count) if ((msgs[i].flags & I2C_M_RECV_LEN) != 0) { return -EINVAL; } + if (msgs[i].len == 0) { + /* Hardware doesn't support zero length messages */ + return -EINVAL; + } } bus->msgs = &msgs[0]; bus->msg_todo = msg_count; bus->current_msg_todo = msgs[0].len; bus->current_msg_byte = msgs[0].buf; + bus->err = 0; regs = bus->regs; - atsam_i2c_setup_transfer(bus, regs); + /* Start with a clean start. Enable error interrupts. */ + TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK); + TWI_EnableIt(regs, ATSAMV_I2C_IRQ_ERROR); - regs->TWIHS_IER = ATSAMV_I2C_IRQ_ERROR; + atsam_i2c_setup_transfer(bus, regs); eno = rtems_binary_semaphore_wait_timed_ticks( &bus->sem, bus->base.timeout ); - if (eno != 0) { + if (eno != 0 || bus->err != 0) { TWI_ConfigureMaster(bus->regs, bus->output_clock, BOARD_MCK); rtems_binary_semaphore_try_wait(&bus->sem); - return -ETIMEDOUT; + if (bus->err != 0) { + return -EIO; + } else { + return -ETIMEDOUT; + } } return 0; |