#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/promif.h>
#include <sys/pci.h>
#include <sys/sysmacros.h>
#include <sys/pcie_impl.h>
#include <sys/machsystm.h>
#include <sys/byteorder.h>
#include <sys/pci_cfgacc.h>
#define PCI_CFG_SPACE (PCI_REG_ADDR_G(PCI_ADDR_CONFIG))
#define PCIE_CFG_SPACE_SIZE (PCI_CONF_HDR_SIZE << 4)
#define RC_PA_BDF_SHIFT 12
#define RC_BDF_TO_CFGADDR(bdf, offset) (((bdf) << RC_PA_BDF_SHIFT) + (offset))
static boolean_t
pci_cfgacc_valid(pci_cfgacc_req_t *req)
{
int sz = req->size;
if (IS_P2ALIGNED(req->offset, sz) &&
(req->offset + sz - 1 < PCIE_CFG_SPACE_SIZE) &&
((sz & 0xf) && ISP2(sz)))
return (B_TRUE);
cmn_err(CE_WARN, "illegal PCI request: offset = %x, size = %d",
req->offset, sz);
return (B_FALSE);
}
static uint64_t
pci_cfgacc_get(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size)
{
pcie_bus_t *bus_p;
uint64_t base_addr;
uint64_t val;
bus_p = PCIE_DIP2DOWNBUS(dip);
ASSERT(bus_p != NULL);
base_addr = bus_p->bus_cfgacc_base;
base_addr += RC_BDF_TO_CFGADDR(bdf, offset);
switch (size) {
case 1:
val = ldbphysio(base_addr);
break;
case 2:
val = ldhphysio(base_addr);
break;
case 4:
val = ldphysio(base_addr);
break;
case 8:
val = lddphysio(base_addr);
break;
default:
return ((uint64_t)-1);
}
return (LE_64(val));
}
static void
pci_cfgacc_set(dev_info_t *dip, uint16_t bdf, uint16_t offset, uint8_t size,
uint64_t val)
{
pcie_bus_t *bus_p;
uint64_t base_addr;
bus_p = PCIE_DIP2DOWNBUS(dip);
ASSERT(bus_p != NULL);
base_addr = bus_p->bus_cfgacc_base;
base_addr += RC_BDF_TO_CFGADDR(bdf, offset);
val = LE_64(val);
switch (size) {
case 1:
stbphysio(base_addr, val);
break;
case 2:
sthphysio(base_addr, val);
break;
case 4:
stphysio(base_addr, val);
break;
case 8:
stdphysio(base_addr, val);
break;
default:
break;
}
}
void
pci_cfgacc_acc(pci_cfgacc_req_t *req)
{
if (!req->write)
VAL64(req) = (uint64_t)-1;
if (!pci_cfgacc_valid(req))
return;
if (req->write) {
pci_cfgacc_set(req->rcdip, req->bdf, req->offset,
req->size, VAL64(req));
} else {
VAL64(req) = pci_cfgacc_get(req->rcdip, req->bdf,
req->offset, req->size);
}
}