diff options
author | Alexander Duyck <alexander.h.duyck@intel.com> | 2014-09-20 19:48:10 -0400 |
---|---|---|
committer | Jeff Kirsher <jeffrey.t.kirsher@intel.com> | 2014-09-23 03:59:15 -0700 |
commit | 0e7b364408176362a6370f7182a8fa1c5c4e0628 (patch) | |
tree | c353730fd4c57bdc0f49e7a5c781a0f82c542c68 /drivers/net/ethernet | |
parent | 401b5383c6c9c5b85e1a60bf9de4c7f2dd09d114 (diff) | |
download | linux-stable-0e7b364408176362a6370f7182a8fa1c5c4e0628.tar.gz linux-stable-0e7b364408176362a6370f7182a8fa1c5c4e0628.tar.bz2 linux-stable-0e7b364408176362a6370f7182a8fa1c5c4e0628.zip |
fm10k: Add netdev
Now that we have the ability to configure the basic settings on the device
we can start allocating and configuring a netdev for the interface.
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
Diffstat (limited to 'drivers/net/ethernet')
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k.h | 92 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 98 | ||||
-rw-r--r-- | drivers/net/ethernet/intel/fm10k/fm10k_pci.c | 230 |
4 files changed, 418 insertions, 4 deletions
diff --git a/drivers/net/ethernet/intel/fm10k/Makefile b/drivers/net/ethernet/intel/fm10k/Makefile index 0904bcba963a..ee4e41576fe1 100644 --- a/drivers/net/ethernet/intel/fm10k/Makefile +++ b/drivers/net/ethernet/intel/fm10k/Makefile @@ -28,5 +28,5 @@ obj-$(CONFIG_FM10K) += fm10k.o fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \ - fm10k_pf.o \ + fm10k_netdev.o fm10k_pf.o \ fm10k_mbx.o fm10k_tlv.o diff --git a/drivers/net/ethernet/intel/fm10k/fm10k.h b/drivers/net/ethernet/intel/fm10k/fm10k.h index 3da3a9904d56..b2ee4fce7635 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k.h +++ b/drivers/net/ethernet/intel/fm10k/fm10k.h @@ -27,15 +27,102 @@ #include <linux/if_vlan.h> #include <linux/pci.h> -#include "fm10k_common.h" +#include "fm10k_pf.h" + +#define FM10K_MAX_JUMBO_FRAME_SIZE 15358 /* Maximum supported size 15K */ + +enum fm10k_ring_f_enum { + RING_F_RSS, + RING_F_QOS, + RING_F_ARRAY_SIZE /* must be last in enum set */ +}; + +struct fm10k_ring_feature { + u16 limit; /* upper limit on feature indices */ + u16 indices; /* current value of indices */ + u16 mask; /* Mask used for feature to ring mapping */ + u16 offset; /* offset to start of feature */ +}; + +#define fm10k_vxlan_port_for_each(vp, intfc) \ + list_for_each_entry(vp, &(intfc)->vxlan_port, list) +struct fm10k_vxlan_port { + struct list_head list; + sa_family_t sa_family; + __be16 port; +}; struct fm10k_intfc { + unsigned long active_vlans[BITS_TO_LONGS(VLAN_N_VID)]; + struct net_device *netdev; struct pci_dev *pdev; + unsigned long state; + + u32 flags; +#define FM10K_FLAG_RESET_REQUESTED (u32)(1 << 0) +#define FM10K_FLAG_RSS_FIELD_IPV4_UDP (u32)(1 << 1) +#define FM10K_FLAG_RSS_FIELD_IPV6_UDP (u32)(1 << 2) +#define FM10K_FLAG_RX_TS_ENABLED (u32)(1 << 3) +#define FM10K_FLAG_SWPRI_CONFIG (u32)(1 << 4) + int xcast_mode; + + u64 rx_overrun_pf; + u64 rx_overrun_vf; + struct fm10k_ring_feature ring_feature[RING_F_ARRAY_SIZE]; + + struct fm10k_hw_stats stats; struct fm10k_hw hw; u32 __iomem *uc_addr; + u16 msg_enable; + + u32 reta[FM10K_RETA_SIZE]; + u32 rssrk[FM10K_RSSRK_SIZE]; + + /* VXLAN port tracking information */ + struct list_head vxlan_port; + +#if defined(HAVE_DCBNL_IEEE) && defined(CONFIG_DCB) + u8 pfc_en; +#endif + u8 rx_pause; + + /* GLORT resources in use by PF */ + u16 glort; + u16 glort_count; + + /* VLAN ID for updating multicast/unicast lists */ + u16 vid; +}; + +enum fm10k_state_t { + __FM10K_RESETTING, + __FM10K_DOWN, + __FM10K_MBX_LOCK, + __FM10K_LINK_DOWN, }; +static inline void fm10k_mbx_lock(struct fm10k_intfc *interface) +{ + /* busy loop if we cannot obtain the lock as some calls + * such as ndo_set_rx_mode may be made in atomic context + */ + while (test_and_set_bit(__FM10K_MBX_LOCK, &interface->state)) + udelay(20); +} + +static inline void fm10k_mbx_unlock(struct fm10k_intfc *interface) +{ + /* flush memory to make sure state is correct */ + smp_mb__before_atomic(); + clear_bit(__FM10K_MBX_LOCK, &interface->state); +} + +static inline int fm10k_mbx_trylock(struct fm10k_intfc *interface) +{ + return !test_and_set_bit(__FM10K_MBX_LOCK, &interface->state); +} + /* main */ extern char fm10k_driver_name[]; extern const char fm10k_driver_version[]; @@ -43,4 +130,7 @@ extern const char fm10k_driver_version[]; /* PCI */ int fm10k_register_pci_driver(void); void fm10k_unregister_pci_driver(void); + +/* Netdev */ +struct net_device *fm10k_alloc_netdev(void); #endif /* _FM10K_H_ */ diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c new file mode 100644 index 000000000000..9a4f3a616241 --- /dev/null +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -0,0 +1,98 @@ +/* Intel Ethernet Switch Host Interface Driver + * Copyright(c) 2013 - 2014 Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Contact Information: + * e1000-devel Mailing List <e1000-devel@lists.sourceforge.net> + * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 + */ + +#include "fm10k.h" + +static netdev_tx_t fm10k_xmit_frame(struct sk_buff *skb, struct net_device *dev) +{ + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static int fm10k_change_mtu(struct net_device *dev, int new_mtu) +{ + if (new_mtu < 68 || new_mtu > FM10K_MAX_JUMBO_FRAME_SIZE) + return -EINVAL; + + dev->mtu = new_mtu; + + return 0; +} + +static int fm10k_set_mac(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + s32 err = 0; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + if (!err) { + ether_addr_copy(dev->dev_addr, addr->sa_data); + dev->addr_assign_type &= ~NET_ADDR_RANDOM; + } + + return err; +} + +static void fm10k_set_rx_mode(struct net_device *dev) +{ +} + +static const struct net_device_ops fm10k_netdev_ops = { + .ndo_validate_addr = eth_validate_addr, + .ndo_start_xmit = fm10k_xmit_frame, + .ndo_set_mac_address = fm10k_set_mac, + .ndo_change_mtu = fm10k_change_mtu, + .ndo_set_rx_mode = fm10k_set_rx_mode, +}; + +#define DEFAULT_DEBUG_LEVEL_SHIFT 3 + +struct net_device *fm10k_alloc_netdev(void) +{ + struct fm10k_intfc *interface; + struct net_device *dev; + + dev = alloc_etherdev(sizeof(struct fm10k_intfc)); + if (!dev) + return NULL; + + /* set net device and ethtool ops */ + dev->netdev_ops = &fm10k_netdev_ops; + + /* configure default debug level */ + interface = netdev_priv(dev); + interface->msg_enable = (1 << DEFAULT_DEBUG_LEVEL_SHIFT) - 1; + + /* configure default features */ + dev->features |= NETIF_F_SG; + + /* all features defined to this point should be changeable */ + dev->hw_features |= dev->features; + + /* configure VLAN features */ + dev->vlan_features |= dev->features; + + /* configure tunnel offloads */ + dev->hw_enc_features = NETIF_F_SG; + + return dev; +} diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c index 6d5e33cde6a7..5fcbd1d621b7 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_pci.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_pci.c @@ -22,6 +22,10 @@ #include "fm10k.h" +static const struct fm10k_info *fm10k_info_tbl[] = { + [fm10k_device_pf] = &fm10k_pf_info, +}; + /** * fm10k_pci_tbl - PCI Device ID Table * @@ -32,7 +36,7 @@ * Class, Class Mask, private data (not used) } */ 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 */ { 0, } }; @@ -62,12 +66,153 @@ u32 fm10k_read_reg(struct fm10k_hw *hw, int reg) return ~value; 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; + netif_device_detach(netdev); + netdev_err(netdev, "PCIe link lost, device now detached\n"); + } 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 * @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, const struct pci_device_id *ent) { + struct net_device *netdev; + struct fm10k_intfc *interface; + struct fm10k_hw *hw; int err; u64 dma_mask; @@ -122,8 +270,75 @@ static int fm10k_probe(struct pci_dev *pdev, pci_set_master(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; +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_dma: pci_disable_device(pdev); @@ -141,6 +356,17 @@ err_dma: **/ 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_select_bars(pdev, IORESOURCE_MEM)); |