mirror of https://github.com/F-Stack/f-stack.git
369 lines
7.4 KiB
C
369 lines
7.4 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Copyright(c) 2019-2020 Xilinx, Inc.
|
|
* Copyright(c) 2019 Solarflare Communications Inc.
|
|
*/
|
|
|
|
#include "efx.h"
|
|
#include "efx_impl.h"
|
|
|
|
#if EFSYS_OPT_PCI
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_pci_config_next_ext_cap(
|
|
__in efsys_pci_config_t *espcp,
|
|
__in const efx_pci_ops_t *epop,
|
|
__inout size_t *offsetp)
|
|
{
|
|
efx_dword_t hdr;
|
|
efx_rc_t rc = 0;
|
|
size_t next;
|
|
|
|
if (offsetp == NULL) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
if (*offsetp == 0) {
|
|
*offsetp = ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE;
|
|
} else {
|
|
rc = epop->epo_config_readd(espcp, *offsetp +
|
|
(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
|
|
&hdr);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail2;
|
|
}
|
|
|
|
next = EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_NEXT);
|
|
if (next < ESE_GZ_PCI_BASE_CONFIG_SPACE_SIZE)
|
|
rc = ENOENT;
|
|
else
|
|
*offsetp = next;
|
|
}
|
|
|
|
/*
|
|
* Returns 0 if the next capability is present otherwise ENOENT
|
|
* indicating that the function finished correctly.
|
|
*/
|
|
return (rc);
|
|
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_pci_config_find_next_ext_cap(
|
|
__in efsys_pci_config_t *espcp,
|
|
__in const efx_pci_ops_t *epop,
|
|
__in uint16_t cap_id,
|
|
__inout size_t *offsetp)
|
|
{
|
|
efx_dword_t hdr;
|
|
size_t position;
|
|
efx_rc_t rc;
|
|
|
|
if (offsetp == NULL) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
position = *offsetp;
|
|
|
|
while (1) {
|
|
rc = efx_pci_config_next_ext_cap(espcp, epop, &position);
|
|
if (rc != 0) {
|
|
if (rc == ENOENT)
|
|
break;
|
|
else
|
|
goto fail2;
|
|
}
|
|
|
|
rc = epop->epo_config_readd(espcp, position +
|
|
(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
|
|
&hdr);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail3;
|
|
}
|
|
|
|
if (EFX_DWORD_FIELD(hdr, ESF_GZ_PCI_EXPRESS_XCAP_ID) ==
|
|
cap_id) {
|
|
*offsetp = position;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns 0 if found otherwise ENOENT indicating that search finished
|
|
* correctly.
|
|
*/
|
|
return (rc);
|
|
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_pci_find_next_xilinx_cap_table(
|
|
__in efsys_pci_config_t *espcp,
|
|
__in const efx_pci_ops_t *epop,
|
|
__inout size_t *pci_cap_offsetp,
|
|
__out unsigned int *xilinx_tbl_barp,
|
|
__out efsys_dma_addr_t *xilinx_tbl_offsetp)
|
|
{
|
|
size_t cap_offset;
|
|
efx_rc_t rc;
|
|
|
|
if (pci_cap_offsetp == NULL) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
cap_offset = *pci_cap_offsetp;
|
|
|
|
while (1) {
|
|
unsigned int tbl_bar;
|
|
efsys_dma_addr_t tbl_offset;
|
|
|
|
rc = efx_pci_config_find_next_ext_cap(espcp, epop,
|
|
ESE_GZ_PCI_EXPRESS_XCAP_ID_VNDR, &cap_offset);
|
|
if (rc != 0) {
|
|
if (rc == ENOENT)
|
|
break;
|
|
else
|
|
goto fail2;
|
|
}
|
|
|
|
/*
|
|
* The found extended PCI capability is a vendor-specific
|
|
* capability, but not necessarily a Xilinx capabilities table
|
|
* locator. Try to read it and skip it if the capability is
|
|
* not the locator.
|
|
*/
|
|
rc = efx_pci_read_ext_cap_xilinx_table(espcp, epop, cap_offset,
|
|
&tbl_bar, &tbl_offset);
|
|
if (rc == 0) {
|
|
*xilinx_tbl_barp = tbl_bar;
|
|
*xilinx_tbl_offsetp = tbl_offset;
|
|
*pci_cap_offsetp = cap_offset;
|
|
break;
|
|
} else {
|
|
if (rc == ENOENT)
|
|
continue;
|
|
else
|
|
goto fail3;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Returns 0 if found otherwise ENOENT indicating that search finished
|
|
* correctly.
|
|
*/
|
|
return (rc);
|
|
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_pci_read_ext_cap_xilinx_table(
|
|
__in efsys_pci_config_t *espcp,
|
|
__in const efx_pci_ops_t *epop,
|
|
__in size_t cap_offset,
|
|
__out unsigned int *barp,
|
|
__out efsys_dma_addr_t *offsetp)
|
|
{
|
|
size_t vsec_offset = cap_offset + ESE_GZ_PCI_EXPRESS_XCAP_HDR_SIZE;
|
|
efx_dword_t cap_hdr;
|
|
efx_oword_t vsec;
|
|
uint32_t vsec_len;
|
|
uint32_t vsec_id;
|
|
uint32_t vsec_rev;
|
|
uint32_t offset_low;
|
|
uint32_t offset_high = 0;
|
|
unsigned int bar;
|
|
efsys_dma_addr_t offset;
|
|
efx_rc_t rc;
|
|
|
|
rc = epop->epo_config_readd(espcp, cap_offset +
|
|
(EFX_LOW_BIT(ESF_GZ_PCI_EXPRESS_XCAP_ID) / 8),
|
|
&cap_hdr);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail1;
|
|
}
|
|
|
|
if (EFX_DWORD_FIELD(cap_hdr, ESF_GZ_PCI_EXPRESS_XCAP_VER) !=
|
|
ESE_GZ_PCI_EXPRESS_XCAP_VER_VSEC) {
|
|
rc = EINVAL;
|
|
goto fail2;
|
|
}
|
|
|
|
rc = epop->epo_config_readd(espcp, vsec_offset +
|
|
(EFX_LOW_BIT(ESF_GZ_VSEC_ID) / 8),
|
|
&vsec.eo_dword[0]);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail3;
|
|
}
|
|
|
|
vsec_len = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_LEN);
|
|
vsec_id = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_ID);
|
|
vsec_rev = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_VER);
|
|
|
|
/*
|
|
* Condition of the vendor-specific extended PCI capability not being
|
|
* a Xilinx capabilities table locator.
|
|
*/
|
|
if (vsec_id != ESE_GZ_XILINX_VSEC_ID) {
|
|
rc = ENOENT;
|
|
goto fail4;
|
|
}
|
|
|
|
if (vsec_rev != ESE_GZ_VSEC_VER_XIL_CFGBAR ||
|
|
vsec_len < ESE_GZ_VSEC_LEN_MIN) {
|
|
rc = EINVAL;
|
|
goto fail5;
|
|
}
|
|
|
|
rc = epop->epo_config_readd(espcp, vsec_offset +
|
|
(EFX_LOW_BIT(ESF_GZ_VSEC_TBL_BAR) / 8),
|
|
&vsec.eo_dword[1]);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail6;
|
|
}
|
|
|
|
bar = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_BAR);
|
|
offset_low = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_LO);
|
|
|
|
if (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT) {
|
|
rc = epop->epo_config_readd(espcp, vsec_offset +
|
|
(EFX_LOW_BIT(ESF_GZ_VSEC_TBL_OFF_HI) / 8),
|
|
&vsec.eo_dword[2]);
|
|
if (rc != 0) {
|
|
rc = EIO;
|
|
goto fail7;
|
|
}
|
|
|
|
offset_high = EFX_OWORD_FIELD32(vsec, ESF_GZ_VSEC_TBL_OFF_HI);
|
|
}
|
|
|
|
/* High bits of low offset are discarded by the shift */
|
|
offset = offset_low << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT;
|
|
|
|
/*
|
|
* Avoid the 'left shift count >= width of type' warning on systems
|
|
* without uint64_t support.
|
|
*/
|
|
#if EFSYS_HAS_UINT64
|
|
offset |= (uint64_t)offset_high << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT;
|
|
#else
|
|
_NOTE(ARGUNUSED(offset_high))
|
|
#endif
|
|
|
|
*offsetp = offset;
|
|
*barp = bar;
|
|
|
|
return (0);
|
|
|
|
fail7:
|
|
EFSYS_PROBE(fail7);
|
|
fail6:
|
|
EFSYS_PROBE(fail6);
|
|
fail5:
|
|
EFSYS_PROBE(fail5);
|
|
fail4:
|
|
EFSYS_PROBE(fail4);
|
|
fail3:
|
|
EFSYS_PROBE(fail3);
|
|
fail2:
|
|
EFSYS_PROBE(fail2);
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
__checkReturn efx_rc_t
|
|
efx_pci_xilinx_cap_tbl_find(
|
|
__in efsys_bar_t *esbp,
|
|
__in uint32_t format_id,
|
|
__in boolean_t skip_first,
|
|
__inout efsys_dma_addr_t *entry_offsetp)
|
|
{
|
|
efsys_dma_addr_t offset;
|
|
boolean_t skip = skip_first;
|
|
efx_qword_t header;
|
|
uint32_t format;
|
|
uint32_t last;
|
|
efx_rc_t rc;
|
|
|
|
if (entry_offsetp == NULL) {
|
|
rc = EINVAL;
|
|
goto fail1;
|
|
}
|
|
|
|
offset = *entry_offsetp;
|
|
rc = ENOENT;
|
|
/*
|
|
* SF-119689-TC Riverhead Host Interface section 4.2.2.
|
|
* describes the following discovery steps.
|
|
*/
|
|
do {
|
|
/*
|
|
* Xilinx Capabilities Table requires 32bit aligned reads.
|
|
* See SF-119689-TC section 4.2.2 "Discovery Steps".
|
|
*/
|
|
EFSYS_BAR_READD(esbp, offset +
|
|
(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_FORMAT) / 8),
|
|
&header.eq_dword[0], B_FALSE);
|
|
EFSYS_BAR_READD(esbp, offset +
|
|
(EFX_LOW_BIT(ESF_GZ_CFGBAR_ENTRY_SIZE) / 8),
|
|
&header.eq_dword[1], B_FALSE);
|
|
|
|
format = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_FORMAT);
|
|
last = EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_LAST);
|
|
|
|
if (skip == B_FALSE && format == format_id) {
|
|
*entry_offsetp = offset;
|
|
rc = 0;
|
|
break;
|
|
}
|
|
|
|
offset += EFX_QWORD_FIELD32(header, ESF_GZ_CFGBAR_ENTRY_SIZE);
|
|
skip = B_FALSE;
|
|
} while (last == B_FALSE);
|
|
|
|
/*
|
|
* Returns 0 if found otherwise ENOENT indicating that
|
|
* search finished correctly.
|
|
*/
|
|
return (rc);
|
|
|
|
fail1:
|
|
EFSYS_PROBE1(fail1, efx_rc_t, rc);
|
|
|
|
return (rc);
|
|
}
|
|
|
|
#endif /* EFSYS_OPT_PCI */
|