ref: bd773dee09460f5d77c96f2fe3d34ec5ee0b54ce
dir: /sys/src/9/pc/i2cdwc.c/
/* * synopsys designware core i²c driver */ #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/pci.h" #include "../port/i2c.h" enum { /* control */ Rcon = 0x00, Raddrtarget = 0x04, Raddr = 0x08, Rdata = 0x10, Renable = 0x6c, /* timing */ Rsclhss = 0x14, Rscllss = 0x18, Rsclhfs = 0x1c, Rscllfs = 0x20, /* fifo */ Rfiforxthresh = 0x38, Rfifotxthresh = 0x3c, /* interrupt */ Rintr = 0x2c, Rintrmask = 0x30, Rintrclear = 0x40, Rintrclearrxunder = 0x44, Rintrclearrxover = 0x48, Rintrcleartxover = 0x4c, Rintrclearrdreq = 0x50, Rintrcleartxabrt = 0x54, Rintrclearrxdone = 0x58, Rintrclearact = 0x5c, Rintrcleardetstop = 0x60, Rintrcleardetstart = 0x64, /* component */ Rcompparam = 0xf4, Rcompver = 0xf8, Rcomptype = 0xfc, }; enum { /* control */ Ccontroller = 1<<0, Cspeed = 1<<1, Cspeedfast = 1<<2, Crestart = 1<<5, Ctargetdisable = 1<<6, }; typedef struct Ctlr Ctlr; struct Ctlr { Rendez; Ctlr *link; u64int port; u32int *portmmio; u32int sclhss; u32int scllss; u32int sclhfs; u32int scllfs; u8int fifotx; u8int fiforx; }; static Ctlr *i2cdwcctlr; static Ctlr *i2cdwcctlrtail; #define csr32r(c, r) (*((c)->portmmio+((r)/4))) #define csr32w(c, r, v) (*((c)->portmmio+((r)/4)) = (v)) static void i2cdwcenable(Ctlr *c, int e) { int r; for(r = 0; r < 100; r++){ csr32w(c, Renable, e); if ((csr32r(c, Renable) & 1) == e) return; microdelay(25); } error("timeout wating for enable"); } static int i2cdwcinit(I2Cbus *b) { Ctlr *c; c = b->ctlr; if(csr32r(c, Rcomptype) != 0x44570140) error("invalid component"); /* read timing parameters */ c->sclhss = csr32r(c, Rsclhss); c->scllss = csr32r(c, Rscllss); c->sclhfs = csr32r(c, Rsclhfs); c->scllfs = csr32r(c, Rscllfs); /* read fifo parameters */ c->fifotx = csr32r(c, Rcompparam) >> 16; c->fiforx = csr32r(c, Rcompparam) >> 8; i2cdwcenable(c, 0); /* write timing parameters */ csr32w(c, Rsclhss, c->sclhss); csr32w(c, Rscllss, c->scllss); csr32w(c, Rsclhfs, c->sclhfs); csr32w(c, Rscllfs, c->scllfs); /* write fifo parameters */ csr32w(c, Rfifotxthresh, c->fifotx / 2); csr32w(c, Rfiforxthresh, 0); /* configure */ csr32w(c, Rcon, Ccontroller | Cspeedfast | Crestart | Ctargetdisable); return 0; } static int i2cdwcio(I2Cdev *, uchar *, int, int) { /* XXX: io */ return 0; } static void i2cdwcpnp(void) { /* XXX: acpi :( */ } static void i2cdwcpnppci(void) { Ctlr *c; Pcidev *p; for(p = nil; p = pcimatch(p, 0x8086, 0);){ if(p->ccrb != 0x0c || p->ccru != 0x80) continue; if(p->mem[0].bar & 1) continue; switch(p->did){ case 0xa160: /* intel 100 series */ case 0xa161: case 0xa162: case 0xa163: case 0x9d60: /* intel 100 series lp */ case 0x9d61: case 0x9d62: case 0x9d63: case 0x9d64: case 0x9d65: case 0xa2e0: /* intel 200 series */ case 0xa2e1: case 0xa2e2: case 0xa2e3: case 0xa368: /* intel 300 series */ case 0xa369: case 0xa36a: case 0xa36b: case 0x9dc5: /* intel 300 series u */ case 0x9dc6: case 0x9de8: case 0x9de9: case 0x9dea: case 0x9deb: case 0x06e8: /* intel 400 series */ case 0x06e9: case 0x06ea: case 0x06eb: case 0x02c5: /* intel 400 series lp */ case 0x02c6: case 0x02e8: case 0x02e9: case 0x02ea: case 0x02eb: case 0xa3e0: /* intel 400 series v */ case 0xa3e1: case 0xa3e2: case 0xa3e3: case 0x34e8: /* intel 495 series lp */ case 0x34e9: case 0x34ea: case 0x34eb: case 0x34c5: case 0x34c6: case 0x43e8: /* intel 500 series */ case 0x43e9: case 0x43ea: case 0x43eb: case 0x43ad: case 0x43ae: case 0x43d8: case 0xa0c5: /* intel 500 series lp */ case 0xa0c6: case 0xa0e8: case 0xa0e9: case 0xa0ae: case 0xa0eb: case 0x7acc: /* intel 600 series */ case 0x7acd: case 0x7ace: case 0x7acf: case 0x7afc: case 0x7afd: case 0x51c5: /* intel 600 series lp */ case 0x51c6: case 0x51d8: case 0x51d9: case 0x51e8: case 0x51e9: case 0x51ea: case 0x51eb: case 0x7a4c: /* intel 700 series */ case 0x7a4d: case 0x7a4e: case 0x7a4f: case 0x7a7c: case 0x7a7d: case 0x5aac: /* intel apollo lake */ case 0x5aae: case 0x5ab0: case 0x5ab2: case 0x5ab4: case 0x5ab6: case 0x5ab8: case 0x5aba: case 0x31ac: /* intel gemini lake */ case 0x31ae: case 0x31b0: case 0x31b2: case 0x31b4: case 0x31b6: case 0x31b8: case 0x31ba: case 0x4de8: /* intel jasper lake */ case 0x4de9: case 0x4dea: case 0x4deb: case 0x4dc5: case 0x4dc6: case 0x4b78: /* intel elkhart lake */ case 0x4b79: case 0x4b7a: case 0x4b7b: case 0x4b4b: case 0x4b4c: case 0x4b44: case 0x4b45: case 0x54e8: /* intel alder lake */ case 0x54e9: case 0x54ea: case 0x54eb: case 0x54c5: case 0x54c6: case 0x54d8: case 0x54d9: case 0x7e78: /* intel metor lake */ case 0x7e79: case 0x7e7a: case 0x7e7b: case 0x7e50: case 0x7e51: case 0xa878: /* intel lunar lake */ case 0xa879: case 0xa87a: case 0xa87b: case 0xa850: case 0xa851: break; default: continue; } c = malloc(sizeof(*c)); if(c == nil) { print("#J: dwc: no memory for ctlr"); continue; } c->port = p->mem[0].bar & ~0xf; c->portmmio = vmap(c->port, p->mem[0].size); if(c->portmmio == nil){ print("#J: dwc: no memory for ctlr mmio"); free(c); continue; } pcienable(p); if(i2cdwcctlr == nil) i2cdwcctlr = c; else i2cdwcctlrtail->link = c; i2cdwcctlrtail = c; } } void i2cdwclink(void) { Ctlr *c; I2Cbus *bus; int busno; busno = 0; i2cdwcpnp(); i2cdwcpnppci(); for(c = i2cdwcctlr; c != nil; c = c->link) { bus = malloc(sizeof(*bus)); if(bus == nil){ print("#J: dwc: no memory for bus"); continue; } bus->name = malloc(KNAMELEN); if(bus->name == nil){ print("#J: dwc: no memory for bus name"); free(bus); continue; } snprint(bus->name, KNAMELEN, "i2c%d", busno++); bus->ctlr = c; bus->init = i2cdwcinit; bus->io = i2cdwcio; addi2cbus(bus); } }