#include "uxn.h"
#include "dat.h"
#include "fns.h"
#include "dev/console.h"
enum {
Rrev = 0x00,
Rie = 0x04,
Riexrdy = 1 << 4, /* tx ready */
Rierrdy = 1 << 3, /* rx ready */
Rieardy = 1 << 2, /* register access ready */
Rienack = 1 << 1, /* no acknowledgment */
Rieal = 1 << 0, /* arbitration lost */
Rstat = 0x08,
Rstatsbd = 1 << 15, /* single byte data */
Rstatbb = 1 << 12, /* bus busy */
Rstatrovr = 1 << 11, /* receive overrun */
Rstatxudf = 1 << 10, /* transmit underflow */
Rstataas = 1 << 9, /* address as slave */
Rstatxrdy = 1 << 4, /* tx ready */
Rstatrrdy = 1 << 3, /* rx ready */
Rstatardy = 1 << 2, /* register access ready */
Rstatnack = 1 << 1, /* no acknowledgment */
Rstatal = 1 << 0, /* arbitration lost */
Rwe = 0x0c,
Rsyss = 0x10,
Rsyssrdone = 1 << 0, /* reset monitoring */
Rbuf = 0x14,
Rbufrdma = 1 << 15, /* recieve dma */
Rbufxdma = 1 << 7, /* transmit dma */
Rcnt = 0x18,
Rdata = 0x1c,
Rsysc = 0x20,
Rcon = 0x24,
Rconen = 1 << 15, /* module enable */
Rconbe = 1 << 14, /* big endian mode */
Rconstb = 1 << 11, /* start byte mode */
Rconmst = 1 << 10, /* master/slave mode */
Rcontrx = 1 << 9, /* transmitter/receiver mode */
Rconxa = 1 << 8, /* expand address */
Rconstp = 1 << 1, /* stop condition */
Rconstt = 1 << 0, /* start condition */
Roa = 0x28,
Rsa = 0x2c,
Rpsc = 0x30,
Rscll = 0x34,
Rsclh = 0x38,
Rsystest = 0x3c,
Rbufstat = 0x40,
};
static Uint32
i2c_reg_read(Uint32 base, Uint8 r)
{
volatile Uint32* reg = (Uint32*) (base + r);
return *reg;
}
static void
i2c_reg_write(Uint32 base, Uint8 r, Uint32 value)
{
volatile Uint32* reg = (Uint32*) (base + r);
*reg = value;
}
static void
i2c_wait_for_bus(Uint32 base)
{
while(i2c_reg_read(base, Rstat) & Rstatbb)
;
}
static void
i2c_flush_fifo(Uint32 base)
{
Uint16 stat;
for(;;) {
stat = i2c_reg_read(base, Rstat);
if(stat == Rstatrrdy) {
i2c_reg_read(base, Rdata);
i2c_reg_write(base, Rstat, Rstatrrdy);
timer_delay(PHYSTIMER2, 1000);
} else {
break;
}
}
}
static Uint32
i2c_wait_for_event(Uint32 base)
{
int timeout;
Uint32 status;
timeout = 1000;
do {
status = i2c_reg_read(base, Rstat);
timer_delay(PHYSTIMER2, 10);
} while(status == 0 && timeout--);
if(timeout <= 0) {
console_puts("i2c: timeout waiting for event\n");
i2c_reg_write(base, Rstat, 0xFFFF);
}
return status;
}
void
i2c_init(Uint32 base)
{
if(i2c_reg_read(base, Rcon) & Rconen) {
i2c_reg_write(base, Rcon, 0);
timer_delay(PHYSTIMER2, 50000);
}
i2c_reg_write(base, Rsysc, 0x2);
timer_delay(PHYSTIMER2, 1000);
i2c_reg_write(base, Rcon, Rconen);
while(!(i2c_reg_read(base, Rsyss) & 1))
timer_delay(PHYSTIMER2, 1000);
i2c_reg_write(base, Roa, 0x11);
i2c_reg_write(base, Rie, Riexrdy
| Rierrdy
| Rieardy
| Rienack
| Rieal);
timer_delay(PHYSTIMER2, 1000);
i2c_flush_fifo(base);
i2c_reg_write(base, Rstat, 0xFFFF);
i2c_reg_write(base, Rbuf, 0);
}
void
i2c_write(Uint32 base, Uint8 sa, Uint8 *data, Uint8 acnt, Uint8 dcnt)
{
Uint32 status;
Uint32 cnt;
cnt = acnt + dcnt;
i2c_wait_for_bus(base);
i2c_reg_write(base, Rcnt, acnt + dcnt);
i2c_reg_write(base, Rsa, sa);
i2c_reg_write(base, Rcon, Rconen | Rconmst | Rcontrx | Rconstt | Rconstp);
while(cnt) {
status = i2c_wait_for_event(base);
if(status == 0) {
console_puts("i2c: status register was zero after event\n");
goto error;
}
if(status & Rstatnack) {
console_puts("i2c: got no acknowledgement\n");
goto error;
}
if(status & Rstatxrdy) {
i2c_reg_write(base, Rdata, *data++);
i2c_reg_write(base, Rstat, Rstatxrdy);
cnt--;
} else {
console_puts("i2c: was not ready to transmit\n");
goto error;
}
}
do {
status = i2c_wait_for_event(base);
} while(!(status & Rstatardy));
error:
i2c_flush_fifo(base);
i2c_reg_write(base, Rstat, 0xffff);
}
void
i2c_read(Uint32 base, Uint8 sa, Uint8 *data, Uint8 acnt, Uint8 dcnt)
{
Uint32 status;
i2c_wait_for_bus(base);
i2c_reg_write(base, Rcnt, acnt);
i2c_reg_write(base, Rsa, sa);
/* transmit mode if we need to send a register address */
if(acnt)
i2c_reg_write(base, Rcon, Rconen | Rconmst | Rcontrx | Rconstt | Rconstp);
while(acnt) {
status = i2c_wait_for_event(base);
if(status == 0) {
console_puts("i2c: status register was zero after event\n");
goto error;
}
if(status & Rstatnack) {
console_puts("i2c: got no acknowledgement\n");
goto error;
}
if(status & Rstatxrdy) {
acnt--;
i2c_reg_write(base, Rdata, *data++);
i2c_reg_write(base, Rstat, Rstatxrdy);
} else {
console_puts("i2c: was not ready to transmit\n");
goto error;
}
if(status & Rstatardy) {
/* address phase over, skip any remaining bytes */
i2c_reg_write(base, Rstat, Rstatardy);
if(acnt != 0)
data += acnt;
break;
}
}
i2c_reg_write(base, Rcnt, dcnt);
i2c_reg_write(base, Rsa, sa);
i2c_reg_write(base, Rcon, Rconen | Rconmst | Rconstt | Rconstp);
while(dcnt) {
status = i2c_wait_for_event(base);
if(status == 0) {
console_puts("i2c: status register was zero after event\n");
goto error;
}
if(status & Rstatnack) {
console_puts("i2c: got no acknowledgement\n");
goto error;
}
if(status & Rstatrrdy) {
dcnt--;
*data++ = i2c_reg_read(base, Rdata);
i2c_reg_write(base, Rstat, Rstatxrdy);
} else {
console_puts("i2c: was not ready to transmit\n");
goto error;
}
if(status & Rstatardy) {
i2c_reg_write(base, Rstat, Rstatardy);
break;
}
}
error:
i2c_flush_fifo(base);
i2c_reg_write(base, Rstat, 0xffff);
}