diff options
Diffstat (limited to 'drivers/net/cxgb4')
| -rw-r--r-- | drivers/net/cxgb4/Makefile | 7 | ||||
| -rw-r--r-- | drivers/net/cxgb4/cxgb4.h | 741 | ||||
| -rw-r--r-- | drivers/net/cxgb4/cxgb4_main.c | 3388 | ||||
| -rw-r--r-- | drivers/net/cxgb4/cxgb4_uld.h | 239 | ||||
| -rw-r--r-- | drivers/net/cxgb4/l2t.c | 624 | ||||
| -rw-r--r-- | drivers/net/cxgb4/l2t.h | 110 | ||||
| -rw-r--r-- | drivers/net/cxgb4/sge.c | 2431 | ||||
| -rw-r--r-- | drivers/net/cxgb4/t4_hw.c | 3131 | ||||
| -rw-r--r-- | drivers/net/cxgb4/t4_hw.h | 100 | ||||
| -rw-r--r-- | drivers/net/cxgb4/t4_msg.h | 664 | ||||
| -rw-r--r-- | drivers/net/cxgb4/t4_regs.h | 878 | ||||
| -rw-r--r-- | drivers/net/cxgb4/t4fw_api.h | 1580 | 
12 files changed, 13893 insertions, 0 deletions
diff --git a/drivers/net/cxgb4/Makefile b/drivers/net/cxgb4/Makefile new file mode 100644 index 00000000000..498667487f5 --- /dev/null +++ b/drivers/net/cxgb4/Makefile @@ -0,0 +1,7 @@ +# +# Chelsio T4 driver +# + +obj-$(CONFIG_CHELSIO_T4) += cxgb4.o + +cxgb4-objs := cxgb4_main.o l2t.o t4_hw.o sge.o diff --git a/drivers/net/cxgb4/cxgb4.h b/drivers/net/cxgb4/cxgb4.h new file mode 100644 index 00000000000..3d8ff4889b5 --- /dev/null +++ b/drivers/net/cxgb4/cxgb4.h @@ -0,0 +1,741 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __CXGB4_H__ +#define __CXGB4_H__ + +#include <linux/bitops.h> +#include <linux/cache.h> +#include <linux/interrupt.h> +#include <linux/list.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <asm/io.h> +#include "cxgb4_uld.h" +#include "t4_hw.h" + +#define FW_VERSION_MAJOR 1 +#define FW_VERSION_MINOR 1 +#define FW_VERSION_MICRO 0 + +enum { +	MAX_NPORTS = 4,     /* max # of ports */ +	SERNUM_LEN = 16,    /* Serial # length */ +	EC_LEN     = 16,    /* E/C length */ +	ID_LEN     = 16,    /* ID length */ +}; + +enum { +	MEM_EDC0, +	MEM_EDC1, +	MEM_MC +}; + +enum dev_master { +	MASTER_CANT, +	MASTER_MAY, +	MASTER_MUST +}; + +enum dev_state { +	DEV_STATE_UNINIT, +	DEV_STATE_INIT, +	DEV_STATE_ERR +}; + +enum { +	PAUSE_RX      = 1 << 0, +	PAUSE_TX      = 1 << 1, +	PAUSE_AUTONEG = 1 << 2 +}; + +struct port_stats { +	u64 tx_octets;            /* total # of octets in good frames */ +	u64 tx_frames;            /* all good frames */ +	u64 tx_bcast_frames;      /* all broadcast frames */ +	u64 tx_mcast_frames;      /* all multicast frames */ +	u64 tx_ucast_frames;      /* all unicast frames */ +	u64 tx_error_frames;      /* all error frames */ + +	u64 tx_frames_64;         /* # of Tx frames in a particular range */ +	u64 tx_frames_65_127; +	u64 tx_frames_128_255; +	u64 tx_frames_256_511; +	u64 tx_frames_512_1023; +	u64 tx_frames_1024_1518; +	u64 tx_frames_1519_max; + +	u64 tx_drop;              /* # of dropped Tx frames */ +	u64 tx_pause;             /* # of transmitted pause frames */ +	u64 tx_ppp0;              /* # of transmitted PPP prio 0 frames */ +	u64 tx_ppp1;              /* # of transmitted PPP prio 1 frames */ +	u64 tx_ppp2;              /* # of transmitted PPP prio 2 frames */ +	u64 tx_ppp3;              /* # of transmitted PPP prio 3 frames */ +	u64 tx_ppp4;              /* # of transmitted PPP prio 4 frames */ +	u64 tx_ppp5;              /* # of transmitted PPP prio 5 frames */ +	u64 tx_ppp6;              /* # of transmitted PPP prio 6 frames */ +	u64 tx_ppp7;              /* # of transmitted PPP prio 7 frames */ + +	u64 rx_octets;            /* total # of octets in good frames */ +	u64 rx_frames;            /* all good frames */ +	u64 rx_bcast_frames;      /* all broadcast frames */ +	u64 rx_mcast_frames;      /* all multicast frames */ +	u64 rx_ucast_frames;      /* all unicast frames */ +	u64 rx_too_long;          /* # of frames exceeding MTU */ +	u64 rx_jabber;            /* # of jabber frames */ +	u64 rx_fcs_err;           /* # of received frames with bad FCS */ +	u64 rx_len_err;           /* # of received frames with length error */ +	u64 rx_symbol_err;        /* symbol errors */ +	u64 rx_runt;              /* # of short frames */ + +	u64 rx_frames_64;         /* # of Rx frames in a particular range */ +	u64 rx_frames_65_127; +	u64 rx_frames_128_255; +	u64 rx_frames_256_511; +	u64 rx_frames_512_1023; +	u64 rx_frames_1024_1518; +	u64 rx_frames_1519_max; + +	u64 rx_pause;             /* # of received pause frames */ +	u64 rx_ppp0;              /* # of received PPP prio 0 frames */ +	u64 rx_ppp1;              /* # of received PPP prio 1 frames */ +	u64 rx_ppp2;              /* # of received PPP prio 2 frames */ +	u64 rx_ppp3;              /* # of received PPP prio 3 frames */ +	u64 rx_ppp4;              /* # of received PPP prio 4 frames */ +	u64 rx_ppp5;              /* # of received PPP prio 5 frames */ +	u64 rx_ppp6;              /* # of received PPP prio 6 frames */ +	u64 rx_ppp7;              /* # of received PPP prio 7 frames */ + +	u64 rx_ovflow0;           /* drops due to buffer-group 0 overflows */ +	u64 rx_ovflow1;           /* drops due to buffer-group 1 overflows */ +	u64 rx_ovflow2;           /* drops due to buffer-group 2 overflows */ +	u64 rx_ovflow3;           /* drops due to buffer-group 3 overflows */ +	u64 rx_trunc0;            /* buffer-group 0 truncated packets */ +	u64 rx_trunc1;            /* buffer-group 1 truncated packets */ +	u64 rx_trunc2;            /* buffer-group 2 truncated packets */ +	u64 rx_trunc3;            /* buffer-group 3 truncated packets */ +}; + +struct lb_port_stats { +	u64 octets; +	u64 frames; +	u64 bcast_frames; +	u64 mcast_frames; +	u64 ucast_frames; +	u64 error_frames; + +	u64 frames_64; +	u64 frames_65_127; +	u64 frames_128_255; +	u64 frames_256_511; +	u64 frames_512_1023; +	u64 frames_1024_1518; +	u64 frames_1519_max; + +	u64 drop; + +	u64 ovflow0; +	u64 ovflow1; +	u64 ovflow2; +	u64 ovflow3; +	u64 trunc0; +	u64 trunc1; +	u64 trunc2; +	u64 trunc3; +}; + +struct tp_tcp_stats { +	u32 tcpOutRsts; +	u64 tcpInSegs; +	u64 tcpOutSegs; +	u64 tcpRetransSegs; +}; + +struct tp_err_stats { +	u32 macInErrs[4]; +	u32 hdrInErrs[4]; +	u32 tcpInErrs[4]; +	u32 tnlCongDrops[4]; +	u32 ofldChanDrops[4]; +	u32 tnlTxDrops[4]; +	u32 ofldVlanDrops[4]; +	u32 tcp6InErrs[4]; +	u32 ofldNoNeigh; +	u32 ofldCongDefer; +}; + +struct tp_params { +	unsigned int ntxchan;        /* # of Tx channels */ +	unsigned int tre;            /* log2 of core clocks per TP tick */ +}; + +struct vpd_params { +	unsigned int cclk; +	u8 ec[EC_LEN + 1]; +	u8 sn[SERNUM_LEN + 1]; +	u8 id[ID_LEN + 1]; +}; + +struct pci_params { +	unsigned char speed; +	unsigned char width; +}; + +struct adapter_params { +	struct tp_params  tp; +	struct vpd_params vpd; +	struct pci_params pci; + +	unsigned int fw_vers; +	unsigned int tp_vers; +	u8 api_vers[7]; + +	unsigned short mtus[NMTUS]; +	unsigned short a_wnd[NCCTRL_WIN]; +	unsigned short b_wnd[NCCTRL_WIN]; + +	unsigned char nports;             /* # of ethernet ports */ +	unsigned char portvec; +	unsigned char rev;                /* chip revision */ +	unsigned char offload; + +	unsigned int ofldq_wr_cred; +}; + +struct trace_params { +	u32 data[TRACE_LEN / 4]; +	u32 mask[TRACE_LEN / 4]; +	unsigned short snap_len; +	unsigned short min_len; +	unsigned char skip_ofst; +	unsigned char skip_len; +	unsigned char invert; +	unsigned char port; +}; + +struct link_config { +	unsigned short supported;        /* link capabilities */ +	unsigned short advertising;      /* advertised capabilities */ +	unsigned short requested_speed;  /* speed user has requested */ +	unsigned short speed;            /* actual link speed */ +	unsigned char  requested_fc;     /* flow control user has requested */ +	unsigned char  fc;               /* actual link flow control */ +	unsigned char  autoneg;          /* autonegotiating? */ +	unsigned char  link_ok;          /* link up? */ +}; + +#define FW_LEN16(fw_struct) FW_CMD_LEN16(sizeof(fw_struct) / 16) + +enum { +	MAX_ETH_QSETS = 32,           /* # of Ethernet Tx/Rx queue sets */ +	MAX_OFLD_QSETS = 16,          /* # of offload Tx/Rx queue sets */ +	MAX_CTRL_QUEUES = NCHAN,      /* # of control Tx queues */ +	MAX_RDMA_QUEUES = NCHAN,      /* # of streaming RDMA Rx queues */ +}; + +enum { +	MAX_EGRQ = 128,         /* max # of egress queues, including FLs */ +	MAX_INGQ = 64           /* max # of interrupt-capable ingress queues */ +}; + +struct adapter; +struct vlan_group; +struct sge_rspq; + +struct port_info { +	struct adapter *adapter; +	struct vlan_group *vlan_grp; +	u16    viid; +	s16    xact_addr_filt;        /* index of exact MAC address filter */ +	u16    rss_size;              /* size of VI's RSS table slice */ +	s8     mdio_addr; +	u8     port_type; +	u8     mod_type; +	u8     port_id; +	u8     tx_chan; +	u8     lport;                 /* associated offload logical port */ +	u8     rx_offload;            /* CSO, etc */ +	u8     nqsets;                /* # of qsets */ +	u8     first_qset;            /* index of first qset */ +	struct link_config link_cfg; +}; + +/* port_info.rx_offload flags */ +enum { +	RX_CSO = 1 << 0, +}; + +struct dentry; +struct work_struct; + +enum {                                 /* adapter flags */ +	FULL_INIT_DONE     = (1 << 0), +	USING_MSI          = (1 << 1), +	USING_MSIX         = (1 << 2), +	QUEUES_BOUND       = (1 << 3), +	FW_OK              = (1 << 4), +}; + +struct rx_sw_desc; + +struct sge_fl {                     /* SGE free-buffer queue state */ +	unsigned int avail;         /* # of available Rx buffers */ +	unsigned int pend_cred;     /* new buffers since last FL DB ring */ +	unsigned int cidx;          /* consumer index */ +	unsigned int pidx;          /* producer index */ +	unsigned long alloc_failed; /* # of times buffer allocation failed */ +	unsigned long large_alloc_failed; +	unsigned long starving; +	/* RO fields */ +	unsigned int cntxt_id;      /* SGE context id for the free list */ +	unsigned int size;          /* capacity of free list */ +	struct rx_sw_desc *sdesc;   /* address of SW Rx descriptor ring */ +	__be64 *desc;               /* address of HW Rx descriptor ring */ +	dma_addr_t addr;            /* bus address of HW ring start */ +}; + +/* A packet gather list */ +struct pkt_gl { +	skb_frag_t frags[MAX_SKB_FRAGS]; +	void *va;                         /* virtual address of first byte */ +	unsigned int nfrags;              /* # of fragments */ +	unsigned int tot_len;             /* total length of fragments */ +}; + +typedef int (*rspq_handler_t)(struct sge_rspq *q, const __be64 *rsp, +			      const struct pkt_gl *gl); + +struct sge_rspq {                   /* state for an SGE response queue */ +	struct napi_struct napi; +	const __be64 *cur_desc;     /* current descriptor in queue */ +	unsigned int cidx;          /* consumer index */ +	u8 gen;                     /* current generation bit */ +	u8 intr_params;             /* interrupt holdoff parameters */ +	u8 next_intr_params;        /* holdoff params for next interrupt */ +	u8 pktcnt_idx;              /* interrupt packet threshold */ +	u8 uld;                     /* ULD handling this queue */ +	u8 idx;                     /* queue index within its group */ +	int offset;                 /* offset into current Rx buffer */ +	u16 cntxt_id;               /* SGE context id for the response q */ +	u16 abs_id;                 /* absolute SGE id for the response q */ +	__be64 *desc;               /* address of HW response ring */ +	dma_addr_t phys_addr;       /* physical address of the ring */ +	unsigned int iqe_len;       /* entry size */ +	unsigned int size;          /* capacity of response queue */ +	struct adapter *adap; +	struct net_device *netdev;  /* associated net device */ +	rspq_handler_t handler; +}; + +struct sge_eth_stats {              /* Ethernet queue statistics */ +	unsigned long pkts;         /* # of ethernet packets */ +	unsigned long lro_pkts;     /* # of LRO super packets */ +	unsigned long lro_merged;   /* # of wire packets merged by LRO */ +	unsigned long rx_cso;       /* # of Rx checksum offloads */ +	unsigned long vlan_ex;      /* # of Rx VLAN extractions */ +	unsigned long rx_drops;     /* # of packets dropped due to no mem */ +}; + +struct sge_eth_rxq {                /* SW Ethernet Rx queue */ +	struct sge_rspq rspq; +	struct sge_fl fl; +	struct sge_eth_stats stats; +} ____cacheline_aligned_in_smp; + +struct sge_ofld_stats {             /* offload queue statistics */ +	unsigned long pkts;         /* # of packets */ +	unsigned long imm;          /* # of immediate-data packets */ +	unsigned long an;           /* # of asynchronous notifications */ +	unsigned long nomem;        /* # of responses deferred due to no mem */ +}; + +struct sge_ofld_rxq {               /* SW offload Rx queue */ +	struct sge_rspq rspq; +	struct sge_fl fl; +	struct sge_ofld_stats stats; +} ____cacheline_aligned_in_smp; + +struct tx_desc { +	__be64 flit[8]; +}; + +struct tx_sw_desc; + +struct sge_txq { +	unsigned int  in_use;       /* # of in-use Tx descriptors */ +	unsigned int  size;         /* # of descriptors */ +	unsigned int  cidx;         /* SW consumer index */ +	unsigned int  pidx;         /* producer index */ +	unsigned long stops;        /* # of times q has been stopped */ +	unsigned long restarts;     /* # of queue restarts */ +	unsigned int  cntxt_id;     /* SGE context id for the Tx q */ +	struct tx_desc *desc;       /* address of HW Tx descriptor ring */ +	struct tx_sw_desc *sdesc;   /* address of SW Tx descriptor ring */ +	struct sge_qstat *stat;     /* queue status entry */ +	dma_addr_t    phys_addr;    /* physical address of the ring */ +}; + +struct sge_eth_txq {                /* state for an SGE Ethernet Tx queue */ +	struct sge_txq q; +	struct netdev_queue *txq;   /* associated netdev TX queue */ +	unsigned long tso;          /* # of TSO requests */ +	unsigned long tx_cso;       /* # of Tx checksum offloads */ +	unsigned long vlan_ins;     /* # of Tx VLAN insertions */ +	unsigned long mapping_err;  /* # of I/O MMU packet mapping errors */ +} ____cacheline_aligned_in_smp; + +struct sge_ofld_txq {               /* state for an SGE offload Tx queue */ +	struct sge_txq q; +	struct adapter *adap; +	struct sk_buff_head sendq;  /* list of backpressured packets */ +	struct tasklet_struct qresume_tsk; /* restarts the queue */ +	u8 full;                    /* the Tx ring is full */ +	unsigned long mapping_err;  /* # of I/O MMU packet mapping errors */ +} ____cacheline_aligned_in_smp; + +struct sge_ctrl_txq {               /* state for an SGE control Tx queue */ +	struct sge_txq q; +	struct adapter *adap; +	struct sk_buff_head sendq;  /* list of backpressured packets */ +	struct tasklet_struct qresume_tsk; /* restarts the queue */ +	u8 full;                    /* the Tx ring is full */ +} ____cacheline_aligned_in_smp; + +struct sge { +	struct sge_eth_txq ethtxq[MAX_ETH_QSETS]; +	struct sge_ofld_txq ofldtxq[MAX_OFLD_QSETS]; +	struct sge_ctrl_txq ctrlq[MAX_CTRL_QUEUES]; + +	struct sge_eth_rxq ethrxq[MAX_ETH_QSETS]; +	struct sge_ofld_rxq ofldrxq[MAX_OFLD_QSETS]; +	struct sge_ofld_rxq rdmarxq[MAX_RDMA_QUEUES]; +	struct sge_rspq fw_evtq ____cacheline_aligned_in_smp; + +	struct sge_rspq intrq ____cacheline_aligned_in_smp; +	spinlock_t intrq_lock; + +	u16 max_ethqsets;           /* # of available Ethernet queue sets */ +	u16 ethqsets;               /* # of active Ethernet queue sets */ +	u16 ethtxq_rover;           /* Tx queue to clean up next */ +	u16 ofldqsets;              /* # of active offload queue sets */ +	u16 rdmaqs;                 /* # of available RDMA Rx queues */ +	u16 ofld_rxq[MAX_OFLD_QSETS]; +	u16 rdma_rxq[NCHAN]; +	u16 timer_val[SGE_NTIMERS]; +	u8 counter_val[SGE_NCOUNTERS]; +	unsigned int starve_thres; +	u8 idma_state[2]; +	void *egr_map[MAX_EGRQ];    /* qid->queue egress queue map */ +	struct sge_rspq *ingr_map[MAX_INGQ]; /* qid->queue ingress queue map */ +	DECLARE_BITMAP(starving_fl, MAX_EGRQ); +	DECLARE_BITMAP(txq_maperr, MAX_EGRQ); +	struct timer_list rx_timer; /* refills starving FLs */ +	struct timer_list tx_timer; /* checks Tx queues */ +}; + +#define for_each_ethrxq(sge, i) for (i = 0; i < (sge)->ethqsets; i++) +#define for_each_ofldrxq(sge, i) for (i = 0; i < (sge)->ofldqsets; i++) +#define for_each_rdmarxq(sge, i) for (i = 0; i < (sge)->rdmaqs; i++) + +struct l2t_data; + +struct adapter { +	void __iomem *regs; +	struct pci_dev *pdev; +	struct device *pdev_dev; +	unsigned long registered_device_map; +	unsigned long open_device_map; +	unsigned long flags; + +	const char *name; +	int msg_enable; + +	struct adapter_params params; +	struct cxgb4_virt_res vres; +	unsigned int swintr; + +	unsigned int wol; + +	struct { +		unsigned short vec; +		char desc[14]; +	} msix_info[MAX_INGQ + 1]; + +	struct sge sge; + +	struct net_device *port[MAX_NPORTS]; +	u8 chan_map[NCHAN];                   /* channel -> port map */ + +	struct l2t_data *l2t; +	void *uld_handle[CXGB4_ULD_MAX]; +	struct list_head list_node; + +	struct tid_info tids; +	void **tid_release_head; +	spinlock_t tid_release_lock; +	struct work_struct tid_release_task; +	bool tid_release_task_busy; + +	struct dentry *debugfs_root; + +	spinlock_t stats_lock; +}; + +static inline u32 t4_read_reg(struct adapter *adap, u32 reg_addr) +{ +	return readl(adap->regs + reg_addr); +} + +static inline void t4_write_reg(struct adapter *adap, u32 reg_addr, u32 val) +{ +	writel(val, adap->regs + reg_addr); +} + +#ifndef readq +static inline u64 readq(const volatile void __iomem *addr) +{ +	return readl(addr) + ((u64)readl(addr + 4) << 32); +} + +static inline void writeq(u64 val, volatile void __iomem *addr) +{ +	writel(val, addr); +	writel(val >> 32, addr + 4); +} +#endif + +static inline u64 t4_read_reg64(struct adapter *adap, u32 reg_addr) +{ +	return readq(adap->regs + reg_addr); +} + +static inline void t4_write_reg64(struct adapter *adap, u32 reg_addr, u64 val) +{ +	writeq(val, adap->regs + reg_addr); +} + +/** + * netdev2pinfo - return the port_info structure associated with a net_device + * @dev: the netdev + * + * Return the struct port_info associated with a net_device + */ +static inline struct port_info *netdev2pinfo(const struct net_device *dev) +{ +	return netdev_priv(dev); +} + +/** + * adap2pinfo - return the port_info of a port + * @adap: the adapter + * @idx: the port index + * + * Return the port_info structure for the port of the given index. + */ +static inline struct port_info *adap2pinfo(struct adapter *adap, int idx) +{ +	return netdev_priv(adap->port[idx]); +} + +/** + * netdev2adap - return the adapter structure associated with a net_device + * @dev: the netdev + * + * Return the struct adapter associated with a net_device + */ +static inline struct adapter *netdev2adap(const struct net_device *dev) +{ +	return netdev2pinfo(dev)->adapter; +} + +void t4_os_portmod_changed(const struct adapter *adap, int port_id); +void t4_os_link_changed(struct adapter *adap, int port_id, int link_stat); + +void *t4_alloc_mem(size_t size); +void t4_free_mem(void *addr); + +void t4_free_sge_resources(struct adapter *adap); +irq_handler_t t4_intr_handler(struct adapter *adap); +netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev); +int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, +		     const struct pkt_gl *gl); +int t4_mgmt_tx(struct adapter *adap, struct sk_buff *skb); +int t4_ofld_send(struct adapter *adap, struct sk_buff *skb); +int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, +		     struct net_device *dev, int intr_idx, +		     struct sge_fl *fl, rspq_handler_t hnd); +int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, +			 struct net_device *dev, struct netdev_queue *netdevq, +			 unsigned int iqid); +int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, +			  struct net_device *dev, unsigned int iqid, +			  unsigned int cmplqid); +int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, +			  struct net_device *dev, unsigned int iqid); +irqreturn_t t4_sge_intr_msix(int irq, void *cookie); +void t4_sge_init(struct adapter *adap); +void t4_sge_start(struct adapter *adap); +void t4_sge_stop(struct adapter *adap); + +#define for_each_port(adapter, iter) \ +	for (iter = 0; iter < (adapter)->params.nports; ++iter) + +static inline unsigned int core_ticks_per_usec(const struct adapter *adap) +{ +	return adap->params.vpd.cclk / 1000; +} + +static inline unsigned int us_to_core_ticks(const struct adapter *adap, +					    unsigned int us) +{ +	return (us * adap->params.vpd.cclk) / 1000; +} + +void t4_set_reg_field(struct adapter *adap, unsigned int addr, u32 mask, +		      u32 val); + +int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, +		    void *rpl, bool sleep_ok); + +static inline int t4_wr_mbox(struct adapter *adap, int mbox, const void *cmd, +			     int size, void *rpl) +{ +	return t4_wr_mbox_meat(adap, mbox, cmd, size, rpl, true); +} + +static inline int t4_wr_mbox_ns(struct adapter *adap, int mbox, const void *cmd, +				int size, void *rpl) +{ +	return t4_wr_mbox_meat(adap, mbox, cmd, size, rpl, false); +} + +void t4_intr_enable(struct adapter *adapter); +void t4_intr_disable(struct adapter *adapter); +void t4_intr_clear(struct adapter *adapter); +int t4_slow_intr_handler(struct adapter *adapter); + +int t4_link_start(struct adapter *adap, unsigned int mbox, unsigned int port, +		  struct link_config *lc); +int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port); +int t4_seeprom_wp(struct adapter *adapter, bool enable); +int t4_read_flash(struct adapter *adapter, unsigned int addr, +		  unsigned int nwords, u32 *data, int byte_oriented); +int t4_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size); +int t4_check_fw_version(struct adapter *adapter); +int t4_prep_adapter(struct adapter *adapter); +int t4_port_init(struct adapter *adap, int mbox, int pf, int vf); +void t4_fatal_err(struct adapter *adapter); +void t4_set_vlan_accel(struct adapter *adapter, unsigned int ports, int on); +int t4_set_trace_filter(struct adapter *adapter, const struct trace_params *tp, +			int filter_index, int enable); +void t4_get_trace_filter(struct adapter *adapter, struct trace_params *tp, +			 int filter_index, int *enabled); +int t4_config_rss_range(struct adapter *adapter, int mbox, unsigned int viid, +			int start, int n, const u16 *rspq, unsigned int nrspq); +int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, +		       unsigned int flags); +int t4_read_rss(struct adapter *adapter, u16 *entries); +int t4_mc_read(struct adapter *adap, u32 addr, __be32 *data, u64 *parity); +int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, +		u64 *parity); + +void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p); +void t4_get_lb_stats(struct adapter *adap, int idx, struct lb_port_stats *p); + +void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log); +void t4_tp_get_err_stats(struct adapter *adap, struct tp_err_stats *st); +void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, +			 struct tp_tcp_stats *v6); +void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, +		  const unsigned short *alpha, const unsigned short *beta); + +void t4_wol_magic_enable(struct adapter *adap, unsigned int port, +			 const u8 *addr); +int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, +		      u64 mask0, u64 mask1, unsigned int crc, bool enable); + +int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox, +		enum dev_master master, enum dev_state *state); +int t4_fw_bye(struct adapter *adap, unsigned int mbox); +int t4_early_init(struct adapter *adap, unsigned int mbox); +int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset); +int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int nparams, const u32 *params, +		    u32 *val); +int t4_set_params(struct adapter *adap, unsigned int mbox, unsigned int pf, +		  unsigned int vf, unsigned int nparams, const u32 *params, +		  const u32 *val); +int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf, +		unsigned int vf, unsigned int txq, unsigned int txq_eth_ctrl, +		unsigned int rxqi, unsigned int rxq, unsigned int tc, +		unsigned int vi, unsigned int cmask, unsigned int pmask, +		unsigned int nexact, unsigned int rcaps, unsigned int wxcaps); +int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port, +		unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac, +		unsigned int *rss_size); +int t4_free_vi(struct adapter *adap, unsigned int mbox, unsigned int pf, +	       unsigned int vf, unsigned int viid); +int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid, +		int mtu, int promisc, int all_multi, int bcast, bool sleep_ok); +int t4_alloc_mac_filt(struct adapter *adap, unsigned int mbox, +		      unsigned int viid, bool free, unsigned int naddr, +		      const u8 **addr, u16 *idx, u64 *hash, bool sleep_ok); +int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid, +		  int idx, const u8 *addr, bool persist, bool add_smt); +int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid, +		     bool ucast, u64 vec, bool sleep_ok); +int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid, +		 bool rx_en, bool tx_en); +int t4_identify_port(struct adapter *adap, unsigned int mbox, unsigned int viid, +		     unsigned int nblinks); +int t4_mdio_rd(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, +	       unsigned int mmd, unsigned int reg, u16 *valp); +int t4_mdio_wr(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, +	       unsigned int mmd, unsigned int reg, u16 val); +int t4_iq_start_stop(struct adapter *adap, unsigned int mbox, bool start, +		     unsigned int pf, unsigned int vf, unsigned int iqid, +		     unsigned int fl0id, unsigned int fl1id); +int t4_iq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +	       unsigned int vf, unsigned int iqtype, unsigned int iqid, +	       unsigned int fl0id, unsigned int fl1id); +int t4_eth_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		   unsigned int vf, unsigned int eqid); +int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int eqid); +int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int eqid); +int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl); +#endif /* __CXGB4_H__ */ diff --git a/drivers/net/cxgb4/cxgb4_main.c b/drivers/net/cxgb4/cxgb4_main.c new file mode 100644 index 00000000000..a7e30a23d32 --- /dev/null +++ b/drivers/net/cxgb4/cxgb4_main.c @@ -0,0 +1,3388 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/bitmap.h> +#include <linux/crc32.h> +#include <linux/ctype.h> +#include <linux/debugfs.h> +#include <linux/err.h> +#include <linux/etherdevice.h> +#include <linux/firmware.h> +#include <linux/if_vlan.h> +#include <linux/init.h> +#include <linux/log2.h> +#include <linux/mdio.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/pci.h> +#include <linux/aer.h> +#include <linux/rtnetlink.h> +#include <linux/sched.h> +#include <linux/seq_file.h> +#include <linux/sockios.h> +#include <linux/vmalloc.h> +#include <linux/workqueue.h> +#include <net/neighbour.h> +#include <net/netevent.h> +#include <asm/uaccess.h> + +#include "cxgb4.h" +#include "t4_regs.h" +#include "t4_msg.h" +#include "t4fw_api.h" +#include "l2t.h" + +#define DRV_VERSION "1.0.0-ko" +#define DRV_DESC "Chelsio T4 Network Driver" + +/* + * Max interrupt hold-off timer value in us.  Queues fall back to this value + * under extreme memory pressure so it's largish to give the system time to + * recover. + */ +#define MAX_SGE_TIMERVAL 200U + +enum { +	MEMWIN0_APERTURE = 65536, +	MEMWIN0_BASE     = 0x30000, +	MEMWIN1_APERTURE = 32768, +	MEMWIN1_BASE     = 0x28000, +	MEMWIN2_APERTURE = 2048, +	MEMWIN2_BASE     = 0x1b800, +}; + +enum { +	MAX_TXQ_ENTRIES      = 16384, +	MAX_CTRL_TXQ_ENTRIES = 1024, +	MAX_RSPQ_ENTRIES     = 16384, +	MAX_RX_BUFFERS       = 16384, +	MIN_TXQ_ENTRIES      = 32, +	MIN_CTRL_TXQ_ENTRIES = 32, +	MIN_RSPQ_ENTRIES     = 128, +	MIN_FL_ENTRIES       = 16 +}; + +#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ +			 NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ +			 NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) + +#define CH_DEVICE(devid) { PCI_VDEVICE(CHELSIO, devid), 0 } + +static DEFINE_PCI_DEVICE_TABLE(cxgb4_pci_tbl) = { +	CH_DEVICE(0xa000),  /* PE10K */ +	{ 0, } +}; + +#define FW_FNAME "cxgb4/t4fw.bin" + +MODULE_DESCRIPTION(DRV_DESC); +MODULE_AUTHOR("Chelsio Communications"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, cxgb4_pci_tbl); +MODULE_FIRMWARE(FW_FNAME); + +static int dflt_msg_enable = DFLT_MSG_ENABLE; + +module_param(dflt_msg_enable, int, 0644); +MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T4 default message enable bitmap"); + +/* + * The driver uses the best interrupt scheme available on a platform in the + * order MSI-X, MSI, legacy INTx interrupts.  This parameter determines which + * of these schemes the driver may consider as follows: + * + * msi = 2: choose from among all three options + * msi = 1: only consider MSI and INTx interrupts + * msi = 0: force INTx interrupts + */ +static int msi = 2; + +module_param(msi, int, 0644); +MODULE_PARM_DESC(msi, "whether to use INTx (0), MSI (1) or MSI-X (2)"); + +/* + * Queue interrupt hold-off timer values.  Queues default to the first of these + * upon creation. + */ +static unsigned int intr_holdoff[SGE_NTIMERS - 1] = { 5, 10, 20, 50, 100 }; + +module_param_array(intr_holdoff, uint, NULL, 0644); +MODULE_PARM_DESC(intr_holdoff, "values for queue interrupt hold-off timers " +		 "0..4 in microseconds"); + +static unsigned int intr_cnt[SGE_NCOUNTERS - 1] = { 4, 8, 16 }; + +module_param_array(intr_cnt, uint, NULL, 0644); +MODULE_PARM_DESC(intr_cnt, +		 "thresholds 1..3 for queue interrupt packet counters"); + +static int vf_acls; + +#ifdef CONFIG_PCI_IOV +module_param(vf_acls, bool, 0644); +MODULE_PARM_DESC(vf_acls, "if set enable virtualization L2 ACL enforcement"); + +static unsigned int num_vf[4]; + +module_param_array(num_vf, uint, NULL, 0644); +MODULE_PARM_DESC(num_vf, "number of VFs for each of PFs 0-3"); +#endif + +static struct dentry *cxgb4_debugfs_root; + +static LIST_HEAD(adapter_list); +static DEFINE_MUTEX(uld_mutex); +static struct cxgb4_uld_info ulds[CXGB4_ULD_MAX]; +static const char *uld_str[] = { "RDMA", "iSCSI" }; + +static void link_report(struct net_device *dev) +{ +	if (!netif_carrier_ok(dev)) +		netdev_info(dev, "link down\n"); +	else { +		static const char *fc[] = { "no", "Rx", "Tx", "Tx/Rx" }; + +		const char *s = "10Mbps"; +		const struct port_info *p = netdev_priv(dev); + +		switch (p->link_cfg.speed) { +		case SPEED_10000: +			s = "10Gbps"; +			break; +		case SPEED_1000: +			s = "1000Mbps"; +			break; +		case SPEED_100: +			s = "100Mbps"; +			break; +		} + +		netdev_info(dev, "link up, %s, full-duplex, %s PAUSE\n", s, +			    fc[p->link_cfg.fc]); +	} +} + +void t4_os_link_changed(struct adapter *adapter, int port_id, int link_stat) +{ +	struct net_device *dev = adapter->port[port_id]; + +	/* Skip changes from disabled ports. */ +	if (netif_running(dev) && link_stat != netif_carrier_ok(dev)) { +		if (link_stat) +			netif_carrier_on(dev); +		else +			netif_carrier_off(dev); + +		link_report(dev); +	} +} + +void t4_os_portmod_changed(const struct adapter *adap, int port_id) +{ +	static const char *mod_str[] = { +		NULL, "LR", "SR", "ER", "passive DA", "active DA" +	}; + +	const struct net_device *dev = adap->port[port_id]; +	const struct port_info *pi = netdev_priv(dev); + +	if (pi->mod_type == FW_PORT_MOD_TYPE_NONE) +		netdev_info(dev, "port module unplugged\n"); +	else +		netdev_info(dev, "%s module inserted\n", mod_str[pi->mod_type]); +} + +/* + * Configure the exact and hash address filters to handle a port's multicast + * and secondary unicast MAC addresses. + */ +static int set_addr_filters(const struct net_device *dev, bool sleep) +{ +	u64 mhash = 0; +	u64 uhash = 0; +	bool free = true; +	u16 filt_idx[7]; +	const u8 *addr[7]; +	int ret, naddr = 0; +	const struct dev_addr_list *d; +	const struct netdev_hw_addr *ha; +	int uc_cnt = netdev_uc_count(dev); +	const struct port_info *pi = netdev_priv(dev); + +	/* first do the secondary unicast addresses */ +	netdev_for_each_uc_addr(ha, dev) { +		addr[naddr++] = ha->addr; +		if (--uc_cnt == 0 || naddr >= ARRAY_SIZE(addr)) { +			ret = t4_alloc_mac_filt(pi->adapter, 0, pi->viid, free, +					naddr, addr, filt_idx, &uhash, sleep); +			if (ret < 0) +				return ret; + +			free = false; +			naddr = 0; +		} +	} + +	/* next set up the multicast addresses */ +	netdev_for_each_mc_addr(d, dev) { +		addr[naddr++] = d->dmi_addr; +		if (naddr >= ARRAY_SIZE(addr) || d->next == NULL) { +			ret = t4_alloc_mac_filt(pi->adapter, 0, pi->viid, free, +					naddr, addr, filt_idx, &mhash, sleep); +			if (ret < 0) +				return ret; + +			free = false; +			naddr = 0; +		} +	} + +	return t4_set_addr_hash(pi->adapter, 0, pi->viid, uhash != 0, +				uhash | mhash, sleep); +} + +/* + * Set Rx properties of a port, such as promiscruity, address filters, and MTU. + * If @mtu is -1 it is left unchanged. + */ +static int set_rxmode(struct net_device *dev, int mtu, bool sleep_ok) +{ +	int ret; +	struct port_info *pi = netdev_priv(dev); + +	ret = set_addr_filters(dev, sleep_ok); +	if (ret == 0) +		ret = t4_set_rxmode(pi->adapter, 0, pi->viid, mtu, +				    (dev->flags & IFF_PROMISC) ? 1 : 0, +				    (dev->flags & IFF_ALLMULTI) ? 1 : 0, 1, +				    sleep_ok); +	return ret; +} + +/** + *	link_start - enable a port + *	@dev: the port to enable + * + *	Performs the MAC and PHY actions needed to enable a port. + */ +static int link_start(struct net_device *dev) +{ +	int ret; +	struct port_info *pi = netdev_priv(dev); + +	/* +	 * We do not set address filters and promiscuity here, the stack does +	 * that step explicitly. +	 */ +	ret = t4_set_rxmode(pi->adapter, 0, pi->viid, dev->mtu, -1, -1, -1, +			    true); +	if (ret == 0) { +		ret = t4_change_mac(pi->adapter, 0, pi->viid, +				    pi->xact_addr_filt, dev->dev_addr, true, +				    false); +		if (ret >= 0) { +			pi->xact_addr_filt = ret; +			ret = 0; +		} +	} +	if (ret == 0) +		ret = t4_link_start(pi->adapter, 0, pi->tx_chan, &pi->link_cfg); +	if (ret == 0) +		ret = t4_enable_vi(pi->adapter, 0, pi->viid, true, true); +	return ret; +} + +/* + * Response queue handler for the FW event queue. + */ +static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp, +			  const struct pkt_gl *gl) +{ +	u8 opcode = ((const struct rss_header *)rsp)->opcode; + +	rsp++;                                          /* skip RSS header */ +	if (likely(opcode == CPL_SGE_EGR_UPDATE)) { +		const struct cpl_sge_egr_update *p = (void *)rsp; +		unsigned int qid = EGR_QID(ntohl(p->opcode_qid)); +		struct sge_txq *txq = q->adap->sge.egr_map[qid]; + +		txq->restarts++; +		if ((u8 *)txq < (u8 *)q->adap->sge.ethrxq) { +			struct sge_eth_txq *eq; + +			eq = container_of(txq, struct sge_eth_txq, q); +			netif_tx_wake_queue(eq->txq); +		} else { +			struct sge_ofld_txq *oq; + +			oq = container_of(txq, struct sge_ofld_txq, q); +			tasklet_schedule(&oq->qresume_tsk); +		} +	} else if (opcode == CPL_FW6_MSG || opcode == CPL_FW4_MSG) { +		const struct cpl_fw6_msg *p = (void *)rsp; + +		if (p->type == 0) +			t4_handle_fw_rpl(q->adap, p->data); +	} else if (opcode == CPL_L2T_WRITE_RPL) { +		const struct cpl_l2t_write_rpl *p = (void *)rsp; + +		do_l2t_write_rpl(q->adap, p); +	} else +		dev_err(q->adap->pdev_dev, +			"unexpected CPL %#x on FW event queue\n", opcode); +	return 0; +} + +/** + *	uldrx_handler - response queue handler for ULD queues + *	@q: the response queue that received the packet + *	@rsp: the response queue descriptor holding the offload message + *	@gl: the gather list of packet fragments + * + *	Deliver an ingress offload packet to a ULD.  All processing is done by + *	the ULD, we just maintain statistics. + */ +static int uldrx_handler(struct sge_rspq *q, const __be64 *rsp, +			 const struct pkt_gl *gl) +{ +	struct sge_ofld_rxq *rxq = container_of(q, struct sge_ofld_rxq, rspq); + +	if (ulds[q->uld].rx_handler(q->adap->uld_handle[q->uld], rsp, gl)) { +		rxq->stats.nomem++; +		return -1; +	} +	if (gl == NULL) +		rxq->stats.imm++; +	else if (gl == CXGB4_MSG_AN) +		rxq->stats.an++; +	else +		rxq->stats.pkts++; +	return 0; +} + +static void disable_msi(struct adapter *adapter) +{ +	if (adapter->flags & USING_MSIX) { +		pci_disable_msix(adapter->pdev); +		adapter->flags &= ~USING_MSIX; +	} else if (adapter->flags & USING_MSI) { +		pci_disable_msi(adapter->pdev); +		adapter->flags &= ~USING_MSI; +	} +} + +/* + * Interrupt handler for non-data events used with MSI-X. + */ +static irqreturn_t t4_nondata_intr(int irq, void *cookie) +{ +	struct adapter *adap = cookie; + +	u32 v = t4_read_reg(adap, MYPF_REG(PL_PF_INT_CAUSE)); +	if (v & PFSW) { +		adap->swintr = 1; +		t4_write_reg(adap, MYPF_REG(PL_PF_INT_CAUSE), v); +	} +	t4_slow_intr_handler(adap); +	return IRQ_HANDLED; +} + +/* + * Name the MSI-X interrupts. + */ +static void name_msix_vecs(struct adapter *adap) +{ +	int i, j, msi_idx = 2, n = sizeof(adap->msix_info[0].desc) - 1; + +	/* non-data interrupts */ +	snprintf(adap->msix_info[0].desc, n, "%s", adap->name); +	adap->msix_info[0].desc[n] = 0; + +	/* FW events */ +	snprintf(adap->msix_info[1].desc, n, "%s-FWeventq", adap->name); +	adap->msix_info[1].desc[n] = 0; + +	/* Ethernet queues */ +	for_each_port(adap, j) { +		struct net_device *d = adap->port[j]; +		const struct port_info *pi = netdev_priv(d); + +		for (i = 0; i < pi->nqsets; i++, msi_idx++) { +			snprintf(adap->msix_info[msi_idx].desc, n, "%s-Rx%d", +				 d->name, i); +			adap->msix_info[msi_idx].desc[n] = 0; +		} +	} + +	/* offload queues */ +	for_each_ofldrxq(&adap->sge, i) { +		snprintf(adap->msix_info[msi_idx].desc, n, "%s-ofld%d", +			 adap->name, i); +		adap->msix_info[msi_idx++].desc[n] = 0; +	} +	for_each_rdmarxq(&adap->sge, i) { +		snprintf(adap->msix_info[msi_idx].desc, n, "%s-rdma%d", +			 adap->name, i); +		adap->msix_info[msi_idx++].desc[n] = 0; +	} +} + +static int request_msix_queue_irqs(struct adapter *adap) +{ +	struct sge *s = &adap->sge; +	int err, ethqidx, ofldqidx = 0, rdmaqidx = 0, msi = 2; + +	err = request_irq(adap->msix_info[1].vec, t4_sge_intr_msix, 0, +			  adap->msix_info[1].desc, &s->fw_evtq); +	if (err) +		return err; + +	for_each_ethrxq(s, ethqidx) { +		err = request_irq(adap->msix_info[msi].vec, t4_sge_intr_msix, 0, +				  adap->msix_info[msi].desc, +				  &s->ethrxq[ethqidx].rspq); +		if (err) +			goto unwind; +		msi++; +	} +	for_each_ofldrxq(s, ofldqidx) { +		err = request_irq(adap->msix_info[msi].vec, t4_sge_intr_msix, 0, +				  adap->msix_info[msi].desc, +				  &s->ofldrxq[ofldqidx].rspq); +		if (err) +			goto unwind; +		msi++; +	} +	for_each_rdmarxq(s, rdmaqidx) { +		err = request_irq(adap->msix_info[msi].vec, t4_sge_intr_msix, 0, +				  adap->msix_info[msi].desc, +				  &s->rdmarxq[rdmaqidx].rspq); +		if (err) +			goto unwind; +		msi++; +	} +	return 0; + +unwind: +	while (--rdmaqidx >= 0) +		free_irq(adap->msix_info[--msi].vec, +			 &s->rdmarxq[rdmaqidx].rspq); +	while (--ofldqidx >= 0) +		free_irq(adap->msix_info[--msi].vec, +			 &s->ofldrxq[ofldqidx].rspq); +	while (--ethqidx >= 0) +		free_irq(adap->msix_info[--msi].vec, &s->ethrxq[ethqidx].rspq); +	free_irq(adap->msix_info[1].vec, &s->fw_evtq); +	return err; +} + +static void free_msix_queue_irqs(struct adapter *adap) +{ +	int i, msi = 2; +	struct sge *s = &adap->sge; + +	free_irq(adap->msix_info[1].vec, &s->fw_evtq); +	for_each_ethrxq(s, i) +		free_irq(adap->msix_info[msi++].vec, &s->ethrxq[i].rspq); +	for_each_ofldrxq(s, i) +		free_irq(adap->msix_info[msi++].vec, &s->ofldrxq[i].rspq); +	for_each_rdmarxq(s, i) +		free_irq(adap->msix_info[msi++].vec, &s->rdmarxq[i].rspq); +} + +/** + *	setup_rss - configure RSS + *	@adap: the adapter + * + *	Sets up RSS to distribute packets to multiple receive queues.  We + *	configure the RSS CPU lookup table to distribute to the number of HW + *	receive queues, and the response queue lookup table to narrow that + *	down to the response queues actually configured for each port. + *	We always configure the RSS mapping for all ports since the mapping + *	table has plenty of entries. + */ +static int setup_rss(struct adapter *adap) +{ +	int i, j, err; +	u16 rss[MAX_ETH_QSETS]; + +	for_each_port(adap, i) { +		const struct port_info *pi = adap2pinfo(adap, i); +		const struct sge_eth_rxq *q = &adap->sge.ethrxq[pi->first_qset]; + +		for (j = 0; j < pi->nqsets; j++) +			rss[j] = q[j].rspq.abs_id; + +		err = t4_config_rss_range(adap, 0, pi->viid, 0, pi->rss_size, +					  rss, pi->nqsets); +		if (err) +			return err; +	} +	return 0; +} + +/* + * Wait until all NAPI handlers are descheduled. + */ +static void quiesce_rx(struct adapter *adap) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { +		struct sge_rspq *q = adap->sge.ingr_map[i]; + +		if (q && q->handler) +			napi_disable(&q->napi); +	} +} + +/* + * Enable NAPI scheduling and interrupt generation for all Rx queues. + */ +static void enable_rx(struct adapter *adap) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(adap->sge.ingr_map); i++) { +		struct sge_rspq *q = adap->sge.ingr_map[i]; + +		if (!q) +			continue; +		if (q->handler) +			napi_enable(&q->napi); +		/* 0-increment GTS to start the timer and enable interrupts */ +		t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), +			     SEINTARM(q->intr_params) | +			     INGRESSQID(q->cntxt_id)); +	} +} + +/** + *	setup_sge_queues - configure SGE Tx/Rx/response queues + *	@adap: the adapter + * + *	Determines how many sets of SGE queues to use and initializes them. + *	We support multiple queue sets per port if we have MSI-X, otherwise + *	just one queue set per port. + */ +static int setup_sge_queues(struct adapter *adap) +{ +	int err, msi_idx, i, j; +	struct sge *s = &adap->sge; + +	bitmap_zero(s->starving_fl, MAX_EGRQ); +	bitmap_zero(s->txq_maperr, MAX_EGRQ); + +	if (adap->flags & USING_MSIX) +		msi_idx = 1;         /* vector 0 is for non-queue interrupts */ +	else { +		err = t4_sge_alloc_rxq(adap, &s->intrq, false, adap->port[0], 0, +				       NULL, NULL); +		if (err) +			return err; +		msi_idx = -((int)s->intrq.abs_id + 1); +	} + +	err = t4_sge_alloc_rxq(adap, &s->fw_evtq, true, adap->port[0], +			       msi_idx, NULL, fwevtq_handler); +	if (err) { +freeout:	t4_free_sge_resources(adap); +		return err; +	} + +	for_each_port(adap, i) { +		struct net_device *dev = adap->port[i]; +		struct port_info *pi = netdev_priv(dev); +		struct sge_eth_rxq *q = &s->ethrxq[pi->first_qset]; +		struct sge_eth_txq *t = &s->ethtxq[pi->first_qset]; + +		for (j = 0; j < pi->nqsets; j++, q++) { +			if (msi_idx > 0) +				msi_idx++; +			err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev, +					       msi_idx, &q->fl, +					       t4_ethrx_handler); +			if (err) +				goto freeout; +			q->rspq.idx = j; +			memset(&q->stats, 0, sizeof(q->stats)); +		} +		for (j = 0; j < pi->nqsets; j++, t++) { +			err = t4_sge_alloc_eth_txq(adap, t, dev, +					netdev_get_tx_queue(dev, j), +					s->fw_evtq.cntxt_id); +			if (err) +				goto freeout; +		} +	} + +	j = s->ofldqsets / adap->params.nports; /* ofld queues per channel */ +	for_each_ofldrxq(s, i) { +		struct sge_ofld_rxq *q = &s->ofldrxq[i]; +		struct net_device *dev = adap->port[i / j]; + +		if (msi_idx > 0) +			msi_idx++; +		err = t4_sge_alloc_rxq(adap, &q->rspq, false, dev, msi_idx, +				       &q->fl, uldrx_handler); +		if (err) +			goto freeout; +		memset(&q->stats, 0, sizeof(q->stats)); +		s->ofld_rxq[i] = q->rspq.abs_id; +		err = t4_sge_alloc_ofld_txq(adap, &s->ofldtxq[i], dev, +					    s->fw_evtq.cntxt_id); +		if (err) +			goto freeout; +	} + +	for_each_rdmarxq(s, i) { +		struct sge_ofld_rxq *q = &s->rdmarxq[i]; + +		if (msi_idx > 0) +			msi_idx++; +		err = t4_sge_alloc_rxq(adap, &q->rspq, false, adap->port[i], +				       msi_idx, &q->fl, uldrx_handler); +		if (err) +			goto freeout; +		memset(&q->stats, 0, sizeof(q->stats)); +		s->rdma_rxq[i] = q->rspq.abs_id; +	} + +	for_each_port(adap, i) { +		/* +		 * Note that ->rdmarxq[i].rspq.cntxt_id below is 0 if we don't +		 * have RDMA queues, and that's the right value. +		 */ +		err = t4_sge_alloc_ctrl_txq(adap, &s->ctrlq[i], adap->port[i], +					    s->fw_evtq.cntxt_id, +					    s->rdmarxq[i].rspq.cntxt_id); +		if (err) +			goto freeout; +	} + +	t4_write_reg(adap, MPS_TRC_RSS_CONTROL, +		     RSSCONTROL(netdev2pinfo(adap->port[0])->tx_chan) | +		     QUEUENUMBER(s->ethrxq[0].rspq.abs_id)); +	return 0; +} + +/* + * Returns 0 if new FW was successfully loaded, a positive errno if a load was + * started but failed, and a negative errno if flash load couldn't start. + */ +static int upgrade_fw(struct adapter *adap) +{ +	int ret; +	u32 vers; +	const struct fw_hdr *hdr; +	const struct firmware *fw; +	struct device *dev = adap->pdev_dev; + +	ret = request_firmware(&fw, FW_FNAME, dev); +	if (ret < 0) { +		dev_err(dev, "unable to load firmware image " FW_FNAME +			", error %d\n", ret); +		return ret; +	} + +	hdr = (const struct fw_hdr *)fw->data; +	vers = ntohl(hdr->fw_ver); +	if (FW_HDR_FW_VER_MAJOR_GET(vers) != FW_VERSION_MAJOR) { +		ret = -EINVAL;              /* wrong major version, won't do */ +		goto out; +	} + +	/* +	 * If the flash FW is unusable or we found something newer, load it. +	 */ +	if (FW_HDR_FW_VER_MAJOR_GET(adap->params.fw_vers) != FW_VERSION_MAJOR || +	    vers > adap->params.fw_vers) { +		ret = -t4_load_fw(adap, fw->data, fw->size); +		if (!ret) +			dev_info(dev, "firmware upgraded to version %pI4 from " +				 FW_FNAME "\n", &hdr->fw_ver); +	} +out:	release_firmware(fw); +	return ret; +} + +/* + * Allocate a chunk of memory using kmalloc or, if that fails, vmalloc. + * The allocated memory is cleared. + */ +void *t4_alloc_mem(size_t size) +{ +	void *p = kmalloc(size, GFP_KERNEL); + +	if (!p) +		p = vmalloc(size); +	if (p) +		memset(p, 0, size); +	return p; +} + +/* + * Free memory allocated through alloc_mem(). + */ +void t4_free_mem(void *addr) +{ +	if (is_vmalloc_addr(addr)) +		vfree(addr); +	else +		kfree(addr); +} + +static inline int is_offload(const struct adapter *adap) +{ +	return adap->params.offload; +} + +/* + * Implementation of ethtool operations. + */ + +static u32 get_msglevel(struct net_device *dev) +{ +	return netdev2adap(dev)->msg_enable; +} + +static void set_msglevel(struct net_device *dev, u32 val) +{ +	netdev2adap(dev)->msg_enable = val; +} + +static char stats_strings[][ETH_GSTRING_LEN] = { +	"TxOctetsOK         ", +	"TxFramesOK         ", +	"TxBroadcastFrames  ", +	"TxMulticastFrames  ", +	"TxUnicastFrames    ", +	"TxErrorFrames      ", + +	"TxFrames64         ", +	"TxFrames65To127    ", +	"TxFrames128To255   ", +	"TxFrames256To511   ", +	"TxFrames512To1023  ", +	"TxFrames1024To1518 ", +	"TxFrames1519ToMax  ", + +	"TxFramesDropped    ", +	"TxPauseFrames      ", +	"TxPPP0Frames       ", +	"TxPPP1Frames       ", +	"TxPPP2Frames       ", +	"TxPPP3Frames       ", +	"TxPPP4Frames       ", +	"TxPPP5Frames       ", +	"TxPPP6Frames       ", +	"TxPPP7Frames       ", + +	"RxOctetsOK         ", +	"RxFramesOK         ", +	"RxBroadcastFrames  ", +	"RxMulticastFrames  ", +	"RxUnicastFrames    ", + +	"RxFramesTooLong    ", +	"RxJabberErrors     ", +	"RxFCSErrors        ", +	"RxLengthErrors     ", +	"RxSymbolErrors     ", +	"RxRuntFrames       ", + +	"RxFrames64         ", +	"RxFrames65To127    ", +	"RxFrames128To255   ", +	"RxFrames256To511   ", +	"RxFrames512To1023  ", +	"RxFrames1024To1518 ", +	"RxFrames1519ToMax  ", + +	"RxPauseFrames      ", +	"RxPPP0Frames       ", +	"RxPPP1Frames       ", +	"RxPPP2Frames       ", +	"RxPPP3Frames       ", +	"RxPPP4Frames       ", +	"RxPPP5Frames       ", +	"RxPPP6Frames       ", +	"RxPPP7Frames       ", + +	"RxBG0FramesDropped ", +	"RxBG1FramesDropped ", +	"RxBG2FramesDropped ", +	"RxBG3FramesDropped ", +	"RxBG0FramesTrunc   ", +	"RxBG1FramesTrunc   ", +	"RxBG2FramesTrunc   ", +	"RxBG3FramesTrunc   ", + +	"TSO                ", +	"TxCsumOffload      ", +	"RxCsumGood         ", +	"VLANextractions    ", +	"VLANinsertions     ", +}; + +static int get_sset_count(struct net_device *dev, int sset) +{ +	switch (sset) { +	case ETH_SS_STATS: +		return ARRAY_SIZE(stats_strings); +	default: +		return -EOPNOTSUPP; +	} +} + +#define T4_REGMAP_SIZE (160 * 1024) + +static int get_regs_len(struct net_device *dev) +{ +	return T4_REGMAP_SIZE; +} + +static int get_eeprom_len(struct net_device *dev) +{ +	return EEPROMSIZE; +} + +static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +{ +	struct adapter *adapter = netdev2adap(dev); + +	strcpy(info->driver, KBUILD_MODNAME); +	strcpy(info->version, DRV_VERSION); +	strcpy(info->bus_info, pci_name(adapter->pdev)); + +	if (!adapter->params.fw_vers) +		strcpy(info->fw_version, "N/A"); +	else +		snprintf(info->fw_version, sizeof(info->fw_version), +			"%u.%u.%u.%u, TP %u.%u.%u.%u", +			FW_HDR_FW_VER_MAJOR_GET(adapter->params.fw_vers), +			FW_HDR_FW_VER_MINOR_GET(adapter->params.fw_vers), +			FW_HDR_FW_VER_MICRO_GET(adapter->params.fw_vers), +			FW_HDR_FW_VER_BUILD_GET(adapter->params.fw_vers), +			FW_HDR_FW_VER_MAJOR_GET(adapter->params.tp_vers), +			FW_HDR_FW_VER_MINOR_GET(adapter->params.tp_vers), +			FW_HDR_FW_VER_MICRO_GET(adapter->params.tp_vers), +			FW_HDR_FW_VER_BUILD_GET(adapter->params.tp_vers)); +} + +static void get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ +	if (stringset == ETH_SS_STATS) +		memcpy(data, stats_strings, sizeof(stats_strings)); +} + +/* + * port stats maintained per queue of the port.  They should be in the same + * order as in stats_strings above. + */ +struct queue_port_stats { +	u64 tso; +	u64 tx_csum; +	u64 rx_csum; +	u64 vlan_ex; +	u64 vlan_ins; +}; + +static void collect_sge_port_stats(const struct adapter *adap, +		const struct port_info *p, struct queue_port_stats *s) +{ +	int i; +	const struct sge_eth_txq *tx = &adap->sge.ethtxq[p->first_qset]; +	const struct sge_eth_rxq *rx = &adap->sge.ethrxq[p->first_qset]; + +	memset(s, 0, sizeof(*s)); +	for (i = 0; i < p->nqsets; i++, rx++, tx++) { +		s->tso += tx->tso; +		s->tx_csum += tx->tx_cso; +		s->rx_csum += rx->stats.rx_cso; +		s->vlan_ex += rx->stats.vlan_ex; +		s->vlan_ins += tx->vlan_ins; +	} +} + +static void get_stats(struct net_device *dev, struct ethtool_stats *stats, +		      u64 *data) +{ +	struct port_info *pi = netdev_priv(dev); +	struct adapter *adapter = pi->adapter; + +	t4_get_port_stats(adapter, pi->tx_chan, (struct port_stats *)data); + +	data += sizeof(struct port_stats) / sizeof(u64); +	collect_sge_port_stats(adapter, pi, (struct queue_port_stats *)data); +} + +/* + * Return a version number to identify the type of adapter.  The scheme is: + * - bits 0..9: chip version + * - bits 10..15: chip revision + */ +static inline unsigned int mk_adap_vers(const struct adapter *ap) +{ +	return 4 | (ap->params.rev << 10); +} + +static void reg_block_dump(struct adapter *ap, void *buf, unsigned int start, +			   unsigned int end) +{ +	u32 *p = buf + start; + +	for ( ; start <= end; start += sizeof(u32)) +		*p++ = t4_read_reg(ap, start); +} + +static void get_regs(struct net_device *dev, struct ethtool_regs *regs, +		     void *buf) +{ +	static const unsigned int reg_ranges[] = { +		0x1008, 0x1108, +		0x1180, 0x11b4, +		0x11fc, 0x123c, +		0x1300, 0x173c, +		0x1800, 0x18fc, +		0x3000, 0x30d8, +		0x30e0, 0x5924, +		0x5960, 0x59d4, +		0x5a00, 0x5af8, +		0x6000, 0x6098, +		0x6100, 0x6150, +		0x6200, 0x6208, +		0x6240, 0x6248, +		0x6280, 0x6338, +		0x6370, 0x638c, +		0x6400, 0x643c, +		0x6500, 0x6524, +		0x6a00, 0x6a38, +		0x6a60, 0x6a78, +		0x6b00, 0x6b84, +		0x6bf0, 0x6c84, +		0x6cf0, 0x6d84, +		0x6df0, 0x6e84, +		0x6ef0, 0x6f84, +		0x6ff0, 0x7084, +		0x70f0, 0x7184, +		0x71f0, 0x7284, +		0x72f0, 0x7384, +		0x73f0, 0x7450, +		0x7500, 0x7530, +		0x7600, 0x761c, +		0x7680, 0x76cc, +		0x7700, 0x7798, +		0x77c0, 0x77fc, +		0x7900, 0x79fc, +		0x7b00, 0x7c38, +		0x7d00, 0x7efc, +		0x8dc0, 0x8e1c, +		0x8e30, 0x8e78, +		0x8ea0, 0x8f6c, +		0x8fc0, 0x9074, +		0x90fc, 0x90fc, +		0x9400, 0x9458, +		0x9600, 0x96bc, +		0x9800, 0x9808, +		0x9820, 0x983c, +		0x9850, 0x9864, +		0x9c00, 0x9c6c, +		0x9c80, 0x9cec, +		0x9d00, 0x9d6c, +		0x9d80, 0x9dec, +		0x9e00, 0x9e6c, +		0x9e80, 0x9eec, +		0x9f00, 0x9f6c, +		0x9f80, 0x9fec, +		0xd004, 0xd03c, +		0xdfc0, 0xdfe0, +		0xe000, 0xea7c, +		0xf000, 0x11190, +		0x19040, 0x19124, +		0x19150, 0x191b0, +		0x191d0, 0x191e8, +		0x19238, 0x1924c, +		0x193f8, 0x19474, +		0x19490, 0x194f8, +		0x19800, 0x19f30, +		0x1a000, 0x1a06c, +		0x1a0b0, 0x1a120, +		0x1a128, 0x1a138, +		0x1a190, 0x1a1c4, +		0x1a1fc, 0x1a1fc, +		0x1e040, 0x1e04c, +		0x1e240, 0x1e28c, +		0x1e2c0, 0x1e2c0, +		0x1e2e0, 0x1e2e0, +		0x1e300, 0x1e384, +		0x1e3c0, 0x1e3c8, +		0x1e440, 0x1e44c, +		0x1e640, 0x1e68c, +		0x1e6c0, 0x1e6c0, +		0x1e6e0, 0x1e6e0, +		0x1e700, 0x1e784, +		0x1e7c0, 0x1e7c8, +		0x1e840, 0x1e84c, +		0x1ea40, 0x1ea8c, +		0x1eac0, 0x1eac0, +		0x1eae0, 0x1eae0, +		0x1eb00, 0x1eb84, +		0x1ebc0, 0x1ebc8, +		0x1ec40, 0x1ec4c, +		0x1ee40, 0x1ee8c, +		0x1eec0, 0x1eec0, +		0x1eee0, 0x1eee0, +		0x1ef00, 0x1ef84, +		0x1efc0, 0x1efc8, +		0x1f040, 0x1f04c, +		0x1f240, 0x1f28c, +		0x1f2c0, 0x1f2c0, +		0x1f2e0, 0x1f2e0, +		0x1f300, 0x1f384, +		0x1f3c0, 0x1f3c8, +		0x1f440, 0x1f44c, +		0x1f640, 0x1f68c, +		0x1f6c0, 0x1f6c0, +		0x1f6e0, 0x1f6e0, +		0x1f700, 0x1f784, +		0x1f7c0, 0x1f7c8, +		0x1f840, 0x1f84c, +		0x1fa40, 0x1fa8c, +		0x1fac0, 0x1fac0, +		0x1fae0, 0x1fae0, +		0x1fb00, 0x1fb84, +		0x1fbc0, 0x1fbc8, +		0x1fc40, 0x1fc4c, +		0x1fe40, 0x1fe8c, +		0x1fec0, 0x1fec0, +		0x1fee0, 0x1fee0, +		0x1ff00, 0x1ff84, +		0x1ffc0, 0x1ffc8, +		0x20000, 0x2002c, +		0x20100, 0x2013c, +		0x20190, 0x201c8, +		0x20200, 0x20318, +		0x20400, 0x20528, +		0x20540, 0x20614, +		0x21000, 0x21040, +		0x2104c, 0x21060, +		0x210c0, 0x210ec, +		0x21200, 0x21268, +		0x21270, 0x21284, +		0x212fc, 0x21388, +		0x21400, 0x21404, +		0x21500, 0x21518, +		0x2152c, 0x2153c, +		0x21550, 0x21554, +		0x21600, 0x21600, +		0x21608, 0x21628, +		0x21630, 0x2163c, +		0x21700, 0x2171c, +		0x21780, 0x2178c, +		0x21800, 0x21c38, +		0x21c80, 0x21d7c, +		0x21e00, 0x21e04, +		0x22000, 0x2202c, +		0x22100, 0x2213c, +		0x22190, 0x221c8, +		0x22200, 0x22318, +		0x22400, 0x22528, +		0x22540, 0x22614, +		0x23000, 0x23040, +		0x2304c, 0x23060, +		0x230c0, 0x230ec, +		0x23200, 0x23268, +		0x23270, 0x23284, +		0x232fc, 0x23388, +		0x23400, 0x23404, +		0x23500, 0x23518, +		0x2352c, 0x2353c, +		0x23550, 0x23554, +		0x23600, 0x23600, +		0x23608, 0x23628, +		0x23630, 0x2363c, +		0x23700, 0x2371c, +		0x23780, 0x2378c, +		0x23800, 0x23c38, +		0x23c80, 0x23d7c, +		0x23e00, 0x23e04, +		0x24000, 0x2402c, +		0x24100, 0x2413c, +		0x24190, 0x241c8, +		0x24200, 0x24318, +		0x24400, 0x24528, +		0x24540, 0x24614, +		0x25000, 0x25040, +		0x2504c, 0x25060, +		0x250c0, 0x250ec, +		0x25200, 0x25268, +		0x25270, 0x25284, +		0x252fc, 0x25388, +		0x25400, 0x25404, +		0x25500, 0x25518, +		0x2552c, 0x2553c, +		0x25550, 0x25554, +		0x25600, 0x25600, +		0x25608, 0x25628, +		0x25630, 0x2563c, +		0x25700, 0x2571c, +		0x25780, 0x2578c, +		0x25800, 0x25c38, +		0x25c80, 0x25d7c, +		0x25e00, 0x25e04, +		0x26000, 0x2602c, +		0x26100, 0x2613c, +		0x26190, 0x261c8, +		0x26200, 0x26318, +		0x26400, 0x26528, +		0x26540, 0x26614, +		0x27000, 0x27040, +		0x2704c, 0x27060, +		0x270c0, 0x270ec, +		0x27200, 0x27268, +		0x27270, 0x27284, +		0x272fc, 0x27388, +		0x27400, 0x27404, +		0x27500, 0x27518, +		0x2752c, 0x2753c, +		0x27550, 0x27554, +		0x27600, 0x27600, +		0x27608, 0x27628, +		0x27630, 0x2763c, +		0x27700, 0x2771c, +		0x27780, 0x2778c, +		0x27800, 0x27c38, +		0x27c80, 0x27d7c, +		0x27e00, 0x27e04 +	}; + +	int i; +	struct adapter *ap = netdev2adap(dev); + +	regs->version = mk_adap_vers(ap); + +	memset(buf, 0, T4_REGMAP_SIZE); +	for (i = 0; i < ARRAY_SIZE(reg_ranges); i += 2) +		reg_block_dump(ap, buf, reg_ranges[i], reg_ranges[i + 1]); +} + +static int restart_autoneg(struct net_device *dev) +{ +	struct port_info *p = netdev_priv(dev); + +	if (!netif_running(dev)) +		return -EAGAIN; +	if (p->link_cfg.autoneg != AUTONEG_ENABLE) +		return -EINVAL; +	t4_restart_aneg(p->adapter, 0, p->tx_chan); +	return 0; +} + +static int identify_port(struct net_device *dev, u32 data) +{ +	if (data == 0) +		data = 2;     /* default to 2 seconds */ + +	return t4_identify_port(netdev2adap(dev), 0, netdev2pinfo(dev)->viid, +				data * 5); +} + +static unsigned int from_fw_linkcaps(unsigned int type, unsigned int caps) +{ +	unsigned int v = 0; + +	if (type == FW_PORT_TYPE_BT_SGMII || type == FW_PORT_TYPE_BT_XAUI) { +		v |= SUPPORTED_TP; +		if (caps & FW_PORT_CAP_SPEED_100M) +			v |= SUPPORTED_100baseT_Full; +		if (caps & FW_PORT_CAP_SPEED_1G) +			v |= SUPPORTED_1000baseT_Full; +		if (caps & FW_PORT_CAP_SPEED_10G) +			v |= SUPPORTED_10000baseT_Full; +	} else if (type == FW_PORT_TYPE_KX4 || type == FW_PORT_TYPE_KX) { +		v |= SUPPORTED_Backplane; +		if (caps & FW_PORT_CAP_SPEED_1G) +			v |= SUPPORTED_1000baseKX_Full; +		if (caps & FW_PORT_CAP_SPEED_10G) +			v |= SUPPORTED_10000baseKX4_Full; +	} else if (type == FW_PORT_TYPE_KR) +		v |= SUPPORTED_Backplane | SUPPORTED_10000baseKR_Full; +	else if (type == FW_PORT_TYPE_FIBER) +		v |= SUPPORTED_FIBRE; + +	if (caps & FW_PORT_CAP_ANEG) +		v |= SUPPORTED_Autoneg; +	return v; +} + +static unsigned int to_fw_linkcaps(unsigned int caps) +{ +	unsigned int v = 0; + +	if (caps & ADVERTISED_100baseT_Full) +		v |= FW_PORT_CAP_SPEED_100M; +	if (caps & ADVERTISED_1000baseT_Full) +		v |= FW_PORT_CAP_SPEED_1G; +	if (caps & ADVERTISED_10000baseT_Full) +		v |= FW_PORT_CAP_SPEED_10G; +	return v; +} + +static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ +	const struct port_info *p = netdev_priv(dev); + +	if (p->port_type == FW_PORT_TYPE_BT_SGMII || +	    p->port_type == FW_PORT_TYPE_BT_XAUI) +		cmd->port = PORT_TP; +	else if (p->port_type == FW_PORT_TYPE_FIBER) +		cmd->port = PORT_FIBRE; +	else if (p->port_type == FW_PORT_TYPE_TWINAX) +		cmd->port = PORT_DA; +	else +		cmd->port = PORT_OTHER; + +	if (p->mdio_addr >= 0) { +		cmd->phy_address = p->mdio_addr; +		cmd->transceiver = XCVR_EXTERNAL; +		cmd->mdio_support = p->port_type == FW_PORT_TYPE_BT_SGMII ? +			MDIO_SUPPORTS_C22 : MDIO_SUPPORTS_C45; +	} else { +		cmd->phy_address = 0;  /* not really, but no better option */ +		cmd->transceiver = XCVR_INTERNAL; +		cmd->mdio_support = 0; +	} + +	cmd->supported = from_fw_linkcaps(p->port_type, p->link_cfg.supported); +	cmd->advertising = from_fw_linkcaps(p->port_type, +					    p->link_cfg.advertising); +	cmd->speed = netif_carrier_ok(dev) ? p->link_cfg.speed : 0; +	cmd->duplex = DUPLEX_FULL; +	cmd->autoneg = p->link_cfg.autoneg; +	cmd->maxtxpkt = 0; +	cmd->maxrxpkt = 0; +	return 0; +} + +static unsigned int speed_to_caps(int speed) +{ +	if (speed == SPEED_100) +		return FW_PORT_CAP_SPEED_100M; +	if (speed == SPEED_1000) +		return FW_PORT_CAP_SPEED_1G; +	if (speed == SPEED_10000) +		return FW_PORT_CAP_SPEED_10G; +	return 0; +} + +static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ +	unsigned int cap; +	struct port_info *p = netdev_priv(dev); +	struct link_config *lc = &p->link_cfg; + +	if (cmd->duplex != DUPLEX_FULL)     /* only full-duplex supported */ +		return -EINVAL; + +	if (!(lc->supported & FW_PORT_CAP_ANEG)) { +		/* +		 * PHY offers a single speed.  See if that's what's +		 * being requested. +		 */ +		if (cmd->autoneg == AUTONEG_DISABLE && +		    (lc->supported & speed_to_caps(cmd->speed))) +				return 0; +		return -EINVAL; +	} + +	if (cmd->autoneg == AUTONEG_DISABLE) { +		cap = speed_to_caps(cmd->speed); + +		if (!(lc->supported & cap) || cmd->speed == SPEED_1000 || +		    cmd->speed == SPEED_10000) +			return -EINVAL; +		lc->requested_speed = cap; +		lc->advertising = 0; +	} else { +		cap = to_fw_linkcaps(cmd->advertising); +		if (!(lc->supported & cap)) +			return -EINVAL; +		lc->requested_speed = 0; +		lc->advertising = cap | FW_PORT_CAP_ANEG; +	} +	lc->autoneg = cmd->autoneg; + +	if (netif_running(dev)) +		return t4_link_start(p->adapter, 0, p->tx_chan, lc); +	return 0; +} + +static void get_pauseparam(struct net_device *dev, +			   struct ethtool_pauseparam *epause) +{ +	struct port_info *p = netdev_priv(dev); + +	epause->autoneg = (p->link_cfg.requested_fc & PAUSE_AUTONEG) != 0; +	epause->rx_pause = (p->link_cfg.fc & PAUSE_RX) != 0; +	epause->tx_pause = (p->link_cfg.fc & PAUSE_TX) != 0; +} + +static int set_pauseparam(struct net_device *dev, +			  struct ethtool_pauseparam *epause) +{ +	struct port_info *p = netdev_priv(dev); +	struct link_config *lc = &p->link_cfg; + +	if (epause->autoneg == AUTONEG_DISABLE) +		lc->requested_fc = 0; +	else if (lc->supported & FW_PORT_CAP_ANEG) +		lc->requested_fc = PAUSE_AUTONEG; +	else +		return -EINVAL; + +	if (epause->rx_pause) +		lc->requested_fc |= PAUSE_RX; +	if (epause->tx_pause) +		lc->requested_fc |= PAUSE_TX; +	if (netif_running(dev)) +		return t4_link_start(p->adapter, 0, p->tx_chan, lc); +	return 0; +} + +static u32 get_rx_csum(struct net_device *dev) +{ +	struct port_info *p = netdev_priv(dev); + +	return p->rx_offload & RX_CSO; +} + +static int set_rx_csum(struct net_device *dev, u32 data) +{ +	struct port_info *p = netdev_priv(dev); + +	if (data) +		p->rx_offload |= RX_CSO; +	else +		p->rx_offload &= ~RX_CSO; +	return 0; +} + +static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ +	const struct port_info *pi = netdev_priv(dev); +	const struct sge *s = &pi->adapter->sge; + +	e->rx_max_pending = MAX_RX_BUFFERS; +	e->rx_mini_max_pending = MAX_RSPQ_ENTRIES; +	e->rx_jumbo_max_pending = 0; +	e->tx_max_pending = MAX_TXQ_ENTRIES; + +	e->rx_pending = s->ethrxq[pi->first_qset].fl.size - 8; +	e->rx_mini_pending = s->ethrxq[pi->first_qset].rspq.size; +	e->rx_jumbo_pending = 0; +	e->tx_pending = s->ethtxq[pi->first_qset].q.size; +} + +static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) +{ +	int i; +	const struct port_info *pi = netdev_priv(dev); +	struct adapter *adapter = pi->adapter; +	struct sge *s = &adapter->sge; + +	if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending || +	    e->tx_pending > MAX_TXQ_ENTRIES || +	    e->rx_mini_pending > MAX_RSPQ_ENTRIES || +	    e->rx_mini_pending < MIN_RSPQ_ENTRIES || +	    e->rx_pending < MIN_FL_ENTRIES || e->tx_pending < MIN_TXQ_ENTRIES) +		return -EINVAL; + +	if (adapter->flags & FULL_INIT_DONE) +		return -EBUSY; + +	for (i = 0; i < pi->nqsets; ++i) { +		s->ethtxq[pi->first_qset + i].q.size = e->tx_pending; +		s->ethrxq[pi->first_qset + i].fl.size = e->rx_pending + 8; +		s->ethrxq[pi->first_qset + i].rspq.size = e->rx_mini_pending; +	} +	return 0; +} + +static int closest_timer(const struct sge *s, int time) +{ +	int i, delta, match = 0, min_delta = INT_MAX; + +	for (i = 0; i < ARRAY_SIZE(s->timer_val); i++) { +		delta = time - s->timer_val[i]; +		if (delta < 0) +			delta = -delta; +		if (delta < min_delta) { +			min_delta = delta; +			match = i; +		} +	} +	return match; +} + +static int closest_thres(const struct sge *s, int thres) +{ +	int i, delta, match = 0, min_delta = INT_MAX; + +	for (i = 0; i < ARRAY_SIZE(s->counter_val); i++) { +		delta = thres - s->counter_val[i]; +		if (delta < 0) +			delta = -delta; +		if (delta < min_delta) { +			min_delta = delta; +			match = i; +		} +	} +	return match; +} + +/* + * Return a queue's interrupt hold-off time in us.  0 means no timer. + */ +static unsigned int qtimer_val(const struct adapter *adap, +			       const struct sge_rspq *q) +{ +	unsigned int idx = q->intr_params >> 1; + +	return idx < SGE_NTIMERS ? adap->sge.timer_val[idx] : 0; +} + +/** + *	set_rxq_intr_params - set a queue's interrupt holdoff parameters + *	@adap: the adapter + *	@q: the Rx queue + *	@us: the hold-off time in us, or 0 to disable timer + *	@cnt: the hold-off packet count, or 0 to disable counter + * + *	Sets an Rx queue's interrupt hold-off time and packet count.  At least + *	one of the two needs to be enabled for the queue to generate interrupts. + */ +static int set_rxq_intr_params(struct adapter *adap, struct sge_rspq *q, +			       unsigned int us, unsigned int cnt) +{ +	if ((us | cnt) == 0) +		cnt = 1; + +	if (cnt) { +		int err; +		u32 v, new_idx; + +		new_idx = closest_thres(&adap->sge, cnt); +		if (q->desc && q->pktcnt_idx != new_idx) { +			/* the queue has already been created, update it */ +			v = FW_PARAMS_MNEM(FW_PARAMS_MNEM_DMAQ) | +			    FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DMAQ_IQ_INTCNTTHRESH) | +			    FW_PARAMS_PARAM_YZ(q->cntxt_id); +			err = t4_set_params(adap, 0, 0, 0, 1, &v, &new_idx); +			if (err) +				return err; +		} +		q->pktcnt_idx = new_idx; +	} + +	us = us == 0 ? 6 : closest_timer(&adap->sge, us); +	q->intr_params = QINTR_TIMER_IDX(us) | (cnt > 0 ? QINTR_CNT_EN : 0); +	return 0; +} + +static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ +	const struct port_info *pi = netdev_priv(dev); +	struct adapter *adap = pi->adapter; + +	return set_rxq_intr_params(adap, &adap->sge.ethrxq[pi->first_qset].rspq, +			c->rx_coalesce_usecs, c->rx_max_coalesced_frames); +} + +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +{ +	const struct port_info *pi = netdev_priv(dev); +	const struct adapter *adap = pi->adapter; +	const struct sge_rspq *rq = &adap->sge.ethrxq[pi->first_qset].rspq; + +	c->rx_coalesce_usecs = qtimer_val(adap, rq); +	c->rx_max_coalesced_frames = (rq->intr_params & QINTR_CNT_EN) ? +		adap->sge.counter_val[rq->pktcnt_idx] : 0; +	return 0; +} + +/* + * Translate a physical EEPROM address to virtual.  The first 1K is accessed + * through virtual addresses starting at 31K, the rest is accessed through + * virtual addresses starting at 0.  This mapping is correct only for PF0. + */ +static int eeprom_ptov(unsigned int phys_addr) +{ +	if (phys_addr < 1024) +		return phys_addr + (31 << 10); +	if (phys_addr < EEPROMSIZE) +		return phys_addr - 1024; +	return -EINVAL; +} + +/* + * The next two routines implement eeprom read/write from physical addresses. + * The physical->virtual translation is correct only for PF0. + */ +static int eeprom_rd_phys(struct adapter *adap, unsigned int phys_addr, u32 *v) +{ +	int vaddr = eeprom_ptov(phys_addr); + +	if (vaddr >= 0) +		vaddr = pci_read_vpd(adap->pdev, vaddr, sizeof(u32), v); +	return vaddr < 0 ? vaddr : 0; +} + +static int eeprom_wr_phys(struct adapter *adap, unsigned int phys_addr, u32 v) +{ +	int vaddr = eeprom_ptov(phys_addr); + +	if (vaddr >= 0) +		vaddr = pci_write_vpd(adap->pdev, vaddr, sizeof(u32), &v); +	return vaddr < 0 ? vaddr : 0; +} + +#define EEPROM_MAGIC 0x38E2F10C + +static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, +		      u8 *data) +{ +	int i, err = 0; +	struct adapter *adapter = netdev2adap(dev); + +	u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	e->magic = EEPROM_MAGIC; +	for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) +		err = eeprom_rd_phys(adapter, i, (u32 *)&buf[i]); + +	if (!err) +		memcpy(data, buf + e->offset, e->len); +	kfree(buf); +	return err; +} + +static int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, +		      u8 *data) +{ +	u8 *buf; +	int err = 0; +	u32 aligned_offset, aligned_len, *p; +	struct adapter *adapter = netdev2adap(dev); + +	if (eeprom->magic != EEPROM_MAGIC) +		return -EINVAL; + +	aligned_offset = eeprom->offset & ~3; +	aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; + +	if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { +		/* +		 * RMW possibly needed for first or last words. +		 */ +		buf = kmalloc(aligned_len, GFP_KERNEL); +		if (!buf) +			return -ENOMEM; +		err = eeprom_rd_phys(adapter, aligned_offset, (u32 *)buf); +		if (!err && aligned_len > 4) +			err = eeprom_rd_phys(adapter, +					     aligned_offset + aligned_len - 4, +					     (u32 *)&buf[aligned_len - 4]); +		if (err) +			goto out; +		memcpy(buf + (eeprom->offset & 3), data, eeprom->len); +	} else +		buf = data; + +	err = t4_seeprom_wp(adapter, false); +	if (err) +		goto out; + +	for (p = (u32 *)buf; !err && aligned_len; aligned_len -= 4, p++) { +		err = eeprom_wr_phys(adapter, aligned_offset, *p); +		aligned_offset += 4; +	} + +	if (!err) +		err = t4_seeprom_wp(adapter, true); +out: +	if (buf != data) +		kfree(buf); +	return err; +} + +static int set_flash(struct net_device *netdev, struct ethtool_flash *ef) +{ +	int ret; +	const struct firmware *fw; +	struct adapter *adap = netdev2adap(netdev); + +	ef->data[sizeof(ef->data) - 1] = '\0'; +	ret = request_firmware(&fw, ef->data, adap->pdev_dev); +	if (ret < 0) +		return ret; + +	ret = t4_load_fw(adap, fw->data, fw->size); +	release_firmware(fw); +	if (!ret) +		dev_info(adap->pdev_dev, "loaded firmware %s\n", ef->data); +	return ret; +} + +#define WOL_SUPPORTED (WAKE_BCAST | WAKE_MAGIC) +#define BCAST_CRC 0xa0ccc1a6 + +static void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ +	wol->supported = WAKE_BCAST | WAKE_MAGIC; +	wol->wolopts = netdev2adap(dev)->wol; +	memset(&wol->sopass, 0, sizeof(wol->sopass)); +} + +static int set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) +{ +	int err = 0; +	struct port_info *pi = netdev_priv(dev); + +	if (wol->wolopts & ~WOL_SUPPORTED) +		return -EINVAL; +	t4_wol_magic_enable(pi->adapter, pi->tx_chan, +			    (wol->wolopts & WAKE_MAGIC) ? dev->dev_addr : NULL); +	if (wol->wolopts & WAKE_BCAST) { +		err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0xfe, ~0ULL, +					~0ULL, 0, false); +		if (!err) +			err = t4_wol_pat_enable(pi->adapter, pi->tx_chan, 1, +						~6ULL, ~0ULL, BCAST_CRC, true); +	} else +		t4_wol_pat_enable(pi->adapter, pi->tx_chan, 0, 0, 0, 0, false); +	return err; +} + +static int set_tso(struct net_device *dev, u32 value) +{ +	if (value) +		dev->features |= NETIF_F_TSO | NETIF_F_TSO6; +	else +		dev->features &= ~(NETIF_F_TSO | NETIF_F_TSO6); +	return 0; +} + +static struct ethtool_ops cxgb_ethtool_ops = { +	.get_settings      = get_settings, +	.set_settings      = set_settings, +	.get_drvinfo       = get_drvinfo, +	.get_msglevel      = get_msglevel, +	.set_msglevel      = set_msglevel, +	.get_ringparam     = get_sge_param, +	.set_ringparam     = set_sge_param, +	.get_coalesce      = get_coalesce, +	.set_coalesce      = set_coalesce, +	.get_eeprom_len    = get_eeprom_len, +	.get_eeprom        = get_eeprom, +	.set_eeprom        = set_eeprom, +	.get_pauseparam    = get_pauseparam, +	.set_pauseparam    = set_pauseparam, +	.get_rx_csum       = get_rx_csum, +	.set_rx_csum       = set_rx_csum, +	.set_tx_csum       = ethtool_op_set_tx_ipv6_csum, +	.set_sg            = ethtool_op_set_sg, +	.get_link          = ethtool_op_get_link, +	.get_strings       = get_strings, +	.phys_id           = identify_port, +	.nway_reset        = restart_autoneg, +	.get_sset_count    = get_sset_count, +	.get_ethtool_stats = get_stats, +	.get_regs_len      = get_regs_len, +	.get_regs          = get_regs, +	.get_wol           = get_wol, +	.set_wol           = set_wol, +	.set_tso           = set_tso, +	.flash_device      = set_flash, +}; + +/* + * debugfs support + */ + +static int mem_open(struct inode *inode, struct file *file) +{ +	file->private_data = inode->i_private; +	return 0; +} + +static ssize_t mem_read(struct file *file, char __user *buf, size_t count, +			loff_t *ppos) +{ +	loff_t pos = *ppos; +	loff_t avail = file->f_path.dentry->d_inode->i_size; +	unsigned int mem = (uintptr_t)file->private_data & 3; +	struct adapter *adap = file->private_data - mem; + +	if (pos < 0) +		return -EINVAL; +	if (pos >= avail) +		return 0; +	if (count > avail - pos) +		count = avail - pos; + +	while (count) { +		size_t len; +		int ret, ofst; +		__be32 data[16]; + +		if (mem == MEM_MC) +			ret = t4_mc_read(adap, pos, data, NULL); +		else +			ret = t4_edc_read(adap, mem, pos, data, NULL); +		if (ret) +			return ret; + +		ofst = pos % sizeof(data); +		len = min(count, sizeof(data) - ofst); +		if (copy_to_user(buf, (u8 *)data + ofst, len)) +			return -EFAULT; + +		buf += len; +		pos += len; +		count -= len; +	} +	count = pos - *ppos; +	*ppos = pos; +	return count; +} + +static const struct file_operations mem_debugfs_fops = { +	.owner   = THIS_MODULE, +	.open    = mem_open, +	.read    = mem_read, +}; + +static void __devinit add_debugfs_mem(struct adapter *adap, const char *name, +				      unsigned int idx, unsigned int size_mb) +{ +	struct dentry *de; + +	de = debugfs_create_file(name, S_IRUSR, adap->debugfs_root, +				 (void *)adap + idx, &mem_debugfs_fops); +	if (de && de->d_inode) +		de->d_inode->i_size = size_mb << 20; +} + +static int __devinit setup_debugfs(struct adapter *adap) +{ +	int i; + +	if (IS_ERR_OR_NULL(adap->debugfs_root)) +		return -1; + +	i = t4_read_reg(adap, MA_TARGET_MEM_ENABLE); +	if (i & EDRAM0_ENABLE) +		add_debugfs_mem(adap, "edc0", MEM_EDC0, 5); +	if (i & EDRAM1_ENABLE) +		add_debugfs_mem(adap, "edc1", MEM_EDC1, 5); +	if (i & EXT_MEM_ENABLE) +		add_debugfs_mem(adap, "mc", MEM_MC, +			EXT_MEM_SIZE_GET(t4_read_reg(adap, MA_EXT_MEMORY_BAR))); +	if (adap->l2t) +		debugfs_create_file("l2t", S_IRUSR, adap->debugfs_root, adap, +				    &t4_l2t_fops); +	return 0; +} + +/* + * upper-layer driver support + */ + +/* + * Allocate an active-open TID and set it to the supplied value. + */ +int cxgb4_alloc_atid(struct tid_info *t, void *data) +{ +	int atid = -1; + +	spin_lock_bh(&t->atid_lock); +	if (t->afree) { +		union aopen_entry *p = t->afree; + +		atid = p - t->atid_tab; +		t->afree = p->next; +		p->data = data; +		t->atids_in_use++; +	} +	spin_unlock_bh(&t->atid_lock); +	return atid; +} +EXPORT_SYMBOL(cxgb4_alloc_atid); + +/* + * Release an active-open TID. + */ +void cxgb4_free_atid(struct tid_info *t, unsigned int atid) +{ +	union aopen_entry *p = &t->atid_tab[atid]; + +	spin_lock_bh(&t->atid_lock); +	p->next = t->afree; +	t->afree = p; +	t->atids_in_use--; +	spin_unlock_bh(&t->atid_lock); +} +EXPORT_SYMBOL(cxgb4_free_atid); + +/* + * Allocate a server TID and set it to the supplied value. + */ +int cxgb4_alloc_stid(struct tid_info *t, int family, void *data) +{ +	int stid; + +	spin_lock_bh(&t->stid_lock); +	if (family == PF_INET) { +		stid = find_first_zero_bit(t->stid_bmap, t->nstids); +		if (stid < t->nstids) +			__set_bit(stid, t->stid_bmap); +		else +			stid = -1; +	} else { +		stid = bitmap_find_free_region(t->stid_bmap, t->nstids, 2); +		if (stid < 0) +			stid = -1; +	} +	if (stid >= 0) { +		t->stid_tab[stid].data = data; +		stid += t->stid_base; +		t->stids_in_use++; +	} +	spin_unlock_bh(&t->stid_lock); +	return stid; +} +EXPORT_SYMBOL(cxgb4_alloc_stid); + +/* + * Release a server TID. + */ +void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family) +{ +	stid -= t->stid_base; +	spin_lock_bh(&t->stid_lock); +	if (family == PF_INET) +		__clear_bit(stid, t->stid_bmap); +	else +		bitmap_release_region(t->stid_bmap, stid, 2); +	t->stid_tab[stid].data = NULL; +	t->stids_in_use--; +	spin_unlock_bh(&t->stid_lock); +} +EXPORT_SYMBOL(cxgb4_free_stid); + +/* + * Populate a TID_RELEASE WR.  Caller must properly size the skb. + */ +static void mk_tid_release(struct sk_buff *skb, unsigned int chan, +			   unsigned int tid) +{ +	struct cpl_tid_release *req; + +	set_wr_txq(skb, CPL_PRIORITY_SETUP, chan); +	req = (struct cpl_tid_release *)__skb_put(skb, sizeof(*req)); +	INIT_TP_WR(req, tid); +	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_TID_RELEASE, tid)); +} + +/* + * Queue a TID release request and if necessary schedule a work queue to + * process it. + */ +void cxgb4_queue_tid_release(struct tid_info *t, unsigned int chan, +			     unsigned int tid) +{ +	void **p = &t->tid_tab[tid]; +	struct adapter *adap = container_of(t, struct adapter, tids); + +	spin_lock_bh(&adap->tid_release_lock); +	*p = adap->tid_release_head; +	/* Low 2 bits encode the Tx channel number */ +	adap->tid_release_head = (void **)((uintptr_t)p | chan); +	if (!adap->tid_release_task_busy) { +		adap->tid_release_task_busy = true; +		schedule_work(&adap->tid_release_task); +	} +	spin_unlock_bh(&adap->tid_release_lock); +} +EXPORT_SYMBOL(cxgb4_queue_tid_release); + +/* + * Process the list of pending TID release requests. + */ +static void process_tid_release_list(struct work_struct *work) +{ +	struct sk_buff *skb; +	struct adapter *adap; + +	adap = container_of(work, struct adapter, tid_release_task); + +	spin_lock_bh(&adap->tid_release_lock); +	while (adap->tid_release_head) { +		void **p = adap->tid_release_head; +		unsigned int chan = (uintptr_t)p & 3; +		p = (void *)p - chan; + +		adap->tid_release_head = *p; +		*p = NULL; +		spin_unlock_bh(&adap->tid_release_lock); + +		while (!(skb = alloc_skb(sizeof(struct cpl_tid_release), +					 GFP_KERNEL))) +			schedule_timeout_uninterruptible(1); + +		mk_tid_release(skb, chan, p - adap->tids.tid_tab); +		t4_ofld_send(adap, skb); +		spin_lock_bh(&adap->tid_release_lock); +	} +	adap->tid_release_task_busy = false; +	spin_unlock_bh(&adap->tid_release_lock); +} + +/* + * Release a TID and inform HW.  If we are unable to allocate the release + * message we defer to a work queue. + */ +void cxgb4_remove_tid(struct tid_info *t, unsigned int chan, unsigned int tid) +{ +	void *old; +	struct sk_buff *skb; +	struct adapter *adap = container_of(t, struct adapter, tids); + +	old = t->tid_tab[tid]; +	skb = alloc_skb(sizeof(struct cpl_tid_release), GFP_ATOMIC); +	if (likely(skb)) { +		t->tid_tab[tid] = NULL; +		mk_tid_release(skb, chan, tid); +		t4_ofld_send(adap, skb); +	} else +		cxgb4_queue_tid_release(t, chan, tid); +	if (old) +		atomic_dec(&t->tids_in_use); +} +EXPORT_SYMBOL(cxgb4_remove_tid); + +/* + * Allocate and initialize the TID tables.  Returns 0 on success. + */ +static int tid_init(struct tid_info *t) +{ +	size_t size; +	unsigned int natids = t->natids; + +	size = t->ntids * sizeof(*t->tid_tab) + natids * sizeof(*t->atid_tab) + +	       t->nstids * sizeof(*t->stid_tab) + +	       BITS_TO_LONGS(t->nstids) * sizeof(long); +	t->tid_tab = t4_alloc_mem(size); +	if (!t->tid_tab) +		return -ENOMEM; + +	t->atid_tab = (union aopen_entry *)&t->tid_tab[t->ntids]; +	t->stid_tab = (struct serv_entry *)&t->atid_tab[natids]; +	t->stid_bmap = (unsigned long *)&t->stid_tab[t->nstids]; +	spin_lock_init(&t->stid_lock); +	spin_lock_init(&t->atid_lock); + +	t->stids_in_use = 0; +	t->afree = NULL; +	t->atids_in_use = 0; +	atomic_set(&t->tids_in_use, 0); + +	/* Setup the free list for atid_tab and clear the stid bitmap. */ +	if (natids) { +		while (--natids) +			t->atid_tab[natids - 1].next = &t->atid_tab[natids]; +		t->afree = t->atid_tab; +	} +	bitmap_zero(t->stid_bmap, t->nstids); +	return 0; +} + +/** + *	cxgb4_create_server - create an IP server + *	@dev: the device + *	@stid: the server TID + *	@sip: local IP address to bind server to + *	@sport: the server's TCP port + *	@queue: queue to direct messages from this server to + * + *	Create an IP server for the given port and address. + *	Returns <0 on error and one of the %NET_XMIT_* values on success. + */ +int cxgb4_create_server(const struct net_device *dev, unsigned int stid, +			__be32 sip, __be16 sport, unsigned int queue) +{ +	unsigned int chan; +	struct sk_buff *skb; +	struct adapter *adap; +	struct cpl_pass_open_req *req; + +	skb = alloc_skb(sizeof(*req), GFP_KERNEL); +	if (!skb) +		return -ENOMEM; + +	adap = netdev2adap(dev); +	req = (struct cpl_pass_open_req *)__skb_put(skb, sizeof(*req)); +	INIT_TP_WR(req, 0); +	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ, stid)); +	req->local_port = sport; +	req->peer_port = htons(0); +	req->local_ip = sip; +	req->peer_ip = htonl(0); +	chan = netdev2pinfo(adap->sge.ingr_map[queue]->netdev)->tx_chan; +	req->opt0 = cpu_to_be64(TX_CHAN(chan)); +	req->opt1 = cpu_to_be64(CONN_POLICY_ASK | +				SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); +	return t4_mgmt_tx(adap, skb); +} +EXPORT_SYMBOL(cxgb4_create_server); + +/** + *	cxgb4_create_server6 - create an IPv6 server + *	@dev: the device + *	@stid: the server TID + *	@sip: local IPv6 address to bind server to + *	@sport: the server's TCP port + *	@queue: queue to direct messages from this server to + * + *	Create an IPv6 server for the given port and address. + *	Returns <0 on error and one of the %NET_XMIT_* values on success. + */ +int cxgb4_create_server6(const struct net_device *dev, unsigned int stid, +			 const struct in6_addr *sip, __be16 sport, +			 unsigned int queue) +{ +	unsigned int chan; +	struct sk_buff *skb; +	struct adapter *adap; +	struct cpl_pass_open_req6 *req; + +	skb = alloc_skb(sizeof(*req), GFP_KERNEL); +	if (!skb) +		return -ENOMEM; + +	adap = netdev2adap(dev); +	req = (struct cpl_pass_open_req6 *)__skb_put(skb, sizeof(*req)); +	INIT_TP_WR(req, 0); +	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_PASS_OPEN_REQ6, stid)); +	req->local_port = sport; +	req->peer_port = htons(0); +	req->local_ip_hi = *(__be64 *)(sip->s6_addr); +	req->local_ip_lo = *(__be64 *)(sip->s6_addr + 8); +	req->peer_ip_hi = cpu_to_be64(0); +	req->peer_ip_lo = cpu_to_be64(0); +	chan = netdev2pinfo(adap->sge.ingr_map[queue]->netdev)->tx_chan; +	req->opt0 = cpu_to_be64(TX_CHAN(chan)); +	req->opt1 = cpu_to_be64(CONN_POLICY_ASK | +				SYN_RSS_ENABLE | SYN_RSS_QUEUE(queue)); +	return t4_mgmt_tx(adap, skb); +} +EXPORT_SYMBOL(cxgb4_create_server6); + +/** + *	cxgb4_best_mtu - find the entry in the MTU table closest to an MTU + *	@mtus: the HW MTU table + *	@mtu: the target MTU + *	@idx: index of selected entry in the MTU table + * + *	Returns the index and the value in the HW MTU table that is closest to + *	but does not exceed @mtu, unless @mtu is smaller than any value in the + *	table, in which case that smallest available value is selected. + */ +unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu, +			    unsigned int *idx) +{ +	unsigned int i = 0; + +	while (i < NMTUS - 1 && mtus[i + 1] <= mtu) +		++i; +	if (idx) +		*idx = i; +	return mtus[i]; +} +EXPORT_SYMBOL(cxgb4_best_mtu); + +/** + *	cxgb4_port_chan - get the HW channel of a port + *	@dev: the net device for the port + * + *	Return the HW Tx channel of the given port. + */ +unsigned int cxgb4_port_chan(const struct net_device *dev) +{ +	return netdev2pinfo(dev)->tx_chan; +} +EXPORT_SYMBOL(cxgb4_port_chan); + +/** + *	cxgb4_port_viid - get the VI id of a port + *	@dev: the net device for the port + * + *	Return the VI id of the given port. + */ +unsigned int cxgb4_port_viid(const struct net_device *dev) +{ +	return netdev2pinfo(dev)->viid; +} +EXPORT_SYMBOL(cxgb4_port_viid); + +/** + *	cxgb4_port_idx - get the index of a port + *	@dev: the net device for the port + * + *	Return the index of the given port. + */ +unsigned int cxgb4_port_idx(const struct net_device *dev) +{ +	return netdev2pinfo(dev)->port_id; +} +EXPORT_SYMBOL(cxgb4_port_idx); + +/** + *	cxgb4_netdev_by_hwid - return the net device of a HW port + *	@pdev: identifies the adapter + *	@id: the HW port id + * + *	Return the net device associated with the interface with the given HW + *	id. + */ +struct net_device *cxgb4_netdev_by_hwid(struct pci_dev *pdev, unsigned int id) +{ +	const struct adapter *adap = pci_get_drvdata(pdev); + +	if (!adap || id >= NCHAN) +		return NULL; +	id = adap->chan_map[id]; +	return id < MAX_NPORTS ? adap->port[id] : NULL; +} +EXPORT_SYMBOL(cxgb4_netdev_by_hwid); + +void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4, +			 struct tp_tcp_stats *v6) +{ +	struct adapter *adap = pci_get_drvdata(pdev); + +	spin_lock(&adap->stats_lock); +	t4_tp_get_tcp_stats(adap, v4, v6); +	spin_unlock(&adap->stats_lock); +} +EXPORT_SYMBOL(cxgb4_get_tcp_stats); + +void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask, +		      const unsigned int *pgsz_order) +{ +	struct adapter *adap = netdev2adap(dev); + +	t4_write_reg(adap, ULP_RX_ISCSI_TAGMASK, tag_mask); +	t4_write_reg(adap, ULP_RX_ISCSI_PSZ, HPZ0(pgsz_order[0]) | +		     HPZ1(pgsz_order[1]) | HPZ2(pgsz_order[2]) | +		     HPZ3(pgsz_order[3])); +} +EXPORT_SYMBOL(cxgb4_iscsi_init); + +static struct pci_driver cxgb4_driver; + +static void check_neigh_update(struct neighbour *neigh) +{ +	const struct device *parent; +	const struct net_device *netdev = neigh->dev; + +	if (netdev->priv_flags & IFF_802_1Q_VLAN) +		netdev = vlan_dev_real_dev(netdev); +	parent = netdev->dev.parent; +	if (parent && parent->driver == &cxgb4_driver.driver) +		t4_l2t_update(dev_get_drvdata(parent), neigh); +} + +static int netevent_cb(struct notifier_block *nb, unsigned long event, +		       void *data) +{ +	switch (event) { +	case NETEVENT_NEIGH_UPDATE: +		check_neigh_update(data); +		break; +	case NETEVENT_PMTU_UPDATE: +	case NETEVENT_REDIRECT: +	default: +		break; +	} +	return 0; +} + +static bool netevent_registered; +static struct notifier_block cxgb4_netevent_nb = { +	.notifier_call = netevent_cb +}; + +static void uld_attach(struct adapter *adap, unsigned int uld) +{ +	void *handle; +	struct cxgb4_lld_info lli; + +	lli.pdev = adap->pdev; +	lli.l2t = adap->l2t; +	lli.tids = &adap->tids; +	lli.ports = adap->port; +	lli.vr = &adap->vres; +	lli.mtus = adap->params.mtus; +	if (uld == CXGB4_ULD_RDMA) { +		lli.rxq_ids = adap->sge.rdma_rxq; +		lli.nrxq = adap->sge.rdmaqs; +	} else if (uld == CXGB4_ULD_ISCSI) { +		lli.rxq_ids = adap->sge.ofld_rxq; +		lli.nrxq = adap->sge.ofldqsets; +	} +	lli.ntxq = adap->sge.ofldqsets; +	lli.nchan = adap->params.nports; +	lli.nports = adap->params.nports; +	lli.wr_cred = adap->params.ofldq_wr_cred; +	lli.adapter_type = adap->params.rev; +	lli.iscsi_iolen = MAXRXDATA_GET(t4_read_reg(adap, TP_PARA_REG2)); +	lli.udb_density = 1 << QUEUESPERPAGEPF0_GET( +			t4_read_reg(adap, SGE_EGRESS_QUEUES_PER_PAGE_PF)); +	lli.ucq_density = 1 << QUEUESPERPAGEPF0_GET( +			t4_read_reg(adap, SGE_INGRESS_QUEUES_PER_PAGE_PF)); +	lli.gts_reg = adap->regs + MYPF_REG(SGE_PF_GTS); +	lli.db_reg = adap->regs + MYPF_REG(SGE_PF_KDOORBELL); +	lli.fw_vers = adap->params.fw_vers; + +	handle = ulds[uld].add(&lli); +	if (IS_ERR(handle)) { +		dev_warn(adap->pdev_dev, +			 "could not attach to the %s driver, error %ld\n", +			 uld_str[uld], PTR_ERR(handle)); +		return; +	} + +	adap->uld_handle[uld] = handle; + +	if (!netevent_registered) { +		register_netevent_notifier(&cxgb4_netevent_nb); +		netevent_registered = true; +	} +} + +static void attach_ulds(struct adapter *adap) +{ +	unsigned int i; + +	mutex_lock(&uld_mutex); +	list_add_tail(&adap->list_node, &adapter_list); +	for (i = 0; i < CXGB4_ULD_MAX; i++) +		if (ulds[i].add) +			uld_attach(adap, i); +	mutex_unlock(&uld_mutex); +} + +static void detach_ulds(struct adapter *adap) +{ +	unsigned int i; + +	mutex_lock(&uld_mutex); +	list_del(&adap->list_node); +	for (i = 0; i < CXGB4_ULD_MAX; i++) +		if (adap->uld_handle[i]) { +			ulds[i].state_change(adap->uld_handle[i], +					     CXGB4_STATE_DETACH); +			adap->uld_handle[i] = NULL; +		} +	if (netevent_registered && list_empty(&adapter_list)) { +		unregister_netevent_notifier(&cxgb4_netevent_nb); +		netevent_registered = false; +	} +	mutex_unlock(&uld_mutex); +} + +static void notify_ulds(struct adapter *adap, enum cxgb4_state new_state) +{ +	unsigned int i; + +	mutex_lock(&uld_mutex); +	for (i = 0; i < CXGB4_ULD_MAX; i++) +		if (adap->uld_handle[i]) +			ulds[i].state_change(adap->uld_handle[i], new_state); +	mutex_unlock(&uld_mutex); +} + +/** + *	cxgb4_register_uld - register an upper-layer driver + *	@type: the ULD type + *	@p: the ULD methods + * + *	Registers an upper-layer driver with this driver and notifies the ULD + *	about any presently available devices that support its type.  Returns + *	%-EBUSY if a ULD of the same type is already registered. + */ +int cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p) +{ +	int ret = 0; +	struct adapter *adap; + +	if (type >= CXGB4_ULD_MAX) +		return -EINVAL; +	mutex_lock(&uld_mutex); +	if (ulds[type].add) { +		ret = -EBUSY; +		goto out; +	} +	ulds[type] = *p; +	list_for_each_entry(adap, &adapter_list, list_node) +		uld_attach(adap, type); +out:	mutex_unlock(&uld_mutex); +	return ret; +} +EXPORT_SYMBOL(cxgb4_register_uld); + +/** + *	cxgb4_unregister_uld - unregister an upper-layer driver + *	@type: the ULD type + * + *	Unregisters an existing upper-layer driver. + */ +int cxgb4_unregister_uld(enum cxgb4_uld type) +{ +	struct adapter *adap; + +	if (type >= CXGB4_ULD_MAX) +		return -EINVAL; +	mutex_lock(&uld_mutex); +	list_for_each_entry(adap, &adapter_list, list_node) +		adap->uld_handle[type] = NULL; +	ulds[type].add = NULL; +	mutex_unlock(&uld_mutex); +	return 0; +} +EXPORT_SYMBOL(cxgb4_unregister_uld); + +/** + *	cxgb_up - enable the adapter + *	@adap: adapter being enabled + * + *	Called when the first port is enabled, this function performs the + *	actions necessary to make an adapter operational, such as completing + *	the initialization of HW modules, and enabling interrupts. + * + *	Must be called with the rtnl lock held. + */ +static int cxgb_up(struct adapter *adap) +{ +	int err = 0; + +	if (!(adap->flags & FULL_INIT_DONE)) { +		err = setup_sge_queues(adap); +		if (err) +			goto out; +		err = setup_rss(adap); +		if (err) { +			t4_free_sge_resources(adap); +			goto out; +		} +		if (adap->flags & USING_MSIX) +			name_msix_vecs(adap); +		adap->flags |= FULL_INIT_DONE; +	} + +	if (adap->flags & USING_MSIX) { +		err = request_irq(adap->msix_info[0].vec, t4_nondata_intr, 0, +				  adap->msix_info[0].desc, adap); +		if (err) +			goto irq_err; + +		err = request_msix_queue_irqs(adap); +		if (err) { +			free_irq(adap->msix_info[0].vec, adap); +			goto irq_err; +		} +	} else { +		err = request_irq(adap->pdev->irq, t4_intr_handler(adap), +				  (adap->flags & USING_MSI) ? 0 : IRQF_SHARED, +				  adap->name, adap); +		if (err) +			goto irq_err; +	} +	enable_rx(adap); +	t4_sge_start(adap); +	t4_intr_enable(adap); +	notify_ulds(adap, CXGB4_STATE_UP); + out: +	return err; + irq_err: +	dev_err(adap->pdev_dev, "request_irq failed, err %d\n", err); +	goto out; +} + +static void cxgb_down(struct adapter *adapter) +{ +	t4_intr_disable(adapter); +	cancel_work_sync(&adapter->tid_release_task); +	adapter->tid_release_task_busy = false; + +	if (adapter->flags & USING_MSIX) { +		free_msix_queue_irqs(adapter); +		free_irq(adapter->msix_info[0].vec, adapter); +	} else +		free_irq(adapter->pdev->irq, adapter); +	quiesce_rx(adapter); +} + +/* + * net_device operations + */ +static int cxgb_open(struct net_device *dev) +{ +	int err; +	struct port_info *pi = netdev_priv(dev); +	struct adapter *adapter = pi->adapter; + +	if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) +		return err; + +	dev->real_num_tx_queues = pi->nqsets; +	set_bit(pi->tx_chan, &adapter->open_device_map); +	link_start(dev); +	netif_tx_start_all_queues(dev); +	return 0; +} + +static int cxgb_close(struct net_device *dev) +{ +	int ret; +	struct port_info *pi = netdev_priv(dev); +	struct adapter *adapter = pi->adapter; + +	netif_tx_stop_all_queues(dev); +	netif_carrier_off(dev); +	ret = t4_enable_vi(adapter, 0, pi->viid, false, false); + +	clear_bit(pi->tx_chan, &adapter->open_device_map); + +	if (!adapter->open_device_map) +		cxgb_down(adapter); +	return 0; +} + +static struct net_device_stats *cxgb_get_stats(struct net_device *dev) +{ +	struct port_stats stats; +	struct port_info *p = netdev_priv(dev); +	struct adapter *adapter = p->adapter; +	struct net_device_stats *ns = &dev->stats; + +	spin_lock(&adapter->stats_lock); +	t4_get_port_stats(adapter, p->tx_chan, &stats); +	spin_unlock(&adapter->stats_lock); + +	ns->tx_bytes   = stats.tx_octets; +	ns->tx_packets = stats.tx_frames; +	ns->rx_bytes   = stats.rx_octets; +	ns->rx_packets = stats.rx_frames; +	ns->multicast  = stats.rx_mcast_frames; + +	/* detailed rx_errors */ +	ns->rx_length_errors = stats.rx_jabber + stats.rx_too_long + +			       stats.rx_runt; +	ns->rx_over_errors   = 0; +	ns->rx_crc_errors    = stats.rx_fcs_err; +	ns->rx_frame_errors  = stats.rx_symbol_err; +	ns->rx_fifo_errors   = stats.rx_ovflow0 + stats.rx_ovflow1 + +			       stats.rx_ovflow2 + stats.rx_ovflow3 + +			       stats.rx_trunc0 + stats.rx_trunc1 + +			       stats.rx_trunc2 + stats.rx_trunc3; +	ns->rx_missed_errors = 0; + +	/* detailed tx_errors */ +	ns->tx_aborted_errors   = 0; +	ns->tx_carrier_errors   = 0; +	ns->tx_fifo_errors      = 0; +	ns->tx_heartbeat_errors = 0; +	ns->tx_window_errors    = 0; + +	ns->tx_errors = stats.tx_error_frames; +	ns->rx_errors = stats.rx_symbol_err + stats.rx_fcs_err + +		ns->rx_length_errors + stats.rx_len_err + ns->rx_fifo_errors; +	return ns; +} + +static int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ +	int ret = 0, prtad, devad; +	struct port_info *pi = netdev_priv(dev); +	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&req->ifr_data; + +	switch (cmd) { +	case SIOCGMIIPHY: +		if (pi->mdio_addr < 0) +			return -EOPNOTSUPP; +		data->phy_id = pi->mdio_addr; +		break; +	case SIOCGMIIREG: +	case SIOCSMIIREG: +		if (mdio_phy_id_is_c45(data->phy_id)) { +			prtad = mdio_phy_id_prtad(data->phy_id); +			devad = mdio_phy_id_devad(data->phy_id); +		} else if (data->phy_id < 32) { +			prtad = data->phy_id; +			devad = 0; +			data->reg_num &= 0x1f; +		} else +			return -EINVAL; + +		if (cmd == SIOCGMIIREG) +			ret = t4_mdio_rd(pi->adapter, 0, prtad, devad, +					 data->reg_num, &data->val_out); +		else +			ret = t4_mdio_wr(pi->adapter, 0, prtad, devad, +					 data->reg_num, data->val_in); +		break; +	default: +		return -EOPNOTSUPP; +	} +	return ret; +} + +static void cxgb_set_rxmode(struct net_device *dev) +{ +	/* unfortunately we can't return errors to the stack */ +	set_rxmode(dev, -1, false); +} + +static int cxgb_change_mtu(struct net_device *dev, int new_mtu) +{ +	int ret; +	struct port_info *pi = netdev_priv(dev); + +	if (new_mtu < 81 || new_mtu > MAX_MTU)         /* accommodate SACK */ +		return -EINVAL; +	ret = t4_set_rxmode(pi->adapter, 0, pi->viid, new_mtu, -1, -1, -1, +			    true); +	if (!ret) +		dev->mtu = new_mtu; +	return ret; +} + +static int cxgb_set_mac_addr(struct net_device *dev, void *p) +{ +	int ret; +	struct sockaddr *addr = p; +	struct port_info *pi = netdev_priv(dev); + +	if (!is_valid_ether_addr(addr->sa_data)) +		return -EINVAL; + +	ret = t4_change_mac(pi->adapter, 0, pi->viid, pi->xact_addr_filt, +			    addr->sa_data, true, true); +	if (ret < 0) +		return ret; + +	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); +	pi->xact_addr_filt = ret; +	return 0; +} + +static void vlan_rx_register(struct net_device *dev, struct vlan_group *grp) +{ +	struct port_info *pi = netdev_priv(dev); + +	pi->vlan_grp = grp; +	t4_set_vlan_accel(pi->adapter, 1 << pi->tx_chan, grp != NULL); +} + +#ifdef CONFIG_NET_POLL_CONTROLLER +static void cxgb_netpoll(struct net_device *dev) +{ +	struct port_info *pi = netdev_priv(dev); +	struct adapter *adap = pi->adapter; + +	if (adap->flags & USING_MSIX) { +		int i; +		struct sge_eth_rxq *rx = &adap->sge.ethrxq[pi->first_qset]; + +		for (i = pi->nqsets; i; i--, rx++) +			t4_sge_intr_msix(0, &rx->rspq); +	} else +		t4_intr_handler(adap)(0, adap); +} +#endif + +static const struct net_device_ops cxgb4_netdev_ops = { +	.ndo_open             = cxgb_open, +	.ndo_stop             = cxgb_close, +	.ndo_start_xmit       = t4_eth_xmit, +	.ndo_get_stats        = cxgb_get_stats, +	.ndo_set_rx_mode      = cxgb_set_rxmode, +	.ndo_set_mac_address  = cxgb_set_mac_addr, +	.ndo_validate_addr    = eth_validate_addr, +	.ndo_do_ioctl         = cxgb_ioctl, +	.ndo_change_mtu       = cxgb_change_mtu, +	.ndo_vlan_rx_register = vlan_rx_register, +#ifdef CONFIG_NET_POLL_CONTROLLER +	.ndo_poll_controller  = cxgb_netpoll, +#endif +}; + +void t4_fatal_err(struct adapter *adap) +{ +	t4_set_reg_field(adap, SGE_CONTROL, GLOBALENABLE, 0); +	t4_intr_disable(adap); +	dev_alert(adap->pdev_dev, "encountered fatal error, adapter stopped\n"); +} + +static void setup_memwin(struct adapter *adap) +{ +	u32 bar0; + +	bar0 = pci_resource_start(adap->pdev, 0);  /* truncation intentional */ +	t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 0), +		     (bar0 + MEMWIN0_BASE) | BIR(0) | +		     WINDOW(ilog2(MEMWIN0_APERTURE) - 10)); +	t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 1), +		     (bar0 + MEMWIN1_BASE) | BIR(0) | +		     WINDOW(ilog2(MEMWIN1_APERTURE) - 10)); +	t4_write_reg(adap, PCIE_MEM_ACCESS_REG(PCIE_MEM_ACCESS_BASE_WIN, 2), +		     (bar0 + MEMWIN2_BASE) | BIR(0) | +		     WINDOW(ilog2(MEMWIN2_APERTURE) - 10)); +} + +/* + * Max # of ATIDs.  The absolute HW max is 16K but we keep it lower. + */ +#define MAX_ATIDS 8192U + +/* + * Phase 0 of initialization: contact FW, obtain config, perform basic init. + */ +static int adap_init0(struct adapter *adap) +{ +	int ret; +	u32 v, port_vec; +	enum dev_state state; +	u32 params[7], val[7]; +	struct fw_caps_config_cmd c; + +	ret = t4_check_fw_version(adap); +	if (ret == -EINVAL || ret > 0) { +		if (upgrade_fw(adap) >= 0)             /* recache FW version */ +			ret = t4_check_fw_version(adap); +	} +	if (ret < 0) +		return ret; + +	/* contact FW, request master */ +	ret = t4_fw_hello(adap, 0, 0, MASTER_MUST, &state); +	if (ret < 0) { +		dev_err(adap->pdev_dev, "could not connect to FW, error %d\n", +			ret); +		return ret; +	} + +	/* reset device */ +	ret = t4_fw_reset(adap, 0, PIORSTMODE | PIORST); +	if (ret < 0) +		goto bye; + +	/* get device capabilities */ +	memset(&c, 0, sizeof(c)); +	c.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | +			      FW_CMD_REQUEST | FW_CMD_READ); +	c.retval_len16 = htonl(FW_LEN16(c)); +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c); +	if (ret < 0) +		goto bye; + +	/* select capabilities we'll be using */ +	if (c.niccaps & htons(FW_CAPS_CONFIG_NIC_VM)) { +		if (!vf_acls) +			c.niccaps ^= htons(FW_CAPS_CONFIG_NIC_VM); +		else +			c.niccaps = htons(FW_CAPS_CONFIG_NIC_VM); +	} else if (vf_acls) { +		dev_err(adap->pdev_dev, "virtualization ACLs not supported"); +		goto bye; +	} +	c.op_to_write = htonl(FW_CMD_OP(FW_CAPS_CONFIG_CMD) | +			      FW_CMD_REQUEST | FW_CMD_WRITE); +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), NULL); +	if (ret < 0) +		goto bye; + +	ret = t4_config_glbl_rss(adap, 0, +				 FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL, +				 FW_RSS_GLB_CONFIG_CMD_TNLMAPEN | +				 FW_RSS_GLB_CONFIG_CMD_TNLALLLKP); +	if (ret < 0) +		goto bye; + +	ret = t4_cfg_pfvf(adap, 0, 0, 0, 64, 64, 64, 0, 0, 4, 0xf, 0xf, 16, +			  FW_CMD_CAP_PF, FW_CMD_CAP_PF); +	if (ret < 0) +		goto bye; + +	for (v = 0; v < SGE_NTIMERS - 1; v++) +		adap->sge.timer_val[v] = min(intr_holdoff[v], MAX_SGE_TIMERVAL); +	adap->sge.timer_val[SGE_NTIMERS - 1] = MAX_SGE_TIMERVAL; +	adap->sge.counter_val[0] = 1; +	for (v = 1; v < SGE_NCOUNTERS; v++) +		adap->sge.counter_val[v] = min(intr_cnt[v - 1], +					       THRESHOLD_3_MASK); +	t4_sge_init(adap); + +	/* get basic stuff going */ +	ret = t4_early_init(adap, 0); +	if (ret < 0) +		goto bye; + +#define FW_PARAM_DEV(param) \ +	(FW_PARAMS_MNEM(FW_PARAMS_MNEM_DEV) | \ +	 FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_DEV_##param)) + +#define FW_PARAM_PFVF(param) \ +	(FW_PARAMS_MNEM(FW_PARAMS_MNEM_PFVF) | \ +	 FW_PARAMS_PARAM_X(FW_PARAMS_PARAM_PFVF_##param)) + +	params[0] = FW_PARAM_DEV(PORTVEC); +	params[1] = FW_PARAM_PFVF(L2T_START); +	params[2] = FW_PARAM_PFVF(L2T_END); +	params[3] = FW_PARAM_PFVF(FILTER_START); +	params[4] = FW_PARAM_PFVF(FILTER_END); +	ret = t4_query_params(adap, 0, 0, 0, 5, params, val); +	if (ret < 0) +		goto bye; +	port_vec = val[0]; +	adap->tids.ftid_base = val[3]; +	adap->tids.nftids = val[4] - val[3] + 1; + +	if (c.ofldcaps) { +		/* query offload-related parameters */ +		params[0] = FW_PARAM_DEV(NTID); +		params[1] = FW_PARAM_PFVF(SERVER_START); +		params[2] = FW_PARAM_PFVF(SERVER_END); +		params[3] = FW_PARAM_PFVF(TDDP_START); +		params[4] = FW_PARAM_PFVF(TDDP_END); +		params[5] = FW_PARAM_DEV(FLOWC_BUFFIFO_SZ); +		ret = t4_query_params(adap, 0, 0, 0, 6, params, val); +		if (ret < 0) +			goto bye; +		adap->tids.ntids = val[0]; +		adap->tids.natids = min(adap->tids.ntids / 2, MAX_ATIDS); +		adap->tids.stid_base = val[1]; +		adap->tids.nstids = val[2] - val[1] + 1; +		adap->vres.ddp.start = val[3]; +		adap->vres.ddp.size = val[4] - val[3] + 1; +		adap->params.ofldq_wr_cred = val[5]; +		adap->params.offload = 1; +	} +	if (c.rdmacaps) { +		params[0] = FW_PARAM_PFVF(STAG_START); +		params[1] = FW_PARAM_PFVF(STAG_END); +		params[2] = FW_PARAM_PFVF(RQ_START); +		params[3] = FW_PARAM_PFVF(RQ_END); +		params[4] = FW_PARAM_PFVF(PBL_START); +		params[5] = FW_PARAM_PFVF(PBL_END); +		ret = t4_query_params(adap, 0, 0, 0, 6, params, val); +		if (ret < 0) +			goto bye; +		adap->vres.stag.start = val[0]; +		adap->vres.stag.size = val[1] - val[0] + 1; +		adap->vres.rq.start = val[2]; +		adap->vres.rq.size = val[3] - val[2] + 1; +		adap->vres.pbl.start = val[4]; +		adap->vres.pbl.size = val[5] - val[4] + 1; +	} +	if (c.iscsicaps) { +		params[0] = FW_PARAM_PFVF(ISCSI_START); +		params[1] = FW_PARAM_PFVF(ISCSI_END); +		ret = t4_query_params(adap, 0, 0, 0, 2, params, val); +		if (ret < 0) +			goto bye; +		adap->vres.iscsi.start = val[0]; +		adap->vres.iscsi.size = val[1] - val[0] + 1; +	} +#undef FW_PARAM_PFVF +#undef FW_PARAM_DEV + +	adap->params.nports = hweight32(port_vec); +	adap->params.portvec = port_vec; +	adap->flags |= FW_OK; + +	/* These are finalized by FW initialization, load their values now */ +	v = t4_read_reg(adap, TP_TIMER_RESOLUTION); +	adap->params.tp.tre = TIMERRESOLUTION_GET(v); +	t4_read_mtu_tbl(adap, adap->params.mtus, NULL); +	t4_load_mtus(adap, adap->params.mtus, adap->params.a_wnd, +		     adap->params.b_wnd); + +	/* tweak some settings */ +	t4_write_reg(adap, TP_SHIFT_CNT, 0x64f8849); +	t4_write_reg(adap, ULP_RX_TDDP_PSZ, HPZ0(PAGE_SHIFT - 12)); +	t4_write_reg(adap, TP_PIO_ADDR, TP_INGRESS_CONFIG); +	v = t4_read_reg(adap, TP_PIO_DATA); +	t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR); +	setup_memwin(adap); +	return 0; + +	/* +	 * If a command timed out or failed with EIO FW does not operate within +	 * its spec or something catastrophic happened to HW/FW, stop issuing +	 * commands. +	 */ +bye:	if (ret != -ETIMEDOUT && ret != -EIO) +		t4_fw_bye(adap, 0); +	return ret; +} + +static inline bool is_10g_port(const struct link_config *lc) +{ +	return (lc->supported & FW_PORT_CAP_SPEED_10G) != 0; +} + +static inline void init_rspq(struct sge_rspq *q, u8 timer_idx, u8 pkt_cnt_idx, +			     unsigned int size, unsigned int iqe_size) +{ +	q->intr_params = QINTR_TIMER_IDX(timer_idx) | +			 (pkt_cnt_idx < SGE_NCOUNTERS ? QINTR_CNT_EN : 0); +	q->pktcnt_idx = pkt_cnt_idx < SGE_NCOUNTERS ? pkt_cnt_idx : 0; +	q->iqe_len = iqe_size; +	q->size = size; +} + +/* + * Perform default configuration of DMA queues depending on the number and type + * of ports we found and the number of available CPUs.  Most settings can be + * modified by the admin prior to actual use. + */ +static void __devinit cfg_queues(struct adapter *adap) +{ +	struct sge *s = &adap->sge; +	int i, q10g = 0, n10g = 0, qidx = 0; + +	for_each_port(adap, i) +		n10g += is_10g_port(&adap2pinfo(adap, i)->link_cfg); + +	/* +	 * We default to 1 queue per non-10G port and up to # of cores queues +	 * per 10G port. +	 */ +	if (n10g) +		q10g = (MAX_ETH_QSETS - (adap->params.nports - n10g)) / n10g; +	if (q10g > num_online_cpus()) +		q10g = num_online_cpus(); + +	for_each_port(adap, i) { +		struct port_info *pi = adap2pinfo(adap, i); + +		pi->first_qset = qidx; +		pi->nqsets = is_10g_port(&pi->link_cfg) ? q10g : 1; +		qidx += pi->nqsets; +	} + +	s->ethqsets = qidx; +	s->max_ethqsets = qidx;   /* MSI-X may lower it later */ + +	if (is_offload(adap)) { +		/* +		 * For offload we use 1 queue/channel if all ports are up to 1G, +		 * otherwise we divide all available queues amongst the channels +		 * capped by the number of available cores. +		 */ +		if (n10g) { +			i = min_t(int, ARRAY_SIZE(s->ofldrxq), +				  num_online_cpus()); +			s->ofldqsets = roundup(i, adap->params.nports); +		} else +			s->ofldqsets = adap->params.nports; +		/* For RDMA one Rx queue per channel suffices */ +		s->rdmaqs = adap->params.nports; +	} + +	for (i = 0; i < ARRAY_SIZE(s->ethrxq); i++) { +		struct sge_eth_rxq *r = &s->ethrxq[i]; + +		init_rspq(&r->rspq, 0, 0, 1024, 64); +		r->fl.size = 72; +	} + +	for (i = 0; i < ARRAY_SIZE(s->ethtxq); i++) +		s->ethtxq[i].q.size = 1024; + +	for (i = 0; i < ARRAY_SIZE(s->ctrlq); i++) +		s->ctrlq[i].q.size = 512; + +	for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++) +		s->ofldtxq[i].q.size = 1024; + +	for (i = 0; i < ARRAY_SIZE(s->ofldrxq); i++) { +		struct sge_ofld_rxq *r = &s->ofldrxq[i]; + +		init_rspq(&r->rspq, 0, 0, 1024, 64); +		r->rspq.uld = CXGB4_ULD_ISCSI; +		r->fl.size = 72; +	} + +	for (i = 0; i < ARRAY_SIZE(s->rdmarxq); i++) { +		struct sge_ofld_rxq *r = &s->rdmarxq[i]; + +		init_rspq(&r->rspq, 0, 0, 511, 64); +		r->rspq.uld = CXGB4_ULD_RDMA; +		r->fl.size = 72; +	} + +	init_rspq(&s->fw_evtq, 6, 0, 512, 64); +	init_rspq(&s->intrq, 6, 0, 2 * MAX_INGQ, 64); +} + +/* + * Reduce the number of Ethernet queues across all ports to at most n. + * n provides at least one queue per port. + */ +static void __devinit reduce_ethqs(struct adapter *adap, int n) +{ +	int i; +	struct port_info *pi; + +	while (n < adap->sge.ethqsets) +		for_each_port(adap, i) { +			pi = adap2pinfo(adap, i); +			if (pi->nqsets > 1) { +				pi->nqsets--; +				adap->sge.ethqsets--; +				if (adap->sge.ethqsets <= n) +					break; +			} +		} + +	n = 0; +	for_each_port(adap, i) { +		pi = adap2pinfo(adap, i); +		pi->first_qset = n; +		n += pi->nqsets; +	} +} + +/* 2 MSI-X vectors needed for the FW queue and non-data interrupts */ +#define EXTRA_VECS 2 + +static int __devinit enable_msix(struct adapter *adap) +{ +	int ofld_need = 0; +	int i, err, want, need; +	struct sge *s = &adap->sge; +	unsigned int nchan = adap->params.nports; +	struct msix_entry entries[MAX_INGQ + 1]; + +	for (i = 0; i < ARRAY_SIZE(entries); ++i) +		entries[i].entry = i; + +	want = s->max_ethqsets + EXTRA_VECS; +	if (is_offload(adap)) { +		want += s->rdmaqs + s->ofldqsets; +		/* need nchan for each possible ULD */ +		ofld_need = 2 * nchan; +	} +	need = adap->params.nports + EXTRA_VECS + ofld_need; + +	while ((err = pci_enable_msix(adap->pdev, entries, want)) >= need) +		want = err; + +	if (!err) { +		/* +		 * Distribute available vectors to the various queue groups. +		 * Every group gets its minimum requirement and NIC gets top +		 * priority for leftovers. +		 */ +		i = want - EXTRA_VECS - ofld_need; +		if (i < s->max_ethqsets) { +			s->max_ethqsets = i; +			if (i < s->ethqsets) +				reduce_ethqs(adap, i); +		} +		if (is_offload(adap)) { +			i = want - EXTRA_VECS - s->max_ethqsets; +			i -= ofld_need - nchan; +			s->ofldqsets = (i / nchan) * nchan;  /* round down */ +		} +		for (i = 0; i < want; ++i) +			adap->msix_info[i].vec = entries[i].vector; +	} else if (err > 0) +		dev_info(adap->pdev_dev, +			 "only %d MSI-X vectors left, not using MSI-X\n", err); +	return err; +} + +#undef EXTRA_VECS + +static void __devinit print_port_info(struct adapter *adap) +{ +	static const char *base[] = { +		"R", "KX4", "T", "KX", "T", "KR", "CX4" +	}; + +	int i; +	char buf[80]; + +	for_each_port(adap, i) { +		struct net_device *dev = adap->port[i]; +		const struct port_info *pi = netdev_priv(dev); +		char *bufp = buf; + +		if (!test_bit(i, &adap->registered_device_map)) +			continue; + +		if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_100M) +			bufp += sprintf(bufp, "100/"); +		if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_1G) +			bufp += sprintf(bufp, "1000/"); +		if (pi->link_cfg.supported & FW_PORT_CAP_SPEED_10G) +			bufp += sprintf(bufp, "10G/"); +		if (bufp != buf) +			--bufp; +		sprintf(bufp, "BASE-%s", base[pi->port_type]); + +		netdev_info(dev, "Chelsio %s rev %d %s %sNIC PCIe x%d%s\n", +			    adap->params.vpd.id, adap->params.rev, +			    buf, is_offload(adap) ? "R" : "", +			    adap->params.pci.width, +			    (adap->flags & USING_MSIX) ? " MSI-X" : +			    (adap->flags & USING_MSI) ? " MSI" : ""); +		if (adap->name == dev->name) +			netdev_info(dev, "S/N: %s, E/C: %s\n", +				    adap->params.vpd.sn, adap->params.vpd.ec); +	} +} + +#define VLAN_FEAT (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_TSO | NETIF_F_TSO6 |\ +		   NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA) + +static int __devinit init_one(struct pci_dev *pdev, +			      const struct pci_device_id *ent) +{ +	int func, i, err; +	struct port_info *pi; +	unsigned int highdma = 0; +	struct adapter *adapter = NULL; + +	printk_once(KERN_INFO "%s - version %s\n", DRV_DESC, DRV_VERSION); + +	err = pci_request_regions(pdev, KBUILD_MODNAME); +	if (err) { +		/* Just info, some other driver may have claimed the device. */ +		dev_info(&pdev->dev, "cannot obtain PCI resources\n"); +		return err; +	} + +	/* We control everything through PF 0 */ +	func = PCI_FUNC(pdev->devfn); +	if (func > 0) +		goto sriov; + +	err = pci_enable_device(pdev); +	if (err) { +		dev_err(&pdev->dev, "cannot enable PCI device\n"); +		goto out_release_regions; +	} + +	if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { +		highdma = NETIF_F_HIGHDMA; +		err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); +		if (err) { +			dev_err(&pdev->dev, "unable to obtain 64-bit DMA for " +				"coherent allocations\n"); +			goto out_disable_device; +		} +	} else { +		err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); +		if (err) { +			dev_err(&pdev->dev, "no usable DMA configuration\n"); +			goto out_disable_device; +		} +	} + +	pci_enable_pcie_error_reporting(pdev); +	pci_set_master(pdev); +	pci_save_state(pdev); + +	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); +	if (!adapter) { +		err = -ENOMEM; +		goto out_disable_device; +	} + +	adapter->regs = pci_ioremap_bar(pdev, 0); +	if (!adapter->regs) { +		dev_err(&pdev->dev, "cannot map device registers\n"); +		err = -ENOMEM; +		goto out_free_adapter; +	} + +	adapter->pdev = pdev; +	adapter->pdev_dev = &pdev->dev; +	adapter->name = pci_name(pdev); +	adapter->msg_enable = dflt_msg_enable; +	memset(adapter->chan_map, 0xff, sizeof(adapter->chan_map)); + +	spin_lock_init(&adapter->stats_lock); +	spin_lock_init(&adapter->tid_release_lock); + +	INIT_WORK(&adapter->tid_release_task, process_tid_release_list); + +	err = t4_prep_adapter(adapter); +	if (err) +		goto out_unmap_bar; +	err = adap_init0(adapter); +	if (err) +		goto out_unmap_bar; + +	for_each_port(adapter, i) { +		struct net_device *netdev; + +		netdev = alloc_etherdev_mq(sizeof(struct port_info), +					   MAX_ETH_QSETS); +		if (!netdev) { +			err = -ENOMEM; +			goto out_free_dev; +		} + +		SET_NETDEV_DEV(netdev, &pdev->dev); + +		adapter->port[i] = netdev; +		pi = netdev_priv(netdev); +		pi->adapter = adapter; +		pi->xact_addr_filt = -1; +		pi->rx_offload = RX_CSO; +		pi->port_id = i; +		netif_carrier_off(netdev); +		netif_tx_stop_all_queues(netdev); +		netdev->irq = pdev->irq; + +		netdev->features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6; +		netdev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; +		netdev->features |= NETIF_F_GRO | highdma; +		netdev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX; +		netdev->vlan_features = netdev->features & VLAN_FEAT; + +		netdev->netdev_ops = &cxgb4_netdev_ops; +		SET_ETHTOOL_OPS(netdev, &cxgb_ethtool_ops); +	} + +	pci_set_drvdata(pdev, adapter); + +	if (adapter->flags & FW_OK) { +		err = t4_port_init(adapter, 0, 0, 0); +		if (err) +			goto out_free_dev; +	} + +	/* +	 * Configure queues and allocate tables now, they can be needed as +	 * soon as the first register_netdev completes. +	 */ +	cfg_queues(adapter); + +	adapter->l2t = t4_init_l2t(); +	if (!adapter->l2t) { +		/* We tolerate a lack of L2T, giving up some functionality */ +		dev_warn(&pdev->dev, "could not allocate L2T, continuing\n"); +		adapter->params.offload = 0; +	} + +	if (is_offload(adapter) && tid_init(&adapter->tids) < 0) { +		dev_warn(&pdev->dev, "could not allocate TID table, " +			 "continuing\n"); +		adapter->params.offload = 0; +	} + +	/* +	 * The card is now ready to go.  If any errors occur during device +	 * registration we do not fail the whole card but rather proceed only +	 * with the ports we manage to register successfully.  However we must +	 * register at least one net device. +	 */ +	for_each_port(adapter, i) { +		err = register_netdev(adapter->port[i]); +		if (err) +			dev_warn(&pdev->dev, +				 "cannot register net device %s, skipping\n", +				 adapter->port[i]->name); +		else { +			/* +			 * Change the name we use for messages to the name of +			 * the first successfully registered interface. +			 */ +			if (!adapter->registered_device_map) +				adapter->name = adapter->port[i]->name; + +			__set_bit(i, &adapter->registered_device_map); +			adapter->chan_map[adap2pinfo(adapter, i)->tx_chan] = i; +		} +	} +	if (!adapter->registered_device_map) { +		dev_err(&pdev->dev, "could not register any net devices\n"); +		goto out_free_dev; +	} + +	if (cxgb4_debugfs_root) { +		adapter->debugfs_root = debugfs_create_dir(pci_name(pdev), +							   cxgb4_debugfs_root); +		setup_debugfs(adapter); +	} + +	/* See what interrupts we'll be using */ +	if (msi > 1 && enable_msix(adapter) == 0) +		adapter->flags |= USING_MSIX; +	else if (msi > 0 && pci_enable_msi(pdev) == 0) +		adapter->flags |= USING_MSI; + +	if (is_offload(adapter)) +		attach_ulds(adapter); + +	print_port_info(adapter); + +sriov: +#ifdef CONFIG_PCI_IOV +	if (func < ARRAY_SIZE(num_vf) && num_vf[func] > 0) +		if (pci_enable_sriov(pdev, num_vf[func]) == 0) +			dev_info(&pdev->dev, +				 "instantiated %u virtual functions\n", +				 num_vf[func]); +#endif +	return 0; + + out_free_dev: +	t4_free_mem(adapter->tids.tid_tab); +	t4_free_mem(adapter->l2t); +	for_each_port(adapter, i) +		if (adapter->port[i]) +			free_netdev(adapter->port[i]); +	if (adapter->flags & FW_OK) +		t4_fw_bye(adapter, 0); + out_unmap_bar: +	iounmap(adapter->regs); + out_free_adapter: +	kfree(adapter); + out_disable_device: +	pci_disable_pcie_error_reporting(pdev); +	pci_disable_device(pdev); + out_release_regions: +	pci_release_regions(pdev); +	pci_set_drvdata(pdev, NULL); +	return err; +} + +static void __devexit remove_one(struct pci_dev *pdev) +{ +	struct adapter *adapter = pci_get_drvdata(pdev); + +	pci_disable_sriov(pdev); + +	if (adapter) { +		int i; + +		if (is_offload(adapter)) +			detach_ulds(adapter); + +		for_each_port(adapter, i) +			if (test_bit(i, &adapter->registered_device_map)) +				unregister_netdev(adapter->port[i]); + +		if (adapter->debugfs_root) +			debugfs_remove_recursive(adapter->debugfs_root); + +		t4_sge_stop(adapter); +		t4_free_sge_resources(adapter); +		t4_free_mem(adapter->l2t); +		t4_free_mem(adapter->tids.tid_tab); +		disable_msi(adapter); + +		for_each_port(adapter, i) +			if (adapter->port[i]) +				free_netdev(adapter->port[i]); + +		if (adapter->flags & FW_OK) +			t4_fw_bye(adapter, 0); +		iounmap(adapter->regs); +		kfree(adapter); +		pci_disable_pcie_error_reporting(pdev); +		pci_disable_device(pdev); +		pci_release_regions(pdev); +		pci_set_drvdata(pdev, NULL); +	} else if (PCI_FUNC(pdev->devfn) > 0) +		pci_release_regions(pdev); +} + +static struct pci_driver cxgb4_driver = { +	.name     = KBUILD_MODNAME, +	.id_table = cxgb4_pci_tbl, +	.probe    = init_one, +	.remove   = __devexit_p(remove_one), +}; + +static int __init cxgb4_init_module(void) +{ +	int ret; + +	/* Debugfs support is optional, just warn if this fails */ +	cxgb4_debugfs_root = debugfs_create_dir(KBUILD_MODNAME, NULL); +	if (!cxgb4_debugfs_root) +		pr_warning("could not create debugfs entry, continuing\n"); + +	ret = pci_register_driver(&cxgb4_driver); +	if (ret < 0) +		debugfs_remove(cxgb4_debugfs_root); +	return ret; +} + +static void __exit cxgb4_cleanup_module(void) +{ +	pci_unregister_driver(&cxgb4_driver); +	debugfs_remove(cxgb4_debugfs_root);  /* NULL ok */ +} + +module_init(cxgb4_init_module); +module_exit(cxgb4_cleanup_module); diff --git a/drivers/net/cxgb4/cxgb4_uld.h b/drivers/net/cxgb4/cxgb4_uld.h new file mode 100644 index 00000000000..5b98546ac92 --- /dev/null +++ b/drivers/net/cxgb4/cxgb4_uld.h @@ -0,0 +1,239 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __CXGB4_OFLD_H +#define __CXGB4_OFLD_H + +#include <linux/cache.h> +#include <linux/spinlock.h> +#include <linux/skbuff.h> +#include <asm/atomic.h> + +/* CPL message priority levels */ +enum { +	CPL_PRIORITY_DATA     = 0,  /* data messages */ +	CPL_PRIORITY_SETUP    = 1,  /* connection setup messages */ +	CPL_PRIORITY_TEARDOWN = 0,  /* connection teardown messages */ +	CPL_PRIORITY_LISTEN   = 1,  /* listen start/stop messages */ +	CPL_PRIORITY_ACK      = 1,  /* RX ACK messages */ +	CPL_PRIORITY_CONTROL  = 1   /* control messages */ +}; + +#define INIT_TP_WR(w, tid) do { \ +	(w)->wr.wr_hi = htonl(FW_WR_OP(FW_TP_WR) | \ +			      FW_WR_IMMDLEN(sizeof(*w) - sizeof(w->wr))); \ +	(w)->wr.wr_mid = htonl(FW_WR_LEN16(DIV_ROUND_UP(sizeof(*w), 16)) | \ +			       FW_WR_FLOWID(tid)); \ +	(w)->wr.wr_lo = cpu_to_be64(0); \ +} while (0) + +#define INIT_TP_WR_CPL(w, cpl, tid) do { \ +	INIT_TP_WR(w, tid); \ +	OPCODE_TID(w) = htonl(MK_OPCODE_TID(cpl, tid)); \ +} while (0) + +#define INIT_ULPTX_WR(w, wrlen, atomic, tid) do { \ +	(w)->wr.wr_hi = htonl(FW_WR_OP(FW_ULPTX_WR) | FW_WR_ATOMIC(atomic)); \ +	(w)->wr.wr_mid = htonl(FW_WR_LEN16(DIV_ROUND_UP(wrlen, 16)) | \ +			       FW_WR_FLOWID(tid)); \ +	(w)->wr.wr_lo = cpu_to_be64(0); \ +} while (0) + +/* Special asynchronous notification message */ +#define CXGB4_MSG_AN ((void *)1) + +struct serv_entry { +	void *data; +}; + +union aopen_entry { +	void *data; +	union aopen_entry *next; +}; + +/* + * Holds the size, base address, free list start, etc of the TID, server TID, + * and active-open TID tables.  The tables themselves are allocated dynamically. + */ +struct tid_info { +	void **tid_tab; +	unsigned int ntids; + +	struct serv_entry *stid_tab; +	unsigned long *stid_bmap; +	unsigned int nstids; +	unsigned int stid_base; + +	union aopen_entry *atid_tab; +	unsigned int natids; + +	unsigned int nftids; +	unsigned int ftid_base; + +	spinlock_t atid_lock ____cacheline_aligned_in_smp; +	union aopen_entry *afree; +	unsigned int atids_in_use; + +	spinlock_t stid_lock; +	unsigned int stids_in_use; + +	atomic_t tids_in_use; +}; + +static inline void *lookup_tid(const struct tid_info *t, unsigned int tid) +{ +	return tid < t->ntids ? t->tid_tab[tid] : NULL; +} + +static inline void *lookup_atid(const struct tid_info *t, unsigned int atid) +{ +	return atid < t->natids ? t->atid_tab[atid].data : NULL; +} + +static inline void *lookup_stid(const struct tid_info *t, unsigned int stid) +{ +	stid -= t->stid_base; +	return stid < t->nstids ? t->stid_tab[stid].data : NULL; +} + +static inline void cxgb4_insert_tid(struct tid_info *t, void *data, +				    unsigned int tid) +{ +	t->tid_tab[tid] = data; +	atomic_inc(&t->tids_in_use); +} + +int cxgb4_alloc_atid(struct tid_info *t, void *data); +int cxgb4_alloc_stid(struct tid_info *t, int family, void *data); +void cxgb4_free_atid(struct tid_info *t, unsigned int atid); +void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family); +void cxgb4_remove_tid(struct tid_info *t, unsigned int qid, unsigned int tid); +void cxgb4_queue_tid_release(struct tid_info *t, unsigned int chan, +			     unsigned int tid); + +struct in6_addr; + +int cxgb4_create_server(const struct net_device *dev, unsigned int stid, +			__be32 sip, __be16 sport, unsigned int queue); +int cxgb4_create_server6(const struct net_device *dev, unsigned int stid, +			 const struct in6_addr *sip, __be16 sport, +			 unsigned int queue); + +static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue) +{ +	skb_set_queue_mapping(skb, (queue << 1) | prio); +} + +enum cxgb4_uld { +	CXGB4_ULD_RDMA, +	CXGB4_ULD_ISCSI, +	CXGB4_ULD_MAX +}; + +enum cxgb4_state { +	CXGB4_STATE_UP, +	CXGB4_STATE_START_RECOVERY, +	CXGB4_STATE_DOWN, +	CXGB4_STATE_DETACH +}; + +struct pci_dev; +struct l2t_data; +struct net_device; +struct pkt_gl; +struct tp_tcp_stats; + +struct cxgb4_range { +	unsigned int start; +	unsigned int size; +}; + +struct cxgb4_virt_res {                      /* virtualized HW resources */ +	struct cxgb4_range ddp; +	struct cxgb4_range iscsi; +	struct cxgb4_range stag; +	struct cxgb4_range rq; +	struct cxgb4_range pbl; +}; + +/* + * Block of information the LLD provides to ULDs attaching to a device. + */ +struct cxgb4_lld_info { +	struct pci_dev *pdev;                /* associated PCI device */ +	struct l2t_data *l2t;                /* L2 table */ +	struct tid_info *tids;               /* TID table */ +	struct net_device **ports;           /* device ports */ +	const struct cxgb4_virt_res *vr;     /* assorted HW resources */ +	const unsigned short *mtus;          /* MTU table */ +	const unsigned short *rxq_ids;       /* the ULD's Rx queue ids */ +	unsigned short nrxq;                 /* # of Rx queues */ +	unsigned short ntxq;                 /* # of Tx queues */ +	unsigned char nchan:4;               /* # of channels */ +	unsigned char nports:4;              /* # of ports */ +	unsigned char wr_cred;               /* WR 16-byte credits */ +	unsigned char adapter_type;          /* type of adapter */ +	unsigned char fw_api_ver;            /* FW API version */ +	unsigned int fw_vers;                /* FW version */ +	unsigned int iscsi_iolen;            /* iSCSI max I/O length */ +	unsigned short udb_density;          /* # of user DB/page */ +	unsigned short ucq_density;          /* # of user CQs/page */ +	void __iomem *gts_reg;               /* address of GTS register */ +	void __iomem *db_reg;                /* address of kernel doorbell */ +}; + +struct cxgb4_uld_info { +	const char *name; +	void *(*add)(const struct cxgb4_lld_info *p); +	int (*rx_handler)(void *handle, const __be64 *rsp, +			  const struct pkt_gl *gl); +	int (*state_change)(void *handle, enum cxgb4_state new_state); +}; + +int cxgb4_register_uld(enum cxgb4_uld type, const struct cxgb4_uld_info *p); +int cxgb4_unregister_uld(enum cxgb4_uld type); +int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb); +unsigned int cxgb4_port_chan(const struct net_device *dev); +unsigned int cxgb4_port_viid(const struct net_device *dev); +unsigned int cxgb4_port_idx(const struct net_device *dev); +struct net_device *cxgb4_netdev_by_hwid(struct pci_dev *pdev, unsigned int id); +unsigned int cxgb4_best_mtu(const unsigned short *mtus, unsigned short mtu, +			    unsigned int *idx); +void cxgb4_get_tcp_stats(struct pci_dev *pdev, struct tp_tcp_stats *v4, +			 struct tp_tcp_stats *v6); +void cxgb4_iscsi_init(struct net_device *dev, unsigned int tag_mask, +		      const unsigned int *pgsz_order); +struct sk_buff *cxgb4_pktgl_to_skb(const struct pkt_gl *gl, +				   unsigned int skb_len, unsigned int pull_len); +#endif  /* !__CXGB4_OFLD_H */ diff --git a/drivers/net/cxgb4/l2t.c b/drivers/net/cxgb4/l2t.c new file mode 100644 index 00000000000..9f96724a133 --- /dev/null +++ b/drivers/net/cxgb4/l2t.c @@ -0,0 +1,624 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/if.h> +#include <linux/if_vlan.h> +#include <linux/jhash.h> +#include <net/neighbour.h> +#include "cxgb4.h" +#include "l2t.h" +#include "t4_msg.h" +#include "t4fw_api.h" + +#define VLAN_NONE 0xfff + +/* identifies sync vs async L2T_WRITE_REQs */ +#define F_SYNC_WR    (1 << 12) + +enum { +	L2T_STATE_VALID,      /* entry is up to date */ +	L2T_STATE_STALE,      /* entry may be used but needs revalidation */ +	L2T_STATE_RESOLVING,  /* entry needs address resolution */ +	L2T_STATE_SYNC_WRITE, /* synchronous write of entry underway */ + +	/* when state is one of the below the entry is not hashed */ +	L2T_STATE_SWITCHING,  /* entry is being used by a switching filter */ +	L2T_STATE_UNUSED      /* entry not in use */ +}; + +struct l2t_data { +	rwlock_t lock; +	atomic_t nfree;             /* number of free entries */ +	struct l2t_entry *rover;    /* starting point for next allocation */ +	struct l2t_entry l2tab[L2T_SIZE]; +}; + +static inline unsigned int vlan_prio(const struct l2t_entry *e) +{ +	return e->vlan >> 13; +} + +static inline void l2t_hold(struct l2t_data *d, struct l2t_entry *e) +{ +	if (atomic_add_return(1, &e->refcnt) == 1)  /* 0 -> 1 transition */ +		atomic_dec(&d->nfree); +} + +/* + * To avoid having to check address families we do not allow v4 and v6 + * neighbors to be on the same hash chain.  We keep v4 entries in the first + * half of available hash buckets and v6 in the second. + */ +enum { +	L2T_SZ_HALF = L2T_SIZE / 2, +	L2T_HASH_MASK = L2T_SZ_HALF - 1 +}; + +static inline unsigned int arp_hash(const u32 *key, int ifindex) +{ +	return jhash_2words(*key, ifindex, 0) & L2T_HASH_MASK; +} + +static inline unsigned int ipv6_hash(const u32 *key, int ifindex) +{ +	u32 xor = key[0] ^ key[1] ^ key[2] ^ key[3]; + +	return L2T_SZ_HALF + (jhash_2words(xor, ifindex, 0) & L2T_HASH_MASK); +} + +static unsigned int addr_hash(const u32 *addr, int addr_len, int ifindex) +{ +	return addr_len == 4 ? arp_hash(addr, ifindex) : +			       ipv6_hash(addr, ifindex); +} + +/* + * Checks if an L2T entry is for the given IP/IPv6 address.  It does not check + * whether the L2T entry and the address are of the same address family. + * Callers ensure an address is only checked against L2T entries of the same + * family, something made trivial by the separation of IP and IPv6 hash chains + * mentioned above.  Returns 0 if there's a match, + */ +static int addreq(const struct l2t_entry *e, const u32 *addr) +{ +	if (e->v6) +		return (e->addr[0] ^ addr[0]) | (e->addr[1] ^ addr[1]) | +		       (e->addr[2] ^ addr[2]) | (e->addr[3] ^ addr[3]); +	return e->addr[0] ^ addr[0]; +} + +static void neigh_replace(struct l2t_entry *e, struct neighbour *n) +{ +	neigh_hold(n); +	if (e->neigh) +		neigh_release(e->neigh); +	e->neigh = n; +} + +/* + * Write an L2T entry.  Must be called with the entry locked. + * The write may be synchronous or asynchronous. + */ +static int write_l2e(struct adapter *adap, struct l2t_entry *e, int sync) +{ +	struct sk_buff *skb; +	struct cpl_l2t_write_req *req; + +	skb = alloc_skb(sizeof(*req), GFP_ATOMIC); +	if (!skb) +		return -ENOMEM; + +	req = (struct cpl_l2t_write_req *)__skb_put(skb, sizeof(*req)); +	INIT_TP_WR(req, 0); + +	OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, +					e->idx | (sync ? F_SYNC_WR : 0) | +					TID_QID(adap->sge.fw_evtq.abs_id))); +	req->params = htons(L2T_W_PORT(e->lport) | L2T_W_NOREPLY(!sync)); +	req->l2t_idx = htons(e->idx); +	req->vlan = htons(e->vlan); +	if (e->neigh) +		memcpy(e->dmac, e->neigh->ha, sizeof(e->dmac)); +	memcpy(req->dst_mac, e->dmac, sizeof(req->dst_mac)); + +	set_wr_txq(skb, CPL_PRIORITY_CONTROL, 0); +	t4_ofld_send(adap, skb); + +	if (sync && e->state != L2T_STATE_SWITCHING) +		e->state = L2T_STATE_SYNC_WRITE; +	return 0; +} + +/* + * Send packets waiting in an L2T entry's ARP queue.  Must be called with the + * entry locked. + */ +static void send_pending(struct adapter *adap, struct l2t_entry *e) +{ +	while (e->arpq_head) { +		struct sk_buff *skb = e->arpq_head; + +		e->arpq_head = skb->next; +		skb->next = NULL; +		t4_ofld_send(adap, skb); +	} +	e->arpq_tail = NULL; +} + +/* + * Process a CPL_L2T_WRITE_RPL.  Wake up the ARP queue if it completes a + * synchronous L2T_WRITE.  Note that the TID in the reply is really the L2T + * index it refers to. + */ +void do_l2t_write_rpl(struct adapter *adap, const struct cpl_l2t_write_rpl *rpl) +{ +	unsigned int tid = GET_TID(rpl); +	unsigned int idx = tid & (L2T_SIZE - 1); + +	if (unlikely(rpl->status != CPL_ERR_NONE)) { +		dev_err(adap->pdev_dev, +			"Unexpected L2T_WRITE_RPL status %u for entry %u\n", +			rpl->status, idx); +		return; +	} + +	if (tid & F_SYNC_WR) { +		struct l2t_entry *e = &adap->l2t->l2tab[idx]; + +		spin_lock(&e->lock); +		if (e->state != L2T_STATE_SWITCHING) { +			send_pending(adap, e); +			e->state = (e->neigh->nud_state & NUD_STALE) ? +					L2T_STATE_STALE : L2T_STATE_VALID; +		} +		spin_unlock(&e->lock); +	} +} + +/* + * Add a packet to an L2T entry's queue of packets awaiting resolution. + * Must be called with the entry's lock held. + */ +static inline void arpq_enqueue(struct l2t_entry *e, struct sk_buff *skb) +{ +	skb->next = NULL; +	if (e->arpq_head) +		e->arpq_tail->next = skb; +	else +		e->arpq_head = skb; +	e->arpq_tail = skb; +} + +int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb, +		   struct l2t_entry *e) +{ +	struct adapter *adap = netdev2adap(dev); + +again: +	switch (e->state) { +	case L2T_STATE_STALE:     /* entry is stale, kick off revalidation */ +		neigh_event_send(e->neigh, NULL); +		spin_lock_bh(&e->lock); +		if (e->state == L2T_STATE_STALE) +			e->state = L2T_STATE_VALID; +		spin_unlock_bh(&e->lock); +	case L2T_STATE_VALID:     /* fast-path, send the packet on */ +		return t4_ofld_send(adap, skb); +	case L2T_STATE_RESOLVING: +	case L2T_STATE_SYNC_WRITE: +		spin_lock_bh(&e->lock); +		if (e->state != L2T_STATE_SYNC_WRITE && +		    e->state != L2T_STATE_RESOLVING) { +			spin_unlock_bh(&e->lock); +			goto again; +		} +		arpq_enqueue(e, skb); +		spin_unlock_bh(&e->lock); + +		if (e->state == L2T_STATE_RESOLVING && +		    !neigh_event_send(e->neigh, NULL)) { +			spin_lock_bh(&e->lock); +			if (e->state == L2T_STATE_RESOLVING && e->arpq_head) +				write_l2e(adap, e, 1); +			spin_unlock_bh(&e->lock); +		} +	} +	return 0; +} +EXPORT_SYMBOL(cxgb4_l2t_send); + +/* + * Allocate a free L2T entry.  Must be called with l2t_data.lock held. + */ +static struct l2t_entry *alloc_l2e(struct l2t_data *d) +{ +	struct l2t_entry *end, *e, **p; + +	if (!atomic_read(&d->nfree)) +		return NULL; + +	/* there's definitely a free entry */ +	for (e = d->rover, end = &d->l2tab[L2T_SIZE]; e != end; ++e) +		if (atomic_read(&e->refcnt) == 0) +			goto found; + +	for (e = d->l2tab; atomic_read(&e->refcnt); ++e) +		; +found: +	d->rover = e + 1; +	atomic_dec(&d->nfree); + +	/* +	 * The entry we found may be an inactive entry that is +	 * presently in the hash table.  We need to remove it. +	 */ +	if (e->state < L2T_STATE_SWITCHING) +		for (p = &d->l2tab[e->hash].first; *p; p = &(*p)->next) +			if (*p == e) { +				*p = e->next; +				e->next = NULL; +				break; +			} + +	e->state = L2T_STATE_UNUSED; +	return e; +} + +/* + * Called when an L2T entry has no more users. + */ +static void t4_l2e_free(struct l2t_entry *e) +{ +	struct l2t_data *d; + +	spin_lock_bh(&e->lock); +	if (atomic_read(&e->refcnt) == 0) {  /* hasn't been recycled */ +		if (e->neigh) { +			neigh_release(e->neigh); +			e->neigh = NULL; +		} +	} +	spin_unlock_bh(&e->lock); + +	d = container_of(e, struct l2t_data, l2tab[e->idx]); +	atomic_inc(&d->nfree); +} + +void cxgb4_l2t_release(struct l2t_entry *e) +{ +	if (atomic_dec_and_test(&e->refcnt)) +		t4_l2e_free(e); +} +EXPORT_SYMBOL(cxgb4_l2t_release); + +/* + * Update an L2T entry that was previously used for the same next hop as neigh. + * Must be called with softirqs disabled. + */ +static void reuse_entry(struct l2t_entry *e, struct neighbour *neigh) +{ +	unsigned int nud_state; + +	spin_lock(&e->lock);                /* avoid race with t4_l2t_free */ +	if (neigh != e->neigh) +		neigh_replace(e, neigh); +	nud_state = neigh->nud_state; +	if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac)) || +	    !(nud_state & NUD_VALID)) +		e->state = L2T_STATE_RESOLVING; +	else if (nud_state & NUD_CONNECTED) +		e->state = L2T_STATE_VALID; +	else +		e->state = L2T_STATE_STALE; +	spin_unlock(&e->lock); +} + +struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, +				const struct net_device *physdev, +				unsigned int priority) +{ +	u8 lport; +	u16 vlan; +	struct l2t_entry *e; +	int addr_len = neigh->tbl->key_len; +	u32 *addr = (u32 *)neigh->primary_key; +	int ifidx = neigh->dev->ifindex; +	int hash = addr_hash(addr, addr_len, ifidx); + +	if (neigh->dev->flags & IFF_LOOPBACK) +		lport = netdev2pinfo(physdev)->tx_chan + 4; +	else +		lport = netdev2pinfo(physdev)->lport; + +	if (neigh->dev->priv_flags & IFF_802_1Q_VLAN) +		vlan = vlan_dev_vlan_id(neigh->dev); +	else +		vlan = VLAN_NONE; + +	write_lock_bh(&d->lock); +	for (e = d->l2tab[hash].first; e; e = e->next) +		if (!addreq(e, addr) && e->ifindex == ifidx && +		    e->vlan == vlan && e->lport == lport) { +			l2t_hold(d, e); +			if (atomic_read(&e->refcnt) == 1) +				reuse_entry(e, neigh); +			goto done; +		} + +	/* Need to allocate a new entry */ +	e = alloc_l2e(d); +	if (e) { +		spin_lock(&e->lock);          /* avoid race with t4_l2t_free */ +		e->state = L2T_STATE_RESOLVING; +		memcpy(e->addr, addr, addr_len); +		e->ifindex = ifidx; +		e->hash = hash; +		e->lport = lport; +		e->v6 = addr_len == 16; +		atomic_set(&e->refcnt, 1); +		neigh_replace(e, neigh); +		e->vlan = vlan; +		e->next = d->l2tab[hash].first; +		d->l2tab[hash].first = e; +		spin_unlock(&e->lock); +	} +done: +	write_unlock_bh(&d->lock); +	return e; +} +EXPORT_SYMBOL(cxgb4_l2t_get); + +/* + * Called when address resolution fails for an L2T entry to handle packets + * on the arpq head.  If a packet specifies a failure handler it is invoked, + * otherwise the packet is sent to the device. + */ +static void handle_failed_resolution(struct adapter *adap, struct sk_buff *arpq) +{ +	while (arpq) { +		struct sk_buff *skb = arpq; +		const struct l2t_skb_cb *cb = L2T_SKB_CB(skb); + +		arpq = skb->next; +		skb->next = NULL; +		if (cb->arp_err_handler) +			cb->arp_err_handler(cb->handle, skb); +		else +			t4_ofld_send(adap, skb); +	} +} + +/* + * Called when the host's neighbor layer makes a change to some entry that is + * loaded into the HW L2 table. + */ +void t4_l2t_update(struct adapter *adap, struct neighbour *neigh) +{ +	struct l2t_entry *e; +	struct sk_buff *arpq = NULL; +	struct l2t_data *d = adap->l2t; +	int addr_len = neigh->tbl->key_len; +	u32 *addr = (u32 *) neigh->primary_key; +	int ifidx = neigh->dev->ifindex; +	int hash = addr_hash(addr, addr_len, ifidx); + +	read_lock_bh(&d->lock); +	for (e = d->l2tab[hash].first; e; e = e->next) +		if (!addreq(e, addr) && e->ifindex == ifidx) { +			spin_lock(&e->lock); +			if (atomic_read(&e->refcnt)) +				goto found; +			spin_unlock(&e->lock); +			break; +		} +	read_unlock_bh(&d->lock); +	return; + + found: +	read_unlock(&d->lock); + +	if (neigh != e->neigh) +		neigh_replace(e, neigh); + +	if (e->state == L2T_STATE_RESOLVING) { +		if (neigh->nud_state & NUD_FAILED) { +			arpq = e->arpq_head; +			e->arpq_head = e->arpq_tail = NULL; +		} else if ((neigh->nud_state & (NUD_CONNECTED | NUD_STALE)) && +			   e->arpq_head) { +			write_l2e(adap, e, 1); +		} +	} else { +		e->state = neigh->nud_state & NUD_CONNECTED ? +			L2T_STATE_VALID : L2T_STATE_STALE; +		if (memcmp(e->dmac, neigh->ha, sizeof(e->dmac))) +			write_l2e(adap, e, 0); +	} + +	spin_unlock_bh(&e->lock); + +	if (arpq) +		handle_failed_resolution(adap, arpq); +} + +/* + * Allocate an L2T entry for use by a switching rule.  Such entries need to be + * explicitly freed and while busy they are not on any hash chain, so normal + * address resolution updates do not see them. + */ +struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d) +{ +	struct l2t_entry *e; + +	write_lock_bh(&d->lock); +	e = alloc_l2e(d); +	if (e) { +		spin_lock(&e->lock);          /* avoid race with t4_l2t_free */ +		e->state = L2T_STATE_SWITCHING; +		atomic_set(&e->refcnt, 1); +		spin_unlock(&e->lock); +	} +	write_unlock_bh(&d->lock); +	return e; +} + +/* + * Sets/updates the contents of a switching L2T entry that has been allocated + * with an earlier call to @t4_l2t_alloc_switching. + */ +int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan, +			 u8 port, u8 *eth_addr) +{ +	e->vlan = vlan; +	e->lport = port; +	memcpy(e->dmac, eth_addr, ETH_ALEN); +	return write_l2e(adap, e, 0); +} + +struct l2t_data *t4_init_l2t(void) +{ +	int i; +	struct l2t_data *d; + +	d = t4_alloc_mem(sizeof(*d)); +	if (!d) +		return NULL; + +	d->rover = d->l2tab; +	atomic_set(&d->nfree, L2T_SIZE); +	rwlock_init(&d->lock); + +	for (i = 0; i < L2T_SIZE; ++i) { +		d->l2tab[i].idx = i; +		d->l2tab[i].state = L2T_STATE_UNUSED; +		spin_lock_init(&d->l2tab[i].lock); +		atomic_set(&d->l2tab[i].refcnt, 0); +	} +	return d; +} + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +static inline void *l2t_get_idx(struct seq_file *seq, loff_t pos) +{ +	struct l2t_entry *l2tab = seq->private; + +	return pos >= L2T_SIZE ? NULL : &l2tab[pos]; +} + +static void *l2t_seq_start(struct seq_file *seq, loff_t *pos) +{ +	return *pos ? l2t_get_idx(seq, *pos - 1) : SEQ_START_TOKEN; +} + +static void *l2t_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ +	v = l2t_get_idx(seq, *pos); +	if (v) +		++*pos; +	return v; +} + +static void l2t_seq_stop(struct seq_file *seq, void *v) +{ +} + +static char l2e_state(const struct l2t_entry *e) +{ +	switch (e->state) { +	case L2T_STATE_VALID: return 'V'; +	case L2T_STATE_STALE: return 'S'; +	case L2T_STATE_SYNC_WRITE: return 'W'; +	case L2T_STATE_RESOLVING: return e->arpq_head ? 'A' : 'R'; +	case L2T_STATE_SWITCHING: return 'X'; +	default: +		return 'U'; +	} +} + +static int l2t_seq_show(struct seq_file *seq, void *v) +{ +	if (v == SEQ_START_TOKEN) +		seq_puts(seq, " Idx IP address                " +			 "Ethernet address  VLAN/P LP State Users Port\n"); +	else { +		char ip[60]; +		struct l2t_entry *e = v; + +		spin_lock_bh(&e->lock); +		if (e->state == L2T_STATE_SWITCHING) +			ip[0] = '\0'; +		else +			sprintf(ip, e->v6 ? "%pI6c" : "%pI4", e->addr); +		seq_printf(seq, "%4u %-25s %17pM %4d %u %2u   %c   %5u %s\n", +			   e->idx, ip, e->dmac, +			   e->vlan & VLAN_VID_MASK, vlan_prio(e), e->lport, +			   l2e_state(e), atomic_read(&e->refcnt), +			   e->neigh ? e->neigh->dev->name : ""); +		spin_unlock_bh(&e->lock); +	} +	return 0; +} + +static const struct seq_operations l2t_seq_ops = { +	.start = l2t_seq_start, +	.next = l2t_seq_next, +	.stop = l2t_seq_stop, +	.show = l2t_seq_show +}; + +static int l2t_seq_open(struct inode *inode, struct file *file) +{ +	int rc = seq_open(file, &l2t_seq_ops); + +	if (!rc) { +		struct adapter *adap = inode->i_private; +		struct seq_file *seq = file->private_data; + +		seq->private = adap->l2t->l2tab; +	} +	return rc; +} + +const struct file_operations t4_l2t_fops = { +	.owner = THIS_MODULE, +	.open = l2t_seq_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = seq_release, +}; diff --git a/drivers/net/cxgb4/l2t.h b/drivers/net/cxgb4/l2t.h new file mode 100644 index 00000000000..643f27ed3cf --- /dev/null +++ b/drivers/net/cxgb4/l2t.h @@ -0,0 +1,110 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __CXGB4_L2T_H +#define __CXGB4_L2T_H + +#include <linux/spinlock.h> +#include <linux/if_ether.h> +#include <asm/atomic.h> + +struct adapter; +struct l2t_data; +struct neighbour; +struct net_device; +struct file_operations; +struct cpl_l2t_write_rpl; + +/* + * Each L2T entry plays multiple roles.  First of all, it keeps state for the + * corresponding entry of the HW L2 table and maintains a queue of offload + * packets awaiting address resolution.  Second, it is a node of a hash table + * chain, where the nodes of the chain are linked together through their next + * pointer.  Finally, each node is a bucket of a hash table, pointing to the + * first element in its chain through its first pointer. + */ +struct l2t_entry { +	u16 state;                  /* entry state */ +	u16 idx;                    /* entry index */ +	u32 addr[4];                /* next hop IP or IPv6 address */ +	int ifindex;                /* neighbor's net_device's ifindex */ +	struct neighbour *neigh;    /* associated neighbour */ +	struct l2t_entry *first;    /* start of hash chain */ +	struct l2t_entry *next;     /* next l2t_entry on chain */ +	struct sk_buff *arpq_head;  /* queue of packets awaiting resolution */ +	struct sk_buff *arpq_tail; +	spinlock_t lock; +	atomic_t refcnt;            /* entry reference count */ +	u16 hash;                   /* hash bucket the entry is on */ +	u16 vlan;                   /* VLAN TCI (id: bits 0-11, prio: 13-15 */ +	u8 v6;                      /* whether entry is for IPv6 */ +	u8 lport;                   /* associated offload logical interface */ +	u8 dmac[ETH_ALEN];          /* neighbour's MAC address */ +}; + +typedef void (*arp_err_handler_t)(void *handle, struct sk_buff *skb); + +/* + * Callback stored in an skb to handle address resolution failure. + */ +struct l2t_skb_cb { +	void *handle; +	arp_err_handler_t arp_err_handler; +}; + +#define L2T_SKB_CB(skb) ((struct l2t_skb_cb *)(skb)->cb) + +static inline void t4_set_arp_err_handler(struct sk_buff *skb, void *handle, +					  arp_err_handler_t handler) +{ +	L2T_SKB_CB(skb)->handle = handle; +	L2T_SKB_CB(skb)->arp_err_handler = handler; +} + +void cxgb4_l2t_release(struct l2t_entry *e); +int cxgb4_l2t_send(struct net_device *dev, struct sk_buff *skb, +		   struct l2t_entry *e); +struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh, +				const struct net_device *physdev, +				unsigned int priority); + +void t4_l2t_update(struct adapter *adap, struct neighbour *neigh); +struct l2t_entry *t4_l2t_alloc_switching(struct l2t_data *d); +int t4_l2t_set_switching(struct adapter *adap, struct l2t_entry *e, u16 vlan, +			 u8 port, u8 *eth_addr); +struct l2t_data *t4_init_l2t(void); +void do_l2t_write_rpl(struct adapter *p, const struct cpl_l2t_write_rpl *rpl); + +extern const struct file_operations t4_l2t_fops; +#endif  /* __CXGB4_L2T_H */ diff --git a/drivers/net/cxgb4/sge.c b/drivers/net/cxgb4/sge.c new file mode 100644 index 00000000000..14adc58e71c --- /dev/null +++ b/drivers/net/cxgb4/sge.c @@ -0,0 +1,2431 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if_vlan.h> +#include <linux/ip.h> +#include <linux/dma-mapping.h> +#include <linux/jiffies.h> +#include <net/ipv6.h> +#include <net/tcp.h> +#include "cxgb4.h" +#include "t4_regs.h" +#include "t4_msg.h" +#include "t4fw_api.h" + +/* + * Rx buffer size.  We use largish buffers if possible but settle for single + * pages under memory shortage. + */ +#if PAGE_SHIFT >= 16 +# define FL_PG_ORDER 0 +#else +# define FL_PG_ORDER (16 - PAGE_SHIFT) +#endif + +/* RX_PULL_LEN should be <= RX_COPY_THRES */ +#define RX_COPY_THRES    256 +#define RX_PULL_LEN      128 + +/* + * Main body length for sk_buffs used for Rx Ethernet packets with fragments. + * Should be >= RX_PULL_LEN but possibly bigger to give pskb_may_pull some room. + */ +#define RX_PKT_SKB_LEN   512 + +/* Ethernet header padding prepended to RX_PKTs */ +#define RX_PKT_PAD 2 + +/* + * Max number of Tx descriptors we clean up at a time.  Should be modest as + * freeing skbs isn't cheap and it happens while holding locks.  We just need + * to free packets faster than they arrive, we eventually catch up and keep + * the amortized cost reasonable.  Must be >= 2 * TXQ_STOP_THRES. + */ +#define MAX_TX_RECLAIM 16 + +/* + * Max number of Rx buffers we replenish at a time.  Again keep this modest, + * allocating buffers isn't cheap either. + */ +#define MAX_RX_REFILL 16U + +/* + * Period of the Rx queue check timer.  This timer is infrequent as it has + * something to do only when the system experiences severe memory shortage. + */ +#define RX_QCHECK_PERIOD (HZ / 2) + +/* + * Period of the Tx queue check timer. + */ +#define TX_QCHECK_PERIOD (HZ / 2) + +/* + * Max number of Tx descriptors to be reclaimed by the Tx timer. + */ +#define MAX_TIMER_TX_RECLAIM 100 + +/* + * Timer index used when backing off due to memory shortage. + */ +#define NOMEM_TMR_IDX (SGE_NTIMERS - 1) + +/* + * An FL with <= FL_STARVE_THRES buffers is starving and a periodic timer will + * attempt to refill it. + */ +#define FL_STARVE_THRES 4 + +/* + * Suspend an Ethernet Tx queue with fewer available descriptors than this. + * This is the same as calc_tx_descs() for a TSO packet with + * nr_frags == MAX_SKB_FRAGS. + */ +#define ETHTXQ_STOP_THRES \ +	(1 + DIV_ROUND_UP((3 * MAX_SKB_FRAGS) / 2 + (MAX_SKB_FRAGS & 1), 8)) + +/* + * Suspension threshold for non-Ethernet Tx queues.  We require enough room + * for a full sized WR. + */ +#define TXQ_STOP_THRES (SGE_MAX_WR_LEN / sizeof(struct tx_desc)) + +/* + * Max Tx descriptor space we allow for an Ethernet packet to be inlined + * into a WR. + */ +#define MAX_IMM_TX_PKT_LEN 128 + +/* + * Max size of a WR sent through a control Tx queue. + */ +#define MAX_CTRL_WR_LEN SGE_MAX_WR_LEN + +enum { +	/* packet alignment in FL buffers */ +	FL_ALIGN = L1_CACHE_BYTES < 32 ? 32 : L1_CACHE_BYTES, +	/* egress status entry size */ +	STAT_LEN = L1_CACHE_BYTES > 64 ? 128 : 64 +}; + +struct tx_sw_desc {                /* SW state per Tx descriptor */ +	struct sk_buff *skb; +	struct ulptx_sgl *sgl; +}; + +struct rx_sw_desc {                /* SW state per Rx descriptor */ +	struct page *page; +	dma_addr_t dma_addr; +}; + +/* + * The low bits of rx_sw_desc.dma_addr have special meaning. + */ +enum { +	RX_LARGE_BUF    = 1 << 0, /* buffer is larger than PAGE_SIZE */ +	RX_UNMAPPED_BUF = 1 << 1, /* buffer is not mapped */ +}; + +static inline dma_addr_t get_buf_addr(const struct rx_sw_desc *d) +{ +	return d->dma_addr & ~(dma_addr_t)(RX_LARGE_BUF | RX_UNMAPPED_BUF); +} + +static inline bool is_buf_mapped(const struct rx_sw_desc *d) +{ +	return !(d->dma_addr & RX_UNMAPPED_BUF); +} + +/** + *	txq_avail - return the number of available slots in a Tx queue + *	@q: the Tx queue + * + *	Returns the number of descriptors in a Tx queue available to write new + *	packets. + */ +static inline unsigned int txq_avail(const struct sge_txq *q) +{ +	return q->size - 1 - q->in_use; +} + +/** + *	fl_cap - return the capacity of a free-buffer list + *	@fl: the FL + * + *	Returns the capacity of a free-buffer list.  The capacity is less than + *	the size because one descriptor needs to be left unpopulated, otherwise + *	HW will think the FL is empty. + */ +static inline unsigned int fl_cap(const struct sge_fl *fl) +{ +	return fl->size - 8;   /* 1 descriptor = 8 buffers */ +} + +static inline bool fl_starving(const struct sge_fl *fl) +{ +	return fl->avail - fl->pend_cred <= FL_STARVE_THRES; +} + +static int map_skb(struct device *dev, const struct sk_buff *skb, +		   dma_addr_t *addr) +{ +	const skb_frag_t *fp, *end; +	const struct skb_shared_info *si; + +	*addr = dma_map_single(dev, skb->data, skb_headlen(skb), DMA_TO_DEVICE); +	if (dma_mapping_error(dev, *addr)) +		goto out_err; + +	si = skb_shinfo(skb); +	end = &si->frags[si->nr_frags]; + +	for (fp = si->frags; fp < end; fp++) { +		*++addr = dma_map_page(dev, fp->page, fp->page_offset, fp->size, +				       DMA_TO_DEVICE); +		if (dma_mapping_error(dev, *addr)) +			goto unwind; +	} +	return 0; + +unwind: +	while (fp-- > si->frags) +		dma_unmap_page(dev, *--addr, fp->size, DMA_TO_DEVICE); + +	dma_unmap_single(dev, addr[-1], skb_headlen(skb), DMA_TO_DEVICE); +out_err: +	return -ENOMEM; +} + +#ifdef CONFIG_NEED_DMA_MAP_STATE +static void unmap_skb(struct device *dev, const struct sk_buff *skb, +		      const dma_addr_t *addr) +{ +	const skb_frag_t *fp, *end; +	const struct skb_shared_info *si; + +	dma_unmap_single(dev, *addr++, skb_headlen(skb), DMA_TO_DEVICE); + +	si = skb_shinfo(skb); +	end = &si->frags[si->nr_frags]; +	for (fp = si->frags; fp < end; fp++) +		dma_unmap_page(dev, *addr++, fp->size, DMA_TO_DEVICE); +} + +/** + *	deferred_unmap_destructor - unmap a packet when it is freed + *	@skb: the packet + * + *	This is the packet destructor used for Tx packets that need to remain + *	mapped until they are freed rather than until their Tx descriptors are + *	freed. + */ +static void deferred_unmap_destructor(struct sk_buff *skb) +{ +	unmap_skb(skb->dev->dev.parent, skb, (dma_addr_t *)skb->head); +} +#endif + +static void unmap_sgl(struct device *dev, const struct sk_buff *skb, +		      const struct ulptx_sgl *sgl, const struct sge_txq *q) +{ +	const struct ulptx_sge_pair *p; +	unsigned int nfrags = skb_shinfo(skb)->nr_frags; + +	if (likely(skb_headlen(skb))) +		dma_unmap_single(dev, be64_to_cpu(sgl->addr0), ntohl(sgl->len0), +				 DMA_TO_DEVICE); +	else { +		dma_unmap_page(dev, be64_to_cpu(sgl->addr0), ntohl(sgl->len0), +			       DMA_TO_DEVICE); +		nfrags--; +	} + +	/* +	 * the complexity below is because of the possibility of a wrap-around +	 * in the middle of an SGL +	 */ +	for (p = sgl->sge; nfrags >= 2; nfrags -= 2) { +		if (likely((u8 *)(p + 1) <= (u8 *)q->stat)) { +unmap:			dma_unmap_page(dev, be64_to_cpu(p->addr[0]), +				       ntohl(p->len[0]), DMA_TO_DEVICE); +			dma_unmap_page(dev, be64_to_cpu(p->addr[1]), +				       ntohl(p->len[1]), DMA_TO_DEVICE); +			p++; +		} else if ((u8 *)p == (u8 *)q->stat) { +			p = (const struct ulptx_sge_pair *)q->desc; +			goto unmap; +		} else if ((u8 *)p + 8 == (u8 *)q->stat) { +			const __be64 *addr = (const __be64 *)q->desc; + +			dma_unmap_page(dev, be64_to_cpu(addr[0]), +				       ntohl(p->len[0]), DMA_TO_DEVICE); +			dma_unmap_page(dev, be64_to_cpu(addr[1]), +				       ntohl(p->len[1]), DMA_TO_DEVICE); +			p = (const struct ulptx_sge_pair *)&addr[2]; +		} else { +			const __be64 *addr = (const __be64 *)q->desc; + +			dma_unmap_page(dev, be64_to_cpu(p->addr[0]), +				       ntohl(p->len[0]), DMA_TO_DEVICE); +			dma_unmap_page(dev, be64_to_cpu(addr[0]), +				       ntohl(p->len[1]), DMA_TO_DEVICE); +			p = (const struct ulptx_sge_pair *)&addr[1]; +		} +	} +	if (nfrags) { +		__be64 addr; + +		if ((u8 *)p == (u8 *)q->stat) +			p = (const struct ulptx_sge_pair *)q->desc; +		addr = (u8 *)p + 16 <= (u8 *)q->stat ? p->addr[0] : +						       *(const __be64 *)q->desc; +		dma_unmap_page(dev, be64_to_cpu(addr), ntohl(p->len[0]), +			       DMA_TO_DEVICE); +	} +} + +/** + *	free_tx_desc - reclaims Tx descriptors and their buffers + *	@adapter: the adapter + *	@q: the Tx queue to reclaim descriptors from + *	@n: the number of descriptors to reclaim + *	@unmap: whether the buffers should be unmapped for DMA + * + *	Reclaims Tx descriptors from an SGE Tx queue and frees the associated + *	Tx buffers.  Called with the Tx queue lock held. + */ +static void free_tx_desc(struct adapter *adap, struct sge_txq *q, +			 unsigned int n, bool unmap) +{ +	struct tx_sw_desc *d; +	unsigned int cidx = q->cidx; +	struct device *dev = adap->pdev_dev; + +	d = &q->sdesc[cidx]; +	while (n--) { +		if (d->skb) {                       /* an SGL is present */ +			if (unmap) +				unmap_sgl(dev, d->skb, d->sgl, q); +			kfree_skb(d->skb); +			d->skb = NULL; +		} +		++d; +		if (++cidx == q->size) { +			cidx = 0; +			d = q->sdesc; +		} +	} +	q->cidx = cidx; +} + +/* + * Return the number of reclaimable descriptors in a Tx queue. + */ +static inline int reclaimable(const struct sge_txq *q) +{ +	int hw_cidx = ntohs(q->stat->cidx); +	hw_cidx -= q->cidx; +	return hw_cidx < 0 ? hw_cidx + q->size : hw_cidx; +} + +/** + *	reclaim_completed_tx - reclaims completed Tx descriptors + *	@adap: the adapter + *	@q: the Tx queue to reclaim completed descriptors from + *	@unmap: whether the buffers should be unmapped for DMA + * + *	Reclaims Tx descriptors that the SGE has indicated it has processed, + *	and frees the associated buffers if possible.  Called with the Tx + *	queue locked. + */ +static inline void reclaim_completed_tx(struct adapter *adap, struct sge_txq *q, +					bool unmap) +{ +	int avail = reclaimable(q); + +	if (avail) { +		/* +		 * Limit the amount of clean up work we do at a time to keep +		 * the Tx lock hold time O(1). +		 */ +		if (avail > MAX_TX_RECLAIM) +			avail = MAX_TX_RECLAIM; + +		free_tx_desc(adap, q, avail, unmap); +		q->in_use -= avail; +	} +} + +static inline int get_buf_size(const struct rx_sw_desc *d) +{ +#if FL_PG_ORDER > 0 +	return (d->dma_addr & RX_LARGE_BUF) ? (PAGE_SIZE << FL_PG_ORDER) : +					      PAGE_SIZE; +#else +	return PAGE_SIZE; +#endif +} + +/** + *	free_rx_bufs - free the Rx buffers on an SGE free list + *	@adap: the adapter + *	@q: the SGE free list to free buffers from + *	@n: how many buffers to free + * + *	Release the next @n buffers on an SGE free-buffer Rx queue.   The + *	buffers must be made inaccessible to HW before calling this function. + */ +static void free_rx_bufs(struct adapter *adap, struct sge_fl *q, int n) +{ +	while (n--) { +		struct rx_sw_desc *d = &q->sdesc[q->cidx]; + +		if (is_buf_mapped(d)) +			dma_unmap_page(adap->pdev_dev, get_buf_addr(d), +				       get_buf_size(d), PCI_DMA_FROMDEVICE); +		put_page(d->page); +		d->page = NULL; +		if (++q->cidx == q->size) +			q->cidx = 0; +		q->avail--; +	} +} + +/** + *	unmap_rx_buf - unmap the current Rx buffer on an SGE free list + *	@adap: the adapter + *	@q: the SGE free list + * + *	Unmap the current buffer on an SGE free-buffer Rx queue.   The + *	buffer must be made inaccessible to HW before calling this function. + * + *	This is similar to @free_rx_bufs above but does not free the buffer. + *	Do note that the FL still loses any further access to the buffer. + */ +static void unmap_rx_buf(struct adapter *adap, struct sge_fl *q) +{ +	struct rx_sw_desc *d = &q->sdesc[q->cidx]; + +	if (is_buf_mapped(d)) +		dma_unmap_page(adap->pdev_dev, get_buf_addr(d), +			       get_buf_size(d), PCI_DMA_FROMDEVICE); +	d->page = NULL; +	if (++q->cidx == q->size) +		q->cidx = 0; +	q->avail--; +} + +static inline void ring_fl_db(struct adapter *adap, struct sge_fl *q) +{ +	if (q->pend_cred >= 8) { +		wmb(); +		t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), DBPRIO | +			     QID(q->cntxt_id) | PIDX(q->pend_cred / 8)); +		q->pend_cred &= 7; +	} +} + +static inline void set_rx_sw_desc(struct rx_sw_desc *sd, struct page *pg, +				  dma_addr_t mapping) +{ +	sd->page = pg; +	sd->dma_addr = mapping;      /* includes size low bits */ +} + +/** + *	refill_fl - refill an SGE Rx buffer ring + *	@adap: the adapter + *	@q: the ring to refill + *	@n: the number of new buffers to allocate + *	@gfp: the gfp flags for the allocations + * + *	(Re)populate an SGE free-buffer queue with up to @n new packet buffers, + *	allocated with the supplied gfp flags.  The caller must assure that + *	@n does not exceed the queue's capacity.  If afterwards the queue is + *	found critically low mark it as starving in the bitmap of starving FLs. + * + *	Returns the number of buffers allocated. + */ +static unsigned int refill_fl(struct adapter *adap, struct sge_fl *q, int n, +			      gfp_t gfp) +{ +	struct page *pg; +	dma_addr_t mapping; +	unsigned int cred = q->avail; +	__be64 *d = &q->desc[q->pidx]; +	struct rx_sw_desc *sd = &q->sdesc[q->pidx]; + +	gfp |= __GFP_NOWARN;         /* failures are expected */ + +#if FL_PG_ORDER > 0 +	/* +	 * Prefer large buffers +	 */ +	while (n) { +		pg = alloc_pages(gfp | __GFP_COMP, FL_PG_ORDER); +		if (unlikely(!pg)) { +			q->large_alloc_failed++; +			break;       /* fall back to single pages */ +		} + +		mapping = dma_map_page(adap->pdev_dev, pg, 0, +				       PAGE_SIZE << FL_PG_ORDER, +				       PCI_DMA_FROMDEVICE); +		if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) { +			__free_pages(pg, FL_PG_ORDER); +			goto out;   /* do not try small pages for this error */ +		} +		mapping |= RX_LARGE_BUF; +		*d++ = cpu_to_be64(mapping); + +		set_rx_sw_desc(sd, pg, mapping); +		sd++; + +		q->avail++; +		if (++q->pidx == q->size) { +			q->pidx = 0; +			sd = q->sdesc; +			d = q->desc; +		} +		n--; +	} +#endif + +	while (n--) { +		pg = __netdev_alloc_page(adap->port[0], gfp); +		if (unlikely(!pg)) { +			q->alloc_failed++; +			break; +		} + +		mapping = dma_map_page(adap->pdev_dev, pg, 0, PAGE_SIZE, +				       PCI_DMA_FROMDEVICE); +		if (unlikely(dma_mapping_error(adap->pdev_dev, mapping))) { +			netdev_free_page(adap->port[0], pg); +			goto out; +		} +		*d++ = cpu_to_be64(mapping); + +		set_rx_sw_desc(sd, pg, mapping); +		sd++; + +		q->avail++; +		if (++q->pidx == q->size) { +			q->pidx = 0; +			sd = q->sdesc; +			d = q->desc; +		} +	} + +out:	cred = q->avail - cred; +	q->pend_cred += cred; +	ring_fl_db(adap, q); + +	if (unlikely(fl_starving(q))) { +		smp_wmb(); +		set_bit(q->cntxt_id, adap->sge.starving_fl); +	} + +	return cred; +} + +static inline void __refill_fl(struct adapter *adap, struct sge_fl *fl) +{ +	refill_fl(adap, fl, min(MAX_RX_REFILL, fl_cap(fl) - fl->avail), +		  GFP_ATOMIC); +} + +/** + *	alloc_ring - allocate resources for an SGE descriptor ring + *	@dev: the PCI device's core device + *	@nelem: the number of descriptors + *	@elem_size: the size of each descriptor + *	@sw_size: the size of the SW state associated with each ring element + *	@phys: the physical address of the allocated ring + *	@metadata: address of the array holding the SW state for the ring + *	@stat_size: extra space in HW ring for status information + * + *	Allocates resources for an SGE descriptor ring, such as Tx queues, + *	free buffer lists, or response queues.  Each SGE ring requires + *	space for its HW descriptors plus, optionally, space for the SW state + *	associated with each HW entry (the metadata).  The function returns + *	three values: the virtual address for the HW ring (the return value + *	of the function), the bus address of the HW ring, and the address + *	of the SW ring. + */ +static void *alloc_ring(struct device *dev, size_t nelem, size_t elem_size, +			size_t sw_size, dma_addr_t *phys, void *metadata, +			size_t stat_size) +{ +	size_t len = nelem * elem_size + stat_size; +	void *s = NULL; +	void *p = dma_alloc_coherent(dev, len, phys, GFP_KERNEL); + +	if (!p) +		return NULL; +	if (sw_size) { +		s = kcalloc(nelem, sw_size, GFP_KERNEL); + +		if (!s) { +			dma_free_coherent(dev, len, p, *phys); +			return NULL; +		} +	} +	if (metadata) +		*(void **)metadata = s; +	memset(p, 0, len); +	return p; +} + +/** + *	sgl_len - calculates the size of an SGL of the given capacity + *	@n: the number of SGL entries + * + *	Calculates the number of flits needed for a scatter/gather list that + *	can hold the given number of entries. + */ +static inline unsigned int sgl_len(unsigned int n) +{ +	n--; +	return (3 * n) / 2 + (n & 1) + 2; +} + +/** + *	flits_to_desc - returns the num of Tx descriptors for the given flits + *	@n: the number of flits + * + *	Returns the number of Tx descriptors needed for the supplied number + *	of flits. + */ +static inline unsigned int flits_to_desc(unsigned int n) +{ +	BUG_ON(n > SGE_MAX_WR_LEN / 8); +	return DIV_ROUND_UP(n, 8); +} + +/** + *	is_eth_imm - can an Ethernet packet be sent as immediate data? + *	@skb: the packet + * + *	Returns whether an Ethernet packet is small enough to fit as + *	immediate data. + */ +static inline int is_eth_imm(const struct sk_buff *skb) +{ +	return skb->len <= MAX_IMM_TX_PKT_LEN - sizeof(struct cpl_tx_pkt); +} + +/** + *	calc_tx_flits - calculate the number of flits for a packet Tx WR + *	@skb: the packet + * + *	Returns the number of flits needed for a Tx WR for the given Ethernet + *	packet, including the needed WR and CPL headers. + */ +static inline unsigned int calc_tx_flits(const struct sk_buff *skb) +{ +	unsigned int flits; + +	if (is_eth_imm(skb)) +		return DIV_ROUND_UP(skb->len + sizeof(struct cpl_tx_pkt), 8); + +	flits = sgl_len(skb_shinfo(skb)->nr_frags + 1) + 4; +	if (skb_shinfo(skb)->gso_size) +		flits += 2; +	return flits; +} + +/** + *	calc_tx_descs - calculate the number of Tx descriptors for a packet + *	@skb: the packet + * + *	Returns the number of Tx descriptors needed for the given Ethernet + *	packet, including the needed WR and CPL headers. + */ +static inline unsigned int calc_tx_descs(const struct sk_buff *skb) +{ +	return flits_to_desc(calc_tx_flits(skb)); +} + +/** + *	write_sgl - populate a scatter/gather list for a packet + *	@skb: the packet + *	@q: the Tx queue we are writing into + *	@sgl: starting location for writing the SGL + *	@end: points right after the end of the SGL + *	@start: start offset into skb main-body data to include in the SGL + *	@addr: the list of bus addresses for the SGL elements + * + *	Generates a gather list for the buffers that make up a packet. + *	The caller must provide adequate space for the SGL that will be written. + *	The SGL includes all of the packet's page fragments and the data in its + *	main body except for the first @start bytes.  @sgl must be 16-byte + *	aligned and within a Tx descriptor with available space.  @end points + *	right after the end of the SGL but does not account for any potential + *	wrap around, i.e., @end > @sgl. + */ +static void write_sgl(const struct sk_buff *skb, struct sge_txq *q, +		      struct ulptx_sgl *sgl, u64 *end, unsigned int start, +		      const dma_addr_t *addr) +{ +	unsigned int i, len; +	struct ulptx_sge_pair *to; +	const struct skb_shared_info *si = skb_shinfo(skb); +	unsigned int nfrags = si->nr_frags; +	struct ulptx_sge_pair buf[MAX_SKB_FRAGS / 2 + 1]; + +	len = skb_headlen(skb) - start; +	if (likely(len)) { +		sgl->len0 = htonl(len); +		sgl->addr0 = cpu_to_be64(addr[0] + start); +		nfrags++; +	} else { +		sgl->len0 = htonl(si->frags[0].size); +		sgl->addr0 = cpu_to_be64(addr[1]); +	} + +	sgl->cmd_nsge = htonl(ULPTX_CMD(ULP_TX_SC_DSGL) | ULPTX_NSGE(nfrags)); +	if (likely(--nfrags == 0)) +		return; +	/* +	 * Most of the complexity below deals with the possibility we hit the +	 * end of the queue in the middle of writing the SGL.  For this case +	 * only we create the SGL in a temporary buffer and then copy it. +	 */ +	to = (u8 *)end > (u8 *)q->stat ? buf : sgl->sge; + +	for (i = (nfrags != si->nr_frags); nfrags >= 2; nfrags -= 2, to++) { +		to->len[0] = cpu_to_be32(si->frags[i].size); +		to->len[1] = cpu_to_be32(si->frags[++i].size); +		to->addr[0] = cpu_to_be64(addr[i]); +		to->addr[1] = cpu_to_be64(addr[++i]); +	} +	if (nfrags) { +		to->len[0] = cpu_to_be32(si->frags[i].size); +		to->len[1] = cpu_to_be32(0); +		to->addr[0] = cpu_to_be64(addr[i + 1]); +	} +	if (unlikely((u8 *)end > (u8 *)q->stat)) { +		unsigned int part0 = (u8 *)q->stat - (u8 *)sgl->sge, part1; + +		if (likely(part0)) +			memcpy(sgl->sge, buf, part0); +		part1 = (u8 *)end - (u8 *)q->stat; +		memcpy(q->desc, (u8 *)buf + part0, part1); +		end = (void *)q->desc + part1; +	} +	if ((uintptr_t)end & 8)           /* 0-pad to multiple of 16 */ +		*(u64 *)end = 0; +} + +/** + *	ring_tx_db - check and potentially ring a Tx queue's doorbell + *	@adap: the adapter + *	@q: the Tx queue + *	@n: number of new descriptors to give to HW + * + *	Ring the doorbel for a Tx queue. + */ +static inline void ring_tx_db(struct adapter *adap, struct sge_txq *q, int n) +{ +	wmb();            /* write descriptors before telling HW */ +	t4_write_reg(adap, MYPF_REG(SGE_PF_KDOORBELL), +		     QID(q->cntxt_id) | PIDX(n)); +} + +/** + *	inline_tx_skb - inline a packet's data into Tx descriptors + *	@skb: the packet + *	@q: the Tx queue where the packet will be inlined + *	@pos: starting position in the Tx queue where to inline the packet + * + *	Inline a packet's contents directly into Tx descriptors, starting at + *	the given position within the Tx DMA ring. + *	Most of the complexity of this operation is dealing with wrap arounds + *	in the middle of the packet we want to inline. + */ +static void inline_tx_skb(const struct sk_buff *skb, const struct sge_txq *q, +			  void *pos) +{ +	u64 *p; +	int left = (void *)q->stat - pos; + +	if (likely(skb->len <= left)) { +		if (likely(!skb->data_len)) +			skb_copy_from_linear_data(skb, pos, skb->len); +		else +			skb_copy_bits(skb, 0, pos, skb->len); +		pos += skb->len; +	} else { +		skb_copy_bits(skb, 0, pos, left); +		skb_copy_bits(skb, left, q->desc, skb->len - left); +		pos = (void *)q->desc + (skb->len - left); +	} + +	/* 0-pad to multiple of 16 */ +	p = PTR_ALIGN(pos, 8); +	if ((uintptr_t)p & 8) +		*p = 0; +} + +/* + * Figure out what HW csum a packet wants and return the appropriate control + * bits. + */ +static u64 hwcsum(const struct sk_buff *skb) +{ +	int csum_type; +	const struct iphdr *iph = ip_hdr(skb); + +	if (iph->version == 4) { +		if (iph->protocol == IPPROTO_TCP) +			csum_type = TX_CSUM_TCPIP; +		else if (iph->protocol == IPPROTO_UDP) +			csum_type = TX_CSUM_UDPIP; +		else { +nocsum:			/* +			 * unknown protocol, disable HW csum +			 * and hope a bad packet is detected +			 */ +			return TXPKT_L4CSUM_DIS; +		} +	} else { +		/* +		 * this doesn't work with extension headers +		 */ +		const struct ipv6hdr *ip6h = (const struct ipv6hdr *)iph; + +		if (ip6h->nexthdr == IPPROTO_TCP) +			csum_type = TX_CSUM_TCPIP6; +		else if (ip6h->nexthdr == IPPROTO_UDP) +			csum_type = TX_CSUM_UDPIP6; +		else +			goto nocsum; +	} + +	if (likely(csum_type >= TX_CSUM_TCPIP)) +		return TXPKT_CSUM_TYPE(csum_type) | +			TXPKT_IPHDR_LEN(skb_network_header_len(skb)) | +			TXPKT_ETHHDR_LEN(skb_network_offset(skb) - ETH_HLEN); +	else { +		int start = skb_transport_offset(skb); + +		return TXPKT_CSUM_TYPE(csum_type) | TXPKT_CSUM_START(start) | +			TXPKT_CSUM_LOC(start + skb->csum_offset); +	} +} + +static void eth_txq_stop(struct sge_eth_txq *q) +{ +	netif_tx_stop_queue(q->txq); +	q->q.stops++; +} + +static inline void txq_advance(struct sge_txq *q, unsigned int n) +{ +	q->in_use += n; +	q->pidx += n; +	if (q->pidx >= q->size) +		q->pidx -= q->size; +} + +/** + *	t4_eth_xmit - add a packet to an Ethernet Tx queue + *	@skb: the packet + *	@dev: the egress net device + * + *	Add a packet to an SGE Ethernet Tx queue.  Runs with softirqs disabled. + */ +netdev_tx_t t4_eth_xmit(struct sk_buff *skb, struct net_device *dev) +{ +	u32 wr_mid; +	u64 cntrl, *end; +	int qidx, credits; +	unsigned int flits, ndesc; +	struct adapter *adap; +	struct sge_eth_txq *q; +	const struct port_info *pi; +	struct fw_eth_tx_pkt_wr *wr; +	struct cpl_tx_pkt_core *cpl; +	const struct skb_shared_info *ssi; +	dma_addr_t addr[MAX_SKB_FRAGS + 1]; + +	/* +	 * The chip min packet length is 10 octets but play safe and reject +	 * anything shorter than an Ethernet header. +	 */ +	if (unlikely(skb->len < ETH_HLEN)) { +out_free:	dev_kfree_skb(skb); +		return NETDEV_TX_OK; +	} + +	pi = netdev_priv(dev); +	adap = pi->adapter; +	qidx = skb_get_queue_mapping(skb); +	q = &adap->sge.ethtxq[qidx + pi->first_qset]; + +	reclaim_completed_tx(adap, &q->q, true); + +	flits = calc_tx_flits(skb); +	ndesc = flits_to_desc(flits); +	credits = txq_avail(&q->q) - ndesc; + +	if (unlikely(credits < 0)) { +		eth_txq_stop(q); +		dev_err(adap->pdev_dev, +			"%s: Tx ring %u full while queue awake!\n", +			dev->name, qidx); +		return NETDEV_TX_BUSY; +	} + +	if (!is_eth_imm(skb) && +	    unlikely(map_skb(adap->pdev_dev, skb, addr) < 0)) { +		q->mapping_err++; +		goto out_free; +	} + +	wr_mid = FW_WR_LEN16(DIV_ROUND_UP(flits, 2)); +	if (unlikely(credits < ETHTXQ_STOP_THRES)) { +		eth_txq_stop(q); +		wr_mid |= FW_WR_EQUEQ | FW_WR_EQUIQ; +	} + +	wr = (void *)&q->q.desc[q->q.pidx]; +	wr->equiq_to_len16 = htonl(wr_mid); +	wr->r3 = cpu_to_be64(0); +	end = (u64 *)wr + flits; + +	ssi = skb_shinfo(skb); +	if (ssi->gso_size) { +		struct cpl_tx_pkt_lso *lso = (void *)wr; +		bool v6 = (ssi->gso_type & SKB_GSO_TCPV6) != 0; +		int l3hdr_len = skb_network_header_len(skb); +		int eth_xtra_len = skb_network_offset(skb) - ETH_HLEN; + +		wr->op_immdlen = htonl(FW_WR_OP(FW_ETH_TX_PKT_WR) | +				       FW_WR_IMMDLEN(sizeof(*lso))); +		lso->lso_ctrl = htonl(LSO_OPCODE(CPL_TX_PKT_LSO) | +				      LSO_FIRST_SLICE | LSO_LAST_SLICE | +				      LSO_IPV6(v6) | +				      LSO_ETHHDR_LEN(eth_xtra_len / 4) | +				      LSO_IPHDR_LEN(l3hdr_len / 4) | +				      LSO_TCPHDR_LEN(tcp_hdr(skb)->doff)); +		lso->ipid_ofst = htons(0); +		lso->mss = htons(ssi->gso_size); +		lso->seqno_offset = htonl(0); +		lso->len = htonl(skb->len); +		cpl = (void *)(lso + 1); +		cntrl = TXPKT_CSUM_TYPE(v6 ? TX_CSUM_TCPIP6 : TX_CSUM_TCPIP) | +			TXPKT_IPHDR_LEN(l3hdr_len) | +			TXPKT_ETHHDR_LEN(eth_xtra_len); +		q->tso++; +		q->tx_cso += ssi->gso_segs; +	} else { +		int len; + +		len = is_eth_imm(skb) ? skb->len + sizeof(*cpl) : sizeof(*cpl); +		wr->op_immdlen = htonl(FW_WR_OP(FW_ETH_TX_PKT_WR) | +				       FW_WR_IMMDLEN(len)); +		cpl = (void *)(wr + 1); +		if (skb->ip_summed == CHECKSUM_PARTIAL) { +			cntrl = hwcsum(skb) | TXPKT_IPCSUM_DIS; +			q->tx_cso++; +		} else +			cntrl = TXPKT_L4CSUM_DIS | TXPKT_IPCSUM_DIS; +	} + +	if (vlan_tx_tag_present(skb)) { +		q->vlan_ins++; +		cntrl |= TXPKT_VLAN_VLD | TXPKT_VLAN(vlan_tx_tag_get(skb)); +	} + +	cpl->ctrl0 = htonl(TXPKT_OPCODE(CPL_TX_PKT_XT) | +			   TXPKT_INTF(pi->tx_chan) | TXPKT_PF(0)); +	cpl->pack = htons(0); +	cpl->len = htons(skb->len); +	cpl->ctrl1 = cpu_to_be64(cntrl); + +	if (is_eth_imm(skb)) { +		inline_tx_skb(skb, &q->q, cpl + 1); +		dev_kfree_skb(skb); +	} else { +		int last_desc; + +		write_sgl(skb, &q->q, (struct ulptx_sgl *)(cpl + 1), end, 0, +			  addr); +		skb_orphan(skb); + +		last_desc = q->q.pidx + ndesc - 1; +		if (last_desc >= q->q.size) +			last_desc -= q->q.size; +		q->q.sdesc[last_desc].skb = skb; +		q->q.sdesc[last_desc].sgl = (struct ulptx_sgl *)(cpl + 1); +	} + +	txq_advance(&q->q, ndesc); + +	ring_tx_db(adap, &q->q, ndesc); +	return NETDEV_TX_OK; +} + +/** + *	reclaim_completed_tx_imm - reclaim completed control-queue Tx descs + *	@q: the SGE control Tx queue + * + *	This is a variant of reclaim_completed_tx() that is used for Tx queues + *	that send only immediate data (presently just the control queues) and + *	thus do not have any sk_buffs to release. + */ +static inline void reclaim_completed_tx_imm(struct sge_txq *q) +{ +	int hw_cidx = ntohs(q->stat->cidx); +	int reclaim = hw_cidx - q->cidx; + +	if (reclaim < 0) +		reclaim += q->size; + +	q->in_use -= reclaim; +	q->cidx = hw_cidx; +} + +/** + *	is_imm - check whether a packet can be sent as immediate data + *	@skb: the packet + * + *	Returns true if a packet can be sent as a WR with immediate data. + */ +static inline int is_imm(const struct sk_buff *skb) +{ +	return skb->len <= MAX_CTRL_WR_LEN; +} + +/** + *	ctrlq_check_stop - check if a control queue is full and should stop + *	@q: the queue + *	@wr: most recent WR written to the queue + * + *	Check if a control queue has become full and should be stopped. + *	We clean up control queue descriptors very lazily, only when we are out. + *	If the queue is still full after reclaiming any completed descriptors + *	we suspend it and have the last WR wake it up. + */ +static void ctrlq_check_stop(struct sge_ctrl_txq *q, struct fw_wr_hdr *wr) +{ +	reclaim_completed_tx_imm(&q->q); +	if (unlikely(txq_avail(&q->q) < TXQ_STOP_THRES)) { +		wr->lo |= htonl(FW_WR_EQUEQ | FW_WR_EQUIQ); +		q->q.stops++; +		q->full = 1; +	} +} + +/** + *	ctrl_xmit - send a packet through an SGE control Tx queue + *	@q: the control queue + *	@skb: the packet + * + *	Send a packet through an SGE control Tx queue.  Packets sent through + *	a control queue must fit entirely as immediate data. + */ +static int ctrl_xmit(struct sge_ctrl_txq *q, struct sk_buff *skb) +{ +	unsigned int ndesc; +	struct fw_wr_hdr *wr; + +	if (unlikely(!is_imm(skb))) { +		WARN_ON(1); +		dev_kfree_skb(skb); +		return NET_XMIT_DROP; +	} + +	ndesc = DIV_ROUND_UP(skb->len, sizeof(struct tx_desc)); +	spin_lock(&q->sendq.lock); + +	if (unlikely(q->full)) { +		skb->priority = ndesc;                  /* save for restart */ +		__skb_queue_tail(&q->sendq, skb); +		spin_unlock(&q->sendq.lock); +		return NET_XMIT_CN; +	} + +	wr = (struct fw_wr_hdr *)&q->q.desc[q->q.pidx]; +	inline_tx_skb(skb, &q->q, wr); + +	txq_advance(&q->q, ndesc); +	if (unlikely(txq_avail(&q->q) < TXQ_STOP_THRES)) +		ctrlq_check_stop(q, wr); + +	ring_tx_db(q->adap, &q->q, ndesc); +	spin_unlock(&q->sendq.lock); + +	kfree_skb(skb); +	return NET_XMIT_SUCCESS; +} + +/** + *	restart_ctrlq - restart a suspended control queue + *	@data: the control queue to restart + * + *	Resumes transmission on a suspended Tx control queue. + */ +static void restart_ctrlq(unsigned long data) +{ +	struct sk_buff *skb; +	unsigned int written = 0; +	struct sge_ctrl_txq *q = (struct sge_ctrl_txq *)data; + +	spin_lock(&q->sendq.lock); +	reclaim_completed_tx_imm(&q->q); +	BUG_ON(txq_avail(&q->q) < TXQ_STOP_THRES);  /* q should be empty */ + +	while ((skb = __skb_dequeue(&q->sendq)) != NULL) { +		struct fw_wr_hdr *wr; +		unsigned int ndesc = skb->priority;     /* previously saved */ + +		/* +		 * Write descriptors and free skbs outside the lock to limit +		 * wait times.  q->full is still set so new skbs will be queued. +		 */ +		spin_unlock(&q->sendq.lock); + +		wr = (struct fw_wr_hdr *)&q->q.desc[q->q.pidx]; +		inline_tx_skb(skb, &q->q, wr); +		kfree_skb(skb); + +		written += ndesc; +		txq_advance(&q->q, ndesc); +		if (unlikely(txq_avail(&q->q) < TXQ_STOP_THRES)) { +			unsigned long old = q->q.stops; + +			ctrlq_check_stop(q, wr); +			if (q->q.stops != old) {          /* suspended anew */ +				spin_lock(&q->sendq.lock); +				goto ringdb; +			} +		} +		if (written > 16) { +			ring_tx_db(q->adap, &q->q, written); +			written = 0; +		} +		spin_lock(&q->sendq.lock); +	} +	q->full = 0; +ringdb: if (written) +		ring_tx_db(q->adap, &q->q, written); +	spin_unlock(&q->sendq.lock); +} + +/** + *	t4_mgmt_tx - send a management message + *	@adap: the adapter + *	@skb: the packet containing the management message + * + *	Send a management message through control queue 0. + */ +int t4_mgmt_tx(struct adapter *adap, struct sk_buff *skb) +{ +	int ret; + +	local_bh_disable(); +	ret = ctrl_xmit(&adap->sge.ctrlq[0], skb); +	local_bh_enable(); +	return ret; +} + +/** + *	is_ofld_imm - check whether a packet can be sent as immediate data + *	@skb: the packet + * + *	Returns true if a packet can be sent as an offload WR with immediate + *	data.  We currently use the same limit as for Ethernet packets. + */ +static inline int is_ofld_imm(const struct sk_buff *skb) +{ +	return skb->len <= MAX_IMM_TX_PKT_LEN; +} + +/** + *	calc_tx_flits_ofld - calculate # of flits for an offload packet + *	@skb: the packet + * + *	Returns the number of flits needed for the given offload packet. + *	These packets are already fully constructed and no additional headers + *	will be added. + */ +static inline unsigned int calc_tx_flits_ofld(const struct sk_buff *skb) +{ +	unsigned int flits, cnt; + +	if (is_ofld_imm(skb)) +		return DIV_ROUND_UP(skb->len, 8); + +	flits = skb_transport_offset(skb) / 8U;   /* headers */ +	cnt = skb_shinfo(skb)->nr_frags; +	if (skb->tail != skb->transport_header) +		cnt++; +	return flits + sgl_len(cnt); +} + +/** + *	txq_stop_maperr - stop a Tx queue due to I/O MMU exhaustion + *	@adap: the adapter + *	@q: the queue to stop + * + *	Mark a Tx queue stopped due to I/O MMU exhaustion and resulting + *	inability to map packets.  A periodic timer attempts to restart + *	queues so marked. + */ +static void txq_stop_maperr(struct sge_ofld_txq *q) +{ +	q->mapping_err++; +	q->q.stops++; +	set_bit(q->q.cntxt_id, q->adap->sge.txq_maperr); +} + +/** + *	ofldtxq_stop - stop an offload Tx queue that has become full + *	@q: the queue to stop + *	@skb: the packet causing the queue to become full + * + *	Stops an offload Tx queue that has become full and modifies the packet + *	being written to request a wakeup. + */ +static void ofldtxq_stop(struct sge_ofld_txq *q, struct sk_buff *skb) +{ +	struct fw_wr_hdr *wr = (struct fw_wr_hdr *)skb->data; + +	wr->lo |= htonl(FW_WR_EQUEQ | FW_WR_EQUIQ); +	q->q.stops++; +	q->full = 1; +} + +/** + *	service_ofldq - restart a suspended offload queue + *	@q: the offload queue + * + *	Services an offload Tx queue by moving packets from its packet queue + *	to the HW Tx ring.  The function starts and ends with the queue locked. + */ +static void service_ofldq(struct sge_ofld_txq *q) +{ +	u64 *pos; +	int credits; +	struct sk_buff *skb; +	unsigned int written = 0; +	unsigned int flits, ndesc; + +	while ((skb = skb_peek(&q->sendq)) != NULL && !q->full) { +		/* +		 * We drop the lock but leave skb on sendq, thus retaining +		 * exclusive access to the state of the queue. +		 */ +		spin_unlock(&q->sendq.lock); + +		reclaim_completed_tx(q->adap, &q->q, false); + +		flits = skb->priority;                /* previously saved */ +		ndesc = flits_to_desc(flits); +		credits = txq_avail(&q->q) - ndesc; +		BUG_ON(credits < 0); +		if (unlikely(credits < TXQ_STOP_THRES)) +			ofldtxq_stop(q, skb); + +		pos = (u64 *)&q->q.desc[q->q.pidx]; +		if (is_ofld_imm(skb)) +			inline_tx_skb(skb, &q->q, pos); +		else if (map_skb(q->adap->pdev_dev, skb, +				 (dma_addr_t *)skb->head)) { +			txq_stop_maperr(q); +			spin_lock(&q->sendq.lock); +			break; +		} else { +			int last_desc, hdr_len = skb_transport_offset(skb); + +			memcpy(pos, skb->data, hdr_len); +			write_sgl(skb, &q->q, (void *)pos + hdr_len, +				  pos + flits, hdr_len, +				  (dma_addr_t *)skb->head); +#ifdef CONFIG_NEED_DMA_MAP_STATE +			skb->dev = q->adap->port[0]; +			skb->destructor = deferred_unmap_destructor; +#endif +			last_desc = q->q.pidx + ndesc - 1; +			if (last_desc >= q->q.size) +				last_desc -= q->q.size; +			q->q.sdesc[last_desc].skb = skb; +		} + +		txq_advance(&q->q, ndesc); +		written += ndesc; +		if (unlikely(written > 32)) { +			ring_tx_db(q->adap, &q->q, written); +			written = 0; +		} + +		spin_lock(&q->sendq.lock); +		__skb_unlink(skb, &q->sendq); +		if (is_ofld_imm(skb)) +			kfree_skb(skb); +	} +	if (likely(written)) +		ring_tx_db(q->adap, &q->q, written); +} + +/** + *	ofld_xmit - send a packet through an offload queue + *	@q: the Tx offload queue + *	@skb: the packet + * + *	Send an offload packet through an SGE offload queue. + */ +static int ofld_xmit(struct sge_ofld_txq *q, struct sk_buff *skb) +{ +	skb->priority = calc_tx_flits_ofld(skb);       /* save for restart */ +	spin_lock(&q->sendq.lock); +	__skb_queue_tail(&q->sendq, skb); +	if (q->sendq.qlen == 1) +		service_ofldq(q); +	spin_unlock(&q->sendq.lock); +	return NET_XMIT_SUCCESS; +} + +/** + *	restart_ofldq - restart a suspended offload queue + *	@data: the offload queue to restart + * + *	Resumes transmission on a suspended Tx offload queue. + */ +static void restart_ofldq(unsigned long data) +{ +	struct sge_ofld_txq *q = (struct sge_ofld_txq *)data; + +	spin_lock(&q->sendq.lock); +	q->full = 0;            /* the queue actually is completely empty now */ +	service_ofldq(q); +	spin_unlock(&q->sendq.lock); +} + +/** + *	skb_txq - return the Tx queue an offload packet should use + *	@skb: the packet + * + *	Returns the Tx queue an offload packet should use as indicated by bits + *	1-15 in the packet's queue_mapping. + */ +static inline unsigned int skb_txq(const struct sk_buff *skb) +{ +	return skb->queue_mapping >> 1; +} + +/** + *	is_ctrl_pkt - return whether an offload packet is a control packet + *	@skb: the packet + * + *	Returns whether an offload packet should use an OFLD or a CTRL + *	Tx queue as indicated by bit 0 in the packet's queue_mapping. + */ +static inline unsigned int is_ctrl_pkt(const struct sk_buff *skb) +{ +	return skb->queue_mapping & 1; +} + +static inline int ofld_send(struct adapter *adap, struct sk_buff *skb) +{ +	unsigned int idx = skb_txq(skb); + +	if (unlikely(is_ctrl_pkt(skb))) +		return ctrl_xmit(&adap->sge.ctrlq[idx], skb); +	return ofld_xmit(&adap->sge.ofldtxq[idx], skb); +} + +/** + *	t4_ofld_send - send an offload packet + *	@adap: the adapter + *	@skb: the packet + * + *	Sends an offload packet.  We use the packet queue_mapping to select the + *	appropriate Tx queue as follows: bit 0 indicates whether the packet + *	should be sent as regular or control, bits 1-15 select the queue. + */ +int t4_ofld_send(struct adapter *adap, struct sk_buff *skb) +{ +	int ret; + +	local_bh_disable(); +	ret = ofld_send(adap, skb); +	local_bh_enable(); +	return ret; +} + +/** + *	cxgb4_ofld_send - send an offload packet + *	@dev: the net device + *	@skb: the packet + * + *	Sends an offload packet.  This is an exported version of @t4_ofld_send, + *	intended for ULDs. + */ +int cxgb4_ofld_send(struct net_device *dev, struct sk_buff *skb) +{ +	return t4_ofld_send(netdev2adap(dev), skb); +} +EXPORT_SYMBOL(cxgb4_ofld_send); + +static inline void copy_frags(struct skb_shared_info *ssi, +			      const struct pkt_gl *gl, unsigned int offset) +{ +	unsigned int n; + +	/* usually there's just one frag */ +	ssi->frags[0].page = gl->frags[0].page; +	ssi->frags[0].page_offset = gl->frags[0].page_offset + offset; +	ssi->frags[0].size = gl->frags[0].size - offset; +	ssi->nr_frags = gl->nfrags; +	n = gl->nfrags - 1; +	if (n) +		memcpy(&ssi->frags[1], &gl->frags[1], n * sizeof(skb_frag_t)); + +	/* get a reference to the last page, we don't own it */ +	get_page(gl->frags[n].page); +} + +/** + *	cxgb4_pktgl_to_skb - build an sk_buff from a packet gather list + *	@gl: the gather list + *	@skb_len: size of sk_buff main body if it carries fragments + *	@pull_len: amount of data to move to the sk_buff's main body + * + *	Builds an sk_buff from the given packet gather list.  Returns the + *	sk_buff or %NULL if sk_buff allocation failed. + */ +struct sk_buff *cxgb4_pktgl_to_skb(const struct pkt_gl *gl, +				   unsigned int skb_len, unsigned int pull_len) +{ +	struct sk_buff *skb; + +	/* +	 * Below we rely on RX_COPY_THRES being less than the smallest Rx buffer +	 * size, which is expected since buffers are at least PAGE_SIZEd. +	 * In this case packets up to RX_COPY_THRES have only one fragment. +	 */ +	if (gl->tot_len <= RX_COPY_THRES) { +		skb = dev_alloc_skb(gl->tot_len); +		if (unlikely(!skb)) +			goto out; +		__skb_put(skb, gl->tot_len); +		skb_copy_to_linear_data(skb, gl->va, gl->tot_len); +	} else { +		skb = dev_alloc_skb(skb_len); +		if (unlikely(!skb)) +			goto out; +		__skb_put(skb, pull_len); +		skb_copy_to_linear_data(skb, gl->va, pull_len); + +		copy_frags(skb_shinfo(skb), gl, pull_len); +		skb->len = gl->tot_len; +		skb->data_len = skb->len - pull_len; +		skb->truesize += skb->data_len; +	} +out:	return skb; +} +EXPORT_SYMBOL(cxgb4_pktgl_to_skb); + +/** + *	t4_pktgl_free - free a packet gather list + *	@gl: the gather list + * + *	Releases the pages of a packet gather list.  We do not own the last + *	page on the list and do not free it. + */ +void t4_pktgl_free(const struct pkt_gl *gl) +{ +	int n; +	const skb_frag_t *p; + +	for (p = gl->frags, n = gl->nfrags - 1; n--; p++) +		put_page(p->page); +} + +/* + * Process an MPS trace packet.  Give it an unused protocol number so it won't + * be delivered to anyone and send it to the stack for capture. + */ +static noinline int handle_trace_pkt(struct adapter *adap, +				     const struct pkt_gl *gl) +{ +	struct sk_buff *skb; +	struct cpl_trace_pkt *p; + +	skb = cxgb4_pktgl_to_skb(gl, RX_PULL_LEN, RX_PULL_LEN); +	if (unlikely(!skb)) { +		t4_pktgl_free(gl); +		return 0; +	} + +	p = (struct cpl_trace_pkt *)skb->data; +	__skb_pull(skb, sizeof(*p)); +	skb_reset_mac_header(skb); +	skb->protocol = htons(0xffff); +	skb->dev = adap->port[0]; +	netif_receive_skb(skb); +	return 0; +} + +static void do_gro(struct sge_eth_rxq *rxq, const struct pkt_gl *gl, +		   const struct cpl_rx_pkt *pkt) +{ +	int ret; +	struct sk_buff *skb; + +	skb = napi_get_frags(&rxq->rspq.napi); +	if (unlikely(!skb)) { +		t4_pktgl_free(gl); +		rxq->stats.rx_drops++; +		return; +	} + +	copy_frags(skb_shinfo(skb), gl, RX_PKT_PAD); +	skb->len = gl->tot_len - RX_PKT_PAD; +	skb->data_len = skb->len; +	skb->truesize += skb->data_len; +	skb->ip_summed = CHECKSUM_UNNECESSARY; +	skb_record_rx_queue(skb, rxq->rspq.idx); + +	if (unlikely(pkt->vlan_ex)) { +		struct port_info *pi = netdev_priv(rxq->rspq.netdev); +		struct vlan_group *grp = pi->vlan_grp; + +		rxq->stats.vlan_ex++; +		if (likely(grp)) { +			ret = vlan_gro_frags(&rxq->rspq.napi, grp, +					     ntohs(pkt->vlan)); +			goto stats; +		} +	} +	ret = napi_gro_frags(&rxq->rspq.napi); +stats:	if (ret == GRO_HELD) +		rxq->stats.lro_pkts++; +	else if (ret == GRO_MERGED || ret == GRO_MERGED_FREE) +		rxq->stats.lro_merged++; +	rxq->stats.pkts++; +	rxq->stats.rx_cso++; +} + +/** + *	t4_ethrx_handler - process an ingress ethernet packet + *	@q: the response queue that received the packet + *	@rsp: the response queue descriptor holding the RX_PKT message + *	@si: the gather list of packet fragments + * + *	Process an ingress ethernet packet and deliver it to the stack. + */ +int t4_ethrx_handler(struct sge_rspq *q, const __be64 *rsp, +		     const struct pkt_gl *si) +{ +	bool csum_ok; +	struct sk_buff *skb; +	struct port_info *pi; +	const struct cpl_rx_pkt *pkt; +	struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq); + +	if (unlikely(*(u8 *)rsp == CPL_TRACE_PKT)) +		return handle_trace_pkt(q->adap, si); + +	pkt = (void *)&rsp[1]; +	csum_ok = pkt->csum_calc && !pkt->err_vec; +	if ((pkt->l2info & htonl(RXF_TCP)) && +	    (q->netdev->features & NETIF_F_GRO) && csum_ok && !pkt->ip_frag) { +		do_gro(rxq, si, pkt); +		return 0; +	} + +	skb = cxgb4_pktgl_to_skb(si, RX_PKT_SKB_LEN, RX_PULL_LEN); +	if (unlikely(!skb)) { +		t4_pktgl_free(si); +		rxq->stats.rx_drops++; +		return 0; +	} + +	__skb_pull(skb, RX_PKT_PAD);      /* remove ethernet header padding */ +	skb->protocol = eth_type_trans(skb, q->netdev); +	skb_record_rx_queue(skb, q->idx); +	pi = netdev_priv(skb->dev); +	rxq->stats.pkts++; + +	if (csum_ok && (pi->rx_offload & RX_CSO) && +	    (pkt->l2info & htonl(RXF_UDP | RXF_TCP))) { +		if (!pkt->ip_frag) +			skb->ip_summed = CHECKSUM_UNNECESSARY; +		else { +			__sum16 c = (__force __sum16)pkt->csum; +			skb->csum = csum_unfold(c); +			skb->ip_summed = CHECKSUM_COMPLETE; +		} +		rxq->stats.rx_cso++; +	} else +		skb->ip_summed = CHECKSUM_NONE; + +	if (unlikely(pkt->vlan_ex)) { +		struct vlan_group *grp = pi->vlan_grp; + +		rxq->stats.vlan_ex++; +		if (likely(grp)) +			vlan_hwaccel_receive_skb(skb, grp, ntohs(pkt->vlan)); +		else +			dev_kfree_skb_any(skb); +	} else +		netif_receive_skb(skb); + +	return 0; +} + +/** + *	restore_rx_bufs - put back a packet's Rx buffers + *	@si: the packet gather list + *	@q: the SGE free list + *	@frags: number of FL buffers to restore + * + *	Puts back on an FL the Rx buffers associated with @si.  The buffers + *	have already been unmapped and are left unmapped, we mark them so to + *	prevent further unmapping attempts. + * + *	This function undoes a series of @unmap_rx_buf calls when we find out + *	that the current packet can't be processed right away afterall and we + *	need to come back to it later.  This is a very rare event and there's + *	no effort to make this particularly efficient. + */ +static void restore_rx_bufs(const struct pkt_gl *si, struct sge_fl *q, +			    int frags) +{ +	struct rx_sw_desc *d; + +	while (frags--) { +		if (q->cidx == 0) +			q->cidx = q->size - 1; +		else +			q->cidx--; +		d = &q->sdesc[q->cidx]; +		d->page = si->frags[frags].page; +		d->dma_addr |= RX_UNMAPPED_BUF; +		q->avail++; +	} +} + +/** + *	is_new_response - check if a response is newly written + *	@r: the response descriptor + *	@q: the response queue + * + *	Returns true if a response descriptor contains a yet unprocessed + *	response. + */ +static inline bool is_new_response(const struct rsp_ctrl *r, +				   const struct sge_rspq *q) +{ +	return RSPD_GEN(r->type_gen) == q->gen; +} + +/** + *	rspq_next - advance to the next entry in a response queue + *	@q: the queue + * + *	Updates the state of a response queue to advance it to the next entry. + */ +static inline void rspq_next(struct sge_rspq *q) +{ +	q->cur_desc = (void *)q->cur_desc + q->iqe_len; +	if (unlikely(++q->cidx == q->size)) { +		q->cidx = 0; +		q->gen ^= 1; +		q->cur_desc = q->desc; +	} +} + +/** + *	process_responses - process responses from an SGE response queue + *	@q: the ingress queue to process + *	@budget: how many responses can be processed in this round + * + *	Process responses from an SGE response queue up to the supplied budget. + *	Responses include received packets as well as control messages from FW + *	or HW. + * + *	Additionally choose the interrupt holdoff time for the next interrupt + *	on this queue.  If the system is under memory shortage use a fairly + *	long delay to help recovery. + */ +static int process_responses(struct sge_rspq *q, int budget) +{ +	int ret, rsp_type; +	int budget_left = budget; +	const struct rsp_ctrl *rc; +	struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq); + +	while (likely(budget_left)) { +		rc = (void *)q->cur_desc + (q->iqe_len - sizeof(*rc)); +		if (!is_new_response(rc, q)) +			break; + +		rmb(); +		rsp_type = RSPD_TYPE(rc->type_gen); +		if (likely(rsp_type == RSP_TYPE_FLBUF)) { +			skb_frag_t *fp; +			struct pkt_gl si; +			const struct rx_sw_desc *rsd; +			u32 len = ntohl(rc->pldbuflen_qid), bufsz, frags; + +			if (len & RSPD_NEWBUF) { +				if (likely(q->offset > 0)) { +					free_rx_bufs(q->adap, &rxq->fl, 1); +					q->offset = 0; +				} +				len &= RSPD_LEN; +			} +			si.tot_len = len; + +			/* gather packet fragments */ +			for (frags = 0, fp = si.frags; ; frags++, fp++) { +				rsd = &rxq->fl.sdesc[rxq->fl.cidx]; +				bufsz = get_buf_size(rsd); +				fp->page = rsd->page; +				fp->page_offset = q->offset; +				fp->size = min(bufsz, len); +				len -= fp->size; +				if (!len) +					break; +				unmap_rx_buf(q->adap, &rxq->fl); +			} + +			/* +			 * Last buffer remains mapped so explicitly make it +			 * coherent for CPU access. +			 */ +			dma_sync_single_for_cpu(q->adap->pdev_dev, +						get_buf_addr(rsd), +						fp->size, DMA_FROM_DEVICE); + +			si.va = page_address(si.frags[0].page) + +				si.frags[0].page_offset; +			prefetch(si.va); + +			si.nfrags = frags + 1; +			ret = q->handler(q, q->cur_desc, &si); +			if (likely(ret == 0)) +				q->offset += ALIGN(fp->size, FL_ALIGN); +			else +				restore_rx_bufs(&si, &rxq->fl, frags); +		} else if (likely(rsp_type == RSP_TYPE_CPL)) { +			ret = q->handler(q, q->cur_desc, NULL); +		} else { +			ret = q->handler(q, (const __be64 *)rc, CXGB4_MSG_AN); +		} + +		if (unlikely(ret)) { +			/* couldn't process descriptor, back off for recovery */ +			q->next_intr_params = QINTR_TIMER_IDX(NOMEM_TMR_IDX); +			break; +		} + +		rspq_next(q); +		budget_left--; +	} + +	if (q->offset >= 0 && rxq->fl.size - rxq->fl.avail >= 16) +		__refill_fl(q->adap, &rxq->fl); +	return budget - budget_left; +} + +/** + *	napi_rx_handler - the NAPI handler for Rx processing + *	@napi: the napi instance + *	@budget: how many packets we can process in this round + * + *	Handler for new data events when using NAPI.  This does not need any + *	locking or protection from interrupts as data interrupts are off at + *	this point and other adapter interrupts do not interfere (the latter + *	in not a concern at all with MSI-X as non-data interrupts then have + *	a separate handler). + */ +static int napi_rx_handler(struct napi_struct *napi, int budget) +{ +	unsigned int params; +	struct sge_rspq *q = container_of(napi, struct sge_rspq, napi); +	int work_done = process_responses(q, budget); + +	if (likely(work_done < budget)) { +		napi_complete(napi); +		params = q->next_intr_params; +		q->next_intr_params = q->intr_params; +	} else +		params = QINTR_TIMER_IDX(7); + +	t4_write_reg(q->adap, MYPF_REG(SGE_PF_GTS), CIDXINC(work_done) | +		     INGRESSQID((u32)q->cntxt_id) | SEINTARM(params)); +	return work_done; +} + +/* + * The MSI-X interrupt handler for an SGE response queue. + */ +irqreturn_t t4_sge_intr_msix(int irq, void *cookie) +{ +	struct sge_rspq *q = cookie; + +	napi_schedule(&q->napi); +	return IRQ_HANDLED; +} + +/* + * Process the indirect interrupt entries in the interrupt queue and kick off + * NAPI for each queue that has generated an entry. + */ +static unsigned int process_intrq(struct adapter *adap) +{ +	unsigned int credits; +	const struct rsp_ctrl *rc; +	struct sge_rspq *q = &adap->sge.intrq; + +	spin_lock(&adap->sge.intrq_lock); +	for (credits = 0; ; credits++) { +		rc = (void *)q->cur_desc + (q->iqe_len - sizeof(*rc)); +		if (!is_new_response(rc, q)) +			break; + +		rmb(); +		if (RSPD_TYPE(rc->type_gen) == RSP_TYPE_INTR) { +			unsigned int qid = ntohl(rc->pldbuflen_qid); + +			napi_schedule(&adap->sge.ingr_map[qid]->napi); +		} + +		rspq_next(q); +	} + +	t4_write_reg(adap, MYPF_REG(SGE_PF_GTS), CIDXINC(credits) | +		     INGRESSQID(q->cntxt_id) | SEINTARM(q->intr_params)); +	spin_unlock(&adap->sge.intrq_lock); +	return credits; +} + +/* + * The MSI interrupt handler, which handles data events from SGE response queues + * as well as error and other async events as they all use the same MSI vector. + */ +static irqreturn_t t4_intr_msi(int irq, void *cookie) +{ +	struct adapter *adap = cookie; + +	t4_slow_intr_handler(adap); +	process_intrq(adap); +	return IRQ_HANDLED; +} + +/* + * Interrupt handler for legacy INTx interrupts. + * Handles data events from SGE response queues as well as error and other + * async events as they all use the same interrupt line. + */ +static irqreturn_t t4_intr_intx(int irq, void *cookie) +{ +	struct adapter *adap = cookie; + +	t4_write_reg(adap, MYPF_REG(PCIE_PF_CLI), 0); +	if (t4_slow_intr_handler(adap) | process_intrq(adap)) +		return IRQ_HANDLED; +	return IRQ_NONE;             /* probably shared interrupt */ +} + +/** + *	t4_intr_handler - select the top-level interrupt handler + *	@adap: the adapter + * + *	Selects the top-level interrupt handler based on the type of interrupts + *	(MSI-X, MSI, or INTx). + */ +irq_handler_t t4_intr_handler(struct adapter *adap) +{ +	if (adap->flags & USING_MSIX) +		return t4_sge_intr_msix; +	if (adap->flags & USING_MSI) +		return t4_intr_msi; +	return t4_intr_intx; +} + +static void sge_rx_timer_cb(unsigned long data) +{ +	unsigned long m; +	unsigned int i, cnt[2]; +	struct adapter *adap = (struct adapter *)data; +	struct sge *s = &adap->sge; + +	for (i = 0; i < ARRAY_SIZE(s->starving_fl); i++) +		for (m = s->starving_fl[i]; m; m &= m - 1) { +			struct sge_eth_rxq *rxq; +			unsigned int id = __ffs(m) + i * BITS_PER_LONG; +			struct sge_fl *fl = s->egr_map[id]; + +			clear_bit(id, s->starving_fl); +			smp_mb__after_clear_bit(); + +			if (fl_starving(fl)) { +				rxq = container_of(fl, struct sge_eth_rxq, fl); +				if (napi_reschedule(&rxq->rspq.napi)) +					fl->starving++; +				else +					set_bit(id, s->starving_fl); +			} +		} + +	t4_write_reg(adap, SGE_DEBUG_INDEX, 13); +	cnt[0] = t4_read_reg(adap, SGE_DEBUG_DATA_HIGH); +	cnt[1] = t4_read_reg(adap, SGE_DEBUG_DATA_LOW); + +	for (i = 0; i < 2; i++) +		if (cnt[i] >= s->starve_thres) { +			if (s->idma_state[i] || cnt[i] == 0xffffffff) +				continue; +			s->idma_state[i] = 1; +			t4_write_reg(adap, SGE_DEBUG_INDEX, 11); +			m = t4_read_reg(adap, SGE_DEBUG_DATA_LOW) >> (i * 16); +			dev_warn(adap->pdev_dev, +				 "SGE idma%u starvation detected for " +				 "queue %lu\n", i, m & 0xffff); +		} else if (s->idma_state[i]) +			s->idma_state[i] = 0; + +	mod_timer(&s->rx_timer, jiffies + RX_QCHECK_PERIOD); +} + +static void sge_tx_timer_cb(unsigned long data) +{ +	unsigned long m; +	unsigned int i, budget; +	struct adapter *adap = (struct adapter *)data; +	struct sge *s = &adap->sge; + +	for (i = 0; i < ARRAY_SIZE(s->txq_maperr); i++) +		for (m = s->txq_maperr[i]; m; m &= m - 1) { +			unsigned long id = __ffs(m) + i * BITS_PER_LONG; +			struct sge_ofld_txq *txq = s->egr_map[id]; + +			clear_bit(id, s->txq_maperr); +			tasklet_schedule(&txq->qresume_tsk); +		} + +	budget = MAX_TIMER_TX_RECLAIM; +	i = s->ethtxq_rover; +	do { +		struct sge_eth_txq *q = &s->ethtxq[i]; + +		if (q->q.in_use && +		    time_after_eq(jiffies, q->txq->trans_start + HZ / 100) && +		    __netif_tx_trylock(q->txq)) { +			int avail = reclaimable(&q->q); + +			if (avail) { +				if (avail > budget) +					avail = budget; + +				free_tx_desc(adap, &q->q, avail, true); +				q->q.in_use -= avail; +				budget -= avail; +			} +			__netif_tx_unlock(q->txq); +		} + +		if (++i >= s->ethqsets) +			i = 0; +	} while (budget && i != s->ethtxq_rover); +	s->ethtxq_rover = i; +	mod_timer(&s->tx_timer, jiffies + (budget ? TX_QCHECK_PERIOD : 2)); +} + +int t4_sge_alloc_rxq(struct adapter *adap, struct sge_rspq *iq, bool fwevtq, +		     struct net_device *dev, int intr_idx, +		     struct sge_fl *fl, rspq_handler_t hnd) +{ +	int ret, flsz = 0; +	struct fw_iq_cmd c; +	struct port_info *pi = netdev_priv(dev); + +	/* Size needs to be multiple of 16, including status entry. */ +	iq->size = roundup(iq->size, 16); + +	iq->desc = alloc_ring(adap->pdev_dev, iq->size, iq->iqe_len, 0, +			      &iq->phys_addr, NULL, 0); +	if (!iq->desc) +		return -ENOMEM; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_IQ_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_CMD_EXEC | +			    FW_IQ_CMD_PFN(0) | FW_IQ_CMD_VFN(0)); +	c.alloc_to_len16 = htonl(FW_IQ_CMD_ALLOC | FW_IQ_CMD_IQSTART(1) | +				 FW_LEN16(c)); +	c.type_to_iqandstindex = htonl(FW_IQ_CMD_TYPE(FW_IQ_TYPE_FL_INT_CAP) | +		FW_IQ_CMD_IQASYNCH(fwevtq) | FW_IQ_CMD_VIID(pi->viid) | +		FW_IQ_CMD_IQANDST(intr_idx < 0) | FW_IQ_CMD_IQANUD(1) | +		FW_IQ_CMD_IQANDSTINDEX(intr_idx >= 0 ? intr_idx : +							-intr_idx - 1)); +	c.iqdroprss_to_iqesize = htons(FW_IQ_CMD_IQPCIECH(pi->tx_chan) | +		FW_IQ_CMD_IQGTSMODE | +		FW_IQ_CMD_IQINTCNTTHRESH(iq->pktcnt_idx) | +		FW_IQ_CMD_IQESIZE(ilog2(iq->iqe_len) - 4)); +	c.iqsize = htons(iq->size); +	c.iqaddr = cpu_to_be64(iq->phys_addr); + +	if (fl) { +		fl->size = roundup(fl->size, 8); +		fl->desc = alloc_ring(adap->pdev_dev, fl->size, sizeof(__be64), +				      sizeof(struct rx_sw_desc), &fl->addr, +				      &fl->sdesc, STAT_LEN); +		if (!fl->desc) +			goto fl_nomem; + +		flsz = fl->size / 8 + STAT_LEN / sizeof(struct tx_desc); +		c.iqns_to_fl0congen = htonl(FW_IQ_CMD_FL0PACKEN | +					    FW_IQ_CMD_FL0PADEN); +		c.fl0dcaen_to_fl0cidxfthresh = htons(FW_IQ_CMD_FL0FBMIN(2) | +				FW_IQ_CMD_FL0FBMAX(3)); +		c.fl0size = htons(flsz); +		c.fl0addr = cpu_to_be64(fl->addr); +	} + +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c); +	if (ret) +		goto err; + +	netif_napi_add(dev, &iq->napi, napi_rx_handler, 64); +	iq->cur_desc = iq->desc; +	iq->cidx = 0; +	iq->gen = 1; +	iq->next_intr_params = iq->intr_params; +	iq->cntxt_id = ntohs(c.iqid); +	iq->abs_id = ntohs(c.physiqid); +	iq->size--;                           /* subtract status entry */ +	iq->adap = adap; +	iq->netdev = dev; +	iq->handler = hnd; + +	/* set offset to -1 to distinguish ingress queues without FL */ +	iq->offset = fl ? 0 : -1; + +	adap->sge.ingr_map[iq->cntxt_id] = iq; + +	if (fl) { +		fl->cntxt_id = htons(c.fl0id); +		fl->avail = fl->pend_cred = 0; +		fl->pidx = fl->cidx = 0; +		fl->alloc_failed = fl->large_alloc_failed = fl->starving = 0; +		adap->sge.egr_map[fl->cntxt_id] = fl; +		refill_fl(adap, fl, fl_cap(fl), GFP_KERNEL); +	} +	return 0; + +fl_nomem: +	ret = -ENOMEM; +err: +	if (iq->desc) { +		dma_free_coherent(adap->pdev_dev, iq->size * iq->iqe_len, +				  iq->desc, iq->phys_addr); +		iq->desc = NULL; +	} +	if (fl && fl->desc) { +		kfree(fl->sdesc); +		fl->sdesc = NULL; +		dma_free_coherent(adap->pdev_dev, flsz * sizeof(struct tx_desc), +				  fl->desc, fl->addr); +		fl->desc = NULL; +	} +	return ret; +} + +static void init_txq(struct adapter *adap, struct sge_txq *q, unsigned int id) +{ +	q->in_use = 0; +	q->cidx = q->pidx = 0; +	q->stops = q->restarts = 0; +	q->stat = (void *)&q->desc[q->size]; +	q->cntxt_id = id; +	adap->sge.egr_map[id] = q; +} + +int t4_sge_alloc_eth_txq(struct adapter *adap, struct sge_eth_txq *txq, +			 struct net_device *dev, struct netdev_queue *netdevq, +			 unsigned int iqid) +{ +	int ret, nentries; +	struct fw_eq_eth_cmd c; +	struct port_info *pi = netdev_priv(dev); + +	/* Add status entries */ +	nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); + +	txq->q.desc = alloc_ring(adap->pdev_dev, txq->q.size, +			sizeof(struct tx_desc), sizeof(struct tx_sw_desc), +			&txq->q.phys_addr, &txq->q.sdesc, STAT_LEN); +	if (!txq->q.desc) +		return -ENOMEM; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_ETH_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_CMD_EXEC | +			    FW_EQ_ETH_CMD_PFN(0) | FW_EQ_ETH_CMD_VFN(0)); +	c.alloc_to_len16 = htonl(FW_EQ_ETH_CMD_ALLOC | +				 FW_EQ_ETH_CMD_EQSTART | FW_LEN16(c)); +	c.viid_pkd = htonl(FW_EQ_ETH_CMD_VIID(pi->viid)); +	c.fetchszm_to_iqid = htonl(FW_EQ_ETH_CMD_HOSTFCMODE(2) | +				   FW_EQ_ETH_CMD_PCIECHN(pi->tx_chan) | +				   FW_EQ_ETH_CMD_IQID(iqid)); +	c.dcaen_to_eqsize = htonl(FW_EQ_ETH_CMD_FBMIN(2) | +				  FW_EQ_ETH_CMD_FBMAX(3) | +				  FW_EQ_ETH_CMD_CIDXFTHRESH(5) | +				  FW_EQ_ETH_CMD_EQSIZE(nentries)); +	c.eqaddr = cpu_to_be64(txq->q.phys_addr); + +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c); +	if (ret) { +		kfree(txq->q.sdesc); +		txq->q.sdesc = NULL; +		dma_free_coherent(adap->pdev_dev, +				  nentries * sizeof(struct tx_desc), +				  txq->q.desc, txq->q.phys_addr); +		txq->q.desc = NULL; +		return ret; +	} + +	init_txq(adap, &txq->q, FW_EQ_ETH_CMD_EQID_GET(ntohl(c.eqid_pkd))); +	txq->txq = netdevq; +	txq->tso = txq->tx_cso = txq->vlan_ins = 0; +	txq->mapping_err = 0; +	return 0; +} + +int t4_sge_alloc_ctrl_txq(struct adapter *adap, struct sge_ctrl_txq *txq, +			  struct net_device *dev, unsigned int iqid, +			  unsigned int cmplqid) +{ +	int ret, nentries; +	struct fw_eq_ctrl_cmd c; +	struct port_info *pi = netdev_priv(dev); + +	/* Add status entries */ +	nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); + +	txq->q.desc = alloc_ring(adap->pdev_dev, nentries, +				 sizeof(struct tx_desc), 0, &txq->q.phys_addr, +				 NULL, 0); +	if (!txq->q.desc) +		return -ENOMEM; + +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_CTRL_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_CMD_EXEC | +			    FW_EQ_CTRL_CMD_PFN(0) | FW_EQ_CTRL_CMD_VFN(0)); +	c.alloc_to_len16 = htonl(FW_EQ_CTRL_CMD_ALLOC | +				 FW_EQ_CTRL_CMD_EQSTART | FW_LEN16(c)); +	c.cmpliqid_eqid = htonl(FW_EQ_CTRL_CMD_CMPLIQID(cmplqid)); +	c.physeqid_pkd = htonl(0); +	c.fetchszm_to_iqid = htonl(FW_EQ_CTRL_CMD_HOSTFCMODE(2) | +				   FW_EQ_CTRL_CMD_PCIECHN(pi->tx_chan) | +				   FW_EQ_CTRL_CMD_IQID(iqid)); +	c.dcaen_to_eqsize = htonl(FW_EQ_CTRL_CMD_FBMIN(2) | +				  FW_EQ_CTRL_CMD_FBMAX(3) | +				  FW_EQ_CTRL_CMD_CIDXFTHRESH(5) | +				  FW_EQ_CTRL_CMD_EQSIZE(nentries)); +	c.eqaddr = cpu_to_be64(txq->q.phys_addr); + +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c); +	if (ret) { +		dma_free_coherent(adap->pdev_dev, +				  nentries * sizeof(struct tx_desc), +				  txq->q.desc, txq->q.phys_addr); +		txq->q.desc = NULL; +		return ret; +	} + +	init_txq(adap, &txq->q, FW_EQ_CTRL_CMD_EQID_GET(ntohl(c.cmpliqid_eqid))); +	txq->adap = adap; +	skb_queue_head_init(&txq->sendq); +	tasklet_init(&txq->qresume_tsk, restart_ctrlq, (unsigned long)txq); +	txq->full = 0; +	return 0; +} + +int t4_sge_alloc_ofld_txq(struct adapter *adap, struct sge_ofld_txq *txq, +			  struct net_device *dev, unsigned int iqid) +{ +	int ret, nentries; +	struct fw_eq_ofld_cmd c; +	struct port_info *pi = netdev_priv(dev); + +	/* Add status entries */ +	nentries = txq->q.size + STAT_LEN / sizeof(struct tx_desc); + +	txq->q.desc = alloc_ring(adap->pdev_dev, txq->q.size, +			sizeof(struct tx_desc), sizeof(struct tx_sw_desc), +			&txq->q.phys_addr, &txq->q.sdesc, STAT_LEN); +	if (!txq->q.desc) +		return -ENOMEM; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_OFLD_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_CMD_EXEC | +			    FW_EQ_OFLD_CMD_PFN(0) | FW_EQ_OFLD_CMD_VFN(0)); +	c.alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_ALLOC | +				 FW_EQ_OFLD_CMD_EQSTART | FW_LEN16(c)); +	c.fetchszm_to_iqid = htonl(FW_EQ_OFLD_CMD_HOSTFCMODE(2) | +				   FW_EQ_OFLD_CMD_PCIECHN(pi->tx_chan) | +				   FW_EQ_OFLD_CMD_IQID(iqid)); +	c.dcaen_to_eqsize = htonl(FW_EQ_OFLD_CMD_FBMIN(2) | +				  FW_EQ_OFLD_CMD_FBMAX(3) | +				  FW_EQ_OFLD_CMD_CIDXFTHRESH(5) | +				  FW_EQ_OFLD_CMD_EQSIZE(nentries)); +	c.eqaddr = cpu_to_be64(txq->q.phys_addr); + +	ret = t4_wr_mbox(adap, 0, &c, sizeof(c), &c); +	if (ret) { +		kfree(txq->q.sdesc); +		txq->q.sdesc = NULL; +		dma_free_coherent(adap->pdev_dev, +				  nentries * sizeof(struct tx_desc), +				  txq->q.desc, txq->q.phys_addr); +		txq->q.desc = NULL; +		return ret; +	} + +	init_txq(adap, &txq->q, FW_EQ_OFLD_CMD_EQID_GET(ntohl(c.eqid_pkd))); +	txq->adap = adap; +	skb_queue_head_init(&txq->sendq); +	tasklet_init(&txq->qresume_tsk, restart_ofldq, (unsigned long)txq); +	txq->full = 0; +	txq->mapping_err = 0; +	return 0; +} + +static void free_txq(struct adapter *adap, struct sge_txq *q) +{ +	dma_free_coherent(adap->pdev_dev, +			  q->size * sizeof(struct tx_desc) + STAT_LEN, +			  q->desc, q->phys_addr); +	q->cntxt_id = 0; +	q->sdesc = NULL; +	q->desc = NULL; +} + +static void free_rspq_fl(struct adapter *adap, struct sge_rspq *rq, +			 struct sge_fl *fl) +{ +	unsigned int fl_id = fl ? fl->cntxt_id : 0xffff; + +	adap->sge.ingr_map[rq->cntxt_id] = NULL; +	t4_iq_free(adap, 0, 0, 0, FW_IQ_TYPE_FL_INT_CAP, rq->cntxt_id, fl_id, +		   0xffff); +	dma_free_coherent(adap->pdev_dev, (rq->size + 1) * rq->iqe_len, +			  rq->desc, rq->phys_addr); +	netif_napi_del(&rq->napi); +	rq->netdev = NULL; +	rq->cntxt_id = rq->abs_id = 0; +	rq->desc = NULL; + +	if (fl) { +		free_rx_bufs(adap, fl, fl->avail); +		dma_free_coherent(adap->pdev_dev, fl->size * 8 + STAT_LEN, +				  fl->desc, fl->addr); +		kfree(fl->sdesc); +		fl->sdesc = NULL; +		fl->cntxt_id = 0; +		fl->desc = NULL; +	} +} + +/** + *	t4_free_sge_resources - free SGE resources + *	@adap: the adapter + * + *	Frees resources used by the SGE queue sets. + */ +void t4_free_sge_resources(struct adapter *adap) +{ +	int i; +	struct sge_eth_rxq *eq = adap->sge.ethrxq; +	struct sge_eth_txq *etq = adap->sge.ethtxq; +	struct sge_ofld_rxq *oq = adap->sge.ofldrxq; + +	/* clean up Ethernet Tx/Rx queues */ +	for (i = 0; i < adap->sge.ethqsets; i++, eq++, etq++) { +		if (eq->rspq.desc) +			free_rspq_fl(adap, &eq->rspq, &eq->fl); +		if (etq->q.desc) { +			t4_eth_eq_free(adap, 0, 0, 0, etq->q.cntxt_id); +			free_tx_desc(adap, &etq->q, etq->q.in_use, true); +			kfree(etq->q.sdesc); +			free_txq(adap, &etq->q); +		} +	} + +	/* clean up RDMA and iSCSI Rx queues */ +	for (i = 0; i < adap->sge.ofldqsets; i++, oq++) { +		if (oq->rspq.desc) +			free_rspq_fl(adap, &oq->rspq, &oq->fl); +	} +	for (i = 0, oq = adap->sge.rdmarxq; i < adap->sge.rdmaqs; i++, oq++) { +		if (oq->rspq.desc) +			free_rspq_fl(adap, &oq->rspq, &oq->fl); +	} + +	/* clean up offload Tx queues */ +	for (i = 0; i < ARRAY_SIZE(adap->sge.ofldtxq); i++) { +		struct sge_ofld_txq *q = &adap->sge.ofldtxq[i]; + +		if (q->q.desc) { +			tasklet_kill(&q->qresume_tsk); +			t4_ofld_eq_free(adap, 0, 0, 0, q->q.cntxt_id); +			free_tx_desc(adap, &q->q, q->q.in_use, false); +			kfree(q->q.sdesc); +			__skb_queue_purge(&q->sendq); +			free_txq(adap, &q->q); +		} +	} + +	/* clean up control Tx queues */ +	for (i = 0; i < ARRAY_SIZE(adap->sge.ctrlq); i++) { +		struct sge_ctrl_txq *cq = &adap->sge.ctrlq[i]; + +		if (cq->q.desc) { +			tasklet_kill(&cq->qresume_tsk); +			t4_ctrl_eq_free(adap, 0, 0, 0, cq->q.cntxt_id); +			__skb_queue_purge(&cq->sendq); +			free_txq(adap, &cq->q); +		} +	} + +	if (adap->sge.fw_evtq.desc) +		free_rspq_fl(adap, &adap->sge.fw_evtq, NULL); + +	if (adap->sge.intrq.desc) +		free_rspq_fl(adap, &adap->sge.intrq, NULL); + +	/* clear the reverse egress queue map */ +	memset(adap->sge.egr_map, 0, sizeof(adap->sge.egr_map)); +} + +void t4_sge_start(struct adapter *adap) +{ +	adap->sge.ethtxq_rover = 0; +	mod_timer(&adap->sge.rx_timer, jiffies + RX_QCHECK_PERIOD); +	mod_timer(&adap->sge.tx_timer, jiffies + TX_QCHECK_PERIOD); +} + +/** + *	t4_sge_stop - disable SGE operation + *	@adap: the adapter + * + *	Stop tasklets and timers associated with the DMA engine.  Note that + *	this is effective only if measures have been taken to disable any HW + *	events that may restart them. + */ +void t4_sge_stop(struct adapter *adap) +{ +	int i; +	struct sge *s = &adap->sge; + +	if (in_interrupt())  /* actions below require waiting */ +		return; + +	if (s->rx_timer.function) +		del_timer_sync(&s->rx_timer); +	if (s->tx_timer.function) +		del_timer_sync(&s->tx_timer); + +	for (i = 0; i < ARRAY_SIZE(s->ofldtxq); i++) { +		struct sge_ofld_txq *q = &s->ofldtxq[i]; + +		if (q->q.desc) +			tasklet_kill(&q->qresume_tsk); +	} +	for (i = 0; i < ARRAY_SIZE(s->ctrlq); i++) { +		struct sge_ctrl_txq *cq = &s->ctrlq[i]; + +		if (cq->q.desc) +			tasklet_kill(&cq->qresume_tsk); +	} +} + +/** + *	t4_sge_init - initialize SGE + *	@adap: the adapter + * + *	Performs SGE initialization needed every time after a chip reset. + *	We do not initialize any of the queues here, instead the driver + *	top-level must request them individually. + */ +void t4_sge_init(struct adapter *adap) +{ +	struct sge *s = &adap->sge; +	unsigned int fl_align_log = ilog2(FL_ALIGN); + +	t4_set_reg_field(adap, SGE_CONTROL, PKTSHIFT_MASK | +			 INGPADBOUNDARY_MASK | EGRSTATUSPAGESIZE, +			 INGPADBOUNDARY(fl_align_log - 5) | PKTSHIFT(2) | +			 RXPKTCPLMODE | +			 (STAT_LEN == 128 ? EGRSTATUSPAGESIZE : 0)); +	t4_set_reg_field(adap, SGE_HOST_PAGE_SIZE, HOSTPAGESIZEPF0_MASK, +			 HOSTPAGESIZEPF0(PAGE_SHIFT - 10)); +	t4_write_reg(adap, SGE_FL_BUFFER_SIZE0, PAGE_SIZE); +#if FL_PG_ORDER > 0 +	t4_write_reg(adap, SGE_FL_BUFFER_SIZE1, PAGE_SIZE << FL_PG_ORDER); +#endif +	t4_write_reg(adap, SGE_INGRESS_RX_THRESHOLD, +		     THRESHOLD_0(s->counter_val[0]) | +		     THRESHOLD_1(s->counter_val[1]) | +		     THRESHOLD_2(s->counter_val[2]) | +		     THRESHOLD_3(s->counter_val[3])); +	t4_write_reg(adap, SGE_TIMER_VALUE_0_AND_1, +		     TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[0])) | +		     TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[1]))); +	t4_write_reg(adap, SGE_TIMER_VALUE_2_AND_3, +		     TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[2])) | +		     TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[3]))); +	t4_write_reg(adap, SGE_TIMER_VALUE_4_AND_5, +		     TIMERVALUE0(us_to_core_ticks(adap, s->timer_val[4])) | +		     TIMERVALUE1(us_to_core_ticks(adap, s->timer_val[5]))); +	setup_timer(&s->rx_timer, sge_rx_timer_cb, (unsigned long)adap); +	setup_timer(&s->tx_timer, sge_tx_timer_cb, (unsigned long)adap); +	s->starve_thres = core_ticks_per_usec(adap) * 1000000;  /* 1 s */ +	s->idma_state[0] = s->idma_state[1] = 0; +	spin_lock_init(&s->intrq_lock); +} diff --git a/drivers/net/cxgb4/t4_hw.c b/drivers/net/cxgb4/t4_hw.c new file mode 100644 index 00000000000..a814a3afe12 --- /dev/null +++ b/drivers/net/cxgb4/t4_hw.c @@ -0,0 +1,3131 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include "cxgb4.h" +#include "t4_regs.h" +#include "t4fw_api.h" + +/** + *	t4_wait_op_done_val - wait until an operation is completed + *	@adapter: the adapter performing the operation + *	@reg: the register to check for completion + *	@mask: a single-bit field within @reg that indicates completion + *	@polarity: the value of the field when the operation is completed + *	@attempts: number of check iterations + *	@delay: delay in usecs between iterations + *	@valp: where to store the value of the register at completion time + * + *	Wait until an operation is completed by checking a bit in a register + *	up to @attempts times.  If @valp is not NULL the value of the register + *	at the time it indicated completion is stored there.  Returns 0 if the + *	operation completes and	-EAGAIN	otherwise. + */ +int t4_wait_op_done_val(struct adapter *adapter, int reg, u32 mask, +			int polarity, int attempts, int delay, u32 *valp) +{ +	while (1) { +		u32 val = t4_read_reg(adapter, reg); + +		if (!!(val & mask) == polarity) { +			if (valp) +				*valp = val; +			return 0; +		} +		if (--attempts == 0) +			return -EAGAIN; +		if (delay) +			udelay(delay); +	} +} + +static inline int t4_wait_op_done(struct adapter *adapter, int reg, u32 mask, +				  int polarity, int attempts, int delay) +{ +	return t4_wait_op_done_val(adapter, reg, mask, polarity, attempts, +				   delay, NULL); +} + +/** + *	t4_set_reg_field - set a register field to a value + *	@adapter: the adapter to program + *	@addr: the register address + *	@mask: specifies the portion of the register to modify + *	@val: the new value for the register field + * + *	Sets a register field specified by the supplied mask to the + *	given value. + */ +void t4_set_reg_field(struct adapter *adapter, unsigned int addr, u32 mask, +		      u32 val) +{ +	u32 v = t4_read_reg(adapter, addr) & ~mask; + +	t4_write_reg(adapter, addr, v | val); +	(void) t4_read_reg(adapter, addr);      /* flush */ +} + +/** + *	t4_read_indirect - read indirectly addressed registers + *	@adap: the adapter + *	@addr_reg: register holding the indirect address + *	@data_reg: register holding the value of the indirect register + *	@vals: where the read register values are stored + *	@nregs: how many indirect registers to read + *	@start_idx: index of first indirect register to read + * + *	Reads registers that are accessed indirectly through an address/data + *	register pair. + */ +void t4_read_indirect(struct adapter *adap, unsigned int addr_reg, +		      unsigned int data_reg, u32 *vals, unsigned int nregs, +		      unsigned int start_idx) +{ +	while (nregs--) { +		t4_write_reg(adap, addr_reg, start_idx); +		*vals++ = t4_read_reg(adap, data_reg); +		start_idx++; +	} +} + +/** + *	t4_write_indirect - write indirectly addressed registers + *	@adap: the adapter + *	@addr_reg: register holding the indirect addresses + *	@data_reg: register holding the value for the indirect registers + *	@vals: values to write + *	@nregs: how many indirect registers to write + *	@start_idx: address of first indirect register to write + * + *	Writes a sequential block of registers that are accessed indirectly + *	through an address/data register pair. + */ +void t4_write_indirect(struct adapter *adap, unsigned int addr_reg, +		       unsigned int data_reg, const u32 *vals, +		       unsigned int nregs, unsigned int start_idx) +{ +	while (nregs--) { +		t4_write_reg(adap, addr_reg, start_idx++); +		t4_write_reg(adap, data_reg, *vals++); +	} +} + +/* + * Get the reply to a mailbox command and store it in @rpl in big-endian order. + */ +static void get_mbox_rpl(struct adapter *adap, __be64 *rpl, int nflit, +			 u32 mbox_addr) +{ +	for ( ; nflit; nflit--, mbox_addr += 8) +		*rpl++ = cpu_to_be64(t4_read_reg64(adap, mbox_addr)); +} + +/* + * Handle a FW assertion reported in a mailbox. + */ +static void fw_asrt(struct adapter *adap, u32 mbox_addr) +{ +	struct fw_debug_cmd asrt; + +	get_mbox_rpl(adap, (__be64 *)&asrt, sizeof(asrt) / 8, mbox_addr); +	dev_alert(adap->pdev_dev, +		  "FW assertion at %.16s:%u, val0 %#x, val1 %#x\n", +		  asrt.u.assert.filename_0_7, ntohl(asrt.u.assert.line), +		  ntohl(asrt.u.assert.x), ntohl(asrt.u.assert.y)); +} + +static void dump_mbox(struct adapter *adap, int mbox, u32 data_reg) +{ +	dev_err(adap->pdev_dev, +		"mbox %d: %llx %llx %llx %llx %llx %llx %llx %llx\n", mbox, +		(unsigned long long)t4_read_reg64(adap, data_reg), +		(unsigned long long)t4_read_reg64(adap, data_reg + 8), +		(unsigned long long)t4_read_reg64(adap, data_reg + 16), +		(unsigned long long)t4_read_reg64(adap, data_reg + 24), +		(unsigned long long)t4_read_reg64(adap, data_reg + 32), +		(unsigned long long)t4_read_reg64(adap, data_reg + 40), +		(unsigned long long)t4_read_reg64(adap, data_reg + 48), +		(unsigned long long)t4_read_reg64(adap, data_reg + 56)); +} + +/** + *	t4_wr_mbox_meat - send a command to FW through the given mailbox + *	@adap: the adapter + *	@mbox: index of the mailbox to use + *	@cmd: the command to write + *	@size: command length in bytes + *	@rpl: where to optionally store the reply + *	@sleep_ok: if true we may sleep while awaiting command completion + * + *	Sends the given command to FW through the selected mailbox and waits + *	for the FW to execute the command.  If @rpl is not %NULL it is used to + *	store the FW's reply to the command.  The command and its optional + *	reply are of the same length.  FW can take up to %FW_CMD_MAX_TIMEOUT ms + *	to respond.  @sleep_ok determines whether we may sleep while awaiting + *	the response.  If sleeping is allowed we use progressive backoff + *	otherwise we spin. + * + *	The return value is 0 on success or a negative errno on failure.  A + *	failure can happen either because we are not able to execute the + *	command or FW executes it but signals an error.  In the latter case + *	the return value is the error code indicated by FW (negated). + */ +int t4_wr_mbox_meat(struct adapter *adap, int mbox, const void *cmd, int size, +		    void *rpl, bool sleep_ok) +{ +	static int delay[] = { +		1, 1, 3, 5, 10, 10, 20, 50, 100, 200 +	}; + +	u32 v; +	u64 res; +	int i, ms, delay_idx; +	const __be64 *p = cmd; +	u32 data_reg = PF_REG(mbox, CIM_PF_MAILBOX_DATA); +	u32 ctl_reg = PF_REG(mbox, CIM_PF_MAILBOX_CTRL); + +	if ((size & 15) || size > MBOX_LEN) +		return -EINVAL; + +	v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); +	for (i = 0; v == MBOX_OWNER_NONE && i < 3; i++) +		v = MBOWNER_GET(t4_read_reg(adap, ctl_reg)); + +	if (v != MBOX_OWNER_DRV) +		return v ? -EBUSY : -ETIMEDOUT; + +	for (i = 0; i < size; i += 8) +		t4_write_reg64(adap, data_reg + i, be64_to_cpu(*p++)); + +	t4_write_reg(adap, ctl_reg, MBMSGVALID | MBOWNER(MBOX_OWNER_FW)); +	t4_read_reg(adap, ctl_reg);          /* flush write */ + +	delay_idx = 0; +	ms = delay[0]; + +	for (i = 0; i < FW_CMD_MAX_TIMEOUT; i += ms) { +		if (sleep_ok) { +			ms = delay[delay_idx];  /* last element may repeat */ +			if (delay_idx < ARRAY_SIZE(delay) - 1) +				delay_idx++; +			msleep(ms); +		} else +			mdelay(ms); + +		v = t4_read_reg(adap, ctl_reg); +		if (MBOWNER_GET(v) == MBOX_OWNER_DRV) { +			if (!(v & MBMSGVALID)) { +				t4_write_reg(adap, ctl_reg, 0); +				continue; +			} + +			res = t4_read_reg64(adap, data_reg); +			if (FW_CMD_OP_GET(res >> 32) == FW_DEBUG_CMD) { +				fw_asrt(adap, data_reg); +				res = FW_CMD_RETVAL(EIO); +			} else if (rpl) +				get_mbox_rpl(adap, rpl, size / 8, data_reg); + +			if (FW_CMD_RETVAL_GET((int)res)) +				dump_mbox(adap, mbox, data_reg); +			t4_write_reg(adap, ctl_reg, 0); +			return -FW_CMD_RETVAL_GET((int)res); +		} +	} + +	dump_mbox(adap, mbox, data_reg); +	dev_err(adap->pdev_dev, "command %#x in mailbox %d timed out\n", +		*(const u8 *)cmd, mbox); +	return -ETIMEDOUT; +} + +/** + *	t4_mc_read - read from MC through backdoor accesses + *	@adap: the adapter + *	@addr: address of first byte requested + *	@data: 64 bytes of data containing the requested address + *	@ecc: where to store the corresponding 64-bit ECC word + * + *	Read 64 bytes of data from MC starting at a 64-byte-aligned address + *	that covers the requested address @addr.  If @parity is not %NULL it + *	is assigned the 64-bit ECC word for the read data. + */ +int t4_mc_read(struct adapter *adap, u32 addr, __be32 *data, u64 *ecc) +{ +	int i; + +	if (t4_read_reg(adap, MC_BIST_CMD) & START_BIST) +		return -EBUSY; +	t4_write_reg(adap, MC_BIST_CMD_ADDR, addr & ~0x3fU); +	t4_write_reg(adap, MC_BIST_CMD_LEN, 64); +	t4_write_reg(adap, MC_BIST_DATA_PATTERN, 0xc); +	t4_write_reg(adap, MC_BIST_CMD, BIST_OPCODE(1) | START_BIST | +		     BIST_CMD_GAP(1)); +	i = t4_wait_op_done(adap, MC_BIST_CMD, START_BIST, 0, 10, 1); +	if (i) +		return i; + +#define MC_DATA(i) MC_BIST_STATUS_REG(MC_BIST_STATUS_RDATA, i) + +	for (i = 15; i >= 0; i--) +		*data++ = htonl(t4_read_reg(adap, MC_DATA(i))); +	if (ecc) +		*ecc = t4_read_reg64(adap, MC_DATA(16)); +#undef MC_DATA +	return 0; +} + +/** + *	t4_edc_read - read from EDC through backdoor accesses + *	@adap: the adapter + *	@idx: which EDC to access + *	@addr: address of first byte requested + *	@data: 64 bytes of data containing the requested address + *	@ecc: where to store the corresponding 64-bit ECC word + * + *	Read 64 bytes of data from EDC starting at a 64-byte-aligned address + *	that covers the requested address @addr.  If @parity is not %NULL it + *	is assigned the 64-bit ECC word for the read data. + */ +int t4_edc_read(struct adapter *adap, int idx, u32 addr, __be32 *data, u64 *ecc) +{ +	int i; + +	idx *= EDC_STRIDE; +	if (t4_read_reg(adap, EDC_BIST_CMD + idx) & START_BIST) +		return -EBUSY; +	t4_write_reg(adap, EDC_BIST_CMD_ADDR + idx, addr & ~0x3fU); +	t4_write_reg(adap, EDC_BIST_CMD_LEN + idx, 64); +	t4_write_reg(adap, EDC_BIST_DATA_PATTERN + idx, 0xc); +	t4_write_reg(adap, EDC_BIST_CMD + idx, +		     BIST_OPCODE(1) | BIST_CMD_GAP(1) | START_BIST); +	i = t4_wait_op_done(adap, EDC_BIST_CMD + idx, START_BIST, 0, 10, 1); +	if (i) +		return i; + +#define EDC_DATA(i) (EDC_BIST_STATUS_REG(EDC_BIST_STATUS_RDATA, i) + idx) + +	for (i = 15; i >= 0; i--) +		*data++ = htonl(t4_read_reg(adap, EDC_DATA(i))); +	if (ecc) +		*ecc = t4_read_reg64(adap, EDC_DATA(16)); +#undef EDC_DATA +	return 0; +} + +#define VPD_ENTRY(name, len) \ +	u8 name##_kword[2]; u8 name##_len; u8 name##_data[len] + +/* + * Partial EEPROM Vital Product Data structure.  Includes only the ID and + * VPD-R sections. + */ +struct t4_vpd { +	u8  id_tag; +	u8  id_len[2]; +	u8  id_data[ID_LEN]; +	u8  vpdr_tag; +	u8  vpdr_len[2]; +	VPD_ENTRY(pn, 16);                     /* part number */ +	VPD_ENTRY(ec, EC_LEN);                 /* EC level */ +	VPD_ENTRY(sn, SERNUM_LEN);             /* serial number */ +	VPD_ENTRY(na, 12);                     /* MAC address base */ +	VPD_ENTRY(port_type, 8);               /* port types */ +	VPD_ENTRY(gpio, 14);                   /* GPIO usage */ +	VPD_ENTRY(cclk, 6);                    /* core clock */ +	VPD_ENTRY(port_addr, 8);               /* port MDIO addresses */ +	VPD_ENTRY(rv, 1);                      /* csum */ +	u32 pad;                  /* for multiple-of-4 sizing and alignment */ +}; + +#define EEPROM_STAT_ADDR   0x7bfc +#define VPD_BASE           0 + +/** + *	t4_seeprom_wp - enable/disable EEPROM write protection + *	@adapter: the adapter + *	@enable: whether to enable or disable write protection + * + *	Enables or disables write protection on the serial EEPROM. + */ +int t4_seeprom_wp(struct adapter *adapter, bool enable) +{ +	unsigned int v = enable ? 0xc : 0; +	int ret = pci_write_vpd(adapter->pdev, EEPROM_STAT_ADDR, 4, &v); +	return ret < 0 ? ret : 0; +} + +/** + *	get_vpd_params - read VPD parameters from VPD EEPROM + *	@adapter: adapter to read + *	@p: where to store the parameters + * + *	Reads card parameters stored in VPD EEPROM. + */ +static int get_vpd_params(struct adapter *adapter, struct vpd_params *p) +{ +	int ret; +	struct t4_vpd vpd; +	u8 *q = (u8 *)&vpd, csum; + +	ret = pci_read_vpd(adapter->pdev, VPD_BASE, sizeof(vpd), &vpd); +	if (ret < 0) +		return ret; + +	for (csum = 0; q <= vpd.rv_data; q++) +		csum += *q; + +	if (csum) { +		dev_err(adapter->pdev_dev, +			"corrupted VPD EEPROM, actual csum %u\n", csum); +		return -EINVAL; +	} + +	p->cclk = simple_strtoul(vpd.cclk_data, NULL, 10); +	memcpy(p->id, vpd.id_data, sizeof(vpd.id_data)); +	strim(p->id); +	memcpy(p->ec, vpd.ec_data, sizeof(vpd.ec_data)); +	strim(p->ec); +	memcpy(p->sn, vpd.sn_data, sizeof(vpd.sn_data)); +	strim(p->sn); +	return 0; +} + +/* serial flash and firmware constants */ +enum { +	SF_ATTEMPTS = 10,             /* max retries for SF operations */ + +	/* flash command opcodes */ +	SF_PROG_PAGE    = 2,          /* program page */ +	SF_WR_DISABLE   = 4,          /* disable writes */ +	SF_RD_STATUS    = 5,          /* read status register */ +	SF_WR_ENABLE    = 6,          /* enable writes */ +	SF_RD_DATA_FAST = 0xb,        /* read flash */ +	SF_ERASE_SECTOR = 0xd8,       /* erase sector */ + +	FW_START_SEC = 8,             /* first flash sector for FW */ +	FW_END_SEC = 15,              /* last flash sector for FW */ +	FW_IMG_START = FW_START_SEC * SF_SEC_SIZE, +	FW_MAX_SIZE = (FW_END_SEC - FW_START_SEC + 1) * SF_SEC_SIZE, +}; + +/** + *	sf1_read - read data from the serial flash + *	@adapter: the adapter + *	@byte_cnt: number of bytes to read + *	@cont: whether another operation will be chained + *	@lock: whether to lock SF for PL access only + *	@valp: where to store the read data + * + *	Reads up to 4 bytes of data from the serial flash.  The location of + *	the read needs to be specified prior to calling this by issuing the + *	appropriate commands to the serial flash. + */ +static int sf1_read(struct adapter *adapter, unsigned int byte_cnt, int cont, +		    int lock, u32 *valp) +{ +	int ret; + +	if (!byte_cnt || byte_cnt > 4) +		return -EINVAL; +	if (t4_read_reg(adapter, SF_OP) & BUSY) +		return -EBUSY; +	cont = cont ? SF_CONT : 0; +	lock = lock ? SF_LOCK : 0; +	t4_write_reg(adapter, SF_OP, lock | cont | BYTECNT(byte_cnt - 1)); +	ret = t4_wait_op_done(adapter, SF_OP, BUSY, 0, SF_ATTEMPTS, 5); +	if (!ret) +		*valp = t4_read_reg(adapter, SF_DATA); +	return ret; +} + +/** + *	sf1_write - write data to the serial flash + *	@adapter: the adapter + *	@byte_cnt: number of bytes to write + *	@cont: whether another operation will be chained + *	@lock: whether to lock SF for PL access only + *	@val: value to write + * + *	Writes up to 4 bytes of data to the serial flash.  The location of + *	the write needs to be specified prior to calling this by issuing the + *	appropriate commands to the serial flash. + */ +static int sf1_write(struct adapter *adapter, unsigned int byte_cnt, int cont, +		     int lock, u32 val) +{ +	if (!byte_cnt || byte_cnt > 4) +		return -EINVAL; +	if (t4_read_reg(adapter, SF_OP) & BUSY) +		return -EBUSY; +	cont = cont ? SF_CONT : 0; +	lock = lock ? SF_LOCK : 0; +	t4_write_reg(adapter, SF_DATA, val); +	t4_write_reg(adapter, SF_OP, lock | +		     cont | BYTECNT(byte_cnt - 1) | OP_WR); +	return t4_wait_op_done(adapter, SF_OP, BUSY, 0, SF_ATTEMPTS, 5); +} + +/** + *	flash_wait_op - wait for a flash operation to complete + *	@adapter: the adapter + *	@attempts: max number of polls of the status register + *	@delay: delay between polls in ms + * + *	Wait for a flash operation to complete by polling the status register. + */ +static int flash_wait_op(struct adapter *adapter, int attempts, int delay) +{ +	int ret; +	u32 status; + +	while (1) { +		if ((ret = sf1_write(adapter, 1, 1, 1, SF_RD_STATUS)) != 0 || +		    (ret = sf1_read(adapter, 1, 0, 1, &status)) != 0) +			return ret; +		if (!(status & 1)) +			return 0; +		if (--attempts == 0) +			return -EAGAIN; +		if (delay) +			msleep(delay); +	} +} + +/** + *	t4_read_flash - read words from serial flash + *	@adapter: the adapter + *	@addr: the start address for the read + *	@nwords: how many 32-bit words to read + *	@data: where to store the read data + *	@byte_oriented: whether to store data as bytes or as words + * + *	Read the specified number of 32-bit words from the serial flash. + *	If @byte_oriented is set the read data is stored as a byte array + *	(i.e., big-endian), otherwise as 32-bit words in the platform's + *	natural endianess. + */ +int t4_read_flash(struct adapter *adapter, unsigned int addr, +		  unsigned int nwords, u32 *data, int byte_oriented) +{ +	int ret; + +	if (addr + nwords * sizeof(u32) > SF_SIZE || (addr & 3)) +		return -EINVAL; + +	addr = swab32(addr) | SF_RD_DATA_FAST; + +	if ((ret = sf1_write(adapter, 4, 1, 0, addr)) != 0 || +	    (ret = sf1_read(adapter, 1, 1, 0, data)) != 0) +		return ret; + +	for ( ; nwords; nwords--, data++) { +		ret = sf1_read(adapter, 4, nwords > 1, nwords == 1, data); +		if (nwords == 1) +			t4_write_reg(adapter, SF_OP, 0);    /* unlock SF */ +		if (ret) +			return ret; +		if (byte_oriented) +			*data = htonl(*data); +	} +	return 0; +} + +/** + *	t4_write_flash - write up to a page of data to the serial flash + *	@adapter: the adapter + *	@addr: the start address to write + *	@n: length of data to write in bytes + *	@data: the data to write + * + *	Writes up to a page of data (256 bytes) to the serial flash starting + *	at the given address.  All the data must be written to the same page. + */ +static int t4_write_flash(struct adapter *adapter, unsigned int addr, +			  unsigned int n, const u8 *data) +{ +	int ret; +	u32 buf[64]; +	unsigned int i, c, left, val, offset = addr & 0xff; + +	if (addr >= SF_SIZE || offset + n > SF_PAGE_SIZE) +		return -EINVAL; + +	val = swab32(addr) | SF_PROG_PAGE; + +	if ((ret = sf1_write(adapter, 1, 0, 1, SF_WR_ENABLE)) != 0 || +	    (ret = sf1_write(adapter, 4, 1, 1, val)) != 0) +		goto unlock; + +	for (left = n; left; left -= c) { +		c = min(left, 4U); +		for (val = 0, i = 0; i < c; ++i) +			val = (val << 8) + *data++; + +		ret = sf1_write(adapter, c, c != left, 1, val); +		if (ret) +			goto unlock; +	} +	ret = flash_wait_op(adapter, 5, 1); +	if (ret) +		goto unlock; + +	t4_write_reg(adapter, SF_OP, 0);    /* unlock SF */ + +	/* Read the page to verify the write succeeded */ +	ret = t4_read_flash(adapter, addr & ~0xff, ARRAY_SIZE(buf), buf, 1); +	if (ret) +		return ret; + +	if (memcmp(data - n, (u8 *)buf + offset, n)) { +		dev_err(adapter->pdev_dev, +			"failed to correctly write the flash page at %#x\n", +			addr); +		return -EIO; +	} +	return 0; + +unlock: +	t4_write_reg(adapter, SF_OP, 0);    /* unlock SF */ +	return ret; +} + +/** + *	get_fw_version - read the firmware version + *	@adapter: the adapter + *	@vers: where to place the version + * + *	Reads the FW version from flash. + */ +static int get_fw_version(struct adapter *adapter, u32 *vers) +{ +	return t4_read_flash(adapter, +			     FW_IMG_START + offsetof(struct fw_hdr, fw_ver), 1, +			     vers, 0); +} + +/** + *	get_tp_version - read the TP microcode version + *	@adapter: the adapter + *	@vers: where to place the version + * + *	Reads the TP microcode version from flash. + */ +static int get_tp_version(struct adapter *adapter, u32 *vers) +{ +	return t4_read_flash(adapter, FW_IMG_START + offsetof(struct fw_hdr, +							      tp_microcode_ver), +			     1, vers, 0); +} + +/** + *	t4_check_fw_version - check if the FW is compatible with this driver + *	@adapter: the adapter + * + *	Checks if an adapter's FW is compatible with the driver.  Returns 0 + *	if there's exact match, a negative error if the version could not be + *	read or there's a major version mismatch, and a positive value if the + *	expected major version is found but there's a minor version mismatch. + */ +int t4_check_fw_version(struct adapter *adapter) +{ +	u32 api_vers[2]; +	int ret, major, minor, micro; + +	ret = get_fw_version(adapter, &adapter->params.fw_vers); +	if (!ret) +		ret = get_tp_version(adapter, &adapter->params.tp_vers); +	if (!ret) +		ret = t4_read_flash(adapter, +			FW_IMG_START + offsetof(struct fw_hdr, intfver_nic), +			2, api_vers, 1); +	if (ret) +		return ret; + +	major = FW_HDR_FW_VER_MAJOR_GET(adapter->params.fw_vers); +	minor = FW_HDR_FW_VER_MINOR_GET(adapter->params.fw_vers); +	micro = FW_HDR_FW_VER_MICRO_GET(adapter->params.fw_vers); +	memcpy(adapter->params.api_vers, api_vers, +	       sizeof(adapter->params.api_vers)); + +	if (major != FW_VERSION_MAJOR) {            /* major mismatch - fail */ +		dev_err(adapter->pdev_dev, +			"card FW has major version %u, driver wants %u\n", +			major, FW_VERSION_MAJOR); +		return -EINVAL; +	} + +	if (minor == FW_VERSION_MINOR && micro == FW_VERSION_MICRO) +		return 0;                                   /* perfect match */ + +	/* Minor/micro version mismatch.  Report it but often it's OK. */ +	return 1; +} + +/** + *	t4_flash_erase_sectors - erase a range of flash sectors + *	@adapter: the adapter + *	@start: the first sector to erase + *	@end: the last sector to erase + * + *	Erases the sectors in the given inclusive range. + */ +static int t4_flash_erase_sectors(struct adapter *adapter, int start, int end) +{ +	int ret = 0; + +	while (start <= end) { +		if ((ret = sf1_write(adapter, 1, 0, 1, SF_WR_ENABLE)) != 0 || +		    (ret = sf1_write(adapter, 4, 0, 1, +				     SF_ERASE_SECTOR | (start << 8))) != 0 || +		    (ret = flash_wait_op(adapter, 5, 500)) != 0) { +			dev_err(adapter->pdev_dev, +				"erase of flash sector %d failed, error %d\n", +				start, ret); +			break; +		} +		start++; +	} +	t4_write_reg(adapter, SF_OP, 0);    /* unlock SF */ +	return ret; +} + +/** + *	t4_load_fw - download firmware + *	@adap: the adapter + *	@fw_data: the firmware image to write + *	@size: image size + * + *	Write the supplied firmware image to the card's serial flash. + */ +int t4_load_fw(struct adapter *adap, const u8 *fw_data, unsigned int size) +{ +	u32 csum; +	int ret, addr; +	unsigned int i; +	u8 first_page[SF_PAGE_SIZE]; +	const u32 *p = (const u32 *)fw_data; +	const struct fw_hdr *hdr = (const struct fw_hdr *)fw_data; + +	if (!size) { +		dev_err(adap->pdev_dev, "FW image has no data\n"); +		return -EINVAL; +	} +	if (size & 511) { +		dev_err(adap->pdev_dev, +			"FW image size not multiple of 512 bytes\n"); +		return -EINVAL; +	} +	if (ntohs(hdr->len512) * 512 != size) { +		dev_err(adap->pdev_dev, +			"FW image size differs from size in FW header\n"); +		return -EINVAL; +	} +	if (size > FW_MAX_SIZE) { +		dev_err(adap->pdev_dev, "FW image too large, max is %u bytes\n", +			FW_MAX_SIZE); +		return -EFBIG; +	} + +	for (csum = 0, i = 0; i < size / sizeof(csum); i++) +		csum += ntohl(p[i]); + +	if (csum != 0xffffffff) { +		dev_err(adap->pdev_dev, +			"corrupted firmware image, checksum %#x\n", csum); +		return -EINVAL; +	} + +	i = DIV_ROUND_UP(size, SF_SEC_SIZE);        /* # of sectors spanned */ +	ret = t4_flash_erase_sectors(adap, FW_START_SEC, FW_START_SEC + i - 1); +	if (ret) +		goto out; + +	/* +	 * We write the correct version at the end so the driver can see a bad +	 * version if the FW write fails.  Start by writing a copy of the +	 * first page with a bad version. +	 */ +	memcpy(first_page, fw_data, SF_PAGE_SIZE); +	((struct fw_hdr *)first_page)->fw_ver = htonl(0xffffffff); +	ret = t4_write_flash(adap, FW_IMG_START, SF_PAGE_SIZE, first_page); +	if (ret) +		goto out; + +	addr = FW_IMG_START; +	for (size -= SF_PAGE_SIZE; size; size -= SF_PAGE_SIZE) { +		addr += SF_PAGE_SIZE; +		fw_data += SF_PAGE_SIZE; +		ret = t4_write_flash(adap, addr, SF_PAGE_SIZE, fw_data); +		if (ret) +			goto out; +	} + +	ret = t4_write_flash(adap, +			     FW_IMG_START + offsetof(struct fw_hdr, fw_ver), +			     sizeof(hdr->fw_ver), (const u8 *)&hdr->fw_ver); +out: +	if (ret) +		dev_err(adap->pdev_dev, "firmware download failed, error %d\n", +			ret); +	return ret; +} + +#define ADVERT_MASK (FW_PORT_CAP_SPEED_100M | FW_PORT_CAP_SPEED_1G |\ +		     FW_PORT_CAP_SPEED_10G | FW_PORT_CAP_ANEG) + +/** + *	t4_link_start - apply link configuration to MAC/PHY + *	@phy: the PHY to setup + *	@mac: the MAC to setup + *	@lc: the requested link configuration + * + *	Set up a port's MAC and PHY according to a desired link configuration. + *	- If the PHY can auto-negotiate first decide what to advertise, then + *	  enable/disable auto-negotiation as desired, and reset. + *	- If the PHY does not auto-negotiate just reset it. + *	- If auto-negotiation is off set the MAC to the proper speed/duplex/FC, + *	  otherwise do it later based on the outcome of auto-negotiation. + */ +int t4_link_start(struct adapter *adap, unsigned int mbox, unsigned int port, +		  struct link_config *lc) +{ +	struct fw_port_cmd c; +	unsigned int fc = 0, mdi = FW_PORT_MDI(FW_PORT_MDI_AUTO); + +	lc->link_ok = 0; +	if (lc->requested_fc & PAUSE_RX) +		fc |= FW_PORT_CAP_FC_RX; +	if (lc->requested_fc & PAUSE_TX) +		fc |= FW_PORT_CAP_FC_TX; + +	memset(&c, 0, sizeof(c)); +	c.op_to_portid = htonl(FW_CMD_OP(FW_PORT_CMD) | FW_CMD_REQUEST | +			       FW_CMD_EXEC | FW_PORT_CMD_PORTID(port)); +	c.action_to_len16 = htonl(FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG) | +				  FW_LEN16(c)); + +	if (!(lc->supported & FW_PORT_CAP_ANEG)) { +		c.u.l1cfg.rcap = htonl((lc->supported & ADVERT_MASK) | fc); +		lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); +	} else if (lc->autoneg == AUTONEG_DISABLE) { +		c.u.l1cfg.rcap = htonl(lc->requested_speed | fc | mdi); +		lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); +	} else +		c.u.l1cfg.rcap = htonl(lc->advertising | fc | mdi); + +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_restart_aneg - restart autonegotiation + *	@adap: the adapter + *	@mbox: mbox to use for the FW command + *	@port: the port id + * + *	Restarts autonegotiation for the selected port. + */ +int t4_restart_aneg(struct adapter *adap, unsigned int mbox, unsigned int port) +{ +	struct fw_port_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_portid = htonl(FW_CMD_OP(FW_PORT_CMD) | FW_CMD_REQUEST | +			       FW_CMD_EXEC | FW_PORT_CMD_PORTID(port)); +	c.action_to_len16 = htonl(FW_PORT_CMD_ACTION(FW_PORT_ACTION_L1_CFG) | +				  FW_LEN16(c)); +	c.u.l1cfg.rcap = htonl(FW_PORT_CAP_ANEG); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_set_vlan_accel - configure HW VLAN extraction + *	@adap: the adapter + *	@ports: bitmap of adapter ports to operate on + *	@on: enable (1) or disable (0) HW VLAN extraction + * + *	Enables or disables HW extraction of VLAN tags for the ports specified + *	by @ports.  @ports is a bitmap with the ith bit designating the port + *	associated with the ith adapter channel. + */ +void t4_set_vlan_accel(struct adapter *adap, unsigned int ports, int on) +{ +	ports <<= VLANEXTENABLE_SHIFT; +	t4_set_reg_field(adap, TP_OUT_CONFIG, ports, on ? ports : 0); +} + +struct intr_info { +	unsigned int mask;       /* bits to check in interrupt status */ +	const char *msg;         /* message to print or NULL */ +	short stat_idx;          /* stat counter to increment or -1 */ +	unsigned short fatal;    /* whether the condition reported is fatal */ +}; + +/** + *	t4_handle_intr_status - table driven interrupt handler + *	@adapter: the adapter that generated the interrupt + *	@reg: the interrupt status register to process + *	@acts: table of interrupt actions + * + *	A table driven interrupt handler that applies a set of masks to an + *	interrupt status word and performs the corresponding actions if the + *	interrupts described by the mask have occured.  The actions include + *	optionally emitting a warning or alert message.  The table is terminated + *	by an entry specifying mask 0.  Returns the number of fatal interrupt + *	conditions. + */ +static int t4_handle_intr_status(struct adapter *adapter, unsigned int reg, +				 const struct intr_info *acts) +{ +	int fatal = 0; +	unsigned int mask = 0; +	unsigned int status = t4_read_reg(adapter, reg); + +	for ( ; acts->mask; ++acts) { +		if (!(status & acts->mask)) +			continue; +		if (acts->fatal) { +			fatal++; +			dev_alert(adapter->pdev_dev, "%s (0x%x)\n", acts->msg, +				  status & acts->mask); +		} else if (acts->msg && printk_ratelimit()) +			dev_warn(adapter->pdev_dev, "%s (0x%x)\n", acts->msg, +				 status & acts->mask); +		mask |= acts->mask; +	} +	status &= mask; +	if (status)                           /* clear processed interrupts */ +		t4_write_reg(adapter, reg, status); +	return fatal; +} + +/* + * Interrupt handler for the PCIE module. + */ +static void pcie_intr_handler(struct adapter *adapter) +{ +	static struct intr_info sysbus_intr_info[] = { +		{ RNPP, "RXNP array parity error", -1, 1 }, +		{ RPCP, "RXPC array parity error", -1, 1 }, +		{ RCIP, "RXCIF array parity error", -1, 1 }, +		{ RCCP, "Rx completions control array parity error", -1, 1 }, +		{ RFTP, "RXFT array parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info pcie_port_intr_info[] = { +		{ TPCP, "TXPC array parity error", -1, 1 }, +		{ TNPP, "TXNP array parity error", -1, 1 }, +		{ TFTP, "TXFT array parity error", -1, 1 }, +		{ TCAP, "TXCA array parity error", -1, 1 }, +		{ TCIP, "TXCIF array parity error", -1, 1 }, +		{ RCAP, "RXCA array parity error", -1, 1 }, +		{ OTDD, "outbound request TLP discarded", -1, 1 }, +		{ RDPE, "Rx data parity error", -1, 1 }, +		{ TDUE, "Tx uncorrectable data error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info pcie_intr_info[] = { +		{ MSIADDRLPERR, "MSI AddrL parity error", -1, 1 }, +		{ MSIADDRHPERR, "MSI AddrH parity error", -1, 1 }, +		{ MSIDATAPERR, "MSI data parity error", -1, 1 }, +		{ MSIXADDRLPERR, "MSI-X AddrL parity error", -1, 1 }, +		{ MSIXADDRHPERR, "MSI-X AddrH parity error", -1, 1 }, +		{ MSIXDATAPERR, "MSI-X data parity error", -1, 1 }, +		{ MSIXDIPERR, "MSI-X DI parity error", -1, 1 }, +		{ PIOCPLPERR, "PCI PIO completion FIFO parity error", -1, 1 }, +		{ PIOREQPERR, "PCI PIO request FIFO parity error", -1, 1 }, +		{ TARTAGPERR, "PCI PCI target tag FIFO parity error", -1, 1 }, +		{ CCNTPERR, "PCI CMD channel count parity error", -1, 1 }, +		{ CREQPERR, "PCI CMD channel request parity error", -1, 1 }, +		{ CRSPPERR, "PCI CMD channel response parity error", -1, 1 }, +		{ DCNTPERR, "PCI DMA channel count parity error", -1, 1 }, +		{ DREQPERR, "PCI DMA channel request parity error", -1, 1 }, +		{ DRSPPERR, "PCI DMA channel response parity error", -1, 1 }, +		{ HCNTPERR, "PCI HMA channel count parity error", -1, 1 }, +		{ HREQPERR, "PCI HMA channel request parity error", -1, 1 }, +		{ HRSPPERR, "PCI HMA channel response parity error", -1, 1 }, +		{ CFGSNPPERR, "PCI config snoop FIFO parity error", -1, 1 }, +		{ FIDPERR, "PCI FID parity error", -1, 1 }, +		{ INTXCLRPERR, "PCI INTx clear parity error", -1, 1 }, +		{ MATAGPERR, "PCI MA tag parity error", -1, 1 }, +		{ PIOTAGPERR, "PCI PIO tag parity error", -1, 1 }, +		{ RXCPLPERR, "PCI Rx completion parity error", -1, 1 }, +		{ RXWRPERR, "PCI Rx write parity error", -1, 1 }, +		{ RPLPERR, "PCI replay buffer parity error", -1, 1 }, +		{ PCIESINT, "PCI core secondary fault", -1, 1 }, +		{ PCIEPINT, "PCI core primary fault", -1, 1 }, +		{ UNXSPLCPLERR, "PCI unexpected split completion error", -1, 0 }, +		{ 0 } +	}; + +	int fat; + +	fat = t4_handle_intr_status(adapter, +				    PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, +				    sysbus_intr_info) + +	      t4_handle_intr_status(adapter, +				    PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, +				    pcie_port_intr_info) + +	      t4_handle_intr_status(adapter, PCIE_INT_CAUSE, pcie_intr_info); +	if (fat) +		t4_fatal_err(adapter); +} + +/* + * TP interrupt handler. + */ +static void tp_intr_handler(struct adapter *adapter) +{ +	static struct intr_info tp_intr_info[] = { +		{ 0x3fffffff, "TP parity error", -1, 1 }, +		{ FLMTXFLSTEMPTY, "TP out of Tx pages", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, TP_INT_CAUSE, tp_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * SGE interrupt handler. + */ +static void sge_intr_handler(struct adapter *adapter) +{ +	u64 v; + +	static struct intr_info sge_intr_info[] = { +		{ ERR_CPL_EXCEED_IQE_SIZE, +		  "SGE received CPL exceeding IQE size", -1, 1 }, +		{ ERR_INVALID_CIDX_INC, +		  "SGE GTS CIDX increment too large", -1, 0 }, +		{ ERR_CPL_OPCODE_0, "SGE received 0-length CPL", -1, 0 }, +		{ ERR_DROPPED_DB, "SGE doorbell dropped", -1, 0 }, +		{ ERR_DATA_CPL_ON_HIGH_QID1 | ERR_DATA_CPL_ON_HIGH_QID0, +		  "SGE IQID > 1023 received CPL for FL", -1, 0 }, +		{ ERR_BAD_DB_PIDX3, "SGE DBP 3 pidx increment too large", -1, +		  0 }, +		{ ERR_BAD_DB_PIDX2, "SGE DBP 2 pidx increment too large", -1, +		  0 }, +		{ ERR_BAD_DB_PIDX1, "SGE DBP 1 pidx increment too large", -1, +		  0 }, +		{ ERR_BAD_DB_PIDX0, "SGE DBP 0 pidx increment too large", -1, +		  0 }, +		{ ERR_ING_CTXT_PRIO, +		  "SGE too many priority ingress contexts", -1, 0 }, +		{ ERR_EGR_CTXT_PRIO, +		  "SGE too many priority egress contexts", -1, 0 }, +		{ INGRESS_SIZE_ERR, "SGE illegal ingress QID", -1, 0 }, +		{ EGRESS_SIZE_ERR, "SGE illegal egress QID", -1, 0 }, +		{ 0 } +	}; + +	v = (u64)t4_read_reg(adapter, SGE_INT_CAUSE1) | +	    ((u64)t4_read_reg(adapter, SGE_INT_CAUSE2) << 32); +	if (v) { +		dev_alert(adapter->pdev_dev, "SGE parity error (%#llx)\n", +			 (unsigned long long)v); +		t4_write_reg(adapter, SGE_INT_CAUSE1, v); +		t4_write_reg(adapter, SGE_INT_CAUSE2, v >> 32); +	} + +	if (t4_handle_intr_status(adapter, SGE_INT_CAUSE3, sge_intr_info) || +	    v != 0) +		t4_fatal_err(adapter); +} + +/* + * CIM interrupt handler. + */ +static void cim_intr_handler(struct adapter *adapter) +{ +	static struct intr_info cim_intr_info[] = { +		{ PREFDROPINT, "CIM control register prefetch drop", -1, 1 }, +		{ OBQPARERR, "CIM OBQ parity error", -1, 1 }, +		{ IBQPARERR, "CIM IBQ parity error", -1, 1 }, +		{ MBUPPARERR, "CIM mailbox uP parity error", -1, 1 }, +		{ MBHOSTPARERR, "CIM mailbox host parity error", -1, 1 }, +		{ TIEQINPARERRINT, "CIM TIEQ outgoing parity error", -1, 1 }, +		{ TIEQOUTPARERRINT, "CIM TIEQ incoming parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info cim_upintr_info[] = { +		{ RSVDSPACEINT, "CIM reserved space access", -1, 1 }, +		{ ILLTRANSINT, "CIM illegal transaction", -1, 1 }, +		{ ILLWRINT, "CIM illegal write", -1, 1 }, +		{ ILLRDINT, "CIM illegal read", -1, 1 }, +		{ ILLRDBEINT, "CIM illegal read BE", -1, 1 }, +		{ ILLWRBEINT, "CIM illegal write BE", -1, 1 }, +		{ SGLRDBOOTINT, "CIM single read from boot space", -1, 1 }, +		{ SGLWRBOOTINT, "CIM single write to boot space", -1, 1 }, +		{ BLKWRBOOTINT, "CIM block write to boot space", -1, 1 }, +		{ SGLRDFLASHINT, "CIM single read from flash space", -1, 1 }, +		{ SGLWRFLASHINT, "CIM single write to flash space", -1, 1 }, +		{ BLKWRFLASHINT, "CIM block write to flash space", -1, 1 }, +		{ SGLRDEEPROMINT, "CIM single EEPROM read", -1, 1 }, +		{ SGLWREEPROMINT, "CIM single EEPROM write", -1, 1 }, +		{ BLKRDEEPROMINT, "CIM block EEPROM read", -1, 1 }, +		{ BLKWREEPROMINT, "CIM block EEPROM write", -1, 1 }, +		{ SGLRDCTLINT , "CIM single read from CTL space", -1, 1 }, +		{ SGLWRCTLINT , "CIM single write to CTL space", -1, 1 }, +		{ BLKRDCTLINT , "CIM block read from CTL space", -1, 1 }, +		{ BLKWRCTLINT , "CIM block write to CTL space", -1, 1 }, +		{ SGLRDPLINT , "CIM single read from PL space", -1, 1 }, +		{ SGLWRPLINT , "CIM single write to PL space", -1, 1 }, +		{ BLKRDPLINT , "CIM block read from PL space", -1, 1 }, +		{ BLKWRPLINT , "CIM block write to PL space", -1, 1 }, +		{ REQOVRLOOKUPINT , "CIM request FIFO overwrite", -1, 1 }, +		{ RSPOVRLOOKUPINT , "CIM response FIFO overwrite", -1, 1 }, +		{ TIMEOUTINT , "CIM PIF timeout", -1, 1 }, +		{ TIMEOUTMAINT , "CIM PIF MA timeout", -1, 1 }, +		{ 0 } +	}; + +	int fat; + +	fat = t4_handle_intr_status(adapter, CIM_HOST_INT_CAUSE, +				    cim_intr_info) + +	      t4_handle_intr_status(adapter, CIM_HOST_UPACC_INT_CAUSE, +				    cim_upintr_info); +	if (fat) +		t4_fatal_err(adapter); +} + +/* + * ULP RX interrupt handler. + */ +static void ulprx_intr_handler(struct adapter *adapter) +{ +	static struct intr_info ulprx_intr_info[] = { +		{ 0x7fffff, "ULPRX parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, ULP_RX_INT_CAUSE, ulprx_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * ULP TX interrupt handler. + */ +static void ulptx_intr_handler(struct adapter *adapter) +{ +	static struct intr_info ulptx_intr_info[] = { +		{ PBL_BOUND_ERR_CH3, "ULPTX channel 3 PBL out of bounds", -1, +		  0 }, +		{ PBL_BOUND_ERR_CH2, "ULPTX channel 2 PBL out of bounds", -1, +		  0 }, +		{ PBL_BOUND_ERR_CH1, "ULPTX channel 1 PBL out of bounds", -1, +		  0 }, +		{ PBL_BOUND_ERR_CH0, "ULPTX channel 0 PBL out of bounds", -1, +		  0 }, +		{ 0xfffffff, "ULPTX parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, ULP_TX_INT_CAUSE, ulptx_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * PM TX interrupt handler. + */ +static void pmtx_intr_handler(struct adapter *adapter) +{ +	static struct intr_info pmtx_intr_info[] = { +		{ PCMD_LEN_OVFL0, "PMTX channel 0 pcmd too large", -1, 1 }, +		{ PCMD_LEN_OVFL1, "PMTX channel 1 pcmd too large", -1, 1 }, +		{ PCMD_LEN_OVFL2, "PMTX channel 2 pcmd too large", -1, 1 }, +		{ ZERO_C_CMD_ERROR, "PMTX 0-length pcmd", -1, 1 }, +		{ PMTX_FRAMING_ERROR, "PMTX framing error", -1, 1 }, +		{ OESPI_PAR_ERROR, "PMTX oespi parity error", -1, 1 }, +		{ DB_OPTIONS_PAR_ERROR, "PMTX db_options parity error", -1, 1 }, +		{ ICSPI_PAR_ERROR, "PMTX icspi parity error", -1, 1 }, +		{ C_PCMD_PAR_ERROR, "PMTX c_pcmd parity error", -1, 1}, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, PM_TX_INT_CAUSE, pmtx_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * PM RX interrupt handler. + */ +static void pmrx_intr_handler(struct adapter *adapter) +{ +	static struct intr_info pmrx_intr_info[] = { +		{ ZERO_E_CMD_ERROR, "PMRX 0-length pcmd", -1, 1 }, +		{ PMRX_FRAMING_ERROR, "PMRX framing error", -1, 1 }, +		{ OCSPI_PAR_ERROR, "PMRX ocspi parity error", -1, 1 }, +		{ DB_OPTIONS_PAR_ERROR, "PMRX db_options parity error", -1, 1 }, +		{ IESPI_PAR_ERROR, "PMRX iespi parity error", -1, 1 }, +		{ E_PCMD_PAR_ERROR, "PMRX e_pcmd parity error", -1, 1}, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, PM_RX_INT_CAUSE, pmrx_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * CPL switch interrupt handler. + */ +static void cplsw_intr_handler(struct adapter *adapter) +{ +	static struct intr_info cplsw_intr_info[] = { +		{ CIM_OP_MAP_PERR, "CPLSW CIM op_map parity error", -1, 1 }, +		{ CIM_OVFL_ERROR, "CPLSW CIM overflow", -1, 1 }, +		{ TP_FRAMING_ERROR, "CPLSW TP framing error", -1, 1 }, +		{ SGE_FRAMING_ERROR, "CPLSW SGE framing error", -1, 1 }, +		{ CIM_FRAMING_ERROR, "CPLSW CIM framing error", -1, 1 }, +		{ ZERO_SWITCH_ERROR, "CPLSW no-switch error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adapter, CPL_INTR_CAUSE, cplsw_intr_info)) +		t4_fatal_err(adapter); +} + +/* + * LE interrupt handler. + */ +static void le_intr_handler(struct adapter *adap) +{ +	static struct intr_info le_intr_info[] = { +		{ LIPMISS, "LE LIP miss", -1, 0 }, +		{ LIP0, "LE 0 LIP error", -1, 0 }, +		{ PARITYERR, "LE parity error", -1, 1 }, +		{ UNKNOWNCMD, "LE unknown command", -1, 1 }, +		{ REQQPARERR, "LE request queue parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adap, LE_DB_INT_CAUSE, le_intr_info)) +		t4_fatal_err(adap); +} + +/* + * MPS interrupt handler. + */ +static void mps_intr_handler(struct adapter *adapter) +{ +	static struct intr_info mps_rx_intr_info[] = { +		{ 0xffffff, "MPS Rx parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_tx_intr_info[] = { +		{ TPFIFO, "MPS Tx TP FIFO parity error", -1, 1 }, +		{ NCSIFIFO, "MPS Tx NC-SI FIFO parity error", -1, 1 }, +		{ TXDATAFIFO, "MPS Tx data FIFO parity error", -1, 1 }, +		{ TXDESCFIFO, "MPS Tx desc FIFO parity error", -1, 1 }, +		{ BUBBLE, "MPS Tx underflow", -1, 1 }, +		{ SECNTERR, "MPS Tx SOP/EOP error", -1, 1 }, +		{ FRMERR, "MPS Tx framing error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_trc_intr_info[] = { +		{ FILTMEM, "MPS TRC filter parity error", -1, 1 }, +		{ PKTFIFO, "MPS TRC packet FIFO parity error", -1, 1 }, +		{ MISCPERR, "MPS TRC misc parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_stat_sram_intr_info[] = { +		{ 0x1fffff, "MPS statistics SRAM parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_stat_tx_intr_info[] = { +		{ 0xfffff, "MPS statistics Tx FIFO parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_stat_rx_intr_info[] = { +		{ 0xffffff, "MPS statistics Rx FIFO parity error", -1, 1 }, +		{ 0 } +	}; +	static struct intr_info mps_cls_intr_info[] = { +		{ MATCHSRAM, "MPS match SRAM parity error", -1, 1 }, +		{ MATCHTCAM, "MPS match TCAM parity error", -1, 1 }, +		{ HASHSRAM, "MPS hash SRAM parity error", -1, 1 }, +		{ 0 } +	}; + +	int fat; + +	fat = t4_handle_intr_status(adapter, MPS_RX_PERR_INT_CAUSE, +				    mps_rx_intr_info) + +	      t4_handle_intr_status(adapter, MPS_TX_INT_CAUSE, +				    mps_tx_intr_info) + +	      t4_handle_intr_status(adapter, MPS_TRC_INT_CAUSE, +				    mps_trc_intr_info) + +	      t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_SRAM, +				    mps_stat_sram_intr_info) + +	      t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_TX_FIFO, +				    mps_stat_tx_intr_info) + +	      t4_handle_intr_status(adapter, MPS_STAT_PERR_INT_CAUSE_RX_FIFO, +				    mps_stat_rx_intr_info) + +	      t4_handle_intr_status(adapter, MPS_CLS_INT_CAUSE, +				    mps_cls_intr_info); + +	t4_write_reg(adapter, MPS_INT_CAUSE, CLSINT | TRCINT | +		     RXINT | TXINT | STATINT); +	t4_read_reg(adapter, MPS_INT_CAUSE);                    /* flush */ +	if (fat) +		t4_fatal_err(adapter); +} + +#define MEM_INT_MASK (PERR_INT_CAUSE | ECC_CE_INT_CAUSE | ECC_UE_INT_CAUSE) + +/* + * EDC/MC interrupt handler. + */ +static void mem_intr_handler(struct adapter *adapter, int idx) +{ +	static const char name[3][5] = { "EDC0", "EDC1", "MC" }; + +	unsigned int addr, cnt_addr, v; + +	if (idx <= MEM_EDC1) { +		addr = EDC_REG(EDC_INT_CAUSE, idx); +		cnt_addr = EDC_REG(EDC_ECC_STATUS, idx); +	} else { +		addr = MC_INT_CAUSE; +		cnt_addr = MC_ECC_STATUS; +	} + +	v = t4_read_reg(adapter, addr) & MEM_INT_MASK; +	if (v & PERR_INT_CAUSE) +		dev_alert(adapter->pdev_dev, "%s FIFO parity error\n", +			  name[idx]); +	if (v & ECC_CE_INT_CAUSE) { +		u32 cnt = ECC_CECNT_GET(t4_read_reg(adapter, cnt_addr)); + +		t4_write_reg(adapter, cnt_addr, ECC_CECNT_MASK); +		if (printk_ratelimit()) +			dev_warn(adapter->pdev_dev, +				 "%u %s correctable ECC data error%s\n", +				 cnt, name[idx], cnt > 1 ? "s" : ""); +	} +	if (v & ECC_UE_INT_CAUSE) +		dev_alert(adapter->pdev_dev, +			  "%s uncorrectable ECC data error\n", name[idx]); + +	t4_write_reg(adapter, addr, v); +	if (v & (PERR_INT_CAUSE | ECC_UE_INT_CAUSE)) +		t4_fatal_err(adapter); +} + +/* + * MA interrupt handler. + */ +static void ma_intr_handler(struct adapter *adap) +{ +	u32 v, status = t4_read_reg(adap, MA_INT_CAUSE); + +	if (status & MEM_PERR_INT_CAUSE) +		dev_alert(adap->pdev_dev, +			  "MA parity error, parity status %#x\n", +			  t4_read_reg(adap, MA_PARITY_ERROR_STATUS)); +	if (status & MEM_WRAP_INT_CAUSE) { +		v = t4_read_reg(adap, MA_INT_WRAP_STATUS); +		dev_alert(adap->pdev_dev, "MA address wrap-around error by " +			  "client %u to address %#x\n", +			  MEM_WRAP_CLIENT_NUM_GET(v), +			  MEM_WRAP_ADDRESS_GET(v) << 4); +	} +	t4_write_reg(adap, MA_INT_CAUSE, status); +	t4_fatal_err(adap); +} + +/* + * SMB interrupt handler. + */ +static void smb_intr_handler(struct adapter *adap) +{ +	static struct intr_info smb_intr_info[] = { +		{ MSTTXFIFOPARINT, "SMB master Tx FIFO parity error", -1, 1 }, +		{ MSTRXFIFOPARINT, "SMB master Rx FIFO parity error", -1, 1 }, +		{ SLVFIFOPARINT, "SMB slave FIFO parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adap, SMB_INT_CAUSE, smb_intr_info)) +		t4_fatal_err(adap); +} + +/* + * NC-SI interrupt handler. + */ +static void ncsi_intr_handler(struct adapter *adap) +{ +	static struct intr_info ncsi_intr_info[] = { +		{ CIM_DM_PRTY_ERR, "NC-SI CIM parity error", -1, 1 }, +		{ MPS_DM_PRTY_ERR, "NC-SI MPS parity error", -1, 1 }, +		{ TXFIFO_PRTY_ERR, "NC-SI Tx FIFO parity error", -1, 1 }, +		{ RXFIFO_PRTY_ERR, "NC-SI Rx FIFO parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adap, NCSI_INT_CAUSE, ncsi_intr_info)) +		t4_fatal_err(adap); +} + +/* + * XGMAC interrupt handler. + */ +static void xgmac_intr_handler(struct adapter *adap, int port) +{ +	u32 v = t4_read_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE)); + +	v &= TXFIFO_PRTY_ERR | RXFIFO_PRTY_ERR; +	if (!v) +		return; + +	if (v & TXFIFO_PRTY_ERR) +		dev_alert(adap->pdev_dev, "XGMAC %d Tx FIFO parity error\n", +			  port); +	if (v & RXFIFO_PRTY_ERR) +		dev_alert(adap->pdev_dev, "XGMAC %d Rx FIFO parity error\n", +			  port); +	t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_INT_CAUSE), v); +	t4_fatal_err(adap); +} + +/* + * PL interrupt handler. + */ +static void pl_intr_handler(struct adapter *adap) +{ +	static struct intr_info pl_intr_info[] = { +		{ FATALPERR, "T4 fatal parity error", -1, 1 }, +		{ PERRVFID, "PL VFID_MAP parity error", -1, 1 }, +		{ 0 } +	}; + +	if (t4_handle_intr_status(adap, PL_PL_INT_CAUSE, pl_intr_info)) +		t4_fatal_err(adap); +} + +#define PF_INTR_MASK (PFSW | PFCIM) +#define GLBL_INTR_MASK (CIM | MPS | PL | PCIE | MC | EDC0 | \ +		EDC1 | LE | TP | MA | PM_TX | PM_RX | ULP_RX | \ +		CPL_SWITCH | SGE | ULP_TX) + +/** + *	t4_slow_intr_handler - control path interrupt handler + *	@adapter: the adapter + * + *	T4 interrupt handler for non-data global interrupt events, e.g., errors. + *	The designation 'slow' is because it involves register reads, while + *	data interrupts typically don't involve any MMIOs. + */ +int t4_slow_intr_handler(struct adapter *adapter) +{ +	u32 cause = t4_read_reg(adapter, PL_INT_CAUSE); + +	if (!(cause & GLBL_INTR_MASK)) +		return 0; +	if (cause & CIM) +		cim_intr_handler(adapter); +	if (cause & MPS) +		mps_intr_handler(adapter); +	if (cause & NCSI) +		ncsi_intr_handler(adapter); +	if (cause & PL) +		pl_intr_handler(adapter); +	if (cause & SMB) +		smb_intr_handler(adapter); +	if (cause & XGMAC0) +		xgmac_intr_handler(adapter, 0); +	if (cause & XGMAC1) +		xgmac_intr_handler(adapter, 1); +	if (cause & XGMAC_KR0) +		xgmac_intr_handler(adapter, 2); +	if (cause & XGMAC_KR1) +		xgmac_intr_handler(adapter, 3); +	if (cause & PCIE) +		pcie_intr_handler(adapter); +	if (cause & MC) +		mem_intr_handler(adapter, MEM_MC); +	if (cause & EDC0) +		mem_intr_handler(adapter, MEM_EDC0); +	if (cause & EDC1) +		mem_intr_handler(adapter, MEM_EDC1); +	if (cause & LE) +		le_intr_handler(adapter); +	if (cause & TP) +		tp_intr_handler(adapter); +	if (cause & MA) +		ma_intr_handler(adapter); +	if (cause & PM_TX) +		pmtx_intr_handler(adapter); +	if (cause & PM_RX) +		pmrx_intr_handler(adapter); +	if (cause & ULP_RX) +		ulprx_intr_handler(adapter); +	if (cause & CPL_SWITCH) +		cplsw_intr_handler(adapter); +	if (cause & SGE) +		sge_intr_handler(adapter); +	if (cause & ULP_TX) +		ulptx_intr_handler(adapter); + +	/* Clear the interrupts just processed for which we are the master. */ +	t4_write_reg(adapter, PL_INT_CAUSE, cause & GLBL_INTR_MASK); +	(void) t4_read_reg(adapter, PL_INT_CAUSE); /* flush */ +	return 1; +} + +/** + *	t4_intr_enable - enable interrupts + *	@adapter: the adapter whose interrupts should be enabled + * + *	Enable PF-specific interrupts for the calling function and the top-level + *	interrupt concentrator for global interrupts.  Interrupts are already + *	enabled at each module,	here we just enable the roots of the interrupt + *	hierarchies. + * + *	Note: this function should be called only when the driver manages + *	non PF-specific interrupts from the various HW modules.  Only one PCI + *	function at a time should be doing this. + */ +void t4_intr_enable(struct adapter *adapter) +{ +	u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); + +	t4_write_reg(adapter, SGE_INT_ENABLE3, ERR_CPL_EXCEED_IQE_SIZE | +		     ERR_INVALID_CIDX_INC | ERR_CPL_OPCODE_0 | +		     ERR_DROPPED_DB | ERR_DATA_CPL_ON_HIGH_QID1 | +		     ERR_DATA_CPL_ON_HIGH_QID0 | ERR_BAD_DB_PIDX3 | +		     ERR_BAD_DB_PIDX2 | ERR_BAD_DB_PIDX1 | +		     ERR_BAD_DB_PIDX0 | ERR_ING_CTXT_PRIO | +		     ERR_EGR_CTXT_PRIO | INGRESS_SIZE_ERR | +		     EGRESS_SIZE_ERR); +	t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), PF_INTR_MASK); +	t4_set_reg_field(adapter, PL_INT_MAP0, 0, 1 << pf); +} + +/** + *	t4_intr_disable - disable interrupts + *	@adapter: the adapter whose interrupts should be disabled + * + *	Disable interrupts.  We only disable the top-level interrupt + *	concentrators.  The caller must be a PCI function managing global + *	interrupts. + */ +void t4_intr_disable(struct adapter *adapter) +{ +	u32 pf = SOURCEPF_GET(t4_read_reg(adapter, PL_WHOAMI)); + +	t4_write_reg(adapter, MYPF_REG(PL_PF_INT_ENABLE), 0); +	t4_set_reg_field(adapter, PL_INT_MAP0, 1 << pf, 0); +} + +/** + *	t4_intr_clear - clear all interrupts + *	@adapter: the adapter whose interrupts should be cleared + * + *	Clears all interrupts.  The caller must be a PCI function managing + *	global interrupts. + */ +void t4_intr_clear(struct adapter *adapter) +{ +	static const unsigned int cause_reg[] = { +		SGE_INT_CAUSE1, SGE_INT_CAUSE2, SGE_INT_CAUSE3, +		PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS, +		PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS, +		PCIE_NONFAT_ERR, PCIE_INT_CAUSE, +		MC_INT_CAUSE, +		MA_INT_WRAP_STATUS, MA_PARITY_ERROR_STATUS, MA_INT_CAUSE, +		EDC_INT_CAUSE, EDC_REG(EDC_INT_CAUSE, 1), +		CIM_HOST_INT_CAUSE, CIM_HOST_UPACC_INT_CAUSE, +		MYPF_REG(CIM_PF_HOST_INT_CAUSE), +		TP_INT_CAUSE, +		ULP_RX_INT_CAUSE, ULP_TX_INT_CAUSE, +		PM_RX_INT_CAUSE, PM_TX_INT_CAUSE, +		MPS_RX_PERR_INT_CAUSE, +		CPL_INTR_CAUSE, +		MYPF_REG(PL_PF_INT_CAUSE), +		PL_PL_INT_CAUSE, +		LE_DB_INT_CAUSE, +	}; + +	unsigned int i; + +	for (i = 0; i < ARRAY_SIZE(cause_reg); ++i) +		t4_write_reg(adapter, cause_reg[i], 0xffffffff); + +	t4_write_reg(adapter, PL_INT_CAUSE, GLBL_INTR_MASK); +	(void) t4_read_reg(adapter, PL_INT_CAUSE);          /* flush */ +} + +/** + *	hash_mac_addr - return the hash value of a MAC address + *	@addr: the 48-bit Ethernet MAC address + * + *	Hashes a MAC address according to the hash function used by HW inexact + *	(hash) address matching. + */ +static int hash_mac_addr(const u8 *addr) +{ +	u32 a = ((u32)addr[0] << 16) | ((u32)addr[1] << 8) | addr[2]; +	u32 b = ((u32)addr[3] << 16) | ((u32)addr[4] << 8) | addr[5]; +	a ^= b; +	a ^= (a >> 12); +	a ^= (a >> 6); +	return a & 0x3f; +} + +/** + *	t4_config_rss_range - configure a portion of the RSS mapping table + *	@adapter: the adapter + *	@mbox: mbox to use for the FW command + *	@viid: virtual interface whose RSS subtable is to be written + *	@start: start entry in the table to write + *	@n: how many table entries to write + *	@rspq: values for the response queue lookup table + *	@nrspq: number of values in @rspq + * + *	Programs the selected part of the VI's RSS mapping table with the + *	provided values.  If @nrspq < @n the supplied values are used repeatedly + *	until the full table range is populated. + * + *	The caller must ensure the values in @rspq are in the range allowed for + *	@viid. + */ +int t4_config_rss_range(struct adapter *adapter, int mbox, unsigned int viid, +			int start, int n, const u16 *rspq, unsigned int nrspq) +{ +	int ret; +	const u16 *rsp = rspq; +	const u16 *rsp_end = rspq + nrspq; +	struct fw_rss_ind_tbl_cmd cmd; + +	memset(&cmd, 0, sizeof(cmd)); +	cmd.op_to_viid = htonl(FW_CMD_OP(FW_RSS_IND_TBL_CMD) | +			       FW_CMD_REQUEST | FW_CMD_WRITE | +			       FW_RSS_IND_TBL_CMD_VIID(viid)); +	cmd.retval_len16 = htonl(FW_LEN16(cmd)); + +	/* each fw_rss_ind_tbl_cmd takes up to 32 entries */ +	while (n > 0) { +		int nq = min(n, 32); +		__be32 *qp = &cmd.iq0_to_iq2; + +		cmd.niqid = htons(nq); +		cmd.startidx = htons(start); + +		start += nq; +		n -= nq; + +		while (nq > 0) { +			unsigned int v; + +			v = FW_RSS_IND_TBL_CMD_IQ0(*rsp); +			if (++rsp >= rsp_end) +				rsp = rspq; +			v |= FW_RSS_IND_TBL_CMD_IQ1(*rsp); +			if (++rsp >= rsp_end) +				rsp = rspq; +			v |= FW_RSS_IND_TBL_CMD_IQ2(*rsp); +			if (++rsp >= rsp_end) +				rsp = rspq; + +			*qp++ = htonl(v); +			nq -= 3; +		} + +		ret = t4_wr_mbox(adapter, mbox, &cmd, sizeof(cmd), NULL); +		if (ret) +			return ret; +	} +	return 0; +} + +/** + *	t4_config_glbl_rss - configure the global RSS mode + *	@adapter: the adapter + *	@mbox: mbox to use for the FW command + *	@mode: global RSS mode + *	@flags: mode-specific flags + * + *	Sets the global RSS mode. + */ +int t4_config_glbl_rss(struct adapter *adapter, int mbox, unsigned int mode, +		       unsigned int flags) +{ +	struct fw_rss_glb_config_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_write = htonl(FW_CMD_OP(FW_RSS_GLB_CONFIG_CMD) | +			      FW_CMD_REQUEST | FW_CMD_WRITE); +	c.retval_len16 = htonl(FW_LEN16(c)); +	if (mode == FW_RSS_GLB_CONFIG_CMD_MODE_MANUAL) { +		c.u.manual.mode_pkd = htonl(FW_RSS_GLB_CONFIG_CMD_MODE(mode)); +	} else if (mode == FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL) { +		c.u.basicvirtual.mode_pkd = +			htonl(FW_RSS_GLB_CONFIG_CMD_MODE(mode)); +		c.u.basicvirtual.synmapen_to_hashtoeplitz = htonl(flags); +	} else +		return -EINVAL; +	return t4_wr_mbox(adapter, mbox, &c, sizeof(c), NULL); +} + +/* Read an RSS table row */ +static int rd_rss_row(struct adapter *adap, int row, u32 *val) +{ +	t4_write_reg(adap, TP_RSS_LKP_TABLE, 0xfff00000 | row); +	return t4_wait_op_done_val(adap, TP_RSS_LKP_TABLE, LKPTBLROWVLD, 1, +				   5, 0, val); +} + +/** + *	t4_read_rss - read the contents of the RSS mapping table + *	@adapter: the adapter + *	@map: holds the contents of the RSS mapping table + * + *	Reads the contents of the RSS hash->queue mapping table. + */ +int t4_read_rss(struct adapter *adapter, u16 *map) +{ +	u32 val; +	int i, ret; + +	for (i = 0; i < RSS_NENTRIES / 2; ++i) { +		ret = rd_rss_row(adapter, i, &val); +		if (ret) +			return ret; +		*map++ = LKPTBLQUEUE0_GET(val); +		*map++ = LKPTBLQUEUE1_GET(val); +	} +	return 0; +} + +/** + *	t4_tp_get_tcp_stats - read TP's TCP MIB counters + *	@adap: the adapter + *	@v4: holds the TCP/IP counter values + *	@v6: holds the TCP/IPv6 counter values + * + *	Returns the values of TP's TCP/IP and TCP/IPv6 MIB counters. + *	Either @v4 or @v6 may be %NULL to skip the corresponding stats. + */ +void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4, +			 struct tp_tcp_stats *v6) +{ +	u32 val[TP_MIB_TCP_RXT_SEG_LO - TP_MIB_TCP_OUT_RST + 1]; + +#define STAT_IDX(x) ((TP_MIB_TCP_##x) - TP_MIB_TCP_OUT_RST) +#define STAT(x)     val[STAT_IDX(x)] +#define STAT64(x)   (((u64)STAT(x##_HI) << 32) | STAT(x##_LO)) + +	if (v4) { +		t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, +				 ARRAY_SIZE(val), TP_MIB_TCP_OUT_RST); +		v4->tcpOutRsts = STAT(OUT_RST); +		v4->tcpInSegs  = STAT64(IN_SEG); +		v4->tcpOutSegs = STAT64(OUT_SEG); +		v4->tcpRetransSegs = STAT64(RXT_SEG); +	} +	if (v6) { +		t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, val, +				 ARRAY_SIZE(val), TP_MIB_TCP_V6OUT_RST); +		v6->tcpOutRsts = STAT(OUT_RST); +		v6->tcpInSegs  = STAT64(IN_SEG); +		v6->tcpOutSegs = STAT64(OUT_SEG); +		v6->tcpRetransSegs = STAT64(RXT_SEG); +	} +#undef STAT64 +#undef STAT +#undef STAT_IDX +} + +/** + *	t4_tp_get_err_stats - read TP's error MIB counters + *	@adap: the adapter + *	@st: holds the counter values + * + *	Returns the values of TP's error counters. + */ +void t4_tp_get_err_stats(struct adapter *adap, struct tp_err_stats *st) +{ +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, st->macInErrs, +			 12, TP_MIB_MAC_IN_ERR_0); +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, st->tnlCongDrops, +			 8, TP_MIB_TNL_CNG_DROP_0); +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, st->tnlTxDrops, +			 4, TP_MIB_TNL_DROP_0); +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, st->ofldVlanDrops, +			 4, TP_MIB_OFD_VLN_DROP_0); +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, st->tcp6InErrs, +			 4, TP_MIB_TCP_V6IN_ERR_0); +	t4_read_indirect(adap, TP_MIB_INDEX, TP_MIB_DATA, &st->ofldNoNeigh, +			 2, TP_MIB_OFD_ARP_DROP); +} + +/** + *	t4_read_mtu_tbl - returns the values in the HW path MTU table + *	@adap: the adapter + *	@mtus: where to store the MTU values + *	@mtu_log: where to store the MTU base-2 log (may be %NULL) + * + *	Reads the HW path MTU table. + */ +void t4_read_mtu_tbl(struct adapter *adap, u16 *mtus, u8 *mtu_log) +{ +	u32 v; +	int i; + +	for (i = 0; i < NMTUS; ++i) { +		t4_write_reg(adap, TP_MTU_TABLE, +			     MTUINDEX(0xff) | MTUVALUE(i)); +		v = t4_read_reg(adap, TP_MTU_TABLE); +		mtus[i] = MTUVALUE_GET(v); +		if (mtu_log) +			mtu_log[i] = MTUWIDTH_GET(v); +	} +} + +/** + *	init_cong_ctrl - initialize congestion control parameters + *	@a: the alpha values for congestion control + *	@b: the beta values for congestion control + * + *	Initialize the congestion control parameters. + */ +static void __devinit init_cong_ctrl(unsigned short *a, unsigned short *b) +{ +	a[0] = a[1] = a[2] = a[3] = a[4] = a[5] = a[6] = a[7] = a[8] = 1; +	a[9] = 2; +	a[10] = 3; +	a[11] = 4; +	a[12] = 5; +	a[13] = 6; +	a[14] = 7; +	a[15] = 8; +	a[16] = 9; +	a[17] = 10; +	a[18] = 14; +	a[19] = 17; +	a[20] = 21; +	a[21] = 25; +	a[22] = 30; +	a[23] = 35; +	a[24] = 45; +	a[25] = 60; +	a[26] = 80; +	a[27] = 100; +	a[28] = 200; +	a[29] = 300; +	a[30] = 400; +	a[31] = 500; + +	b[0] = b[1] = b[2] = b[3] = b[4] = b[5] = b[6] = b[7] = b[8] = 0; +	b[9] = b[10] = 1; +	b[11] = b[12] = 2; +	b[13] = b[14] = b[15] = b[16] = 3; +	b[17] = b[18] = b[19] = b[20] = b[21] = 4; +	b[22] = b[23] = b[24] = b[25] = b[26] = b[27] = 5; +	b[28] = b[29] = 6; +	b[30] = b[31] = 7; +} + +/* The minimum additive increment value for the congestion control table */ +#define CC_MIN_INCR 2U + +/** + *	t4_load_mtus - write the MTU and congestion control HW tables + *	@adap: the adapter + *	@mtus: the values for the MTU table + *	@alpha: the values for the congestion control alpha parameter + *	@beta: the values for the congestion control beta parameter + * + *	Write the HW MTU table with the supplied MTUs and the high-speed + *	congestion control table with the supplied alpha, beta, and MTUs. + *	We write the two tables together because the additive increments + *	depend on the MTUs. + */ +void t4_load_mtus(struct adapter *adap, const unsigned short *mtus, +		  const unsigned short *alpha, const unsigned short *beta) +{ +	static const unsigned int avg_pkts[NCCTRL_WIN] = { +		2, 6, 10, 14, 20, 28, 40, 56, 80, 112, 160, 224, 320, 448, 640, +		896, 1281, 1792, 2560, 3584, 5120, 7168, 10240, 14336, 20480, +		28672, 40960, 57344, 81920, 114688, 163840, 229376 +	}; + +	unsigned int i, w; + +	for (i = 0; i < NMTUS; ++i) { +		unsigned int mtu = mtus[i]; +		unsigned int log2 = fls(mtu); + +		if (!(mtu & ((1 << log2) >> 2)))     /* round */ +			log2--; +		t4_write_reg(adap, TP_MTU_TABLE, MTUINDEX(i) | +			     MTUWIDTH(log2) | MTUVALUE(mtu)); + +		for (w = 0; w < NCCTRL_WIN; ++w) { +			unsigned int inc; + +			inc = max(((mtu - 40) * alpha[w]) / avg_pkts[w], +				  CC_MIN_INCR); + +			t4_write_reg(adap, TP_CCTRL_TABLE, (i << 21) | +				     (w << 16) | (beta[w] << 13) | inc); +		} +	} +} + +/** + *	t4_set_trace_filter - configure one of the tracing filters + *	@adap: the adapter + *	@tp: the desired trace filter parameters + *	@idx: which filter to configure + *	@enable: whether to enable or disable the filter + * + *	Configures one of the tracing filters available in HW.  If @enable is + *	%0 @tp is not examined and may be %NULL. + */ +int t4_set_trace_filter(struct adapter *adap, const struct trace_params *tp, +			int idx, int enable) +{ +	int i, ofst = idx * 4; +	u32 data_reg, mask_reg, cfg; +	u32 multitrc = TRCMULTIFILTER; + +	if (!enable) { +		t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + ofst, 0); +		goto out; +	} + +	if (tp->port > 11 || tp->invert > 1 || tp->skip_len > 0x1f || +	    tp->skip_ofst > 0x1f || tp->min_len > 0x1ff || +	    tp->snap_len > 9600 || (idx && tp->snap_len > 256)) +		return -EINVAL; + +	if (tp->snap_len > 256) {            /* must be tracer 0 */ +		if ((t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + 4) | +		     t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + 8) | +		     t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + 12)) & TFEN) +			return -EINVAL;  /* other tracers are enabled */ +		multitrc = 0; +	} else if (idx) { +		i = t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_B); +		if (TFCAPTUREMAX_GET(i) > 256 && +		    (t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A) & TFEN)) +			return -EINVAL; +	} + +	/* stop the tracer we'll be changing */ +	t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + ofst, 0); + +	/* disable tracing globally if running in the wrong single/multi mode */ +	cfg = t4_read_reg(adap, MPS_TRC_CFG); +	if ((cfg & TRCEN) && multitrc != (cfg & TRCMULTIFILTER)) { +		t4_write_reg(adap, MPS_TRC_CFG, cfg ^ TRCEN); +		t4_read_reg(adap, MPS_TRC_CFG);                  /* flush */ +		msleep(1); +		if (!(t4_read_reg(adap, MPS_TRC_CFG) & TRCFIFOEMPTY)) +			return -ETIMEDOUT; +	} +	/* +	 * At this point either the tracing is enabled and in the right mode or +	 * disabled. +	 */ + +	idx *= (MPS_TRC_FILTER1_MATCH - MPS_TRC_FILTER0_MATCH); +	data_reg = MPS_TRC_FILTER0_MATCH + idx; +	mask_reg = MPS_TRC_FILTER0_DONT_CARE + idx; + +	for (i = 0; i < TRACE_LEN / 4; i++, data_reg += 4, mask_reg += 4) { +		t4_write_reg(adap, data_reg, tp->data[i]); +		t4_write_reg(adap, mask_reg, ~tp->mask[i]); +	} +	t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_B + ofst, +		     TFCAPTUREMAX(tp->snap_len) | +		     TFMINPKTSIZE(tp->min_len)); +	t4_write_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + ofst, +		     TFOFFSET(tp->skip_ofst) | TFLENGTH(tp->skip_len) | +		     TFPORT(tp->port) | TFEN | +		     (tp->invert ? TFINVERTMATCH : 0)); + +	cfg &= ~TRCMULTIFILTER; +	t4_write_reg(adap, MPS_TRC_CFG, cfg | TRCEN | multitrc); +out:	t4_read_reg(adap, MPS_TRC_CFG);  /* flush */ +	return 0; +} + +/** + *	t4_get_trace_filter - query one of the tracing filters + *	@adap: the adapter + *	@tp: the current trace filter parameters + *	@idx: which trace filter to query + *	@enabled: non-zero if the filter is enabled + * + *	Returns the current settings of one of the HW tracing filters. + */ +void t4_get_trace_filter(struct adapter *adap, struct trace_params *tp, int idx, +			 int *enabled) +{ +	u32 ctla, ctlb; +	int i, ofst = idx * 4; +	u32 data_reg, mask_reg; + +	ctla = t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_A + ofst); +	ctlb = t4_read_reg(adap, MPS_TRC_FILTER_MATCH_CTL_B + ofst); + +	*enabled = !!(ctla & TFEN); +	tp->snap_len = TFCAPTUREMAX_GET(ctlb); +	tp->min_len = TFMINPKTSIZE_GET(ctlb); +	tp->skip_ofst = TFOFFSET_GET(ctla); +	tp->skip_len = TFLENGTH_GET(ctla); +	tp->invert = !!(ctla & TFINVERTMATCH); +	tp->port = TFPORT_GET(ctla); + +	ofst = (MPS_TRC_FILTER1_MATCH - MPS_TRC_FILTER0_MATCH) * idx; +	data_reg = MPS_TRC_FILTER0_MATCH + ofst; +	mask_reg = MPS_TRC_FILTER0_DONT_CARE + ofst; + +	for (i = 0; i < TRACE_LEN / 4; i++, data_reg += 4, mask_reg += 4) { +		tp->mask[i] = ~t4_read_reg(adap, mask_reg); +		tp->data[i] = t4_read_reg(adap, data_reg) & tp->mask[i]; +	} +} + +/** + *	get_mps_bg_map - return the buffer groups associated with a port + *	@adap: the adapter + *	@idx: the port index + * + *	Returns a bitmap indicating which MPS buffer groups are associated + *	with the given port.  Bit i is set if buffer group i is used by the + *	port. + */ +static unsigned int get_mps_bg_map(struct adapter *adap, int idx) +{ +	u32 n = NUMPORTS_GET(t4_read_reg(adap, MPS_CMN_CTL)); + +	if (n == 0) +		return idx == 0 ? 0xf : 0; +	if (n == 1) +		return idx < 2 ? (3 << (2 * idx)) : 0; +	return 1 << idx; +} + +/** + *	t4_get_port_stats - collect port statistics + *	@adap: the adapter + *	@idx: the port index + *	@p: the stats structure to fill + * + *	Collect statistics related to the given port from HW. + */ +void t4_get_port_stats(struct adapter *adap, int idx, struct port_stats *p) +{ +	u32 bgmap = get_mps_bg_map(adap, idx); + +#define GET_STAT(name) \ +	t4_read_reg64(adap, PORT_REG(idx, MPS_PORT_STAT_##name##_L)) +#define GET_STAT_COM(name) t4_read_reg64(adap, MPS_STAT_##name##_L) + +	p->tx_octets           = GET_STAT(TX_PORT_BYTES); +	p->tx_frames           = GET_STAT(TX_PORT_FRAMES); +	p->tx_bcast_frames     = GET_STAT(TX_PORT_BCAST); +	p->tx_mcast_frames     = GET_STAT(TX_PORT_MCAST); +	p->tx_ucast_frames     = GET_STAT(TX_PORT_UCAST); +	p->tx_error_frames     = GET_STAT(TX_PORT_ERROR); +	p->tx_frames_64        = GET_STAT(TX_PORT_64B); +	p->tx_frames_65_127    = GET_STAT(TX_PORT_65B_127B); +	p->tx_frames_128_255   = GET_STAT(TX_PORT_128B_255B); +	p->tx_frames_256_511   = GET_STAT(TX_PORT_256B_511B); +	p->tx_frames_512_1023  = GET_STAT(TX_PORT_512B_1023B); +	p->tx_frames_1024_1518 = GET_STAT(TX_PORT_1024B_1518B); +	p->tx_frames_1519_max  = GET_STAT(TX_PORT_1519B_MAX); +	p->tx_drop             = GET_STAT(TX_PORT_DROP); +	p->tx_pause            = GET_STAT(TX_PORT_PAUSE); +	p->tx_ppp0             = GET_STAT(TX_PORT_PPP0); +	p->tx_ppp1             = GET_STAT(TX_PORT_PPP1); +	p->tx_ppp2             = GET_STAT(TX_PORT_PPP2); +	p->tx_ppp3             = GET_STAT(TX_PORT_PPP3); +	p->tx_ppp4             = GET_STAT(TX_PORT_PPP4); +	p->tx_ppp5             = GET_STAT(TX_PORT_PPP5); +	p->tx_ppp6             = GET_STAT(TX_PORT_PPP6); +	p->tx_ppp7             = GET_STAT(TX_PORT_PPP7); + +	p->rx_octets           = GET_STAT(RX_PORT_BYTES); +	p->rx_frames           = GET_STAT(RX_PORT_FRAMES); +	p->rx_bcast_frames     = GET_STAT(RX_PORT_BCAST); +	p->rx_mcast_frames     = GET_STAT(RX_PORT_MCAST); +	p->rx_ucast_frames     = GET_STAT(RX_PORT_UCAST); +	p->rx_too_long         = GET_STAT(RX_PORT_MTU_ERROR); +	p->rx_jabber           = GET_STAT(RX_PORT_MTU_CRC_ERROR); +	p->rx_fcs_err          = GET_STAT(RX_PORT_CRC_ERROR); +	p->rx_len_err          = GET_STAT(RX_PORT_LEN_ERROR); +	p->rx_symbol_err       = GET_STAT(RX_PORT_SYM_ERROR); +	p->rx_runt             = GET_STAT(RX_PORT_LESS_64B); +	p->rx_frames_64        = GET_STAT(RX_PORT_64B); +	p->rx_frames_65_127    = GET_STAT(RX_PORT_65B_127B); +	p->rx_frames_128_255   = GET_STAT(RX_PORT_128B_255B); +	p->rx_frames_256_511   = GET_STAT(RX_PORT_256B_511B); +	p->rx_frames_512_1023  = GET_STAT(RX_PORT_512B_1023B); +	p->rx_frames_1024_1518 = GET_STAT(RX_PORT_1024B_1518B); +	p->rx_frames_1519_max  = GET_STAT(RX_PORT_1519B_MAX); +	p->rx_pause            = GET_STAT(RX_PORT_PAUSE); +	p->rx_ppp0             = GET_STAT(RX_PORT_PPP0); +	p->rx_ppp1             = GET_STAT(RX_PORT_PPP1); +	p->rx_ppp2             = GET_STAT(RX_PORT_PPP2); +	p->rx_ppp3             = GET_STAT(RX_PORT_PPP3); +	p->rx_ppp4             = GET_STAT(RX_PORT_PPP4); +	p->rx_ppp5             = GET_STAT(RX_PORT_PPP5); +	p->rx_ppp6             = GET_STAT(RX_PORT_PPP6); +	p->rx_ppp7             = GET_STAT(RX_PORT_PPP7); + +	p->rx_ovflow0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_MAC_DROP_FRAME) : 0; +	p->rx_ovflow1 = (bgmap & 2) ? GET_STAT_COM(RX_BG_1_MAC_DROP_FRAME) : 0; +	p->rx_ovflow2 = (bgmap & 4) ? GET_STAT_COM(RX_BG_2_MAC_DROP_FRAME) : 0; +	p->rx_ovflow3 = (bgmap & 8) ? GET_STAT_COM(RX_BG_3_MAC_DROP_FRAME) : 0; +	p->rx_trunc0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_MAC_TRUNC_FRAME) : 0; +	p->rx_trunc1 = (bgmap & 2) ? GET_STAT_COM(RX_BG_1_MAC_TRUNC_FRAME) : 0; +	p->rx_trunc2 = (bgmap & 4) ? GET_STAT_COM(RX_BG_2_MAC_TRUNC_FRAME) : 0; +	p->rx_trunc3 = (bgmap & 8) ? GET_STAT_COM(RX_BG_3_MAC_TRUNC_FRAME) : 0; + +#undef GET_STAT +#undef GET_STAT_COM +} + +/** + *	t4_get_lb_stats - collect loopback port statistics + *	@adap: the adapter + *	@idx: the loopback port index + *	@p: the stats structure to fill + * + *	Return HW statistics for the given loopback port. + */ +void t4_get_lb_stats(struct adapter *adap, int idx, struct lb_port_stats *p) +{ +	u32 bgmap = get_mps_bg_map(adap, idx); + +#define GET_STAT(name) \ +	t4_read_reg64(adap, PORT_REG(idx, MPS_PORT_STAT_LB_PORT_##name##_L)) +#define GET_STAT_COM(name) t4_read_reg64(adap, MPS_STAT_##name##_L) + +	p->octets           = GET_STAT(BYTES); +	p->frames           = GET_STAT(FRAMES); +	p->bcast_frames     = GET_STAT(BCAST); +	p->mcast_frames     = GET_STAT(MCAST); +	p->ucast_frames     = GET_STAT(UCAST); +	p->error_frames     = GET_STAT(ERROR); + +	p->frames_64        = GET_STAT(64B); +	p->frames_65_127    = GET_STAT(65B_127B); +	p->frames_128_255   = GET_STAT(128B_255B); +	p->frames_256_511   = GET_STAT(256B_511B); +	p->frames_512_1023  = GET_STAT(512B_1023B); +	p->frames_1024_1518 = GET_STAT(1024B_1518B); +	p->frames_1519_max  = GET_STAT(1519B_MAX); +	p->drop             = t4_read_reg(adap, PORT_REG(idx, +					  MPS_PORT_STAT_LB_PORT_DROP_FRAMES)); + +	p->ovflow0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_LB_DROP_FRAME) : 0; +	p->ovflow1 = (bgmap & 2) ? GET_STAT_COM(RX_BG_1_LB_DROP_FRAME) : 0; +	p->ovflow2 = (bgmap & 4) ? GET_STAT_COM(RX_BG_2_LB_DROP_FRAME) : 0; +	p->ovflow3 = (bgmap & 8) ? GET_STAT_COM(RX_BG_3_LB_DROP_FRAME) : 0; +	p->trunc0 = (bgmap & 1) ? GET_STAT_COM(RX_BG_0_LB_TRUNC_FRAME) : 0; +	p->trunc1 = (bgmap & 2) ? GET_STAT_COM(RX_BG_1_LB_TRUNC_FRAME) : 0; +	p->trunc2 = (bgmap & 4) ? GET_STAT_COM(RX_BG_2_LB_TRUNC_FRAME) : 0; +	p->trunc3 = (bgmap & 8) ? GET_STAT_COM(RX_BG_3_LB_TRUNC_FRAME) : 0; + +#undef GET_STAT +#undef GET_STAT_COM +} + +/** + *	t4_wol_magic_enable - enable/disable magic packet WoL + *	@adap: the adapter + *	@port: the physical port index + *	@addr: MAC address expected in magic packets, %NULL to disable + * + *	Enables/disables magic packet wake-on-LAN for the selected port. + */ +void t4_wol_magic_enable(struct adapter *adap, unsigned int port, +			 const u8 *addr) +{ +	if (addr) { +		t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_MAGIC_MACID_LO), +			     (addr[2] << 24) | (addr[3] << 16) | +			     (addr[4] << 8) | addr[5]); +		t4_write_reg(adap, PORT_REG(port, XGMAC_PORT_MAGIC_MACID_HI), +			     (addr[0] << 8) | addr[1]); +	} +	t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), MAGICEN, +			 addr ? MAGICEN : 0); +} + +/** + *	t4_wol_pat_enable - enable/disable pattern-based WoL + *	@adap: the adapter + *	@port: the physical port index + *	@map: bitmap of which HW pattern filters to set + *	@mask0: byte mask for bytes 0-63 of a packet + *	@mask1: byte mask for bytes 64-127 of a packet + *	@crc: Ethernet CRC for selected bytes + *	@enable: enable/disable switch + * + *	Sets the pattern filters indicated in @map to mask out the bytes + *	specified in @mask0/@mask1 in received packets and compare the CRC of + *	the resulting packet against @crc.  If @enable is %true pattern-based + *	WoL is enabled, otherwise disabled. + */ +int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map, +		      u64 mask0, u64 mask1, unsigned int crc, bool enable) +{ +	int i; + +	if (!enable) { +		t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), +				 PATEN, 0); +		return 0; +	} +	if (map > 0xff) +		return -EINVAL; + +#define EPIO_REG(name) PORT_REG(port, XGMAC_PORT_EPIO_##name) + +	t4_write_reg(adap, EPIO_REG(DATA1), mask0 >> 32); +	t4_write_reg(adap, EPIO_REG(DATA2), mask1); +	t4_write_reg(adap, EPIO_REG(DATA3), mask1 >> 32); + +	for (i = 0; i < NWOL_PAT; i++, map >>= 1) { +		if (!(map & 1)) +			continue; + +		/* write byte masks */ +		t4_write_reg(adap, EPIO_REG(DATA0), mask0); +		t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i) | EPIOWR); +		t4_read_reg(adap, EPIO_REG(OP));                /* flush */ +		if (t4_read_reg(adap, EPIO_REG(OP)) & BUSY) +			return -ETIMEDOUT; + +		/* write CRC */ +		t4_write_reg(adap, EPIO_REG(DATA0), crc); +		t4_write_reg(adap, EPIO_REG(OP), ADDRESS(i + 32) | EPIOWR); +		t4_read_reg(adap, EPIO_REG(OP));                /* flush */ +		if (t4_read_reg(adap, EPIO_REG(OP)) & BUSY) +			return -ETIMEDOUT; +	} +#undef EPIO_REG + +	t4_set_reg_field(adap, PORT_REG(port, XGMAC_PORT_CFG2), 0, PATEN); +	return 0; +} + +#define INIT_CMD(var, cmd, rd_wr) do { \ +	(var).op_to_write = htonl(FW_CMD_OP(FW_##cmd##_CMD) | \ +				  FW_CMD_REQUEST | FW_CMD_##rd_wr); \ +	(var).retval_len16 = htonl(FW_LEN16(var)); \ +} while (0) + +/** + *	t4_mdio_rd - read a PHY register through MDIO + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@phy_addr: the PHY address + *	@mmd: the PHY MMD to access (0 for clause 22 PHYs) + *	@reg: the register to read + *	@valp: where to store the value + * + *	Issues a FW command through the given mailbox to read a PHY register. + */ +int t4_mdio_rd(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, +	       unsigned int mmd, unsigned int reg, u16 *valp) +{ +	int ret; +	struct fw_ldst_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_addrspace = htonl(FW_CMD_OP(FW_LDST_CMD) | FW_CMD_REQUEST | +		FW_CMD_READ | FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_MDIO)); +	c.cycles_to_len16 = htonl(FW_LEN16(c)); +	c.u.mdio.paddr_mmd = htons(FW_LDST_CMD_PADDR(phy_addr) | +				   FW_LDST_CMD_MMD(mmd)); +	c.u.mdio.raddr = htons(reg); + +	ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +	if (ret == 0) +		*valp = ntohs(c.u.mdio.rval); +	return ret; +} + +/** + *	t4_mdio_wr - write a PHY register through MDIO + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@phy_addr: the PHY address + *	@mmd: the PHY MMD to access (0 for clause 22 PHYs) + *	@reg: the register to write + *	@valp: value to write + * + *	Issues a FW command through the given mailbox to write a PHY register. + */ +int t4_mdio_wr(struct adapter *adap, unsigned int mbox, unsigned int phy_addr, +	       unsigned int mmd, unsigned int reg, u16 val) +{ +	struct fw_ldst_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_addrspace = htonl(FW_CMD_OP(FW_LDST_CMD) | FW_CMD_REQUEST | +		FW_CMD_WRITE | FW_LDST_CMD_ADDRSPACE(FW_LDST_ADDRSPC_MDIO)); +	c.cycles_to_len16 = htonl(FW_LEN16(c)); +	c.u.mdio.paddr_mmd = htons(FW_LDST_CMD_PADDR(phy_addr) | +				   FW_LDST_CMD_MMD(mmd)); +	c.u.mdio.raddr = htons(reg); +	c.u.mdio.rval = htons(val); + +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_fw_hello - establish communication with FW + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@evt_mbox: mailbox to receive async FW events + *	@master: specifies the caller's willingness to be the device master + *	@state: returns the current device state + * + *	Issues a command to establish communication with FW. + */ +int t4_fw_hello(struct adapter *adap, unsigned int mbox, unsigned int evt_mbox, +		enum dev_master master, enum dev_state *state) +{ +	int ret; +	struct fw_hello_cmd c; + +	INIT_CMD(c, HELLO, WRITE); +	c.err_to_mbasyncnot = htonl( +		FW_HELLO_CMD_MASTERDIS(master == MASTER_CANT) | +		FW_HELLO_CMD_MASTERFORCE(master == MASTER_MUST) | +		FW_HELLO_CMD_MBMASTER(master == MASTER_MUST ? mbox : 0xff) | +		FW_HELLO_CMD_MBASYNCNOT(evt_mbox)); + +	ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +	if (ret == 0 && state) { +		u32 v = ntohl(c.err_to_mbasyncnot); +		if (v & FW_HELLO_CMD_INIT) +			*state = DEV_STATE_INIT; +		else if (v & FW_HELLO_CMD_ERR) +			*state = DEV_STATE_ERR; +		else +			*state = DEV_STATE_UNINIT; +	} +	return ret; +} + +/** + *	t4_fw_bye - end communication with FW + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + * + *	Issues a command to terminate communication with FW. + */ +int t4_fw_bye(struct adapter *adap, unsigned int mbox) +{ +	struct fw_bye_cmd c; + +	INIT_CMD(c, BYE, WRITE); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_init_cmd - ask FW to initialize the device + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + * + *	Issues a command to FW to partially initialize the device.  This + *	performs initialization that generally doesn't depend on user input. + */ +int t4_early_init(struct adapter *adap, unsigned int mbox) +{ +	struct fw_initialize_cmd c; + +	INIT_CMD(c, INITIALIZE, WRITE); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_fw_reset - issue a reset to FW + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@reset: specifies the type of reset to perform + * + *	Issues a reset command of the specified type to FW. + */ +int t4_fw_reset(struct adapter *adap, unsigned int mbox, int reset) +{ +	struct fw_reset_cmd c; + +	INIT_CMD(c, RESET, WRITE); +	c.val = htonl(reset); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_query_params - query FW or device parameters + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF + *	@vf: the VF + *	@nparams: the number of parameters + *	@params: the parameter names + *	@val: the parameter values + * + *	Reads the value of FW or device parameters.  Up to 7 parameters can be + *	queried at once. + */ +int t4_query_params(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int nparams, const u32 *params, +		    u32 *val) +{ +	int i, ret; +	struct fw_params_cmd c; +	__be32 *p = &c.param[0].mnem; + +	if (nparams > 7) +		return -EINVAL; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_PARAMS_CMD) | FW_CMD_REQUEST | +			    FW_CMD_READ | FW_PARAMS_CMD_PFN(pf) | +			    FW_PARAMS_CMD_VFN(vf)); +	c.retval_len16 = htonl(FW_LEN16(c)); +	for (i = 0; i < nparams; i++, p += 2) +		*p = htonl(*params++); + +	ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +	if (ret == 0) +		for (i = 0, p = &c.param[0].val; i < nparams; i++, p += 2) +			*val++ = ntohl(*p); +	return ret; +} + +/** + *	t4_set_params - sets FW or device parameters + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF + *	@vf: the VF + *	@nparams: the number of parameters + *	@params: the parameter names + *	@val: the parameter values + * + *	Sets the value of FW or device parameters.  Up to 7 parameters can be + *	specified at once. + */ +int t4_set_params(struct adapter *adap, unsigned int mbox, unsigned int pf, +		  unsigned int vf, unsigned int nparams, const u32 *params, +		  const u32 *val) +{ +	struct fw_params_cmd c; +	__be32 *p = &c.param[0].mnem; + +	if (nparams > 7) +		return -EINVAL; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_PARAMS_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_PARAMS_CMD_PFN(pf) | +			    FW_PARAMS_CMD_VFN(vf)); +	c.retval_len16 = htonl(FW_LEN16(c)); +	while (nparams--) { +		*p++ = htonl(*params++); +		*p++ = htonl(*val++); +	} + +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_cfg_pfvf - configure PF/VF resource limits + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF being configured + *	@vf: the VF being configured + *	@txq: the max number of egress queues + *	@txq_eth_ctrl: the max number of egress Ethernet or control queues + *	@rxqi: the max number of interrupt-capable ingress queues + *	@rxq: the max number of interruptless ingress queues + *	@tc: the PCI traffic class + *	@vi: the max number of virtual interfaces + *	@cmask: the channel access rights mask for the PF/VF + *	@pmask: the port access rights mask for the PF/VF + *	@nexact: the maximum number of exact MPS filters + *	@rcaps: read capabilities + *	@wxcaps: write/execute capabilities + * + *	Configures resource limits and capabilities for a physical or virtual + *	function. + */ +int t4_cfg_pfvf(struct adapter *adap, unsigned int mbox, unsigned int pf, +		unsigned int vf, unsigned int txq, unsigned int txq_eth_ctrl, +		unsigned int rxqi, unsigned int rxq, unsigned int tc, +		unsigned int vi, unsigned int cmask, unsigned int pmask, +		unsigned int nexact, unsigned int rcaps, unsigned int wxcaps) +{ +	struct fw_pfvf_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_PFVF_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_PFVF_CMD_PFN(pf) | +			    FW_PFVF_CMD_VFN(vf)); +	c.retval_len16 = htonl(FW_LEN16(c)); +	c.niqflint_niq = htonl(FW_PFVF_CMD_NIQFLINT(rxqi) | +			       FW_PFVF_CMD_NIQ(rxq)); +	c.cmask_to_neq = htonl(FW_PFVF_CMD_CMASK(cmask) | +			       FW_PFVF_CMD_PMASK(pmask) | +			       FW_PFVF_CMD_NEQ(txq)); +	c.tc_to_nexactf = htonl(FW_PFVF_CMD_TC(tc) | FW_PFVF_CMD_NVI(vi) | +				FW_PFVF_CMD_NEXACTF(nexact)); +	c.r_caps_to_nethctrl = htonl(FW_PFVF_CMD_R_CAPS(rcaps) | +				     FW_PFVF_CMD_WX_CAPS(wxcaps) | +				     FW_PFVF_CMD_NETHCTRL(txq_eth_ctrl)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_alloc_vi - allocate a virtual interface + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@port: physical port associated with the VI + *	@pf: the PF owning the VI + *	@vf: the VF owning the VI + *	@nmac: number of MAC addresses needed (1 to 5) + *	@mac: the MAC addresses of the VI + *	@rss_size: size of RSS table slice associated with this VI + * + *	Allocates a virtual interface for the given physical port.  If @mac is + *	not %NULL it contains the MAC addresses of the VI as assigned by FW. + *	@mac should be large enough to hold @nmac Ethernet addresses, they are + *	stored consecutively so the space needed is @nmac * 6 bytes. + *	Returns a negative error number or the non-negative VI id. + */ +int t4_alloc_vi(struct adapter *adap, unsigned int mbox, unsigned int port, +		unsigned int pf, unsigned int vf, unsigned int nmac, u8 *mac, +		unsigned int *rss_size) +{ +	int ret; +	struct fw_vi_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_VI_CMD) | FW_CMD_REQUEST | +			    FW_CMD_WRITE | FW_CMD_EXEC | +			    FW_VI_CMD_PFN(pf) | FW_VI_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_VI_CMD_ALLOC | FW_LEN16(c)); +	c.portid_pkd = FW_VI_CMD_PORTID(port); +	c.nmac = nmac - 1; + +	ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +	if (ret) +		return ret; + +	if (mac) { +		memcpy(mac, c.mac, sizeof(c.mac)); +		switch (nmac) { +		case 5: +			memcpy(mac + 24, c.nmac3, sizeof(c.nmac3)); +		case 4: +			memcpy(mac + 18, c.nmac2, sizeof(c.nmac2)); +		case 3: +			memcpy(mac + 12, c.nmac1, sizeof(c.nmac1)); +		case 2: +			memcpy(mac + 6,  c.nmac0, sizeof(c.nmac0)); +		} +	} +	if (rss_size) +		*rss_size = FW_VI_CMD_RSSSIZE_GET(ntohs(c.rsssize_pkd)); +	return ntohs(c.viid_pkd); +} + +/** + *	t4_free_vi - free a virtual interface + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF owning the VI + *	@vf: the VF owning the VI + *	@viid: virtual interface identifiler + * + *	Free a previously allocated virtual interface. + */ +int t4_free_vi(struct adapter *adap, unsigned int mbox, unsigned int pf, +	       unsigned int vf, unsigned int viid) +{ +	struct fw_vi_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_VI_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_VI_CMD_PFN(pf) | +			    FW_VI_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_VI_CMD_FREE | FW_LEN16(c)); +	c.viid_pkd = htons(FW_VI_CMD_VIID(viid)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +} + +/** + *	t4_set_rxmode - set Rx properties of a virtual interface + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@mtu: the new MTU or -1 + *	@promisc: 1 to enable promiscuous mode, 0 to disable it, -1 no change + *	@all_multi: 1 to enable all-multi mode, 0 to disable it, -1 no change + *	@bcast: 1 to enable broadcast Rx, 0 to disable it, -1 no change + *	@sleep_ok: if true we may sleep while awaiting command completion + * + *	Sets Rx properties of a virtual interface. + */ +int t4_set_rxmode(struct adapter *adap, unsigned int mbox, unsigned int viid, +		  int mtu, int promisc, int all_multi, int bcast, bool sleep_ok) +{ +	struct fw_vi_rxmode_cmd c; + +	/* convert to FW values */ +	if (mtu < 0) +		mtu = FW_RXMODE_MTU_NO_CHG; +	if (promisc < 0) +		promisc = FW_VI_RXMODE_CMD_PROMISCEN_MASK; +	if (all_multi < 0) +		all_multi = FW_VI_RXMODE_CMD_ALLMULTIEN_MASK; +	if (bcast < 0) +		bcast = FW_VI_RXMODE_CMD_BROADCASTEN_MASK; + +	memset(&c, 0, sizeof(c)); +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_RXMODE_CMD) | FW_CMD_REQUEST | +			     FW_CMD_WRITE | FW_VI_RXMODE_CMD_VIID(viid)); +	c.retval_len16 = htonl(FW_LEN16(c)); +	c.mtu_to_broadcasten = htonl(FW_VI_RXMODE_CMD_MTU(mtu) | +				     FW_VI_RXMODE_CMD_PROMISCEN(promisc) | +				     FW_VI_RXMODE_CMD_ALLMULTIEN(all_multi) | +				     FW_VI_RXMODE_CMD_BROADCASTEN(bcast)); +	return t4_wr_mbox_meat(adap, mbox, &c, sizeof(c), NULL, sleep_ok); +} + +/** + *	t4_alloc_mac_filt - allocates exact-match filters for MAC addresses + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@free: if true any existing filters for this VI id are first removed + *	@naddr: the number of MAC addresses to allocate filters for (up to 7) + *	@addr: the MAC address(es) + *	@idx: where to store the index of each allocated filter + *	@hash: pointer to hash address filter bitmap + *	@sleep_ok: call is allowed to sleep + * + *	Allocates an exact-match filter for each of the supplied addresses and + *	sets it to the corresponding address.  If @idx is not %NULL it should + *	have at least @naddr entries, each of which will be set to the index of + *	the filter allocated for the corresponding MAC address.  If a filter + *	could not be allocated for an address its index is set to 0xffff. + *	If @hash is not %NULL addresses that fail to allocate an exact filter + *	are hashed and update the hash filter bitmap pointed at by @hash. + * + *	Returns a negative error number or the number of filters allocated. + */ +int t4_alloc_mac_filt(struct adapter *adap, unsigned int mbox, +		      unsigned int viid, bool free, unsigned int naddr, +		      const u8 **addr, u16 *idx, u64 *hash, bool sleep_ok) +{ +	int i, ret; +	struct fw_vi_mac_cmd c; +	struct fw_vi_mac_exact *p; + +	if (naddr > 7) +		return -EINVAL; + +	memset(&c, 0, sizeof(c)); +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_MAC_CMD) | FW_CMD_REQUEST | +			     FW_CMD_WRITE | (free ? FW_CMD_EXEC : 0) | +			     FW_VI_MAC_CMD_VIID(viid)); +	c.freemacs_to_len16 = htonl(FW_VI_MAC_CMD_FREEMACS(free) | +				    FW_CMD_LEN16((naddr + 2) / 2)); + +	for (i = 0, p = c.u.exact; i < naddr; i++, p++) { +		p->valid_to_idx = htons(FW_VI_MAC_CMD_VALID | +				      FW_VI_MAC_CMD_IDX(FW_VI_MAC_ADD_MAC)); +		memcpy(p->macaddr, addr[i], sizeof(p->macaddr)); +	} + +	ret = t4_wr_mbox_meat(adap, mbox, &c, sizeof(c), &c, sleep_ok); +	if (ret) +		return ret; + +	for (i = 0, p = c.u.exact; i < naddr; i++, p++) { +		u16 index = FW_VI_MAC_CMD_IDX_GET(ntohs(p->valid_to_idx)); + +		if (idx) +			idx[i] = index >= NEXACT_MAC ? 0xffff : index; +		if (index < NEXACT_MAC) +			ret++; +		else if (hash) +			*hash |= (1 << hash_mac_addr(addr[i])); +	} +	return ret; +} + +/** + *	t4_change_mac - modifies the exact-match filter for a MAC address + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@idx: index of existing filter for old value of MAC address, or -1 + *	@addr: the new MAC address value + *	@persist: whether a new MAC allocation should be persistent + *	@add_smt: if true also add the address to the HW SMT + * + *	Modifies an exact-match filter and sets it to the new MAC address. + *	Note that in general it is not possible to modify the value of a given + *	filter so the generic way to modify an address filter is to free the one + *	being used by the old address value and allocate a new filter for the + *	new address value.  @idx can be -1 if the address is a new addition. + * + *	Returns a negative error number or the index of the filter with the new + *	MAC value. + */ +int t4_change_mac(struct adapter *adap, unsigned int mbox, unsigned int viid, +		  int idx, const u8 *addr, bool persist, bool add_smt) +{ +	int ret, mode; +	struct fw_vi_mac_cmd c; +	struct fw_vi_mac_exact *p = c.u.exact; + +	if (idx < 0)                             /* new allocation */ +		idx = persist ? FW_VI_MAC_ADD_PERSIST_MAC : FW_VI_MAC_ADD_MAC; +	mode = add_smt ? FW_VI_MAC_SMT_AND_MPSTCAM : FW_VI_MAC_MPS_TCAM_ENTRY; + +	memset(&c, 0, sizeof(c)); +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_MAC_CMD) | FW_CMD_REQUEST | +			     FW_CMD_WRITE | FW_VI_MAC_CMD_VIID(viid)); +	c.freemacs_to_len16 = htonl(FW_CMD_LEN16(1)); +	p->valid_to_idx = htons(FW_VI_MAC_CMD_VALID | +				FW_VI_MAC_CMD_SMAC_RESULT(mode) | +				FW_VI_MAC_CMD_IDX(idx)); +	memcpy(p->macaddr, addr, sizeof(p->macaddr)); + +	ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +	if (ret == 0) { +		ret = FW_VI_MAC_CMD_IDX_GET(ntohs(p->valid_to_idx)); +		if (ret >= NEXACT_MAC) +			ret = -ENOMEM; +	} +	return ret; +} + +/** + *	t4_set_addr_hash - program the MAC inexact-match hash filter + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@ucast: whether the hash filter should also match unicast addresses + *	@vec: the value to be written to the hash filter + *	@sleep_ok: call is allowed to sleep + * + *	Sets the 64-bit inexact-match hash filter for a virtual interface. + */ +int t4_set_addr_hash(struct adapter *adap, unsigned int mbox, unsigned int viid, +		     bool ucast, u64 vec, bool sleep_ok) +{ +	struct fw_vi_mac_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_MAC_CMD) | FW_CMD_REQUEST | +			     FW_CMD_WRITE | FW_VI_ENABLE_CMD_VIID(viid)); +	c.freemacs_to_len16 = htonl(FW_VI_MAC_CMD_HASHVECEN | +				    FW_VI_MAC_CMD_HASHUNIEN(ucast) | +				    FW_CMD_LEN16(1)); +	c.u.hash.hashvec = cpu_to_be64(vec); +	return t4_wr_mbox_meat(adap, mbox, &c, sizeof(c), NULL, sleep_ok); +} + +/** + *	t4_enable_vi - enable/disable a virtual interface + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@rx_en: 1=enable Rx, 0=disable Rx + *	@tx_en: 1=enable Tx, 0=disable Tx + * + *	Enables/disables a virtual interface. + */ +int t4_enable_vi(struct adapter *adap, unsigned int mbox, unsigned int viid, +		 bool rx_en, bool tx_en) +{ +	struct fw_vi_enable_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST | +			     FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid)); +	c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_IEN(rx_en) | +			       FW_VI_ENABLE_CMD_EEN(tx_en) | FW_LEN16(c)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_identify_port - identify a VI's port by blinking its LED + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@viid: the VI id + *	@nblinks: how many times to blink LED at 2.5 Hz + * + *	Identifies a VI's port by blinking its LED. + */ +int t4_identify_port(struct adapter *adap, unsigned int mbox, unsigned int viid, +		     unsigned int nblinks) +{ +	struct fw_vi_enable_cmd c; + +	c.op_to_viid = htonl(FW_CMD_OP(FW_VI_ENABLE_CMD) | FW_CMD_REQUEST | +			     FW_CMD_EXEC | FW_VI_ENABLE_CMD_VIID(viid)); +	c.ien_to_len16 = htonl(FW_VI_ENABLE_CMD_LED | FW_LEN16(c)); +	c.blinkdur = htons(nblinks); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_iq_start_stop - enable/disable an ingress queue and its FLs + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@start: %true to enable the queues, %false to disable them + *	@pf: the PF owning the queues + *	@vf: the VF owning the queues + *	@iqid: ingress queue id + *	@fl0id: FL0 queue id or 0xffff if no attached FL0 + *	@fl1id: FL1 queue id or 0xffff if no attached FL1 + * + *	Starts or stops an ingress queue and its associated FLs, if any. + */ +int t4_iq_start_stop(struct adapter *adap, unsigned int mbox, bool start, +		     unsigned int pf, unsigned int vf, unsigned int iqid, +		     unsigned int fl0id, unsigned int fl1id) +{ +	struct fw_iq_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_IQ_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_IQ_CMD_PFN(pf) | +			    FW_IQ_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_IQ_CMD_IQSTART(start) | +				 FW_IQ_CMD_IQSTOP(!start) | FW_LEN16(c)); +	c.iqid = htons(iqid); +	c.fl0id = htons(fl0id); +	c.fl1id = htons(fl1id); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_iq_free - free an ingress queue and its FLs + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF owning the queues + *	@vf: the VF owning the queues + *	@iqtype: the ingress queue type + *	@iqid: ingress queue id + *	@fl0id: FL0 queue id or 0xffff if no attached FL0 + *	@fl1id: FL1 queue id or 0xffff if no attached FL1 + * + *	Frees an ingress queue and its associated FLs, if any. + */ +int t4_iq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +	       unsigned int vf, unsigned int iqtype, unsigned int iqid, +	       unsigned int fl0id, unsigned int fl1id) +{ +	struct fw_iq_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_IQ_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_IQ_CMD_PFN(pf) | +			    FW_IQ_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_IQ_CMD_FREE | FW_LEN16(c)); +	c.type_to_iqandstindex = htonl(FW_IQ_CMD_TYPE(iqtype)); +	c.iqid = htons(iqid); +	c.fl0id = htons(fl0id); +	c.fl1id = htons(fl1id); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_eth_eq_free - free an Ethernet egress queue + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF owning the queue + *	@vf: the VF owning the queue + *	@eqid: egress queue id + * + *	Frees an Ethernet egress queue. + */ +int t4_eth_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		   unsigned int vf, unsigned int eqid) +{ +	struct fw_eq_eth_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_ETH_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_EQ_ETH_CMD_PFN(pf) | +			    FW_EQ_ETH_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_EQ_ETH_CMD_FREE | FW_LEN16(c)); +	c.eqid_pkd = htonl(FW_EQ_ETH_CMD_EQID(eqid)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_ctrl_eq_free - free a control egress queue + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF owning the queue + *	@vf: the VF owning the queue + *	@eqid: egress queue id + * + *	Frees a control egress queue. + */ +int t4_ctrl_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int eqid) +{ +	struct fw_eq_ctrl_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_CTRL_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_EQ_CTRL_CMD_PFN(pf) | +			    FW_EQ_CTRL_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_EQ_CTRL_CMD_FREE | FW_LEN16(c)); +	c.cmpliqid_eqid = htonl(FW_EQ_CTRL_CMD_EQID(eqid)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_ofld_eq_free - free an offload egress queue + *	@adap: the adapter + *	@mbox: mailbox to use for the FW command + *	@pf: the PF owning the queue + *	@vf: the VF owning the queue + *	@eqid: egress queue id + * + *	Frees a control egress queue. + */ +int t4_ofld_eq_free(struct adapter *adap, unsigned int mbox, unsigned int pf, +		    unsigned int vf, unsigned int eqid) +{ +	struct fw_eq_ofld_cmd c; + +	memset(&c, 0, sizeof(c)); +	c.op_to_vfn = htonl(FW_CMD_OP(FW_EQ_OFLD_CMD) | FW_CMD_REQUEST | +			    FW_CMD_EXEC | FW_EQ_OFLD_CMD_PFN(pf) | +			    FW_EQ_OFLD_CMD_VFN(vf)); +	c.alloc_to_len16 = htonl(FW_EQ_OFLD_CMD_FREE | FW_LEN16(c)); +	c.eqid_pkd = htonl(FW_EQ_OFLD_CMD_EQID(eqid)); +	return t4_wr_mbox(adap, mbox, &c, sizeof(c), NULL); +} + +/** + *	t4_handle_fw_rpl - process a FW reply message + *	@adap: the adapter + *	@rpl: start of the FW message + * + *	Processes a FW message, such as link state change messages. + */ +int t4_handle_fw_rpl(struct adapter *adap, const __be64 *rpl) +{ +	u8 opcode = *(const u8 *)rpl; + +	if (opcode == FW_PORT_CMD) {    /* link/module state change message */ +		int speed = 0, fc = 0; +		const struct fw_port_cmd *p = (void *)rpl; +		int chan = FW_PORT_CMD_PORTID_GET(ntohl(p->op_to_portid)); +		int port = adap->chan_map[chan]; +		struct port_info *pi = adap2pinfo(adap, port); +		struct link_config *lc = &pi->link_cfg; +		u32 stat = ntohl(p->u.info.lstatus_to_modtype); +		int link_ok = (stat & FW_PORT_CMD_LSTATUS) != 0; +		u32 mod = FW_PORT_CMD_MODTYPE_GET(stat); + +		if (stat & FW_PORT_CMD_RXPAUSE) +			fc |= PAUSE_RX; +		if (stat & FW_PORT_CMD_TXPAUSE) +			fc |= PAUSE_TX; +		if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_100M)) +			speed = SPEED_100; +		else if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_1G)) +			speed = SPEED_1000; +		else if (stat & FW_PORT_CMD_LSPEED(FW_PORT_CAP_SPEED_10G)) +			speed = SPEED_10000; + +		if (link_ok != lc->link_ok || speed != lc->speed || +		    fc != lc->fc) {                    /* something changed */ +			lc->link_ok = link_ok; +			lc->speed = speed; +			lc->fc = fc; +			t4_os_link_changed(adap, port, link_ok); +		} +		if (mod != pi->mod_type) { +			pi->mod_type = mod; +			t4_os_portmod_changed(adap, port); +		} +	} +	return 0; +} + +static void __devinit get_pci_mode(struct adapter *adapter, +				   struct pci_params *p) +{ +	u16 val; +	u32 pcie_cap = pci_pcie_cap(adapter->pdev); + +	if (pcie_cap) { +		pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA, +				     &val); +		p->speed = val & PCI_EXP_LNKSTA_CLS; +		p->width = (val & PCI_EXP_LNKSTA_NLW) >> 4; +	} +} + +/** + *	init_link_config - initialize a link's SW state + *	@lc: structure holding the link state + *	@caps: link capabilities + * + *	Initializes the SW state maintained for each link, including the link's + *	capabilities and default speed/flow-control/autonegotiation settings. + */ +static void __devinit init_link_config(struct link_config *lc, +				       unsigned int caps) +{ +	lc->supported = caps; +	lc->requested_speed = 0; +	lc->speed = 0; +	lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX; +	if (lc->supported & FW_PORT_CAP_ANEG) { +		lc->advertising = lc->supported & ADVERT_MASK; +		lc->autoneg = AUTONEG_ENABLE; +		lc->requested_fc |= PAUSE_AUTONEG; +	} else { +		lc->advertising = 0; +		lc->autoneg = AUTONEG_DISABLE; +	} +} + +static int __devinit wait_dev_ready(struct adapter *adap) +{ +	if (t4_read_reg(adap, PL_WHOAMI) != 0xffffffff) +		return 0; +	msleep(500); +	return t4_read_reg(adap, PL_WHOAMI) != 0xffffffff ? 0 : -EIO; +} + +/** + *	t4_prep_adapter - prepare SW and HW for operation + *	@adapter: the adapter + *	@reset: if true perform a HW reset + * + *	Initialize adapter SW state for the various HW modules, set initial + *	values for some adapter tunables, take PHYs out of reset, and + *	initialize the MDIO interface. + */ +int __devinit t4_prep_adapter(struct adapter *adapter) +{ +	int ret; + +	ret = wait_dev_ready(adapter); +	if (ret < 0) +		return ret; + +	get_pci_mode(adapter, &adapter->params.pci); +	adapter->params.rev = t4_read_reg(adapter, PL_REV); + +	ret = get_vpd_params(adapter, &adapter->params.vpd); +	if (ret < 0) +		return ret; + +	init_cong_ctrl(adapter->params.a_wnd, adapter->params.b_wnd); + +	/* +	 * Default port for debugging in case we can't reach FW. +	 */ +	adapter->params.nports = 1; +	adapter->params.portvec = 1; +	return 0; +} + +int __devinit t4_port_init(struct adapter *adap, int mbox, int pf, int vf) +{ +	u8 addr[6]; +	int ret, i, j = 0; +	struct fw_port_cmd c; + +	memset(&c, 0, sizeof(c)); + +	for_each_port(adap, i) { +		unsigned int rss_size; +		struct port_info *p = adap2pinfo(adap, i); + +		while ((adap->params.portvec & (1 << j)) == 0) +			j++; + +		c.op_to_portid = htonl(FW_CMD_OP(FW_PORT_CMD) | +				       FW_CMD_REQUEST | FW_CMD_READ | +				       FW_PORT_CMD_PORTID(j)); +		c.action_to_len16 = htonl( +			FW_PORT_CMD_ACTION(FW_PORT_ACTION_GET_PORT_INFO) | +			FW_LEN16(c)); +		ret = t4_wr_mbox(adap, mbox, &c, sizeof(c), &c); +		if (ret) +			return ret; + +		ret = t4_alloc_vi(adap, mbox, j, pf, vf, 1, addr, &rss_size); +		if (ret < 0) +			return ret; + +		p->viid = ret; +		p->tx_chan = j; +		p->lport = j; +		p->rss_size = rss_size; +		memcpy(adap->port[i]->dev_addr, addr, ETH_ALEN); +		memcpy(adap->port[i]->perm_addr, addr, ETH_ALEN); + +		ret = ntohl(c.u.info.lstatus_to_modtype); +		p->mdio_addr = (ret & FW_PORT_CMD_MDIOCAP) ? +			FW_PORT_CMD_MDIOADDR_GET(ret) : -1; +		p->port_type = FW_PORT_CMD_PTYPE_GET(ret); +		p->mod_type = FW_PORT_CMD_MODTYPE_GET(ret); + +		init_link_config(&p->link_cfg, ntohs(c.u.info.pcap)); +		j++; +	} +	return 0; +} diff --git a/drivers/net/cxgb4/t4_hw.h b/drivers/net/cxgb4/t4_hw.h new file mode 100644 index 00000000000..025623285c9 --- /dev/null +++ b/drivers/net/cxgb4/t4_hw.h @@ -0,0 +1,100 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __T4_HW_H +#define __T4_HW_H + +#include <linux/types.h> + +enum { +	NCHAN          = 4,     /* # of HW channels */ +	MAX_MTU        = 9600,  /* max MAC MTU, excluding header + FCS */ +	EEPROMSIZE     = 17408, /* Serial EEPROM physical size */ +	EEPROMVSIZE    = 32768, /* Serial EEPROM virtual address space size */ +	RSS_NENTRIES   = 2048,  /* # of entries in RSS mapping table */ +	TCB_SIZE       = 128,   /* TCB size */ +	NMTUS          = 16,    /* size of MTU table */ +	NCCTRL_WIN     = 32,    /* # of congestion control windows */ +	NEXACT_MAC     = 336,   /* # of exact MAC address filters */ +	L2T_SIZE       = 4096,  /* # of L2T entries */ +	MBOX_LEN       = 64,    /* mailbox size in bytes */ +	TRACE_LEN      = 112,   /* length of trace data and mask */ +	FILTER_OPT_LEN = 36,    /* filter tuple width for optional components */ +	NWOL_PAT       = 8,     /* # of WoL patterns */ +	WOL_PAT_LEN    = 128,   /* length of WoL patterns */ +}; + +enum { +	SF_PAGE_SIZE = 256,           /* serial flash page size */ +	SF_SEC_SIZE = 64 * 1024,      /* serial flash sector size */ +	SF_SIZE = SF_SEC_SIZE * 16,   /* serial flash size */ +}; + +enum { RSP_TYPE_FLBUF, RSP_TYPE_CPL, RSP_TYPE_INTR }; /* response entry types */ + +enum { MBOX_OWNER_NONE, MBOX_OWNER_FW, MBOX_OWNER_DRV };    /* mailbox owners */ + +enum { +	SGE_MAX_WR_LEN = 512,     /* max WR size in bytes */ +	SGE_NTIMERS = 6,          /* # of interrupt holdoff timer values */ +	SGE_NCOUNTERS = 4,        /* # of interrupt packet counter values */ +}; + +struct sge_qstat {                /* data written to SGE queue status entries */ +	__be32 qid; +	__be16 cidx; +	__be16 pidx; +}; + +/* + * Structure for last 128 bits of response descriptors + */ +struct rsp_ctrl { +	__be32 hdrbuflen_pidx; +	__be32 pldbuflen_qid; +	union { +		u8 type_gen; +		__be64 last_flit; +	}; +}; + +#define RSPD_NEWBUF 0x80000000U +#define RSPD_LEN    0x7fffffffU + +#define RSPD_GEN(x)  ((x) >> 7) +#define RSPD_TYPE(x) (((x) >> 4) & 3) + +#define QINTR_CNT_EN       0x1 +#define QINTR_TIMER_IDX(x) ((x) << 1) +#endif /* __T4_HW_H */ diff --git a/drivers/net/cxgb4/t4_msg.h b/drivers/net/cxgb4/t4_msg.h new file mode 100644 index 00000000000..fdb11744314 --- /dev/null +++ b/drivers/net/cxgb4/t4_msg.h @@ -0,0 +1,664 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2003-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __T4_MSG_H +#define __T4_MSG_H + +#include <linux/types.h> + +enum { +	CPL_PASS_OPEN_REQ     = 0x1, +	CPL_PASS_ACCEPT_RPL   = 0x2, +	CPL_ACT_OPEN_REQ      = 0x3, +	CPL_SET_TCB_FIELD     = 0x5, +	CPL_GET_TCB           = 0x6, +	CPL_CLOSE_CON_REQ     = 0x8, +	CPL_CLOSE_LISTSRV_REQ = 0x9, +	CPL_ABORT_REQ         = 0xA, +	CPL_ABORT_RPL         = 0xB, +	CPL_RX_DATA_ACK       = 0xD, +	CPL_TX_PKT            = 0xE, +	CPL_L2T_WRITE_REQ     = 0x12, +	CPL_TID_RELEASE       = 0x1A, + +	CPL_CLOSE_LISTSRV_RPL = 0x20, +	CPL_L2T_WRITE_RPL     = 0x23, +	CPL_PASS_OPEN_RPL     = 0x24, +	CPL_ACT_OPEN_RPL      = 0x25, +	CPL_PEER_CLOSE        = 0x26, +	CPL_ABORT_REQ_RSS     = 0x2B, +	CPL_ABORT_RPL_RSS     = 0x2D, + +	CPL_CLOSE_CON_RPL     = 0x32, +	CPL_ISCSI_HDR         = 0x33, +	CPL_RDMA_CQE          = 0x35, +	CPL_RDMA_CQE_READ_RSP = 0x36, +	CPL_RDMA_CQE_ERR      = 0x37, +	CPL_RX_DATA           = 0x39, +	CPL_SET_TCB_RPL       = 0x3A, +	CPL_RX_PKT            = 0x3B, +	CPL_RX_DDP_COMPLETE   = 0x3F, + +	CPL_ACT_ESTABLISH     = 0x40, +	CPL_PASS_ESTABLISH    = 0x41, +	CPL_RX_DATA_DDP       = 0x42, +	CPL_PASS_ACCEPT_REQ   = 0x44, + +	CPL_RDMA_READ_REQ     = 0x60, + +	CPL_PASS_OPEN_REQ6    = 0x81, +	CPL_ACT_OPEN_REQ6     = 0x83, + +	CPL_RDMA_TERMINATE    = 0xA2, +	CPL_RDMA_WRITE        = 0xA4, +	CPL_SGE_EGR_UPDATE    = 0xA5, + +	CPL_TRACE_PKT         = 0xB0, + +	CPL_FW4_MSG           = 0xC0, +	CPL_FW4_PLD           = 0xC1, +	CPL_FW4_ACK           = 0xC3, + +	CPL_FW6_MSG           = 0xE0, +	CPL_FW6_PLD           = 0xE1, +	CPL_TX_PKT_LSO        = 0xED, +	CPL_TX_PKT_XT         = 0xEE, + +	NUM_CPL_CMDS +}; + +enum CPL_error { +	CPL_ERR_NONE               = 0, +	CPL_ERR_TCAM_FULL          = 3, +	CPL_ERR_BAD_LENGTH         = 15, +	CPL_ERR_BAD_ROUTE          = 18, +	CPL_ERR_CONN_RESET         = 20, +	CPL_ERR_CONN_EXIST_SYNRECV = 21, +	CPL_ERR_CONN_EXIST         = 22, +	CPL_ERR_ARP_MISS           = 23, +	CPL_ERR_BAD_SYN            = 24, +	CPL_ERR_CONN_TIMEDOUT      = 30, +	CPL_ERR_XMIT_TIMEDOUT      = 31, +	CPL_ERR_PERSIST_TIMEDOUT   = 32, +	CPL_ERR_FINWAIT2_TIMEDOUT  = 33, +	CPL_ERR_KEEPALIVE_TIMEDOUT = 34, +	CPL_ERR_RTX_NEG_ADVICE     = 35, +	CPL_ERR_PERSIST_NEG_ADVICE = 36, +	CPL_ERR_ABORT_FAILED       = 42, +	CPL_ERR_IWARP_FLM          = 50, +}; + +enum { +	ULP_MODE_NONE          = 0, +	ULP_MODE_ISCSI         = 2, +	ULP_MODE_RDMA          = 4, +	ULP_MODE_FCOE          = 6, +}; + +enum { +	ULP_CRC_HEADER = 1 << 0, +	ULP_CRC_DATA   = 1 << 1 +}; + +enum { +	CPL_ABORT_SEND_RST = 0, +	CPL_ABORT_NO_RST, +}; + +enum {                     /* TX_PKT_XT checksum types */ +	TX_CSUM_TCP    = 0, +	TX_CSUM_UDP    = 1, +	TX_CSUM_CRC16  = 4, +	TX_CSUM_CRC32  = 5, +	TX_CSUM_CRC32C = 6, +	TX_CSUM_FCOE   = 7, +	TX_CSUM_TCPIP  = 8, +	TX_CSUM_UDPIP  = 9, +	TX_CSUM_TCPIP6 = 10, +	TX_CSUM_UDPIP6 = 11, +	TX_CSUM_IP     = 12, +}; + +union opcode_tid { +	__be32 opcode_tid; +	u8 opcode; +}; + +#define CPL_OPCODE(x) ((x) << 24) +#define MK_OPCODE_TID(opcode, tid) (CPL_OPCODE(opcode) | (tid)) +#define OPCODE_TID(cmd) ((cmd)->ot.opcode_tid) +#define GET_TID(cmd) (ntohl(OPCODE_TID(cmd)) & 0xFFFFFF) + +/* partitioning of TID fields that also carry a queue id */ +#define GET_TID_TID(x) ((x) & 0x3fff) +#define GET_TID_QID(x) (((x) >> 14) & 0x3ff) +#define TID_QID(x)     ((x) << 14) + +struct rss_header { +	u8 opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) +	u8 channel:2; +	u8 filter_hit:1; +	u8 filter_tid:1; +	u8 hash_type:2; +	u8 ipv6:1; +	u8 send2fw:1; +#else +	u8 send2fw:1; +	u8 ipv6:1; +	u8 hash_type:2; +	u8 filter_tid:1; +	u8 filter_hit:1; +	u8 channel:2; +#endif +	__be16 qid; +	__be32 hash_val; +}; + +struct work_request_hdr { +	__be32 wr_hi; +	__be32 wr_mid; +	__be64 wr_lo; +}; + +#define WR_HDR struct work_request_hdr wr + +struct cpl_pass_open_req { +	WR_HDR; +	union opcode_tid ot; +	__be16 local_port; +	__be16 peer_port; +	__be32 local_ip; +	__be32 peer_ip; +	__be64 opt0; +#define TX_CHAN(x)    ((x) << 2) +#define DELACK(x)     ((x) << 5) +#define ULP_MODE(x)   ((x) << 8) +#define RCV_BUFSIZ(x) ((x) << 12) +#define DSCP(x)       ((x) << 22) +#define SMAC_SEL(x)   ((u64)(x) << 28) +#define L2T_IDX(x)    ((u64)(x) << 36) +#define NAGLE(x)      ((u64)(x) << 49) +#define WND_SCALE(x)  ((u64)(x) << 50) +#define KEEP_ALIVE(x) ((u64)(x) << 54) +#define MSS_IDX(x)    ((u64)(x) << 60) +	__be64 opt1; +#define SYN_RSS_ENABLE   (1 << 0) +#define SYN_RSS_QUEUE(x) ((x) << 2) +#define CONN_POLICY_ASK  (1 << 22) +}; + +struct cpl_pass_open_req6 { +	WR_HDR; +	union opcode_tid ot; +	__be16 local_port; +	__be16 peer_port; +	__be64 local_ip_hi; +	__be64 local_ip_lo; +	__be64 peer_ip_hi; +	__be64 peer_ip_lo; +	__be64 opt0; +	__be64 opt1; +}; + +struct cpl_pass_open_rpl { +	union opcode_tid ot; +	u8 rsvd[3]; +	u8 status; +}; + +struct cpl_pass_accept_rpl { +	WR_HDR; +	union opcode_tid ot; +	__be32 opt2; +#define RSS_QUEUE(x)         ((x) << 0) +#define RSS_QUEUE_VALID      (1 << 10) +#define RX_COALESCE_VALID(x) ((x) << 11) +#define RX_COALESCE(x)       ((x) << 12) +#define TX_QUEUE(x)          ((x) << 23) +#define RX_CHANNEL(x)        ((x) << 26) +#define WND_SCALE_EN(x)      ((x) << 28) +#define TSTAMPS_EN(x)        ((x) << 29) +#define SACK_EN(x)           ((x) << 30) +	__be64 opt0; +}; + +struct cpl_act_open_req { +	WR_HDR; +	union opcode_tid ot; +	__be16 local_port; +	__be16 peer_port; +	__be32 local_ip; +	__be32 peer_ip; +	__be64 opt0; +	__be32 params; +	__be32 opt2; +}; + +struct cpl_act_open_req6 { +	WR_HDR; +	union opcode_tid ot; +	__be16 local_port; +	__be16 peer_port; +	__be64 local_ip_hi; +	__be64 local_ip_lo; +	__be64 peer_ip_hi; +	__be64 peer_ip_lo; +	__be64 opt0; +	__be32 params; +	__be32 opt2; +}; + +struct cpl_act_open_rpl { +	union opcode_tid ot; +	__be32 atid_status; +#define GET_AOPEN_STATUS(x) ((x) & 0xff) +#define GET_AOPEN_ATID(x)   (((x) >> 8) & 0xffffff) +}; + +struct cpl_pass_establish { +	union opcode_tid ot; +	__be32 rsvd; +	__be32 tos_stid; +#define GET_POPEN_TID(x) ((x) & 0xffffff) +#define GET_POPEN_TOS(x) (((x) >> 24) & 0xff) +	__be16 mac_idx; +	__be16 tcp_opt; +#define GET_TCPOPT_WSCALE_OK(x)  (((x) >> 5) & 1) +#define GET_TCPOPT_SACK(x)       (((x) >> 6) & 1) +#define GET_TCPOPT_TSTAMP(x)     (((x) >> 7) & 1) +#define GET_TCPOPT_SND_WSCALE(x) (((x) >> 8) & 0xf) +#define GET_TCPOPT_MSS(x)        (((x) >> 12) & 0xf) +	__be32 snd_isn; +	__be32 rcv_isn; +}; + +struct cpl_act_establish { +	union opcode_tid ot; +	__be32 rsvd; +	__be32 tos_atid; +	__be16 mac_idx; +	__be16 tcp_opt; +	__be32 snd_isn; +	__be32 rcv_isn; +}; + +struct cpl_get_tcb { +	WR_HDR; +	union opcode_tid ot; +	__be16 reply_ctrl; +#define QUEUENO(x)    ((x) << 0) +#define REPLY_CHAN(x) ((x) << 14) +#define NO_REPLY(x)   ((x) << 15) +	__be16 cookie; +}; + +struct cpl_set_tcb_field { +	WR_HDR; +	union opcode_tid ot; +	__be16 reply_ctrl; +	__be16 word_cookie; +#define TCB_WORD(x)   ((x) << 0) +#define TCB_COOKIE(x) ((x) << 5) +	__be64 mask; +	__be64 val; +}; + +struct cpl_set_tcb_rpl { +	union opcode_tid ot; +	__be16 rsvd; +	u8 cookie; +	u8 status; +	__be64 oldval; +}; + +struct cpl_close_con_req { +	WR_HDR; +	union opcode_tid ot; +	__be32 rsvd; +}; + +struct cpl_close_con_rpl { +	union opcode_tid ot; +	u8 rsvd[3]; +	u8 status; +	__be32 snd_nxt; +	__be32 rcv_nxt; +}; + +struct cpl_close_listsvr_req { +	WR_HDR; +	union opcode_tid ot; +	__be16 reply_ctrl; +#define LISTSVR_IPV6 (1 << 14) +	__be16 rsvd; +}; + +struct cpl_close_listsvr_rpl { +	union opcode_tid ot; +	u8 rsvd[3]; +	u8 status; +}; + +struct cpl_abort_req_rss { +	union opcode_tid ot; +	u8 rsvd[3]; +	u8 status; +}; + +struct cpl_abort_req { +	WR_HDR; +	union opcode_tid ot; +	__be32 rsvd0; +	u8 rsvd1; +	u8 cmd; +	u8 rsvd2[6]; +}; + +struct cpl_abort_rpl_rss { +	union opcode_tid ot; +	u8 rsvd[3]; +	u8 status; +}; + +struct cpl_abort_rpl { +	WR_HDR; +	union opcode_tid ot; +	__be32 rsvd0; +	u8 rsvd1; +	u8 cmd; +	u8 rsvd2[6]; +}; + +struct cpl_peer_close { +	union opcode_tid ot; +	__be32 rcv_nxt; +}; + +struct cpl_tid_release { +	WR_HDR; +	union opcode_tid ot; +	__be32 rsvd; +}; + +struct cpl_tx_pkt_core { +	__be32 ctrl0; +#define TXPKT_VF(x)        ((x) << 0) +#define TXPKT_PF(x)        ((x) << 8) +#define TXPKT_VF_VLD       (1 << 11) +#define TXPKT_OVLAN_IDX(x) ((x) << 12) +#define TXPKT_INTF(x)      ((x) << 16) +#define TXPKT_INS_OVLAN    (1 << 21) +#define TXPKT_OPCODE(x)    ((x) << 24) +	__be16 pack; +	__be16 len; +	__be64 ctrl1; +#define TXPKT_CSUM_END(x)   ((x) << 12) +#define TXPKT_CSUM_START(x) ((x) << 20) +#define TXPKT_IPHDR_LEN(x)  ((u64)(x) << 20) +#define TXPKT_CSUM_LOC(x)   ((u64)(x) << 30) +#define TXPKT_ETHHDR_LEN(x) ((u64)(x) << 34) +#define TXPKT_CSUM_TYPE(x)  ((u64)(x) << 40) +#define TXPKT_VLAN(x)       ((u64)(x) << 44) +#define TXPKT_VLAN_VLD      (1ULL << 60) +#define TXPKT_IPCSUM_DIS    (1ULL << 62) +#define TXPKT_L4CSUM_DIS    (1ULL << 63) +}; + +struct cpl_tx_pkt { +	WR_HDR; +	struct cpl_tx_pkt_core c; +}; + +#define cpl_tx_pkt_xt cpl_tx_pkt + +struct cpl_tx_pkt_lso { +	WR_HDR; +	__be32 lso_ctrl; +#define LSO_TCPHDR_LEN(x) ((x) << 0) +#define LSO_IPHDR_LEN(x)  ((x) << 4) +#define LSO_ETHHDR_LEN(x) ((x) << 16) +#define LSO_IPV6(x)       ((x) << 20) +#define LSO_LAST_SLICE    (1 << 22) +#define LSO_FIRST_SLICE   (1 << 23) +#define LSO_OPCODE(x)     ((x) << 24) +	__be16 ipid_ofst; +	__be16 mss; +	__be32 seqno_offset; +	__be32 len; +	/* encapsulated CPL (TX_PKT, TX_PKT_XT or TX_DATA) follows here */ +}; + +struct cpl_iscsi_hdr { +	union opcode_tid ot; +	__be16 pdu_len_ddp; +#define ISCSI_PDU_LEN(x) ((x) & 0x7FFF) +#define ISCSI_DDP        (1 << 15) +	__be16 len; +	__be32 seq; +	__be16 urg; +	u8 rsvd; +	u8 status; +}; + +struct cpl_rx_data { +	union opcode_tid ot; +	__be16 rsvd; +	__be16 len; +	__be32 seq; +	__be16 urg; +#if defined(__LITTLE_ENDIAN_BITFIELD) +	u8 dack_mode:2; +	u8 psh:1; +	u8 heartbeat:1; +	u8 ddp_off:1; +	u8 :3; +#else +	u8 :3; +	u8 ddp_off:1; +	u8 heartbeat:1; +	u8 psh:1; +	u8 dack_mode:2; +#endif +	u8 status; +}; + +struct cpl_rx_data_ack { +	WR_HDR; +	union opcode_tid ot; +	__be32 credit_dack; +#define RX_CREDITS(x)   ((x) << 0) +#define RX_FORCE_ACK(x) ((x) << 28) +}; + +struct cpl_rx_pkt { +	u8 opcode; +#if defined(__LITTLE_ENDIAN_BITFIELD) +	u8 iff:4; +	u8 csum_calc:1; +	u8 ipmi_pkt:1; +	u8 vlan_ex:1; +	u8 ip_frag:1; +#else +	u8 ip_frag:1; +	u8 vlan_ex:1; +	u8 ipmi_pkt:1; +	u8 csum_calc:1; +	u8 iff:4; +#endif +	__be16 csum; +	__be16 vlan; +	__be16 len; +	__be32 l2info; +#define RXF_UDP (1 << 22) +#define RXF_TCP (1 << 23) +	__be16 hdr_len; +	__be16 err_vec; +}; + +struct cpl_trace_pkt { +	u8 opcode; +	u8 intf; +#if defined(__LITTLE_ENDIAN_BITFIELD) +	u8 runt:4; +	u8 filter_hit:4; +	u8 :6; +	u8 err:1; +	u8 trunc:1; +#else +	u8 filter_hit:4; +	u8 runt:4; +	u8 trunc:1; +	u8 err:1; +	u8 :6; +#endif +	__be16 rsvd; +	__be16 len; +	__be64 tstamp; +}; + +struct cpl_l2t_write_req { +	WR_HDR; +	union opcode_tid ot; +	__be16 params; +#define L2T_W_INFO(x)    ((x) << 2) +#define L2T_W_PORT(x)    ((x) << 8) +#define L2T_W_NOREPLY(x) ((x) << 15) +	__be16 l2t_idx; +	__be16 vlan; +	u8 dst_mac[6]; +}; + +struct cpl_l2t_write_rpl { +	union opcode_tid ot; +	u8 status; +	u8 rsvd[3]; +}; + +struct cpl_rdma_terminate { +	union opcode_tid ot; +	__be16 rsvd; +	__be16 len; +}; + +struct cpl_sge_egr_update { +	__be32 opcode_qid; +#define EGR_QID(x) ((x) & 0x1FFFF) +	__be16 cidx; +	__be16 pidx; +}; + +struct cpl_fw4_pld { +	u8 opcode; +	u8 rsvd0[3]; +	u8 type; +	u8 rsvd1; +	__be16 len; +	__be64 data; +	__be64 rsvd2; +}; + +struct cpl_fw6_pld { +	u8 opcode; +	u8 rsvd[5]; +	__be16 len; +	__be64 data[4]; +}; + +struct cpl_fw4_msg { +	u8 opcode; +	u8 type; +	__be16 rsvd0; +	__be32 rsvd1; +	__be64 data[2]; +}; + +struct cpl_fw4_ack { +	union opcode_tid ot; +	u8 credits; +	u8 rsvd0[2]; +	u8 seq_vld; +	__be32 snd_nxt; +	__be32 snd_una; +	__be64 rsvd1; +}; + +struct cpl_fw6_msg { +	u8 opcode; +	u8 type; +	__be16 rsvd0; +	__be32 rsvd1; +	__be64 data[4]; +}; + +enum { +	ULP_TX_MEM_READ = 2, +	ULP_TX_MEM_WRITE = 3, +	ULP_TX_PKT = 4 +}; + +enum { +	ULP_TX_SC_NOOP = 0x80, +	ULP_TX_SC_IMM  = 0x81, +	ULP_TX_SC_DSGL = 0x82, +	ULP_TX_SC_ISGL = 0x83 +}; + +struct ulptx_sge_pair { +	__be32 len[2]; +	__be64 addr[2]; +}; + +struct ulptx_sgl { +	__be32 cmd_nsge; +#define ULPTX_CMD(x) ((x) << 24) +#define ULPTX_NSGE(x) ((x) << 0) +	__be32 len0; +	__be64 addr0; +	struct ulptx_sge_pair sge[0]; +}; + +struct ulp_mem_io { +	WR_HDR; +	__be32 cmd; +#define ULP_MEMIO_ORDER(x) ((x) << 23) +	__be32 len16;             /* command length */ +	__be32 dlen;              /* data length in 32-byte units */ +#define ULP_MEMIO_DATA_LEN(x) ((x) << 0) +	__be32 lock_addr; +#define ULP_MEMIO_ADDR(x) ((x) << 0) +#define ULP_MEMIO_LOCK(x) ((x) << 31) +}; + +#endif  /* __T4_MSG_H */ diff --git a/drivers/net/cxgb4/t4_regs.h b/drivers/net/cxgb4/t4_regs.h new file mode 100644 index 00000000000..5ed56483cbc --- /dev/null +++ b/drivers/net/cxgb4/t4_regs.h @@ -0,0 +1,878 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef __T4_REGS_H +#define __T4_REGS_H + +#define MYPF_BASE 0x1b000 +#define MYPF_REG(reg_addr) (MYPF_BASE + (reg_addr)) + +#define PF0_BASE 0x1e000 +#define PF0_REG(reg_addr) (PF0_BASE + (reg_addr)) + +#define PF_STRIDE 0x400 +#define PF_BASE(idx) (PF0_BASE + (idx) * PF_STRIDE) +#define PF_REG(idx, reg) (PF_BASE(idx) + (reg)) + +#define MYPORT_BASE 0x1c000 +#define MYPORT_REG(reg_addr) (MYPORT_BASE + (reg_addr)) + +#define PORT0_BASE 0x20000 +#define PORT0_REG(reg_addr) (PORT0_BASE + (reg_addr)) + +#define PORT_STRIDE 0x2000 +#define PORT_BASE(idx) (PORT0_BASE + (idx) * PORT_STRIDE) +#define PORT_REG(idx, reg) (PORT_BASE(idx) + (reg)) + +#define EDC_STRIDE (EDC_1_BASE_ADDR - EDC_0_BASE_ADDR) +#define EDC_REG(reg, idx) (reg + EDC_STRIDE * idx) + +#define PCIE_MEM_ACCESS_REG(reg_addr, idx) ((reg_addr) + (idx) * 8) +#define PCIE_MAILBOX_REG(reg_addr, idx) ((reg_addr) + (idx) * 8) +#define MC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) +#define EDC_BIST_STATUS_REG(reg_addr, idx) ((reg_addr) + (idx) * 4) + +#define SGE_PF_KDOORBELL 0x0 +#define  QID_MASK    0xffff8000U +#define  QID_SHIFT   15 +#define  QID(x)      ((x) << QID_SHIFT) +#define  DBPRIO      0x00004000U +#define  PIDX_MASK   0x00003fffU +#define  PIDX_SHIFT  0 +#define  PIDX(x)     ((x) << PIDX_SHIFT) + +#define SGE_PF_GTS 0x4 +#define  INGRESSQID_MASK   0xffff0000U +#define  INGRESSQID_SHIFT  16 +#define  INGRESSQID(x)     ((x) << INGRESSQID_SHIFT) +#define  TIMERREG_MASK     0x0000e000U +#define  TIMERREG_SHIFT    13 +#define  TIMERREG(x)       ((x) << TIMERREG_SHIFT) +#define  SEINTARM_MASK     0x00001000U +#define  SEINTARM_SHIFT    12 +#define  SEINTARM(x)       ((x) << SEINTARM_SHIFT) +#define  CIDXINC_MASK      0x00000fffU +#define  CIDXINC_SHIFT     0 +#define  CIDXINC(x)        ((x) << CIDXINC_SHIFT) + +#define SGE_CONTROL 0x1008 +#define  DCASYSTYPE             0x00080000U +#define  RXPKTCPLMODE           0x00040000U +#define  EGRSTATUSPAGESIZE      0x00020000U +#define  PKTSHIFT_MASK          0x00001c00U +#define  PKTSHIFT_SHIFT         10 +#define  PKTSHIFT(x)            ((x) << PKTSHIFT_SHIFT) +#define  INGPCIEBOUNDARY_MASK   0x00000380U +#define  INGPCIEBOUNDARY_SHIFT  7 +#define  INGPCIEBOUNDARY(x)     ((x) << INGPCIEBOUNDARY_SHIFT) +#define  INGPADBOUNDARY_MASK    0x00000070U +#define  INGPADBOUNDARY_SHIFT   4 +#define  INGPADBOUNDARY(x)      ((x) << INGPADBOUNDARY_SHIFT) +#define  EGRPCIEBOUNDARY_MASK   0x0000000eU +#define  EGRPCIEBOUNDARY_SHIFT  1 +#define  EGRPCIEBOUNDARY(x)     ((x) << EGRPCIEBOUNDARY_SHIFT) +#define  GLOBALENABLE           0x00000001U + +#define SGE_HOST_PAGE_SIZE 0x100c +#define  HOSTPAGESIZEPF0_MASK   0x0000000fU +#define  HOSTPAGESIZEPF0_SHIFT  0 +#define  HOSTPAGESIZEPF0(x)     ((x) << HOSTPAGESIZEPF0_SHIFT) + +#define SGE_EGRESS_QUEUES_PER_PAGE_PF 0x1010 +#define  QUEUESPERPAGEPF0_MASK   0x0000000fU +#define  QUEUESPERPAGEPF0_GET(x) ((x) & QUEUESPERPAGEPF0_MASK) + +#define SGE_INT_CAUSE1 0x1024 +#define SGE_INT_CAUSE2 0x1030 +#define SGE_INT_CAUSE3 0x103c +#define  ERR_FLM_DBP               0x80000000U +#define  ERR_FLM_IDMA1             0x40000000U +#define  ERR_FLM_IDMA0             0x20000000U +#define  ERR_FLM_HINT              0x10000000U +#define  ERR_PCIE_ERROR3           0x08000000U +#define  ERR_PCIE_ERROR2           0x04000000U +#define  ERR_PCIE_ERROR1           0x02000000U +#define  ERR_PCIE_ERROR0           0x01000000U +#define  ERR_TIMER_ABOVE_MAX_QID   0x00800000U +#define  ERR_CPL_EXCEED_IQE_SIZE   0x00400000U +#define  ERR_INVALID_CIDX_INC      0x00200000U +#define  ERR_ITP_TIME_PAUSED       0x00100000U +#define  ERR_CPL_OPCODE_0          0x00080000U +#define  ERR_DROPPED_DB            0x00040000U +#define  ERR_DATA_CPL_ON_HIGH_QID1 0x00020000U +#define  ERR_DATA_CPL_ON_HIGH_QID0 0x00010000U +#define  ERR_BAD_DB_PIDX3          0x00008000U +#define  ERR_BAD_DB_PIDX2          0x00004000U +#define  ERR_BAD_DB_PIDX1          0x00002000U +#define  ERR_BAD_DB_PIDX0          0x00001000U +#define  ERR_ING_PCIE_CHAN         0x00000800U +#define  ERR_ING_CTXT_PRIO         0x00000400U +#define  ERR_EGR_CTXT_PRIO         0x00000200U +#define  DBFIFO_HP_INT             0x00000100U +#define  DBFIFO_LP_INT             0x00000080U +#define  REG_ADDRESS_ERR           0x00000040U +#define  INGRESS_SIZE_ERR          0x00000020U +#define  EGRESS_SIZE_ERR           0x00000010U +#define  ERR_INV_CTXT3             0x00000008U +#define  ERR_INV_CTXT2             0x00000004U +#define  ERR_INV_CTXT1             0x00000002U +#define  ERR_INV_CTXT0             0x00000001U + +#define SGE_INT_ENABLE3 0x1040 +#define SGE_FL_BUFFER_SIZE0 0x1044 +#define SGE_FL_BUFFER_SIZE1 0x1048 +#define SGE_INGRESS_RX_THRESHOLD 0x10a0 +#define  THRESHOLD_0_MASK   0x3f000000U +#define  THRESHOLD_0_SHIFT  24 +#define  THRESHOLD_0(x)     ((x) << THRESHOLD_0_SHIFT) +#define  THRESHOLD_0_GET(x) (((x) & THRESHOLD_0_MASK) >> THRESHOLD_0_SHIFT) +#define  THRESHOLD_1_MASK   0x003f0000U +#define  THRESHOLD_1_SHIFT  16 +#define  THRESHOLD_1(x)     ((x) << THRESHOLD_1_SHIFT) +#define  THRESHOLD_1_GET(x) (((x) & THRESHOLD_1_MASK) >> THRESHOLD_1_SHIFT) +#define  THRESHOLD_2_MASK   0x00003f00U +#define  THRESHOLD_2_SHIFT  8 +#define  THRESHOLD_2(x)     ((x) << THRESHOLD_2_SHIFT) +#define  THRESHOLD_2_GET(x) (((x) & THRESHOLD_2_MASK) >> THRESHOLD_2_SHIFT) +#define  THRESHOLD_3_MASK   0x0000003fU +#define  THRESHOLD_3_SHIFT  0 +#define  THRESHOLD_3(x)     ((x) << THRESHOLD_3_SHIFT) +#define  THRESHOLD_3_GET(x) (((x) & THRESHOLD_3_MASK) >> THRESHOLD_3_SHIFT) + +#define SGE_TIMER_VALUE_0_AND_1 0x10b8 +#define  TIMERVALUE0_MASK   0xffff0000U +#define  TIMERVALUE0_SHIFT  16 +#define  TIMERVALUE0(x)     ((x) << TIMERVALUE0_SHIFT) +#define  TIMERVALUE0_GET(x) (((x) & TIMERVALUE0_MASK) >> TIMERVALUE0_SHIFT) +#define  TIMERVALUE1_MASK   0x0000ffffU +#define  TIMERVALUE1_SHIFT  0 +#define  TIMERVALUE1(x)     ((x) << TIMERVALUE1_SHIFT) +#define  TIMERVALUE1_GET(x) (((x) & TIMERVALUE1_MASK) >> TIMERVALUE1_SHIFT) + +#define SGE_TIMER_VALUE_2_AND_3 0x10bc +#define SGE_TIMER_VALUE_4_AND_5 0x10c0 +#define SGE_DEBUG_INDEX 0x10cc +#define SGE_DEBUG_DATA_HIGH 0x10d0 +#define SGE_DEBUG_DATA_LOW 0x10d4 +#define SGE_INGRESS_QUEUES_PER_PAGE_PF 0x10f4 + +#define PCIE_PF_CLI 0x44 +#define PCIE_INT_CAUSE 0x3004 +#define  UNXSPLCPLERR  0x20000000U +#define  PCIEPINT      0x10000000U +#define  PCIESINT      0x08000000U +#define  RPLPERR       0x04000000U +#define  RXWRPERR      0x02000000U +#define  RXCPLPERR     0x01000000U +#define  PIOTAGPERR    0x00800000U +#define  MATAGPERR     0x00400000U +#define  INTXCLRPERR   0x00200000U +#define  FIDPERR       0x00100000U +#define  CFGSNPPERR    0x00080000U +#define  HRSPPERR      0x00040000U +#define  HREQPERR      0x00020000U +#define  HCNTPERR      0x00010000U +#define  DRSPPERR      0x00008000U +#define  DREQPERR      0x00004000U +#define  DCNTPERR      0x00002000U +#define  CRSPPERR      0x00001000U +#define  CREQPERR      0x00000800U +#define  CCNTPERR      0x00000400U +#define  TARTAGPERR    0x00000200U +#define  PIOREQPERR    0x00000100U +#define  PIOCPLPERR    0x00000080U +#define  MSIXDIPERR    0x00000040U +#define  MSIXDATAPERR  0x00000020U +#define  MSIXADDRHPERR 0x00000010U +#define  MSIXADDRLPERR 0x00000008U +#define  MSIDATAPERR   0x00000004U +#define  MSIADDRHPERR  0x00000002U +#define  MSIADDRLPERR  0x00000001U + +#define PCIE_NONFAT_ERR 0x3010 +#define PCIE_MEM_ACCESS_BASE_WIN 0x3068 +#define  PCIEOFST_MASK   0xfffffc00U +#define  BIR_MASK        0x00000300U +#define  BIR_SHIFT       8 +#define  BIR(x)          ((x) << BIR_SHIFT) +#define  WINDOW_MASK     0x000000ffU +#define  WINDOW_SHIFT    0 +#define  WINDOW(x)       ((x) << WINDOW_SHIFT) + +#define PCIE_CORE_UTL_SYSTEM_BUS_AGENT_STATUS 0x5908 +#define  RNPP 0x80000000U +#define  RPCP 0x20000000U +#define  RCIP 0x08000000U +#define  RCCP 0x04000000U +#define  RFTP 0x00800000U +#define  PTRP 0x00100000U + +#define PCIE_CORE_UTL_PCI_EXPRESS_PORT_STATUS 0x59a4 +#define  TPCP 0x40000000U +#define  TNPP 0x20000000U +#define  TFTP 0x10000000U +#define  TCAP 0x08000000U +#define  TCIP 0x04000000U +#define  RCAP 0x02000000U +#define  PLUP 0x00800000U +#define  PLDN 0x00400000U +#define  OTDD 0x00200000U +#define  GTRP 0x00100000U +#define  RDPE 0x00040000U +#define  TDCE 0x00020000U +#define  TDUE 0x00010000U + +#define MC_INT_CAUSE 0x7518 +#define  ECC_UE_INT_CAUSE 0x00000004U +#define  ECC_CE_INT_CAUSE 0x00000002U +#define  PERR_INT_CAUSE   0x00000001U + +#define MC_ECC_STATUS 0x751c +#define  ECC_CECNT_MASK   0xffff0000U +#define  ECC_CECNT_SHIFT  16 +#define  ECC_CECNT(x)     ((x) << ECC_CECNT_SHIFT) +#define  ECC_CECNT_GET(x) (((x) & ECC_CECNT_MASK) >> ECC_CECNT_SHIFT) +#define  ECC_UECNT_MASK   0x0000ffffU +#define  ECC_UECNT_SHIFT  0 +#define  ECC_UECNT(x)     ((x) << ECC_UECNT_SHIFT) +#define  ECC_UECNT_GET(x) (((x) & ECC_UECNT_MASK) >> ECC_UECNT_SHIFT) + +#define MC_BIST_CMD 0x7600 +#define  START_BIST          0x80000000U +#define  BIST_CMD_GAP_MASK   0x0000ff00U +#define  BIST_CMD_GAP_SHIFT  8 +#define  BIST_CMD_GAP(x)     ((x) << BIST_CMD_GAP_SHIFT) +#define  BIST_OPCODE_MASK    0x00000003U +#define  BIST_OPCODE_SHIFT   0 +#define  BIST_OPCODE(x)      ((x) << BIST_OPCODE_SHIFT) + +#define MC_BIST_CMD_ADDR 0x7604 +#define MC_BIST_CMD_LEN 0x7608 +#define MC_BIST_DATA_PATTERN 0x760c +#define  BIST_DATA_TYPE_MASK   0x0000000fU +#define  BIST_DATA_TYPE_SHIFT  0 +#define  BIST_DATA_TYPE(x)     ((x) << BIST_DATA_TYPE_SHIFT) + +#define MC_BIST_STATUS_RDATA 0x7688 + +#define MA_EXT_MEMORY_BAR 0x77c8 +#define  EXT_MEM_SIZE_MASK   0x00000fffU +#define  EXT_MEM_SIZE_SHIFT  0 +#define  EXT_MEM_SIZE_GET(x) (((x) & EXT_MEM_SIZE_MASK) >> EXT_MEM_SIZE_SHIFT) + +#define MA_TARGET_MEM_ENABLE 0x77d8 +#define  EXT_MEM_ENABLE 0x00000004U +#define  EDRAM1_ENABLE  0x00000002U +#define  EDRAM0_ENABLE  0x00000001U + +#define MA_INT_CAUSE 0x77e0 +#define  MEM_PERR_INT_CAUSE 0x00000002U +#define  MEM_WRAP_INT_CAUSE 0x00000001U + +#define MA_INT_WRAP_STATUS 0x77e4 +#define  MEM_WRAP_ADDRESS_MASK   0xfffffff0U +#define  MEM_WRAP_ADDRESS_SHIFT  4 +#define  MEM_WRAP_ADDRESS_GET(x) (((x) & MEM_WRAP_ADDRESS_MASK) >> MEM_WRAP_ADDRESS_SHIFT) +#define  MEM_WRAP_CLIENT_NUM_MASK   0x0000000fU +#define  MEM_WRAP_CLIENT_NUM_SHIFT  0 +#define  MEM_WRAP_CLIENT_NUM_GET(x) (((x) & MEM_WRAP_CLIENT_NUM_MASK) >> MEM_WRAP_CLIENT_NUM_SHIFT) + +#define MA_PARITY_ERROR_STATUS 0x77f4 + +#define EDC_0_BASE_ADDR 0x7900 + +#define EDC_BIST_CMD 0x7904 +#define EDC_BIST_CMD_ADDR 0x7908 +#define EDC_BIST_CMD_LEN 0x790c +#define EDC_BIST_DATA_PATTERN 0x7910 +#define EDC_BIST_STATUS_RDATA 0x7928 +#define EDC_INT_CAUSE 0x7978 +#define  ECC_UE_PAR     0x00000020U +#define  ECC_CE_PAR     0x00000010U +#define  PERR_PAR_CAUSE 0x00000008U + +#define EDC_ECC_STATUS 0x797c + +#define EDC_1_BASE_ADDR 0x7980 + +#define CIM_PF_MAILBOX_DATA 0x240 +#define CIM_PF_MAILBOX_CTRL 0x280 +#define  MBMSGVALID     0x00000008U +#define  MBINTREQ       0x00000004U +#define  MBOWNER_MASK   0x00000003U +#define  MBOWNER_SHIFT  0 +#define  MBOWNER(x)     ((x) << MBOWNER_SHIFT) +#define  MBOWNER_GET(x) (((x) & MBOWNER_MASK) >> MBOWNER_SHIFT) + +#define CIM_PF_HOST_INT_CAUSE 0x28c +#define  MBMSGRDYINT 0x00080000U + +#define CIM_HOST_INT_CAUSE 0x7b2c +#define  TIEQOUTPARERRINT  0x00100000U +#define  TIEQINPARERRINT   0x00080000U +#define  MBHOSTPARERR      0x00040000U +#define  MBUPPARERR        0x00020000U +#define  IBQPARERR         0x0001f800U +#define  IBQTP0PARERR      0x00010000U +#define  IBQTP1PARERR      0x00008000U +#define  IBQULPPARERR      0x00004000U +#define  IBQSGELOPARERR    0x00002000U +#define  IBQSGEHIPARERR    0x00001000U +#define  IBQNCSIPARERR     0x00000800U +#define  OBQPARERR         0x000007e0U +#define  OBQULP0PARERR     0x00000400U +#define  OBQULP1PARERR     0x00000200U +#define  OBQULP2PARERR     0x00000100U +#define  OBQULP3PARERR     0x00000080U +#define  OBQSGEPARERR      0x00000040U +#define  OBQNCSIPARERR     0x00000020U +#define  PREFDROPINT       0x00000002U +#define  UPACCNONZERO      0x00000001U + +#define CIM_HOST_UPACC_INT_CAUSE 0x7b34 +#define  EEPROMWRINT      0x40000000U +#define  TIMEOUTMAINT     0x20000000U +#define  TIMEOUTINT       0x10000000U +#define  RSPOVRLOOKUPINT  0x08000000U +#define  REQOVRLOOKUPINT  0x04000000U +#define  BLKWRPLINT       0x02000000U +#define  BLKRDPLINT       0x01000000U +#define  SGLWRPLINT       0x00800000U +#define  SGLRDPLINT       0x00400000U +#define  BLKWRCTLINT      0x00200000U +#define  BLKRDCTLINT      0x00100000U +#define  SGLWRCTLINT      0x00080000U +#define  SGLRDCTLINT      0x00040000U +#define  BLKWREEPROMINT   0x00020000U +#define  BLKRDEEPROMINT   0x00010000U +#define  SGLWREEPROMINT   0x00008000U +#define  SGLRDEEPROMINT   0x00004000U +#define  BLKWRFLASHINT    0x00002000U +#define  BLKRDFLASHINT    0x00001000U +#define  SGLWRFLASHINT    0x00000800U +#define  SGLRDFLASHINT    0x00000400U +#define  BLKWRBOOTINT     0x00000200U +#define  BLKRDBOOTINT     0x00000100U +#define  SGLWRBOOTINT     0x00000080U +#define  SGLRDBOOTINT     0x00000040U +#define  ILLWRBEINT       0x00000020U +#define  ILLRDBEINT       0x00000010U +#define  ILLRDINT         0x00000008U +#define  ILLWRINT         0x00000004U +#define  ILLTRANSINT      0x00000002U +#define  RSVDSPACEINT     0x00000001U + +#define TP_OUT_CONFIG 0x7d04 +#define  VLANEXTENABLE_MASK  0x0000f000U +#define  VLANEXTENABLE_SHIFT 12 + +#define TP_PARA_REG2 0x7d68 +#define  MAXRXDATA_MASK    0xffff0000U +#define  MAXRXDATA_SHIFT   16 +#define  MAXRXDATA_GET(x) (((x) & MAXRXDATA_MASK) >> MAXRXDATA_SHIFT) + +#define TP_TIMER_RESOLUTION 0x7d90 +#define  TIMERRESOLUTION_MASK   0x00ff0000U +#define  TIMERRESOLUTION_SHIFT  16 +#define  TIMERRESOLUTION_GET(x) (((x) & TIMERRESOLUTION_MASK) >> TIMERRESOLUTION_SHIFT) + +#define TP_SHIFT_CNT 0x7dc0 + +#define TP_CCTRL_TABLE 0x7ddc +#define TP_MTU_TABLE 0x7de4 +#define  MTUINDEX_MASK   0xff000000U +#define  MTUINDEX_SHIFT  24 +#define  MTUINDEX(x)     ((x) << MTUINDEX_SHIFT) +#define  MTUWIDTH_MASK   0x000f0000U +#define  MTUWIDTH_SHIFT  16 +#define  MTUWIDTH(x)     ((x) << MTUWIDTH_SHIFT) +#define  MTUWIDTH_GET(x) (((x) & MTUWIDTH_MASK) >> MTUWIDTH_SHIFT) +#define  MTUVALUE_MASK   0x00003fffU +#define  MTUVALUE_SHIFT  0 +#define  MTUVALUE(x)     ((x) << MTUVALUE_SHIFT) +#define  MTUVALUE_GET(x) (((x) & MTUVALUE_MASK) >> MTUVALUE_SHIFT) + +#define TP_RSS_LKP_TABLE 0x7dec +#define  LKPTBLROWVLD        0x80000000U +#define  LKPTBLQUEUE1_MASK   0x000ffc00U +#define  LKPTBLQUEUE1_SHIFT  10 +#define  LKPTBLQUEUE1(x)     ((x) << LKPTBLQUEUE1_SHIFT) +#define  LKPTBLQUEUE1_GET(x) (((x) & LKPTBLQUEUE1_MASK) >> LKPTBLQUEUE1_SHIFT) +#define  LKPTBLQUEUE0_MASK   0x000003ffU +#define  LKPTBLQUEUE0_SHIFT  0 +#define  LKPTBLQUEUE0(x)     ((x) << LKPTBLQUEUE0_SHIFT) +#define  LKPTBLQUEUE0_GET(x) (((x) & LKPTBLQUEUE0_MASK) >> LKPTBLQUEUE0_SHIFT) + +#define TP_PIO_ADDR 0x7e40 +#define TP_PIO_DATA 0x7e44 +#define TP_MIB_INDEX 0x7e50 +#define TP_MIB_DATA 0x7e54 +#define TP_INT_CAUSE 0x7e74 +#define  FLMTXFLSTEMPTY 0x40000000U + +#define TP_INGRESS_CONFIG 0x141 +#define  VNIC                0x00000800U +#define  CSUM_HAS_PSEUDO_HDR 0x00000400U +#define  RM_OVLAN            0x00000200U +#define  LOOKUPEVERYPKT      0x00000100U + +#define TP_MIB_MAC_IN_ERR_0 0x0 +#define TP_MIB_TCP_OUT_RST 0xc +#define TP_MIB_TCP_IN_SEG_HI 0x10 +#define TP_MIB_TCP_IN_SEG_LO 0x11 +#define TP_MIB_TCP_OUT_SEG_HI 0x12 +#define TP_MIB_TCP_OUT_SEG_LO 0x13 +#define TP_MIB_TCP_RXT_SEG_HI 0x14 +#define TP_MIB_TCP_RXT_SEG_LO 0x15 +#define TP_MIB_TNL_CNG_DROP_0 0x18 +#define TP_MIB_TCP_V6IN_ERR_0 0x28 +#define TP_MIB_TCP_V6OUT_RST 0x2c +#define TP_MIB_OFD_ARP_DROP 0x36 +#define TP_MIB_TNL_DROP_0 0x44 +#define TP_MIB_OFD_VLN_DROP_0 0x58 + +#define ULP_TX_INT_CAUSE 0x8dcc +#define  PBL_BOUND_ERR_CH3 0x80000000U +#define  PBL_BOUND_ERR_CH2 0x40000000U +#define  PBL_BOUND_ERR_CH1 0x20000000U +#define  PBL_BOUND_ERR_CH0 0x10000000U + +#define PM_RX_INT_CAUSE 0x8fdc +#define  ZERO_E_CMD_ERROR     0x00400000U +#define  PMRX_FRAMING_ERROR   0x003ffff0U +#define  OCSPI_PAR_ERROR      0x00000008U +#define  DB_OPTIONS_PAR_ERROR 0x00000004U +#define  IESPI_PAR_ERROR      0x00000002U +#define  E_PCMD_PAR_ERROR     0x00000001U + +#define PM_TX_INT_CAUSE 0x8ffc +#define  PCMD_LEN_OVFL0     0x80000000U +#define  PCMD_LEN_OVFL1     0x40000000U +#define  PCMD_LEN_OVFL2     0x20000000U +#define  ZERO_C_CMD_ERROR   0x10000000U +#define  PMTX_FRAMING_ERROR 0x0ffffff0U +#define  OESPI_PAR_ERROR    0x00000008U +#define  ICSPI_PAR_ERROR    0x00000002U +#define  C_PCMD_PAR_ERROR   0x00000001U + +#define MPS_PORT_STAT_TX_PORT_BYTES_L 0x400 +#define MPS_PORT_STAT_TX_PORT_BYTES_H 0x404 +#define MPS_PORT_STAT_TX_PORT_FRAMES_L 0x408 +#define MPS_PORT_STAT_TX_PORT_FRAMES_H 0x40c +#define MPS_PORT_STAT_TX_PORT_BCAST_L 0x410 +#define MPS_PORT_STAT_TX_PORT_BCAST_H 0x414 +#define MPS_PORT_STAT_TX_PORT_MCAST_L 0x418 +#define MPS_PORT_STAT_TX_PORT_MCAST_H 0x41c +#define MPS_PORT_STAT_TX_PORT_UCAST_L 0x420 +#define MPS_PORT_STAT_TX_PORT_UCAST_H 0x424 +#define MPS_PORT_STAT_TX_PORT_ERROR_L 0x428 +#define MPS_PORT_STAT_TX_PORT_ERROR_H 0x42c +#define MPS_PORT_STAT_TX_PORT_64B_L 0x430 +#define MPS_PORT_STAT_TX_PORT_64B_H 0x434 +#define MPS_PORT_STAT_TX_PORT_65B_127B_L 0x438 +#define MPS_PORT_STAT_TX_PORT_65B_127B_H 0x43c +#define MPS_PORT_STAT_TX_PORT_128B_255B_L 0x440 +#define MPS_PORT_STAT_TX_PORT_128B_255B_H 0x444 +#define MPS_PORT_STAT_TX_PORT_256B_511B_L 0x448 +#define MPS_PORT_STAT_TX_PORT_256B_511B_H 0x44c +#define MPS_PORT_STAT_TX_PORT_512B_1023B_L 0x450 +#define MPS_PORT_STAT_TX_PORT_512B_1023B_H 0x454 +#define MPS_PORT_STAT_TX_PORT_1024B_1518B_L 0x458 +#define MPS_PORT_STAT_TX_PORT_1024B_1518B_H 0x45c +#define MPS_PORT_STAT_TX_PORT_1519B_MAX_L 0x460 +#define MPS_PORT_STAT_TX_PORT_1519B_MAX_H 0x464 +#define MPS_PORT_STAT_TX_PORT_DROP_L 0x468 +#define MPS_PORT_STAT_TX_PORT_DROP_H 0x46c +#define MPS_PORT_STAT_TX_PORT_PAUSE_L 0x470 +#define MPS_PORT_STAT_TX_PORT_PAUSE_H 0x474 +#define MPS_PORT_STAT_TX_PORT_PPP0_L 0x478 +#define MPS_PORT_STAT_TX_PORT_PPP0_H 0x47c +#define MPS_PORT_STAT_TX_PORT_PPP1_L 0x480 +#define MPS_PORT_STAT_TX_PORT_PPP1_H 0x484 +#define MPS_PORT_STAT_TX_PORT_PPP2_L 0x488 +#define MPS_PORT_STAT_TX_PORT_PPP2_H 0x48c +#define MPS_PORT_STAT_TX_PORT_PPP3_L 0x490 +#define MPS_PORT_STAT_TX_PORT_PPP3_H 0x494 +#define MPS_PORT_STAT_TX_PORT_PPP4_L 0x498 +#define MPS_PORT_STAT_TX_PORT_PPP4_H 0x49c +#define MPS_PORT_STAT_TX_PORT_PPP5_L 0x4a0 +#define MPS_PORT_STAT_TX_PORT_PPP5_H 0x4a4 +#define MPS_PORT_STAT_TX_PORT_PPP6_L 0x4a8 +#define MPS_PORT_STAT_TX_PORT_PPP6_H 0x4ac +#define MPS_PORT_STAT_TX_PORT_PPP7_L 0x4b0 +#define MPS_PORT_STAT_TX_PORT_PPP7_H 0x4b4 +#define MPS_PORT_STAT_LB_PORT_BYTES_L 0x4c0 +#define MPS_PORT_STAT_LB_PORT_BYTES_H 0x4c4 +#define MPS_PORT_STAT_LB_PORT_FRAMES_L 0x4c8 +#define MPS_PORT_STAT_LB_PORT_FRAMES_H 0x4cc +#define MPS_PORT_STAT_LB_PORT_BCAST_L 0x4d0 +#define MPS_PORT_STAT_LB_PORT_BCAST_H 0x4d4 +#define MPS_PORT_STAT_LB_PORT_MCAST_L 0x4d8 +#define MPS_PORT_STAT_LB_PORT_MCAST_H 0x4dc +#define MPS_PORT_STAT_LB_PORT_UCAST_L 0x4e0 +#define MPS_PORT_STAT_LB_PORT_UCAST_H 0x4e4 +#define MPS_PORT_STAT_LB_PORT_ERROR_L 0x4e8 +#define MPS_PORT_STAT_LB_PORT_ERROR_H 0x4ec +#define MPS_PORT_STAT_LB_PORT_64B_L 0x4f0 +#define MPS_PORT_STAT_LB_PORT_64B_H 0x4f4 +#define MPS_PORT_STAT_LB_PORT_65B_127B_L 0x4f8 +#define MPS_PORT_STAT_LB_PORT_65B_127B_H 0x4fc +#define MPS_PORT_STAT_LB_PORT_128B_255B_L 0x500 +#define MPS_PORT_STAT_LB_PORT_128B_255B_H 0x504 +#define MPS_PORT_STAT_LB_PORT_256B_511B_L 0x508 +#define MPS_PORT_STAT_LB_PORT_256B_511B_H 0x50c +#define MPS_PORT_STAT_LB_PORT_512B_1023B_L 0x510 +#define MPS_PORT_STAT_LB_PORT_512B_1023B_H 0x514 +#define MPS_PORT_STAT_LB_PORT_1024B_1518B_L 0x518 +#define MPS_PORT_STAT_LB_PORT_1024B_1518B_H 0x51c +#define MPS_PORT_STAT_LB_PORT_1519B_MAX_L 0x520 +#define MPS_PORT_STAT_LB_PORT_1519B_MAX_H 0x524 +#define MPS_PORT_STAT_LB_PORT_DROP_FRAMES 0x528 +#define MPS_PORT_STAT_RX_PORT_BYTES_L 0x540 +#define MPS_PORT_STAT_RX_PORT_BYTES_H 0x544 +#define MPS_PORT_STAT_RX_PORT_FRAMES_L 0x548 +#define MPS_PORT_STAT_RX_PORT_FRAMES_H 0x54c +#define MPS_PORT_STAT_RX_PORT_BCAST_L 0x550 +#define MPS_PORT_STAT_RX_PORT_BCAST_H 0x554 +#define MPS_PORT_STAT_RX_PORT_MCAST_L 0x558 +#define MPS_PORT_STAT_RX_PORT_MCAST_H 0x55c +#define MPS_PORT_STAT_RX_PORT_UCAST_L 0x560 +#define MPS_PORT_STAT_RX_PORT_UCAST_H 0x564 +#define MPS_PORT_STAT_RX_PORT_MTU_ERROR_L 0x568 +#define MPS_PORT_STAT_RX_PORT_MTU_ERROR_H 0x56c +#define MPS_PORT_STAT_RX_PORT_MTU_CRC_ERROR_L 0x570 +#define MPS_PORT_STAT_RX_PORT_MTU_CRC_ERROR_H 0x574 +#define MPS_PORT_STAT_RX_PORT_CRC_ERROR_L 0x578 +#define MPS_PORT_STAT_RX_PORT_CRC_ERROR_H 0x57c +#define MPS_PORT_STAT_RX_PORT_LEN_ERROR_L 0x580 +#define MPS_PORT_STAT_RX_PORT_LEN_ERROR_H 0x584 +#define MPS_PORT_STAT_RX_PORT_SYM_ERROR_L 0x588 +#define MPS_PORT_STAT_RX_PORT_SYM_ERROR_H 0x58c +#define MPS_PORT_STAT_RX_PORT_64B_L 0x590 +#define MPS_PORT_STAT_RX_PORT_64B_H 0x594 +#define MPS_PORT_STAT_RX_PORT_65B_127B_L 0x598 +#define MPS_PORT_STAT_RX_PORT_65B_127B_H 0x59c +#define MPS_PORT_STAT_RX_PORT_128B_255B_L 0x5a0 +#define MPS_PORT_STAT_RX_PORT_128B_255B_H 0x5a4 +#define MPS_PORT_STAT_RX_PORT_256B_511B_L 0x5a8 +#define MPS_PORT_STAT_RX_PORT_256B_511B_H 0x5ac +#define MPS_PORT_STAT_RX_PORT_512B_1023B_L 0x5b0 +#define MPS_PORT_STAT_RX_PORT_512B_1023B_H 0x5b4 +#define MPS_PORT_STAT_RX_PORT_1024B_1518B_L 0x5b8 +#define MPS_PORT_STAT_RX_PORT_1024B_1518B_H 0x5bc +#define MPS_PORT_STAT_RX_PORT_1519B_MAX_L 0x5c0 +#define MPS_PORT_STAT_RX_PORT_1519B_MAX_H 0x5c4 +#define MPS_PORT_STAT_RX_PORT_PAUSE_L 0x5c8 +#define MPS_PORT_STAT_RX_PORT_PAUSE_H 0x5cc +#define MPS_PORT_STAT_RX_PORT_PPP0_L 0x5d0 +#define MPS_PORT_STAT_RX_PORT_PPP0_H 0x5d4 +#define MPS_PORT_STAT_RX_PORT_PPP1_L 0x5d8 +#define MPS_PORT_STAT_RX_PORT_PPP1_H 0x5dc +#define MPS_PORT_STAT_RX_PORT_PPP2_L 0x5e0 +#define MPS_PORT_STAT_RX_PORT_PPP2_H 0x5e4 +#define MPS_PORT_STAT_RX_PORT_PPP3_L 0x5e8 +#define MPS_PORT_STAT_RX_PORT_PPP3_H 0x5ec +#define MPS_PORT_STAT_RX_PORT_PPP4_L 0x5f0 +#define MPS_PORT_STAT_RX_PORT_PPP4_H 0x5f4 +#define MPS_PORT_STAT_RX_PORT_PPP5_L 0x5f8 +#define MPS_PORT_STAT_RX_PORT_PPP5_H 0x5fc +#define MPS_PORT_STAT_RX_PORT_PPP6_L 0x600 +#define MPS_PORT_STAT_RX_PORT_PPP6_H 0x604 +#define MPS_PORT_STAT_RX_PORT_PPP7_L 0x608 +#define MPS_PORT_STAT_RX_PORT_PPP7_H 0x60c +#define MPS_PORT_STAT_RX_PORT_LESS_64B_L 0x610 +#define MPS_PORT_STAT_RX_PORT_LESS_64B_H 0x614 +#define MPS_CMN_CTL 0x9000 +#define  NUMPORTS_MASK   0x00000003U +#define  NUMPORTS_SHIFT  0 +#define  NUMPORTS_GET(x) (((x) & NUMPORTS_MASK) >> NUMPORTS_SHIFT) + +#define MPS_INT_CAUSE 0x9008 +#define  STATINT 0x00000020U +#define  TXINT   0x00000010U +#define  RXINT   0x00000008U +#define  TRCINT  0x00000004U +#define  CLSINT  0x00000002U +#define  PLINT   0x00000001U + +#define MPS_TX_INT_CAUSE 0x9408 +#define  PORTERR    0x00010000U +#define  FRMERR     0x00008000U +#define  SECNTERR   0x00004000U +#define  BUBBLE     0x00002000U +#define  TXDESCFIFO 0x00001e00U +#define  TXDATAFIFO 0x000001e0U +#define  NCSIFIFO   0x00000010U +#define  TPFIFO     0x0000000fU + +#define MPS_STAT_PERR_INT_CAUSE_SRAM 0x9614 +#define MPS_STAT_PERR_INT_CAUSE_TX_FIFO 0x9620 +#define MPS_STAT_PERR_INT_CAUSE_RX_FIFO 0x962c + +#define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_L 0x9640 +#define MPS_STAT_RX_BG_0_MAC_DROP_FRAME_H 0x9644 +#define MPS_STAT_RX_BG_1_MAC_DROP_FRAME_L 0x9648 +#define MPS_STAT_RX_BG_1_MAC_DROP_FRAME_H 0x964c +#define MPS_STAT_RX_BG_2_MAC_DROP_FRAME_L 0x9650 +#define MPS_STAT_RX_BG_2_MAC_DROP_FRAME_H 0x9654 +#define MPS_STAT_RX_BG_3_MAC_DROP_FRAME_L 0x9658 +#define MPS_STAT_RX_BG_3_MAC_DROP_FRAME_H 0x965c +#define MPS_STAT_RX_BG_0_LB_DROP_FRAME_L 0x9660 +#define MPS_STAT_RX_BG_0_LB_DROP_FRAME_H 0x9664 +#define MPS_STAT_RX_BG_1_LB_DROP_FRAME_L 0x9668 +#define MPS_STAT_RX_BG_1_LB_DROP_FRAME_H 0x966c +#define MPS_STAT_RX_BG_2_LB_DROP_FRAME_L 0x9670 +#define MPS_STAT_RX_BG_2_LB_DROP_FRAME_H 0x9674 +#define MPS_STAT_RX_BG_3_LB_DROP_FRAME_L 0x9678 +#define MPS_STAT_RX_BG_3_LB_DROP_FRAME_H 0x967c +#define MPS_STAT_RX_BG_0_MAC_TRUNC_FRAME_L 0x9680 +#define MPS_STAT_RX_BG_0_MAC_TRUNC_FRAME_H 0x9684 +#define MPS_STAT_RX_BG_1_MAC_TRUNC_FRAME_L 0x9688 +#define MPS_STAT_RX_BG_1_MAC_TRUNC_FRAME_H 0x968c +#define MPS_STAT_RX_BG_2_MAC_TRUNC_FRAME_L 0x9690 +#define MPS_STAT_RX_BG_2_MAC_TRUNC_FRAME_H 0x9694 +#define MPS_STAT_RX_BG_3_MAC_TRUNC_FRAME_L 0x9698 +#define MPS_STAT_RX_BG_3_MAC_TRUNC_FRAME_H 0x969c +#define MPS_STAT_RX_BG_0_LB_TRUNC_FRAME_L 0x96a0 +#define MPS_STAT_RX_BG_0_LB_TRUNC_FRAME_H 0x96a4 +#define MPS_STAT_RX_BG_1_LB_TRUNC_FRAME_L 0x96a8 +#define MPS_STAT_RX_BG_1_LB_TRUNC_FRAME_H 0x96ac +#define MPS_STAT_RX_BG_2_LB_TRUNC_FRAME_L 0x96b0 +#define MPS_STAT_RX_BG_2_LB_TRUNC_FRAME_H 0x96b4 +#define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_L 0x96b8 +#define MPS_STAT_RX_BG_3_LB_TRUNC_FRAME_H 0x96bc +#define MPS_TRC_CFG 0x9800 +#define  TRCFIFOEMPTY       0x00000010U +#define  TRCIGNOREDROPINPUT 0x00000008U +#define  TRCKEEPDUPLICATES  0x00000004U +#define  TRCEN              0x00000002U +#define  TRCMULTIFILTER     0x00000001U + +#define MPS_TRC_RSS_CONTROL 0x9808 +#define  RSSCONTROL_MASK    0x00ff0000U +#define  RSSCONTROL_SHIFT   16 +#define  RSSCONTROL(x)      ((x) << RSSCONTROL_SHIFT) +#define  QUEUENUMBER_MASK   0x0000ffffU +#define  QUEUENUMBER_SHIFT  0 +#define  QUEUENUMBER(x)     ((x) << QUEUENUMBER_SHIFT) + +#define MPS_TRC_FILTER_MATCH_CTL_A 0x9810 +#define  TFINVERTMATCH   0x01000000U +#define  TFPKTTOOLARGE   0x00800000U +#define  TFEN            0x00400000U +#define  TFPORT_MASK     0x003c0000U +#define  TFPORT_SHIFT    18 +#define  TFPORT(x)       ((x) << TFPORT_SHIFT) +#define  TFPORT_GET(x)   (((x) & TFPORT_MASK) >> TFPORT_SHIFT) +#define  TFDROP          0x00020000U +#define  TFSOPEOPERR     0x00010000U +#define  TFLENGTH_MASK   0x00001f00U +#define  TFLENGTH_SHIFT  8 +#define  TFLENGTH(x)     ((x) << TFLENGTH_SHIFT) +#define  TFLENGTH_GET(x) (((x) & TFLENGTH_MASK) >> TFLENGTH_SHIFT) +#define  TFOFFSET_MASK   0x0000001fU +#define  TFOFFSET_SHIFT  0 +#define  TFOFFSET(x)     ((x) << TFOFFSET_SHIFT) +#define  TFOFFSET_GET(x) (((x) & TFOFFSET_MASK) >> TFOFFSET_SHIFT) + +#define MPS_TRC_FILTER_MATCH_CTL_B 0x9820 +#define  TFMINPKTSIZE_MASK   0x01ff0000U +#define  TFMINPKTSIZE_SHIFT  16 +#define  TFMINPKTSIZE(x)     ((x) << TFMINPKTSIZE_SHIFT) +#define  TFMINPKTSIZE_GET(x) (((x) & TFMINPKTSIZE_MASK) >> TFMINPKTSIZE_SHIFT) +#define  TFCAPTUREMAX_MASK   0x00003fffU +#define  TFCAPTUREMAX_SHIFT  0 +#define  TFCAPTUREMAX(x)     ((x) << TFCAPTUREMAX_SHIFT) +#define  TFCAPTUREMAX_GET(x) (((x) & TFCAPTUREMAX_MASK) >> TFCAPTUREMAX_SHIFT) + +#define MPS_TRC_INT_CAUSE 0x985c +#define  MISCPERR 0x00000100U +#define  PKTFIFO  0x000000f0U +#define  FILTMEM  0x0000000fU + +#define MPS_TRC_FILTER0_MATCH 0x9c00 +#define MPS_TRC_FILTER0_DONT_CARE 0x9c80 +#define MPS_TRC_FILTER1_MATCH 0x9d00 +#define MPS_CLS_INT_CAUSE 0xd028 +#define  PLERRENB  0x00000008U +#define  HASHSRAM  0x00000004U +#define  MATCHTCAM 0x00000002U +#define  MATCHSRAM 0x00000001U + +#define MPS_RX_PERR_INT_CAUSE 0x11074 + +#define CPL_INTR_CAUSE 0x19054 +#define  CIM_OP_MAP_PERR   0x00000020U +#define  CIM_OVFL_ERROR    0x00000010U +#define  TP_FRAMING_ERROR  0x00000008U +#define  SGE_FRAMING_ERROR 0x00000004U +#define  CIM_FRAMING_ERROR 0x00000002U +#define  ZERO_SWITCH_ERROR 0x00000001U + +#define SMB_INT_CAUSE 0x19090 +#define  MSTTXFIFOPARINT 0x00200000U +#define  MSTRXFIFOPARINT 0x00100000U +#define  SLVFIFOPARINT   0x00080000U + +#define ULP_RX_INT_CAUSE 0x19158 +#define ULP_RX_ISCSI_TAGMASK 0x19164 +#define ULP_RX_ISCSI_PSZ 0x19168 +#define  HPZ3_MASK   0x0f000000U +#define  HPZ3_SHIFT  24 +#define  HPZ3(x)     ((x) << HPZ3_SHIFT) +#define  HPZ2_MASK   0x000f0000U +#define  HPZ2_SHIFT  16 +#define  HPZ2(x)     ((x) << HPZ2_SHIFT) +#define  HPZ1_MASK   0x00000f00U +#define  HPZ1_SHIFT  8 +#define  HPZ1(x)     ((x) << HPZ1_SHIFT) +#define  HPZ0_MASK   0x0000000fU +#define  HPZ0_SHIFT  0 +#define  HPZ0(x)     ((x) << HPZ0_SHIFT) + +#define ULP_RX_TDDP_PSZ 0x19178 + +#define SF_DATA 0x193f8 +#define SF_OP 0x193fc +#define  BUSY          0x80000000U +#define  SF_LOCK       0x00000010U +#define  SF_CONT       0x00000008U +#define  BYTECNT_MASK  0x00000006U +#define  BYTECNT_SHIFT 1 +#define  BYTECNT(x)    ((x) << BYTECNT_SHIFT) +#define  OP_WR         0x00000001U + +#define PL_PF_INT_CAUSE 0x3c0 +#define  PFSW  0x00000008U +#define  PFSGE 0x00000004U +#define  PFCIM 0x00000002U +#define  PFMPS 0x00000001U + +#define PL_PF_INT_ENABLE 0x3c4 +#define PL_PF_CTL 0x3c8 +#define  SWINT 0x00000001U + +#define PL_WHOAMI 0x19400 +#define  SOURCEPF_MASK   0x00000700U +#define  SOURCEPF_SHIFT  8 +#define  SOURCEPF(x)     ((x) << SOURCEPF_SHIFT) +#define  SOURCEPF_GET(x) (((x) & SOURCEPF_MASK) >> SOURCEPF_SHIFT) +#define  ISVF            0x00000080U +#define  VFID_MASK       0x0000007fU +#define  VFID_SHIFT      0 +#define  VFID(x)         ((x) << VFID_SHIFT) +#define  VFID_GET(x)     (((x) & VFID_MASK) >> VFID_SHIFT) + +#define PL_INT_CAUSE 0x1940c +#define  ULP_TX     0x08000000U +#define  SGE        0x04000000U +#define  HMA        0x02000000U +#define  CPL_SWITCH 0x01000000U +#define  ULP_RX     0x00800000U +#define  PM_RX      0x00400000U +#define  PM_TX      0x00200000U +#define  MA         0x00100000U +#define  TP         0x00080000U +#define  LE         0x00040000U +#define  EDC1       0x00020000U +#define  EDC0       0x00010000U +#define  MC         0x00008000U +#define  PCIE       0x00004000U +#define  PMU        0x00002000U +#define  XGMAC_KR1  0x00001000U +#define  XGMAC_KR0  0x00000800U +#define  XGMAC1     0x00000400U +#define  XGMAC0     0x00000200U +#define  SMB        0x00000100U +#define  SF         0x00000080U +#define  PL         0x00000040U +#define  NCSI       0x00000020U +#define  MPS        0x00000010U +#define  MI         0x00000008U +#define  DBG        0x00000004U +#define  I2CM       0x00000002U +#define  CIM        0x00000001U + +#define PL_INT_MAP0 0x19414 +#define PL_RST 0x19428 +#define  PIORST     0x00000002U +#define  PIORSTMODE 0x00000001U + +#define PL_PL_INT_CAUSE 0x19430 +#define  FATALPERR 0x00000010U +#define  PERRVFID  0x00000001U + +#define PL_REV 0x1943c + +#define LE_DB_CONFIG 0x19c04 +#define  HASHEN 0x00100000U + +#define LE_DB_SERVER_INDEX 0x19c18 +#define LE_DB_ACT_CNT_IPV4 0x19c20 +#define LE_DB_ACT_CNT_IPV6 0x19c24 + +#define LE_DB_INT_CAUSE 0x19c3c +#define  REQQPARERR 0x00010000U +#define  UNKNOWNCMD 0x00008000U +#define  PARITYERR  0x00000040U +#define  LIPMISS    0x00000020U +#define  LIP0       0x00000010U + +#define LE_DB_TID_HASHBASE 0x19df8 + +#define NCSI_INT_CAUSE 0x1a0d8 +#define  CIM_DM_PRTY_ERR 0x00000100U +#define  MPS_DM_PRTY_ERR 0x00000080U +#define  TXFIFO_PRTY_ERR 0x00000002U +#define  RXFIFO_PRTY_ERR 0x00000001U + +#define XGMAC_PORT_CFG2 0x1018 +#define  PATEN   0x00040000U +#define  MAGICEN 0x00020000U + +#define XGMAC_PORT_MAGIC_MACID_LO 0x1024 +#define XGMAC_PORT_MAGIC_MACID_HI 0x1028 + +#define XGMAC_PORT_EPIO_DATA0 0x10c0 +#define XGMAC_PORT_EPIO_DATA1 0x10c4 +#define XGMAC_PORT_EPIO_DATA2 0x10c8 +#define XGMAC_PORT_EPIO_DATA3 0x10cc +#define XGMAC_PORT_EPIO_OP 0x10d0 +#define  EPIOWR         0x00000100U +#define  ADDRESS_MASK   0x000000ffU +#define  ADDRESS_SHIFT  0 +#define  ADDRESS(x)     ((x) << ADDRESS_SHIFT) + +#define XGMAC_PORT_INT_CAUSE 0x10dc +#endif /* __T4_REGS_H */ diff --git a/drivers/net/cxgb4/t4fw_api.h b/drivers/net/cxgb4/t4fw_api.h new file mode 100644 index 00000000000..3393d05a388 --- /dev/null +++ b/drivers/net/cxgb4/t4fw_api.h @@ -0,0 +1,1580 @@ +/* + * This file is part of the Chelsio T4 Ethernet driver for Linux. + * + * Copyright (c) 2009-2010 Chelsio Communications, Inc. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses.  You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + *     Redistribution and use in source and binary forms, with or + *     without modification, are permitted provided that the following + *     conditions are met: + * + *      - Redistributions of source code must retain the above + *        copyright notice, this list of conditions and the following + *        disclaimer. + * + *      - Redistributions in binary form must reproduce the above + *        copyright notice, this list of conditions and the following + *        disclaimer in the documentation and/or other materials + *        provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _T4FW_INTERFACE_H_ +#define _T4FW_INTERFACE_H_ + +#define FW_T4VF_SGE_BASE_ADDR      0x0000 +#define FW_T4VF_MPS_BASE_ADDR      0x0100 +#define FW_T4VF_PL_BASE_ADDR       0x0200 +#define FW_T4VF_MBDATA_BASE_ADDR   0x0240 +#define FW_T4VF_CIM_BASE_ADDR      0x0300 + +enum fw_wr_opcodes { +	FW_FILTER_WR                   = 0x02, +	FW_ULPTX_WR                    = 0x04, +	FW_TP_WR                       = 0x05, +	FW_ETH_TX_PKT_WR               = 0x08, +	FW_FLOWC_WR                    = 0x0a, +	FW_OFLD_TX_DATA_WR             = 0x0b, +	FW_CMD_WR                      = 0x10, +	FW_ETH_TX_PKT_VM_WR            = 0x11, +	FW_RI_RES_WR                   = 0x0c, +	FW_RI_INIT_WR                  = 0x0d, +	FW_RI_RDMA_WRITE_WR            = 0x14, +	FW_RI_SEND_WR                  = 0x15, +	FW_RI_RDMA_READ_WR             = 0x16, +	FW_RI_RECV_WR                  = 0x17, +	FW_RI_BIND_MW_WR               = 0x18, +	FW_RI_FR_NSMR_WR               = 0x19, +	FW_RI_INV_LSTAG_WR             = 0x1a, +	FW_LASTC2E_WR                  = 0x40 +}; + +struct fw_wr_hdr { +	__be32 hi; +	__be32 lo; +}; + +#define FW_WR_OP(x)	 ((x) << 24) +#define FW_WR_ATOMIC(x)	 ((x) << 23) +#define FW_WR_FLUSH(x)   ((x) << 22) +#define FW_WR_COMPL(x)   ((x) << 21) +#define FW_WR_IMMDLEN(x) ((x) << 0) + +#define FW_WR_EQUIQ	(1U << 31) +#define FW_WR_EQUEQ	(1U << 30) +#define FW_WR_FLOWID(x)	((x) << 8) +#define FW_WR_LEN16(x)	((x) << 0) + +struct fw_ulptx_wr { +	__be32 op_to_compl; +	__be32 flowid_len16; +	u64 cookie; +}; + +struct fw_tp_wr { +	__be32 op_to_immdlen; +	__be32 flowid_len16; +	u64 cookie; +}; + +struct fw_eth_tx_pkt_wr { +	__be32 op_immdlen; +	__be32 equiq_to_len16; +	__be64 r3; +}; + +enum fw_flowc_mnem { +	FW_FLOWC_MNEM_PFNVFN,		/* PFN [15:8] VFN [7:0] */ +	FW_FLOWC_MNEM_CH, +	FW_FLOWC_MNEM_PORT, +	FW_FLOWC_MNEM_IQID, +	FW_FLOWC_MNEM_SNDNXT, +	FW_FLOWC_MNEM_RCVNXT, +	FW_FLOWC_MNEM_SNDBUF, +	FW_FLOWC_MNEM_MSS, +}; + +struct fw_flowc_mnemval { +	u8 mnemonic; +	u8 r4[3]; +	__be32 val; +}; + +struct fw_flowc_wr { +	__be32 op_to_nparams; +#define FW_FLOWC_WR_NPARAMS(x)	((x) << 0) +	__be32 flowid_len16; +	struct fw_flowc_mnemval mnemval[0]; +}; + +struct fw_ofld_tx_data_wr { +	__be32 op_to_immdlen; +	__be32 flowid_len16; +	__be32 plen; +	__be32 tunnel_to_proxy; +#define FW_OFLD_TX_DATA_WR_TUNNEL(x)	 ((x) << 19) +#define FW_OFLD_TX_DATA_WR_SAVE(x)	 ((x) << 18) +#define FW_OFLD_TX_DATA_WR_FLUSH(x)	 ((x) << 17) +#define FW_OFLD_TX_DATA_WR_URGENT(x)	 ((x) << 16) +#define FW_OFLD_TX_DATA_WR_MORE(x)	 ((x) << 15) +#define FW_OFLD_TX_DATA_WR_SHOVE(x)	 ((x) << 14) +#define FW_OFLD_TX_DATA_WR_ULPMODE(x)	 ((x) << 10) +#define FW_OFLD_TX_DATA_WR_ULPSUBMODE(x) ((x) << 6) +}; + +struct fw_cmd_wr { +	__be32 op_dma; +#define FW_CMD_WR_DMA (1U << 17) +	__be32 len16_pkd; +	__be64 cookie_daddr; +}; + +struct fw_eth_tx_pkt_vm_wr { +	__be32 op_immdlen; +	__be32 equiq_to_len16; +	__be32 r3[2]; +	u8 ethmacdst[6]; +	u8 ethmacsrc[6]; +	__be16 ethtype; +	__be16 vlantci; +}; + +#define FW_CMD_MAX_TIMEOUT 3000 + +enum fw_cmd_opcodes { +	FW_LDST_CMD                    = 0x01, +	FW_RESET_CMD                   = 0x03, +	FW_HELLO_CMD                   = 0x04, +	FW_BYE_CMD                     = 0x05, +	FW_INITIALIZE_CMD              = 0x06, +	FW_CAPS_CONFIG_CMD             = 0x07, +	FW_PARAMS_CMD                  = 0x08, +	FW_PFVF_CMD                    = 0x09, +	FW_IQ_CMD                      = 0x10, +	FW_EQ_MNGT_CMD                 = 0x11, +	FW_EQ_ETH_CMD                  = 0x12, +	FW_EQ_CTRL_CMD                 = 0x13, +	FW_EQ_OFLD_CMD                 = 0x21, +	FW_VI_CMD                      = 0x14, +	FW_VI_MAC_CMD                  = 0x15, +	FW_VI_RXMODE_CMD               = 0x16, +	FW_VI_ENABLE_CMD               = 0x17, +	FW_ACL_MAC_CMD                 = 0x18, +	FW_ACL_VLAN_CMD                = 0x19, +	FW_VI_STATS_CMD                = 0x1a, +	FW_PORT_CMD                    = 0x1b, +	FW_PORT_STATS_CMD              = 0x1c, +	FW_PORT_LB_STATS_CMD           = 0x1d, +	FW_PORT_TRACE_CMD              = 0x1e, +	FW_PORT_TRACE_MMAP_CMD         = 0x1f, +	FW_RSS_IND_TBL_CMD             = 0x20, +	FW_RSS_GLB_CONFIG_CMD          = 0x22, +	FW_RSS_VI_CONFIG_CMD           = 0x23, +	FW_LASTC2E_CMD                 = 0x40, +	FW_ERROR_CMD                   = 0x80, +	FW_DEBUG_CMD                   = 0x81, +}; + +enum fw_cmd_cap { +	FW_CMD_CAP_PF                  = 0x01, +	FW_CMD_CAP_DMAQ                = 0x02, +	FW_CMD_CAP_PORT                = 0x04, +	FW_CMD_CAP_PORTPROMISC         = 0x08, +	FW_CMD_CAP_PORTSTATS           = 0x10, +	FW_CMD_CAP_VF                  = 0x80, +}; + +/* + * Generic command header flit0 + */ +struct fw_cmd_hdr { +	__be32 hi; +	__be32 lo; +}; + +#define FW_CMD_OP(x)		((x) << 24) +#define FW_CMD_OP_GET(x)        (((x) >> 24) & 0xff) +#define FW_CMD_REQUEST          (1U << 23) +#define FW_CMD_READ		(1U << 22) +#define FW_CMD_WRITE		(1U << 21) +#define FW_CMD_EXEC		(1U << 20) +#define FW_CMD_RAMASK(x)	((x) << 20) +#define FW_CMD_RETVAL(x)	((x) << 8) +#define FW_CMD_RETVAL_GET(x)	(((x) >> 8) & 0xff) +#define FW_CMD_LEN16(x)         ((x) << 0) + +enum fw_ldst_addrspc { +	FW_LDST_ADDRSPC_FIRMWARE  = 0x0001, +	FW_LDST_ADDRSPC_SGE_EGRC  = 0x0008, +	FW_LDST_ADDRSPC_SGE_INGC  = 0x0009, +	FW_LDST_ADDRSPC_SGE_FLMC  = 0x000a, +	FW_LDST_ADDRSPC_SGE_CONMC = 0x000b, +	FW_LDST_ADDRSPC_TP_PIO    = 0x0010, +	FW_LDST_ADDRSPC_TP_TM_PIO = 0x0011, +	FW_LDST_ADDRSPC_TP_MIB    = 0x0012, +	FW_LDST_ADDRSPC_MDIO      = 0x0018, +	FW_LDST_ADDRSPC_MPS       = 0x0020, +	FW_LDST_ADDRSPC_FUNC      = 0x0028 +}; + +enum fw_ldst_mps_fid { +	FW_LDST_MPS_ATRB, +	FW_LDST_MPS_RPLC +}; + +enum fw_ldst_func_access_ctl { +	FW_LDST_FUNC_ACC_CTL_VIID, +	FW_LDST_FUNC_ACC_CTL_FID +}; + +enum fw_ldst_func_mod_index { +	FW_LDST_FUNC_MPS +}; + +struct fw_ldst_cmd { +	__be32 op_to_addrspace; +#define FW_LDST_CMD_ADDRSPACE(x) ((x) << 0) +	__be32 cycles_to_len16; +	union fw_ldst { +		struct fw_ldst_addrval { +			__be32 addr; +			__be32 val; +		} addrval; +		struct fw_ldst_idctxt { +			__be32 physid; +			__be32 msg_pkd; +			__be32 ctxt_data7; +			__be32 ctxt_data6; +			__be32 ctxt_data5; +			__be32 ctxt_data4; +			__be32 ctxt_data3; +			__be32 ctxt_data2; +			__be32 ctxt_data1; +			__be32 ctxt_data0; +		} idctxt; +		struct fw_ldst_mdio { +			__be16 paddr_mmd; +			__be16 raddr; +			__be16 vctl; +			__be16 rval; +		} mdio; +		struct fw_ldst_mps { +			__be16 fid_ctl; +			__be16 rplcpf_pkd; +			__be32 rplc127_96; +			__be32 rplc95_64; +			__be32 rplc63_32; +			__be32 rplc31_0; +			__be32 atrb; +			__be16 vlan[16]; +		} mps; +		struct fw_ldst_func { +			u8 access_ctl; +			u8 mod_index; +			__be16 ctl_id; +			__be32 offset; +			__be64 data0; +			__be64 data1; +		} func; +	} u; +}; + +#define FW_LDST_CMD_MSG(x)	((x) << 31) +#define FW_LDST_CMD_PADDR(x)	((x) << 8) +#define FW_LDST_CMD_MMD(x)	((x) << 0) +#define FW_LDST_CMD_FID(x)	((x) << 15) +#define FW_LDST_CMD_CTL(x)	((x) << 0) +#define FW_LDST_CMD_RPLCPF(x)	((x) << 0) + +struct fw_reset_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	__be32 val; +	__be32 r3; +}; + +struct fw_hello_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	__be32 err_to_mbasyncnot; +#define FW_HELLO_CMD_ERR	    (1U << 31) +#define FW_HELLO_CMD_INIT	    (1U << 30) +#define FW_HELLO_CMD_MASTERDIS(x)   ((x) << 29) +#define FW_HELLO_CMD_MASTERFORCE(x) ((x) << 28) +#define FW_HELLO_CMD_MBMASTER(x)    ((x) << 24) +#define FW_HELLO_CMD_MBASYNCNOT(x)  ((x) << 20) +	__be32 fwrev; +}; + +struct fw_bye_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	__be64 r3; +}; + +struct fw_initialize_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	__be64 r3; +}; + +enum fw_caps_config_hm { +	FW_CAPS_CONFIG_HM_PCIE		= 0x00000001, +	FW_CAPS_CONFIG_HM_PL		= 0x00000002, +	FW_CAPS_CONFIG_HM_SGE		= 0x00000004, +	FW_CAPS_CONFIG_HM_CIM		= 0x00000008, +	FW_CAPS_CONFIG_HM_ULPTX		= 0x00000010, +	FW_CAPS_CONFIG_HM_TP		= 0x00000020, +	FW_CAPS_CONFIG_HM_ULPRX		= 0x00000040, +	FW_CAPS_CONFIG_HM_PMRX		= 0x00000080, +	FW_CAPS_CONFIG_HM_PMTX		= 0x00000100, +	FW_CAPS_CONFIG_HM_MC		= 0x00000200, +	FW_CAPS_CONFIG_HM_LE		= 0x00000400, +	FW_CAPS_CONFIG_HM_MPS		= 0x00000800, +	FW_CAPS_CONFIG_HM_XGMAC		= 0x00001000, +	FW_CAPS_CONFIG_HM_CPLSWITCH	= 0x00002000, +	FW_CAPS_CONFIG_HM_T4DBG		= 0x00004000, +	FW_CAPS_CONFIG_HM_MI		= 0x00008000, +	FW_CAPS_CONFIG_HM_I2CM		= 0x00010000, +	FW_CAPS_CONFIG_HM_NCSI		= 0x00020000, +	FW_CAPS_CONFIG_HM_SMB		= 0x00040000, +	FW_CAPS_CONFIG_HM_MA		= 0x00080000, +	FW_CAPS_CONFIG_HM_EDRAM		= 0x00100000, +	FW_CAPS_CONFIG_HM_PMU		= 0x00200000, +	FW_CAPS_CONFIG_HM_UART		= 0x00400000, +	FW_CAPS_CONFIG_HM_SF		= 0x00800000, +}; + +enum fw_caps_config_nbm { +	FW_CAPS_CONFIG_NBM_IPMI		= 0x00000001, +	FW_CAPS_CONFIG_NBM_NCSI		= 0x00000002, +}; + +enum fw_caps_config_link { +	FW_CAPS_CONFIG_LINK_PPP		= 0x00000001, +	FW_CAPS_CONFIG_LINK_QFC		= 0x00000002, +	FW_CAPS_CONFIG_LINK_DCBX	= 0x00000004, +}; + +enum fw_caps_config_switch { +	FW_CAPS_CONFIG_SWITCH_INGRESS	= 0x00000001, +	FW_CAPS_CONFIG_SWITCH_EGRESS	= 0x00000002, +}; + +enum fw_caps_config_nic { +	FW_CAPS_CONFIG_NIC		= 0x00000001, +	FW_CAPS_CONFIG_NIC_VM		= 0x00000002, +}; + +enum fw_caps_config_ofld { +	FW_CAPS_CONFIG_OFLD		= 0x00000001, +}; + +enum fw_caps_config_rdma { +	FW_CAPS_CONFIG_RDMA_RDDP	= 0x00000001, +	FW_CAPS_CONFIG_RDMA_RDMAC	= 0x00000002, +}; + +enum fw_caps_config_iscsi { +	FW_CAPS_CONFIG_ISCSI_INITIATOR_PDU = 0x00000001, +	FW_CAPS_CONFIG_ISCSI_TARGET_PDU = 0x00000002, +	FW_CAPS_CONFIG_ISCSI_INITIATOR_CNXOFLD = 0x00000004, +	FW_CAPS_CONFIG_ISCSI_TARGET_CNXOFLD = 0x00000008, +}; + +enum fw_caps_config_fcoe { +	FW_CAPS_CONFIG_FCOE_INITIATOR	= 0x00000001, +	FW_CAPS_CONFIG_FCOE_TARGET	= 0x00000002, +}; + +struct fw_caps_config_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	__be32 r2; +	__be32 hwmbitmap; +	__be16 nbmcaps; +	__be16 linkcaps; +	__be16 switchcaps; +	__be16 r3; +	__be16 niccaps; +	__be16 ofldcaps; +	__be16 rdmacaps; +	__be16 r4; +	__be16 iscsicaps; +	__be16 fcoecaps; +	__be32 r5; +	__be64 r6; +}; + +/* + * params command mnemonics + */ +enum fw_params_mnem { +	FW_PARAMS_MNEM_DEV		= 1,	/* device params */ +	FW_PARAMS_MNEM_PFVF		= 2,	/* function params */ +	FW_PARAMS_MNEM_REG		= 3,	/* limited register access */ +	FW_PARAMS_MNEM_DMAQ		= 4,	/* dma queue params */ +	FW_PARAMS_MNEM_LAST +}; + +/* + * device parameters + */ +enum fw_params_param_dev { +	FW_PARAMS_PARAM_DEV_CCLK	= 0x00, /* chip core clock in khz */ +	FW_PARAMS_PARAM_DEV_PORTVEC	= 0x01, /* the port vector */ +	FW_PARAMS_PARAM_DEV_NTID	= 0x02, /* reads the number of TIDs +						 * allocated by the device's +						 * Lookup Engine +						 */ +	FW_PARAMS_PARAM_DEV_FLOWC_BUFFIFO_SZ = 0x03, +	FW_PARAMS_PARAM_DEV_INTVER_NIC	= 0x04, +	FW_PARAMS_PARAM_DEV_INTVER_VNIC = 0x05, +	FW_PARAMS_PARAM_DEV_INTVER_OFLD = 0x06, +	FW_PARAMS_PARAM_DEV_INTVER_RI	= 0x07, +	FW_PARAMS_PARAM_DEV_INTVER_ISCSIPDU = 0x08, +	FW_PARAMS_PARAM_DEV_INTVER_ISCSI = 0x09, +	FW_PARAMS_PARAM_DEV_INTVER_FCOE = 0x0A +}; + +/* + * physical and virtual function parameters + */ +enum fw_params_param_pfvf { +	FW_PARAMS_PARAM_PFVF_RWXCAPS	= 0x00, +	FW_PARAMS_PARAM_PFVF_ROUTE_START = 0x01, +	FW_PARAMS_PARAM_PFVF_ROUTE_END = 0x02, +	FW_PARAMS_PARAM_PFVF_CLIP_START = 0x03, +	FW_PARAMS_PARAM_PFVF_CLIP_END = 0x04, +	FW_PARAMS_PARAM_PFVF_FILTER_START = 0x05, +	FW_PARAMS_PARAM_PFVF_FILTER_END = 0x06, +	FW_PARAMS_PARAM_PFVF_SERVER_START = 0x07, +	FW_PARAMS_PARAM_PFVF_SERVER_END = 0x08, +	FW_PARAMS_PARAM_PFVF_TDDP_START = 0x09, +	FW_PARAMS_PARAM_PFVF_TDDP_END = 0x0A, +	FW_PARAMS_PARAM_PFVF_ISCSI_START = 0x0B, +	FW_PARAMS_PARAM_PFVF_ISCSI_END = 0x0C, +	FW_PARAMS_PARAM_PFVF_STAG_START = 0x0D, +	FW_PARAMS_PARAM_PFVF_STAG_END = 0x0E, +	FW_PARAMS_PARAM_PFVF_RQ_START = 0x1F, +	FW_PARAMS_PARAM_PFVF_RQ_END	= 0x10, +	FW_PARAMS_PARAM_PFVF_PBL_START = 0x11, +	FW_PARAMS_PARAM_PFVF_PBL_END	= 0x12, +	FW_PARAMS_PARAM_PFVF_L2T_START = 0x13, +	FW_PARAMS_PARAM_PFVF_L2T_END = 0x14, +	FW_PARAMS_PARAM_PFVF_SCHEDCLASS_ETH = 0x20, +}; + +/* + * dma queue parameters + */ +enum fw_params_param_dmaq { +	FW_PARAMS_PARAM_DMAQ_IQ_DCAEN_DCACPU = 0x00, +	FW_PARAMS_PARAM_DMAQ_IQ_INTCNTTHRESH = 0x01, +	FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_MNGT = 0x10, +	FW_PARAMS_PARAM_DMAQ_EQ_CMPLIQID_CTRL = 0x11, +	FW_PARAMS_PARAM_DMAQ_EQ_SCHEDCLASS_ETH = 0x12, +}; + +#define FW_PARAMS_MNEM(x)      ((x) << 24) +#define FW_PARAMS_PARAM_X(x)   ((x) << 16) +#define FW_PARAMS_PARAM_Y(x)   ((x) << 8) +#define FW_PARAMS_PARAM_Z(x)   ((x) << 0) +#define FW_PARAMS_PARAM_XYZ(x) ((x) << 0) +#define FW_PARAMS_PARAM_YZ(x)  ((x) << 0) + +struct fw_params_cmd { +	__be32 op_to_vfn; +	__be32 retval_len16; +	struct fw_params_param { +		__be32 mnem; +		__be32 val; +	} param[7]; +}; + +#define FW_PARAMS_CMD_PFN(x) ((x) << 8) +#define FW_PARAMS_CMD_VFN(x) ((x) << 0) + +struct fw_pfvf_cmd { +	__be32 op_to_vfn; +	__be32 retval_len16; +	__be32 niqflint_niq; +	__be32 cmask_to_neq; +	__be32 tc_to_nexactf; +	__be32 r_caps_to_nethctrl; +	__be16 nricq; +	__be16 nriqp; +	__be32 r4; +}; + +#define FW_PFVF_CMD_PFN(x) ((x) << 8) +#define FW_PFVF_CMD_VFN(x) ((x) << 0) + +#define FW_PFVF_CMD_NIQFLINT(x) ((x) << 20) +#define FW_PFVF_CMD_NIQFLINT_GET(x) (((x) >> 20) & 0xfff) + +#define FW_PFVF_CMD_NIQ(x) ((x) << 0) +#define FW_PFVF_CMD_NIQ_GET(x) (((x) >> 0) & 0xfffff) + +#define FW_PFVF_CMD_CMASK(x) ((x) << 24) +#define FW_PFVF_CMD_CMASK_GET(x) (((x) >> 24) & 0xf) + +#define FW_PFVF_CMD_PMASK(x) ((x) << 20) +#define FW_PFVF_CMD_PMASK_GET(x) (((x) >> 20) & 0xf) + +#define FW_PFVF_CMD_NEQ(x) ((x) << 0) +#define FW_PFVF_CMD_NEQ_GET(x) (((x) >> 0) & 0xfffff) + +#define FW_PFVF_CMD_TC(x) ((x) << 24) +#define FW_PFVF_CMD_TC_GET(x) (((x) >> 24) & 0xff) + +#define FW_PFVF_CMD_NVI(x) ((x) << 16) +#define FW_PFVF_CMD_NVI_GET(x) (((x) >> 16) & 0xff) + +#define FW_PFVF_CMD_NEXACTF(x) ((x) << 0) +#define FW_PFVF_CMD_NEXACTF_GET(x) (((x) >> 0) & 0xffff) + +#define FW_PFVF_CMD_R_CAPS(x) ((x) << 24) +#define FW_PFVF_CMD_R_CAPS_GET(x) (((x) >> 24) & 0xff) + +#define FW_PFVF_CMD_WX_CAPS(x) ((x) << 16) +#define FW_PFVF_CMD_WX_CAPS_GET(x) (((x) >> 16) & 0xff) + +#define FW_PFVF_CMD_NETHCTRL(x) ((x) << 0) +#define FW_PFVF_CMD_NETHCTRL_GET(x) (((x) >> 0) & 0xffff) + +enum fw_iq_type { +	FW_IQ_TYPE_FL_INT_CAP, +	FW_IQ_TYPE_NO_FL_INT_CAP +}; + +struct fw_iq_cmd { +	__be32 op_to_vfn; +	__be32 alloc_to_len16; +	__be16 physiqid; +	__be16 iqid; +	__be16 fl0id; +	__be16 fl1id; +	__be32 type_to_iqandstindex; +	__be16 iqdroprss_to_iqesize; +	__be16 iqsize; +	__be64 iqaddr; +	__be32 iqns_to_fl0congen; +	__be16 fl0dcaen_to_fl0cidxfthresh; +	__be16 fl0size; +	__be64 fl0addr; +	__be32 fl1cngchmap_to_fl1congen; +	__be16 fl1dcaen_to_fl1cidxfthresh; +	__be16 fl1size; +	__be64 fl1addr; +}; + +#define FW_IQ_CMD_PFN(x) ((x) << 8) +#define FW_IQ_CMD_VFN(x) ((x) << 0) + +#define FW_IQ_CMD_ALLOC (1U << 31) +#define FW_IQ_CMD_FREE (1U << 30) +#define FW_IQ_CMD_MODIFY (1U << 29) +#define FW_IQ_CMD_IQSTART(x) ((x) << 28) +#define FW_IQ_CMD_IQSTOP(x) ((x) << 27) + +#define FW_IQ_CMD_TYPE(x) ((x) << 29) +#define FW_IQ_CMD_IQASYNCH(x) ((x) << 28) +#define FW_IQ_CMD_VIID(x) ((x) << 16) +#define FW_IQ_CMD_IQANDST(x) ((x) << 15) +#define FW_IQ_CMD_IQANUS(x) ((x) << 14) +#define FW_IQ_CMD_IQANUD(x) ((x) << 12) +#define FW_IQ_CMD_IQANDSTINDEX(x) ((x) << 0) + +#define FW_IQ_CMD_IQDROPRSS (1U << 15) +#define FW_IQ_CMD_IQGTSMODE (1U << 14) +#define FW_IQ_CMD_IQPCIECH(x) ((x) << 12) +#define FW_IQ_CMD_IQDCAEN(x) ((x) << 11) +#define FW_IQ_CMD_IQDCACPU(x) ((x) << 6) +#define FW_IQ_CMD_IQINTCNTTHRESH(x) ((x) << 4) +#define FW_IQ_CMD_IQO (1U << 3) +#define FW_IQ_CMD_IQCPRIO(x) ((x) << 2) +#define FW_IQ_CMD_IQESIZE(x) ((x) << 0) + +#define FW_IQ_CMD_IQNS(x) ((x) << 31) +#define FW_IQ_CMD_IQRO(x) ((x) << 30) +#define FW_IQ_CMD_IQFLINTIQHSEN(x) ((x) << 28) +#define FW_IQ_CMD_IQFLINTCONGEN(x) ((x) << 27) +#define FW_IQ_CMD_IQFLINTISCSIC(x) ((x) << 26) +#define FW_IQ_CMD_FL0CNGCHMAP(x) ((x) << 20) +#define FW_IQ_CMD_FL0CACHELOCK(x) ((x) << 15) +#define FW_IQ_CMD_FL0DBP(x) ((x) << 14) +#define FW_IQ_CMD_FL0DATANS(x) ((x) << 13) +#define FW_IQ_CMD_FL0DATARO(x) ((x) << 12) +#define FW_IQ_CMD_FL0CONGCIF(x) ((x) << 11) +#define FW_IQ_CMD_FL0ONCHIP(x) ((x) << 10) +#define FW_IQ_CMD_FL0STATUSPGNS(x) ((x) << 9) +#define FW_IQ_CMD_FL0STATUSPGRO(x) ((x) << 8) +#define FW_IQ_CMD_FL0FETCHNS(x) ((x) << 7) +#define FW_IQ_CMD_FL0FETCHRO(x) ((x) << 6) +#define FW_IQ_CMD_FL0HOSTFCMODE(x) ((x) << 4) +#define FW_IQ_CMD_FL0CPRIO(x) ((x) << 3) +#define FW_IQ_CMD_FL0PADEN (1U << 2) +#define FW_IQ_CMD_FL0PACKEN (1U << 1) +#define FW_IQ_CMD_FL0CONGEN (1U << 0) + +#define FW_IQ_CMD_FL0DCAEN(x) ((x) << 15) +#define FW_IQ_CMD_FL0DCACPU(x) ((x) << 10) +#define FW_IQ_CMD_FL0FBMIN(x) ((x) << 7) +#define FW_IQ_CMD_FL0FBMAX(x) ((x) << 4) +#define FW_IQ_CMD_FL0CIDXFTHRESHO (1U << 3) +#define FW_IQ_CMD_FL0CIDXFTHRESH(x) ((x) << 0) + +#define FW_IQ_CMD_FL1CNGCHMAP(x) ((x) << 20) +#define FW_IQ_CMD_FL1CACHELOCK(x) ((x) << 15) +#define FW_IQ_CMD_FL1DBP(x) ((x) << 14) +#define FW_IQ_CMD_FL1DATANS(x) ((x) << 13) +#define FW_IQ_CMD_FL1DATARO(x) ((x) << 12) +#define FW_IQ_CMD_FL1CONGCIF(x) ((x) << 11) +#define FW_IQ_CMD_FL1ONCHIP(x) ((x) << 10) +#define FW_IQ_CMD_FL1STATUSPGNS(x) ((x) << 9) +#define FW_IQ_CMD_FL1STATUSPGRO(x) ((x) << 8) +#define FW_IQ_CMD_FL1FETCHNS(x) ((x) << 7) +#define FW_IQ_CMD_FL1FETCHRO(x) ((x) << 6) +#define FW_IQ_CMD_FL1HOSTFCMODE(x) ((x) << 4) +#define FW_IQ_CMD_FL1CPRIO(x) ((x) << 3) +#define FW_IQ_CMD_FL1PADEN (1U << 2) +#define FW_IQ_CMD_FL1PACKEN (1U << 1) +#define FW_IQ_CMD_FL1CONGEN (1U << 0) + +#define FW_IQ_CMD_FL1DCAEN(x) ((x) << 15) +#define FW_IQ_CMD_FL1DCACPU(x) ((x) << 10) +#define FW_IQ_CMD_FL1FBMIN(x) ((x) << 7) +#define FW_IQ_CMD_FL1FBMAX(x) ((x) << 4) +#define FW_IQ_CMD_FL1CIDXFTHRESHO (1U << 3) +#define FW_IQ_CMD_FL1CIDXFTHRESH(x) ((x) << 0) + +struct fw_eq_eth_cmd { +	__be32 op_to_vfn; +	__be32 alloc_to_len16; +	__be32 eqid_pkd; +	__be32 physeqid_pkd; +	__be32 fetchszm_to_iqid; +	__be32 dcaen_to_eqsize; +	__be64 eqaddr; +	__be32 viid_pkd; +	__be32 r8_lo; +	__be64 r9; +}; + +#define FW_EQ_ETH_CMD_PFN(x) ((x) << 8) +#define FW_EQ_ETH_CMD_VFN(x) ((x) << 0) +#define FW_EQ_ETH_CMD_ALLOC (1U << 31) +#define FW_EQ_ETH_CMD_FREE (1U << 30) +#define FW_EQ_ETH_CMD_MODIFY (1U << 29) +#define FW_EQ_ETH_CMD_EQSTART (1U << 28) +#define FW_EQ_ETH_CMD_EQSTOP (1U << 27) + +#define FW_EQ_ETH_CMD_EQID(x) ((x) << 0) +#define FW_EQ_ETH_CMD_EQID_GET(x) (((x) >> 0) & 0xfffff) +#define FW_EQ_ETH_CMD_PHYSEQID(x) ((x) << 0) + +#define FW_EQ_ETH_CMD_FETCHSZM(x) ((x) << 26) +#define FW_EQ_ETH_CMD_STATUSPGNS(x) ((x) << 25) +#define FW_EQ_ETH_CMD_STATUSPGRO(x) ((x) << 24) +#define FW_EQ_ETH_CMD_FETCHNS(x) ((x) << 23) +#define FW_EQ_ETH_CMD_FETCHRO(x) ((x) << 22) +#define FW_EQ_ETH_CMD_HOSTFCMODE(x) ((x) << 20) +#define FW_EQ_ETH_CMD_CPRIO(x) ((x) << 19) +#define FW_EQ_ETH_CMD_ONCHIP(x) ((x) << 18) +#define FW_EQ_ETH_CMD_PCIECHN(x) ((x) << 16) +#define FW_EQ_ETH_CMD_IQID(x) ((x) << 0) + +#define FW_EQ_ETH_CMD_DCAEN(x) ((x) << 31) +#define FW_EQ_ETH_CMD_DCACPU(x) ((x) << 26) +#define FW_EQ_ETH_CMD_FBMIN(x) ((x) << 23) +#define FW_EQ_ETH_CMD_FBMAX(x) ((x) << 20) +#define FW_EQ_ETH_CMD_CIDXFTHRESHO(x) ((x) << 19) +#define FW_EQ_ETH_CMD_CIDXFTHRESH(x) ((x) << 16) +#define FW_EQ_ETH_CMD_EQSIZE(x) ((x) << 0) + +#define FW_EQ_ETH_CMD_VIID(x) ((x) << 16) + +struct fw_eq_ctrl_cmd { +	__be32 op_to_vfn; +	__be32 alloc_to_len16; +	__be32 cmpliqid_eqid; +	__be32 physeqid_pkd; +	__be32 fetchszm_to_iqid; +	__be32 dcaen_to_eqsize; +	__be64 eqaddr; +}; + +#define FW_EQ_CTRL_CMD_PFN(x) ((x) << 8) +#define FW_EQ_CTRL_CMD_VFN(x) ((x) << 0) + +#define FW_EQ_CTRL_CMD_ALLOC (1U << 31) +#define FW_EQ_CTRL_CMD_FREE (1U << 30) +#define FW_EQ_CTRL_CMD_MODIFY (1U << 29) +#define FW_EQ_CTRL_CMD_EQSTART (1U << 28) +#define FW_EQ_CTRL_CMD_EQSTOP (1U << 27) + +#define FW_EQ_CTRL_CMD_CMPLIQID(x) ((x) << 20) +#define FW_EQ_CTRL_CMD_EQID(x) ((x) << 0) +#define FW_EQ_CTRL_CMD_EQID_GET(x) (((x) >> 0) & 0xfffff) +#define FW_EQ_CTRL_CMD_PHYSEQID_GET(x) (((x) >> 0) & 0xfffff) + +#define FW_EQ_CTRL_CMD_FETCHSZM (1U << 26) +#define FW_EQ_CTRL_CMD_STATUSPGNS (1U << 25) +#define FW_EQ_CTRL_CMD_STATUSPGRO (1U << 24) +#define FW_EQ_CTRL_CMD_FETCHNS (1U << 23) +#define FW_EQ_CTRL_CMD_FETCHRO (1U << 22) +#define FW_EQ_CTRL_CMD_HOSTFCMODE(x) ((x) << 20) +#define FW_EQ_CTRL_CMD_CPRIO(x) ((x) << 19) +#define FW_EQ_CTRL_CMD_ONCHIP(x) ((x) << 18) +#define FW_EQ_CTRL_CMD_PCIECHN(x) ((x) << 16) +#define FW_EQ_CTRL_CMD_IQID(x) ((x) << 0) + +#define FW_EQ_CTRL_CMD_DCAEN(x) ((x) << 31) +#define FW_EQ_CTRL_CMD_DCACPU(x) ((x) << 26) +#define FW_EQ_CTRL_CMD_FBMIN(x) ((x) << 23) +#define FW_EQ_CTRL_CMD_FBMAX(x) ((x) << 20) +#define FW_EQ_CTRL_CMD_CIDXFTHRESHO(x) ((x) << 19) +#define FW_EQ_CTRL_CMD_CIDXFTHRESH(x) ((x) << 16) +#define FW_EQ_CTRL_CMD_EQSIZE(x) ((x) << 0) + +struct fw_eq_ofld_cmd { +	__be32 op_to_vfn; +	__be32 alloc_to_len16; +	__be32 eqid_pkd; +	__be32 physeqid_pkd; +	__be32 fetchszm_to_iqid; +	__be32 dcaen_to_eqsize; +	__be64 eqaddr; +}; + +#define FW_EQ_OFLD_CMD_PFN(x) ((x) << 8) +#define FW_EQ_OFLD_CMD_VFN(x) ((x) << 0) + +#define FW_EQ_OFLD_CMD_ALLOC (1U << 31) +#define FW_EQ_OFLD_CMD_FREE (1U << 30) +#define FW_EQ_OFLD_CMD_MODIFY (1U << 29) +#define FW_EQ_OFLD_CMD_EQSTART (1U << 28) +#define FW_EQ_OFLD_CMD_EQSTOP (1U << 27) + +#define FW_EQ_OFLD_CMD_EQID(x) ((x) << 0) +#define FW_EQ_OFLD_CMD_EQID_GET(x) (((x) >> 0) & 0xfffff) +#define FW_EQ_OFLD_CMD_PHYSEQID_GET(x) (((x) >> 0) & 0xfffff) + +#define FW_EQ_OFLD_CMD_FETCHSZM(x) ((x) << 26) +#define FW_EQ_OFLD_CMD_STATUSPGNS(x) ((x) << 25) +#define FW_EQ_OFLD_CMD_STATUSPGRO(x) ((x) << 24) +#define FW_EQ_OFLD_CMD_FETCHNS(x) ((x) << 23) +#define FW_EQ_OFLD_CMD_FETCHRO(x) ((x) << 22) +#define FW_EQ_OFLD_CMD_HOSTFCMODE(x) ((x) << 20) +#define FW_EQ_OFLD_CMD_CPRIO(x) ((x) << 19) +#define FW_EQ_OFLD_CMD_ONCHIP(x) ((x) << 18) +#define FW_EQ_OFLD_CMD_PCIECHN(x) ((x) << 16) +#define FW_EQ_OFLD_CMD_IQID(x) ((x) << 0) + +#define FW_EQ_OFLD_CMD_DCAEN(x) ((x) << 31) +#define FW_EQ_OFLD_CMD_DCACPU(x) ((x) << 26) +#define FW_EQ_OFLD_CMD_FBMIN(x) ((x) << 23) +#define FW_EQ_OFLD_CMD_FBMAX(x) ((x) << 20) +#define FW_EQ_OFLD_CMD_CIDXFTHRESHO(x) ((x) << 19) +#define FW_EQ_OFLD_CMD_CIDXFTHRESH(x) ((x) << 16) +#define FW_EQ_OFLD_CMD_EQSIZE(x) ((x) << 0) + +/* + * Macros for VIID parsing: + * VIID - [10:8] PFN, [7] VI Valid, [6:0] VI number + */ +#define FW_VIID_PFN_GET(x) (((x) >> 8) & 0x7) +#define FW_VIID_VIVLD_GET(x) (((x) >> 7) & 0x1) +#define FW_VIID_VIN_GET(x) (((x) >> 0) & 0x7F) + +struct fw_vi_cmd { +	__be32 op_to_vfn; +	__be32 alloc_to_len16; +	__be16 viid_pkd; +	u8 mac[6]; +	u8 portid_pkd; +	u8 nmac; +	u8 nmac0[6]; +	__be16 rsssize_pkd; +	u8 nmac1[6]; +	__be16 r7; +	u8 nmac2[6]; +	__be16 r8; +	u8 nmac3[6]; +	__be64 r9; +	__be64 r10; +}; + +#define FW_VI_CMD_PFN(x) ((x) << 8) +#define FW_VI_CMD_VFN(x) ((x) << 0) +#define FW_VI_CMD_ALLOC (1U << 31) +#define FW_VI_CMD_FREE (1U << 30) +#define FW_VI_CMD_VIID(x) ((x) << 0) +#define FW_VI_CMD_PORTID(x) ((x) << 4) +#define FW_VI_CMD_RSSSIZE_GET(x) (((x) >> 0) & 0x7ff) + +/* Special VI_MAC command index ids */ +#define FW_VI_MAC_ADD_MAC		0x3FF +#define FW_VI_MAC_ADD_PERSIST_MAC	0x3FE +#define FW_VI_MAC_MAC_BASED_FREE	0x3FD + +enum fw_vi_mac_smac { +	FW_VI_MAC_MPS_TCAM_ENTRY, +	FW_VI_MAC_MPS_TCAM_ONLY, +	FW_VI_MAC_SMT_ONLY, +	FW_VI_MAC_SMT_AND_MPSTCAM +}; + +enum fw_vi_mac_result { +	FW_VI_MAC_R_SUCCESS, +	FW_VI_MAC_R_F_NONEXISTENT_NOMEM, +	FW_VI_MAC_R_SMAC_FAIL, +	FW_VI_MAC_R_F_ACL_CHECK +}; + +struct fw_vi_mac_cmd { +	__be32 op_to_viid; +	__be32 freemacs_to_len16; +	union fw_vi_mac { +		struct fw_vi_mac_exact { +			__be16 valid_to_idx; +			u8 macaddr[6]; +		} exact[7]; +		struct fw_vi_mac_hash { +			__be64 hashvec; +		} hash; +	} u; +}; + +#define FW_VI_MAC_CMD_VIID(x) ((x) << 0) +#define FW_VI_MAC_CMD_FREEMACS(x) ((x) << 31) +#define FW_VI_MAC_CMD_HASHVECEN (1U << 23) +#define FW_VI_MAC_CMD_HASHUNIEN(x) ((x) << 22) +#define FW_VI_MAC_CMD_VALID (1U << 15) +#define FW_VI_MAC_CMD_PRIO(x) ((x) << 12) +#define FW_VI_MAC_CMD_SMAC_RESULT(x) ((x) << 10) +#define FW_VI_MAC_CMD_SMAC_RESULT_GET(x) (((x) >> 10) & 0x3) +#define FW_VI_MAC_CMD_IDX(x) ((x) << 0) +#define FW_VI_MAC_CMD_IDX_GET(x) (((x) >> 0) & 0x3ff) + +#define FW_RXMODE_MTU_NO_CHG	65535 + +struct fw_vi_rxmode_cmd { +	__be32 op_to_viid; +	__be32 retval_len16; +	__be32 mtu_to_broadcasten; +	__be32 r4_lo; +}; + +#define FW_VI_RXMODE_CMD_VIID(x) ((x) << 0) +#define FW_VI_RXMODE_CMD_MTU(x) ((x) << 16) +#define FW_VI_RXMODE_CMD_PROMISCEN_MASK 0x3 +#define FW_VI_RXMODE_CMD_PROMISCEN(x) ((x) << 14) +#define FW_VI_RXMODE_CMD_ALLMULTIEN_MASK 0x3 +#define FW_VI_RXMODE_CMD_ALLMULTIEN(x) ((x) << 12) +#define FW_VI_RXMODE_CMD_BROADCASTEN_MASK 0x3 +#define FW_VI_RXMODE_CMD_BROADCASTEN(x) ((x) << 10) + +struct fw_vi_enable_cmd { +	__be32 op_to_viid; +	__be32 ien_to_len16; +	__be16 blinkdur; +	__be16 r3; +	__be32 r4; +}; + +#define FW_VI_ENABLE_CMD_VIID(x) ((x) << 0) +#define FW_VI_ENABLE_CMD_IEN(x) ((x) << 31) +#define FW_VI_ENABLE_CMD_EEN(x) ((x) << 30) +#define FW_VI_ENABLE_CMD_LED (1U << 29) + +/* VI VF stats offset definitions */ +#define VI_VF_NUM_STATS	16 +enum fw_vi_stats_vf_index { +	FW_VI_VF_STAT_TX_BCAST_BYTES_IX, +	FW_VI_VF_STAT_TX_BCAST_FRAMES_IX, +	FW_VI_VF_STAT_TX_MCAST_BYTES_IX, +	FW_VI_VF_STAT_TX_MCAST_FRAMES_IX, +	FW_VI_VF_STAT_TX_UCAST_BYTES_IX, +	FW_VI_VF_STAT_TX_UCAST_FRAMES_IX, +	FW_VI_VF_STAT_TX_DROP_FRAMES_IX, +	FW_VI_VF_STAT_TX_OFLD_BYTES_IX, +	FW_VI_VF_STAT_TX_OFLD_FRAMES_IX, +	FW_VI_VF_STAT_RX_BCAST_BYTES_IX, +	FW_VI_VF_STAT_RX_BCAST_FRAMES_IX, +	FW_VI_VF_STAT_RX_MCAST_BYTES_IX, +	FW_VI_VF_STAT_RX_MCAST_FRAMES_IX, +	FW_VI_VF_STAT_RX_UCAST_BYTES_IX, +	FW_VI_VF_STAT_RX_UCAST_FRAMES_IX, +	FW_VI_VF_STAT_RX_ERR_FRAMES_IX +}; + +/* VI PF stats offset definitions */ +#define VI_PF_NUM_STATS	17 +enum fw_vi_stats_pf_index { +	FW_VI_PF_STAT_TX_BCAST_BYTES_IX, +	FW_VI_PF_STAT_TX_BCAST_FRAMES_IX, +	FW_VI_PF_STAT_TX_MCAST_BYTES_IX, +	FW_VI_PF_STAT_TX_MCAST_FRAMES_IX, +	FW_VI_PF_STAT_TX_UCAST_BYTES_IX, +	FW_VI_PF_STAT_TX_UCAST_FRAMES_IX, +	FW_VI_PF_STAT_TX_OFLD_BYTES_IX, +	FW_VI_PF_STAT_TX_OFLD_FRAMES_IX, +	FW_VI_PF_STAT_RX_BYTES_IX, +	FW_VI_PF_STAT_RX_FRAMES_IX, +	FW_VI_PF_STAT_RX_BCAST_BYTES_IX, +	FW_VI_PF_STAT_RX_BCAST_FRAMES_IX, +	FW_VI_PF_STAT_RX_MCAST_BYTES_IX, +	FW_VI_PF_STAT_RX_MCAST_FRAMES_IX, +	FW_VI_PF_STAT_RX_UCAST_BYTES_IX, +	FW_VI_PF_STAT_RX_UCAST_FRAMES_IX, +	FW_VI_PF_STAT_RX_ERR_FRAMES_IX +}; + +struct fw_vi_stats_cmd { +	__be32 op_to_viid; +	__be32 retval_len16; +	union fw_vi_stats { +		struct fw_vi_stats_ctl { +			__be16 nstats_ix; +			__be16 r6; +			__be32 r7; +			__be64 stat0; +			__be64 stat1; +			__be64 stat2; +			__be64 stat3; +			__be64 stat4; +			__be64 stat5; +		} ctl; +		struct fw_vi_stats_pf { +			__be64 tx_bcast_bytes; +			__be64 tx_bcast_frames; +			__be64 tx_mcast_bytes; +			__be64 tx_mcast_frames; +			__be64 tx_ucast_bytes; +			__be64 tx_ucast_frames; +			__be64 tx_offload_bytes; +			__be64 tx_offload_frames; +			__be64 rx_pf_bytes; +			__be64 rx_pf_frames; +			__be64 rx_bcast_bytes; +			__be64 rx_bcast_frames; +			__be64 rx_mcast_bytes; +			__be64 rx_mcast_frames; +			__be64 rx_ucast_bytes; +			__be64 rx_ucast_frames; +			__be64 rx_err_frames; +		} pf; +		struct fw_vi_stats_vf { +			__be64 tx_bcast_bytes; +			__be64 tx_bcast_frames; +			__be64 tx_mcast_bytes; +			__be64 tx_mcast_frames; +			__be64 tx_ucast_bytes; +			__be64 tx_ucast_frames; +			__be64 tx_drop_frames; +			__be64 tx_offload_bytes; +			__be64 tx_offload_frames; +			__be64 rx_bcast_bytes; +			__be64 rx_bcast_frames; +			__be64 rx_mcast_bytes; +			__be64 rx_mcast_frames; +			__be64 rx_ucast_bytes; +			__be64 rx_ucast_frames; +			__be64 rx_err_frames; +		} vf; +	} u; +}; + +#define FW_VI_STATS_CMD_VIID(x) ((x) << 0) +#define FW_VI_STATS_CMD_NSTATS(x) ((x) << 12) +#define FW_VI_STATS_CMD_IX(x) ((x) << 0) + +struct fw_acl_mac_cmd { +	__be32 op_to_vfn; +	__be32 en_to_len16; +	u8 nmac; +	u8 r3[7]; +	__be16 r4; +	u8 macaddr0[6]; +	__be16 r5; +	u8 macaddr1[6]; +	__be16 r6; +	u8 macaddr2[6]; +	__be16 r7; +	u8 macaddr3[6]; +}; + +#define FW_ACL_MAC_CMD_PFN(x) ((x) << 8) +#define FW_ACL_MAC_CMD_VFN(x) ((x) << 0) +#define FW_ACL_MAC_CMD_EN(x) ((x) << 31) + +struct fw_acl_vlan_cmd { +	__be32 op_to_vfn; +	__be32 en_to_len16; +	u8 nvlan; +	u8 dropnovlan_fm; +	u8 r3_lo[6]; +	__be16 vlanid[16]; +}; + +#define FW_ACL_VLAN_CMD_PFN(x) ((x) << 8) +#define FW_ACL_VLAN_CMD_VFN(x) ((x) << 0) +#define FW_ACL_VLAN_CMD_EN(x) ((x) << 31) +#define FW_ACL_VLAN_CMD_DROPNOVLAN(x) ((x) << 7) +#define FW_ACL_VLAN_CMD_FM(x) ((x) << 6) + +enum fw_port_cap { +	FW_PORT_CAP_SPEED_100M		= 0x0001, +	FW_PORT_CAP_SPEED_1G		= 0x0002, +	FW_PORT_CAP_SPEED_2_5G		= 0x0004, +	FW_PORT_CAP_SPEED_10G		= 0x0008, +	FW_PORT_CAP_SPEED_40G		= 0x0010, +	FW_PORT_CAP_SPEED_100G		= 0x0020, +	FW_PORT_CAP_FC_RX		= 0x0040, +	FW_PORT_CAP_FC_TX		= 0x0080, +	FW_PORT_CAP_ANEG		= 0x0100, +	FW_PORT_CAP_MDI_0		= 0x0200, +	FW_PORT_CAP_MDI_1		= 0x0400, +	FW_PORT_CAP_BEAN		= 0x0800, +	FW_PORT_CAP_PMA_LPBK		= 0x1000, +	FW_PORT_CAP_PCS_LPBK		= 0x2000, +	FW_PORT_CAP_PHYXS_LPBK		= 0x4000, +	FW_PORT_CAP_FAR_END_LPBK	= 0x8000, +}; + +enum fw_port_mdi { +	FW_PORT_MDI_UNCHANGED, +	FW_PORT_MDI_AUTO, +	FW_PORT_MDI_F_STRAIGHT, +	FW_PORT_MDI_F_CROSSOVER +}; + +#define FW_PORT_MDI(x) ((x) << 9) + +enum fw_port_action { +	FW_PORT_ACTION_L1_CFG		= 0x0001, +	FW_PORT_ACTION_L2_CFG		= 0x0002, +	FW_PORT_ACTION_GET_PORT_INFO	= 0x0003, +	FW_PORT_ACTION_L2_PPP_CFG	= 0x0004, +	FW_PORT_ACTION_L2_DCB_CFG	= 0x0005, +	FW_PORT_ACTION_LOW_PWR_TO_NORMAL = 0x0010, +	FW_PORT_ACTION_L1_LOW_PWR_EN	= 0x0011, +	FW_PORT_ACTION_L2_WOL_MODE_EN	= 0x0012, +	FW_PORT_ACTION_LPBK_TO_NORMAL	= 0x0020, +	FW_PORT_ACTION_L1_LPBK		= 0x0021, +	FW_PORT_ACTION_L1_PMA_LPBK	= 0x0022, +	FW_PORT_ACTION_L1_PCS_LPBK	= 0x0023, +	FW_PORT_ACTION_L1_PHYXS_CSIDE_LPBK = 0x0024, +	FW_PORT_ACTION_L1_PHYXS_ESIDE_LPBK = 0x0025, +	FW_PORT_ACTION_PHY_RESET	= 0x0040, +	FW_PORT_ACTION_PMA_RESET	= 0x0041, +	FW_PORT_ACTION_PCS_RESET	= 0x0042, +	FW_PORT_ACTION_PHYXS_RESET	= 0x0043, +	FW_PORT_ACTION_DTEXS_REEST	= 0x0044, +	FW_PORT_ACTION_AN_RESET		= 0x0045 +}; + +enum fw_port_l2cfg_ctlbf { +	FW_PORT_L2_CTLBF_OVLAN0	= 0x01, +	FW_PORT_L2_CTLBF_OVLAN1	= 0x02, +	FW_PORT_L2_CTLBF_OVLAN2	= 0x04, +	FW_PORT_L2_CTLBF_OVLAN3	= 0x08, +	FW_PORT_L2_CTLBF_IVLAN	= 0x10, +	FW_PORT_L2_CTLBF_TXIPG	= 0x20 +}; + +enum fw_port_dcb_cfg { +	FW_PORT_DCB_CFG_PG	= 0x01, +	FW_PORT_DCB_CFG_PFC	= 0x02, +	FW_PORT_DCB_CFG_APPL	= 0x04 +}; + +enum fw_port_dcb_cfg_rc { +	FW_PORT_DCB_CFG_SUCCESS	= 0x0, +	FW_PORT_DCB_CFG_ERROR	= 0x1 +}; + +struct fw_port_cmd { +	__be32 op_to_portid; +	__be32 action_to_len16; +	union fw_port { +		struct fw_port_l1cfg { +			__be32 rcap; +			__be32 r; +		} l1cfg; +		struct fw_port_l2cfg { +			__be16 ctlbf_to_ivlan0; +			__be16 ivlantype; +			__be32 txipg_pkd; +			__be16 ovlan0mask; +			__be16 ovlan0type; +			__be16 ovlan1mask; +			__be16 ovlan1type; +			__be16 ovlan2mask; +			__be16 ovlan2type; +			__be16 ovlan3mask; +			__be16 ovlan3type; +		} l2cfg; +		struct fw_port_info { +			__be32 lstatus_to_modtype; +			__be16 pcap; +			__be16 acap; +		} info; +		struct fw_port_ppp { +			__be32 pppen_to_ncsich; +			__be32 r11; +		} ppp; +		struct fw_port_dcb { +			__be16 cfg; +			u8 up_map; +			u8 sf_cfgrc; +			__be16 prot_ix; +			u8 pe7_to_pe0; +			u8 numTCPFCs; +			__be32 pgid0_to_pgid7; +			__be32 numTCs_oui; +			u8 pgpc[8]; +		} dcb; +	} u; +}; + +#define FW_PORT_CMD_READ (1U << 22) + +#define FW_PORT_CMD_PORTID(x) ((x) << 0) +#define FW_PORT_CMD_PORTID_GET(x) (((x) >> 0) & 0xf) + +#define FW_PORT_CMD_ACTION(x) ((x) << 16) + +#define FW_PORT_CMD_CTLBF(x) ((x) << 10) +#define FW_PORT_CMD_OVLAN3(x) ((x) << 7) +#define FW_PORT_CMD_OVLAN2(x) ((x) << 6) +#define FW_PORT_CMD_OVLAN1(x) ((x) << 5) +#define FW_PORT_CMD_OVLAN0(x) ((x) << 4) +#define FW_PORT_CMD_IVLAN0(x) ((x) << 3) + +#define FW_PORT_CMD_TXIPG(x) ((x) << 19) + +#define FW_PORT_CMD_LSTATUS (1U << 31) +#define FW_PORT_CMD_LSPEED(x) ((x) << 24) +#define FW_PORT_CMD_LSPEED_GET(x) (((x) >> 24) & 0x3f) +#define FW_PORT_CMD_TXPAUSE (1U << 23) +#define FW_PORT_CMD_RXPAUSE (1U << 22) +#define FW_PORT_CMD_MDIOCAP (1U << 21) +#define FW_PORT_CMD_MDIOADDR_GET(x) (((x) >> 16) & 0x1f) +#define FW_PORT_CMD_LPTXPAUSE (1U << 15) +#define FW_PORT_CMD_LPRXPAUSE (1U << 14) +#define FW_PORT_CMD_PTYPE_MASK 0x1f +#define FW_PORT_CMD_PTYPE_GET(x) (((x) >> 8) & FW_PORT_CMD_PTYPE_MASK) +#define FW_PORT_CMD_MODTYPE_MASK 0x1f +#define FW_PORT_CMD_MODTYPE_GET(x) (((x) >> 0) & FW_PORT_CMD_MODTYPE_MASK) + +#define FW_PORT_CMD_PPPEN(x) ((x) << 31) +#define FW_PORT_CMD_TPSRC(x) ((x) << 28) +#define FW_PORT_CMD_NCSISRC(x) ((x) << 24) + +#define FW_PORT_CMD_CH0(x) ((x) << 20) +#define FW_PORT_CMD_CH1(x) ((x) << 16) +#define FW_PORT_CMD_CH2(x) ((x) << 12) +#define FW_PORT_CMD_CH3(x) ((x) << 8) +#define FW_PORT_CMD_NCSICH(x) ((x) << 4) + +enum fw_port_type { +	FW_PORT_TYPE_FIBER, +	FW_PORT_TYPE_KX4, +	FW_PORT_TYPE_BT_SGMII, +	FW_PORT_TYPE_KX, +	FW_PORT_TYPE_BT_XAUI, +	FW_PORT_TYPE_KR, +	FW_PORT_TYPE_CX4, +	FW_PORT_TYPE_TWINAX, + +	FW_PORT_TYPE_NONE = FW_PORT_CMD_PTYPE_MASK +}; + +enum fw_port_module_type { +	FW_PORT_MOD_TYPE_NA, +	FW_PORT_MOD_TYPE_LR, +	FW_PORT_MOD_TYPE_SR, +	FW_PORT_MOD_TYPE_ER, + +	FW_PORT_MOD_TYPE_NONE = FW_PORT_CMD_MODTYPE_MASK +}; + +/* port stats */ +#define FW_NUM_PORT_STATS 50 +#define FW_NUM_PORT_TX_STATS 23 +#define FW_NUM_PORT_RX_STATS 27 + +enum fw_port_stats_tx_index { +	FW_STAT_TX_PORT_BYTES_IX, +	FW_STAT_TX_PORT_FRAMES_IX, +	FW_STAT_TX_PORT_BCAST_IX, +	FW_STAT_TX_PORT_MCAST_IX, +	FW_STAT_TX_PORT_UCAST_IX, +	FW_STAT_TX_PORT_ERROR_IX, +	FW_STAT_TX_PORT_64B_IX, +	FW_STAT_TX_PORT_65B_127B_IX, +	FW_STAT_TX_PORT_128B_255B_IX, +	FW_STAT_TX_PORT_256B_511B_IX, +	FW_STAT_TX_PORT_512B_1023B_IX, +	FW_STAT_TX_PORT_1024B_1518B_IX, +	FW_STAT_TX_PORT_1519B_MAX_IX, +	FW_STAT_TX_PORT_DROP_IX, +	FW_STAT_TX_PORT_PAUSE_IX, +	FW_STAT_TX_PORT_PPP0_IX, +	FW_STAT_TX_PORT_PPP1_IX, +	FW_STAT_TX_PORT_PPP2_IX, +	FW_STAT_TX_PORT_PPP3_IX, +	FW_STAT_TX_PORT_PPP4_IX, +	FW_STAT_TX_PORT_PPP5_IX, +	FW_STAT_TX_PORT_PPP6_IX, +	FW_STAT_TX_PORT_PPP7_IX +}; + +enum fw_port_stat_rx_index { +	FW_STAT_RX_PORT_BYTES_IX, +	FW_STAT_RX_PORT_FRAMES_IX, +	FW_STAT_RX_PORT_BCAST_IX, +	FW_STAT_RX_PORT_MCAST_IX, +	FW_STAT_RX_PORT_UCAST_IX, +	FW_STAT_RX_PORT_MTU_ERROR_IX, +	FW_STAT_RX_PORT_MTU_CRC_ERROR_IX, +	FW_STAT_RX_PORT_CRC_ERROR_IX, +	FW_STAT_RX_PORT_LEN_ERROR_IX, +	FW_STAT_RX_PORT_SYM_ERROR_IX, +	FW_STAT_RX_PORT_64B_IX, +	FW_STAT_RX_PORT_65B_127B_IX, +	FW_STAT_RX_PORT_128B_255B_IX, +	FW_STAT_RX_PORT_256B_511B_IX, +	FW_STAT_RX_PORT_512B_1023B_IX, +	FW_STAT_RX_PORT_1024B_1518B_IX, +	FW_STAT_RX_PORT_1519B_MAX_IX, +	FW_STAT_RX_PORT_PAUSE_IX, +	FW_STAT_RX_PORT_PPP0_IX, +	FW_STAT_RX_PORT_PPP1_IX, +	FW_STAT_RX_PORT_PPP2_IX, +	FW_STAT_RX_PORT_PPP3_IX, +	FW_STAT_RX_PORT_PPP4_IX, +	FW_STAT_RX_PORT_PPP5_IX, +	FW_STAT_RX_PORT_PPP6_IX, +	FW_STAT_RX_PORT_PPP7_IX, +	FW_STAT_RX_PORT_LESS_64B_IX +}; + +struct fw_port_stats_cmd { +	__be32 op_to_portid; +	__be32 retval_len16; +	union fw_port_stats { +		struct fw_port_stats_ctl { +			u8 nstats_bg_bm; +			u8 tx_ix; +			__be16 r6; +			__be32 r7; +			__be64 stat0; +			__be64 stat1; +			__be64 stat2; +			__be64 stat3; +			__be64 stat4; +			__be64 stat5; +		} ctl; +		struct fw_port_stats_all { +			__be64 tx_bytes; +			__be64 tx_frames; +			__be64 tx_bcast; +			__be64 tx_mcast; +			__be64 tx_ucast; +			__be64 tx_error; +			__be64 tx_64b; +			__be64 tx_65b_127b; +			__be64 tx_128b_255b; +			__be64 tx_256b_511b; +			__be64 tx_512b_1023b; +			__be64 tx_1024b_1518b; +			__be64 tx_1519b_max; +			__be64 tx_drop; +			__be64 tx_pause; +			__be64 tx_ppp0; +			__be64 tx_ppp1; +			__be64 tx_ppp2; +			__be64 tx_ppp3; +			__be64 tx_ppp4; +			__be64 tx_ppp5; +			__be64 tx_ppp6; +			__be64 tx_ppp7; +			__be64 rx_bytes; +			__be64 rx_frames; +			__be64 rx_bcast; +			__be64 rx_mcast; +			__be64 rx_ucast; +			__be64 rx_mtu_error; +			__be64 rx_mtu_crc_error; +			__be64 rx_crc_error; +			__be64 rx_len_error; +			__be64 rx_sym_error; +			__be64 rx_64b; +			__be64 rx_65b_127b; +			__be64 rx_128b_255b; +			__be64 rx_256b_511b; +			__be64 rx_512b_1023b; +			__be64 rx_1024b_1518b; +			__be64 rx_1519b_max; +			__be64 rx_pause; +			__be64 rx_ppp0; +			__be64 rx_ppp1; +			__be64 rx_ppp2; +			__be64 rx_ppp3; +			__be64 rx_ppp4; +			__be64 rx_ppp5; +			__be64 rx_ppp6; +			__be64 rx_ppp7; +			__be64 rx_less_64b; +			__be64 rx_bg_drop; +			__be64 rx_bg_trunc; +		} all; +	} u; +}; + +#define FW_PORT_STATS_CMD_NSTATS(x) ((x) << 4) +#define FW_PORT_STATS_CMD_BG_BM(x) ((x) << 0) +#define FW_PORT_STATS_CMD_TX(x) ((x) << 7) +#define FW_PORT_STATS_CMD_IX(x) ((x) << 0) + +/* port loopback stats */ +#define FW_NUM_LB_STATS 16 +enum fw_port_lb_stats_index { +	FW_STAT_LB_PORT_BYTES_IX, +	FW_STAT_LB_PORT_FRAMES_IX, +	FW_STAT_LB_PORT_BCAST_IX, +	FW_STAT_LB_PORT_MCAST_IX, +	FW_STAT_LB_PORT_UCAST_IX, +	FW_STAT_LB_PORT_ERROR_IX, +	FW_STAT_LB_PORT_64B_IX, +	FW_STAT_LB_PORT_65B_127B_IX, +	FW_STAT_LB_PORT_128B_255B_IX, +	FW_STAT_LB_PORT_256B_511B_IX, +	FW_STAT_LB_PORT_512B_1023B_IX, +	FW_STAT_LB_PORT_1024B_1518B_IX, +	FW_STAT_LB_PORT_1519B_MAX_IX, +	FW_STAT_LB_PORT_DROP_FRAMES_IX +}; + +struct fw_port_lb_stats_cmd { +	__be32 op_to_lbport; +	__be32 retval_len16; +	union fw_port_lb_stats { +		struct fw_port_lb_stats_ctl { +			u8 nstats_bg_bm; +			u8 ix_pkd; +			__be16 r6; +			__be32 r7; +			__be64 stat0; +			__be64 stat1; +			__be64 stat2; +			__be64 stat3; +			__be64 stat4; +			__be64 stat5; +		} ctl; +		struct fw_port_lb_stats_all { +			__be64 tx_bytes; +			__be64 tx_frames; +			__be64 tx_bcast; +			__be64 tx_mcast; +			__be64 tx_ucast; +			__be64 tx_error; +			__be64 tx_64b; +			__be64 tx_65b_127b; +			__be64 tx_128b_255b; +			__be64 tx_256b_511b; +			__be64 tx_512b_1023b; +			__be64 tx_1024b_1518b; +			__be64 tx_1519b_max; +			__be64 rx_lb_drop; +			__be64 rx_lb_trunc; +		} all; +	} u; +}; + +#define FW_PORT_LB_STATS_CMD_LBPORT(x) ((x) << 0) +#define FW_PORT_LB_STATS_CMD_NSTATS(x) ((x) << 4) +#define FW_PORT_LB_STATS_CMD_BG_BM(x) ((x) << 0) +#define FW_PORT_LB_STATS_CMD_IX(x) ((x) << 0) + +struct fw_rss_ind_tbl_cmd { +	__be32 op_to_viid; +#define FW_RSS_IND_TBL_CMD_VIID(x) ((x) << 0) +	__be32 retval_len16; +	__be16 niqid; +	__be16 startidx; +	__be32 r3; +	__be32 iq0_to_iq2; +#define FW_RSS_IND_TBL_CMD_IQ0(x) ((x) << 20) +#define FW_RSS_IND_TBL_CMD_IQ1(x) ((x) << 10) +#define FW_RSS_IND_TBL_CMD_IQ2(x) ((x) << 0) +	__be32 iq3_to_iq5; +	__be32 iq6_to_iq8; +	__be32 iq9_to_iq11; +	__be32 iq12_to_iq14; +	__be32 iq15_to_iq17; +	__be32 iq18_to_iq20; +	__be32 iq21_to_iq23; +	__be32 iq24_to_iq26; +	__be32 iq27_to_iq29; +	__be32 iq30_iq31; +	__be32 r15_lo; +}; + +struct fw_rss_glb_config_cmd { +	__be32 op_to_write; +	__be32 retval_len16; +	union fw_rss_glb_config { +		struct fw_rss_glb_config_manual { +			__be32 mode_pkd; +			__be32 r3; +			__be64 r4; +			__be64 r5; +		} manual; +		struct fw_rss_glb_config_basicvirtual { +			__be32 mode_pkd; +			__be32 synmapen_to_hashtoeplitz; +#define FW_RSS_GLB_CONFIG_CMD_SYNMAPEN      (1U << 8) +#define FW_RSS_GLB_CONFIG_CMD_SYN4TUPENIPV6 (1U << 7) +#define FW_RSS_GLB_CONFIG_CMD_SYN2TUPENIPV6 (1U << 6) +#define FW_RSS_GLB_CONFIG_CMD_SYN4TUPENIPV4 (1U << 5) +#define FW_RSS_GLB_CONFIG_CMD_SYN2TUPENIPV4 (1U << 4) +#define FW_RSS_GLB_CONFIG_CMD_OFDMAPEN      (1U << 3) +#define FW_RSS_GLB_CONFIG_CMD_TNLMAPEN      (1U << 2) +#define FW_RSS_GLB_CONFIG_CMD_TNLALLLKP     (1U << 1) +#define FW_RSS_GLB_CONFIG_CMD_HASHTOEPLITZ  (1U << 0) +			__be64 r8; +			__be64 r9; +		} basicvirtual; +	} u; +}; + +#define FW_RSS_GLB_CONFIG_CMD_MODE(x)	((x) << 28) + +#define FW_RSS_GLB_CONFIG_CMD_MODE_MANUAL	0 +#define FW_RSS_GLB_CONFIG_CMD_MODE_BASICVIRTUAL	1 + +struct fw_rss_vi_config_cmd { +	__be32 op_to_viid; +#define FW_RSS_VI_CONFIG_CMD_VIID(x) ((x) << 0) +	__be32 retval_len16; +	union fw_rss_vi_config { +		struct fw_rss_vi_config_manual { +			__be64 r3; +			__be64 r4; +			__be64 r5; +		} manual; +		struct fw_rss_vi_config_basicvirtual { +			__be32 r6; +			__be32 defaultq_to_ip4udpen; +#define FW_RSS_VI_CONFIG_CMD_DEFAULTQ(x)  ((x) << 16) +#define FW_RSS_VI_CONFIG_CMD_IP6FOURTUPEN (1U << 4) +#define FW_RSS_VI_CONFIG_CMD_IP6TWOTUPEN  (1U << 3) +#define FW_RSS_VI_CONFIG_CMD_IP4FOURTUPEN (1U << 2) +#define FW_RSS_VI_CONFIG_CMD_IP4TWOTUPEN  (1U << 1) +#define FW_RSS_VI_CONFIG_CMD_IP4UDPEN     (1U << 0) +			__be64 r9; +			__be64 r10; +		} basicvirtual; +	} u; +}; + +enum fw_error_type { +	FW_ERROR_TYPE_EXCEPTION		= 0x0, +	FW_ERROR_TYPE_HWMODULE		= 0x1, +	FW_ERROR_TYPE_WR		= 0x2, +	FW_ERROR_TYPE_ACL		= 0x3, +}; + +struct fw_error_cmd { +	__be32 op_to_type; +	__be32 len16_pkd; +	union fw_error { +		struct fw_error_exception { +			__be32 info[6]; +		} exception; +		struct fw_error_hwmodule { +			__be32 regaddr; +			__be32 regval; +		} hwmodule; +		struct fw_error_wr { +			__be16 cidx; +			__be16 pfn_vfn; +			__be32 eqid; +			u8 wrhdr[16]; +		} wr; +		struct fw_error_acl { +			__be16 cidx; +			__be16 pfn_vfn; +			__be32 eqid; +			__be16 mv_pkd; +			u8 val[6]; +			__be64 r4; +		} acl; +	} u; +}; + +struct fw_debug_cmd { +	__be32 op_type; +#define FW_DEBUG_CMD_TYPE_GET(x) ((x) & 0xff) +	__be32 len16_pkd; +	union fw_debug { +		struct fw_debug_assert { +			__be32 fcid; +			__be32 line; +			__be32 x; +			__be32 y; +			u8 filename_0_7[8]; +			u8 filename_8_15[8]; +			__be64 r3; +		} assert; +		struct fw_debug_prt { +			__be16 dprtstridx; +			__be16 r3[3]; +			__be32 dprtstrparam0; +			__be32 dprtstrparam1; +			__be32 dprtstrparam2; +			__be32 dprtstrparam3; +		} prt; +	} u; +}; + +struct fw_hdr { +	u8 ver; +	u8 reserved1; +	__be16	len512;			/* bin length in units of 512-bytes */ +	__be32	fw_ver;			/* firmware version */ +	__be32	tp_microcode_ver; +	u8 intfver_nic; +	u8 intfver_vnic; +	u8 intfver_ofld; +	u8 intfver_ri; +	u8 intfver_iscsipdu; +	u8 intfver_iscsi; +	u8 intfver_fcoe; +	u8 reserved2; +	__be32  reserved3[27]; +}; + +#define FW_HDR_FW_VER_MAJOR_GET(x) (((x) >> 24) & 0xff) +#define FW_HDR_FW_VER_MINOR_GET(x) (((x) >> 16) & 0xff) +#define FW_HDR_FW_VER_MICRO_GET(x) (((x) >> 8) & 0xff) +#define FW_HDR_FW_VER_BUILD_GET(x) (((x) >> 0) & 0xff) +#endif /* _T4FW_INTERFACE_H_ */  |