The unified diff between revisions [a2621a92..] and [7f3278b1..] 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 [a2621a92a8c03a907239e78df69f38370d023a70] # new_revision [7f3278b164104bb0564a391a1f7b4fd955a3904a] # # patch "i2c.c" # from [1d978ba6c211e6b68c567fa852d9cbefe8ebad13] # to [7f5362fc29f8f8f81682424c73930b5e3044c994] # ============================================================ --- i2c.c 1d978ba6c211e6b68c567fa852d9cbefe8ebad13 +++ i2c.c 7f5362fc29f8f8f81682424c73930b5e3044c994 @@ -1,5 +1,7 @@ #include "i2c.h" +#include "interrupt.h" +#include "event.h" #define I2CBASE 0xE001C000 @@ -21,35 +23,25 @@ #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 */ -#if 0 +#ifdef I2C_FAST IWREG(I2SCLL) = (25 * 100); IWREG(I2SCLH) = (12 * 100); #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 +52,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(unsigned 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(unsigned 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 { + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + event_set(EVENT_I2C_COMPLETE); + } + } + 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 { + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + event_set(EVENT_I2C_COMPLETE); + } + 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; + i2c_transaction = NULL; + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + event_set(EVENT_I2C_COMPLETE); + 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(); }