sortix-mirror/kernel/pci.cpp

215 lines
5.6 KiB
C++
Raw Normal View History

/*******************************************************************************
2011-08-05 12:25:00 +00:00
Copyright(C) Jonas 'Sortie' Termansen 2011, 2012, 2013.
2011-08-05 12:25:00 +00:00
This file is part of Sortix.
2011-08-05 12:25:00 +00:00
Sortix is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.
2011-08-05 12:25:00 +00:00
Sortix is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.
2011-08-05 12:25:00 +00:00
You should have received a copy of the GNU General Public License along with
Sortix. If not, see <http://www.gnu.org/licenses/>.
2011-08-05 12:25:00 +00:00
pci.cpp
Functions for handling PCI devices.
2011-08-05 12:25:00 +00:00
*******************************************************************************/
2011-08-05 12:25:00 +00:00
#include <assert.h>
#include <endian.h>
2013-05-12 22:23:24 +00:00
#include <sortix/kernel/cpu.h>
2013-10-27 00:42:10 +00:00
#include <sortix/kernel/kernel.h>
#include <sortix/kernel/pci.h>
2011-08-05 12:25:00 +00:00
namespace Sortix {
namespace PCI {
2011-08-05 12:25:00 +00:00
const uint16_t CONFIG_ADDRESS = 0xCF8;
const uint16_t CONFIG_DATA = 0xCFC;
2011-08-05 12:25:00 +00:00
uint32_t MakeDevAddr(uint8_t bus, uint8_t slot, uint8_t func)
{
//assert(bus < 1UL<<8UL); // bus is 8 bit anyways.
assert(slot < 1UL<<5UL);
assert(func < 1UL<<3UL);
return func << 8U | slot << 11U | bus << 16U | 1 << 31U;
}
2011-08-05 12:25:00 +00:00
void SplitDevAddr(uint32_t devaddr, uint8_t* vals /* bus, slot, func */)
{
vals[0] = devaddr >> 16U & ((1UL<<8UL)-1);
vals[1] = devaddr >> 11U & ((1UL<<3UL)-1);
vals[2] = devaddr >> 8U & ((1UL<<5UL)-1);
}
2011-08-05 12:25:00 +00:00
uint32_t ReadRaw32(uint32_t devaddr, uint8_t off)
{
CPU::OutPortL(CONFIG_ADDRESS, devaddr + off);
return CPU::InPortL(CONFIG_DATA);
}
2011-08-05 12:25:00 +00:00
void WriteRaw32(uint32_t devaddr, uint8_t off, uint32_t val)
{
CPU::OutPortL(CONFIG_ADDRESS, devaddr + off);
CPU::OutPortL(CONFIG_DATA, val);
}
2011-08-05 12:25:00 +00:00
uint32_t Read32(uint32_t devaddr, uint8_t off)
{
return le32toh(ReadRaw32(devaddr, off));
}
2011-08-05 12:25:00 +00:00
void Write32(uint32_t devaddr, uint8_t off, uint32_t val)
{
WriteRaw32(devaddr, off, htole32(val));
}
2011-08-05 12:25:00 +00:00
uint16_t Read16(uint32_t devaddr, uint8_t off)
{
assert((off & 0x1) == 0);
uint8_t alignedoff = off & ~0x3;
union { uint16_t val16[2]; uint32_t val32; };
val32 = ReadRaw32(devaddr, alignedoff);
uint16_t ret = off & 0x2 ? val16[0] : val16[1];
return le16toh(ret);
}
2011-08-05 12:25:00 +00:00
uint8_t Read8(uint32_t devaddr, uint8_t off)
{
uint8_t alignedoff = off & ~0x1;
union { uint8_t val8[2]; uint32_t val16; };
val16 = htole16(Read16(devaddr, alignedoff));
uint8_t ret = off & 0x1 ? val8[0] : val8[1];
return ret;
}
2011-08-05 12:25:00 +00:00
uint32_t CheckDevice(uint8_t bus, uint8_t slot, uint8_t func)
{
return Read32(MakeDevAddr(bus, slot, func), 0x0);
}
2011-08-05 12:25:00 +00:00
pciid_t GetDeviceId(uint32_t devaddr)
{
pciid_t ret;
ret.deviceid = Read16(devaddr, 0x00);
ret.vendorid = Read16(devaddr, 0x02);
return ret;
}
2011-08-05 12:25:00 +00:00
pcitype_t GetDeviceType(uint32_t devaddr)
{
pcitype_t ret;
ret.classid = Read8(devaddr, 0x08);
ret.subclassid = Read8(devaddr, 0x09);
ret.progif = Read8(devaddr, 0x0A);
ret.revid = Read8(devaddr, 0x0B);
return ret;
}
2011-08-05 12:25:00 +00:00
static bool MatchesSearchCriteria(uint32_t devaddr, pcifind_t pcifind)
{
pciid_t id = GetDeviceId(devaddr);
if ( id.vendorid == 0xFFFF && id.deviceid == 0xFFFF )
return false;
pcitype_t type = GetDeviceType(devaddr);
if ( pcifind.vendorid != 0xFFFF && id.vendorid != pcifind.vendorid )
return false;
if ( pcifind.deviceid != 0xFFFF && id.deviceid != pcifind.deviceid )
return false;
if ( pcifind.classid != 0xFF && type.classid != pcifind.classid )
return false;
if ( pcifind.subclassid != 0xFF && type.subclassid != pcifind.subclassid )
return false;
if ( pcifind.progif != 0xFF && type.progif != pcifind.progif )
return false;
if ( pcifind.revid != 0xFF && type.revid != pcifind.revid )
return false;
return true;
}
2011-08-05 12:25:00 +00:00
static uint32_t SearchForDeviceOnBus(uint8_t bus, pcifind_t pcifind)
{
for ( unsigned slot = 0; slot < 32; slot++ )
{
unsigned numfuncs = 1;
for ( unsigned func = 0; func < numfuncs; func++ )
2011-08-05 12:25:00 +00:00
{
uint32_t devaddr = MakeDevAddr(bus, slot, func);
if ( MatchesSearchCriteria(devaddr, pcifind) )
return devaddr;
uint8_t header = Read8(devaddr, 0x0D); // Secondary Bus Number.
if ( header & 0x80 ) // Multi function device.
numfuncs = 8;
if ( (header & 0x7F) == 0x01 ) // PCI to PCI bus.
{
uint8_t subbusid = Read8(devaddr, 0x1A);
uint32_t recret = SearchForDeviceOnBus(subbusid, pcifind);
if ( recret )
return recret;
}
2011-08-05 12:25:00 +00:00
}
}
return 0;
}
uint32_t SearchForDevice(pcifind_t pcifind)
{
// Search on bus 0 and recurse on other detected busses.
return SearchForDeviceOnBus(0, pcifind);
}
// TODO: This is just a hack but will do for now.
addr_t ParseDevBar0(uint32_t devaddr)
{
uint32_t bar0 = Read32(devaddr, 0x10);
if ( bar0 & 0x1 ) // IO Space
return bar0 & ~0x7UL;
else // Memory Space
{
//uint32_t type = bar0 >> 1 & 0x3;
//uint32_t prefetchable = bar0 >> 3 & 0x1;
//if ( type == 0x01 )
// // TODO: Support 16-bit addresses here.
//if ( type == 0x02 )
// // TODO: Support 64-bit addresses here.
return bar0 & ~0xFUL;
}
}
2013-05-27 18:25:36 +00:00
bool IsIOSpaceBar(uint32_t devaddr, uint8_t bar)
{
uint32_t val = PCI::Read32(devaddr, 0x10 + 4 * bar);
return val & 0x1;
}
bool Is64BitBar(uint32_t devaddr, uint8_t bar)
{
uint32_t val = PCI::Read32(devaddr, 0x10 + 4 * bar);
return (val & 0x3 << 1) == 0x2 << 1;
}
uint64_t GetPCIBAR(uint32_t devaddr, uint8_t bar)
{
uint64_t low = PCI::Read32(devaddr, 0x10 + 4 * (bar+0));
if ( (low & (0x3 << 1)) != (0x2 << 1) )
return low & 0xFFFFFFF0ULL;
uint64_t high = PCI::Read32(devaddr, 0x10 + 4 * (bar+1));
return (low & 0xFFFFFFF0ULL) | high << 32ULL;
}
void Init()
{
2011-08-05 12:25:00 +00:00
}
} // namespace PCI
} // namespace Sortix