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