1*a81c3ea0SDavid du Colombier /*
2*a81c3ea0SDavid du Colombier * PCI support code.
3*a81c3ea0SDavid du Colombier */
4*a81c3ea0SDavid du Colombier
5*a81c3ea0SDavid du Colombier #include "u.h"
6*a81c3ea0SDavid du Colombier #include "../port/lib.h"
7*a81c3ea0SDavid du Colombier #include "mem.h"
8*a81c3ea0SDavid du Colombier #include "dat.h"
9*a81c3ea0SDavid du Colombier #include "fns.h"
10*a81c3ea0SDavid du Colombier #include "io.h"
11*a81c3ea0SDavid du Colombier
12*a81c3ea0SDavid du Colombier typedef struct Pci Pci;
13*a81c3ea0SDavid du Colombier
14*a81c3ea0SDavid du Colombier struct Pci {
15*a81c3ea0SDavid du Colombier ulong id;
16*a81c3ea0SDavid du Colombier ulong cs;
17*a81c3ea0SDavid du Colombier ulong revclass;
18*a81c3ea0SDavid du Colombier ulong misc; /* cache line size, latency timer, header type, bist */
19*a81c3ea0SDavid du Colombier ulong base[6]; /* base addr regs */
20*a81c3ea0SDavid du Colombier ulong unused[5];
21*a81c3ea0SDavid du Colombier ulong intr;
22*a81c3ea0SDavid du Colombier ulong mask[6];
23*a81c3ea0SDavid du Colombier ulong trans[6];
24*a81c3ea0SDavid du Colombier };
25*a81c3ea0SDavid du Colombier
26*a81c3ea0SDavid du Colombier enum {
27*a81c3ea0SDavid du Colombier /* cs bits */
28*a81c3ea0SDavid du Colombier CIoEn = (1<<0),
29*a81c3ea0SDavid du Colombier CMemEn = (1<<1),
30*a81c3ea0SDavid du Colombier CMasEn = (1<<2),
31*a81c3ea0SDavid du Colombier CSpcEn = (1<<4),
32*a81c3ea0SDavid du Colombier CParEn = (1<<6),
33*a81c3ea0SDavid du Colombier CSErrEn = (1<<8),
34*a81c3ea0SDavid du Colombier
35*a81c3ea0SDavid du Colombier SMasTgtAb = (1<<24), /* master target abort */
36*a81c3ea0SDavid du Colombier SMasAb = (1<<25), /* master abort */
37*a81c3ea0SDavid du Colombier };
38*a81c3ea0SDavid du Colombier
39*a81c3ea0SDavid du Colombier enum {
40*a81c3ea0SDavid du Colombier MaxFNO = 7,
41*a81c3ea0SDavid du Colombier MaxUBN = 255,
42*a81c3ea0SDavid du Colombier };
43*a81c3ea0SDavid du Colombier
44*a81c3ea0SDavid du Colombier enum
45*a81c3ea0SDavid du Colombier { /* command register */
46*a81c3ea0SDavid du Colombier IOen = (1<<0),
47*a81c3ea0SDavid du Colombier MEMen = (1<<1),
48*a81c3ea0SDavid du Colombier MASen = (1<<2),
49*a81c3ea0SDavid du Colombier MemWrInv = (1<<4),
50*a81c3ea0SDavid du Colombier PErrEn = (1<<6),
51*a81c3ea0SDavid du Colombier SErrEn = (1<<8),
52*a81c3ea0SDavid du Colombier };
53*a81c3ea0SDavid du Colombier
54*a81c3ea0SDavid du Colombier static Lock pcicfglock;
55*a81c3ea0SDavid du Colombier static Lock pcicfginitlock;
56*a81c3ea0SDavid du Colombier static int pcicfgmode = -1;
57*a81c3ea0SDavid du Colombier static int pcimaxbno = 0;
58*a81c3ea0SDavid du Colombier static int pcimaxdno;
59*a81c3ea0SDavid du Colombier static Pcidev *pciroot;
60*a81c3ea0SDavid du Colombier static Pcidev *pcilist;
61*a81c3ea0SDavid du Colombier static Pcidev *pcitail;
62*a81c3ea0SDavid du Colombier static Pci *pci = (Pci*)PCICFG;
63*a81c3ea0SDavid du Colombier
64*a81c3ea0SDavid du Colombier static int pcicfgrw8(int, int, int, int);
65*a81c3ea0SDavid du Colombier static int pcicfgrw16(int, int, int, int);
66*a81c3ea0SDavid du Colombier static int pcicfgrw32(int, int, int, int);
67*a81c3ea0SDavid du Colombier
68*a81c3ea0SDavid du Colombier static char* bustypes[] = {
69*a81c3ea0SDavid du Colombier "CBUSI",
70*a81c3ea0SDavid du Colombier "CBUSII",
71*a81c3ea0SDavid du Colombier "EISA",
72*a81c3ea0SDavid du Colombier "FUTURE",
73*a81c3ea0SDavid du Colombier "INTERN",
74*a81c3ea0SDavid du Colombier "ISA",
75*a81c3ea0SDavid du Colombier "MBI",
76*a81c3ea0SDavid du Colombier "MBII",
77*a81c3ea0SDavid du Colombier "MCA",
78*a81c3ea0SDavid du Colombier "MPI",
79*a81c3ea0SDavid du Colombier "MPSA",
80*a81c3ea0SDavid du Colombier "NUBUS",
81*a81c3ea0SDavid du Colombier "PCI",
82*a81c3ea0SDavid du Colombier "PCMCIA",
83*a81c3ea0SDavid du Colombier "TC",
84*a81c3ea0SDavid du Colombier "VL",
85*a81c3ea0SDavid du Colombier "VME",
86*a81c3ea0SDavid du Colombier "XPRESS",
87*a81c3ea0SDavid du Colombier };
88*a81c3ea0SDavid du Colombier
89*a81c3ea0SDavid du Colombier static int
tbdffmt(Fmt * fmt)90*a81c3ea0SDavid du Colombier tbdffmt(Fmt* fmt)
91*a81c3ea0SDavid du Colombier {
92*a81c3ea0SDavid du Colombier char *p;
93*a81c3ea0SDavid du Colombier int l, r;
94*a81c3ea0SDavid du Colombier uint type, tbdf;
95*a81c3ea0SDavid du Colombier
96*a81c3ea0SDavid du Colombier if((p = malloc(READSTR)) == nil)
97*a81c3ea0SDavid du Colombier return fmtstrcpy(fmt, "(tbdfconv)");
98*a81c3ea0SDavid du Colombier
99*a81c3ea0SDavid du Colombier switch(fmt->r){
100*a81c3ea0SDavid du Colombier case 'T':
101*a81c3ea0SDavid du Colombier tbdf = va_arg(fmt->args, int);
102*a81c3ea0SDavid du Colombier if(tbdf == BUSUNKNOWN)
103*a81c3ea0SDavid du Colombier snprint(p, READSTR, "unknown");
104*a81c3ea0SDavid du Colombier else{
105*a81c3ea0SDavid du Colombier type = BUSTYPE(tbdf);
106*a81c3ea0SDavid du Colombier if(type < nelem(bustypes))
107*a81c3ea0SDavid du Colombier l = snprint(p, READSTR, bustypes[type]);
108*a81c3ea0SDavid du Colombier else
109*a81c3ea0SDavid du Colombier l = snprint(p, READSTR, "%d", type);
110*a81c3ea0SDavid du Colombier snprint(p+l, READSTR-l, ".%d.%d.%d",
111*a81c3ea0SDavid du Colombier BUSBNO(tbdf), BUSDNO(tbdf), BUSFNO(tbdf));
112*a81c3ea0SDavid du Colombier }
113*a81c3ea0SDavid du Colombier break;
114*a81c3ea0SDavid du Colombier
115*a81c3ea0SDavid du Colombier default:
116*a81c3ea0SDavid du Colombier snprint(p, READSTR, "(tbdfconv)");
117*a81c3ea0SDavid du Colombier break;
118*a81c3ea0SDavid du Colombier }
119*a81c3ea0SDavid du Colombier r = fmtstrcpy(fmt, p);
120*a81c3ea0SDavid du Colombier free(p);
121*a81c3ea0SDavid du Colombier
122*a81c3ea0SDavid du Colombier return r;
123*a81c3ea0SDavid du Colombier }
124*a81c3ea0SDavid du Colombier
125*a81c3ea0SDavid du Colombier ulong
pcibarsize(Pcidev * p,int rno)126*a81c3ea0SDavid du Colombier pcibarsize(Pcidev *p, int rno)
127*a81c3ea0SDavid du Colombier {
128*a81c3ea0SDavid du Colombier ulong v, size;
129*a81c3ea0SDavid du Colombier
130*a81c3ea0SDavid du Colombier v = pcicfgrw32(p->tbdf, rno, 0, 1);
131*a81c3ea0SDavid du Colombier pcicfgrw32(p->tbdf, rno, 0xFFFFFFF0, 0);
132*a81c3ea0SDavid du Colombier size = pcicfgrw32(p->tbdf, rno, 0, 1);
133*a81c3ea0SDavid du Colombier if(v & 1)
134*a81c3ea0SDavid du Colombier size |= 0xFFFF0000;
135*a81c3ea0SDavid du Colombier pcicfgrw32(p->tbdf, rno, v, 0);
136*a81c3ea0SDavid du Colombier
137*a81c3ea0SDavid du Colombier return -(size & ~0x0F);
138*a81c3ea0SDavid du Colombier }
139*a81c3ea0SDavid du Colombier
140*a81c3ea0SDavid du Colombier static int
pcilscan(int bno,Pcidev ** list)141*a81c3ea0SDavid du Colombier pcilscan(int bno, Pcidev** list)
142*a81c3ea0SDavid du Colombier {
143*a81c3ea0SDavid du Colombier Pcidev *p, *head, *tail;
144*a81c3ea0SDavid du Colombier int dno, fno, i, hdt, l, maxfno, maxubn, rno, sbn, tbdf, ubn;
145*a81c3ea0SDavid du Colombier
146*a81c3ea0SDavid du Colombier maxubn = bno;
147*a81c3ea0SDavid du Colombier head = nil;
148*a81c3ea0SDavid du Colombier tail = nil;
149*a81c3ea0SDavid du Colombier /* dno from 5 due to its address mode */
150*a81c3ea0SDavid du Colombier for(dno = 5; dno <= pcimaxdno; dno++){
151*a81c3ea0SDavid du Colombier maxfno = 0;
152*a81c3ea0SDavid du Colombier for(fno = 0; fno <= maxfno; fno++){
153*a81c3ea0SDavid du Colombier /*
154*a81c3ea0SDavid du Colombier * For this possible device, form the
155*a81c3ea0SDavid du Colombier * bus+device+function triplet needed to address it
156*a81c3ea0SDavid du Colombier * and try to read the vendor and device ID.
157*a81c3ea0SDavid du Colombier * If successful, allocate a device struct and
158*a81c3ea0SDavid du Colombier * start to fill it in with some useful information
159*a81c3ea0SDavid du Colombier * from the device's configuration space.
160*a81c3ea0SDavid du Colombier */
161*a81c3ea0SDavid du Colombier tbdf = MKBUS(BusPCI, bno, dno, fno);
162*a81c3ea0SDavid du Colombier l = pcicfgrw32(tbdf, PciVID, 0, 1);
163*a81c3ea0SDavid du Colombier if(l == 0xFFFFFFFF || l == 0)
164*a81c3ea0SDavid du Colombier continue;
165*a81c3ea0SDavid du Colombier p = malloc(sizeof(*p));
166*a81c3ea0SDavid du Colombier if(p == nil)
167*a81c3ea0SDavid du Colombier panic("pcilscan: no memory");
168*a81c3ea0SDavid du Colombier p->tbdf = tbdf;
169*a81c3ea0SDavid du Colombier p->vid = l;
170*a81c3ea0SDavid du Colombier p->did = l>>16;
171*a81c3ea0SDavid du Colombier
172*a81c3ea0SDavid du Colombier if(pcilist != nil)
173*a81c3ea0SDavid du Colombier pcitail->list = p;
174*a81c3ea0SDavid du Colombier else
175*a81c3ea0SDavid du Colombier pcilist = p;
176*a81c3ea0SDavid du Colombier pcitail = p;
177*a81c3ea0SDavid du Colombier
178*a81c3ea0SDavid du Colombier p->pcr = pcicfgr16(p, PciPCR);
179*a81c3ea0SDavid du Colombier p->rid = pcicfgr8(p, PciRID);
180*a81c3ea0SDavid du Colombier p->ccrp = pcicfgr8(p, PciCCRp);
181*a81c3ea0SDavid du Colombier p->ccru = pcicfgr8(p, PciCCRu);
182*a81c3ea0SDavid du Colombier p->ccrb = pcicfgr8(p, PciCCRb);
183*a81c3ea0SDavid du Colombier p->cls = pcicfgr8(p, PciCLS);
184*a81c3ea0SDavid du Colombier p->ltr = pcicfgr8(p, PciLTR);
185*a81c3ea0SDavid du Colombier
186*a81c3ea0SDavid du Colombier p->intl = pcicfgr8(p, PciINTL);
187*a81c3ea0SDavid du Colombier p->intp = pcicfgr8(p, PciINTP);
188*a81c3ea0SDavid du Colombier
189*a81c3ea0SDavid du Colombier /*
190*a81c3ea0SDavid du Colombier * If the device is a multi-function device adjust the
191*a81c3ea0SDavid du Colombier * loop count so all possible functions are checked.
192*a81c3ea0SDavid du Colombier */
193*a81c3ea0SDavid du Colombier hdt = pcicfgr8(p, PciHDT);
194*a81c3ea0SDavid du Colombier if(hdt & 0x80)
195*a81c3ea0SDavid du Colombier maxfno = MaxFNO;
196*a81c3ea0SDavid du Colombier
197*a81c3ea0SDavid du Colombier /*
198*a81c3ea0SDavid du Colombier * If appropriate, read the base address registers
199*a81c3ea0SDavid du Colombier * and work out the sizes.
200*a81c3ea0SDavid du Colombier */
201*a81c3ea0SDavid du Colombier switch(p->ccrb) {
202*a81c3ea0SDavid du Colombier case 0x03: /* display controller */
203*a81c3ea0SDavid du Colombier /* fall through */
204*a81c3ea0SDavid du Colombier case 0x01: /* mass storage controller */
205*a81c3ea0SDavid du Colombier case 0x02: /* network controller */
206*a81c3ea0SDavid du Colombier case 0x04: /* multimedia device */
207*a81c3ea0SDavid du Colombier case 0x07: /* simple comm. controllers */
208*a81c3ea0SDavid du Colombier case 0x08: /* base system peripherals */
209*a81c3ea0SDavid du Colombier case 0x09: /* input devices */
210*a81c3ea0SDavid du Colombier case 0x0A: /* docking stations */
211*a81c3ea0SDavid du Colombier case 0x0B: /* processors */
212*a81c3ea0SDavid du Colombier case 0x0C: /* serial bus controllers */
213*a81c3ea0SDavid du Colombier if((hdt & 0x7F) != 0)
214*a81c3ea0SDavid du Colombier break;
215*a81c3ea0SDavid du Colombier rno = PciBAR0 - 4;
216*a81c3ea0SDavid du Colombier for(i = 0; i < nelem(p->mem); i++) {
217*a81c3ea0SDavid du Colombier rno += 4;
218*a81c3ea0SDavid du Colombier p->mem[i].bar = pcicfgr32(p, rno);
219*a81c3ea0SDavid du Colombier p->mem[i].size = pcibarsize(p, rno);
220*a81c3ea0SDavid du Colombier }
221*a81c3ea0SDavid du Colombier break;
222*a81c3ea0SDavid du Colombier
223*a81c3ea0SDavid du Colombier case 0x00:
224*a81c3ea0SDavid du Colombier case 0x05: /* memory controller */
225*a81c3ea0SDavid du Colombier case 0x06: /* bridge device */
226*a81c3ea0SDavid du Colombier default:
227*a81c3ea0SDavid du Colombier break;
228*a81c3ea0SDavid du Colombier }
229*a81c3ea0SDavid du Colombier
230*a81c3ea0SDavid du Colombier if(head != nil)
231*a81c3ea0SDavid du Colombier tail->link = p;
232*a81c3ea0SDavid du Colombier else
233*a81c3ea0SDavid du Colombier head = p;
234*a81c3ea0SDavid du Colombier tail = p;
235*a81c3ea0SDavid du Colombier }
236*a81c3ea0SDavid du Colombier }
237*a81c3ea0SDavid du Colombier
238*a81c3ea0SDavid du Colombier *list = head;
239*a81c3ea0SDavid du Colombier for(p = head; p != nil; p = p->link){
240*a81c3ea0SDavid du Colombier /*
241*a81c3ea0SDavid du Colombier * Find PCI-PCI bridges and recursively descend the tree.
242*a81c3ea0SDavid du Colombier */
243*a81c3ea0SDavid du Colombier if(p->ccrb != 0x06 || p->ccru != 0x04)
244*a81c3ea0SDavid du Colombier continue;
245*a81c3ea0SDavid du Colombier
246*a81c3ea0SDavid du Colombier /*
247*a81c3ea0SDavid du Colombier * If the secondary or subordinate bus number is not
248*a81c3ea0SDavid du Colombier * initialised try to do what the PCI BIOS should have
249*a81c3ea0SDavid du Colombier * done and fill in the numbers as the tree is descended.
250*a81c3ea0SDavid du Colombier * On the way down the subordinate bus number is set to
251*a81c3ea0SDavid du Colombier * the maximum as it's not known how many buses are behind
252*a81c3ea0SDavid du Colombier * this one; the final value is set on the way back up.
253*a81c3ea0SDavid du Colombier */
254*a81c3ea0SDavid du Colombier sbn = pcicfgr8(p, PciSBN);
255*a81c3ea0SDavid du Colombier ubn = pcicfgr8(p, PciUBN);
256*a81c3ea0SDavid du Colombier
257*a81c3ea0SDavid du Colombier if(sbn == 0 || ubn == 0) {
258*a81c3ea0SDavid du Colombier sbn = maxubn+1;
259*a81c3ea0SDavid du Colombier /*
260*a81c3ea0SDavid du Colombier * Make sure memory, I/O and master enables are
261*a81c3ea0SDavid du Colombier * off, set the primary, secondary and subordinate
262*a81c3ea0SDavid du Colombier * bus numbers and clear the secondary status before
263*a81c3ea0SDavid du Colombier * attempting to scan the secondary bus.
264*a81c3ea0SDavid du Colombier *
265*a81c3ea0SDavid du Colombier * Initialisation of the bridge should be done here.
266*a81c3ea0SDavid du Colombier */
267*a81c3ea0SDavid du Colombier pcicfgw32(p, PciPCR, 0xFFFF0000);
268*a81c3ea0SDavid du Colombier l = (MaxUBN<<16)|(sbn<<8)|bno;
269*a81c3ea0SDavid du Colombier pcicfgw32(p, PciPBN, l);
270*a81c3ea0SDavid du Colombier pcicfgw16(p, PciSPSR, 0xFFFF);
271*a81c3ea0SDavid du Colombier maxubn = pcilscan(sbn, &p->bridge);
272*a81c3ea0SDavid du Colombier l = (maxubn<<16)|(sbn<<8)|bno;
273*a81c3ea0SDavid du Colombier
274*a81c3ea0SDavid du Colombier pcicfgw32(p, PciPBN, l);
275*a81c3ea0SDavid du Colombier }
276*a81c3ea0SDavid du Colombier else {
277*a81c3ea0SDavid du Colombier if(ubn > maxubn)
278*a81c3ea0SDavid du Colombier maxubn = ubn;
279*a81c3ea0SDavid du Colombier pcilscan(sbn, &p->bridge);
280*a81c3ea0SDavid du Colombier }
281*a81c3ea0SDavid du Colombier }
282*a81c3ea0SDavid du Colombier
283*a81c3ea0SDavid du Colombier return maxubn;
284*a81c3ea0SDavid du Colombier }
285*a81c3ea0SDavid du Colombier
286*a81c3ea0SDavid du Colombier static void
pcicfginit(void)287*a81c3ea0SDavid du Colombier pcicfginit(void)
288*a81c3ea0SDavid du Colombier {
289*a81c3ea0SDavid du Colombier char *p;
290*a81c3ea0SDavid du Colombier int n, bno;
291*a81c3ea0SDavid du Colombier Pcidev **list;
292*a81c3ea0SDavid du Colombier
293*a81c3ea0SDavid du Colombier lock(&pcicfginitlock);
294*a81c3ea0SDavid du Colombier if(pcicfgmode != -1) {
295*a81c3ea0SDavid du Colombier unlock(&pcicfginitlock);
296*a81c3ea0SDavid du Colombier return;
297*a81c3ea0SDavid du Colombier }
298*a81c3ea0SDavid du Colombier
299*a81c3ea0SDavid du Colombier pcicfgmode = 1;
300*a81c3ea0SDavid du Colombier pcimaxdno = 19;
301*a81c3ea0SDavid du Colombier
302*a81c3ea0SDavid du Colombier fmtinstall('T', tbdffmt);
303*a81c3ea0SDavid du Colombier
304*a81c3ea0SDavid du Colombier if(p = getconf("*pcimaxbno")){
305*a81c3ea0SDavid du Colombier n = strtoul(p, 0, 0);
306*a81c3ea0SDavid du Colombier if(n < pcimaxbno)
307*a81c3ea0SDavid du Colombier pcimaxbno = n;
308*a81c3ea0SDavid du Colombier }
309*a81c3ea0SDavid du Colombier if(p = getconf("*pcimaxdno")){
310*a81c3ea0SDavid du Colombier n = strtoul(p, 0, 0);
311*a81c3ea0SDavid du Colombier if(n < pcimaxdno)
312*a81c3ea0SDavid du Colombier pcimaxdno = n;
313*a81c3ea0SDavid du Colombier }
314*a81c3ea0SDavid du Colombier
315*a81c3ea0SDavid du Colombier list = &pciroot;
316*a81c3ea0SDavid du Colombier for(bno = 0; bno <= pcimaxbno; bno++){
317*a81c3ea0SDavid du Colombier bno = pcilscan(bno, list);
318*a81c3ea0SDavid du Colombier while(*list)
319*a81c3ea0SDavid du Colombier list = &(*list)->link;
320*a81c3ea0SDavid du Colombier }
321*a81c3ea0SDavid du Colombier unlock(&pcicfginitlock);
322*a81c3ea0SDavid du Colombier }
323*a81c3ea0SDavid du Colombier
324*a81c3ea0SDavid du Colombier /* map the devince's cfg space and calculate the address */
325*a81c3ea0SDavid du Colombier static void*
pcidevcfgaddr(int tbdf,int rno)326*a81c3ea0SDavid du Colombier pcidevcfgaddr(int tbdf, int rno)
327*a81c3ea0SDavid du Colombier {
328*a81c3ea0SDavid du Colombier ulong addr;
329*a81c3ea0SDavid du Colombier ulong b, d, f, type;
330*a81c3ea0SDavid du Colombier
331*a81c3ea0SDavid du Colombier b = BUSBNO(tbdf);
332*a81c3ea0SDavid du Colombier d = BUSDNO(tbdf);
333*a81c3ea0SDavid du Colombier f = BUSFNO(tbdf);
334*a81c3ea0SDavid du Colombier if(b == 0) {
335*a81c3ea0SDavid du Colombier /* Type 0 configuration on onboard PCI bus */
336*a81c3ea0SDavid du Colombier addr = (1<<(d+11))|(f<<8)|rno;
337*a81c3ea0SDavid du Colombier type = 0x00000;
338*a81c3ea0SDavid du Colombier } else {
339*a81c3ea0SDavid du Colombier /* Type 1 configuration on offboard PCI bus */
340*a81c3ea0SDavid du Colombier addr = (b<<16)|(d<<11)|(f<<8)|rno;
341*a81c3ea0SDavid du Colombier type = 0x10000;
342*a81c3ea0SDavid du Colombier }
343*a81c3ea0SDavid du Colombier
344*a81c3ea0SDavid du Colombier /* clear aborts */
345*a81c3ea0SDavid du Colombier pci->cs |= SMasAb | SMasTgtAb;
346*a81c3ea0SDavid du Colombier
347*a81c3ea0SDavid du Colombier /* config map cfg reg to map the device's cfg space */
348*a81c3ea0SDavid du Colombier *Pcimapcfg = (addr>>16)|type;
349*a81c3ea0SDavid du Colombier
350*a81c3ea0SDavid du Colombier return (void*)(PCIDEVCFG+(addr&0xffff));
351*a81c3ea0SDavid du Colombier }
352*a81c3ea0SDavid du Colombier
353*a81c3ea0SDavid du Colombier static int
pcicfgrw8(int tbdf,int rno,int data,int read)354*a81c3ea0SDavid du Colombier pcicfgrw8(int tbdf, int rno, int data, int read)
355*a81c3ea0SDavid du Colombier {
356*a81c3ea0SDavid du Colombier int x;
357*a81c3ea0SDavid du Colombier void *addr;
358*a81c3ea0SDavid du Colombier
359*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
360*a81c3ea0SDavid du Colombier pcicfginit();
361*a81c3ea0SDavid du Colombier
362*a81c3ea0SDavid du Colombier x = -1;
363*a81c3ea0SDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
364*a81c3ea0SDavid du Colombier return x;
365*a81c3ea0SDavid du Colombier
366*a81c3ea0SDavid du Colombier lock(&pcicfglock);
367*a81c3ea0SDavid du Colombier addr = pcidevcfgaddr(tbdf, rno);
368*a81c3ea0SDavid du Colombier if(read)
369*a81c3ea0SDavid du Colombier x = *(uchar*)addr;
370*a81c3ea0SDavid du Colombier else
371*a81c3ea0SDavid du Colombier *(uchar*)addr = data;
372*a81c3ea0SDavid du Colombier unlock(&pcicfglock);
373*a81c3ea0SDavid du Colombier
374*a81c3ea0SDavid du Colombier return x;
375*a81c3ea0SDavid du Colombier }
376*a81c3ea0SDavid du Colombier
377*a81c3ea0SDavid du Colombier int
pcicfgr8(Pcidev * pcidev,int rno)378*a81c3ea0SDavid du Colombier pcicfgr8(Pcidev* pcidev, int rno)
379*a81c3ea0SDavid du Colombier {
380*a81c3ea0SDavid du Colombier return pcicfgrw8(pcidev->tbdf, rno, 0, 1);
381*a81c3ea0SDavid du Colombier }
382*a81c3ea0SDavid du Colombier
383*a81c3ea0SDavid du Colombier void
pcicfgw8(Pcidev * pcidev,int rno,int data)384*a81c3ea0SDavid du Colombier pcicfgw8(Pcidev* pcidev, int rno, int data)
385*a81c3ea0SDavid du Colombier {
386*a81c3ea0SDavid du Colombier pcicfgrw8(pcidev->tbdf, rno, data, 0);
387*a81c3ea0SDavid du Colombier }
388*a81c3ea0SDavid du Colombier
389*a81c3ea0SDavid du Colombier static int
pcicfgrw16(int tbdf,int rno,int data,int read)390*a81c3ea0SDavid du Colombier pcicfgrw16(int tbdf, int rno, int data, int read)
391*a81c3ea0SDavid du Colombier {
392*a81c3ea0SDavid du Colombier int x;
393*a81c3ea0SDavid du Colombier void *addr;
394*a81c3ea0SDavid du Colombier
395*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
396*a81c3ea0SDavid du Colombier pcicfginit();
397*a81c3ea0SDavid du Colombier
398*a81c3ea0SDavid du Colombier x = -1;
399*a81c3ea0SDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
400*a81c3ea0SDavid du Colombier return x;
401*a81c3ea0SDavid du Colombier
402*a81c3ea0SDavid du Colombier lock(&pcicfglock);
403*a81c3ea0SDavid du Colombier addr = pcidevcfgaddr(tbdf, rno);
404*a81c3ea0SDavid du Colombier if(read)
405*a81c3ea0SDavid du Colombier x = *(ushort*)addr;
406*a81c3ea0SDavid du Colombier else
407*a81c3ea0SDavid du Colombier *(ushort*)addr = data;
408*a81c3ea0SDavid du Colombier unlock(&pcicfglock);
409*a81c3ea0SDavid du Colombier
410*a81c3ea0SDavid du Colombier return x;
411*a81c3ea0SDavid du Colombier }
412*a81c3ea0SDavid du Colombier
413*a81c3ea0SDavid du Colombier int
pcicfgr16(Pcidev * pcidev,int rno)414*a81c3ea0SDavid du Colombier pcicfgr16(Pcidev* pcidev, int rno)
415*a81c3ea0SDavid du Colombier {
416*a81c3ea0SDavid du Colombier return pcicfgrw16(pcidev->tbdf, rno, 0, 1);
417*a81c3ea0SDavid du Colombier }
418*a81c3ea0SDavid du Colombier
419*a81c3ea0SDavid du Colombier void
pcicfgw16(Pcidev * pcidev,int rno,int data)420*a81c3ea0SDavid du Colombier pcicfgw16(Pcidev* pcidev, int rno, int data)
421*a81c3ea0SDavid du Colombier {
422*a81c3ea0SDavid du Colombier pcicfgrw16(pcidev->tbdf, rno, data, 0);
423*a81c3ea0SDavid du Colombier }
424*a81c3ea0SDavid du Colombier
425*a81c3ea0SDavid du Colombier static int
pcicfgrw32(int tbdf,int rno,int data,int read)426*a81c3ea0SDavid du Colombier pcicfgrw32(int tbdf, int rno, int data, int read)
427*a81c3ea0SDavid du Colombier {
428*a81c3ea0SDavid du Colombier int x;
429*a81c3ea0SDavid du Colombier void *addr;
430*a81c3ea0SDavid du Colombier
431*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
432*a81c3ea0SDavid du Colombier pcicfginit();
433*a81c3ea0SDavid du Colombier
434*a81c3ea0SDavid du Colombier x = -1;
435*a81c3ea0SDavid du Colombier if(BUSDNO(tbdf) > pcimaxdno)
436*a81c3ea0SDavid du Colombier return x;
437*a81c3ea0SDavid du Colombier
438*a81c3ea0SDavid du Colombier lock(&pcicfglock);
439*a81c3ea0SDavid du Colombier addr = pcidevcfgaddr(tbdf, rno);
440*a81c3ea0SDavid du Colombier if(read)
441*a81c3ea0SDavid du Colombier x = *(ulong*)addr;
442*a81c3ea0SDavid du Colombier else
443*a81c3ea0SDavid du Colombier *(ulong*)addr = data;
444*a81c3ea0SDavid du Colombier unlock(&pcicfglock);
445*a81c3ea0SDavid du Colombier
446*a81c3ea0SDavid du Colombier return x;
447*a81c3ea0SDavid du Colombier }
448*a81c3ea0SDavid du Colombier
449*a81c3ea0SDavid du Colombier int
pcicfgr32(Pcidev * pcidev,int rno)450*a81c3ea0SDavid du Colombier pcicfgr32(Pcidev* pcidev, int rno)
451*a81c3ea0SDavid du Colombier {
452*a81c3ea0SDavid du Colombier return pcicfgrw32(pcidev->tbdf, rno, 0, 1);
453*a81c3ea0SDavid du Colombier }
454*a81c3ea0SDavid du Colombier
455*a81c3ea0SDavid du Colombier void
pcicfgw32(Pcidev * pcidev,int rno,int data)456*a81c3ea0SDavid du Colombier pcicfgw32(Pcidev* pcidev, int rno, int data)
457*a81c3ea0SDavid du Colombier {
458*a81c3ea0SDavid du Colombier pcicfgrw32(pcidev->tbdf, rno, data, 0);
459*a81c3ea0SDavid du Colombier }
460*a81c3ea0SDavid du Colombier
461*a81c3ea0SDavid du Colombier Pcidev*
pcimatch(Pcidev * prev,int vid,int did)462*a81c3ea0SDavid du Colombier pcimatch(Pcidev* prev, int vid, int did)
463*a81c3ea0SDavid du Colombier {
464*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
465*a81c3ea0SDavid du Colombier pcicfginit();
466*a81c3ea0SDavid du Colombier
467*a81c3ea0SDavid du Colombier if(prev == nil)
468*a81c3ea0SDavid du Colombier prev = pcilist;
469*a81c3ea0SDavid du Colombier else
470*a81c3ea0SDavid du Colombier prev = prev->list;
471*a81c3ea0SDavid du Colombier
472*a81c3ea0SDavid du Colombier while(prev != nil){
473*a81c3ea0SDavid du Colombier if((vid == 0 || prev->vid == vid)
474*a81c3ea0SDavid du Colombier && (did == 0 || prev->did == did))
475*a81c3ea0SDavid du Colombier break;
476*a81c3ea0SDavid du Colombier prev = prev->list;
477*a81c3ea0SDavid du Colombier }
478*a81c3ea0SDavid du Colombier return prev;
479*a81c3ea0SDavid du Colombier }
480*a81c3ea0SDavid du Colombier
481*a81c3ea0SDavid du Colombier Pcidev*
pcimatchtbdf(int tbdf)482*a81c3ea0SDavid du Colombier pcimatchtbdf(int tbdf)
483*a81c3ea0SDavid du Colombier {
484*a81c3ea0SDavid du Colombier Pcidev *pcidev;
485*a81c3ea0SDavid du Colombier
486*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
487*a81c3ea0SDavid du Colombier pcicfginit();
488*a81c3ea0SDavid du Colombier
489*a81c3ea0SDavid du Colombier for(pcidev = pcilist; pcidev != nil; pcidev = pcidev->list) {
490*a81c3ea0SDavid du Colombier if(pcidev->tbdf == tbdf)
491*a81c3ea0SDavid du Colombier break;
492*a81c3ea0SDavid du Colombier }
493*a81c3ea0SDavid du Colombier return pcidev;
494*a81c3ea0SDavid du Colombier }
495*a81c3ea0SDavid du Colombier
496*a81c3ea0SDavid du Colombier void
pcireset(void)497*a81c3ea0SDavid du Colombier pcireset(void)
498*a81c3ea0SDavid du Colombier {
499*a81c3ea0SDavid du Colombier Pcidev *p;
500*a81c3ea0SDavid du Colombier
501*a81c3ea0SDavid du Colombier if(pcicfgmode == -1)
502*a81c3ea0SDavid du Colombier pcicfginit();
503*a81c3ea0SDavid du Colombier
504*a81c3ea0SDavid du Colombier for(p = pcilist; p != nil; p = p->list) {
505*a81c3ea0SDavid du Colombier /* don't mess with the bridges */
506*a81c3ea0SDavid du Colombier if(p->ccrb == 0x06)
507*a81c3ea0SDavid du Colombier continue;
508*a81c3ea0SDavid du Colombier pciclrbme(p);
509*a81c3ea0SDavid du Colombier }
510*a81c3ea0SDavid du Colombier }
511*a81c3ea0SDavid du Colombier
512*a81c3ea0SDavid du Colombier void
pcisetioe(Pcidev * p)513*a81c3ea0SDavid du Colombier pcisetioe(Pcidev* p)
514*a81c3ea0SDavid du Colombier {
515*a81c3ea0SDavid du Colombier p->pcr |= IOen;
516*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
517*a81c3ea0SDavid du Colombier }
518*a81c3ea0SDavid du Colombier
519*a81c3ea0SDavid du Colombier void
pciclrioe(Pcidev * p)520*a81c3ea0SDavid du Colombier pciclrioe(Pcidev* p)
521*a81c3ea0SDavid du Colombier {
522*a81c3ea0SDavid du Colombier p->pcr &= ~IOen;
523*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
524*a81c3ea0SDavid du Colombier }
525*a81c3ea0SDavid du Colombier
526*a81c3ea0SDavid du Colombier void
pcisetbme(Pcidev * p)527*a81c3ea0SDavid du Colombier pcisetbme(Pcidev* p)
528*a81c3ea0SDavid du Colombier {
529*a81c3ea0SDavid du Colombier p->pcr |= MASen;
530*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
531*a81c3ea0SDavid du Colombier }
532*a81c3ea0SDavid du Colombier
533*a81c3ea0SDavid du Colombier void
pciclrbme(Pcidev * p)534*a81c3ea0SDavid du Colombier pciclrbme(Pcidev* p)
535*a81c3ea0SDavid du Colombier {
536*a81c3ea0SDavid du Colombier p->pcr &= ~MASen;
537*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
538*a81c3ea0SDavid du Colombier }
539*a81c3ea0SDavid du Colombier
540*a81c3ea0SDavid du Colombier void
pcisetmwi(Pcidev * p)541*a81c3ea0SDavid du Colombier pcisetmwi(Pcidev* p)
542*a81c3ea0SDavid du Colombier {
543*a81c3ea0SDavid du Colombier p->pcr |= MemWrInv;
544*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
545*a81c3ea0SDavid du Colombier }
546*a81c3ea0SDavid du Colombier
547*a81c3ea0SDavid du Colombier void
pciclrmwi(Pcidev * p)548*a81c3ea0SDavid du Colombier pciclrmwi(Pcidev* p)
549*a81c3ea0SDavid du Colombier {
550*a81c3ea0SDavid du Colombier p->pcr &= ~MemWrInv;
551*a81c3ea0SDavid du Colombier pcicfgw16(p, PciPCR, p->pcr);
552*a81c3ea0SDavid du Colombier }
553*a81c3ea0SDavid du Colombier
554*a81c3ea0SDavid du Colombier static int
pcigetpmrb(Pcidev * p)555*a81c3ea0SDavid du Colombier pcigetpmrb(Pcidev* p)
556*a81c3ea0SDavid du Colombier {
557*a81c3ea0SDavid du Colombier int ptr;
558*a81c3ea0SDavid du Colombier
559*a81c3ea0SDavid du Colombier if(p->pmrb != 0)
560*a81c3ea0SDavid du Colombier return p->pmrb;
561*a81c3ea0SDavid du Colombier p->pmrb = -1;
562*a81c3ea0SDavid du Colombier
563*a81c3ea0SDavid du Colombier /*
564*a81c3ea0SDavid du Colombier * If there are no extended capabilities implemented,
565*a81c3ea0SDavid du Colombier * (bit 4 in the status register) assume there's no standard
566*a81c3ea0SDavid du Colombier * power management method.
567*a81c3ea0SDavid du Colombier * Find the capabilities pointer based on PCI header type.
568*a81c3ea0SDavid du Colombier */
569*a81c3ea0SDavid du Colombier if(!(pcicfgr16(p, PciPSR) & 0x0010))
570*a81c3ea0SDavid du Colombier return -1;
571*a81c3ea0SDavid du Colombier switch(pcicfgr8(p, PciHDT)){
572*a81c3ea0SDavid du Colombier default:
573*a81c3ea0SDavid du Colombier return -1;
574*a81c3ea0SDavid du Colombier case 0: /* all other */
575*a81c3ea0SDavid du Colombier case 1: /* PCI to PCI bridge */
576*a81c3ea0SDavid du Colombier ptr = 0x34;
577*a81c3ea0SDavid du Colombier break;
578*a81c3ea0SDavid du Colombier case 2: /* CardBus bridge */
579*a81c3ea0SDavid du Colombier ptr = 0x14;
580*a81c3ea0SDavid du Colombier break;
581*a81c3ea0SDavid du Colombier }
582*a81c3ea0SDavid du Colombier ptr = pcicfgr32(p, ptr);
583*a81c3ea0SDavid du Colombier
584*a81c3ea0SDavid du Colombier while(ptr != 0){
585*a81c3ea0SDavid du Colombier /*
586*a81c3ea0SDavid du Colombier * Check for validity.
587*a81c3ea0SDavid du Colombier * Can't be in standard header and must be double
588*a81c3ea0SDavid du Colombier * word aligned.
589*a81c3ea0SDavid du Colombier */
590*a81c3ea0SDavid du Colombier if(ptr < 0x40 || (ptr & ~0xFC))
591*a81c3ea0SDavid du Colombier return -1;
592*a81c3ea0SDavid du Colombier if(pcicfgr8(p, ptr) == 0x01){
593*a81c3ea0SDavid du Colombier p->pmrb = ptr;
594*a81c3ea0SDavid du Colombier return ptr;
595*a81c3ea0SDavid du Colombier }
596*a81c3ea0SDavid du Colombier
597*a81c3ea0SDavid du Colombier ptr = pcicfgr8(p, ptr+1);
598*a81c3ea0SDavid du Colombier }
599*a81c3ea0SDavid du Colombier
600*a81c3ea0SDavid du Colombier return -1;
601*a81c3ea0SDavid du Colombier }
602*a81c3ea0SDavid du Colombier
603*a81c3ea0SDavid du Colombier int
pcigetpms(Pcidev * p)604*a81c3ea0SDavid du Colombier pcigetpms(Pcidev* p)
605*a81c3ea0SDavid du Colombier {
606*a81c3ea0SDavid du Colombier int pmcsr, ptr;
607*a81c3ea0SDavid du Colombier
608*a81c3ea0SDavid du Colombier if((ptr = pcigetpmrb(p)) == -1)
609*a81c3ea0SDavid du Colombier return -1;
610*a81c3ea0SDavid du Colombier
611*a81c3ea0SDavid du Colombier /*
612*a81c3ea0SDavid du Colombier * Power Management Register Block:
613*a81c3ea0SDavid du Colombier * offset 0: Capability ID
614*a81c3ea0SDavid du Colombier * 1: next item pointer
615*a81c3ea0SDavid du Colombier * 2: capabilities
616*a81c3ea0SDavid du Colombier * 4: control/status
617*a81c3ea0SDavid du Colombier * 6: bridge support extensions
618*a81c3ea0SDavid du Colombier * 7: data
619*a81c3ea0SDavid du Colombier */
620*a81c3ea0SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
621*a81c3ea0SDavid du Colombier
622*a81c3ea0SDavid du Colombier return pmcsr & 0x0003;
623*a81c3ea0SDavid du Colombier }
624*a81c3ea0SDavid du Colombier
625*a81c3ea0SDavid du Colombier int
pcisetpms(Pcidev * p,int state)626*a81c3ea0SDavid du Colombier pcisetpms(Pcidev* p, int state)
627*a81c3ea0SDavid du Colombier {
628*a81c3ea0SDavid du Colombier int ostate, pmc, pmcsr, ptr;
629*a81c3ea0SDavid du Colombier
630*a81c3ea0SDavid du Colombier if((ptr = pcigetpmrb(p)) == -1)
631*a81c3ea0SDavid du Colombier return -1;
632*a81c3ea0SDavid du Colombier
633*a81c3ea0SDavid du Colombier pmc = pcicfgr16(p, ptr+2);
634*a81c3ea0SDavid du Colombier pmcsr = pcicfgr16(p, ptr+4);
635*a81c3ea0SDavid du Colombier ostate = pmcsr & 0x0003;
636*a81c3ea0SDavid du Colombier pmcsr &= ~0x0003;
637*a81c3ea0SDavid du Colombier
638*a81c3ea0SDavid du Colombier switch(state){
639*a81c3ea0SDavid du Colombier default:
640*a81c3ea0SDavid du Colombier return -1;
641*a81c3ea0SDavid du Colombier case 0:
642*a81c3ea0SDavid du Colombier break;
643*a81c3ea0SDavid du Colombier case 1:
644*a81c3ea0SDavid du Colombier if(!(pmc & 0x0200))
645*a81c3ea0SDavid du Colombier return -1;
646*a81c3ea0SDavid du Colombier break;
647*a81c3ea0SDavid du Colombier case 2:
648*a81c3ea0SDavid du Colombier if(!(pmc & 0x0400))
649*a81c3ea0SDavid du Colombier return -1;
650*a81c3ea0SDavid du Colombier break;
651*a81c3ea0SDavid du Colombier case 3:
652*a81c3ea0SDavid du Colombier break;
653*a81c3ea0SDavid du Colombier }
654*a81c3ea0SDavid du Colombier pmcsr |= state;
655*a81c3ea0SDavid du Colombier pcicfgw16(p, ptr+4, pmcsr);
656*a81c3ea0SDavid du Colombier
657*a81c3ea0SDavid du Colombier return ostate;
658*a81c3ea0SDavid du Colombier }
659*a81c3ea0SDavid du Colombier
660*a81c3ea0SDavid du Colombier int
pcisubirq(int tbdf)661*a81c3ea0SDavid du Colombier pcisubirq(int tbdf)
662*a81c3ea0SDavid du Colombier {
663*a81c3ea0SDavid du Colombier return Pciintrbase + pcicfgrw8(tbdf, PciINTP, 0, 1);
664*a81c3ea0SDavid du Colombier }
665