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