Plan 9 from Bell Labs’s /n/sources/contrib/miller/9/bcm/i2c.c

Copyright © 2009 Alcatel-Lucent.
Distributed under the Lucent Public License version 1.02.
Download the Plan 9 distribution.


/*
 * 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)
 */

#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
#define	Alt0	0x4

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,
	Intr	= 1<<10,
	Intt	= 1<<9,
	Intd	= 1<<8,
	Start	= 1<<7,
	Clear	= 1<<4,
	Read	= 1<<0,
	Write	= 0<<0,

	/* stat */
	Clkt	= 1<<9,
	Err	= 1<<8,
	Rxf	= 1<<7,
	Txe	= 1<<6,
	Rxd	= 1<<5,
	Txd	= 1<<4,
	Rxr	= 1<<3,
	Txw	= 1<<2,
	Done	= 1<<1,
	Ta	= 1<<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, Alt0);
	gpiosel(SCL0Pin, Alt0);
	gpiopullup(SDA0Pin);
	gpiopullup(SCL0Pin);
	intrenable(IRQi2c, i2cinterrupt, 0, 0, "i2c");
}

static void
i2cio(int rw, uint addr, void *buf, int len)
{
	Bsc *r;
	uchar *p;
	int st;

	qlock(&i2c.lock);
	if(i2c.regs == 0)
		i2cinit();
	r = i2c.regs;
	p = buf;
	r->ctrl = I2cen | Clear;
	r->addr = addr;
	r->dlen = len;
	r->stat = Clkt|Err|Done;
	r->ctrl = I2cen | Start | Intd | rw;
	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
i2cread(uint addr, void *buf, int len)
{
	i2cio(Read, addr, buf, len);
}

void
i2cwrite(uint addr, void *buf, int len)
{
	i2cio(Write, addr, buf, len);
}

(This is a user-contributed directory and
should not be interpreted as statement by Alcatel-Lucent.)

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2009 Alcatel-Lucent. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.