1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4#include <assert.h> 5#include <endian.h> 6#include <kernel/mutex.h> 7#include <zircon/types.h> 8#include <lib/pci/pio.h> 9#include <kernel/auto_lock.h> 10#include <kernel/spinlock.h> 11 12// TODO: This library exists as a shim for the awkward period between bringing 13// PCI legacy support online, and moving PCI to userspace. Initially, it exists 14// as a kernel library that userspace accesses via syscalls so that a userspace 15// process never causes a race condition with the bus driver's accesses. Later, 16// all accesses will go through the library itself in userspace and the syscalls 17// will no longer exist. 18 19namespace Pci { 20 21#ifdef ARCH_X86 22#include <arch/x86.h> 23static SpinLock pio_lock; 24 25static constexpr uint16_t kPciConfigAddr = 0xCF8; 26static constexpr uint16_t kPciConfigData = 0xCFC; 27static constexpr uint32_t kPciCfgEnable = (1 << 31); 28static constexpr uint32_t WidthMask(size_t width) { 29 return (width == 32) ? 0xffffffff : (1u << width) - 1u; 30} 31 32zx_status_t PioCfgRead(uint32_t addr, uint32_t* val, size_t width) { 33 AutoSpinLock lock(&pio_lock); 34 35 size_t shift = (addr & 0x3) * 8u; 36 if (shift + width > 32) { 37 return ZX_ERR_INVALID_ARGS; 38 } 39 40 outpd(kPciConfigAddr, (addr & ~0x3) | kPciCfgEnable);; 41 uint32_t tmp_val = LE32(inpd(kPciConfigData)); 42 uint32_t width_mask = WidthMask(width); 43 44 // Align the read to the correct offset, then mask based on byte width 45 *val = (tmp_val >> shift) & width_mask; 46 return ZX_OK; 47} 48 49zx_status_t PioCfgRead(uint8_t bus, uint8_t dev, uint8_t func, 50 uint8_t offset, uint32_t* val, size_t width) { 51 return PioCfgRead(PciBdfRawAddr(bus, dev, func, offset), val, width); 52} 53 54zx_status_t PioCfgWrite(uint32_t addr, uint32_t val, size_t width) { 55 AutoSpinLock lock(&pio_lock); 56 57 size_t shift = (addr & 0x3) * 8u; 58 if (shift + width > 32) { 59 return ZX_ERR_INVALID_ARGS; 60 } 61 62 uint32_t width_mask = WidthMask(width); 63 uint32_t write_mask = width_mask << shift; 64 outpd(kPciConfigAddr, (addr & ~0x3) | kPciCfgEnable); 65 uint32_t tmp_val = LE32(inpd(kPciConfigData)); 66 67 val &= width_mask; 68 tmp_val &= ~write_mask; 69 tmp_val |= (val << shift); 70 outpd(kPciConfigData, LE32(tmp_val)); 71 72 return ZX_OK; 73} 74 75zx_status_t PioCfgWrite(uint8_t bus, uint8_t dev, uint8_t func, 76 uint8_t offset, uint32_t val, size_t width) { 77 return PioCfgWrite(PciBdfRawAddr(bus, dev, func, offset), val, width); 78} 79 80#else // not x86 81zx_status_t PioCfgRead(uint32_t addr, uint32_t* val, size_t width) { 82 return ZX_ERR_NOT_SUPPORTED; 83} 84 85zx_status_t PioCfgRead(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, 86 uint32_t* val, size_t width) { 87 return ZX_ERR_NOT_SUPPORTED; 88} 89 90zx_status_t PioCfgWrite(uint32_t addr, uint32_t val, size_t width) { 91 return ZX_ERR_NOT_SUPPORTED;; 92} 93 94zx_status_t PioCfgWrite(uint8_t bus, uint8_t dev, uint8_t func, uint8_t offset, 95 uint32_t val, size_t width) { 96 return ZX_ERR_NOT_SUPPORTED; 97} 98 99#endif // ARCH_X86 100}; // namespace PCI 101