#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/spinlock.h>
#include <sys/spinlock2.h>
#include <sys/queue.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
#include "pci_cfgreg.h"
#include <machine/pc/bios.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
#include <vm/vm_kern.h>
#include <vm/vm_extern.h>
#include <vm/pmap.h>
#include <machine/pmap.h>
enum {
CFGMECH_NONE = 0,
CFGMECH_1,
CFGMECH_PCIE,
};
static vm_offset_t pcie_base;
static int pcie_minbus, pcie_maxbus;
static uint32_t pcie_badslots;
static int cfgmech;
static struct spinlock pcicfg_spin;
static int mcfg_enable = 1;
TUNABLE_INT("hw.pci.mcfg", &mcfg_enable);
static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, int bytes);
static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes);
static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data,
int bytes);
static int pciereg_cfgread(int bus, unsigned slot, unsigned func,
unsigned reg, unsigned bytes);
static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func,
unsigned reg, int data, unsigned bytes);
int
pci_cfgregopen(void)
{
static int inited = 0;
uint64_t pciebar;
uint16_t vid, did;
if (!inited) {
inited = 1;
spin_init(&pcicfg_spin, "pcicfg");
}
if (cfgmech != CFGMECH_NONE)
return 1;
cfgmech = CFGMECH_1;
vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2);
did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2);
switch (vid) {
case 0x8086:
switch (did) {
case 0x3590:
case 0x3592:
pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16;
pcie_cfgregopen(pciebar, 0, 255);
break;
case 0x2580:
case 0x2584:
case 0x2590:
pciebar = pci_cfgregread(0, 0, 0, 0x48, 4);
pcie_cfgregopen(pciebar, 0, 255);
break;
}
}
return 1;
}
static uint32_t
pci_docfgregread(int bus, int slot, int func, int reg, int bytes)
{
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus) &&
(bus != 0 || !(1 << slot & pcie_badslots)))
return pciereg_cfgread(bus, slot, func, reg, bytes);
else
return pcireg_cfgread(bus, slot, func, reg, bytes);
}
uint32_t
pci_cfgregread(int bus, int slot, int func, int reg, int bytes)
{
if (reg == PCIR_INTLINE && bytes == 1) {
uint32_t line;
line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1);
if (line == 0 || line >= 128)
return (PCI_INVALID_IRQ);
return line;
}
return pci_docfgregread(bus, slot, func, reg, bytes);
}
void
pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes)
{
if (cfgmech == CFGMECH_PCIE &&
(bus >= pcie_minbus && bus <= pcie_maxbus) &&
(bus != 0 || !(1 << slot & pcie_badslots)))
pciereg_cfgwrite(bus, slot, func, reg, data, bytes);
else
pcireg_cfgwrite(bus, slot, func, reg, data, bytes);
}
static int
pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes)
{
int dataport = 0;
if (bus <= PCI_BUSMAX &&
slot <= PCI_SLOTMAX &&
func <= PCI_FUNCMAX &&
(unsigned)reg <= PCI_REGMAX &&
bytes != 3 &&
(unsigned)bytes <= 4 &&
(reg & (bytes - 1)) == 0) {
outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) |
(func << 8) | (reg & ~0x03));
dataport = CONF1_DATA_PORT + (reg & 0x03);
}
return dataport;
}
static void
pci_cfgdisable(void)
{
}
static int
pcireg_cfgread(int bus, int slot, int func, int reg, int bytes)
{
int data = -1;
int port;
spin_lock(&pcicfg_spin);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
switch (bytes) {
case 1:
data = inb(port);
break;
case 2:
data = inw(port);
break;
case 4:
data = inl(port);
break;
}
pci_cfgdisable();
}
spin_unlock(&pcicfg_spin);
return data;
}
static void
pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes)
{
int port;
spin_lock(&pcicfg_spin);
port = pci_cfgenable(bus, slot, func, reg, bytes);
if (port != 0) {
switch (bytes) {
case 1:
outb(port, data);
break;
case 2:
outw(port, data);
break;
case 4:
outl(port, data);
break;
}
pci_cfgdisable();
}
spin_unlock(&pcicfg_spin);
}
int
pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus)
{
if (bootverbose) {
kprintf("PCIe: Memory Mapped configuration base @ 0x%jx, "
"bus [%d, %d]\n", (uintmax_t)base, minbus, maxbus);
}
if (!mcfg_enable)
return 0;
if (minbus != 0)
return 0;
if (bootverbose)
kprintf("PCIe: Using Memory Mapped configuration\n");
pcie_base = (vm_offset_t)pmap_mapdev_uncacheable(base,
((unsigned)maxbus + 1) << 20);
pcie_minbus = minbus;
pcie_maxbus = maxbus;
cfgmech = CFGMECH_PCIE;
if (pci_cfgregopen() != 0) {
int slot;
for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
uint32_t val1, val2;
val1 = pcireg_cfgread(0, slot, 0, 0, 4);
if (val1 == 0xffffffff)
continue;
val2 = pciereg_cfgread(0, slot, 0, 0, 4);
if (val2 != val1)
pcie_badslots |= (1 << slot);
}
}
return 1;
}
#define PCIE_VADDR(base, reg, bus, slot, func) \
((base) + \
((((bus) & 0xff) << 20) | \
(((slot) & 0x1f) << 15) | \
(((func) & 0x7) << 12) | \
((reg) & 0xfff)))
static int
pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg,
unsigned bytes)
{
volatile vm_offset_t va;
int data = -1;
if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return -1;
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
switch (bytes) {
case 4:
data = *(volatile uint32_t *)(va);
break;
case 2:
data = *(volatile uint16_t *)(va);
break;
case 1:
data = *(volatile uint8_t *)(va);
break;
}
return data;
}
static void
pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data,
unsigned bytes)
{
volatile vm_offset_t va;
if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX ||
func > PCI_FUNCMAX || reg > PCIE_REGMAX)
return;
va = PCIE_VADDR(pcie_base, reg, bus, slot, func);
switch (bytes) {
case 4:
*(volatile uint32_t *)(va) = data;
break;
case 2:
*(volatile uint16_t *)(va) = data;
break;
case 1:
*(volatile uint8_t *)(va) = data;
break;
}
}