ref: e9b2b1562c1295c164d3d416414cef0af257a7c2
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);
}
}