devi2c: add generic i2c bus driver
This commit is contained in:
parent
3e176bd975
commit
276f2039a9
2 changed files with 653 additions and 0 deletions
597
sys/src/9/port/devi2c.c
Normal file
597
sys/src/9/port/devi2c.c
Normal file
|
@ -0,0 +1,597 @@
|
||||||
|
/* I²C bus driver */
|
||||||
|
#include "u.h"
|
||||||
|
#include "../port/lib.h"
|
||||||
|
#include "mem.h"
|
||||||
|
#include "dat.h"
|
||||||
|
#include "fns.h"
|
||||||
|
#include "../port/error.h"
|
||||||
|
#include "../port/i2c.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Qdir = 0, /* #J */
|
||||||
|
Qbus, /* #J/bus */
|
||||||
|
Qctl, /* #J/bus/i2c.n.ctl */
|
||||||
|
Qdata, /* #J/bus/i2c.n.data */
|
||||||
|
};
|
||||||
|
|
||||||
|
#define TYPE(q) ((ulong)(q).path & 0x0F)
|
||||||
|
#define BUS(q) (((ulong)(q).path>>4) & 0xFF)
|
||||||
|
#define DEV(q) (((ulong)(q).path>>12) & 0xFFF)
|
||||||
|
#define QID(d, b, t) (((d)<<12)|((b)<<4)|(t))
|
||||||
|
|
||||||
|
static I2Cbus *buses[16];
|
||||||
|
|
||||||
|
static I2Cdev *devs[1024];
|
||||||
|
static Lock devslock;
|
||||||
|
|
||||||
|
static void probebus(I2Cbus *bus);
|
||||||
|
|
||||||
|
void
|
||||||
|
addi2cbus(I2Cbus *bus)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(bus == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(buses); i++){
|
||||||
|
if(buses[i] == nil
|
||||||
|
|| buses[i] == bus
|
||||||
|
|| strcmp(bus->name, buses[i]->name) == 0){
|
||||||
|
buses[i] = bus;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
I2Cbus*
|
||||||
|
i2cbus(char *name)
|
||||||
|
{
|
||||||
|
I2Cbus *bus;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for(i = 0; i < nelem(buses); i++){
|
||||||
|
bus = buses[i];
|
||||||
|
if(bus == nil)
|
||||||
|
break;
|
||||||
|
if(strcmp(bus->name, name) == 0){
|
||||||
|
probebus(bus);
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
addi2cdev(I2Cdev *dev)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(dev == nil || dev->bus == nil)
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock(&devslock);
|
||||||
|
for(i = 0; i < nelem(devs); i++){
|
||||||
|
if(devs[i] == nil
|
||||||
|
|| devs[i] == dev
|
||||||
|
|| devs[i]->addr == dev->addr && devs[i]->bus == dev->bus){
|
||||||
|
devs[i] = dev;
|
||||||
|
unlock(&devslock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock(&devslock);
|
||||||
|
}
|
||||||
|
|
||||||
|
I2Cdev*
|
||||||
|
i2cdev(I2Cbus *bus, int addr)
|
||||||
|
{
|
||||||
|
I2Cdev *dev;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if(bus == nil || addr < 0 || addr >= 1<<10)
|
||||||
|
return nil;
|
||||||
|
|
||||||
|
lock(&devslock);
|
||||||
|
for(i = 0; i < nelem(devs) && (dev = devs[i]) != nil; i++){
|
||||||
|
if(dev->addr == addr && dev->bus == bus){
|
||||||
|
unlock(&devslock);
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock(&devslock);
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
enterbus(I2Cbus *bus)
|
||||||
|
{
|
||||||
|
if(up != nil && islo()){
|
||||||
|
eqlock(bus);
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
while(!canqlock(bus))
|
||||||
|
;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
leavebus(I2Cbus *bus)
|
||||||
|
{
|
||||||
|
qunlock(bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i2cbusio(I2Cbus *bus, uchar *pkt, int olen, int ilen)
|
||||||
|
{
|
||||||
|
int user, n;
|
||||||
|
|
||||||
|
user = enterbus(bus);
|
||||||
|
if(!bus->probed){
|
||||||
|
leavebus(bus);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if(user && waserror()){
|
||||||
|
(*bus->io)(bus, nil, 0, 0);
|
||||||
|
leavebus(bus);
|
||||||
|
nexterror();
|
||||||
|
}
|
||||||
|
// iprint("%s: <- %.*H\n", bus->name, olen, pkt);
|
||||||
|
n = (*bus->io)(bus, pkt, olen, ilen);
|
||||||
|
// if(n > olen) iprint("%s: -> %.*H\n", bus->name, n - olen, pkt+olen);
|
||||||
|
|
||||||
|
leavebus(bus);
|
||||||
|
if(user) poperror();
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
putaddr(I2Cdev *dev, int isread, uchar *pkt, vlong addr)
|
||||||
|
{
|
||||||
|
int n, o = 0;
|
||||||
|
|
||||||
|
if(dev->a10){
|
||||||
|
pkt[o++] = 0xF0 | (dev->addr>>(8-1))&6 | (isread != 0);
|
||||||
|
pkt[o++] = dev->addr;
|
||||||
|
} else
|
||||||
|
pkt[o++] = dev->addr<<1 | (isread != 0);
|
||||||
|
|
||||||
|
if(addr >= 0){
|
||||||
|
for(n=0; n<dev->subaddr; n++)
|
||||||
|
pkt[o++] = addr >> (n*8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i2csend(I2Cdev *dev, void *data, int len, vlong addr)
|
||||||
|
{
|
||||||
|
uchar pkt[138];
|
||||||
|
int o;
|
||||||
|
|
||||||
|
o = putaddr(dev, 0, pkt, addr);
|
||||||
|
if(o+len > sizeof(pkt))
|
||||||
|
len = sizeof(pkt)-o;
|
||||||
|
|
||||||
|
if(len > 0)
|
||||||
|
memmove(pkt+o, data, len);
|
||||||
|
|
||||||
|
return i2cbusio(dev->bus, pkt, o + len, 0) - o;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i2crecv(I2Cdev *dev, void *data, int len, vlong addr)
|
||||||
|
{
|
||||||
|
uchar pkt[138];
|
||||||
|
int o;
|
||||||
|
|
||||||
|
o = putaddr(dev, 1, pkt, addr);
|
||||||
|
if(o+len > sizeof(pkt))
|
||||||
|
len = sizeof(pkt)-o;
|
||||||
|
|
||||||
|
len = i2cbusio(dev->bus, pkt, o, len) - o;
|
||||||
|
if(len > 0)
|
||||||
|
memmove(data, pkt+o, len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
i2cquick(I2Cdev *dev, int rw)
|
||||||
|
{
|
||||||
|
uchar pkt[2];
|
||||||
|
int o = putaddr(dev, rw, pkt, -1);
|
||||||
|
if(i2cbusio(dev->bus, pkt, o, 0) != o)
|
||||||
|
return -1;
|
||||||
|
return rw != 0;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2crecvbyte(I2Cdev *dev)
|
||||||
|
{
|
||||||
|
uchar pkt[2+1];
|
||||||
|
int o = putaddr(dev, 1, pkt, -1);
|
||||||
|
if(i2cbusio(dev->bus, pkt, o, 1) - o != 1)
|
||||||
|
return -1;
|
||||||
|
return pkt[o];
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2csendbyte(I2Cdev *dev, uchar b)
|
||||||
|
{
|
||||||
|
uchar pkt[2+1];
|
||||||
|
int o = putaddr(dev, 0, pkt, -1);
|
||||||
|
pkt[o] = b;
|
||||||
|
if(i2cbusio(dev->bus, pkt, o+1, 0) - o != 1)
|
||||||
|
return -1;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2creadbyte(I2Cdev *dev, ulong addr)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+1];
|
||||||
|
int o = putaddr(dev, 1, pkt, addr);
|
||||||
|
if(i2cbusio(dev->bus, pkt, o, 1) - o != 1)
|
||||||
|
return -1;
|
||||||
|
return pkt[o];
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2cwritebyte(I2Cdev *dev, ulong addr, uchar b)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+1];
|
||||||
|
int o = putaddr(dev, 0, pkt, addr);
|
||||||
|
pkt[o] = b;
|
||||||
|
if(i2cbusio(dev->bus, pkt, o+1, 0) - o != 1)
|
||||||
|
return -1;
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2creadword(I2Cdev *dev, ulong addr)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+2];
|
||||||
|
int o = putaddr(dev, 1, pkt, addr);
|
||||||
|
if(i2cbusio(dev->bus, pkt, o, 2) - o != 2)
|
||||||
|
return -1;
|
||||||
|
return pkt[o] | (ushort)pkt[o+1]<<8;
|
||||||
|
}
|
||||||
|
int
|
||||||
|
i2cwriteword(I2Cdev *dev, ulong addr, ushort w)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+2];
|
||||||
|
int o = putaddr(dev, 0, pkt, addr);
|
||||||
|
pkt[o+0] = w;
|
||||||
|
pkt[o+1] = w>>8;
|
||||||
|
if(i2cbusio(dev->bus, pkt, o+2, 0) - o != 2)
|
||||||
|
return -1;
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
vlong
|
||||||
|
i2cread32(I2Cdev *dev, ulong addr)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+4];
|
||||||
|
int o = putaddr(dev, 1, pkt, addr);
|
||||||
|
if(i2cbusio(dev->bus, pkt, o, 4) - o != 4)
|
||||||
|
return -1;
|
||||||
|
return pkt[o] | (ulong)pkt[o+1]<<8 | (ulong)pkt[o+2]<<16 | (ulong)pkt[o+3]<<24;
|
||||||
|
}
|
||||||
|
vlong
|
||||||
|
i2cwrite32(I2Cdev *dev, ulong addr, ulong u)
|
||||||
|
{
|
||||||
|
uchar pkt[2+4+4];
|
||||||
|
int o = putaddr(dev, 0, pkt, addr);
|
||||||
|
pkt[o+0] = u;
|
||||||
|
pkt[o+1] = u>>8;
|
||||||
|
pkt[o+2] = u>>16;
|
||||||
|
pkt[o+3] = u>>24;
|
||||||
|
if(i2cbusio(dev->bus, pkt, o+4, 0) - o != 4)
|
||||||
|
return -1;
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
probeddev(I2Cdev *dummy)
|
||||||
|
{
|
||||||
|
I2Cdev *dev = smalloc(sizeof(I2Cdev));
|
||||||
|
memmove(dev, dummy, sizeof(I2Cdev));
|
||||||
|
addi2cdev(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
probebus(I2Cbus *bus)
|
||||||
|
{
|
||||||
|
I2Cdev dummy;
|
||||||
|
uchar pkt[2];
|
||||||
|
int user, n;
|
||||||
|
|
||||||
|
if(bus->probed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
user = enterbus(bus);
|
||||||
|
if(bus->probed){
|
||||||
|
leavebus(bus);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(user && waserror()
|
||||||
|
|| (*bus->init)(bus)){
|
||||||
|
leavebus(bus);
|
||||||
|
if(user) nexterror();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&dummy, 0, sizeof(dummy));
|
||||||
|
dummy.bus = bus;
|
||||||
|
|
||||||
|
dummy.a10 = 0;
|
||||||
|
for(dummy.addr = 8; dummy.addr < 0x78; dummy.addr++) {
|
||||||
|
if(i2cdev(bus, dummy.addr) != nil)
|
||||||
|
continue;
|
||||||
|
if(user && waserror()){
|
||||||
|
(*bus->io)(bus, nil, 0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
n = putaddr(&dummy, 0, pkt, -1);
|
||||||
|
if((*bus->io)(bus, pkt, n, 0) == n)
|
||||||
|
probeddev(&dummy);
|
||||||
|
if(user) poperror();
|
||||||
|
}
|
||||||
|
|
||||||
|
dummy.a10 = 1;
|
||||||
|
for(dummy.addr = 0; dummy.addr < (1<<10); dummy.addr++) {
|
||||||
|
if(i2cdev(bus, dummy.addr) != nil)
|
||||||
|
continue;
|
||||||
|
if(user && waserror()){
|
||||||
|
(*bus->io)(bus, nil, 0, 0);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
n = putaddr(&dummy, 0, pkt, -1);
|
||||||
|
if((*bus->io)(bus, pkt, n, 0) == n)
|
||||||
|
probeddev(&dummy);
|
||||||
|
if(user) poperror();
|
||||||
|
}
|
||||||
|
|
||||||
|
bus->probed = 1;
|
||||||
|
leavebus(bus);
|
||||||
|
if(user) poperror();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i2cgen(Chan *c, char *, Dirtab*, int, int s, Dir *dp)
|
||||||
|
{
|
||||||
|
I2Cbus *bus;
|
||||||
|
I2Cdev *dev;
|
||||||
|
|
||||||
|
Qid q;
|
||||||
|
|
||||||
|
switch(TYPE(c->qid)){
|
||||||
|
case Qdir:
|
||||||
|
if(s == DEVDOTDOT){
|
||||||
|
Gendir:
|
||||||
|
mkqid(&q, QID(0, 0, Qdir), 0, QTDIR);
|
||||||
|
snprint(up->genbuf, sizeof up->genbuf, "#J");
|
||||||
|
devdir(c, q, up->genbuf, 0, eve, 0500, dp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if(s >= nelem(buses))
|
||||||
|
return -1;
|
||||||
|
bus = buses[s];
|
||||||
|
if(bus == nil)
|
||||||
|
return -1;
|
||||||
|
mkqid(&q, QID(0, s, Qbus), 0, QTDIR);
|
||||||
|
devdir(c, q, bus->name, 0, eve, 0500, dp);
|
||||||
|
return 1;
|
||||||
|
case Qbus:
|
||||||
|
if(s == DEVDOTDOT)
|
||||||
|
goto Gendir;
|
||||||
|
if((s/2) >= nelem(devs))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
bus = buses[BUS(c->qid)];
|
||||||
|
probebus(bus);
|
||||||
|
|
||||||
|
lock(&devslock);
|
||||||
|
dev = devs[s/2];
|
||||||
|
unlock(&devslock);
|
||||||
|
|
||||||
|
if(dev == nil)
|
||||||
|
return -1;
|
||||||
|
if(dev->bus != bus)
|
||||||
|
return 0;
|
||||||
|
if(s & 1){
|
||||||
|
mkqid(&q, QID(dev->addr, BUS(c->qid), Qdata), 0, 0);
|
||||||
|
goto Gendata;
|
||||||
|
}
|
||||||
|
mkqid(&q, QID(dev->addr, BUS(c->qid), Qctl), 0, 0);
|
||||||
|
goto Genctl;
|
||||||
|
case Qctl:
|
||||||
|
q = c->qid;
|
||||||
|
Genctl:
|
||||||
|
snprint(up->genbuf, sizeof up->genbuf, "i2c.%lux.ctl", DEV(q));
|
||||||
|
devdir(c, q, up->genbuf, 0, eve, 0600, dp);
|
||||||
|
return 1;
|
||||||
|
case Qdata:
|
||||||
|
q = c->qid;
|
||||||
|
bus = buses[BUS(q)];
|
||||||
|
dev = i2cdev(bus, DEV(q));
|
||||||
|
if(dev == nil)
|
||||||
|
return -1;
|
||||||
|
Gendata:
|
||||||
|
snprint(up->genbuf, sizeof up->genbuf, "i2c.%lux.data", DEV(q));
|
||||||
|
devdir(c, q, up->genbuf, dev->size, eve, 0600, dp);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Chan*
|
||||||
|
i2cattach(char *spec)
|
||||||
|
{
|
||||||
|
return devattach('J', spec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Chan*
|
||||||
|
i2copen(Chan *c, int mode)
|
||||||
|
{
|
||||||
|
c = devopen(c, mode, nil, 0, i2cgen);
|
||||||
|
switch(TYPE(c->qid)){
|
||||||
|
case Qctl:
|
||||||
|
case Qdata:
|
||||||
|
c->aux = i2cdev(buses[BUS(c->qid)], DEV(c->qid));
|
||||||
|
if(c->aux == nil)
|
||||||
|
error(Enonexist);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CMsize,
|
||||||
|
CMsubaddress,
|
||||||
|
};
|
||||||
|
|
||||||
|
static Cmdtab i2cctlmsg[] =
|
||||||
|
{
|
||||||
|
CMsize, "size", 2,
|
||||||
|
CMsubaddress, "subaddress", 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static long
|
||||||
|
i2cwrctl(I2Cdev *dev, void *data, long len)
|
||||||
|
{
|
||||||
|
Cmdbuf *cb;
|
||||||
|
Cmdtab *ct;
|
||||||
|
ulong u;
|
||||||
|
|
||||||
|
cb = parsecmd(data, len);
|
||||||
|
if(waserror()){
|
||||||
|
free(cb);
|
||||||
|
nexterror();
|
||||||
|
}
|
||||||
|
ct = lookupcmd(cb, i2cctlmsg, nelem(i2cctlmsg));
|
||||||
|
switch(ct->index){
|
||||||
|
case CMsize:
|
||||||
|
dev->size = strtoul(cb->f[1], nil, 0);
|
||||||
|
break;
|
||||||
|
case CMsubaddress:
|
||||||
|
u = strtoul(cb->f[1], nil, 0);
|
||||||
|
if(u > 4)
|
||||||
|
cmderror(cb, Ebadarg);
|
||||||
|
dev->subaddr = u;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
cmderror(cb, Ebadarg);
|
||||||
|
}
|
||||||
|
free(cb);
|
||||||
|
poperror();
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
i2crdctl(I2Cdev *dev, void *data, long len, vlong offset)
|
||||||
|
{
|
||||||
|
char cfg[64];
|
||||||
|
|
||||||
|
snprint(cfg, sizeof(cfg), "size %lud\nsubaddress %d\n", dev->size, dev->subaddr);
|
||||||
|
return readstr((ulong)offset, data, len, cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
i2cwrite(Chan *c, void *data, long len, vlong offset)
|
||||||
|
{
|
||||||
|
I2Cdev *dev;
|
||||||
|
|
||||||
|
switch(TYPE(c->qid)){
|
||||||
|
default:
|
||||||
|
error(Egreg);
|
||||||
|
return -1;
|
||||||
|
case Qctl:
|
||||||
|
dev = c->aux;
|
||||||
|
return i2cwrctl(dev, data, len);
|
||||||
|
case Qdata:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev = c->aux;
|
||||||
|
if(dev->size){
|
||||||
|
if(offset+len > dev->size){
|
||||||
|
if(offset >= dev->size)
|
||||||
|
return 0;
|
||||||
|
len = dev->size - offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = i2csend(dev, data, len, offset);
|
||||||
|
if(len < 0)
|
||||||
|
error(Eio);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long
|
||||||
|
i2cread(Chan *c, void *data, long len, vlong offset)
|
||||||
|
{
|
||||||
|
I2Cdev *dev;
|
||||||
|
|
||||||
|
if(c->qid.type == QTDIR)
|
||||||
|
return devdirread(c, data, len, nil, 0, i2cgen);
|
||||||
|
|
||||||
|
switch(TYPE(c->qid)){
|
||||||
|
default:
|
||||||
|
error(Egreg);
|
||||||
|
case Qctl:
|
||||||
|
dev = c->aux;
|
||||||
|
return i2crdctl(dev, data, len, offset);
|
||||||
|
case Qdata:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev = c->aux;
|
||||||
|
if(dev->size){
|
||||||
|
if(offset+len > dev->size){
|
||||||
|
if(offset >= dev->size)
|
||||||
|
return 0;
|
||||||
|
len = dev->size - offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
len = i2crecv(dev, data, len, offset);
|
||||||
|
if(len < 0)
|
||||||
|
error(Eio);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
i2cclose(Chan*)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static Walkqid*
|
||||||
|
i2cwalk(Chan *c, Chan *nc, char **name, int nname)
|
||||||
|
{
|
||||||
|
return devwalk(c, nc, name, nname, nil, 0, i2cgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
i2cstat(Chan *c, uchar *dp, int n)
|
||||||
|
{
|
||||||
|
return devstat(c, dp, n, nil, 0, i2cgen);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dev i2cdevtab = {
|
||||||
|
'J',
|
||||||
|
"i2c",
|
||||||
|
|
||||||
|
devreset,
|
||||||
|
devinit,
|
||||||
|
devshutdown,
|
||||||
|
i2cattach,
|
||||||
|
i2cwalk,
|
||||||
|
i2cstat,
|
||||||
|
i2copen,
|
||||||
|
devcreate,
|
||||||
|
i2cclose,
|
||||||
|
i2cread,
|
||||||
|
devbread,
|
||||||
|
i2cwrite,
|
||||||
|
devbwrite,
|
||||||
|
devremove,
|
||||||
|
devwstat,
|
||||||
|
devpower,
|
||||||
|
};
|
56
sys/src/9/port/i2c.h
Normal file
56
sys/src/9/port/i2c.h
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
typedef struct I2Cbus I2Cbus;
|
||||||
|
struct I2Cbus
|
||||||
|
{
|
||||||
|
char *name;
|
||||||
|
int speed;
|
||||||
|
|
||||||
|
void *ctlr;
|
||||||
|
int (*init)(I2Cbus *bus);
|
||||||
|
int (*io)(I2Cbus *bus, uchar *pkt, int olen, int ilen);
|
||||||
|
|
||||||
|
int probed;
|
||||||
|
QLock;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct I2Cdev I2Cdev;
|
||||||
|
struct I2Cdev
|
||||||
|
{
|
||||||
|
I2Cbus *bus;
|
||||||
|
|
||||||
|
int a10;
|
||||||
|
int addr;
|
||||||
|
int subaddr;
|
||||||
|
ulong size;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register busses (controllers) and devices (addresses)
|
||||||
|
*/
|
||||||
|
extern void addi2cbus(I2Cbus *bus);
|
||||||
|
extern void addi2cdev(I2Cdev *dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Look-up busses and devices by name and address
|
||||||
|
*/
|
||||||
|
extern I2Cbus* i2cbus(char *name);
|
||||||
|
extern I2Cdev* i2cdev(I2Cbus *bus, int addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* generic I/O
|
||||||
|
*/
|
||||||
|
extern int i2cbusio(I2Cbus *bus, uchar *pkt, int olen, int ilen);
|
||||||
|
extern int i2crecv(I2Cdev *dev, void *data, int len, vlong addr);
|
||||||
|
extern int i2csend(I2Cdev *dev, void *data, int len, vlong addr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* common I/O for SMbus
|
||||||
|
*/
|
||||||
|
extern int i2cquick(I2Cdev *dev, int rw);
|
||||||
|
extern int i2crecvbyte(I2Cdev *dev);
|
||||||
|
extern int i2csendbyte(I2Cdev *dev, uchar b);
|
||||||
|
extern int i2creadbyte(I2Cdev *dev, ulong addr);
|
||||||
|
extern int i2cwritebyte(I2Cdev *dev, ulong addr, uchar b);
|
||||||
|
extern int i2creadword(I2Cdev *dev, ulong addr);
|
||||||
|
extern int i2cwriteword(I2Cdev *dev, ulong addr, ushort w);
|
||||||
|
extern vlong i2cread32(I2Cdev *dev, ulong addr);
|
||||||
|
extern vlong i2cwrite32(I2Cdev *dev, ulong addr, ulong u);
|
Loading…
Reference in a new issue