shithub: front

ref: 44105baa85baa6c29e39f6c56f2ca0c68123e8c2
dir: /sys/src/9/pc/i2cdwc.c/

View raw version
/*
 * 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);
	}
}