blob: 09d04f53dba055fc7a8a5ae8edb5ef39014c3a06 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <limits.h>
#include <acpica/acpi.h>
#include <fuchsia/hardware/pciroot/c/banjo.h>
#include <lib/pci/pio.h>
#include "zircon/system/ulib/acpica/osfuchsia.h"
namespace {
const size_t PCIE_MAX_DEVICES_PER_BUS = 32;
const size_t PCIE_MAX_FUNCTIONS_PER_DEVICE = 8;
} // namespace
/**
* @brief Read/Write a value from a PCI configuration register.
*
* @param PciId The full PCI configuration space address, consisting of a
* segment number, bus number, device number, and function number.
* @param Register The PCI register address to be read from.
* @param Value A pointer to a location where the data is to be returned.
* @param Width The register width in bits, either 8, 16, 32, or 64.
* @param Write Write or Read.
*
* @return Exception code that indicates success or reason for failure.
*/
static ACPI_STATUS AcpiOsReadWritePciConfiguration(ACPI_PCI_ID* PciId, UINT32 Register,
UINT64* Value, UINT32 Width, bool Write) {
if (LOCAL_TRACE) {
printf("ACPIOS: %s PCI Config %x:%x:%x:%x register %#x width %u\n", Write ? "write" : "read",
PciId->Segment, PciId->Bus, PciId->Device, PciId->Function, Register, Width);
}
// Only segment 0 is supported for now
if (PciId->Segment != 0) {
printf("ACPIOS: read/write config, segment != 0 not supported.\n");
return AE_ERROR;
}
// Check bounds of device and function offsets
if (PciId->Device >= PCIE_MAX_DEVICES_PER_BUS ||
PciId->Function >= PCIE_MAX_FUNCTIONS_PER_DEVICE) {
printf("ACPIOS: device out of reasonable bounds.\n");
return AE_ERROR;
}
// PCI config only supports up to 32 bit values
if (Write && (*Value > UINT_MAX)) {
printf("ACPIOS: read/write config, Value passed does not fit confg registers.\n");
}
// Clear higher bits before a read
if (!Write) {
*Value = 0;
}
#if __x86_64__
uint8_t bus = static_cast<uint8_t>(PciId->Bus);
uint8_t dev = static_cast<uint8_t>(PciId->Device);
uint8_t func = static_cast<uint8_t>(PciId->Function);
uint8_t offset = static_cast<uint8_t>(Register);
zx_status_t st;
#ifdef ENABLE_USER_PCI
pci_bdf_t addr = {bus, dev, func};
switch (Width) {
case 8u:
(Write) ? st = pci_pio_write8(addr, offset, static_cast<uint8_t>(*Value))
: st = pci_pio_read8(addr, offset, reinterpret_cast<uint8_t*>(Value));
break;
case 16u:
(Write) ? st = pci_pio_write16(addr, offset, static_cast<uint16_t>(*Value))
: st = pci_pio_read16(addr, offset, reinterpret_cast<uint16_t*>(Value));
break;
// assume 32bit by default since 64 bit reads on IO ports are not a thing supported by the
// spec
default:
(Write) ? st = pci_pio_write32(addr, offset, static_cast<uint32_t>(*Value))
: st = pci_pio_read32(addr, offset, reinterpret_cast<uint32_t*>(Value));
}
#else
st = zx_pci_cfg_pio_rw(root_resource_handle, bus, dev, func, offset,
reinterpret_cast<uint32_t*>(Value), static_cast<uint8_t>(Width), Write);
#endif // ENABLE_USER_PCI
#ifdef ACPI_DEBUG_OUTPUT
if (st != ZX_OK) {
printf("ACPIOS: pci rw error: %d\n", st);
}
#endif // ACPI_DEBUG_OUTPUT
return (st == ZX_OK) ? AE_OK : AE_ERROR;
#endif // __x86_64__
return AE_NOT_IMPLEMENTED;
}
/**
* @brief Read a value from a PCI configuration register.
*
* @param PciId The full PCI configuration space address, consisting of a
* segment number, bus number, device number, and function number.
* @param Register The PCI register address to be read from.
* @param Value A pointer to a location where the data is to be returned.
* @param Width The register width in bits, either 8, 16, 32, or 64.
*
* @return Exception code that indicates success or reason for failure.
*/
ACPI_STATUS AcpiOsReadPciConfiguration(ACPI_PCI_ID* PciId, UINT32 Register, UINT64* Value,
UINT32 Width) {
return AcpiOsReadWritePciConfiguration(PciId, Register, Value, Width, false);
}
/**
* @brief Write a value to a PCI configuration register.
*
* @param PciId The full PCI configuration space address, consisting of a
* segment number, bus number, device number, and function number.
* @param Register The PCI register address to be written to.
* @param Value Data to be written.
* @param Width The register width in bits, either 8, 16, or 32.
*
* @return Exception code that indicates success or reason for failure.
*/
ACPI_STATUS AcpiOsWritePciConfiguration(ACPI_PCI_ID* PciId, UINT32 Register, UINT64 Value,
UINT32 Width) {
return AcpiOsReadWritePciConfiguration(PciId, Register, &Value, Width, true);
}