The unified diff between revisions [d8ed90db..] 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 [d8ed90db2d4284a290224447c40a0d9cef3fbc31] # new_revision [056a532c92301bcb224e1f786c5f6720e8acf3eb] # # patch "i2c.c" # from [7a497687684cdd6463a65bd4f3e08b1bc8f580a7] # to [2dff1acf82a051d155952429555a65a193ba0fd5] # ============================================================ --- i2c.c 7a497687684cdd6463a65bd4f3e08b1bc8f580a7 +++ i2c.c 2dff1acf82a051d155952429555a65a193ba0fd5 @@ -1,5 +1,7 @@ #include "i2c.h" +#include "interrupt.h" +#include "event.h" #define I2CBASE 0xE001C000 @@ -21,84 +23,146 @@ #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 - IWREG(I2SCLL) = 1475; /* ~5kHz */ - IWREG(I2SCLH) = 1475; + IWREG(I2SCLL) = 73; /* ~100kHz */ + IWREG(I2SCLH) = 73; #endif + interrupt_register(I2C0, i2c_interrupt_handler); } -int i2cstat; - -int i2c_wait(void) +int i2c_conreg(void) { - int stat; - while (!SI); - stat = IREG(I2STAT); - i2cstat = stat; - return stat; + return IREG(I2CONSET); } -void i2c_go(void) +int i2c_statreg(void) { - IREG(I2CONCLR) = SIFLAG; + return IREG(I2STAT); } -int i2c_conreg(void) +bool i2c_busy(void) { - return IREG(I2CONSET); + return i2c_transaction != NULL; } -int i2c_statreg(void) +bool i2c_start_transaction(struct i2c_transaction *t) { - return IREG(I2STAT); -} + if (i2c_transaction) + return FALSE; -bool i2c_send_start(void) -{ + 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; -/* i2c_go(); */ - switch (i2c_wait()) { - case 0x08: - case 0x10: -/* IREG(I2CONCLR) = STAFLAG; */ - return TRUE; - default: -/* IREG(I2CONCLR) = STAFLAG; */ - return FALSE; - } + + return TRUE; } -bool i2c_send_address(int addr, bool write) +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void) { - return i2c_send_data((addr<<1) + (write?0:1)); -} + int stat = IREG(I2STAT); -bool i2c_send_data(int data) -{ - IREG(I2DAT) = data; - IREG(I2CONCLR) = STAFLAG | SIFLAG; - switch (i2c_wait()) { - case 0x18: - case 0x28: - return TRUE; - default: - return FALSE; + if (!i2c_transaction) { + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + interrupt_clear(); + return; } -} -void i2c_send_stop(void) -{ - IREG(I2CONCLR) = STAFLAG | AAFLAG; - IREG(I2CONSET) = STOFLAG; - i2c_go(); - /* Don't think we need to wait here. Could be wrong. */ + 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 */ + } + + interrupt_clear(); }