ref: e8dc10b3b573fb5b696d8667826cd16629983494
dir: /i2cn900.c/
#include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/i2c.h" enum { Rrev = 0x00, Rie = 0x04, Ris = 0x08, Ial = 1 << 0, /* arbitration lost */ Inack = 1 << 1, /* no acknowledgement */ Iardy = 1 << 2, /* address ready */ Irrdy = 1 << 3, /* receive ready */ Ixrdy = 1 << 4, /* transmit ready */ Ibb = 1 << 12, /* bus busy */ Iall = 0xffff, Rwe = 0x0c, Rsyss = 0x10, SSreset = 1 << 0, /* reset status */ Rbuf = 0x14, Rcnt = 0x18, Rdata = 0x1c, Rsysc = 0x20, SCreset = 1 << 1, /* software reset */ Rcon = 0x24, Cstt = 1 << 0, /* start condition */ Cstp = 1 << 1, /* stop condiction */ Cxoa3 = 1 << 4, /* expand address */ Cxoa2 = 1 << 5, Cxoa1 = 1 << 6, Cxoa0 = 1 << 7, Cxa = 1 << 8, Ctrx = 1 << 9, /* transmit mode */ Cmst = 1 << 10, /* master mode */ Cstb = 1 << 11, /* start byte */ Cen = 1 << 15, /* enable */ Raddr = 0x2c, }; #define csr32r(c, r) ((c)->io[(r)/4]) #define csr32w(c, r, w) ((c)->io[(r)/4] = (w)) typedef struct Ctlr Ctlr; struct Ctlr { u32int *io; ulong irq; Rendez; }; static Ctlr ctlr[] = { { .io = (u32int*) PHYSI2C1, .irq = IRQI2C1 }, { .io = (u32int*) PHYSI2C2, .irq = IRQI2C2 }, { .io = (u32int*) PHYSI2C3, .irq = IRQI2C3 }, }; static void n900i2cwaitbus(Ctlr *ctlr) { /* FIXME: timeout here? */ while(csr32r(ctlr, Ris) & Ibb) ; } static int n900i2cwaitirq(void *arg) { Ctlr *ctlr = arg; return csr32r(ctlr, Ris); } static uint n900i2cwait(Ctlr *ctlr) { uint s; /* FIXME: timeout here? */ while(!(s = csr32r(ctlr, Ris))) { if(!up || !islo()) continue; tsleep(ctlr, n900i2cwaitirq, ctlr, 5); } return s; } static void n900i2cflush(Ctlr *ctlr) { while(csr32r(ctlr, Ris) & Irrdy) { USED(csr32r(ctlr, Rdata)); csr32w(ctlr, Ris, Irrdy); } } static void n900i2cintr(Ureg *, void *arg) { Ctlr *ctlr; ctlr = arg; wakeup(ctlr); } static int n900i2cinit(I2Cbus *bus) { Ctlr *ctlr; /* reset the ctlr */ ctlr = bus->ctlr; csr32w(ctlr, Rsysc, SCreset); csr32w(ctlr, Rcon, Cen); /* FIXME: timeout here? */ while(!(csr32r(ctlr, Rsyss) & SSreset)) ; intrenable(ctlr->irq, n900i2cintr, ctlr, 0, bus->name); return 0; } static int n900i2cio(I2Cbus *bus, uchar *pkt, int olen, int ilen) { Ctlr *ctlr; uint con, addr, stat; uint o; ctlr = bus->ctlr; if(olen <= 0 || pkt == nil) return -1; o = 0; con = Cen | Cmst | Ctrx | Cstp | Cstt; if((pkt[o] & 0xf8) == 0xf0) { /* 10-bit address: qemu has bugs, nothing on the n900 needs them. * con |= Cxa; * addr = ((pkt[o++] & 6) << 7) | pkt[o++]; */ return -1; } else { /* 7-bit address */ addr = pkt[o++] >> 1; } /* wait for bus */ n900i2cwaitbus(ctlr); /* first attempt to probe, will get nack here if no dev */ csr32w(ctlr, Rcnt, olen); csr32w(ctlr, Raddr, addr); csr32w(ctlr, Rcon, con); stat = n900i2cwait(ctlr); if(stat & Inack || stat & Ial) { o = -1; goto err; } /* transmit */ while(o < olen) { stat = n900i2cwait(ctlr); if(stat == 0 || stat & Inack || stat & Ial) { o = -1; goto err; } if(stat & Iardy) { csr32w(ctlr, Ris, Iardy); break; } if(stat & Ixrdy) { csr32w(ctlr, Rdata, pkt[o++]); csr32w(ctlr, Ris, Ixrdy); } } /* receive */ csr32w(ctlr, Rcnt, ilen); csr32w(ctlr, Raddr, addr); csr32w(ctlr, Rcon, Cen | Cmst | Cstp | Cstt); while(o < olen + ilen) { stat = n900i2cwait(ctlr); if(stat == 0 || stat & Inack || stat & Ial) { o = -1; goto err; } if(stat & Iardy) { csr32w(ctlr, Ris, Iardy); break; } if(stat & Irrdy) { pkt[o++] = csr32r(ctlr, Rdata); csr32w(ctlr, Ris, Irrdy); } } err: n900i2cflush(ctlr); csr32w(ctlr, Ris, Iall); return o; } void i2cn900link(void) { int i; static I2Cbus bus[] = { { "i2c1", 4000000, &ctlr[0], n900i2cinit, n900i2cio }, { "i2c2", 4000000, &ctlr[1], n900i2cinit, n900i2cio }, { "i2c3", 4000000, &ctlr[2], n900i2cinit, n900i2cio }, }; for(i = 0; i < nelem(bus); i++) addi2cbus(&bus[i]); }