mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
007e29ebb5
Make all `spi_acquire` implementations return `void` and add assertions to check for valid device identifier where missing.
569 lines
18 KiB
C
569 lines
18 KiB
C
/*
|
|
* Copyright (C) 2014 Freie Universität Berlin
|
|
* Copyright (C) 2014 PHYTEC Messtechnik GmbH
|
|
* Copyright (C) 2015-2018 Eistec AB
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU Lesser
|
|
* General Public License v2.1. See the file LICENSE in the top level
|
|
* directory for more details.
|
|
*/
|
|
|
|
/**
|
|
* @ingroup cpu_kinetis
|
|
* @ingroup drivers_periph_i2c
|
|
*
|
|
* @{
|
|
*
|
|
* @file
|
|
* @brief Low-level I2C driver implementation
|
|
*
|
|
* @author Johann Fischer <j.fischer@phytec.de>
|
|
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
|
|
*
|
|
* @}
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
|
|
#include "cpu.h"
|
|
#include "irq.h"
|
|
#include "bit.h"
|
|
#include "mutex.h"
|
|
#include "thread.h"
|
|
#include "thread_flags.h"
|
|
#include "periph_conf.h"
|
|
#include "periph/i2c.h"
|
|
|
|
#define ENABLE_DEBUG 0
|
|
/* Define ENABLE_TRACE to 1 to enable printing of all TX/RX bytes to UART for extra verbose debugging */
|
|
#define ENABLE_TRACE 1
|
|
/* Define ENABLE_INIT_DEBUG to 1 to enable DEBUG prints in i2c_init. Currently
|
|
* this causes the system to hang when running i2c_init during boot because of
|
|
* uninitialized stdio UART */
|
|
#define ENABLE_INIT_DEBUG 0
|
|
#include "debug.h"
|
|
|
|
#if ENABLE_TRACE
|
|
#define TRACE(...) DEBUG(__VA_ARGS__)
|
|
#else
|
|
#define TRACE(...)
|
|
#endif
|
|
|
|
/**
|
|
* @brief Thread flag used internally for signaling between ISR and user thread
|
|
*/
|
|
#define THREAD_FLAG_KINETIS_I2C (1u << 8)
|
|
|
|
/**
|
|
* @brief Array of I2C module clock dividers
|
|
*
|
|
* The index in this array is the I2C_F setting number
|
|
*/
|
|
/*
|
|
* Divider values were copied from K22F reference manual
|
|
*/
|
|
static const uint16_t i2c_dividers[] = {
|
|
20, 22, 24, 26, 28, 30, 34, 40, /* 0x00 ~ 0x07 */
|
|
28, 32, 36, 40, 44, 48, 56, 68, /* 0x08 ~ 0x0f */
|
|
48, 56, 64, 72, 80, 88, 104, 128, /* 0x10 ~ 0x17 */
|
|
80, 96, 112, 128, 144, 160, 192, 240, /* 0x18 ~ 0x1f */
|
|
160, 192, 224, 256, 288, 320, 384, 480, /* 0x20 ~ 0x27 */
|
|
320, 384, 448, 512, 576, 640, 768, 960, /* 0x28 ~ 0x2f */
|
|
640, 768, 896, 1024, 1152, 1280, 1536, 1920, /* 0x30 ~ 0x37 */
|
|
1280, 1536, 1792, 2048, 2304, 2560, 3072, 3840, /* 0x38 ~ 0x3f */
|
|
};
|
|
|
|
/**
|
|
* @brief I2C IRQ reception state
|
|
*/
|
|
typedef struct {
|
|
uint8_t *datap; /**< pointer to output buffer */
|
|
size_t bytes_left; /**< how many bytes left to receive */
|
|
} i2c_rx_t;
|
|
|
|
/**
|
|
* @brief I2C IRQ transmission state
|
|
*/
|
|
typedef struct {
|
|
const uint8_t *datap; /**< pointer to input buffer */
|
|
size_t bytes_left; /**< how many bytes left to transmit */
|
|
} i2c_tx_t;
|
|
/**
|
|
* @brief Driver internal state
|
|
*/
|
|
typedef struct {
|
|
mutex_t mtx; /**< Mutex preventing multiple users of the same bus */
|
|
kernel_pid_t pid; /**< PID of thread waiting for a transfer to complete */
|
|
uint8_t active; /**< State variable to help catch user mistakes */
|
|
union {
|
|
i2c_tx_t tx; /**< State of ongoing transmission sequence */
|
|
i2c_rx_t rx; /**< State of ongoing reception sequence */
|
|
};
|
|
int retval; /**< return value from ISR */
|
|
} i2c_state_t;
|
|
|
|
static i2c_state_t i2c_state[I2C_NUMOF];
|
|
|
|
void i2c_acquire(i2c_t dev)
|
|
{
|
|
assert((unsigned)dev < I2C_NUMOF);
|
|
mutex_lock(&i2c_state[dev].mtx);
|
|
i2c_state[dev].pid = thread_getpid();
|
|
}
|
|
|
|
void i2c_release(i2c_t dev)
|
|
{
|
|
assert(dev < I2C_NUMOF);
|
|
/* Check that the bus was properly stopped before releasing */
|
|
/* It is a programming error to release the bus after sending a start
|
|
* condition but before sending a stop condition */
|
|
assert(i2c_state[dev].active == 0);
|
|
|
|
mutex_unlock(&i2c_state[dev].mtx);
|
|
}
|
|
|
|
static uint8_t i2c_find_divider(unsigned freq, unsigned speed)
|
|
{
|
|
unsigned diff = UINT_MAX;
|
|
/* Use maximum divider if nothing matches */
|
|
uint8_t F = ARRAY_SIZE(i2c_dividers) - 1;
|
|
/* We avoid using the MULT field to simplify the driver and avoid having to
|
|
* work around hardware errata on some Kinetis parts
|
|
*
|
|
* See:
|
|
* https://community.nxp.com/thread/377611
|
|
* https://mcuoneclipse.com/2012/12/05/kl25z-and-i2c-missing-repeated-start-condition/
|
|
*
|
|
* e6070: I2C: Repeat start cannot be generated if the I2Cx_F[MULT] field is set to a non-zero value
|
|
*
|
|
* Description:
|
|
* If the I2Cx_F[MULT] field is written with a non-zero value, then a repeat
|
|
* start cannot be generated
|
|
*
|
|
* Workaround:
|
|
* There are two possible workarounds:
|
|
* 1) Configure I2Cx_F[MULT] to zero if a repeat start has to be generated.
|
|
* 2) Temporarily set I2Cx_F [MULT] to zero immediately before setting the
|
|
* Repeat START bit in the I2C C1 register (I2Cx_C1[RSTA]=1) and restore
|
|
* the I2Cx_F [MULT] field to the original value after the repeated start
|
|
* has occurred
|
|
*/
|
|
for (unsigned k = 0; k < ARRAY_SIZE(i2c_dividers); ++k) {
|
|
/* Test dividers until we find one that gives a good match */
|
|
unsigned lim = (speed * i2c_dividers[k]);
|
|
if (lim >= freq) {
|
|
if ((lim - freq) < diff) {
|
|
diff = (lim - freq);
|
|
F = k;
|
|
}
|
|
}
|
|
}
|
|
if (ENABLE_INIT_DEBUG) {
|
|
DEBUG("i2c_divider: speed = %u, freq = %u, diff = %u, F = 0x%02x\n", speed, freq, diff, F);
|
|
}
|
|
return F;
|
|
}
|
|
|
|
static inline void i2c_clear_irq_flags(I2C_Type *i2c)
|
|
{
|
|
/* cppcheck-suppress selfAssignment
|
|
* (reason: intentional self assignment to clear all pending IRQs) */
|
|
i2c->S = i2c->S;
|
|
}
|
|
|
|
void i2c_init(i2c_t dev)
|
|
{
|
|
if (ENABLE_INIT_DEBUG) {
|
|
DEBUG("i2c_init: %u\n", (unsigned) dev);
|
|
}
|
|
assert((unsigned)dev < I2C_NUMOF);
|
|
const i2c_conf_t *conf = &i2c_config[dev];
|
|
I2C_Type *i2c = conf->i2c;
|
|
/* Turn on the module clock gate */
|
|
switch ((uintptr_t)i2c) {
|
|
#ifdef I2C0
|
|
case (uintptr_t)I2C0:
|
|
I2C0_CLKEN();
|
|
break;
|
|
#endif
|
|
#ifdef I2C1
|
|
case (uintptr_t)I2C1:
|
|
I2C1_CLKEN();
|
|
break;
|
|
#endif
|
|
#ifdef I2C2
|
|
case (uintptr_t)I2C2:
|
|
I2C2_CLKEN();
|
|
break;
|
|
#endif
|
|
default:
|
|
if (ENABLE_INIT_DEBUG) {
|
|
DEBUG("i2c_init: Unknown I2C device %p\n", (void *)i2c);
|
|
}
|
|
return;
|
|
}
|
|
i2c_state[dev].mtx = (mutex_t)MUTEX_INIT_LOCKED;
|
|
i2c_state[dev].active = 0;
|
|
i2c_state[dev].pid = KERNEL_PID_UNDEF;
|
|
if (ENABLE_INIT_DEBUG) {
|
|
DEBUG("i2c_init: init SCL pin\n");
|
|
}
|
|
gpio_init_port(conf->scl_pin, conf->scl_pcr);
|
|
if (ENABLE_INIT_DEBUG) {
|
|
DEBUG("i2c_init: init SDA pin\n");
|
|
}
|
|
gpio_init_port(conf->sda_pin, conf->sda_pcr);
|
|
/* Configure master settings */
|
|
i2c->C1 = 0; /* Disable module while messing with the settings */
|
|
/* Configure glitch filter register */
|
|
#ifdef I2C_FLT_SHEN_MASK
|
|
i2c->FLT = I2C_FLT_SHEN_MASK;
|
|
#else
|
|
i2c->FLT = 0;
|
|
#endif
|
|
i2c->F = i2c_find_divider(conf->freq, (unsigned)conf->speed);
|
|
/* Enable module */
|
|
i2c->C1 = I2C_C1_IICEN_MASK;
|
|
i2c_clear_irq_flags(i2c);
|
|
NVIC_EnableIRQ(conf->irqn);
|
|
mutex_unlock(&i2c_state[dev].mtx);
|
|
}
|
|
|
|
static int i2c_stop(i2c_t dev)
|
|
{
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
/* Send stop condition */
|
|
TRACE("i2c: stop C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
unsigned timeout = 0x10000;
|
|
while ((i2c->S & I2C_S_BUSY_MASK) && --timeout) {}
|
|
i2c_state[dev].active = 0;
|
|
if (timeout == 0) {
|
|
/* slave stretches the clock for too long */
|
|
DEBUG("i2c: timeout while stopping\n");
|
|
return -ETIMEDOUT;
|
|
}
|
|
TRACE("i2c: STOP C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_tx_addr(i2c_t dev, uint8_t byte)
|
|
{
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
TRACE("i2c: txa: %02x\n", (unsigned)byte);
|
|
i2c->D = byte;
|
|
uint8_t S;
|
|
uint16_t timeout = UINT16_MAX;
|
|
do {
|
|
S = i2c->S;
|
|
} while (!(S & I2C_S_IICIF_MASK) && --timeout);
|
|
|
|
i2c_clear_irq_flags(i2c);
|
|
|
|
int res = 0;
|
|
if (S & I2C_S_ARBL_MASK) {
|
|
DEBUG("i2c: arbitration lost\n");
|
|
res = -EAGAIN;
|
|
}
|
|
else if (timeout == 0) {
|
|
/* slave stretches the clock for too long */
|
|
DEBUG("i2c: tx timeout\n");
|
|
res = -ETIMEDOUT;
|
|
}
|
|
else if (S & I2C_S_RXAK_MASK) {
|
|
DEBUG("i2c: NACK\n");
|
|
res = -ENXIO;
|
|
}
|
|
if (res < 0) {
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
i2c_state[dev].active = 0;
|
|
i2c_clear_irq_flags(i2c);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static int i2c_start(i2c_t dev, uint16_t addr, unsigned read_flag, uint8_t flags)
|
|
{
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
TRACE("i2c: start C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
/* Send start condition */
|
|
if (i2c->C1 & I2C_C1_MST_MASK) {
|
|
/* Repeat start */
|
|
i2c->C1 |= I2C_C1_IICEN_MASK | I2C_C1_RSTA_MASK | I2C_C1_TX_MASK;
|
|
}
|
|
else {
|
|
/* Initial start */
|
|
if (read_flag && (flags & I2C_ADDR10)) {
|
|
/* 10 bit addressing does not allow reading without a repeated start */
|
|
return -EINVAL;
|
|
}
|
|
uint16_t timeout = UINT16_MAX;
|
|
while ((i2c->S & I2C_S_BUSY_MASK) && --timeout) {}
|
|
if (timeout == 0) {
|
|
/* Someone else is using the bus for a long time, try again later */
|
|
DEBUG("i2c: start timeout\n");
|
|
return -EAGAIN;
|
|
}
|
|
/* slave -> master transition triggers the initial start condition */
|
|
i2c->C1 |= I2C_C1_IICEN_MASK | I2C_C1_MST_MASK | I2C_C1_TX_MASK;
|
|
}
|
|
i2c_state[dev].active = 1;
|
|
TRACE("i2c: Start C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
if (i2c->S & I2C_S_ARBL_MASK) {
|
|
DEBUG("i2c: arbitration lost\n");
|
|
i2c_clear_irq_flags(i2c);
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
i2c_state[dev].active = 0;
|
|
return -EAGAIN;
|
|
}
|
|
if (flags & I2C_ADDR10) {
|
|
/* 10 bit addressing */
|
|
/* Send 10 bit address tag + 2 msb + R/W flag */
|
|
uint8_t msb = (addr >> 8);
|
|
uint8_t lsb = (addr & 0xff);
|
|
uint8_t addr_rw = I2C_10BIT_MAGIC | (msb << 1);
|
|
if (read_flag) {
|
|
addr_rw |= I2C_READ;
|
|
}
|
|
int res = i2c_tx_addr(dev, addr_rw);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
if (!read_flag) {
|
|
/* Send lower 8 bits of address */
|
|
res = i2c_tx_addr(dev, lsb);
|
|
}
|
|
return res;
|
|
}
|
|
/* 7 bit addressing */
|
|
/* Send address + R/W flag */
|
|
uint8_t addr_rw = (addr << 1);
|
|
if (read_flag) {
|
|
addr_rw |= I2C_READ;
|
|
}
|
|
return i2c_tx_addr(dev, addr_rw);
|
|
}
|
|
|
|
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags)
|
|
{
|
|
assert((data != NULL) || (len == 0));
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
DEBUG("i2c: r a:%02x, l:%u, f:%02x\n", addr, (unsigned)len, (unsigned)flags);
|
|
if (len == 1) {
|
|
/* Send NACK after next byte */
|
|
bit_set8(&i2c->C1, I2C_C1_TXAK_SHIFT);
|
|
}
|
|
else {
|
|
bit_clear8(&i2c->C1, I2C_C1_TXAK_SHIFT);
|
|
}
|
|
|
|
if (!(flags & I2C_NOSTART)) {
|
|
/* Send start condition and address */
|
|
int res = i2c_start(dev, addr, 1, flags);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
if (len > 0) {
|
|
/* We need IRQs enabled for interrupt based transfers */
|
|
assert(!__get_PRIMASK());
|
|
assert(!irq_is_in());
|
|
bit_clear8(&i2c->C1, I2C_C1_TX_SHIFT);
|
|
/* Configure IRQ transfer */
|
|
i2c_state[dev].rx.datap = data;
|
|
i2c_state[dev].rx.bytes_left = len;
|
|
i2c_state[dev].retval = 0;
|
|
bit_set8(&i2c->C1, I2C_C1_IICIE_SHIFT);
|
|
/* Initiate master receive mode by reading the data register once when
|
|
* the C1[TX] bit is cleared and C1[MST] is set */
|
|
volatile uint8_t dummy;
|
|
/* cppcheck-suppress unreadVariable
|
|
* (reason: needed to empty D register) */
|
|
dummy = i2c->D;
|
|
(void)dummy;
|
|
/* Wait until the ISR signals back */
|
|
TRACE("i2c: read C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
thread_flags_t tflg = thread_flags_wait_any(THREAD_FLAG_KINETIS_I2C | THREAD_FLAG_TIMEOUT);
|
|
TRACE("i2c: rx done, %u left, ret: %d\n",
|
|
i2c_state[dev].rx.bytes_left, i2c_state[dev].retval);
|
|
if (!(tflg & THREAD_FLAG_KINETIS_I2C)) {
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
i2c_state[dev].active = 0;
|
|
return -ETIMEDOUT;
|
|
}
|
|
if (i2c_state[dev].retval < 0) {
|
|
/* An error occurred */
|
|
/* The module has been stopped from the ISR, no need to clear the MST bit */
|
|
return i2c_state[dev].retval;
|
|
}
|
|
}
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
int res = i2c_stop(dev);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, uint8_t flags)
|
|
{
|
|
assert((data != NULL) || (len == 0));
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
DEBUG("i2c: w a:%02x, l:%u, f:%02x\n", addr, (unsigned)len, (unsigned)flags);
|
|
if (!(flags & I2C_NOSTART)) {
|
|
int res = i2c_start(dev, addr, 0, flags);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
if (len > 0) {
|
|
/* We need IRQs enabled for interrupt based transfers */
|
|
assert(!__get_PRIMASK());
|
|
assert(!irq_is_in());
|
|
/* Configure IRQ transfer */
|
|
bit_set8(&i2c->C1, I2C_C1_TX_SHIFT);
|
|
i2c_state[dev].tx.datap = data;
|
|
i2c_state[dev].tx.bytes_left = len;
|
|
i2c_state[dev].retval = 0;
|
|
bit_set8(&i2c->C1, I2C_C1_IICIE_SHIFT);
|
|
TRACE("i2c: write C1=%02x S=%02x\n", (unsigned)i2c->C1, (unsigned)i2c->S);
|
|
/* Initiate transfer by writing the first byte, the remaining bytes will
|
|
* be fed by the ISR */
|
|
i2c->D = *((const uint8_t *)data);
|
|
/* Wait until the ISR signals back */
|
|
thread_flags_t tflg = thread_flags_wait_any(THREAD_FLAG_KINETIS_I2C | THREAD_FLAG_TIMEOUT);
|
|
TRACE("i2c: rx done, %u left, ret: %d\n",
|
|
i2c_state[dev].rx.bytes_left, i2c_state[dev].retval);
|
|
if (!(tflg & THREAD_FLAG_KINETIS_I2C)) {
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
i2c_state[dev].active = 0;
|
|
return -ETIMEDOUT;
|
|
}
|
|
if (i2c_state[dev].retval < 0) {
|
|
/* An error occurred */
|
|
/* The module has been stopped from the ISR, no need to clear the MST bit */
|
|
return i2c_state[dev].retval;
|
|
}
|
|
}
|
|
if (!(flags & I2C_NOSTOP)) {
|
|
int res = i2c_stop(dev);
|
|
if (res < 0) {
|
|
return res;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static inline void i2c_irq_signal_done(I2C_Type *i2c, kernel_pid_t pid)
|
|
{
|
|
thread_flags_set((thread_t *)thread_get(pid), THREAD_FLAG_KINETIS_I2C);
|
|
bit_clear8(&i2c->C1, I2C_C1_IICIE_SHIFT);
|
|
}
|
|
|
|
/**
|
|
* @brief Master transmit mode IRQ handler
|
|
*/
|
|
static void i2c_irq_mst_tx_handler(I2C_Type *i2c, i2c_state_t *state, uint8_t S)
|
|
{
|
|
size_t len = state->tx.bytes_left;
|
|
assert(len != 0); /* This only happens if this periph driver is broken */
|
|
--len;
|
|
state->tx.bytes_left = len;
|
|
if (S & I2C_S_RXAK_MASK) {
|
|
/* NACK */
|
|
/* Abort master transfer */
|
|
DEBUG("i2c: NACK\n");
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
state->active = 0;
|
|
state->retval = -EIO;
|
|
i2c_irq_signal_done(i2c, state->pid);
|
|
}
|
|
else if (len == 0) {
|
|
/* We are done, NACK on the last byte is OK */
|
|
DEBUG("i2c: TX done\n");
|
|
i2c_irq_signal_done(i2c, state->pid);
|
|
}
|
|
else {
|
|
/* transmit the next byte */
|
|
/* Increment first, datap points to the last byte transmitted */
|
|
++state->tx.datap;
|
|
i2c->D = *state->tx.datap;
|
|
TRACE("i2c: tx: %02x\n", (unsigned)*(state->tx.datap));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Master receive mode IRQ handler
|
|
*/
|
|
static void i2c_irq_mst_rx_handler(I2C_Type *i2c, i2c_state_t *state)
|
|
{
|
|
size_t len = state->rx.bytes_left;
|
|
assert(len != 0); /* This only happens if this periph driver is broken */
|
|
--len;
|
|
if (len == 1) {
|
|
/* Send NACK after the next byte */
|
|
bit_set8(&i2c->C1, I2C_C1_TXAK_SHIFT);
|
|
}
|
|
else if (len == 0) {
|
|
/* Switching the module to TX mode lets us read the data register
|
|
* without triggering a new reception */
|
|
bit_set8(&i2c->C1, I2C_C1_TX_SHIFT);
|
|
i2c_irq_signal_done(i2c, state->pid);
|
|
}
|
|
state->rx.bytes_left = len;
|
|
/* Read data reception buffer, this will trigger reception of the following
|
|
* byte iff TX and MST bits in the C1 register are set */
|
|
*state->rx.datap = i2c->D;
|
|
TRACE("i2c: rx: %02x\n", (unsigned)*(state->rx.datap));
|
|
++state->rx.datap;
|
|
}
|
|
|
|
void i2c_irq_handler(i2c_t dev)
|
|
{
|
|
I2C_Type *i2c = i2c_config[dev].i2c;
|
|
i2c_state_t *state = &i2c_state[dev];
|
|
uint8_t S = i2c->S;
|
|
/* Clear IRQ flags */
|
|
i2c->S = S;
|
|
if (i2c->C1 & I2C_C1_MST_MASK) {
|
|
/* Master mode handler */
|
|
if (S & I2C_S_ARBL_MASK) {
|
|
DEBUG("i2c: arbitration lost\n");
|
|
/* Abort master transfer */
|
|
bit_clear8(&i2c->C1, I2C_C1_MST_SHIFT);
|
|
state->active = 0;
|
|
state->retval = -EAGAIN;
|
|
i2c_irq_signal_done(i2c, state->pid);
|
|
}
|
|
if (i2c->C1 & I2C_C1_TX_MASK) {
|
|
/* Transmit mode */
|
|
i2c_irq_mst_tx_handler(i2c, state, S);
|
|
}
|
|
else {
|
|
/* Receive mode */
|
|
i2c_irq_mst_rx_handler(i2c, state);
|
|
}
|
|
}
|
|
else {
|
|
/* TODO: Slave mode handler goes here */
|
|
}
|
|
cortexm_isr_end();
|
|
}
|
|
|
|
#ifdef I2C_0_ISR
|
|
void I2C_0_ISR(void)
|
|
{
|
|
i2c_irq_handler(I2C_DEV(0));
|
|
}
|
|
#endif /* I2C_0_ISR */
|
|
|
|
#ifdef I2C_1_ISR
|
|
void I2C_1_ISR(void)
|
|
{
|
|
i2c_irq_handler(I2C_DEV(1));
|
|
}
|
|
#endif /* I2C_1_ISR */
|