vmware-svga/lib/metalkit/pci.c
2009-04-13 07:05:42 +00:00

244 lines
6.4 KiB
C

/* -*- Mode: C; c-basic-offset: 3 -*-
*
* pci.c - Simple PCI configuration interface. This implementation
* only supports type 1 accesses to configuration space,
* and it ignores the PCI BIOS.
*
* This file is part of Metalkit, a simple collection of modules for
* writing software that runs on the bare metal. Get the latest code
* at http://svn.navi.cx/misc/trunk/metalkit/
*
* Copyright (c) 2008-2009 Micah Dowty
*
* 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 AUTHORS OR COPYRIGHT
* HOLDERS 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.
*/
#include "pci.h"
#include "io.h"
/*
* There can be up to 256 PCI busses, but it takes a noticeable
* amount of time to scan that whole space. Limit the number of
* supported busses to something more reasonable...
*/
#define PCI_MAX_BUSSES 0x20
#define PCI_REG_CONFIG_ADDRESS 0xCF8
#define PCI_REG_CONFIG_DATA 0xCFC
/*
* PCIConfigPackAddress --
*
* Pack a 32-bit CONFIG_ADDRESS value.
*/
static fastcall uint32
PCIConfigPackAddress(const PCIAddress *addr, uint16 offset)
{
const uint32 enableBit = 0x80000000UL;
return (((uint32)addr->bus << 16) |
((uint32)addr->device << 11) |
((uint32)addr->function << 8) |
offset | enableBit);
}
/*
* PCI_ConfigRead32 --
* PCI_ConfigRead16 --
* PCI_ConfigRead8 --
* PCI_ConfigWrite32 --
* PCI_ConfigWrite16 --
* PCI_ConfigWrite8 --
*
* Access a device's PCI configuration space, using configuration
* mechanism #1. All new machines should use method #1, method #2
* is for legacy compatibility.
*
* See http://www.osdev.org/wiki/PCI
*/
fastcall uint32
PCI_ConfigRead32(const PCIAddress *addr, uint16 offset)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
return IO_In32(PCI_REG_CONFIG_DATA);
}
fastcall uint16
PCI_ConfigRead16(const PCIAddress *addr, uint16 offset)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
return IO_In16(PCI_REG_CONFIG_DATA);
}
fastcall uint8
PCI_ConfigRead8(const PCIAddress *addr, uint16 offset)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
return IO_In8(PCI_REG_CONFIG_DATA);
}
fastcall void
PCI_ConfigWrite32(const PCIAddress *addr, uint16 offset, uint32 data)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
IO_Out32(PCI_REG_CONFIG_DATA, data);
}
fastcall void
PCI_ConfigWrite16(const PCIAddress *addr, uint16 offset, uint16 data)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
IO_Out16(PCI_REG_CONFIG_DATA, data);
}
fastcall void
PCI_ConfigWrite8(const PCIAddress *addr, uint16 offset, uint8 data)
{
IO_Out32(PCI_REG_CONFIG_ADDRESS, PCIConfigPackAddress(addr, offset));
IO_Out8(PCI_REG_CONFIG_DATA, data);
}
/*
* PCI_ScanBus --
*
* Scan the PCI bus for devices. Before starting a scan,
* the caller should zero the PCIScanState structure.
* Every time this function is called, it returns the next
* device in sequence.
*
* Returns TRUE if a device was found, leaving that device's
* vendorId, productId, and address in 'state'.
*
* Returns FALSE if there are no more devices.
*/
fastcall Bool
PCI_ScanBus(PCIScanState *state)
{
PCIConfigSpace config;
for (;;) {
config.words[0] = PCI_ConfigRead32(&state->nextAddr, 0);
state->addr = state->nextAddr;
if (++state->nextAddr.function == 0x8) {
state->nextAddr.function = 0;
if (++state->nextAddr.device == 0x20) {
state->nextAddr.device = 0;
if (++state->nextAddr.bus == PCI_MAX_BUSSES) {
return FALSE;
}
}
}
if (config.words[0] != 0xFFFFFFFFUL) {
state->vendorId = config.vendorId;
state->deviceId = config.deviceId;
return TRUE;
}
}
}
/*
* PCI_FindDevice --
*
* Scan the PCI bus for a device with a specific vendor and device ID.
*
* On success, returns TRUE and puts the device address into 'addrOut'.
* If the device was not found, returns FALSE.
*/
fastcall Bool
PCI_FindDevice(uint16 vendorId, uint16 deviceId, PCIAddress *addrOut)
{
PCIScanState busScan = {};
while (PCI_ScanBus(&busScan)) {
if (busScan.vendorId == vendorId && busScan.deviceId == deviceId) {
*addrOut = busScan.addr;
return TRUE;
}
}
return FALSE;
}
/*
* PCI_SetBAR --
*
* Set one of a device's Base Address Registers to the provided value.
*/
fastcall void
PCI_SetBAR(const PCIAddress *addr, int index, uint32 value)
{
PCI_ConfigWrite32(addr, offsetof(PCIConfigSpace, BAR[index]), value);
}
/*
* PCI_GetBARAddr --
*
* Get the current address set in one of the device's Base Address Registers.
* (We mask off the lower 2 bits, which hold memory type flags.)
*/
fastcall uint32
PCI_GetBARAddr(const PCIAddress *addr, int index)
{
return PCI_ConfigRead32(addr, offsetof(PCIConfigSpace, BAR[index])) & ~3;
}
/*
* PCI_SetMemEnable --
*
* Enable or disable a device's memory and IO space. This must be
* called to enable a device's resources after setting all
* applicable BARs. Also enables/disables bus mastering.
*/
fastcall void
PCI_SetMemEnable(const PCIAddress *addr, Bool enable)
{
uint16 command = PCI_ConfigRead16(addr, offsetof(PCIConfigSpace, command));
/* Mem space enable, IO space enable, bus mastering. */
const uint16 flags = 0x0007;
if (enable) {
command |= flags;
} else {
command &= ~flags;
}
PCI_ConfigWrite16(addr, offsetof(PCIConfigSpace, command), command);
}