#include "u.h" #include "../port/lib.h" #include "../port/error.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/i2c.h" enum { Moduleclk = 25*Mhz, I2C_IADR = 0x00, I2C_IFDR = 0x04, I2C_I2CR = 0x08, I2CR_IEN = 1<<7, I2CR_IIEN = 1<<6, I2CR_MSTA = 1<<5, I2CR_MTX = 1<<4, I2CR_TXAK = 1<<3, I2CR_RSTA = 1<<2, I2C_I2SR = 0x0C, I2SR_ICF = 1<<7, I2SR_IAAS = 1<<6, I2SR_IBB = 1<<5, I2SR_IAL = 1<<4, I2SR_SRW = 1<<2, I2SR_IIF = 1<<1, I2SR_RXAK = 1<<0, I2C_I2DR = 0x10, }; typedef struct Ctlr Ctlr; struct Ctlr { void *regs; int irq; Rendez; }; static void interrupt(Ureg*, void *arg) { I2Cbus *bus = arg; Ctlr *ctlr = bus->ctlr; wakeup(ctlr); } static int haveirq(void *arg) { uchar *regs = arg; return regs[I2C_I2SR] & (I2SR_IAL|I2SR_IIF); } static int waitsr(Ctlr *ctlr, int inv, int mask) { uchar *regs = ctlr->regs; int sr; for(;;){ sr = regs[I2C_I2SR]; if(sr & I2SR_IAL){ regs[I2C_I2SR] = sr & ~(I2SR_IAL|I2SR_IIF); break; } if(sr & I2SR_IIF) regs[I2C_I2SR] = sr & ~I2SR_IIF; if((sr ^ inv) & mask) break; /* polling mode */ if(up == nil || !islo()) continue; tsleep(ctlr, haveirq, regs, 1); } return sr ^ inv; } static uchar dummy; static int io(I2Cbus *bus, uchar *pkt, int olen, int ilen) { Ctlr *ctlr = bus->ctlr; uchar *regs = ctlr->regs; int cr, sr, alen, o, i; cr = regs[I2C_I2CR]; if((cr & I2CR_IEN) == 0) return -1; o = 0; if(olen <= 0) goto Stop; alen = 1; if((pkt[0] & 0xF8) == 0xF0 && olen > alen) alen++; regs[I2C_IADR] = (pkt[0]&0xFE)^0xFE; /* make sure doesnt match */ /* wait for bus idle */ waitsr(ctlr, I2SR_IBB, I2SR_IBB); /* start */ cr |= I2CR_MSTA | I2CR_MTX | I2CR_TXAK | I2CR_IIEN; regs[I2C_I2CR] = cr; /* wait for bus busy */ if(waitsr(ctlr, 0, I2SR_IBB) & I2SR_IAL) goto Err; if(olen > alen) pkt[0] &= ~1; for(o=0; octlr; uchar *regs = ctlr->regs; clkenable(bus->name, 1); regs[I2C_IFDR] = divindex(Moduleclk / bus->speed); regs[I2C_IADR] = 0; regs[I2C_I2CR] = I2CR_IEN; delay(1); intrenable(ctlr->irq, interrupt, bus, BUSUNKNOWN, bus->name); return 0; } static Ctlr ctlr1 = { .regs = (void*)(VIRTIO + 0xA20000), .irq = IRQi2c1, }; static Ctlr ctlr2 = { .regs = (void*)(VIRTIO + 0xA30000), .irq = IRQi2c2, }; static Ctlr ctlr3 = { .regs = (void*)(VIRTIO + 0xA40000), .irq = IRQi2c3, }; static Ctlr ctlr4 = { .regs = (void*)(VIRTIO + 0xA50000), .irq = IRQi2c4, }; void i2cimxlink(void) { static I2Cbus i2c1 = { "i2c1", 400000, &ctlr1, init, io }; static I2Cbus i2c3 = { "i2c3", 400000, &ctlr3, init, io }; static I2Cbus i2c4 = { "i2c4", 400000, &ctlr4, init, io }; iomuxpad("pad_i2c1_sda", "i2c1_sda", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM"); iomuxpad("pad_i2c1_scl", "i2c1_scl", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM"); addi2cbus(&i2c1); iomuxpad("pad_i2c3_sda", "i2c3_sda", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM"); iomuxpad("pad_i2c3_scl", "i2c3_scl", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM VSEL_0"); addi2cbus(&i2c3); iomuxpad("pad_i2c4_sda", "i2c4_sda", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM"); iomuxpad("pad_i2c4_scl", "i2c4_scl", "SION ~LVTTL ~HYS PUE ODE MAX 40_OHM"); addi2cbus(&i2c4); }