--- /sys/src/9/pc/winbond.c Fri Feb 19 20:41:06 2016 +++ /sys/src/9/pc/winbond.c Fri Feb 19 00:00:00 2016 @@ -0,0 +1,635 @@ +/* + * winbond 82567hg &c hw monitors + * copyright © 2008 erik quanstrom, + * coraid, inc. + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "io.h" +#include "ureg.h" +#include "../port/error.h" + +enum{ + Stemp = 1<<0, + Temp = 1<<1, + Fans = 1<<2, + Dog = 1<<3, + Volts = 1<<4, + Beep = 1<<5, + + Maxfans = 10, + + /* below 0x30 are available through any fn */ + Id = 0x20, + Rev = 0x21, + Wdog = 0x08, + Hwmon = 0x0b, + + /* dog registers */ + Dscale = 0xf5, + Dogto = 0xf6, + Dogst = 0xf7, /* 0x10 force to, 0x08 timed out */ + + /* hw monitor function registers */ + Hwstat = 0x30, + + /* hw monitor io port */ + Index = 0x2e, + Data = 0x2f, + Cfg = 0x40, + Bank = 0x4e, + Vbat = 0x5d, + + /* fans registers */ + Fandiv1 = 0x47, + Pinctl = 0x4b, + Diode = 0x59, + + Syst = 0x27, + Cputin = 0x150, + Cputoff = 0x455, + Beepctl = 0x453, +}; + +static Lock wblock; +static int hwaddr; +static uchar cap; +static uint fandiv[Maxfans]; +static uint vid; +static uint vidµv; + +static uchar +wbread(int reg) +{ + outb(Index, reg); + return inb(Data); +} + +static ushort +wbreadw(int reg) +{ + return wbread(reg)<<8 | wbread(reg + 1); +} + +static void +wbwrite(int r, ushort u) +{ + outb(Index, r); + outb(Data, u); +} + +static void +selfn(int n) +{ + lock(&wblock); + outb(Index, 0x87); + outb(Index, 0x87); + wbwrite(7, n); +} + +static void +release(void) +{ + unlock(&wblock); +} + +static ushort +hwread0(int r) +{ + while(inb(hwaddr + 5) & 0x80) + ; + outb(hwaddr + 5, r); + return inb(hwaddr + 6); +} + +static void +hwwrite0(int r, ushort v) +{ + while(inb(hwaddr + 5) & 0x80) + ; + outb(hwaddr + 5, r); + outb(hwaddr + 6, v); +} + +static ushort +banksw(int r) +{ + int bank; + + bank = r>>8; + r &= 0xff; + outb(hwaddr + 5, Bank); + outb(hwaddr + 6, 0x80 | bank); + return r; +} + +static ushort +hwread(int r) +{ + return hwread0(banksw(r)); +} + +static void +hwwrite(int r, ushort v) +{ + if(v & 0xff00){ + hwwrite0(banksw(r), v>>8); + r++; + } + hwwrite0(banksw(r), v); +} + +static ushort +hwreadw(int r) +{ + return hwread(r)<<8 | hwread(r + 1); +} + +static long +wbtemp(Chan*, void *a, long n, vlong offset) +{ + char buf[64], *p, *e; + short s, i; + + selfn(Hwmon); + s = hwread(Syst); + release(); + p = buf; + e = p + sizeof buf; + for(i = 0; i < conf.nmach; i++) + p = seprint(p, e, "%d 1\n", s); + return readstr(offset, a, n, buf); +} + +int +fanspeed(int i, ushort v) +{ + if(v == 0) + return fandiv[i]/76; + if(v == 255) + return 0; + return fandiv[i]/v; +} + +static int fantab[] = {0x28, 0x29, 0x2a, 0x3f, 0x553}; +static long +wbfans(Chan*, void *a, long n, vlong offset) +{ + char buf[256], *p, *e; + uint s[Maxfans], i; + + selfn(Hwmon); + for(i = 0; i < nelem(fantab); i++) + s[i] = fanspeed(i, hwread(fantab[i])); + release(); + p = buf; + e = p + sizeof buf; + for(i = 0; i < nelem(fantab); i++) + p = seprint(p, e, "%d\n", s[i]); + return readstr(offset, a, n, buf); +} + +static void +cfandiv(void) +{ + uint u, i; + + u = hwread(Fandiv1); + fandiv[0] = (u & 0x30) >> 4; + fandiv[1] = (u & 0xc0) >> 6; + u = hwread(Pinctl); + fandiv[2] = (u & 0xc0) >> 6; + u = hwread(Vbat); + for(i = 0; i < 3; i++){ + fandiv[i] |= (u >> i+3) & 4; + fandiv[i] = 1< 180){ + wbwrite(Dscale, 8); + t /= 60; + }else + wbwrite(Dscale, 0); + wbwrite(Dogto, t); + release(); +} + +static long +wbdogw(Chan*, void *a, long n, vlong) +{ + char c, *s; + Cmdbuf *cb; + Cmdtab *ct; + + cb = parsecmd(a, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, dogcmd, nelem(dogcmd)); + switch(ct->index){ + case 0: + dogto = strtoul(cb->f[1], &s, 0); + while(c = *s++)switch(c){ + case 's': + break; + case 'm': + dogto *= 60; + break; + default: + error(Ebadarg); + } + if(dogto < 0 || dogto > 200*60) + error(Ebadarg); + break; + case 2: + dogto = 0; + break; + } + poperror(); + free(cb); + + setdog(); + + return n; +} + +enum{ + V3, + V12, + V5, + Vvsb, + V5n, + V12n, + Vcorea, + Vcoreb, + Vb, + Vmax, + V18, + V15, +}; + +static char *vname[] = { +[V3] "+3.3", +[Vvsb] "vsb", +[V5] "+5", +[V12] "+12", +[V5n] "-5", +[V12n] "-12", +[Vcorea] "Vcorea", +[Vcoreb] "Vcoreb", +[Vb] "Vbat", +[V15] "+1.5", +[V18] "+1.8", +}; + +static int volt[Vmax][4] = { + {V3, 0x30, 0x22, 0x2f}, + {Vvsb, 0x555, 0x550, 0x554}, + {V5, 0x32, 0x23, 0x31}, + {V12, 0x34, 0x24, 0x33}, + {V5n, 0x38, 0x26, 0x37}, + {V12n, 0x36, 0x25, 0x35}, + {Vcorea, 0x2c, 0x20, 0x2b}, + {Vcoreb, 0x2e, 0x21, 0x2d}, + {Vb, 0x557, 0x551, 0x556}, +}; + +static int smvolt[Vmax][4] = { + {V12n, 0x30, 0x22, 0x2f}, /* should be V3 */ + {Vvsb, 0x555, 0x550, 0x554}, + {V5, 0x32, 0x23, 0x31}, + {V12, 0x34, 0x24, 0x33}, + {V18, 0x38, 0x26, 0x37}, /* should be V5n */ + {V15, 0x36, 0x25, 0x35}, /* should be V12n */ + {Vcorea, 0x2c, 0x20, 0x2b}, + {Vcoreb, 0x2e, 0x21, 0x2d}, + {Vb, 0x557, 0x551, 0x556}, +}; + +static int smpvolt[Vmax][4] = { + {Vcorea, 0x20, 0x20, 0x20}, + {V12, 0x21, 0x21, 0x21}, + {V3, 0x22, 0x22, 0x22}, + {V18, 0x24, 0x24, 0x24}, /* dimma */ + {V5, 0x25, 0x25, 0x25}, + {V15, 0x26, 0x26, 0x26}, + {Vvsb, 0x550, 0x550, 0x550}, + {Vb, 0x551, 0x551, 0x551}, +}; + +/* + * the vid11 calculation is as follows + * return 1600000 - (u - 2)*6250; + */ + +static uint +map10(uint v) +{ + uint r; + + if(v == 0 || v == 0x29) + return ~0; + if((v & 0x7c) == 0x7c) + return ~0; + r = 1856250; + if(v <= 0x28){ + /* the 6250µV bit is inverted */ + r += 25000; + v += 0x80; + } + return r - (v/2)*12500 + (v&1)*6250; +} + +static int +vidcvt(int v) +{ + int i, u; + static int order[] = {4, 3, 2, 1, 0, 5, 6}; + + u = 0; + for(i = 0; i < nelem(order); i++){ + u <<= 1; + if(v & 1<= 5) + mv -= 10; + }else{ + amv = mv; + if(amv%10 >= 5) + mv += 10; + } + return snprint(buf, n, "%d.%.02d", mv/1000, (amv%1000)/10); +} + +static char* +prvolt(char *p, char *e, char *s, int scale, int *reg) +{ + char buf[3][32]; + int i, v[3]; + + for(i = 0; i < 3; i++){ + v[i] = hwread(reg[i]); + mvprint(buf[i], sizeof buf[i], rawtomv(v[i], scale)); + } + p = seprint(p ,e, "%s" "\t" "%s < %s < %s\t", s, buf[0], buf[1], buf[2]); + return seprint(p ,e, "\t" "%d < %d < %d\n", v[0], v[1], v[2]); + +} + +static long +wbvolt(Chan*, void *a, long n, vlong offset) +{ + char *buf, *p, *e; + int i, scale; + + buf = smalloc(1024); + p = buf; + e = buf + 1024; + selfn(Hwmon); + for(i = 0; i < Vmax; i++){ + scale = smpvolt[i][0]; + p = prvolt(p, e, vname[scale], scale, smpvolt[i] + 1); + } + release(); + n = readstr(offset, a, n, buf); + free(buf); + return n; +} + +static long +wbbeep(Chan*, void *, long n, vlong) +{ + selfn(Hwmon); + hwwrite(Beepctl, hwread(Beepctl) | 0x20); + release(); + + if(waserror()) + nexterror(); + tsleep(&up->sleep, return0, 0, 500); + poperror(); + + selfn(Hwmon); + hwwrite(Beepctl, hwread(Beepctl) & ~0x20); + release(); + + return n; +} + +static void +limbo(char *msg) +{ + iofree(Index); + iofree(hwaddr); + print(" %s\n", msg); + release(); +} + +void +winbondlink(void) +{ + char *chip; + int id, rev, i²c; + + chip = " "; + selfn(0); + i²c = 1; + switch(wbread(Id)){ + case 0x52: + case 0xb0: /* 83627dhg-p */ + chip = "83627dhg-p "; + cap = Beep | Dog | Fans | Stemp | Volts; + break; + case 0x88: + case 0xa0: + cap = Beep | Dog | Fans | Stemp; + break; + case 0xf2: /* 83627hf */ + chip = "83627hf "; + cap = Beep | Fans | Stemp | Volts; + i²c = 0; + break; + default: + break; + } + wbwrite(7, Hwmon); /* BOTCH */ + hwaddr = wbreadw(0x60) & ~0x7; + id = wbreadw(Id); + rev = wbread(Rev); + print("winbond %s%.4ux.%.2x hw %.4ux \n", chip, id, rev, hwaddr); + if(cap == 0 || hwaddr == 0){ + limbo("no capabilities"); + return; + } + wbwrite(Hwstat, wbread(Hwstat) | 1); + if(i²c){ + hwwrite(0x4a, 0x89); /* sleezy way to disable i2c */ + hwwrite(0x48, 0x1f); + hwwrite(Cfg, 0x88); + while(hwread(Cfg) & 0x80) + ; + } + hwwrite(Cfg, 1); + hwwrite(Cfg, 9); + + if(ioalloc(Index, 2, 0, "winbond") == -1){ + limbo("ioalloc fails"); + return; + } + if(ioalloc(hwaddr, 2, 0, "winbond.hw") == -1){ + limbo("ioalloc fails"); + return; + } + if(cap & Stemp){ + addarchfile("cputemp", 0444, wbtemp, nil); /* backstop */ + addarchfile("wbtemp", 0444, wbtemp, nil); + } + if(cap & Fans){ + cfandiv(); + addarchfile("fans", 0444, wbfans, nil); + } + if(cap & Dog) + addarchfile("dog", 0644, wbdogr, wbdogw); + if(cap & Volts){ + hwwrite(Vbat, hwread(Vbat) | 1); + vid = hwread(0x47) & 0xf | (hwread(0x49)&1) << 4; + vidµv = vidcvt(vid); + addarchfile("volt", 0444, wbvolt, nil); + } + if(cap & Beep) + addarchfile("beep", 0200, nil, wbbeep); + release(); +} + +static void +unused(void) +{ + int t[] = {14, 24, 48, 0}; + + print("cr22-26 %.2x %.2x\n", wbread(0x22), wbread(0x26)); + print("rt %.2ux %.2ux %.2ux\n", hwread(0x459), hwread(0x45a), hwread(0x45b)); + print("fanctl %.2ux\n", hwread(0x04d)); + print("pin div %d; input %dmhz\n", 1<< (hwread(Pinctl)>>4 & 3), t[hwread(Pinctl)>>2 & 3]); + //hwwrite(0x3b, 200); hwwrite(0x3c, 200); hwwrite(0x3d, 200); + print("fan lim %d %d %d\n", hwread(0x3b), hwread(0x3c), hwread(0x3d)); + print("smi %.2x %.2x\n", hwread(0x43), hwread(0x44)); + print("0x40 %d conf\n", hwread(0x40)); + print("pwm1 %.2x %.2x; 0x4a %.2x\n", hwread(0x5a), hwread(0x5b), hwread(0x4a)); + print("clock %dmhz; irq %.2x %.2x\n", 24*(1+(wbread(0x24)>>6 & 1)), hwread(0x41), hwread(0x42)); + print("0x46 %d chassis\n", hwread(0x46)); + print("0x48 %d serial\n", hwread(0x48)); + print("0x4a %.2ux\n", hwread(0x4a)); + print("0x4f %.2ux\n", hwread(0x4f)); + print("0x62 %.4ux\n", hwreadw(0x62)); + print("0x550 %d 5vsb\n", hwread(0x550)); + print("0x27 %d\n", hwread(0x27)); + if((cap & Temp) == 0) + return; + print("Cputin %d\n", hwread(Cputin)); + print("0x152 %d\n", hwread(0x152)); + print("0x153 %d\n", hwread(0x153)); + print("0x154 %d\n", hwread(0x154)); + print("0x155 %d\n", hwread(0x155)); + print("Cputin2 %d\n", hwread(0x250)); + print("0x454 %d %d %d temp offset\n", hwread(0x454), hwread(0x455), hwread(0x456)); + print("0x459 %.2ux %.2ux %.2ux rt hw\n", hwread(0x459), hwread(0x45a), hwread(0x45b)); +}