devlm78.c 5.9 KB

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