#include "bnxe.h"
static uint_t
bnxe_get_phy_id(um_device_t *um)
{
if (um->lm_dev.params.link.num_phys <= 1)
return (ELINK_INT_PHY);
if (um->lm_dev.vars.link.link_up) {
if ((um->lm_dev.vars.link.link_status &
LINK_STATUS_SERDES_LINK) &&
(um->lm_dev.params.link.phy[ELINK_EXT_PHY2].supported &
ELINK_SUPPORTED_FIBRE))
return (ELINK_EXT_PHY2);
return (ELINK_EXT_PHY1);
} else {
switch (elink_phy_selection(&um->lm_dev.params.link)) {
case PORT_HW_CFG_PHY_SELECTION_HARDWARE_DEFAULT:
case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY:
case PORT_HW_CFG_PHY_SELECTION_FIRST_PHY_PRIORITY:
return (ELINK_EXT_PHY1);
case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY:
case PORT_HW_CFG_PHY_SELECTION_SECOND_PHY_PRIORITY:
return (ELINK_EXT_PHY2);
default:
return (ELINK_EXT_PHY1);
}
}
}
typedef struct {
uint32_t bmm_sfp;
uint32_t bmm_speed;
mac_ether_media_t bmm_media;
} bnxe_media_map_t;
static const bnxe_media_map_t bnxe_media_map[] = {
{ ELINK_ETH_SFP_10GBASE_SR, 10000, ETHER_MEDIA_10GBASE_SR },
{ ELINK_ETH_SFP_10GBASE_SR, 1000, ETHER_MEDIA_1000BASE_SX },
{ ELINK_ETH_SFP_1GBASE_SX, 1000, ETHER_MEDIA_1000BASE_SX },
{ ELINK_ETH_SFP_10GBASE_LR, 10000, ETHER_MEDIA_10GBASE_LR },
{ ELINK_ETH_SFP_10GBASE_LR, 1000, ETHER_MEDIA_1000BASE_LX },
{ ELINK_ETH_SFP_1GBASE_LX, 1000, ETHER_MEDIA_1000BASE_LX },
{ ELINK_ETH_SFP_10GBASE_LRM, 10000, ETHER_MEDIA_10GBASE_LRM },
{ ELINK_ETH_SFP_10GBASE_ER, 10000, ETHER_MEDIA_10GBASE_ER },
{ ELINK_ETH_SFP_1GBASE_T, 1000, ETHER_MEDIA_1000BASE_T },
{ ELINK_ETH_SFP_1GBASE_CX, 1000, ETHER_MEDIA_1000BASE_CX },
{ ELINK_ETH_SFP_DAC, 10000, ETHER_MEDIA_10GBASE_CR },
{ ELINK_ETH_SFP_ACC, 10000, ETHER_MEDIA_10GBASE_ACC },
};
mac_ether_media_t
bnxe_phy_to_media(um_device_t *um)
{
uint_t phyid;
struct elink_params *params;
struct elink_phy *phy;
mac_ether_media_t media = ETHER_MEDIA_UNKNOWN;
BNXE_LOCK_ENTER_PHY(um);
phyid = bnxe_get_phy_id(um);
params = &um->lm_dev.params.link;
phy = ¶ms->phy[phyid];
switch (phy->media_type) {
case ELINK_ETH_PHY_SFPP_10G_FIBER:
case ELINK_ETH_PHY_XFP_FIBER:
case ELINK_ETH_PHY_DA_TWINAX:
case ELINK_ETH_PHY_SFP_1G_FIBER:
for (size_t i = 0; i < ARRAY_SIZE(bnxe_media_map); i++) {
const bnxe_media_map_t *map = &bnxe_media_map[i];
if (phy->sfp_media == map->bmm_sfp &&
um->props.link_speed == map->bmm_speed) {
media = map->bmm_media;
break;
}
}
break;
case ELINK_ETH_PHY_BASE_T:
switch (um->props.link_speed) {
case 10:
media = ETHER_MEDIA_10BASE_T;
break;
case 100:
media = ETHER_MEDIA_100BASE_TX;
break;
case 1000:
media = ETHER_MEDIA_1000BASE_T;
break;
case 10000:
media = ETHER_MEDIA_10GBASE_T;
break;
default:
break;
}
break;
case ELINK_ETH_PHY_KR:
switch (um->props.link_speed) {
case 1000:
media = ETHER_MEDIA_1000BASE_KX;
break;
case 10000:
media = ETHER_MEDIA_10GBASE_KR;
break;
default:
break;
}
break;
case ELINK_ETH_PHY_CX4:
if (um->props.link_speed == 10000) {
media = ETHER_MEDIA_10GBASE_CX4;
}
break;
case ELINK_ETH_PHY_NOT_PRESENT:
media = ETHER_MEDIA_NONE;
break;
case ELINK_ETH_PHY_UNSPECIFIED:
default:
media = ETHER_MEDIA_UNKNOWN;
break;
}
BNXE_LOCK_EXIT_PHY(um);
return (media);
}
static int
bnxe_transceiver_info(void *arg, uint_t id, mac_transceiver_info_t *infop)
{
uint_t phyid;
um_device_t *um = arg;
struct elink_params *params;
struct elink_phy *phy;
boolean_t present = B_FALSE, usable = B_FALSE;
elink_status_t ret;
uint8_t buf;
if (id != 0 || arg == NULL || infop == NULL)
return (EINVAL);
BNXE_LOCK_ENTER_PHY(um);
phyid = bnxe_get_phy_id(um);
params = &um->lm_dev.params.link;
phy = ¶ms->phy[phyid];
switch (phy->media_type) {
case ELINK_ETH_PHY_SFPP_10G_FIBER:
case ELINK_ETH_PHY_DA_TWINAX:
case ELINK_ETH_PHY_SFP_1G_FIBER:
break;
default:
BNXE_LOCK_EXIT_PHY(um);
return (ENOTSUP);
}
PHY_HW_LOCK(&um->lm_dev);
ret = elink_read_sfp_module_eeprom(phy, params, 0xa0, 0, sizeof (buf),
&buf);
PHY_HW_UNLOCK(&um->lm_dev);
if (ret != ELINK_STATUS_OK && ret != ELINK_STATUS_TIMEOUT) {
BNXE_LOCK_EXIT_PHY(um);
return (EIO);
}
if (ret == ELINK_STATUS_OK) {
present = B_TRUE;
if ((phy->flags & ELINK_FLAGS_SFP_NOT_APPROVED) == 0)
usable = B_TRUE;
}
BNXE_LOCK_EXIT_PHY(um);
mac_transceiver_info_set_present(infop, present);
mac_transceiver_info_set_usable(infop, usable);
return (0);
}
static int
bnxe_transceiver_read(void *arg, uint_t id, uint_t page, void *bp,
size_t nbytes, off_t offset, size_t *nread)
{
uint_t phyid;
um_device_t *um = arg;
struct elink_phy *phy;
struct elink_params *params;
elink_status_t ret;
if (id != 0 || bp == NULL || nbytes == 0 || nread == NULL ||
(page != 0xa0 && page != 0xa2) || offset < 0)
return (EINVAL);
if (nbytes > 256 || offset >= 256 || (offset + nbytes > 256)) {
return (EINVAL);
}
BNXE_LOCK_ENTER_PHY(um);
phyid = bnxe_get_phy_id(um);
params = &um->lm_dev.params.link;
phy = &um->lm_dev.params.link.phy[phyid];
switch (phy->media_type) {
case ELINK_ETH_PHY_SFPP_10G_FIBER:
case ELINK_ETH_PHY_DA_TWINAX:
case ELINK_ETH_PHY_SFP_1G_FIBER:
break;
default:
BNXE_LOCK_EXIT_PHY(um);
return (ENOTSUP);
}
PHY_HW_LOCK(&um->lm_dev);
ret = elink_read_sfp_module_eeprom(phy, params, (uint8_t)page,
(uint16_t)offset, (uint16_t)nbytes, bp);
PHY_HW_UNLOCK(&um->lm_dev);
BNXE_LOCK_EXIT_PHY(um);
switch (ret) {
case ELINK_STATUS_OK:
*nread = nbytes;
return (0);
case ELINK_OP_NOT_SUPPORTED:
return (ENOTSUP);
default:
return (EIO);
}
}
boolean_t
bnxe_fill_transceiver(um_device_t *um, void *arg)
{
uint_t ntran = 1;
mac_capab_transceiver_t *mct = arg;
mct->mct_flags = 0;
mct->mct_ntransceivers = 1;
mct->mct_info = bnxe_transceiver_info;
mct->mct_read = bnxe_transceiver_read;
return (B_TRUE);
}