ref: 27a696b145c85e7d892e97a5819565d298c8cd2b
dir: /sys/src/9/bcm64/i2cbcm.c/
/* * bcm2835 i2c controller * * Only i2c1 is supported. * i2c2 is reserved for HDMI. * i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards) * * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254 */ #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" #define I2CREGS (VIRTIO+0x804000) #define SDA0Pin 2 #define SCL0Pin 3 #define Alt0 0x4 typedef struct Ctlr Ctlr; typedef struct Bsc Bsc; /* * Registers for Broadcom Serial Controller (i2c compatible) */ struct Bsc { u32int ctrl; u32int stat; u32int dlen; u32int addr; u32int fifo; u32int clkdiv; /* default 1500 => 100 KHz assuming 150Mhz input clock */ u32int delay; /* default (48<<16)|48 falling:rising edge */ u32int clktimeout; /* default 64 */ }; /* * Per-controller info */ struct Ctlr { QLock lock; Bsc *regs; Rendez r; }; static Ctlr ctlr; enum { /* ctrl */ I2cen = 1<<15, /* I2c enable */ Intr = 1<<10, /* interrupt on reception */ Intt = 1<<9, /* interrupt on transmission */ Intd = 1<<8, /* interrupt on done */ Start = 1<<7, /* aka ST, start a transfer */ Clear = 1<<4, /* clear fifo */ Read = 1<<0, /* read transfer */ Write = 0<<0, /* write transfer */ /* stat */ Clkt = 1<<9, /* clock stretch timeout */ Err = 1<<8, /* NAK */ Rxf = 1<<7, /* RX fifo full */ Txe = 1<<6, /* TX fifo full */ Rxd = 1<<5, /* RX fifo has data */ Txd = 1<<4, /* TX fifo has space */ Rxr = 1<<3, /* RX fiio needs reading */ Txw = 1<<2, /* TX fifo needs writing */ Done = 1<<1, /* transfer done */ Ta = 1<<0, /* Transfer active */ }; static void i2cinterrupt(Ureg*, void*) { Bsc *r; int st; r = ctlr.regs; st = 0; if((r->ctrl & Intr) && (r->stat & Rxd)) st |= Intr; if((r->ctrl & Intt) && (r->stat & Txd)) st |= Intt; if(r->stat & Done) st |= Intd; if(st){ r->ctrl &= ~st; wakeup(&ctlr.r); } } static int i2cready(void *st) { return (ctlr.regs->stat & (uintptr)st); } static int i2cinit(I2Cbus*) { ctlr.regs = (Bsc*)I2CREGS; ctlr.regs->clkdiv = 2500; gpiosel(SDA0Pin, Alt0); gpiosel(SCL0Pin, Alt0); gpiopullup(SDA0Pin); gpiopullup(SCL0Pin); intrenable(IRQi2c, i2cinterrupt, nil, BUSUNKNOWN, "i2c"); return 0; } /* * Basic I²C driver for Raspberry Pi * subaddressing wasn't reliable, so it is just not allowed * * 10 bit addressing is also disabled. */ static int i2cio(I2Cdev *dev, uchar *pkt, int olen, int ilen) { Bsc *r; uchar *p; int st; int o; //print("enter io\n"); // r->ctrl = 0; /* Shutdown ctrl incase it was left in bad state */ /* arguements from Miller's i2cio() */ int rw, len; uint addr; o = 0; if(dev->subaddr > 0){ //print("subaddr\n"); return -1; } if((pkt[0] & 0xF8) == 0xF0){ /* b11110xxx reserved for 10bit addressing*/ //print("10bit\n"); r->ctrl = 0; return -1; } rw = pkt[0] & 1; /* rw bit is first bit of pkt[0], read == 1 */ addr = dev->addr; pkt++; /* move past device addr packet */ o++; /* have to atleast return processing the dev addr */ //print("addr=%ux rw=%d olen=%d ilen=%d\n", addr, rw, olen, ilen); /* * If 9Front is just running a probe * return 1, * else the controller throws an NAK error * when doing a write with just the dev addr */ if((olen == 1) && (ilen == 0)){ return 1; } qlock(&ctlr.lock); r = ctlr.regs; r->ctrl = I2cen | Clear; r->addr = addr; r->stat = Clkt|Err|Done; len = (olen - 1) + ilen; r->dlen = len; r->ctrl = I2cen | Start | Intd | rw; //print("len=%d\n", len); p = pkt; st = rw == Read? Rxd : Txd; while(len > 0){ while((r->stat & (st|Done)) == 0){ r->ctrl |= rw == Read? Intr : Intt; sleep(&ctlr.r, i2cready, (void*)(st|Done)); } if(r->stat & (Err|Clkt)){ qunlock(&ctlr.lock); //print("error1\n"); r->ctrl = 0; return -1; } if(rw == Read){ do{ *p++ = r->fifo; len--; o++; }while ((r->stat & Rxd) && len > 0); }else{ do{ r->fifo = *p++; len--; o++; }while((r->stat & Txd) && len > 0); } } while((r->stat & Done) == 0) sleep(&ctlr.r, i2cready, (void*)Done); if(r->stat & (Err|Clkt)){ qunlock(&ctlr.lock); //print("error2 %ux\n", r->stat); r->ctrl = 0; return -1; } r->ctrl = 0; qunlock(&ctlr.lock); return o; } void i2cbcmlink(void) { static I2Cbus i2c = {"i2c", 400000, &ctlr, i2cinit, i2cio}; addi2cbus(&i2c); }