@@ -4,6 +4,8 @@
; Refer to default.ini for the full list of available PMD features.
;
[Features]
+Speed capabilities = Y
+Link status = Y
Promiscuous mode = Y
Allmulticast mode = Y
RSS hash = Y
@@ -109,6 +109,15 @@ struct enetc_msg_swbd {
#define IFMODE_SGMII 5
#define PM_IF_MODE_ENA BIT(15)
+/* Port MAC 0 Interface Status Register */
+#define ENETC4_PM_IF_STATUS(mac) (0x5304 + (mac) * 0x400)
+#define ENETC4_LINK_MODE 0x0000000000080000ULL
+#define ENETC4_LINK_STATUS 0x0000000000010000ULL
+#define ENETC4_LINK_SPEED_MASK 0x0000000000060000ULL
+#define ENETC4_LINK_SPEED_10M 0x0ULL
+#define ENETC4_LINK_SPEED_100M 0x0000000000020000ULL
+#define ENETC4_LINK_SPEED_1G 0x0000000000040000ULL
+
#define ENETC4_DEF_VSI_WAIT_TIMEOUT_UPDATE 100
#define ENETC4_DEF_VSI_WAIT_DELAY_UPDATE 2000 /* us */
@@ -151,6 +151,8 @@ struct enetc_eth_adapter {
enum enetc_msg_cmd_class_id {
ENETC_CLASS_ID_MAC_FILTER = 0x20,
ENETC_CLASS_ID_VLAN_FILTER = 0x21,
+ ENETC_CLASS_ID_LINK_STATUS = 0x80,
+ ENETC_CLASS_ID_LINK_SPEED = 0x81
};
/* Enum for command IDs */
@@ -158,6 +160,8 @@ enum enetc_msg_cmd_id {
ENETC_CMD_ID_SET_PRIMARY_MAC = 0,
ENETC_CMD_ID_SET_MAC_PROMISCUOUS = 5,
ENETC_CMD_ID_SET_VLAN_PROMISCUOUS = 4,
+ ENETC_CMD_ID_GET_LINK_STATUS = 0,
+ ENETC_CMD_ID_GET_LINK_SPEED = 0
};
enum mac_addr_status {
@@ -166,6 +170,27 @@ enum mac_addr_status {
ENETC_MAC_ADDR_NOT_FOUND = 0X2,
};
+enum link_status {
+ ENETC_LINK_UP = 0x0,
+ ENETC_LINK_DOWN = 0x1
+};
+
+enum speed {
+ ENETC_SPEED_UNKNOWN = 0x0,
+ ENETC_SPEED_10_HALF_DUPLEX = 0x1,
+ ENETC_SPEED_10_FULL_DUPLEX = 0x2,
+ ENETC_SPEED_100_HALF_DUPLEX = 0x3,
+ ENETC_SPEED_100_FULL_DUPLEX = 0x4,
+ ENETC_SPEED_1000 = 0x5,
+ ENETC_SPEED_2500 = 0x6,
+ ENETC_SPEED_5000 = 0x7,
+ ENETC_SPEED_10G = 0x8,
+ ENETC_SPEED_25G = 0x9,
+ ENETC_SPEED_50G = 0xA,
+ ENETC_SPEED_100G = 0xB,
+ ENETC_SPEED_NOT_SUPPORTED = 0xF
+};
+
/* PSI-VSI command header format */
struct enetc_msg_cmd_header {
uint16_t csum; /* INET_CHECKSUM */
@@ -75,6 +75,49 @@ enetc4_dev_stop(struct rte_eth_dev *dev)
return 0;
}
+/* return 0 means link status changed, -1 means not changed */
+static int
+enetc4_link_update(struct rte_eth_dev *dev, int wait_to_complete __rte_unused)
+{
+ struct enetc_eth_hw *hw =
+ ENETC_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct enetc_hw *enetc_hw = &hw->hw;
+ struct rte_eth_link link;
+ uint32_t status;
+
+ PMD_INIT_FUNC_TRACE();
+
+ memset(&link, 0, sizeof(link));
+
+ status = enetc4_port_rd(enetc_hw, ENETC4_PM_IF_STATUS(0));
+
+ if (status & ENETC4_LINK_MODE)
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ else
+ link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+
+ if (status & ENETC4_LINK_STATUS)
+ link.link_status = RTE_ETH_LINK_UP;
+ else
+ link.link_status = RTE_ETH_LINK_DOWN;
+
+ switch (status & ENETC4_LINK_SPEED_MASK) {
+ case ENETC4_LINK_SPEED_1G:
+ link.link_speed = RTE_ETH_SPEED_NUM_1G;
+ break;
+
+ case ENETC4_LINK_SPEED_100M:
+ link.link_speed = RTE_ETH_SPEED_NUM_100M;
+ break;
+
+ default:
+ case ENETC4_LINK_SPEED_10M:
+ link.link_speed = RTE_ETH_SPEED_NUM_10M;
+ }
+
+ return rte_eth_linkstatus_set(dev, &link);
+}
+
static int
enetc4_mac_init(struct enetc_eth_hw *hw, struct rte_eth_dev *eth_dev)
{
@@ -867,6 +910,7 @@ static const struct eth_dev_ops enetc4_ops = {
.dev_stop = enetc4_dev_stop,
.dev_close = enetc4_dev_close,
.dev_infos_get = enetc4_dev_infos_get,
+ .link_update = enetc4_link_update,
.stats_get = enetc4_stats_get,
.stats_reset = enetc4_stats_reset,
.promiscuous_enable = enetc4_promiscuous_enable,
@@ -206,6 +206,8 @@ enetc4_msg_vsi_send(struct enetc_hw *enetc_hw, struct enetc_msg_swbd *msg)
err = -EINVAL;
break;
case ENETC_CLASS_ID_MAC_FILTER:
+ case ENETC_CLASS_ID_LINK_STATUS:
+ case ENETC_CLASS_ID_LINK_SPEED:
break;
default:
err = -EIO;
@@ -479,6 +481,216 @@ enetc4_vf_promisc_disable(struct rte_eth_dev *dev)
return 0;
}
+static int
+enetc4_vf_get_link_status(struct rte_eth_dev *dev, struct enetc_psi_reply_msg *reply_msg)
+{
+ struct enetc_eth_hw *hw = ENETC_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct enetc_hw *enetc_hw = &hw->hw;
+ struct enetc_msg_swbd *msg;
+ int msg_size;
+ int err = 0;
+
+ msg = rte_zmalloc(NULL, sizeof(*msg), RTE_CACHE_LINE_SIZE);
+ if (!msg) {
+ ENETC_PMD_ERR("Failed to alloc msg");
+ err = -ENOMEM;
+ return err;
+ }
+
+ msg_size = RTE_ALIGN(sizeof(struct enetc_msg_cmd_get_link_status),
+ ENETC_VSI_PSI_MSG_SIZE);
+ msg->vaddr = rte_zmalloc(NULL, msg_size, 0);
+ if (!msg->vaddr) {
+ ENETC_PMD_ERR("Failed to alloc memory for msg");
+ rte_free(msg);
+ return -ENOMEM;
+ }
+
+ msg->dma = rte_mem_virt2iova((const void *)msg->vaddr);
+ msg->size = msg_size;
+
+ enetc_msg_vf_fill_common_hdr(msg, ENETC_CLASS_ID_LINK_STATUS,
+ ENETC_CMD_ID_GET_LINK_STATUS, 0, 0, 0);
+
+ /* send the command and wait */
+ err = enetc4_msg_vsi_send(enetc_hw, msg);
+ if (err) {
+ ENETC_PMD_ERR("VSI message send error");
+ goto end;
+ }
+
+ enetc4_msg_vsi_reply_msg(enetc_hw, reply_msg);
+end:
+ /* free memory no longer required */
+ rte_free(msg->vaddr);
+ rte_free(msg);
+ return err;
+}
+
+static int
+enetc4_vf_get_link_speed(struct rte_eth_dev *dev, struct enetc_psi_reply_msg *reply_msg)
+{
+ struct enetc_eth_hw *hw = ENETC_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+ struct enetc_hw *enetc_hw = &hw->hw;
+ struct enetc_msg_swbd *msg;
+ int msg_size;
+ int err = 0;
+
+ msg = rte_zmalloc(NULL, sizeof(*msg), RTE_CACHE_LINE_SIZE);
+ if (!msg) {
+ ENETC_PMD_ERR("Failed to alloc msg");
+ err = -ENOMEM;
+ return err;
+ }
+
+ msg_size = RTE_ALIGN(sizeof(struct enetc_msg_cmd_get_link_speed),
+ ENETC_VSI_PSI_MSG_SIZE);
+ msg->vaddr = rte_zmalloc(NULL, msg_size, 0);
+ if (!msg->vaddr) {
+ ENETC_PMD_ERR("Failed to alloc memory for msg");
+ rte_free(msg);
+ return -ENOMEM;
+ }
+
+ msg->dma = rte_mem_virt2iova((const void *)msg->vaddr);
+ msg->size = msg_size;
+
+ enetc_msg_vf_fill_common_hdr(msg, ENETC_CLASS_ID_LINK_SPEED,
+ ENETC_CMD_ID_GET_LINK_SPEED, 0, 0, 0);
+
+ /* send the command and wait */
+ err = enetc4_msg_vsi_send(enetc_hw, msg);
+ if (err) {
+ ENETC_PMD_ERR("VSI message send error");
+ goto end;
+ }
+
+ enetc4_msg_vsi_reply_msg(enetc_hw, reply_msg);
+end:
+ /* free memory no longer required */
+ rte_free(msg->vaddr);
+ rte_free(msg);
+ return err;
+}
+
+static int
+enetc4_vf_link_update(struct rte_eth_dev *dev, int wait_to_complete __rte_unused)
+{
+ struct enetc_psi_reply_msg *reply_msg;
+ struct rte_eth_link link;
+ int err;
+
+ PMD_INIT_FUNC_TRACE();
+ reply_msg = rte_zmalloc(NULL, sizeof(*reply_msg), RTE_CACHE_LINE_SIZE);
+ if (!reply_msg) {
+ ENETC_PMD_ERR("Failed to alloc memory for reply_msg");
+ return -ENOMEM;
+ }
+
+ memset(&link, 0, sizeof(struct rte_eth_link));
+
+ err = enetc4_vf_get_link_status(dev, reply_msg);
+ if (err) {
+ ENETC_PMD_ERR("Failed to get link status");
+ rte_free(reply_msg);
+ return err;
+ }
+
+ if (reply_msg->class_id == ENETC_CLASS_ID_LINK_STATUS) {
+ switch (reply_msg->status) {
+ case ENETC_LINK_UP:
+ link.link_status = RTE_ETH_LINK_UP;
+ break;
+ case ENETC_LINK_DOWN:
+ link.link_status = RTE_ETH_LINK_DOWN;
+ break;
+ default:
+ ENETC_PMD_ERR("Unknown link status");
+ break;
+ }
+ } else {
+ ENETC_PMD_ERR("Wrong reply message");
+ return -1;
+ }
+
+ err = enetc4_vf_get_link_speed(dev, reply_msg);
+ if (err) {
+ ENETC_PMD_ERR("Failed to get link speed");
+ rte_free(reply_msg);
+ return err;
+ }
+
+ if (reply_msg->class_id == ENETC_CLASS_ID_LINK_SPEED) {
+ switch (reply_msg->status) {
+ case ENETC_SPEED_UNKNOWN:
+ ENETC_PMD_DEBUG("Speed unknown");
+ link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+ break;
+ case ENETC_SPEED_10_HALF_DUPLEX:
+ link.link_speed = RTE_ETH_SPEED_NUM_10M;
+ link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+ break;
+ case ENETC_SPEED_10_FULL_DUPLEX:
+ link.link_speed = RTE_ETH_SPEED_NUM_10M;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_100_HALF_DUPLEX:
+ link.link_speed = RTE_ETH_SPEED_NUM_100M;
+ link.link_duplex = RTE_ETH_LINK_HALF_DUPLEX;
+ break;
+ case ENETC_SPEED_100_FULL_DUPLEX:
+ link.link_speed = RTE_ETH_SPEED_NUM_100M;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_1000:
+ link.link_speed = RTE_ETH_SPEED_NUM_1G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_2500:
+ link.link_speed = RTE_ETH_SPEED_NUM_2_5G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_5000:
+ link.link_speed = RTE_ETH_SPEED_NUM_5G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_10G:
+ link.link_speed = RTE_ETH_SPEED_NUM_10G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_25G:
+ link.link_speed = RTE_ETH_SPEED_NUM_25G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_50G:
+ link.link_speed = RTE_ETH_SPEED_NUM_50G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_100G:
+ link.link_speed = RTE_ETH_SPEED_NUM_100G;
+ link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+ break;
+ case ENETC_SPEED_NOT_SUPPORTED:
+ ENETC_PMD_DEBUG("Speed not supported");
+ link.link_speed = RTE_ETH_SPEED_NUM_UNKNOWN;
+ break;
+ default:
+ ENETC_PMD_ERR("Unknown speed status");
+ break;
+ }
+ } else {
+ ENETC_PMD_ERR("Wrong reply message");
+ return -1;
+ }
+
+ link.link_autoneg = 1;
+
+ rte_eth_linkstatus_set(dev, &link);
+
+ rte_free(reply_msg);
+ return 0;
+}
+
static int
enetc4_vf_vlan_promisc(struct rte_eth_dev *dev, bool promisc_en)
{
@@ -584,6 +796,7 @@ static const struct eth_dev_ops enetc4_vf_ops = {
.promiscuous_disable = enetc4_vf_promisc_disable,
.allmulticast_enable = enetc4_vf_multicast_enable,
.allmulticast_disable = enetc4_vf_multicast_disable,
+ .link_update = enetc4_vf_link_update,
.vlan_offload_set = enetc4_vf_vlan_offload_set,
.rx_queue_setup = enetc4_rx_queue_setup,
.rx_queue_start = enetc4_rx_queue_start,
@@ -685,6 +898,9 @@ enetc4_vf_dev_init(struct rte_eth_dev *eth_dev)
ENETC_PMD_DEBUG("port_id %d vendorID=0x%x deviceID=0x%x",
eth_dev->data->port_id, pci_dev->id.vendor_id,
pci_dev->id.device_id);
+ /* update link */
+ enetc4_vf_link_update(eth_dev, 0);
+
return 0;
}