You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
3.8 KiB
164 lines
3.8 KiB
|
|
#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(); |
|
} |
|
|
|
|