devlm78.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. #include "u.h"
  2. #include "../port/lib.h"
  3. #include "mem.h"
  4. #include "dat.h"
  5. #include "fns.h"
  6. #include "io.h"
  7. #include "ureg.h"
  8. #include "../port/error.h"
  9. /* this driver doesn't implement the management interrupts. we
  10. * leave the LM78 interrupts set to whatever the BIOS did. we do
  11. * allow reading and writing the the readouts and alarm values.
  12. * Read(2)ing or write(2)ing at offset 0x0-0x1f, is
  13. * equivalent to reading or writing lm78 registers 0x20-0x3f.
  14. */
  15. enum
  16. {
  17. /* address of chip on serial interface */
  18. Serialaddr= 0x2d,
  19. /* parallel access registers */
  20. Rpaddr= 0x5,
  21. Bbusy= (1<<7),
  22. Rpdata= 0x6,
  23. /* internal register addresses */
  24. Rconfig= 0x40,
  25. Bstart= (1<<0),
  26. Bsmiena= (1<<1),
  27. Birqena= (1<<2),
  28. Bintclr= (1<<3),
  29. Breset= (1<<4),
  30. Bnmi= (1<<5), /* if set, use nmi, else irq */
  31. Bpowbypass= (1<<6),
  32. Binit= (1<<7),
  33. Ristat1= 0x41,
  34. Ristat2= 0x42,
  35. Rsmimask1= 0x43,
  36. Rsmimask2= 0x44,
  37. Rnmimask1= 0x45,
  38. Rnmimask2= 0x46,
  39. Rvidfan= 0x47, /* set fan counter, and read voltage level */
  40. Mvid= 0x0f,
  41. Mfan= 0xf0,
  42. Raddr= 0x48, /* address used on serial bus */
  43. Rresetid= 0x49, /* chip reset and ID register */
  44. Rpost= 0x00, /* start of post ram */
  45. Rvalue= 0x20, /* start of value ram */
  46. VRsize= 0x20, /* size of value ram */
  47. };
  48. enum
  49. {
  50. Qdir,
  51. Qlm78vram,
  52. };
  53. static Dirtab lm78dir[] = {
  54. ".", { Qdir, 0, QTDIR}, 0, 0555,
  55. "lm78vram", { Qlm78vram, 0 }, 0, 0444,
  56. };
  57. /* interface type */
  58. enum
  59. {
  60. None= 0,
  61. Smbus,
  62. Parallel,
  63. };
  64. static struct {
  65. QLock;
  66. int probed;
  67. int ifc; /* which interface is connected */
  68. SMBus *smbus; /* serial interface */
  69. int port; /* parallel interface */
  70. } lm78;
  71. extern SMBus* piix4smbus(void);
  72. /* wait for device to become quiescent and then set the */
  73. /* register address */
  74. static void
  75. setreg(int reg)
  76. {
  77. int tries;
  78. for(tries = 0; tries < 1000000; tries++)
  79. if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
  80. outb(lm78.port+Rpaddr, reg);
  81. return;
  82. }
  83. error("lm78 broken");
  84. }
  85. /* routines that actually touch the device */
  86. static void
  87. lm78wrreg(int reg, uchar val)
  88. {
  89. if(waserror()){
  90. qunlock(&lm78);
  91. nexterror();
  92. }
  93. qlock(&lm78);
  94. switch(lm78.ifc){
  95. case Smbus:
  96. lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
  97. break;
  98. case Parallel:
  99. setreg(reg);
  100. outb(lm78.port+Rpdata, val);
  101. break;
  102. default:
  103. error(Enodev);
  104. break;
  105. }
  106. qunlock(&lm78);
  107. poperror();
  108. }
  109. static int
  110. lm78rdreg(int reg)
  111. {
  112. uchar val;
  113. if(waserror()){
  114. qunlock(&lm78);
  115. nexterror();
  116. }
  117. qlock(&lm78);
  118. switch(lm78.ifc){
  119. case Smbus:
  120. lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
  121. lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
  122. break;
  123. case Parallel:
  124. setreg(reg);
  125. val = inb(lm78.port+Rpdata);
  126. break;
  127. default:
  128. error(Enodev);
  129. break;
  130. }
  131. qunlock(&lm78);
  132. poperror();
  133. return val;
  134. }
  135. /* start the chip monitoring but don't change any smi
  136. * interrupts and/or alarms that the BIOS may have set up.
  137. * this isn't locked because it's thought to be idempotent
  138. */
  139. static void
  140. lm78enable(void)
  141. {
  142. uchar config;
  143. if(lm78.ifc == None)
  144. error(Enodev);
  145. if(lm78.probed == 0){
  146. /* make sure its really there */
  147. if(lm78rdreg(Raddr) != Serialaddr){
  148. lm78.ifc = None;
  149. error(Enodev);
  150. } else {
  151. /* start the sampling */
  152. config = lm78rdreg(Rconfig);
  153. config = (config | Bstart) & ~(Bintclr|Binit);
  154. lm78wrreg(Rconfig, config);
  155. pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
  156. }
  157. lm78.probed = 1;
  158. }
  159. }
  160. enum
  161. {
  162. IntelVendID= 0x8086,
  163. PiixID= 0x122E,
  164. Piix3ID= 0x7000,
  165. Piix4PMID= 0x7113, /* PIIX4 power management function */
  166. PCSC= 0x78, /* programmable chip select control register */
  167. PCSC8bytes= 0x01,
  168. };
  169. /* figure out what kind of interface we could have */
  170. void
  171. lm78reset(void)
  172. {
  173. int pcs;
  174. Pcidev *p;
  175. lm78.ifc = None;
  176. p = nil;
  177. while((p = pcimatch(p, IntelVendID, 0)) != nil){
  178. switch(p->did){
  179. /* these bridges use the PCSC to map the lm78 into port space. */
  180. /* for this case the lm78's CS# select is connected to the PIIX's */
  181. /* PCS# output and the bottom 3 bits of address are passed to the */
  182. /* LM78's A0-A2 inputs. */
  183. case PiixID:
  184. case Piix3ID:
  185. pcs = pcicfgr16(p, PCSC);
  186. if(pcs & 3) {
  187. /* already enabled */
  188. lm78.port = pcs & ~3;
  189. lm78.ifc = Parallel;
  190. return;
  191. }
  192. /* enable the chip, use default address 0x50 */
  193. pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
  194. pcs = pcicfgr16(p, PCSC);
  195. lm78.port = pcs & ~3;
  196. lm78.ifc = Parallel;
  197. return;
  198. /* this bridge puts the lm78's serial interface on the smbus */
  199. case Piix4PMID:
  200. lm78.smbus = piix4smbus();
  201. if(lm78.smbus == nil)
  202. continue;
  203. print("found piix4 smbus, base %lud\n", lm78.smbus->base);
  204. lm78.ifc = Smbus;
  205. return;
  206. }
  207. }
  208. }
  209. Walkqid *
  210. lm78walk(Chan* c, Chan *nc, char** name, int nname)
  211. {
  212. return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
  213. }
  214. static int
  215. lm78stat(Chan* c, uchar* dp, int n)
  216. {
  217. return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
  218. }
  219. static Chan*
  220. lm78open(Chan* c, int omode)
  221. {
  222. return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
  223. }
  224. static void
  225. lm78close(Chan*)
  226. {
  227. }
  228. enum
  229. {
  230. Linelen= 25,
  231. };
  232. static long
  233. lm78read(Chan *c, void *a, long n, vlong offset)
  234. {
  235. uchar *va = a;
  236. int off, e;
  237. off = offset;
  238. switch((ulong)c->qid.path){
  239. case Qdir:
  240. return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
  241. case Qlm78vram:
  242. if(off >= VRsize)
  243. return 0;
  244. e = off + n;
  245. if(e > VRsize)
  246. e = VRsize;
  247. for(; off < e; off++)
  248. *va++ = lm78rdreg(Rvalue+off);
  249. return (int)(va - (uchar*)a);
  250. }
  251. return 0;
  252. }
  253. static long
  254. lm78write(Chan *c, void *a, long n, vlong offset)
  255. {
  256. uchar *va = a;
  257. int off, e;
  258. off = offset;
  259. switch((ulong)c->qid.path){
  260. default:
  261. error(Eperm);
  262. case Qlm78vram:
  263. if(off >= VRsize)
  264. return 0;
  265. e = off + n;
  266. if(e > VRsize)
  267. e = VRsize;
  268. for(; off < e; off++)
  269. lm78wrreg(Rvalue+off, *va++);
  270. return va - (uchar*)a;
  271. }
  272. return 0;
  273. }
  274. extern Dev lm78devtab;
  275. static Chan*
  276. lm78attach(char* spec)
  277. {
  278. lm78enable();
  279. return devattach(lm78devtab.dc, spec);
  280. }
  281. Dev lm78devtab = {
  282. 'T',
  283. "lm78",
  284. lm78reset,
  285. devinit,
  286. devshutdown,
  287. lm78attach,
  288. lm78walk,
  289. lm78stat,
  290. lm78open,
  291. devcreate,
  292. lm78close,
  293. lm78read,
  294. devbread,
  295. lm78write,
  296. devbwrite,
  297. devremove,
  298. devwstat,
  299. };