#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(); }