genode/repos/os/src/drivers/pci/pci_device_config.h

216 lines
5.8 KiB
C++

/*
* \brief PCI device configuration
* \author Norman Feske
* \date 2008-01-29
*/
/*
* Copyright (C) 2008-2013 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU General Public License version 2.
*/
#ifndef _DEVICE_CONFIG_H_
#define _DEVICE_CONFIG_H_
#include <pci_device/pci_device.h>
#include "pci_config_access.h"
namespace Pci {
class Device_config
{
private:
int _bus, _device, _function; /* location at PCI bus */
/*
* Information provided by the PCI config space
*/
unsigned _vendor_id, _device_id;
unsigned _class_code;
unsigned _header_type;
/*
* Header type definitions
*/
enum {
HEADER_FUNCTION = 0,
HEADER_PCI_TO_PCI = 1,
HEADER_CARD_BUS = 2
};
Device::Resource _resource[Device::NUM_RESOURCES];
bool _resource_id_is_valid(int resource_id)
{
/*
* The maximum number of PCI resources depends on the
* header type of the device.
*/
int max_num = _header_type == HEADER_FUNCTION ? Device::NUM_RESOURCES
: _header_type == HEADER_PCI_TO_PCI ? 2
: 0;
return resource_id >= 0 && resource_id < max_num;
}
enum { INVALID_VENDOR = 0xffffU };
public:
enum { MAX_BUSES = 256, MAX_DEVICES = 32, MAX_FUNCTIONS = 8 };
/**
* Constructor
*/
Device_config() : _vendor_id(INVALID_VENDOR) { }
Device_config(int bus, int device, int function,
Config_access *pci_config):
_bus(bus), _device(device), _function(function)
{
_vendor_id = pci_config->read(bus, device, function, 0, Device::ACCESS_16BIT);
/* break here if device is invalid */
if (_vendor_id == INVALID_VENDOR)
return;
_device_id = pci_config->read(bus, device, function, 2, Device::ACCESS_16BIT);
_class_code = pci_config->read(bus, device, function, 8) >> 8;
_class_code &= 0xffffff;
_header_type = pci_config->read(bus, device, function, 0xe, Device::ACCESS_8BIT);
_header_type &= 0x7f;
/*
* We prevent scanning function 1-7 of non-multi-function
* devices by checking bit 7 (mf bit) of function 0 of the
* device. Note, the mf bit of function 1-7 is not significant
* and may be set or unset.
*/
if (function != 0
&& !(pci_config->read(bus, device, 0, 0xe, Device::ACCESS_8BIT) & 0x80)) {
_vendor_id = INVALID_VENDOR;
return;
}
for (int i = 0; _resource_id_is_valid(i); i++) {
/* index of base-address register in configuration space */
unsigned bar_idx = 0x10 + 4 * i;
/* read original base-address register value */
unsigned orig_bar = pci_config->read(bus, device, function, bar_idx);
/* check for invalid resource */
if (orig_bar == (unsigned)~0) {
_resource[i] = Device::Resource(0, 0);
continue;
}
/*
* Determine resource size by writing a magic value (all bits set)
* to the base-address register. In response, the device clears a number
* of lowest-significant bits corresponding to the resource size.
* Finally, we write back the original value as assigned by the BIOS.
*/
pci_config->write(bus, device, function, bar_idx, ~0);
unsigned bar = pci_config->read(bus, device, function, bar_idx);
pci_config->write(bus, device, function, bar_idx, orig_bar);
/*
* Scan base-address-register value for the lowest set bit but
* ignore the lower bits that are used to describe the resource type.
* I/O resources use the lowest 3 bits, memory resources use the
* lowest four bits for the resource description.
*/
unsigned start = (bar & 1) ? 3 : 4;
unsigned size = 1 << start;
for (unsigned bit = start; bit < 32; bit++, size += size)
/* stop at the lowest-significant set bit */
if (bar & (1 << bit))
break;
_resource[i] = Device::Resource(orig_bar, size);
}
}
/**
* Accessor functions for device location
*/
int bus_number() { return _bus; }
int device_number() { return _device; }
int function_number() { return _function; }
/**
* Accessor functions for device information
*/
unsigned short device_id() { return _device_id; }
unsigned short vendor_id() { return _vendor_id; }
unsigned int class_code() { return _class_code; }
bool is_pci_bridge() { return _header_type == HEADER_PCI_TO_PCI; }
/**
* Return true if device is valid
*/
bool valid() { return _vendor_id != INVALID_VENDOR; }
/**
* Return resource description by resource ID
*/
Device::Resource resource(int resource_id)
{
/* return invalid resource if sanity check fails */
if (!_resource_id_is_valid(resource_id))
return Device::Resource(0, 0);
return _resource[resource_id];
}
/**
* Read configuration space
*/
unsigned read(Config_access *pci_config, unsigned char address,
Device::Access_size size)
{
return pci_config->read(_bus, _device, _function, address, size);
}
/**
* Write configuration space
*/
void write(Config_access *pci_config, unsigned char address,
unsigned long value, Device::Access_size size)
{
pci_config->write(_bus, _device, _function, address, value, size);
}
};
class Config_space : public Genode::List<Config_space>::Element
{
private:
Genode::uint32_t _bdf_start;
Genode::uint32_t _func_count;
Genode::addr_t _base;
public:
Config_space(Genode::uint32_t bdf_start,
Genode::uint32_t func_count, Genode::addr_t base)
:
_bdf_start(bdf_start), _func_count(func_count), _base(base) {}
Genode::addr_t lookup_config_space(Genode::uint32_t bdf)
{
if ((_bdf_start <= bdf) && (bdf <= _bdf_start + _func_count - 1))
return _base + (bdf << 12);
return 0;
}
};
}
#endif /* _DEVICE_CONFIG_H_ */