* Copyright 2022, Haiku, Inc.
* Distributed under the terms of the MIT License.
*/
#include "X86PCIController.h"
#include <ioapic.h>
#include <AutoDeleterDrivers.h>
#include <util/AutoLock.h>
#include <string.h>
#include <new>
#define PCI_MECH1_REQ_PORT 0xCF8
#define PCI_MECH1_DATA_PORT 0xCFC
#define PCI_MECH1_REQ_DATA(bus, device, func, offset) \
(0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (offset & ~3))
#define PCI_MECH2_ENABLE_PORT 0x0cf8
#define PCI_MECH2_FORWARD_PORT 0x0cfa
#define PCI_MECH2_CONFIG_PORT(dev, offset) \
(uint16)(0xC00 | (dev << 8) | offset)
float
X86PCIController::SupportsDevice(device_node* parent)
{
const char* bus;
if (gDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false) < B_OK)
return -1.0f;
if (strcmp(bus, "root") == 0)
return 1.0f;
return 0.0;
}
status_t
X86PCIController::RegisterDevice(device_node* parent)
{
device_attr attrs[] = {
{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, {.string = "X86 PCI Host Controller"} },
{ B_DEVICE_FIXED_CHILD, B_STRING_TYPE, {.string = "bus_managers/pci/root/driver_v1"} },
{}
};
return gDeviceManager->register_node(parent, PCI_X86_DRIVER_MODULE_NAME, attrs, NULL, NULL);
}
status_t
X86PCIController::InitDriver(device_node* node, X86PCIController*& outDriver)
{
bool search_mech1 = true;
bool search_mech2 = true;
bool search_mechpcie = true;
void *config = NULL;
config = load_driver_settings("pci");
if (config) {
const char *mech = get_driver_parameter(config, "mechanism", NULL, NULL);
if (mech) {
search_mech1 = search_mech2 = search_mechpcie = false;
if (strcmp(mech, "1") == 0)
search_mech1 = true;
else if (strcmp(mech, "2") == 0)
search_mech2 = true;
else if (strcmp(mech, "pcie") == 0)
search_mechpcie = true;
else
panic("Unknown pci config mechanism setting %s\n", mech);
}
unload_driver_settings(config);
}
if (search_mechpcie) {
if (CreateDriver(node, new(std::nothrow) X86PCIControllerMethPcie(), outDriver) >= B_OK)
return B_OK;
}
if (search_mech1) {
if (CreateDriver(node, new(std::nothrow) X86PCIControllerMeth1(), outDriver) >= B_OK)
return B_OK;
}
if (search_mech2) {
if (CreateDriver(node, new(std::nothrow) X86PCIControllerMeth2(), outDriver) >= B_OK)
return B_OK;
}
dprintf("PCI: no configuration mechanism found\n");
return B_ERROR;
}
status_t
X86PCIController::CreateDriver(device_node* node, X86PCIController* driverIn,
X86PCIController*& driverOut)
{
ObjectDeleter<X86PCIController> driver(driverIn);
if (!driver.IsSet())
return B_NO_MEMORY;
CHECK_RET(driver->InitDriverInt(node));
driverOut = driver.Detach();
return B_OK;
}
status_t
X86PCIController::InitDriverInt(device_node* node)
{
fNode = node;
return B_OK;
}
void
X86PCIController::UninitDriver()
{
delete this;
}
status_t
X86PCIController::ReadIrq(uint8 bus, uint8 device, uint8 function,
uint8 pin, uint8& irq)
{
return B_UNSUPPORTED;
}
status_t
X86PCIController::WriteIrq(uint8 bus, uint8 device, uint8 function,
uint8 pin, uint8 irq)
{
return B_UNSUPPORTED;
}
status_t
X86PCIController::GetRange(uint32 index, pci_resource_range* range)
{
return B_BAD_INDEX;
}
status_t
X86PCIController::Finalize()
{
ioapic_init();
return B_OK;
}
status_t
X86PCIControllerMeth1::InitDriverInt(device_node* node)
{
CHECK_RET(X86PCIController::InitDriverInt(node));
out32(0x80000000, PCI_MECH1_REQ_PORT);
if (0x80000000 == in32(PCI_MECH1_REQ_PORT)) {
dprintf("PCI: mechanism 1 controller found\n");
return B_OK;
}
return B_ERROR;
}
status_t
X86PCIControllerMeth1::ReadConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 &value)
{
if (offset > 0xff)
return ERANGE;
InterruptsSpinLocker lock(fLock);
out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
switch (size) {
case 1:
value = in8(PCI_MECH1_DATA_PORT + (offset & 3));
break;
case 2:
value = in16(PCI_MECH1_DATA_PORT + (offset & 3));
break;
case 4:
value = in32(PCI_MECH1_DATA_PORT);
break;
default:
return B_BAD_VALUE;
}
return B_OK;
}
status_t
X86PCIControllerMeth1::WriteConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 value)
{
if (offset > 0xff)
return ERANGE;
InterruptsSpinLocker lock(fLock);
out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
switch (size) {
case 1:
out8(value, PCI_MECH1_DATA_PORT + (offset & 3));
break;
case 2:
out16(value, PCI_MECH1_DATA_PORT + (offset & 3));
break;
case 4:
out32(value, PCI_MECH1_DATA_PORT);
break;
default:
return B_BAD_VALUE;
}
return B_OK;
}
status_t X86PCIControllerMeth1::GetMaxBusDevices(int32& count)
{
count = 32;
return B_OK;
}
status_t
X86PCIControllerMeth2::InitDriverInt(device_node* node)
{
CHECK_RET(X86PCIController::InitDriverInt(node));
out8(0x00, 0xCFB);
out8(0x00, 0xCF8);
out8(0x00, 0xCFA);
if (in8(0xCF8) == 0x00 && in8(0xCFA) == 0x00) {
dprintf("PCI: mechanism 2 controller found\n");
return B_OK;
}
return B_ERROR;
}
status_t
X86PCIControllerMeth2::ReadConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 &value)
{
if (offset > 0xff)
return ERANGE;
InterruptsSpinLocker lock(fLock);
out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
out8(bus, PCI_MECH2_FORWARD_PORT);
switch (size) {
case 1:
value = in8(PCI_MECH2_CONFIG_PORT(device, offset));
break;
case 2:
value = in16(PCI_MECH2_CONFIG_PORT(device, offset));
break;
case 4:
value = in32(PCI_MECH2_CONFIG_PORT(device, offset));
break;
default:
return B_BAD_VALUE;
}
out8(0, PCI_MECH2_ENABLE_PORT);
return B_OK;
}
status_t
X86PCIControllerMeth2::WriteConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 value)
{
if (offset > 0xff)
return ERANGE;
InterruptsSpinLocker lock(fLock);
out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
out8(bus, PCI_MECH2_FORWARD_PORT);
switch (size) {
case 1:
out8(value, PCI_MECH2_CONFIG_PORT(device, offset));
break;
case 2:
out16(value, PCI_MECH2_CONFIG_PORT(device, offset));
break;
case 4:
out32(value, PCI_MECH2_CONFIG_PORT(device, offset));
break;
default:
return B_BAD_VALUE;
}
out8(0, PCI_MECH2_ENABLE_PORT);
return B_OK;
}
status_t X86PCIControllerMeth2::GetMaxBusDevices(int32& count)
{
count = 16;
return B_OK;
}
status_t
X86PCIControllerMethPcie::InitDriverInt(device_node* node)
{
status_t status = X86PCIController::InitDriverInt(node);
if (status != B_OK)
return status;
device_node *acpiNode = NULL;
{
device_node* deviceRoot = gDeviceManager->get_root_node();
device_attr acpiAttrs[] = {
{ B_DEVICE_BUS, B_STRING_TYPE, { .string = "acpi" }},
{ ACPI_DEVICE_HID_ITEM, B_STRING_TYPE, { .string = "PNP0A08" }},
{ NULL }
};
if (gDeviceManager->find_child_node(deviceRoot, acpiAttrs, &acpiNode) != B_OK)
return ENODEV;
}
status = fECAMPCIController.ReadResourceInfo(acpiNode);
gDeviceManager->put_node(acpiNode);
return status;
}
status_t
X86PCIControllerMethPcie::ReadConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 &value)
{
if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
return X86PCIControllerMeth1::ReadConfig(bus, device, function, offset,
size, value);
}
return fECAMPCIController.ReadConfig(bus, device, function, offset, size, value);
}
status_t
X86PCIControllerMethPcie::WriteConfig(
uint8 bus, uint8 device, uint8 function,
uint16 offset, uint8 size, uint32 value)
{
if (bus < fECAMPCIController.fStartBusNumber || bus > fECAMPCIController.fEndBusNumber) {
return X86PCIControllerMeth1::WriteConfig(bus, device, function, offset,
size, value);
}
return fECAMPCIController.WriteConfig(bus, device, function, offset, size, value);
}
status_t
X86PCIControllerMethPcie::GetMaxBusDevices(int32& count)
{
return fECAMPCIController.GetMaxBusDevices(count);
}
status_t
X86PCIControllerMethPcie::GetRange(uint32 index, pci_resource_range* range)
{
return fECAMPCIController.GetRange(index, range);
}