summaryrefslogtreecommitdiff
path: root/hw/xfree86/os-support/bus/460gxPCI.c
blob: 3b16f401ae90f22b34ff70f2bec90627e6d1cd35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/bus/460gxPCI.c,v 1.4 2003/08/29 21:08:06 tsi Exp $ */
/*
 * Copyright (C) 2002-2003 The XFree86 Project, Inc.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of the XFree86 Project shall
 * not be used in advertising or otherwise to promote the sale, use or other
 * dealings in this Software without prior written authorization from the
 * XFree86 Project.
 */

/*
 * This file contains the glue necessary for support of Intel's 460GX chipset.
 */

#include "460gxPCI.h"
#include "xf86.h"
#include "Pci.h"

/* 460GX register definitions */
/* SAC at 0:10:0 */
#define CBN		0x0040
/* SAC at CBN:0:0 */
#define DEVNPRES	0x0070
/* SAC at CBN:1[0-7]:0 */
#define BUSNO		0x0048
#define SUBNO		0x0049
#define VGASE		0x0080
#define PCIS		0x0084
#define IOR		0x008C
#define IORD		0x008E	/* CBN:10:0 only */
/* PXB at CBN:1[0-7]:1 */
#define ERRCMD		0x0046

static int cbn_460gx = -1;
static CARD32 cbdevs_460gx = 0;
static CARD16 iord_460gx;
static int busno_460gx[8], subno_460gx[8];
static CARD8 pcis_460gx[8], ior_460gx[8];
static CARD8 has_err_460gx[8], err_460gx[8];
static CARD8 iomap_460gx[16];		/* One for each 4k */
static pciBusFuncs_t BusFuncs_460gx;

static pciConfigPtr
Verify460GXBus(int bus)
{
    pciConfigPtr pPCI;

    if ((bus < 0) || (bus >= pciNumBuses) ||
	!pciBusInfo[bus] || !(pPCI = pciBusInfo[bus]->bridge) ||
	(pPCI->busnum != cbn_460gx) || (pPCI->funcnum != 0) ||
	(pPCI->devnum < 0x10) || (pPCI->devnum > 0x17))
	return NULL;

    return pPCI;
}

/*
 * This function is called to emulate the various settings in a P2P or CardBus
 * bridge's control register using one of a 460GX's SAC host bridges.
 */
static CARD16
Control460GXBridge(int bus, CARD16 mask, CARD16 value)
{
    pciConfigPtr pPCI;
    PCITAG tag;
    CARD16 current = 0;
    CARD8  tmp;

    if ((pPCI = Verify460GXBus(bus))) {
	/* Start with VGA enablement */
	tmp = pciReadByte(pPCI->tag, VGASE);
	if (tmp & 0x01) {
	    current |= PCI_PCI_BRIDGE_VGA_EN;
	    if ((mask & PCI_PCI_BRIDGE_VGA_EN) &&
		!(value & PCI_PCI_BRIDGE_VGA_EN))
		pciWriteByte(pPCI->tag, VGASE, tmp & ~0x01);
	} else {
	    if (mask & value & PCI_PCI_BRIDGE_VGA_EN)
		pciWriteByte(pPCI->tag, VGASE, tmp | 0x01);
	}

	/* Move on to master abort failure enablement */
	if (has_err_460gx[pPCI->devnum - 0x10]) {
	    tag = PCI_MAKE_TAG(pPCI->busnum, pPCI->devnum, pPCI->funcnum + 1);
	    tmp = pciReadByte(tag, ERRCMD);
	    if (tmp & 0x01) {
		current |= PCI_PCI_BRIDGE_MASTER_ABORT_EN;
		if ((mask & PCI_PCI_BRIDGE_MASTER_ABORT_EN) &&
		    !(value & PCI_PCI_BRIDGE_MASTER_ABORT_EN))
		    pciWriteByte(tag, ERRCMD, tmp & ~0x01);
	    } else {
		if (mask & value & PCI_PCI_BRIDGE_MASTER_ABORT_EN)
		    pciWriteByte(tag, ERRCMD, tmp | 0x01);
	    }
	}

	/* Put emulation of any other P2P bridge control here */
    }

    return (current & ~mask) | (value & mask);
}

/*
 * Retrieve various bus numbers representing the connections provided by 460GX
 * host bridges.
 */
static void
Get460GXBridgeBuses(int bus, int *primary, int *secondary, int *subordinate)
{
    pciConfigPtr pPCI = Verify460GXBus(bus);
    int i;

    /* The returned bus numbers are initialised by the caller */

    if (!pPCI)
	return;

    i = pPCI->devnum - 0x10;

    /* These are not modified, so no need to re-read them */
    if (primary)
	*primary = pPCI->busnum;
    if (secondary)
	*secondary = busno_460gx[i];
    if (subordinate)
	*subordinate = subno_460gx[i];
}

/* Retrieves a list of the resources routed to a host bridge's secondary bus */
static void
Get460GXBridgeResources(int bus,
			pointer *ppIoRes,
			pointer *ppMemRes,
			pointer *ppPmemRes)
{
    pciConfigPtr pPCI = Verify460GXBus(bus);
    resRange range;
    unsigned int i, j;

    if (ppIoRes) {
	xf86FreeResList(*ppIoRes);
	*ppIoRes = NULL;

	if (pPCI) {
	    for (i = 0;  i <= 0x0F;  i++) {
		if (iomap_460gx[i] != pPCI->devnum)
		    continue;

		RANGE(range, i << 12, ((i + 1) << 12) - 1,
		      RANGE_TYPE(ResExcIoBlock, 0));
		*ppIoRes = xf86AddResToList(*ppIoRes, &range, -1);
	    }
	}
    }

    if (ppMemRes) {
	xf86FreeResList(*ppMemRes);
	*ppMemRes = NULL;

	if (pPCI) {
	    if (!(i = (pPCI->devnum - 0x10)))
		j = 127;	/* (4GB - 32M) / 32M */
	    else
		j = pcis_460gx[i - 1] & 0x7F;

	    i = pcis_460gx[i] & 0x7F;
	    if (i < j) {
		RANGE(range, i << 25, (j << 25) - 1,
		    RANGE_TYPE(ResExcMemBlock, 0));
		*ppMemRes = xf86AddResToList(*ppMemRes, &range, -1);
	    }
	}
    }

    if (ppPmemRes) {
	xf86FreeResList(*ppPmemRes);
	*ppPmemRes = NULL;
    }
}

/*
 * This checks for, and validates, the presence of the 460GX chipset, and sets
 * cbn_460gx to a positive value accordingly.  This function returns TRUE if
 * the chipset scan is to be stopped, or FALSE if the scan is to move on to the
 * next chipset.
 */
Bool
xf86PreScan460GX(void)
{
    pciBusInfo_t *pBusInfo;
    PCITAG tag;
    CARD32 tmp;
    int i, devno;

    /* Bus zero should already be set up */
    if (!(pBusInfo = pciBusInfo[0])) {
	cbn_460gx = -1;
	return FALSE;
    }

    /* First look for a 460GX's primary host bridge */
    tag = PCI_MAKE_TAG(0, 0x10, 0);
    if (pciReadLong(tag, PCI_ID_REG) != DEVID(VENDOR_INTEL, CHIP_460GX_SAC)) {
	cbn_460gx = -1;
	return FALSE;
    }

    /* Get CBN (Chipset bus number) */
    if (!(cbn_460gx = (unsigned int)pciReadByte(tag, CBN))) {
	/* Sanity check failed */
	cbn_460gx = -1;
	return TRUE;
    }

    if (pciNumBuses <= cbn_460gx)
	pciNumBuses = cbn_460gx + 1;

    /* Set up bus CBN */
    if (!pciBusInfo[cbn_460gx]) {
	pciBusInfo[cbn_460gx] = xnfalloc(sizeof(pciBusInfo_t));
	*pciBusInfo[cbn_460gx] = *pBusInfo;
    }

    tag = PCI_MAKE_TAG(cbn_460gx, 0, 0);
    if (pciReadLong(tag, PCI_ID_REG) != DEVID(VENDOR_INTEL, CHIP_460GX_SAC)) {
	/* Sanity check failed */
	cbn_460gx = -1;
	return TRUE;
    }

    /*
     * Find out which CBN devices the firmware thinks are present.  Of these,
     * we are only interested in devices 0x10 through 0x17.
     */
    cbdevs_460gx = pciReadLong(tag, DEVNPRES);

    for (i = 0, devno = 0x10;  devno <= 0x17;  i++, devno++) {
	tag = PCI_MAKE_TAG(cbn_460gx, devno, 0);
	if (pciReadLong(tag, PCI_ID_REG) !=
	    DEVID(VENDOR_INTEL, CHIP_460GX_SAC)) {
	    /* Sanity check failed */
	    cbn_460gx = -1;
	    return TRUE;
	}

	if (devno == 0x10)
	    iord_460gx = pciReadWord(tag, IORD);

	busno_460gx[i] = (unsigned int)pciReadByte(tag, BUSNO);
	subno_460gx[i] = (unsigned int)pciReadByte(tag, SUBNO);
	pcis_460gx[i] = pciReadByte(tag, PCIS);
	ior_460gx[i] = pciReadByte(tag, IOR);

	has_err_460gx[i] = err_460gx[i] = 0;	/* Insurance */

	tag = PCI_MAKE_TAG(cbn_460gx, devno, 1);
	tmp = pciReadLong(tag, PCI_ID_REG);
	switch (tmp) {
	case DEVID(VENDOR_INTEL, CHIP_460GX_PXB):
	case DEVID(VENDOR_INTEL, CHIP_460GX_WXB):
	    if (cbdevs_460gx & (1 << devno)) {
		/* Sanity check failed */
		cbn_460gx = -1;
		return TRUE;
	    }

	    /*
	     * XXX  I don't have WXB docs, but PCI register dumps indicate that
	     * the registers we are interested in are consistent with those of
	     * the PXB.
	     */
	    err_460gx[i] = pciReadByte(tag, ERRCMD);
	    has_err_460gx[i] = 1;
	    break;

	case DEVID(VENDOR_INTEL, CHIP_460GX_GXB_1):
	    if (cbdevs_460gx & (1 << devno)) {
		/* Sanity check failed */
		cbn_460gx = -1;
		return TRUE;
	    }

	    /*
	     * XXX  GXB isn't documented to have an ERRCMD register, nor any
	     * other means of failing master aborts.  For now, assume master
	     * aborts are always allowed to complete normally.
	     */
	    break;

	default:
	    if (((CARD16)(tmp + 1U) <= (CARD16)1U) &&
		(cbdevs_460gx & (1U << devno)))
		break;
	    /* Sanity check failed */
	    cbn_460gx = -1;
	    return TRUE;
	}
    }

    /* Allow master aborts to complete normally */
    for (i = 0, devno = 0x10;  devno <= 0x17;  i++, devno++) {
	if (!(err_460gx[i] & 0x01))
	    continue;

	pciWriteByte(PCI_MAKE_TAG(cbn_460gx, devno, 1),
		     ERRCMD, err_460gx[i] & ~0x01);
    }

    /*
     * The 460GX spec says that any access to buses higher than CBN will be
     * master-aborted.  It seems possible however that this is not the case in
     * all 460GX implementations.  For now, limit the bus scan to CBN, unless
     * we have already found a higher bus number.
     */
    for (i = 0;  subno_460gx[i] < cbn_460gx;  ) {
	if (++i < 8)
	    continue;

	pciMaxBusNum = cbn_460gx + 1;
	break;
    }

    return TRUE;
}

/* This does some 460GX-related processing after the PCI bus scan */
void
xf86PostScan460GX(void)
{
    pciConfigPtr pPCI, *ppPCI;
    pciBusInfo_t *pBusInfo;
    int i, j, devno;

    if (cbn_460gx <= 0)
	return;

    /* Set up our extra bus functions */
    BusFuncs_460gx = *(pciBusInfo[0]->funcs);
    BusFuncs_460gx.pciControlBridge = Control460GXBridge;
    BusFuncs_460gx.pciGetBridgeBuses = Get460GXBridgeBuses;
    BusFuncs_460gx.pciGetBridgeResources = Get460GXBridgeResources;

    /*
     * Mark all host bridges so that they are ignored by the upper-level
     * xf86GetPciBridgeInfo() function.  This marking is later clobbered by the
     * tail end of xf86scanpci() for those bridges that actually have bus
     * segments associated with them.
     */
    ppPCI = xf86scanpci(0);	/* Recursion is only apparent */
    while ((pPCI = *ppPCI++)) {
	if ((pPCI->pci_base_class == PCI_CLASS_BRIDGE) &&
	    (pPCI->pci_sub_class == PCI_SUBCLASS_BRIDGE_HOST))
	    pPCI->businfo = HOST_NO_BUS;
    }

    ppPCI = xf86scanpci(0);	/* Recursion is only apparent */
    j = 0;

    /*
     * Fix up CBN bus linkage.  This is somewhat arbitrary.  The bridge chosen
     * for this must be a CBN device so that bus CBN can be recognised as the
     * root segment.  It also cannot be any of the bus expanders (devices
     * CBN:0x10:0 through CBN:0x17:0 nor any of their functions).  For now, we
     * chose the SAC host bridge at CBN:0:0.
     */
    pBusInfo = pciBusInfo[cbn_460gx];
    pBusInfo->bridge = pciBusInfo[0]->bridge;	/* Just in case */
    while ((pPCI = *ppPCI++)) {
	if (pPCI->busnum < cbn_460gx)
	    continue;
	if (pPCI->busnum > cbn_460gx)
	    break;
	if (pPCI->devnum < 0)
	    continue;
	if (pPCI->devnum > 0)
	    break;
	if (pPCI->funcnum < 0)
	    continue;
	if (pPCI->funcnum > 0)
	    break;

	pBusInfo->bridge = pPCI;
	pBusInfo->secondary = FALSE;
	pBusInfo->primary_bus = cbn_460gx;
	break;
    }

    for (i = 0, devno = 0x10;  devno <= 0x17;  i++, devno++) {
	/* Restore ERRCMD registers */
	if (err_460gx[i] & 0x01)
	    pciWriteByte(PCI_MAKE_TAG(cbn_460gx, devno, 1),
			 ERRCMD, err_460gx[i]);

	if (!(cbdevs_460gx & (1 << devno))) {
	    while ((pPCI = *ppPCI++)) {
		if (pPCI->busnum < cbn_460gx)
		    continue;
		if (pPCI->busnum > cbn_460gx)
		    break;
		if (pPCI->devnum < devno)
		    continue;
		if (pPCI->devnum > devno)
		    break;
		if (pPCI->funcnum < 0)
		    continue;
		if (pPCI->funcnum > 0)
		    break;

		if ((pBusInfo = pciBusInfo[busno_460gx[i]]))
		    break;

		/* Fix bus linkage */
		pBusInfo->bridge = pPCI;
		pBusInfo->secondary = TRUE;
		pBusInfo->primary_bus = cbn_460gx;

		/* Plug in chipset routines */
		pBusInfo->funcs = &BusFuncs_460gx;
		break;
	    }
	}

	/* Decode IOR registers */
	for(;  j <= (ior_460gx[i] & 0x0F);  j++)
	    iomap_460gx[j] = devno;
    }

    /* The bottom 4k of I/O space is always routed to PCI0a */
    iomap_460gx[0] = 0x10;

    /* Decode IORD register */
    for (j = 1;  j <= 0x0F;  j++)
	if (iord_460gx & (1 << j))
	    iomap_460gx[j] = 0x10;
}