|
@@ -22,6 +22,10 @@
|
|
|
|
|
|
#include "fm10k.h"
|
|
#include "fm10k.h"
|
|
|
|
|
|
|
|
+static const struct fm10k_info *fm10k_info_tbl[] = {
|
|
|
|
+ [fm10k_device_pf] = &fm10k_pf_info,
|
|
|
|
+};
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* fm10k_pci_tbl - PCI Device ID Table
|
|
* fm10k_pci_tbl - PCI Device ID Table
|
|
*
|
|
*
|
|
@@ -32,7 +36,7 @@
|
|
* Class, Class Mask, private data (not used) }
|
|
* Class, Class Mask, private data (not used) }
|
|
*/
|
|
*/
|
|
static const struct pci_device_id fm10k_pci_tbl[] = {
|
|
static const struct pci_device_id fm10k_pci_tbl[] = {
|
|
- { PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF) },
|
|
|
|
|
|
+ { PCI_VDEVICE(INTEL, FM10K_DEV_ID_PF), fm10k_device_pf },
|
|
/* required last entry */
|
|
/* required last entry */
|
|
{ 0, }
|
|
{ 0, }
|
|
};
|
|
};
|
|
@@ -62,12 +66,153 @@ u32 fm10k_read_reg(struct fm10k_hw *hw, int reg)
|
|
return ~value;
|
|
return ~value;
|
|
|
|
|
|
value = readl(&hw_addr[reg]);
|
|
value = readl(&hw_addr[reg]);
|
|
- if (!(~value) && (!reg || !(~readl(hw_addr))))
|
|
|
|
|
|
+ if (!(~value) && (!reg || !(~readl(hw_addr)))) {
|
|
|
|
+ struct fm10k_intfc *interface = hw->back;
|
|
|
|
+ struct net_device *netdev = interface->netdev;
|
|
|
|
+
|
|
hw->hw_addr = NULL;
|
|
hw->hw_addr = NULL;
|
|
|
|
+ netif_device_detach(netdev);
|
|
|
|
+ netdev_err(netdev, "PCIe link lost, device now detached\n");
|
|
|
|
+ }
|
|
|
|
|
|
return value;
|
|
return value;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int fm10k_hw_ready(struct fm10k_intfc *interface)
|
|
|
|
+{
|
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
|
+
|
|
|
|
+ fm10k_write_flush(hw);
|
|
|
|
+
|
|
|
|
+ return FM10K_REMOVED(hw->hw_addr) ? -ENODEV : 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * fm10k_sw_init - Initialize general software structures
|
|
|
|
+ * @interface: host interface private structure to initialize
|
|
|
|
+ *
|
|
|
|
+ * fm10k_sw_init initializes the interface private data structure.
|
|
|
|
+ * Fields are initialized based on PCI device information and
|
|
|
|
+ * OS network device settings (MTU size).
|
|
|
|
+ **/
|
|
|
|
+static int fm10k_sw_init(struct fm10k_intfc *interface,
|
|
|
|
+ const struct pci_device_id *ent)
|
|
|
|
+{
|
|
|
|
+ static const u32 seed[FM10K_RSSRK_SIZE] = { 0xda565a6d, 0xc20e5b25,
|
|
|
|
+ 0x3d256741, 0xb08fa343,
|
|
|
|
+ 0xcb2bcad0, 0xb4307bae,
|
|
|
|
+ 0xa32dcb77, 0x0cf23080,
|
|
|
|
+ 0x3bb7426a, 0xfa01acbe };
|
|
|
|
+ const struct fm10k_info *fi = fm10k_info_tbl[ent->driver_data];
|
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
|
+ struct pci_dev *pdev = interface->pdev;
|
|
|
|
+ struct net_device *netdev = interface->netdev;
|
|
|
|
+ unsigned int rss;
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ /* initialize back pointer */
|
|
|
|
+ hw->back = interface;
|
|
|
|
+ hw->hw_addr = interface->uc_addr;
|
|
|
|
+
|
|
|
|
+ /* PCI config space info */
|
|
|
|
+ hw->vendor_id = pdev->vendor;
|
|
|
|
+ hw->device_id = pdev->device;
|
|
|
|
+ hw->revision_id = pdev->revision;
|
|
|
|
+ hw->subsystem_vendor_id = pdev->subsystem_vendor;
|
|
|
|
+ hw->subsystem_device_id = pdev->subsystem_device;
|
|
|
|
+
|
|
|
|
+ /* Setup hw api */
|
|
|
|
+ memcpy(&hw->mac.ops, fi->mac_ops, sizeof(hw->mac.ops));
|
|
|
|
+ hw->mac.type = fi->mac;
|
|
|
|
+
|
|
|
|
+ /* Set common capability flags and settings */
|
|
|
|
+ rss = min_t(int, FM10K_MAX_RSS_INDICES, num_online_cpus());
|
|
|
|
+ interface->ring_feature[RING_F_RSS].limit = rss;
|
|
|
|
+ fi->get_invariants(hw);
|
|
|
|
+
|
|
|
|
+ /* pick up the PCIe bus settings for reporting later */
|
|
|
|
+ if (hw->mac.ops.get_bus_info)
|
|
|
|
+ hw->mac.ops.get_bus_info(hw);
|
|
|
|
+
|
|
|
|
+ /* limit the usable DMA range */
|
|
|
|
+ if (hw->mac.ops.set_dma_mask)
|
|
|
|
+ hw->mac.ops.set_dma_mask(hw, dma_get_mask(&pdev->dev));
|
|
|
|
+
|
|
|
|
+ /* update netdev with DMA restrictions */
|
|
|
|
+ if (dma_get_mask(&pdev->dev) > DMA_BIT_MASK(32)) {
|
|
|
|
+ netdev->features |= NETIF_F_HIGHDMA;
|
|
|
|
+ netdev->vlan_features |= NETIF_F_HIGHDMA;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* reset and initialize the hardware so it is in a known state */
|
|
|
|
+ err = hw->mac.ops.reset_hw(hw) ? : hw->mac.ops.init_hw(hw);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_err(&pdev->dev, "init_hw failed: %d\n", err);
|
|
|
|
+ return err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* initialize hardware statistics */
|
|
|
|
+ hw->mac.ops.update_hw_stats(hw, &interface->stats);
|
|
|
|
+
|
|
|
|
+ /* Start with random Ethernet address */
|
|
|
|
+ eth_random_addr(hw->mac.addr);
|
|
|
|
+
|
|
|
|
+ /* Initialize MAC address from hardware */
|
|
|
|
+ err = hw->mac.ops.read_mac_addr(hw);
|
|
|
|
+ if (err) {
|
|
|
|
+ dev_warn(&pdev->dev,
|
|
|
|
+ "Failed to obtain MAC address defaulting to random\n");
|
|
|
|
+ /* tag address assignment as random */
|
|
|
|
+ netdev->addr_assign_type |= NET_ADDR_RANDOM;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(netdev->dev_addr, hw->mac.addr, netdev->addr_len);
|
|
|
|
+ memcpy(netdev->perm_addr, hw->mac.addr, netdev->addr_len);
|
|
|
|
+
|
|
|
|
+ if (!is_valid_ether_addr(netdev->perm_addr)) {
|
|
|
|
+ dev_err(&pdev->dev, "Invalid MAC Address\n");
|
|
|
|
+ return -EIO;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Only the PF can support VXLAN and NVGRE offloads */
|
|
|
|
+ if (hw->mac.type != fm10k_mac_pf) {
|
|
|
|
+ netdev->hw_enc_features = 0;
|
|
|
|
+ netdev->features &= ~NETIF_F_GSO_UDP_TUNNEL;
|
|
|
|
+ netdev->hw_features &= ~NETIF_F_GSO_UDP_TUNNEL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* initialize vxlan_port list */
|
|
|
|
+ INIT_LIST_HEAD(&interface->vxlan_port);
|
|
|
|
+
|
|
|
|
+ /* initialize RSS key */
|
|
|
|
+ memcpy(interface->rssrk, seed, sizeof(seed));
|
|
|
|
+
|
|
|
|
+ /* Start off interface as being down */
|
|
|
|
+ set_bit(__FM10K_DOWN, &interface->state);
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void fm10k_slot_warn(struct fm10k_intfc *interface)
|
|
|
|
+{
|
|
|
|
+ struct device *dev = &interface->pdev->dev;
|
|
|
|
+ struct fm10k_hw *hw = &interface->hw;
|
|
|
|
+
|
|
|
|
+ if (hw->mac.ops.is_slot_appropriate(hw))
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ dev_warn(dev,
|
|
|
|
+ "For optimal performance, a %s %s slot is recommended.\n",
|
|
|
|
+ (hw->bus_caps.width == fm10k_bus_width_pcie_x1 ? "x1" :
|
|
|
|
+ hw->bus_caps.width == fm10k_bus_width_pcie_x4 ? "x4" :
|
|
|
|
+ "x8"),
|
|
|
|
+ (hw->bus_caps.speed == fm10k_bus_speed_2500 ? "2.5GT/s" :
|
|
|
|
+ hw->bus_caps.speed == fm10k_bus_speed_5000 ? "5.0GT/s" :
|
|
|
|
+ "8.0GT/s"));
|
|
|
|
+ dev_warn(dev,
|
|
|
|
+ "A slot with more lanes and/or higher speed is suggested.\n");
|
|
|
|
+}
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* fm10k_probe - Device Initialization Routine
|
|
* fm10k_probe - Device Initialization Routine
|
|
* @pdev: PCI device information struct
|
|
* @pdev: PCI device information struct
|
|
@@ -82,6 +227,9 @@ u32 fm10k_read_reg(struct fm10k_hw *hw, int reg)
|
|
static int fm10k_probe(struct pci_dev *pdev,
|
|
static int fm10k_probe(struct pci_dev *pdev,
|
|
const struct pci_device_id *ent)
|
|
const struct pci_device_id *ent)
|
|
{
|
|
{
|
|
|
|
+ struct net_device *netdev;
|
|
|
|
+ struct fm10k_intfc *interface;
|
|
|
|
+ struct fm10k_hw *hw;
|
|
int err;
|
|
int err;
|
|
u64 dma_mask;
|
|
u64 dma_mask;
|
|
|
|
|
|
@@ -122,8 +270,75 @@ static int fm10k_probe(struct pci_dev *pdev,
|
|
pci_set_master(pdev);
|
|
pci_set_master(pdev);
|
|
pci_save_state(pdev);
|
|
pci_save_state(pdev);
|
|
|
|
|
|
|
|
+ netdev = fm10k_alloc_netdev();
|
|
|
|
+ if (!netdev) {
|
|
|
|
+ err = -ENOMEM;
|
|
|
|
+ goto err_alloc_netdev;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ SET_NETDEV_DEV(netdev, &pdev->dev);
|
|
|
|
+
|
|
|
|
+ interface = netdev_priv(netdev);
|
|
|
|
+ pci_set_drvdata(pdev, interface);
|
|
|
|
+
|
|
|
|
+ interface->netdev = netdev;
|
|
|
|
+ interface->pdev = pdev;
|
|
|
|
+ hw = &interface->hw;
|
|
|
|
+
|
|
|
|
+ interface->uc_addr = ioremap(pci_resource_start(pdev, 0),
|
|
|
|
+ FM10K_UC_ADDR_SIZE);
|
|
|
|
+ if (!interface->uc_addr) {
|
|
|
|
+ err = -EIO;
|
|
|
|
+ goto err_ioremap;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = fm10k_sw_init(interface, ent);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_sw_init;
|
|
|
|
+
|
|
|
|
+ /* final check of hardware state before registering the interface */
|
|
|
|
+ err = fm10k_hw_ready(interface);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_register;
|
|
|
|
+
|
|
|
|
+ err = register_netdev(netdev);
|
|
|
|
+ if (err)
|
|
|
|
+ goto err_register;
|
|
|
|
+
|
|
|
|
+ /* carrier off reporting is important to ethtool even BEFORE open */
|
|
|
|
+ netif_carrier_off(netdev);
|
|
|
|
+
|
|
|
|
+ /* stop all the transmit queues from transmitting until link is up */
|
|
|
|
+ netif_tx_stop_all_queues(netdev);
|
|
|
|
+
|
|
|
|
+ /* print bus type/speed/width info */
|
|
|
|
+ dev_info(&pdev->dev, "(PCI Express:%s Width: %s Payload: %s)\n",
|
|
|
|
+ (hw->bus.speed == fm10k_bus_speed_8000 ? "8.0GT/s" :
|
|
|
|
+ hw->bus.speed == fm10k_bus_speed_5000 ? "5.0GT/s" :
|
|
|
|
+ hw->bus.speed == fm10k_bus_speed_2500 ? "2.5GT/s" :
|
|
|
|
+ "Unknown"),
|
|
|
|
+ (hw->bus.width == fm10k_bus_width_pcie_x8 ? "x8" :
|
|
|
|
+ hw->bus.width == fm10k_bus_width_pcie_x4 ? "x4" :
|
|
|
|
+ hw->bus.width == fm10k_bus_width_pcie_x1 ? "x1" :
|
|
|
|
+ "Unknown"),
|
|
|
|
+ (hw->bus.payload == fm10k_bus_payload_128 ? "128B" :
|
|
|
|
+ hw->bus.payload == fm10k_bus_payload_256 ? "256B" :
|
|
|
|
+ hw->bus.payload == fm10k_bus_payload_512 ? "512B" :
|
|
|
|
+ "Unknown"));
|
|
|
|
+
|
|
|
|
+ /* print warning for non-optimal configurations */
|
|
|
|
+ fm10k_slot_warn(interface);
|
|
|
|
+
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+err_register:
|
|
|
|
+err_sw_init:
|
|
|
|
+ iounmap(interface->uc_addr);
|
|
|
|
+err_ioremap:
|
|
|
|
+ free_netdev(netdev);
|
|
|
|
+err_alloc_netdev:
|
|
|
|
+ pci_release_selected_regions(pdev,
|
|
|
|
+ pci_select_bars(pdev, IORESOURCE_MEM));
|
|
err_pci_reg:
|
|
err_pci_reg:
|
|
err_dma:
|
|
err_dma:
|
|
pci_disable_device(pdev);
|
|
pci_disable_device(pdev);
|
|
@@ -141,6 +356,17 @@ err_dma:
|
|
**/
|
|
**/
|
|
static void fm10k_remove(struct pci_dev *pdev)
|
|
static void fm10k_remove(struct pci_dev *pdev)
|
|
{
|
|
{
|
|
|
|
+ struct fm10k_intfc *interface = pci_get_drvdata(pdev);
|
|
|
|
+ struct net_device *netdev = interface->netdev;
|
|
|
|
+
|
|
|
|
+ /* free netdev, this may bounce the interrupts due to setup_tc */
|
|
|
|
+ if (netdev->reg_state == NETREG_REGISTERED)
|
|
|
|
+ unregister_netdev(netdev);
|
|
|
|
+
|
|
|
|
+ iounmap(interface->uc_addr);
|
|
|
|
+
|
|
|
|
+ free_netdev(netdev);
|
|
|
|
+
|
|
pci_release_selected_regions(pdev,
|
|
pci_release_selected_regions(pdev,
|
|
pci_select_bars(pdev, IORESOURCE_MEM));
|
|
pci_select_bars(pdev, IORESOURCE_MEM));
|
|
|
|
|