Plan 9 from Bell Labs’s /usr/web/sources/contrib/tristan/kw/devtwsi.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


/*
 *	kirkwood TWSI driver
 */

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"../port/error.h"
#include	"io.h"

enum
{
	Qdir		= 0,
	Qtwsi,

	Isleep = 1000000,
};

struct kw_twsi
{
	ulong slave_addr;
	ulong data;
	ulong control;
	union {
		ulong status;	/* read only */
		ulong rate;	/* write only */
	};
	ulong slave_addr_ext;
	ulong soft_reset;
	ulong last_data;
};

enum
{
	TWSI_do_write,
	TWSI_do_read,

	TWSI_ack = 1<<2,
	TWSI_int = 1<<3,
	TWSI_stop = 1<<4,
	TWSI_start = 1<<5,
	TWSI_slave_en = 1<<6,
	TWSI_int_en = 1<<7,

	TWSI_bus_error = 0x0,
	TWSI_started = 0x08,
	TWSI_write_ack = 0x18,
	TWSI_addr2_write_ack = 0xd0,
	TWSI_write_data_ack = 0x28,
	TWSI_read_ack = 0x40,
	TWSI_addr2_read_ack = 0xe0,
	TWSI_read_data_ack = 0x50,
	TWSI_read_data_noack = 0x58,
};

struct
{
	QLock;
	Rendez vous;
	struct kw_twsi *reg;
	int done;
	void (*handle)(void);
	ulong addr, len, off;
	uchar *buf;
} twsi;

Dirtab
twsidir[] =
{
	".",	{Qdir, 0, QTDIR},		0,	DMDIR|0555,
	"twsi",	{Qtwsi},		0,	0660,
};

static int
twsidone(void *){
	return twsi.done;
}

uchar readbuf[256];

static void
twsi_handle_read(void){
	switch(twsi.reg->status){
	case TWSI_started:
		twsi.reg->data = twsi.addr << 1 | TWSI_do_read;
		break;
	case TWSI_read_ack:
		twsi.reg->control |= TWSI_ack;
		break;
	case TWSI_read_data_ack:
		/* if this goes straight into the first buffer, it faults */
		readbuf[twsi.off] = (uchar)twsi.reg->data;
		twsi.off++;
		if(twsi.off != twsi.len)
			twsi.reg->control |= TWSI_ack;
		else
			twsi.reg->control &= ~(TWSI_ack);
		break;
	default:
		error("abnormal status\n");
	case TWSI_read_data_noack:
		twsi.handle = nil;
		twsi.done = 1;
		wakeup(&twsi.vous);
		twsi.reg->control |= TWSI_stop;
		break;
	}
}

static long
twsi_run(uchar  *buf, ulong len, ulong addr){
	qlock(&twsi);
	twsi.addr = addr;
	twsi.len = len;
	twsi.done = 0;
	twsi.off = 0;
	twsi.reg->control = ~TWSI_int & twsi.reg->control | TWSI_start;
	tsleep(&twsi.vous, twsidone, 0, Isleep);
	if(twsi.done == 0) {
		twsi.handle=nil;
		error("timeout");
	}
	for(len=0; len < twsi.off; len++)
		buf[len]=readbuf[len];
	qunlock(&twsi);
	return len;
}

long
twsi_read(uchar  *buf, ulong len, ulong addr){
	twsi.handle = twsi_handle_read;
	return twsi_run(buf, len, addr);
}

static void
interrupt(Ureg *, void */*arg*/)
{
/*	iprint("µs %lud data %lux control %lux status %lux off %lux\n",
		µs(), twsi.reg->data, twsi.reg->control, twsi.reg->status, twsi.off);*/
	if(twsi.handle)
		twsi.handle();
	else
		twsi.reg->control |= TWSI_stop;
	twsi.reg->control &= ~TWSI_int;
	intrclear(Irqlo, IRQ0twsi);
}

static void
twsiinit(void)
{
	twsi.reg = (void *)AddrTwsi;
	twsi.reg->control &= ~(TWSI_int);
	twsi.reg->control |= TWSI_int_en;
	twsi.reg->rate = 4 << 3 | 4;	/* Tclk / (10 * 5 * 2⁵) = 1/8 Mhz */
	twsi.handle = nil;
	intrenable(Irqlo, IRQ0twsi, interrupt, nil, "TWSI");
}

static void
twsideinit(void) {
	intrdisable(Irqlo, IRQ0twsi, interrupt, nil, "TWSI");
}

static Chan*
twsiattach(char *param)
{
	return devattach(L'⁲', param);
}

static Walkqid*
twsiwalk(Chan *c, Chan *nc, char **name, int nname)
{
	return devwalk(c, nc, name, nname, twsidir, nelem(twsidir), devgen);
}

static int
twsistat(Chan *c, uchar *db, int n)
{
	return devstat(c, db, n, twsidir, nelem(twsidir), devgen);
}

static Chan*
twsiopen(Chan *c, int omode)
{
	switch((ulong)c->qid.path) {
	default:
		error(Eperm);
		break;
	case Qdir:
		break;
	case Qtwsi:
		break;
	}
	c = devopen(c, omode, twsidir, nelem(twsidir), devgen);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;

	return c;
}

static void
twsiclose(Chan *c)
{
	switch((ulong)c->qid.path) {
	default:
		error(Eperm);
		break;
	case Qtwsi:
		break;
	}
}

static long
twsiread(Chan *c, void *v, long n, vlong off)
{
	switch((ulong)c->qid.path) {
	default:
		error(Eperm);
		break;
	case Qdir:
		return devdirread(c, v, n, twsidir, nelem(twsidir), devgen);
	case Qtwsi:
		return twsi_read(v, n, off);
		break;
	}
	return 0;
}

static long
twsiwrite(Chan *c, void *v, long n, vlong off)
{
	switch((ulong)c->qid.path) {
	default:
		error(Eperm);
		break;
	case Qtwsi:
//		return twsi_write(v, n, off);
		break;
	}
	return 0;
}

Dev twsidevtab = {
	L'⁲',
	"twsi",

	devreset,
	twsiinit,
	devshutdown,
	twsiattach,
	twsiwalk,
	twsistat,
	twsiopen,
	devcreate,
	twsiclose,
	twsiread,
	devbread,
	twsiwrite,
	devbwrite,
	devremove,
	devwstat,
};

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to [email protected].