Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | 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
 
8
/*
9
 *	SMBus support for the PIIX4
10
 */
11
enum
12
{
13
	IntelVendID=	0x8086,
14
	Piix4PMID=	0x7113,		/* PIIX4 power management function */
15
 
16
	/* SMBus configuration registers (function 3) */
17
	SMBbase=	0x90, /* 4 byte base address (bit 0 == 1, bit 3:1 == 0) */
18
	SMBconfig=	0xd2,
19
	SMBintrselect=	(7<<1),
20
	SMIenable=	(0<<1),		/* interrupts sent to SMI# */
21
	IRQ9enable=	(4<<1),		/* interrupts sent to IRQ9 */
22
	SMBenable=	(1<<0),		/* 1 enables */
23
 
24
	/* SMBus IO space registers */
25
	Hoststatus=	0x0,	/* (writing 1 bits reset the interrupt bits) */
26
	Failed=		(1<<4),		/* transaction terminated by KILL */
27
	Bus_error=	(1<<3),		/* transaction collision */
28
	Dev_error=	(1<<2),		/* device error interrupt */
29
	Host_complete=	(1<<1),		/* host command completion interrupt */
30
	Host_busy=	(1<<0),		/*  */
31
	Slavestatus=	0x1,	/* (writing 1 bits reset) */
32
	Alert_sts=	(1<<5),		/* someone asserted SMBALERT# */
33
	Shdw2_sts=	(1<<4),		/* slave accessed shadow 2 port */
34
	Shdw1_sts=	(1<<3),		/* slave accessed shadow 1 port */
35
	Slv_sts=	(1<<2),		/* slave accessed shadow 1 port */
36
	Slv_bsy=	(1<<0),
37
	Hostcontrol=	0x2,
38
	Start=		(1<<6),		/* start execution */
39
	Cmd_prot=	(7<<2),		/* command protocol mask */
40
	Quick=		(0<<2),		/*  address only */
41
	Byte=		(1<<2),		/*  address + cmd */
42
	ByteData=	(2<<2),		/*  address + cmd + data */
43
	WordData=	(3<<2),		/*  address + cmd + data + data */
44
	Kill=		(1<<1),		/* abort in progress command */
45
	Ienable=	(1<<0),		/* enable completion interrupts */
46
	Hostcommand=	0x3,
47
	Hostaddress=	0x4,
48
	AddressMask=	(0x7f<<1),	/* target address */
49
	Read=		(1<<0),		/* 1 == read, 0 == write */
50
	Hostdata0=	0x5,
51
	Hostdata1=	0x6,
52
	Blockdata=	0x7,
53
	Slavecontrol=	0x8,
54
	Alert_en=	(1<<3),	/* enable inter on SMBALERT# */
55
	Shdw2_en=	(1<<2),	/* enable inter on external shadow 2 access */
56
	Shdw1_en=	(1<<1),	/* enable inter on external shadow 1 access */
57
	Slv_en=		(1<<0),	/* enable inter on access of host ctlr slave port */
58
	Shadowcommand=	0x9,
59
	Slaveevent=	0xa,
60
	Slavedata=	0xc,
61
};
62
 
63
static struct
64
{
65
	int	rw;
66
	int	cmd;
67
	int	len;
68
	int	proto;
69
} proto[] =
70
{
71
	[SMBquick]	{ 0,	0,	0,	Quick },
72
	[SMBsend]	{ 0,	1,	0,	Byte },
73
	[SMBbytewrite]	{ 0,	1,	1,	ByteData },
74
	[SMBwordwrite]	{ 0,	1,	2,	WordData },
75
	[SMBrecv]	{ Read,	0,	1, 	Byte },
76
	[SMBbyteread]	{ Read,	1,	1,	ByteData },
77
	[SMBwordread]	{ Read,	1,	2,	WordData },
78
};
79
 
80
static void
81
transact(SMBus *s, int type, int addr, int cmd, uchar *data)
82
{
83
	int tries, status;
84
	char err[256];
85
 
86
	if(type < 0 || type > nelem(proto))
87
		panic("piix4smbus: illegal transaction type %d", type);
88
 
89
	if(waserror()){
90
		qunlock(s);
91
		nexterror();
92
	}
93
	qlock(s);
94
 
95
	/* wait a while for the host interface to be available */
96
	for(tries = 0; tries < 1000000; tries++){
97
		if((inb(s->base+Hoststatus) & Host_busy) == 0)
98
			break;
99
		sched();
100
	}
101
	if(tries >= 1000000){
102
		/* try aborting current transaction */
103
		outb(s->base+Hostcontrol, Kill);
104
		for(tries = 0; tries < 1000000; tries++){
105
			if((inb(s->base+Hoststatus) & Host_busy) == 0)
106
				break;
107
			sched();
108
		}
109
		if(tries >= 1000000){
110
			snprint(err, sizeof(err), "SMBus jammed: %2.2ux", inb(s->base+Hoststatus));
111
			error(err);
112
		}
113
	}
114
 
115
	/* set up for transaction */
116
	outb(s->base+Hostaddress, (addr<<1)|proto[type].rw);
117
	if(proto[type].cmd)
118
		outb(s->base+Hostcommand, cmd);
119
	if(proto[type].rw != Read){
120
		switch(proto[type].len){
121
		case 2:
122
			outb(s->base+Hostdata1, data[1]);
123
			/* fall through */
124
		case 1:
125
			outb(s->base+Hostdata0, data[0]);
126
			break;
127
		}
128
	}
129
 
130
 
131
	/* reset the completion/error bits and start transaction */
132
	outb(s->base+Hoststatus, Failed|Bus_error|Dev_error|Host_complete);
133
	outb(s->base+Hostcontrol, Start|proto[type].proto);
134
 
135
	/* wait for completion */
136
	status = 0;
137
	for(tries = 0; tries < 1000000; tries++){
138
		status = inb(s->base+Hoststatus);
139
		if(status & (Failed|Bus_error|Dev_error|Host_complete))
140
			break;
141
		sched();
142
	}
143
	if((status & Host_complete) == 0){
144
		snprint(err, sizeof(err), "SMBus request failed: %2.2ux", status);
145
		error(err);
146
	}
147
 
148
	/* get results */
149
	if(proto[type].rw == Read){
150
		switch(proto[type].len){
151
		case 2:
152
			data[1] = inb(s->base+Hostdata1);
153
			/* fall through */
154
		case 1:
155
			data[0] = inb(s->base+Hostdata0);
156
			break;
157
		}
158
	}
159
	qunlock(s);
160
	poperror();
161
}
162
 
163
static SMBus smbusproto =
164
{
165
	.transact = transact,
166
};
167
 
168
/*
169
 *  return 0 if this is a piix4 with an smbus interface
170
 */
171
SMBus*
172
piix4smbus(void)
173
{
174
	Pcidev *p;
175
	static SMBus *s;
176
 
177
	if(s != nil)
178
		return s;
179
 
180
	p = pcimatch(nil, IntelVendID, Piix4PMID);
181
	if(p == nil)
182
		return nil;
183
 
184
	s = smalloc(sizeof(*s));
185
	memmove(s, &smbusproto, sizeof(*s));
186
	s->arg = p;
187
 
188
	/* disable the smbus */
189
	pcicfgw8(p, SMBconfig, IRQ9enable|0);
190
 
191
	/* see if bios gave us a viable port space */
192
	s->base = pcicfgr32(p, SMBbase) & ~1;
193
print("SMB base from bios is 0x%lux\n", s->base);
194
	if(ioalloc(s->base, 0xd, 0, "piix4smbus") < 0){
195
		s->base = ioalloc(-1, 0xd, 2, "piix4smbus");
196
		if(s->base < 0){
197
			free(s);
198
			print("piix4smbus: can't allocate io port\n");
199
			return nil;
200
		}
201
print("SMB base ialloc is 0x%lux\n", s->base);
202
		pcicfgw32(p, SMBbase, s->base|1);
203
	}
204
 
205
	/* disable SMBus interrupts, abort any transaction in progress */
206
	outb(s->base+Hostcontrol, Kill);
207
	outb(s->base+Slavecontrol, 0);
208
 
209
	/* enable the smbus */
210
	pcicfgw8(p, SMBconfig, IRQ9enable|SMBenable);
211
 
212
	return s;
213
}