ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /os/pc/devds1620.c/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
enum {
// Ziatech 5512 Digital I/O ASIC register info
PortSelect = 0xE7,
Port = 0xE1,
DQ = 1<<0,
CLK = 1<<1,
RST = 1<<2,
TL = 1<<3,
TH = 1<<4,
// ds1620 Masks
Mread = 0xA0,
Mwrite = 0,
// ds1620 Registers
Rtemp = 0x0A,
Rcounter = 0x00,
Rslope = 0x09,
Rhi = 0x01,
Rlo = 0x02,
Rconfig = 0x0C,
Cdone = 1<<7, // conversion done
Cthf = 1<<6, // temp >= Rhi
Ctlf = 1<<5, // temp <= Rlo
Cnvb = 1<<4, // e^2 nvram busy (write may take up to 10ms)
Ccpu = 1<<1, // cpu use (0=clk starts conversion when rst lo)
C1shot = 1<<0, // perform one conversion then stop
// ds1620 Commands
Startconv = 0xEE,
Stopconv = 0x22,
ALOTEMP = 0,
AHITEMP = 1,
};
#define send(v) outb(Port, v); delay(1)
#define recv() (!(inb(Port) & 1))
enum {
Qdir = 0,
Qtemp,
Qalarm,
};
Dirtab ds1620tab[]={
"temp", {Qtemp, 0}, 0, 0666,
"alarm", {Qalarm, 0}, 0, 0444,
};
typedef struct Temp Temp;
struct Temp
{
Lock;
int lo;
int cur;
int hi;
int alo;
int ahi;
int atime;
Queue *aq;
};
static Temp t;
static void
sendreg(int r)
{
int d, i;
r = ~r;
for(i=0;i<8;i++) {
d = (r >> i) & 1;
send(CLK|d);
send(d);
send(CLK);
}
}
static int
ds1620rdreg(int r, int nb)
{
int i, s;
s = splhi();
outb(PortSelect, 0);
send(RST|CLK);
sendreg(r|Mread);
r = 0;
for(i=0; i < nb; i++) {
r |= recv() << i;
delay(1);
send(0);
send(CLK);
}
send(RST);
splx(s);
return r;
}
static void
ds1620wrreg(int r, int v, int nb)
{
int d, i, s;
s = splhi();
outb(PortSelect, 0);
send(RST|CLK);
sendreg(r|Mwrite);
v = ~v;
for(i=0; i < nb; i++) {
d = (v >> i) & 1;
send(CLK|d);
send(0);
send(CLK);
}
send(RST);
splx(s);
}
static void
ds1620cmd(int r)
{
int s;
s = splhi();
outb(PortSelect, 0);
send(RST|CLK);
sendreg(r);
send(RST);
splx(s);
}
static char*
t2s(int t)
{
static char s[16];
sprint(s, "%4d.", t>>1);
if(t&1)
strcat(s, "5");
else
strcat(s, "0");
return s;
}
static int
s2t(char *s)
{
int v;
char *p;
p = strchr(s, '.');
if(p != nil)
*p++ = '\0';
v = strtoul(s, nil, 0);
v <<= 1;
if(p != nil && *p != '\0' && *p >= '5')
v |= 1;
return v;
}
static void
alarm(int code, Temp *tt)
{
char buf[256], *end;
int s;
s = seconds();
if(s - tt->atime < 60)
return;
tt->atime = s;
end = buf;
end += sprint(buf, "(alarm) %8.8uX %uld temp ", code, seconds());
switch(code) {
case ALOTEMP:
end += sprint(end, "%s below threshold ", t2s(tt->lo));
end += sprint(end, "%s.\n", t2s(tt->alo));
break;
case AHITEMP:
end += sprint(end, "%s above threshold ", t2s(tt->hi));
end += sprint(end, "%s.\n", t2s(tt->ahi));
break;
}
qproduce(tt->aq, buf, end-buf);
}
void
tmon(void *a)
{
int r;
Temp *t;
t = a;
r = ds1620rdreg(Rtemp, 9);
lock(t);
t->lo = t->cur = t->hi = r;
unlock(t);
for(;;) {
tsleep(&up->sleep, return0, nil, 1000);
r = ds1620rdreg(Rtemp, 9);
lock(t);
t->cur = r;
if(r < t->lo)
t->lo = r;
if(r > t->hi)
t->hi = r;
if(t->lo < t->alo)
alarm(ALOTEMP, t);
if(t->hi > t->ahi)
alarm(AHITEMP, t);
unlock(t);
}
pexit("", 0);
}
static void
ds1620init(void)
{
int r;
t.aq = qopen(8*1024, Qmsg, nil, nil);
if(t.aq == nil)
error(Enomem);
ds1620wrreg(Rconfig, Ccpu, 8); // continuous sample mode
ds1620cmd(Startconv);
r = ds1620rdreg(Rtemp, 9);
t.alo = ds1620rdreg(Rlo, 9);
t.ahi = ds1620rdreg(Rhi, 9);
print("#L: temp %s (c) ", t2s(r));
print("low threshold %s (c) ", t2s(t.alo));
print("high threshold %s (c)\n", t2s(t.ahi));
kproc("tempmon", tmon, &t, 0);
}
static Chan*
ds1620attach(char *spec)
{
return devattach('L', spec);
}
static int
ds1620walk(Chan *c, char* name)
{
return devwalk(c, name, ds1620tab, nelem(ds1620tab), devgen);
}
static void
ds1620stat(Chan *c, char* db)
{
ds1620tab[1].length = qlen(t.aq);
devstat(c, db, ds1620tab, nelem(ds1620tab), devgen);
}
static Chan*
ds1620open(Chan *c, int omode)
{
return devopen(c, omode, ds1620tab, nelem(ds1620tab), devgen);
}
static void
ds1620close(Chan*)
{
}
static long
ds1620read(Chan *c, void *a, long n, vlong offset)
{
Temp tt;
char buf[64];
char *s;
if(c->qid.path & CHDIR)
return devdirread(c, a, n, ds1620tab, nelem(ds1620tab), devgen);
buf[0] = 0;
switch(c->qid.path) {
case Qtemp:
lock(&t);
tt = t;
unlock(&t);
s = buf;
s+= sprint(s, "%s ", t2s(tt.lo));
s+= sprint(s, "%s ", t2s(tt.cur));
s+= sprint(s, "%s ", t2s(tt.hi));
s+= sprint(s, "%s ", t2s(tt.alo));
sprint(s, "%s", t2s(tt.ahi));
return readstr(offset, a, n, buf);
case Qalarm:
return qread(t.aq, a, n);
default:
error(Egreg);
return 0;
}
}
static long
ds1620write(Chan *c, void *a, long n, vlong)
{
char buf[64];
char *f[2];
int lo, hi;
int nf;
if(c->qid.path & CHDIR)
error(Eperm);
if(c->qid.path == Qtemp) {
if(n >= sizeof(buf))
n = sizeof(buf)-1;
memmove(buf, a, n);
buf[n] = '\0';
nf = getfields(buf, f, 2, 1, " \t");
if(nf != 2)
error(Ebadarg);
lo = s2t(f[0]);
hi = s2t(f[1]);
lock(&t);
t.alo = lo;
t.ahi = hi;
t.atime = 0;
ds1620wrreg(Rlo, lo, 9);
delay(1);
ds1620wrreg(Rhi, hi, 9);
unlock(&t);
return n;
} else
error(Eio);
return 0;
}
Dev ds1620devtab = {
'L',
"ds1620",
devreset,
ds1620init,
ds1620attach,
devdetach,
devclone,
ds1620walk,
ds1620stat,
ds1620open,
devcreate,
ds1620close,
ds1620read,
devbread,
ds1620write,
devbwrite,
devremove,
devwstat,
};