net/igc: implement device base operations
Bellow ops are implemented: dev_configure dev_start dev_stop dev_close dev_reset dev_set_link_up dev_set_link_down link_update fw_version_get dev_led_on dev_led_off Signed-off-by: Alvin Zhang <alvinx.zhang@intel.com> Reviewed-by: Ferruh Yigit <ferruh.yigit@intel.com>
This commit is contained in:
parent
8cb7c57d9b
commit
4f09bc55ac
@ -3,6 +3,11 @@
|
||||
; Refer to default.ini for the full list of available PMD features.
|
||||
;
|
||||
[Features]
|
||||
Speed capabilities = Y
|
||||
Link status = Y
|
||||
Link status event = Y
|
||||
FW version = Y
|
||||
LED = Y
|
||||
Linux UIO = Y
|
||||
Linux VFIO = Y
|
||||
x86-64 = Y
|
||||
|
@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rte_pci.h>
|
||||
#include <rte_bus_pci.h>
|
||||
@ -15,7 +16,34 @@
|
||||
|
||||
#define IGC_INTEL_VENDOR_ID 0x8086
|
||||
|
||||
/*
|
||||
* The overhead from MTU to max frame size.
|
||||
* Considering VLAN so tag needs to be counted.
|
||||
*/
|
||||
#define IGC_ETH_OVERHEAD (RTE_ETHER_HDR_LEN + \
|
||||
RTE_ETHER_CRC_LEN + VLAN_TAG_SIZE)
|
||||
|
||||
#define IGC_FC_PAUSE_TIME 0x0680
|
||||
#define IGC_LINK_UPDATE_CHECK_TIMEOUT 90 /* 9s */
|
||||
#define IGC_LINK_UPDATE_CHECK_INTERVAL 100 /* ms */
|
||||
|
||||
#define IGC_MISC_VEC_ID RTE_INTR_VEC_ZERO_OFFSET
|
||||
#define IGC_RX_VEC_START RTE_INTR_VEC_RXTX_OFFSET
|
||||
#define IGC_MSIX_OTHER_INTR_VEC 0 /* MSI-X other interrupt vector */
|
||||
#define IGC_FLAG_NEED_LINK_UPDATE (1u << 0) /* need update link */
|
||||
|
||||
#define IGC_DEFAULT_RX_FREE_THRESH 32
|
||||
|
||||
#define IGC_DEFAULT_RX_PTHRESH 8
|
||||
#define IGC_DEFAULT_RX_HTHRESH 8
|
||||
#define IGC_DEFAULT_RX_WTHRESH 4
|
||||
|
||||
#define IGC_DEFAULT_TX_PTHRESH 8
|
||||
#define IGC_DEFAULT_TX_HTHRESH 1
|
||||
#define IGC_DEFAULT_TX_WTHRESH 16
|
||||
|
||||
/* MSI-X other interrupt vector */
|
||||
#define IGC_MSIX_OTHER_INTR_VEC 0
|
||||
|
||||
static const struct rte_pci_id pci_id_igc_map[] = {
|
||||
{ RTE_PCI_DEVICE(IGC_INTEL_VENDOR_ID, IGC_DEV_ID_I225_LM) },
|
||||
@ -29,12 +57,20 @@ static int eth_igc_configure(struct rte_eth_dev *dev);
|
||||
static int eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete);
|
||||
static void eth_igc_stop(struct rte_eth_dev *dev);
|
||||
static int eth_igc_start(struct rte_eth_dev *dev);
|
||||
static int eth_igc_set_link_up(struct rte_eth_dev *dev);
|
||||
static int eth_igc_set_link_down(struct rte_eth_dev *dev);
|
||||
static void eth_igc_close(struct rte_eth_dev *dev);
|
||||
static int eth_igc_reset(struct rte_eth_dev *dev);
|
||||
static int eth_igc_promiscuous_enable(struct rte_eth_dev *dev);
|
||||
static int eth_igc_promiscuous_disable(struct rte_eth_dev *dev);
|
||||
static int eth_igc_fw_version_get(struct rte_eth_dev *dev,
|
||||
char *fw_version, size_t fw_size);
|
||||
static int eth_igc_infos_get(struct rte_eth_dev *dev,
|
||||
struct rte_eth_dev_info *dev_info);
|
||||
static int eth_igc_led_on(struct rte_eth_dev *dev);
|
||||
static int eth_igc_led_off(struct rte_eth_dev *dev);
|
||||
static void eth_igc_tx_queue_release(void *txq);
|
||||
static void eth_igc_rx_queue_release(void *rxq);
|
||||
static int
|
||||
eth_igc_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id,
|
||||
uint16_t nb_rx_desc, unsigned int socket_id,
|
||||
@ -52,35 +88,394 @@ static const struct eth_dev_ops eth_igc_ops = {
|
||||
.dev_start = eth_igc_start,
|
||||
.dev_close = eth_igc_close,
|
||||
.dev_reset = eth_igc_reset,
|
||||
.dev_set_link_up = eth_igc_set_link_up,
|
||||
.dev_set_link_down = eth_igc_set_link_down,
|
||||
.promiscuous_enable = eth_igc_promiscuous_enable,
|
||||
.promiscuous_disable = eth_igc_promiscuous_disable,
|
||||
|
||||
.fw_version_get = eth_igc_fw_version_get,
|
||||
.dev_infos_get = eth_igc_infos_get,
|
||||
.dev_led_on = eth_igc_led_on,
|
||||
.dev_led_off = eth_igc_led_off,
|
||||
|
||||
.rx_queue_setup = eth_igc_rx_queue_setup,
|
||||
.rx_queue_release = eth_igc_rx_queue_release,
|
||||
.tx_queue_setup = eth_igc_tx_queue_setup,
|
||||
.tx_queue_release = eth_igc_tx_queue_release,
|
||||
};
|
||||
|
||||
/*
|
||||
* multiple queue mode checking
|
||||
*/
|
||||
static int
|
||||
igc_check_mq_mode(struct rte_eth_dev *dev)
|
||||
{
|
||||
enum rte_eth_rx_mq_mode rx_mq_mode = dev->data->dev_conf.rxmode.mq_mode;
|
||||
enum rte_eth_tx_mq_mode tx_mq_mode = dev->data->dev_conf.txmode.mq_mode;
|
||||
|
||||
if (RTE_ETH_DEV_SRIOV(dev).active != 0) {
|
||||
PMD_INIT_LOG(ERR, "SRIOV is not supported.");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (rx_mq_mode != ETH_MQ_RX_NONE &&
|
||||
rx_mq_mode != ETH_MQ_RX_RSS) {
|
||||
/* RSS together with VMDq not supported*/
|
||||
PMD_INIT_LOG(ERR, "RX mode %d is not supported.",
|
||||
rx_mq_mode);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* To no break software that set invalid mode, only display
|
||||
* warning if invalid mode is used.
|
||||
*/
|
||||
if (tx_mq_mode != ETH_MQ_TX_NONE)
|
||||
PMD_INIT_LOG(WARNING,
|
||||
"TX mode %d is not supported. Due to meaningless in this driver, just ignore",
|
||||
tx_mq_mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_configure(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev);
|
||||
int ret;
|
||||
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
RTE_SET_USED(dev);
|
||||
|
||||
ret = igc_check_mq_mode(dev);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
intr->flags |= IGC_FLAG_NEED_LINK_UPDATE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete)
|
||||
eth_igc_set_link_up(struct rte_eth_dev *dev)
|
||||
{
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
RTE_SET_USED(dev);
|
||||
RTE_SET_USED(wait_to_complete);
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
|
||||
if (hw->phy.media_type == igc_media_type_copper)
|
||||
igc_power_up_phy(hw);
|
||||
else
|
||||
igc_power_up_fiber_serdes_link(hw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_set_link_down(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
|
||||
if (hw->phy.media_type == igc_media_type_copper)
|
||||
igc_power_down_phy(hw);
|
||||
else
|
||||
igc_shutdown_fiber_serdes_link(hw);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* disable other interrupt
|
||||
*/
|
||||
static void
|
||||
igc_intr_other_disable(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
|
||||
if (rte_intr_allow_others(intr_handle) &&
|
||||
dev->data->dev_conf.intr_conf.lsc) {
|
||||
IGC_WRITE_REG(hw, IGC_EIMC, 1u << IGC_MSIX_OTHER_INTR_VEC);
|
||||
}
|
||||
|
||||
IGC_WRITE_REG(hw, IGC_IMC, ~0);
|
||||
IGC_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
/*
|
||||
* enable other interrupt
|
||||
*/
|
||||
static inline void
|
||||
igc_intr_other_enable(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev);
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
|
||||
if (rte_intr_allow_others(intr_handle) &&
|
||||
dev->data->dev_conf.intr_conf.lsc) {
|
||||
IGC_WRITE_REG(hw, IGC_EIMS, 1u << IGC_MSIX_OTHER_INTR_VEC);
|
||||
}
|
||||
|
||||
IGC_WRITE_REG(hw, IGC_IMS, intr->mask);
|
||||
IGC_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
/*
|
||||
* It reads ICR and gets interrupt causes, check it and set a bit flag
|
||||
* to update link status.
|
||||
*/
|
||||
static void
|
||||
eth_igc_interrupt_get_status(struct rte_eth_dev *dev)
|
||||
{
|
||||
uint32_t icr;
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev);
|
||||
|
||||
/* read-on-clear nic registers here */
|
||||
icr = IGC_READ_REG(hw, IGC_ICR);
|
||||
|
||||
intr->flags = 0;
|
||||
if (icr & IGC_ICR_LSC)
|
||||
intr->flags |= IGC_FLAG_NEED_LINK_UPDATE;
|
||||
}
|
||||
|
||||
/* return 0 means link status changed, -1 means not changed */
|
||||
static int
|
||||
eth_igc_link_update(struct rte_eth_dev *dev, int wait_to_complete)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct rte_eth_link link;
|
||||
int link_check, count;
|
||||
|
||||
link_check = 0;
|
||||
hw->mac.get_link_status = 1;
|
||||
|
||||
/* possible wait-to-complete in up to 9 seconds */
|
||||
for (count = 0; count < IGC_LINK_UPDATE_CHECK_TIMEOUT; count++) {
|
||||
/* Read the real link status */
|
||||
switch (hw->phy.media_type) {
|
||||
case igc_media_type_copper:
|
||||
/* Do the work to read phy */
|
||||
igc_check_for_link(hw);
|
||||
link_check = !hw->mac.get_link_status;
|
||||
break;
|
||||
|
||||
case igc_media_type_fiber:
|
||||
igc_check_for_link(hw);
|
||||
link_check = (IGC_READ_REG(hw, IGC_STATUS) &
|
||||
IGC_STATUS_LU);
|
||||
break;
|
||||
|
||||
case igc_media_type_internal_serdes:
|
||||
igc_check_for_link(hw);
|
||||
link_check = hw->mac.serdes_has_link;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (link_check || wait_to_complete == 0)
|
||||
break;
|
||||
rte_delay_ms(IGC_LINK_UPDATE_CHECK_INTERVAL);
|
||||
}
|
||||
memset(&link, 0, sizeof(link));
|
||||
|
||||
/* Now we check if a transition has happened */
|
||||
if (link_check) {
|
||||
uint16_t duplex, speed;
|
||||
hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
|
||||
link.link_duplex = (duplex == FULL_DUPLEX) ?
|
||||
ETH_LINK_FULL_DUPLEX :
|
||||
ETH_LINK_HALF_DUPLEX;
|
||||
link.link_speed = speed;
|
||||
link.link_status = ETH_LINK_UP;
|
||||
link.link_autoneg = !(dev->data->dev_conf.link_speeds &
|
||||
ETH_LINK_SPEED_FIXED);
|
||||
|
||||
if (speed == SPEED_2500) {
|
||||
uint32_t tipg = IGC_READ_REG(hw, IGC_TIPG);
|
||||
if ((tipg & IGC_TIPG_IPGT_MASK) != 0x0b) {
|
||||
tipg &= ~IGC_TIPG_IPGT_MASK;
|
||||
tipg |= 0x0b;
|
||||
IGC_WRITE_REG(hw, IGC_TIPG, tipg);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
link.link_speed = 0;
|
||||
link.link_duplex = ETH_LINK_HALF_DUPLEX;
|
||||
link.link_status = ETH_LINK_DOWN;
|
||||
link.link_autoneg = ETH_LINK_FIXED;
|
||||
}
|
||||
|
||||
return rte_eth_linkstatus_set(dev, &link);
|
||||
}
|
||||
|
||||
/*
|
||||
* It executes link_update after knowing an interrupt is present.
|
||||
*/
|
||||
static void
|
||||
eth_igc_interrupt_action(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_eth_link link;
|
||||
int ret;
|
||||
|
||||
if (intr->flags & IGC_FLAG_NEED_LINK_UPDATE) {
|
||||
intr->flags &= ~IGC_FLAG_NEED_LINK_UPDATE;
|
||||
|
||||
/* set get_link_status to check register later */
|
||||
ret = eth_igc_link_update(dev, 0);
|
||||
|
||||
/* check if link has changed */
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
rte_eth_linkstatus_get(dev, &link);
|
||||
if (link.link_status)
|
||||
PMD_DRV_LOG(INFO,
|
||||
" Port %d: Link Up - speed %u Mbps - %s",
|
||||
dev->data->port_id,
|
||||
(unsigned int)link.link_speed,
|
||||
link.link_duplex == ETH_LINK_FULL_DUPLEX ?
|
||||
"full-duplex" : "half-duplex");
|
||||
else
|
||||
PMD_DRV_LOG(INFO, " Port %d: Link Down",
|
||||
dev->data->port_id);
|
||||
|
||||
PMD_DRV_LOG(DEBUG, "PCI Address: " PCI_PRI_FMT,
|
||||
pci_dev->addr.domain,
|
||||
pci_dev->addr.bus,
|
||||
pci_dev->addr.devid,
|
||||
pci_dev->addr.function);
|
||||
_rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_LSC,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Interrupt handler which shall be registered at first.
|
||||
*
|
||||
* @handle
|
||||
* Pointer to interrupt handle.
|
||||
* @param
|
||||
* The address of parameter (struct rte_eth_dev *) registered before.
|
||||
*/
|
||||
static void
|
||||
eth_igc_interrupt_handler(void *param)
|
||||
{
|
||||
struct rte_eth_dev *dev = (struct rte_eth_dev *)param;
|
||||
|
||||
eth_igc_interrupt_get_status(dev);
|
||||
eth_igc_interrupt_action(dev);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine disables all traffic on the adapter by issuing a
|
||||
* global reset on the MAC.
|
||||
*/
|
||||
static void
|
||||
eth_igc_stop(struct rte_eth_dev *dev)
|
||||
{
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
RTE_SET_USED(dev);
|
||||
struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev);
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
struct rte_eth_link link;
|
||||
|
||||
adapter->stopped = 1;
|
||||
|
||||
/* disable all MSI-X interrupts */
|
||||
IGC_WRITE_REG(hw, IGC_EIMC, 0x1f);
|
||||
IGC_WRITE_FLUSH(hw);
|
||||
|
||||
/* clear all MSI-X interrupts */
|
||||
IGC_WRITE_REG(hw, IGC_EICR, 0x1f);
|
||||
|
||||
igc_intr_other_disable(dev);
|
||||
|
||||
/* disable intr eventfd mapping */
|
||||
rte_intr_disable(intr_handle);
|
||||
|
||||
igc_reset_hw(hw);
|
||||
|
||||
/* disable all wake up */
|
||||
IGC_WRITE_REG(hw, IGC_WUC, 0);
|
||||
|
||||
/* Set bit for Go Link disconnect */
|
||||
igc_read_reg_check_set_bits(hw, IGC_82580_PHY_POWER_MGMT,
|
||||
IGC_82580_PM_GO_LINKD);
|
||||
|
||||
/* Power down the phy. Needed to make the link go Down */
|
||||
eth_igc_set_link_down(dev);
|
||||
|
||||
/* clear the recorded link status */
|
||||
memset(&link, 0, sizeof(link));
|
||||
rte_eth_linkstatus_set(dev, &link);
|
||||
|
||||
if (!rte_intr_allow_others(intr_handle))
|
||||
/* resume to the default handler */
|
||||
rte_intr_callback_register(intr_handle,
|
||||
eth_igc_interrupt_handler,
|
||||
(void *)dev);
|
||||
|
||||
/* Clean datapath event and queue/vec mapping */
|
||||
rte_intr_efd_disable(intr_handle);
|
||||
}
|
||||
|
||||
/* Sets up the hardware to generate MSI-X interrupts properly
|
||||
* @hw
|
||||
* board private structure
|
||||
*/
|
||||
static void
|
||||
igc_configure_msix_intr(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
|
||||
uint32_t intr_mask;
|
||||
|
||||
/* won't configure msix register if no mapping is done
|
||||
* between intr vector and event fd
|
||||
*/
|
||||
if (!rte_intr_dp_is_en(intr_handle) ||
|
||||
!dev->data->dev_conf.intr_conf.lsc)
|
||||
return;
|
||||
|
||||
/* turn on MSI-X capability first */
|
||||
IGC_WRITE_REG(hw, IGC_GPIE, IGC_GPIE_MSIX_MODE |
|
||||
IGC_GPIE_PBA | IGC_GPIE_EIAME |
|
||||
IGC_GPIE_NSICR);
|
||||
|
||||
intr_mask = (1u << IGC_MSIX_OTHER_INTR_VEC);
|
||||
|
||||
/* enable msix auto-clear */
|
||||
igc_read_reg_check_set_bits(hw, IGC_EIAC, intr_mask);
|
||||
|
||||
/* set other cause interrupt vector */
|
||||
igc_read_reg_check_set_bits(hw, IGC_IVAR_MISC,
|
||||
(uint32_t)(IGC_MSIX_OTHER_INTR_VEC | IGC_IVAR_VALID) << 8);
|
||||
|
||||
/* enable auto-mask */
|
||||
igc_read_reg_check_set_bits(hw, IGC_EIAM, intr_mask);
|
||||
|
||||
IGC_WRITE_FLUSH(hw);
|
||||
}
|
||||
|
||||
/**
|
||||
* It enables the interrupt mask and then enable the interrupt.
|
||||
*
|
||||
* @dev
|
||||
* Pointer to struct rte_eth_dev.
|
||||
* @on
|
||||
* Enable or Disable
|
||||
*/
|
||||
static void
|
||||
igc_lsc_interrupt_setup(struct rte_eth_dev *dev, uint8_t on)
|
||||
{
|
||||
struct igc_interrupt *intr = IGC_DEV_PRIVATE_INTR(dev);
|
||||
|
||||
if (on)
|
||||
intr->mask |= IGC_ICR_LSC;
|
||||
else
|
||||
intr->mask &= ~IGC_ICR_LSC;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -170,9 +565,134 @@ igc_hardware_init(struct igc_hw *hw)
|
||||
static int
|
||||
eth_igc_start(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev);
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
uint32_t *speeds;
|
||||
int num_speeds;
|
||||
bool autoneg;
|
||||
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
RTE_SET_USED(dev);
|
||||
|
||||
/* disable all MSI-X interrupts */
|
||||
IGC_WRITE_REG(hw, IGC_EIMC, 0x1f);
|
||||
IGC_WRITE_FLUSH(hw);
|
||||
|
||||
/* clear all MSI-X interrupts */
|
||||
IGC_WRITE_REG(hw, IGC_EICR, 0x1f);
|
||||
|
||||
/* disable uio/vfio intr/eventfd mapping */
|
||||
if (!adapter->stopped)
|
||||
rte_intr_disable(intr_handle);
|
||||
|
||||
/* Power up the phy. Needed to make the link go Up */
|
||||
eth_igc_set_link_up(dev);
|
||||
|
||||
/* Put the address into the Receive Address Array */
|
||||
igc_rar_set(hw, hw->mac.addr, 0);
|
||||
|
||||
/* Initialize the hardware */
|
||||
if (igc_hardware_init(hw)) {
|
||||
PMD_DRV_LOG(ERR, "Unable to initialize the hardware");
|
||||
return -EIO;
|
||||
}
|
||||
adapter->stopped = 0;
|
||||
|
||||
/* confiugre msix for rx interrupt */
|
||||
igc_configure_msix_intr(dev);
|
||||
|
||||
igc_clear_hw_cntrs_base_generic(hw);
|
||||
|
||||
/* Setup link speed and duplex */
|
||||
speeds = &dev->data->dev_conf.link_speeds;
|
||||
if (*speeds == ETH_LINK_SPEED_AUTONEG) {
|
||||
hw->phy.autoneg_advertised = IGC_ALL_SPEED_DUPLEX_2500;
|
||||
hw->mac.autoneg = 1;
|
||||
} else {
|
||||
num_speeds = 0;
|
||||
autoneg = (*speeds & ETH_LINK_SPEED_FIXED) == 0;
|
||||
|
||||
/* Reset */
|
||||
hw->phy.autoneg_advertised = 0;
|
||||
|
||||
if (*speeds & ~(ETH_LINK_SPEED_10M_HD | ETH_LINK_SPEED_10M |
|
||||
ETH_LINK_SPEED_100M_HD | ETH_LINK_SPEED_100M |
|
||||
ETH_LINK_SPEED_1G | ETH_LINK_SPEED_2_5G |
|
||||
ETH_LINK_SPEED_FIXED)) {
|
||||
num_speeds = -1;
|
||||
goto error_invalid_config;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_10M_HD) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_10_HALF;
|
||||
num_speeds++;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_10M) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_10_FULL;
|
||||
num_speeds++;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_100M_HD) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_100_HALF;
|
||||
num_speeds++;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_100M) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_100_FULL;
|
||||
num_speeds++;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_1G) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_1000_FULL;
|
||||
num_speeds++;
|
||||
}
|
||||
if (*speeds & ETH_LINK_SPEED_2_5G) {
|
||||
hw->phy.autoneg_advertised |= ADVERTISE_2500_FULL;
|
||||
num_speeds++;
|
||||
}
|
||||
if (num_speeds == 0 || (!autoneg && num_speeds > 1))
|
||||
goto error_invalid_config;
|
||||
|
||||
/* Set/reset the mac.autoneg based on the link speed,
|
||||
* fixed or not
|
||||
*/
|
||||
if (!autoneg) {
|
||||
hw->mac.autoneg = 0;
|
||||
hw->mac.forced_speed_duplex =
|
||||
hw->phy.autoneg_advertised;
|
||||
} else {
|
||||
hw->mac.autoneg = 1;
|
||||
}
|
||||
}
|
||||
|
||||
igc_setup_link(hw);
|
||||
|
||||
if (rte_intr_allow_others(intr_handle)) {
|
||||
/* check if lsc interrupt is enabled */
|
||||
if (dev->data->dev_conf.intr_conf.lsc)
|
||||
igc_lsc_interrupt_setup(dev, 1);
|
||||
else
|
||||
igc_lsc_interrupt_setup(dev, 0);
|
||||
} else {
|
||||
rte_intr_callback_unregister(intr_handle,
|
||||
eth_igc_interrupt_handler,
|
||||
(void *)dev);
|
||||
if (dev->data->dev_conf.intr_conf.lsc)
|
||||
PMD_DRV_LOG(INFO,
|
||||
"LSC won't enable because of no intr multiplex");
|
||||
}
|
||||
|
||||
/* enable uio/vfio intr/eventfd mapping */
|
||||
rte_intr_enable(intr_handle);
|
||||
|
||||
/* resume enabled intr since hw reset */
|
||||
igc_intr_other_enable(dev);
|
||||
|
||||
eth_igc_link_update(dev, 0);
|
||||
|
||||
return 0;
|
||||
|
||||
error_invalid_config:
|
||||
PMD_DRV_LOG(ERR, "Invalid advertised speeds (%u) for port %u",
|
||||
dev->data->dev_conf.link_speeds, dev->data->port_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -232,10 +752,28 @@ igc_reset_swfw_lock(struct igc_hw *hw)
|
||||
static void
|
||||
eth_igc_close(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct igc_adapter *adapter = IGC_DEV_PRIVATE(dev);
|
||||
int retry = 0;
|
||||
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
|
||||
if (!adapter->stopped)
|
||||
eth_igc_stop(dev);
|
||||
|
||||
igc_intr_other_disable(dev);
|
||||
do {
|
||||
int ret = rte_intr_callback_unregister(intr_handle,
|
||||
eth_igc_interrupt_handler, dev);
|
||||
if (ret >= 0 || ret == -ENOENT || ret == -EINVAL)
|
||||
break;
|
||||
|
||||
PMD_DRV_LOG(ERR, "intr callback unregister failed: %d", ret);
|
||||
DELAY(200 * 1000); /* delay 200ms */
|
||||
} while (retry++ < 5);
|
||||
|
||||
igc_phy_hw_reset(hw);
|
||||
igc_hw_control_release(hw);
|
||||
|
||||
@ -258,6 +796,7 @@ static int
|
||||
eth_igc_dev_init(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct rte_pci_device *pci_dev = RTE_ETH_DEV_TO_PCI(dev);
|
||||
struct igc_adapter *igc = IGC_DEV_PRIVATE(dev);
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
int error = 0;
|
||||
|
||||
@ -364,6 +903,7 @@ eth_igc_dev_init(struct rte_eth_dev *dev)
|
||||
dev->data->dev_flags |= RTE_ETH_DEV_CLOSE_REMOVE;
|
||||
|
||||
hw->mac.get_link_status = 1;
|
||||
igc->stopped = 0;
|
||||
|
||||
/* Indicate SOL/IDER usage */
|
||||
if (igc_check_reset_block(hw) < 0)
|
||||
@ -374,6 +914,15 @@ eth_igc_dev_init(struct rte_eth_dev *dev)
|
||||
dev->data->port_id, pci_dev->id.vendor_id,
|
||||
pci_dev->id.device_id);
|
||||
|
||||
rte_intr_callback_register(&pci_dev->intr_handle,
|
||||
eth_igc_interrupt_handler, (void *)dev);
|
||||
|
||||
/* enable uio/vfio intr/eventfd mapping */
|
||||
rte_intr_enable(&pci_dev->intr_handle);
|
||||
|
||||
/* enable support intr */
|
||||
igc_intr_other_enable(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_late:
|
||||
@ -423,16 +972,81 @@ eth_igc_promiscuous_disable(struct rte_eth_dev *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_fw_version_get(struct rte_eth_dev *dev, char *fw_version,
|
||||
size_t fw_size)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
struct igc_fw_version fw;
|
||||
int ret;
|
||||
|
||||
igc_get_fw_version(hw, &fw);
|
||||
|
||||
/* if option rom is valid, display its version too */
|
||||
if (fw.or_valid) {
|
||||
ret = snprintf(fw_version, fw_size,
|
||||
"%d.%d, 0x%08x, %d.%d.%d",
|
||||
fw.eep_major, fw.eep_minor, fw.etrack_id,
|
||||
fw.or_major, fw.or_build, fw.or_patch);
|
||||
/* no option rom */
|
||||
} else {
|
||||
if (fw.etrack_id != 0X0000) {
|
||||
ret = snprintf(fw_version, fw_size,
|
||||
"%d.%d, 0x%08x",
|
||||
fw.eep_major, fw.eep_minor,
|
||||
fw.etrack_id);
|
||||
} else {
|
||||
ret = snprintf(fw_version, fw_size,
|
||||
"%d.%d.%d",
|
||||
fw.eep_major, fw.eep_minor,
|
||||
fw.eep_build);
|
||||
}
|
||||
}
|
||||
|
||||
ret += 1; /* add the size of '\0' */
|
||||
if (fw_size < (u32)ret)
|
||||
return ret;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
|
||||
{
|
||||
PMD_INIT_FUNC_TRACE();
|
||||
RTE_SET_USED(dev);
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
|
||||
dev_info->min_rx_bufsize = 256; /* See BSIZE field of RCTL register. */
|
||||
dev_info->max_rx_pktlen = MAX_RX_JUMBO_FRAME_SIZE;
|
||||
dev_info->max_mac_addrs = hw->mac.rar_entry_count;
|
||||
dev_info->max_rx_queues = IGC_QUEUE_PAIRS_NUM;
|
||||
dev_info->max_tx_queues = IGC_QUEUE_PAIRS_NUM;
|
||||
dev_info->max_vmdq_pools = 0;
|
||||
|
||||
dev_info->speed_capa = ETH_LINK_SPEED_10M_HD | ETH_LINK_SPEED_10M |
|
||||
ETH_LINK_SPEED_100M_HD | ETH_LINK_SPEED_100M |
|
||||
ETH_LINK_SPEED_1G | ETH_LINK_SPEED_2_5G;
|
||||
|
||||
dev_info->max_mtu = dev_info->max_rx_pktlen - IGC_ETH_OVERHEAD;
|
||||
dev_info->min_mtu = RTE_ETHER_MIN_MTU;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_led_on(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
|
||||
return igc_led_on(hw) == IGC_SUCCESS ? 0 : -ENOTSUP;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_led_off(struct rte_eth_dev *dev)
|
||||
{
|
||||
struct igc_hw *hw = IGC_DEV_PRIVATE_HW(dev);
|
||||
|
||||
return igc_led_off(hw) == IGC_SUCCESS ? 0 : -ENOTSUP;
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id,
|
||||
uint16_t nb_rx_desc, unsigned int socket_id,
|
||||
@ -463,6 +1077,16 @@ eth_igc_tx_queue_setup(struct rte_eth_dev *dev, uint16_t queue_idx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eth_igc_tx_queue_release(void *txq)
|
||||
{
|
||||
RTE_SET_USED(txq);
|
||||
}
|
||||
|
||||
static void eth_igc_rx_queue_release(void *rxq)
|
||||
{
|
||||
RTE_SET_USED(rxq);
|
||||
}
|
||||
|
||||
static int
|
||||
eth_igc_pci_probe(struct rte_pci_driver *pci_drv __rte_unused,
|
||||
struct rte_pci_device *pci_dev)
|
||||
|
@ -18,11 +18,19 @@ extern "C" {
|
||||
|
||||
#define IGC_QUEUE_PAIRS_NUM 4
|
||||
|
||||
/* structure for interrupt relative data */
|
||||
struct igc_interrupt {
|
||||
uint32_t flags;
|
||||
uint32_t mask;
|
||||
};
|
||||
|
||||
/*
|
||||
* Structure to store private data for each driver instance (for each port).
|
||||
*/
|
||||
struct igc_adapter {
|
||||
struct igc_hw hw;
|
||||
struct igc_interrupt intr;
|
||||
bool stopped;
|
||||
};
|
||||
|
||||
#define IGC_DEV_PRIVATE(_dev) ((_dev)->data->dev_private)
|
||||
@ -30,6 +38,33 @@ struct igc_adapter {
|
||||
#define IGC_DEV_PRIVATE_HW(_dev) \
|
||||
(&((struct igc_adapter *)(_dev)->data->dev_private)->hw)
|
||||
|
||||
#define IGC_DEV_PRIVATE_INTR(_dev) \
|
||||
(&((struct igc_adapter *)(_dev)->data->dev_private)->intr)
|
||||
|
||||
static inline void
|
||||
igc_read_reg_check_set_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits)
|
||||
{
|
||||
uint32_t reg_val = IGC_READ_REG(hw, reg);
|
||||
|
||||
bits |= reg_val;
|
||||
if (bits == reg_val)
|
||||
return; /* no need to write back */
|
||||
|
||||
IGC_WRITE_REG(hw, reg, bits);
|
||||
}
|
||||
|
||||
static inline void
|
||||
igc_read_reg_check_clear_bits(struct igc_hw *hw, uint32_t reg, uint32_t bits)
|
||||
{
|
||||
uint32_t reg_val = IGC_READ_REG(hw, reg);
|
||||
|
||||
bits = reg_val & ~bits;
|
||||
if (bits == reg_val)
|
||||
return; /* no need to write back */
|
||||
|
||||
IGC_WRITE_REG(hw, reg, bits);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user