summaryrefslogtreecommitdiffstats
path: root/bsps/arm/atsam/i2c/atsam_i2c_bus.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/arm/atsam/i2c/atsam_i2c_bus.c')
-rw-r--r--bsps/arm/atsam/i2c/atsam_i2c_bus.c177
1 files changed, 83 insertions, 94 deletions
diff --git a/bsps/arm/atsam/i2c/atsam_i2c_bus.c b/bsps/arm/atsam/i2c/atsam_i2c_bus.c
index 4425975de8..fca48c580d 100644
--- a/bsps/arm/atsam/i2c/atsam_i2c_bus.c
+++ b/bsps/arm/atsam/i2c/atsam_i2c_bus.c
@@ -1,15 +1,28 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+
/*
- * Copyright (c) 2016 embedded brains GmbH. All rights reserved.
+ * Copyright (c) 2016 embedded brains GmbH & Co. KG
*
- * embedded brains GmbH
- * Dornierstr. 4
- * 82178 Puchheim
- * Germany
- * <info@embedded-brains.de>
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
*
- * The license and distribution terms for this file may be
- * found in the file LICENSE in this distribution or at
- * http://www.rtems.org/license/LICENSE.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
*/
#include <bsp/atsam-clock-config.h>
@@ -33,72 +46,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 +121,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 +179,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 +192,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 +201,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 +223,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 +269,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;