470 lines
12 KiB
C
470 lines
12 KiB
C
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
#ifdef HAVE_XORG_CONFIG_H
|
||
|
#include <xorg-config.h>
|
||
|
#endif
|
||
|
|
||
|
#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
|
||
|
xorgProbe460GX(scanpciWrapperOpt flags)
|
||
|
{
|
||
|
pciBusInfo_t *pBusInfo;
|
||
|
PCITAG tag;
|
||
|
|
||
|
/* 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)) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
cbn_460gx = -1;
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
xf86PreScan460GX(void)
|
||
|
{
|
||
|
pciBusInfo_t *pBusInfo;
|
||
|
PCITAG tag;
|
||
|
CARD32 tmp;
|
||
|
int i, devno;
|
||
|
|
||
|
if (!(pBusInfo = pciBusInfo[0]))
|
||
|
return;
|
||
|
|
||
|
/* Get CBN (Chipset bus number) */
|
||
|
tag = PCI_MAKE_TAG(0, 0x10, 0);
|
||
|
if (!(cbn_460gx = (unsigned int)pciReadByte(tag, CBN))) {
|
||
|
/* Sanity check failed */
|
||
|
cbn_460gx = -1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
}
|
||
|
|
||
|
/* 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;
|
||
|
}
|