FreeBSD/Linux Kernel Cross Reference
sys/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
Cache object: f47eb28fdc8df7cebf856c7ea0952f7f
|