The unified diff between revisions [7ac10cd3..] and [be147b11..] 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 [7ac10cd34167baa43683a09e9e9e6778e691171d] # new_revision [be147b11caac304fda1579ac71017eecc3bb79e0] # # add_file "i2c.c" # content [7f5362fc29f8f8f81682424c73930b5e3044c994] # ============================================================ --- /dev/null +++ i2c.c 7f5362fc29f8f8f81682424c73930b5e3044c994 @@ -0,0 +1,164 @@ + +#include "i2c.h" +#include "interrupt.h" +#include "event.h" + +#define I2CBASE 0xE001C000 + +#define I2CONSET 0x00 +#define I2STAT 0x04 +#define I2DAT 0x08 +#define I2ADR 0x0c +#define I2SCLH 0x10 +#define I2SCLL 0x14 +#define I2CONCLR 0x18 + +#define IREG(x) (((volatile unsigned char *)I2CBASE)[x]) +#define IWREG(x) (((volatile unsigned int *)I2CBASE)[(x)/sizeof(unsigned int)]) + +#define AAFLAG (1<<2) +#define SIFLAG (1<<3) +#define STOFLAG (1<<4) +#define STAFLAG (1<<5) + +#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 + IWREG(I2SCLL) = (25 * 100); + IWREG(I2SCLH) = (12 * 100); +#else + IWREG(I2SCLL) = 73; /* ~100kHz */ + IWREG(I2SCLH) = 73; +#endif + interrupt_register(I2C0, i2c_interrupt_handler); +} + +int i2c_conreg(void) +{ + return IREG(I2CONSET); +} + +int i2c_statreg(void) +{ + return IREG(I2STAT); +} + +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; + + return TRUE; +} + +void __attribute__((interrupt("IRQ"))) i2c_interrupt_handler(void) +{ + int stat = IREG(I2STAT); + + if (!i2c_transaction) { + IREG(I2CONSET) = STOFLAG; + IREG(I2CONCLR) = STAFLAG | AAFLAG | SIFLAG; + interrupt_clear(); + return; + } + + 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 */ + } + + interrupt_clear(); +} +