forked from Minki/linux
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/net-next
Jeff Kirsher says: ==================== Intel Wired LAN Driver Updates 2014-09-23 This patch series adds support for the FM10000 Ethernet switch host interface. The Intel FM10000 Ethernet Switch is a 48-port Ethernet switch supporting both Ethernet ports and PCI Express host interfaces. The fm10k driver provides support for the host interface portion of the switch, both PF and VF. As the host interfaces are directly connected to the switch this results in some significant differences versus a standard network driver. For example there is no PHY or MII on the device. Since packets are delivered directly from the switch to the host interface these are unnecessary. Otherwise most of the functionality is very similar to our other network drivers such as ixgbe or igb. For example we support all the standard network offloads, jumbo frames, SR-IOV (64 VFS), PTP, and some VXLAN and NVGRE offloads. v2: converted dev_consume_skb_any() to dev_kfree_skb_any() fix up PTP code based on feedback from the community v3: converted the use of smb_mb__before_clear_bit() to smb_mb__before_atomic() added vmalloc header to patch 15 added prefetch header to patch 16 ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
b184006050
@ -300,4 +300,23 @@ config I40EVF
|
||||
will be called i40evf. MSI-X interrupt support is required
|
||||
for this driver to work correctly.
|
||||
|
||||
config FM10K
|
||||
tristate "Intel(R) FM10000 Ethernet Switch Host Interface Support"
|
||||
default n
|
||||
depends on PCI_MSI
|
||||
---help---
|
||||
This driver supports Intel(R) FM10000 Ethernet Switch Host
|
||||
Interface. For more information on how to identify your adapter,
|
||||
go to the Adapter & Driver ID Guide at:
|
||||
|
||||
<http://support.intel.com/support/network/sb/CS-008441.htm>
|
||||
|
||||
For general information and support, go to the Intel support
|
||||
website at:
|
||||
|
||||
<http://support.intel.com>
|
||||
|
||||
To compile this driver as a module, choose M here. The module
|
||||
will be called fm10k. MSI-X interrupt support is required
|
||||
|
||||
endif # NET_VENDOR_INTEL
|
||||
|
@ -12,3 +12,4 @@ obj-$(CONFIG_IXGBEVF) += ixgbevf/
|
||||
obj-$(CONFIG_I40E) += i40e/
|
||||
obj-$(CONFIG_IXGB) += ixgb/
|
||||
obj-$(CONFIG_I40EVF) += i40evf/
|
||||
obj-$(CONFIG_FM10K) += fm10k/
|
||||
|
33
drivers/net/ethernet/intel/fm10k/Makefile
Normal file
33
drivers/net/ethernet/intel/fm10k/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
################################################################################
|
||||
#
|
||||
# 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
|
||||
#
|
||||
################################################################################
|
||||
|
||||
#
|
||||
# Makefile for the Intel(R) FM10000 Ethernet Switch Host Interface driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_FM10K) += fm10k.o
|
||||
|
||||
fm10k-objs := fm10k_main.o fm10k_common.o fm10k_pci.o \
|
||||
fm10k_netdev.o fm10k_ethtool.o fm10k_pf.o fm10k_vf.o \
|
||||
fm10k_mbx.o fm10k_iov.o fm10k_tlv.o \
|
||||
fm10k_debugfs.o fm10k_ptp.o fm10k_dcbnl.o
|
534
drivers/net/ethernet/intel/fm10k/fm10k.h
Normal file
534
drivers/net/ethernet/intel/fm10k/fm10k.h
Normal file
@ -0,0 +1,534 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_H_
|
||||
#define _FM10K_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/if_vlan.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/net_tstamp.h>
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
#include "fm10k_pf.h"
|
||||
#include "fm10k_vf.h"
|
||||
|
||||
#define FM10K_MAX_JUMBO_FRAME_SIZE 15358 /* Maximum supported size 15K */
|
||||
|
||||
#define MAX_QUEUES FM10K_MAX_QUEUES_PF
|
||||
|
||||
#define FM10K_MIN_RXD 128
|
||||
#define FM10K_MAX_RXD 4096
|
||||
#define FM10K_DEFAULT_RXD 256
|
||||
|
||||
#define FM10K_MIN_TXD 128
|
||||
#define FM10K_MAX_TXD 4096
|
||||
#define FM10K_DEFAULT_TXD 256
|
||||
#define FM10K_DEFAULT_TX_WORK 256
|
||||
|
||||
#define FM10K_RXBUFFER_256 256
|
||||
#define FM10K_RXBUFFER_16384 16384
|
||||
#define FM10K_RX_HDR_LEN FM10K_RXBUFFER_256
|
||||
#if PAGE_SIZE <= FM10K_RXBUFFER_16384
|
||||
#define FM10K_RX_BUFSZ (PAGE_SIZE / 2)
|
||||
#else
|
||||
#define FM10K_RX_BUFSZ FM10K_RXBUFFER_16384
|
||||
#endif
|
||||
|
||||
/* How many Rx Buffers do we bundle into one write to the hardware ? */
|
||||
#define FM10K_RX_BUFFER_WRITE 16 /* Must be power of 2 */
|
||||
|
||||
#define FM10K_MAX_STATIONS 63
|
||||
struct fm10k_l2_accel {
|
||||
int size;
|
||||
u16 count;
|
||||
u16 dglort;
|
||||
struct rcu_head rcu;
|
||||
struct net_device *macvlan[0];
|
||||
};
|
||||
|
||||
enum fm10k_ring_state_t {
|
||||
__FM10K_TX_DETECT_HANG,
|
||||
__FM10K_HANG_CHECK_ARMED,
|
||||
};
|
||||
|
||||
#define check_for_tx_hang(ring) \
|
||||
test_bit(__FM10K_TX_DETECT_HANG, &(ring)->state)
|
||||
#define set_check_for_tx_hang(ring) \
|
||||
set_bit(__FM10K_TX_DETECT_HANG, &(ring)->state)
|
||||
#define clear_check_for_tx_hang(ring) \
|
||||
clear_bit(__FM10K_TX_DETECT_HANG, &(ring)->state)
|
||||
|
||||
struct fm10k_tx_buffer {
|
||||
struct fm10k_tx_desc *next_to_watch;
|
||||
struct sk_buff *skb;
|
||||
unsigned int bytecount;
|
||||
u16 gso_segs;
|
||||
u16 tx_flags;
|
||||
DEFINE_DMA_UNMAP_ADDR(dma);
|
||||
DEFINE_DMA_UNMAP_LEN(len);
|
||||
};
|
||||
|
||||
struct fm10k_rx_buffer {
|
||||
dma_addr_t dma;
|
||||
struct page *page;
|
||||
u32 page_offset;
|
||||
};
|
||||
|
||||
struct fm10k_queue_stats {
|
||||
u64 packets;
|
||||
u64 bytes;
|
||||
};
|
||||
|
||||
struct fm10k_tx_queue_stats {
|
||||
u64 restart_queue;
|
||||
u64 csum_err;
|
||||
u64 tx_busy;
|
||||
u64 tx_done_old;
|
||||
};
|
||||
|
||||
struct fm10k_rx_queue_stats {
|
||||
u64 alloc_failed;
|
||||
u64 csum_err;
|
||||
u64 errors;
|
||||
};
|
||||
|
||||
struct fm10k_ring {
|
||||
struct fm10k_q_vector *q_vector;/* backpointer to host q_vector */
|
||||
struct net_device *netdev; /* netdev ring belongs to */
|
||||
struct device *dev; /* device for DMA mapping */
|
||||
struct fm10k_l2_accel __rcu *l2_accel; /* L2 acceleration list */
|
||||
void *desc; /* descriptor ring memory */
|
||||
union {
|
||||
struct fm10k_tx_buffer *tx_buffer;
|
||||
struct fm10k_rx_buffer *rx_buffer;
|
||||
};
|
||||
u32 __iomem *tail;
|
||||
unsigned long state;
|
||||
dma_addr_t dma; /* phys. address of descriptor ring */
|
||||
unsigned int size; /* length in bytes */
|
||||
|
||||
u8 queue_index; /* needed for queue management */
|
||||
u8 reg_idx; /* holds the special value that gets
|
||||
* the hardware register offset
|
||||
* associated with this ring, which is
|
||||
* different for DCB and RSS modes
|
||||
*/
|
||||
u8 qos_pc; /* priority class of queue */
|
||||
u16 vid; /* default vlan ID of queue */
|
||||
u16 count; /* amount of descriptors */
|
||||
|
||||
u16 next_to_alloc;
|
||||
u16 next_to_use;
|
||||
u16 next_to_clean;
|
||||
|
||||
struct fm10k_queue_stats stats;
|
||||
struct u64_stats_sync syncp;
|
||||
union {
|
||||
/* Tx */
|
||||
struct fm10k_tx_queue_stats tx_stats;
|
||||
/* Rx */
|
||||
struct {
|
||||
struct fm10k_rx_queue_stats rx_stats;
|
||||
struct sk_buff *skb;
|
||||
};
|
||||
};
|
||||
} ____cacheline_internodealigned_in_smp;
|
||||
|
||||
struct fm10k_ring_container {
|
||||
struct fm10k_ring *ring; /* pointer to linked list of rings */
|
||||
unsigned int total_bytes; /* total bytes processed this int */
|
||||
unsigned int total_packets; /* total packets processed this int */
|
||||
u16 work_limit; /* total work allowed per interrupt */
|
||||
u16 itr; /* interrupt throttle rate value */
|
||||
u8 count; /* total number of rings in vector */
|
||||
};
|
||||
|
||||
#define FM10K_ITR_MAX 0x0FFF /* maximum value for ITR */
|
||||
#define FM10K_ITR_10K 100 /* 100us */
|
||||
#define FM10K_ITR_20K 50 /* 50us */
|
||||
#define FM10K_ITR_ADAPTIVE 0x8000 /* adaptive interrupt moderation flag */
|
||||
|
||||
#define FM10K_ITR_ENABLE (FM10K_ITR_AUTOMASK | FM10K_ITR_MASK_CLEAR)
|
||||
|
||||
static inline struct netdev_queue *txring_txq(const struct fm10k_ring *ring)
|
||||
{
|
||||
return &ring->netdev->_tx[ring->queue_index];
|
||||
}
|
||||
|
||||
/* iterator for handling rings in ring container */
|
||||
#define fm10k_for_each_ring(pos, head) \
|
||||
for (pos = &(head).ring[(head).count]; (--pos) >= (head).ring;)
|
||||
|
||||
#define MAX_Q_VECTORS 256
|
||||
#define MIN_Q_VECTORS 1
|
||||
enum fm10k_non_q_vectors {
|
||||
FM10K_MBX_VECTOR,
|
||||
#define NON_Q_VECTORS_VF NON_Q_VECTORS_PF
|
||||
NON_Q_VECTORS_PF
|
||||
};
|
||||
|
||||
#define NON_Q_VECTORS(hw) (((hw)->mac.type == fm10k_mac_pf) ? \
|
||||
NON_Q_VECTORS_PF : \
|
||||
NON_Q_VECTORS_VF)
|
||||
#define MIN_MSIX_COUNT(hw) (MIN_Q_VECTORS + NON_Q_VECTORS(hw))
|
||||
|
||||
struct fm10k_q_vector {
|
||||
struct fm10k_intfc *interface;
|
||||
u32 __iomem *itr; /* pointer to ITR register for this vector */
|
||||
u16 v_idx; /* index of q_vector within interface array */
|
||||
struct fm10k_ring_container rx, tx;
|
||||
|
||||
struct napi_struct napi;
|
||||
char name[IFNAMSIZ + 9];
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dbg_q_vector;
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
struct rcu_head rcu; /* to avoid race with update stats on free */
|
||||
|
||||
/* for dynamic allocation of rings associated with this q_vector */
|
||||
struct fm10k_ring ring[0] ____cacheline_internodealigned_in_smp;
|
||||
};
|
||||
|
||||
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 */
|
||||
};
|
||||
|
||||
struct fm10k_iov_data {
|
||||
unsigned int num_vfs;
|
||||
unsigned int next_vf_mbx;
|
||||
struct rcu_head rcu;
|
||||
struct fm10k_vf_info vf_info[0];
|
||||
};
|
||||
|
||||
#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 fm10k_l2_accel *l2_accel; /* pointer to L2 acceleration list */
|
||||
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;
|
||||
|
||||
/* Tx fast path data */
|
||||
int num_tx_queues;
|
||||
u16 tx_itr;
|
||||
|
||||
/* Rx fast path data */
|
||||
int num_rx_queues;
|
||||
u16 rx_itr;
|
||||
|
||||
/* TX */
|
||||
struct fm10k_ring *tx_ring[MAX_QUEUES] ____cacheline_aligned_in_smp;
|
||||
|
||||
u64 restart_queue;
|
||||
u64 tx_busy;
|
||||
u64 tx_csum_errors;
|
||||
u64 alloc_failed;
|
||||
u64 rx_csum_errors;
|
||||
u64 rx_errors;
|
||||
|
||||
u64 tx_bytes_nic;
|
||||
u64 tx_packets_nic;
|
||||
u64 rx_bytes_nic;
|
||||
u64 rx_packets_nic;
|
||||
u64 rx_drops_nic;
|
||||
u64 rx_overrun_pf;
|
||||
u64 rx_overrun_vf;
|
||||
u32 tx_timeout_count;
|
||||
|
||||
/* RX */
|
||||
struct fm10k_ring *rx_ring[MAX_QUEUES];
|
||||
|
||||
/* Queueing vectors */
|
||||
struct fm10k_q_vector *q_vector[MAX_Q_VECTORS];
|
||||
struct msix_entry *msix_entries;
|
||||
int num_q_vectors; /* current number of q_vectors for device */
|
||||
struct fm10k_ring_feature ring_feature[RING_F_ARRAY_SIZE];
|
||||
|
||||
/* SR-IOV information management structure */
|
||||
struct fm10k_iov_data *iov_data;
|
||||
|
||||
struct fm10k_hw_stats stats;
|
||||
struct fm10k_hw hw;
|
||||
u32 __iomem *uc_addr;
|
||||
u32 __iomem *sw_addr;
|
||||
u16 msg_enable;
|
||||
u16 tx_ring_count;
|
||||
u16 rx_ring_count;
|
||||
struct timer_list service_timer;
|
||||
struct work_struct service_task;
|
||||
unsigned long next_stats_update;
|
||||
unsigned long next_tx_hang_check;
|
||||
unsigned long last_reset;
|
||||
unsigned long link_down_event;
|
||||
bool host_ready;
|
||||
|
||||
u32 reta[FM10K_RETA_SIZE];
|
||||
u32 rssrk[FM10K_RSSRK_SIZE];
|
||||
|
||||
/* VXLAN port tracking information */
|
||||
struct list_head vxlan_port;
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *dbg_intfc;
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
struct ptp_clock_info ptp_caps;
|
||||
struct ptp_clock *ptp_clock;
|
||||
|
||||
struct sk_buff_head ts_tx_skb_queue;
|
||||
u32 tx_hwtstamp_timeouts;
|
||||
|
||||
struct hwtstamp_config ts_config;
|
||||
/* We are unable to actually adjust the clock beyond the frequency
|
||||
* value. Once the clock is started there is no resetting it. As
|
||||
* such we maintain a separate offset from the actual hardware clock
|
||||
* to allow for offset adjustment.
|
||||
*/
|
||||
s64 ptp_adjust;
|
||||
rwlock_t systime_lock;
|
||||
#ifdef 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_SERVICE_SCHED,
|
||||
__FM10K_SERVICE_DISABLE,
|
||||
__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);
|
||||
}
|
||||
|
||||
/* fm10k_test_staterr - test bits in Rx descriptor status and error fields */
|
||||
static inline __le32 fm10k_test_staterr(union fm10k_rx_desc *rx_desc,
|
||||
const u32 stat_err_bits)
|
||||
{
|
||||
return rx_desc->d.staterr & cpu_to_le32(stat_err_bits);
|
||||
}
|
||||
|
||||
/* fm10k_desc_unused - calculate if we have unused descriptors */
|
||||
static inline u16 fm10k_desc_unused(struct fm10k_ring *ring)
|
||||
{
|
||||
s16 unused = ring->next_to_clean - ring->next_to_use - 1;
|
||||
|
||||
return likely(unused < 0) ? unused + ring->count : unused;
|
||||
}
|
||||
|
||||
#define FM10K_TX_DESC(R, i) \
|
||||
(&(((struct fm10k_tx_desc *)((R)->desc))[i]))
|
||||
#define FM10K_RX_DESC(R, i) \
|
||||
(&(((union fm10k_rx_desc *)((R)->desc))[i]))
|
||||
|
||||
#define FM10K_MAX_TXD_PWR 14
|
||||
#define FM10K_MAX_DATA_PER_TXD (1 << FM10K_MAX_TXD_PWR)
|
||||
|
||||
/* Tx Descriptors needed, worst case */
|
||||
#define TXD_USE_COUNT(S) DIV_ROUND_UP((S), FM10K_MAX_DATA_PER_TXD)
|
||||
#define DESC_NEEDED (MAX_SKB_FRAGS + 4)
|
||||
|
||||
enum fm10k_tx_flags {
|
||||
/* Tx offload flags */
|
||||
FM10K_TX_FLAGS_CSUM = 0x01,
|
||||
};
|
||||
|
||||
/* This structure is stored as little endian values as that is the native
|
||||
* format of the Rx descriptor. The ordering of these fields is reversed
|
||||
* from the actual ftag header to allow for a single bswap to take care
|
||||
* of placing all of the values in network order
|
||||
*/
|
||||
union fm10k_ftag_info {
|
||||
__le64 ftag;
|
||||
struct {
|
||||
/* dglort and sglort combined into a single 32bit desc read */
|
||||
__le32 glort;
|
||||
/* upper 16 bits of vlan are reserved 0 for swpri_type_user */
|
||||
__le32 vlan;
|
||||
} d;
|
||||
struct {
|
||||
__le16 dglort;
|
||||
__le16 sglort;
|
||||
__le16 vlan;
|
||||
__le16 swpri_type_user;
|
||||
} w;
|
||||
};
|
||||
|
||||
struct fm10k_cb {
|
||||
union {
|
||||
__le64 tstamp;
|
||||
unsigned long ts_tx_timeout;
|
||||
};
|
||||
union fm10k_ftag_info fi;
|
||||
};
|
||||
|
||||
#define FM10K_CB(skb) ((struct fm10k_cb *)(skb)->cb)
|
||||
|
||||
/* main */
|
||||
extern char fm10k_driver_name[];
|
||||
extern const char fm10k_driver_version[];
|
||||
int fm10k_init_queueing_scheme(struct fm10k_intfc *interface);
|
||||
void fm10k_clear_queueing_scheme(struct fm10k_intfc *interface);
|
||||
netdev_tx_t fm10k_xmit_frame_ring(struct sk_buff *skb,
|
||||
struct fm10k_ring *tx_ring);
|
||||
void fm10k_tx_timeout_reset(struct fm10k_intfc *interface);
|
||||
bool fm10k_check_tx_hang(struct fm10k_ring *tx_ring);
|
||||
void fm10k_alloc_rx_buffers(struct fm10k_ring *rx_ring, u16 cleaned_count);
|
||||
|
||||
/* PCI */
|
||||
void fm10k_mbx_free_irq(struct fm10k_intfc *);
|
||||
int fm10k_mbx_request_irq(struct fm10k_intfc *);
|
||||
void fm10k_qv_free_irq(struct fm10k_intfc *interface);
|
||||
int fm10k_qv_request_irq(struct fm10k_intfc *interface);
|
||||
int fm10k_register_pci_driver(void);
|
||||
void fm10k_unregister_pci_driver(void);
|
||||
void fm10k_up(struct fm10k_intfc *interface);
|
||||
void fm10k_down(struct fm10k_intfc *interface);
|
||||
void fm10k_update_stats(struct fm10k_intfc *interface);
|
||||
void fm10k_service_event_schedule(struct fm10k_intfc *interface);
|
||||
void fm10k_update_rx_drop_en(struct fm10k_intfc *interface);
|
||||
|
||||
/* Netdev */
|
||||
struct net_device *fm10k_alloc_netdev(void);
|
||||
int fm10k_setup_rx_resources(struct fm10k_ring *);
|
||||
int fm10k_setup_tx_resources(struct fm10k_ring *);
|
||||
void fm10k_free_rx_resources(struct fm10k_ring *);
|
||||
void fm10k_free_tx_resources(struct fm10k_ring *);
|
||||
void fm10k_clean_all_rx_rings(struct fm10k_intfc *);
|
||||
void fm10k_clean_all_tx_rings(struct fm10k_intfc *);
|
||||
void fm10k_unmap_and_free_tx_resource(struct fm10k_ring *,
|
||||
struct fm10k_tx_buffer *);
|
||||
void fm10k_restore_rx_state(struct fm10k_intfc *);
|
||||
void fm10k_reset_rx_state(struct fm10k_intfc *);
|
||||
int fm10k_setup_tc(struct net_device *dev, u8 tc);
|
||||
int fm10k_open(struct net_device *netdev);
|
||||
int fm10k_close(struct net_device *netdev);
|
||||
|
||||
/* Ethtool */
|
||||
void fm10k_set_ethtool_ops(struct net_device *dev);
|
||||
|
||||
/* IOV */
|
||||
s32 fm10k_iov_event(struct fm10k_intfc *interface);
|
||||
s32 fm10k_iov_mbx(struct fm10k_intfc *interface);
|
||||
void fm10k_iov_suspend(struct pci_dev *pdev);
|
||||
int fm10k_iov_resume(struct pci_dev *pdev);
|
||||
void fm10k_iov_disable(struct pci_dev *pdev);
|
||||
int fm10k_iov_configure(struct pci_dev *pdev, int num_vfs);
|
||||
s32 fm10k_iov_update_pvid(struct fm10k_intfc *interface, u16 glort, u16 pvid);
|
||||
int fm10k_ndo_set_vf_mac(struct net_device *netdev, int vf_idx, u8 *mac);
|
||||
int fm10k_ndo_set_vf_vlan(struct net_device *netdev,
|
||||
int vf_idx, u16 vid, u8 qos);
|
||||
int fm10k_ndo_set_vf_bw(struct net_device *netdev, int vf_idx, int rate,
|
||||
int unused);
|
||||
int fm10k_ndo_get_vf_config(struct net_device *netdev,
|
||||
int vf_idx, struct ifla_vf_info *ivi);
|
||||
|
||||
/* DebugFS */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector);
|
||||
void fm10k_dbg_q_vector_exit(struct fm10k_q_vector *q_vector);
|
||||
void fm10k_dbg_intfc_init(struct fm10k_intfc *interface);
|
||||
void fm10k_dbg_intfc_exit(struct fm10k_intfc *interface);
|
||||
void fm10k_dbg_init(void);
|
||||
void fm10k_dbg_exit(void);
|
||||
#else
|
||||
static inline void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector) {}
|
||||
static inline void fm10k_dbg_q_vector_exit(struct fm10k_q_vector *q_vector) {}
|
||||
static inline void fm10k_dbg_intfc_init(struct fm10k_intfc *interface) {}
|
||||
static inline void fm10k_dbg_intfc_exit(struct fm10k_intfc *interface) {}
|
||||
static inline void fm10k_dbg_init(void) {}
|
||||
static inline void fm10k_dbg_exit(void) {}
|
||||
#endif /* CONFIG_DEBUG_FS */
|
||||
|
||||
/* Time Stamping */
|
||||
void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
|
||||
struct skb_shared_hwtstamps *hwtstamp,
|
||||
u64 systime);
|
||||
void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb);
|
||||
void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
|
||||
u64 systime);
|
||||
void fm10k_ts_reset(struct fm10k_intfc *interface);
|
||||
void fm10k_ts_init(struct fm10k_intfc *interface);
|
||||
void fm10k_ts_tx_subtask(struct fm10k_intfc *interface);
|
||||
void fm10k_ptp_register(struct fm10k_intfc *interface);
|
||||
void fm10k_ptp_unregister(struct fm10k_intfc *interface);
|
||||
int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr);
|
||||
int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr);
|
||||
|
||||
/* DCB */
|
||||
void fm10k_dcbnl_set_ops(struct net_device *dev);
|
||||
#endif /* _FM10K_H_ */
|
534
drivers/net/ethernet/intel/fm10k/fm10k_common.c
Normal file
534
drivers/net/ethernet/intel/fm10k/fm10k_common.c
Normal file
@ -0,0 +1,534 @@
|
||||
/* 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_common.h"
|
||||
|
||||
/**
|
||||
* fm10k_get_bus_info_generic - Generic set PCI bus info
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* Gets the PCI bus info (speed, width, type) then calls helper function to
|
||||
* store this data within the fm10k_hw structure.
|
||||
**/
|
||||
s32 fm10k_get_bus_info_generic(struct fm10k_hw *hw)
|
||||
{
|
||||
u16 link_cap, link_status, device_cap, device_control;
|
||||
|
||||
/* Get the maximum link width and speed from PCIe config space */
|
||||
link_cap = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_LINK_CAP);
|
||||
|
||||
switch (link_cap & FM10K_PCIE_LINK_WIDTH) {
|
||||
case FM10K_PCIE_LINK_WIDTH_1:
|
||||
hw->bus_caps.width = fm10k_bus_width_pcie_x1;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_2:
|
||||
hw->bus_caps.width = fm10k_bus_width_pcie_x2;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_4:
|
||||
hw->bus_caps.width = fm10k_bus_width_pcie_x4;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_8:
|
||||
hw->bus_caps.width = fm10k_bus_width_pcie_x8;
|
||||
break;
|
||||
default:
|
||||
hw->bus_caps.width = fm10k_bus_width_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (link_cap & FM10K_PCIE_LINK_SPEED) {
|
||||
case FM10K_PCIE_LINK_SPEED_2500:
|
||||
hw->bus_caps.speed = fm10k_bus_speed_2500;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_SPEED_5000:
|
||||
hw->bus_caps.speed = fm10k_bus_speed_5000;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_SPEED_8000:
|
||||
hw->bus_caps.speed = fm10k_bus_speed_8000;
|
||||
break;
|
||||
default:
|
||||
hw->bus_caps.speed = fm10k_bus_speed_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the PCIe maximum payload size for the PCIe function */
|
||||
device_cap = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_DEV_CAP);
|
||||
|
||||
switch (device_cap & FM10K_PCIE_DEV_CAP_PAYLOAD) {
|
||||
case FM10K_PCIE_DEV_CAP_PAYLOAD_128:
|
||||
hw->bus_caps.payload = fm10k_bus_payload_128;
|
||||
break;
|
||||
case FM10K_PCIE_DEV_CAP_PAYLOAD_256:
|
||||
hw->bus_caps.payload = fm10k_bus_payload_256;
|
||||
break;
|
||||
case FM10K_PCIE_DEV_CAP_PAYLOAD_512:
|
||||
hw->bus_caps.payload = fm10k_bus_payload_512;
|
||||
break;
|
||||
default:
|
||||
hw->bus_caps.payload = fm10k_bus_payload_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the negotiated link width and speed from PCIe config space */
|
||||
link_status = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_LINK_STATUS);
|
||||
|
||||
switch (link_status & FM10K_PCIE_LINK_WIDTH) {
|
||||
case FM10K_PCIE_LINK_WIDTH_1:
|
||||
hw->bus.width = fm10k_bus_width_pcie_x1;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_2:
|
||||
hw->bus.width = fm10k_bus_width_pcie_x2;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_4:
|
||||
hw->bus.width = fm10k_bus_width_pcie_x4;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_WIDTH_8:
|
||||
hw->bus.width = fm10k_bus_width_pcie_x8;
|
||||
break;
|
||||
default:
|
||||
hw->bus.width = fm10k_bus_width_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (link_status & FM10K_PCIE_LINK_SPEED) {
|
||||
case FM10K_PCIE_LINK_SPEED_2500:
|
||||
hw->bus.speed = fm10k_bus_speed_2500;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_SPEED_5000:
|
||||
hw->bus.speed = fm10k_bus_speed_5000;
|
||||
break;
|
||||
case FM10K_PCIE_LINK_SPEED_8000:
|
||||
hw->bus.speed = fm10k_bus_speed_8000;
|
||||
break;
|
||||
default:
|
||||
hw->bus.speed = fm10k_bus_speed_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the negotiated PCIe maximum payload size for the PCIe function */
|
||||
device_control = fm10k_read_pci_cfg_word(hw, FM10K_PCIE_DEV_CTRL);
|
||||
|
||||
switch (device_control & FM10K_PCIE_DEV_CTRL_PAYLOAD) {
|
||||
case FM10K_PCIE_DEV_CTRL_PAYLOAD_128:
|
||||
hw->bus.payload = fm10k_bus_payload_128;
|
||||
break;
|
||||
case FM10K_PCIE_DEV_CTRL_PAYLOAD_256:
|
||||
hw->bus.payload = fm10k_bus_payload_256;
|
||||
break;
|
||||
case FM10K_PCIE_DEV_CTRL_PAYLOAD_512:
|
||||
hw->bus.payload = fm10k_bus_payload_512;
|
||||
break;
|
||||
default:
|
||||
hw->bus.payload = fm10k_bus_payload_unknown;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 fm10k_get_pcie_msix_count_generic(struct fm10k_hw *hw)
|
||||
{
|
||||
u16 msix_count;
|
||||
|
||||
/* read in value from MSI-X capability register */
|
||||
msix_count = fm10k_read_pci_cfg_word(hw, FM10K_PCI_MSIX_MSG_CTRL);
|
||||
msix_count &= FM10K_PCI_MSIX_MSG_CTRL_TBL_SZ_MASK;
|
||||
|
||||
/* MSI-X count is zero-based in HW */
|
||||
msix_count++;
|
||||
|
||||
if (msix_count > FM10K_MAX_MSIX_VECTORS)
|
||||
msix_count = FM10K_MAX_MSIX_VECTORS;
|
||||
|
||||
return msix_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_get_invariants_generic - Inits constant values
|
||||
* @hw: pointer to the hardware structure
|
||||
*
|
||||
* Initialize the common invariants for the device.
|
||||
**/
|
||||
s32 fm10k_get_invariants_generic(struct fm10k_hw *hw)
|
||||
{
|
||||
struct fm10k_mac_info *mac = &hw->mac;
|
||||
|
||||
/* initialize GLORT state to avoid any false hits */
|
||||
mac->dglort_map = FM10K_DGLORTMAP_NONE;
|
||||
|
||||
/* record maximum number of MSI-X vectors */
|
||||
mac->max_msix_vectors = fm10k_get_pcie_msix_count_generic(hw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_start_hw_generic - Prepare hardware for Tx/Rx
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* This function sets the Tx ready flag to indicate that the Tx path has
|
||||
* been initialized.
|
||||
**/
|
||||
s32 fm10k_start_hw_generic(struct fm10k_hw *hw)
|
||||
{
|
||||
/* set flag indicating we are beginning Tx */
|
||||
hw->mac.tx_ready = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_disable_queues_generic - Stop Tx/Rx queues
|
||||
* @hw: pointer to hardware structure
|
||||
* @q_cnt: number of queues to be disabled
|
||||
*
|
||||
**/
|
||||
s32 fm10k_disable_queues_generic(struct fm10k_hw *hw, u16 q_cnt)
|
||||
{
|
||||
u32 reg;
|
||||
u16 i, time;
|
||||
|
||||
/* clear tx_ready to prevent any false hits for reset */
|
||||
hw->mac.tx_ready = false;
|
||||
|
||||
/* clear the enable bit for all rings */
|
||||
for (i = 0; i < q_cnt; i++) {
|
||||
reg = fm10k_read_reg(hw, FM10K_TXDCTL(i));
|
||||
fm10k_write_reg(hw, FM10K_TXDCTL(i),
|
||||
reg & ~FM10K_TXDCTL_ENABLE);
|
||||
reg = fm10k_read_reg(hw, FM10K_RXQCTL(i));
|
||||
fm10k_write_reg(hw, FM10K_RXQCTL(i),
|
||||
reg & ~FM10K_RXQCTL_ENABLE);
|
||||
}
|
||||
|
||||
fm10k_write_flush(hw);
|
||||
udelay(1);
|
||||
|
||||
/* loop through all queues to verify that they are all disabled */
|
||||
for (i = 0, time = FM10K_QUEUE_DISABLE_TIMEOUT; time;) {
|
||||
/* if we are at end of rings all rings are disabled */
|
||||
if (i == q_cnt)
|
||||
return 0;
|
||||
|
||||
/* if queue enables cleared, then move to next ring pair */
|
||||
reg = fm10k_read_reg(hw, FM10K_TXDCTL(i));
|
||||
if (!~reg || !(reg & FM10K_TXDCTL_ENABLE)) {
|
||||
reg = fm10k_read_reg(hw, FM10K_RXQCTL(i));
|
||||
if (!~reg || !(reg & FM10K_RXQCTL_ENABLE)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* decrement time and wait 1 usec */
|
||||
time--;
|
||||
if (time)
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return FM10K_ERR_REQUESTS_PENDING;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_stop_hw_generic - Stop Tx/Rx units
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
**/
|
||||
s32 fm10k_stop_hw_generic(struct fm10k_hw *hw)
|
||||
{
|
||||
return fm10k_disable_queues_generic(hw, hw->mac.max_queues);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_read_hw_stats_32b - Reads value of 32-bit registers
|
||||
* @hw: pointer to the hardware structure
|
||||
* @addr: address of register containing a 32-bit value
|
||||
*
|
||||
* Function reads the content of the register and returns the delta
|
||||
* between the base and the current value.
|
||||
* **/
|
||||
u32 fm10k_read_hw_stats_32b(struct fm10k_hw *hw, u32 addr,
|
||||
struct fm10k_hw_stat *stat)
|
||||
{
|
||||
u32 delta = fm10k_read_reg(hw, addr) - stat->base_l;
|
||||
|
||||
if (FM10K_REMOVED(hw->hw_addr))
|
||||
stat->base_h = 0;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_read_hw_stats_48b - Reads value of 48-bit registers
|
||||
* @hw: pointer to the hardware structure
|
||||
* @addr: address of register containing the lower 32-bit value
|
||||
*
|
||||
* Function reads the content of 2 registers, combined to represent a 48-bit
|
||||
* statistical value. Extra processing is required to handle overflowing.
|
||||
* Finally, a delta value is returned representing the difference between the
|
||||
* values stored in registers and values stored in the statistic counters.
|
||||
* **/
|
||||
static u64 fm10k_read_hw_stats_48b(struct fm10k_hw *hw, u32 addr,
|
||||
struct fm10k_hw_stat *stat)
|
||||
{
|
||||
u32 count_l;
|
||||
u32 count_h;
|
||||
u32 count_tmp;
|
||||
u64 delta;
|
||||
|
||||
count_h = fm10k_read_reg(hw, addr + 1);
|
||||
|
||||
/* Check for overflow */
|
||||
do {
|
||||
count_tmp = count_h;
|
||||
count_l = fm10k_read_reg(hw, addr);
|
||||
count_h = fm10k_read_reg(hw, addr + 1);
|
||||
} while (count_h != count_tmp);
|
||||
|
||||
delta = ((u64)(count_h - stat->base_h) << 32) + count_l;
|
||||
delta -= stat->base_l;
|
||||
|
||||
return delta & FM10K_48_BIT_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_hw_base_48b - Updates 48-bit statistic base value
|
||||
* @stat: pointer to the hardware statistic structure
|
||||
* @delta: value to be updated into the hardware statistic structure
|
||||
*
|
||||
* Function receives a value and determines if an update is required based on
|
||||
* a delta calculation. Only the base value will be updated.
|
||||
**/
|
||||
static void fm10k_update_hw_base_48b(struct fm10k_hw_stat *stat, u64 delta)
|
||||
{
|
||||
if (!delta)
|
||||
return;
|
||||
|
||||
/* update lower 32 bits */
|
||||
delta += stat->base_l;
|
||||
stat->base_l = (u32)delta;
|
||||
|
||||
/* update upper 32 bits */
|
||||
stat->base_h += (u32)(delta >> 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_hw_stats_tx_q - Updates TX queue statistics counters
|
||||
* @hw: pointer to the hardware structure
|
||||
* @q: pointer to the ring of hardware statistics queue
|
||||
* @idx: index pointing to the start of the ring iteration
|
||||
*
|
||||
* Function updates the TX queue statistics counters that are related to the
|
||||
* hardware.
|
||||
**/
|
||||
static void fm10k_update_hw_stats_tx_q(struct fm10k_hw *hw,
|
||||
struct fm10k_hw_stats_q *q,
|
||||
u32 idx)
|
||||
{
|
||||
u32 id_tx, id_tx_prev, tx_packets;
|
||||
u64 tx_bytes = 0;
|
||||
|
||||
/* Retrieve TX Owner Data */
|
||||
id_tx = fm10k_read_reg(hw, FM10K_TXQCTL(idx));
|
||||
|
||||
/* Process TX Ring */
|
||||
do {
|
||||
tx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPTC(idx),
|
||||
&q->tx_packets);
|
||||
|
||||
if (tx_packets)
|
||||
tx_bytes = fm10k_read_hw_stats_48b(hw,
|
||||
FM10K_QBTC_L(idx),
|
||||
&q->tx_bytes);
|
||||
|
||||
/* Re-Check Owner Data */
|
||||
id_tx_prev = id_tx;
|
||||
id_tx = fm10k_read_reg(hw, FM10K_TXQCTL(idx));
|
||||
} while ((id_tx ^ id_tx_prev) & FM10K_TXQCTL_ID_MASK);
|
||||
|
||||
/* drop non-ID bits and set VALID ID bit */
|
||||
id_tx &= FM10K_TXQCTL_ID_MASK;
|
||||
id_tx |= FM10K_STAT_VALID;
|
||||
|
||||
/* update packet counts */
|
||||
if (q->tx_stats_idx == id_tx) {
|
||||
q->tx_packets.count += tx_packets;
|
||||
q->tx_bytes.count += tx_bytes;
|
||||
}
|
||||
|
||||
/* update bases and record ID */
|
||||
fm10k_update_hw_base_32b(&q->tx_packets, tx_packets);
|
||||
fm10k_update_hw_base_48b(&q->tx_bytes, tx_bytes);
|
||||
|
||||
q->tx_stats_idx = id_tx;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_hw_stats_rx_q - Updates RX queue statistics counters
|
||||
* @hw: pointer to the hardware structure
|
||||
* @q: pointer to the ring of hardware statistics queue
|
||||
* @idx: index pointing to the start of the ring iteration
|
||||
*
|
||||
* Function updates the RX queue statistics counters that are related to the
|
||||
* hardware.
|
||||
**/
|
||||
static void fm10k_update_hw_stats_rx_q(struct fm10k_hw *hw,
|
||||
struct fm10k_hw_stats_q *q,
|
||||
u32 idx)
|
||||
{
|
||||
u32 id_rx, id_rx_prev, rx_packets, rx_drops;
|
||||
u64 rx_bytes = 0;
|
||||
|
||||
/* Retrieve RX Owner Data */
|
||||
id_rx = fm10k_read_reg(hw, FM10K_RXQCTL(idx));
|
||||
|
||||
/* Process RX Ring*/
|
||||
do {
|
||||
rx_drops = fm10k_read_hw_stats_32b(hw, FM10K_QPRDC(idx),
|
||||
&q->rx_drops);
|
||||
|
||||
rx_packets = fm10k_read_hw_stats_32b(hw, FM10K_QPRC(idx),
|
||||
&q->rx_packets);
|
||||
|
||||
if (rx_packets)
|
||||
rx_bytes = fm10k_read_hw_stats_48b(hw,
|
||||
FM10K_QBRC_L(idx),
|
||||
&q->rx_bytes);
|
||||
|
||||
/* Re-Check Owner Data */
|
||||
id_rx_prev = id_rx;
|
||||
id_rx = fm10k_read_reg(hw, FM10K_RXQCTL(idx));
|
||||
} while ((id_rx ^ id_rx_prev) & FM10K_RXQCTL_ID_MASK);
|
||||
|
||||
/* drop non-ID bits and set VALID ID bit */
|
||||
id_rx &= FM10K_RXQCTL_ID_MASK;
|
||||
id_rx |= FM10K_STAT_VALID;
|
||||
|
||||
/* update packet counts */
|
||||
if (q->rx_stats_idx == id_rx) {
|
||||
q->rx_drops.count += rx_drops;
|
||||
q->rx_packets.count += rx_packets;
|
||||
q->rx_bytes.count += rx_bytes;
|
||||
}
|
||||
|
||||
/* update bases and record ID */
|
||||
fm10k_update_hw_base_32b(&q->rx_drops, rx_drops);
|
||||
fm10k_update_hw_base_32b(&q->rx_packets, rx_packets);
|
||||
fm10k_update_hw_base_48b(&q->rx_bytes, rx_bytes);
|
||||
|
||||
q->rx_stats_idx = id_rx;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_hw_stats_q - Updates queue statistics counters
|
||||
* @hw: pointer to the hardware structure
|
||||
* @q: pointer to the ring of hardware statistics queue
|
||||
* @idx: index pointing to the start of the ring iteration
|
||||
* @count: number of queues to iterate over
|
||||
*
|
||||
* Function updates the queue statistics counters that are related to the
|
||||
* hardware.
|
||||
**/
|
||||
void fm10k_update_hw_stats_q(struct fm10k_hw *hw, struct fm10k_hw_stats_q *q,
|
||||
u32 idx, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < count; i++, idx++, q++) {
|
||||
fm10k_update_hw_stats_tx_q(hw, q, idx);
|
||||
fm10k_update_hw_stats_rx_q(hw, q, idx);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_unbind_hw_stats_q - Unbind the queue counters from their queues
|
||||
* @hw: pointer to the hardware structure
|
||||
* @q: pointer to the ring of hardware statistics queue
|
||||
* @idx: index pointing to the start of the ring iteration
|
||||
* @count: number of queues to iterate over
|
||||
*
|
||||
* Function invalidates the index values for the queues so any updates that
|
||||
* may have happened are ignored and the base for the queue stats is reset.
|
||||
**/
|
||||
|
||||
void fm10k_unbind_hw_stats_q(struct fm10k_hw_stats_q *q, u32 idx, u32 count)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < count; i++, idx++, q++) {
|
||||
q->rx_stats_idx = 0;
|
||||
q->tx_stats_idx = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_get_host_state_generic - Returns the state of the host
|
||||
* @hw: pointer to hardware structure
|
||||
* @host_ready: pointer to boolean value that will record host state
|
||||
*
|
||||
* This function will check the health of the mailbox and Tx queue 0
|
||||
* in order to determine if we should report that the link is up or not.
|
||||
**/
|
||||
s32 fm10k_get_host_state_generic(struct fm10k_hw *hw, bool *host_ready)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
struct fm10k_mac_info *mac = &hw->mac;
|
||||
s32 ret_val = 0;
|
||||
u32 txdctl = fm10k_read_reg(hw, FM10K_TXDCTL(0));
|
||||
|
||||
/* process upstream mailbox in case interrupts were disabled */
|
||||
mbx->ops.process(hw, mbx);
|
||||
|
||||
/* If Tx is no longer enabled link should come down */
|
||||
if (!(~txdctl) || !(txdctl & FM10K_TXDCTL_ENABLE))
|
||||
mac->get_host_state = true;
|
||||
|
||||
/* exit if not checking for link, or link cannot be changed */
|
||||
if (!mac->get_host_state || !(~txdctl))
|
||||
goto out;
|
||||
|
||||
/* if we somehow dropped the Tx enable we should reset */
|
||||
if (hw->mac.tx_ready && !(txdctl & FM10K_TXDCTL_ENABLE)) {
|
||||
ret_val = FM10K_ERR_RESET_REQUESTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* if Mailbox timed out we should request reset */
|
||||
if (!mbx->timeout) {
|
||||
ret_val = FM10K_ERR_RESET_REQUESTED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* verify Mailbox is still valid */
|
||||
if (!mbx->ops.tx_ready(mbx, FM10K_VFMBX_MSG_MTU))
|
||||
goto out;
|
||||
|
||||
/* interface cannot receive traffic without logical ports */
|
||||
if (mac->dglort_map == FM10K_DGLORTMAP_NONE)
|
||||
goto out;
|
||||
|
||||
/* if we passed all the tests above then the switch is ready and we no
|
||||
* longer need to check for link
|
||||
*/
|
||||
mac->get_host_state = false;
|
||||
|
||||
out:
|
||||
*host_ready = !mac->get_host_state;
|
||||
return ret_val;
|
||||
}
|
65
drivers/net/ethernet/intel/fm10k/fm10k_common.h
Normal file
65
drivers/net/ethernet/intel/fm10k/fm10k_common.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_COMMON_H_
|
||||
#define _FM10K_COMMON_H_
|
||||
|
||||
#include "fm10k_type.h"
|
||||
|
||||
#define FM10K_REMOVED(hw_addr) unlikely(!(hw_addr))
|
||||
|
||||
/* PCI configuration read */
|
||||
u16 fm10k_read_pci_cfg_word(struct fm10k_hw *hw, u32 reg);
|
||||
|
||||
/* read operations, indexed using DWORDS */
|
||||
u32 fm10k_read_reg(struct fm10k_hw *hw, int reg);
|
||||
|
||||
/* write operations, indexed using DWORDS */
|
||||
#define fm10k_write_reg(hw, reg, val) \
|
||||
do { \
|
||||
u32 __iomem *hw_addr = ACCESS_ONCE((hw)->hw_addr); \
|
||||
if (!FM10K_REMOVED(hw_addr)) \
|
||||
writel((val), &hw_addr[(reg)]); \
|
||||
} while (0)
|
||||
|
||||
/* Switch register write operations, index using DWORDS */
|
||||
#define fm10k_write_sw_reg(hw, reg, val) \
|
||||
do { \
|
||||
u32 __iomem *sw_addr = ACCESS_ONCE((hw)->sw_addr); \
|
||||
if (!FM10K_REMOVED(sw_addr)) \
|
||||
writel((val), &sw_addr[(reg)]); \
|
||||
} while (0)
|
||||
|
||||
/* read ctrl register which has no clear on read fields as PCIe flush */
|
||||
#define fm10k_write_flush(hw) fm10k_read_reg((hw), FM10K_CTRL)
|
||||
s32 fm10k_get_bus_info_generic(struct fm10k_hw *hw);
|
||||
s32 fm10k_get_invariants_generic(struct fm10k_hw *hw);
|
||||
s32 fm10k_disable_queues_generic(struct fm10k_hw *hw, u16 q_cnt);
|
||||
s32 fm10k_start_hw_generic(struct fm10k_hw *hw);
|
||||
s32 fm10k_stop_hw_generic(struct fm10k_hw *hw);
|
||||
u32 fm10k_read_hw_stats_32b(struct fm10k_hw *hw, u32 addr,
|
||||
struct fm10k_hw_stat *stat);
|
||||
#define fm10k_update_hw_base_32b(stat, delta) ((stat)->base_l += (delta))
|
||||
void fm10k_update_hw_stats_q(struct fm10k_hw *hw, struct fm10k_hw_stats_q *q,
|
||||
u32 idx, u32 count);
|
||||
#define fm10k_unbind_hw_stats_32b(s) ((s)->base_h = 0)
|
||||
void fm10k_unbind_hw_stats_q(struct fm10k_hw_stats_q *q, u32 idx, u32 count);
|
||||
s32 fm10k_get_host_state_generic(struct fm10k_hw *hw, bool *host_ready);
|
||||
#endif /* _FM10K_COMMON_H_ */
|
174
drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
Normal file
174
drivers/net/ethernet/intel/fm10k/fm10k_dcbnl.c
Normal file
@ -0,0 +1,174 @@
|
||||
/* 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"
|
||||
|
||||
#ifdef CONFIG_DCB
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_getets - get the ETS configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
* @ets: ETS structure to push configuration to
|
||||
**/
|
||||
static int fm10k_dcbnl_ieee_getets(struct net_device *dev, struct ieee_ets *ets)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* we support 8 TCs in all modes */
|
||||
ets->ets_cap = IEEE_8021QAZ_MAX_TCS;
|
||||
ets->cbs = 0;
|
||||
|
||||
/* we only support strict priority and cannot do traffic shaping */
|
||||
memset(ets->tc_tx_bw, 0, sizeof(ets->tc_tx_bw));
|
||||
memset(ets->tc_rx_bw, 0, sizeof(ets->tc_rx_bw));
|
||||
memset(ets->tc_tsa, IEEE_8021QAZ_TSA_STRICT, sizeof(ets->tc_tsa));
|
||||
|
||||
/* populate the prio map based on the netdev */
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
|
||||
ets->prio_tc[i] = netdev_get_prio_tc_map(dev, i);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_setets - set the ETS configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
* @ets: ETS structure to pull configuration from
|
||||
**/
|
||||
static int fm10k_dcbnl_ieee_setets(struct net_device *dev, struct ieee_ets *ets)
|
||||
{
|
||||
u8 num_tc = 0;
|
||||
int i, err;
|
||||
|
||||
/* verify type and determine num_tcs needed */
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++) {
|
||||
if (ets->tc_tx_bw[i] || ets->tc_rx_bw[i])
|
||||
return -EINVAL;
|
||||
if (ets->tc_tsa[i] != IEEE_8021QAZ_TSA_STRICT)
|
||||
return -EINVAL;
|
||||
if (ets->prio_tc[i] > num_tc)
|
||||
num_tc = ets->prio_tc[i];
|
||||
}
|
||||
|
||||
/* if requested TC is greater than 0 then num_tcs is max + 1 */
|
||||
if (num_tc)
|
||||
num_tc++;
|
||||
|
||||
if (num_tc > IEEE_8021QAZ_MAX_TCS)
|
||||
return -EINVAL;
|
||||
|
||||
/* update TC hardware mapping if necessary */
|
||||
if (num_tc != netdev_get_num_tc(dev)) {
|
||||
err = fm10k_setup_tc(dev, num_tc);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* update priority mapping */
|
||||
for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
|
||||
netdev_set_prio_tc_map(dev, i, ets->prio_tc[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_getpfc - get the PFC configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
* @pfc: PFC structure to push configuration to
|
||||
**/
|
||||
static int fm10k_dcbnl_ieee_getpfc(struct net_device *dev, struct ieee_pfc *pfc)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(dev);
|
||||
|
||||
/* record flow control max count and state of TCs */
|
||||
pfc->pfc_cap = IEEE_8021QAZ_MAX_TCS;
|
||||
pfc->pfc_en = interface->pfc_en;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_setpfc - set the PFC configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
* @pfc: PFC structure to pull configuration from
|
||||
**/
|
||||
static int fm10k_dcbnl_ieee_setpfc(struct net_device *dev, struct ieee_pfc *pfc)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(dev);
|
||||
|
||||
/* record PFC configuration to interface */
|
||||
interface->pfc_en = pfc->pfc_en;
|
||||
|
||||
/* if we are running update the drop_en state for all queues */
|
||||
if (netif_running(dev))
|
||||
fm10k_update_rx_drop_en(interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_getdcbx - get the DCBX configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
*
|
||||
* Returns that we support only IEEE DCB for this interface
|
||||
**/
|
||||
static u8 fm10k_dcbnl_getdcbx(struct net_device *dev)
|
||||
{
|
||||
return DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dcbnl_ieee_setdcbx - get the DCBX configuration for the device
|
||||
* @dev: netdev interface for the device
|
||||
* @mode: new mode for this device
|
||||
*
|
||||
* Returns error on attempt to enable anything but IEEE DCB for this interface
|
||||
**/
|
||||
static u8 fm10k_dcbnl_setdcbx(struct net_device *dev, u8 mode)
|
||||
{
|
||||
return (mode != (DCB_CAP_DCBX_HOST | DCB_CAP_DCBX_VER_IEEE)) ? 1 : 0;
|
||||
}
|
||||
|
||||
static const struct dcbnl_rtnl_ops fm10k_dcbnl_ops = {
|
||||
.ieee_getets = fm10k_dcbnl_ieee_getets,
|
||||
.ieee_setets = fm10k_dcbnl_ieee_setets,
|
||||
.ieee_getpfc = fm10k_dcbnl_ieee_getpfc,
|
||||
.ieee_setpfc = fm10k_dcbnl_ieee_setpfc,
|
||||
|
||||
.getdcbx = fm10k_dcbnl_getdcbx,
|
||||
.setdcbx = fm10k_dcbnl_setdcbx,
|
||||
};
|
||||
|
||||
#endif /* CONFIG_DCB */
|
||||
/**
|
||||
* fm10k_dcbnl_set_ops - Configures dcbnl ops pointer for netdev
|
||||
* @dev: netdev interface for the device
|
||||
*
|
||||
* Enables PF for DCB by assigning DCBNL ops pointer.
|
||||
**/
|
||||
void fm10k_dcbnl_set_ops(struct net_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_DCB
|
||||
struct fm10k_intfc *interface = netdev_priv(dev);
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
|
||||
if (hw->mac.type == fm10k_mac_pf)
|
||||
dev->dcbnl_ops = &fm10k_dcbnl_ops;
|
||||
#endif /* CONFIG_DCB */
|
||||
}
|
259
drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
Normal file
259
drivers/net/ethernet/intel/fm10k/fm10k_debugfs.c
Normal file
@ -0,0 +1,259 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
||||
#include "fm10k.h"
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
static struct dentry *dbg_root;
|
||||
|
||||
/* Descriptor Seq Functions */
|
||||
|
||||
static void *fm10k_dbg_desc_seq_start(struct seq_file *s, loff_t *pos)
|
||||
{
|
||||
struct fm10k_ring *ring = s->private;
|
||||
|
||||
return (*pos < ring->count) ? pos : NULL;
|
||||
}
|
||||
|
||||
static void *fm10k_dbg_desc_seq_next(struct seq_file *s, void *v, loff_t *pos)
|
||||
{
|
||||
struct fm10k_ring *ring = s->private;
|
||||
|
||||
return (++(*pos) < ring->count) ? pos : NULL;
|
||||
}
|
||||
|
||||
static void fm10k_dbg_desc_seq_stop(struct seq_file *s, void *v)
|
||||
{
|
||||
/* Do nothing. */
|
||||
}
|
||||
|
||||
static void fm10k_dbg_desc_break(struct seq_file *s, int i)
|
||||
{
|
||||
while (i--)
|
||||
seq_puts(s, "-");
|
||||
|
||||
seq_puts(s, "\n");
|
||||
}
|
||||
|
||||
static int fm10k_dbg_tx_desc_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fm10k_ring *ring = s->private;
|
||||
int i = *(loff_t *)v;
|
||||
static const char tx_desc_hdr[] =
|
||||
"DES BUFFER_ADDRESS LENGTH VLAN MSS HDRLEN FLAGS\n";
|
||||
|
||||
/* Generate header */
|
||||
if (!i) {
|
||||
seq_printf(s, tx_desc_hdr);
|
||||
fm10k_dbg_desc_break(s, sizeof(tx_desc_hdr) - 1);
|
||||
}
|
||||
|
||||
/* Validate descriptor allocation */
|
||||
if (!ring->desc) {
|
||||
seq_printf(s, "%03X Descriptor ring not allocated.\n", i);
|
||||
} else {
|
||||
struct fm10k_tx_desc *txd = FM10K_TX_DESC(ring, i);
|
||||
|
||||
seq_printf(s, "%03X %#018llx %#06x %#06x %#06x %#06x %#04x\n",
|
||||
i, txd->buffer_addr, txd->buflen, txd->vlan,
|
||||
txd->mss, txd->hdrlen, txd->flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fm10k_dbg_rx_desc_seq_show(struct seq_file *s, void *v)
|
||||
{
|
||||
struct fm10k_ring *ring = s->private;
|
||||
int i = *(loff_t *)v;
|
||||
static const char rx_desc_hdr[] =
|
||||
"DES DATA RSS STATERR LENGTH VLAN DGLORT SGLORT TIMESTAMP\n";
|
||||
|
||||
/* Generate header */
|
||||
if (!i) {
|
||||
seq_printf(s, rx_desc_hdr);
|
||||
fm10k_dbg_desc_break(s, sizeof(rx_desc_hdr) - 1);
|
||||
}
|
||||
|
||||
/* Validate descriptor allocation */
|
||||
if (!ring->desc) {
|
||||
seq_printf(s, "%03X Descriptor ring not allocated.\n", i);
|
||||
} else {
|
||||
union fm10k_rx_desc *rxd = FM10K_RX_DESC(ring, i);
|
||||
|
||||
seq_printf(s,
|
||||
"%03X %#010x %#010x %#010x %#06x %#06x %#06x %#06x %#018llx\n",
|
||||
i, rxd->d.data, rxd->d.rss, rxd->d.staterr,
|
||||
rxd->w.length, rxd->w.vlan, rxd->w.dglort,
|
||||
rxd->w.sglort, rxd->q.timestamp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct seq_operations fm10k_dbg_tx_desc_seq_ops = {
|
||||
.start = fm10k_dbg_desc_seq_start,
|
||||
.next = fm10k_dbg_desc_seq_next,
|
||||
.stop = fm10k_dbg_desc_seq_stop,
|
||||
.show = fm10k_dbg_tx_desc_seq_show,
|
||||
};
|
||||
|
||||
static const struct seq_operations fm10k_dbg_rx_desc_seq_ops = {
|
||||
.start = fm10k_dbg_desc_seq_start,
|
||||
.next = fm10k_dbg_desc_seq_next,
|
||||
.stop = fm10k_dbg_desc_seq_stop,
|
||||
.show = fm10k_dbg_rx_desc_seq_show,
|
||||
};
|
||||
|
||||
static int fm10k_dbg_desc_open(struct inode *inode, struct file *filep)
|
||||
{
|
||||
struct fm10k_ring *ring = inode->i_private;
|
||||
struct fm10k_q_vector *q_vector = ring->q_vector;
|
||||
const struct seq_operations *desc_seq_ops;
|
||||
int err;
|
||||
|
||||
if (ring < q_vector->rx.ring)
|
||||
desc_seq_ops = &fm10k_dbg_tx_desc_seq_ops;
|
||||
else
|
||||
desc_seq_ops = &fm10k_dbg_rx_desc_seq_ops;
|
||||
|
||||
err = seq_open(filep, desc_seq_ops);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
((struct seq_file *)filep->private_data)->private = ring;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations fm10k_dbg_desc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = fm10k_dbg_desc_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* fm10k_dbg_q_vector_init - setup debugfs for the q_vectors
|
||||
* @q_vector: q_vector to allocate directories for
|
||||
*
|
||||
* A folder is created for each q_vector found. In each q_vector
|
||||
* folder, a debugfs file is created for each tx and rx ring
|
||||
* allocated to the q_vector.
|
||||
**/
|
||||
void fm10k_dbg_q_vector_init(struct fm10k_q_vector *q_vector)
|
||||
{
|
||||
struct fm10k_intfc *interface = q_vector->interface;
|
||||
char name[16];
|
||||
int i;
|
||||
|
||||
if (!interface->dbg_intfc)
|
||||
return;
|
||||
|
||||
/* Generate a folder for each q_vector */
|
||||
sprintf(name, "q_vector.%03d", q_vector->v_idx);
|
||||
|
||||
q_vector->dbg_q_vector = debugfs_create_dir(name, interface->dbg_intfc);
|
||||
if (!q_vector->dbg_q_vector)
|
||||
return;
|
||||
|
||||
/* Generate a file for each rx ring in the q_vector */
|
||||
for (i = 0; i < q_vector->tx.count; i++) {
|
||||
struct fm10k_ring *ring = &q_vector->tx.ring[i];
|
||||
|
||||
sprintf(name, "tx_ring.%03d", ring->queue_index);
|
||||
|
||||
debugfs_create_file(name, 0600,
|
||||
q_vector->dbg_q_vector, ring,
|
||||
&fm10k_dbg_desc_fops);
|
||||
}
|
||||
|
||||
/* Generate a file for each rx ring in the q_vector */
|
||||
for (i = 0; i < q_vector->rx.count; i++) {
|
||||
struct fm10k_ring *ring = &q_vector->rx.ring[i];
|
||||
|
||||
sprintf(name, "rx_ring.%03d", ring->queue_index);
|
||||
|
||||
debugfs_create_file(name, 0600,
|
||||
q_vector->dbg_q_vector, ring,
|
||||
&fm10k_dbg_desc_fops);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dbg_free_q_vector_dir - setup debugfs for the q_vectors
|
||||
* @q_vector: q_vector to allocate directories for
|
||||
**/
|
||||
void fm10k_dbg_q_vector_exit(struct fm10k_q_vector *q_vector)
|
||||
{
|
||||
struct fm10k_intfc *interface = q_vector->interface;
|
||||
|
||||
if (interface->dbg_intfc)
|
||||
debugfs_remove_recursive(q_vector->dbg_q_vector);
|
||||
q_vector->dbg_q_vector = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dbg_intfc_init - setup the debugfs directory for the intferface
|
||||
* @interface: the interface that is starting up
|
||||
**/
|
||||
|
||||
void fm10k_dbg_intfc_init(struct fm10k_intfc *interface)
|
||||
{
|
||||
const char *name = pci_name(interface->pdev);
|
||||
|
||||
if (dbg_root)
|
||||
interface->dbg_intfc = debugfs_create_dir(name, dbg_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dbg_intfc_exit - clean out the interface's debugfs entries
|
||||
* @interface: the interface that is stopping
|
||||
**/
|
||||
void fm10k_dbg_intfc_exit(struct fm10k_intfc *interface)
|
||||
{
|
||||
if (dbg_root)
|
||||
debugfs_remove_recursive(interface->dbg_intfc);
|
||||
interface->dbg_intfc = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dbg_init - start up debugfs for the driver
|
||||
**/
|
||||
void fm10k_dbg_init(void)
|
||||
{
|
||||
dbg_root = debugfs_create_dir(fm10k_driver_name, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_dbg_exit - clean out the driver's debugfs entries
|
||||
**/
|
||||
void fm10k_dbg_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(dbg_root);
|
||||
dbg_root = NULL;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_FS */
|
1069
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
Normal file
1069
drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c
Normal file
File diff suppressed because it is too large
Load Diff
536
drivers/net/ethernet/intel/fm10k/fm10k_iov.c
Normal file
536
drivers/net/ethernet/intel/fm10k/fm10k_iov.c
Normal file
@ -0,0 +1,536 @@
|
||||
/* 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"
|
||||
#include "fm10k_vf.h"
|
||||
#include "fm10k_pf.h"
|
||||
|
||||
static s32 fm10k_iov_msg_error(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *mbx)
|
||||
{
|
||||
struct fm10k_vf_info *vf_info = (struct fm10k_vf_info *)mbx;
|
||||
struct fm10k_intfc *interface = hw->back;
|
||||
struct pci_dev *pdev = interface->pdev;
|
||||
|
||||
dev_err(&pdev->dev, "Unknown message ID %u on VF %d\n",
|
||||
**results & FM10K_TLV_ID_MASK, vf_info->vf_idx);
|
||||
|
||||
return fm10k_tlv_msg_error(hw, results, mbx);
|
||||
}
|
||||
|
||||
static const struct fm10k_msg_data iov_mbx_data[] = {
|
||||
FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
|
||||
FM10K_VF_MSG_MSIX_HANDLER(fm10k_iov_msg_msix_pf),
|
||||
FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_iov_msg_mac_vlan_pf),
|
||||
FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_iov_msg_lport_state_pf),
|
||||
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_iov_msg_error),
|
||||
};
|
||||
|
||||
s32 fm10k_iov_event(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
struct fm10k_iov_data *iov_data;
|
||||
s64 mbicr, vflre;
|
||||
int i;
|
||||
|
||||
/* if there is no iov_data then there is no mailboxes to process */
|
||||
if (!ACCESS_ONCE(interface->iov_data))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
iov_data = interface->iov_data;
|
||||
|
||||
/* check again now that we are in the RCU block */
|
||||
if (!iov_data)
|
||||
goto read_unlock;
|
||||
|
||||
if (!(fm10k_read_reg(hw, FM10K_EICR) & FM10K_EICR_VFLR))
|
||||
goto process_mbx;
|
||||
|
||||
/* read VFLRE to determine if any VFs have been reset */
|
||||
do {
|
||||
vflre = fm10k_read_reg(hw, FM10K_PFVFLRE(0));
|
||||
vflre <<= 32;
|
||||
vflre |= fm10k_read_reg(hw, FM10K_PFVFLRE(1));
|
||||
vflre = (vflre << 32) | (vflre >> 32);
|
||||
vflre |= fm10k_read_reg(hw, FM10K_PFVFLRE(0));
|
||||
|
||||
i = iov_data->num_vfs;
|
||||
|
||||
for (vflre <<= 64 - i; vflre && i--; vflre += vflre) {
|
||||
struct fm10k_vf_info *vf_info = &iov_data->vf_info[i];
|
||||
|
||||
if (vflre >= 0)
|
||||
continue;
|
||||
|
||||
hw->iov.ops.reset_resources(hw, vf_info);
|
||||
vf_info->mbx.ops.connect(hw, &vf_info->mbx);
|
||||
}
|
||||
} while (i != iov_data->num_vfs);
|
||||
|
||||
process_mbx:
|
||||
/* read MBICR to determine which VFs require attention */
|
||||
mbicr = fm10k_read_reg(hw, FM10K_MBICR(1));
|
||||
mbicr <<= 32;
|
||||
mbicr |= fm10k_read_reg(hw, FM10K_MBICR(0));
|
||||
|
||||
i = iov_data->next_vf_mbx ? : iov_data->num_vfs;
|
||||
|
||||
for (mbicr <<= 64 - i; i--; mbicr += mbicr) {
|
||||
struct fm10k_mbx_info *mbx = &iov_data->vf_info[i].mbx;
|
||||
|
||||
if (mbicr >= 0)
|
||||
continue;
|
||||
|
||||
if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU))
|
||||
break;
|
||||
|
||||
mbx->ops.process(hw, mbx);
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
iov_data->next_vf_mbx = i + 1;
|
||||
} else if (iov_data->next_vf_mbx) {
|
||||
iov_data->next_vf_mbx = 0;
|
||||
goto process_mbx;
|
||||
}
|
||||
read_unlock:
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 fm10k_iov_mbx(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
struct fm10k_iov_data *iov_data;
|
||||
int i;
|
||||
|
||||
/* if there is no iov_data then there is no mailboxes to process */
|
||||
if (!ACCESS_ONCE(interface->iov_data))
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
iov_data = interface->iov_data;
|
||||
|
||||
/* check again now that we are in the RCU block */
|
||||
if (!iov_data)
|
||||
goto read_unlock;
|
||||
|
||||
/* lock the mailbox for transmit and receive */
|
||||
fm10k_mbx_lock(interface);
|
||||
|
||||
process_mbx:
|
||||
for (i = iov_data->next_vf_mbx ? : iov_data->num_vfs; i--;) {
|
||||
struct fm10k_vf_info *vf_info = &iov_data->vf_info[i];
|
||||
struct fm10k_mbx_info *mbx = &vf_info->mbx;
|
||||
u16 glort = vf_info->glort;
|
||||
|
||||
/* verify port mapping is valid, if not reset port */
|
||||
if (vf_info->vf_flags && !fm10k_glort_valid_pf(hw, glort))
|
||||
hw->iov.ops.reset_lport(hw, vf_info);
|
||||
|
||||
/* reset VFs that have mailbox timed out */
|
||||
if (!mbx->timeout) {
|
||||
hw->iov.ops.reset_resources(hw, vf_info);
|
||||
mbx->ops.connect(hw, mbx);
|
||||
}
|
||||
|
||||
/* no work pending, then just continue */
|
||||
if (mbx->ops.tx_complete(mbx) && !mbx->ops.rx_ready(mbx))
|
||||
continue;
|
||||
|
||||
/* guarantee we have free space in the SM mailbox */
|
||||
if (!hw->mbx.ops.tx_ready(&hw->mbx, FM10K_VFMBX_MSG_MTU))
|
||||
break;
|
||||
|
||||
/* cleanup mailbox and process received messages */
|
||||
mbx->ops.process(hw, mbx);
|
||||
}
|
||||
|
||||
if (i >= 0) {
|
||||
iov_data->next_vf_mbx = i + 1;
|
||||
} else if (iov_data->next_vf_mbx) {
|
||||
iov_data->next_vf_mbx = 0;
|
||||
goto process_mbx;
|
||||
}
|
||||
|
||||
/* free the lock */
|
||||
fm10k_mbx_unlock(interface);
|
||||
|
||||
read_unlock:
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fm10k_iov_suspend(struct pci_dev *pdev)
|
||||
{
|
||||
struct fm10k_intfc *interface = pci_get_drvdata(pdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
int num_vfs, i;
|
||||
|
||||
/* pull out num_vfs from iov_data */
|
||||
num_vfs = iov_data ? iov_data->num_vfs : 0;
|
||||
|
||||
/* shut down queue mapping for VFs */
|
||||
fm10k_write_reg(hw, FM10K_DGLORTMAP(fm10k_dglort_vf_rss),
|
||||
FM10K_DGLORTMAP_NONE);
|
||||
|
||||
/* Stop any active VFs and reset their resources */
|
||||
for (i = 0; i < num_vfs; i++) {
|
||||
struct fm10k_vf_info *vf_info = &iov_data->vf_info[i];
|
||||
|
||||
hw->iov.ops.reset_resources(hw, vf_info);
|
||||
hw->iov.ops.reset_lport(hw, vf_info);
|
||||
}
|
||||
}
|
||||
|
||||
int fm10k_iov_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct fm10k_intfc *interface = pci_get_drvdata(pdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_dglort_cfg dglort = { 0 };
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
int num_vfs, i;
|
||||
|
||||
/* pull out num_vfs from iov_data */
|
||||
num_vfs = iov_data ? iov_data->num_vfs : 0;
|
||||
|
||||
/* return error if iov_data is not already populated */
|
||||
if (!iov_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* allocate hardware resources for the VFs */
|
||||
hw->iov.ops.assign_resources(hw, num_vfs, num_vfs);
|
||||
|
||||
/* configure DGLORT mapping for RSS */
|
||||
dglort.glort = hw->mac.dglort_map & FM10K_DGLORTMAP_NONE;
|
||||
dglort.idx = fm10k_dglort_vf_rss;
|
||||
dglort.inner_rss = 1;
|
||||
dglort.rss_l = fls(fm10k_queues_per_pool(hw) - 1);
|
||||
dglort.queue_b = fm10k_vf_queue_index(hw, 0);
|
||||
dglort.vsi_l = fls(hw->iov.total_vfs - 1);
|
||||
dglort.vsi_b = 1;
|
||||
|
||||
hw->mac.ops.configure_dglort_map(hw, &dglort);
|
||||
|
||||
/* assign resources to the device */
|
||||
for (i = 0; i < num_vfs; i++) {
|
||||
struct fm10k_vf_info *vf_info = &iov_data->vf_info[i];
|
||||
|
||||
/* allocate all but the last GLORT to the VFs */
|
||||
if (i == ((~hw->mac.dglort_map) >> FM10K_DGLORTMAP_MASK_SHIFT))
|
||||
break;
|
||||
|
||||
/* assign GLORT to VF, and restrict it to multicast */
|
||||
hw->iov.ops.set_lport(hw, vf_info, i,
|
||||
FM10K_VF_FLAG_MULTI_CAPABLE);
|
||||
|
||||
/* assign our default vid to the VF following reset */
|
||||
vf_info->sw_vid = hw->mac.default_vid;
|
||||
|
||||
/* mailbox is disconnected so we don't send a message */
|
||||
hw->iov.ops.assign_default_mac_vlan(hw, vf_info);
|
||||
|
||||
/* now we are ready so we can connect */
|
||||
vf_info->mbx.ops.connect(hw, &vf_info->mbx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 fm10k_iov_update_pvid(struct fm10k_intfc *interface, u16 glort, u16 pvid)
|
||||
{
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
struct fm10k_vf_info *vf_info;
|
||||
u16 vf_idx = (glort - hw->mac.dglort_map) & FM10K_DGLORTMAP_NONE;
|
||||
|
||||
/* no IOV support, not our message to process */
|
||||
if (!iov_data)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* glort outside our range, not our message to process */
|
||||
if (vf_idx >= iov_data->num_vfs)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* determine if an update has occured and if so notify the VF */
|
||||
vf_info = &iov_data->vf_info[vf_idx];
|
||||
if (vf_info->sw_vid != pvid) {
|
||||
vf_info->sw_vid = pvid;
|
||||
hw->iov.ops.assign_default_mac_vlan(hw, vf_info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fm10k_iov_free_data(struct pci_dev *pdev)
|
||||
{
|
||||
struct fm10k_intfc *interface = pci_get_drvdata(pdev);
|
||||
|
||||
if (!interface->iov_data)
|
||||
return;
|
||||
|
||||
/* reclaim hardware resources */
|
||||
fm10k_iov_suspend(pdev);
|
||||
|
||||
/* drop iov_data from interface */
|
||||
kfree_rcu(interface->iov_data, rcu);
|
||||
interface->iov_data = NULL;
|
||||
}
|
||||
|
||||
static s32 fm10k_iov_alloc_data(struct pci_dev *pdev, int num_vfs)
|
||||
{
|
||||
struct fm10k_intfc *interface = pci_get_drvdata(pdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
size_t size;
|
||||
int i, err;
|
||||
|
||||
/* return error if iov_data is already populated */
|
||||
if (iov_data)
|
||||
return -EBUSY;
|
||||
|
||||
/* The PF should always be able to assign resources */
|
||||
if (!hw->iov.ops.assign_resources)
|
||||
return -ENODEV;
|
||||
|
||||
/* nothing to do if no VFs are requested */
|
||||
if (!num_vfs)
|
||||
return 0;
|
||||
|
||||
/* allocate memory for VF storage */
|
||||
size = offsetof(struct fm10k_iov_data, vf_info[num_vfs]);
|
||||
iov_data = kzalloc(size, GFP_KERNEL);
|
||||
if (!iov_data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* record number of VFs */
|
||||
iov_data->num_vfs = num_vfs;
|
||||
|
||||
/* loop through vf_info structures initializing each entry */
|
||||
for (i = 0; i < num_vfs; i++) {
|
||||
struct fm10k_vf_info *vf_info = &iov_data->vf_info[i];
|
||||
|
||||
/* Record VF VSI value */
|
||||
vf_info->vsi = i + 1;
|
||||
vf_info->vf_idx = i;
|
||||
|
||||
/* initialize mailbox memory */
|
||||
err = fm10k_pfvf_mbx_init(hw, &vf_info->mbx, iov_mbx_data, i);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to initialize SR-IOV mailbox\n");
|
||||
kfree(iov_data);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* assign iov_data to interface */
|
||||
interface->iov_data = iov_data;
|
||||
|
||||
/* allocate hardware resources for the VFs */
|
||||
fm10k_iov_resume(pdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fm10k_iov_disable(struct pci_dev *pdev)
|
||||
{
|
||||
if (pci_num_vf(pdev) && pci_vfs_assigned(pdev))
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot disable SR-IOV while VFs are assigned\n");
|
||||
else
|
||||
pci_disable_sriov(pdev);
|
||||
|
||||
fm10k_iov_free_data(pdev);
|
||||
}
|
||||
|
||||
static void fm10k_disable_aer_comp_abort(struct pci_dev *pdev)
|
||||
{
|
||||
u32 err_sev;
|
||||
int pos;
|
||||
|
||||
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
pci_read_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, &err_sev);
|
||||
err_sev &= ~PCI_ERR_UNC_COMP_ABORT;
|
||||
pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, err_sev);
|
||||
}
|
||||
|
||||
int fm10k_iov_configure(struct pci_dev *pdev, int num_vfs)
|
||||
{
|
||||
int current_vfs = pci_num_vf(pdev);
|
||||
int err = 0;
|
||||
|
||||
if (current_vfs && pci_vfs_assigned(pdev)) {
|
||||
dev_err(&pdev->dev,
|
||||
"Cannot modify SR-IOV while VFs are assigned\n");
|
||||
num_vfs = current_vfs;
|
||||
} else {
|
||||
pci_disable_sriov(pdev);
|
||||
fm10k_iov_free_data(pdev);
|
||||
}
|
||||
|
||||
/* allocate resources for the VFs */
|
||||
err = fm10k_iov_alloc_data(pdev, num_vfs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* allocate VFs if not already allocated */
|
||||
if (num_vfs && (num_vfs != current_vfs)) {
|
||||
/* Disable completer abort error reporting as
|
||||
* the VFs can trigger this any time they read a queue
|
||||
* that they don't own.
|
||||
*/
|
||||
fm10k_disable_aer_comp_abort(pdev);
|
||||
|
||||
err = pci_enable_sriov(pdev, num_vfs);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Enable PCI SR-IOV failed: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return num_vfs;
|
||||
}
|
||||
|
||||
int fm10k_ndo_set_vf_mac(struct net_device *netdev, int vf_idx, u8 *mac)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
struct fm10k_vf_info *vf_info;
|
||||
|
||||
/* verify SR-IOV is active and that vf idx is valid */
|
||||
if (!iov_data || vf_idx >= iov_data->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
/* verify MAC addr is valid */
|
||||
if (!is_zero_ether_addr(mac) && !is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
||||
|
||||
/* record new MAC address */
|
||||
vf_info = &iov_data->vf_info[vf_idx];
|
||||
ether_addr_copy(vf_info->mac, mac);
|
||||
|
||||
/* assigning the MAC will send a mailbox message so lock is needed */
|
||||
fm10k_mbx_lock(interface);
|
||||
|
||||
/* assign MAC address to VF */
|
||||
hw->iov.ops.assign_default_mac_vlan(hw, vf_info);
|
||||
|
||||
fm10k_mbx_unlock(interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fm10k_ndo_set_vf_vlan(struct net_device *netdev, int vf_idx, u16 vid,
|
||||
u8 qos)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
struct fm10k_vf_info *vf_info;
|
||||
|
||||
/* verify SR-IOV is active and that vf idx is valid */
|
||||
if (!iov_data || vf_idx >= iov_data->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
/* QOS is unsupported and VLAN IDs accepted range 0-4094 */
|
||||
if (qos || (vid > (VLAN_VID_MASK - 1)))
|
||||
return -EINVAL;
|
||||
|
||||
vf_info = &iov_data->vf_info[vf_idx];
|
||||
|
||||
/* exit if there is nothing to do */
|
||||
if (vf_info->pf_vid == vid)
|
||||
return 0;
|
||||
|
||||
/* record default VLAN ID for VF */
|
||||
vf_info->pf_vid = vid;
|
||||
|
||||
/* assigning the VLAN will send a mailbox message so lock is needed */
|
||||
fm10k_mbx_lock(interface);
|
||||
|
||||
/* Clear the VLAN table for the VF */
|
||||
hw->mac.ops.update_vlan(hw, FM10K_VLAN_ALL, vf_info->vsi, false);
|
||||
|
||||
/* Update VF assignment and trigger reset */
|
||||
hw->iov.ops.assign_default_mac_vlan(hw, vf_info);
|
||||
|
||||
fm10k_mbx_unlock(interface);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fm10k_ndo_set_vf_bw(struct net_device *netdev, int vf_idx, int unused,
|
||||
int rate)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
|
||||
/* verify SR-IOV is active and that vf idx is valid */
|
||||
if (!iov_data || vf_idx >= iov_data->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
/* rate limit cannot be less than 10Mbs or greater than link speed */
|
||||
if (rate && ((rate < FM10K_VF_TC_MIN) || rate > FM10K_VF_TC_MAX))
|
||||
return -EINVAL;
|
||||
|
||||
/* store values */
|
||||
iov_data->vf_info[vf_idx].rate = rate;
|
||||
|
||||
/* update hardware configuration */
|
||||
hw->iov.ops.configure_tc(hw, vf_idx, rate);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fm10k_ndo_get_vf_config(struct net_device *netdev,
|
||||
int vf_idx, struct ifla_vf_info *ivi)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct fm10k_iov_data *iov_data = interface->iov_data;
|
||||
struct fm10k_vf_info *vf_info;
|
||||
|
||||
/* verify SR-IOV is active and that vf idx is valid */
|
||||
if (!iov_data || vf_idx >= iov_data->num_vfs)
|
||||
return -EINVAL;
|
||||
|
||||
vf_info = &iov_data->vf_info[vf_idx];
|
||||
|
||||
ivi->vf = vf_idx;
|
||||
ivi->max_tx_rate = vf_info->rate;
|
||||
ivi->min_tx_rate = 0;
|
||||
ether_addr_copy(ivi->mac, vf_info->mac);
|
||||
ivi->vlan = vf_info->pf_vid;
|
||||
ivi->qos = 0;
|
||||
|
||||
return 0;
|
||||
}
|
1979
drivers/net/ethernet/intel/fm10k/fm10k_main.c
Normal file
1979
drivers/net/ethernet/intel/fm10k/fm10k_main.c
Normal file
File diff suppressed because it is too large
Load Diff
2125
drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
Normal file
2125
drivers/net/ethernet/intel/fm10k/fm10k_mbx.c
Normal file
File diff suppressed because it is too large
Load Diff
307
drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
Normal file
307
drivers/net/ethernet/intel/fm10k/fm10k_mbx.h
Normal file
@ -0,0 +1,307 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_MBX_H_
|
||||
#define _FM10K_MBX_H_
|
||||
|
||||
/* forward declaration */
|
||||
struct fm10k_mbx_info;
|
||||
|
||||
#include "fm10k_type.h"
|
||||
#include "fm10k_tlv.h"
|
||||
|
||||
/* PF Mailbox Registers */
|
||||
#define FM10K_MBMEM(_n) ((_n) + 0x18000)
|
||||
#define FM10K_MBMEM_VF(_n, _m) (((_n) * 0x10) + (_m) + 0x18000)
|
||||
#define FM10K_MBMEM_SM(_n) ((_n) + 0x18400)
|
||||
#define FM10K_MBMEM_PF(_n) ((_n) + 0x18600)
|
||||
/* XOR provides means of switching from Tx to Rx FIFO */
|
||||
#define FM10K_MBMEM_PF_XOR (FM10K_MBMEM_SM(0) ^ FM10K_MBMEM_PF(0))
|
||||
#define FM10K_MBX(_n) ((_n) + 0x18800)
|
||||
#define FM10K_MBX_REQ 0x00000002
|
||||
#define FM10K_MBX_ACK 0x00000004
|
||||
#define FM10K_MBX_REQ_INTERRUPT 0x00000008
|
||||
#define FM10K_MBX_ACK_INTERRUPT 0x00000010
|
||||
#define FM10K_MBX_INTERRUPT_ENABLE 0x00000020
|
||||
#define FM10K_MBX_INTERRUPT_DISABLE 0x00000040
|
||||
#define FM10K_MBICR(_n) ((_n) + 0x18840)
|
||||
#define FM10K_GMBX 0x18842
|
||||
|
||||
/* VF Mailbox Registers */
|
||||
#define FM10K_VFMBX 0x00010
|
||||
#define FM10K_VFMBMEM(_n) ((_n) + 0x00020)
|
||||
#define FM10K_VFMBMEM_LEN 16
|
||||
#define FM10K_VFMBMEM_VF_XOR (FM10K_VFMBMEM_LEN / 2)
|
||||
|
||||
/* Delays/timeouts */
|
||||
#define FM10K_MBX_DISCONNECT_TIMEOUT 500
|
||||
#define FM10K_MBX_POLL_DELAY 19
|
||||
#define FM10K_MBX_INT_DELAY 20
|
||||
|
||||
/* PF/VF Mailbox state machine
|
||||
*
|
||||
* +----------+ connect() +----------+
|
||||
* | CLOSED | --------------> | CONNECT |
|
||||
* +----------+ +----------+
|
||||
* ^ ^ |
|
||||
* | rcv: rcv: | | rcv:
|
||||
* | Connect Disconnect | | Connect
|
||||
* | Disconnect Error | | Data
|
||||
* | | |
|
||||
* | | V
|
||||
* +----------+ disconnect() +----------+
|
||||
* |DISCONNECT| <-------------- | OPEN |
|
||||
* +----------+ +----------+
|
||||
*
|
||||
* The diagram above describes the PF/VF mailbox state machine. There
|
||||
* are four main states to this machine.
|
||||
* Closed: This state represents a mailbox that is in a standby state
|
||||
* with interrupts disabled. In this state the mailbox should not
|
||||
* read the mailbox or write any data. The only means of exiting
|
||||
* this state is for the system to make the connect() call for the
|
||||
* mailbox, it will then transition to the connect state.
|
||||
* Connect: In this state the mailbox is seeking a connection. It will
|
||||
* post a connect message with no specified destination and will
|
||||
* wait for a reply from the other side of the mailbox. This state
|
||||
* is exited when either a connect with the local mailbox as the
|
||||
* destination is received or when a data message is received with
|
||||
* a valid sequence number.
|
||||
* Open: In this state the mailbox is able to transfer data between the local
|
||||
* entity and the remote. It will fall back to connect in the event of
|
||||
* receiving either an error message, or a disconnect message. It will
|
||||
* transition to disconnect on a call to disconnect();
|
||||
* Disconnect: In this state the mailbox is attempting to gracefully terminate
|
||||
* the connection. It will do so at the first point where it knows
|
||||
* that the remote endpoint is either done sending, or when the
|
||||
* remote endpoint has fallen back into connect.
|
||||
*/
|
||||
enum fm10k_mbx_state {
|
||||
FM10K_STATE_CLOSED,
|
||||
FM10K_STATE_CONNECT,
|
||||
FM10K_STATE_OPEN,
|
||||
FM10K_STATE_DISCONNECT,
|
||||
};
|
||||
|
||||
/* PF/VF Mailbox header format
|
||||
* 3 2 1 0
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Size/Err_no/CRC | Rsvd0 | Head | Tail | Type |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* The layout above describes the format for the header used in the PF/VF
|
||||
* mailbox. The header is broken out into the following fields:
|
||||
* Type: There are 4 supported message types
|
||||
* 0x8: Data header - used to transport message data
|
||||
* 0xC: Connect header - used to establish connection
|
||||
* 0xD: Disconnect header - used to tear down a connection
|
||||
* 0xE: Error header - used to address message exceptions
|
||||
* Tail: Tail index for local FIFO
|
||||
* Tail index actually consists of two parts. The MSB of
|
||||
* the head is a loop tracker, it is 0 on an even numbered
|
||||
* loop through the FIFO, and 1 on the odd numbered loops.
|
||||
* To get the actual mailbox offset based on the tail it
|
||||
* is necessary to add bit 3 to bit 0 and clear bit 3. This
|
||||
* gives us a valid range of 0x1 - 0xE.
|
||||
* Head: Head index for remote FIFO
|
||||
* Head index follows the same format as the tail index.
|
||||
* Rsvd0: Reserved 0 portion of the mailbox header
|
||||
* CRC: Running CRC for all data since connect plus current message header
|
||||
* Size: Maximum message size - Applies only to connect headers
|
||||
* The maximum message size is provided during connect to avoid
|
||||
* jamming the mailbox with messages that do not fit.
|
||||
* Err_no: Error number - Applies only to error headers
|
||||
* The error number provides a indication of the type of error
|
||||
* experienced.
|
||||
*/
|
||||
|
||||
/* macros for retriving and setting header values */
|
||||
#define FM10K_MSG_HDR_MASK(name) \
|
||||
((0x1u << FM10K_MSG_##name##_SIZE) - 1)
|
||||
#define FM10K_MSG_HDR_FIELD_SET(value, name) \
|
||||
(((u32)(value) & FM10K_MSG_HDR_MASK(name)) << FM10K_MSG_##name##_SHIFT)
|
||||
#define FM10K_MSG_HDR_FIELD_GET(value, name) \
|
||||
((u16)((value) >> FM10K_MSG_##name##_SHIFT) & FM10K_MSG_HDR_MASK(name))
|
||||
|
||||
/* offsets shared between all headers */
|
||||
#define FM10K_MSG_TYPE_SHIFT 0
|
||||
#define FM10K_MSG_TYPE_SIZE 4
|
||||
#define FM10K_MSG_TAIL_SHIFT 4
|
||||
#define FM10K_MSG_TAIL_SIZE 4
|
||||
#define FM10K_MSG_HEAD_SHIFT 8
|
||||
#define FM10K_MSG_HEAD_SIZE 4
|
||||
#define FM10K_MSG_RSVD0_SHIFT 12
|
||||
#define FM10K_MSG_RSVD0_SIZE 4
|
||||
|
||||
/* offsets for data/disconnect headers */
|
||||
#define FM10K_MSG_CRC_SHIFT 16
|
||||
#define FM10K_MSG_CRC_SIZE 16
|
||||
|
||||
/* offsets for connect headers */
|
||||
#define FM10K_MSG_CONNECT_SIZE_SHIFT 16
|
||||
#define FM10K_MSG_CONNECT_SIZE_SIZE 16
|
||||
|
||||
/* offsets for error headers */
|
||||
#define FM10K_MSG_ERR_NO_SHIFT 16
|
||||
#define FM10K_MSG_ERR_NO_SIZE 16
|
||||
|
||||
enum fm10k_msg_type {
|
||||
FM10K_MSG_DATA = 0x8,
|
||||
FM10K_MSG_CONNECT = 0xC,
|
||||
FM10K_MSG_DISCONNECT = 0xD,
|
||||
FM10K_MSG_ERROR = 0xE,
|
||||
};
|
||||
|
||||
/* HNI/SM Mailbox FIFO format
|
||||
* 3 2 1 0
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-------+-----------------------+-------+-----------------------+
|
||||
* | Error | Remote Head |Version| Local Tail |
|
||||
* +-------+-----------------------+-------+-----------------------+
|
||||
* | |
|
||||
* . Local FIFO Data .
|
||||
* . .
|
||||
* +-------+-----------------------+-------+-----------------------+
|
||||
*
|
||||
* The layout above describes the format for the FIFOs used by the host
|
||||
* network interface and the switch manager to communicate messages back
|
||||
* and forth. Both the HNI and the switch maintain one such FIFO. The
|
||||
* layout in memory has the switch manager FIFO followed immediately by
|
||||
* the HNI FIFO. For this reason I am using just the pointer to the
|
||||
* HNI FIFO in the mailbox ops as the offset between the two is fixed.
|
||||
*
|
||||
* The header for the FIFO is broken out into the following fields:
|
||||
* Local Tail: Offset into FIFO region for next DWORD to write.
|
||||
* Version: Version info for mailbox, only values of 0/1 are supported.
|
||||
* Remote Head: Offset into remote FIFO to indicate how much we have read.
|
||||
* Error: Error indication, values TBD.
|
||||
*/
|
||||
|
||||
/* version number for switch manager mailboxes */
|
||||
#define FM10K_SM_MBX_VERSION 1
|
||||
#define FM10K_SM_MBX_FIFO_LEN (FM10K_MBMEM_PF_XOR - 1)
|
||||
|
||||
/* offsets shared between all SM FIFO headers */
|
||||
#define FM10K_MSG_SM_TAIL_SHIFT 0
|
||||
#define FM10K_MSG_SM_TAIL_SIZE 12
|
||||
#define FM10K_MSG_SM_VER_SHIFT 12
|
||||
#define FM10K_MSG_SM_VER_SIZE 4
|
||||
#define FM10K_MSG_SM_HEAD_SHIFT 16
|
||||
#define FM10K_MSG_SM_HEAD_SIZE 12
|
||||
#define FM10K_MSG_SM_ERR_SHIFT 28
|
||||
#define FM10K_MSG_SM_ERR_SIZE 4
|
||||
|
||||
/* All error messages returned by mailbox functions
|
||||
* The value -511 is 0xFE01 in hex. The idea is to order the errors
|
||||
* from 0xFE01 - 0xFEFF so error codes are easily visible in the mailbox
|
||||
* messages. This also helps to avoid error number collisions as Linux
|
||||
* doesn't appear to use error numbers 256 - 511.
|
||||
*/
|
||||
#define FM10K_MBX_ERR(_n) ((_n) - 512)
|
||||
#define FM10K_MBX_ERR_NO_MBX FM10K_MBX_ERR(0x01)
|
||||
#define FM10K_MBX_ERR_NO_SPACE FM10K_MBX_ERR(0x03)
|
||||
#define FM10K_MBX_ERR_TAIL FM10K_MBX_ERR(0x05)
|
||||
#define FM10K_MBX_ERR_HEAD FM10K_MBX_ERR(0x06)
|
||||
#define FM10K_MBX_ERR_SRC FM10K_MBX_ERR(0x08)
|
||||
#define FM10K_MBX_ERR_TYPE FM10K_MBX_ERR(0x09)
|
||||
#define FM10K_MBX_ERR_SIZE FM10K_MBX_ERR(0x0B)
|
||||
#define FM10K_MBX_ERR_BUSY FM10K_MBX_ERR(0x0C)
|
||||
#define FM10K_MBX_ERR_RSVD0 FM10K_MBX_ERR(0x0E)
|
||||
#define FM10K_MBX_ERR_CRC FM10K_MBX_ERR(0x0F)
|
||||
|
||||
#define FM10K_MBX_CRC_SEED 0xFFFF
|
||||
|
||||
struct fm10k_mbx_ops {
|
||||
s32 (*connect)(struct fm10k_hw *, struct fm10k_mbx_info *);
|
||||
void (*disconnect)(struct fm10k_hw *, struct fm10k_mbx_info *);
|
||||
bool (*rx_ready)(struct fm10k_mbx_info *);
|
||||
bool (*tx_ready)(struct fm10k_mbx_info *, u16);
|
||||
bool (*tx_complete)(struct fm10k_mbx_info *);
|
||||
s32 (*enqueue_tx)(struct fm10k_hw *, struct fm10k_mbx_info *,
|
||||
const u32 *);
|
||||
s32 (*process)(struct fm10k_hw *, struct fm10k_mbx_info *);
|
||||
s32 (*register_handlers)(struct fm10k_mbx_info *,
|
||||
const struct fm10k_msg_data *);
|
||||
};
|
||||
|
||||
struct fm10k_mbx_fifo {
|
||||
u32 *buffer;
|
||||
u16 head;
|
||||
u16 tail;
|
||||
u16 size;
|
||||
};
|
||||
|
||||
/* size of buffer to be stored in mailbox for FIFOs */
|
||||
#define FM10K_MBX_TX_BUFFER_SIZE 512
|
||||
#define FM10K_MBX_RX_BUFFER_SIZE 128
|
||||
#define FM10K_MBX_BUFFER_SIZE \
|
||||
(FM10K_MBX_TX_BUFFER_SIZE + FM10K_MBX_RX_BUFFER_SIZE)
|
||||
|
||||
/* minimum and maximum message size in dwords */
|
||||
#define FM10K_MBX_MSG_MAX_SIZE \
|
||||
((FM10K_MBX_TX_BUFFER_SIZE - 1) & (FM10K_MBX_RX_BUFFER_SIZE - 1))
|
||||
#define FM10K_VFMBX_MSG_MTU ((FM10K_VFMBMEM_LEN / 2) - 1)
|
||||
|
||||
#define FM10K_MBX_INIT_TIMEOUT 2000 /* number of retries on mailbox */
|
||||
#define FM10K_MBX_INIT_DELAY 500 /* microseconds between retries */
|
||||
|
||||
struct fm10k_mbx_info {
|
||||
/* function pointers for mailbox operations */
|
||||
struct fm10k_mbx_ops ops;
|
||||
const struct fm10k_msg_data *msg_data;
|
||||
|
||||
/* message FIFOs */
|
||||
struct fm10k_mbx_fifo rx;
|
||||
struct fm10k_mbx_fifo tx;
|
||||
|
||||
/* delay for handling timeouts */
|
||||
u32 timeout;
|
||||
u32 udelay;
|
||||
|
||||
/* mailbox state info */
|
||||
u32 mbx_reg, mbmem_reg, mbx_lock, mbx_hdr;
|
||||
u16 max_size, mbmem_len;
|
||||
u16 tail, tail_len, pulled;
|
||||
u16 head, head_len, pushed;
|
||||
u16 local, remote;
|
||||
enum fm10k_mbx_state state;
|
||||
|
||||
/* result of last mailbox test */
|
||||
s32 test_result;
|
||||
|
||||
/* statistics */
|
||||
u64 tx_busy;
|
||||
u64 tx_dropped;
|
||||
u64 tx_messages;
|
||||
u64 tx_dwords;
|
||||
u64 rx_messages;
|
||||
u64 rx_dwords;
|
||||
u64 rx_parse_err;
|
||||
|
||||
/* Buffer to store messages */
|
||||
u32 buffer[FM10K_MBX_BUFFER_SIZE];
|
||||
};
|
||||
|
||||
s32 fm10k_pfvf_mbx_init(struct fm10k_hw *, struct fm10k_mbx_info *,
|
||||
const struct fm10k_msg_data *, u8);
|
||||
s32 fm10k_sm_mbx_init(struct fm10k_hw *, struct fm10k_mbx_info *,
|
||||
const struct fm10k_msg_data *);
|
||||
|
||||
#endif /* _FM10K_MBX_H_ */
|
1431
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
Normal file
1431
drivers/net/ethernet/intel/fm10k/fm10k_netdev.c
Normal file
File diff suppressed because it is too large
Load Diff
2166
drivers/net/ethernet/intel/fm10k/fm10k_pci.c
Normal file
2166
drivers/net/ethernet/intel/fm10k/fm10k_pci.c
Normal file
File diff suppressed because it is too large
Load Diff
1880
drivers/net/ethernet/intel/fm10k/fm10k_pf.c
Normal file
1880
drivers/net/ethernet/intel/fm10k/fm10k_pf.c
Normal file
File diff suppressed because it is too large
Load Diff
135
drivers/net/ethernet/intel/fm10k/fm10k_pf.h
Normal file
135
drivers/net/ethernet/intel/fm10k/fm10k_pf.h
Normal file
@ -0,0 +1,135 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_PF_H_
|
||||
#define _FM10K_PF_H_
|
||||
|
||||
#include "fm10k_type.h"
|
||||
#include "fm10k_common.h"
|
||||
|
||||
bool fm10k_glort_valid_pf(struct fm10k_hw *hw, u16 glort);
|
||||
u16 fm10k_queues_per_pool(struct fm10k_hw *hw);
|
||||
u16 fm10k_vf_queue_index(struct fm10k_hw *hw, u16 vf_idx);
|
||||
|
||||
enum fm10k_pf_tlv_msg_id_v1 {
|
||||
FM10K_PF_MSG_ID_TEST = 0x000, /* msg ID reserved */
|
||||
FM10K_PF_MSG_ID_XCAST_MODES = 0x001,
|
||||
FM10K_PF_MSG_ID_UPDATE_MAC_FWD_RULE = 0x002,
|
||||
FM10K_PF_MSG_ID_LPORT_MAP = 0x100,
|
||||
FM10K_PF_MSG_ID_LPORT_CREATE = 0x200,
|
||||
FM10K_PF_MSG_ID_LPORT_DELETE = 0x201,
|
||||
FM10K_PF_MSG_ID_CONFIG = 0x300,
|
||||
FM10K_PF_MSG_ID_UPDATE_PVID = 0x400,
|
||||
FM10K_PF_MSG_ID_CREATE_FLOW_TABLE = 0x501,
|
||||
FM10K_PF_MSG_ID_DELETE_FLOW_TABLE = 0x502,
|
||||
FM10K_PF_MSG_ID_UPDATE_FLOW = 0x503,
|
||||
FM10K_PF_MSG_ID_DELETE_FLOW = 0x504,
|
||||
FM10K_PF_MSG_ID_SET_FLOW_STATE = 0x505,
|
||||
FM10K_PF_MSG_ID_GET_1588_INFO = 0x506,
|
||||
FM10K_PF_MSG_ID_1588_TIMESTAMP = 0x701,
|
||||
};
|
||||
|
||||
enum fm10k_pf_tlv_attr_id_v1 {
|
||||
FM10K_PF_ATTR_ID_ERR = 0x00,
|
||||
FM10K_PF_ATTR_ID_LPORT_MAP = 0x01,
|
||||
FM10K_PF_ATTR_ID_XCAST_MODE = 0x02,
|
||||
FM10K_PF_ATTR_ID_MAC_UPDATE = 0x03,
|
||||
FM10K_PF_ATTR_ID_VLAN_UPDATE = 0x04,
|
||||
FM10K_PF_ATTR_ID_CONFIG = 0x05,
|
||||
FM10K_PF_ATTR_ID_CREATE_FLOW_TABLE = 0x06,
|
||||
FM10K_PF_ATTR_ID_DELETE_FLOW_TABLE = 0x07,
|
||||
FM10K_PF_ATTR_ID_UPDATE_FLOW = 0x08,
|
||||
FM10K_PF_ATTR_ID_FLOW_STATE = 0x09,
|
||||
FM10K_PF_ATTR_ID_FLOW_HANDLE = 0x0A,
|
||||
FM10K_PF_ATTR_ID_DELETE_FLOW = 0x0B,
|
||||
FM10K_PF_ATTR_ID_PORT = 0x0C,
|
||||
FM10K_PF_ATTR_ID_UPDATE_PVID = 0x0D,
|
||||
FM10K_PF_ATTR_ID_1588_TIMESTAMP = 0x10,
|
||||
};
|
||||
|
||||
#define FM10K_MSG_LPORT_MAP_GLORT_SHIFT 0
|
||||
#define FM10K_MSG_LPORT_MAP_GLORT_SIZE 16
|
||||
#define FM10K_MSG_LPORT_MAP_MASK_SHIFT 16
|
||||
#define FM10K_MSG_LPORT_MAP_MASK_SIZE 16
|
||||
|
||||
#define FM10K_MSG_UPDATE_PVID_GLORT_SHIFT 0
|
||||
#define FM10K_MSG_UPDATE_PVID_GLORT_SIZE 16
|
||||
#define FM10K_MSG_UPDATE_PVID_PVID_SHIFT 16
|
||||
#define FM10K_MSG_UPDATE_PVID_PVID_SIZE 16
|
||||
|
||||
struct fm10k_mac_update {
|
||||
__le32 mac_lower;
|
||||
__le16 mac_upper;
|
||||
__le16 vlan;
|
||||
__le16 glort;
|
||||
u8 flags;
|
||||
u8 action;
|
||||
};
|
||||
|
||||
struct fm10k_global_table_data {
|
||||
__le32 used;
|
||||
__le32 avail;
|
||||
};
|
||||
|
||||
struct fm10k_swapi_error {
|
||||
__le32 status;
|
||||
struct fm10k_global_table_data mac;
|
||||
struct fm10k_global_table_data nexthop;
|
||||
struct fm10k_global_table_data ffu;
|
||||
};
|
||||
|
||||
struct fm10k_swapi_1588_timestamp {
|
||||
__le64 egress;
|
||||
__le64 ingress;
|
||||
__le16 dglort;
|
||||
__le16 sglort;
|
||||
};
|
||||
|
||||
s32 fm10k_msg_lport_map_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_tlv_attr fm10k_lport_map_msg_attr[];
|
||||
#define FM10K_PF_MSG_LPORT_MAP_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_LPORT_MAP, \
|
||||
fm10k_lport_map_msg_attr, func)
|
||||
s32 fm10k_msg_update_pvid_pf(struct fm10k_hw *, u32 **,
|
||||
struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_tlv_attr fm10k_update_pvid_msg_attr[];
|
||||
#define FM10K_PF_MSG_UPDATE_PVID_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_UPDATE_PVID, \
|
||||
fm10k_update_pvid_msg_attr, func)
|
||||
|
||||
s32 fm10k_msg_err_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_tlv_attr fm10k_err_msg_attr[];
|
||||
#define FM10K_PF_MSG_ERR_HANDLER(msg, func) \
|
||||
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_##msg, fm10k_err_msg_attr, func)
|
||||
|
||||
extern const struct fm10k_tlv_attr fm10k_1588_timestamp_msg_attr[];
|
||||
#define FM10K_PF_MSG_1588_TIMESTAMP_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_PF_MSG_ID_1588_TIMESTAMP, \
|
||||
fm10k_1588_timestamp_msg_attr, func)
|
||||
|
||||
s32 fm10k_iov_msg_msix_pf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
|
||||
s32 fm10k_iov_msg_mac_vlan_pf(struct fm10k_hw *, u32 **,
|
||||
struct fm10k_mbx_info *);
|
||||
s32 fm10k_iov_msg_lport_state_pf(struct fm10k_hw *, u32 **,
|
||||
struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_msg_data fm10k_iov_msg_data_pf[];
|
||||
|
||||
extern struct fm10k_info fm10k_pf_info;
|
||||
#endif /* _FM10K_PF_H */
|
463
drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
Normal file
463
drivers/net/ethernet/intel/fm10k/fm10k_ptp.c
Normal file
@ -0,0 +1,463 @@
|
||||
/* 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 <linux/ptp_classify.h>
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
#include "fm10k.h"
|
||||
|
||||
#define FM10K_TS_TX_TIMEOUT (HZ * 15)
|
||||
|
||||
void fm10k_systime_to_hwtstamp(struct fm10k_intfc *interface,
|
||||
struct skb_shared_hwtstamps *hwtstamp,
|
||||
u64 systime)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
read_lock_irqsave(&interface->systime_lock, flags);
|
||||
systime += interface->ptp_adjust;
|
||||
read_unlock_irqrestore(&interface->systime_lock, flags);
|
||||
|
||||
hwtstamp->hwtstamp = ns_to_ktime(systime);
|
||||
}
|
||||
|
||||
static struct sk_buff *fm10k_ts_tx_skb(struct fm10k_intfc *interface,
|
||||
__le16 dglort)
|
||||
{
|
||||
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb_queue_walk(list, skb) {
|
||||
if (FM10K_CB(skb)->fi.w.dglort == dglort)
|
||||
return skb;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void fm10k_ts_tx_enqueue(struct fm10k_intfc *interface, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
|
||||
struct sk_buff *clone;
|
||||
unsigned long flags;
|
||||
__le16 dglort;
|
||||
|
||||
/* create clone for us to return on the Tx path */
|
||||
clone = skb_clone_sk(skb);
|
||||
if (!clone)
|
||||
return;
|
||||
|
||||
FM10K_CB(clone)->ts_tx_timeout = jiffies + FM10K_TS_TX_TIMEOUT;
|
||||
dglort = FM10K_CB(clone)->fi.w.dglort;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
/* attempt to locate any buffers with the same dglort,
|
||||
* if none are present then insert skb in tail of list
|
||||
*/
|
||||
skb = fm10k_ts_tx_skb(interface, FM10K_CB(clone)->fi.w.dglort);
|
||||
if (!skb)
|
||||
__skb_queue_tail(list, clone);
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
|
||||
/* if list is already has one then we just free the clone */
|
||||
if (skb)
|
||||
kfree_skb(skb);
|
||||
else
|
||||
skb_shinfo(clone)->tx_flags |= SKBTX_IN_PROGRESS;
|
||||
}
|
||||
|
||||
void fm10k_ts_tx_hwtstamp(struct fm10k_intfc *interface, __le16 dglort,
|
||||
u64 systime)
|
||||
{
|
||||
struct skb_shared_hwtstamps shhwtstamps;
|
||||
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
/* attempt to locate and pull the sk_buff out of the list */
|
||||
skb = fm10k_ts_tx_skb(interface, dglort);
|
||||
if (skb)
|
||||
__skb_unlink(skb, list);
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
|
||||
/* if not found do nothing */
|
||||
if (!skb)
|
||||
return;
|
||||
|
||||
/* timestamp the sk_buff and return it to the socket */
|
||||
fm10k_systime_to_hwtstamp(interface, &shhwtstamps, systime);
|
||||
skb_complete_tx_timestamp(skb, &shhwtstamps);
|
||||
}
|
||||
|
||||
void fm10k_ts_tx_subtask(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct sk_buff_head *list = &interface->ts_tx_skb_queue;
|
||||
struct sk_buff *skb, *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
/* If we're down or resetting, just bail */
|
||||
if (test_bit(__FM10K_DOWN, &interface->state) ||
|
||||
test_bit(__FM10K_RESETTING, &interface->state))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&list->lock, flags);
|
||||
|
||||
/* walk though the list and flush any expired timestamp packets */
|
||||
skb_queue_walk_safe(list, skb, tmp) {
|
||||
if (!time_is_after_jiffies(FM10K_CB(skb)->ts_tx_timeout))
|
||||
continue;
|
||||
__skb_unlink(skb, list);
|
||||
kfree_skb(skb);
|
||||
interface->tx_hwtstamp_timeouts++;
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&list->lock, flags);
|
||||
}
|
||||
|
||||
static u64 fm10k_systime_read(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct fm10k_hw *hw = &interface->hw;
|
||||
|
||||
return hw->mac.ops.read_systime(hw);
|
||||
}
|
||||
|
||||
void fm10k_ts_reset(struct fm10k_intfc *interface)
|
||||
{
|
||||
s64 ns = ktime_to_ns(ktime_get_real());
|
||||
unsigned long flags;
|
||||
|
||||
/* reinitialize the clock */
|
||||
write_lock_irqsave(&interface->systime_lock, flags);
|
||||
interface->ptp_adjust = fm10k_systime_read(interface) - ns;
|
||||
write_unlock_irqrestore(&interface->systime_lock, flags);
|
||||
}
|
||||
|
||||
void fm10k_ts_init(struct fm10k_intfc *interface)
|
||||
{
|
||||
/* Initialize lock protecting systime access */
|
||||
rwlock_init(&interface->systime_lock);
|
||||
|
||||
/* Initialize skb queue for pending timestamp requests */
|
||||
skb_queue_head_init(&interface->ts_tx_skb_queue);
|
||||
|
||||
/* reset the clock to current kernel time */
|
||||
fm10k_ts_reset(interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_get_ts_config - get current hardware timestamping configuration
|
||||
* @netdev: network interface device structure
|
||||
* @ifreq: ioctl data
|
||||
*
|
||||
* This function returns the current timestamping settings. Rather than
|
||||
* attempt to deconstruct registers to fill in the values, simply keep a copy
|
||||
* of the old settings around, and return a copy when requested.
|
||||
*/
|
||||
int fm10k_get_ts_config(struct net_device *netdev, struct ifreq *ifr)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct hwtstamp_config *config = &interface->ts_config;
|
||||
|
||||
return copy_to_user(ifr->ifr_data, config, sizeof(*config)) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_set_ts_config - control hardware time stamping
|
||||
* @netdev: network interface device structure
|
||||
* @ifreq: ioctl data
|
||||
*
|
||||
* Outgoing time stamping can be enabled and disabled. Play nice and
|
||||
* disable it when requested, although it shouldn't cause any overhead
|
||||
* when no packet needs it. At most one packet in the queue may be
|
||||
* marked for time stamping, otherwise it would be impossible to tell
|
||||
* for sure to which packet the hardware time stamp belongs.
|
||||
*
|
||||
* Incoming time stamping has to be configured via the hardware
|
||||
* filters. Not all combinations are supported, in particular event
|
||||
* type has to be specified. Matching the kind of event packet is
|
||||
* not supported, with the exception of "all V2 events regardless of
|
||||
* level 2 or 4".
|
||||
*
|
||||
* Since hardware always timestamps Path delay packets when timestamping V2
|
||||
* packets, regardless of the type specified in the register, only use V2
|
||||
* Event mode. This more accurately tells the user what the hardware is going
|
||||
* to do anyways.
|
||||
*/
|
||||
int fm10k_set_ts_config(struct net_device *netdev, struct ifreq *ifr)
|
||||
{
|
||||
struct fm10k_intfc *interface = netdev_priv(netdev);
|
||||
struct hwtstamp_config ts_config;
|
||||
|
||||
if (copy_from_user(&ts_config, ifr->ifr_data, sizeof(ts_config)))
|
||||
return -EFAULT;
|
||||
|
||||
/* reserved for future extensions */
|
||||
if (ts_config.flags)
|
||||
return -EINVAL;
|
||||
|
||||
switch (ts_config.tx_type) {
|
||||
case HWTSTAMP_TX_OFF:
|
||||
break;
|
||||
case HWTSTAMP_TX_ON:
|
||||
/* we likely need some check here to see if this is supported */
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
switch (ts_config.rx_filter) {
|
||||
case HWTSTAMP_FILTER_NONE:
|
||||
interface->flags &= ~FM10K_FLAG_RX_TS_ENABLED;
|
||||
break;
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_PTP_V2_EVENT:
|
||||
case HWTSTAMP_FILTER_PTP_V2_SYNC:
|
||||
case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
|
||||
case HWTSTAMP_FILTER_ALL:
|
||||
interface->flags |= FM10K_FLAG_RX_TS_ENABLED;
|
||||
ts_config.rx_filter = HWTSTAMP_FILTER_ALL;
|
||||
break;
|
||||
default:
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
/* save these settings for future reference */
|
||||
interface->ts_config = ts_config;
|
||||
|
||||
return copy_to_user(ifr->ifr_data, &ts_config, sizeof(ts_config)) ?
|
||||
-EFAULT : 0;
|
||||
}
|
||||
|
||||
static int fm10k_ptp_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
|
||||
{
|
||||
struct fm10k_intfc *interface;
|
||||
struct fm10k_hw *hw;
|
||||
int err;
|
||||
|
||||
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
|
||||
hw = &interface->hw;
|
||||
|
||||
err = hw->mac.ops.adjust_systime(hw, ppb);
|
||||
|
||||
/* the only error we should see is if the value is out of range */
|
||||
return (err == FM10K_ERR_PARAM) ? -ERANGE : err;
|
||||
}
|
||||
|
||||
static int fm10k_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct fm10k_intfc *interface;
|
||||
unsigned long flags;
|
||||
|
||||
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
|
||||
|
||||
write_lock_irqsave(&interface->systime_lock, flags);
|
||||
interface->ptp_adjust += delta;
|
||||
write_unlock_irqrestore(&interface->systime_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fm10k_ptp_gettime(struct ptp_clock_info *ptp, struct timespec *ts)
|
||||
{
|
||||
struct fm10k_intfc *interface;
|
||||
unsigned long flags;
|
||||
u64 now;
|
||||
|
||||
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
|
||||
|
||||
read_lock_irqsave(&interface->systime_lock, flags);
|
||||
now = fm10k_systime_read(interface) + interface->ptp_adjust;
|
||||
read_unlock_irqrestore(&interface->systime_lock, flags);
|
||||
|
||||
*ts = ns_to_timespec(now);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fm10k_ptp_settime(struct ptp_clock_info *ptp,
|
||||
const struct timespec *ts)
|
||||
{
|
||||
struct fm10k_intfc *interface;
|
||||
unsigned long flags;
|
||||
u64 ns = timespec_to_ns(ts);
|
||||
|
||||
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
|
||||
|
||||
write_lock_irqsave(&interface->systime_lock, flags);
|
||||
interface->ptp_adjust = fm10k_systime_read(interface) - ns;
|
||||
write_unlock_irqrestore(&interface->systime_lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fm10k_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *rq, int on)
|
||||
{
|
||||
struct ptp_clock_time *t = &rq->perout.period;
|
||||
struct fm10k_intfc *interface;
|
||||
struct fm10k_hw *hw;
|
||||
u64 period;
|
||||
u32 step;
|
||||
|
||||
/* we can only support periodic output */
|
||||
if (rq->type != PTP_CLK_REQ_PEROUT)
|
||||
return -EINVAL;
|
||||
|
||||
/* verify the requested channel is there */
|
||||
if (rq->perout.index >= ptp->n_per_out)
|
||||
return -EINVAL;
|
||||
|
||||
/* we cannot enforce start time as there is no
|
||||
* mechanism for that in the hardware, we can only control
|
||||
* the period.
|
||||
*/
|
||||
|
||||
/* we cannot support periods greater than 4 seconds due to reg limit */
|
||||
if (t->sec > 4 || t->sec < 0)
|
||||
return -ERANGE;
|
||||
|
||||
interface = container_of(ptp, struct fm10k_intfc, ptp_caps);
|
||||
hw = &interface->hw;
|
||||
|
||||
/* we simply cannot support the operation if we don't have BAR4 */
|
||||
if (!hw->sw_addr)
|
||||
return -ENOTSUPP;
|
||||
|
||||
/* convert to unsigned 64b ns, verify we can put it in a 32b register */
|
||||
period = t->sec * 1000000000LL + t->nsec;
|
||||
|
||||
/* determine the minimum size for period */
|
||||
step = 2 * (fm10k_read_reg(hw, FM10K_SYSTIME_CFG) &
|
||||
FM10K_SYSTIME_CFG_STEP_MASK);
|
||||
|
||||
/* verify the value is in range supported by hardware */
|
||||
if ((period && (period < step)) || (period > U32_MAX))
|
||||
return -ERANGE;
|
||||
|
||||
/* notify hardware of request to being sending pulses */
|
||||
fm10k_write_sw_reg(hw, FM10K_SW_SYSTIME_PULSE(rq->perout.index),
|
||||
(u32)period);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ptp_pin_desc fm10k_ptp_pd[2] = {
|
||||
{
|
||||
.name = "IEEE1588_PULSE0",
|
||||
.index = 0,
|
||||
.func = PTP_PF_PEROUT,
|
||||
.chan = 0
|
||||
},
|
||||
{
|
||||
.name = "IEEE1588_PULSE1",
|
||||
.index = 1,
|
||||
.func = PTP_PF_PEROUT,
|
||||
.chan = 1
|
||||
}
|
||||
};
|
||||
|
||||
static int fm10k_ptp_verify(struct ptp_clock_info *ptp, unsigned int pin,
|
||||
enum ptp_pin_function func, unsigned int chan)
|
||||
{
|
||||
/* verify the requested pin is there */
|
||||
if (pin >= ptp->n_pins || !ptp->pin_config)
|
||||
return -EINVAL;
|
||||
|
||||
/* enforce locked channels, no changing them */
|
||||
if (chan != ptp->pin_config[pin].chan)
|
||||
return -EINVAL;
|
||||
|
||||
/* we want to keep the functions locked as well */
|
||||
if (func != ptp->pin_config[pin].func)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fm10k_ptp_register(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct ptp_clock_info *ptp_caps = &interface->ptp_caps;
|
||||
struct device *dev = &interface->pdev->dev;
|
||||
struct ptp_clock *ptp_clock;
|
||||
|
||||
snprintf(ptp_caps->name, sizeof(ptp_caps->name),
|
||||
"%s", interface->netdev->name);
|
||||
ptp_caps->owner = THIS_MODULE;
|
||||
/* This math is simply the inverse of the math in
|
||||
* fm10k_adjust_systime_pf applied to an adjustment value
|
||||
* of 2^30 - 1 which is the maximum value of the register:
|
||||
* max_ppb == ((2^30 - 1) * 5^9) / 2^31
|
||||
*/
|
||||
ptp_caps->max_adj = 976562;
|
||||
ptp_caps->adjfreq = fm10k_ptp_adjfreq;
|
||||
ptp_caps->adjtime = fm10k_ptp_adjtime;
|
||||
ptp_caps->gettime = fm10k_ptp_gettime;
|
||||
ptp_caps->settime = fm10k_ptp_settime;
|
||||
|
||||
/* provide pins if BAR4 is accessible */
|
||||
if (interface->sw_addr) {
|
||||
/* enable periodic outputs */
|
||||
ptp_caps->n_per_out = 2;
|
||||
ptp_caps->enable = fm10k_ptp_enable;
|
||||
|
||||
/* enable clock pins */
|
||||
ptp_caps->verify = fm10k_ptp_verify;
|
||||
ptp_caps->n_pins = 2;
|
||||
ptp_caps->pin_config = fm10k_ptp_pd;
|
||||
}
|
||||
|
||||
ptp_clock = ptp_clock_register(ptp_caps, dev);
|
||||
if (IS_ERR(ptp_clock)) {
|
||||
ptp_clock = NULL;
|
||||
dev_err(dev, "ptp_clock_register failed\n");
|
||||
} else {
|
||||
dev_info(dev, "registered PHC device %s\n", ptp_caps->name);
|
||||
}
|
||||
|
||||
interface->ptp_clock = ptp_clock;
|
||||
}
|
||||
|
||||
void fm10k_ptp_unregister(struct fm10k_intfc *interface)
|
||||
{
|
||||
struct ptp_clock *ptp_clock = interface->ptp_clock;
|
||||
struct device *dev = &interface->pdev->dev;
|
||||
|
||||
if (!ptp_clock)
|
||||
return;
|
||||
|
||||
interface->ptp_clock = NULL;
|
||||
|
||||
ptp_clock_unregister(ptp_clock);
|
||||
dev_info(dev, "removed PHC %s\n", interface->ptp_caps.name);
|
||||
}
|
863
drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
Normal file
863
drivers/net/ethernet/intel/fm10k/fm10k_tlv.c
Normal file
@ -0,0 +1,863 @@
|
||||
/* 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_tlv.h"
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_init - Initialize message block for TLV data storage
|
||||
* @msg: Pointer to message block
|
||||
* @msg_id: Message ID indicating message type
|
||||
*
|
||||
* This function return success if provided with a valid message pointer
|
||||
**/
|
||||
s32 fm10k_tlv_msg_init(u32 *msg, u16 msg_id)
|
||||
{
|
||||
/* verify pointer is not NULL */
|
||||
if (!msg)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
*msg = (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT) | msg_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_put_null_string - Place null terminated string on message
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
* @string: Pointer to string to be stored in attribute
|
||||
*
|
||||
* This function will reorder a string to be CPU endian and store it in
|
||||
* the attribute buffer. It will return success if provided with a valid
|
||||
* pointers.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_put_null_string(u32 *msg, u16 attr_id,
|
||||
const unsigned char *string)
|
||||
{
|
||||
u32 attr_data = 0, len = 0;
|
||||
u32 *attr;
|
||||
|
||||
/* verify pointers are not NULL */
|
||||
if (!string || !msg)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
|
||||
/* copy string into local variable and then write to msg */
|
||||
do {
|
||||
/* write data to message */
|
||||
if (len && !(len % 4)) {
|
||||
attr[len / 4] = attr_data;
|
||||
attr_data = 0;
|
||||
}
|
||||
|
||||
/* record character to offset location */
|
||||
attr_data |= (u32)(*string) << (8 * (len % 4));
|
||||
len++;
|
||||
|
||||
/* test for NULL and then increment */
|
||||
} while (*(string++));
|
||||
|
||||
/* write last piece of data to message */
|
||||
attr[(len + 3) / 4] = attr_data;
|
||||
|
||||
/* record attribute header, update message length */
|
||||
len <<= FM10K_TLV_LEN_SHIFT;
|
||||
attr[0] = len | attr_id;
|
||||
|
||||
/* add header length to length */
|
||||
len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
*msg += FM10K_TLV_LEN_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_get_null_string - Get null terminated string from attribute
|
||||
* @attr: Pointer to attribute
|
||||
* @string: Pointer to location of destination string
|
||||
*
|
||||
* This function pulls the string back out of the attribute and will place
|
||||
* it in the array pointed by by string. It will return success if provided
|
||||
* with a valid pointers.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_get_null_string(u32 *attr, unsigned char *string)
|
||||
{
|
||||
u32 len;
|
||||
|
||||
/* verify pointers are not NULL */
|
||||
if (!string || !attr)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
len = *attr >> FM10K_TLV_LEN_SHIFT;
|
||||
attr++;
|
||||
|
||||
while (len--)
|
||||
string[len] = (u8)(attr[len / 4] >> (8 * (len % 4)));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_put_mac_vlan - Store MAC/VLAN attribute in message
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
* @mac_addr: MAC address to be stored
|
||||
*
|
||||
* This function will reorder a MAC address to be CPU endian and store it
|
||||
* in the attribute buffer. It will return success if provided with a
|
||||
* valid pointers.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_put_mac_vlan(u32 *msg, u16 attr_id,
|
||||
const u8 *mac_addr, u16 vlan)
|
||||
{
|
||||
u32 len = ETH_ALEN << FM10K_TLV_LEN_SHIFT;
|
||||
u32 *attr;
|
||||
|
||||
/* verify pointers are not NULL */
|
||||
if (!msg || !mac_addr)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
|
||||
/* record attribute header, update message length */
|
||||
attr[0] = len | attr_id;
|
||||
|
||||
/* copy value into local variable and then write to msg */
|
||||
attr[1] = le32_to_cpu(*(const __le32 *)&mac_addr[0]);
|
||||
attr[2] = le16_to_cpu(*(const __le16 *)&mac_addr[4]);
|
||||
attr[2] |= (u32)vlan << 16;
|
||||
|
||||
/* add header length to length */
|
||||
len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
*msg += FM10K_TLV_LEN_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_get_mac_vlan - Get MAC/VLAN stored in attribute
|
||||
* @attr: Pointer to attribute
|
||||
* @attr_id: Attribute ID
|
||||
* @mac_addr: location of buffer to store MAC address
|
||||
*
|
||||
* This function pulls the MAC address back out of the attribute and will
|
||||
* place it in the array pointed by by mac_addr. It will return success
|
||||
* if provided with a valid pointers.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_get_mac_vlan(u32 *attr, u8 *mac_addr, u16 *vlan)
|
||||
{
|
||||
/* verify pointers are not NULL */
|
||||
if (!mac_addr || !attr)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
*(__le32 *)&mac_addr[0] = cpu_to_le32(attr[1]);
|
||||
*(__le16 *)&mac_addr[4] = cpu_to_le16((u16)(attr[2]));
|
||||
*vlan = (u16)(attr[2] >> 16);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_put_bool - Add header indicating value "true"
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
*
|
||||
* This function will simply add an attribute header, the fact
|
||||
* that the header is here means the attribute value is true, else
|
||||
* it is false. The function will return success if provided with a
|
||||
* valid pointers.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_put_bool(u32 *msg, u16 attr_id)
|
||||
{
|
||||
/* verify pointers are not NULL */
|
||||
if (!msg)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* record attribute header */
|
||||
msg[FM10K_TLV_DWORD_LEN(*msg)] = attr_id;
|
||||
|
||||
/* add header length to length */
|
||||
*msg += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_put_value - Store integer value attribute in message
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
* @value: Value to be written
|
||||
* @len: Size of value
|
||||
*
|
||||
* This function will place an integer value of up to 8 bytes in size
|
||||
* in a message attribute. The function will return success provided
|
||||
* that msg is a valid pointer, and len is 1, 2, 4, or 8.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_put_value(u32 *msg, u16 attr_id, s64 value, u32 len)
|
||||
{
|
||||
u32 *attr;
|
||||
|
||||
/* verify non-null msg and len is 1, 2, 4, or 8 */
|
||||
if (!msg || !len || len > 8 || (len & (len - 1)))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
|
||||
if (len < 4) {
|
||||
attr[1] = (u32)value & ((0x1ul << (8 * len)) - 1);
|
||||
} else {
|
||||
attr[1] = (u32)value;
|
||||
if (len > 4)
|
||||
attr[2] = (u32)(value >> 32);
|
||||
}
|
||||
|
||||
/* record attribute header, update message length */
|
||||
len <<= FM10K_TLV_LEN_SHIFT;
|
||||
attr[0] = len | attr_id;
|
||||
|
||||
/* add header length to length */
|
||||
len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
*msg += FM10K_TLV_LEN_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_get_value - Get integer value stored in attribute
|
||||
* @attr: Pointer to attribute
|
||||
* @value: Pointer to destination buffer
|
||||
* @len: Size of value
|
||||
*
|
||||
* This function will place an integer value of up to 8 bytes in size
|
||||
* in the offset pointed to by value. The function will return success
|
||||
* provided that pointers are valid and the len value matches the
|
||||
* attribute length.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_get_value(u32 *attr, void *value, u32 len)
|
||||
{
|
||||
/* verify pointers are not NULL */
|
||||
if (!attr || !value)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
if (len == 8)
|
||||
*(u64 *)value = ((u64)attr[2] << 32) | attr[1];
|
||||
else if (len == 4)
|
||||
*(u32 *)value = attr[1];
|
||||
else if (len == 2)
|
||||
*(u16 *)value = (u16)attr[1];
|
||||
else
|
||||
*(u8 *)value = (u8)attr[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_put_le_struct - Store little endian structure in message
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
* @le_struct: Pointer to structure to be written
|
||||
* @len: Size of le_struct
|
||||
*
|
||||
* This function will place a little endian structure value in a message
|
||||
* attribute. The function will return success provided that all pointers
|
||||
* are valid and length is a non-zero multiple of 4.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_put_le_struct(u32 *msg, u16 attr_id,
|
||||
const void *le_struct, u32 len)
|
||||
{
|
||||
const __le32 *le32_ptr = (const __le32 *)le_struct;
|
||||
u32 *attr;
|
||||
u32 i;
|
||||
|
||||
/* verify non-null msg and len is in 32 bit words */
|
||||
if (!msg || !len || (len % 4))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
|
||||
/* copy le32 structure into host byte order at 32b boundaries */
|
||||
for (i = 0; i < (len / 4); i++)
|
||||
attr[i + 1] = le32_to_cpu(le32_ptr[i]);
|
||||
|
||||
/* record attribute header, update message length */
|
||||
len <<= FM10K_TLV_LEN_SHIFT;
|
||||
attr[0] = len | attr_id;
|
||||
|
||||
/* add header length to length */
|
||||
len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
*msg += FM10K_TLV_LEN_ALIGN(len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_get_le_struct - Get little endian struct form attribute
|
||||
* @attr: Pointer to attribute
|
||||
* @le_struct: Pointer to structure to be written
|
||||
* @len: Size of structure
|
||||
*
|
||||
* This function will place a little endian structure in the buffer
|
||||
* pointed to by le_struct. The function will return success
|
||||
* provided that pointers are valid and the len value matches the
|
||||
* attribute length.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_get_le_struct(u32 *attr, void *le_struct, u32 len)
|
||||
{
|
||||
__le32 *le32_ptr = (__le32 *)le_struct;
|
||||
u32 i;
|
||||
|
||||
/* verify pointers are not NULL */
|
||||
if (!le_struct || !attr)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
if ((*attr >> FM10K_TLV_LEN_SHIFT) != len)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
attr++;
|
||||
|
||||
for (i = 0; len; i++, len -= 4)
|
||||
le32_ptr[i] = cpu_to_le32(attr[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_nest_start - Start a set of nested attributes
|
||||
* @msg: Pointer to message block
|
||||
* @attr_id: Attribute ID
|
||||
*
|
||||
* This function will mark off a new nested region for encapsulating
|
||||
* a given set of attributes. The idea is if you wish to place a secondary
|
||||
* structure within the message this mechanism allows for that. The
|
||||
* function will return NULL on failure, and a pointer to the start
|
||||
* of the nested attributes on success.
|
||||
**/
|
||||
u32 *fm10k_tlv_attr_nest_start(u32 *msg, u16 attr_id)
|
||||
{
|
||||
u32 *attr;
|
||||
|
||||
/* verify pointer is not NULL */
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
|
||||
attr[0] = attr_id;
|
||||
|
||||
/* return pointer to nest header */
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_nest_start - Start a set of nested attributes
|
||||
* @msg: Pointer to message block
|
||||
*
|
||||
* This function closes off an existing set of nested attributes. The
|
||||
* message pointer should be pointing to the parent of the nest. So in
|
||||
* the case of a nest within the nest this would be the outer nest pointer.
|
||||
* This function will return success provided all pointers are valid.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_nest_stop(u32 *msg)
|
||||
{
|
||||
u32 *attr;
|
||||
u32 len;
|
||||
|
||||
/* verify pointer is not NULL */
|
||||
if (!msg)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* locate the nested header and retrieve its length */
|
||||
attr = &msg[FM10K_TLV_DWORD_LEN(*msg)];
|
||||
len = (attr[0] >> FM10K_TLV_LEN_SHIFT) << FM10K_TLV_LEN_SHIFT;
|
||||
|
||||
/* only include nest if data was added to it */
|
||||
if (len) {
|
||||
len += FM10K_TLV_HDR_LEN << FM10K_TLV_LEN_SHIFT;
|
||||
*msg += len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_validate - Validate attribute metadata
|
||||
* @attr: Pointer to attribute
|
||||
* @tlv_attr: Type and length info for attribute
|
||||
*
|
||||
* This function does some basic validation of the input TLV. It
|
||||
* verifies the length, and in the case of null terminated strings
|
||||
* it verifies that the last byte is null. The function will
|
||||
* return FM10K_ERR_PARAM if any attribute is malformed, otherwise
|
||||
* it returns 0.
|
||||
**/
|
||||
static s32 fm10k_tlv_attr_validate(u32 *attr,
|
||||
const struct fm10k_tlv_attr *tlv_attr)
|
||||
{
|
||||
u32 attr_id = *attr & FM10K_TLV_ID_MASK;
|
||||
u16 len = *attr >> FM10K_TLV_LEN_SHIFT;
|
||||
|
||||
/* verify this is an attribute and not a message */
|
||||
if (*attr & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* search through the list of attributes to find a matching ID */
|
||||
while (tlv_attr->id < attr_id)
|
||||
tlv_attr++;
|
||||
|
||||
/* if didn't find a match then we should exit */
|
||||
if (tlv_attr->id != attr_id)
|
||||
return FM10K_NOT_IMPLEMENTED;
|
||||
|
||||
/* move to start of attribute data */
|
||||
attr++;
|
||||
|
||||
switch (tlv_attr->type) {
|
||||
case FM10K_TLV_NULL_STRING:
|
||||
if (!len ||
|
||||
(attr[(len - 1) / 4] & (0xFF << (8 * ((len - 1) % 4)))))
|
||||
return FM10K_ERR_PARAM;
|
||||
if (len > tlv_attr->len)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
case FM10K_TLV_MAC_ADDR:
|
||||
if (len != ETH_ALEN)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
case FM10K_TLV_BOOL:
|
||||
if (len)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
case FM10K_TLV_UNSIGNED:
|
||||
case FM10K_TLV_SIGNED:
|
||||
if (len != tlv_attr->len)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
case FM10K_TLV_LE_STRUCT:
|
||||
/* struct must be 4 byte aligned */
|
||||
if ((len % 4) || len != tlv_attr->len)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
case FM10K_TLV_NESTED:
|
||||
/* nested attributes must be 4 byte aligned */
|
||||
if (len % 4)
|
||||
return FM10K_ERR_PARAM;
|
||||
break;
|
||||
default:
|
||||
/* attribute id is mapped to bad value */
|
||||
return FM10K_ERR_PARAM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_attr_parse - Parses stream of attribute data
|
||||
* @attr: Pointer to attribute list
|
||||
* @results: Pointer array to store pointers to attributes
|
||||
* @tlv_attr: Type and length info for attributes
|
||||
*
|
||||
* This function validates a stream of attributes and parses them
|
||||
* up into an array of pointers stored in results. The function will
|
||||
* return FM10K_ERR_PARAM on any input or message error,
|
||||
* FM10K_NOT_IMPLEMENTED for any attribute that is outside of the array
|
||||
* and 0 on success.
|
||||
**/
|
||||
s32 fm10k_tlv_attr_parse(u32 *attr, u32 **results,
|
||||
const struct fm10k_tlv_attr *tlv_attr)
|
||||
{
|
||||
u32 i, attr_id, offset = 0;
|
||||
s32 err = 0;
|
||||
u16 len;
|
||||
|
||||
/* verify pointers are not NULL */
|
||||
if (!attr || !results)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* initialize results to NULL */
|
||||
for (i = 0; i < FM10K_TLV_RESULTS_MAX; i++)
|
||||
results[i] = NULL;
|
||||
|
||||
/* pull length from the message header */
|
||||
len = *attr >> FM10K_TLV_LEN_SHIFT;
|
||||
|
||||
/* no attributes to parse if there is no length */
|
||||
if (!len)
|
||||
return 0;
|
||||
|
||||
/* no attributes to parse, just raw data, message becomes attribute */
|
||||
if (!tlv_attr) {
|
||||
results[0] = attr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* move to start of attribute data */
|
||||
attr++;
|
||||
|
||||
/* run through list parsing all attributes */
|
||||
while (offset < len) {
|
||||
attr_id = *attr & FM10K_TLV_ID_MASK;
|
||||
|
||||
if (attr_id < FM10K_TLV_RESULTS_MAX)
|
||||
err = fm10k_tlv_attr_validate(attr, tlv_attr);
|
||||
else
|
||||
err = FM10K_NOT_IMPLEMENTED;
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
if (!err)
|
||||
results[attr_id] = attr;
|
||||
|
||||
/* update offset */
|
||||
offset += FM10K_TLV_DWORD_LEN(*attr) * 4;
|
||||
|
||||
/* move to next attribute */
|
||||
attr = &attr[FM10K_TLV_DWORD_LEN(*attr)];
|
||||
}
|
||||
|
||||
/* we should find ourselves at the end of the list */
|
||||
if (offset != len)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_parse - Parses message header and calls function handler
|
||||
* @hw: Pointer to hardware structure
|
||||
* @msg: Pointer to message
|
||||
* @mbx: Pointer to mailbox information structure
|
||||
* @func: Function array containing list of message handling functions
|
||||
*
|
||||
* This function should be the first function called upon receiving a
|
||||
* message. The handler will identify the message type and call the correct
|
||||
* handler for the given message. It will return the value from the function
|
||||
* call on a recognized message type, otherwise it will return
|
||||
* FM10K_NOT_IMPLEMENTED on an unrecognized type.
|
||||
**/
|
||||
s32 fm10k_tlv_msg_parse(struct fm10k_hw *hw, u32 *msg,
|
||||
struct fm10k_mbx_info *mbx,
|
||||
const struct fm10k_msg_data *data)
|
||||
{
|
||||
u32 *results[FM10K_TLV_RESULTS_MAX];
|
||||
u32 msg_id;
|
||||
s32 err;
|
||||
|
||||
/* verify pointer is not NULL */
|
||||
if (!msg || !data)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* verify this is a message and not an attribute */
|
||||
if (!(*msg & (FM10K_TLV_FLAGS_MSG << FM10K_TLV_FLAGS_SHIFT)))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* grab message ID */
|
||||
msg_id = *msg & FM10K_TLV_ID_MASK;
|
||||
|
||||
while (data->id < msg_id)
|
||||
data++;
|
||||
|
||||
/* if we didn't find it then pass it up as an error */
|
||||
if (data->id != msg_id) {
|
||||
while (data->id != FM10K_TLV_ERROR)
|
||||
data++;
|
||||
}
|
||||
|
||||
/* parse the attributes into the results list */
|
||||
err = fm10k_tlv_attr_parse(msg, results, data->attr);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return data->func(hw, results, mbx);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_error - Default handler for unrecognized TLV message IDs
|
||||
* @hw: Pointer to hardware structure
|
||||
* @results: Pointer array to message, results[0] is pointer to message
|
||||
* @mbx: Unused mailbox pointer
|
||||
*
|
||||
* This function is a default handler for unrecognized messages. At a
|
||||
* a minimum it just indicates that the message requested was
|
||||
* unimplemented.
|
||||
**/
|
||||
s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *mbx)
|
||||
{
|
||||
return FM10K_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
static const unsigned char test_str[] = "fm10k";
|
||||
static const unsigned char test_mac[ETH_ALEN] = { 0x12, 0x34, 0x56,
|
||||
0x78, 0x9a, 0xbc };
|
||||
static const u16 test_vlan = 0x0FED;
|
||||
static const u64 test_u64 = 0xfedcba9876543210ull;
|
||||
static const u32 test_u32 = 0x87654321;
|
||||
static const u16 test_u16 = 0x8765;
|
||||
static const u8 test_u8 = 0x87;
|
||||
static const s64 test_s64 = -0x123456789abcdef0ll;
|
||||
static const s32 test_s32 = -0x1235678;
|
||||
static const s16 test_s16 = -0x1234;
|
||||
static const s8 test_s8 = -0x12;
|
||||
static const __le32 test_le[2] = { cpu_to_le32(0x12345678),
|
||||
cpu_to_le32(0x9abcdef0)};
|
||||
|
||||
/* The message below is meant to be used as a test message to demonstrate
|
||||
* how to use the TLV interface and to test the types. Normally this code
|
||||
* be compiled out by stripping the code wrapped in FM10K_TLV_TEST_MSG
|
||||
*/
|
||||
const struct fm10k_tlv_attr fm10k_tlv_msg_test_attr[] = {
|
||||
FM10K_TLV_ATTR_NULL_STRING(FM10K_TEST_MSG_STRING, 80),
|
||||
FM10K_TLV_ATTR_MAC_ADDR(FM10K_TEST_MSG_MAC_ADDR),
|
||||
FM10K_TLV_ATTR_U8(FM10K_TEST_MSG_U8),
|
||||
FM10K_TLV_ATTR_U16(FM10K_TEST_MSG_U16),
|
||||
FM10K_TLV_ATTR_U32(FM10K_TEST_MSG_U32),
|
||||
FM10K_TLV_ATTR_U64(FM10K_TEST_MSG_U64),
|
||||
FM10K_TLV_ATTR_S8(FM10K_TEST_MSG_S8),
|
||||
FM10K_TLV_ATTR_S16(FM10K_TEST_MSG_S16),
|
||||
FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_S32),
|
||||
FM10K_TLV_ATTR_S64(FM10K_TEST_MSG_S64),
|
||||
FM10K_TLV_ATTR_LE_STRUCT(FM10K_TEST_MSG_LE_STRUCT, 8),
|
||||
FM10K_TLV_ATTR_NESTED(FM10K_TEST_MSG_NESTED),
|
||||
FM10K_TLV_ATTR_S32(FM10K_TEST_MSG_RESULT),
|
||||
FM10K_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_test_generate_data - Stuff message with data
|
||||
* @msg: Pointer to message
|
||||
* @attr_flags: List of flags indicating what attributes to add
|
||||
*
|
||||
* This function is meant to load a message buffer with attribute data
|
||||
**/
|
||||
static void fm10k_tlv_msg_test_generate_data(u32 *msg, u32 attr_flags)
|
||||
{
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_STRING))
|
||||
fm10k_tlv_attr_put_null_string(msg, FM10K_TEST_MSG_STRING,
|
||||
test_str);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_MAC_ADDR))
|
||||
fm10k_tlv_attr_put_mac_vlan(msg, FM10K_TEST_MSG_MAC_ADDR,
|
||||
test_mac, test_vlan);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_U8))
|
||||
fm10k_tlv_attr_put_u8(msg, FM10K_TEST_MSG_U8, test_u8);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_U16))
|
||||
fm10k_tlv_attr_put_u16(msg, FM10K_TEST_MSG_U16, test_u16);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_U32))
|
||||
fm10k_tlv_attr_put_u32(msg, FM10K_TEST_MSG_U32, test_u32);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_U64))
|
||||
fm10k_tlv_attr_put_u64(msg, FM10K_TEST_MSG_U64, test_u64);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_S8))
|
||||
fm10k_tlv_attr_put_s8(msg, FM10K_TEST_MSG_S8, test_s8);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_S16))
|
||||
fm10k_tlv_attr_put_s16(msg, FM10K_TEST_MSG_S16, test_s16);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_S32))
|
||||
fm10k_tlv_attr_put_s32(msg, FM10K_TEST_MSG_S32, test_s32);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_S64))
|
||||
fm10k_tlv_attr_put_s64(msg, FM10K_TEST_MSG_S64, test_s64);
|
||||
if (attr_flags & (1 << FM10K_TEST_MSG_LE_STRUCT))
|
||||
fm10k_tlv_attr_put_le_struct(msg, FM10K_TEST_MSG_LE_STRUCT,
|
||||
test_le, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_test_create - Create a test message testing all attributes
|
||||
* @msg: Pointer to message
|
||||
* @attr_flags: List of flags indicating what attributes to add
|
||||
*
|
||||
* This function is meant to load a message buffer with all attribute types
|
||||
* including a nested attribute.
|
||||
**/
|
||||
void fm10k_tlv_msg_test_create(u32 *msg, u32 attr_flags)
|
||||
{
|
||||
u32 *nest = NULL;
|
||||
|
||||
fm10k_tlv_msg_init(msg, FM10K_TLV_MSG_ID_TEST);
|
||||
|
||||
fm10k_tlv_msg_test_generate_data(msg, attr_flags);
|
||||
|
||||
/* check for nested attributes */
|
||||
attr_flags >>= FM10K_TEST_MSG_NESTED;
|
||||
|
||||
if (attr_flags) {
|
||||
nest = fm10k_tlv_attr_nest_start(msg, FM10K_TEST_MSG_NESTED);
|
||||
|
||||
fm10k_tlv_msg_test_generate_data(nest, attr_flags);
|
||||
|
||||
fm10k_tlv_attr_nest_stop(msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_tlv_msg_test - Validate all results on test message receive
|
||||
* @hw: Pointer to hardware structure
|
||||
* @results: Pointer array to attributes in the mesage
|
||||
* @mbx: Pointer to mailbox information structure
|
||||
*
|
||||
* This function does a check to verify all attributes match what the test
|
||||
* message placed in the message buffer. It is the default handler
|
||||
* for TLV test messages.
|
||||
**/
|
||||
s32 fm10k_tlv_msg_test(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *mbx)
|
||||
{
|
||||
u32 *nest_results[FM10K_TLV_RESULTS_MAX];
|
||||
unsigned char result_str[80];
|
||||
unsigned char result_mac[ETH_ALEN];
|
||||
s32 err = 0;
|
||||
__le32 result_le[2];
|
||||
u16 result_vlan;
|
||||
u64 result_u64;
|
||||
u32 result_u32;
|
||||
u16 result_u16;
|
||||
u8 result_u8;
|
||||
s64 result_s64;
|
||||
s32 result_s32;
|
||||
s16 result_s16;
|
||||
s8 result_s8;
|
||||
u32 reply[3];
|
||||
|
||||
/* retrieve results of a previous test */
|
||||
if (!!results[FM10K_TEST_MSG_RESULT])
|
||||
return fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_RESULT],
|
||||
&mbx->test_result);
|
||||
|
||||
parse_nested:
|
||||
if (!!results[FM10K_TEST_MSG_STRING]) {
|
||||
err = fm10k_tlv_attr_get_null_string(
|
||||
results[FM10K_TEST_MSG_STRING],
|
||||
result_str);
|
||||
if (!err && memcmp(test_str, result_str, sizeof(test_str)))
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_MAC_ADDR]) {
|
||||
err = fm10k_tlv_attr_get_mac_vlan(
|
||||
results[FM10K_TEST_MSG_MAC_ADDR],
|
||||
result_mac, &result_vlan);
|
||||
if (!err && memcmp(test_mac, result_mac, ETH_ALEN))
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (!err && test_vlan != result_vlan)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_U8]) {
|
||||
err = fm10k_tlv_attr_get_u8(results[FM10K_TEST_MSG_U8],
|
||||
&result_u8);
|
||||
if (!err && test_u8 != result_u8)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_U16]) {
|
||||
err = fm10k_tlv_attr_get_u16(results[FM10K_TEST_MSG_U16],
|
||||
&result_u16);
|
||||
if (!err && test_u16 != result_u16)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_U32]) {
|
||||
err = fm10k_tlv_attr_get_u32(results[FM10K_TEST_MSG_U32],
|
||||
&result_u32);
|
||||
if (!err && test_u32 != result_u32)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_U64]) {
|
||||
err = fm10k_tlv_attr_get_u64(results[FM10K_TEST_MSG_U64],
|
||||
&result_u64);
|
||||
if (!err && test_u64 != result_u64)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_S8]) {
|
||||
err = fm10k_tlv_attr_get_s8(results[FM10K_TEST_MSG_S8],
|
||||
&result_s8);
|
||||
if (!err && test_s8 != result_s8)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_S16]) {
|
||||
err = fm10k_tlv_attr_get_s16(results[FM10K_TEST_MSG_S16],
|
||||
&result_s16);
|
||||
if (!err && test_s16 != result_s16)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_S32]) {
|
||||
err = fm10k_tlv_attr_get_s32(results[FM10K_TEST_MSG_S32],
|
||||
&result_s32);
|
||||
if (!err && test_s32 != result_s32)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_S64]) {
|
||||
err = fm10k_tlv_attr_get_s64(results[FM10K_TEST_MSG_S64],
|
||||
&result_s64);
|
||||
if (!err && test_s64 != result_s64)
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
if (!!results[FM10K_TEST_MSG_LE_STRUCT]) {
|
||||
err = fm10k_tlv_attr_get_le_struct(
|
||||
results[FM10K_TEST_MSG_LE_STRUCT],
|
||||
result_le,
|
||||
sizeof(result_le));
|
||||
if (!err && memcmp(test_le, result_le, sizeof(test_le)))
|
||||
err = FM10K_ERR_INVALID_VALUE;
|
||||
if (err)
|
||||
goto report_result;
|
||||
}
|
||||
|
||||
if (!!results[FM10K_TEST_MSG_NESTED]) {
|
||||
/* clear any pointers */
|
||||
memset(nest_results, 0, sizeof(nest_results));
|
||||
|
||||
/* parse the nested attributes into the nest results list */
|
||||
err = fm10k_tlv_attr_parse(results[FM10K_TEST_MSG_NESTED],
|
||||
nest_results,
|
||||
fm10k_tlv_msg_test_attr);
|
||||
if (err)
|
||||
goto report_result;
|
||||
|
||||
/* loop back through to the start */
|
||||
results = nest_results;
|
||||
goto parse_nested;
|
||||
}
|
||||
|
||||
report_result:
|
||||
/* generate reply with test result */
|
||||
fm10k_tlv_msg_init(reply, FM10K_TLV_MSG_ID_TEST);
|
||||
fm10k_tlv_attr_put_s32(reply, FM10K_TEST_MSG_RESULT, err);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, reply);
|
||||
}
|
186
drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
Normal file
186
drivers/net/ethernet/intel/fm10k/fm10k_tlv.h
Normal file
@ -0,0 +1,186 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_TLV_H_
|
||||
#define _FM10K_TLV_H_
|
||||
|
||||
/* forward declaration */
|
||||
struct fm10k_msg_data;
|
||||
|
||||
#include "fm10k_type.h"
|
||||
|
||||
/* Message / Argument header format
|
||||
* 3 2 1 0
|
||||
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* | Length | Flags | Type / ID |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* The message header format described here is used for messages that are
|
||||
* passed between the PF and the VF. To allow for messages larger then
|
||||
* mailbox size we will provide a message with the above header and it
|
||||
* will be segmented and transported to the mailbox to the other side where
|
||||
* it is reassembled. It contains the following fields:
|
||||
* Len: Length of the message in bytes excluding the message header
|
||||
* Flags: TBD
|
||||
* Rule: These will be the message/argument types we pass
|
||||
*/
|
||||
/* message data header */
|
||||
#define FM10K_TLV_ID_SHIFT 0
|
||||
#define FM10K_TLV_ID_SIZE 16
|
||||
#define FM10K_TLV_ID_MASK ((1u << FM10K_TLV_ID_SIZE) - 1)
|
||||
#define FM10K_TLV_FLAGS_SHIFT 16
|
||||
#define FM10K_TLV_FLAGS_MSG 0x1
|
||||
#define FM10K_TLV_FLAGS_SIZE 4
|
||||
#define FM10K_TLV_LEN_SHIFT 20
|
||||
#define FM10K_TLV_LEN_SIZE 12
|
||||
|
||||
#define FM10K_TLV_HDR_LEN 4ul
|
||||
#define FM10K_TLV_LEN_ALIGN_MASK \
|
||||
((FM10K_TLV_HDR_LEN - 1) << FM10K_TLV_LEN_SHIFT)
|
||||
#define FM10K_TLV_LEN_ALIGN(tlv) \
|
||||
(((tlv) + FM10K_TLV_LEN_ALIGN_MASK) & ~FM10K_TLV_LEN_ALIGN_MASK)
|
||||
#define FM10K_TLV_DWORD_LEN(tlv) \
|
||||
((u16)((FM10K_TLV_LEN_ALIGN(tlv)) >> (FM10K_TLV_LEN_SHIFT + 2)) + 1)
|
||||
|
||||
#define FM10K_TLV_RESULTS_MAX 32
|
||||
|
||||
enum fm10k_tlv_type {
|
||||
FM10K_TLV_NULL_STRING,
|
||||
FM10K_TLV_MAC_ADDR,
|
||||
FM10K_TLV_BOOL,
|
||||
FM10K_TLV_UNSIGNED,
|
||||
FM10K_TLV_SIGNED,
|
||||
FM10K_TLV_LE_STRUCT,
|
||||
FM10K_TLV_NESTED,
|
||||
FM10K_TLV_MAX_TYPE
|
||||
};
|
||||
|
||||
#define FM10K_TLV_ERROR (~0u)
|
||||
|
||||
struct fm10k_tlv_attr {
|
||||
unsigned int id;
|
||||
enum fm10k_tlv_type type;
|
||||
u16 len;
|
||||
};
|
||||
|
||||
#define FM10K_TLV_ATTR_NULL_STRING(id, len) { id, FM10K_TLV_NULL_STRING, len }
|
||||
#define FM10K_TLV_ATTR_MAC_ADDR(id) { id, FM10K_TLV_MAC_ADDR, 6 }
|
||||
#define FM10K_TLV_ATTR_BOOL(id) { id, FM10K_TLV_BOOL, 0 }
|
||||
#define FM10K_TLV_ATTR_U8(id) { id, FM10K_TLV_UNSIGNED, 1 }
|
||||
#define FM10K_TLV_ATTR_U16(id) { id, FM10K_TLV_UNSIGNED, 2 }
|
||||
#define FM10K_TLV_ATTR_U32(id) { id, FM10K_TLV_UNSIGNED, 4 }
|
||||
#define FM10K_TLV_ATTR_U64(id) { id, FM10K_TLV_UNSIGNED, 8 }
|
||||
#define FM10K_TLV_ATTR_S8(id) { id, FM10K_TLV_SIGNED, 1 }
|
||||
#define FM10K_TLV_ATTR_S16(id) { id, FM10K_TLV_SIGNED, 2 }
|
||||
#define FM10K_TLV_ATTR_S32(id) { id, FM10K_TLV_SIGNED, 4 }
|
||||
#define FM10K_TLV_ATTR_S64(id) { id, FM10K_TLV_SIGNED, 8 }
|
||||
#define FM10K_TLV_ATTR_LE_STRUCT(id, len) { id, FM10K_TLV_LE_STRUCT, len }
|
||||
#define FM10K_TLV_ATTR_NESTED(id) { id, FM10K_TLV_NESTED }
|
||||
#define FM10K_TLV_ATTR_LAST { FM10K_TLV_ERROR }
|
||||
|
||||
struct fm10k_msg_data {
|
||||
unsigned int id;
|
||||
const struct fm10k_tlv_attr *attr;
|
||||
s32 (*func)(struct fm10k_hw *, u32 **,
|
||||
struct fm10k_mbx_info *);
|
||||
};
|
||||
|
||||
#define FM10K_MSG_HANDLER(id, attr, func) { id, attr, func }
|
||||
|
||||
s32 fm10k_tlv_msg_init(u32 *, u16);
|
||||
s32 fm10k_tlv_attr_put_null_string(u32 *, u16, const unsigned char *);
|
||||
s32 fm10k_tlv_attr_get_null_string(u32 *, unsigned char *);
|
||||
s32 fm10k_tlv_attr_put_mac_vlan(u32 *, u16, const u8 *, u16);
|
||||
s32 fm10k_tlv_attr_get_mac_vlan(u32 *, u8 *, u16 *);
|
||||
s32 fm10k_tlv_attr_put_bool(u32 *, u16);
|
||||
s32 fm10k_tlv_attr_put_value(u32 *, u16, s64, u32);
|
||||
#define fm10k_tlv_attr_put_u8(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 1)
|
||||
#define fm10k_tlv_attr_put_u16(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 2)
|
||||
#define fm10k_tlv_attr_put_u32(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 4)
|
||||
#define fm10k_tlv_attr_put_u64(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 8)
|
||||
#define fm10k_tlv_attr_put_s8(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 1)
|
||||
#define fm10k_tlv_attr_put_s16(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 2)
|
||||
#define fm10k_tlv_attr_put_s32(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 4)
|
||||
#define fm10k_tlv_attr_put_s64(msg, attr_id, val) \
|
||||
fm10k_tlv_attr_put_value(msg, attr_id, val, 8)
|
||||
s32 fm10k_tlv_attr_get_value(u32 *, void *, u32);
|
||||
#define fm10k_tlv_attr_get_u8(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(u8))
|
||||
#define fm10k_tlv_attr_get_u16(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(u16))
|
||||
#define fm10k_tlv_attr_get_u32(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(u32))
|
||||
#define fm10k_tlv_attr_get_u64(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(u64))
|
||||
#define fm10k_tlv_attr_get_s8(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s8))
|
||||
#define fm10k_tlv_attr_get_s16(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s16))
|
||||
#define fm10k_tlv_attr_get_s32(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s32))
|
||||
#define fm10k_tlv_attr_get_s64(attr, ptr) \
|
||||
fm10k_tlv_attr_get_value(attr, ptr, sizeof(s64))
|
||||
s32 fm10k_tlv_attr_put_le_struct(u32 *, u16, const void *, u32);
|
||||
s32 fm10k_tlv_attr_get_le_struct(u32 *, void *, u32);
|
||||
u32 *fm10k_tlv_attr_nest_start(u32 *, u16);
|
||||
s32 fm10k_tlv_attr_nest_stop(u32 *);
|
||||
s32 fm10k_tlv_attr_parse(u32 *, u32 **, const struct fm10k_tlv_attr *);
|
||||
s32 fm10k_tlv_msg_parse(struct fm10k_hw *, u32 *, struct fm10k_mbx_info *,
|
||||
const struct fm10k_msg_data *);
|
||||
s32 fm10k_tlv_msg_error(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *);
|
||||
|
||||
#define FM10K_TLV_MSG_ID_TEST 0
|
||||
|
||||
enum fm10k_tlv_test_attr_id {
|
||||
FM10K_TEST_MSG_UNSET,
|
||||
FM10K_TEST_MSG_STRING,
|
||||
FM10K_TEST_MSG_MAC_ADDR,
|
||||
FM10K_TEST_MSG_U8,
|
||||
FM10K_TEST_MSG_U16,
|
||||
FM10K_TEST_MSG_U32,
|
||||
FM10K_TEST_MSG_U64,
|
||||
FM10K_TEST_MSG_S8,
|
||||
FM10K_TEST_MSG_S16,
|
||||
FM10K_TEST_MSG_S32,
|
||||
FM10K_TEST_MSG_S64,
|
||||
FM10K_TEST_MSG_LE_STRUCT,
|
||||
FM10K_TEST_MSG_NESTED,
|
||||
FM10K_TEST_MSG_RESULT,
|
||||
FM10K_TEST_MSG_MAX
|
||||
};
|
||||
|
||||
extern const struct fm10k_tlv_attr fm10k_tlv_msg_test_attr[];
|
||||
void fm10k_tlv_msg_test_create(u32 *, u32);
|
||||
s32 fm10k_tlv_msg_test(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
|
||||
|
||||
#define FM10K_TLV_MSG_TEST_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_TLV_MSG_ID_TEST, fm10k_tlv_msg_test_attr, func)
|
||||
#define FM10K_TLV_MSG_ERROR_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_TLV_ERROR, NULL, func)
|
||||
#endif /* _FM10K_MSG_H_ */
|
770
drivers/net/ethernet/intel/fm10k/fm10k_type.h
Normal file
770
drivers/net/ethernet/intel/fm10k/fm10k_type.h
Normal file
@ -0,0 +1,770 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_TYPE_H_
|
||||
#define _FM10K_TYPE_H_
|
||||
|
||||
/* forward declaration */
|
||||
struct fm10k_hw;
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
#include "fm10k_mbx.h"
|
||||
|
||||
#define FM10K_DEV_ID_PF 0x15A4
|
||||
#define FM10K_DEV_ID_VF 0x15A5
|
||||
|
||||
#define FM10K_MAX_QUEUES 256
|
||||
#define FM10K_MAX_QUEUES_PF 128
|
||||
#define FM10K_MAX_QUEUES_POOL 16
|
||||
|
||||
#define FM10K_48_BIT_MASK 0x0000FFFFFFFFFFFFull
|
||||
#define FM10K_STAT_VALID 0x80000000
|
||||
|
||||
/* PCI Bus Info */
|
||||
#define FM10K_PCIE_LINK_CAP 0x7C
|
||||
#define FM10K_PCIE_LINK_STATUS 0x82
|
||||
#define FM10K_PCIE_LINK_WIDTH 0x3F0
|
||||
#define FM10K_PCIE_LINK_WIDTH_1 0x10
|
||||
#define FM10K_PCIE_LINK_WIDTH_2 0x20
|
||||
#define FM10K_PCIE_LINK_WIDTH_4 0x40
|
||||
#define FM10K_PCIE_LINK_WIDTH_8 0x80
|
||||
#define FM10K_PCIE_LINK_SPEED 0xF
|
||||
#define FM10K_PCIE_LINK_SPEED_2500 0x1
|
||||
#define FM10K_PCIE_LINK_SPEED_5000 0x2
|
||||
#define FM10K_PCIE_LINK_SPEED_8000 0x3
|
||||
|
||||
/* PCIe payload size */
|
||||
#define FM10K_PCIE_DEV_CAP 0x74
|
||||
#define FM10K_PCIE_DEV_CAP_PAYLOAD 0x07
|
||||
#define FM10K_PCIE_DEV_CAP_PAYLOAD_128 0x00
|
||||
#define FM10K_PCIE_DEV_CAP_PAYLOAD_256 0x01
|
||||
#define FM10K_PCIE_DEV_CAP_PAYLOAD_512 0x02
|
||||
#define FM10K_PCIE_DEV_CTRL 0x78
|
||||
#define FM10K_PCIE_DEV_CTRL_PAYLOAD 0xE0
|
||||
#define FM10K_PCIE_DEV_CTRL_PAYLOAD_128 0x00
|
||||
#define FM10K_PCIE_DEV_CTRL_PAYLOAD_256 0x20
|
||||
#define FM10K_PCIE_DEV_CTRL_PAYLOAD_512 0x40
|
||||
|
||||
/* PCIe MSI-X Capability info */
|
||||
#define FM10K_PCI_MSIX_MSG_CTRL 0xB2
|
||||
#define FM10K_PCI_MSIX_MSG_CTRL_TBL_SZ_MASK 0x7FF
|
||||
#define FM10K_MAX_MSIX_VECTORS 256
|
||||
#define FM10K_MAX_VECTORS_PF 256
|
||||
#define FM10K_MAX_VECTORS_POOL 32
|
||||
|
||||
/* PCIe SR-IOV Info */
|
||||
#define FM10K_PCIE_SRIOV_CTRL 0x190
|
||||
#define FM10K_PCIE_SRIOV_CTRL_VFARI 0x10
|
||||
|
||||
#define FM10K_ERR_PARAM -2
|
||||
#define FM10K_ERR_REQUESTS_PENDING -4
|
||||
#define FM10K_ERR_RESET_REQUESTED -5
|
||||
#define FM10K_ERR_DMA_PENDING -6
|
||||
#define FM10K_ERR_RESET_FAILED -7
|
||||
#define FM10K_ERR_INVALID_MAC_ADDR -8
|
||||
#define FM10K_ERR_INVALID_VALUE -9
|
||||
#define FM10K_NOT_IMPLEMENTED 0x7FFFFFFF
|
||||
|
||||
/* Start of PF registers */
|
||||
#define FM10K_CTRL 0x0000
|
||||
#define FM10K_CTRL_BAR4_ALLOWED 0x00000004
|
||||
|
||||
#define FM10K_CTRL_EXT 0x0001
|
||||
#define FM10K_GCR 0x0003
|
||||
#define FM10K_GCR_EXT 0x0005
|
||||
|
||||
/* Interrupt control registers */
|
||||
#define FM10K_EICR 0x0006
|
||||
#define FM10K_EICR_FAULT_MASK 0x0000003F
|
||||
#define FM10K_EICR_MAILBOX 0x00000040
|
||||
#define FM10K_EICR_SWITCHREADY 0x00000080
|
||||
#define FM10K_EICR_SWITCHNOTREADY 0x00000100
|
||||
#define FM10K_EICR_SWITCHINTERRUPT 0x00000200
|
||||
#define FM10K_EICR_VFLR 0x00000800
|
||||
#define FM10K_EICR_MAXHOLDTIME 0x00001000
|
||||
#define FM10K_EIMR 0x0007
|
||||
#define FM10K_EIMR_PCA_FAULT 0x00000001
|
||||
#define FM10K_EIMR_THI_FAULT 0x00000010
|
||||
#define FM10K_EIMR_FUM_FAULT 0x00000400
|
||||
#define FM10K_EIMR_MAILBOX 0x00001000
|
||||
#define FM10K_EIMR_SWITCHREADY 0x00004000
|
||||
#define FM10K_EIMR_SWITCHNOTREADY 0x00010000
|
||||
#define FM10K_EIMR_SWITCHINTERRUPT 0x00040000
|
||||
#define FM10K_EIMR_SRAMERROR 0x00100000
|
||||
#define FM10K_EIMR_VFLR 0x00400000
|
||||
#define FM10K_EIMR_MAXHOLDTIME 0x01000000
|
||||
#define FM10K_EIMR_ALL 0x55555555
|
||||
#define FM10K_EIMR_DISABLE(NAME) ((FM10K_EIMR_ ## NAME) << 0)
|
||||
#define FM10K_EIMR_ENABLE(NAME) ((FM10K_EIMR_ ## NAME) << 1)
|
||||
#define FM10K_FAULT_ADDR_LO 0x0
|
||||
#define FM10K_FAULT_ADDR_HI 0x1
|
||||
#define FM10K_FAULT_SPECINFO 0x2
|
||||
#define FM10K_FAULT_FUNC 0x3
|
||||
#define FM10K_FAULT_SIZE 0x4
|
||||
#define FM10K_FAULT_FUNC_VALID 0x00008000
|
||||
#define FM10K_FAULT_FUNC_PF 0x00004000
|
||||
#define FM10K_FAULT_FUNC_VF_MASK 0x00003F00
|
||||
#define FM10K_FAULT_FUNC_VF_SHIFT 8
|
||||
#define FM10K_FAULT_FUNC_TYPE_MASK 0x000000FF
|
||||
|
||||
#define FM10K_PCA_FAULT 0x0008
|
||||
#define FM10K_THI_FAULT 0x0010
|
||||
#define FM10K_FUM_FAULT 0x001C
|
||||
|
||||
/* Rx queue timeout indicator */
|
||||
#define FM10K_MAXHOLDQ(_n) ((_n) + 0x0020)
|
||||
|
||||
/* Switch Manager info */
|
||||
#define FM10K_SM_AREA(_n) ((_n) + 0x0028)
|
||||
|
||||
/* GLORT mapping registers */
|
||||
#define FM10K_DGLORTMAP(_n) ((_n) + 0x0030)
|
||||
#define FM10K_DGLORT_COUNT 8
|
||||
#define FM10K_DGLORTMAP_MASK_SHIFT 16
|
||||
#define FM10K_DGLORTMAP_ANY 0x00000000
|
||||
#define FM10K_DGLORTMAP_NONE 0x0000FFFF
|
||||
#define FM10K_DGLORTMAP_ZERO 0xFFFF0000
|
||||
#define FM10K_DGLORTDEC(_n) ((_n) + 0x0038)
|
||||
#define FM10K_DGLORTDEC_VSILENGTH_SHIFT 4
|
||||
#define FM10K_DGLORTDEC_VSIBASE_SHIFT 7
|
||||
#define FM10K_DGLORTDEC_PCLENGTH_SHIFT 14
|
||||
#define FM10K_DGLORTDEC_QBASE_SHIFT 16
|
||||
#define FM10K_DGLORTDEC_RSSLENGTH_SHIFT 24
|
||||
#define FM10K_DGLORTDEC_INNERRSS_ENABLE 0x08000000
|
||||
#define FM10K_TUNNEL_CFG 0x0040
|
||||
#define FM10K_TUNNEL_CFG_NVGRE_SHIFT 16
|
||||
#define FM10K_SWPRI_MAP(_n) ((_n) + 0x0050)
|
||||
#define FM10K_SWPRI_MAX 16
|
||||
#define FM10K_RSSRK(_n, _m) (((_n) * 0x10) + (_m) + 0x0800)
|
||||
#define FM10K_RSSRK_SIZE 10
|
||||
#define FM10K_RSSRK_ENTRIES_PER_REG 4
|
||||
#define FM10K_RETA(_n, _m) (((_n) * 0x20) + (_m) + 0x1000)
|
||||
#define FM10K_RETA_SIZE 32
|
||||
#define FM10K_RETA_ENTRIES_PER_REG 4
|
||||
#define FM10K_MAX_RSS_INDICES 128
|
||||
|
||||
/* Rate limiting registers */
|
||||
#define FM10K_TC_CREDIT(_n) ((_n) + 0x2000)
|
||||
#define FM10K_TC_CREDIT_CREDIT_MASK 0x001FFFFF
|
||||
#define FM10K_TC_MAXCREDIT(_n) ((_n) + 0x2040)
|
||||
#define FM10K_TC_MAXCREDIT_64K 0x00010000
|
||||
#define FM10K_TC_RATE(_n) ((_n) + 0x2080)
|
||||
#define FM10K_TC_RATE_QUANTA_MASK 0x0000FFFF
|
||||
#define FM10K_TC_RATE_INTERVAL_4US_GEN1 0x00020000
|
||||
#define FM10K_TC_RATE_INTERVAL_4US_GEN2 0x00040000
|
||||
#define FM10K_TC_RATE_INTERVAL_4US_GEN3 0x00080000
|
||||
|
||||
/* DMA control registers */
|
||||
#define FM10K_DMA_CTRL 0x20C3
|
||||
#define FM10K_DMA_CTRL_TX_ENABLE 0x00000001
|
||||
#define FM10K_DMA_CTRL_TX_ACTIVE 0x00000008
|
||||
#define FM10K_DMA_CTRL_RX_ENABLE 0x00000010
|
||||
#define FM10K_DMA_CTRL_RX_ACTIVE 0x00000080
|
||||
#define FM10K_DMA_CTRL_RX_DESC_SIZE 0x00000100
|
||||
#define FM10K_DMA_CTRL_MINMSS_64 0x00008000
|
||||
#define FM10K_DMA_CTRL_MAX_HOLD_1US_GEN3 0x04800000
|
||||
#define FM10K_DMA_CTRL_MAX_HOLD_1US_GEN2 0x04000000
|
||||
#define FM10K_DMA_CTRL_MAX_HOLD_1US_GEN1 0x03800000
|
||||
#define FM10K_DMA_CTRL_DATAPATH_RESET 0x20000000
|
||||
#define FM10K_DMA_CTRL_32_DESC 0x00000000
|
||||
|
||||
#define FM10K_DMA_CTRL2 0x20C4
|
||||
#define FM10K_DMA_CTRL2_SWITCH_READY 0x00002000
|
||||
|
||||
/* TSO flags configuration
|
||||
* First packet contains all flags except for fin and psh
|
||||
* Middle packet contains only urg and ack
|
||||
* Last packet contains urg, ack, fin, and psh
|
||||
*/
|
||||
#define FM10K_TSO_FLAGS_LOW 0x00300FF6
|
||||
#define FM10K_TSO_FLAGS_HI 0x00000039
|
||||
#define FM10K_DTXTCPFLGL 0x20C5
|
||||
#define FM10K_DTXTCPFLGH 0x20C6
|
||||
|
||||
#define FM10K_TPH_CTRL 0x20C7
|
||||
#define FM10K_MRQC(_n) ((_n) + 0x2100)
|
||||
#define FM10K_MRQC_TCP_IPV4 0x00000001
|
||||
#define FM10K_MRQC_IPV4 0x00000002
|
||||
#define FM10K_MRQC_IPV6 0x00000010
|
||||
#define FM10K_MRQC_TCP_IPV6 0x00000020
|
||||
#define FM10K_MRQC_UDP_IPV4 0x00000040
|
||||
#define FM10K_MRQC_UDP_IPV6 0x00000080
|
||||
|
||||
#define FM10K_TQMAP(_n) ((_n) + 0x2800)
|
||||
#define FM10K_TQMAP_TABLE_SIZE 2048
|
||||
#define FM10K_RQMAP(_n) ((_n) + 0x3000)
|
||||
|
||||
/* Hardware Statistics */
|
||||
#define FM10K_STATS_TIMEOUT 0x3800
|
||||
#define FM10K_STATS_UR 0x3801
|
||||
#define FM10K_STATS_CA 0x3802
|
||||
#define FM10K_STATS_UM 0x3803
|
||||
#define FM10K_STATS_XEC 0x3804
|
||||
#define FM10K_STATS_VLAN_DROP 0x3805
|
||||
#define FM10K_STATS_LOOPBACK_DROP 0x3806
|
||||
#define FM10K_STATS_NODESC_DROP 0x3807
|
||||
|
||||
/* Timesync registers */
|
||||
#define FM10K_SYSTIME 0x3814
|
||||
#define FM10K_SYSTIME_CFG 0x3818
|
||||
#define FM10K_SYSTIME_CFG_STEP_MASK 0x0000000F
|
||||
|
||||
/* PCIe state registers */
|
||||
#define FM10K_PHYADDR 0x381C
|
||||
|
||||
/* Rx ring registers */
|
||||
#define FM10K_RDBAL(_n) ((0x40 * (_n)) + 0x4000)
|
||||
#define FM10K_RDBAH(_n) ((0x40 * (_n)) + 0x4001)
|
||||
#define FM10K_RDLEN(_n) ((0x40 * (_n)) + 0x4002)
|
||||
#define FM10K_TPH_RXCTRL(_n) ((0x40 * (_n)) + 0x4003)
|
||||
#define FM10K_TPH_RXCTRL_DESC_TPHEN 0x00000020
|
||||
#define FM10K_TPH_RXCTRL_DESC_RROEN 0x00000200
|
||||
#define FM10K_TPH_RXCTRL_DATA_WROEN 0x00002000
|
||||
#define FM10K_TPH_RXCTRL_HDR_WROEN 0x00008000
|
||||
#define FM10K_RDH(_n) ((0x40 * (_n)) + 0x4004)
|
||||
#define FM10K_RDT(_n) ((0x40 * (_n)) + 0x4005)
|
||||
#define FM10K_RXQCTL(_n) ((0x40 * (_n)) + 0x4006)
|
||||
#define FM10K_RXQCTL_ENABLE 0x00000001
|
||||
#define FM10K_RXQCTL_PF 0x000000FC
|
||||
#define FM10K_RXQCTL_VF_SHIFT 2
|
||||
#define FM10K_RXQCTL_VF 0x00000100
|
||||
#define FM10K_RXQCTL_ID_MASK (FM10K_RXQCTL_PF | FM10K_RXQCTL_VF)
|
||||
#define FM10K_RXDCTL(_n) ((0x40 * (_n)) + 0x4007)
|
||||
#define FM10K_RXDCTL_WRITE_BACK_MIN_DELAY 0x00000001
|
||||
#define FM10K_RXDCTL_DROP_ON_EMPTY 0x00000200
|
||||
#define FM10K_RXINT(_n) ((0x40 * (_n)) + 0x4008)
|
||||
#define FM10K_SRRCTL(_n) ((0x40 * (_n)) + 0x4009)
|
||||
#define FM10K_SRRCTL_BSIZEPKT_SHIFT 8 /* shift _right_ */
|
||||
#define FM10K_SRRCTL_LOOPBACK_SUPPRESS 0x40000000
|
||||
#define FM10K_SRRCTL_BUFFER_CHAINING_EN 0x80000000
|
||||
|
||||
/* Rx Statistics */
|
||||
#define FM10K_QPRC(_n) ((0x40 * (_n)) + 0x400A)
|
||||
#define FM10K_QPRDC(_n) ((0x40 * (_n)) + 0x400B)
|
||||
#define FM10K_QBRC_L(_n) ((0x40 * (_n)) + 0x400C)
|
||||
#define FM10K_QBRC_H(_n) ((0x40 * (_n)) + 0x400D)
|
||||
|
||||
/* Rx GLORT register */
|
||||
#define FM10K_RX_SGLORT(_n) ((0x40 * (_n)) + 0x400E)
|
||||
|
||||
/* Tx ring registers */
|
||||
#define FM10K_TDBAL(_n) ((0x40 * (_n)) + 0x8000)
|
||||
#define FM10K_TDBAH(_n) ((0x40 * (_n)) + 0x8001)
|
||||
#define FM10K_TDLEN(_n) ((0x40 * (_n)) + 0x8002)
|
||||
#define FM10K_TPH_TXCTRL(_n) ((0x40 * (_n)) + 0x8003)
|
||||
#define FM10K_TPH_TXCTRL_DESC_TPHEN 0x00000020
|
||||
#define FM10K_TPH_TXCTRL_DESC_RROEN 0x00000200
|
||||
#define FM10K_TPH_TXCTRL_DESC_WROEN 0x00000800
|
||||
#define FM10K_TPH_TXCTRL_DATA_RROEN 0x00002000
|
||||
#define FM10K_TDH(_n) ((0x40 * (_n)) + 0x8004)
|
||||
#define FM10K_TDT(_n) ((0x40 * (_n)) + 0x8005)
|
||||
#define FM10K_TXDCTL(_n) ((0x40 * (_n)) + 0x8006)
|
||||
#define FM10K_TXDCTL_ENABLE 0x00004000
|
||||
#define FM10K_TXDCTL_MAX_TIME_SHIFT 16
|
||||
#define FM10K_TXQCTL(_n) ((0x40 * (_n)) + 0x8007)
|
||||
#define FM10K_TXQCTL_PF 0x0000003F
|
||||
#define FM10K_TXQCTL_VF 0x00000040
|
||||
#define FM10K_TXQCTL_ID_MASK (FM10K_TXQCTL_PF | FM10K_TXQCTL_VF)
|
||||
#define FM10K_TXQCTL_PC_SHIFT 7
|
||||
#define FM10K_TXQCTL_PC_MASK 0x00000380
|
||||
#define FM10K_TXQCTL_TC_SHIFT 10
|
||||
#define FM10K_TXQCTL_VID_SHIFT 16
|
||||
#define FM10K_TXQCTL_VID_MASK 0x0FFF0000
|
||||
#define FM10K_TXQCTL_UNLIMITED_BW 0x10000000
|
||||
#define FM10K_TXINT(_n) ((0x40 * (_n)) + 0x8008)
|
||||
|
||||
/* Tx Statistics */
|
||||
#define FM10K_QPTC(_n) ((0x40 * (_n)) + 0x8009)
|
||||
#define FM10K_QBTC_L(_n) ((0x40 * (_n)) + 0x800A)
|
||||
#define FM10K_QBTC_H(_n) ((0x40 * (_n)) + 0x800B)
|
||||
|
||||
/* Tx Push registers */
|
||||
#define FM10K_TQDLOC(_n) ((0x40 * (_n)) + 0x800C)
|
||||
#define FM10K_TQDLOC_BASE_32_DESC 0x08
|
||||
#define FM10K_TQDLOC_SIZE_32_DESC 0x00050000
|
||||
|
||||
/* Tx GLORT registers */
|
||||
#define FM10K_TX_SGLORT(_n) ((0x40 * (_n)) + 0x800D)
|
||||
#define FM10K_PFVTCTL(_n) ((0x40 * (_n)) + 0x800E)
|
||||
#define FM10K_PFVTCTL_FTAG_DESC_ENABLE 0x00000001
|
||||
|
||||
/* Interrupt moderation and control registers */
|
||||
#define FM10K_INT_MAP(_n) ((_n) + 0x10080)
|
||||
#define FM10K_INT_MAP_TIMER0 0x00000000
|
||||
#define FM10K_INT_MAP_TIMER1 0x00000100
|
||||
#define FM10K_INT_MAP_IMMEDIATE 0x00000200
|
||||
#define FM10K_INT_MAP_DISABLE 0x00000300
|
||||
#define FM10K_MSIX_VECTOR_MASK(_n) ((0x4 * (_n)) + 0x11003)
|
||||
#define FM10K_INT_CTRL 0x12000
|
||||
#define FM10K_INT_CTRL_ENABLEMODERATOR 0x00000400
|
||||
#define FM10K_ITR(_n) ((_n) + 0x12400)
|
||||
#define FM10K_ITR_INTERVAL1_SHIFT 12
|
||||
#define FM10K_ITR_PENDING2 0x10000000
|
||||
#define FM10K_ITR_AUTOMASK 0x20000000
|
||||
#define FM10K_ITR_MASK_SET 0x40000000
|
||||
#define FM10K_ITR_MASK_CLEAR 0x80000000
|
||||
#define FM10K_ITR2(_n) ((0x2 * (_n)) + 0x12800)
|
||||
#define FM10K_ITR_REG_COUNT 768
|
||||
#define FM10K_ITR_REG_COUNT_PF 256
|
||||
|
||||
/* Switch manager interrupt registers */
|
||||
#define FM10K_IP 0x13000
|
||||
#define FM10K_IP_NOTINRESET 0x00000100
|
||||
|
||||
/* VLAN registers */
|
||||
#define FM10K_VLAN_TABLE(_n, _m) ((0x80 * (_n)) + (_m) + 0x14000)
|
||||
#define FM10K_VLAN_TABLE_SIZE 128
|
||||
|
||||
/* VLAN specific message offsets */
|
||||
#define FM10K_VLAN_TABLE_VID_MAX 4096
|
||||
#define FM10K_VLAN_TABLE_VSI_MAX 64
|
||||
#define FM10K_VLAN_LENGTH_SHIFT 16
|
||||
#define FM10K_VLAN_CLEAR (1 << 15)
|
||||
#define FM10K_VLAN_ALL \
|
||||
((FM10K_VLAN_TABLE_VID_MAX - 1) << FM10K_VLAN_LENGTH_SHIFT)
|
||||
|
||||
/* VF FLR event notification registers */
|
||||
#define FM10K_PFVFLRE(_n) ((0x1 * (_n)) + 0x18844)
|
||||
#define FM10K_PFVFLREC(_n) ((0x1 * (_n)) + 0x18846)
|
||||
|
||||
/* Defines for size of uncacheable memories */
|
||||
#define FM10K_UC_ADDR_START 0x000000 /* start of standard regs */
|
||||
#define FM10K_UC_ADDR_END 0x100000 /* end of standard regs */
|
||||
#define FM10K_UC_ADDR_SIZE (FM10K_UC_ADDR_END - FM10K_UC_ADDR_START)
|
||||
|
||||
/* Define timeouts for resets and disables */
|
||||
#define FM10K_QUEUE_DISABLE_TIMEOUT 100
|
||||
#define FM10K_RESET_TIMEOUT 100
|
||||
|
||||
/* VF registers */
|
||||
#define FM10K_VFCTRL 0x00000
|
||||
#define FM10K_VFCTRL_RST 0x00000008
|
||||
#define FM10K_VFINT_MAP 0x00030
|
||||
#define FM10K_VFSYSTIME 0x00040
|
||||
#define FM10K_VFITR(_n) ((_n) + 0x00060)
|
||||
|
||||
/* Registers contained in BAR 4 for Switch management */
|
||||
#define FM10K_SW_SYSTIME_ADJUST 0x0224D
|
||||
#define FM10K_SW_SYSTIME_ADJUST_MASK 0x3FFFFFFF
|
||||
#define FM10K_SW_SYSTIME_ADJUST_DIR_NEGATIVE 0x80000000
|
||||
#define FM10K_SW_SYSTIME_PULSE(_n) ((_n) + 0x02252)
|
||||
|
||||
enum fm10k_int_source {
|
||||
fm10k_int_Mailbox = 0,
|
||||
fm10k_int_PCIeFault = 1,
|
||||
fm10k_int_SwitchUpDown = 2,
|
||||
fm10k_int_SwitchEvent = 3,
|
||||
fm10k_int_SRAM = 4,
|
||||
fm10k_int_VFLR = 5,
|
||||
fm10k_int_MaxHoldTime = 6,
|
||||
fm10k_int_sources_max_pf
|
||||
};
|
||||
|
||||
/* PCIe bus speeds */
|
||||
enum fm10k_bus_speed {
|
||||
fm10k_bus_speed_unknown = 0,
|
||||
fm10k_bus_speed_2500 = 2500,
|
||||
fm10k_bus_speed_5000 = 5000,
|
||||
fm10k_bus_speed_8000 = 8000,
|
||||
fm10k_bus_speed_reserved
|
||||
};
|
||||
|
||||
/* PCIe bus widths */
|
||||
enum fm10k_bus_width {
|
||||
fm10k_bus_width_unknown = 0,
|
||||
fm10k_bus_width_pcie_x1 = 1,
|
||||
fm10k_bus_width_pcie_x2 = 2,
|
||||
fm10k_bus_width_pcie_x4 = 4,
|
||||
fm10k_bus_width_pcie_x8 = 8,
|
||||
fm10k_bus_width_reserved
|
||||
};
|
||||
|
||||
/* PCIe payload sizes */
|
||||
enum fm10k_bus_payload {
|
||||
fm10k_bus_payload_unknown = 0,
|
||||
fm10k_bus_payload_128 = 1,
|
||||
fm10k_bus_payload_256 = 2,
|
||||
fm10k_bus_payload_512 = 3,
|
||||
fm10k_bus_payload_reserved
|
||||
};
|
||||
|
||||
/* Bus parameters */
|
||||
struct fm10k_bus_info {
|
||||
enum fm10k_bus_speed speed;
|
||||
enum fm10k_bus_width width;
|
||||
enum fm10k_bus_payload payload;
|
||||
};
|
||||
|
||||
/* Statistics related declarations */
|
||||
struct fm10k_hw_stat {
|
||||
u64 count;
|
||||
u32 base_l;
|
||||
u32 base_h;
|
||||
};
|
||||
|
||||
struct fm10k_hw_stats_q {
|
||||
struct fm10k_hw_stat tx_bytes;
|
||||
struct fm10k_hw_stat tx_packets;
|
||||
#define tx_stats_idx tx_packets.base_h
|
||||
struct fm10k_hw_stat rx_bytes;
|
||||
struct fm10k_hw_stat rx_packets;
|
||||
#define rx_stats_idx rx_packets.base_h
|
||||
struct fm10k_hw_stat rx_drops;
|
||||
};
|
||||
|
||||
struct fm10k_hw_stats {
|
||||
struct fm10k_hw_stat timeout;
|
||||
#define stats_idx timeout.base_h
|
||||
struct fm10k_hw_stat ur;
|
||||
struct fm10k_hw_stat ca;
|
||||
struct fm10k_hw_stat um;
|
||||
struct fm10k_hw_stat xec;
|
||||
struct fm10k_hw_stat vlan_drop;
|
||||
struct fm10k_hw_stat loopback_drop;
|
||||
struct fm10k_hw_stat nodesc_drop;
|
||||
struct fm10k_hw_stats_q q[FM10K_MAX_QUEUES_PF];
|
||||
};
|
||||
|
||||
/* Establish DGLORT feature priority */
|
||||
enum fm10k_dglortdec_idx {
|
||||
fm10k_dglort_default = 0,
|
||||
fm10k_dglort_vf_rsvd0 = 1,
|
||||
fm10k_dglort_vf_rss = 2,
|
||||
fm10k_dglort_pf_rsvd0 = 3,
|
||||
fm10k_dglort_pf_queue = 4,
|
||||
fm10k_dglort_pf_vsi = 5,
|
||||
fm10k_dglort_pf_rsvd1 = 6,
|
||||
fm10k_dglort_pf_rss = 7
|
||||
};
|
||||
|
||||
struct fm10k_dglort_cfg {
|
||||
u16 glort; /* GLORT base */
|
||||
u16 queue_b; /* Base value for queue */
|
||||
u8 vsi_b; /* Base value for VSI */
|
||||
u8 idx; /* index of DGLORTDEC entry */
|
||||
u8 rss_l; /* RSS indices */
|
||||
u8 pc_l; /* Priority Class indices */
|
||||
u8 vsi_l; /* Number of bits from GLORT used to determine VSI */
|
||||
u8 queue_l; /* Number of bits from GLORT used to determine queue */
|
||||
u8 shared_l; /* Ignored bits from GLORT resulting in shared VSI */
|
||||
u8 inner_rss; /* Boolean value if inner header is used for RSS */
|
||||
};
|
||||
|
||||
enum fm10k_pca_fault {
|
||||
PCA_NO_FAULT,
|
||||
PCA_UNMAPPED_ADDR,
|
||||
PCA_BAD_QACCESS_PF,
|
||||
PCA_BAD_QACCESS_VF,
|
||||
PCA_MALICIOUS_REQ,
|
||||
PCA_POISONED_TLP,
|
||||
PCA_TLP_ABORT,
|
||||
__PCA_MAX
|
||||
};
|
||||
|
||||
enum fm10k_thi_fault {
|
||||
THI_NO_FAULT,
|
||||
THI_MAL_DIS_Q_FAULT,
|
||||
__THI_MAX
|
||||
};
|
||||
|
||||
enum fm10k_fum_fault {
|
||||
FUM_NO_FAULT,
|
||||
FUM_UNMAPPED_ADDR,
|
||||
FUM_POISONED_TLP,
|
||||
FUM_BAD_VF_QACCESS,
|
||||
FUM_ADD_DECODE_ERR,
|
||||
FUM_RO_ERROR,
|
||||
FUM_QPRC_CRC_ERROR,
|
||||
FUM_CSR_TIMEOUT,
|
||||
FUM_INVALID_TYPE,
|
||||
FUM_INVALID_LENGTH,
|
||||
FUM_INVALID_BE,
|
||||
FUM_INVALID_ALIGN,
|
||||
__FUM_MAX
|
||||
};
|
||||
|
||||
struct fm10k_fault {
|
||||
u64 address; /* Address at the time fault was detected */
|
||||
u32 specinfo; /* Extra info on this fault (fault dependent) */
|
||||
u8 type; /* Fault value dependent on subunit */
|
||||
u8 func; /* Function number of the fault */
|
||||
};
|
||||
|
||||
struct fm10k_mac_ops {
|
||||
/* basic bring-up and tear-down */
|
||||
s32 (*reset_hw)(struct fm10k_hw *);
|
||||
s32 (*init_hw)(struct fm10k_hw *);
|
||||
s32 (*start_hw)(struct fm10k_hw *);
|
||||
s32 (*stop_hw)(struct fm10k_hw *);
|
||||
s32 (*get_bus_info)(struct fm10k_hw *);
|
||||
s32 (*get_host_state)(struct fm10k_hw *, bool *);
|
||||
bool (*is_slot_appropriate)(struct fm10k_hw *);
|
||||
s32 (*update_vlan)(struct fm10k_hw *, u32, u8, bool);
|
||||
s32 (*read_mac_addr)(struct fm10k_hw *);
|
||||
s32 (*update_uc_addr)(struct fm10k_hw *, u16, const u8 *,
|
||||
u16, bool, u8);
|
||||
s32 (*update_mc_addr)(struct fm10k_hw *, u16, const u8 *, u16, bool);
|
||||
s32 (*update_xcast_mode)(struct fm10k_hw *, u16, u8);
|
||||
void (*update_int_moderator)(struct fm10k_hw *);
|
||||
s32 (*update_lport_state)(struct fm10k_hw *, u16, u16, bool);
|
||||
void (*update_hw_stats)(struct fm10k_hw *, struct fm10k_hw_stats *);
|
||||
void (*rebind_hw_stats)(struct fm10k_hw *, struct fm10k_hw_stats *);
|
||||
s32 (*configure_dglort_map)(struct fm10k_hw *,
|
||||
struct fm10k_dglort_cfg *);
|
||||
void (*set_dma_mask)(struct fm10k_hw *, u64);
|
||||
s32 (*get_fault)(struct fm10k_hw *, int, struct fm10k_fault *);
|
||||
void (*request_lport_map)(struct fm10k_hw *);
|
||||
s32 (*adjust_systime)(struct fm10k_hw *, s32 ppb);
|
||||
u64 (*read_systime)(struct fm10k_hw *);
|
||||
};
|
||||
|
||||
enum fm10k_mac_type {
|
||||
fm10k_mac_unknown = 0,
|
||||
fm10k_mac_pf,
|
||||
fm10k_mac_vf,
|
||||
fm10k_num_macs
|
||||
};
|
||||
|
||||
struct fm10k_mac_info {
|
||||
struct fm10k_mac_ops ops;
|
||||
enum fm10k_mac_type type;
|
||||
u8 addr[ETH_ALEN];
|
||||
u8 perm_addr[ETH_ALEN];
|
||||
u16 default_vid;
|
||||
u16 max_msix_vectors;
|
||||
u16 max_queues;
|
||||
bool vlan_override;
|
||||
bool get_host_state;
|
||||
bool tx_ready;
|
||||
u32 dglort_map;
|
||||
};
|
||||
|
||||
struct fm10k_swapi_table_info {
|
||||
u32 used;
|
||||
u32 avail;
|
||||
};
|
||||
|
||||
struct fm10k_swapi_info {
|
||||
u32 status;
|
||||
struct fm10k_swapi_table_info mac;
|
||||
struct fm10k_swapi_table_info nexthop;
|
||||
struct fm10k_swapi_table_info ffu;
|
||||
};
|
||||
|
||||
enum fm10k_xcast_modes {
|
||||
FM10K_XCAST_MODE_ALLMULTI = 0,
|
||||
FM10K_XCAST_MODE_MULTI = 1,
|
||||
FM10K_XCAST_MODE_PROMISC = 2,
|
||||
FM10K_XCAST_MODE_NONE = 3,
|
||||
FM10K_XCAST_MODE_DISABLE = 4
|
||||
};
|
||||
|
||||
#define FM10K_VF_TC_MAX 100000 /* 100,000 Mb/s aka 100Gb/s */
|
||||
#define FM10K_VF_TC_MIN 1 /* 1 Mb/s is the slowest rate */
|
||||
|
||||
struct fm10k_vf_info {
|
||||
/* mbx must be first field in struct unless all default IOV message
|
||||
* handlers are redone as the assumption is that vf_info starts
|
||||
* at the same offset as the mailbox
|
||||
*/
|
||||
struct fm10k_mbx_info mbx; /* PF side of VF mailbox */
|
||||
int rate; /* Tx BW cap as defined by OS */
|
||||
u16 glort; /* resource tag for this VF */
|
||||
u16 sw_vid; /* Switch API assigned VLAN */
|
||||
u16 pf_vid; /* PF assigned Default VLAN */
|
||||
u8 mac[ETH_ALEN]; /* PF Default MAC address */
|
||||
u8 vsi; /* VSI idenfifier */
|
||||
u8 vf_idx; /* which VF this is */
|
||||
u8 vf_flags; /* flags indicating what modes
|
||||
* are supported for the port
|
||||
*/
|
||||
};
|
||||
|
||||
#define FM10K_VF_FLAG_ALLMULTI_CAPABLE ((u8)1 << FM10K_XCAST_MODE_ALLMULTI)
|
||||
#define FM10K_VF_FLAG_MULTI_CAPABLE ((u8)1 << FM10K_XCAST_MODE_MULTI)
|
||||
#define FM10K_VF_FLAG_PROMISC_CAPABLE ((u8)1 << FM10K_XCAST_MODE_PROMISC)
|
||||
#define FM10K_VF_FLAG_NONE_CAPABLE ((u8)1 << FM10K_XCAST_MODE_NONE)
|
||||
#define FM10K_VF_FLAG_CAPABLE(vf_info) ((vf_info)->vf_flags & (u8)0xF)
|
||||
#define FM10K_VF_FLAG_ENABLED(vf_info) ((vf_info)->vf_flags >> 4)
|
||||
#define FM10K_VF_FLAG_SET_MODE(mode) ((u8)0x10 << (mode))
|
||||
#define FM10K_VF_FLAG_SET_MODE_NONE \
|
||||
FM10K_VF_FLAG_SET_MODE(FM10K_XCAST_MODE_NONE)
|
||||
#define FM10K_VF_FLAG_MULTI_ENABLED \
|
||||
(FM10K_VF_FLAG_SET_MODE(FM10K_XCAST_MODE_ALLMULTI) | \
|
||||
FM10K_VF_FLAG_SET_MODE(FM10K_XCAST_MODE_MULTI) | \
|
||||
FM10K_VF_FLAG_SET_MODE(FM10K_XCAST_MODE_PROMISC))
|
||||
|
||||
struct fm10k_iov_ops {
|
||||
/* IOV related bring-up and tear-down */
|
||||
s32 (*assign_resources)(struct fm10k_hw *, u16, u16);
|
||||
s32 (*configure_tc)(struct fm10k_hw *, u16, int);
|
||||
s32 (*assign_int_moderator)(struct fm10k_hw *, u16);
|
||||
s32 (*assign_default_mac_vlan)(struct fm10k_hw *,
|
||||
struct fm10k_vf_info *);
|
||||
s32 (*reset_resources)(struct fm10k_hw *,
|
||||
struct fm10k_vf_info *);
|
||||
s32 (*set_lport)(struct fm10k_hw *, struct fm10k_vf_info *, u16, u8);
|
||||
void (*reset_lport)(struct fm10k_hw *, struct fm10k_vf_info *);
|
||||
void (*update_stats)(struct fm10k_hw *, struct fm10k_hw_stats_q *, u16);
|
||||
s32 (*report_timestamp)(struct fm10k_hw *, struct fm10k_vf_info *, u64);
|
||||
};
|
||||
|
||||
struct fm10k_iov_info {
|
||||
struct fm10k_iov_ops ops;
|
||||
u16 total_vfs;
|
||||
u16 num_vfs;
|
||||
u16 num_pools;
|
||||
};
|
||||
|
||||
enum fm10k_devices {
|
||||
fm10k_device_pf,
|
||||
fm10k_device_vf,
|
||||
};
|
||||
|
||||
struct fm10k_info {
|
||||
enum fm10k_mac_type mac;
|
||||
s32 (*get_invariants)(struct fm10k_hw *);
|
||||
struct fm10k_mac_ops *mac_ops;
|
||||
struct fm10k_iov_ops *iov_ops;
|
||||
};
|
||||
|
||||
struct fm10k_hw {
|
||||
u32 __iomem *hw_addr;
|
||||
u32 __iomem *sw_addr;
|
||||
void *back;
|
||||
struct fm10k_mac_info mac;
|
||||
struct fm10k_bus_info bus;
|
||||
struct fm10k_bus_info bus_caps;
|
||||
struct fm10k_iov_info iov;
|
||||
struct fm10k_mbx_info mbx;
|
||||
struct fm10k_swapi_info swapi;
|
||||
u16 device_id;
|
||||
u16 vendor_id;
|
||||
u16 subsystem_device_id;
|
||||
u16 subsystem_vendor_id;
|
||||
u8 revision_id;
|
||||
};
|
||||
|
||||
/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
|
||||
#define FM10K_REQ_TX_DESCRIPTOR_MULTIPLE 8
|
||||
#define FM10K_REQ_RX_DESCRIPTOR_MULTIPLE 8
|
||||
|
||||
/* Transmit Descriptor */
|
||||
struct fm10k_tx_desc {
|
||||
__le64 buffer_addr; /* Address of the descriptor's data buffer */
|
||||
__le16 buflen; /* Length of data to be DMAed */
|
||||
__le16 vlan; /* VLAN_ID and VPRI to be inserted in FTAG */
|
||||
__le16 mss; /* MSS for segmentation offload */
|
||||
u8 hdrlen; /* Header size for segmentation offload */
|
||||
u8 flags; /* Status and offload request flags */
|
||||
};
|
||||
|
||||
/* Transmit Descriptor Cache Structure */
|
||||
struct fm10k_tx_desc_cache {
|
||||
struct fm10k_tx_desc tx_desc[256];
|
||||
};
|
||||
|
||||
#define FM10K_TXD_FLAG_INT 0x01
|
||||
#define FM10K_TXD_FLAG_TIME 0x02
|
||||
#define FM10K_TXD_FLAG_CSUM 0x04
|
||||
#define FM10K_TXD_FLAG_FTAG 0x10
|
||||
#define FM10K_TXD_FLAG_RS 0x20
|
||||
#define FM10K_TXD_FLAG_LAST 0x40
|
||||
#define FM10K_TXD_FLAG_DONE 0x80
|
||||
|
||||
/* These macros are meant to enable optimal placement of the RS and INT
|
||||
* bits. It will point us to the last descriptor in the cache for either the
|
||||
* start of the packet, or the end of the packet. If the index is actually
|
||||
* at the start of the FIFO it will point to the offset for the last index
|
||||
* in the FIFO to prevent an unnecessary write.
|
||||
*/
|
||||
#define FM10K_TXD_WB_FIFO_SIZE 4
|
||||
|
||||
/* Receive Descriptor - 32B */
|
||||
union fm10k_rx_desc {
|
||||
struct {
|
||||
__le64 pkt_addr; /* Packet buffer address */
|
||||
__le64 hdr_addr; /* Header buffer address */
|
||||
__le64 reserved; /* Empty space, RSS hash */
|
||||
__le64 timestamp;
|
||||
} q; /* Read, Writeback, 64b quad-words */
|
||||
struct {
|
||||
__le32 data; /* RSS and header data */
|
||||
__le32 rss; /* RSS Hash */
|
||||
__le32 staterr;
|
||||
__le32 vlan_len;
|
||||
__le32 glort; /* sglort/dglort */
|
||||
} d; /* Writeback, 32b double-words */
|
||||
struct {
|
||||
__le16 pkt_info; /* RSS, Pkt type */
|
||||
__le16 hdr_info; /* Splithdr, hdrlen, xC */
|
||||
__le16 rss_lower;
|
||||
__le16 rss_upper;
|
||||
__le16 status; /* status/error */
|
||||
__le16 csum_err; /* checksum or extended error value */
|
||||
__le16 length; /* Packet length */
|
||||
__le16 vlan; /* VLAN tag */
|
||||
__le16 dglort;
|
||||
__le16 sglort;
|
||||
} w; /* Writeback, 16b words */
|
||||
};
|
||||
|
||||
#define FM10K_RXD_RSSTYPE_MASK 0x000F
|
||||
enum fm10k_rdesc_rss_type {
|
||||
FM10K_RSSTYPE_NONE = 0x0,
|
||||
FM10K_RSSTYPE_IPV4_TCP = 0x1,
|
||||
FM10K_RSSTYPE_IPV4 = 0x2,
|
||||
FM10K_RSSTYPE_IPV6_TCP = 0x3,
|
||||
/* Reserved 0x4 */
|
||||
FM10K_RSSTYPE_IPV6 = 0x5,
|
||||
/* Reserved 0x6 */
|
||||
FM10K_RSSTYPE_IPV4_UDP = 0x7,
|
||||
FM10K_RSSTYPE_IPV6_UDP = 0x8
|
||||
/* Reserved 0x9 - 0xF */
|
||||
};
|
||||
|
||||
#define FM10K_RXD_HDR_INFO_XC_MASK 0x0006
|
||||
enum fm10k_rxdesc_xc {
|
||||
FM10K_XC_UNICAST = 0x0,
|
||||
FM10K_XC_MULTICAST = 0x4,
|
||||
FM10K_XC_BROADCAST = 0x6
|
||||
};
|
||||
|
||||
#define FM10K_RXD_STATUS_DD 0x0001 /* Descriptor done */
|
||||
#define FM10K_RXD_STATUS_EOP 0x0002 /* End of packet */
|
||||
#define FM10K_RXD_STATUS_L4CS 0x0010 /* Indicates an L4 csum */
|
||||
#define FM10K_RXD_STATUS_L4CS2 0x0040 /* Inner header L4 csum */
|
||||
#define FM10K_RXD_STATUS_L4E2 0x0800 /* Inner header L4 csum err */
|
||||
#define FM10K_RXD_STATUS_IPE2 0x1000 /* Inner header IPv4 csum err */
|
||||
#define FM10K_RXD_STATUS_RXE 0x2000 /* Generic Rx error */
|
||||
#define FM10K_RXD_STATUS_L4E 0x4000 /* L4 csum error */
|
||||
#define FM10K_RXD_STATUS_IPE 0x8000 /* IPv4 csum error */
|
||||
|
||||
struct fm10k_ftag {
|
||||
__be16 swpri_type_user;
|
||||
__be16 vlan;
|
||||
__be16 sglort;
|
||||
__be16 dglort;
|
||||
};
|
||||
|
||||
#endif /* _FM10K_TYPE_H */
|
578
drivers/net/ethernet/intel/fm10k/fm10k_vf.c
Normal file
578
drivers/net/ethernet/intel/fm10k/fm10k_vf.c
Normal file
@ -0,0 +1,578 @@
|
||||
/* 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_vf.h"
|
||||
|
||||
/**
|
||||
* fm10k_stop_hw_vf - Stop Tx/Rx units
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
**/
|
||||
static s32 fm10k_stop_hw_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
u8 *perm_addr = hw->mac.perm_addr;
|
||||
u32 bal = 0, bah = 0;
|
||||
s32 err;
|
||||
u16 i;
|
||||
|
||||
/* we need to disable the queues before taking further steps */
|
||||
err = fm10k_stop_hw_generic(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* If permenant address is set then we need to restore it */
|
||||
if (is_valid_ether_addr(perm_addr)) {
|
||||
bal = (((u32)perm_addr[3]) << 24) |
|
||||
(((u32)perm_addr[4]) << 16) |
|
||||
(((u32)perm_addr[5]) << 8);
|
||||
bah = (((u32)0xFF) << 24) |
|
||||
(((u32)perm_addr[0]) << 16) |
|
||||
(((u32)perm_addr[1]) << 8) |
|
||||
((u32)perm_addr[2]);
|
||||
}
|
||||
|
||||
/* The queues have already been disabled so we just need to
|
||||
* update their base address registers
|
||||
*/
|
||||
for (i = 0; i < hw->mac.max_queues; i++) {
|
||||
fm10k_write_reg(hw, FM10K_TDBAL(i), bal);
|
||||
fm10k_write_reg(hw, FM10K_TDBAH(i), bah);
|
||||
fm10k_write_reg(hw, FM10K_RDBAL(i), bal);
|
||||
fm10k_write_reg(hw, FM10K_RDBAH(i), bah);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_reset_hw_vf - VF hardware reset
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* This function should return the hardare to a state similar to the
|
||||
* one it is in after just being initialized.
|
||||
**/
|
||||
static s32 fm10k_reset_hw_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
s32 err;
|
||||
|
||||
/* shut down queues we own and reset DMA configuration */
|
||||
err = fm10k_stop_hw_vf(hw);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Inititate VF reset */
|
||||
fm10k_write_reg(hw, FM10K_VFCTRL, FM10K_VFCTRL_RST);
|
||||
|
||||
/* Flush write and allow 100us for reset to complete */
|
||||
fm10k_write_flush(hw);
|
||||
udelay(FM10K_RESET_TIMEOUT);
|
||||
|
||||
/* Clear reset bit and verify it was cleared */
|
||||
fm10k_write_reg(hw, FM10K_VFCTRL, 0);
|
||||
if (fm10k_read_reg(hw, FM10K_VFCTRL) & FM10K_VFCTRL_RST)
|
||||
err = FM10K_ERR_RESET_FAILED;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_init_hw_vf - VF hardware initialization
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
**/
|
||||
static s32 fm10k_init_hw_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
u32 tqdloc, tqdloc0 = ~fm10k_read_reg(hw, FM10K_TQDLOC(0));
|
||||
s32 err;
|
||||
u16 i;
|
||||
|
||||
/* assume we always have at least 1 queue */
|
||||
for (i = 1; tqdloc0 && (i < FM10K_MAX_QUEUES_POOL); i++) {
|
||||
/* verify the Descriptor cache offsets are increasing */
|
||||
tqdloc = ~fm10k_read_reg(hw, FM10K_TQDLOC(i));
|
||||
if (!tqdloc || (tqdloc == tqdloc0))
|
||||
break;
|
||||
|
||||
/* check to verify the PF doesn't own any of our queues */
|
||||
if (!~fm10k_read_reg(hw, FM10K_TXQCTL(i)) ||
|
||||
!~fm10k_read_reg(hw, FM10K_RXQCTL(i)))
|
||||
break;
|
||||
}
|
||||
|
||||
/* shut down queues we own and reset DMA configuration */
|
||||
err = fm10k_disable_queues_generic(hw, i);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* record maximum queue count */
|
||||
hw->mac.max_queues = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_is_slot_appropriate_vf - Indicate appropriate slot for this SKU
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* Looks at the PCIe bus info to confirm whether or not this slot can support
|
||||
* the necessary bandwidth for this device. Since the VF has no control over
|
||||
* the "slot" it is in, always indicate that the slot is appropriate.
|
||||
**/
|
||||
static bool fm10k_is_slot_appropriate_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This structure defines the attibutes to be parsed below */
|
||||
const struct fm10k_tlv_attr fm10k_mac_vlan_msg_attr[] = {
|
||||
FM10K_TLV_ATTR_U32(FM10K_MAC_VLAN_MSG_VLAN),
|
||||
FM10K_TLV_ATTR_BOOL(FM10K_MAC_VLAN_MSG_SET),
|
||||
FM10K_TLV_ATTR_MAC_ADDR(FM10K_MAC_VLAN_MSG_MAC),
|
||||
FM10K_TLV_ATTR_MAC_ADDR(FM10K_MAC_VLAN_MSG_DEFAULT_MAC),
|
||||
FM10K_TLV_ATTR_MAC_ADDR(FM10K_MAC_VLAN_MSG_MULTICAST),
|
||||
FM10K_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
/**
|
||||
* fm10k_update_vlan_vf - Update status of VLAN ID in VLAN filter table
|
||||
* @hw: pointer to hardware structure
|
||||
* @vid: VLAN ID to add to table
|
||||
* @vsi: Reserved, should always be 0
|
||||
* @set: Indicates if this is a set or clear operation
|
||||
*
|
||||
* This function adds or removes the corresponding VLAN ID from the VLAN
|
||||
* filter table for this VF.
|
||||
**/
|
||||
static s32 fm10k_update_vlan_vf(struct fm10k_hw *hw, u32 vid, u8 vsi, bool set)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[4];
|
||||
|
||||
/* verify the index is not set */
|
||||
if (vsi)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* verify upper 4 bits of vid and length are 0 */
|
||||
if ((vid << 16 | vid) >> 28)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* encode set bit into the VLAN ID */
|
||||
if (!set)
|
||||
vid |= FM10K_VLAN_CLEAR;
|
||||
|
||||
/* generate VLAN request */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_MAC_VLAN);
|
||||
fm10k_tlv_attr_put_u32(msg, FM10K_MAC_VLAN_MSG_VLAN, vid);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_msg_mac_vlan_vf - Read device MAC address from mailbox message
|
||||
* @hw: pointer to the HW structure
|
||||
* @results: Attributes for message
|
||||
* @mbx: unused mailbox data
|
||||
*
|
||||
* This function should determine the MAC address for the VF
|
||||
**/
|
||||
s32 fm10k_msg_mac_vlan_vf(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *mbx)
|
||||
{
|
||||
u8 perm_addr[ETH_ALEN];
|
||||
u16 vid;
|
||||
s32 err;
|
||||
|
||||
/* record MAC address requested */
|
||||
err = fm10k_tlv_attr_get_mac_vlan(
|
||||
results[FM10K_MAC_VLAN_MSG_DEFAULT_MAC],
|
||||
perm_addr, &vid);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
ether_addr_copy(hw->mac.perm_addr, perm_addr);
|
||||
hw->mac.default_vid = vid & (FM10K_VLAN_TABLE_VID_MAX - 1);
|
||||
hw->mac.vlan_override = !!(vid & FM10K_VLAN_CLEAR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_read_mac_addr_vf - Read device MAC address
|
||||
* @hw: pointer to the HW structure
|
||||
*
|
||||
* This function should determine the MAC address for the VF
|
||||
**/
|
||||
static s32 fm10k_read_mac_addr_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
u8 perm_addr[ETH_ALEN];
|
||||
u32 base_addr;
|
||||
|
||||
base_addr = fm10k_read_reg(hw, FM10K_TDBAL(0));
|
||||
|
||||
/* last byte should be 0 */
|
||||
if (base_addr << 24)
|
||||
return FM10K_ERR_INVALID_MAC_ADDR;
|
||||
|
||||
perm_addr[3] = (u8)(base_addr >> 24);
|
||||
perm_addr[4] = (u8)(base_addr >> 16);
|
||||
perm_addr[5] = (u8)(base_addr >> 8);
|
||||
|
||||
base_addr = fm10k_read_reg(hw, FM10K_TDBAH(0));
|
||||
|
||||
/* first byte should be all 1's */
|
||||
if ((~base_addr) >> 24)
|
||||
return FM10K_ERR_INVALID_MAC_ADDR;
|
||||
|
||||
perm_addr[0] = (u8)(base_addr >> 16);
|
||||
perm_addr[1] = (u8)(base_addr >> 8);
|
||||
perm_addr[2] = (u8)(base_addr);
|
||||
|
||||
ether_addr_copy(hw->mac.perm_addr, perm_addr);
|
||||
ether_addr_copy(hw->mac.addr, perm_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_uc_addr_vf - Update device unicast address
|
||||
* @hw: pointer to the HW structure
|
||||
* @glort: unused
|
||||
* @mac: MAC address to add/remove from table
|
||||
* @vid: VLAN ID to add/remove from table
|
||||
* @add: Indicates if this is an add or remove operation
|
||||
* @flags: flags field to indicate add and secure - unused
|
||||
*
|
||||
* This function is used to add or remove unicast MAC addresses for
|
||||
* the VF.
|
||||
**/
|
||||
static s32 fm10k_update_uc_addr_vf(struct fm10k_hw *hw, u16 glort,
|
||||
const u8 *mac, u16 vid, bool add, u8 flags)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[7];
|
||||
|
||||
/* verify VLAN ID is valid */
|
||||
if (vid >= FM10K_VLAN_TABLE_VID_MAX)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* verify MAC address is valid */
|
||||
if (!is_valid_ether_addr(mac))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* verify we are not locked down on the MAC address */
|
||||
if (is_valid_ether_addr(hw->mac.perm_addr) &&
|
||||
memcmp(hw->mac.perm_addr, mac, ETH_ALEN))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* add bit to notify us if this is a set of clear operation */
|
||||
if (!add)
|
||||
vid |= FM10K_VLAN_CLEAR;
|
||||
|
||||
/* generate VLAN request */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_MAC_VLAN);
|
||||
fm10k_tlv_attr_put_mac_vlan(msg, FM10K_MAC_VLAN_MSG_MAC, mac, vid);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_mc_addr_vf - Update device multicast address
|
||||
* @hw: pointer to the HW structure
|
||||
* @glort: unused
|
||||
* @mac: MAC address to add/remove from table
|
||||
* @vid: VLAN ID to add/remove from table
|
||||
* @add: Indicates if this is an add or remove operation
|
||||
*
|
||||
* This function is used to add or remove multicast MAC addresses for
|
||||
* the VF.
|
||||
**/
|
||||
static s32 fm10k_update_mc_addr_vf(struct fm10k_hw *hw, u16 glort,
|
||||
const u8 *mac, u16 vid, bool add)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[7];
|
||||
|
||||
/* verify VLAN ID is valid */
|
||||
if (vid >= FM10K_VLAN_TABLE_VID_MAX)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* verify multicast address is valid */
|
||||
if (!is_multicast_ether_addr(mac))
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* add bit to notify us if this is a set of clear operation */
|
||||
if (!add)
|
||||
vid |= FM10K_VLAN_CLEAR;
|
||||
|
||||
/* generate VLAN request */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_MAC_VLAN);
|
||||
fm10k_tlv_attr_put_mac_vlan(msg, FM10K_MAC_VLAN_MSG_MULTICAST,
|
||||
mac, vid);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_int_moderator_vf - Request update of interrupt moderator list
|
||||
* @hw: pointer to hardware structure
|
||||
*
|
||||
* This function will issue a request to the PF to rescan our MSI-X table
|
||||
* and to update the interrupt moderator linked list.
|
||||
**/
|
||||
static void fm10k_update_int_moderator_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[1];
|
||||
|
||||
/* generate MSI-X request */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_MSIX);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
/* This structure defines the attibutes to be parsed below */
|
||||
const struct fm10k_tlv_attr fm10k_lport_state_msg_attr[] = {
|
||||
FM10K_TLV_ATTR_BOOL(FM10K_LPORT_STATE_MSG_DISABLE),
|
||||
FM10K_TLV_ATTR_U8(FM10K_LPORT_STATE_MSG_XCAST_MODE),
|
||||
FM10K_TLV_ATTR_BOOL(FM10K_LPORT_STATE_MSG_READY),
|
||||
FM10K_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
/**
|
||||
* fm10k_msg_lport_state_vf - Message handler for lport_state message from PF
|
||||
* @hw: Pointer to hardware structure
|
||||
* @results: pointer array containing parsed data
|
||||
* @mbx: Pointer to mailbox information structure
|
||||
*
|
||||
* This handler is meant to capture the indication from the PF that we
|
||||
* are ready to bring up the interface.
|
||||
**/
|
||||
s32 fm10k_msg_lport_state_vf(struct fm10k_hw *hw, u32 **results,
|
||||
struct fm10k_mbx_info *mbx)
|
||||
{
|
||||
hw->mac.dglort_map = !results[FM10K_LPORT_STATE_MSG_READY] ?
|
||||
FM10K_DGLORTMAP_NONE : FM10K_DGLORTMAP_ZERO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_lport_state_vf - Update device state in lower device
|
||||
* @hw: pointer to the HW structure
|
||||
* @glort: unused
|
||||
* @count: number of logical ports to enable - unused (always 1)
|
||||
* @enable: boolean value indicating if this is an enable or disable request
|
||||
*
|
||||
* Notify the lower device of a state change. If the lower device is
|
||||
* enabled we can add filters, if it is disabled all filters for this
|
||||
* logical port are flushed.
|
||||
**/
|
||||
static s32 fm10k_update_lport_state_vf(struct fm10k_hw *hw, u16 glort,
|
||||
u16 count, bool enable)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[2];
|
||||
|
||||
/* reset glort mask 0 as we have to wait to be enabled */
|
||||
hw->mac.dglort_map = FM10K_DGLORTMAP_NONE;
|
||||
|
||||
/* generate port state request */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_LPORT_STATE);
|
||||
if (!enable)
|
||||
fm10k_tlv_attr_put_bool(msg, FM10K_LPORT_STATE_MSG_DISABLE);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_update_xcast_mode_vf - Request update of multicast mode
|
||||
* @hw: pointer to hardware structure
|
||||
* @glort: unused
|
||||
* @mode: integer value indicating mode being requested
|
||||
*
|
||||
* This function will attempt to request a higher mode for the port
|
||||
* so that it can enable either multicast, multicast promiscuous, or
|
||||
* promiscuous mode of operation.
|
||||
**/
|
||||
static s32 fm10k_update_xcast_mode_vf(struct fm10k_hw *hw, u16 glort, u8 mode)
|
||||
{
|
||||
struct fm10k_mbx_info *mbx = &hw->mbx;
|
||||
u32 msg[3];
|
||||
|
||||
if (mode > FM10K_XCAST_MODE_NONE)
|
||||
return FM10K_ERR_PARAM;
|
||||
/* generate message requesting to change xcast mode */
|
||||
fm10k_tlv_msg_init(msg, FM10K_VF_MSG_ID_LPORT_STATE);
|
||||
fm10k_tlv_attr_put_u8(msg, FM10K_LPORT_STATE_MSG_XCAST_MODE, mode);
|
||||
|
||||
/* load onto outgoing mailbox */
|
||||
return mbx->ops.enqueue_tx(hw, mbx, msg);
|
||||
}
|
||||
|
||||
const struct fm10k_tlv_attr fm10k_1588_msg_attr[] = {
|
||||
FM10K_TLV_ATTR_U64(FM10K_1588_MSG_TIMESTAMP),
|
||||
FM10K_TLV_ATTR_LAST
|
||||
};
|
||||
|
||||
/* currently there is no shared 1588 timestamp handler */
|
||||
|
||||
/**
|
||||
* fm10k_update_hw_stats_vf - Updates hardware related statistics of VF
|
||||
* @hw: pointer to hardware structure
|
||||
* @stats: pointer to statistics structure
|
||||
*
|
||||
* This function collects and aggregates per queue hardware statistics.
|
||||
**/
|
||||
static void fm10k_update_hw_stats_vf(struct fm10k_hw *hw,
|
||||
struct fm10k_hw_stats *stats)
|
||||
{
|
||||
fm10k_update_hw_stats_q(hw, stats->q, 0, hw->mac.max_queues);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_rebind_hw_stats_vf - Resets base for hardware statistics of VF
|
||||
* @hw: pointer to hardware structure
|
||||
* @stats: pointer to the stats structure to update
|
||||
*
|
||||
* This function resets the base for queue hardware statistics.
|
||||
**/
|
||||
static void fm10k_rebind_hw_stats_vf(struct fm10k_hw *hw,
|
||||
struct fm10k_hw_stats *stats)
|
||||
{
|
||||
/* Unbind Queue Statistics */
|
||||
fm10k_unbind_hw_stats_q(stats->q, 0, hw->mac.max_queues);
|
||||
|
||||
/* Reinitialize bases for all stats */
|
||||
fm10k_update_hw_stats_vf(hw, stats);
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_configure_dglort_map_vf - Configures GLORT entry and queues
|
||||
* @hw: pointer to hardware structure
|
||||
* @dglort: pointer to dglort configuration structure
|
||||
*
|
||||
* Reads the configuration structure contained in dglort_cfg and uses
|
||||
* that information to then populate a DGLORTMAP/DEC entry and the queues
|
||||
* to which it has been assigned.
|
||||
**/
|
||||
static s32 fm10k_configure_dglort_map_vf(struct fm10k_hw *hw,
|
||||
struct fm10k_dglort_cfg *dglort)
|
||||
{
|
||||
/* verify the dglort pointer */
|
||||
if (!dglort)
|
||||
return FM10K_ERR_PARAM;
|
||||
|
||||
/* stub for now until we determine correct message for this */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_adjust_systime_vf - Adjust systime frequency
|
||||
* @hw: pointer to hardware structure
|
||||
* @ppb: adjustment rate in parts per billion
|
||||
*
|
||||
* This function takes an adjustment rate in parts per billion and will
|
||||
* verify that this value is 0 as the VF cannot support adjusting the
|
||||
* systime clock.
|
||||
*
|
||||
* If the ppb value is non-zero the return is ERR_PARAM else success
|
||||
**/
|
||||
static s32 fm10k_adjust_systime_vf(struct fm10k_hw *hw, s32 ppb)
|
||||
{
|
||||
/* The VF cannot adjust the clock frequency, however it should
|
||||
* already have a syntonic clock with whichever host interface is
|
||||
* running as the master for the host interface clock domain so
|
||||
* there should be not frequency adjustment necessary.
|
||||
*/
|
||||
return ppb ? FM10K_ERR_PARAM : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* fm10k_read_systime_vf - Reads value of systime registers
|
||||
* @hw: pointer to the hardware structure
|
||||
*
|
||||
* Function reads the content of 2 registers, combined to represent a 64 bit
|
||||
* value measured in nanosecods. In order to guarantee the value is accurate
|
||||
* we check the 32 most significant bits both before and after reading the
|
||||
* 32 least significant bits to verify they didn't change as we were reading
|
||||
* the registers.
|
||||
**/
|
||||
static u64 fm10k_read_systime_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
u32 systime_l, systime_h, systime_tmp;
|
||||
|
||||
systime_h = fm10k_read_reg(hw, FM10K_VFSYSTIME + 1);
|
||||
|
||||
do {
|
||||
systime_tmp = systime_h;
|
||||
systime_l = fm10k_read_reg(hw, FM10K_VFSYSTIME);
|
||||
systime_h = fm10k_read_reg(hw, FM10K_VFSYSTIME + 1);
|
||||
} while (systime_tmp != systime_h);
|
||||
|
||||
return ((u64)systime_h << 32) | systime_l;
|
||||
}
|
||||
|
||||
static const struct fm10k_msg_data fm10k_msg_data_vf[] = {
|
||||
FM10K_TLV_MSG_TEST_HANDLER(fm10k_tlv_msg_test),
|
||||
FM10K_VF_MSG_MAC_VLAN_HANDLER(fm10k_msg_mac_vlan_vf),
|
||||
FM10K_VF_MSG_LPORT_STATE_HANDLER(fm10k_msg_lport_state_vf),
|
||||
FM10K_TLV_MSG_ERROR_HANDLER(fm10k_tlv_msg_error),
|
||||
};
|
||||
|
||||
static struct fm10k_mac_ops mac_ops_vf = {
|
||||
.get_bus_info = &fm10k_get_bus_info_generic,
|
||||
.reset_hw = &fm10k_reset_hw_vf,
|
||||
.init_hw = &fm10k_init_hw_vf,
|
||||
.start_hw = &fm10k_start_hw_generic,
|
||||
.stop_hw = &fm10k_stop_hw_vf,
|
||||
.is_slot_appropriate = &fm10k_is_slot_appropriate_vf,
|
||||
.update_vlan = &fm10k_update_vlan_vf,
|
||||
.read_mac_addr = &fm10k_read_mac_addr_vf,
|
||||
.update_uc_addr = &fm10k_update_uc_addr_vf,
|
||||
.update_mc_addr = &fm10k_update_mc_addr_vf,
|
||||
.update_xcast_mode = &fm10k_update_xcast_mode_vf,
|
||||
.update_int_moderator = &fm10k_update_int_moderator_vf,
|
||||
.update_lport_state = &fm10k_update_lport_state_vf,
|
||||
.update_hw_stats = &fm10k_update_hw_stats_vf,
|
||||
.rebind_hw_stats = &fm10k_rebind_hw_stats_vf,
|
||||
.configure_dglort_map = &fm10k_configure_dglort_map_vf,
|
||||
.get_host_state = &fm10k_get_host_state_generic,
|
||||
.adjust_systime = &fm10k_adjust_systime_vf,
|
||||
.read_systime = &fm10k_read_systime_vf,
|
||||
};
|
||||
|
||||
static s32 fm10k_get_invariants_vf(struct fm10k_hw *hw)
|
||||
{
|
||||
fm10k_get_invariants_generic(hw);
|
||||
|
||||
return fm10k_pfvf_mbx_init(hw, &hw->mbx, fm10k_msg_data_vf, 0);
|
||||
}
|
||||
|
||||
struct fm10k_info fm10k_vf_info = {
|
||||
.mac = fm10k_mac_vf,
|
||||
.get_invariants = &fm10k_get_invariants_vf,
|
||||
.mac_ops = &mac_ops_vf,
|
||||
};
|
78
drivers/net/ethernet/intel/fm10k/fm10k_vf.h
Normal file
78
drivers/net/ethernet/intel/fm10k/fm10k_vf.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* 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
|
||||
*/
|
||||
|
||||
#ifndef _FM10K_VF_H_
|
||||
#define _FM10K_VF_H_
|
||||
|
||||
#include "fm10k_type.h"
|
||||
#include "fm10k_common.h"
|
||||
|
||||
enum fm10k_vf_tlv_msg_id {
|
||||
FM10K_VF_MSG_ID_TEST = 0, /* msg ID reserved for testing */
|
||||
FM10K_VF_MSG_ID_MSIX,
|
||||
FM10K_VF_MSG_ID_MAC_VLAN,
|
||||
FM10K_VF_MSG_ID_LPORT_STATE,
|
||||
FM10K_VF_MSG_ID_1588,
|
||||
FM10K_VF_MSG_ID_MAX,
|
||||
};
|
||||
|
||||
enum fm10k_tlv_mac_vlan_attr_id {
|
||||
FM10K_MAC_VLAN_MSG_VLAN,
|
||||
FM10K_MAC_VLAN_MSG_SET,
|
||||
FM10K_MAC_VLAN_MSG_MAC,
|
||||
FM10K_MAC_VLAN_MSG_DEFAULT_MAC,
|
||||
FM10K_MAC_VLAN_MSG_MULTICAST,
|
||||
FM10K_MAC_VLAN_MSG_ID_MAX
|
||||
};
|
||||
|
||||
enum fm10k_tlv_lport_state_attr_id {
|
||||
FM10K_LPORT_STATE_MSG_DISABLE,
|
||||
FM10K_LPORT_STATE_MSG_XCAST_MODE,
|
||||
FM10K_LPORT_STATE_MSG_READY,
|
||||
FM10K_LPORT_STATE_MSG_MAX
|
||||
};
|
||||
|
||||
enum fm10k_tlv_1588_attr_id {
|
||||
FM10K_1588_MSG_TIMESTAMP,
|
||||
FM10K_1588_MSG_MAX
|
||||
};
|
||||
|
||||
#define FM10K_VF_MSG_MSIX_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_MSIX, NULL, func)
|
||||
|
||||
s32 fm10k_msg_mac_vlan_vf(struct fm10k_hw *, u32 **, struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_tlv_attr fm10k_mac_vlan_msg_attr[];
|
||||
#define FM10K_VF_MSG_MAC_VLAN_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_MAC_VLAN, \
|
||||
fm10k_mac_vlan_msg_attr, func)
|
||||
|
||||
s32 fm10k_msg_lport_state_vf(struct fm10k_hw *, u32 **,
|
||||
struct fm10k_mbx_info *);
|
||||
extern const struct fm10k_tlv_attr fm10k_lport_state_msg_attr[];
|
||||
#define FM10K_VF_MSG_LPORT_STATE_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_LPORT_STATE, \
|
||||
fm10k_lport_state_msg_attr, func)
|
||||
|
||||
extern const struct fm10k_tlv_attr fm10k_1588_msg_attr[];
|
||||
#define FM10K_VF_MSG_1588_HANDLER(func) \
|
||||
FM10K_MSG_HANDLER(FM10K_VF_MSG_ID_1588, fm10k_1588_msg_attr, func)
|
||||
|
||||
extern struct fm10k_info fm10k_vf_info;
|
||||
#endif /* _FM10K_VF_H */
|
Loading…
Reference in New Issue
Block a user