blob: e3d640eea32726ee7b7dde513e5589beae7f1a20 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include <assert.h>
#include <endian.h>
#include <lib/pci/pio.h>
#include <zircon/types.h>
#include <kernel/auto_lock.h>
#include <kernel/mutex.h>
#include <kernel/spinlock.h>
// TODO: This library exists as a shim for the awkward period between bringing
// PCI legacy support online, and moving PCI to userspace. Initially, it exists
// as a kernel library that userspace accesses via syscalls so that a userspace
// process never causes a race condition with the bus driver's accesses. Later,
// all accesses will go through the library itself in userspace and the syscalls
// will no longer exist.
namespace Pci {
#ifdef ARCH_X86
#include <arch/x86.h>
static SpinLock pio_lock;
static constexpr uint16_t kPciConfigAddr = 0xCF8;
static constexpr uint16_t kPciConfigData = 0xCFC;
static constexpr uint32_t kPciCfgEnable = (1 << 31);
static constexpr uint32_t WidthMask(size_t width) {
return (width == 32) ? 0xffffffff : (1u << width) - 1u;
}
zx_status_t PioCfgRead(uint32_t addr, uint32_t* val, size_t width) {
AutoSpinLock lock(&pio_lock);
size_t shift = (addr & 0x3) * 8u;
if (shift + width > 32) {
return ZX_ERR_INVALID_ARGS;
}
outpd(kPciConfigAddr, (addr & ~0x3) | kPciCfgEnable);
uint32_t tmp_val = LE32(inpd(kPciConfigData));
uint32_t width_mask = WidthMask(width);
// Align the read to the correct offset, then mask based on byte width
*val = (tmp_val >> shift) & width_mask;
return ZX_OK;
}
zx_status_t PioCfgRead(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t* val,
size_t width) {
return PioCfgRead(PciBdfRawAddr(bus, dev, func, offset), val, width);
}
zx_status_t PioCfgWrite(uint32_t addr, uint32_t val, size_t width) {
AutoSpinLock lock(&pio_lock);
size_t shift = (addr & 0x3) * 8u;
if (shift + width > 32) {
return ZX_ERR_INVALID_ARGS;
}
uint32_t width_mask = WidthMask(width);
uint32_t write_mask = width_mask << shift;
outpd(kPciConfigAddr, (addr & ~0x3) | kPciCfgEnable);
uint32_t tmp_val = LE32(inpd(kPciConfigData));
val &= width_mask;
tmp_val &= ~write_mask;
tmp_val |= (val << shift);
outpd(kPciConfigData, LE32(tmp_val));
return ZX_OK;
}
zx_status_t PioCfgWrite(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t val,
size_t width) {
return PioCfgWrite(PciBdfRawAddr(bus, dev, func, offset), val, width);
}
#else // not x86
zx_status_t PioCfgRead(uint32_t addr, uint32_t* val, size_t width) { return ZX_ERR_NOT_SUPPORTED; }
zx_status_t PioCfgRead(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t* val,
size_t width) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t PioCfgWrite(uint32_t addr, uint32_t val, size_t width) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t PioCfgWrite(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, uint32_t val,
size_t width) {
return ZX_ERR_NOT_SUPPORTED;
}
#endif // ARCH_X86
} // namespace Pci