From a23e9ac65d85d31ebc6155050070546fa3a26df6 Mon Sep 17 00:00:00 2001 From: cinap_lenrek Date: Sat, 18 Jun 2022 12:48:26 +0000 Subject: imx8: add i2c bus driver --- sys/src/9/imx8/i2cimx.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) create mode 100644 sys/src/9/imx8/i2cimx.c (limited to 'sys/src/9/imx8/i2cimx.c') diff --git a/sys/src/9/imx8/i2cimx.c b/sys/src/9/imx8/i2cimx.c new file mode 100644 index 000000000..41e297697 --- /dev/null +++ b/sys/src/9/imx8/i2cimx.c @@ -0,0 +1,270 @@ +#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); +} -- cgit v1.2.3