Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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