Mercurial > illumos > illumos-gate
changeset 2871:9983c0b50e87
6471094 x86 pci_pci should detect HT-MSI capability
author | jveta |
---|---|
date | Thu, 05 Oct 2006 21:20:39 -0700 |
parents | a343ed00e23c |
children | ef6b1a8f4b3d |
files | usr/src/uts/i86pc/io/pci/pci_pci.c |
diffstat | 1 files changed, 152 insertions(+), 57 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/i86pc/io/pci/pci_pci.c Thu Oct 05 19:20:42 2006 -0700 +++ b/usr/src/uts/i86pc/io/pci/pci_pci.c Thu Oct 05 21:20:39 2006 -0700 @@ -65,24 +65,33 @@ /* * ppb_support_msi: Flag that controls MSI support across P2P Bridges. - * By default, MSI is not supported. Special case is AMD-8132 chipset. + * By default, MSI is not supported except for special cases like HT + * bridges/tunnels that have HT MSI mapping enabled. * * However, MSI support behavior can be patched on a system by changing * the value of this flag as shown below:- - * 0 = default value, MSI is allowed only for special case (AMD-8132) - * 1 = MSI supported without any checks + * 0 = default value, MSI is allowed by this driver for special cases + * 1 = MSI supported without any checks for this driver * -1 = MSI not supported at all */ int ppb_support_msi = 0; -#define PCI_VENID_AMD 0x1022 /* AMD vendor-id */ -#define PCI_DEVID_8132 0x7458 /* 8132 chipset id */ -#define PCI_MSI_MAPPING_CAP_OFF 0xF4 -#define PCI_MSI_MAPPING_CAP_MASK 0xFF01000F -#define PCI_MSI_MAPPING_ENABLE 0xA8010008 +/* + * Controls the usage of the Hypertransport MSI mapping capability + * 0 = default value, leave hardware function as it is + * 1 = always enable HT MSI mapping + * -1 = always disable HT MSI mapping + */ +int ppb_support_ht_msimap = 0; -extern int (*psm_intr_ops)(dev_info_t *, ddi_intr_handle_impl_t *, - psm_intr_op_t, int *); +/* + * masks and values for the upper 16-bits of hypertransport cap headers + */ +#define PCI_CAP_HT_MSIMAP_TYPE 0xA800 +#define PCI_CAP_HT_MSIMAP_TYPE_MASK 0xFF00 +#define PCI_CAP_HT_MSIMAP_ENABLE 0x0001 +#define PCI_CAP_HT_MSIMAP_ENABLE_MASK 0x0001 + struct bus_ops ppb_bus_ops = { BUSO_REV, @@ -217,6 +226,16 @@ static int ppb_initchild(dev_info_t *child); static void ppb_save_config_regs(ppb_devstate_t *ppb_p); static void ppb_restore_config_regs(ppb_devstate_t *ppb_p); +static uint8_t ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, + uint16_t reg_val); +static boolean_t ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl); +static int ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd); + +/* + * for <cmd> in ppb_ht_msimap_set + */ +#define HT_MSIMAP_ENABLE 1 +#define HT_MSIMAP_DISABLE 0 int @@ -312,6 +331,14 @@ ddi_soft_state_free(ppb_state, instance); return (DDI_FAILURE); } + + if (ppb_support_ht_msimap == 1) + (void) ppb_ht_msimap_set(config_handle, + HT_MSIMAP_ENABLE); + else if (ppb_support_ht_msimap == -1) + (void) ppb_ht_msimap_set(config_handle, + HT_MSIMAP_DISABLE); + pci_config_teardown(&config_handle); /* @@ -688,16 +715,93 @@ /* - * ppb_intr_ops + * returns the location of a hypertransport capability whose upper 16-bit + * register of the cap header matches <reg_val> after masking the register + * with <reg_mask>; if both <reg_mask> and <reg_val> are 0, it will return the + * first HT cap found + */ +static uint8_t +ppb_find_ht_cap(ddi_acc_handle_t cfg_hdl, uint16_t reg_mask, uint16_t reg_val) +{ + uint16_t status, reg; + uint8_t ptr, id; + + status = pci_config_get16(cfg_hdl, PCI_CONF_STAT); + if (status == 0xffff || !((status & PCI_STAT_CAP))) + return (PCI_CAP_NEXT_PTR_NULL); + + ptr = pci_config_get8(cfg_hdl, PCI_CONF_CAP_PTR); + while (ptr != 0xFF && + ptr != PCI_CAP_NEXT_PTR_NULL && + ptr >= PCI_CAP_PTR_OFF) { + + ptr &= PCI_CAP_PTR_MASK; + id = pci_config_get8(cfg_hdl, ptr + PCI_CAP_ID); + + if (id == PCI_CAP_ID_HT) { + reg = pci_config_get16(cfg_hdl, + ptr + PCI_CAP_ID_REGS_OFF); + if ((reg & reg_mask) == reg_val) + return (ptr); + } + ptr = pci_config_get8(cfg_hdl, ptr + PCI_CAP_NEXT_PTR); + } + + return (PCI_CAP_NEXT_PTR_NULL); +} + + +static boolean_t +ppb_ht_msimap_check(ddi_acc_handle_t cfg_hdl) +{ + uint8_t ptr; + + ptr = ppb_find_ht_cap(cfg_hdl, + PCI_CAP_HT_MSIMAP_TYPE_MASK | PCI_CAP_HT_MSIMAP_ENABLE_MASK, + PCI_CAP_HT_MSIMAP_TYPE | PCI_CAP_HT_MSIMAP_ENABLE); + + if (ptr == PCI_CAP_NEXT_PTR_NULL) + return (B_FALSE); + + return (B_TRUE); +} + + +static int +ppb_ht_msimap_set(ddi_acc_handle_t cfg_hdl, int cmd) +{ + uint8_t ptr; + uint16_t reg; + + ptr = ppb_find_ht_cap(cfg_hdl, PCI_CAP_HT_MSIMAP_TYPE_MASK, + PCI_CAP_HT_MSIMAP_TYPE); + if (ptr == PCI_CAP_NEXT_PTR_NULL) + return (0); + + reg = pci_config_get16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF); + switch (cmd) { + case HT_MSIMAP_ENABLE: + reg |= PCI_CAP_HT_MSIMAP_ENABLE; + break; + case HT_MSIMAP_DISABLE: + default: + reg &= ~(uint16_t)PCI_CAP_HT_MSIMAP_ENABLE; + } + + pci_config_put16(cfg_hdl, ptr + PCI_CAP_ID_REGS_OFF, reg); + return (1); +} + + +/* + * intercept certain interrupt services to handle special cases */ static int ppb_intr_ops(dev_info_t *pdip, dev_info_t *rdip, ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result) { - ddi_acc_handle_t config_handle; - ddi_intr_handle_impl_t tmp_hdl; - uint_t msi_mapping; - int types = 0; + ddi_acc_handle_t cfg_hdl; + int rv = DDI_SUCCESS; if (intr_op != DDI_INTROP_SUPPORTED_TYPES) return (i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result)); @@ -709,54 +813,45 @@ /* Fixed interrupt is supported by default */ *(int *)result = DDI_INTR_TYPE_FIXED; - if (psm_intr_ops == NULL || ppb_support_msi == -1) { - /* MSI is not allowed */ - DDI_INTR_NEXDBG((CE_CONT, "ppb_intr_ops: psm_intr_ops == NULL " - "or MSI is not allowed\n")); - } else if (ppb_support_msi == 1) { - /* MSI is always allowed */ + if (ppb_support_msi == -1) { + DDI_INTR_NEXDBG((CE_CONT, + "ppb_intr_ops: MSI is not allowed\n")); + goto OUT; + } + + if (ppb_support_msi == 1) { DDI_INTR_NEXDBG((CE_CONT, "ppb_intr_ops: MSI is always allowed\n")); - if (pci_msi_get_supported_type(rdip, &types) == DDI_SUCCESS) { - *(int *)result |= types; - bzero(&tmp_hdl, sizeof (ddi_intr_handle_impl_t)); - tmp_hdl.ih_type = *(int *)result; - (void) (*psm_intr_ops)(rdip, &tmp_hdl, - PSM_INTR_OP_CHECK_MSI, result); - } - } else if (pci_config_setup(pdip, &config_handle) == DDI_SUCCESS) { - /* - * ppb_support_msi == 0 i.e. by default, MSI is disabled - * Check only for special case like AMD8132 which supports MSI - */ - if ((pci_config_get16(config_handle, PCI_CONF_VENID) == - PCI_VENID_AMD) && (pci_config_get16(config_handle, - PCI_CONF_DEVID) == PCI_DEVID_8132)) { - msi_mapping = pci_config_get32(config_handle, - PCI_MSI_MAPPING_CAP_OFF); - /* make sure MSI enable bit is on */ - if ((msi_mapping & PCI_MSI_MAPPING_CAP_MASK) == - PCI_MSI_MAPPING_ENABLE) { - /* MSI/X is enable */ - DDI_INTR_NEXDBG((CE_CONT, "ppb_intr_ops: " - "MSI is allowed for AMD8132\n")); - if (pci_msi_get_supported_type(rdip, &types) - == DDI_SUCCESS) { - *(int *)result |= types; - bzero(&tmp_hdl, - sizeof (ddi_intr_handle_impl_t)); - tmp_hdl.ih_type = *(int *)result; - (void) (*psm_intr_ops)(rdip, &tmp_hdl, - PSM_INTR_OP_CHECK_MSI, result); - } - } - } - pci_config_teardown(&config_handle); + rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); + goto OUT; + } + + if (pci_config_setup(pdip, &cfg_hdl) != DDI_SUCCESS) { + DDI_INTR_NEXDBG((CE_CONT, + "ppb_intr_ops: pci_config_setup() failed\n")); + goto OUT; } + + /* + * check for hypertransport msi mapping capability + */ + if (ppb_ht_msimap_check(cfg_hdl)) { + DDI_INTR_NEXDBG((CE_CONT, + "ppb_intr_ops: HT MSI mapping enabled\n")); + rv = i_ddi_intr_ops(pdip, rdip, intr_op, hdlp, result); + } + + /* + * if we add failure conditions after pci_config_setup, move this to + * OUT and use an extra flag to indicate the need to teardown cfg_hdl + */ + pci_config_teardown(&cfg_hdl); + +OUT: DDI_INTR_NEXDBG((CE_CONT, "ppb_intr_ops: rdip 0x%p, returns supported types: 0x%x\n", (void *)rdip, *(int *)result)); - return (DDI_SUCCESS); + return (rv); } static int