The unified diff between revisions [b85a3bbc..] and [056a532c..] is displayed below. It can also be downloaded as a raw diff.
This diff has been restricted to the following files: 'i2c.c'
# # old_revision [b85a3bbccc40f21e02f50101af764be93eeb9538] # new_revision [056a532c92301bcb224e1f786c5f6720e8acf3eb] # # patch "i2c.c" # from [aa38cfd51b998ec3960be6052827278b26edcc40] # to [2dff1acf82a051d155952429555a65a193ba0fd5] # ============================================================ --- i2c.c aa38cfd51b998ec3960be6052827278b26edcc40 +++ i2c.c 2dff1acf82a051d155952429555a65a193ba0fd5 @@ -1,5 +1,7 @@ #include "i2c.h" +#include "interrupt.h" +#include "event.h" #define I2CBASE 0xE001C000 @@ -21,35 +23,29 @@ #define SI (IREG(I2CONSET) & SIFLAG) +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void); + +struct i2c_transaction *i2c_transaction; +int i2c_bytes; + void init_i2c(void) { IREG(I2CONSET) = 0x40; /* Enable I2C ready for Master Tx */ /* Set up for just under 400kHz */ +#ifdef I2C_FAST #if 0 IWREG(I2SCLL) = (25 * 100); IWREG(I2SCLH) = (12 * 100); +#endif + IWREG(I2SCLL) = 25 * 4; /* ~400kHz */ + IWREG(I2SCLH) = 12 * 4; #else -# if 0 - IWREG(I2SCLL) = 1475; /* ~5kHz */ - IWREG(I2SCLH) = 1475; -# else IWREG(I2SCLL) = 73; /* ~100kHz */ IWREG(I2SCLH) = 73; -# endif #endif + interrupt_register(I2C0, i2c_interrupt_handler); } -int i2cstat; - -int i2c_wait(void) -{ - int stat; - while (!SI); - stat = IREG(I2STAT); - i2cstat = stat; - return stat; -} - int i2c_conreg(void) { return IREG(I2CONSET); @@ -60,61 +56,113 @@ int i2c_statreg(void) return IREG(I2STAT); } -bool i2c_send_start(void) +bool i2c_busy(void) { + return i2c_transaction != NULL; +} + +bool i2c_start_transaction(struct i2c_transaction *t) +{ + if (i2c_transaction) + return FALSE; + + i2c_transaction = t; + *(t->result) = I2C_IN_PROGRESS; + + /* Paranoia in case we left things in a bad state */ IREG(I2CONCLR) = STOFLAG | STAFLAG; + + /* Set it all going */ IREG(I2CONSET) = STAFLAG; - switch (i2c_wait()) { - case 0x08: - case 0x10: - return TRUE; - default: - i2c_send_stop(); - return FALSE; - } -} -bool i2c_send_address(int addr, bool write) -{ - return i2c_send_data((addr<<1) + (write?0:1)); + return TRUE; } -bool i2c_send_data(int data) +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void) { - IREG(I2DAT) = data; - IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; - switch (i2c_wait()) { - case 0x18: - case 0x28: - case 0x40: - return TRUE; - default: - i2c_send_stop(); - return FALSE; + int stat = IREG(I2STAT); + + if (!i2c_transaction) { + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + interrupt_clear(); + return; } -} -bool i2c_receive_data(int *data, bool last) -{ - if (!last) - IREG(I2CONSET) = AAFLAG; - IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG | (last?AAFLAG:0); - switch (i2c_wait()) { - case 0x50: - case 0x58: - *data = IREG(I2DAT); - return TRUE; - default: - i2c_send_stop(); - return FALSE; + switch (stat) { + + case 0x08: /* START transmitted */ + case 0x10: /* repeated START transmitted */ + IREG(I2DAT) = i2c_transaction->address; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + i2c_bytes = 0; + break; + case 0x18: /* SA+W transmitted, ACK received */ + case 0x28: /* data transmitted, ACK received */ + if (i2c_bytes < i2c_transaction->bytes) { + IREG(I2DAT) = i2c_transaction->data[i2c_bytes++]; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + } else { + *(i2c_transaction->result) = I2C_SUCCESS; + if (i2c_transaction->next) { + i2c_transaction = i2c_transaction->next; + i2c_bytes = 0; + IREG(I2CONSET) = STAFLAG; + IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; + } else { + event_set(i2c_transaction->event); + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + } + } + break; + + case 0x50: /* data received, ACK returned */ + i2c_transaction->data[i2c_bytes++] = IREG(I2DAT); + /* fall through */ + + case 0x40: /* SA+R transmitted, ACK received */ + if (i2c_bytes < (i2c_transaction->bytes-1)) { + IREG(I2CONSET) = AAFLAG; + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG; + } else { + IREG(I2CONCLR) = STAFLAG | STOFLAG | SIFLAG + | AAFLAG; + } + break; + + case 0x58: /* data received, NACK returned */ + i2c_transaction->data[i2c_bytes] = IREG(I2DAT); + *(i2c_transaction->result) = I2C_SUCCESS; + if (i2c_transaction->next) { + i2c_transaction = i2c_transaction->next; + i2c_bytes = 0; + IREG(I2CONSET) = STAFLAG; + IREG(I2CONCLR) = STOFLAG | AAFLAG | SIFLAG; + } else { + event_set(i2c_transaction->event); + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + } + break; + + case 0x20: /* SA+W transmitted, NACK received */ + case 0x30: /* data transmitted, NACK received */ + case 0x48: /* SA+R transmitted, NACK received */ + case 0x38: /* arbitration lost during SA+W or data */ + case 0x00: /* bus error */ + *(i2c_transaction->result) = I2C_FAIL; + event_set(i2c_transaction->event); + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + break; + + /* We don't handle slave mode */ } -} -void i2c_send_stop(void) -{ - IREG(I2CONCLR) = STAFLAG | AAFLAG; - IREG(I2CONSET) = STOFLAG; - IREG(I2CONCLR) = SIFLAG; - /* Don't think we need to wait here. Could be wrong. */ + interrupt_clear(); }