Input and Output are just too generic for public header files, so renamed to prefix with Gpio. this fits with the existing style. also, remove conflicting #define of Alt0. Reference: /n/atom/patch/applied/pigpionames Date: Mon Jan 4 03:11:05 CET 2016 Signed-off-by: quanstro@quanstro.net --- /sys/src/9/bcm/devgpio.c Mon Jan 4 03:09:59 2016 +++ /sys/src/9/bcm/devgpio.c Mon Jan 4 03:10:00 2016 @@ -44,8 +44,8 @@ static char *funcs[] = { "in", "out", "alt5", "alt4", "alt0", "alt1", "alt2", "alt3", "pulse"}; -static int ifuncs[] = { Input, Output, Alt5, Alt4, Alt0, - Alt1, Alt2, Alt3, -1}; +static int ifuncs[] = { GpioInput, GpioOutput, GpioAlt5, GpioAlt4, GpioAlt0, + GpioAlt1, GpioAlt2, GpioAlt3, -1}; static Chan* gpioattach(char* spec) @@ -115,9 +115,9 @@ if(i >= nelem(funcs)) error(Ebadctl); if(ifuncs[i] == -1) { - gpiosel(pin, Output); + gpiosel(pin, GpioOutput); microdelay(2); - gpiosel(pin, Input); + gpiosel(pin, GpioInput); } else { gpiosel(pin, ifuncs[i]); --- /sys/src/9/bcm/i2c.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/i2c.c Mon Jan 4 03:10:01 2016 @@ -0,0 +1,236 @@ +/* + * 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" + +#define I2CREGS (VIRTIO+0x804000) +#define SDA0Pin 2 +#define SCL0Pin 3 + +typedef struct I2c I2c; +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 I2c { + QLock lock; + Lock reglock; + Rendez r; + Bsc *regs; +}; + +static I2c i2c; + +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 */ + + /* maximum I2C I/O (can change) */ + MaxIO = 128, + MaxSA = 2, /* longest subaddress */ + Bufsize = (MaxIO+MaxSA+1+4)&~3, /* extra space for subaddress/clock bytes and alignment */ + + Chatty = 0, +}; + +static void +i2cinterrupt(Ureg*, void*) +{ + Bsc *r; + int st; + + r = i2c.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(&i2c.r); + } +} + +static int +i2cready(void *st) +{ + return (i2c.regs->stat & (uintptr)st); +} + +static void +i2cinit(void) +{ + i2c.regs = (Bsc*)I2CREGS; + i2c.regs->clkdiv = 2500; + + gpiosel(SDA0Pin, GpioAlt0); + gpiosel(SCL0Pin, GpioAlt0); + gpiopullup(SDA0Pin); + gpiopullup(SCL0Pin); + + intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c"); +} + +/* + * To do subaddressing avoiding a STOP condition between the address and payload. + * - write the subaddress, + * - poll until the transfer starts, + * - overwrite the registers for the payload transfer, before the subaddress + * transaction has completed. + * + * FIXME: neither 10bit adressing nor 100Khz transfers implemented yet. + */ +static void +i2cio(int rw, int tenbit, uint addr, void *buf, int len, int salen, uint subaddr) +{ + Bsc *r; + uchar *p; + int st; + + if(tenbit) + error("10bit addressing not supported"); + if(salen == 0 && subaddr) /* default subaddress len == 1byte */ + salen = 1; + + qlock(&i2c.lock); + r = i2c.regs; + r->ctrl = I2cen | Clear; + r->addr = addr; + r->stat = Clkt|Err|Done; + + if(salen){ + r->dlen = salen; + r->ctrl = I2cen | Start | Write; + while((r->stat & Ta) == 0) { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + } + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + for(; salen > 0; salen--) + r->fifo = subaddr >> ((salen-1)*8); + /* + * Adapted from Linux code...uses undocumented + * status information. + */ + if(rw == Read) { + do { + if(r->stat & (Err|Clkt)) { + qunlock(&i2c.lock); + error(Eio); + } + st = r->stat >> 28; + } while(st != 0 && st != 4 && st != 5); + } + } + else { + r->dlen = len; + r->ctrl = I2cen | Start | Intd | rw; + } + + p = buf; + st = rw == Read? Rxd : Txd; + while(len > 0){ + while((r->stat & (st|Done)) == 0){ + r->ctrl |= rw == Read? Intr : Intt; + sleep(&i2c.r, i2cready, (void*)(st|Done)); + } + if(r->stat & (Err|Clkt)){ + qunlock(&i2c.lock); + error(Eio); + } + if(rw == Read){ + do{ + *p++ = r->fifo; + len--; + }while ((r->stat & Rxd) && len > 0); + }else{ + do{ + r->fifo = *p++; + len--; + }while((r->stat & Txd) && len > 0); + } + } + while((r->stat & Done) == 0) + sleep(&i2c.r, i2cready, (void*)Done); + if(r->stat & (Err|Clkt)){ + qunlock(&i2c.lock); + error(Eio); + } + r->ctrl = 0; + qunlock(&i2c.lock); +} + + +void +i2csetup(int ) +{ + print("i2csetup\n"); + i2cinit(); +} + +long +i2csend(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Write, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} + +long +i2crecv(I2Cdev *d, void *buf, long len, ulong offset) +{ + i2cio(Read, d->tenbit, d->addr, buf, len, d->salen, offset); + return len; +} + --- /sys/src/9/bcm/spi.c Thu Jan 1 00:00:00 1970 +++ /sys/src/9/bcm/spi.c Mon Jan 4 03:10:01 2016 @@ -0,0 +1,147 @@ +/* + * bcm2835 spi controller + */ + +#include "u.h" +#include "../port/lib.h" +#include "../port/error.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" + +#define SPIREGS (VIRTIO+0x204000) +#define SPI0_CE1_N 7 /* P1 pin 26 */ +#define SPI0_CE0_N 8 /* P1 pin 24 */ +#define SPI0_MISO 9 /* P1 pin 21 */ +#define SPI0_MOSI 10 /* P1 pin 19 */ +#define SPI0_SCLK 11 /* P1 pin 23 */ + +typedef struct Ctlr Ctlr; +typedef struct Spiregs Spiregs; + +/* + * Registers for main SPI controller + */ +struct Spiregs { + u32int cs; /* control and status */ + u32int data; + u32int clkdiv; + u32int dlen; + u32int lossitoh; + u32int dmactl; +}; + +/* + * Per-controller info + */ +struct Ctlr { + Spiregs *regs; + QLock lock; + Lock reglock; + Rendez r; +}; + +static Ctlr spi; + +enum { + /* cs */ + Lossi32bit = 1<<25, + Lossidma = 1<<24, + Cspol2 = 1<<23, + Cspol1 = 1<<22, + Cspol0 = 1<<21, + Rxf = 1<<20, + Rxr = 1<<19, + Txd = 1<<18, + Rxd = 1<<17, + Done = 1<<16, + Lossi = 1<<13, + Ren = 1<<12, + Adcs = 1<<11, /* automatically deassert chip select (dma) */ + Intr = 1<<10, + Intd = 1<<9, + Dmaen = 1<<8, + Ta = 1<<7, + Cspol = 1<<6, + Rxclear = 1<<5, + Txclear = 1<<4, + Cpol = 1<<3, + Cpha = 1<<2, + Csmask = 3<<0, + Csshift = 0, + + /* dmactl */ + Rpanicshift = 24, + Rdreqshift = 16, + Tpanicshift = 8, + Tdreqshift = 0, +}; + +static void +spiinit(void) +{ + spi.regs = (Spiregs*)SPIREGS; + spi.regs->clkdiv = 250; /* 1 MHz */ + gpiosel(SPI0_MISO, GpioAlt0); + gpiosel(SPI0_MOSI, GpioAlt0); + gpiosel(SPI0_SCLK, GpioAlt0); + gpiosel(SPI0_CE0_N, GpioAlt0); + gpiosel(SPI0_CE1_N, GpioAlt0); +} + +void +spimode(int mode) +{ + spi.regs->cs = (spi.regs->cs & ~(Cpha | Cpol)) | (mode << 2); +} + +/* + * According the Broadcom docs, the divisor has to + * be a power of 2, but an errata says that should + * be multiple of 2 and scope observations confirm + * that restricting it to a power of 2 is unnecessary. + */ +void +spiclock(uint mhz) +{ + if(spi.regs == 0) + spiinit(); + if(mhz == 0) { + spi.regs->clkdiv = 32768; /* about 8 KHz */ + return; + } + spi.regs->clkdiv = 2 * ((125 + (mhz - 1)) / mhz); +} + +void +spirw(uint cs, void *buf, int len) +{ + Spiregs *r; + + assert(cs <= 2); + assert(len < (1<<16)); + qlock(&spi.lock); + if(waserror()){ + qunlock(&spi.lock); + nexterror(); + } + if(spi.regs == 0) + spiinit(); + r = spi.regs; + r->dlen = len; + r->cs = (cs << Csshift) | Rxclear | Txclear | Dmaen | Adcs | Ta; + /* + * Start write channel before read channel - cache wb before inv + */ + dmastart(DmaChanSpiTx, DmaDevSpiTx, DmaM2D, + buf, &r->data, len); + dmastart(DmaChanSpiRx, DmaDevSpiRx, DmaD2M, + &r->data, buf, len); + if(dmawait(DmaChanSpiRx) < 0) + error(Eio); + cachedinvse(buf, len); + r->cs = 0; + qunlock(&spi.lock); + poperror(); +} --- /sys/src/9/bcm/io.h Mon Jan 4 03:10:03 2016 +++ /sys/src/9/bcm/io.h Mon Jan 4 03:10:05 2016 @@ -55,14 +55,14 @@ ClkPwm, /* gpio */ - Input = 0x0, - Output = 0x1, - Alt0 = 0x4, - Alt1 = 0x5, - Alt2 = 0x6, - Alt3 = 0x7, - Alt4 = 0x3, - Alt5 = 0x2, + GpioInput = 0x0, + GpioOutput = 0x1, + GpioAlt0 = 0x4, + GpioAlt1 = 0x5, + GpioAlt2 = 0x6, + GpioAlt3 = 0x7, + GpioAlt4 = 0x3, + GpioAlt5 = 0x2, TempCpu = 0, }; --- /sys/src/9/bcm/uartmini.c Mon Jan 4 03:10:07 2016 +++ /sys/src/9/bcm/uartmini.c Mon Jan 4 03:10:08 2016 @@ -98,8 +98,8 @@ ap = (u32int*)uart->regs; delay(10); - gpiosel(TxPin, Alt5); - gpiosel(RxPin, Alt5); + gpiosel(TxPin, GpioAlt5); + gpiosel(RxPin, GpioAlt5); gpiopulloff(TxPin); gpiopulloff(RxPin); ap[Enables] |= UartEn; @@ -359,7 +359,7 @@ if(p == nil) p = getconf("bcm2708.disk_led_active_low"); polarity = (p == nil || *p == '1'); - gpiosel(okled, Output); + gpiosel(okled, GpioOutput); } gpioout(okled, on^polarity); }