/* * 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, };