#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/fm/protocol.h>
#include <sys/fm/util.h>
#include <sys/promif.h>
#include <sys/disp.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/pci_cap.h>
#include <sys/pci_impl.h>
#include <sys/pcie_impl.h>
#include <sys/hotplug/pci/pcie_hp.h>
#include <sys/hotplug/pci/pciehpc.h>
#include <sys/hotplug/pci/pcishpc.h>
#include <sys/hotplug/pci/pcicfg.h>
#include <sys/pci_cfgacc.h>
#include <sys/sysevent.h>
#include <sys/sysevent/eventdefs.h>
#include <sys/sysevent/pcie.h>
static void pcie_init_pfd(dev_info_t *);
static void pcie_fini_pfd(dev_info_t *);
#ifdef DEBUG
uint_t pcie_debug_flags = 0;
static void pcie_print_bus(pcie_bus_t *bus_p);
void pcie_dbg(char *fmt, ...);
#endif
ushort_t pcie_command_default =
PCI_COMM_SERR_ENABLE |
PCI_COMM_WAIT_CYC_ENAB |
PCI_COMM_PARITY_DETECT |
PCI_COMM_ME |
PCI_COMM_MAE |
PCI_COMM_IO;
ushort_t pcie_command_default_fw =
PCI_COMM_SPEC_CYC |
PCI_COMM_MEMWR_INVAL |
PCI_COMM_PALETTE_SNOOP |
PCI_COMM_WAIT_CYC_ENAB |
0xF800;
ushort_t pcie_bdg_command_default_fw =
PCI_BCNF_BCNTRL_ISA_ENABLE |
PCI_BCNF_BCNTRL_VGA_ENABLE |
0xF000;
ushort_t pcie_base_err_default =
PCIE_DEVCTL_CE_REPORTING_EN |
PCIE_DEVCTL_NFE_REPORTING_EN |
PCIE_DEVCTL_FE_REPORTING_EN |
PCIE_DEVCTL_UR_REPORTING_EN;
uint16_t pcie_devctl_default = PCIE_DEVCTL_RO_EN | PCIE_DEVCTL_MAX_READ_REQ_512;
uint16_t pcie_devctl_default_mask = PCIE_DEVCTL_MAX_READ_REQ_MASK |
PCIE_DEVCTL_MAX_PAYLOAD_MASK | PCIE_DEVCTL_EXT_TAG_FIELD_EN;
#define PCIE_ROOT_SYS_ERR (PCIE_ROOTCTL_SYS_ERR_ON_CE_EN | \
PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN | \
PCIE_ROOTCTL_SYS_ERR_ON_FE_EN)
ushort_t pcie_root_ctrl_default =
PCIE_ROOTCTL_SYS_ERR_ON_CE_EN |
PCIE_ROOTCTL_SYS_ERR_ON_NFE_EN |
PCIE_ROOTCTL_SYS_ERR_ON_FE_EN;
ushort_t pcie_root_error_cmd_default =
PCIE_AER_RE_CMD_CE_REP_EN |
PCIE_AER_RE_CMD_NFE_REP_EN |
PCIE_AER_RE_CMD_FE_REP_EN;
uint32_t pcie_ecrc_value =
PCIE_AER_CTL_ECRC_GEN_ENA |
PCIE_AER_CTL_ECRC_CHECK_ENA;
uint32_t pcie_aer_uce_mask = 0;
uint32_t pcie_aer_ce_mask = 0;
uint32_t pcie_aer_suce_mask = 0;
uint32_t pcie_serr_disable_flag = 0;
uint32_t pcie_aer_uce_severity = PCIE_AER_UCE_MTLP | PCIE_AER_UCE_RO | \
PCIE_AER_UCE_FCP | PCIE_AER_UCE_SD | PCIE_AER_UCE_DLP | \
PCIE_AER_UCE_TRAINING;
uint32_t pcie_aer_suce_severity = PCIE_AER_SUCE_SERR_ASSERT | \
PCIE_AER_SUCE_UC_ADDR_ERR | PCIE_AER_SUCE_UC_ATTR_ERR | \
PCIE_AER_SUCE_USC_MSG_DATA_ERR;
int pcie_disable_ari = 0;
int pcie_disable_lbw = 0;
uint32_t pcie_link_retrain_count = 500;
uint32_t pcie_link_retrain_delay_ms = 10;
taskq_t *pcie_link_tq;
kmutex_t pcie_link_tq_mutex;
static int pcie_link_bw_intr(dev_info_t *);
static void pcie_capture_speeds(dev_info_t *);
dev_info_t *pcie_get_rc_dip(dev_info_t *dip);
static struct modlmisc modlmisc = {
&mod_miscops,
"PCI Express Framework Module"
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modlmisc,
NULL
};
char *pcie_nv_buf;
nv_alloc_t *pcie_nvap;
nvlist_t *pcie_nvl;
int
_init(void)
{
int rval;
pcie_nv_buf = kmem_alloc(ERPT_DATA_SZ, KM_SLEEP);
pcie_nvap = fm_nva_xcreate(pcie_nv_buf, ERPT_DATA_SZ);
pcie_nvl = fm_nvlist_create(pcie_nvap);
mutex_init(&pcie_link_tq_mutex, NULL, MUTEX_DRIVER, NULL);
if ((rval = mod_install(&modlinkage)) != 0) {
mutex_destroy(&pcie_link_tq_mutex);
fm_nvlist_destroy(pcie_nvl, FM_NVA_RETAIN);
fm_nva_xdestroy(pcie_nvap);
kmem_free(pcie_nv_buf, ERPT_DATA_SZ);
}
return (rval);
}
int
_fini()
{
int rval;
if ((rval = mod_remove(&modlinkage)) == 0) {
if (pcie_link_tq != NULL) {
taskq_destroy(pcie_link_tq);
}
mutex_destroy(&pcie_link_tq_mutex);
fm_nvlist_destroy(pcie_nvl, FM_NVA_RETAIN);
fm_nva_xdestroy(pcie_nvap);
kmem_free(pcie_nv_buf, ERPT_DATA_SZ);
}
return (rval);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
int
pcie_init(dev_info_t *dip, caddr_t arg)
{
int ret = DDI_SUCCESS;
mutex_enter(&pcie_link_tq_mutex);
if (pcie_link_tq == NULL) {
pcie_link_tq = taskq_create("pcie_link", 1, minclsyspri, 0, 0,
0);
}
mutex_exit(&pcie_link_tq_mutex);
if ((ret = ddi_create_minor_node(dip, "devctl", S_IFCHR,
PCI_MINOR_NUM(ddi_get_instance(dip), PCI_DEVCTL_MINOR),
DDI_NT_NEXUS, 0)) != DDI_SUCCESS) {
PCIE_DBG("Failed to create devctl minor node for %s%d\n",
ddi_driver_name(dip), ddi_get_instance(dip));
return (ret);
}
if ((ret = pcie_hp_init(dip, arg)) != DDI_SUCCESS) {
PCIE_DBG("%s%d: Failed setting hotplug framework\n",
ddi_driver_name(dip), ddi_get_instance(dip));
#if defined(__sparc)
ddi_remove_minor_node(dip, "devctl");
return (ret);
#endif
}
return (DDI_SUCCESS);
}
int
pcie_uninit(dev_info_t *dip)
{
int ret = DDI_SUCCESS;
if (pcie_ari_is_enabled(dip) == PCIE_ARI_FORW_ENABLED)
(void) pcie_ari_disable(dip);
if ((ret = pcie_hp_uninit(dip)) != DDI_SUCCESS) {
PCIE_DBG("Failed to uninitialize hotplug for %s%d\n",
ddi_driver_name(dip), ddi_get_instance(dip));
return (ret);
}
if (pcie_link_bw_supported(dip)) {
(void) pcie_link_bw_disable(dip);
}
ddi_remove_minor_node(dip, "devctl");
return (ret);
}
int
pcie_hpintr_enable(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
pcie_hp_ctrl_t *ctrl_p = PCIE_GET_HP_CTRL(dip);
if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
(void) (ctrl_p->hc_ops.enable_hpc_intr)(ctrl_p);
} else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
(void) pcishpc_enable_irqs(ctrl_p);
}
return (DDI_SUCCESS);
}
int
pcie_hpintr_disable(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
pcie_hp_ctrl_t *ctrl_p = PCIE_GET_HP_CTRL(dip);
if (PCIE_IS_PCIE_HOTPLUG_ENABLED(bus_p)) {
(void) (ctrl_p->hc_ops.disable_hpc_intr)(ctrl_p);
} else if (PCIE_IS_PCI_HOTPLUG_ENABLED(bus_p)) {
(void) pcishpc_disable_irqs(ctrl_p);
}
return (DDI_SUCCESS);
}
int
pcie_intr(dev_info_t *dip)
{
int hp, lbw;
hp = pcie_hp_intr(dip);
lbw = pcie_link_bw_intr(dip);
if (hp == DDI_INTR_CLAIMED || lbw == DDI_INTR_CLAIMED) {
return (DDI_INTR_CLAIMED);
}
return (DDI_INTR_UNCLAIMED);
}
int
pcie_open(dev_info_t *dip, dev_t *devp, int flags, int otyp, cred_t *credp)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (otyp != OTYP_CHR)
return (EINVAL);
if ((bus_p->bus_soft_state == PCI_SOFT_STATE_OPEN_EXCL) ||
((flags & FEXCL) &&
(bus_p->bus_soft_state != PCI_SOFT_STATE_CLOSED))) {
return (EBUSY);
}
if (flags & FEXCL)
bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN_EXCL;
else
bus_p->bus_soft_state = PCI_SOFT_STATE_OPEN;
return (0);
}
int
pcie_close(dev_info_t *dip, dev_t dev, int flags, int otyp, cred_t *credp)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (otyp != OTYP_CHR)
return (EINVAL);
bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
return (0);
}
int
pcie_ioctl(dev_info_t *dip, dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp)
{
struct devctl_iocdata *dcp;
uint_t bus_state;
int rv = DDI_SUCCESS;
switch (cmd) {
case DEVCTL_DEVICE_GETSTATE:
case DEVCTL_DEVICE_ONLINE:
case DEVCTL_DEVICE_OFFLINE:
case DEVCTL_BUS_GETSTATE:
return (ndi_devctl_ioctl(dip, cmd, arg, mode, 0));
default:
break;
}
if (ndi_dc_allochdl((void *)arg, &dcp) != NDI_SUCCESS)
return (EFAULT);
switch (cmd) {
case DEVCTL_BUS_QUIESCE:
if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
if (bus_state == BUS_QUIESCED)
break;
(void) ndi_set_bus_state(dip, BUS_QUIESCED);
break;
case DEVCTL_BUS_UNQUIESCE:
if (ndi_get_bus_state(dip, &bus_state) == NDI_SUCCESS)
if (bus_state == BUS_ACTIVE)
break;
(void) ndi_set_bus_state(dip, BUS_ACTIVE);
break;
case DEVCTL_BUS_RESET:
case DEVCTL_BUS_RESETALL:
case DEVCTL_DEVICE_RESET:
rv = ENOTSUP;
break;
default:
rv = ENOTTY;
}
ndi_dc_freehdl(dcp);
return (rv);
}
int
pcie_prop_op(dev_t dev, dev_info_t *dip, ddi_prop_op_t prop_op,
int flags, char *name, caddr_t valuep, int *lengthp)
{
if (dev == DDI_DEV_T_ANY)
goto skip;
if (PCIE_IS_HOTPLUG_CAPABLE(dip) &&
strcmp(name, "pci-occupant") == 0) {
int pci_dev = PCI_MINOR_NUM_TO_PCI_DEVNUM(getminor(dev));
pcie_hp_create_occupant_props(dip, dev, pci_dev);
}
skip:
return (ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp));
}
int
pcie_init_cfghdl(dev_info_t *cdip)
{
pcie_bus_t *bus_p;
ddi_acc_handle_t eh = NULL;
bus_p = PCIE_DIP2BUS(cdip);
if (bus_p == NULL)
return (DDI_FAILURE);
if (pci_config_setup(cdip, &eh) != DDI_SUCCESS) {
cmn_err(CE_WARN, "Cannot setup config access"
" for BDF 0x%x\n", bus_p->bus_bdf);
return (DDI_FAILURE);
}
bus_p->bus_cfg_hdl = eh;
return (DDI_SUCCESS);
}
void
pcie_fini_cfghdl(dev_info_t *cdip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(cdip);
pci_config_teardown(&bus_p->bus_cfg_hdl);
}
void
pcie_determine_serial(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
ddi_acc_handle_t h;
uint16_t cap;
uchar_t serial[8];
uint32_t low, high;
if (!PCIE_IS_PCIE(bus_p))
return;
h = bus_p->bus_cfg_hdl;
if ((PCI_CAP_LOCATE(h, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_SER), &cap)) ==
DDI_FAILURE)
return;
high = PCI_XCAP_GET32(h, 0, cap, PCIE_SER_SID_UPPER_DW);
low = PCI_XCAP_GET32(h, 0, cap, PCIE_SER_SID_LOWER_DW);
if (high == PCI_EINVAL32 && low == PCI_EINVAL32)
return;
serial[0] = low & 0xff;
serial[1] = (low >> 8) & 0xff;
serial[2] = (low >> 16) & 0xff;
serial[3] = (low >> 24) & 0xff;
serial[4] = high & 0xff;
serial[5] = (high >> 8) & 0xff;
serial[6] = (high >> 16) & 0xff;
serial[7] = (high >> 24) & 0xff;
(void) ndi_prop_update_byte_array(DDI_DEV_T_NONE, dip, "pcie-serial",
serial, sizeof (serial));
}
static void
pcie_determine_aspm(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint32_t linkcap;
uint16_t linkctl;
if (!PCIE_IS_PCIE(bus_p))
return;
linkcap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
linkctl = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL);
switch (linkcap & PCIE_LINKCAP_ASPM_SUP_MASK) {
case PCIE_LINKCAP_ASPM_SUP_L0S:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-support", "l0s");
break;
case PCIE_LINKCAP_ASPM_SUP_L1:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-support", "l1");
break;
case PCIE_LINKCAP_ASPM_SUP_L0S_L1:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-support", "l0s,l1");
break;
default:
return;
}
switch (linkctl & PCIE_LINKCTL_ASPM_CTL_MASK) {
case PCIE_LINKCTL_ASPM_CTL_DIS:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-state", "disabled");
break;
case PCIE_LINKCTL_ASPM_CTL_L0S:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-state", "l0s");
break;
case PCIE_LINKCTL_ASPM_CTL_L1:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-state", "l1");
break;
case PCIE_LINKCTL_ASPM_CTL_L0S_L1:
(void) ndi_prop_update_string(DDI_DEV_T_NONE, dip,
"pcie-aspm-state", "l0s,l1");
break;
}
}
int
pcie_initchild(dev_info_t *cdip)
{
uint16_t tmp16, reg16;
pcie_bus_t *bus_p;
bus_p = PCIE_DIP2BUS(cdip);
if (bus_p == NULL) {
PCIE_DBG("%s: BUS not found.\n",
ddi_driver_name(cdip));
return (DDI_FAILURE);
}
if (pcie_init_cfghdl(cdip) != DDI_SUCCESS)
return (DDI_FAILURE);
reg16 = PCIE_GET(16, bus_p, PCI_CONF_STAT);
PCIE_PUT(16, bus_p, PCI_CONF_STAT, reg16);
reg16 = PCIE_GET(16, bus_p, PCI_CONF_COMM);
tmp16 = (reg16 & pcie_command_default_fw) | pcie_command_default;
if (pcie_serr_disable_flag && PCIE_IS_PCIE(bus_p))
tmp16 &= ~PCI_COMM_SERR_ENABLE;
PCIE_PUT(16, bus_p, PCI_CONF_COMM, tmp16);
PCIE_DBG_CFG(cdip, bus_p, "COMMAND", 16, PCI_CONF_COMM, reg16);
if (PCIE_IS_BDG(bus_p)) {
reg16 = PCIE_GET(16, bus_p, PCI_BCNF_SEC_STATUS);
PCIE_PUT(16, bus_p, PCI_BCNF_SEC_STATUS, reg16);
reg16 = PCIE_GET(16, bus_p, PCI_BCNF_BCNTRL);
tmp16 = (reg16 & pcie_bdg_command_default_fw);
tmp16 |= PCI_BCNF_BCNTRL_SERR_ENABLE;
if (bus_p->bus_dev_ven_id == 0x037010DE)
tmp16 &= ~PCI_BCNF_BCNTRL_SERR_ENABLE;
if (pcie_command_default & PCI_COMM_PARITY_DETECT)
tmp16 |= PCI_BCNF_BCNTRL_PARITY_ENABLE;
if (pcie_aer_uce_mask & PCIE_AER_UCE_UR)
tmp16 &= ~PCI_BCNF_BCNTRL_MAST_AB_MODE;
else
tmp16 |= PCI_BCNF_BCNTRL_MAST_AB_MODE;
PCIE_PUT(16, bus_p, PCI_BCNF_BCNTRL, tmp16);
PCIE_DBG_CFG(cdip, bus_p, "SEC CMD", 16, PCI_BCNF_BCNTRL,
reg16);
}
if (PCIE_IS_PCIE(bus_p)) {
reg16 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
tmp16 = (reg16 & pcie_devctl_default_mask) |
(pcie_devctl_default & ~pcie_devctl_default_mask);
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL, tmp16);
PCIE_DBG_CAP(cdip, bus_p, "DEVCTL", 16, PCIE_DEVCTL, reg16);
pcie_enable_errors(cdip);
pcie_determine_serial(cdip);
pcie_determine_aspm(cdip);
pcie_capture_speeds(cdip);
}
bus_p->bus_ari = B_FALSE;
if ((pcie_ari_is_enabled(ddi_get_parent(cdip))
== PCIE_ARI_FORW_ENABLED) && (pcie_ari_device(cdip)
== PCIE_ARI_DEVICE)) {
bus_p->bus_ari = B_TRUE;
}
return (DDI_SUCCESS);
}
static void
pcie_init_pfd(dev_info_t *dip)
{
pf_data_t *pfd_p = PCIE_ZALLOC(pf_data_t);
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
PCIE_DIP2PFD(dip) = pfd_p;
pfd_p->pe_bus_p = bus_p;
pfd_p->pe_severity_flags = 0;
pfd_p->pe_severity_mask = 0;
pfd_p->pe_orig_severity_flags = 0;
pfd_p->pe_lock = B_FALSE;
pfd_p->pe_valid = B_FALSE;
if (PCIE_IS_ROOT(bus_p)) {
PCIE_ROOT_FAULT(pfd_p) = PCIE_ZALLOC(pf_root_fault_t);
PCIE_ROOT_FAULT(pfd_p)->scan_bdf = PCIE_INVALID_BDF;
PCIE_ROOT_EH_SRC(pfd_p) = PCIE_ZALLOC(pf_root_eh_src_t);
}
PCI_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pci_err_regs_t);
PFD_AFFECTED_DEV(pfd_p) = PCIE_ZALLOC(pf_affected_dev_t);
PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf = PCIE_INVALID_BDF;
if (PCIE_IS_BDG(bus_p))
PCI_BDG_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pci_bdg_err_regs_t);
if (PCIE_IS_PCIE(bus_p)) {
PCIE_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_err_regs_t);
if (PCIE_IS_RP(bus_p))
PCIE_RP_REG(pfd_p) =
PCIE_ZALLOC(pf_pcie_rp_err_regs_t);
PCIE_ADV_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_adv_err_regs_t);
PCIE_ADV_REG(pfd_p)->pcie_ue_tgt_bdf = PCIE_INVALID_BDF;
if (PCIE_IS_RP(bus_p)) {
PCIE_ADV_RP_REG(pfd_p) =
PCIE_ZALLOC(pf_pcie_adv_rp_err_regs_t);
PCIE_ADV_RP_REG(pfd_p)->pcie_rp_ce_src_id =
PCIE_INVALID_BDF;
PCIE_ADV_RP_REG(pfd_p)->pcie_rp_ue_src_id =
PCIE_INVALID_BDF;
} else if (PCIE_IS_PCIE_BDG(bus_p)) {
PCIE_ADV_BDG_REG(pfd_p) =
PCIE_ZALLOC(pf_pcie_adv_bdg_err_regs_t);
PCIE_ADV_BDG_REG(pfd_p)->pcie_sue_tgt_bdf =
PCIE_INVALID_BDF;
}
if (PCIE_IS_PCIE_BDG(bus_p) && PCIE_IS_PCIX(bus_p)) {
PCIX_BDG_ERR_REG(pfd_p) =
PCIE_ZALLOC(pf_pcix_bdg_err_regs_t);
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
PCIX_BDG_ECC_REG(pfd_p, 0) =
PCIE_ZALLOC(pf_pcix_ecc_regs_t);
PCIX_BDG_ECC_REG(pfd_p, 1) =
PCIE_ZALLOC(pf_pcix_ecc_regs_t);
}
}
PCIE_SLOT_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_slot_regs_t);
PCIE_SLOT_REG(pfd_p)->pcie_slot_regs_valid = B_FALSE;
PCIE_SLOT_REG(pfd_p)->pcie_slot_cap = 0;
PCIE_SLOT_REG(pfd_p)->pcie_slot_control = 0;
PCIE_SLOT_REG(pfd_p)->pcie_slot_status = 0;
} else if (PCIE_IS_PCIX(bus_p)) {
if (PCIE_IS_BDG(bus_p)) {
PCIX_BDG_ERR_REG(pfd_p) =
PCIE_ZALLOC(pf_pcix_bdg_err_regs_t);
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
PCIX_BDG_ECC_REG(pfd_p, 0) =
PCIE_ZALLOC(pf_pcix_ecc_regs_t);
PCIX_BDG_ECC_REG(pfd_p, 1) =
PCIE_ZALLOC(pf_pcix_ecc_regs_t);
}
} else {
PCIX_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pcix_err_regs_t);
if (PCIX_ECC_VERSION_CHECK(bus_p))
PCIX_ECC_REG(pfd_p) =
PCIE_ZALLOC(pf_pcix_ecc_regs_t);
}
}
}
static void
pcie_fini_pfd(dev_info_t *dip)
{
pf_data_t *pfd_p = PCIE_DIP2PFD(dip);
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (PCIE_IS_PCIE(bus_p)) {
if (PCIE_IS_PCIE_BDG(bus_p) && PCIE_IS_PCIX(bus_p)) {
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
kmem_free(PCIX_BDG_ECC_REG(pfd_p, 0),
sizeof (pf_pcix_ecc_regs_t));
kmem_free(PCIX_BDG_ECC_REG(pfd_p, 1),
sizeof (pf_pcix_ecc_regs_t));
}
kmem_free(PCIX_BDG_ERR_REG(pfd_p),
sizeof (pf_pcix_bdg_err_regs_t));
}
if (PCIE_IS_RP(bus_p))
kmem_free(PCIE_ADV_RP_REG(pfd_p),
sizeof (pf_pcie_adv_rp_err_regs_t));
else if (PCIE_IS_PCIE_BDG(bus_p))
kmem_free(PCIE_ADV_BDG_REG(pfd_p),
sizeof (pf_pcie_adv_bdg_err_regs_t));
kmem_free(PCIE_ADV_REG(pfd_p),
sizeof (pf_pcie_adv_err_regs_t));
if (PCIE_IS_RP(bus_p))
kmem_free(PCIE_RP_REG(pfd_p),
sizeof (pf_pcie_rp_err_regs_t));
kmem_free(PCIE_ERR_REG(pfd_p), sizeof (pf_pcie_err_regs_t));
} else if (PCIE_IS_PCIX(bus_p)) {
if (PCIE_IS_BDG(bus_p)) {
if (PCIX_ECC_VERSION_CHECK(bus_p)) {
kmem_free(PCIX_BDG_ECC_REG(pfd_p, 0),
sizeof (pf_pcix_ecc_regs_t));
kmem_free(PCIX_BDG_ECC_REG(pfd_p, 1),
sizeof (pf_pcix_ecc_regs_t));
}
kmem_free(PCIX_BDG_ERR_REG(pfd_p),
sizeof (pf_pcix_bdg_err_regs_t));
} else {
if (PCIX_ECC_VERSION_CHECK(bus_p))
kmem_free(PCIX_ECC_REG(pfd_p),
sizeof (pf_pcix_ecc_regs_t));
kmem_free(PCIX_ERR_REG(pfd_p),
sizeof (pf_pcix_err_regs_t));
}
}
if (PCIE_IS_BDG(bus_p))
kmem_free(PCI_BDG_ERR_REG(pfd_p),
sizeof (pf_pci_bdg_err_regs_t));
kmem_free(PFD_AFFECTED_DEV(pfd_p), sizeof (pf_affected_dev_t));
kmem_free(PCI_ERR_REG(pfd_p), sizeof (pf_pci_err_regs_t));
if (PCIE_IS_ROOT(bus_p)) {
kmem_free(PCIE_ROOT_FAULT(pfd_p), sizeof (pf_root_fault_t));
kmem_free(PCIE_ROOT_EH_SRC(pfd_p), sizeof (pf_root_eh_src_t));
}
kmem_free(PCIE_DIP2PFD(dip), sizeof (pf_data_t));
PCIE_DIP2PFD(dip) = NULL;
}
void
pcie_rc_init_pfd(dev_info_t *dip, pf_data_t *pfd_p)
{
pfd_p->pe_bus_p = PCIE_DIP2DOWNBUS(dip);
pfd_p->pe_severity_flags = 0;
pfd_p->pe_severity_mask = 0;
pfd_p->pe_orig_severity_flags = 0;
pfd_p->pe_lock = B_FALSE;
pfd_p->pe_valid = B_FALSE;
PCIE_ROOT_FAULT(pfd_p) = PCIE_ZALLOC(pf_root_fault_t);
PCIE_ROOT_FAULT(pfd_p)->scan_bdf = PCIE_INVALID_BDF;
PCIE_ROOT_EH_SRC(pfd_p) = PCIE_ZALLOC(pf_root_eh_src_t);
PCI_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pci_err_regs_t);
PFD_AFFECTED_DEV(pfd_p) = PCIE_ZALLOC(pf_affected_dev_t);
PFD_AFFECTED_DEV(pfd_p)->pe_affected_bdf = PCIE_INVALID_BDF;
PCI_BDG_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pci_bdg_err_regs_t);
PCIE_ERR_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_err_regs_t);
PCIE_RP_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_rp_err_regs_t);
PCIE_ADV_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_adv_err_regs_t);
PCIE_ADV_RP_REG(pfd_p) = PCIE_ZALLOC(pf_pcie_adv_rp_err_regs_t);
PCIE_ADV_RP_REG(pfd_p)->pcie_rp_ce_src_id = PCIE_INVALID_BDF;
PCIE_ADV_RP_REG(pfd_p)->pcie_rp_ue_src_id = PCIE_INVALID_BDF;
PCIE_ADV_REG(pfd_p)->pcie_ue_sev = pcie_aer_uce_severity;
}
void
pcie_rc_fini_pfd(pf_data_t *pfd_p)
{
kmem_free(PCIE_ADV_RP_REG(pfd_p), sizeof (pf_pcie_adv_rp_err_regs_t));
kmem_free(PCIE_ADV_REG(pfd_p), sizeof (pf_pcie_adv_err_regs_t));
kmem_free(PCIE_RP_REG(pfd_p), sizeof (pf_pcie_rp_err_regs_t));
kmem_free(PCIE_ERR_REG(pfd_p), sizeof (pf_pcie_err_regs_t));
kmem_free(PCI_BDG_ERR_REG(pfd_p), sizeof (pf_pci_bdg_err_regs_t));
kmem_free(PFD_AFFECTED_DEV(pfd_p), sizeof (pf_affected_dev_t));
kmem_free(PCI_ERR_REG(pfd_p), sizeof (pf_pci_err_regs_t));
kmem_free(PCIE_ROOT_FAULT(pfd_p), sizeof (pf_root_fault_t));
kmem_free(PCIE_ROOT_EH_SRC(pfd_p), sizeof (pf_root_eh_src_t));
}
void
pcie_rc_init_bus(dev_info_t *dip)
{
pcie_bus_t *bus_p;
bus_p = (pcie_bus_t *)kmem_zalloc(sizeof (pcie_bus_t), KM_SLEEP);
bus_p->bus_dip = dip;
bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_RC_PSEUDO;
bus_p->bus_hdr_type = PCI_HEADER_ONE;
bus_p->bus_aer_off = (uint16_t)-1;
atomic_or_uint(&bus_p->bus_fm_flags, PF_FM_READY);
ndi_set_bus_private(dip, B_FALSE, DEVI_PORT_TYPE_PCI, bus_p);
PCIE_BUS2DOM(bus_p) = PCIE_ZALLOC(pcie_domain_t);
}
void
pcie_rc_fini_bus(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2DOWNBUS(dip);
ndi_set_bus_private(dip, B_FALSE, 0, NULL);
kmem_free(PCIE_BUS2DOM(bus_p), sizeof (pcie_domain_t));
kmem_free(bus_p, sizeof (pcie_bus_t));
}
static int
pcie_width_to_int(pcie_link_width_t width)
{
switch (width) {
case PCIE_LINK_WIDTH_X1:
return (1);
case PCIE_LINK_WIDTH_X2:
return (2);
case PCIE_LINK_WIDTH_X4:
return (4);
case PCIE_LINK_WIDTH_X8:
return (8);
case PCIE_LINK_WIDTH_X12:
return (12);
case PCIE_LINK_WIDTH_X16:
return (16);
case PCIE_LINK_WIDTH_X32:
return (32);
default:
return (0);
}
}
static int64_t
pcie_speed_to_int(pcie_link_speed_t speed)
{
switch (speed) {
case PCIE_LINK_SPEED_2_5:
return (2500000000LL);
case PCIE_LINK_SPEED_5:
return (5000000000LL);
case PCIE_LINK_SPEED_8:
return (8000000000LL);
case PCIE_LINK_SPEED_16:
return (16000000000LL);
case PCIE_LINK_SPEED_32:
return (32000000000LL);
case PCIE_LINK_SPEED_64:
return (64000000000LL);
default:
return (0);
}
}
static void
pcie_speeds_to_devinfo(dev_info_t *dip, pcie_bus_t *bus_p)
{
if (bus_p->bus_max_width != PCIE_LINK_WIDTH_UNKNOWN) {
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"pcie-link-maximum-width",
pcie_width_to_int(bus_p->bus_max_width));
}
if (bus_p->bus_cur_width != PCIE_LINK_WIDTH_UNKNOWN) {
(void) ndi_prop_update_int(DDI_DEV_T_NONE, dip,
"pcie-link-current-width",
pcie_width_to_int(bus_p->bus_cur_width));
}
if (bus_p->bus_cur_speed != PCIE_LINK_SPEED_UNKNOWN) {
(void) ndi_prop_update_int64(DDI_DEV_T_NONE, dip,
"pcie-link-current-speed",
pcie_speed_to_int(bus_p->bus_cur_speed));
}
if (bus_p->bus_max_speed != PCIE_LINK_SPEED_UNKNOWN) {
(void) ndi_prop_update_int64(DDI_DEV_T_NONE, dip,
"pcie-link-maximum-speed",
pcie_speed_to_int(bus_p->bus_max_speed));
}
if (bus_p->bus_target_speed != PCIE_LINK_SPEED_UNKNOWN) {
(void) ndi_prop_update_int64(DDI_DEV_T_NONE, dip,
"pcie-link-target-speed",
pcie_speed_to_int(bus_p->bus_target_speed));
}
if ((bus_p->bus_speed_flags & PCIE_LINK_F_ADMIN_TARGET) != 0) {
(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
"pcie-link-admin-target-speed");
}
if (bus_p->bus_sup_speed != PCIE_LINK_SPEED_UNKNOWN) {
int64_t speeds[PCIE_NSPEEDS];
uint_t nspeeds = 0;
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_2_5) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_2_5);
}
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_5) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_5);
}
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_8) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_8);
}
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_16) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_16);
}
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_32) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_32);
}
if (bus_p->bus_sup_speed & PCIE_LINK_SPEED_64) {
speeds[nspeeds++] =
pcie_speed_to_int(PCIE_LINK_SPEED_64);
}
(void) ndi_prop_update_int64_array(DDI_DEV_T_NONE, dip,
"pcie-link-supported-speeds", speeds, nspeeds);
}
}
static void
pcie_capture_speeds(dev_info_t *dip)
{
uint16_t vers, status;
uint32_t cap, cap2, ctl2;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
dev_info_t *rcdip;
if (!PCIE_IS_PCIE(bus_p))
return;
rcdip = pcie_get_rc_dip(dip);
if (bus_p->bus_cfg_hdl == NULL) {
vers = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_PCIECAP);
} else {
vers = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
}
if (vers == PCI_EINVAL16)
return;
vers &= PCIE_PCIECAP_VER_MASK;
switch (vers) {
case PCIE_PCIECAP_VER_1_0:
cap2 = 0;
ctl2 = 0;
break;
case PCIE_PCIECAP_VER_2_0:
if (bus_p->bus_cfg_hdl == NULL) {
cap2 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_LINKCAP2);
ctl2 = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_LINKCTL2);
} else {
cap2 = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP2);
ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2);
}
if (cap2 == PCI_EINVAL32)
cap2 = 0;
if (ctl2 == PCI_EINVAL16)
ctl2 = 0;
break;
default:
return;
}
if (bus_p->bus_cfg_hdl == NULL) {
status = pci_cfgacc_get16(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_LINKSTS);
cap = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_LINKCAP);
} else {
status = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
cap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
}
if (status == PCI_EINVAL16 || cap == PCI_EINVAL32)
return;
mutex_enter(&bus_p->bus_speed_mutex);
switch (status & PCIE_LINKSTS_SPEED_MASK) {
case PCIE_LINKSTS_SPEED_2_5:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_2_5;
break;
case PCIE_LINKSTS_SPEED_5:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_5;
break;
case PCIE_LINKSTS_SPEED_8:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_8;
break;
case PCIE_LINKSTS_SPEED_16:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_16;
break;
case PCIE_LINKSTS_SPEED_32:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_32;
break;
case PCIE_LINKSTS_SPEED_64:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_64;
break;
default:
bus_p->bus_cur_speed = PCIE_LINK_SPEED_UNKNOWN;
break;
}
switch (status & PCIE_LINKSTS_NEG_WIDTH_MASK) {
case PCIE_LINKSTS_NEG_WIDTH_X1:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X1;
break;
case PCIE_LINKSTS_NEG_WIDTH_X2:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X2;
break;
case PCIE_LINKSTS_NEG_WIDTH_X4:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X4;
break;
case PCIE_LINKSTS_NEG_WIDTH_X8:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X8;
break;
case PCIE_LINKSTS_NEG_WIDTH_X12:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X12;
break;
case PCIE_LINKSTS_NEG_WIDTH_X16:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X16;
break;
case PCIE_LINKSTS_NEG_WIDTH_X32:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_X32;
break;
default:
bus_p->bus_cur_width = PCIE_LINK_WIDTH_UNKNOWN;
break;
}
if ((cap & PCIE_LINKCAP_DLL_ACTIVE_REP_CAPABLE) != 0 &&
(status & PCIE_LINKSTS_DLL_LINK_ACTIVE) == 0) {
bus_p->bus_cur_width = PCIE_LINK_WIDTH_UNKNOWN;
bus_p->bus_cur_speed = PCIE_LINK_SPEED_UNKNOWN;
}
switch (cap & PCIE_LINKCAP_MAX_WIDTH_MASK) {
case PCIE_LINKCAP_MAX_WIDTH_X1:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X1;
break;
case PCIE_LINKCAP_MAX_WIDTH_X2:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X2;
break;
case PCIE_LINKCAP_MAX_WIDTH_X4:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X4;
break;
case PCIE_LINKCAP_MAX_WIDTH_X8:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X8;
break;
case PCIE_LINKCAP_MAX_WIDTH_X12:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X12;
break;
case PCIE_LINKCAP_MAX_WIDTH_X16:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X16;
break;
case PCIE_LINKCAP_MAX_WIDTH_X32:
bus_p->bus_max_width = PCIE_LINK_WIDTH_X32;
break;
default:
bus_p->bus_max_width = PCIE_LINK_WIDTH_UNKNOWN;
break;
}
if (cap2 != 0) {
if (cap2 & PCIE_LINKCAP2_SPEED_2_5)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_2_5;
if (cap2 & PCIE_LINKCAP2_SPEED_5)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_5;
if (cap2 & PCIE_LINKCAP2_SPEED_8)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_8;
if (cap2 & PCIE_LINKCAP2_SPEED_16)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_16;
if (cap2 & PCIE_LINKCAP2_SPEED_32)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_32;
if (cap2 & PCIE_LINKCAP2_SPEED_64)
bus_p->bus_sup_speed |= PCIE_LINK_SPEED_64;
switch (cap & PCIE_LINKCAP_MAX_SPEED_MASK) {
case PCIE_LINKCAP_MAX_SPEED_2_5:
bus_p->bus_max_speed = PCIE_LINK_SPEED_2_5;
break;
case PCIE_LINKCAP_MAX_SPEED_5:
bus_p->bus_max_speed = PCIE_LINK_SPEED_5;
break;
case PCIE_LINKCAP_MAX_SPEED_8:
bus_p->bus_max_speed = PCIE_LINK_SPEED_8;
break;
case PCIE_LINKCAP_MAX_SPEED_16:
bus_p->bus_max_speed = PCIE_LINK_SPEED_16;
break;
case PCIE_LINKCAP_MAX_SPEED_32:
bus_p->bus_max_speed = PCIE_LINK_SPEED_32;
break;
case PCIE_LINKCAP_MAX_SPEED_64:
bus_p->bus_max_speed = PCIE_LINK_SPEED_64;
break;
default:
bus_p->bus_max_speed = PCIE_LINK_SPEED_UNKNOWN;
break;
}
} else {
if (cap & PCIE_LINKCAP_MAX_SPEED_5) {
bus_p->bus_max_speed = PCIE_LINK_SPEED_5;
bus_p->bus_sup_speed = PCIE_LINK_SPEED_2_5 |
PCIE_LINK_SPEED_5;
} else if (cap & PCIE_LINKCAP_MAX_SPEED_2_5) {
bus_p->bus_max_speed = PCIE_LINK_SPEED_2_5;
bus_p->bus_sup_speed = PCIE_LINK_SPEED_2_5;
}
}
switch (ctl2 & PCIE_LINKCTL2_TARGET_SPEED_MASK) {
case PCIE_LINKCTL2_TARGET_SPEED_2_5:
bus_p->bus_target_speed = PCIE_LINK_SPEED_2_5;
break;
case PCIE_LINKCTL2_TARGET_SPEED_5:
bus_p->bus_target_speed = PCIE_LINK_SPEED_5;
break;
case PCIE_LINKCTL2_TARGET_SPEED_8:
bus_p->bus_target_speed = PCIE_LINK_SPEED_8;
break;
case PCIE_LINKCTL2_TARGET_SPEED_16:
bus_p->bus_target_speed = PCIE_LINK_SPEED_16;
break;
case PCIE_LINKCTL2_TARGET_SPEED_32:
bus_p->bus_target_speed = PCIE_LINK_SPEED_32;
break;
case PCIE_LINKCTL2_TARGET_SPEED_64:
bus_p->bus_target_speed = PCIE_LINK_SPEED_64;
break;
default:
bus_p->bus_target_speed = PCIE_LINK_SPEED_UNKNOWN;
break;
}
pcie_speeds_to_devinfo(dip, bus_p);
mutex_exit(&bus_p->bus_speed_mutex);
}
pcie_bus_t *
pcie_init_bus(dev_info_t *dip, pcie_req_id_t bdf, uint8_t flags)
{
uint16_t status, base, baseptr, num_cap;
uint32_t capid;
int range_size;
pcie_bus_t *bus_p = NULL;
dev_info_t *rcdip;
dev_info_t *pdip;
const char *errstr = NULL;
if (!(flags & PCIE_BUS_INITIAL))
goto initial_done;
bus_p = kmem_zalloc(sizeof (pcie_bus_t), KM_SLEEP);
bus_p->bus_dip = dip;
bus_p->bus_bdf = bdf;
rcdip = pcie_get_rc_dip(dip);
ASSERT(rcdip != NULL);
bus_p->bus_dev_ven_id = pci_cfgacc_get32(rcdip, bdf, PCI_CONF_VENID);
bus_p->bus_rev_id = pci_cfgacc_get8(rcdip, bdf, PCI_CONF_REVID);
bus_p->bus_hdr_type = pci_cfgacc_get8(rcdip, bdf, PCI_CONF_HEADER);
bus_p->bus_hdr_type &= PCI_HEADER_TYPE_M;
bus_p->bus_dev_type = PCIE_PCIECAP_DEV_TYPE_PCI_PSEUDO;
status = pci_cfgacc_get16(rcdip, bdf, PCI_CONF_STAT);
if (status == PCI_CAP_EINVAL16 || !(status & PCI_STAT_CAP))
goto caps_done;
num_cap = 2;
switch (bus_p->bus_hdr_type) {
case PCI_HEADER_ZERO:
baseptr = PCI_CONF_CAP_PTR;
break;
case PCI_HEADER_PPB:
baseptr = PCI_BCNF_CAP_PTR;
break;
case PCI_HEADER_CARDBUS:
baseptr = PCI_CBUS_CAP_PTR;
break;
default:
cmn_err(CE_WARN, "%s: unexpected pci header type:%x",
__func__, bus_p->bus_hdr_type);
goto caps_done;
}
base = baseptr;
for (base = pci_cfgacc_get8(rcdip, bdf, base); base && num_cap;
base = pci_cfgacc_get8(rcdip, bdf, base + PCI_CAP_NEXT_PTR)) {
capid = pci_cfgacc_get8(rcdip, bdf, base);
uint16_t pcap;
switch (capid) {
case PCI_CAP_ID_PCI_E:
bus_p->bus_pcie_off = base;
pcap = pci_cfgacc_get16(rcdip, bdf, base +
PCIE_PCIECAP);
bus_p->bus_dev_type = pcap & PCIE_PCIECAP_DEV_TYPE_MASK;
bus_p->bus_pcie_vers = pcap & PCIE_PCIECAP_VER_MASK;
if ((PCIE_IS_RP(bus_p) || PCIE_IS_SWD(bus_p)) &&
(pci_cfgacc_get16(rcdip, bdf, base + PCIE_PCIECAP)
& PCIE_PCIECAP_SLOT_IMPL) &&
(pci_cfgacc_get32(rcdip, bdf, base + PCIE_SLOTCAP)
& PCIE_SLOTCAP_HP_CAPABLE))
bus_p->bus_hp_sup_modes |= PCIE_NATIVE_HP_MODE;
num_cap--;
break;
case PCI_CAP_ID_PCIX:
bus_p->bus_pcix_off = base;
if (PCIE_IS_BDG(bus_p))
bus_p->bus_ecc_ver =
pci_cfgacc_get16(rcdip, bdf, base +
PCI_PCIX_SEC_STATUS) & PCI_PCIX_VER_MASK;
else
bus_p->bus_ecc_ver =
pci_cfgacc_get16(rcdip, bdf, base +
PCI_PCIX_COMMAND) & PCI_PCIX_VER_MASK;
num_cap--;
break;
default:
break;
}
}
if (PCIE_IS_BDG(bus_p)) {
base = baseptr;
for (base = pci_cfgacc_get8(rcdip, bdf, base);
base; base = pci_cfgacc_get8(rcdip, bdf,
base + PCI_CAP_NEXT_PTR)) {
capid = pci_cfgacc_get8(rcdip, bdf, base);
if (capid == PCI_CAP_ID_PCI_HOTPLUG) {
bus_p->bus_pci_hp_off = base;
bus_p->bus_hp_sup_modes |= PCIE_PCI_HP_MODE;
break;
}
}
}
if (!PCIE_IS_PCIE(bus_p))
goto caps_done;
for (base = PCIE_EXT_CAP; base; base = (capid >>
PCIE_EXT_CAP_NEXT_PTR_SHIFT) & PCIE_EXT_CAP_NEXT_PTR_MASK) {
capid = pci_cfgacc_get32(rcdip, bdf, base);
if (capid == PCI_CAP_EINVAL32)
break;
switch ((capid >> PCIE_EXT_CAP_ID_SHIFT) &
PCIE_EXT_CAP_ID_MASK) {
case PCIE_EXT_CAP_ID_AER:
bus_p->bus_aer_off = base;
break;
case PCIE_EXT_CAP_ID_DEV3:
bus_p->bus_dev3_off = base;
break;
}
}
caps_done:
if (PCIE_IS_RP(bus_p)) {
bus_p->bus_rp_dip = dip;
bus_p->bus_rp_bdf = bus_p->bus_bdf;
bus_p->bus_fab = PCIE_ZALLOC(pcie_fabric_data_t);
} else {
for (pdip = ddi_get_parent(dip); pdip;
pdip = ddi_get_parent(pdip)) {
pcie_bus_t *parent_bus_p = PCIE_DIP2BUS(pdip);
if (parent_bus_p->bus_rp_dip != NULL) {
bus_p->bus_rp_dip = parent_bus_p->bus_rp_dip;
bus_p->bus_rp_bdf = parent_bus_p->bus_rp_bdf;
break;
}
if (PCIE_IS_ROOT(parent_bus_p)) {
bus_p->bus_rp_dip = pdip;
bus_p->bus_rp_bdf = parent_bus_p->bus_bdf;
break;
}
}
}
bus_p->bus_soft_state = PCI_SOFT_STATE_CLOSED;
(void) atomic_swap_uint(&bus_p->bus_fm_flags, 0);
ndi_set_bus_private(dip, B_TRUE, DEVI_PORT_TYPE_PCI, (void *)bus_p);
if (PCIE_IS_HOTPLUG_CAPABLE(dip))
(void) ndi_prop_create_boolean(DDI_DEV_T_NONE, dip,
"hotplug-capable");
initial_done:
if (!(flags & PCIE_BUS_FINAL))
goto final_done;
bus_p = PCIE_DIP2BUS(dip);
if (PCIE_IS_BDG(bus_p)) {
range_size = sizeof (pci_bus_range_t);
if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"bus-range", (caddr_t)&bus_p->bus_bus_range, &range_size)
!= DDI_PROP_SUCCESS) {
errstr = "Cannot find \"bus-range\" property";
cmn_err(CE_WARN,
"PCIE init err info failed BDF 0x%x:%s\n",
bus_p->bus_bdf, errstr);
}
rcdip = pcie_get_rc_dip(dip);
ASSERT(rcdip != NULL);
bus_p->bus_bdg_secbus = pci_cfgacc_get8(rcdip,
bus_p->bus_bdf, PCI_BCNF_SECBUS);
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"ranges", (caddr_t)&bus_p->bus_addr_ranges,
&bus_p->bus_addr_entries) != DDI_PROP_SUCCESS)
bus_p->bus_addr_entries = 0;
bus_p->bus_addr_entries /= sizeof (ppb_ranges_t);
}
if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"assigned-addresses", (caddr_t)&bus_p->bus_assigned_addr,
&bus_p->bus_assigned_entries) == DDI_PROP_SUCCESS)
bus_p->bus_assigned_entries /= sizeof (pci_regspec_t);
else
bus_p->bus_assigned_entries = 0;
pcie_init_pfd(dip);
pcie_init_plat(dip);
pcie_capture_speeds(dip);
final_done:
PCIE_DBG("Add %s(dip 0x%p, bdf 0x%x, secbus 0x%x)\n",
ddi_driver_name(dip), (void *)dip, bus_p->bus_bdf,
bus_p->bus_bdg_secbus);
#ifdef DEBUG
if (bus_p != NULL) {
pcie_print_bus(bus_p);
}
#endif
return (bus_p);
}
void
pcie_fini_bus(dev_info_t *dip, uint8_t flags)
{
pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip);
ASSERT(bus_p);
if (flags & PCIE_BUS_INITIAL) {
pcie_fini_plat(dip);
pcie_fini_pfd(dip);
if (PCIE_IS_RP(bus_p)) {
kmem_free(bus_p->bus_fab, sizeof (pcie_fabric_data_t));
bus_p->bus_fab = NULL;
}
kmem_free(bus_p->bus_assigned_addr,
(sizeof (pci_regspec_t) * bus_p->bus_assigned_entries));
kmem_free(bus_p->bus_addr_ranges,
(sizeof (ppb_ranges_t) * bus_p->bus_addr_entries));
bus_p->bus_assigned_addr = NULL;
bus_p->bus_addr_ranges = NULL;
bus_p->bus_assigned_entries = 0;
bus_p->bus_addr_entries = 0;
}
if (flags & PCIE_BUS_FINAL) {
if (PCIE_IS_HOTPLUG_CAPABLE(dip)) {
(void) ndi_prop_remove(DDI_DEV_T_NONE, dip,
"hotplug-capable");
}
ndi_set_bus_private(dip, B_TRUE, 0, NULL);
kmem_free(bus_p, sizeof (pcie_bus_t));
}
}
int
pcie_postattach_child(dev_info_t *cdip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(cdip);
if (!bus_p)
return (DDI_FAILURE);
return (pcie_enable_ce(cdip));
}
void
pcie_uninitchild(dev_info_t *cdip)
{
pcie_disable_errors(cdip);
pcie_fini_cfghdl(cdip);
pcie_fini_dom(cdip);
}
dev_info_t *
pcie_get_rc_dip(dev_info_t *dip)
{
dev_info_t *rcdip;
pcie_bus_t *rc_bus_p;
for (rcdip = ddi_get_parent(dip); rcdip;
rcdip = ddi_get_parent(rcdip)) {
rc_bus_p = PCIE_DIP2BUS(rcdip);
if (rc_bus_p && PCIE_IS_RC(rc_bus_p))
break;
}
return (rcdip);
}
boolean_t
pcie_is_pci_device(dev_info_t *dip)
{
dev_info_t *pdip;
char *device_type;
pdip = ddi_get_parent(dip);
if (pdip == NULL)
return (B_FALSE);
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip, DDI_PROP_DONTPASS,
"device_type", &device_type) != DDI_PROP_SUCCESS)
return (B_FALSE);
if (strcmp(device_type, "pciex") != 0 &&
strcmp(device_type, "pci") != 0) {
ddi_prop_free(device_type);
return (B_FALSE);
}
ddi_prop_free(device_type);
return (B_TRUE);
}
typedef struct {
boolean_t init;
uint8_t flags;
} pcie_bus_arg_t;
static int
pcie_fab_do_init_fini(dev_info_t *dip, void *arg)
{
pcie_req_id_t bdf;
pcie_bus_arg_t *bus_arg = (pcie_bus_arg_t *)arg;
if (!pcie_is_pci_device(dip))
goto out;
if (bus_arg->init) {
if (pcie_get_bdf_from_dip(dip, &bdf) != DDI_SUCCESS)
goto out;
(void) pcie_init_bus(dip, bdf, bus_arg->flags);
} else {
(void) pcie_fini_bus(dip, bus_arg->flags);
}
return (DDI_WALK_CONTINUE);
out:
return (DDI_WALK_PRUNECHILD);
}
void
pcie_fab_init_bus(dev_info_t *rcdip, uint8_t flags)
{
dev_info_t *dip = ddi_get_child(rcdip);
pcie_bus_arg_t arg;
arg.init = B_TRUE;
arg.flags = flags;
ndi_devi_enter(rcdip);
ddi_walk_devs(dip, pcie_fab_do_init_fini, &arg);
ndi_devi_exit(rcdip);
}
void
pcie_fab_fini_bus(dev_info_t *rcdip, uint8_t flags)
{
dev_info_t *dip = ddi_get_child(rcdip);
pcie_bus_arg_t arg;
arg.init = B_FALSE;
arg.flags = flags;
ndi_devi_enter(rcdip);
ddi_walk_devs(dip, pcie_fab_do_init_fini, &arg);
ndi_devi_exit(rcdip);
}
void
pcie_enable_errors(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint16_t reg16, tmp16;
uint32_t reg32, tmp32;
ASSERT(bus_p);
pcie_clear_errors(dip);
if (!PCIE_IS_PCIE(bus_p))
return;
if ((reg16 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL)) !=
PCI_CAP_EINVAL16) {
tmp16 = (reg16 & pcie_devctl_default_mask) |
(pcie_devctl_default & ~pcie_devctl_default_mask) |
(pcie_base_err_default & ~PCIE_DEVCTL_CE_REPORTING_EN);
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL, tmp16);
PCIE_DBG_CAP(dip, bus_p, "DEVCTL", 16, PCIE_DEVCTL, reg16);
}
if (PCIE_IS_ROOT(bus_p) &&
(reg16 = PCIE_CAP_GET(16, bus_p, PCIE_ROOTCTL)) !=
PCI_CAP_EINVAL16) {
tmp16 = pcie_serr_disable_flag ?
(pcie_root_ctrl_default & ~PCIE_ROOT_SYS_ERR) :
pcie_root_ctrl_default;
PCIE_CAP_PUT(16, bus_p, PCIE_ROOTCTL, tmp16);
PCIE_DBG_CAP(dip, bus_p, "ROOT DEVCTL", 16, PCIE_ROOTCTL,
reg16);
}
if (!PCIE_HAS_AER(bus_p))
return;
if ((reg32 = PCIE_AER_GET(32, bus_p, PCIE_AER_UCE_SERV)) !=
PCI_CAP_EINVAL32) {
tmp32 = pcie_aer_uce_severity;
PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_SERV, tmp32);
PCIE_DBG_AER(dip, bus_p, "AER UCE SEV", 32, PCIE_AER_UCE_SERV,
reg32);
}
if ((reg32 = PCIE_AER_GET(32, bus_p, PCIE_AER_UCE_MASK)) !=
PCI_CAP_EINVAL32) {
tmp32 = pcie_aer_uce_mask;
PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_MASK, tmp32);
PCIE_DBG_AER(dip, bus_p, "AER UCE MASK", 32, PCIE_AER_UCE_MASK,
reg32);
}
if ((reg32 = PCIE_AER_GET(32, bus_p, PCIE_AER_CTL)) !=
PCI_CAP_EINVAL32) {
tmp32 = reg32 | pcie_ecrc_value;
PCIE_AER_PUT(32, bus_p, PCIE_AER_CTL, tmp32);
PCIE_DBG_AER(dip, bus_p, "AER CTL", 32, PCIE_AER_CTL, reg32);
}
if (!PCIE_IS_PCIE_BDG(bus_p))
goto root;
if ((reg32 = PCIE_AER_GET(32, bus_p, PCIE_AER_SUCE_SERV)) !=
PCI_CAP_EINVAL32) {
tmp32 = pcie_aer_suce_severity;
PCIE_AER_PUT(32, bus_p, PCIE_AER_SUCE_SERV, tmp32);
PCIE_DBG_AER(dip, bus_p, "AER SUCE SEV", 32, PCIE_AER_SUCE_SERV,
reg32);
}
if ((reg32 = PCIE_AER_GET(32, bus_p, PCIE_AER_SUCE_MASK)) !=
PCI_CAP_EINVAL32) {
PCIE_AER_PUT(32, bus_p, PCIE_AER_SUCE_MASK, pcie_aer_suce_mask);
PCIE_DBG_AER(dip, bus_p, "AER SUCE MASK", 32,
PCIE_AER_SUCE_MASK, reg32);
}
root:
if (!PCIE_IS_ROOT(bus_p))
return;
if ((reg16 = PCIE_AER_GET(16, bus_p, PCIE_AER_RE_CMD)) !=
PCI_CAP_EINVAL16) {
PCIE_AER_PUT(16, bus_p, PCIE_AER_RE_CMD,
pcie_root_error_cmd_default);
PCIE_DBG_AER(dip, bus_p, "AER Root Err Cmd", 16,
PCIE_AER_RE_CMD, reg16);
}
}
int
pcie_enable_ce(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint16_t device_sts, device_ctl;
uint32_t tmp_pcie_aer_ce_mask;
if (!PCIE_IS_PCIE(bus_p))
return (DDI_SUCCESS);
tmp_pcie_aer_ce_mask = (uint32_t)ddi_prop_get_int(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS, "pcie_ce_mask", pcie_aer_ce_mask);
if (tmp_pcie_aer_ce_mask == (uint32_t)-1) {
return (DDI_SUCCESS);
}
if (PCIE_HAS_AER(bus_p)) {
PCIE_AER_PUT(32, bus_p, PCIE_AER_CE_MASK, tmp_pcie_aer_ce_mask);
PCIE_DBG_AER(dip, bus_p, "AER CE MASK", 32, PCIE_AER_CE_MASK,
0);
PCIE_AER_PUT(32, bus_p, PCIE_AER_CE_STS, -1);
}
if ((device_sts = PCIE_CAP_GET(16, bus_p, PCIE_DEVSTS)) !=
PCI_CAP_EINVAL16)
PCIE_CAP_PUT(16, bus_p, PCIE_DEVSTS,
device_sts & (~PCIE_DEVSTS_CE_DETECTED));
device_ctl = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL,
(device_ctl & (~PCIE_DEVCTL_ERR_MASK)) | pcie_base_err_default);
PCIE_DBG_CAP(dip, bus_p, "DEVCTL", 16, PCIE_DEVCTL, device_ctl);
return (DDI_SUCCESS);
}
void
pcie_disable_errors(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint16_t device_ctl;
uint32_t aer_reg;
if (!PCIE_IS_PCIE(bus_p))
return;
device_ctl = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL);
device_ctl &= ~PCIE_DEVCTL_ERR_MASK;
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL, device_ctl);
if (!PCIE_HAS_AER(bus_p))
goto root;
PCIE_AER_PUT(32, bus_p, PCIE_AER_UCE_MASK, PCIE_AER_UCE_BITS);
PCIE_AER_PUT(32, bus_p, PCIE_AER_CE_MASK, PCIE_AER_CE_BITS);
if ((aer_reg = PCIE_AER_GET(32, bus_p, PCIE_AER_CTL)) !=
PCI_CAP_EINVAL32) {
aer_reg &= ~(PCIE_AER_CTL_ECRC_GEN_ENA |
PCIE_AER_CTL_ECRC_CHECK_ENA);
PCIE_AER_PUT(32, bus_p, PCIE_AER_CTL, aer_reg);
}
if (!PCIE_IS_PCIE_BDG(bus_p))
goto root;
PCIE_AER_PUT(32, bus_p, PCIE_AER_SUCE_MASK, PCIE_AER_SUCE_BITS);
root:
if (!PCIE_IS_ROOT(bus_p))
return;
if (!pcie_serr_disable_flag) {
device_ctl = PCIE_CAP_GET(16, bus_p, PCIE_ROOTCTL);
device_ctl &= ~PCIE_ROOT_SYS_ERR;
PCIE_CAP_PUT(16, bus_p, PCIE_ROOTCTL, device_ctl);
}
if (!PCIE_HAS_AER(bus_p))
return;
if ((device_ctl = PCIE_CAP_GET(16, bus_p, PCIE_AER_RE_CMD)) !=
PCI_CAP_EINVAL16) {
device_ctl &= ~pcie_root_error_cmd_default;
PCIE_CAP_PUT(16, bus_p, PCIE_AER_RE_CMD, device_ctl);
}
}
int
pcie_get_bdf_from_dip(dev_info_t *dip, pcie_req_id_t *bdf)
{
pci_regspec_t *regspec;
int reglen;
if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
"reg", (int **)®spec, (uint_t *)®len) != DDI_SUCCESS)
return (DDI_FAILURE);
if (reglen < (sizeof (pci_regspec_t) / sizeof (int))) {
ddi_prop_free(regspec);
return (DDI_FAILURE);
}
*bdf = (regspec->pci_phys_hi & (PCI_REG_BDFR_M ^ PCI_REG_REG_M)) >> 8;
ddi_prop_free(regspec);
return (DDI_SUCCESS);
}
dev_info_t *
pcie_get_my_childs_dip(dev_info_t *dip, dev_info_t *rdip)
{
dev_info_t *cdip = rdip;
for (; ddi_get_parent(cdip) != dip; cdip = ddi_get_parent(cdip))
;
return (cdip);
}
uint32_t
pcie_get_bdf_for_dma_xfer(dev_info_t *dip, dev_info_t *rdip)
{
dev_info_t *cdip;
if (rdip == dip)
return (PCIE_INVALID_BDF);
cdip = pcie_get_my_childs_dip(dip, rdip);
return (PCI_GET_PCIE2PCI_SECBUS(cdip) ?
PCIE_INVALID_BDF : PCI_GET_BDF(cdip));
}
uint32_t
pcie_get_aer_uce_mask()
{
return (pcie_aer_uce_mask);
}
uint32_t
pcie_get_aer_ce_mask()
{
return (pcie_aer_ce_mask);
}
uint32_t
pcie_get_aer_suce_mask()
{
return (pcie_aer_suce_mask);
}
uint32_t
pcie_get_serr_mask()
{
return (pcie_serr_disable_flag);
}
void
pcie_set_aer_uce_mask(uint32_t mask)
{
pcie_aer_uce_mask = mask;
if (mask & PCIE_AER_UCE_UR)
pcie_base_err_default &= ~PCIE_DEVCTL_UR_REPORTING_EN;
else
pcie_base_err_default |= PCIE_DEVCTL_UR_REPORTING_EN;
if (mask & PCIE_AER_UCE_ECRC)
pcie_ecrc_value = 0;
}
void
pcie_set_aer_ce_mask(uint32_t mask)
{
pcie_aer_ce_mask = mask;
}
void
pcie_set_aer_suce_mask(uint32_t mask)
{
pcie_aer_suce_mask = mask;
}
void
pcie_set_serr_mask(uint32_t mask)
{
pcie_serr_disable_flag = mask;
}
boolean_t
pcie_is_child(dev_info_t *dip, dev_info_t *rdip)
{
dev_info_t *cdip = ddi_get_child(dip);
for (; cdip; cdip = ddi_get_next_sibling(cdip))
if (cdip == rdip)
break;
return (cdip != NULL);
}
boolean_t
pcie_is_link_disabled(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (PCIE_IS_PCIE(bus_p)) {
if (PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL) &
PCIE_LINKCTL_LINK_DISABLE)
return (B_TRUE);
}
return (B_FALSE);
}
int
pcie_root_port(dev_info_t *dip)
{
int port_type;
uint16_t cap_ptr;
ddi_acc_handle_t config_handle;
dev_info_t *cdip = ddi_get_child(dip);
for (; cdip; cdip = ddi_get_next_sibling(cdip)) {
if (pci_config_setup(cdip, &config_handle) != DDI_SUCCESS)
continue;
if ((PCI_CAP_LOCATE(config_handle, PCI_CAP_ID_PCI_E,
&cap_ptr)) == DDI_FAILURE) {
pci_config_teardown(&config_handle);
continue;
}
port_type = PCI_CAP_GET16(config_handle, 0, cap_ptr,
PCIE_PCIECAP) & PCIE_PCIECAP_DEV_TYPE_MASK;
pci_config_teardown(&config_handle);
if (port_type == PCIE_PCIECAP_DEV_TYPE_ROOT)
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
int
pcie_dev(dev_info_t *dip)
{
char *device_type;
int rc = DDI_FAILURE;
dev_info_t *pdip = ddi_get_parent(dip);
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, pdip,
DDI_PROP_DONTPASS, "device_type", &device_type)
!= DDI_PROP_SUCCESS) {
return (DDI_FAILURE);
}
if (strcmp(device_type, "pciex") == 0)
rc = DDI_SUCCESS;
else
rc = DDI_FAILURE;
ddi_prop_free(device_type);
return (rc);
}
void
pcie_set_rber_fatal(dev_info_t *dip, boolean_t val)
{
pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip);
bus_p->bus_pfd->pe_rber_fatal = val;
}
boolean_t
pcie_get_rber_fatal(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2UPBUS(dip);
pcie_bus_t *rp_bus_p = PCIE_DIP2UPBUS(bus_p->bus_rp_dip);
return (rp_bus_p->bus_pfd->pe_rber_fatal);
}
int
pcie_ari_supported(dev_info_t *dip)
{
uint32_t devcap2;
uint16_t pciecap;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint8_t dev_type;
PCIE_DBG("pcie_ari_supported: dip=%p\n", dip);
if (bus_p == NULL)
return (PCIE_ARI_FORW_NOT_SUPPORTED);
dev_type = bus_p->bus_dev_type;
if ((dev_type != PCIE_PCIECAP_DEV_TYPE_DOWN) &&
(dev_type != PCIE_PCIECAP_DEV_TYPE_ROOT))
return (PCIE_ARI_FORW_NOT_SUPPORTED);
if (pcie_disable_ari) {
PCIE_DBG("pcie_ari_supported: dip=%p: ARI Disabled\n", dip);
return (PCIE_ARI_FORW_NOT_SUPPORTED);
}
pciecap = PCIE_CAP_GET(16, bus_p, PCIE_PCIECAP);
if ((pciecap & PCIE_PCIECAP_VER_MASK) < PCIE_PCIECAP_VER_2_0) {
PCIE_DBG("pcie_ari_supported: dip=%p: Not 2.0\n", dip);
return (PCIE_ARI_FORW_NOT_SUPPORTED);
}
devcap2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCAP2);
PCIE_DBG("pcie_ari_supported: dip=%p: DevCap2=0x%x\n",
dip, devcap2);
if (devcap2 & PCIE_DEVCAP2_ARI_FORWARD) {
PCIE_DBG("pcie_ari_supported: "
"dip=%p: ARI Forwarding is supported\n", dip);
return (PCIE_ARI_FORW_SUPPORTED);
}
return (PCIE_ARI_FORW_NOT_SUPPORTED);
}
int
pcie_ari_enable(dev_info_t *dip)
{
uint16_t devctl2;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
PCIE_DBG("pcie_ari_enable: dip=%p\n", dip);
if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
return (DDI_FAILURE);
devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
devctl2 |= PCIE_DEVCTL2_ARI_FORWARD_EN;
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
PCIE_DBG("pcie_ari_enable: dip=%p: writing 0x%x to DevCtl2\n",
dip, devctl2);
return (DDI_SUCCESS);
}
int
pcie_ari_disable(dev_info_t *dip)
{
uint16_t devctl2;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
PCIE_DBG("pcie_ari_disable: dip=%p\n", dip);
if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
return (DDI_FAILURE);
devctl2 = PCIE_CAP_GET(16, bus_p, PCIE_DEVCTL2);
devctl2 &= ~PCIE_DEVCTL2_ARI_FORWARD_EN;
PCIE_CAP_PUT(16, bus_p, PCIE_DEVCTL2, devctl2);
PCIE_DBG("pcie_ari_disable: dip=%p: writing 0x%x to DevCtl2\n",
dip, devctl2);
return (DDI_SUCCESS);
}
int
pcie_ari_is_enabled(dev_info_t *dip)
{
uint16_t devctl2;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
PCIE_DBG("pcie_ari_is_enabled: dip=%p\n", dip);
if (pcie_ari_supported(dip) == PCIE_ARI_FORW_NOT_SUPPORTED)
return (PCIE_ARI_FORW_DISABLED);
devctl2 = PCIE_CAP_GET(32, bus_p, PCIE_DEVCTL2);
PCIE_DBG("pcie_ari_is_enabled: dip=%p: DevCtl2=0x%x\n",
dip, devctl2);
if (devctl2 & PCIE_DEVCTL2_ARI_FORWARD_EN) {
PCIE_DBG("pcie_ari_is_enabled: "
"dip=%p: ARI Forwarding is enabled\n", dip);
return (PCIE_ARI_FORW_ENABLED);
}
return (PCIE_ARI_FORW_DISABLED);
}
int
pcie_ari_device(dev_info_t *dip)
{
ddi_acc_handle_t handle;
uint16_t cap_ptr;
PCIE_DBG("pcie_ari_device: dip=%p\n", dip);
if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
return (PCIE_NOT_ARI_DEVICE);
if ((PCI_CAP_LOCATE(handle, PCI_CAP_ID_PCI_E, &cap_ptr))
!= DDI_SUCCESS) {
pci_config_teardown(&handle);
return (PCIE_NOT_ARI_DEVICE);
}
if ((PCI_CAP_LOCATE(handle, PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI),
&cap_ptr)) == DDI_FAILURE) {
pci_config_teardown(&handle);
return (PCIE_NOT_ARI_DEVICE);
}
PCIE_DBG("pcie_ari_device: ARI Device dip=%p\n", dip);
pci_config_teardown(&handle);
return (PCIE_ARI_DEVICE);
}
int
pcie_ari_get_next_function(dev_info_t *dip, int *func)
{
uint32_t val;
uint16_t cap_ptr, next_function;
ddi_acc_handle_t handle;
if (pci_config_setup(dip, &handle) != DDI_SUCCESS)
return (DDI_FAILURE);
if ((PCI_CAP_LOCATE(handle,
PCI_CAP_XCFG_SPC(PCIE_EXT_CAP_ID_ARI), &cap_ptr)) == DDI_FAILURE) {
pci_config_teardown(&handle);
return (DDI_FAILURE);
}
val = PCI_CAP_GET32(handle, 0, cap_ptr, PCIE_ARI_CAP);
next_function = (val >> PCIE_ARI_CAP_NEXT_FUNC_SHIFT) &
PCIE_ARI_CAP_NEXT_FUNC_MASK;
pci_config_teardown(&handle);
*func = next_function;
return (DDI_SUCCESS);
}
dev_info_t *
pcie_func_to_dip(dev_info_t *dip, pcie_req_id_t function)
{
pcie_req_id_t child_bdf;
dev_info_t *cdip;
for (cdip = ddi_get_child(dip); cdip;
cdip = ddi_get_next_sibling(cdip)) {
if (pcie_get_bdf_from_dip(cdip, &child_bdf) == DDI_FAILURE)
return (NULL);
if ((child_bdf & PCIE_REQ_ID_ARI_FUNC_MASK) == function)
return (cdip);
}
return (NULL);
}
#ifdef DEBUG
static void
pcie_print_bus(pcie_bus_t *bus_p)
{
pcie_dbg("\tbus_dip = 0x%p\n", bus_p->bus_dip);
pcie_dbg("\tbus_fm_flags = 0x%x\n", bus_p->bus_fm_flags);
pcie_dbg("\tbus_bdf = 0x%x\n", bus_p->bus_bdf);
pcie_dbg("\tbus_dev_ven_id = 0x%x\n", bus_p->bus_dev_ven_id);
pcie_dbg("\tbus_rev_id = 0x%x\n", bus_p->bus_rev_id);
pcie_dbg("\tbus_hdr_type = 0x%x\n", bus_p->bus_hdr_type);
pcie_dbg("\tbus_dev_type = 0x%x\n", bus_p->bus_dev_type);
pcie_dbg("\tbus_bdg_secbus = 0x%x\n", bus_p->bus_bdg_secbus);
pcie_dbg("\tbus_pcie_off = 0x%x\n", bus_p->bus_pcie_off);
pcie_dbg("\tbus_aer_off = 0x%x\n", bus_p->bus_aer_off);
pcie_dbg("\tbus_pcix_off = 0x%x\n", bus_p->bus_pcix_off);
pcie_dbg("\tbus_ecc_ver = 0x%x\n", bus_p->bus_ecc_ver);
}
int pcie_dbg_print = 0;
void
pcie_dbg(char *fmt, ...)
{
va_list ap;
if (!pcie_debug_flags) {
return;
}
va_start(ap, fmt);
if (servicing_interrupt()) {
if (pcie_dbg_print) {
prom_vprintf(fmt, ap);
}
} else {
prom_vprintf(fmt, ap);
}
va_end(ap);
}
#endif
boolean_t
pcie_link_bw_supported(dev_info_t *dip)
{
uint32_t linkcap;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (!PCIE_IS_PCIE(bus_p)) {
return (B_FALSE);
}
if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
return (B_FALSE);
}
linkcap = PCIE_CAP_GET(32, bus_p, PCIE_LINKCAP);
return ((linkcap & PCIE_LINKCAP_LINK_BW_NOTIFY_CAP) != 0);
}
int
pcie_link_bw_enable(dev_info_t *dip)
{
uint16_t linkctl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (pcie_disable_lbw != 0) {
return (DDI_FAILURE);
}
if (!pcie_link_bw_supported(dip)) {
return (DDI_FAILURE);
}
mutex_init(&bus_p->bus_lbw_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&bus_p->bus_lbw_cv, NULL, CV_DRIVER, NULL);
linkctl = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL);
linkctl |= PCIE_LINKCTL_LINK_BW_INTR_EN;
linkctl |= PCIE_LINKCTL_LINK_AUTO_BW_INTR_EN;
PCIE_CAP_PUT(16, bus_p, PCIE_LINKCTL, linkctl);
bus_p->bus_lbw_pbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
bus_p->bus_lbw_cbuf = kmem_zalloc(MAXPATHLEN, KM_SLEEP);
bus_p->bus_lbw_state |= PCIE_LBW_S_ENABLED;
return (DDI_SUCCESS);
}
int
pcie_link_bw_disable(dev_info_t *dip)
{
uint16_t linkctl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if ((bus_p->bus_lbw_state & PCIE_LBW_S_ENABLED) == 0) {
return (DDI_FAILURE);
}
mutex_enter(&bus_p->bus_lbw_mutex);
while ((bus_p->bus_lbw_state &
(PCIE_LBW_S_DISPATCHED | PCIE_LBW_S_RUNNING)) != 0) {
cv_wait(&bus_p->bus_lbw_cv, &bus_p->bus_lbw_mutex);
}
mutex_exit(&bus_p->bus_lbw_mutex);
linkctl = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL);
linkctl &= ~PCIE_LINKCTL_LINK_BW_INTR_EN;
linkctl &= ~PCIE_LINKCTL_LINK_AUTO_BW_INTR_EN;
PCIE_CAP_PUT(16, bus_p, PCIE_LINKCTL, linkctl);
bus_p->bus_lbw_state &= ~PCIE_LBW_S_ENABLED;
kmem_free(bus_p->bus_lbw_pbuf, MAXPATHLEN);
kmem_free(bus_p->bus_lbw_cbuf, MAXPATHLEN);
bus_p->bus_lbw_pbuf = NULL;
bus_p->bus_lbw_cbuf = NULL;
mutex_destroy(&bus_p->bus_lbw_mutex);
cv_destroy(&bus_p->bus_lbw_cv);
return (DDI_SUCCESS);
}
void
pcie_link_bw_taskq(void *arg)
{
dev_info_t *dip = arg;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
dev_info_t *cdip;
boolean_t again;
sysevent_t *se;
sysevent_value_t se_val;
sysevent_id_t eid;
sysevent_attr_list_t *ev_attr_list;
top:
ndi_devi_enter(dip);
se = NULL;
ev_attr_list = NULL;
mutex_enter(&bus_p->bus_lbw_mutex);
bus_p->bus_lbw_state &= ~PCIE_LBW_S_DISPATCHED;
bus_p->bus_lbw_state |= PCIE_LBW_S_RUNNING;
mutex_exit(&bus_p->bus_lbw_mutex);
pcie_capture_speeds(dip);
for (cdip = ddi_get_child(dip); cdip != NULL;
cdip = ddi_get_next_sibling(cdip)) {
pcie_bus_t *cbus_p = PCIE_DIP2BUS(cdip);
if (cbus_p == NULL) {
continue;
}
if ((cbus_p->bus_bdf & PCIE_REQ_ID_FUNC_MASK) != 0) {
continue;
}
if (cbus_p->bus_cfg_hdl == NULL) {
continue;
}
pcie_capture_speeds(cdip);
break;
}
se = sysevent_alloc(EC_PCIE, ESC_PCIE_LINK_STATE,
ILLUMOS_KERN_PUB "pcie", SE_SLEEP);
(void) ddi_pathname(dip, bus_p->bus_lbw_pbuf);
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = bus_p->bus_lbw_pbuf;
if (sysevent_add_attr(&ev_attr_list, PCIE_EV_DETECTOR_PATH, &se_val,
SE_SLEEP) != 0) {
ndi_devi_exit(dip);
goto err;
}
if (cdip != NULL) {
(void) ddi_pathname(cdip, bus_p->bus_lbw_cbuf);
se_val.value_type = SE_DATA_TYPE_STRING;
se_val.value.sv_string = bus_p->bus_lbw_cbuf;
(void) sysevent_add_attr(&ev_attr_list, PCIE_EV_CHILD_PATH,
&se_val, SE_SLEEP);
}
ndi_devi_exit(dip);
ddi_prop_cache_invalidate(DDI_DEV_T_NONE, NULL, NULL, 0);
if (sysevent_attach_attributes(se, ev_attr_list) != 0) {
goto err;
}
ev_attr_list = NULL;
if (log_sysevent(se, SE_SLEEP, &eid) != 0) {
goto err;
}
err:
sysevent_free_attr(ev_attr_list);
sysevent_free(se);
mutex_enter(&bus_p->bus_lbw_mutex);
bus_p->bus_lbw_state &= ~PCIE_LBW_S_RUNNING;
cv_broadcast(&bus_p->bus_lbw_cv);
again = (bus_p->bus_lbw_state & PCIE_LBW_S_DISPATCHED) != 0;
mutex_exit(&bus_p->bus_lbw_mutex);
if (again) {
goto top;
}
}
int
pcie_link_bw_intr(dev_info_t *dip)
{
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
uint16_t linksts;
uint16_t flags = PCIE_LINKSTS_LINK_BW_MGMT | PCIE_LINKSTS_AUTO_BW;
hrtime_t now;
if ((bus_p->bus_lbw_state & PCIE_LBW_S_ENABLED) == 0) {
return (DDI_INTR_UNCLAIMED);
}
linksts = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
if ((linksts & flags) == 0) {
return (DDI_INTR_UNCLAIMED);
}
now = gethrtime();
mutex_enter(&bus_p->bus_lbw_mutex);
bus_p->bus_lbw_nevents++;
bus_p->bus_lbw_last_ts = now;
if ((bus_p->bus_lbw_state & PCIE_LBW_S_DISPATCHED) == 0) {
if ((bus_p->bus_lbw_state & PCIE_LBW_S_RUNNING) == 0) {
taskq_dispatch_ent(pcie_link_tq, pcie_link_bw_taskq,
dip, 0, &bus_p->bus_lbw_ent);
}
bus_p->bus_lbw_state |= PCIE_LBW_S_DISPATCHED;
}
mutex_exit(&bus_p->bus_lbw_mutex);
PCIE_CAP_PUT(16, bus_p, PCIE_LINKSTS, flags);
return (DDI_INTR_CLAIMED);
}
int
pcie_link_set_target(dev_info_t *dip, pcie_link_speed_t speed)
{
uint16_t ctl2, rval;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (!PCIE_IS_PCIE(bus_p)) {
return (ENOTSUP);
}
if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
return (ENOTSUP);
}
if (bus_p->bus_pcie_vers < 2) {
return (ENOTSUP);
}
switch (speed) {
case PCIE_LINK_SPEED_2_5:
rval = PCIE_LINKCTL2_TARGET_SPEED_2_5;
break;
case PCIE_LINK_SPEED_5:
rval = PCIE_LINKCTL2_TARGET_SPEED_5;
break;
case PCIE_LINK_SPEED_8:
rval = PCIE_LINKCTL2_TARGET_SPEED_8;
break;
case PCIE_LINK_SPEED_16:
rval = PCIE_LINKCTL2_TARGET_SPEED_16;
break;
case PCIE_LINK_SPEED_32:
rval = PCIE_LINKCTL2_TARGET_SPEED_32;
break;
case PCIE_LINK_SPEED_64:
rval = PCIE_LINKCTL2_TARGET_SPEED_64;
break;
default:
return (EINVAL);
}
mutex_enter(&bus_p->bus_speed_mutex);
if ((bus_p->bus_sup_speed & speed) == 0) {
mutex_exit(&bus_p->bus_speed_mutex);
return (ENOTSUP);
}
bus_p->bus_target_speed = speed;
bus_p->bus_speed_flags |= PCIE_LINK_F_ADMIN_TARGET;
ctl2 = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL2);
ctl2 &= ~PCIE_LINKCTL2_TARGET_SPEED_MASK;
ctl2 |= rval;
PCIE_CAP_PUT(16, bus_p, PCIE_LINKCTL2, ctl2);
mutex_exit(&bus_p->bus_speed_mutex);
pcie_capture_speeds(dip);
return (0);
}
int
pcie_link_retrain(dev_info_t *dip)
{
uint16_t ctl;
pcie_bus_t *bus_p = PCIE_DIP2BUS(dip);
if (!PCIE_IS_PCIE(bus_p)) {
return (ENOTSUP);
}
if (!PCIE_IS_RP(bus_p) && !PCIE_IS_SWD(bus_p)) {
return (ENOTSUP);
}
for (uint32_t i = 0; i < pcie_link_retrain_count; i++) {
uint16_t sts;
sts = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
if ((sts & PCIE_LINKSTS_LINK_TRAINING) == 0)
break;
delay(drv_usectohz(pcie_link_retrain_delay_ms * 1000));
}
ctl = PCIE_CAP_GET(16, bus_p, PCIE_LINKCTL);
ctl |= PCIE_LINKCTL_RETRAIN_LINK;
PCIE_CAP_PUT(16, bus_p, PCIE_LINKCTL, ctl);
for (uint32_t i = 0; i < pcie_link_retrain_count; i++) {
uint16_t sts;
sts = PCIE_CAP_GET(16, bus_p, PCIE_LINKSTS);
if ((sts & PCIE_LINKSTS_LINK_TRAINING) == 0)
break;
delay(drv_usectohz(pcie_link_retrain_delay_ms * 1000));
}
return (0);
}
static int
pcie_fabric_feature_scan(dev_info_t *dip, void *arg)
{
pcie_bus_t *bus_p;
uint32_t devcap;
uint16_t mps;
dev_info_t *rcdip;
pcie_fabric_data_t *fab = arg;
if (pcie_dev(dip) != DDI_SUCCESS) {
return (DDI_WALK_PRUNECHILD);
}
bus_p = PCIE_DIP2BUS(dip);
if (bus_p == NULL) {
dev_err(dip, CE_WARN, "failed to find associated pcie_bus_t "
"during fabric scan");
fab->pfd_flags |= PCIE_FABRIC_F_COMPLEX;
return (DDI_WALK_TERMINATE);
}
if (bus_p->bus_pcie_off == 0) {
dev_err(dip, CE_WARN, "encountered PCIe device without PCIe "
"capability");
fab->pfd_flags |= PCIE_FABRIC_F_COMPLEX;
return (DDI_WALK_TERMINATE);
}
rcdip = pcie_get_rc_dip(dip);
ASSERT3U(bus_p->bus_pcie_off, !=, 0);
devcap = pci_cfgacc_get32(rcdip, bus_p->bus_bdf, bus_p->bus_pcie_off +
PCIE_DEVCAP);
mps = devcap & PCIE_DEVCAP_MAX_PAYLOAD_MASK;
if (mps < fab->pfd_mps_found) {
fab->pfd_mps_found = mps;
}
if ((devcap & PCIE_DEVCAP_EXT_TAG_8BIT) == 0) {
fab->pfd_tag_found &= ~PCIE_TAG_8B;
}
if (bus_p->bus_pcie_vers == PCIE_PCIECAP_VER_2_0) {
uint32_t devcap2 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_DEVCAP2);
if ((devcap2 & PCIE_DEVCAP2_10B_TAG_COMP_SUP) == 0) {
fab->pfd_tag_found &= ~PCIE_TAG_10B_COMP;
}
} else {
fab->pfd_tag_found &= ~PCIE_TAG_10B_COMP;
}
if (bus_p->bus_dev3_off != 0) {
uint32_t devcap3 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_dev3_off + PCIE_DEVCAP3);
if ((devcap3 & PCIE_DEVCAP3_14B_TAG_COMP_SUP) == 0) {
fab->pfd_tag_found &= ~PCIE_TAG_14B_COMP;
}
} else {
fab->pfd_tag_found &= ~PCIE_TAG_14B_COMP;
}
if (bus_p->bus_hp_sup_modes != 0) {
if (!PCIE_IS_RP(bus_p)) {
fab->pfd_flags |= PCIE_FABRIC_F_COMPLEX;
return (DDI_WALK_TERMINATE);
}
fab->pfd_flags |= PCIE_FABRIC_F_RP_HP;
}
if (bus_p->bus_rp_dip == dip) {
return (DDI_WALK_PRUNESIB);
}
return (DDI_WALK_CONTINUE);
}
static int
pcie_fabric_feature_set(dev_info_t *dip, void *arg)
{
pcie_bus_t *bus_p;
dev_info_t *rcdip;
pcie_fabric_data_t *fab = arg;
uint32_t devcap, devctl;
if (pcie_dev(dip) != DDI_SUCCESS) {
return (DDI_WALK_PRUNECHILD);
}
bus_p = PCIE_DIP2BUS(dip);
if (bus_p == NULL || bus_p->bus_pcie_off == 0) {
return (DDI_WALK_CONTINUE);
}
rcdip = pcie_get_rc_dip(dip);
devcap = pci_cfgacc_get32(rcdip, bus_p->bus_bdf, bus_p->bus_pcie_off +
PCIE_DEVCAP);
devctl = pci_cfgacc_get16(rcdip, bus_p->bus_bdf, bus_p->bus_pcie_off +
PCIE_DEVCTL);
if ((devcap & PCIE_DEVCAP_EXT_TAG_8BIT) != 0 &&
(fab->pfd_tag_act & PCIE_TAG_8B) != 0) {
devctl |= PCIE_DEVCTL_EXT_TAG_FIELD_EN;
}
devctl &= ~PCIE_DEVCTL_MAX_PAYLOAD_MASK;
ASSERT0(fab->pfd_mps_act & ~PCIE_DEVCAP_MAX_PAYLOAD_MASK);
devctl |= fab->pfd_mps_act << PCIE_DEVCTL_MAX_PAYLOAD_SHIFT;
pci_cfgacc_put16(rcdip, bus_p->bus_bdf, bus_p->bus_pcie_off +
PCIE_DEVCTL, devctl);
if (bus_p->bus_pcie_vers == PCIE_PCIECAP_VER_2_0 &&
(fab->pfd_tag_act & PCIE_TAG_10B_COMP) != 0) {
uint32_t devcap2 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_DEVCAP2);
if ((devcap2 & PCIE_DEVCAP2_10B_TAG_REQ_SUP) == 0) {
uint16_t devctl2 = pci_cfgacc_get16(rcdip,
bus_p->bus_bdf, bus_p->bus_pcie_off + PCIE_DEVCTL2);
devctl2 |= PCIE_DEVCTL2_10B_TAG_REQ_EN;
pci_cfgacc_put16(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_DEVCTL2, devctl2);
}
}
if (bus_p->bus_dev3_off != 0 &&
(fab->pfd_tag_act & PCIE_TAG_14B_COMP) != 0) {
uint32_t devcap3 = pci_cfgacc_get32(rcdip, bus_p->bus_bdf,
bus_p->bus_dev3_off + PCIE_DEVCAP3);
if ((devcap3 & PCIE_DEVCAP3_14B_TAG_REQ_SUP) == 0) {
uint16_t devctl3 = pci_cfgacc_get16(rcdip,
bus_p->bus_bdf, bus_p->bus_dev3_off + PCIE_DEVCTL3);
devctl3 |= PCIE_DEVCTL3_14B_TAG_REQ_EN;
pci_cfgacc_put16(rcdip, bus_p->bus_bdf,
bus_p->bus_pcie_off + PCIE_DEVCTL2, devctl3);
}
}
if (bus_p->bus_rp_dip == dip) {
return (DDI_WALK_PRUNESIB);
}
return (DDI_WALK_CONTINUE);
}
void
pcie_fabric_setup(dev_info_t *dip)
{
pcie_bus_t *bus_p;
pcie_fabric_data_t *fab;
dev_info_t *pdip;
bus_p = PCIE_DIP2BUS(dip);
if (bus_p == NULL || !PCIE_IS_RP(bus_p)) {
return;
}
VERIFY3P(bus_p->bus_fab, !=, NULL);
fab = bus_p->bus_fab;
pdip = ddi_get_parent(dip);
VERIFY3P(pdip, !=, NULL);
ndi_devi_enter(pdip);
fab->pfd_flags |= PCIE_FABRIC_F_SCANNING;
fab->pfd_mps_found = PCIE_DEVCAP_MAX_PAYLOAD_4096;
fab->pfd_tag_found = PCIE_TAG_ALL;
fab->pfd_flags &= ~PCIE_FABRIC_F_COMPLEX;
ddi_walk_devs(dip, pcie_fabric_feature_scan, fab);
if ((fab->pfd_flags & PCIE_FABRIC_F_COMPLEX) != 0) {
fab->pfd_tag_act = PCIE_TAG_5B;
fab->pfd_mps_act = PCIE_DEVCAP_MAX_PAYLOAD_128;
} else {
fab->pfd_tag_act = fab->pfd_tag_found;
fab->pfd_mps_act = fab->pfd_mps_found;
}
ddi_walk_devs(dip, pcie_fabric_feature_set, fab);
fab->pfd_flags &= ~PCIE_FABRIC_F_SCANNING;
ndi_devi_exit(pdip);
}