/*
* Ziatech 550x (5503) watchdog driver
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
enum {
Qdir,
Qwatchdog,
};
static Dirtab zt550x_dirtab[] = {
".", { Qdir, 0, QTDIR}, 0, 0555,
"watchdog", { Qwatchdog, 0}, 0, 0600,
};
/* Useful I/O ports */
enum {
IOP_Watchdog = 0x79,
IOP_KBReset = 0x64, /* Keyboard controller reset */
};
/* Useful bitmasks for the Watchdog */
enum {
Watchdog_Enabled = 0x20,
Watchdog_250ms_Timeout = 0x00,
Watchdog_500ms_Timeout = 0x01,
Watchdog_1s_Timeout = 0x02,
Watchdog_8s_Timeout = 0x03,
Watchdog_32s_Timeout = 0x04,
Watchdog_64s_Timeout = 0x05,
Watchdog_128s_Timeout = 0x06,
Watchdog_256s_Timeout = 0x07,
Watchdog_Timeout_Mask = 0x07,
Watchdog_Invalid_Timeout = -1,
ZT550x_Reset_Code = 0xfe,
};
static Timer *watchdog_timer;
struct resolution {
ulong timeout;
ulong resolution;
};
static const char *zt550x_reg2str(ulong);
static struct resolution zt550x_res2res(char *);
void
watchdog_strobe(void)
{
inb(IOP_Watchdog);
}
static void
zt550x_reset(void)
{
/* To reset this board, we need to actually force a reset in
* hardware. I don't know how bad this is. To force this reset
* we can out 0xFEh to 0x64.
*/
outb(IOP_KBReset, ZT550x_Reset_Code);
}
static Chan *
zt550x_attach(char *spec)
{
return devattach('Z', spec);
}
static Walkqid *
zt550x_walk(Chan *c, Chan *nc, char **name, int nname)
{
return devwalk(c, nc, name, nname, zt550x_dirtab, nelem(zt550x_dirtab), devgen);
}
static int
zt550x_stat(Chan *c, uchar *dp, int n)
{
return devstat(c, dp, n, zt550x_dirtab, nelem(zt550x_dirtab), devgen);
}
static Chan *
zt550x_open(Chan *c, int mode)
{
return devopen(c, mode, zt550x_dirtab, nelem(zt550x_dirtab), devgen);
}
static void
zt550x_close(Chan *c)
{
USED(c);
}
static long
zt550x_read(Chan *c, void *a, long n, vlong off)
{
ulong ioreg;
switch((ulong)c->qid.path) {
case Qdir:
return devdirread(c, a, n, zt550x_dirtab, nelem(zt550x_dirtab), devgen);
case Qwatchdog:
ioreg = inb(IOP_Watchdog);
if (ioreg & Watchdog_Enabled)
return readstr(off, a, n, zt550x_reg2str(ioreg));
else
return readstr(off, a, n, "disabled");
default:
n = 0;
break;
}
return n;
}
static long
zt550x_write(Chan *c, void *a, long n, vlong off)
{
ulong ioreg;
struct resolution res;
char buffer[128];
USED(off);
if (n >= sizeof(buffer))
n = sizeof(buffer);
strncpy(buffer, a, n - 1);
buffer[n] = 0;
switch((ulong)c->qid.path) {
case Qwatchdog:
ioreg = inb(IOP_Watchdog);
if (strncmp(buffer, "enable resolution", 17) == 0) {
res = zt550x_res2res(buffer + 17);
if (res.resolution == Watchdog_Invalid_Timeout)
error (Ebadarg);
if (!(ioreg & Watchdog_Enabled)) {
watchdog_timer = addclock0link(watchdog_strobe, res.resolution);
ioreg &= ~Watchdog_Timeout_Mask;
ioreg|= Watchdog_Enabled + res.timeout;
outb(IOP_Watchdog, ioreg);
}
} else if (strncmp(buffer, "disable", 7) == 0) {
if (ioreg & Watchdog_Enabled) {
ioreg &= ~Watchdog_Enabled;
outb(IOP_Watchdog, ioreg);
timerdel(watchdog_timer);
}
}
default:
error(Ebadusefd);
}
return n;
}
static struct resolution
zt550x_res2res(char *buffer)
{
struct resolution r;
if (strncmp(buffer, "250ms", 5) == 0) {
r.timeout = Watchdog_250ms_Timeout;
r.resolution = 250;
} else if (strncmp(buffer, "500ms", 5) == 0) {
r.timeout = Watchdog_500ms_Timeout;
r.resolution = 500;
} else if (strncmp(buffer, "1s", 2) == 0) {
r.timeout = Watchdog_1s_Timeout;
r.resolution = 1000;
} else if (strncmp(buffer, "8s", 2) == 0) {
r.timeout = Watchdog_8s_Timeout;
r.resolution = 8000;
} else if (strncmp(buffer, "32s", 3) == 0) {
r.timeout = Watchdog_32s_Timeout;
r.resolution = 32000;
} else if (strncmp(buffer, "64s", 3) == 0) {
r.timeout = Watchdog_64s_Timeout;
r.resolution = 64000;
} else if (strncmp(buffer, "128s", 4) == 0) {
r.timeout = Watchdog_128s_Timeout;
r.resolution = 128000;
} else if (strncmp(buffer, "256s", 4) == 0) {
r.timeout = Watchdog_256s_Timeout;
r.resolution = 256000;
} else
r.timeout = r.resolution = Watchdog_Invalid_Timeout;
return r;
}
static const char *
zt550x_reg2str(ulong ioreg)
{
ioreg &= ~Watchdog_Timeout_Mask;
switch(ioreg) {
case Watchdog_250ms_Timeout:
return "enabled; resolution 250ms";
case Watchdog_500ms_Timeout:
return "enabled; resolution 500ms";
case Watchdog_1s_Timeout:
return "enabled; resolution 1s";
case Watchdog_8s_Timeout:
return "enabled; resolution 8s";
case Watchdog_32s_Timeout:
return "enabled; resolution 32s";
case Watchdog_64s_Timeout:
return "enabled; resolution 64s";
case Watchdog_128s_Timeout:
return "enabled; resolution 128s";
case Watchdog_256s_Timeout:
return "enabled; resolution 256s";
}
}
Dev zt550xdevtab = {
'Z',
"zt550x",
zt550x_reset,
devinit,
devshutdown,
zt550x_attach,
zt550x_walk,
zt550x_stat,
zt550x_open,
devcreate,
zt550x_close,
zt550x_read,
devbread,
zt550x_write,
devbwrite,
devremove,
devwstat,
};
|