devlm78.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. "lm78vram", { Qlm78vram, 0 }, 0, 0444,
  55. };
  56. /* interface type */
  57. enum
  58. {
  59. None= 0,
  60. Smbus,
  61. Parallel,
  62. };
  63. static struct {
  64. QLock;
  65. int probed;
  66. int ifc; /* which interface is connected */
  67. SMBus *smbus; /* serial interface */
  68. int port; /* parallel interface */
  69. } lm78;
  70. extern SMBus* piix4smbus(void);
  71. /* wait for device to become quiescent and then set the */
  72. /* register address */
  73. static void
  74. setreg(int reg)
  75. {
  76. int tries;
  77. for(tries = 0; tries < 1000000; tries++)
  78. if((inb(lm78.port+Rpaddr) & Bbusy) == 0){
  79. outb(lm78.port+Rpaddr, reg);
  80. return;
  81. }
  82. error("lm78 broken");
  83. }
  84. /* routines that actually touch the device */
  85. static void
  86. lm78wrreg(int reg, uchar val)
  87. {
  88. if(waserror()){
  89. qunlock(&lm78);
  90. nexterror();
  91. }
  92. qlock(&lm78);
  93. switch(lm78.ifc){
  94. case Smbus:
  95. lm78.smbus->transact(lm78.smbus, SMBbytewrite, Serialaddr, reg, &val);
  96. break;
  97. case Parallel:
  98. setreg(reg);
  99. outb(lm78.port+Rpdata, val);
  100. break;
  101. default:
  102. error(Enodev);
  103. break;
  104. }
  105. qunlock(&lm78);
  106. poperror();
  107. }
  108. static int
  109. lm78rdreg(int reg)
  110. {
  111. uchar val;
  112. if(waserror()){
  113. qunlock(&lm78);
  114. nexterror();
  115. }
  116. qlock(&lm78);
  117. switch(lm78.ifc){
  118. case Smbus:
  119. lm78.smbus->transact(lm78.smbus, SMBsend, Serialaddr, reg, nil);
  120. lm78.smbus->transact(lm78.smbus, SMBrecv, Serialaddr, 0, &val);
  121. break;
  122. case Parallel:
  123. setreg(reg);
  124. val = inb(lm78.port+Rpdata);
  125. break;
  126. default:
  127. error(Enodev);
  128. break;
  129. }
  130. qunlock(&lm78);
  131. poperror();
  132. return val;
  133. }
  134. /* start the chip monitoring but don't change any smi
  135. * interrupts and/or alarms that the BIOS may have set up.
  136. * this isn't locked because it's thought to be idempotent
  137. */
  138. static void
  139. lm78enable(void)
  140. {
  141. uchar config;
  142. if(lm78.ifc == None)
  143. error(Enodev);
  144. if(lm78.probed == 0){
  145. /* make sure its really there */
  146. if(lm78rdreg(Raddr) != Serialaddr){
  147. lm78.ifc = None;
  148. error(Enodev);
  149. } else {
  150. /* start the sampling */
  151. config = lm78rdreg(Rconfig);
  152. config = (config | Bstart) & ~(Bintclr|Binit);
  153. lm78wrreg(Rconfig, config);
  154. pprint("Rvidfan %2.2ux\n", lm78rdreg(Rconfig), lm78rdreg(Rvidfan));
  155. }
  156. lm78.probed = 1;
  157. }
  158. }
  159. enum
  160. {
  161. IntelVendID= 0x8086,
  162. PiixID= 0x122E,
  163. Piix3ID= 0x7000,
  164. Piix4PMID= 0x7113, /* PIIX4 power management function */
  165. PCSC= 0x78, /* programmable chip select control register */
  166. PCSC8bytes= 0x01,
  167. };
  168. /* figure out what kind of interface we could have */
  169. void
  170. lm78reset(void)
  171. {
  172. int pcs;
  173. Pcidev *p;
  174. lm78.ifc = None;
  175. p = nil;
  176. while((p = pcimatch(p, IntelVendID, 0)) != nil){
  177. switch(p->did){
  178. /* these bridges use the PCSC to map the lm78 into port space. */
  179. /* for this case the lm78's CS# select is connected to the PIIX's */
  180. /* PCS# output and the bottom 3 bits of address are passed to the */
  181. /* LM78's A0-A2 inputs. */
  182. case PiixID:
  183. case Piix3ID:
  184. pcs = pcicfgr16(p, PCSC);
  185. if(pcs & 3) {
  186. /* already enabled */
  187. lm78.port = pcs & ~3;
  188. lm78.ifc = Parallel;
  189. return;
  190. }
  191. /* enable the chip, use default address 0x50 */
  192. pcicfgw16(p, PCSC, 0x50|PCSC8bytes);
  193. pcs = pcicfgr16(p, PCSC);
  194. lm78.port = pcs & ~3;
  195. lm78.ifc = Parallel;
  196. return;
  197. /* this bridge puts the lm78's serial interface on the smbus */
  198. case Piix4PMID:
  199. lm78.smbus = piix4smbus();
  200. if(lm78.smbus == nil)
  201. continue;
  202. print("found piix4 smbus, base %lud\n", lm78.smbus->base);
  203. lm78.ifc = Smbus;
  204. return;
  205. }
  206. }
  207. }
  208. Walkqid *
  209. lm78walk(Chan* c, Chan *nc, char** name, int nname)
  210. {
  211. return devwalk(c, nc, name, nname, lm78dir, nelem(lm78dir), devgen);
  212. }
  213. static int
  214. lm78stat(Chan* c, uchar* dp, int n)
  215. {
  216. return devstat(c, dp, n, lm78dir, nelem(lm78dir), devgen);
  217. }
  218. static Chan*
  219. lm78open(Chan* c, int omode)
  220. {
  221. return devopen(c, omode, lm78dir, nelem(lm78dir), devgen);
  222. }
  223. static void
  224. lm78close(Chan*)
  225. {
  226. }
  227. enum
  228. {
  229. Linelen= 25,
  230. };
  231. static long
  232. lm78read(Chan *c, void *a, long n, vlong offset)
  233. {
  234. uchar *va = a;
  235. int off, e;
  236. off = offset;
  237. switch((ulong)c->qid.path){
  238. case Qdir:
  239. return devdirread(c, a, n, lm78dir, nelem(lm78dir), devgen);
  240. case Qlm78vram:
  241. if(off >= VRsize)
  242. return 0;
  243. e = off + n;
  244. if(e > VRsize)
  245. e = VRsize;
  246. for(; off < e; off++)
  247. *va++ = lm78rdreg(Rvalue+off);
  248. return (int)(va - (uchar*)a);
  249. }
  250. return 0;
  251. }
  252. static long
  253. lm78write(Chan *c, void *a, long n, vlong offset)
  254. {
  255. uchar *va = a;
  256. int off, e;
  257. off = offset;
  258. switch((ulong)c->qid.path){
  259. default:
  260. error(Eperm);
  261. case Qlm78vram:
  262. if(off >= VRsize)
  263. return 0;
  264. e = off + n;
  265. if(e > VRsize)
  266. e = VRsize;
  267. for(; off < e; off++)
  268. lm78wrreg(Rvalue+off, *va++);
  269. return va - (uchar*)a;
  270. }
  271. return 0;
  272. }
  273. extern Dev lm78devtab;
  274. static Chan*
  275. lm78attach(char* spec)
  276. {
  277. lm78enable();
  278. return devattach(lm78devtab.dc, spec);
  279. }
  280. Dev lm78devtab = {
  281. 'T',
  282. "lm78",
  283. lm78reset,
  284. devinit,
  285. devshutdown,
  286. lm78attach,
  287. lm78walk,
  288. lm78stat,
  289. lm78open,
  290. devcreate,
  291. lm78close,
  292. lm78read,
  293. devbread,
  294. lm78write,
  295. devbwrite,
  296. devremove,
  297. devwstat,
  298. };