diff options
Diffstat (limited to 'drivers/net')
98 files changed, 8100 insertions, 390 deletions
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index ef2cb241853..b7d45f367d4 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -4431,8 +4431,6 @@ static void bond_uninit(struct net_device *bond_dev)  	list_del(&bond->bond_list); -	bond_work_cancel_all(bond); -  	bond_debug_unregister(bond);  	__hw_addr_flush(&bond->mc_list); diff --git a/drivers/net/can/sja1000/sja1000_of_platform.c b/drivers/net/can/sja1000/sja1000_of_platform.c index 0f5917000aa..6433b81256c 100644 --- a/drivers/net/can/sja1000/sja1000_of_platform.c +++ b/drivers/net/can/sja1000/sja1000_of_platform.c @@ -121,7 +121,7 @@ static int sja1000_ofp_probe(struct platform_device *ofdev)  	}  	irq = irq_of_parse_and_map(np, 0); -	if (irq == NO_IRQ) { +	if (irq == 0) {  		dev_err(&ofdev->dev, "no irq found\n");  		err = -ENODEV;  		goto exit_unmap_mem; diff --git a/drivers/net/ethernet/adi/Kconfig b/drivers/net/ethernet/adi/Kconfig index e49c0eff040..a9481606bbc 100644 --- a/drivers/net/ethernet/adi/Kconfig +++ b/drivers/net/ethernet/adi/Kconfig @@ -61,6 +61,7 @@ config BFIN_RX_DESC_NUM  config BFIN_MAC_USE_HWSTAMP  	bool "Use IEEE 1588 hwstamp" +	depends on BFIN_MAC && BF518  	select PTP_1588_CLOCK  	default y  	---help--- diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 01588b66a38..f771ddfba64 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -80,12 +80,37 @@ static inline void bnx2x_move_fp(struct bnx2x *bp, int from, int to)  		new_txdata_index = new_max_eth_txqs + FCOE_TXQ_IDX_OFFSET;  	} -	memcpy(&bp->bnx2x_txq[old_txdata_index], -	       &bp->bnx2x_txq[new_txdata_index], +	memcpy(&bp->bnx2x_txq[new_txdata_index], +	       &bp->bnx2x_txq[old_txdata_index],  	       sizeof(struct bnx2x_fp_txdata));  	to_fp->txdata_ptr[0] = &bp->bnx2x_txq[new_txdata_index];  } +/** + * bnx2x_shrink_eth_fp - guarantees fastpath structures stay intact + * + * @bp:	driver handle + * @delta:	number of eth queues which were not allocated + */ +static void bnx2x_shrink_eth_fp(struct bnx2x *bp, int delta) +{ +	int i, cos, old_eth_num = BNX2X_NUM_ETH_QUEUES(bp); + +	/* Queue pointer cannot be re-set on an fp-basis, as moving pointer +	 * backward along the array could cause memory to be overriden +	 */ +	for (cos = 1; cos < bp->max_cos; cos++) { +		for (i = 0; i < old_eth_num - delta; i++) { +			struct bnx2x_fastpath *fp = &bp->fp[i]; +			int new_idx = cos * (old_eth_num - delta) + i; + +			memcpy(&bp->bnx2x_txq[new_idx], fp->txdata_ptr[cos], +			       sizeof(struct bnx2x_fp_txdata)); +			fp->txdata_ptr[cos] = &bp->bnx2x_txq[new_idx]; +		} +	} +} +  int load_count[2][3] = { {0} }; /* per-path: 0-common, 1-port0, 2-port1 */  /* free skb in the packet ring at pos idx @@ -3863,6 +3888,7 @@ int bnx2x_alloc_fp_mem(struct bnx2x *bp)  		int delta = BNX2X_NUM_ETH_QUEUES(bp) - i;  		WARN_ON(delta < 0); +		bnx2x_shrink_eth_fp(bp, delta);  		if (CNIC_SUPPORT(bp))  			/* move non eth FPs next to last eth FP  			 * must be done in that order diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 277f17e3c8f..a427b49a886 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -2777,10 +2777,10 @@ static int bnx2x_set_rss_flags(struct bnx2x *bp, struct ethtool_rxnfc *info)  		} else if ((info->flow_type == UDP_V6_FLOW) &&  			   (bp->rss_conf_obj.udp_rss_v6 != udp_rss_requested)) {  			bp->rss_conf_obj.udp_rss_v6 = udp_rss_requested; -			return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0);  			DP(BNX2X_MSG_ETHTOOL,  			   "rss re-configured, UDP 4-tupple %s\n",  			   udp_rss_requested ? "enabled" : "disabled"); +			return bnx2x_config_rss_pf(bp, &bp->rss_conf_obj, 0);  		} else {  			return 0;  		} diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c index 940ef859dc6..5523da3afcd 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c @@ -127,6 +127,17 @@ MODULE_PARM_DESC(debug, " Default debug msglevel");  struct workqueue_struct *bnx2x_wq; +struct bnx2x_mac_vals { +	u32 xmac_addr; +	u32 xmac_val; +	u32 emac_addr; +	u32 emac_val; +	u32 umac_addr; +	u32 umac_val; +	u32 bmac_addr; +	u32 bmac_val[2]; +}; +  enum bnx2x_board_type {  	BCM57710 = 0,  	BCM57711, @@ -9420,12 +9431,19 @@ static inline void bnx2x_undi_int_disable(struct bnx2x *bp)  		bnx2x_undi_int_disable_e1h(bp);  } -static void bnx2x_prev_unload_close_mac(struct bnx2x *bp) +static void bnx2x_prev_unload_close_mac(struct bnx2x *bp, +					struct bnx2x_mac_vals *vals)  {  	u32 val, base_addr, offset, mask, reset_reg;  	bool mac_stopped = false;  	u8 port = BP_PORT(bp); +	/* reset addresses as they also mark which values were changed */ +	vals->bmac_addr = 0; +	vals->umac_addr = 0; +	vals->xmac_addr = 0; +	vals->emac_addr = 0; +  	reset_reg = REG_RD(bp, MISC_REG_RESET_REG_2);  	if (!CHIP_IS_E3(bp)) { @@ -9447,14 +9465,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp)  			 */  			wb_data[0] = REG_RD(bp, base_addr + offset);  			wb_data[1] = REG_RD(bp, base_addr + offset + 0x4); +			vals->bmac_addr = base_addr + offset; +			vals->bmac_val[0] = wb_data[0]; +			vals->bmac_val[1] = wb_data[1];  			wb_data[0] &= ~BMAC_CONTROL_RX_ENABLE; -			REG_WR(bp, base_addr + offset, wb_data[0]); -			REG_WR(bp, base_addr + offset + 0x4, wb_data[1]); +			REG_WR(bp, vals->bmac_addr, wb_data[0]); +			REG_WR(bp, vals->bmac_addr + 0x4, wb_data[1]);  		}  		BNX2X_DEV_INFO("Disable emac Rx\n"); -		REG_WR(bp, NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4, 0); - +		vals->emac_addr = NIG_REG_NIG_EMAC0_EN + BP_PORT(bp)*4; +		vals->emac_val = REG_RD(bp, vals->emac_addr); +		REG_WR(bp, vals->emac_addr, 0);  		mac_stopped = true;  	} else {  		if (reset_reg & MISC_REGISTERS_RESET_REG_2_XMAC) { @@ -9465,14 +9487,18 @@ static void bnx2x_prev_unload_close_mac(struct bnx2x *bp)  			       val & ~(1 << 1));  			REG_WR(bp, base_addr + XMAC_REG_PFC_CTRL_HI,  			       val | (1 << 1)); -			REG_WR(bp, base_addr + XMAC_REG_CTRL, 0); +			vals->xmac_addr = base_addr + XMAC_REG_CTRL; +			vals->xmac_val = REG_RD(bp, vals->xmac_addr); +			REG_WR(bp, vals->xmac_addr, 0);  			mac_stopped = true;  		}  		mask = MISC_REGISTERS_RESET_REG_2_UMAC0 << port;  		if (mask & reset_reg) {  			BNX2X_DEV_INFO("Disable umac Rx\n");  			base_addr = BP_PORT(bp) ? GRCBASE_UMAC1 : GRCBASE_UMAC0; -			REG_WR(bp, base_addr + UMAC_REG_COMMAND_CONFIG, 0); +			vals->umac_addr = base_addr + UMAC_REG_COMMAND_CONFIG; +			vals->umac_val = REG_RD(bp, vals->umac_addr); +			REG_WR(bp, vals->umac_addr, 0);  			mac_stopped = true;  		}  	} @@ -9664,12 +9690,16 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)  {  	u32 reset_reg, tmp_reg = 0, rc;  	bool prev_undi = false; +	struct bnx2x_mac_vals mac_vals; +  	/* It is possible a previous function received 'common' answer,  	 * but hasn't loaded yet, therefore creating a scenario of  	 * multiple functions receiving 'common' on the same path.  	 */  	BNX2X_DEV_INFO("Common unload Flow\n"); +	memset(&mac_vals, 0, sizeof(mac_vals)); +  	if (bnx2x_prev_is_path_marked(bp))  		return bnx2x_prev_mcp_done(bp); @@ -9680,7 +9710,10 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)  		u32 timer_count = 1000;  		/* Close the MAC Rx to prevent BRB from filling up */ -		bnx2x_prev_unload_close_mac(bp); +		bnx2x_prev_unload_close_mac(bp, &mac_vals); + +		/* close LLH filters towards the BRB */ +		bnx2x_set_rx_filter(&bp->link_params, 0);  		/* Check if the UNDI driver was previously loaded  		 * UNDI driver initializes CID offset for normal bell to 0x7 @@ -9727,6 +9760,17 @@ static int bnx2x_prev_unload_common(struct bnx2x *bp)  	/* No packets are in the pipeline, path is ready for reset */  	bnx2x_reset_common(bp); +	if (mac_vals.xmac_addr) +		REG_WR(bp, mac_vals.xmac_addr, mac_vals.xmac_val); +	if (mac_vals.umac_addr) +		REG_WR(bp, mac_vals.umac_addr, mac_vals.umac_val); +	if (mac_vals.emac_addr) +		REG_WR(bp, mac_vals.emac_addr, mac_vals.emac_val); +	if (mac_vals.bmac_addr) { +		REG_WR(bp, mac_vals.bmac_addr, mac_vals.bmac_val[0]); +		REG_WR(bp, mac_vals.bmac_addr + 4, mac_vals.bmac_val[1]); +	} +  	rc = bnx2x_prev_mark_path(bp, prev_undi);  	if (rc) {  		bnx2x_prev_mcp_done(bp); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h index 378988b5709..6db997c78a5 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h @@ -35,6 +35,8 @@  #ifndef __CXGB4_H__  #define __CXGB4_H__ +#include "t4_hw.h" +  #include <linux/bitops.h>  #include <linux/cache.h>  #include <linux/interrupt.h> @@ -212,6 +214,8 @@ struct tp_err_stats {  struct tp_params {  	unsigned int ntxchan;        /* # of Tx channels */  	unsigned int tre;            /* log2 of core clocks per TP tick */ +	unsigned short tx_modq_map;  /* TX modulation scheduler queue to */ +				     /* channel map */  	uint32_t dack_re;            /* DACK timer resolution */  	unsigned short tx_modq[NCHAN];	/* channel to modulation queue map */ @@ -526,6 +530,7 @@ struct adapter {  	struct net_device *port[MAX_NPORTS];  	u8 chan_map[NCHAN];                   /* channel -> port map */ +	u32 filter_mode;  	unsigned int l2t_start;  	unsigned int l2t_end;  	struct l2t_data *l2t; @@ -545,6 +550,129 @@ struct adapter {  	spinlock_t stats_lock;  }; +/* Defined bit width of user definable filter tuples + */ +#define ETHTYPE_BITWIDTH 16 +#define FRAG_BITWIDTH 1 +#define MACIDX_BITWIDTH 9 +#define FCOE_BITWIDTH 1 +#define IPORT_BITWIDTH 3 +#define MATCHTYPE_BITWIDTH 3 +#define PROTO_BITWIDTH 8 +#define TOS_BITWIDTH 8 +#define PF_BITWIDTH 8 +#define VF_BITWIDTH 8 +#define IVLAN_BITWIDTH 16 +#define OVLAN_BITWIDTH 16 + +/* Filter matching rules.  These consist of a set of ingress packet field + * (value, mask) tuples.  The associated ingress packet field matches the + * tuple when ((field & mask) == value).  (Thus a wildcard "don't care" field + * rule can be constructed by specifying a tuple of (0, 0).)  A filter rule + * matches an ingress packet when all of the individual individual field + * matching rules are true. + * + * Partial field masks are always valid, however, while it may be easy to + * understand their meanings for some fields (e.g. IP address to match a + * subnet), for others making sensible partial masks is less intuitive (e.g. + * MPS match type) ... + * + * Most of the following data structures are modeled on T4 capabilities. + * Drivers for earlier chips use the subsets which make sense for those chips. + * We really need to come up with a hardware-independent mechanism to + * represent hardware filter capabilities ... + */ +struct ch_filter_tuple { +	/* Compressed header matching field rules.  The TP_VLAN_PRI_MAP +	 * register selects which of these fields will participate in the +	 * filter match rules -- up to a maximum of 36 bits.  Because +	 * TP_VLAN_PRI_MAP is a global register, all filters must use the same +	 * set of fields. +	 */ +	uint32_t ethtype:ETHTYPE_BITWIDTH;      /* Ethernet type */ +	uint32_t frag:FRAG_BITWIDTH;            /* IP fragmentation header */ +	uint32_t ivlan_vld:1;                   /* inner VLAN valid */ +	uint32_t ovlan_vld:1;                   /* outer VLAN valid */ +	uint32_t pfvf_vld:1;                    /* PF/VF valid */ +	uint32_t macidx:MACIDX_BITWIDTH;        /* exact match MAC index */ +	uint32_t fcoe:FCOE_BITWIDTH;            /* FCoE packet */ +	uint32_t iport:IPORT_BITWIDTH;          /* ingress port */ +	uint32_t matchtype:MATCHTYPE_BITWIDTH;  /* MPS match type */ +	uint32_t proto:PROTO_BITWIDTH;          /* protocol type */ +	uint32_t tos:TOS_BITWIDTH;              /* TOS/Traffic Type */ +	uint32_t pf:PF_BITWIDTH;                /* PCI-E PF ID */ +	uint32_t vf:VF_BITWIDTH;                /* PCI-E VF ID */ +	uint32_t ivlan:IVLAN_BITWIDTH;          /* inner VLAN */ +	uint32_t ovlan:OVLAN_BITWIDTH;          /* outer VLAN */ + +	/* Uncompressed header matching field rules.  These are always +	 * available for field rules. +	 */ +	uint8_t lip[16];        /* local IP address (IPv4 in [3:0]) */ +	uint8_t fip[16];        /* foreign IP address (IPv4 in [3:0]) */ +	uint16_t lport;         /* local port */ +	uint16_t fport;         /* foreign port */ +}; + +/* A filter ioctl command. + */ +struct ch_filter_specification { +	/* Administrative fields for filter. +	 */ +	uint32_t hitcnts:1;     /* count filter hits in TCB */ +	uint32_t prio:1;        /* filter has priority over active/server */ + +	/* Fundamental filter typing.  This is the one element of filter +	 * matching that doesn't exist as a (value, mask) tuple. +	 */ +	uint32_t type:1;        /* 0 => IPv4, 1 => IPv6 */ + +	/* Packet dispatch information.  Ingress packets which match the +	 * filter rules will be dropped, passed to the host or switched back +	 * out as egress packets. +	 */ +	uint32_t action:2;      /* drop, pass, switch */ + +	uint32_t rpttid:1;      /* report TID in RSS hash field */ + +	uint32_t dirsteer:1;    /* 0 => RSS, 1 => steer to iq */ +	uint32_t iq:10;         /* ingress queue */ + +	uint32_t maskhash:1;    /* dirsteer=0: store RSS hash in TCB */ +	uint32_t dirsteerhash:1;/* dirsteer=1: 0 => TCB contains RSS hash */ +				/*             1 => TCB contains IQ ID */ + +	/* Switch proxy/rewrite fields.  An ingress packet which matches a +	 * filter with "switch" set will be looped back out as an egress +	 * packet -- potentially with some Ethernet header rewriting. +	 */ +	uint32_t eport:2;       /* egress port to switch packet out */ +	uint32_t newdmac:1;     /* rewrite destination MAC address */ +	uint32_t newsmac:1;     /* rewrite source MAC address */ +	uint32_t newvlan:2;     /* rewrite VLAN Tag */ +	uint8_t dmac[ETH_ALEN]; /* new destination MAC address */ +	uint8_t smac[ETH_ALEN]; /* new source MAC address */ +	uint16_t vlan;          /* VLAN Tag to insert */ + +	/* Filter rule value/mask pairs. +	 */ +	struct ch_filter_tuple val; +	struct ch_filter_tuple mask; +}; + +enum { +	FILTER_PASS = 0,        /* default */ +	FILTER_DROP, +	FILTER_SWITCH +}; + +enum { +	VLAN_NOCHANGE = 0,      /* default */ +	VLAN_REMOVE, +	VLAN_INSERT, +	VLAN_REWRITE +}; +  static inline u32 t4_read_reg(struct adapter *adap, u32 reg_addr)  {  	return readl(adap->regs + reg_addr); @@ -701,6 +829,12 @@ static inline int t4_wr_mbox_ns(struct adapter *adap, int mbox, const void *cmd,  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); +void t4_read_indirect(struct adapter *adap, unsigned int addr_reg, +		      unsigned int data_reg, u32 *vals, unsigned int nregs, +		      unsigned int start_idx); + +struct fw_filter_wr; +  void t4_intr_enable(struct adapter *adapter);  void t4_intr_disable(struct adapter *adapter);  int t4_slow_intr_handler(struct adapter *adapter); @@ -737,6 +871,8 @@ void t4_tp_get_tcp_stats(struct adapter *adap, struct tp_tcp_stats *v4,  void t4_load_mtus(struct adapter *adap, const unsigned short *mtus,  		  const unsigned short *alpha, const unsigned short *beta); +void t4_mk_filtdelwr(unsigned int ftid, struct fw_filter_wr *wr, int qid); +  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, diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index a27b4ae20f4..f0718e1a836 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -175,6 +175,30 @@ enum {  	MIN_FL_ENTRIES       = 16  }; +/* Host shadow copy of ingress filter entry.  This is in host native format + * and doesn't match the ordering or bit order, etc. of the hardware of the + * firmware command.  The use of bit-field structure elements is purely to + * remind ourselves of the field size limitations and save memory in the case + * where the filter table is large. + */ +struct filter_entry { +	/* Administrative fields for filter. +	 */ +	u32 valid:1;            /* filter allocated and valid */ +	u32 locked:1;           /* filter is administratively locked */ + +	u32 pending:1;          /* filter action is pending firmware reply */ +	u32 smtidx:8;           /* Source MAC Table index for smac */ +	struct l2t_entry *l2t;  /* Layer Two Table entry for dmac */ + +	/* The filter itself.  Most of this is a straight copy of information +	 * provided by the extended ioctl().  Some fields are translated to +	 * internal forms -- for instance the Ingress Queue ID passed in from +	 * the ioctl() is translated into the Absolute Ingress Queue ID. +	 */ +	struct ch_filter_specification fs; +}; +  #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) @@ -325,6 +349,9 @@ enum {  static unsigned int tp_vlan_pri_map = TP_VLAN_PRI_MAP_DEFAULT; +module_param(tp_vlan_pri_map, uint, 0644); +MODULE_PARM_DESC(tp_vlan_pri_map, "global compressed filter configuration"); +  static struct dentry *cxgb4_debugfs_root;  static LIST_HEAD(adapter_list); @@ -506,8 +533,67 @@ static int link_start(struct net_device *dev)  	return ret;  } -/* - * Response queue handler for the FW event queue. +/* Clear a filter and release any of its resources that we own.  This also + * clears the filter's "pending" status. + */ +static void clear_filter(struct adapter *adap, struct filter_entry *f) +{ +	/* If the new or old filter have loopback rewriteing rules then we'll +	 * need to free any existing Layer Two Table (L2T) entries of the old +	 * filter rule.  The firmware will handle freeing up any Source MAC +	 * Table (SMT) entries used for rewriting Source MAC Addresses in +	 * loopback rules. +	 */ +	if (f->l2t) +		cxgb4_l2t_release(f->l2t); + +	/* The zeroing of the filter rule below clears the filter valid, +	 * pending, locked flags, l2t pointer, etc. so it's all we need for +	 * this operation. +	 */ +	memset(f, 0, sizeof(*f)); +} + +/* Handle a filter write/deletion reply. + */ +static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl) +{ +	unsigned int idx = GET_TID(rpl); +	unsigned int nidx = idx - adap->tids.ftid_base; +	unsigned int ret; +	struct filter_entry *f; + +	if (idx >= adap->tids.ftid_base && nidx < +	   (adap->tids.nftids + adap->tids.nsftids)) { +		idx = nidx; +		ret = GET_TCB_COOKIE(rpl->cookie); +		f = &adap->tids.ftid_tab[idx]; + +		if (ret == FW_FILTER_WR_FLT_DELETED) { +			/* Clear the filter when we get confirmation from the +			 * hardware that the filter has been deleted. +			 */ +			clear_filter(adap, f); +		} else if (ret == FW_FILTER_WR_SMT_TBL_FULL) { +			dev_err(adap->pdev_dev, "filter %u setup failed due to full SMT\n", +				idx); +			clear_filter(adap, f); +		} else if (ret == FW_FILTER_WR_FLT_ADDED) { +			f->smtidx = (be64_to_cpu(rpl->oldval) >> 24) & 0xff; +			f->pending = 0;  /* asynchronous setup completed */ +			f->valid = 1; +		} else { +			/* Something went wrong.  Issue a warning about the +			 * problem and clear everything out. +			 */ +			dev_err(adap->pdev_dev, "filter %u setup failed with error %u\n", +				idx, ret); +			clear_filter(adap, f); +		} +	} +} + +/* Response queue handler for the FW event queue.   */  static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,  			  const struct pkt_gl *gl) @@ -542,6 +628,10 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 *rsp,  		const struct cpl_l2t_write_rpl *p = (void *)rsp;  		do_l2t_write_rpl(q->adap, p); +	} else if (opcode == CPL_SET_TCB_RPL) { +		const struct cpl_set_tcb_rpl *p = (void *)rsp; + +		filter_rpl(q->adap, p);  	} else  		dev_err(q->adap->pdev_dev,  			"unexpected CPL %#x on FW event queue\n", opcode); @@ -983,6 +1073,148 @@ static void t4_free_mem(void *addr)  		kfree(addr);  } +/* Send a Work Request to write the filter at a specified index.  We construct + * a Firmware Filter Work Request to have the work done and put the indicated + * filter into "pending" mode which will prevent any further actions against + * it till we get a reply from the firmware on the completion status of the + * request. + */ +static int set_filter_wr(struct adapter *adapter, int fidx) +{ +	struct filter_entry *f = &adapter->tids.ftid_tab[fidx]; +	struct sk_buff *skb; +	struct fw_filter_wr *fwr; +	unsigned int ftid; + +	/* If the new filter requires loopback Destination MAC and/or VLAN +	 * rewriting then we need to allocate a Layer 2 Table (L2T) entry for +	 * the filter. +	 */ +	if (f->fs.newdmac || f->fs.newvlan) { +		/* allocate L2T entry for new filter */ +		f->l2t = t4_l2t_alloc_switching(adapter->l2t); +		if (f->l2t == NULL) +			return -EAGAIN; +		if (t4_l2t_set_switching(adapter, f->l2t, f->fs.vlan, +					f->fs.eport, f->fs.dmac)) { +			cxgb4_l2t_release(f->l2t); +			f->l2t = NULL; +			return -ENOMEM; +		} +	} + +	ftid = adapter->tids.ftid_base + fidx; + +	skb = alloc_skb(sizeof(*fwr), GFP_KERNEL | __GFP_NOFAIL); +	fwr = (struct fw_filter_wr *)__skb_put(skb, sizeof(*fwr)); +	memset(fwr, 0, sizeof(*fwr)); + +	/* It would be nice to put most of the following in t4_hw.c but most +	 * of the work is translating the cxgbtool ch_filter_specification +	 * into the Work Request and the definition of that structure is +	 * currently in cxgbtool.h which isn't appropriate to pull into the +	 * common code.  We may eventually try to come up with a more neutral +	 * filter specification structure but for now it's easiest to simply +	 * put this fairly direct code in line ... +	 */ +	fwr->op_pkd = htonl(FW_WR_OP(FW_FILTER_WR)); +	fwr->len16_pkd = htonl(FW_WR_LEN16(sizeof(*fwr)/16)); +	fwr->tid_to_iq = +		htonl(V_FW_FILTER_WR_TID(ftid) | +		      V_FW_FILTER_WR_RQTYPE(f->fs.type) | +		      V_FW_FILTER_WR_NOREPLY(0) | +		      V_FW_FILTER_WR_IQ(f->fs.iq)); +	fwr->del_filter_to_l2tix = +		htonl(V_FW_FILTER_WR_RPTTID(f->fs.rpttid) | +		      V_FW_FILTER_WR_DROP(f->fs.action == FILTER_DROP) | +		      V_FW_FILTER_WR_DIRSTEER(f->fs.dirsteer) | +		      V_FW_FILTER_WR_MASKHASH(f->fs.maskhash) | +		      V_FW_FILTER_WR_DIRSTEERHASH(f->fs.dirsteerhash) | +		      V_FW_FILTER_WR_LPBK(f->fs.action == FILTER_SWITCH) | +		      V_FW_FILTER_WR_DMAC(f->fs.newdmac) | +		      V_FW_FILTER_WR_SMAC(f->fs.newsmac) | +		      V_FW_FILTER_WR_INSVLAN(f->fs.newvlan == VLAN_INSERT || +					     f->fs.newvlan == VLAN_REWRITE) | +		      V_FW_FILTER_WR_RMVLAN(f->fs.newvlan == VLAN_REMOVE || +					    f->fs.newvlan == VLAN_REWRITE) | +		      V_FW_FILTER_WR_HITCNTS(f->fs.hitcnts) | +		      V_FW_FILTER_WR_TXCHAN(f->fs.eport) | +		      V_FW_FILTER_WR_PRIO(f->fs.prio) | +		      V_FW_FILTER_WR_L2TIX(f->l2t ? f->l2t->idx : 0)); +	fwr->ethtype = htons(f->fs.val.ethtype); +	fwr->ethtypem = htons(f->fs.mask.ethtype); +	fwr->frag_to_ovlan_vldm = +		(V_FW_FILTER_WR_FRAG(f->fs.val.frag) | +		 V_FW_FILTER_WR_FRAGM(f->fs.mask.frag) | +		 V_FW_FILTER_WR_IVLAN_VLD(f->fs.val.ivlan_vld) | +		 V_FW_FILTER_WR_OVLAN_VLD(f->fs.val.ovlan_vld) | +		 V_FW_FILTER_WR_IVLAN_VLDM(f->fs.mask.ivlan_vld) | +		 V_FW_FILTER_WR_OVLAN_VLDM(f->fs.mask.ovlan_vld)); +	fwr->smac_sel = 0; +	fwr->rx_chan_rx_rpl_iq = +		htons(V_FW_FILTER_WR_RX_CHAN(0) | +		      V_FW_FILTER_WR_RX_RPL_IQ(adapter->sge.fw_evtq.abs_id)); +	fwr->maci_to_matchtypem = +		htonl(V_FW_FILTER_WR_MACI(f->fs.val.macidx) | +		      V_FW_FILTER_WR_MACIM(f->fs.mask.macidx) | +		      V_FW_FILTER_WR_FCOE(f->fs.val.fcoe) | +		      V_FW_FILTER_WR_FCOEM(f->fs.mask.fcoe) | +		      V_FW_FILTER_WR_PORT(f->fs.val.iport) | +		      V_FW_FILTER_WR_PORTM(f->fs.mask.iport) | +		      V_FW_FILTER_WR_MATCHTYPE(f->fs.val.matchtype) | +		      V_FW_FILTER_WR_MATCHTYPEM(f->fs.mask.matchtype)); +	fwr->ptcl = f->fs.val.proto; +	fwr->ptclm = f->fs.mask.proto; +	fwr->ttyp = f->fs.val.tos; +	fwr->ttypm = f->fs.mask.tos; +	fwr->ivlan = htons(f->fs.val.ivlan); +	fwr->ivlanm = htons(f->fs.mask.ivlan); +	fwr->ovlan = htons(f->fs.val.ovlan); +	fwr->ovlanm = htons(f->fs.mask.ovlan); +	memcpy(fwr->lip, f->fs.val.lip, sizeof(fwr->lip)); +	memcpy(fwr->lipm, f->fs.mask.lip, sizeof(fwr->lipm)); +	memcpy(fwr->fip, f->fs.val.fip, sizeof(fwr->fip)); +	memcpy(fwr->fipm, f->fs.mask.fip, sizeof(fwr->fipm)); +	fwr->lp = htons(f->fs.val.lport); +	fwr->lpm = htons(f->fs.mask.lport); +	fwr->fp = htons(f->fs.val.fport); +	fwr->fpm = htons(f->fs.mask.fport); +	if (f->fs.newsmac) +		memcpy(fwr->sma, f->fs.smac, sizeof(fwr->sma)); + +	/* Mark the filter as "pending" and ship off the Filter Work Request. +	 * When we get the Work Request Reply we'll clear the pending status. +	 */ +	f->pending = 1; +	set_wr_txq(skb, CPL_PRIORITY_CONTROL, f->fs.val.iport & 0x3); +	t4_ofld_send(adapter, skb); +	return 0; +} + +/* Delete the filter at a specified index. + */ +static int del_filter_wr(struct adapter *adapter, int fidx) +{ +	struct filter_entry *f = &adapter->tids.ftid_tab[fidx]; +	struct sk_buff *skb; +	struct fw_filter_wr *fwr; +	unsigned int len, ftid; + +	len = sizeof(*fwr); +	ftid = adapter->tids.ftid_base + fidx; + +	skb = alloc_skb(len, GFP_KERNEL | __GFP_NOFAIL); +	fwr = (struct fw_filter_wr *)__skb_put(skb, len); +	t4_mk_filtdelwr(ftid, fwr, adapter->sge.fw_evtq.abs_id); + +	/* Mark the filter as "pending" and ship off the Filter Work Request. +	 * When we get the Work Request Reply we'll clear the pending status. +	 */ +	f->pending = 1; +	t4_mgmt_tx(adapter, skb); +	return 0; +} +  static inline int is_offload(const struct adapter *adap)  {  	return adap->params.offload; @@ -2195,7 +2427,7 @@ int cxgb4_alloc_atid(struct tid_info *t, void *data)  	if (t->afree) {  		union aopen_entry *p = t->afree; -		atid = p - t->atid_tab; +		atid = (p - t->atid_tab) + t->atid_base;  		t->afree = p->next;  		p->data = data;  		t->atids_in_use++; @@ -2210,7 +2442,7 @@ EXPORT_SYMBOL(cxgb4_alloc_atid);   */  void cxgb4_free_atid(struct tid_info *t, unsigned int atid)  { -	union aopen_entry *p = &t->atid_tab[atid]; +	union aopen_entry *p = &t->atid_tab[atid - t->atid_base];  	spin_lock_bh(&t->atid_lock);  	p->next = t->afree; @@ -2249,8 +2481,34 @@ int cxgb4_alloc_stid(struct tid_info *t, int family, void *data)  }  EXPORT_SYMBOL(cxgb4_alloc_stid); -/* - * Release a server TID. +/* Allocate a server filter TID and set it to the supplied value. + */ +int cxgb4_alloc_sftid(struct tid_info *t, int family, void *data) +{ +	int stid; + +	spin_lock_bh(&t->stid_lock); +	if (family == PF_INET) { +		stid = find_next_zero_bit(t->stid_bmap, +				t->nstids + t->nsftids, t->nstids); +		if (stid < (t->nstids + t->nsftids)) +			__set_bit(stid, t->stid_bmap); +		else +			stid = -1; +	} else { +		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_sftid); + +/* Release a server TID.   */  void cxgb4_free_stid(struct tid_info *t, unsigned int stid, int family)  { @@ -2362,18 +2620,26 @@ EXPORT_SYMBOL(cxgb4_remove_tid);  static int tid_init(struct tid_info *t)  {  	size_t size; +	unsigned int stid_bmap_size;  	unsigned int natids = t->natids; -	size = t->ntids * sizeof(*t->tid_tab) + natids * sizeof(*t->atid_tab) + +	stid_bmap_size = BITS_TO_LONGS(t->nstids + t->nsftids); +	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->nsftids * sizeof(*t->stid_tab) + +	       stid_bmap_size * sizeof(long) + +	       t->nftids * sizeof(*t->ftid_tab) + +	       t->nsftids * sizeof(*t->ftid_tab); +  	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]; +	t->stid_bmap = (unsigned long *)&t->stid_tab[t->nstids + t->nsftids]; +	t->ftid_tab = (struct filter_entry *)&t->stid_bmap[stid_bmap_size];  	spin_lock_init(&t->stid_lock);  	spin_lock_init(&t->atid_lock); @@ -2388,7 +2654,7 @@ static int tid_init(struct tid_info *t)  			t->atid_tab[natids - 1].next = &t->atid_tab[natids];  		t->afree = t->atid_tab;  	} -	bitmap_zero(t->stid_bmap, t->nstids); +	bitmap_zero(t->stid_bmap, t->nstids + t->nsftids);  	return 0;  } @@ -2404,7 +2670,8 @@ static int tid_init(struct tid_info *t)   *	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) +			__be32 sip, __be16 sport, __be16 vlan, +			unsigned int queue)  {  	unsigned int chan;  	struct sk_buff *skb; @@ -2750,6 +3017,7 @@ static void uld_attach(struct adapter *adap, unsigned int uld)  {  	void *handle;  	struct cxgb4_lld_info lli; +	unsigned short i;  	lli.pdev = adap->pdev;  	lli.l2t = adap->l2t; @@ -2776,10 +3044,16 @@ static void uld_attach(struct adapter *adap, unsigned int uld)  	lli.ucq_density = 1 << QUEUESPERPAGEPF0_GET(  			t4_read_reg(adap, SGE_INGRESS_QUEUES_PER_PAGE_PF) >>  			(adap->fn * 4)); +	lli.filt_mode = adap->filter_mode; +	/* MODQ_REQ_MAP sets queues 0-3 to chan 0-3 */ +	for (i = 0; i < NCHAN; i++) +		lli.tx_modq[i] = i;  	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;  	lli.dbfifo_int_thresh = dbfifo_int_thresh; +	lli.sge_pktshift = adap->sge.pktshift; +	lli.enable_fw_ofld_conn = adap->flags & FW_OFLD_CONN;  	handle = ulds[uld].add(&lli);  	if (IS_ERR(handle)) { @@ -2999,6 +3273,126 @@ static int cxgb_close(struct net_device *dev)  	return t4_enable_vi(adapter, adapter->fn, pi->viid, false, false);  } +/* Return an error number if the indicated filter isn't writable ... + */ +static int writable_filter(struct filter_entry *f) +{ +	if (f->locked) +		return -EPERM; +	if (f->pending) +		return -EBUSY; + +	return 0; +} + +/* Delete the filter at the specified index (if valid).  The checks for all + * the common problems with doing this like the filter being locked, currently + * pending in another operation, etc. + */ +static int delete_filter(struct adapter *adapter, unsigned int fidx) +{ +	struct filter_entry *f; +	int ret; + +	if (fidx >= adapter->tids.nftids + adapter->tids.nsftids) +		return -EINVAL; + +	f = &adapter->tids.ftid_tab[fidx]; +	ret = writable_filter(f); +	if (ret) +		return ret; +	if (f->valid) +		return del_filter_wr(adapter, fidx); + +	return 0; +} + +int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, +		__be32 sip, __be16 sport, __be16 vlan, +		unsigned int queue, unsigned char port, unsigned char mask) +{ +	int ret; +	struct filter_entry *f; +	struct adapter *adap; +	int i; +	u8 *val; + +	adap = netdev2adap(dev); + +	/* Adjust stid to correct filter index */ +	stid -= adap->tids.nstids; +	stid += adap->tids.nftids; + +	/* Check to make sure the filter requested is writable ... +	 */ +	f = &adap->tids.ftid_tab[stid]; +	ret = writable_filter(f); +	if (ret) +		return ret; + +	/* Clear out any old resources being used by the filter before +	 * we start constructing the new filter. +	 */ +	if (f->valid) +		clear_filter(adap, f); + +	/* Clear out filter specifications */ +	memset(&f->fs, 0, sizeof(struct ch_filter_specification)); +	f->fs.val.lport = cpu_to_be16(sport); +	f->fs.mask.lport  = ~0; +	val = (u8 *)&sip; +	if ((val[0] | val[1] | val[2] | val[3]) != 0) { +		for (i = 0; i < 4; i++) { +			f->fs.val.lip[i] = val[i]; +			f->fs.mask.lip[i] = ~0; +		} +		if (adap->filter_mode & F_PORT) { +			f->fs.val.iport = port; +			f->fs.mask.iport = mask; +		} +	} + +	f->fs.dirsteer = 1; +	f->fs.iq = queue; +	/* Mark filter as locked */ +	f->locked = 1; +	f->fs.rpttid = 1; + +	ret = set_filter_wr(adap, stid); +	if (ret) { +		clear_filter(adap, f); +		return ret; +	} + +	return 0; +} +EXPORT_SYMBOL(cxgb4_create_server_filter); + +int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid, +		unsigned int queue, bool ipv6) +{ +	int ret; +	struct filter_entry *f; +	struct adapter *adap; + +	adap = netdev2adap(dev); + +	/* Adjust stid to correct filter index */ +	stid -= adap->tids.nstids; +	stid += adap->tids.nftids; + +	f = &adap->tids.ftid_tab[stid]; +	/* Unlock the filter */ +	f->locked = 0; + +	ret = delete_filter(adap, stid); +	if (ret) +		return ret; + +	return 0; +} +EXPORT_SYMBOL(cxgb4_remove_server_filter); +  static struct rtnl_link_stats64 *cxgb_get_stats(struct net_device *dev,  						struct rtnl_link_stats64 *ns)  { @@ -3245,6 +3639,34 @@ static int adap_init1(struct adapter *adap, struct fw_caps_config_cmd *c)  	v = t4_read_reg(adap, TP_PIO_DATA);  	t4_write_reg(adap, TP_PIO_DATA, v & ~CSUM_HAS_PSEUDO_HDR); +	/* first 4 Tx modulation queues point to consecutive Tx channels */ +	adap->params.tp.tx_modq_map = 0xE4; +	t4_write_reg(adap, A_TP_TX_MOD_QUEUE_REQ_MAP, +		     V_TX_MOD_QUEUE_REQ_MAP(adap->params.tp.tx_modq_map)); + +	/* associate each Tx modulation queue with consecutive Tx channels */ +	v = 0x84218421; +	t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, +			  &v, 1, A_TP_TX_SCHED_HDR); +	t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, +			  &v, 1, A_TP_TX_SCHED_FIFO); +	t4_write_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, +			  &v, 1, A_TP_TX_SCHED_PCMD); + +#define T4_TX_MODQ_10G_WEIGHT_DEFAULT 16 /* in KB units */ +	if (is_offload(adap)) { +		t4_write_reg(adap, A_TP_TX_MOD_QUEUE_WEIGHT0, +			     V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); +		t4_write_reg(adap, A_TP_TX_MOD_CHANNEL_WEIGHT, +			     V_TX_MODQ_WEIGHT0(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT1(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT2(T4_TX_MODQ_10G_WEIGHT_DEFAULT) | +			     V_TX_MODQ_WEIGHT3(T4_TX_MODQ_10G_WEIGHT_DEFAULT)); +	} +  	/* get basic stuff going */  	return t4_early_init(adap, adap->fn);  } @@ -4035,6 +4457,10 @@ static int adap_init0(struct adapter *adap)  	for (j = 0; j < NCHAN; j++)  		adap->params.tp.tx_modq[j] = j; +	t4_read_indirect(adap, TP_PIO_ADDR, TP_PIO_DATA, +			 &adap->filter_mode, 1, +			 TP_VLAN_PRI_MAP); +  	adap->flags |= FW_OK;  	return 0; @@ -4661,6 +5087,17 @@ static void remove_one(struct pci_dev *pdev)  		if (adapter->debugfs_root)  			debugfs_remove_recursive(adapter->debugfs_root); +		/* If we allocated filters, free up state associated with any +		 * valid filters ... +		 */ +		if (adapter->tids.ftid_tab) { +			struct filter_entry *f = &adapter->tids.ftid_tab[0]; +			for (i = 0; i < (adapter->tids.nftids + +					adapter->tids.nsftids); i++, f++) +				if (f->valid) +					clear_filter(adapter, f); +		} +  		if (adapter->flags & FULL_INIT_DONE)  			cxgb_down(adapter); diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h index 39bec73ff87..e2bbc7f3e2d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h @@ -38,6 +38,7 @@  #include <linux/cache.h>  #include <linux/spinlock.h>  #include <linux/skbuff.h> +#include <linux/inetdevice.h>  #include <linux/atomic.h>  /* CPL message priority levels */ @@ -97,7 +98,9 @@ struct tid_info {  	union aopen_entry *atid_tab;  	unsigned int natids; +	unsigned int atid_base; +	struct filter_entry *ftid_tab;  	unsigned int nftids;  	unsigned int ftid_base;  	unsigned int aftid_base; @@ -129,7 +132,7 @@ static inline void *lookup_atid(const struct tid_info *t, unsigned int atid)  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; +	return stid < (t->nstids + t->nsftids) ? t->stid_tab[stid].data : NULL;  }  static inline void cxgb4_insert_tid(struct tid_info *t, void *data, @@ -141,6 +144,7 @@ static inline void cxgb4_insert_tid(struct tid_info *t, void *data,  int cxgb4_alloc_atid(struct tid_info *t, void *data);  int cxgb4_alloc_stid(struct tid_info *t, int family, void *data); +int cxgb4_alloc_sftid(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); @@ -148,8 +152,14 @@ void cxgb4_remove_tid(struct tid_info *t, unsigned int qid, 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); - +			__be32 sip, __be16 sport, __be16 vlan, +			unsigned int queue); +int cxgb4_create_server_filter(const struct net_device *dev, unsigned int stid, +			       __be32 sip, __be16 sport, __be16 vlan, +			       unsigned int queue, +			       unsigned char port, unsigned char mask); +int cxgb4_remove_server_filter(const struct net_device *dev, unsigned int stid, +			       unsigned int queue, bool ipv6);  static inline void set_wr_txq(struct sk_buff *skb, int prio, int queue)  {  	skb_set_queue_mapping(skb, (queue << 1) | prio); @@ -221,9 +231,16 @@ struct cxgb4_lld_info {  	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 */ +	unsigned short filt_mode;            /* filter optional components */ +	unsigned short tx_modq[NCHAN];       /* maps each tx channel to a */ +					     /* scheduler queue */  	void __iomem *gts_reg;               /* address of GTS register */  	void __iomem *db_reg;                /* address of kernel doorbell */  	int dbfifo_int_thresh;		     /* doorbell fifo int threshold */ +	unsigned int sge_pktshift;           /* Padding between CPL and */ +					     /*	packet data */ +	bool enable_fw_ofld_conn;            /* Enable connection through fw */ +					     /* WR */  };  struct cxgb4_uld_info { diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.c b/drivers/net/ethernet/chelsio/cxgb4/l2t.c index 6ac77a62f36..29878098101 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.c +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.c @@ -484,6 +484,38 @@ void t4_l2t_update(struct adapter *adap, struct neighbour *neigh)  		handle_failed_resolution(adap, arpq);  } +/* Allocate an L2T entry for use by a switching rule.  Such 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; diff --git a/drivers/net/ethernet/chelsio/cxgb4/l2t.h b/drivers/net/ethernet/chelsio/cxgb4/l2t.h index 02b31d0c641..108c0f1fce1 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/l2t.h +++ b/drivers/net/ethernet/chelsio/cxgb4/l2t.h @@ -100,6 +100,9 @@ struct l2t_entry *cxgb4_l2t_get(struct l2t_data *d, struct neighbour *neigh,  				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); diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c index 8d9c7547b07..22f3af5166b 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c @@ -109,7 +109,7 @@ void t4_set_reg_field(struct adapter *adapter, unsigned int addr, u32 mask,   *	Reads registers that are accessed indirectly through an address/data   *	register pair.   */ -static void t4_read_indirect(struct adapter *adap, unsigned int addr_reg, +void t4_read_indirect(struct adapter *adap, unsigned int addr_reg,  			     unsigned int data_reg, u32 *vals,  			     unsigned int nregs, unsigned int start_idx)  { @@ -2268,6 +2268,26 @@ int t4_wol_pat_enable(struct adapter *adap, unsigned int port, unsigned int map,  	return 0;  } +/*     t4_mk_filtdelwr - create a delete filter WR + *     @ftid: the filter ID + *     @wr: the filter work request to populate + *     @qid: ingress queue to receive the delete notification + * + *     Creates a filter work request to delete the supplied filter.  If @qid is + *     negative the delete notification is suppressed. + */ +void t4_mk_filtdelwr(unsigned int ftid, struct fw_filter_wr *wr, int qid) +{ +	memset(wr, 0, sizeof(*wr)); +	wr->op_pkd = htonl(FW_WR_OP(FW_FILTER_WR)); +	wr->len16_pkd = htonl(FW_WR_LEN16(sizeof(*wr) / 16)); +	wr->tid_to_iq = htonl(V_FW_FILTER_WR_TID(ftid) | +			V_FW_FILTER_WR_NOREPLY(qid < 0)); +	wr->del_filter_to_l2tix = htonl(F_FW_FILTER_WR_DEL_FILTER); +	if (qid >= 0) +		wr->rx_chan_rx_rpl_iq = htons(V_FW_FILTER_WR_RX_RPL_IQ(qid)); +} +  #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); \ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h index b760808fd6d..261d17703ad 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_msg.h @@ -193,8 +193,24 @@ struct work_request_hdr {  	__be64 wr_lo;  }; +/* wr_hi fields */ +#define S_WR_OP    24 +#define V_WR_OP(x) ((__u64)(x) << S_WR_OP) +  #define WR_HDR struct work_request_hdr wr +/* option 0 fields */ +#define S_MSS_IDX    60 +#define M_MSS_IDX    0xF +#define V_MSS_IDX(x) ((__u64)(x) << S_MSS_IDX) +#define G_MSS_IDX(x) (((x) >> S_MSS_IDX) & M_MSS_IDX) + +/* option 2 fields */ +#define S_RSS_QUEUE    0 +#define M_RSS_QUEUE    0x3FF +#define V_RSS_QUEUE(x) ((x) << S_RSS_QUEUE) +#define G_RSS_QUEUE(x) (((x) >> S_RSS_QUEUE) & M_RSS_QUEUE) +  struct cpl_pass_open_req {  	WR_HDR;  	union opcode_tid ot; @@ -204,12 +220,14 @@ struct cpl_pass_open_req {  	__be32 peer_ip;  	__be64 opt0;  #define TX_CHAN(x)    ((x) << 2) +#define NO_CONG(x)    ((x) << 4)  #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 TCAM_BYPASS(x) ((u64)(x) << 48)  #define NAGLE(x)      ((u64)(x) << 49)  #define WND_SCALE(x)  ((u64)(x) << 50)  #define KEEP_ALIVE(x) ((u64)(x) << 54) @@ -247,8 +265,10 @@ struct cpl_pass_accept_rpl {  #define RSS_QUEUE_VALID      (1 << 10)  #define RX_COALESCE_VALID(x) ((x) << 11)  #define RX_COALESCE(x)       ((x) << 12) +#define PACE(x)	      ((x) << 16)  #define TX_QUEUE(x)          ((x) << 23)  #define RX_CHANNEL(x)        ((x) << 26) +#define CCTRL_ECN(x)         ((x) << 27)  #define WND_SCALE_EN(x)      ((x) << 28)  #define TSTAMPS_EN(x)        ((x) << 29)  #define SACK_EN(x)           ((x) << 30) @@ -292,6 +312,9 @@ struct cpl_pass_establish {  	union opcode_tid ot;  	__be32 rsvd;  	__be32 tos_stid; +#define PASS_OPEN_TID(x) ((x) << 0) +#define PASS_OPEN_TOS(x) ((x) << 24) +#define GET_PASS_OPEN_TID(x)	(((x) >> 0) & 0xFFFFFF)  #define GET_POPEN_TID(x) ((x) & 0xffffff)  #define GET_POPEN_TOS(x) (((x) >> 24) & 0xff)  	__be16 mac_idx; @@ -332,6 +355,7 @@ struct cpl_set_tcb_field {  	__be16 word_cookie;  #define TCB_WORD(x)   ((x) << 0)  #define TCB_COOKIE(x) ((x) << 5) +#define GET_TCB_COOKIE(x) (((x) >> 5) & 7)  	__be64 mask;  	__be64 val;  }; @@ -536,6 +560,37 @@ struct cpl_rx_pkt {  	__be16 err_vec;  }; +/* rx_pkt.l2info fields */ +#define S_RX_ETHHDR_LEN    0 +#define M_RX_ETHHDR_LEN    0x1F +#define V_RX_ETHHDR_LEN(x) ((x) << S_RX_ETHHDR_LEN) +#define G_RX_ETHHDR_LEN(x) (((x) >> S_RX_ETHHDR_LEN) & M_RX_ETHHDR_LEN) + +#define S_RX_MACIDX    8 +#define M_RX_MACIDX    0x1FF +#define V_RX_MACIDX(x) ((x) << S_RX_MACIDX) +#define G_RX_MACIDX(x) (((x) >> S_RX_MACIDX) & M_RX_MACIDX) + +#define S_RXF_SYN    21 +#define V_RXF_SYN(x) ((x) << S_RXF_SYN) +#define F_RXF_SYN    V_RXF_SYN(1U) + +#define S_RX_CHAN    28 +#define M_RX_CHAN    0xF +#define V_RX_CHAN(x) ((x) << S_RX_CHAN) +#define G_RX_CHAN(x) (((x) >> S_RX_CHAN) & M_RX_CHAN) + +/* rx_pkt.hdr_len fields */ +#define S_RX_TCPHDR_LEN    0 +#define M_RX_TCPHDR_LEN    0x3F +#define V_RX_TCPHDR_LEN(x) ((x) << S_RX_TCPHDR_LEN) +#define G_RX_TCPHDR_LEN(x) (((x) >> S_RX_TCPHDR_LEN) & M_RX_TCPHDR_LEN) + +#define S_RX_IPHDR_LEN    6 +#define M_RX_IPHDR_LEN    0x3FF +#define V_RX_IPHDR_LEN(x) ((x) << S_RX_IPHDR_LEN) +#define G_RX_IPHDR_LEN(x) (((x) >> S_RX_IPHDR_LEN) & M_RX_IPHDR_LEN) +  struct cpl_trace_pkt {  	u8 opcode;  	u8 intf; @@ -634,6 +689,17 @@ struct cpl_fw6_msg {  /* cpl_fw6_msg.type values */  enum {  	FW6_TYPE_CMD_RPL = 0, +	FW6_TYPE_WR_RPL = 1, +	FW6_TYPE_CQE = 2, +	FW6_TYPE_OFLD_CONNECTION_WR_RPL = 3, +}; + +struct cpl_fw6_msg_ofld_connection_wr_rpl { +	__u64   cookie; +	__be32  tid;    /* or atid in case of active failure */ +	__u8    t_state; +	__u8    retval; +	__u8    rsvd[2];  };  enum { diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h index 75393f5cff4..83ec5f7844a 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4_regs.h @@ -1064,4 +1064,41 @@  #define  ADDRESS(x)     ((x) << ADDRESS_SHIFT)  #define XGMAC_PORT_INT_CAUSE 0x10dc + +#define A_TP_TX_MOD_QUEUE_REQ_MAP 0x7e28 + +#define A_TP_TX_MOD_CHANNEL_WEIGHT 0x7e34 + +#define S_TX_MOD_QUEUE_REQ_MAP    0 +#define M_TX_MOD_QUEUE_REQ_MAP    0xffffU +#define V_TX_MOD_QUEUE_REQ_MAP(x) ((x) << S_TX_MOD_QUEUE_REQ_MAP) + +#define A_TP_TX_MOD_QUEUE_WEIGHT0 0x7e30 + +#define S_TX_MODQ_WEIGHT3    24 +#define M_TX_MODQ_WEIGHT3    0xffU +#define V_TX_MODQ_WEIGHT3(x) ((x) << S_TX_MODQ_WEIGHT3) + +#define S_TX_MODQ_WEIGHT2    16 +#define M_TX_MODQ_WEIGHT2    0xffU +#define V_TX_MODQ_WEIGHT2(x) ((x) << S_TX_MODQ_WEIGHT2) + +#define S_TX_MODQ_WEIGHT1    8 +#define M_TX_MODQ_WEIGHT1    0xffU +#define V_TX_MODQ_WEIGHT1(x) ((x) << S_TX_MODQ_WEIGHT1) + +#define S_TX_MODQ_WEIGHT0    0 +#define M_TX_MODQ_WEIGHT0    0xffU +#define V_TX_MODQ_WEIGHT0(x) ((x) << S_TX_MODQ_WEIGHT0) + +#define A_TP_TX_SCHED_HDR 0x23 + +#define A_TP_TX_SCHED_FIFO 0x24 + +#define A_TP_TX_SCHED_PCMD 0x25 + +#define S_PORT    1 +#define V_PORT(x) ((x) << S_PORT) +#define F_PORT    V_PORT(1U) +  #endif /* __T4_REGS_H */ diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h index 0abc864cdd3..a0dcccd846c 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h +++ b/drivers/net/ethernet/chelsio/cxgb4/t4fw_api.h @@ -35,6 +35,45 @@  #ifndef _T4FW_INTERFACE_H_  #define _T4FW_INTERFACE_H_ +enum fw_retval { +	FW_SUCCESS		= 0,	/* completed sucessfully */ +	FW_EPERM		= 1,	/* operation not permitted */ +	FW_ENOENT		= 2,	/* no such file or directory */ +	FW_EIO			= 5,	/* input/output error; hw bad */ +	FW_ENOEXEC		= 8,	/* exec format error; inv microcode */ +	FW_EAGAIN		= 11,	/* try again */ +	FW_ENOMEM		= 12,	/* out of memory */ +	FW_EFAULT		= 14,	/* bad address; fw bad */ +	FW_EBUSY		= 16,	/* resource busy */ +	FW_EEXIST		= 17,	/* file exists */ +	FW_EINVAL		= 22,	/* invalid argument */ +	FW_ENOSPC		= 28,	/* no space left on device */ +	FW_ENOSYS		= 38,	/* functionality not implemented */ +	FW_EPROTO		= 71,	/* protocol error */ +	FW_EADDRINUSE		= 98,	/* address already in use */ +	FW_EADDRNOTAVAIL	= 99,	/* cannot assigned requested address */ +	FW_ENETDOWN		= 100,	/* network is down */ +	FW_ENETUNREACH		= 101,	/* network is unreachable */ +	FW_ENOBUFS		= 105,	/* no buffer space available */ +	FW_ETIMEDOUT		= 110,	/* timeout */ +	FW_EINPROGRESS		= 115,	/* fw internal */ +	FW_SCSI_ABORT_REQUESTED	= 128,	/* */ +	FW_SCSI_ABORT_TIMEDOUT	= 129,	/* */ +	FW_SCSI_ABORTED		= 130,	/* */ +	FW_SCSI_CLOSE_REQUESTED	= 131,	/* */ +	FW_ERR_LINK_DOWN	= 132,	/* */ +	FW_RDEV_NOT_READY	= 133,	/* */ +	FW_ERR_RDEV_LOST	= 134,	/* */ +	FW_ERR_RDEV_LOGO	= 135,	/* */ +	FW_FCOE_NO_XCHG		= 136,	/* */ +	FW_SCSI_RSP_ERR		= 137,	/* */ +	FW_ERR_RDEV_IMPL_LOGO	= 138,	/* */ +	FW_SCSI_UNDER_FLOW_ERR  = 139,	/* */ +	FW_SCSI_OVER_FLOW_ERR   = 140,	/* */ +	FW_SCSI_DDP_ERR		= 141,	/* DDP error*/ +	FW_SCSI_TASK_ERR	= 142,	/* No SCSI tasks available */ +}; +  #define FW_T4VF_SGE_BASE_ADDR      0x0000  #define FW_T4VF_MPS_BASE_ADDR      0x0100  #define FW_T4VF_PL_BASE_ADDR       0x0200 @@ -46,6 +85,7 @@ enum fw_wr_opcodes {  	FW_ULPTX_WR                    = 0x04,  	FW_TP_WR                       = 0x05,  	FW_ETH_TX_PKT_WR               = 0x08, +	FW_OFLD_CONNECTION_WR          = 0x2f,  	FW_FLOWC_WR                    = 0x0a,  	FW_OFLD_TX_DATA_WR             = 0x0b,  	FW_CMD_WR                      = 0x10, @@ -81,6 +121,282 @@ struct fw_wr_hdr {  #define FW_WR_LEN16(x)	((x) << 0)  #define HW_TPL_FR_MT_PR_IV_P_FC         0X32B +#define HW_TPL_FR_MT_PR_OV_P_FC         0X327 + +/* filter wr reply code in cookie in CPL_SET_TCB_RPL */ +enum fw_filter_wr_cookie { +	FW_FILTER_WR_SUCCESS, +	FW_FILTER_WR_FLT_ADDED, +	FW_FILTER_WR_FLT_DELETED, +	FW_FILTER_WR_SMT_TBL_FULL, +	FW_FILTER_WR_EINVAL, +}; + +struct fw_filter_wr { +	__be32 op_pkd; +	__be32 len16_pkd; +	__be64 r3; +	__be32 tid_to_iq; +	__be32 del_filter_to_l2tix; +	__be16 ethtype; +	__be16 ethtypem; +	__u8   frag_to_ovlan_vldm; +	__u8   smac_sel; +	__be16 rx_chan_rx_rpl_iq; +	__be32 maci_to_matchtypem; +	__u8   ptcl; +	__u8   ptclm; +	__u8   ttyp; +	__u8   ttypm; +	__be16 ivlan; +	__be16 ivlanm; +	__be16 ovlan; +	__be16 ovlanm; +	__u8   lip[16]; +	__u8   lipm[16]; +	__u8   fip[16]; +	__u8   fipm[16]; +	__be16 lp; +	__be16 lpm; +	__be16 fp; +	__be16 fpm; +	__be16 r7; +	__u8   sma[6]; +}; + +#define S_FW_FILTER_WR_TID      12 +#define M_FW_FILTER_WR_TID      0xfffff +#define V_FW_FILTER_WR_TID(x)   ((x) << S_FW_FILTER_WR_TID) +#define G_FW_FILTER_WR_TID(x)   \ +	(((x) >> S_FW_FILTER_WR_TID) & M_FW_FILTER_WR_TID) + +#define S_FW_FILTER_WR_RQTYPE           11 +#define M_FW_FILTER_WR_RQTYPE           0x1 +#define V_FW_FILTER_WR_RQTYPE(x)        ((x) << S_FW_FILTER_WR_RQTYPE) +#define G_FW_FILTER_WR_RQTYPE(x)        \ +	(((x) >> S_FW_FILTER_WR_RQTYPE) & M_FW_FILTER_WR_RQTYPE) +#define F_FW_FILTER_WR_RQTYPE   V_FW_FILTER_WR_RQTYPE(1U) + +#define S_FW_FILTER_WR_NOREPLY          10 +#define M_FW_FILTER_WR_NOREPLY          0x1 +#define V_FW_FILTER_WR_NOREPLY(x)       ((x) << S_FW_FILTER_WR_NOREPLY) +#define G_FW_FILTER_WR_NOREPLY(x)       \ +	(((x) >> S_FW_FILTER_WR_NOREPLY) & M_FW_FILTER_WR_NOREPLY) +#define F_FW_FILTER_WR_NOREPLY  V_FW_FILTER_WR_NOREPLY(1U) + +#define S_FW_FILTER_WR_IQ       0 +#define M_FW_FILTER_WR_IQ       0x3ff +#define V_FW_FILTER_WR_IQ(x)    ((x) << S_FW_FILTER_WR_IQ) +#define G_FW_FILTER_WR_IQ(x)    \ +	(((x) >> S_FW_FILTER_WR_IQ) & M_FW_FILTER_WR_IQ) + +#define S_FW_FILTER_WR_DEL_FILTER       31 +#define M_FW_FILTER_WR_DEL_FILTER       0x1 +#define V_FW_FILTER_WR_DEL_FILTER(x)    ((x) << S_FW_FILTER_WR_DEL_FILTER) +#define G_FW_FILTER_WR_DEL_FILTER(x)    \ +	(((x) >> S_FW_FILTER_WR_DEL_FILTER) & M_FW_FILTER_WR_DEL_FILTER) +#define F_FW_FILTER_WR_DEL_FILTER       V_FW_FILTER_WR_DEL_FILTER(1U) + +#define S_FW_FILTER_WR_RPTTID           25 +#define M_FW_FILTER_WR_RPTTID           0x1 +#define V_FW_FILTER_WR_RPTTID(x)        ((x) << S_FW_FILTER_WR_RPTTID) +#define G_FW_FILTER_WR_RPTTID(x)        \ +	(((x) >> S_FW_FILTER_WR_RPTTID) & M_FW_FILTER_WR_RPTTID) +#define F_FW_FILTER_WR_RPTTID   V_FW_FILTER_WR_RPTTID(1U) + +#define S_FW_FILTER_WR_DROP     24 +#define M_FW_FILTER_WR_DROP     0x1 +#define V_FW_FILTER_WR_DROP(x)  ((x) << S_FW_FILTER_WR_DROP) +#define G_FW_FILTER_WR_DROP(x)  \ +	(((x) >> S_FW_FILTER_WR_DROP) & M_FW_FILTER_WR_DROP) +#define F_FW_FILTER_WR_DROP     V_FW_FILTER_WR_DROP(1U) + +#define S_FW_FILTER_WR_DIRSTEER         23 +#define M_FW_FILTER_WR_DIRSTEER         0x1 +#define V_FW_FILTER_WR_DIRSTEER(x)      ((x) << S_FW_FILTER_WR_DIRSTEER) +#define G_FW_FILTER_WR_DIRSTEER(x)      \ +	(((x) >> S_FW_FILTER_WR_DIRSTEER) & M_FW_FILTER_WR_DIRSTEER) +#define F_FW_FILTER_WR_DIRSTEER V_FW_FILTER_WR_DIRSTEER(1U) + +#define S_FW_FILTER_WR_MASKHASH         22 +#define M_FW_FILTER_WR_MASKHASH         0x1 +#define V_FW_FILTER_WR_MASKHASH(x)      ((x) << S_FW_FILTER_WR_MASKHASH) +#define G_FW_FILTER_WR_MASKHASH(x)      \ +	(((x) >> S_FW_FILTER_WR_MASKHASH) & M_FW_FILTER_WR_MASKHASH) +#define F_FW_FILTER_WR_MASKHASH V_FW_FILTER_WR_MASKHASH(1U) + +#define S_FW_FILTER_WR_DIRSTEERHASH     21 +#define M_FW_FILTER_WR_DIRSTEERHASH     0x1 +#define V_FW_FILTER_WR_DIRSTEERHASH(x)  ((x) << S_FW_FILTER_WR_DIRSTEERHASH) +#define G_FW_FILTER_WR_DIRSTEERHASH(x)  \ +	(((x) >> S_FW_FILTER_WR_DIRSTEERHASH) & M_FW_FILTER_WR_DIRSTEERHASH) +#define F_FW_FILTER_WR_DIRSTEERHASH     V_FW_FILTER_WR_DIRSTEERHASH(1U) + +#define S_FW_FILTER_WR_LPBK     20 +#define M_FW_FILTER_WR_LPBK     0x1 +#define V_FW_FILTER_WR_LPBK(x)  ((x) << S_FW_FILTER_WR_LPBK) +#define G_FW_FILTER_WR_LPBK(x)  \ +	(((x) >> S_FW_FILTER_WR_LPBK) & M_FW_FILTER_WR_LPBK) +#define F_FW_FILTER_WR_LPBK     V_FW_FILTER_WR_LPBK(1U) + +#define S_FW_FILTER_WR_DMAC     19 +#define M_FW_FILTER_WR_DMAC     0x1 +#define V_FW_FILTER_WR_DMAC(x)  ((x) << S_FW_FILTER_WR_DMAC) +#define G_FW_FILTER_WR_DMAC(x)  \ +	(((x) >> S_FW_FILTER_WR_DMAC) & M_FW_FILTER_WR_DMAC) +#define F_FW_FILTER_WR_DMAC     V_FW_FILTER_WR_DMAC(1U) + +#define S_FW_FILTER_WR_SMAC     18 +#define M_FW_FILTER_WR_SMAC     0x1 +#define V_FW_FILTER_WR_SMAC(x)  ((x) << S_FW_FILTER_WR_SMAC) +#define G_FW_FILTER_WR_SMAC(x)  \ +	(((x) >> S_FW_FILTER_WR_SMAC) & M_FW_FILTER_WR_SMAC) +#define F_FW_FILTER_WR_SMAC     V_FW_FILTER_WR_SMAC(1U) + +#define S_FW_FILTER_WR_INSVLAN          17 +#define M_FW_FILTER_WR_INSVLAN          0x1 +#define V_FW_FILTER_WR_INSVLAN(x)       ((x) << S_FW_FILTER_WR_INSVLAN) +#define G_FW_FILTER_WR_INSVLAN(x)       \ +	(((x) >> S_FW_FILTER_WR_INSVLAN) & M_FW_FILTER_WR_INSVLAN) +#define F_FW_FILTER_WR_INSVLAN  V_FW_FILTER_WR_INSVLAN(1U) + +#define S_FW_FILTER_WR_RMVLAN           16 +#define M_FW_FILTER_WR_RMVLAN           0x1 +#define V_FW_FILTER_WR_RMVLAN(x)        ((x) << S_FW_FILTER_WR_RMVLAN) +#define G_FW_FILTER_WR_RMVLAN(x)        \ +	(((x) >> S_FW_FILTER_WR_RMVLAN) & M_FW_FILTER_WR_RMVLAN) +#define F_FW_FILTER_WR_RMVLAN   V_FW_FILTER_WR_RMVLAN(1U) + +#define S_FW_FILTER_WR_HITCNTS          15 +#define M_FW_FILTER_WR_HITCNTS          0x1 +#define V_FW_FILTER_WR_HITCNTS(x)       ((x) << S_FW_FILTER_WR_HITCNTS) +#define G_FW_FILTER_WR_HITCNTS(x)       \ +	(((x) >> S_FW_FILTER_WR_HITCNTS) & M_FW_FILTER_WR_HITCNTS) +#define F_FW_FILTER_WR_HITCNTS  V_FW_FILTER_WR_HITCNTS(1U) + +#define S_FW_FILTER_WR_TXCHAN           13 +#define M_FW_FILTER_WR_TXCHAN           0x3 +#define V_FW_FILTER_WR_TXCHAN(x)        ((x) << S_FW_FILTER_WR_TXCHAN) +#define G_FW_FILTER_WR_TXCHAN(x)        \ +	(((x) >> S_FW_FILTER_WR_TXCHAN) & M_FW_FILTER_WR_TXCHAN) + +#define S_FW_FILTER_WR_PRIO     12 +#define M_FW_FILTER_WR_PRIO     0x1 +#define V_FW_FILTER_WR_PRIO(x)  ((x) << S_FW_FILTER_WR_PRIO) +#define G_FW_FILTER_WR_PRIO(x)  \ +	(((x) >> S_FW_FILTER_WR_PRIO) & M_FW_FILTER_WR_PRIO) +#define F_FW_FILTER_WR_PRIO     V_FW_FILTER_WR_PRIO(1U) + +#define S_FW_FILTER_WR_L2TIX    0 +#define M_FW_FILTER_WR_L2TIX    0xfff +#define V_FW_FILTER_WR_L2TIX(x) ((x) << S_FW_FILTER_WR_L2TIX) +#define G_FW_FILTER_WR_L2TIX(x) \ +	(((x) >> S_FW_FILTER_WR_L2TIX) & M_FW_FILTER_WR_L2TIX) + +#define S_FW_FILTER_WR_FRAG     7 +#define M_FW_FILTER_WR_FRAG     0x1 +#define V_FW_FILTER_WR_FRAG(x)  ((x) << S_FW_FILTER_WR_FRAG) +#define G_FW_FILTER_WR_FRAG(x)  \ +	(((x) >> S_FW_FILTER_WR_FRAG) & M_FW_FILTER_WR_FRAG) +#define F_FW_FILTER_WR_FRAG     V_FW_FILTER_WR_FRAG(1U) + +#define S_FW_FILTER_WR_FRAGM    6 +#define M_FW_FILTER_WR_FRAGM    0x1 +#define V_FW_FILTER_WR_FRAGM(x) ((x) << S_FW_FILTER_WR_FRAGM) +#define G_FW_FILTER_WR_FRAGM(x) \ +	(((x) >> S_FW_FILTER_WR_FRAGM) & M_FW_FILTER_WR_FRAGM) +#define F_FW_FILTER_WR_FRAGM    V_FW_FILTER_WR_FRAGM(1U) + +#define S_FW_FILTER_WR_IVLAN_VLD        5 +#define M_FW_FILTER_WR_IVLAN_VLD        0x1 +#define V_FW_FILTER_WR_IVLAN_VLD(x)     ((x) << S_FW_FILTER_WR_IVLAN_VLD) +#define G_FW_FILTER_WR_IVLAN_VLD(x)     \ +	(((x) >> S_FW_FILTER_WR_IVLAN_VLD) & M_FW_FILTER_WR_IVLAN_VLD) +#define F_FW_FILTER_WR_IVLAN_VLD        V_FW_FILTER_WR_IVLAN_VLD(1U) + +#define S_FW_FILTER_WR_OVLAN_VLD        4 +#define M_FW_FILTER_WR_OVLAN_VLD        0x1 +#define V_FW_FILTER_WR_OVLAN_VLD(x)     ((x) << S_FW_FILTER_WR_OVLAN_VLD) +#define G_FW_FILTER_WR_OVLAN_VLD(x)     \ +	(((x) >> S_FW_FILTER_WR_OVLAN_VLD) & M_FW_FILTER_WR_OVLAN_VLD) +#define F_FW_FILTER_WR_OVLAN_VLD        V_FW_FILTER_WR_OVLAN_VLD(1U) + +#define S_FW_FILTER_WR_IVLAN_VLDM       3 +#define M_FW_FILTER_WR_IVLAN_VLDM       0x1 +#define V_FW_FILTER_WR_IVLAN_VLDM(x)    ((x) << S_FW_FILTER_WR_IVLAN_VLDM) +#define G_FW_FILTER_WR_IVLAN_VLDM(x)    \ +	(((x) >> S_FW_FILTER_WR_IVLAN_VLDM) & M_FW_FILTER_WR_IVLAN_VLDM) +#define F_FW_FILTER_WR_IVLAN_VLDM       V_FW_FILTER_WR_IVLAN_VLDM(1U) + +#define S_FW_FILTER_WR_OVLAN_VLDM       2 +#define M_FW_FILTER_WR_OVLAN_VLDM       0x1 +#define V_FW_FILTER_WR_OVLAN_VLDM(x)    ((x) << S_FW_FILTER_WR_OVLAN_VLDM) +#define G_FW_FILTER_WR_OVLAN_VLDM(x)    \ +	(((x) >> S_FW_FILTER_WR_OVLAN_VLDM) & M_FW_FILTER_WR_OVLAN_VLDM) +#define F_FW_FILTER_WR_OVLAN_VLDM       V_FW_FILTER_WR_OVLAN_VLDM(1U) + +#define S_FW_FILTER_WR_RX_CHAN          15 +#define M_FW_FILTER_WR_RX_CHAN          0x1 +#define V_FW_FILTER_WR_RX_CHAN(x)       ((x) << S_FW_FILTER_WR_RX_CHAN) +#define G_FW_FILTER_WR_RX_CHAN(x)       \ +	(((x) >> S_FW_FILTER_WR_RX_CHAN) & M_FW_FILTER_WR_RX_CHAN) +#define F_FW_FILTER_WR_RX_CHAN  V_FW_FILTER_WR_RX_CHAN(1U) + +#define S_FW_FILTER_WR_RX_RPL_IQ        0 +#define M_FW_FILTER_WR_RX_RPL_IQ        0x3ff +#define V_FW_FILTER_WR_RX_RPL_IQ(x)     ((x) << S_FW_FILTER_WR_RX_RPL_IQ) +#define G_FW_FILTER_WR_RX_RPL_IQ(x)     \ +	(((x) >> S_FW_FILTER_WR_RX_RPL_IQ) & M_FW_FILTER_WR_RX_RPL_IQ) + +#define S_FW_FILTER_WR_MACI     23 +#define M_FW_FILTER_WR_MACI     0x1ff +#define V_FW_FILTER_WR_MACI(x)  ((x) << S_FW_FILTER_WR_MACI) +#define G_FW_FILTER_WR_MACI(x)  \ +	(((x) >> S_FW_FILTER_WR_MACI) & M_FW_FILTER_WR_MACI) + +#define S_FW_FILTER_WR_MACIM    14 +#define M_FW_FILTER_WR_MACIM    0x1ff +#define V_FW_FILTER_WR_MACIM(x) ((x) << S_FW_FILTER_WR_MACIM) +#define G_FW_FILTER_WR_MACIM(x) \ +	(((x) >> S_FW_FILTER_WR_MACIM) & M_FW_FILTER_WR_MACIM) + +#define S_FW_FILTER_WR_FCOE     13 +#define M_FW_FILTER_WR_FCOE     0x1 +#define V_FW_FILTER_WR_FCOE(x)  ((x) << S_FW_FILTER_WR_FCOE) +#define G_FW_FILTER_WR_FCOE(x)  \ +	(((x) >> S_FW_FILTER_WR_FCOE) & M_FW_FILTER_WR_FCOE) +#define F_FW_FILTER_WR_FCOE     V_FW_FILTER_WR_FCOE(1U) + +#define S_FW_FILTER_WR_FCOEM    12 +#define M_FW_FILTER_WR_FCOEM    0x1 +#define V_FW_FILTER_WR_FCOEM(x) ((x) << S_FW_FILTER_WR_FCOEM) +#define G_FW_FILTER_WR_FCOEM(x) \ +	(((x) >> S_FW_FILTER_WR_FCOEM) & M_FW_FILTER_WR_FCOEM) +#define F_FW_FILTER_WR_FCOEM    V_FW_FILTER_WR_FCOEM(1U) + +#define S_FW_FILTER_WR_PORT     9 +#define M_FW_FILTER_WR_PORT     0x7 +#define V_FW_FILTER_WR_PORT(x)  ((x) << S_FW_FILTER_WR_PORT) +#define G_FW_FILTER_WR_PORT(x)  \ +	(((x) >> S_FW_FILTER_WR_PORT) & M_FW_FILTER_WR_PORT) + +#define S_FW_FILTER_WR_PORTM    6 +#define M_FW_FILTER_WR_PORTM    0x7 +#define V_FW_FILTER_WR_PORTM(x) ((x) << S_FW_FILTER_WR_PORTM) +#define G_FW_FILTER_WR_PORTM(x) \ +	(((x) >> S_FW_FILTER_WR_PORTM) & M_FW_FILTER_WR_PORTM) + +#define S_FW_FILTER_WR_MATCHTYPE        3 +#define M_FW_FILTER_WR_MATCHTYPE        0x7 +#define V_FW_FILTER_WR_MATCHTYPE(x)     ((x) << S_FW_FILTER_WR_MATCHTYPE) +#define G_FW_FILTER_WR_MATCHTYPE(x)     \ +	(((x) >> S_FW_FILTER_WR_MATCHTYPE) & M_FW_FILTER_WR_MATCHTYPE) + +#define S_FW_FILTER_WR_MATCHTYPEM       0 +#define M_FW_FILTER_WR_MATCHTYPEM       0x7 +#define V_FW_FILTER_WR_MATCHTYPEM(x)    ((x) << S_FW_FILTER_WR_MATCHTYPEM) +#define G_FW_FILTER_WR_MATCHTYPEM(x)    \ +	(((x) >> S_FW_FILTER_WR_MATCHTYPEM) & M_FW_FILTER_WR_MATCHTYPEM)  struct fw_ulptx_wr {  	__be32 op_to_compl; @@ -100,6 +416,108 @@ struct fw_eth_tx_pkt_wr {  	__be64 r3;  }; +struct fw_ofld_connection_wr { +	__be32 op_compl; +	__be32 len16_pkd; +	__u64  cookie; +	__be64 r2; +	__be64 r3; +	struct fw_ofld_connection_le { +		__be32 version_cpl; +		__be32 filter; +		__be32 r1; +		__be16 lport; +		__be16 pport; +		union fw_ofld_connection_leip { +			struct fw_ofld_connection_le_ipv4 { +				__be32 pip; +				__be32 lip; +				__be64 r0; +				__be64 r1; +				__be64 r2; +			} ipv4; +			struct fw_ofld_connection_le_ipv6 { +				__be64 pip_hi; +				__be64 pip_lo; +				__be64 lip_hi; +				__be64 lip_lo; +			} ipv6; +		} u; +	} le; +	struct fw_ofld_connection_tcb { +		__be32 t_state_to_astid; +		__be16 cplrxdataack_cplpassacceptrpl; +		__be16 rcv_adv; +		__be32 rcv_nxt; +		__be32 tx_max; +		__be64 opt0; +		__be32 opt2; +		__be32 r1; +		__be64 r2; +		__be64 r3; +	} tcb; +}; + +#define S_FW_OFLD_CONNECTION_WR_VERSION                31 +#define M_FW_OFLD_CONNECTION_WR_VERSION                0x1 +#define V_FW_OFLD_CONNECTION_WR_VERSION(x)     \ +	((x) << S_FW_OFLD_CONNECTION_WR_VERSION) +#define G_FW_OFLD_CONNECTION_WR_VERSION(x)     \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_VERSION) & \ +	M_FW_OFLD_CONNECTION_WR_VERSION) +#define F_FW_OFLD_CONNECTION_WR_VERSION        \ +	V_FW_OFLD_CONNECTION_WR_VERSION(1U) + +#define S_FW_OFLD_CONNECTION_WR_CPL    30 +#define M_FW_OFLD_CONNECTION_WR_CPL    0x1 +#define V_FW_OFLD_CONNECTION_WR_CPL(x) ((x) << S_FW_OFLD_CONNECTION_WR_CPL) +#define G_FW_OFLD_CONNECTION_WR_CPL(x) \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_CPL) & M_FW_OFLD_CONNECTION_WR_CPL) +#define F_FW_OFLD_CONNECTION_WR_CPL    V_FW_OFLD_CONNECTION_WR_CPL(1U) + +#define S_FW_OFLD_CONNECTION_WR_T_STATE                28 +#define M_FW_OFLD_CONNECTION_WR_T_STATE                0xf +#define V_FW_OFLD_CONNECTION_WR_T_STATE(x)     \ +	((x) << S_FW_OFLD_CONNECTION_WR_T_STATE) +#define G_FW_OFLD_CONNECTION_WR_T_STATE(x)     \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_T_STATE) & \ +	M_FW_OFLD_CONNECTION_WR_T_STATE) + +#define S_FW_OFLD_CONNECTION_WR_RCV_SCALE      24 +#define M_FW_OFLD_CONNECTION_WR_RCV_SCALE      0xf +#define V_FW_OFLD_CONNECTION_WR_RCV_SCALE(x)   \ +	((x) << S_FW_OFLD_CONNECTION_WR_RCV_SCALE) +#define G_FW_OFLD_CONNECTION_WR_RCV_SCALE(x)   \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_RCV_SCALE) & \ +	M_FW_OFLD_CONNECTION_WR_RCV_SCALE) + +#define S_FW_OFLD_CONNECTION_WR_ASTID          0 +#define M_FW_OFLD_CONNECTION_WR_ASTID          0xffffff +#define V_FW_OFLD_CONNECTION_WR_ASTID(x)       \ +	((x) << S_FW_OFLD_CONNECTION_WR_ASTID) +#define G_FW_OFLD_CONNECTION_WR_ASTID(x)       \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_ASTID) & M_FW_OFLD_CONNECTION_WR_ASTID) + +#define S_FW_OFLD_CONNECTION_WR_CPLRXDATAACK   15 +#define M_FW_OFLD_CONNECTION_WR_CPLRXDATAACK   0x1 +#define V_FW_OFLD_CONNECTION_WR_CPLRXDATAACK(x)        \ +	((x) << S_FW_OFLD_CONNECTION_WR_CPLRXDATAACK) +#define G_FW_OFLD_CONNECTION_WR_CPLRXDATAACK(x)        \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_CPLRXDATAACK) & \ +	M_FW_OFLD_CONNECTION_WR_CPLRXDATAACK) +#define F_FW_OFLD_CONNECTION_WR_CPLRXDATAACK   \ +	V_FW_OFLD_CONNECTION_WR_CPLRXDATAACK(1U) + +#define S_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL       14 +#define M_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL       0x1 +#define V_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL(x)    \ +	((x) << S_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL) +#define G_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL(x)    \ +	(((x) >> S_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL) & \ +	M_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL) +#define F_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL       \ +	V_FW_OFLD_CONNECTION_WR_CPLPASSACCEPTRPL(1U) +  enum fw_flowc_mnem {  	FW_FLOWC_MNEM_PFNVFN,		/* PFN [15:8] VFN [7:0] */  	FW_FLOWC_MNEM_CH, diff --git a/drivers/net/ethernet/emulex/benet/be.h b/drivers/net/ethernet/emulex/benet/be.h index abf26c7c1d1..4eba17b83ba 100644 --- a/drivers/net/ethernet/emulex/benet/be.h +++ b/drivers/net/ethernet/emulex/benet/be.h @@ -190,6 +190,7 @@ struct be_eq_obj {  	u8 idx;			/* array index */  	u16 tx_budget; +	u16 spurious_intr;  	struct napi_struct napi;  	struct be_adapter *adapter;  } ____cacheline_aligned_in_smp; @@ -616,7 +617,7 @@ static inline bool be_error(struct be_adapter *adapter)  	return adapter->eeh_error || adapter->hw_error || adapter->fw_timeout;  } -static inline bool be_crit_error(struct be_adapter *adapter) +static inline bool be_hw_error(struct be_adapter *adapter)  {  	return adapter->eeh_error || adapter->hw_error;  } diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.c b/drivers/net/ethernet/emulex/benet/be_cmds.c index f2875aa4766..8a250c38fb8 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.c +++ b/drivers/net/ethernet/emulex/benet/be_cmds.c @@ -298,7 +298,12 @@ void be_async_mcc_enable(struct be_adapter *adapter)  void be_async_mcc_disable(struct be_adapter *adapter)  { +	spin_lock_bh(&adapter->mcc_cq_lock); +  	adapter->mcc_obj.rearm_cq = false; +	be_cq_notify(adapter, adapter->mcc_obj.cq.id, false, 0); + +	spin_unlock_bh(&adapter->mcc_cq_lock);  }  int be_process_mcc(struct be_adapter *adapter) diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index f95612b907a..5c995700e53 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -1689,15 +1689,41 @@ static void be_rx_cq_clean(struct be_rx_obj *rxo)  	struct be_queue_info *rxq = &rxo->q;  	struct be_queue_info *rx_cq = &rxo->cq;  	struct be_rx_compl_info *rxcp; +	struct be_adapter *adapter = rxo->adapter; +	int flush_wait = 0;  	u16 tail; -	/* First cleanup pending rx completions */ -	while ((rxcp = be_rx_compl_get(rxo)) != NULL) { -		be_rx_compl_discard(rxo, rxcp); -		be_cq_notify(rxo->adapter, rx_cq->id, false, 1); +	/* Consume pending rx completions. +	 * Wait for the flush completion (identified by zero num_rcvd) +	 * to arrive. Notify CQ even when there are no more CQ entries +	 * for HW to flush partially coalesced CQ entries. +	 * In Lancer, there is no need to wait for flush compl. +	 */ +	for (;;) { +		rxcp = be_rx_compl_get(rxo); +		if (rxcp == NULL) { +			if (lancer_chip(adapter)) +				break; + +			if (flush_wait++ > 10 || be_hw_error(adapter)) { +				dev_warn(&adapter->pdev->dev, +					 "did not receive flush compl\n"); +				break; +			} +			be_cq_notify(adapter, rx_cq->id, true, 0); +			mdelay(1); +		} else { +			be_rx_compl_discard(rxo, rxcp); +			be_cq_notify(adapter, rx_cq->id, true, 1); +			if (rxcp->num_rcvd == 0) +				break; +		}  	} -	/* Then free posted rx buffer that were not used */ +	/* After cleanup, leave the CQ in unarmed state */ +	be_cq_notify(adapter, rx_cq->id, false, 0); + +	/* Then free posted rx buffers that were not used */  	tail = (rxq->head + rxq->len - atomic_read(&rxq->used)) % rxq->len;  	for (; atomic_read(&rxq->used) > 0; index_inc(&tail, rxq->len)) {  		page_info = get_rx_page_info(rxo, tail); @@ -2000,19 +2026,30 @@ static irqreturn_t be_intx(int irq, void *dev)  	struct be_adapter *adapter = eqo->adapter;  	int num_evts = 0; -	/* On Lancer, clear-intr bit of the EQ DB does not work. -	 * INTx is de-asserted only on notifying num evts. +	/* IRQ is not expected when NAPI is scheduled as the EQ +	 * will not be armed. +	 * But, this can happen on Lancer INTx where it takes +	 * a while to de-assert INTx or in BE2 where occasionaly +	 * an interrupt may be raised even when EQ is unarmed. +	 * If NAPI is already scheduled, then counting & notifying +	 * events will orphan them.  	 */ -	if (lancer_chip(adapter)) +	if (napi_schedule_prep(&eqo->napi)) {  		num_evts = events_get(eqo); +		__napi_schedule(&eqo->napi); +		if (num_evts) +			eqo->spurious_intr = 0; +	} +	be_eq_notify(adapter, eqo->q.id, false, true, num_evts); -	/* The EQ-notify may not de-assert INTx rightaway, causing -	 * the ISR to be invoked again. So, return HANDLED even when -	 * num_evts is zero. +	/* Return IRQ_HANDLED only for the the first spurious intr +	 * after a valid intr to stop the kernel from branding +	 * this irq as a bad one!  	 */ -	be_eq_notify(adapter, eqo->q.id, false, true, num_evts); -	napi_schedule(&eqo->napi); -	return IRQ_HANDLED; +	if (num_evts || eqo->spurious_intr++ == 0) +		return IRQ_HANDLED; +	else +		return IRQ_NONE;  }  static irqreturn_t be_msix(int irq, void *dev) @@ -2157,7 +2194,7 @@ void be_detect_error(struct be_adapter *adapter)  	u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0;  	u32 i; -	if (be_crit_error(adapter)) +	if (be_hw_error(adapter))  		return;  	if (lancer_chip(adapter)) { @@ -2398,13 +2435,22 @@ static int be_close(struct net_device *netdev)  	be_roce_dev_close(adapter); -	be_async_mcc_disable(adapter); -  	if (!lancer_chip(adapter))  		be_intr_set(adapter, false); -	for_all_evt_queues(adapter, eqo, i) { +	for_all_evt_queues(adapter, eqo, i)  		napi_disable(&eqo->napi); + +	be_async_mcc_disable(adapter); + +	/* Wait for all pending tx completions to arrive so that +	 * all tx skbs are freed. +	 */ +	be_tx_compl_clean(adapter); + +	be_rx_qs_destroy(adapter); + +	for_all_evt_queues(adapter, eqo, i) {  		if (msix_enabled(adapter))  			synchronize_irq(be_msix_vec_get(adapter, eqo));  		else @@ -2414,12 +2460,6 @@ static int be_close(struct net_device *netdev)  	be_irq_unregister(adapter); -	/* Wait for all pending tx completions to arrive so that -	 * all tx skbs are freed. -	 */ -	be_tx_compl_clean(adapter); - -	be_rx_qs_destroy(adapter);  	return 0;  } diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 5ba6e1cbd34..ec490d741fc 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -94,9 +94,8 @@ config GIANFAR  config FEC_PTP  	bool "PTP Hardware Clock (PHC)" -	depends on FEC && ARCH_MXC +	depends on FEC && ARCH_MXC && !SOC_IMX25 && !SOC_IMX27 && !SOC_IMX35 && !SOC_IMX5  	select PTP_1588_CLOCK -	default y if SOC_IMX6Q  	--help---  	  Say Y here if you want to use PTP Hardware Clock (PHC) in the  	  driver.  Only the basic clock operations have been implemented. diff --git a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h index 8364815c32f..99b6c2a38db 100644 --- a/drivers/net/ethernet/ibm/ehea/ehea_phyp.h +++ b/drivers/net/ethernet/ibm/ehea/ehea_phyp.h @@ -39,26 +39,6 @@   * hcp_*  - structures, variables and functions releated to Hypervisor Calls   */ -static inline u32 get_longbusy_msecs(int long_busy_ret_code) -{ -	switch (long_busy_ret_code) { -	case H_LONG_BUSY_ORDER_1_MSEC: -		return 1; -	case H_LONG_BUSY_ORDER_10_MSEC: -		return 10; -	case H_LONG_BUSY_ORDER_100_MSEC: -		return 100; -	case H_LONG_BUSY_ORDER_1_SEC: -		return 1000; -	case H_LONG_BUSY_ORDER_10_SEC: -		return 10000; -	case H_LONG_BUSY_ORDER_100_SEC: -		return 100000; -	default: -		return 1; -	} -} -  /* Number of pages which can be registered at once by H_REGISTER_HEA_RPAGES */  #define EHEA_MAX_RPAGE 512 diff --git a/drivers/net/ethernet/marvell/mvmdio.c b/drivers/net/ethernet/marvell/mvmdio.c index 6d6002bab06..74f1c157a48 100644 --- a/drivers/net/ethernet/marvell/mvmdio.c +++ b/drivers/net/ethernet/marvell/mvmdio.c @@ -141,7 +141,7 @@ static int orion_mdio_reset(struct mii_bus *bus)  	return 0;  } -static int __devinit orion_mdio_probe(struct platform_device *pdev) +static int orion_mdio_probe(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node;  	struct mii_bus *bus; @@ -197,7 +197,7 @@ static int __devinit orion_mdio_probe(struct platform_device *pdev)  	return 0;  } -static int __devexit orion_mdio_remove(struct platform_device *pdev) +static int orion_mdio_remove(struct platform_device *pdev)  {  	struct mii_bus *bus = platform_get_drvdata(pdev);  	mdiobus_unregister(bus); @@ -214,7 +214,7 @@ MODULE_DEVICE_TABLE(of, orion_mdio_match);  static struct platform_driver orion_mdio_driver = {  	.probe = orion_mdio_probe, -	.remove = __devexit_p(orion_mdio_remove), +	.remove = orion_mdio_remove,  	.driver = {  		.name = "orion-mdio",  		.of_match_table = orion_mdio_match, diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index 3f8086b9f5e..b6025c305e1 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -635,7 +635,7 @@ static void mvneta_rxq_bm_disable(struct mvneta_port *pp,  /* Sets the RGMII Enable bit (RGMIIEn) in port MAC control register */ -static void __devinit mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable) +static void mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable)  {  	u32  val; @@ -650,7 +650,7 @@ static void __devinit mvneta_gmac_rgmii_set(struct mvneta_port *pp, int enable)  }  /* Config SGMII port */ -static void __devinit mvneta_port_sgmii_config(struct mvneta_port *pp) +static void mvneta_port_sgmii_config(struct mvneta_port *pp)  {  	u32 val; @@ -2564,7 +2564,7 @@ const struct ethtool_ops mvneta_eth_tool_ops = {  };  /* Initialize hw */ -static int __devinit mvneta_init(struct mvneta_port *pp, int phy_addr) +static int mvneta_init(struct mvneta_port *pp, int phy_addr)  {  	int queue; @@ -2613,9 +2613,8 @@ static void mvneta_deinit(struct mvneta_port *pp)  }  /* platform glue : initialize decoding windows */ -static void __devinit -mvneta_conf_mbus_windows(struct mvneta_port *pp, -			 const struct mbus_dram_target_info *dram) +static void mvneta_conf_mbus_windows(struct mvneta_port *pp, +				     const struct mbus_dram_target_info *dram)  {  	u32 win_enable;  	u32 win_protect; @@ -2648,7 +2647,7 @@ mvneta_conf_mbus_windows(struct mvneta_port *pp,  }  /* Power up the port */ -static void __devinit mvneta_port_power_up(struct mvneta_port *pp, int phy_mode) +static void mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)  {  	u32 val; @@ -2671,7 +2670,7 @@ static void __devinit mvneta_port_power_up(struct mvneta_port *pp, int phy_mode)  }  /* Device initialization routine */ -static int __devinit mvneta_probe(struct platform_device *pdev) +static int mvneta_probe(struct platform_device *pdev)  {  	const struct mbus_dram_target_info *dram_target_info;  	struct device_node *dn = pdev->dev.of_node; @@ -2803,7 +2802,7 @@ err_free_netdev:  }  /* Device removal routine */ -static int __devexit mvneta_remove(struct platform_device *pdev) +static int mvneta_remove(struct platform_device *pdev)  {  	struct net_device  *dev = platform_get_drvdata(pdev);  	struct mvneta_port *pp = netdev_priv(dev); @@ -2828,7 +2827,7 @@ MODULE_DEVICE_TABLE(of, mvneta_match);  static struct platform_driver mvneta_driver = {  	.probe = mvneta_probe, -	.remove = __devexit_p(mvneta_remove), +	.remove = mvneta_remove,  	.driver = {  		.name = MVNETA_DRIVER_NAME,  		.of_match_table = mvneta_match, diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.c b/drivers/net/ethernet/mellanox/mlx4/fw.c index 9a9de51ecc9..8b3d0512a46 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.c +++ b/drivers/net/ethernet/mellanox/mlx4/fw.c @@ -1338,6 +1338,7 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev,  {  	struct mlx4_cmd_mailbox *mailbox;  	__be32 *outbox; +	u32 dword_field;  	int err;  	u8 byte_field; @@ -1372,10 +1373,18 @@ int mlx4_QUERY_HCA(struct mlx4_dev *dev,  	MLX4_GET(param->rdmarc_base,   outbox, INIT_HCA_RDMARC_BASE_OFFSET);  	MLX4_GET(param->log_rd_per_qp, outbox, INIT_HCA_LOG_RD_OFFSET); +	MLX4_GET(dword_field, outbox, INIT_HCA_FLAGS_OFFSET); +	if (dword_field & (1 << INIT_HCA_DEVICE_MANAGED_FLOW_STEERING_EN)) { +		param->steering_mode = MLX4_STEERING_MODE_DEVICE_MANAGED; +	} else { +		MLX4_GET(byte_field, outbox, INIT_HCA_UC_STEERING_OFFSET); +		if (byte_field & 0x8) +			param->steering_mode = MLX4_STEERING_MODE_B0; +		else +			param->steering_mode = MLX4_STEERING_MODE_A0; +	}  	/* steering attributes */ -	if (dev->caps.steering_mode == -	    MLX4_STEERING_MODE_DEVICE_MANAGED) { - +	if (param->steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) {  		MLX4_GET(param->mc_base, outbox, INIT_HCA_FS_BASE_OFFSET);  		MLX4_GET(param->log_mc_entry_sz, outbox,  			 INIT_HCA_FS_LOG_ENTRY_SZ_OFFSET); diff --git a/drivers/net/ethernet/mellanox/mlx4/fw.h b/drivers/net/ethernet/mellanox/mlx4/fw.h index 2c2e7ade2a3..dbf2f69cc59 100644 --- a/drivers/net/ethernet/mellanox/mlx4/fw.h +++ b/drivers/net/ethernet/mellanox/mlx4/fw.h @@ -172,6 +172,7 @@ struct mlx4_init_hca_param {  	u8  log_uar_sz;  	u8  uar_page_sz; /* log pg sz in 4k chunks */  	u8  fs_hash_enable_bits; +	u8  steering_mode; /* for QUERY_HCA */  	u64 dev_cap_enabled;  }; diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c index b2acbe7706a..e1bafffbc3b 100644 --- a/drivers/net/ethernet/mellanox/mlx4/main.c +++ b/drivers/net/ethernet/mellanox/mlx4/main.c @@ -85,15 +85,15 @@ static int probe_vf;  module_param(probe_vf, int, 0644);  MODULE_PARM_DESC(probe_vf, "number of vfs to probe by pf driver (num_vfs > 0)"); -int mlx4_log_num_mgm_entry_size = 10; +int mlx4_log_num_mgm_entry_size = MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE;  module_param_named(log_num_mgm_entry_size,  			mlx4_log_num_mgm_entry_size, int, 0444);  MODULE_PARM_DESC(log_num_mgm_entry_size, "log mgm size, that defines the num"  					 " of qp per mcg, for example:" -					 " 10 gives 248.range: 9<=" +					 " 10 gives 248.range: 7 <="  					 " log_num_mgm_entry_size <= 12." -					 " Not in use with device managed" -					 " flow steering"); +					 " To activate device managed" +					 " flow steering when available, set to -1");  static bool enable_64b_cqe_eqe;  module_param(enable_64b_cqe_eqe, bool, 0444); @@ -281,28 +281,6 @@ static int mlx4_dev_cap(struct mlx4_dev *dev, struct mlx4_dev_cap *dev_cap)  	dev->caps.max_gso_sz	     = dev_cap->max_gso_sz;  	dev->caps.max_rss_tbl_sz     = dev_cap->max_rss_tbl_sz; -	if (dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN) { -		dev->caps.steering_mode = MLX4_STEERING_MODE_DEVICE_MANAGED; -		dev->caps.num_qp_per_mgm = dev_cap->fs_max_num_qp_per_entry; -		dev->caps.fs_log_max_ucast_qp_range_size = -			dev_cap->fs_log_max_ucast_qp_range_size; -	} else { -		if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && -		    dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) { -			dev->caps.steering_mode = MLX4_STEERING_MODE_B0; -		} else { -			dev->caps.steering_mode = MLX4_STEERING_MODE_A0; - -			if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || -			    dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) -				mlx4_warn(dev, "Must have UC_STEER and MC_STEER flags " -						"set to use B0 steering. Falling back to A0 steering mode.\n"); -		} -		dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); -	} -	mlx4_dbg(dev, "Steering mode is: %s\n", -		 mlx4_steering_mode_str(dev->caps.steering_mode)); -  	/* Sense port always allowed on supported devices for ConnectX-1 and -2 */  	if (mlx4_priv(dev)->pci_dev_data & MLX4_PCI_DEV_FORCE_SENSE_PORT)  		dev->caps.flags |= MLX4_DEV_CAP_FLAG_SENSE_SUPPORT; @@ -493,6 +471,23 @@ int mlx4_is_slave_active(struct mlx4_dev *dev, int slave)  }  EXPORT_SYMBOL(mlx4_is_slave_active); +static void slave_adjust_steering_mode(struct mlx4_dev *dev, +				       struct mlx4_dev_cap *dev_cap, +				       struct mlx4_init_hca_param *hca_param) +{ +	dev->caps.steering_mode = hca_param->steering_mode; +	if (dev->caps.steering_mode == MLX4_STEERING_MODE_DEVICE_MANAGED) { +		dev->caps.num_qp_per_mgm = dev_cap->fs_max_num_qp_per_entry; +		dev->caps.fs_log_max_ucast_qp_range_size = +			dev_cap->fs_log_max_ucast_qp_range_size; +	} else +		dev->caps.num_qp_per_mgm = +			4 * ((1 << hca_param->log_mc_entry_sz)/16 - 2); + +	mlx4_dbg(dev, "Steering mode is: %s\n", +		 mlx4_steering_mode_str(dev->caps.steering_mode)); +} +  static int mlx4_slave_cap(struct mlx4_dev *dev)  {  	int			   err; @@ -635,6 +630,8 @@ static int mlx4_slave_cap(struct mlx4_dev *dev)  		dev->caps.cqe_size   = 32;  	} +	slave_adjust_steering_mode(dev, &dev_cap, &hca_param); +  	return 0;  err_mem: @@ -1321,6 +1318,59 @@ static void mlx4_parav_master_pf_caps(struct mlx4_dev *dev)  	}  } +static int choose_log_fs_mgm_entry_size(int qp_per_entry) +{ +	int i = MLX4_MIN_MGM_LOG_ENTRY_SIZE; + +	for (i = MLX4_MIN_MGM_LOG_ENTRY_SIZE; i <= MLX4_MAX_MGM_LOG_ENTRY_SIZE; +	      i++) { +		if (qp_per_entry <= 4 * ((1 << i) / 16 - 2)) +			break; +	} + +	return (i <= MLX4_MAX_MGM_LOG_ENTRY_SIZE) ? i : -1; +} + +static void choose_steering_mode(struct mlx4_dev *dev, +				 struct mlx4_dev_cap *dev_cap) +{ +	if (mlx4_log_num_mgm_entry_size == -1 && +	    dev_cap->flags2 & MLX4_DEV_CAP_FLAG2_FS_EN && +	    (!mlx4_is_mfunc(dev) || +	     (dev_cap->fs_max_num_qp_per_entry >= (num_vfs + 1))) && +	    choose_log_fs_mgm_entry_size(dev_cap->fs_max_num_qp_per_entry) >= +		MLX4_MIN_MGM_LOG_ENTRY_SIZE) { +		dev->oper_log_mgm_entry_size = +			choose_log_fs_mgm_entry_size(dev_cap->fs_max_num_qp_per_entry); +		dev->caps.steering_mode = MLX4_STEERING_MODE_DEVICE_MANAGED; +		dev->caps.num_qp_per_mgm = dev_cap->fs_max_num_qp_per_entry; +		dev->caps.fs_log_max_ucast_qp_range_size = +			dev_cap->fs_log_max_ucast_qp_range_size; +	} else { +		if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER && +		    dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) +			dev->caps.steering_mode = MLX4_STEERING_MODE_B0; +		else { +			dev->caps.steering_mode = MLX4_STEERING_MODE_A0; + +			if (dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_UC_STEER || +			    dev->caps.flags & MLX4_DEV_CAP_FLAG_VEP_MC_STEER) +				mlx4_warn(dev, "Must have both UC_STEER and MC_STEER flags " +					  "set to use B0 steering. Falling back to A0 steering mode.\n"); +		} +		dev->oper_log_mgm_entry_size = +			mlx4_log_num_mgm_entry_size > 0 ? +			mlx4_log_num_mgm_entry_size : +			MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE; +		dev->caps.num_qp_per_mgm = mlx4_get_qp_per_mgm(dev); +	} +	mlx4_dbg(dev, "Steering mode is: %s, oper_log_mgm_entry_size = %d, " +		 "modparam log_num_mgm_entry_size = %d\n", +		 mlx4_steering_mode_str(dev->caps.steering_mode), +		 dev->oper_log_mgm_entry_size, +		 mlx4_log_num_mgm_entry_size); +} +  static int mlx4_init_hca(struct mlx4_dev *dev)  {  	struct mlx4_priv	  *priv = mlx4_priv(dev); @@ -1360,6 +1410,8 @@ static int mlx4_init_hca(struct mlx4_dev *dev)  			goto err_stop_fw;  		} +		choose_steering_mode(dev, &dev_cap); +  		if (mlx4_is_master(dev))  			mlx4_parav_master_pf_caps(dev); @@ -2452,6 +2504,17 @@ static int __init mlx4_verify_params(void)  		port_type_array[0] = true;  	} +	if (mlx4_log_num_mgm_entry_size != -1 && +	    (mlx4_log_num_mgm_entry_size < MLX4_MIN_MGM_LOG_ENTRY_SIZE || +	     mlx4_log_num_mgm_entry_size > MLX4_MAX_MGM_LOG_ENTRY_SIZE)) { +		pr_warning("mlx4_core: mlx4_log_num_mgm_entry_size (%d) not " +			   "in legal range (-1 or %d..%d)\n", +			   mlx4_log_num_mgm_entry_size, +			   MLX4_MIN_MGM_LOG_ENTRY_SIZE, +			   MLX4_MAX_MGM_LOG_ENTRY_SIZE); +		return -1; +	} +  	return 0;  } diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c index e151c21baf2..1ee4db3c640 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mcg.c +++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c @@ -54,12 +54,7 @@ struct mlx4_mgm {  int mlx4_get_mgm_entry_size(struct mlx4_dev *dev)  { -	if (dev->caps.steering_mode == -	    MLX4_STEERING_MODE_DEVICE_MANAGED) -		return 1 << MLX4_FS_MGM_LOG_ENTRY_SIZE; -	else -		return min((1 << mlx4_log_num_mgm_entry_size), -			   MLX4_MAX_MGM_ENTRY_SIZE); +	return 1 << dev->oper_log_mgm_entry_size;  }  int mlx4_get_qp_per_mgm(struct mlx4_dev *dev) diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h index 1cf42036d7b..116c5c29d2d 100644 --- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h +++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h @@ -94,8 +94,10 @@ enum {  };  enum { -	MLX4_MAX_MGM_ENTRY_SIZE = 0x1000, -	MLX4_MAX_QP_PER_MGM	= 4 * (MLX4_MAX_MGM_ENTRY_SIZE / 16 - 2), +	MLX4_DEFAULT_MGM_LOG_ENTRY_SIZE = 10, +	MLX4_MIN_MGM_LOG_ENTRY_SIZE = 7, +	MLX4_MAX_MGM_LOG_ENTRY_SIZE = 12, +	MLX4_MAX_QP_PER_MGM = 4 * ((1 << MLX4_MAX_MGM_LOG_ENTRY_SIZE) / 16 - 2),  	MLX4_MTT_ENTRY_PER_SEG	= 8,  }; diff --git a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c index b05705f50f0..561ed2a22a1 100644 --- a/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c +++ b/drivers/net/ethernet/mellanox/mlx4/resource_tracker.c @@ -3071,6 +3071,7 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	struct mlx4_resource_tracker *tracker = &priv->mfunc.master.res_tracker;  	struct list_head *rlist = &tracker->slave_list[slave].res_list[RES_MAC];  	int err; +	int qpn;  	struct mlx4_net_trans_rule_hw_ctrl *ctrl;  	struct _rule_hw  *rule_header;  	int header_id; @@ -3080,13 +3081,21 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  		return -EOPNOTSUPP;  	ctrl = (struct mlx4_net_trans_rule_hw_ctrl *)inbox->buf; +	qpn = be32_to_cpu(ctrl->qpn) & 0xffffff; +	err = get_res(dev, slave, qpn, RES_QP, NULL); +	if (err) { +		pr_err("Steering rule with qpn 0x%x rejected.\n", qpn); +		return err; +	}  	rule_header = (struct _rule_hw *)(ctrl + 1);  	header_id = map_hw_to_sw_id(be16_to_cpu(rule_header->id));  	switch (header_id) {  	case MLX4_NET_TRANS_RULE_ID_ETH: -		if (validate_eth_header_mac(slave, rule_header, rlist)) -			return -EINVAL; +		if (validate_eth_header_mac(slave, rule_header, rlist)) { +			err = -EINVAL; +			goto err_put; +		}  		break;  	case MLX4_NET_TRANS_RULE_ID_IB:  		break; @@ -3094,14 +3103,17 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  	case MLX4_NET_TRANS_RULE_ID_TCP:  	case MLX4_NET_TRANS_RULE_ID_UDP:  		pr_warn("Can't attach FS rule without L2 headers, adding L2 header.\n"); -		if (add_eth_header(dev, slave, inbox, rlist, header_id)) -			return -EINVAL; +		if (add_eth_header(dev, slave, inbox, rlist, header_id)) { +			err = -EINVAL; +			goto err_put; +		}  		vhcr->in_modifier +=  			sizeof(struct mlx4_net_trans_rule_hw_eth) >> 2;  		break;  	default:  		pr_err("Corrupted mailbox.\n"); -		return -EINVAL; +		err = -EINVAL; +		goto err_put;  	}  	err = mlx4_cmd_imm(dev, inbox->dma, &vhcr->out_param, @@ -3109,16 +3121,18 @@ int mlx4_QP_FLOW_STEERING_ATTACH_wrapper(struct mlx4_dev *dev, int slave,  			   MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A,  			   MLX4_CMD_NATIVE);  	if (err) -		return err; +		goto err_put;  	err = add_res_range(dev, slave, vhcr->out_param, 1, RES_FS_RULE, 0);  	if (err) {  		mlx4_err(dev, "Fail to add flow steering resources.\n ");  		/* detach rule*/  		mlx4_cmd(dev, vhcr->out_param, 0, 0, -			 MLX4_QP_FLOW_STEERING_ATTACH, MLX4_CMD_TIME_CLASS_A, +			 MLX4_QP_FLOW_STEERING_DETACH, MLX4_CMD_TIME_CLASS_A,  			 MLX4_CMD_NATIVE);  	} +err_put: +	put_res(dev, slave, qpn, RES_QP);  	return err;  } diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c index 83f0ea929d3..8ebc352bcbe 100644 --- a/drivers/net/ethernet/micrel/ksz884x.c +++ b/drivers/net/ethernet/micrel/ksz884x.c @@ -4761,7 +4761,7 @@ static void transmit_cleanup(struct dev_info *hw_priv, int normal)  	struct ksz_dma_buf *dma_buf;  	struct net_device *dev = NULL; -	spin_lock(&hw_priv->hwlock); +	spin_lock_irq(&hw_priv->hwlock);  	last = info->last;  	while (info->avail < info->alloc) { @@ -4795,7 +4795,7 @@ static void transmit_cleanup(struct dev_info *hw_priv, int normal)  		info->avail++;  	}  	info->last = last; -	spin_unlock(&hw_priv->hwlock); +	spin_unlock_irq(&hw_priv->hwlock);  	/* Notify the network subsystem that the packet has been sent. */  	if (dev) @@ -5259,11 +5259,15 @@ static irqreturn_t netdev_intr(int irq, void *dev_id)  	struct dev_info *hw_priv = priv->adapter;  	struct ksz_hw *hw = &hw_priv->hw; +	spin_lock(&hw_priv->hwlock); +  	hw_read_intr(hw, &int_enable);  	/* Not our interrupt! */ -	if (!int_enable) +	if (!int_enable) { +		spin_unlock(&hw_priv->hwlock);  		return IRQ_NONE; +	}  	do {  		hw_ack_intr(hw, int_enable); @@ -5310,6 +5314,8 @@ static irqreturn_t netdev_intr(int irq, void *dev_id)  	hw_ena_intr(hw); +	spin_unlock(&hw_priv->hwlock); +  	return IRQ_HANDLED;  } diff --git a/drivers/net/ethernet/nvidia/forcedeth.c b/drivers/net/ethernet/nvidia/forcedeth.c index 653487dc7b5..87fa5919c45 100644 --- a/drivers/net/ethernet/nvidia/forcedeth.c +++ b/drivers/net/ethernet/nvidia/forcedeth.c @@ -1821,6 +1821,11 @@ static int nv_alloc_rx(struct net_device *dev)  							     skb->data,  							     skb_tailroom(skb),  							     PCI_DMA_FROMDEVICE); +			if (pci_dma_mapping_error(np->pci_dev, +						  np->put_rx_ctx->dma)) { +				kfree_skb(skb); +				goto packet_dropped; +			}  			np->put_rx_ctx->dma_len = skb_tailroom(skb);  			np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma);  			wmb(); @@ -1830,6 +1835,7 @@ static int nv_alloc_rx(struct net_device *dev)  			if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx))  				np->put_rx_ctx = np->first_rx_ctx;  		} else { +packet_dropped:  			u64_stats_update_begin(&np->swstats_rx_syncp);  			np->stat_rx_dropped++;  			u64_stats_update_end(&np->swstats_rx_syncp); @@ -1856,6 +1862,11 @@ static int nv_alloc_rx_optimized(struct net_device *dev)  							     skb->data,  							     skb_tailroom(skb),  							     PCI_DMA_FROMDEVICE); +			if (pci_dma_mapping_error(np->pci_dev, +						  np->put_rx_ctx->dma)) { +				kfree_skb(skb); +				goto packet_dropped; +			}  			np->put_rx_ctx->dma_len = skb_tailroom(skb);  			np->put_rx.ex->bufhigh = cpu_to_le32(dma_high(np->put_rx_ctx->dma));  			np->put_rx.ex->buflow = cpu_to_le32(dma_low(np->put_rx_ctx->dma)); @@ -1866,6 +1877,7 @@ static int nv_alloc_rx_optimized(struct net_device *dev)  			if (unlikely(np->put_rx_ctx++ == np->last_rx_ctx))  				np->put_rx_ctx = np->first_rx_ctx;  		} else { +packet_dropped:  			u64_stats_update_begin(&np->swstats_rx_syncp);  			np->stat_rx_dropped++;  			u64_stats_update_end(&np->swstats_rx_syncp); @@ -2217,6 +2229,15 @@ static netdev_tx_t nv_start_xmit(struct sk_buff *skb, struct net_device *dev)  		bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;  		np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt,  						PCI_DMA_TODEVICE); +		if (pci_dma_mapping_error(np->pci_dev, +					  np->put_tx_ctx->dma)) { +			/* on DMA mapping error - drop the packet */ +			kfree_skb(skb); +			u64_stats_update_begin(&np->swstats_tx_syncp); +			np->stat_tx_dropped++; +			u64_stats_update_end(&np->swstats_tx_syncp); +			return NETDEV_TX_OK; +		}  		np->put_tx_ctx->dma_len = bcnt;  		np->put_tx_ctx->dma_single = 1;  		put_tx->buf = cpu_to_le32(np->put_tx_ctx->dma); @@ -2337,6 +2358,15 @@ static netdev_tx_t nv_start_xmit_optimized(struct sk_buff *skb,  		bcnt = (size > NV_TX2_TSO_MAX_SIZE) ? NV_TX2_TSO_MAX_SIZE : size;  		np->put_tx_ctx->dma = pci_map_single(np->pci_dev, skb->data + offset, bcnt,  						PCI_DMA_TODEVICE); +		if (pci_dma_mapping_error(np->pci_dev, +					  np->put_tx_ctx->dma)) { +			/* on DMA mapping error - drop the packet */ +			kfree_skb(skb); +			u64_stats_update_begin(&np->swstats_tx_syncp); +			np->stat_tx_dropped++; +			u64_stats_update_end(&np->swstats_tx_syncp); +			return NETDEV_TX_OK; +		}  		np->put_tx_ctx->dma_len = bcnt;  		np->put_tx_ctx->dma_single = 1;  		put_tx->bufhigh = cpu_to_le32(dma_high(np->put_tx_ctx->dma)); @@ -5003,6 +5033,11 @@ static int nv_loopback_test(struct net_device *dev)  	test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data,  				       skb_tailroom(tx_skb),  				       PCI_DMA_FROMDEVICE); +	if (pci_dma_mapping_error(np->pci_dev, +				  test_dma_addr)) { +		dev_kfree_skb_any(tx_skb); +		goto out; +	}  	pkt_data = skb_put(tx_skb, pkt_len);  	for (i = 0; i < pkt_len; i++)  		pkt_data[i] = (u8)(i & 0xff); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index 53790247968..bc7ec64e9c7 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -36,8 +36,8 @@  #define _QLCNIC_LINUX_MAJOR 5  #define _QLCNIC_LINUX_MINOR 0 -#define _QLCNIC_LINUX_SUBVERSION 29 -#define QLCNIC_LINUX_VERSIONID  "5.0.29" +#define _QLCNIC_LINUX_SUBVERSION 30 +#define QLCNIC_LINUX_VERSIONID  "5.0.30"  #define QLCNIC_DRV_IDC_VER  0x01  #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\  		 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index 58f094ca052..b14b8f0787e 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -134,7 +134,7 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)  	__le32 *tmp_buf;  	struct qlcnic_cmd_args cmd;  	struct qlcnic_hardware_context *ahw; -	struct qlcnic_dump_template_hdr *tmpl_hdr, *tmp_tmpl; +	struct qlcnic_dump_template_hdr *tmpl_hdr;  	dma_addr_t tmp_addr_t = 0;  	ahw = adapter->ahw; @@ -150,6 +150,8 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)  	}  	temp_size = cmd.rsp.arg2;  	version = cmd.rsp.arg3; +	dev_info(&adapter->pdev->dev, +		 "minidump template version = 0x%x", version);  	if (!temp_size)  		return -EIO; @@ -174,7 +176,6 @@ int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter)  		err = -EIO;  		goto error;  	} -	tmp_tmpl = tmp_addr;  	ahw->fw_dump.tmpl_hdr = vzalloc(temp_size);  	if (!ahw->fw_dump.tmpl_hdr) {  		err = -EIO; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index fc48e000f35..7a6d5ebe4e0 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -365,7 +365,7 @@ static int  qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,  		struct cmd_desc_type0 *cmd_desc_arr, int nr_desc)  { -	u32 i, producer, consumer; +	u32 i, producer;  	struct qlcnic_cmd_buffer *pbuf;  	struct cmd_desc_type0 *cmd_desc;  	struct qlcnic_host_tx_ring *tx_ring; @@ -379,7 +379,6 @@ qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,  	__netif_tx_lock_bh(tx_ring->txq);  	producer = tx_ring->producer; -	consumer = tx_ring->sw_consumer;  	if (nr_desc >= qlcnic_tx_avail(tx_ring)) {  		netif_tx_stop_queue(tx_ring->txq); @@ -402,7 +401,7 @@ qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,  		pbuf->frag_count = 0;  		memcpy(&tx_ring->desc_head[producer], -			&cmd_desc_arr[i], sizeof(struct cmd_desc_type0)); +		       cmd_desc, sizeof(struct cmd_desc_type0));  		producer = get_next_index(producer, tx_ring->num_desc);  		i++; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index a7554d9aab0..d833f592789 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -445,13 +445,10 @@ static int  qlcnic_set_function_modes(struct qlcnic_adapter *adapter)  {  	u8 id; -	u32 ref_count;  	int i, ret = 1;  	u32 data = QLCNIC_MGMT_FUNC;  	struct qlcnic_hardware_context *ahw = adapter->ahw; -	/* If other drivers are not in use set their privilege level */ -	ref_count = QLCRD32(adapter, QLCNIC_CRB_DRV_ACTIVE);  	ret = qlcnic_api_lock(adapter);  	if (ret)  		goto err_lock; @@ -531,11 +528,9 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev,  {  	u32 offset;  	void __iomem *mem_ptr0 = NULL; -	resource_size_t mem_base;  	unsigned long mem_len, pci_len0 = 0, bar0_len;  	/* remap phys address */ -	mem_base = pci_resource_start(pdev, 0);	/* 0 is for BAR 0 */  	mem_len = pci_resource_len(pdev, 0);  	qlcnic_get_bar_length(pdev->device, &bar0_len); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index 12ff2927074..0b8d8625834 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -197,7 +197,7 @@ static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter,  	int i, k, timeout = 0;  	void __iomem *base = adapter->ahw->pci_base0;  	u32 addr, data; -	u8 opcode, no_ops; +	u8 no_ops;  	struct __ctrl *ctr = &entry->region.ctrl;  	struct qlcnic_dump_template_hdr *t_hdr = adapter->ahw->fw_dump.tmpl_hdr; @@ -206,7 +206,6 @@ static u32 qlcnic_dump_ctrl(struct qlcnic_adapter *adapter,  	for (i = 0; i < no_ops; i++) {  		k = 0; -		opcode = 0;  		for (k = 0; k < 8; k++) {  			if (!(ctr->opcode & (1 << k)))  				continue; diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index f80cd975dae..3e73742024b 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -4678,7 +4678,7 @@ static int qlge_probe(struct pci_dev *pdev,  	qdev = netdev_priv(ndev);  	SET_NETDEV_DEV(ndev, &pdev->dev);  	ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | -		NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN | +		NETIF_F_TSO | NETIF_F_TSO_ECN |  		NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM;  	ndev->features = ndev->hw_features |  		NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; diff --git a/drivers/net/ethernet/realtek/8139cp.c b/drivers/net/ethernet/realtek/8139cp.c index cb6fc5a743c..5ac93323a40 100644 --- a/drivers/net/ethernet/realtek/8139cp.c +++ b/drivers/net/ethernet/realtek/8139cp.c @@ -577,28 +577,30 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)  {  	struct net_device *dev = dev_instance;  	struct cp_private *cp; +	int handled = 0;  	u16 status;  	if (unlikely(dev == NULL))  		return IRQ_NONE;  	cp = netdev_priv(dev); +	spin_lock(&cp->lock); +  	status = cpr16(IntrStatus);  	if (!status || (status == 0xFFFF)) -		return IRQ_NONE; +		goto out_unlock; + +	handled = 1;  	netif_dbg(cp, intr, dev, "intr, status %04x cmd %02x cpcmd %04x\n",  		  status, cpr8(Cmd), cpr16(CpCmd));  	cpw16(IntrStatus, status & ~cp_rx_intr_mask); -	spin_lock(&cp->lock); -  	/* close possible race's with dev_close */  	if (unlikely(!netif_running(dev))) {  		cpw16(IntrMask, 0); -		spin_unlock(&cp->lock); -		return IRQ_HANDLED; +		goto out_unlock;  	}  	if (status & (RxOK | RxErr | RxEmpty | RxFIFOOvr)) @@ -612,7 +614,6 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)  	if (status & LinkChg)  		mii_check_media(&cp->mii_if, netif_msg_link(cp), false); -	spin_unlock(&cp->lock);  	if (status & PciErr) {  		u16 pci_status; @@ -625,7 +626,10 @@ static irqreturn_t cp_interrupt (int irq, void *dev_instance)  		/* TODO: reset hardware */  	} -	return IRQ_HANDLED; +out_unlock: +	spin_unlock(&cp->lock); + +	return IRQ_RETVAL(handled);  }  #ifdef CONFIG_NET_POLL_CONTROLLER diff --git a/drivers/net/ethernet/smsc/smc91x.c b/drivers/net/ethernet/smsc/smc91x.c index 022b45bc14f..a670d23d934 100644 --- a/drivers/net/ethernet/smsc/smc91x.c +++ b/drivers/net/ethernet/smsc/smc91x.c @@ -2386,8 +2386,6 @@ static const struct of_device_id smc91x_match[] = {  	{},  };  MODULE_DEVICE_TABLE(of, smc91x_match); -#else -#define smc91x_match NULL  #endif  static struct dev_pm_ops smc_drv_pm_ops = { @@ -2402,7 +2400,7 @@ static struct platform_driver smc_driver = {  		.name	= CARDNAME,  		.owner	= THIS_MODULE,  		.pm	= &smc_drv_pm_ops, -		.of_match_table = smc91x_match, +		.of_match_table = of_match_ptr(smc91x_match),  	},  }; diff --git a/drivers/net/ethernet/smsc/smsc911x.c b/drivers/net/ethernet/smsc/smsc911x.c index 4616bf27d51..e112877d15d 100644 --- a/drivers/net/ethernet/smsc/smsc911x.c +++ b/drivers/net/ethernet/smsc/smsc911x.c @@ -2575,11 +2575,13 @@ static const struct dev_pm_ops smsc911x_pm_ops = {  #define SMSC911X_PM_OPS NULL  #endif +#ifdef CONFIG_OF  static const struct of_device_id smsc911x_dt_ids[] = {  	{ .compatible = "smsc,lan9115", },  	{ /* sentinel */ }  };  MODULE_DEVICE_TABLE(of, smsc911x_dt_ids); +#endif  static struct platform_driver smsc911x_driver = {  	.probe = smsc911x_drv_probe, @@ -2588,7 +2590,7 @@ static struct platform_driver smsc911x_driver = {  		.name	= SMSC_CHIPNAME,  		.owner	= THIS_MODULE,  		.pm	= SMSC911X_PM_OPS, -		.of_match_table = smsc911x_dt_ids, +		.of_match_table = of_match_ptr(smsc911x_dt_ids),  	},  }; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h index 023a4fb4efa..b05df8983be 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h @@ -127,14 +127,14 @@ static inline int stmmac_register_platform(void)  }  static inline void stmmac_unregister_platform(void)  { -	platform_driver_register(&stmmac_pltfr_driver); +	platform_driver_unregister(&stmmac_pltfr_driver);  }  #else  static inline int stmmac_register_platform(void)  {  	pr_debug("stmmac: do not register the platf driver\n"); -	return -EINVAL; +	return 0;  }  static inline void stmmac_unregister_platform(void)  { @@ -162,7 +162,7 @@ static inline int stmmac_register_pci(void)  {  	pr_debug("stmmac: do not register the PCI driver\n"); -	return -EINVAL; +	return 0;  }  static inline void stmmac_unregister_pci(void)  { diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 542edbcd92c..f07c0612abf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2194,18 +2194,20 @@ int stmmac_restore(struct net_device *ndev)   */  static int __init stmmac_init(void)  { -	int err_plt = 0; -	int err_pci = 0; - -	err_plt = stmmac_register_platform(); -	err_pci = stmmac_register_pci(); - -	if ((err_pci) && (err_plt)) { -		pr_err("stmmac: driver registration failed\n"); -		return -EINVAL; -	} +	int ret; +	ret = stmmac_register_platform(); +	if (ret) +		goto err; +	ret = stmmac_register_pci(); +	if (ret) +		goto err_pci;  	return 0; +err_pci: +	stmmac_unregister_platform(); +err: +	pr_err("stmmac: driver registration failed\n"); +	return ret;  }  static void __exit stmmac_exit(void) diff --git a/drivers/net/ethernet/ti/cpts.c b/drivers/net/ethernet/ti/cpts.c index 337766738ec..463597f919f 100644 --- a/drivers/net/ethernet/ti/cpts.c +++ b/drivers/net/ethernet/ti/cpts.c @@ -27,8 +27,6 @@  #include <linux/uaccess.h>  #include <linux/workqueue.h> -#include <plat/clock.h> -  #include "cpts.h"  #ifdef CONFIG_TI_CPTS @@ -249,8 +247,7 @@ static void cpts_clk_init(struct cpts *cpts)  		cpts->refclk = NULL;  		return;  	} -	clk_enable(cpts->refclk); -	cpts->freq = cpts->refclk->recalc(cpts->refclk); +	clk_prepare_enable(cpts->refclk);  }  static void cpts_clk_release(struct cpts *cpts) diff --git a/drivers/net/ethernet/ti/cpts.h b/drivers/net/ethernet/ti/cpts.h index e1bba3a496b..fe993cdd7e2 100644 --- a/drivers/net/ethernet/ti/cpts.h +++ b/drivers/net/ethernet/ti/cpts.h @@ -120,7 +120,6 @@ struct cpts {  	struct delayed_work overflow_work;  	int phc_index;  	struct clk *refclk; -	unsigned long freq;  	struct list_head events;  	struct list_head pool;  	struct cpts_event pool_data[CPTS_MAX_EVENTS]; diff --git a/drivers/net/ethernet/xilinx/Kconfig b/drivers/net/ethernet/xilinx/Kconfig index 5778a4ae116..122d60c0481 100644 --- a/drivers/net/ethernet/xilinx/Kconfig +++ b/drivers/net/ethernet/xilinx/Kconfig @@ -27,7 +27,7 @@ config XILINX_EMACLITE  config XILINX_AXI_EMAC  	tristate "Xilinx 10/100/1000 AXI Ethernet support" -	depends on (PPC32 || MICROBLAZE) +	depends on MICROBLAZE  	select PHYLIB  	---help---  	  This driver supports the 10/100/1000 Ethernet from Xilinx for the diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index d9f69b82cc4..6f47100e58d 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1590,7 +1590,7 @@ static int axienet_of_probe(struct platform_device *op)  	lp->rx_irq = irq_of_parse_and_map(np, 1);  	lp->tx_irq = irq_of_parse_and_map(np, 0);  	of_node_put(np); -	if ((lp->rx_irq == NO_IRQ) || (lp->tx_irq == NO_IRQ)) { +	if ((lp->rx_irq <= 0) || (lp->tx_irq <= 0)) {  		dev_err(&op->dev, "could not determine irqs\n");  		ret = -ENOMEM;  		goto err_iounmap_2; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 40b426edc9e..af372d0957f 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -138,6 +138,8 @@ struct tun_file {  	/* only used for fasnyc */  	unsigned int flags;  	u16 queue_index; +	struct list_head next; +	struct tun_struct *detached;  };  struct tun_flow_entry { @@ -178,10 +180,11 @@ struct tun_struct {  	int debug;  #endif  	spinlock_t lock; -	struct kmem_cache *flow_cache;  	struct hlist_head flows[TUN_NUM_FLOW_ENTRIES];  	struct timer_list flow_gc_timer;  	unsigned long ageing_time; +	unsigned int numdisabled; +	struct list_head disabled;  };  static inline u32 tun_hashfn(u32 rxhash) @@ -205,8 +208,8 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,  					      struct hlist_head *head,  					      u32 rxhash, u16 queue_index)  { -	struct tun_flow_entry *e = kmem_cache_alloc(tun->flow_cache, -						    GFP_ATOMIC); +	struct tun_flow_entry *e = kmalloc(sizeof(*e), GFP_ATOMIC); +  	if (e) {  		tun_debug(KERN_INFO, tun, "create flow: hash %u index %u\n",  			  rxhash, queue_index); @@ -219,19 +222,12 @@ static struct tun_flow_entry *tun_flow_create(struct tun_struct *tun,  	return e;  } -static void tun_flow_free(struct rcu_head *head) -{ -	struct tun_flow_entry *e -		= container_of(head, struct tun_flow_entry, rcu); -	kmem_cache_free(e->tun->flow_cache, e); -} -  static void tun_flow_delete(struct tun_struct *tun, struct tun_flow_entry *e)  {  	tun_debug(KERN_INFO, tun, "delete flow: hash %u index %u\n",  		  e->rxhash, e->queue_index);  	hlist_del_rcu(&e->hash_link); -	call_rcu(&e->rcu, tun_flow_free); +	kfree_rcu(e, rcu);  }  static void tun_flow_flush(struct tun_struct *tun) @@ -385,14 +381,31 @@ static void tun_set_real_num_queues(struct tun_struct *tun)  	netif_set_real_num_rx_queues(tun->dev, tun->numqueues);  } +static void tun_disable_queue(struct tun_struct *tun, struct tun_file *tfile) +{ +	tfile->detached = tun; +	list_add_tail(&tfile->next, &tun->disabled); +	++tun->numdisabled; +} + +static struct tun_struct *tun_enable_queue(struct tun_file *tfile) +{ +	struct tun_struct *tun = tfile->detached; + +	tfile->detached = NULL; +	list_del_init(&tfile->next); +	--tun->numdisabled; +	return tun; +} +  static void __tun_detach(struct tun_file *tfile, bool clean)  {  	struct tun_file *ntfile;  	struct tun_struct *tun;  	struct net_device *dev; -	tun = rcu_dereference_protected(tfile->tun, -					lockdep_rtnl_is_held()); +	tun = rtnl_dereference(tfile->tun); +  	if (tun) {  		u16 index = tfile->queue_index;  		BUG_ON(index >= tun->numqueues); @@ -401,25 +414,31 @@ static void __tun_detach(struct tun_file *tfile, bool clean)  		rcu_assign_pointer(tun->tfiles[index],  				   tun->tfiles[tun->numqueues - 1]);  		rcu_assign_pointer(tfile->tun, NULL); -		ntfile = rcu_dereference_protected(tun->tfiles[index], -						   lockdep_rtnl_is_held()); +		ntfile = rtnl_dereference(tun->tfiles[index]);  		ntfile->queue_index = index;  		--tun->numqueues; -		sock_put(&tfile->sk); +		if (clean) +			sock_put(&tfile->sk); +		else +			tun_disable_queue(tun, tfile);  		synchronize_net();  		tun_flow_delete_by_queue(tun, tun->numqueues + 1);  		/* Drop read queue */  		skb_queue_purge(&tfile->sk.sk_receive_queue);  		tun_set_real_num_queues(tun); - -		if (tun->numqueues == 0 && !(tun->flags & TUN_PERSIST)) -			if (dev->reg_state == NETREG_REGISTERED) -				unregister_netdevice(dev); +	} else if (tfile->detached && clean) { +		tun = tun_enable_queue(tfile); +		sock_put(&tfile->sk);  	}  	if (clean) { +		if (tun && tun->numqueues == 0 && tun->numdisabled == 0 && +		    !(tun->flags & TUN_PERSIST)) +			if (tun->dev->reg_state == NETREG_REGISTERED) +				unregister_netdevice(tun->dev); +  		BUG_ON(!test_bit(SOCK_EXTERNALLY_ALLOCATED,  				 &tfile->socket.flags));  		sk_release_kernel(&tfile->sk); @@ -436,12 +455,11 @@ static void tun_detach(struct tun_file *tfile, bool clean)  static void tun_detach_all(struct net_device *dev)  {  	struct tun_struct *tun = netdev_priv(dev); -	struct tun_file *tfile; +	struct tun_file *tfile, *tmp;  	int i, n = tun->numqueues;  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		BUG_ON(!tfile);  		wake_up_all(&tfile->wq.wait);  		rcu_assign_pointer(tfile->tun, NULL); @@ -451,12 +469,20 @@ static void tun_detach_all(struct net_device *dev)  	synchronize_net();  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		/* Drop read queue */  		skb_queue_purge(&tfile->sk.sk_receive_queue);  		sock_put(&tfile->sk);  	} +	list_for_each_entry_safe(tfile, tmp, &tun->disabled, next) { +		tun_enable_queue(tfile); +		skb_queue_purge(&tfile->sk.sk_receive_queue); +		sock_put(&tfile->sk); +	} +	BUG_ON(tun->numdisabled != 0); + +	if (tun->flags & TUN_PERSIST) +		module_put(THIS_MODULE);  }  static int tun_attach(struct tun_struct *tun, struct file *file) @@ -465,7 +491,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file)  	int err;  	err = -EINVAL; -	if (rcu_dereference_protected(tfile->tun, lockdep_rtnl_is_held())) +	if (rtnl_dereference(tfile->tun))  		goto out;  	err = -EBUSY; @@ -473,7 +499,8 @@ static int tun_attach(struct tun_struct *tun, struct file *file)  		goto out;  	err = -E2BIG; -	if (tun->numqueues == MAX_TAP_QUEUES) +	if (!tfile->detached && +	    tun->numqueues + tun->numdisabled == MAX_TAP_QUEUES)  		goto out;  	err = 0; @@ -487,9 +514,13 @@ static int tun_attach(struct tun_struct *tun, struct file *file)  	tfile->queue_index = tun->numqueues;  	rcu_assign_pointer(tfile->tun, tun);  	rcu_assign_pointer(tun->tfiles[tun->numqueues], tfile); -	sock_hold(&tfile->sk);  	tun->numqueues++; +	if (tfile->detached) +		tun_enable_queue(tfile); +	else +		sock_hold(&tfile->sk); +  	tun_set_real_num_queues(tun);  	/* device is allowed to go away first, so no need to hold extra @@ -796,12 +827,6 @@ static int tun_flow_init(struct tun_struct *tun)  {  	int i; -	tun->flow_cache = kmem_cache_create("tun_flow_cache", -					    sizeof(struct tun_flow_entry), 0, 0, -					    NULL); -	if (!tun->flow_cache) -		return -ENOMEM; -  	for (i = 0; i < TUN_NUM_FLOW_ENTRIES; i++)  		INIT_HLIST_HEAD(&tun->flows[i]); @@ -817,10 +842,6 @@ static void tun_flow_uninit(struct tun_struct *tun)  {  	del_timer_sync(&tun->flow_gc_timer);  	tun_flow_flush(tun); - -	/* Wait for completion of call_rcu()'s */ -	rcu_barrier(); -	kmem_cache_destroy(tun->flow_cache);  }  /* Initialize net device. */ @@ -1162,6 +1183,7 @@ static ssize_t tun_get_user(struct tun_struct *tun, struct tun_file *tfile,  		skb_shinfo(skb)->tx_flags |= SKBTX_DEV_ZEROCOPY;  	} +	skb_reset_network_header(skb);  	rxhash = skb_get_rxhash(skb);  	netif_rx_ni(skb); @@ -1349,6 +1371,7 @@ static void tun_free_netdev(struct net_device *dev)  {  	struct tun_struct *tun = netdev_priv(dev); +	BUG_ON(!(list_empty(&tun->disabled)));  	tun_flow_uninit(tun);  	free_netdev(dev);  } @@ -1523,6 +1546,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  	struct net_device *dev;  	int err; +	if (tfile->detached) +		return -EINVAL; +  	dev = __dev_get_by_name(net, ifr->ifr_name);  	if (dev) {  		if (ifr->ifr_flags & IFF_TUN_EXCL) @@ -1543,6 +1569,10 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  		err = tun_attach(tun, file);  		if (err < 0)  			return err; + +		if (tun->flags & TUN_TAP_MQ && +		    (tun->numqueues + tun->numdisabled > 1)) +			return err;  	}  	else {  		char *name; @@ -1601,6 +1631,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)  			TUN_USER_FEATURES;  		dev->features = dev->hw_features; +		INIT_LIST_HEAD(&tun->disabled);  		err = tun_attach(tun, file);  		if (err < 0)  			goto err_free_dev; @@ -1712,8 +1743,7 @@ static void tun_detach_filter(struct tun_struct *tun, int n)  	struct tun_file *tfile;  	for (i = 0; i < n; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		sk_detach_filter(tfile->socket.sk);  	} @@ -1726,8 +1756,7 @@ static int tun_attach_filter(struct tun_struct *tun)  	struct tun_file *tfile;  	for (i = 0; i < tun->numqueues; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						  lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		ret = sk_attach_filter(&tun->fprog, tfile->socket.sk);  		if (ret) {  			tun_detach_filter(tun, i); @@ -1745,8 +1774,7 @@ static void tun_set_sndbuf(struct tun_struct *tun)  	int i;  	for (i = 0; i < tun->numqueues; i++) { -		tfile = rcu_dereference_protected(tun->tfiles[i], -						lockdep_rtnl_is_held()); +		tfile = rtnl_dereference(tun->tfiles[i]);  		tfile->socket.sk->sk_sndbuf = tun->sndbuf;  	}  } @@ -1755,32 +1783,25 @@ static int tun_set_queue(struct file *file, struct ifreq *ifr)  {  	struct tun_file *tfile = file->private_data;  	struct tun_struct *tun; -	struct net_device *dev;  	int ret = 0;  	rtnl_lock();  	if (ifr->ifr_flags & IFF_ATTACH_QUEUE) { -		dev = __dev_get_by_name(tfile->net, ifr->ifr_name); -		if (!dev) { -			ret = -EINVAL; -			goto unlock; -		} - -		tun = netdev_priv(dev); -		if (dev->netdev_ops != &tap_netdev_ops && -			dev->netdev_ops != &tun_netdev_ops) +		tun = tfile->detached; +		if (!tun)  			ret = -EINVAL; -		else if (tun_not_capable(tun)) -			ret = -EPERM;  		else  			ret = tun_attach(tun, file); -	} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) -		__tun_detach(tfile, false); -	else +	} else if (ifr->ifr_flags & IFF_DETACH_QUEUE) { +		tun = rtnl_dereference(tfile->tun); +		if (!tun || !(tun->flags & TUN_TAP_MQ)) +			ret = -EINVAL; +		else +			__tun_detach(tfile, false); +	} else  		ret = -EINVAL; -unlock:  	rtnl_unlock();  	return ret;  } @@ -1858,10 +1879,11 @@ static long __tun_chr_ioctl(struct file *file, unsigned int cmd,  		/* Disable/Enable persist mode. Keep an extra reference to the  		 * module to prevent the module being unprobed.  		 */ -		if (arg) { +		if (arg && !(tun->flags & TUN_PERSIST)) {  			tun->flags |= TUN_PERSIST;  			__module_get(THIS_MODULE); -		} else { +		} +		if (!arg && (tun->flags & TUN_PERSIST)) {  			tun->flags &= ~TUN_PERSIST;  			module_put(THIS_MODULE);  		} @@ -2092,6 +2114,7 @@ static int tun_chr_open(struct inode *inode, struct file * file)  	file->private_data = tfile;  	set_bit(SOCK_EXTERNALLY_ALLOCATED, &tfile->socket.flags); +	INIT_LIST_HEAD(&tfile->next);  	return 0;  } diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index d0129827602..3f3d12d766e 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -457,12 +457,6 @@ int usbnet_cdc_bind(struct usbnet *dev, struct usb_interface *intf)  }  EXPORT_SYMBOL_GPL(usbnet_cdc_bind); -static int cdc_manage_power(struct usbnet *dev, int on) -{ -	dev->intf->needs_remote_wakeup = on; -	return 0; -} -  static const struct driver_info	cdc_info = {  	.description =	"CDC Ethernet Device",  	.flags =	FLAG_ETHER | FLAG_POINTTOPOINT, @@ -470,7 +464,7 @@ static const struct driver_info	cdc_info = {  	.bind =		usbnet_cdc_bind,  	.unbind =	usbnet_cdc_unbind,  	.status =	usbnet_cdc_status, -	.manage_power =	cdc_manage_power, +	.manage_power =	usbnet_manage_power,  };  static const struct driver_info wwan_info = { @@ -479,7 +473,7 @@ static const struct driver_info wwan_info = {  	.bind =		usbnet_cdc_bind,  	.unbind =	usbnet_cdc_unbind,  	.status =	usbnet_cdc_status, -	.manage_power =	cdc_manage_power, +	.manage_power =	usbnet_manage_power,  };  /*-------------------------------------------------------------------------*/ @@ -487,6 +481,7 @@ static const struct driver_info wwan_info = {  #define HUAWEI_VENDOR_ID	0x12D1  #define NOVATEL_VENDOR_ID	0x1410  #define ZTE_VENDOR_ID		0x19D2 +#define DELL_VENDOR_ID		0x413C  static const struct usb_device_id	products [] = {  /* @@ -594,27 +589,29 @@ static const struct usb_device_id	products [] = {  /* Novatel USB551L and MC551 - handled by qmi_wwan */  { -	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR -		 | USB_DEVICE_ID_MATCH_PRODUCT -		 | USB_DEVICE_ID_MATCH_INT_INFO, -	.idVendor               = NOVATEL_VENDOR_ID, -	.idProduct		= 0xB001, -	.bInterfaceClass	= USB_CLASS_COMM, -	.bInterfaceSubClass	= USB_CDC_SUBCLASS_ETHERNET, -	.bInterfaceProtocol	= USB_CDC_PROTO_NONE, +	USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0xB001, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),  	.driver_info = 0,  },  /* Novatel E362 - handled by qmi_wwan */  { -	.match_flags    =   USB_DEVICE_ID_MATCH_VENDOR -		 | USB_DEVICE_ID_MATCH_PRODUCT -		 | USB_DEVICE_ID_MATCH_INT_INFO, -	.idVendor               = NOVATEL_VENDOR_ID, -	.idProduct		= 0x9010, -	.bInterfaceClass	= USB_CLASS_COMM, -	.bInterfaceSubClass	= USB_CDC_SUBCLASS_ETHERNET, -	.bInterfaceProtocol	= USB_CDC_PROTO_NONE, +	USB_DEVICE_AND_INTERFACE_INFO(NOVATEL_VENDOR_ID, 0x9010, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8195, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE), +	.driver_info = 0, +}, + +/* Dell Wireless 5800 (Novatel E362) - handled by qmi_wwan */ +{ +	USB_DEVICE_AND_INTERFACE_INFO(DELL_VENDOR_ID, 0x8196, USB_CLASS_COMM, +			USB_CDC_SUBCLASS_ETHERNET, USB_CDC_PROTO_NONE),  	.driver_info = 0,  }, diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d38bc20a60e..71b6e92b8e9 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -1129,19 +1129,13 @@ static void cdc_ncm_disconnect(struct usb_interface *intf)  	usbnet_disconnect(intf);  } -static int cdc_ncm_manage_power(struct usbnet *dev, int status) -{ -	dev->intf->needs_remote_wakeup = status; -	return 0; -} -  static const struct driver_info cdc_ncm_info = {  	.description = "CDC NCM",  	.flags = FLAG_POINTTOPOINT | FLAG_NO_SETINT | FLAG_MULTI_PACKET,  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind,  	.check_connect = cdc_ncm_check_connect, -	.manage_power = cdc_ncm_manage_power, +	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup,  	.tx_fixup = cdc_ncm_tx_fixup, @@ -1155,7 +1149,7 @@ static const struct driver_info wwan_info = {  	.bind = cdc_ncm_bind,  	.unbind = cdc_ncm_unbind,  	.check_connect = cdc_ncm_check_connect, -	.manage_power = cdc_ncm_manage_power, +	.manage_power = usbnet_manage_power,  	.status = cdc_ncm_status,  	.rx_fixup = cdc_ncm_rx_fixup,  	.tx_fixup = cdc_ncm_tx_fixup, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 1ea91f4237f..6a1ca500e61 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -383,6 +383,20 @@ static const struct usb_device_id products[] = {  		                              USB_CDC_PROTO_NONE),  		.driver_info        = (unsigned long)&qmi_wwan_info,  	}, +	{	/* Dell Wireless 5800 (Novatel E362) */ +		USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8195, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	}, +	{	/* Dell Wireless 5800 V2 (Novatel E362) */ +		USB_DEVICE_AND_INTERFACE_INFO(0x413C, 0x8196, +					      USB_CLASS_COMM, +					      USB_CDC_SUBCLASS_ETHERNET, +					      USB_CDC_PROTO_NONE), +		.driver_info        = (unsigned long)&qmi_wwan_info, +	},  	/* 3. Combined interface devices matching on interface number */  	{QMI_FIXED_INTF(0x12d1, 0x140c, 1)},	/* Huawei E173 */ @@ -419,6 +433,7 @@ static const struct usb_device_id products[] = {  	{QMI_FIXED_INTF(0x19d2, 0x0199, 1)},	/* ZTE MF820S */  	{QMI_FIXED_INTF(0x19d2, 0x0200, 1)},  	{QMI_FIXED_INTF(0x19d2, 0x0257, 3)},	/* ZTE MF821 */ +	{QMI_FIXED_INTF(0x19d2, 0x0284, 4)},	/* ZTE MF880 */  	{QMI_FIXED_INTF(0x19d2, 0x0326, 4)},	/* ZTE MF821D */  	{QMI_FIXED_INTF(0x19d2, 0x1008, 4)},	/* ZTE (Vodafone) K3570-Z */  	{QMI_FIXED_INTF(0x19d2, 0x1010, 4)},	/* ZTE (Vodafone) K3571-Z */ @@ -443,6 +458,7 @@ static const struct usb_device_id products[] = {  	{QMI_FIXED_INTF(0x1199, 0x68a2, 8)},	/* Sierra Wireless MC7710 in QMI mode */  	{QMI_FIXED_INTF(0x1199, 0x68a2, 19)},	/* Sierra Wireless MC7710 in QMI mode */  	{QMI_FIXED_INTF(0x1199, 0x901c, 8)},    /* Sierra Wireless EM7700 */ +	{QMI_FIXED_INTF(0x1bbb, 0x011e, 4)},	/* Telekom Speedstick LTE II (Alcatel One Touch L100V LTE) */  	/* 4. Gobi 1000 devices */  	{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)},	/* Acer Gobi Modem Device */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index c04110ba677..3d4bf01641b 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -719,7 +719,8 @@ int usbnet_stop (struct net_device *net)  	dev->flags = 0;  	del_timer_sync (&dev->delay);  	tasklet_kill (&dev->bh); -	if (info->manage_power) +	if (info->manage_power && +	    !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags))  		info->manage_power(dev, 0);  	else  		usb_autopm_put_interface(dev->intf); @@ -794,14 +795,14 @@ int usbnet_open (struct net_device *net)  	tasklet_schedule (&dev->bh);  	if (info->manage_power) {  		retval = info->manage_power(dev, 1); -		if (retval < 0) -			goto done_manage_power_error; -		usb_autopm_put_interface(dev->intf); +		if (retval < 0) { +			retval = 0; +			set_bit(EVENT_NO_RUNTIME_PM, &dev->flags); +		} else { +			usb_autopm_put_interface(dev->intf); +		}  	}  	return retval; - -done_manage_power_error: -	clear_bit(EVENT_DEV_OPEN, &dev->flags);  done:  	usb_autopm_put_interface(dev->intf);  done_nopm: @@ -1615,6 +1616,16 @@ void usbnet_device_suggests_idle(struct usbnet *dev)  }  EXPORT_SYMBOL(usbnet_device_suggests_idle); +/* + * For devices that can do without special commands + */ +int usbnet_manage_power(struct usbnet *dev, int on) +{ +	dev->intf->needs_remote_wakeup = on; +	return 0; +} +EXPORT_SYMBOL(usbnet_manage_power); +  /*-------------------------------------------------------------------------*/  static int __usbnet_read_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,  			     u16 value, u16 index, void *data, u16 size) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 68d64f0313e..a6fcf15adc4 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -130,7 +130,6 @@ struct skb_vnet_hdr {  		struct virtio_net_hdr hdr;  		struct virtio_net_hdr_mrg_rxbuf mhdr;  	}; -	unsigned int num_sg;  };  struct padded_vnet_hdr { @@ -530,10 +529,10 @@ static bool try_fill_recv(struct receive_queue *rq, gfp_t gfp)  			err = add_recvbuf_small(rq, gfp);  		oom = err == -ENOMEM; -		if (err < 0) +		if (err)  			break;  		++rq->num; -	} while (err > 0); +	} while (rq->vq->num_free);  	if (unlikely(rq->num > rq->max))  		rq->max = rq->num;  	virtqueue_kick(rq->vq); @@ -640,10 +639,10 @@ static int virtnet_open(struct net_device *dev)  	return 0;  } -static unsigned int free_old_xmit_skbs(struct send_queue *sq) +static void free_old_xmit_skbs(struct send_queue *sq)  {  	struct sk_buff *skb; -	unsigned int len, tot_sgs = 0; +	unsigned int len;  	struct virtnet_info *vi = sq->vq->vdev->priv;  	struct virtnet_stats *stats = this_cpu_ptr(vi->stats); @@ -655,10 +654,8 @@ static unsigned int free_old_xmit_skbs(struct send_queue *sq)  		stats->tx_packets++;  		u64_stats_update_end(&stats->tx_syncp); -		tot_sgs += skb_vnet_hdr(skb)->num_sg;  		dev_kfree_skb_any(skb);  	} -	return tot_sgs;  }  static int xmit_skb(struct send_queue *sq, struct sk_buff *skb) @@ -666,6 +663,7 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)  	struct skb_vnet_hdr *hdr = skb_vnet_hdr(skb);  	const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest;  	struct virtnet_info *vi = sq->vq->vdev->priv; +	unsigned num_sg;  	pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest); @@ -704,8 +702,8 @@ static int xmit_skb(struct send_queue *sq, struct sk_buff *skb)  	else  		sg_set_buf(sq->sg, &hdr->hdr, sizeof hdr->hdr); -	hdr->num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1; -	return virtqueue_add_buf(sq->vq, sq->sg, hdr->num_sg, +	num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len) + 1; +	return virtqueue_add_buf(sq->vq, sq->sg, num_sg,  				 0, skb, GFP_ATOMIC);  } @@ -714,28 +712,20 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)  	struct virtnet_info *vi = netdev_priv(dev);  	int qnum = skb_get_queue_mapping(skb);  	struct send_queue *sq = &vi->sq[qnum]; -	int capacity; +	int err;  	/* Free up any pending old buffers before queueing new ones. */  	free_old_xmit_skbs(sq);  	/* Try to transmit */ -	capacity = xmit_skb(sq, skb); +	err = xmit_skb(sq, skb); -	/* This can happen with OOM and indirect buffers. */ -	if (unlikely(capacity < 0)) { -		if (likely(capacity == -ENOMEM)) { -			if (net_ratelimit()) -				dev_warn(&dev->dev, -					 "TXQ (%d) failure: out of memory\n", -					 qnum); -		} else { -			dev->stats.tx_fifo_errors++; -			if (net_ratelimit()) -				dev_warn(&dev->dev, -					 "Unexpected TXQ (%d) failure: %d\n", -					 qnum, capacity); -		} +	/* This should not happen! */ +	if (unlikely(err)) { +		dev->stats.tx_fifo_errors++; +		if (net_ratelimit()) +			dev_warn(&dev->dev, +				 "Unexpected TXQ (%d) queue failure: %d\n", qnum, err);  		dev->stats.tx_dropped++;  		kfree_skb(skb);  		return NETDEV_TX_OK; @@ -748,12 +738,12 @@ static netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev)  	/* Apparently nice girls don't return TX_BUSY; stop the queue  	 * before it gets out of hand.  Naturally, this wastes entries. */ -	if (capacity < 2+MAX_SKB_FRAGS) { +	if (sq->vq->num_free < 2+MAX_SKB_FRAGS) {  		netif_stop_subqueue(dev, qnum);  		if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) {  			/* More just got used, free them then recheck. */ -			capacity += free_old_xmit_skbs(sq); -			if (capacity >= 2+MAX_SKB_FRAGS) { +			free_old_xmit_skbs(sq); +			if (sq->vq->num_free >= 2+MAX_SKB_FRAGS) {  				netif_start_subqueue(dev, qnum);  				virtqueue_disable_cb(sq->vq);  			} diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 3b3fdf648ea..656230e0d18 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -505,7 +505,8 @@ static int vxlan_join_group(struct net_device *dev)  	struct vxlan_net *vn = net_generic(dev_net(dev), vxlan_net_id);  	struct sock *sk = vn->sock->sk;  	struct ip_mreqn mreq = { -		.imr_multiaddr.s_addr = vxlan->gaddr, +		.imr_multiaddr.s_addr	= vxlan->gaddr, +		.imr_ifindex		= vxlan->link,  	};  	int err; @@ -532,7 +533,8 @@ static int vxlan_leave_group(struct net_device *dev)  	int err = 0;  	struct sock *sk = vn->sock->sk;  	struct ip_mreqn mreq = { -		.imr_multiaddr.s_addr = vxlan->gaddr, +		.imr_multiaddr.s_addr	= vxlan->gaddr, +		.imr_ifindex		= vxlan->link,  	};  	/* Only leave group when last vxlan is done. */ @@ -1189,6 +1191,7 @@ static void vxlan_setup(struct net_device *dev)  	dev->hw_features |= NETIF_F_SG | NETIF_F_HW_CSUM | NETIF_F_RXCSUM;  	dev->priv_flags	&= ~IFF_XMIT_DST_RELEASE; +	dev->priv_flags |= IFF_LIVE_ADDR_CHANGE;  	spin_lock_init(&vxlan->hash_lock); diff --git a/drivers/net/wimax/i2400m/i2400m-usb.h b/drivers/net/wimax/i2400m/i2400m-usb.h index 6650fde99e1..9f1e947f355 100644 --- a/drivers/net/wimax/i2400m/i2400m-usb.h +++ b/drivers/net/wimax/i2400m/i2400m-usb.h @@ -152,6 +152,9 @@ enum {  	/* Device IDs */  	USB_DEVICE_ID_I6050 = 0x0186,  	USB_DEVICE_ID_I6050_2 = 0x0188, +	USB_DEVICE_ID_I6150 = 0x07d6, +	USB_DEVICE_ID_I6150_2 = 0x07d7, +	USB_DEVICE_ID_I6150_3 = 0x07d9,  	USB_DEVICE_ID_I6250 = 0x0187,  }; diff --git a/drivers/net/wimax/i2400m/usb.c b/drivers/net/wimax/i2400m/usb.c index 713d033891e..080f36303a4 100644 --- a/drivers/net/wimax/i2400m/usb.c +++ b/drivers/net/wimax/i2400m/usb.c @@ -510,6 +510,9 @@ int i2400mu_probe(struct usb_interface *iface,  	switch (id->idProduct) {  	case USB_DEVICE_ID_I6050:  	case USB_DEVICE_ID_I6050_2: +	case USB_DEVICE_ID_I6150: +	case USB_DEVICE_ID_I6150_2: +	case USB_DEVICE_ID_I6150_3:  	case USB_DEVICE_ID_I6250:  		i2400mu->i6050 = 1;  		break; @@ -759,6 +762,9 @@ static  struct usb_device_id i2400mu_id_table[] = {  	{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6050) },  	{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6050_2) }, +	{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150) }, +	{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_2) }, +	{ USB_DEVICE(0x8087, USB_DEVICE_ID_I6150_3) },  	{ USB_DEVICE(0x8086, USB_DEVICE_ID_I6250) },  	{ USB_DEVICE(0x8086, 0x0181) },  	{ USB_DEVICE(0x8086, 0x1403) }, diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index 062dfdff636..67156efe14c 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -47,7 +47,7 @@ obj-$(CONFIG_RT2X00)	+= rt2x00/  obj-$(CONFIG_P54_COMMON)	+= p54/ -obj-$(CONFIG_ATH_COMMON)	+= ath/ +obj-$(CONFIG_ATH_CARDS)		+= ath/  obj-$(CONFIG_MAC80211_HWSIM)	+= mac80211_hwsim.o diff --git a/drivers/net/wireless/ath/Kconfig b/drivers/net/wireless/ath/Kconfig index 1a67a4f829f..2c02b4e8409 100644 --- a/drivers/net/wireless/ath/Kconfig +++ b/drivers/net/wireless/ath/Kconfig @@ -30,5 +30,6 @@ source "drivers/net/wireless/ath/ath9k/Kconfig"  source "drivers/net/wireless/ath/carl9170/Kconfig"  source "drivers/net/wireless/ath/ath6kl/Kconfig"  source "drivers/net/wireless/ath/ar5523/Kconfig" +source "drivers/net/wireless/ath/wil6210/Kconfig"  endif diff --git a/drivers/net/wireless/ath/Makefile b/drivers/net/wireless/ath/Makefile index 1e18621326d..97b964ded2b 100644 --- a/drivers/net/wireless/ath/Makefile +++ b/drivers/net/wireless/ath/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_ATH9K_HW)		+= ath9k/  obj-$(CONFIG_CARL9170)		+= carl9170/  obj-$(CONFIG_ATH6KL)		+= ath6kl/  obj-$(CONFIG_AR5523)		+= ar5523/ +obj-$(CONFIG_WIL6210)		+= wil6210/  obj-$(CONFIG_ATH_COMMON)	+= ath.o diff --git a/drivers/net/wireless/ath/ath9k/Kconfig b/drivers/net/wireless/ath/ath9k/Kconfig index 5fc15bf8be0..7647ed6b73d 100644 --- a/drivers/net/wireless/ath/ath9k/Kconfig +++ b/drivers/net/wireless/ath/ath9k/Kconfig @@ -2,6 +2,7 @@ config ATH9K_HW  	tristate  config ATH9K_COMMON  	tristate +	select ATH_COMMON  config ATH9K_DFS_DEBUGFS  	def_bool y  	depends on ATH9K_DEBUGFS && ATH9K_DFS_CERTIFIED @@ -17,7 +18,6 @@ config ATH9K_BTCOEX_SUPPORT  config ATH9K  	tristate "Atheros 802.11n wireless cards support"  	depends on MAC80211 -	select ATH_COMMON  	select ATH9K_HW  	select MAC80211_LEDS  	select LEDS_CLASS @@ -56,7 +56,8 @@ config ATH9K_AHB  config ATH9K_DEBUGFS  	bool "Atheros ath9k debugging" -	depends on ATH9K && DEBUG_FS +	depends on ATH9K +	select MAC80211_DEBUGFS  	---help---  	  Say Y, if you need access to ath9k's statistics for  	  interrupts, rate control, etc. diff --git a/drivers/net/wireless/ath/ath9k/ar9003_hw.c b/drivers/net/wireless/ath/ath9k/ar9003_hw.c index 74fd3977fee..59bf5f31e21 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_hw.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_hw.c @@ -544,7 +544,7 @@ static void ar9003_rx_gain_table_mode0(struct ath_hw *ah)  				ar9340Common_rx_gain_table_1p0);  	else if (AR_SREV_9485_11(ah))  		INIT_INI_ARRAY(&ah->iniModesRxGain, -				ar9485Common_wo_xlna_rx_gain_1_1); +			       ar9485_common_rx_gain_1_1);  	else if (AR_SREV_9550(ah)) {  		INIT_INI_ARRAY(&ah->iniModesRxGain,  				ar955x_1p0_common_rx_gain_table); diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index aaebecd19e5..63fd9af3fd3 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -336,8 +336,12 @@ static int carl9170_fw(struct ar9170 *ar, const __u8 *data, size_t len)  		if (SUPP(CARL9170FW_WLANTX_CAB)) {  			if_comb_types |=  				BIT(NL80211_IFTYPE_AP) | -				BIT(NL80211_IFTYPE_MESH_POINT) |  				BIT(NL80211_IFTYPE_P2P_GO); + +#ifdef CONFIG_MAC80211_MESH +			if_comb_types |= +				BIT(NL80211_IFTYPE_MESH_POINT); +#endif /* CONFIG_MAC80211_MESH */  		}  	} diff --git a/drivers/net/wireless/ath/wil6210/Kconfig b/drivers/net/wireless/ath/wil6210/Kconfig new file mode 100644 index 00000000000..bac3d98a0cf --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Kconfig @@ -0,0 +1,29 @@ +config WIL6210 +	tristate "Wilocity 60g WiFi card wil6210 support" +	depends on CFG80211 +	depends on PCI +	default n +	---help--- +	  This module adds support for wireless adapter based on +	  wil6210 chip by Wilocity. It supports operation on the +	  60 GHz band, covered by the IEEE802.11ad standard. + +	  http://wireless.kernel.org/en/users/Drivers/wil6210 + +	  If you choose to build it as a module, it will be called +	  wil6210 + +config WIL6210_ISR_COR +	bool "Use Clear-On-Read mode for ISR registers for wil6210" +	depends on WIL6210 +	default y +	---help--- +	  ISR registers on wil6210 chip may operate in either +	  COR (Clear-On-Read) or W1C (Write-1-to-Clear) mode. +	  For production code, use COR (say y); is default since +	  it saves extra target transaction; +	  For ISR debug, use W1C (say n); is allows to monitor ISR +	  registers with debugfs. If COR were used, ISR would +	  self-clear when accessed for debug purposes, it makes +	  such monitoring impossible. +	  Say y unless you debug interrupts diff --git a/drivers/net/wireless/ath/wil6210/Makefile b/drivers/net/wireless/ath/wil6210/Makefile new file mode 100644 index 00000000000..9396dc9fe3c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_WIL6210) += wil6210.o + +wil6210-objs := main.o +wil6210-objs += netdev.o +wil6210-objs += cfg80211.o +wil6210-objs += pcie_bus.o +wil6210-objs += debugfs.o +wil6210-objs += wmi.o +wil6210-objs += interrupt.o +wil6210-objs += txrx.o + +subdir-ccflags-y += -Werror +subdir-ccflags-y += -D__CHECK_ENDIAN__ diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c new file mode 100644 index 00000000000..116f4e807ae --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/etherdevice.h> +#include <linux/wireless.h> +#include <linux/ieee80211.h> +#include <linux/slab.h> +#include <linux/version.h> +#include <net/cfg80211.h> + +#include "wil6210.h" +#include "wmi.h" + +#define CHAN60G(_channel, _flags) {				\ +	.band			= IEEE80211_BAND_60GHZ,		\ +	.center_freq		= 56160 + (2160 * (_channel)),	\ +	.hw_value		= (_channel),			\ +	.flags			= (_flags),			\ +	.max_antenna_gain	= 0,				\ +	.max_power		= 40,				\ +} + +static struct ieee80211_channel wil_60ghz_channels[] = { +	CHAN60G(1, 0), +	CHAN60G(2, 0), +	CHAN60G(3, 0), +/* channel 4 not supported yet */ +}; + +static struct ieee80211_supported_band wil_band_60ghz = { +	.channels = wil_60ghz_channels, +	.n_channels = ARRAY_SIZE(wil_60ghz_channels), +	.ht_cap = { +		.ht_supported = true, +		.cap = 0, /* TODO */ +		.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K, /* TODO */ +		.ampdu_density = IEEE80211_HT_MPDU_DENSITY_8, /* TODO */ +		.mcs = { +				/* MCS 1..12 - SC PHY */ +			.rx_mask = {0xfe, 0x1f}, /* 1..12 */ +			.tx_params = IEEE80211_HT_MCS_TX_DEFINED, /* TODO */ +		}, +	}, +}; + +static const struct ieee80211_txrx_stypes +wil_mgmt_stypes[NUM_NL80211_IFTYPES] = { +	[NL80211_IFTYPE_STATION] = { +		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_RESP >> 4), +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +	}, +	[NL80211_IFTYPE_AP] = { +		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_RESP >> 4), +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +	}, +	[NL80211_IFTYPE_P2P_CLIENT] = { +		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_RESP >> 4), +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +	}, +	[NL80211_IFTYPE_P2P_GO] = { +		.tx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_RESP >> 4), +		.rx = BIT(IEEE80211_STYPE_ACTION >> 4) | +		BIT(IEEE80211_STYPE_PROBE_REQ >> 4) +	}, +}; + +static const u32 wil_cipher_suites[] = { +	WLAN_CIPHER_SUITE_GCMP, +}; + +int wil_iftype_nl2wmi(enum nl80211_iftype type) +{ +	static const struct { +		enum nl80211_iftype nl; +		enum wmi_network_type wmi; +	} __nl2wmi[] = { +		{NL80211_IFTYPE_ADHOC,		WMI_NETTYPE_ADHOC}, +		{NL80211_IFTYPE_STATION,	WMI_NETTYPE_INFRA}, +		{NL80211_IFTYPE_AP,		WMI_NETTYPE_AP}, +		{NL80211_IFTYPE_P2P_CLIENT,	WMI_NETTYPE_P2P}, +		{NL80211_IFTYPE_P2P_GO,		WMI_NETTYPE_P2P}, +		{NL80211_IFTYPE_MONITOR,	WMI_NETTYPE_ADHOC}, /* FIXME */ +	}; +	uint i; + +	for (i = 0; i < ARRAY_SIZE(__nl2wmi); i++) { +		if (__nl2wmi[i].nl == type) +			return __nl2wmi[i].wmi; +	} + +	return -EOPNOTSUPP; +} + +static int wil_cfg80211_get_station(struct wiphy *wiphy, +				    struct net_device *ndev, +				    u8 *mac, struct station_info *sinfo) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	int rc; +	struct wmi_notify_req_cmd cmd = { +		.cid = 0, +		.interval_usec = 0, +	}; + +	if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) +		return -ENOENT; + +	/* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ +	rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), +		      WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); +	if (rc) +		return rc; + +	sinfo->generation = wil->sinfo_gen; + +	sinfo->filled |= STATION_INFO_TX_BITRATE; +	sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; +	sinfo->txrate.mcs = wil->stats.bf_mcs; +	sinfo->filled |= STATION_INFO_RX_BITRATE; +	sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; +	sinfo->rxrate.mcs = wil->stats.last_mcs_rx; + +	if (test_bit(wil_status_fwconnected, &wil->status)) { +		sinfo->filled |= STATION_INFO_SIGNAL; +		sinfo->signal = 12; /* TODO: provide real value */ +	} + +	return 0; +} + +static int wil_cfg80211_change_iface(struct wiphy *wiphy, +				     struct net_device *ndev, +				     enum nl80211_iftype type, u32 *flags, +				     struct vif_params *params) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct wireless_dev *wdev = wil->wdev; + +	switch (type) { +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_CLIENT: +	case NL80211_IFTYPE_P2P_GO: +		break; +	case NL80211_IFTYPE_MONITOR: +		if (flags) +			wil->monitor_flags = *flags; +		else +			wil->monitor_flags = 0; + +		break; +	default: +		return -EOPNOTSUPP; +	} + +	wdev->iftype = type; + +	return 0; +} + +static int wil_cfg80211_scan(struct wiphy *wiphy, +			     struct cfg80211_scan_request *request) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct wireless_dev *wdev = wil->wdev; +	struct { +		struct wmi_start_scan_cmd cmd; +		u16 chnl[4]; +	} __packed cmd; +	uint i, n; + +	if (wil->scan_request) { +		wil_err(wil, "Already scanning\n"); +		return -EAGAIN; +	} + +	/* check we are client side */ +	switch (wdev->iftype) { +	case NL80211_IFTYPE_STATION: +	case NL80211_IFTYPE_P2P_CLIENT: +		break; +	default: +		return -EOPNOTSUPP; + +	} + +	/* FW don't support scan after connection attempt */ +	if (test_bit(wil_status_dontscan, &wil->status)) { +		wil_err(wil, "Scan after connect attempt not supported\n"); +		return -EBUSY; +	} + +	wil->scan_request = request; + +	memset(&cmd, 0, sizeof(cmd)); +	cmd.cmd.num_channels = 0; +	n = min(request->n_channels, 4U); +	for (i = 0; i < n; i++) { +		int ch = request->channels[i]->hw_value; +		if (ch == 0) { +			wil_err(wil, +				"Scan requested for unknown frequency %dMhz\n", +				request->channels[i]->center_freq); +			continue; +		} +		/* 0-based channel indexes */ +		cmd.cmd.channel_list[cmd.cmd.num_channels++].channel = ch - 1; +		wil_dbg(wil, "Scan for ch %d  : %d MHz\n", ch, +			request->channels[i]->center_freq); +	} + +	return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + +			cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); +} + +static int wil_cfg80211_connect(struct wiphy *wiphy, +				struct net_device *ndev, +				struct cfg80211_connect_params *sme) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct cfg80211_bss *bss; +	struct wmi_connect_cmd conn; +	const u8 *ssid_eid; +	const u8 *rsn_eid; +	int ch; +	int rc = 0; + +	bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, +			       sme->ssid, sme->ssid_len, +			       WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); +	if (!bss) { +		wil_err(wil, "Unable to find BSS\n"); +		return -ENOENT; +	} + +	ssid_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SSID); +	if (!ssid_eid) { +		wil_err(wil, "No SSID\n"); +		rc = -ENOENT; +		goto out; +	} + +	rsn_eid = sme->ie ? +			cfg80211_find_ie(WLAN_EID_RSN, sme->ie, sme->ie_len) : +			NULL; +	if (rsn_eid) { +		if (sme->ie_len > WMI_MAX_IE_LEN) { +			rc = -ERANGE; +			wil_err(wil, "IE too large (%td bytes)\n", +				sme->ie_len); +			goto out; +		} +		/* +		 * For secure assoc, send: +		 * (1) WMI_DELETE_CIPHER_KEY_CMD +		 * (2) WMI_SET_APPIE_CMD +		 */ +		rc = wmi_del_cipher_key(wil, 0, bss->bssid); +		if (rc) { +			wil_err(wil, "WMI_DELETE_CIPHER_KEY_CMD failed\n"); +			goto out; +		} +		/* WMI_SET_APPIE_CMD */ +		rc = wmi_set_ie(wil, WMI_FRAME_ASSOC_REQ, sme->ie_len, sme->ie); +		if (rc) { +			wil_err(wil, "WMI_SET_APPIE_CMD failed\n"); +			goto out; +		} +	} + +	/* WMI_CONNECT_CMD */ +	memset(&conn, 0, sizeof(conn)); +	switch (bss->capability & 0x03) { +	case WLAN_CAPABILITY_DMG_TYPE_AP: +		conn.network_type = WMI_NETTYPE_INFRA; +		break; +	case WLAN_CAPABILITY_DMG_TYPE_PBSS: +		conn.network_type = WMI_NETTYPE_P2P; +		break; +	default: +		wil_err(wil, "Unsupported BSS type, capability= 0x%04x\n", +			bss->capability); +		goto out; +	} +	if (rsn_eid) { +		conn.dot11_auth_mode = WMI_AUTH11_SHARED; +		conn.auth_mode = WMI_AUTH_WPA2_PSK; +		conn.pairwise_crypto_type = WMI_CRYPT_AES_GCMP; +		conn.pairwise_crypto_len = 16; +	} else { +		conn.dot11_auth_mode = WMI_AUTH11_OPEN; +		conn.auth_mode = WMI_AUTH_NONE; +	} + +	conn.ssid_len = min_t(u8, ssid_eid[1], 32); +	memcpy(conn.ssid, ssid_eid+2, conn.ssid_len); + +	ch = bss->channel->hw_value; +	if (ch == 0) { +		wil_err(wil, "BSS at unknown frequency %dMhz\n", +			bss->channel->center_freq); +		rc = -EOPNOTSUPP; +		goto out; +	} +	conn.channel = ch - 1; + +	memcpy(conn.bssid, bss->bssid, 6); +	memcpy(conn.dst_mac, bss->bssid, 6); +	/* +	 * FW don't support scan after connection attempt +	 */ +	set_bit(wil_status_dontscan, &wil->status); + +	rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); +	if (rc == 0) { +		/* Connect can take lots of time */ +		mod_timer(&wil->connect_timer, +			  jiffies + msecs_to_jiffies(2000)); +	} + + out: +	cfg80211_put_bss(bss); + +	return rc; +} + +static int wil_cfg80211_disconnect(struct wiphy *wiphy, +				   struct net_device *ndev, +				   u16 reason_code) +{ +	int rc; +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); + +	rc = wmi_send(wil, WMI_DISCONNECT_CMDID, NULL, 0); + +	return rc; +} + +static int wil_cfg80211_set_channel(struct wiphy *wiphy, +				    struct cfg80211_chan_def *chandef) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct wireless_dev *wdev = wil->wdev; + +	wdev->preset_chandef = *chandef; + +	return 0; +} + +static int wil_cfg80211_add_key(struct wiphy *wiphy, +				struct net_device *ndev, +				u8 key_index, bool pairwise, +				const u8 *mac_addr, +				struct key_params *params) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); + +	/* group key is not used */ +	if (!pairwise) +		return 0; + +	return wmi_add_cipher_key(wil, key_index, mac_addr, +				  params->key_len, params->key); +} + +static int wil_cfg80211_del_key(struct wiphy *wiphy, +				struct net_device *ndev, +				u8 key_index, bool pairwise, +				const u8 *mac_addr) +{ +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); + +	/* group key is not used */ +	if (!pairwise) +		return 0; + +	return wmi_del_cipher_key(wil, key_index, mac_addr); +} + +/* Need to be present or wiphy_new() will WARN */ +static int wil_cfg80211_set_default_key(struct wiphy *wiphy, +					struct net_device *ndev, +					u8 key_index, bool unicast, +					bool multicast) +{ +	return 0; +} + +static int wil_cfg80211_start_ap(struct wiphy *wiphy, +				 struct net_device *ndev, +				 struct cfg80211_ap_settings *info) +{ +	int rc = 0; +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct wireless_dev *wdev = ndev->ieee80211_ptr; +	struct ieee80211_channel *channel = info->chandef.chan; +	struct cfg80211_beacon_data *bcon = &info->beacon; +	u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + +	if (!channel) { +		wil_err(wil, "AP: No channel???\n"); +		return -EINVAL; +	} + +	wil_dbg(wil, "AP on Channel %d %d MHz, %s\n", channel->hw_value, +		channel->center_freq, info->privacy ? "secure" : "open"); +	print_hex_dump_bytes("SSID ", DUMP_PREFIX_OFFSET, +			     info->ssid, info->ssid_len); + +	rc = wil_reset(wil); +	if (rc) +		return rc; + +	rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); +	if (rc) +		return rc; + +	rc = wmi_set_channel(wil, channel->hw_value); +	if (rc) +		return rc; + +	/* MAC address - pre-requisite for other commands */ +	wmi_set_mac_address(wil, ndev->dev_addr); + +	/* IE's */ +	/* bcon 'head IE's are not relevant for 60g band */ +	wmi_set_ie(wil, WMI_FRAME_BEACON, bcon->beacon_ies_len, +		   bcon->beacon_ies); +	wmi_set_ie(wil, WMI_FRAME_PROBE_RESP, bcon->proberesp_ies_len, +		   bcon->proberesp_ies); +	wmi_set_ie(wil, WMI_FRAME_ASSOC_RESP, bcon->assocresp_ies_len, +		   bcon->assocresp_ies); + +	wil->secure_pcp = info->privacy; + +	rc = wmi_set_bcon(wil, info->beacon_interval, wmi_nettype); +	if (rc) +		return rc; + +	/* Rx VRING. After MAC and beacon */ +	rc = wil_rx_init(wil); + +	netif_carrier_on(ndev); + +	return rc; +} + +static int wil_cfg80211_stop_ap(struct wiphy *wiphy, +				struct net_device *ndev) +{ +	int rc = 0; +	struct wil6210_priv *wil = wiphy_to_wil(wiphy); +	struct wireless_dev *wdev = ndev->ieee80211_ptr; +	u8 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + +	/* To stop beaconing, set BI to 0 */ +	rc = wmi_set_bcon(wil, 0, wmi_nettype); + +	return rc; +} + +static struct cfg80211_ops wil_cfg80211_ops = { +	.scan = wil_cfg80211_scan, +	.connect = wil_cfg80211_connect, +	.disconnect = wil_cfg80211_disconnect, +	.change_virtual_intf = wil_cfg80211_change_iface, +	.get_station = wil_cfg80211_get_station, +	.set_monitor_channel = wil_cfg80211_set_channel, +	.add_key = wil_cfg80211_add_key, +	.del_key = wil_cfg80211_del_key, +	.set_default_key = wil_cfg80211_set_default_key, +	/* AP mode */ +	.start_ap = wil_cfg80211_start_ap, +	.stop_ap = wil_cfg80211_stop_ap, +}; + +static void wil_wiphy_init(struct wiphy *wiphy) +{ +	/* TODO: set real value */ +	wiphy->max_scan_ssids = 10; +	wiphy->max_num_pmkids = 0 /* TODO: */; +	wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | +				 BIT(NL80211_IFTYPE_AP) | +				 BIT(NL80211_IFTYPE_MONITOR); +	/* TODO: enable P2P when integrated with supplicant: +	 * BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) +	 */ +	wiphy->flags |= WIPHY_FLAG_HAVE_AP_SME | +			WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; +	dev_warn(wiphy_dev(wiphy), "%s : flags = 0x%08x\n", +		 __func__, wiphy->flags); +	wiphy->probe_resp_offload = +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS | +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2 | +		NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P; + +	wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; + +	/* TODO: figure this out */ +	wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + +	wiphy->cipher_suites = wil_cipher_suites; +	wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); +	wiphy->mgmt_stypes = wil_mgmt_stypes; +} + +struct wireless_dev *wil_cfg80211_init(struct device *dev) +{ +	int rc = 0; +	struct wireless_dev *wdev; + +	wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL); +	if (!wdev) +		return ERR_PTR(-ENOMEM); + +	wdev->wiphy = wiphy_new(&wil_cfg80211_ops, +				sizeof(struct wil6210_priv)); +	if (!wdev->wiphy) { +		rc = -ENOMEM; +		goto out; +	} + +	set_wiphy_dev(wdev->wiphy, dev); +	wil_wiphy_init(wdev->wiphy); + +	rc = wiphy_register(wdev->wiphy); +	if (rc < 0) +		goto out_failed_reg; + +	return wdev; + +out_failed_reg: +	wiphy_free(wdev->wiphy); +out: +	kfree(wdev); + +	return ERR_PTR(rc); +} + +void wil_wdev_free(struct wil6210_priv *wil) +{ +	struct wireless_dev *wdev = wil_to_wdev(wil); + +	if (!wdev) +		return; + +	wiphy_unregister(wdev->wiphy); +	wiphy_free(wdev->wiphy); +	kfree(wdev); +} diff --git a/drivers/net/wireless/ath/wil6210/dbg_hexdump.h b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h new file mode 100644 index 00000000000..6a315ba5aa7 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/dbg_hexdump.h @@ -0,0 +1,30 @@ +#ifndef WIL_DBG_HEXDUMP_H_ +#define WIL_DBG_HEXDUMP_H_ + +#if defined(CONFIG_DYNAMIC_DEBUG) +#define wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize,	\ +			     groupsize, buf, len, ascii)	\ +do {								\ +	DEFINE_DYNAMIC_DEBUG_METADATA(descriptor,		\ +		__builtin_constant_p(prefix_str) ? prefix_str : "hexdump");\ +	if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT))	\ +		print_hex_dump(KERN_DEBUG, prefix_str,		\ +			       prefix_type, rowsize, groupsize,	\ +			       buf, len, ascii);		\ +} while (0) + +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,	\ +				 groupsize, buf, len, ascii)		\ +	wil_dynamic_hex_dump(prefix_str, prefix_type, rowsize,		\ +			     groupsize, buf, len, ascii) + +#define print_hex_dump_bytes(prefix_str, prefix_type, buf, len)	\ +	wil_dynamic_hex_dump(prefix_str, prefix_type, 16, 1, buf, len, true) +#else /* defined(CONFIG_DYNAMIC_DEBUG) */ +#define wil_print_hex_dump_debug(prefix_str, prefix_type, rowsize,	\ +				 groupsize, buf, len, ascii)		\ +	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, rowsize,	\ +		       groupsize, buf, len, ascii) +#endif /* defined(CONFIG_DYNAMIC_DEBUG) */ + +#endif /* WIL_DBG_HEXDUMP_H_ */ diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c new file mode 100644 index 00000000000..65fc9683bfd --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/pci.h> +#include <linux/rtnetlink.h> + +#include "wil6210.h" +#include "txrx.h" + +/* Nasty hack. Better have per device instances */ +static u32 mem_addr; +static u32 dbg_txdesc_index; + +static void wil_print_vring(struct seq_file *s, struct wil6210_priv *wil, +			    const char *name, struct vring *vring) +{ +	void __iomem *x = wmi_addr(wil, vring->hwtail); + +	seq_printf(s, "VRING %s = {\n", name); +	seq_printf(s, "  pa     = 0x%016llx\n", (unsigned long long)vring->pa); +	seq_printf(s, "  va     = 0x%p\n", vring->va); +	seq_printf(s, "  size   = %d\n", vring->size); +	seq_printf(s, "  swtail = %d\n", vring->swtail); +	seq_printf(s, "  swhead = %d\n", vring->swhead); +	seq_printf(s, "  hwtail = [0x%08x] -> ", vring->hwtail); +	if (x) +		seq_printf(s, "0x%08x\n", ioread32(x)); +	else +		seq_printf(s, "???\n"); + +	if (vring->va && (vring->size < 1025)) { +		uint i; +		for (i = 0; i < vring->size; i++) { +			volatile struct vring_tx_desc *d = &vring->va[i].tx; +			if ((i % 64) == 0 && (i != 0)) +				seq_printf(s, "\n"); +			seq_printf(s, "%s", (d->dma.status & BIT(0)) ? +					"S" : (vring->ctx[i] ? "H" : "h")); +		} +		seq_printf(s, "\n"); +	} +	seq_printf(s, "}\n"); +} + +static int wil_vring_debugfs_show(struct seq_file *s, void *data) +{ +	uint i; +	struct wil6210_priv *wil = s->private; + +	wil_print_vring(s, wil, "rx", &wil->vring_rx); + +	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { +		struct vring *vring = &(wil->vring_tx[i]); +		if (vring->va) { +			char name[10]; +			snprintf(name, sizeof(name), "tx_%2d", i); +			wil_print_vring(s, wil, name, vring); +		} +	} + +	return 0; +} + +static int wil_vring_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_vring_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_vring = { +	.open		= wil_vring_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; + +static void wil_print_ring(struct seq_file *s, const char *prefix, +			   void __iomem *off) +{ +	struct wil6210_priv *wil = s->private; +	struct wil6210_mbox_ring r; +	int rsize; +	uint i; + +	wil_memcpy_fromio_32(&r, off, sizeof(r)); +	wil_mbox_ring_le2cpus(&r); +	/* +	 * we just read memory block from NIC. This memory may be +	 * garbage. Check validity before using it. +	 */ +	rsize = r.size / sizeof(struct wil6210_mbox_ring_desc); + +	seq_printf(s, "ring %s = {\n", prefix); +	seq_printf(s, "  base = 0x%08x\n", r.base); +	seq_printf(s, "  size = 0x%04x bytes -> %d entries\n", r.size, rsize); +	seq_printf(s, "  tail = 0x%08x\n", r.tail); +	seq_printf(s, "  head = 0x%08x\n", r.head); +	seq_printf(s, "  entry size = %d\n", r.entry_size); + +	if (r.size % sizeof(struct wil6210_mbox_ring_desc)) { +		seq_printf(s, "  ??? size is not multiple of %zd, garbage?\n", +			   sizeof(struct wil6210_mbox_ring_desc)); +		goto out; +	} + +	if (!wmi_addr(wil, r.base) || +	    !wmi_addr(wil, r.tail) || +	    !wmi_addr(wil, r.head)) { +		seq_printf(s, "  ??? pointers are garbage?\n"); +		goto out; +	} + +	for (i = 0; i < rsize; i++) { +		struct wil6210_mbox_ring_desc d; +		struct wil6210_mbox_hdr hdr; +		size_t delta = i * sizeof(d); +		void __iomem *x = wil->csr + HOSTADDR(r.base) + delta; + +		wil_memcpy_fromio_32(&d, x, sizeof(d)); + +		seq_printf(s, "  [%2x] %s %s%s 0x%08x", i, +			   d.sync ? "F" : "E", +			   (r.tail - r.base == delta) ? "t" : " ", +			   (r.head - r.base == delta) ? "h" : " ", +			   le32_to_cpu(d.addr)); +		if (0 == wmi_read_hdr(wil, d.addr, &hdr)) { +			u16 len = le16_to_cpu(hdr.len); +			seq_printf(s, " -> %04x %04x %04x %02x\n", +				   le16_to_cpu(hdr.seq), len, +				   le16_to_cpu(hdr.type), hdr.flags); +			if (len <= MAX_MBOXITEM_SIZE) { +				int n = 0; +				unsigned char printbuf[16 * 3 + 2]; +				unsigned char databuf[MAX_MBOXITEM_SIZE]; +				void __iomem *src = wmi_buffer(wil, d.addr) + +					sizeof(struct wil6210_mbox_hdr); +				/* +				 * No need to check @src for validity - +				 * we already validated @d.addr while +				 * reading header +				 */ +				wil_memcpy_fromio_32(databuf, src, len); +				while (n < len) { +					int l = min(len - n, 16); +					hex_dump_to_buffer(databuf + n, l, +							   16, 1, printbuf, +							   sizeof(printbuf), +							   false); +					seq_printf(s, "      : %s\n", printbuf); +					n += l; +				} +			} +		} else { +			seq_printf(s, "\n"); +		} +	} + out: +	seq_printf(s, "}\n"); +} + +static int wil_mbox_debugfs_show(struct seq_file *s, void *data) +{ +	struct wil6210_priv *wil = s->private; + +	wil_print_ring(s, "tx", wil->csr + HOST_MBOX + +		       offsetof(struct wil6210_mbox_ctl, tx)); +	wil_print_ring(s, "rx", wil->csr + HOST_MBOX + +		       offsetof(struct wil6210_mbox_ctl, rx)); + +	return 0; +} + +static int wil_mbox_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_mbox_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_mbox = { +	.open		= wil_mbox_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; + +static int wil_debugfs_iomem_x32_set(void *data, u64 val) +{ +	iowrite32(val, (void __iomem *)data); +	wmb(); /* make sure write propagated to HW */ + +	return 0; +} + +static int wil_debugfs_iomem_x32_get(void *data, u64 *val) +{ +	*val = ioread32((void __iomem *)data); + +	return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, wil_debugfs_iomem_x32_get, +			wil_debugfs_iomem_x32_set, "0x%08llx\n"); + +static struct dentry *wil_debugfs_create_iomem_x32(const char *name, +						   mode_t mode, +						   struct dentry *parent, +						   void __iomem *value) +{ +	return debugfs_create_file(name, mode, parent, (void * __force)value, +				   &fops_iomem_x32); +} + +static int wil6210_debugfs_create_ISR(struct wil6210_priv *wil, +				      const char *name, +				      struct dentry *parent, u32 off) +{ +	struct dentry *d = debugfs_create_dir(name, parent); + +	if (IS_ERR_OR_NULL(d)) +		return -ENODEV; + +	wil_debugfs_create_iomem_x32("ICC", S_IRUGO | S_IWUSR, d, +				     wil->csr + off); +	wil_debugfs_create_iomem_x32("ICR", S_IRUGO | S_IWUSR, d, +				     wil->csr + off + 4); +	wil_debugfs_create_iomem_x32("ICM", S_IRUGO | S_IWUSR, d, +				     wil->csr + off + 8); +	wil_debugfs_create_iomem_x32("ICS", S_IWUSR, d, +				     wil->csr + off + 12); +	wil_debugfs_create_iomem_x32("IMV", S_IRUGO | S_IWUSR, d, +				     wil->csr + off + 16); +	wil_debugfs_create_iomem_x32("IMS", S_IWUSR, d, +				     wil->csr + off + 20); +	wil_debugfs_create_iomem_x32("IMC", S_IWUSR, d, +				     wil->csr + off + 24); + +	return 0; +} + +static int wil6210_debugfs_create_pseudo_ISR(struct wil6210_priv *wil, +					     struct dentry *parent) +{ +	struct dentry *d = debugfs_create_dir("PSEUDO_ISR", parent); + +	if (IS_ERR_OR_NULL(d)) +		return -ENODEV; + +	wil_debugfs_create_iomem_x32("CAUSE", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); +	wil_debugfs_create_iomem_x32("MASK_SW", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +	wil_debugfs_create_iomem_x32("MASK_FW", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_FW)); + +	return 0; +} + +static int wil6210_debugfs_create_ITR_CNT(struct wil6210_priv *wil, +					  struct dentry *parent) +{ +	struct dentry *d = debugfs_create_dir("ITR_CNT", parent); + +	if (IS_ERR_OR_NULL(d)) +		return -ENODEV; + +	wil_debugfs_create_iomem_x32("TRSH", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_ITR_CNT_TRSH)); +	wil_debugfs_create_iomem_x32("DATA", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_ITR_CNT_DATA)); +	wil_debugfs_create_iomem_x32("CTL", S_IRUGO, d, wil->csr + +				     HOSTADDR(RGF_DMA_ITR_CNT_CRL)); + +	return 0; +} + +static int wil_memread_debugfs_show(struct seq_file *s, void *data) +{ +	struct wil6210_priv *wil = s->private; +	void __iomem *a = wmi_buffer(wil, cpu_to_le32(mem_addr)); + +	if (a) +		seq_printf(s, "[0x%08x] = 0x%08x\n", mem_addr, ioread32(a)); +	else +		seq_printf(s, "[0x%08x] = INVALID\n", mem_addr); + +	return 0; +} + +static int wil_memread_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_memread_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_memread = { +	.open		= wil_memread_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; + +static int wil_default_open(struct inode *inode, struct file *file) +{ +	if (inode->i_private) +		file->private_data = inode->i_private; + +	return 0; +} + +static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf, +				size_t count, loff_t *ppos) +{ +	enum { max_count = 4096 }; +	struct debugfs_blob_wrapper *blob = file->private_data; +	loff_t pos = *ppos; +	size_t available = blob->size; +	void *buf; +	size_t ret; + +	if (pos < 0) +		return -EINVAL; + +	if (pos >= available || !count) +		return 0; + +	if (count > available - pos) +		count = available - pos; +	if (count > max_count) +		count = max_count; + +	buf = kmalloc(count, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	wil_memcpy_fromio_32(buf, (const volatile void __iomem *)blob->data + +			     pos, count); + +	ret = copy_to_user(user_buf, buf, count); +	kfree(buf); +	if (ret == count) +		return -EFAULT; + +	count -= ret; +	*ppos = pos + count; + +	return count; +} + +static const struct file_operations fops_ioblob = { +	.read =		wil_read_file_ioblob, +	.open =		wil_default_open, +	.llseek =	default_llseek, +}; + +static +struct dentry *wil_debugfs_create_ioblob(const char *name, +					 mode_t mode, +					 struct dentry *parent, +					 struct debugfs_blob_wrapper *blob) +{ +	return debugfs_create_file(name, mode, parent, blob, &fops_ioblob); +} +/*---reset---*/ +static ssize_t wil_write_file_reset(struct file *file, const char __user *buf, +				    size_t len, loff_t *ppos) +{ +	struct wil6210_priv *wil = file->private_data; +	struct net_device *ndev = wil_to_ndev(wil); + +	/** +	 * BUG: +	 * this code does NOT sync device state with the rest of system +	 * use with care, debug only!!! +	 */ +	rtnl_lock(); +	dev_close(ndev); +	ndev->flags &= ~IFF_UP; +	rtnl_unlock(); +	wil_reset(wil); + +	return len; +} + +static const struct file_operations fops_reset = { +	.write = wil_write_file_reset, +	.open  = wil_default_open, +}; +/*---------Tx descriptor------------*/ + +static int wil_txdesc_debugfs_show(struct seq_file *s, void *data) +{ +	struct wil6210_priv *wil = s->private; +	struct vring *vring = &(wil->vring_tx[0]); + +	if (!vring->va) { +		seq_printf(s, "No Tx VRING\n"); +		return 0; +	} + +	if (dbg_txdesc_index < vring->size) { +		volatile struct vring_tx_desc *d = +				&(vring->va[dbg_txdesc_index].tx); +		volatile u32 *u = (volatile u32 *)d; +		struct sk_buff *skb = vring->ctx[dbg_txdesc_index]; + +		seq_printf(s, "Tx[%3d] = {\n", dbg_txdesc_index); +		seq_printf(s, "  MAC = 0x%08x 0x%08x 0x%08x 0x%08x\n", +			   u[0], u[1], u[2], u[3]); +		seq_printf(s, "  DMA = 0x%08x 0x%08x 0x%08x 0x%08x\n", +			   u[4], u[5], u[6], u[7]); +		seq_printf(s, "  SKB = %p\n", skb); + +		if (skb) { +			unsigned char printbuf[16 * 3 + 2]; +			int i = 0; +			int len = skb_headlen(skb); +			void *p = skb->data; + +			seq_printf(s, "    len = %d\n", len); + +			while (i < len) { +				int l = min(len - i, 16); +				hex_dump_to_buffer(p + i, l, 16, 1, printbuf, +						   sizeof(printbuf), false); +				seq_printf(s, "      : %s\n", printbuf); +				i += l; +			} +		} +		seq_printf(s, "}\n"); +	} else { +		seq_printf(s, "TxDesc index (%d) >= size (%d)\n", +			   dbg_txdesc_index, vring->size); +	} + +	return 0; +} + +static int wil_txdesc_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_txdesc_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_txdesc = { +	.open		= wil_txdesc_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; + +/*---------beamforming------------*/ +static int wil_bf_debugfs_show(struct seq_file *s, void *data) +{ +	struct wil6210_priv *wil = s->private; +	seq_printf(s, +		   "TSF : 0x%016llx\n" +		   "TxMCS : %d\n" +		   "Sectors(rx:tx) my %2d:%2d peer %2d:%2d\n", +		   wil->stats.tsf, wil->stats.bf_mcs, +		   wil->stats.my_rx_sector, wil->stats.my_tx_sector, +		   wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +	return 0; +} + +static int wil_bf_seq_open(struct inode *inode, struct file *file) +{ +	return single_open(file, wil_bf_debugfs_show, inode->i_private); +} + +static const struct file_operations fops_bf = { +	.open		= wil_bf_seq_open, +	.release	= single_release, +	.read		= seq_read, +	.llseek		= seq_lseek, +}; +/*---------SSID------------*/ +static ssize_t wil_read_file_ssid(struct file *file, char __user *user_buf, +				  size_t count, loff_t *ppos) +{ +	struct wil6210_priv *wil = file->private_data; +	struct wireless_dev *wdev = wil_to_wdev(wil); + +	return simple_read_from_buffer(user_buf, count, ppos, +				       wdev->ssid, wdev->ssid_len); +} + +static ssize_t wil_write_file_ssid(struct file *file, const char __user *buf, +				   size_t count, loff_t *ppos) +{ +	struct wil6210_priv *wil = file->private_data; +	struct wireless_dev *wdev = wil_to_wdev(wil); +	struct net_device *ndev = wil_to_ndev(wil); + +	if (*ppos != 0) { +		wil_err(wil, "Unable to set SSID substring from [%d]\n", +			(int)*ppos); +		return -EINVAL; +	} + +	if (count > sizeof(wdev->ssid)) { +		wil_err(wil, "SSID too long, len = %d\n", (int)count); +		return -EINVAL; +	} +	if (netif_running(ndev)) { +		wil_err(wil, "Unable to change SSID on running interface\n"); +		return -EINVAL; +	} + +	wdev->ssid_len = count; +	return simple_write_to_buffer(wdev->ssid, wdev->ssid_len, ppos, +				      buf, count); +} + +static const struct file_operations fops_ssid = { +	.read = wil_read_file_ssid, +	.write = wil_write_file_ssid, +	.open  = wil_default_open, +}; + +/*----------------*/ +int wil6210_debugfs_init(struct wil6210_priv *wil) +{ +	struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, +			wil_to_wiphy(wil)->debugfsdir); + +	if (IS_ERR_OR_NULL(dbg)) +		return -ENODEV; + +	debugfs_create_file("mbox", S_IRUGO, dbg, wil, &fops_mbox); +	debugfs_create_file("vrings", S_IRUGO, dbg, wil, &fops_vring); +	debugfs_create_file("txdesc", S_IRUGO, dbg, wil, &fops_txdesc); +	debugfs_create_u32("txdesc_index", S_IRUGO | S_IWUSR, dbg, +			   &dbg_txdesc_index); +	debugfs_create_file("bf", S_IRUGO, dbg, wil, &fops_bf); +	debugfs_create_file("ssid", S_IRUGO | S_IWUSR, dbg, wil, &fops_ssid); +	debugfs_create_u32("secure_pcp", S_IRUGO | S_IWUSR, dbg, +			   &wil->secure_pcp); + +	wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, +				   HOSTADDR(RGF_USER_USER_ICR)); +	wil6210_debugfs_create_ISR(wil, "DMA_EP_TX_ICR", dbg, +				   HOSTADDR(RGF_DMA_EP_TX_ICR)); +	wil6210_debugfs_create_ISR(wil, "DMA_EP_RX_ICR", dbg, +				   HOSTADDR(RGF_DMA_EP_RX_ICR)); +	wil6210_debugfs_create_ISR(wil, "DMA_EP_MISC_ICR", dbg, +				   HOSTADDR(RGF_DMA_EP_MISC_ICR)); +	wil6210_debugfs_create_pseudo_ISR(wil, dbg); +	wil6210_debugfs_create_ITR_CNT(wil, dbg); + +	debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); +	debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); + +	debugfs_create_file("reset", S_IWUSR, dbg, wil, &fops_reset); + +	wil->rgf_blob.data = (void * __force)wil->csr + 0; +	wil->rgf_blob.size = 0xa000; +	wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); + +	wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; +	wil->fw_code_blob.size = 0x40000; +	wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, +				  &wil->fw_code_blob); + +	wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; +	wil->fw_data_blob.size = 0x8000; +	wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, +				  &wil->fw_data_blob); + +	wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; +	wil->fw_peri_blob.size = 0x18000; +	wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, +				  &wil->fw_peri_blob); + +	wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; +	wil->uc_code_blob.size = 0x10000; +	wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, +				  &wil->uc_code_blob); + +	wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; +	wil->uc_data_blob.size = 0x4000; +	wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, +				  &wil->uc_data_blob); + +	return 0; +} + +void wil6210_debugfs_remove(struct wil6210_priv *wil) +{ +	debugfs_remove_recursive(wil->debug); +	wil->debug = NULL; +} diff --git a/drivers/net/wireless/ath/wil6210/interrupt.c b/drivers/net/wireless/ath/wil6210/interrupt.c new file mode 100644 index 00000000000..38049da7104 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/interrupt.c @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/interrupt.h> + +#include "wil6210.h" + +/** + * Theory of operation: + * + * There is ISR pseudo-cause register, + * dma_rgf->DMA_RGF.PSEUDO_CAUSE.PSEUDO_CAUSE + * Its bits represents OR'ed bits from 3 real ISR registers: + * TX, RX, and MISC. + * + * Registers may be configured to either "write 1 to clear" or + * "clear on read" mode + * + * When handling interrupt, one have to mask/unmask interrupts for the + * real ISR registers, or hardware may malfunction. + * + */ + +#define WIL6210_IRQ_DISABLE	(0xFFFFFFFFUL) +#define WIL6210_IMC_RX		BIT_DMA_EP_RX_ICR_RX_DONE +#define WIL6210_IMC_TX		(BIT_DMA_EP_TX_ICR_TX_DONE | \ +				BIT_DMA_EP_TX_ICR_TX_DONE_N(0)) +#define WIL6210_IMC_MISC	(ISR_MISC_FW_READY | ISR_MISC_MBOX_EVT) + +#define WIL6210_IRQ_PSEUDO_MASK (u32)(~(BIT_DMA_PSEUDO_CAUSE_RX | \ +					BIT_DMA_PSEUDO_CAUSE_TX | \ +					BIT_DMA_PSEUDO_CAUSE_MISC)) + +#if defined(CONFIG_WIL6210_ISR_COR) +/* configure to Clear-On-Read mode */ +#define WIL_ICR_ICC_VALUE	(0xFFFFFFFFUL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ + +} +#else /* defined(CONFIG_WIL6210_ISR_COR) */ +/* configure to Write-1-to-Clear mode */ +#define WIL_ICR_ICC_VALUE	(0UL) + +static inline void wil_icr_clear(u32 x, void __iomem *addr) +{ +	iowrite32(x, addr); +} +#endif /* defined(CONFIG_WIL6210_ISR_COR) */ + +static inline u32 wil_ioread32_and_clear(void __iomem *addr) +{ +	u32 x = ioread32(addr); + +	wil_icr_clear(x, addr); + +	return x; +} + +static void wil6210_mask_irq_tx(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IRQ_DISABLE, wil->csr + +		  HOSTADDR(RGF_DMA_EP_TX_ICR) + +		  offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_rx(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IRQ_DISABLE, wil->csr + +		  HOSTADDR(RGF_DMA_EP_RX_ICR) + +		  offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_misc(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IRQ_DISABLE, wil->csr + +		  HOSTADDR(RGF_DMA_EP_MISC_ICR) + +		  offsetof(struct RGF_ICR, IMS)); +} + +static void wil6210_mask_irq_pseudo(struct wil6210_priv *wil) +{ +	wil_dbg_IRQ(wil, "%s()\n", __func__); + +	iowrite32(WIL6210_IRQ_DISABLE, wil->csr + +		  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); + +	clear_bit(wil_status_irqen, &wil->status); +} + +static void wil6210_unmask_irq_tx(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IMC_TX, wil->csr + +		  HOSTADDR(RGF_DMA_EP_TX_ICR) + +		  offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_rx(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IMC_RX, wil->csr + +		  HOSTADDR(RGF_DMA_EP_RX_ICR) + +		  offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_misc(struct wil6210_priv *wil) +{ +	iowrite32(WIL6210_IMC_MISC, wil->csr + +		  HOSTADDR(RGF_DMA_EP_MISC_ICR) + +		  offsetof(struct RGF_ICR, IMC)); +} + +static void wil6210_unmask_irq_pseudo(struct wil6210_priv *wil) +{ +	wil_dbg_IRQ(wil, "%s()\n", __func__); + +	set_bit(wil_status_irqen, &wil->status); + +	iowrite32(WIL6210_IRQ_PSEUDO_MASK, wil->csr + +		  HOSTADDR(RGF_DMA_PSEUDO_CAUSE_MASK_SW)); +} + +void wil6210_disable_irq(struct wil6210_priv *wil) +{ +	wil_dbg_IRQ(wil, "%s()\n", __func__); + +	wil6210_mask_irq_tx(wil); +	wil6210_mask_irq_rx(wil); +	wil6210_mask_irq_misc(wil); +	wil6210_mask_irq_pseudo(wil); +} + +void wil6210_enable_irq(struct wil6210_priv *wil) +{ +	wil_dbg_IRQ(wil, "%s()\n", __func__); + +	iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_RX_ICR) + +		  offsetof(struct RGF_ICR, ICC)); +	iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_TX_ICR) + +		  offsetof(struct RGF_ICR, ICC)); +	iowrite32(WIL_ICR_ICC_VALUE, wil->csr + HOSTADDR(RGF_DMA_EP_MISC_ICR) + +		  offsetof(struct RGF_ICR, ICC)); + +	wil6210_unmask_irq_pseudo(wil); +	wil6210_unmask_irq_tx(wil); +	wil6210_unmask_irq_rx(wil); +	wil6210_unmask_irq_misc(wil); +} + +static irqreturn_t wil6210_irq_rx(int irq, void *cookie) +{ +	struct wil6210_priv *wil = cookie; +	u32 isr = wil_ioread32_and_clear(wil->csr + +					 HOSTADDR(RGF_DMA_EP_RX_ICR) + +					 offsetof(struct RGF_ICR, ICR)); + +	wil_dbg_IRQ(wil, "ISR RX 0x%08x\n", isr); + +	if (!isr) { +		wil_err(wil, "spurious IRQ: RX\n"); +		return IRQ_NONE; +	} + +	wil6210_mask_irq_rx(wil); + +	if (isr & BIT_DMA_EP_RX_ICR_RX_DONE) { +		wil_dbg_IRQ(wil, "RX done\n"); +		isr &= ~BIT_DMA_EP_RX_ICR_RX_DONE; +		wil_rx_handle(wil); +	} + +	if (isr) +		wil_err(wil, "un-handled RX ISR bits 0x%08x\n", isr); + +	wil6210_unmask_irq_rx(wil); + +	return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_tx(int irq, void *cookie) +{ +	struct wil6210_priv *wil = cookie; +	u32 isr = wil_ioread32_and_clear(wil->csr + +					 HOSTADDR(RGF_DMA_EP_TX_ICR) + +					 offsetof(struct RGF_ICR, ICR)); + +	wil_dbg_IRQ(wil, "ISR TX 0x%08x\n", isr); + +	if (!isr) { +		wil_err(wil, "spurious IRQ: TX\n"); +		return IRQ_NONE; +	} + +	wil6210_mask_irq_tx(wil); + +	if (isr & BIT_DMA_EP_TX_ICR_TX_DONE) { +		uint i; +		wil_dbg_IRQ(wil, "TX done\n"); +		isr &= ~BIT_DMA_EP_TX_ICR_TX_DONE; +		for (i = 0; i < 24; i++) { +			u32 mask = BIT_DMA_EP_TX_ICR_TX_DONE_N(i); +			if (isr & mask) { +				isr &= ~mask; +				wil_dbg_IRQ(wil, "TX done(%i)\n", i); +				wil_tx_complete(wil, i); +			} +		} +	} + +	if (isr) +		wil_err(wil, "un-handled TX ISR bits 0x%08x\n", isr); + +	wil6210_unmask_irq_tx(wil); + +	return IRQ_HANDLED; +} + +static irqreturn_t wil6210_irq_misc(int irq, void *cookie) +{ +	struct wil6210_priv *wil = cookie; +	u32 isr = wil_ioread32_and_clear(wil->csr + +					 HOSTADDR(RGF_DMA_EP_MISC_ICR) + +					 offsetof(struct RGF_ICR, ICR)); + +	wil_dbg_IRQ(wil, "ISR MISC 0x%08x\n", isr); + +	if (!isr) { +		wil_err(wil, "spurious IRQ: MISC\n"); +		return IRQ_NONE; +	} + +	wil6210_mask_irq_misc(wil); + +	if (isr & ISR_MISC_FW_READY) { +		wil_dbg_IRQ(wil, "IRQ: FW ready\n"); +		/** +		 * Actual FW ready indicated by the +		 * WMI_FW_READY_EVENTID +		 */ +		isr &= ~ISR_MISC_FW_READY; +	} + +	wil->isr_misc = isr; + +	if (isr) { +		return IRQ_WAKE_THREAD; +	} else { +		wil6210_unmask_irq_misc(wil); +		return IRQ_HANDLED; +	} +} + +static irqreturn_t wil6210_irq_misc_thread(int irq, void *cookie) +{ +	struct wil6210_priv *wil = cookie; +	u32 isr = wil->isr_misc; + +	wil_dbg_IRQ(wil, "Thread ISR MISC 0x%08x\n", isr); + +	if (isr & ISR_MISC_MBOX_EVT) { +		wil_dbg_IRQ(wil, "MBOX event\n"); +		wmi_recv_cmd(wil); +		isr &= ~ISR_MISC_MBOX_EVT; +	} + +	if (isr) +		wil_err(wil, "un-handled MISC ISR bits 0x%08x\n", isr); + +	wil->isr_misc = 0; + +	wil6210_unmask_irq_misc(wil); + +	return IRQ_HANDLED; +} + +/** + * thread IRQ handler + */ +static irqreturn_t wil6210_thread_irq(int irq, void *cookie) +{ +	struct wil6210_priv *wil = cookie; + +	wil_dbg_IRQ(wil, "Thread IRQ\n"); +	/* Discover real IRQ cause */ +	if (wil->isr_misc) +		wil6210_irq_misc_thread(irq, cookie); + +	wil6210_unmask_irq_pseudo(wil); + +	return IRQ_HANDLED; +} + +/* DEBUG + * There is subtle bug in hardware that causes IRQ to raise when it should be + * masked. It is quite rare and hard to debug. + * + * Catch irq issue if it happens and print all I can. + */ +static int wil6210_debug_irq_mask(struct wil6210_priv *wil, u32 pseudo_cause) +{ +	if (!test_bit(wil_status_irqen, &wil->status)) { +		u32 icm_rx = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_RX_ICR) + +				offsetof(struct RGF_ICR, ICM)); +		u32 icr_rx = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_RX_ICR) + +				offsetof(struct RGF_ICR, ICR)); +		u32 imv_rx = ioread32(wil->csr + +				HOSTADDR(RGF_DMA_EP_RX_ICR) + +				offsetof(struct RGF_ICR, IMV)); +		u32 icm_tx = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_TX_ICR) + +				offsetof(struct RGF_ICR, ICM)); +		u32 icr_tx = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_TX_ICR) + +				offsetof(struct RGF_ICR, ICR)); +		u32 imv_tx = ioread32(wil->csr + +				HOSTADDR(RGF_DMA_EP_TX_ICR) + +				offsetof(struct RGF_ICR, IMV)); +		u32 icm_misc = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_MISC_ICR) + +				offsetof(struct RGF_ICR, ICM)); +		u32 icr_misc = wil_ioread32_and_clear(wil->csr + +				HOSTADDR(RGF_DMA_EP_MISC_ICR) + +				offsetof(struct RGF_ICR, ICR)); +		u32 imv_misc = ioread32(wil->csr + +				HOSTADDR(RGF_DMA_EP_MISC_ICR) + +				offsetof(struct RGF_ICR, IMV)); +		wil_err(wil, "IRQ when it should be masked: pseudo 0x%08x\n" +				"Rx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n" +				"Tx   icm:icr:imv 0x%08x 0x%08x 0x%08x\n" +				"Misc icm:icr:imv 0x%08x 0x%08x 0x%08x\n", +				pseudo_cause, +				icm_rx, icr_rx, imv_rx, +				icm_tx, icr_tx, imv_tx, +				icm_misc, icr_misc, imv_misc); + +		return -EINVAL; +	} + +	return 0; +} + +static irqreturn_t wil6210_hardirq(int irq, void *cookie) +{ +	irqreturn_t rc = IRQ_HANDLED; +	struct wil6210_priv *wil = cookie; +	u32 pseudo_cause = ioread32(wil->csr + HOSTADDR(RGF_DMA_PSEUDO_CAUSE)); + +	/** +	 * pseudo_cause is Clear-On-Read, no need to ACK +	 */ +	if ((pseudo_cause == 0) || ((pseudo_cause & 0xff) == 0xff)) +		return IRQ_NONE; + +	/* FIXME: IRQ mask debug */ +	if (wil6210_debug_irq_mask(wil, pseudo_cause)) +		return IRQ_NONE; + +	wil6210_mask_irq_pseudo(wil); + +	/* Discover real IRQ cause +	 * There are 2 possible phases for every IRQ: +	 * - hard IRQ handler called right here +	 * - threaded handler called later +	 * +	 * Hard IRQ handler reads and clears ISR. +	 * +	 * If threaded handler requested, hard IRQ handler +	 * returns IRQ_WAKE_THREAD and saves ISR register value +	 * for the threaded handler use. +	 * +	 * voting for wake thread - need at least 1 vote +	 */ +	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_RX) && +	    (wil6210_irq_rx(irq, cookie) == IRQ_WAKE_THREAD)) +		rc = IRQ_WAKE_THREAD; + +	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_TX) && +	    (wil6210_irq_tx(irq, cookie) == IRQ_WAKE_THREAD)) +		rc = IRQ_WAKE_THREAD; + +	if ((pseudo_cause & BIT_DMA_PSEUDO_CAUSE_MISC) && +	    (wil6210_irq_misc(irq, cookie) == IRQ_WAKE_THREAD)) +		rc = IRQ_WAKE_THREAD; + +	/* if thread is requested, it will unmask IRQ */ +	if (rc != IRQ_WAKE_THREAD) +		wil6210_unmask_irq_pseudo(wil); + +	wil_dbg_IRQ(wil, "Hard IRQ 0x%08x\n", pseudo_cause); + +	return rc; +} + +static int wil6210_request_3msi(struct wil6210_priv *wil, int irq) +{ +	int rc; +	/* +	 * IRQ's are in the following order: +	 * - Tx +	 * - Rx +	 * - Misc +	 */ + +	rc = request_irq(irq, wil6210_irq_tx, IRQF_SHARED, +			 WIL_NAME"_tx", wil); +	if (rc) +		return rc; + +	rc = request_irq(irq + 1, wil6210_irq_rx, IRQF_SHARED, +			 WIL_NAME"_rx", wil); +	if (rc) +		goto free0; + +	rc = request_threaded_irq(irq + 2, wil6210_irq_misc, +				  wil6210_irq_misc_thread, +				  IRQF_SHARED, WIL_NAME"_misc", wil); +	if (rc) +		goto free1; + +	return 0; +	/* error branch */ +free1: +	free_irq(irq + 1, wil); +free0: +	free_irq(irq, wil); + +	return rc; +} + +int wil6210_init_irq(struct wil6210_priv *wil, int irq) +{ +	int rc; +	if (wil->n_msi == 3) +		rc = wil6210_request_3msi(wil, irq); +	else +		rc = request_threaded_irq(irq, wil6210_hardirq, +					  wil6210_thread_irq, +					  wil->n_msi ? 0 : IRQF_SHARED, +					  WIL_NAME, wil); +	if (rc) +		return rc; + +	wil6210_enable_irq(wil); + +	return 0; +} + +void wil6210_fini_irq(struct wil6210_priv *wil, int irq) +{ +	wil6210_disable_irq(wil); +	free_irq(irq, wil); +	if (wil->n_msi == 3) { +		free_irq(irq + 1, wil); +		free_irq(irq + 2, wil); +	} +} diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c new file mode 100644 index 00000000000..95fcd361322 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/sched.h> +#include <linux/ieee80211.h> +#include <linux/wireless.h> +#include <linux/slab.h> +#include <linux/moduleparam.h> +#include <linux/if_arp.h> + +#include "wil6210.h" + +/* + * Due to a hardware issue, + * one has to read/write to/from NIC in 32-bit chunks; + * regular memcpy_fromio and siblings will + * not work on 64-bit platform - it uses 64-bit transactions + * + * Force 32-bit transactions to enable NIC on 64-bit platforms + * + * To avoid byte swap on big endian host, __raw_{read|write}l + * should be used - {read|write}l would swap bytes to provide + * little endian on PCI value in host endianness. + */ +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, +			  size_t count) +{ +	u32 *d = dst; +	const volatile u32 __iomem *s = src; + +	/* size_t is unsigned, if (count%4 != 0) it will wrap */ +	for (count += 4; count > 4; count -= 4) +		*d++ = __raw_readl(s++); +} + +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, +			size_t count) +{ +	volatile u32 __iomem *d = dst; +	const u32 *s = src; + +	for (count += 4; count > 4; count -= 4) +		__raw_writel(*s++, d++); +} + +static void _wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ +	uint i; +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; + +	wil_dbg(wil, "%s()\n", __func__); + +	wil_link_off(wil); +	clear_bit(wil_status_fwconnected, &wil->status); + +	switch (wdev->sme_state) { +	case CFG80211_SME_CONNECTED: +		cfg80211_disconnected(ndev, WLAN_STATUS_UNSPECIFIED_FAILURE, +				      NULL, 0, GFP_KERNEL); +		break; +	case CFG80211_SME_CONNECTING: +		cfg80211_connect_result(ndev, bssid, NULL, 0, NULL, 0, +					WLAN_STATUS_UNSPECIFIED_FAILURE, +					GFP_KERNEL); +		break; +	default: +		; +	} + +	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) +		wil_vring_fini_tx(wil, i); +} + +static void wil_disconnect_worker(struct work_struct *work) +{ +	struct wil6210_priv *wil = container_of(work, +			struct wil6210_priv, disconnect_worker); + +	_wil6210_disconnect(wil, NULL); +} + +static void wil_connect_timer_fn(ulong x) +{ +	struct wil6210_priv *wil = (void *)x; + +	wil_dbg(wil, "Connect timeout\n"); + +	/* reschedule to thread context - disconnect won't +	 * run from atomic context +	 */ +	schedule_work(&wil->disconnect_worker); +} + +int wil_priv_init(struct wil6210_priv *wil) +{ +	wil_dbg(wil, "%s()\n", __func__); + +	mutex_init(&wil->mutex); +	mutex_init(&wil->wmi_mutex); + +	init_completion(&wil->wmi_ready); + +	wil->pending_connect_cid = -1; +	setup_timer(&wil->connect_timer, wil_connect_timer_fn, (ulong)wil); + +	INIT_WORK(&wil->wmi_connect_worker, wmi_connect_worker); +	INIT_WORK(&wil->disconnect_worker, wil_disconnect_worker); +	INIT_WORK(&wil->wmi_event_worker, wmi_event_worker); + +	INIT_LIST_HEAD(&wil->pending_wmi_ev); +	spin_lock_init(&wil->wmi_ev_lock); + +	wil->wmi_wq = create_singlethread_workqueue(WIL_NAME"_wmi"); +	if (!wil->wmi_wq) +		return -EAGAIN; + +	wil->wmi_wq_conn = create_singlethread_workqueue(WIL_NAME"_connect"); +	if (!wil->wmi_wq_conn) { +		destroy_workqueue(wil->wmi_wq); +		return -EAGAIN; +	} + +	/* make shadow copy of registers that should not change on run time */ +	wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, +			     sizeof(struct wil6210_mbox_ctl)); +	wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); +	wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + +	return 0; +} + +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid) +{ +	del_timer_sync(&wil->connect_timer); +	_wil6210_disconnect(wil, bssid); +} + +void wil_priv_deinit(struct wil6210_priv *wil) +{ +	cancel_work_sync(&wil->disconnect_worker); +	wil6210_disconnect(wil, NULL); +	wmi_event_flush(wil); +	destroy_workqueue(wil->wmi_wq_conn); +	destroy_workqueue(wil->wmi_wq); +} + +static void wil_target_reset(struct wil6210_priv *wil) +{ +	wil_dbg(wil, "Resetting...\n"); + +	/* register write */ +#define W(a, v) iowrite32(v, wil->csr + HOSTADDR(a)) +	/* register set = read, OR, write */ +#define S(a, v) iowrite32(ioread32(wil->csr + HOSTADDR(a)) | v, \ +		wil->csr + HOSTADDR(a)) + +	/* hpal_perst_from_pad_src_n_mask */ +	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); +	/* car_perst_rst_src_n_mask */ +	S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); + +	W(RGF_USER_MAC_CPU_0,  BIT(1)); /* mac_cpu_man_rst */ +	W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ + +	msleep(100); + +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); + +	msleep(100); + +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); +	W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); + +	msleep(2000); + +	W(RGF_USER_USER_CPU_0, BIT(0)); /* user_cpu_man_de_rst */ + +	msleep(2000); + +	wil_dbg(wil, "Reset completed\n"); + +#undef W +#undef S +} + +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r) +{ +	le32_to_cpus(&r->base); +	le16_to_cpus(&r->entry_size); +	le16_to_cpus(&r->size); +	le32_to_cpus(&r->tail); +	le32_to_cpus(&r->head); +} + +static int wil_wait_for_fw_ready(struct wil6210_priv *wil) +{ +	ulong to = msecs_to_jiffies(1000); +	ulong left = wait_for_completion_timeout(&wil->wmi_ready, to); +	if (0 == left) { +		wil_err(wil, "Firmware not ready\n"); +		return -ETIME; +	} else { +		wil_dbg(wil, "FW ready after %d ms\n", +			jiffies_to_msecs(to-left)); +	} +	return 0; +} + +/* + * We reset all the structures, and we reset the UMAC. + * After calling this routine, you're expected to reload + * the firmware. + */ +int wil_reset(struct wil6210_priv *wil) +{ +	int rc; + +	cancel_work_sync(&wil->disconnect_worker); +	wil6210_disconnect(wil, NULL); + +	wmi_event_flush(wil); + +	flush_workqueue(wil->wmi_wq); +	flush_workqueue(wil->wmi_wq_conn); + +	wil6210_disable_irq(wil); +	wil->status = 0; + +	/* TODO: put MAC in reset */ +	wil_target_reset(wil); + +	/* init after reset */ +	wil->pending_connect_cid = -1; +	INIT_COMPLETION(wil->wmi_ready); + +	/* make shadow copy of registers that should not change on run time */ +	wil_memcpy_fromio_32(&wil->mbox_ctl, wil->csr + HOST_MBOX, +			     sizeof(struct wil6210_mbox_ctl)); +	wil_mbox_ring_le2cpus(&wil->mbox_ctl.rx); +	wil_mbox_ring_le2cpus(&wil->mbox_ctl.tx); + +	/* TODO: release MAC reset */ +	wil6210_enable_irq(wil); + +	/* we just started MAC, wait for FW ready */ +	rc = wil_wait_for_fw_ready(wil); + +	return rc; +} + + +void wil_link_on(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); + +	wil_dbg(wil, "%s()\n", __func__); + +	netif_carrier_on(ndev); +	netif_tx_wake_all_queues(ndev); +} + +void wil_link_off(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); + +	wil_dbg(wil, "%s()\n", __func__); + +	netif_tx_stop_all_queues(ndev); +	netif_carrier_off(ndev); +} + +static int __wil_up(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; +	struct ieee80211_channel *channel = wdev->preset_chandef.chan; +	int rc; +	int bi; +	u16 wmi_nettype = wil_iftype_nl2wmi(wdev->iftype); + +	rc = wil_reset(wil); +	if (rc) +		return rc; + +	/* FIXME Firmware works now in PBSS mode(ToDS=0, FromDS=0) */ +	wmi_nettype = wil_iftype_nl2wmi(NL80211_IFTYPE_ADHOC); +	switch (wdev->iftype) { +	case NL80211_IFTYPE_STATION: +		wil_dbg(wil, "type: STATION\n"); +		bi = 0; +		ndev->type = ARPHRD_ETHER; +		break; +	case NL80211_IFTYPE_AP: +		wil_dbg(wil, "type: AP\n"); +		bi = 100; +		ndev->type = ARPHRD_ETHER; +		break; +	case NL80211_IFTYPE_P2P_CLIENT: +		wil_dbg(wil, "type: P2P_CLIENT\n"); +		bi = 0; +		ndev->type = ARPHRD_ETHER; +		break; +	case NL80211_IFTYPE_P2P_GO: +		wil_dbg(wil, "type: P2P_GO\n"); +		bi = 100; +		ndev->type = ARPHRD_ETHER; +		break; +	case NL80211_IFTYPE_MONITOR: +		wil_dbg(wil, "type: Monitor\n"); +		bi = 0; +		ndev->type = ARPHRD_IEEE80211_RADIOTAP; +		/* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_RADIOTAP ? */ +		break; +	default: +		return -EOPNOTSUPP; +	} + +	/* Apply profile in the following order: */ +	/* SSID and channel for the AP */ +	switch (wdev->iftype) { +	case NL80211_IFTYPE_AP: +	case NL80211_IFTYPE_P2P_GO: +		if (wdev->ssid_len == 0) { +			wil_err(wil, "SSID not set\n"); +			return -EINVAL; +		} +		wmi_set_ssid(wil, wdev->ssid_len, wdev->ssid); +		if (channel) +			wmi_set_channel(wil, channel->hw_value); +		break; +	default: +		; +	} + +	/* MAC address - pre-requisite for other commands */ +	wmi_set_mac_address(wil, ndev->dev_addr); + +	/* Set up beaconing if required. */ +	rc = wmi_set_bcon(wil, bi, wmi_nettype); +	if (rc) +		return rc; + +	/* Rx VRING. After MAC and beacon */ +	wil_rx_init(wil); + +	return 0; +} + +int wil_up(struct wil6210_priv *wil) +{ +	int rc; + +	mutex_lock(&wil->mutex); +	rc = __wil_up(wil); +	mutex_unlock(&wil->mutex); + +	return rc; +} + +static int __wil_down(struct wil6210_priv *wil) +{ +	if (wil->scan_request) { +		cfg80211_scan_done(wil->scan_request, true); +		wil->scan_request = NULL; +	} + +	wil6210_disconnect(wil, NULL); +	wil_rx_fini(wil); + +	return 0; +} + +int wil_down(struct wil6210_priv *wil) +{ +	int rc; + +	mutex_lock(&wil->mutex); +	rc = __wil_down(wil); +	mutex_unlock(&wil->mutex); + +	return rc; +} diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c new file mode 100644 index 00000000000..3068b5cb53a --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/module.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/slab.h> + +#include "wil6210.h" + +static int wil_open(struct net_device *ndev) +{ +	struct wil6210_priv *wil = ndev_to_wil(ndev); + +	return wil_up(wil); +} + +static int wil_stop(struct net_device *ndev) +{ +	struct wil6210_priv *wil = ndev_to_wil(ndev); + +	return wil_down(wil); +} + +/* + * AC to queue mapping + * + * AC_VO -> queue 3 + * AC_VI -> queue 2 + * AC_BE -> queue 1 + * AC_BK -> queue 0 + */ +static u16 wil_select_queue(struct net_device *ndev, struct sk_buff *skb) +{ +	static const u16 wil_1d_to_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; +	struct wil6210_priv *wil = ndev_to_wil(ndev); +	u16 rc; + +	skb->priority = cfg80211_classify8021d(skb); + +	rc = wil_1d_to_queue[skb->priority]; + +	wil_dbg_TXRX(wil, "%s() %d -> %d\n", __func__, (int)skb->priority, +		     (int)rc); + +	return rc; +} + +static const struct net_device_ops wil_netdev_ops = { +	.ndo_open		= wil_open, +	.ndo_stop		= wil_stop, +	.ndo_start_xmit		= wil_start_xmit, +	.ndo_select_queue	= wil_select_queue, +	.ndo_set_mac_address    = eth_mac_addr, +	.ndo_validate_addr      = eth_validate_addr, +}; + +void *wil_if_alloc(struct device *dev, void __iomem *csr) +{ +	struct net_device *ndev; +	struct wireless_dev *wdev; +	struct wil6210_priv *wil; +	struct ieee80211_channel *ch; +	int rc = 0; + +	wdev = wil_cfg80211_init(dev); +	if (IS_ERR(wdev)) { +		dev_err(dev, "wil_cfg80211_init failed\n"); +		return wdev; +	} + +	wil = wdev_to_wil(wdev); +	wil->csr = csr; +	wil->wdev = wdev; + +	rc = wil_priv_init(wil); +	if (rc) { +		dev_err(dev, "wil_priv_init failed\n"); +		goto out_wdev; +	} + +	wdev->iftype = NL80211_IFTYPE_STATION; /* TODO */ +	/* default monitor channel */ +	ch = wdev->wiphy->bands[IEEE80211_BAND_60GHZ]->channels; +	cfg80211_chandef_create(&wdev->preset_chandef, ch, NL80211_CHAN_NO_HT); + +	ndev = alloc_netdev_mqs(0, "wlan%d", ether_setup, WIL6210_TX_QUEUES, 1); +	if (!ndev) { +		dev_err(dev, "alloc_netdev_mqs failed\n"); +		rc = -ENOMEM; +		goto out_priv; +	} + +	ndev->netdev_ops = &wil_netdev_ops; +	ndev->ieee80211_ptr = wdev; +	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); +	wdev->netdev = ndev; + +	wil_link_off(wil); + +	return wil; + + out_priv: +	wil_priv_deinit(wil); + + out_wdev: +	wil_wdev_free(wil); + +	return ERR_PTR(rc); +} + +void wil_if_free(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	if (!ndev) +		return; + +	free_netdev(ndev); +	wil_priv_deinit(wil); +	wil_wdev_free(wil); +} + +int wil_if_add(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	int rc; + +	rc = register_netdev(ndev); +	if (rc < 0) { +		dev_err(&ndev->dev, "Failed to register netdev: %d\n", rc); +		return rc; +	} + +	wil_link_off(wil); + +	return 0; +} + +void wil_if_remove(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); + +	unregister_netdev(ndev); +} diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c new file mode 100644 index 00000000000..0fc83edd6ba --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/netdevice.h> +#include <linux/debugfs.h> +#include <linux/pci.h> +#include <linux/moduleparam.h> + +#include "wil6210.h" + +static int use_msi = 1; +module_param(use_msi, int, S_IRUGO); +MODULE_PARM_DESC(use_msi, +		 " Use MSI interrupt: " +		 "0 - don't, 1 - (default) - single, or 3"); + +/* Bus ops */ +static int wil_if_pcie_enable(struct wil6210_priv *wil) +{ +	struct pci_dev *pdev = wil->pdev; +	int rc; + +	pci_set_master(pdev); + +	/* +	 * how many MSI interrupts to request? +	 */ +	switch (use_msi) { +	case 3: +	case 1: +	case 0: +		break; +	default: +		wil_err(wil, "Invalid use_msi=%d, default to 1\n", +			use_msi); +		use_msi = 1; +	} +	wil->n_msi = use_msi; +	if (wil->n_msi) { +		wil_dbg(wil, "Setup %d MSI interrupts\n", use_msi); +		rc = pci_enable_msi_block(pdev, wil->n_msi); +		if (rc && (wil->n_msi == 3)) { +			wil_err(wil, "3 MSI mode failed, try 1 MSI\n"); +			wil->n_msi = 1; +			rc = pci_enable_msi_block(pdev, wil->n_msi); +		} +		if (rc) { +			wil_err(wil, "pci_enable_msi failed, use INTx\n"); +			wil->n_msi = 0; +		} +	} else { +		wil_dbg(wil, "MSI interrupts disabled, use INTx\n"); +	} + +	rc = wil6210_init_irq(wil, pdev->irq); +	if (rc) +		goto stop_master; + +	/* need reset here to obtain MAC */ +	rc = wil_reset(wil); +	if (rc) +		goto release_irq; + +	return 0; + + release_irq: +	wil6210_fini_irq(wil, pdev->irq); +	/* safe to call if no MSI */ +	pci_disable_msi(pdev); + stop_master: +	pci_clear_master(pdev); +	return rc; +} + +static int wil_if_pcie_disable(struct wil6210_priv *wil) +{ +	struct pci_dev *pdev = wil->pdev; + +	pci_clear_master(pdev); +	/* disable and release IRQ */ +	wil6210_fini_irq(wil, pdev->irq); +	/* safe to call if no MSI */ +	pci_disable_msi(pdev); +	/* TODO: disable HW */ + +	return 0; +} + +static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ +	struct wil6210_priv *wil; +	struct device *dev = &pdev->dev; +	void __iomem *csr; +	int rc; + +	/* check HW */ +	dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", +		 (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); + +	if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { +		dev_err(&pdev->dev, "Not " WIL_NAME "? " +			"BAR0 size is %lu while expecting %lu\n", +			(ulong)pci_resource_len(pdev, 0), WIL6210_MEM_SIZE); +		return -ENODEV; +	} + +	rc = pci_enable_device(pdev); +	if (rc) { +		dev_err(&pdev->dev, "pci_enable_device failed\n"); +		return -ENODEV; +	} +	/* rollback to err_disable_pdev */ + +	rc = pci_request_region(pdev, 0, WIL_NAME); +	if (rc) { +		dev_err(&pdev->dev, "pci_request_region failed\n"); +		goto err_disable_pdev; +	} +	/* rollback to err_release_reg */ + +	csr = pci_ioremap_bar(pdev, 0); +	if (!csr) { +		dev_err(&pdev->dev, "pci_ioremap_bar failed\n"); +		rc = -ENODEV; +		goto err_release_reg; +	} +	/* rollback to err_iounmap */ +	dev_info(&pdev->dev, "CSR at %pR -> %p\n", &pdev->resource[0], csr); + +	wil = wil_if_alloc(dev, csr); +	if (IS_ERR(wil)) { +		rc = (int)PTR_ERR(wil); +		dev_err(dev, "wil_if_alloc failed: %d\n", rc); +		goto err_iounmap; +	} +	/* rollback to if_free */ + +	pci_set_drvdata(pdev, wil); +	wil->pdev = pdev; + +	/* FW should raise IRQ when ready */ +	rc = wil_if_pcie_enable(wil); +	if (rc) { +		wil_err(wil, "Enable device failed\n"); +		goto if_free; +	} +	/* rollback to bus_disable */ + +	rc = wil_if_add(wil); +	if (rc) { +		wil_err(wil, "wil_if_add failed: %d\n", rc); +		goto bus_disable; +	} + +	wil6210_debugfs_init(wil); + +	/* check FW is alive */ +	wmi_echo(wil); + +	return 0; + + bus_disable: +	wil_if_pcie_disable(wil); + if_free: +	wil_if_free(wil); + err_iounmap: +	pci_iounmap(pdev, csr); + err_release_reg: +	pci_release_region(pdev, 0); + err_disable_pdev: +	pci_disable_device(pdev); + +	return rc; +} + +static void wil_pcie_remove(struct pci_dev *pdev) +{ +	struct wil6210_priv *wil = pci_get_drvdata(pdev); + +	wil6210_debugfs_remove(wil); +	wil_if_pcie_disable(wil); +	wil_if_remove(wil); +	wil_if_free(wil); +	pci_iounmap(pdev, wil->csr); +	pci_release_region(pdev, 0); +	pci_disable_device(pdev); +	pci_set_drvdata(pdev, NULL); +} + +static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { +	{ PCI_DEVICE(0x1ae9, 0x0301) }, +	{ /* end: all zeroes */	}, +}; +MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); + +static struct pci_driver wil6210_driver = { +	.probe		= wil_pcie_probe, +	.remove		= wil_pcie_remove, +	.id_table	= wil6210_pcie_ids, +	.name		= WIL_NAME, +}; + +module_pci_driver(wil6210_driver); + +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_AUTHOR("Qualcomm Atheros <wil6210@qca.qualcomm.com>"); +MODULE_DESCRIPTION("Driver for 60g WiFi WIL6210 card"); diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c new file mode 100644 index 00000000000..f29c294413c --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -0,0 +1,871 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/kernel.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/hardirq.h> +#include <net/ieee80211_radiotap.h> +#include <linux/if_arp.h> +#include <linux/moduleparam.h> + +#include "wil6210.h" +#include "wmi.h" +#include "txrx.h" + +static bool rtap_include_phy_info; +module_param(rtap_include_phy_info, bool, S_IRUGO); +MODULE_PARM_DESC(rtap_include_phy_info, +		 " Include PHY info in the radiotap header, default - no"); + +static inline int wil_vring_is_empty(struct vring *vring) +{ +	return vring->swhead == vring->swtail; +} + +static inline u32 wil_vring_next_tail(struct vring *vring) +{ +	return (vring->swtail + 1) % vring->size; +} + +static inline void wil_vring_advance_head(struct vring *vring, int n) +{ +	vring->swhead = (vring->swhead + n) % vring->size; +} + +static inline int wil_vring_is_full(struct vring *vring) +{ +	return wil_vring_next_tail(vring) == vring->swhead; +} +/* + * Available space in Tx Vring + */ +static inline int wil_vring_avail_tx(struct vring *vring) +{ +	u32 swhead = vring->swhead; +	u32 swtail = vring->swtail; +	int used = (vring->size + swhead - swtail) % vring->size; + +	return vring->size - used - 1; +} + +static int wil_vring_alloc(struct wil6210_priv *wil, struct vring *vring) +{ +	struct device *dev = wil_to_dev(wil); +	size_t sz = vring->size * sizeof(vring->va[0]); +	uint i; + +	BUILD_BUG_ON(sizeof(vring->va[0]) != 32); + +	vring->swhead = 0; +	vring->swtail = 0; +	vring->ctx = kzalloc(vring->size * sizeof(vring->ctx[0]), GFP_KERNEL); +	if (!vring->ctx) { +		wil_err(wil, "vring_alloc [%d] failed to alloc ctx mem\n", +			vring->size); +		vring->va = NULL; +		return -ENOMEM; +	} +	/* +	 * vring->va should be aligned on its size rounded up to power of 2 +	 * This is granted by the dma_alloc_coherent +	 */ +	vring->va = dma_alloc_coherent(dev, sz, &vring->pa, GFP_KERNEL); +	if (!vring->va) { +		wil_err(wil, "vring_alloc [%d] failed to alloc DMA mem\n", +			vring->size); +		kfree(vring->ctx); +		vring->ctx = NULL; +		return -ENOMEM; +	} +	/* initially, all descriptors are SW owned +	 * For Tx and Rx, ownership bit is at the same location, thus +	 * we can use any +	 */ +	for (i = 0; i < vring->size; i++) { +		volatile struct vring_tx_desc *d = &(vring->va[i].tx); +		d->dma.status = TX_DMA_STATUS_DU; +	} + +	wil_dbg(wil, "vring[%d] 0x%p:0x%016llx 0x%p\n", vring->size, +		vring->va, (unsigned long long)vring->pa, vring->ctx); + +	return 0; +} + +static void wil_vring_free(struct wil6210_priv *wil, struct vring *vring, +			   int tx) +{ +	struct device *dev = wil_to_dev(wil); +	size_t sz = vring->size * sizeof(vring->va[0]); + +	while (!wil_vring_is_empty(vring)) { +		if (tx) { +			volatile struct vring_tx_desc *d = +					&vring->va[vring->swtail].tx; +			dma_addr_t pa = d->dma.addr_low | +					((u64)d->dma.addr_high << 32); +			struct sk_buff *skb = vring->ctx[vring->swtail]; +			if (skb) { +				dma_unmap_single(dev, pa, d->dma.length, +						 DMA_TO_DEVICE); +				dev_kfree_skb_any(skb); +				vring->ctx[vring->swtail] = NULL; +			} else { +				dma_unmap_page(dev, pa, d->dma.length, +					       DMA_TO_DEVICE); +			} +			vring->swtail = wil_vring_next_tail(vring); +		} else { /* rx */ +			volatile struct vring_rx_desc *d = +					&vring->va[vring->swtail].rx; +			dma_addr_t pa = d->dma.addr_low | +					((u64)d->dma.addr_high << 32); +			struct sk_buff *skb = vring->ctx[vring->swhead]; +			dma_unmap_single(dev, pa, d->dma.length, +					 DMA_FROM_DEVICE); +			kfree_skb(skb); +			wil_vring_advance_head(vring, 1); +		} +	} +	dma_free_coherent(dev, sz, (void *)vring->va, vring->pa); +	kfree(vring->ctx); +	vring->pa = 0; +	vring->va = NULL; +	vring->ctx = NULL; +} + +/** + * Allocate one skb for Rx VRING + * + * Safe to call from IRQ + */ +static int wil_vring_alloc_skb(struct wil6210_priv *wil, struct vring *vring, +			       u32 i, int headroom) +{ +	struct device *dev = wil_to_dev(wil); +	unsigned int sz = RX_BUF_LEN; +	volatile struct vring_rx_desc *d = &(vring->va[i].rx); +	dma_addr_t pa; + +	/* TODO align */ +	struct sk_buff *skb = dev_alloc_skb(sz + headroom); +	if (unlikely(!skb)) +		return -ENOMEM; + +	skb_reserve(skb, headroom); +	skb_put(skb, sz); + +	pa = dma_map_single(dev, skb->data, skb->len, DMA_FROM_DEVICE); +	if (unlikely(dma_mapping_error(dev, pa))) { +		kfree_skb(skb); +		return -ENOMEM; +	} + +	d->dma.d0 = BIT(9) | RX_DMA_D0_CMD_DMA_IT; +	d->dma.addr_low = lower_32_bits(pa); +	d->dma.addr_high = (u16)upper_32_bits(pa); +	/* ip_length don't care */ +	/* b11 don't care */ +	/* error don't care */ +	d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ +	d->dma.length = sz; +	vring->ctx[i] = skb; + +	return 0; +} + +/** + * Adds radiotap header + * + * Any error indicated as "Bad FCS" + * + * Vendor data for 04:ce:14-1 (Wilocity-1) consists of: + *  - Rx descriptor: 32 bytes + *  - Phy info + */ +static void wil_rx_add_radiotap_header(struct wil6210_priv *wil, +				       struct sk_buff *skb, +				       volatile struct vring_rx_desc *d) +{ +	struct wireless_dev *wdev = wil->wdev; +	struct wil6210_rtap { +		struct ieee80211_radiotap_header rthdr; +		/* fields should be in the order of bits in rthdr.it_present */ +		/* flags */ +		u8 flags; +		/* channel */ +		__le16 chnl_freq __aligned(2); +		__le16 chnl_flags; +		/* MCS */ +		u8 mcs_present; +		u8 mcs_flags; +		u8 mcs_index; +	} __packed; +	struct wil6210_rtap_vendor { +		struct wil6210_rtap rtap; +		/* vendor */ +		u8 vendor_oui[3] __aligned(2); +		u8 vendor_ns; +		__le16 vendor_skip; +		u8 vendor_data[0]; +	} __packed; +	struct wil6210_rtap_vendor *rtap_vendor; +	int rtap_len = sizeof(struct wil6210_rtap); +	int phy_length = 0; /* phy info header size, bytes */ +	static char phy_data[128]; +	struct ieee80211_channel *ch = wdev->preset_chandef.chan; + +	if (rtap_include_phy_info) { +		rtap_len = sizeof(*rtap_vendor) + sizeof(*d); +		/* calculate additional length */ +		if (d->dma.status & RX_DMA_STATUS_PHY_INFO) { +			/** +			 * PHY info starts from 8-byte boundary +			 * there are 8-byte lines, last line may be partially +			 * written (HW bug), thus FW configures for last line +			 * to be excessive. Driver skips this last line. +			 */ +			int len = min_t(int, 8 + sizeof(phy_data), +					wil_rxdesc_phy_length(d)); +			if (len > 8) { +				void *p = skb_tail_pointer(skb); +				void *pa = PTR_ALIGN(p, 8); +				if (skb_tailroom(skb) >= len + (pa - p)) { +					phy_length = len - 8; +					memcpy(phy_data, pa, phy_length); +				} +			} +		} +		rtap_len += phy_length; +	} + +	if (skb_headroom(skb) < rtap_len && +	    pskb_expand_head(skb, rtap_len, 0, GFP_ATOMIC)) { +		wil_err(wil, "Unable to expand headrom to %d\n", rtap_len); +		return; +	} + +	rtap_vendor = (void *)skb_push(skb, rtap_len); +	memset(rtap_vendor, 0, rtap_len); + +	rtap_vendor->rtap.rthdr.it_version = PKTHDR_RADIOTAP_VERSION; +	rtap_vendor->rtap.rthdr.it_len = cpu_to_le16(rtap_len); +	rtap_vendor->rtap.rthdr.it_present = cpu_to_le32( +			(1 << IEEE80211_RADIOTAP_FLAGS) | +			(1 << IEEE80211_RADIOTAP_CHANNEL) | +			(1 << IEEE80211_RADIOTAP_MCS)); +	if (d->dma.status & RX_DMA_STATUS_ERROR) +		rtap_vendor->rtap.flags |= IEEE80211_RADIOTAP_F_BADFCS; + +	rtap_vendor->rtap.chnl_freq = cpu_to_le16(ch ? ch->center_freq : 58320); +	rtap_vendor->rtap.chnl_flags = cpu_to_le16(0); + +	rtap_vendor->rtap.mcs_present = IEEE80211_RADIOTAP_MCS_HAVE_MCS; +	rtap_vendor->rtap.mcs_flags = 0; +	rtap_vendor->rtap.mcs_index = wil_rxdesc_mcs(d); + +	if (rtap_include_phy_info) { +		rtap_vendor->rtap.rthdr.it_present |= cpu_to_le32(1 << +				IEEE80211_RADIOTAP_VENDOR_NAMESPACE); +		/* OUI for Wilocity 04:ce:14 */ +		rtap_vendor->vendor_oui[0] = 0x04; +		rtap_vendor->vendor_oui[1] = 0xce; +		rtap_vendor->vendor_oui[2] = 0x14; +		rtap_vendor->vendor_ns = 1; +		/* Rx descriptor + PHY data  */ +		rtap_vendor->vendor_skip = cpu_to_le16(sizeof(*d) + +						       phy_length); +		memcpy(rtap_vendor->vendor_data, (void *)d, sizeof(*d)); +		memcpy(rtap_vendor->vendor_data + sizeof(*d), phy_data, +		       phy_length); +	} +} + +/* + * Fast swap in place between 2 registers + */ +static void wil_swap_u16(u16 *a, u16 *b) +{ +	*a ^= *b; +	*b ^= *a; +	*a ^= *b; +} + +static void wil_swap_ethaddr(void *data) +{ +	struct ethhdr *eth = data; +	u16 *s = (u16 *)eth->h_source; +	u16 *d = (u16 *)eth->h_dest; + +	wil_swap_u16(s++, d++); +	wil_swap_u16(s++, d++); +	wil_swap_u16(s, d); +} + +/** + * reap 1 frame from @swhead + * + * Safe to call from IRQ + */ +static struct sk_buff *wil_vring_reap_rx(struct wil6210_priv *wil, +					 struct vring *vring) +{ +	struct device *dev = wil_to_dev(wil); +	struct net_device *ndev = wil_to_ndev(wil); +	volatile struct vring_rx_desc *d; +	struct sk_buff *skb; +	dma_addr_t pa; +	unsigned int sz = RX_BUF_LEN; +	u8 ftype; +	u8 ds_bits; + +	if (wil_vring_is_empty(vring)) +		return NULL; + +	d = &(vring->va[vring->swhead].rx); +	if (!(d->dma.status & RX_DMA_STATUS_DU)) { +		/* it is not error, we just reached end of Rx done area */ +		return NULL; +	} + +	pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); +	skb = vring->ctx[vring->swhead]; +	dma_unmap_single(dev, pa, sz, DMA_FROM_DEVICE); +	skb_trim(skb, d->dma.length); + +	wil->stats.last_mcs_rx = wil_rxdesc_mcs(d); + +	/* use radiotap header only if required */ +	if (ndev->type == ARPHRD_IEEE80211_RADIOTAP) +		wil_rx_add_radiotap_header(wil, skb, d); + +	wil_dbg_TXRX(wil, "Rx[%3d] : %d bytes\n", vring->swhead, d->dma.length); +	wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_NONE, 32, 4, +			  (const void *)d, sizeof(*d), false); + +	wil_vring_advance_head(vring, 1); + +	/* no extra checks if in sniffer mode */ +	if (ndev->type != ARPHRD_ETHER) +		return skb; +	/* +	 * Non-data frames may be delivered through Rx DMA channel (ex: BAR) +	 * Driver should recognize it by frame type, that is found +	 * in Rx descriptor. If type is not data, it is 802.11 frame as is +	 */ +	ftype = wil_rxdesc_ftype(d) << 2; +	if (ftype != IEEE80211_FTYPE_DATA) { +		wil_dbg_TXRX(wil, "Non-data frame ftype 0x%08x\n", ftype); +		/* TODO: process it */ +		kfree_skb(skb); +		return NULL; +	} + +	if (skb->len < ETH_HLEN) { +		wil_err(wil, "Short frame, len = %d\n", skb->len); +		/* TODO: process it (i.e. BAR) */ +		kfree_skb(skb); +		return NULL; +	} + +	ds_bits = wil_rxdesc_ds_bits(d); +	if (ds_bits == 1) { +		/* +		 * HW bug - in ToDS mode, i.e. Rx on AP side, +		 * addresses get swapped +		 */ +		wil_swap_ethaddr(skb->data); +	} + +	return skb; +} + +/** + * allocate and fill up to @count buffers in rx ring + * buffers posted at @swtail + */ +static int wil_rx_refill(struct wil6210_priv *wil, int count) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct vring *v = &wil->vring_rx; +	u32 next_tail; +	int rc = 0; +	int headroom = ndev->type == ARPHRD_IEEE80211_RADIOTAP ? +			WIL6210_RTAP_SIZE : 0; + +	for (; next_tail = wil_vring_next_tail(v), +			(next_tail != v->swhead) && (count-- > 0); +			v->swtail = next_tail) { +		rc = wil_vring_alloc_skb(wil, v, v->swtail, headroom); +		if (rc) { +			wil_err(wil, "Error %d in wil_rx_refill[%d]\n", +				rc, v->swtail); +			break; +		} +	} +	iowrite32(v->swtail, wil->csr + HOSTADDR(v->hwtail)); + +	return rc; +} + +/* + * Pass Rx packet to the netif. Update statistics. + */ +static void wil_netif_rx_any(struct sk_buff *skb, struct net_device *ndev) +{ +	int rc; +	unsigned int len = skb->len; + +	if (in_interrupt()) +		rc = netif_rx(skb); +	else +		rc = netif_rx_ni(skb); + +	if (likely(rc == NET_RX_SUCCESS)) { +		ndev->stats.rx_packets++; +		ndev->stats.rx_bytes += len; + +	} else { +		ndev->stats.rx_dropped++; +	} +} + +/** + * Proceed all completed skb's from Rx VRING + * + * Safe to call from IRQ + */ +void wil_rx_handle(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct vring *v = &wil->vring_rx; +	struct sk_buff *skb; + +	if (!v->va) { +		wil_err(wil, "Rx IRQ while Rx not yet initialized\n"); +		return; +	} +	wil_dbg_TXRX(wil, "%s()\n", __func__); +	while (NULL != (skb = wil_vring_reap_rx(wil, v))) { +		wil_hex_dump_TXRX("Rx ", DUMP_PREFIX_OFFSET, 16, 1, +				  skb->data, skb_headlen(skb), false); + +		skb_orphan(skb); + +		if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { +			skb->dev = ndev; +			skb_reset_mac_header(skb); +			skb->ip_summed = CHECKSUM_UNNECESSARY; +			skb->pkt_type = PACKET_OTHERHOST; +			skb->protocol = htons(ETH_P_802_2); + +		} else { +			skb->protocol = eth_type_trans(skb, ndev); +		} + +		wil_netif_rx_any(skb, ndev); +	} +	wil_rx_refill(wil, v->size); +} + +int wil_rx_init(struct wil6210_priv *wil) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; +	struct vring *vring = &wil->vring_rx; +	int rc; +	struct wmi_cfg_rx_chain_cmd cmd = { +		.action = WMI_RX_CHAIN_ADD, +		.rx_sw_ring = { +			.max_mpdu_size = cpu_to_le16(RX_BUF_LEN), +		}, +		.mid = 0, /* TODO - what is it? */ +		.decap_trans_type = WMI_DECAP_TYPE_802_3, +	}; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_cfg_rx_chain_done_event evt; +	} __packed evt; + +	vring->size = WIL6210_RX_RING_SIZE; +	rc = wil_vring_alloc(wil, vring); +	if (rc) +		return rc; + +	cmd.rx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); +	cmd.rx_sw_ring.ring_size = cpu_to_le16(vring->size); +	if (wdev->iftype == NL80211_IFTYPE_MONITOR) { +		struct ieee80211_channel *ch = wdev->preset_chandef.chan; + +		cmd.sniffer_cfg.mode = cpu_to_le32(WMI_SNIFFER_ON); +		if (ch) +			cmd.sniffer_cfg.channel = ch->hw_value - 1; +		cmd.sniffer_cfg.phy_info_mode = +			cpu_to_le32(ndev->type == ARPHRD_IEEE80211_RADIOTAP); +		cmd.sniffer_cfg.phy_support = +			cpu_to_le32((wil->monitor_flags & MONITOR_FLAG_CONTROL) +				    ? WMI_SNIFFER_CP : WMI_SNIFFER_DP); +	} +	/* typical time for secure PCP is 840ms */ +	rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), +		      WMI_CFG_RX_CHAIN_DONE_EVENTID, &evt, sizeof(evt), 2000); +	if (rc) +		goto err_free; + +	vring->hwtail = le32_to_cpu(evt.evt.rx_ring_tail_ptr); + +	wil_dbg(wil, "Rx init: status %d tail 0x%08x\n", +		le32_to_cpu(evt.evt.status), vring->hwtail); + +	rc = wil_rx_refill(wil, vring->size); +	if (rc) +		goto err_free; + +	return 0; + err_free: +	wil_vring_free(wil, vring, 0); + +	return rc; +} + +void wil_rx_fini(struct wil6210_priv *wil) +{ +	struct vring *vring = &wil->vring_rx; + +	if (vring->va) { +		int rc; +		struct wmi_cfg_rx_chain_cmd cmd = { +			.action = cpu_to_le32(WMI_RX_CHAIN_DEL), +			.rx_sw_ring = { +				.max_mpdu_size = cpu_to_le16(RX_BUF_LEN), +			}, +		}; +		struct { +			struct wil6210_mbox_hdr_wmi wmi; +			struct wmi_cfg_rx_chain_done_event cfg; +		} __packed wmi_rx_cfg_reply; + +		rc = wmi_call(wil, WMI_CFG_RX_CHAIN_CMDID, &cmd, sizeof(cmd), +			      WMI_CFG_RX_CHAIN_DONE_EVENTID, +			      &wmi_rx_cfg_reply, sizeof(wmi_rx_cfg_reply), +			      100); +		wil_vring_free(wil, vring, 0); +	} +} + +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, +		      int cid, int tid) +{ +	int rc; +	struct wmi_vring_cfg_cmd cmd = { +		.action = cpu_to_le32(WMI_VRING_CMD_ADD), +		.vring_cfg = { +			.tx_sw_ring = { +				.max_mpdu_size = cpu_to_le16(TX_BUF_LEN), +			}, +			.ringid = id, +			.cidxtid = (cid & 0xf) | ((tid & 0xf) << 4), +			.encap_trans_type = WMI_VRING_ENC_TYPE_802_3, +			.mac_ctrl = 0, +			.to_resolution = 0, +			.agg_max_wsize = 16, +			.schd_params = { +				.priority = cpu_to_le16(0), +				.timeslot_us = cpu_to_le16(0xfff), +			}, +		}, +	}; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_vring_cfg_done_event cmd; +	} __packed reply; +	struct vring *vring = &wil->vring_tx[id]; + +	if (vring->va) { +		wil_err(wil, "Tx ring [%d] already allocated\n", id); +		rc = -EINVAL; +		goto out; +	} + +	vring->size = size; +	rc = wil_vring_alloc(wil, vring); +	if (rc) +		goto out; + +	cmd.vring_cfg.tx_sw_ring.ring_mem_base = cpu_to_le64(vring->pa); +	cmd.vring_cfg.tx_sw_ring.ring_size = cpu_to_le16(vring->size); + +	rc = wmi_call(wil, WMI_VRING_CFG_CMDID, &cmd, sizeof(cmd), +		      WMI_VRING_CFG_DONE_EVENTID, &reply, sizeof(reply), 100); +	if (rc) +		goto out_free; + +	if (reply.cmd.status != WMI_VRING_CFG_SUCCESS) { +		wil_err(wil, "Tx config failed, status 0x%02x\n", +			reply.cmd.status); +		goto out_free; +	} +	vring->hwtail = le32_to_cpu(reply.cmd.tx_vring_tail_ptr); + +	return 0; + out_free: +	wil_vring_free(wil, vring, 1); + out: + +	return rc; +} + +void wil_vring_fini_tx(struct wil6210_priv *wil, int id) +{ +	struct vring *vring = &wil->vring_tx[id]; + +	if (!vring->va) +		return; + +	wil_vring_free(wil, vring, 1); +} + +static struct vring *wil_find_tx_vring(struct wil6210_priv *wil, +				       struct sk_buff *skb) +{ +	struct vring *v = &wil->vring_tx[0]; + +	if (v->va) +		return v; + +	return NULL; +} + +static int wil_tx_desc_map(volatile struct vring_tx_desc *d, +			   dma_addr_t pa, u32 len) +{ +	d->dma.addr_low = lower_32_bits(pa); +	d->dma.addr_high = (u16)upper_32_bits(pa); +	d->dma.ip_length = 0; +	/* 0..6: mac_length; 7:ip_version 0-IP6 1-IP4*/ +	d->dma.b11 = 0/*14 | BIT(7)*/; +	d->dma.error = 0; +	d->dma.status = 0; /* BIT(0) should be 0 for HW_OWNED */ +	d->dma.length = len; +	d->dma.d0 = 0; +	d->mac.d[0] = 0; +	d->mac.d[1] = 0; +	d->mac.d[2] = 0; +	d->mac.ucode_cmd = 0; +	/* use dst index 0 */ +	d->mac.d[1] |= BIT(MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS) | +		       (0 << MAC_CFG_DESC_TX_1_DST_INDEX_POS); +	/* translation type:  0 - bypass; 1 - 802.3; 2 - native wifi */ +	d->mac.d[2] = BIT(MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS) | +		      (1 << MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS); + +	return 0; +} + +static int wil_tx_vring(struct wil6210_priv *wil, struct vring *vring, +			struct sk_buff *skb) +{ +	struct device *dev = wil_to_dev(wil); +	volatile struct vring_tx_desc *d; +	u32 swhead = vring->swhead; +	int avail = wil_vring_avail_tx(vring); +	int nr_frags = skb_shinfo(skb)->nr_frags; +	uint f; +	int vring_index = vring - wil->vring_tx; +	uint i = swhead; +	dma_addr_t pa; + +	wil_dbg_TXRX(wil, "%s()\n", __func__); + +	if (avail < vring->size/8) +		netif_tx_stop_all_queues(wil_to_ndev(wil)); +	if (avail < 1 + nr_frags) { +		wil_err(wil, "Tx ring full. No space for %d fragments\n", +			1 + nr_frags); +		return -ENOMEM; +	} +	d = &(vring->va[i].tx); + +	/* FIXME FW can accept only unicast frames for the peer */ +	memcpy(skb->data, wil->dst_addr[vring_index], ETH_ALEN); + +	pa = dma_map_single(dev, skb->data, +			skb_headlen(skb), DMA_TO_DEVICE); + +	wil_dbg_TXRX(wil, "Tx skb %d bytes %p -> %#08llx\n", skb_headlen(skb), +		     skb->data, (unsigned long long)pa); +	wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_OFFSET, 16, 1, +			  skb->data, skb_headlen(skb), false); + +	if (unlikely(dma_mapping_error(dev, pa))) +		return -EINVAL; +	/* 1-st segment */ +	wil_tx_desc_map(d, pa, skb_headlen(skb)); +	d->mac.d[2] |= ((nr_frags + 1) << +		       MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS); +	/* middle segments */ +	for (f = 0; f < nr_frags; f++) { +		const struct skb_frag_struct *frag = +				&skb_shinfo(skb)->frags[f]; +		int len = skb_frag_size(frag); +		i = (swhead + f + 1) % vring->size; +		d = &(vring->va[i].tx); +		pa = skb_frag_dma_map(dev, frag, 0, skb_frag_size(frag), +				DMA_TO_DEVICE); +		if (unlikely(dma_mapping_error(dev, pa))) +			goto dma_error; +		wil_tx_desc_map(d, pa, len); +		vring->ctx[i] = NULL; +	} +	/* for the last seg only */ +	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_EOP_POS); +	d->dma.d0 |= BIT(9); /* BUG: undocumented bit */ +	d->dma.d0 |= BIT(DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS); +	d->dma.d0 |= (vring_index << DMA_CFG_DESC_TX_0_QID_POS); + +	wil_hex_dump_TXRX("Tx ", DUMP_PREFIX_NONE, 32, 4, +			  (const void *)d, sizeof(*d), false); + +	/* advance swhead */ +	wil_vring_advance_head(vring, nr_frags + 1); +	wil_dbg_TXRX(wil, "Tx swhead %d -> %d\n", swhead, vring->swhead); +	iowrite32(vring->swhead, wil->csr + HOSTADDR(vring->hwtail)); +	/* hold reference to skb +	 * to prevent skb release before accounting +	 * in case of immediate "tx done" +	 */ +	vring->ctx[i] = skb_get(skb); + +	return 0; + dma_error: +	/* unmap what we have mapped */ +	/* Note: increment @f to operate with positive index */ +	for (f++; f > 0; f--) { +		i = (swhead + f) % vring->size; +		d = &(vring->va[i].tx); +		d->dma.status = TX_DMA_STATUS_DU; +		pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); +		if (vring->ctx[i]) +			dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); +		else +			dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); +	} + +	return -EINVAL; +} + + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev) +{ +	struct wil6210_priv *wil = ndev_to_wil(ndev); +	struct vring *vring; +	int rc; + +	wil_dbg_TXRX(wil, "%s()\n", __func__); +	if (!test_bit(wil_status_fwready, &wil->status)) { +		wil_err(wil, "FW not ready\n"); +		goto drop; +	} +	if (!test_bit(wil_status_fwconnected, &wil->status)) { +		wil_err(wil, "FW not connected\n"); +		goto drop; +	} +	if (wil->wdev->iftype == NL80211_IFTYPE_MONITOR) { +		wil_err(wil, "Xmit in monitor mode not supported\n"); +		goto drop; +	} +	if (skb->protocol == cpu_to_be16(ETH_P_PAE)) { +		rc = wmi_tx_eapol(wil, skb); +	} else { +		/* find vring */ +		vring = wil_find_tx_vring(wil, skb); +		if (!vring) { +			wil_err(wil, "No Tx VRING available\n"); +			goto drop; +		} +		/* set up vring entry */ +		rc = wil_tx_vring(wil, vring, skb); +	} +	switch (rc) { +	case 0: +		ndev->stats.tx_packets++; +		ndev->stats.tx_bytes += skb->len; +		dev_kfree_skb_any(skb); +		return NETDEV_TX_OK; +	case -ENOMEM: +		return NETDEV_TX_BUSY; +	default: +		; /* goto drop; */ +		break; +	} + drop: +	netif_tx_stop_all_queues(ndev); +	ndev->stats.tx_dropped++; +	dev_kfree_skb_any(skb); + +	return NET_XMIT_DROP; +} + +/** + * Clean up transmitted skb's from the Tx VRING + * + * Safe to call from IRQ + */ +void wil_tx_complete(struct wil6210_priv *wil, int ringid) +{ +	struct device *dev = wil_to_dev(wil); +	struct vring *vring = &wil->vring_tx[ringid]; + +	if (!vring->va) { +		wil_err(wil, "Tx irq[%d]: vring not initialized\n", ringid); +		return; +	} + +	wil_dbg_TXRX(wil, "%s(%d)\n", __func__, ringid); + +	while (!wil_vring_is_empty(vring)) { +		volatile struct vring_tx_desc *d = &vring->va[vring->swtail].tx; +		dma_addr_t pa; +		struct sk_buff *skb; +		if (!(d->dma.status & TX_DMA_STATUS_DU)) +			break; + +		wil_dbg_TXRX(wil, +			     "Tx[%3d] : %d bytes, status 0x%02x err 0x%02x\n", +			     vring->swtail, d->dma.length, d->dma.status, +			     d->dma.error); +		wil_hex_dump_TXRX("TxC ", DUMP_PREFIX_NONE, 32, 4, +				  (const void *)d, sizeof(*d), false); + +		pa = d->dma.addr_low | ((u64)d->dma.addr_high << 32); +		skb = vring->ctx[vring->swtail]; +		if (skb) { +			dma_unmap_single(dev, pa, d->dma.length, DMA_TO_DEVICE); +			dev_kfree_skb_any(skb); +			vring->ctx[vring->swtail] = NULL; +		} else { +			dma_unmap_page(dev, pa, d->dma.length, DMA_TO_DEVICE); +		} +		d->dma.addr_low = 0; +		d->dma.addr_high = 0; +		d->dma.length = 0; +		d->dma.status = TX_DMA_STATUS_DU; +		vring->swtail = wil_vring_next_tail(vring); +	} +	if (wil_vring_avail_tx(vring) > vring->size/4) +		netif_tx_wake_all_queues(wil_to_ndev(wil)); +} diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h new file mode 100644 index 00000000000..45a61f597c5 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef WIL6210_TXRX_H +#define WIL6210_TXRX_H + +#define BUF_SW_OWNED    (1) +#define BUF_HW_OWNED    (0) + +/* size of max. Rx packet */ +#define RX_BUF_LEN      (2048) +#define TX_BUF_LEN      (2048) +/* how many bytes to reserve for rtap header? */ +#define WIL6210_RTAP_SIZE (128) + +/* Tx/Rx path */ +/* + * Tx descriptor - MAC part + * [dword 0] + * bit  0.. 9 : lifetime_expiry_value:10 + * bit     10 : interrup_en:1 + * bit     11 : status_en:1 + * bit 12..13 : txss_override:2 + * bit     14 : timestamp_insertion:1 + * bit     15 : duration_preserve:1 + * bit 16..21 : reserved0:6 + * bit 22..26 : mcs_index:5 + * bit     27 : mcs_en:1 + * bit 28..29 : reserved1:2 + * bit     30 : reserved2:1 + * bit     31 : sn_preserved:1 + * [dword 1] + * bit  0.. 3 : pkt_mode:4 + * bit      4 : pkt_mode_en:1 + * bit  5.. 7 : reserved0:3 + * bit  8..13 : reserved1:6 + * bit     14 : reserved2:1 + * bit     15 : ack_policy_en:1 + * bit 16..19 : dst_index:4 + * bit     20 : dst_index_en:1 + * bit 21..22 : ack_policy:2 + * bit     23 : lifetime_en:1 + * bit 24..30 : max_retry:7 + * bit     31 : max_retry_en:1 + * [dword 2] + * bit  0.. 7 : num_of_descriptors:8 + * bit  8..17 : reserved:10 + * bit 18..19 : l2_translation_type:2 + * bit     20 : snap_hdr_insertion_en:1 + * bit     21 : vlan_removal_en:1 + * bit 22..31 : reserved0:10 + * [dword 3] + * bit  0.. 31: ucode_cmd:32 + */ +struct vring_tx_mac { +	u32 d[3]; +	u32 ucode_cmd; +} __packed; + +/* TX MAC Dword 0 */ +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_POS 0 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_LEN 10 +#define MAC_CFG_DESC_TX_0_LIFETIME_EXPIRY_VALUE_MSK 0x3FF + +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_POS 10 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_INTERRUP_EN_MSK 0x400 + +#define MAC_CFG_DESC_TX_0_STATUS_EN_POS 11 +#define MAC_CFG_DESC_TX_0_STATUS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_STATUS_EN_MSK 0x800 + +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_POS 12 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_LEN 2 +#define MAC_CFG_DESC_TX_0_TXSS_OVERRIDE_MSK 0x3000 + +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_POS 14 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_LEN 1 +#define MAC_CFG_DESC_TX_0_TIMESTAMP_INSERTION_MSK 0x4000 + +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_POS 15 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_LEN 1 +#define MAC_CFG_DESC_TX_0_DURATION_PRESERVE_MSK 0x8000 + +#define MAC_CFG_DESC_TX_0_MCS_INDEX_POS 22 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_LEN 5 +#define MAC_CFG_DESC_TX_0_MCS_INDEX_MSK 0x7C00000 + +#define MAC_CFG_DESC_TX_0_MCS_EN_POS 27 +#define MAC_CFG_DESC_TX_0_MCS_EN_LEN 1 +#define MAC_CFG_DESC_TX_0_MCS_EN_MSK 0x8000000 + +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_POS 31 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_LEN 1 +#define MAC_CFG_DESC_TX_0_SN_PRESERVED_MSK 0x80000000 + +/* TX MAC Dword 1 */ +#define MAC_CFG_DESC_TX_1_PKT_MODE_POS 0 +#define MAC_CFG_DESC_TX_1_PKT_MODE_LEN 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_MSK 0xF + +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_POS 4 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_PKT_MODE_EN_MSK 0x10 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_POS 15 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_EN_MSK 0x8000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_POS 16 +#define MAC_CFG_DESC_TX_1_DST_INDEX_LEN 4 +#define MAC_CFG_DESC_TX_1_DST_INDEX_MSK 0xF0000 + +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_POS 20 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_DST_INDEX_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_1_ACK_POLICY_POS 21 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_LEN 2 +#define MAC_CFG_DESC_TX_1_ACK_POLICY_MSK 0x600000 + +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_POS 23 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_LIFETIME_EN_MSK 0x800000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_POS 24 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_LEN 7 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_MSK 0x7F000000 + +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_POS 31 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_LEN 1 +#define MAC_CFG_DESC_TX_1_MAX_RETRY_EN_MSK 0x80000000 + +/* TX MAC Dword 2 */ +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_POS 0 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_LEN 8 +#define MAC_CFG_DESC_TX_2_NUM_OF_DESCRIPTORS_MSK 0xFF + +#define MAC_CFG_DESC_TX_2_RESERVED_POS 8 +#define MAC_CFG_DESC_TX_2_RESERVED_LEN 10 +#define MAC_CFG_DESC_TX_2_RESERVED_MSK 0x3FF00 + +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_POS 18 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_LEN 2 +#define MAC_CFG_DESC_TX_2_L2_TRANSLATION_TYPE_MSK 0xC0000 + +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_POS 20 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_SNAP_HDR_INSERTION_EN_MSK 0x100000 + +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_POS 21 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_LEN 1 +#define MAC_CFG_DESC_TX_2_VLAN_REMOVAL_EN_MSK 0x200000 + +/* TX MAC Dword 3 */ +#define MAC_CFG_DESC_TX_3_UCODE_CMD_POS 0 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_LEN 32 +#define MAC_CFG_DESC_TX_3_UCODE_CMD_MSK 0xFFFFFFFF + +/* TX DMA Dword 0 */ +#define DMA_CFG_DESC_TX_0_L4_LENGTH_POS 0 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_LEN 8 +#define DMA_CFG_DESC_TX_0_L4_LENGTH_MSK 0xFF + +#define DMA_CFG_DESC_TX_0_CMD_EOP_POS 8 +#define DMA_CFG_DESC_TX_0_CMD_EOP_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_EOP_MSK 0x100 + +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_POS 10 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_LEN 1 +#define DMA_CFG_DESC_TX_0_CMD_DMA_IT_MSK 0x400 + +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_POS 11 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_LEN 2 +#define DMA_CFG_DESC_TX_0_SEGMENT_BUF_DETAILS_MSK 0x1800 + +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_POS 13 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_SEG_EN_MSK 0x2000 + +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_POS 14 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_IPV4_CHECKSUM_EN_MSK 0x4000 + +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_POS 15 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_TCP_UDP_CHECKSUM_EN_MSK 0x8000 + +#define DMA_CFG_DESC_TX_0_QID_POS 16 +#define DMA_CFG_DESC_TX_0_QID_LEN 5 +#define DMA_CFG_DESC_TX_0_QID_MSK 0x1F0000 + +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_POS 21 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_LEN 1 +#define DMA_CFG_DESC_TX_0_PSEUDO_HEADER_CALC_EN_MSK 0x200000 + +#define DMA_CFG_DESC_TX_0_L4_TYPE_POS 30 +#define DMA_CFG_DESC_TX_0_L4_TYPE_LEN 2 +#define DMA_CFG_DESC_TX_0_L4_TYPE_MSK 0xC0000000 + + +#define TX_DMA_STATUS_DU         BIT(0) + +struct vring_tx_dma { +	u32 d0; +	u32 addr_low; +	u16 addr_high; +	u8  ip_length; +	u8  b11;       /* 0..6: mac_length; 7:ip_version */ +	u8  error;     /* 0..2: err; 3..7: reserved; */ +	u8  status;    /* 0: used; 1..7; reserved */ +	u16 length; +} __packed; + +/* + * Rx descriptor - MAC part + * [dword 0] + * bit  0.. 3 : tid:4 The QoS (b3-0) TID Field + * bit  4.. 6 : connection_id:3 :The Source index that  was found during + *  Parsing the TA.  This field is used to  define the source of the packet + * bit      7 : reserved:1 + * bit  8.. 9 : mac_id:2 : The MAC virtual  Ring number (always zero) + * bit 10..11 : frame_type:2 : The FC Control  (b3-2) -  MPDU Type + *              (management, data, control  and extension) + * bit 12..15 : frame_subtype:4 : The FC Control  (b7-4) -  Frame Subtype + * bit 16..27 : seq_number:12 The received Sequence number field + * bit 28..31 : extended:4 extended subtype + * [dword 1] + * bit  0.. 3 : reserved + * bit  4.. 5 : key_id:2 + * bit      6 : decrypt_bypass:1 + * bit      7 : security:1 + * bit  8.. 9 : ds_bits:2 + * bit     10 : a_msdu_present:1  from qos header + * bit     11 : a_msdu_type:1  from qos header + * bit     12 : a_mpdu:1  part of AMPDU aggregation + * bit     13 : broadcast:1 + * bit     14 : mutlicast:1 + * bit     15 : reserved:1 + * bit 16..20 : rx_mac_qid:5   The Queue Identifier that the packet + *                             is received from + * bit 21..24 : mcs:4 + * bit 25..28 : mic_icr:4 + * bit 29..31 : reserved:3 + * [dword 2] + * bit  0.. 2 : time_slot:3 The timeslot that the MPDU is received + * bit      3 : fc_protocol_ver:1 The FC Control  (b0) - Protocol  Version + * bit      4 : fc_order:1 The FC Control (b15) -Order + * bit  5.. 7 : qos_ack_policy:3  The QoS (b6-5) ack policy Field + * bit      8 : esop:1 The QoS (b4) ESOP field + * bit      9 : qos_rdg_more_ppdu:1 The QoS (b9) RDG  field + * bit 10..14 : qos_reserved:5 The QoS (b14-10) Reserved  field + * bit     15 : qos_ac_constraint:1 + * bit 16..31 : pn_15_0:16 low 2 bytes of PN + * [dword 3] + * bit  0..31 : pn_47_16:32 high 4 bytes of PN + */ +struct vring_rx_mac { +	u32 d0; +	u32 d1; +	u16 w4; +	u16 pn_15_0; +	u32 pn_47_16; +} __packed; + +/* + * Rx descriptor - DMA part + * [dword 0] + * bit  0.. 7 : l4_length:8 layer 4 length + * bit  8.. 9 : reserved:2 + * bit     10 : cmd_dma_it:1 + * bit 11..15 : reserved:5 + * bit 16..29 : phy_info_length:14 + * bit 30..31 : l4_type:2 valid if the L4I bit is set in the status field + * [dword 1] + * bit  0..31 : addr_low:32 The payload buffer low address + * [dword 2] + * bit  0..15 : addr_high:16 The payload buffer high address + * bit 16..23 : ip_length:8 + * bit 24..30 : mac_length:7 + * bit     31 : ip_version:1 + * [dword 3] + *  [byte 12] error + *  [byte 13] status + * bit      0 : du:1 + * bit      1 : eop:1 + * bit      2 : error:1 + * bit      3 : mi:1 + * bit      4 : l3_identified:1 + * bit      5 : l4_identified:1 + * bit      6 : phy_info_included:1 + * bit      7 : reserved:1 + *  [word 7] length + * + */ + +#define RX_DMA_D0_CMD_DMA_IT     BIT(10) + +#define RX_DMA_STATUS_DU         BIT(0) +#define RX_DMA_STATUS_ERROR      BIT(2) +#define RX_DMA_STATUS_PHY_INFO   BIT(6) + +struct vring_rx_dma { +	u32 d0; +	u32 addr_low; +	u16 addr_high; +	u8  ip_length; +	u8  b11; +	u8  error; +	u8  status; +	u16 length; +} __packed; + +struct vring_tx_desc { +	struct vring_tx_mac mac; +	struct vring_tx_dma dma; +} __packed; + +struct vring_rx_desc { +	struct vring_rx_mac mac; +	struct vring_rx_dma dma; +} __packed; + +union vring_desc { +	struct vring_tx_desc tx; +	struct vring_rx_desc rx; +} __packed; + +static inline int wil_rxdesc_phy_length(volatile struct vring_rx_desc *d) +{ +	return WIL_GET_BITS(d->dma.d0, 16, 29); +} + +static inline int wil_rxdesc_mcs(volatile struct vring_rx_desc *d) +{ +	return WIL_GET_BITS(d->mac.d1, 21, 24); +} + +static inline int wil_rxdesc_ds_bits(volatile struct vring_rx_desc *d) +{ +	return WIL_GET_BITS(d->mac.d1, 8, 9); +} + +static inline int wil_rxdesc_ftype(volatile struct vring_rx_desc *d) +{ +	return WIL_GET_BITS(d->mac.d0, 10, 11); +} + +#endif /* WIL6210_TXRX_H */ diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h new file mode 100644 index 00000000000..9bcfffa4006 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -0,0 +1,363 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef __WIL6210_H__ +#define __WIL6210_H__ + +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <net/cfg80211.h> + +#include "dbg_hexdump.h" + +#define WIL_NAME "wil6210" + +/** + * extract bits [@b0:@b1] (inclusive) from the value @x + * it should be @b0 <= @b1, or result is incorrect + */ +static inline u32 WIL_GET_BITS(u32 x, int b0, int b1) +{ +	return (x >> b0) & ((1 << (b1 - b0 + 1)) - 1); +} + +#define WIL6210_MEM_SIZE (2*1024*1024UL) + +#define WIL6210_TX_QUEUES (4) + +#define WIL6210_RX_RING_SIZE (128) +#define WIL6210_TX_RING_SIZE (128) +#define WIL6210_MAX_TX_RINGS (24) + +/* Hardware definitions begin */ + +/* + * Mapping + * RGF File      | Host addr    |  FW addr + *               |              | + * user_rgf      | 0x000000     | 0x880000 + *  dma_rgf      | 0x001000     | 0x881000 + * pcie_rgf      | 0x002000     | 0x882000 + *               |              | + */ + +/* Where various structures placed in host address space */ +#define WIL6210_FW_HOST_OFF      (0x880000UL) + +#define HOSTADDR(fwaddr)        (fwaddr - WIL6210_FW_HOST_OFF) + +/* + * Interrupt control registers block + * + * each interrupt controlled by the same bit in all registers + */ +struct RGF_ICR { +	u32 ICC; /* Cause Control, RW: 0 - W1C, 1 - COR */ +	u32 ICR; /* Cause, W1C/COR depending on ICC */ +	u32 ICM; /* Cause masked (ICR & ~IMV), W1C/COR depending on ICC */ +	u32 ICS; /* Cause Set, WO */ +	u32 IMV; /* Mask, RW+S/C */ +	u32 IMS; /* Mask Set, write 1 to set */ +	u32 IMC; /* Mask Clear, write 1 to clear */ +} __packed; + +/* registers - FW addresses */ +#define RGF_USER_USER_SCRATCH_PAD	(0x8802bc) +#define RGF_USER_USER_ICR		(0x880b4c) /* struct RGF_ICR */ +	#define BIT_USER_USER_ICR_SW_INT_2	BIT(18) +#define RGF_USER_CLKS_CTL_SW_RST_MASK_0	(0x880b14) +#define RGF_USER_MAC_CPU_0		(0x8801fc) +#define RGF_USER_USER_CPU_0		(0x8801e0) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_0	(0x880b04) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_1	(0x880b08) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_2	(0x880b0c) +#define RGF_USER_CLKS_CTL_SW_RST_VEC_3	(0x880b10) + +#define RGF_DMA_PSEUDO_CAUSE		(0x881c68) +#define RGF_DMA_PSEUDO_CAUSE_MASK_SW	(0x881c6c) +#define RGF_DMA_PSEUDO_CAUSE_MASK_FW	(0x881c70) +	#define BIT_DMA_PSEUDO_CAUSE_RX		BIT(0) +	#define BIT_DMA_PSEUDO_CAUSE_TX		BIT(1) +	#define BIT_DMA_PSEUDO_CAUSE_MISC	BIT(2) + +#define RGF_DMA_EP_TX_ICR		(0x881bb4) /* struct RGF_ICR */ +	#define BIT_DMA_EP_TX_ICR_TX_DONE	BIT(0) +	#define BIT_DMA_EP_TX_ICR_TX_DONE_N(n)	BIT(n+1) /* n = [0..23] */ +#define RGF_DMA_EP_RX_ICR		(0x881bd0) /* struct RGF_ICR */ +	#define BIT_DMA_EP_RX_ICR_RX_DONE	BIT(0) +#define RGF_DMA_EP_MISC_ICR		(0x881bec) /* struct RGF_ICR */ +	#define BIT_DMA_EP_MISC_ICR_RX_HTRSH	BIT(0) +	#define BIT_DMA_EP_MISC_ICR_TX_NO_ACT	BIT(1) +	#define BIT_DMA_EP_MISC_ICR_FW_INT0	BIT(28) +	#define BIT_DMA_EP_MISC_ICR_FW_INT1	BIT(29) + +/* Interrupt moderation control */ +#define RGF_DMA_ITR_CNT_TRSH		(0x881c5c) +#define RGF_DMA_ITR_CNT_DATA		(0x881c60) +#define RGF_DMA_ITR_CNT_CRL		(0x881C64) +	#define BIT_DMA_ITR_CNT_CRL_EN		BIT(0) +	#define BIT_DMA_ITR_CNT_CRL_EXT_TICK	BIT(1) +	#define BIT_DMA_ITR_CNT_CRL_FOREVER	BIT(2) +	#define BIT_DMA_ITR_CNT_CRL_CLR		BIT(3) +	#define BIT_DMA_ITR_CNT_CRL_REACH_TRSH	BIT(4) + +/* popular locations */ +#define HOST_MBOX   HOSTADDR(RGF_USER_USER_SCRATCH_PAD) +#define HOST_SW_INT (HOSTADDR(RGF_USER_USER_ICR) + \ +	offsetof(struct RGF_ICR, ICS)) +#define SW_INT_MBOX BIT_USER_USER_ICR_SW_INT_2 + +/* ISR register bits */ +#define ISR_MISC_FW_READY BIT_DMA_EP_MISC_ICR_FW_INT0 +#define ISR_MISC_MBOX_EVT BIT_DMA_EP_MISC_ICR_FW_INT1 + +/* Hardware definitions end */ + +struct wil6210_mbox_ring { +	u32 base; +	u16 entry_size; /* max. size of mbox entry, incl. all headers */ +	u16 size; +	u32 tail; +	u32 head; +} __packed; + +struct wil6210_mbox_ring_desc { +	__le32 sync; +	__le32 addr; +} __packed; + +/* at HOST_OFF_WIL6210_MBOX_CTL */ +struct wil6210_mbox_ctl { +	struct wil6210_mbox_ring tx; +	struct wil6210_mbox_ring rx; +} __packed; + +struct wil6210_mbox_hdr { +	__le16 seq; +	__le16 len; /* payload, bytes after this header */ +	__le16 type; +	u8 flags; +	u8 reserved; +} __packed; + +#define WIL_MBOX_HDR_TYPE_WMI (0) + +/* max. value for wil6210_mbox_hdr.len */ +#define MAX_MBOXITEM_SIZE   (240) + +struct wil6210_mbox_hdr_wmi { +	u8 reserved0[2]; +	__le16 id; +	__le16 info1; /* bits [0..3] - device_id, rest - unused */ +	u8 reserved1[2]; +} __packed; + +struct pending_wmi_event { +	struct list_head list; +	struct { +		struct wil6210_mbox_hdr hdr; +		struct wil6210_mbox_hdr_wmi wmi; +		u8 data[0]; +	} __packed event; +}; + +union vring_desc; + +struct vring { +	dma_addr_t pa; +	volatile union vring_desc *va; /* vring_desc[size], WriteBack by DMA */ +	u16 size; /* number of vring_desc elements */ +	u32 swtail; +	u32 swhead; +	u32 hwtail; /* write here to inform hw */ +	void **ctx; /* void *ctx[size] - software context */ +}; + +enum { /* for wil6210_priv.status */ +	wil_status_fwready = 0, +	wil_status_fwconnected, +	wil_status_dontscan, +	wil_status_irqen, /* FIXME: interrupts enabled - for debug */ +}; + +struct pci_dev; + +struct wil6210_stats { +	u64 tsf; +	u32 snr; +	u16 last_mcs_rx; +	u16 bf_mcs; /* last BF, used for Tx */ +	u16 my_rx_sector; +	u16 my_tx_sector; +	u16 peer_rx_sector; +	u16 peer_tx_sector; +}; + +struct wil6210_priv { +	struct pci_dev *pdev; +	int n_msi; +	struct wireless_dev *wdev; +	void __iomem *csr; +	ulong status; +	/* profile */ +	u32 monitor_flags; +	u32 secure_pcp; /* create secure PCP? */ +	int sinfo_gen; +	/* cached ISR registers */ +	u32 isr_misc; +	/* mailbox related */ +	struct mutex wmi_mutex; +	struct wil6210_mbox_ctl mbox_ctl; +	struct completion wmi_ready; +	u16 wmi_seq; +	u16 reply_id; /**< wait for this WMI event */ +	void *reply_buf; +	u16 reply_size; +	struct workqueue_struct *wmi_wq; /* for deferred calls */ +	struct work_struct wmi_event_worker; +	struct workqueue_struct *wmi_wq_conn; /* for connect worker */ +	struct work_struct wmi_connect_worker; +	struct work_struct disconnect_worker; +	struct timer_list connect_timer; +	int pending_connect_cid; +	struct list_head pending_wmi_ev; +	/* +	 * protect pending_wmi_ev +	 * - fill in IRQ from wil6210_irq_misc, +	 * - consumed in thread by wmi_event_worker +	 */ +	spinlock_t wmi_ev_lock; +	/* DMA related */ +	struct vring vring_rx; +	struct vring vring_tx[WIL6210_MAX_TX_RINGS]; +	u8 dst_addr[WIL6210_MAX_TX_RINGS][ETH_ALEN]; +	/* scan */ +	struct cfg80211_scan_request *scan_request; + +	struct mutex mutex; /* for wil6210_priv access in wil_{up|down} */ +	/* statistics */ +	struct wil6210_stats stats; +	/* debugfs */ +	struct dentry *debug; +	struct debugfs_blob_wrapper fw_code_blob; +	struct debugfs_blob_wrapper fw_data_blob; +	struct debugfs_blob_wrapper fw_peri_blob; +	struct debugfs_blob_wrapper uc_code_blob; +	struct debugfs_blob_wrapper uc_data_blob; +	struct debugfs_blob_wrapper rgf_blob; +}; + +#define wil_to_wiphy(i) (i->wdev->wiphy) +#define wil_to_dev(i) (wiphy_dev(wil_to_wiphy(i))) +#define wiphy_to_wil(w) (struct wil6210_priv *)(wiphy_priv(w)) +#define wil_to_wdev(i) (i->wdev) +#define wdev_to_wil(w) (struct wil6210_priv *)(wdev_priv(w)) +#define wil_to_ndev(i) (wil_to_wdev(i)->netdev) +#define ndev_to_wil(n) (wdev_to_wil(n->ieee80211_ptr)) + +#define wil_dbg(wil, fmt, arg...) netdev_dbg(wil_to_ndev(wil), fmt, ##arg) +#define wil_info(wil, fmt, arg...) netdev_info(wil_to_ndev(wil), fmt, ##arg) +#define wil_err(wil, fmt, arg...) netdev_err(wil_to_ndev(wil), fmt, ##arg) + +#define wil_dbg_IRQ(wil, fmt, arg...) wil_dbg(wil, "DBG[ IRQ]" fmt, ##arg) +#define wil_dbg_TXRX(wil, fmt, arg...) wil_dbg(wil, "DBG[TXRX]" fmt, ##arg) +#define wil_dbg_WMI(wil, fmt, arg...) wil_dbg(wil, "DBG[ WMI]" fmt, ##arg) + +#define wil_hex_dump_TXRX(prefix_str, prefix_type, rowsize,	\ +			  groupsize, buf, len, ascii)		\ +			  wil_print_hex_dump_debug("DBG[TXRX]" prefix_str,\ +					 prefix_type, rowsize,	\ +					 groupsize, buf, len, ascii) + +#define wil_hex_dump_WMI(prefix_str, prefix_type, rowsize,	\ +			 groupsize, buf, len, ascii)		\ +			 wil_print_hex_dump_debug("DBG[ WMI]" prefix_str,\ +					prefix_type, rowsize,	\ +					groupsize, buf, len, ascii) + +void wil_memcpy_fromio_32(void *dst, const volatile void __iomem *src, +			  size_t count); +void wil_memcpy_toio_32(volatile void __iomem *dst, const void *src, +			size_t count); + +void *wil_if_alloc(struct device *dev, void __iomem *csr); +void wil_if_free(struct wil6210_priv *wil); +int wil_if_add(struct wil6210_priv *wil); +void wil_if_remove(struct wil6210_priv *wil); +int wil_priv_init(struct wil6210_priv *wil); +void wil_priv_deinit(struct wil6210_priv *wil); +int wil_reset(struct wil6210_priv *wil); +void wil_link_on(struct wil6210_priv *wil); +void wil_link_off(struct wil6210_priv *wil); +int wil_up(struct wil6210_priv *wil); +int wil_down(struct wil6210_priv *wil); +void wil_mbox_ring_le2cpus(struct wil6210_mbox_ring *r); + +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr); +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr); +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, +		 struct wil6210_mbox_hdr *hdr); +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len); +void wmi_recv_cmd(struct wil6210_priv *wil); +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, +	     u16 reply_id, void *reply, u8 reply_size, int to_msec); +void wmi_connect_worker(struct work_struct *work); +void wmi_event_worker(struct work_struct *work); +void wmi_event_flush(struct wil6210_priv *wil); +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid); +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid); +int wmi_set_channel(struct wil6210_priv *wil, int channel); +int wmi_get_channel(struct wil6210_priv *wil, int *channel); +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb); +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, +		       const void *mac_addr); +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, +		       const void *mac_addr, int key_len, const void *key); +int wmi_echo(struct wil6210_priv *wil); +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie); + +int wil6210_init_irq(struct wil6210_priv *wil, int irq); +void wil6210_fini_irq(struct wil6210_priv *wil, int irq); +void wil6210_disable_irq(struct wil6210_priv *wil); +void wil6210_enable_irq(struct wil6210_priv *wil); + +int wil6210_debugfs_init(struct wil6210_priv *wil); +void wil6210_debugfs_remove(struct wil6210_priv *wil); + +struct wireless_dev *wil_cfg80211_init(struct device *dev); +void wil_wdev_free(struct wil6210_priv *wil); + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr); +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype); +void wil6210_disconnect(struct wil6210_priv *wil, void *bssid); + +int wil_rx_init(struct wil6210_priv *wil); +void wil_rx_fini(struct wil6210_priv *wil); + +/* TX API */ +int wil_vring_init_tx(struct wil6210_priv *wil, int id, int size, +		      int cid, int tid); +void wil_vring_fini_tx(struct wil6210_priv *wil, int id); + +netdev_tx_t wil_start_xmit(struct sk_buff *skb, struct net_device *ndev); +void wil_tx_complete(struct wil6210_priv *wil, int ringid); + +/* RX API */ +void wil_rx_handle(struct wil6210_priv *wil); + +int wil_iftype_nl2wmi(enum nl80211_iftype type); + +#endif /* __WIL6210_H__ */ diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c new file mode 100644 index 00000000000..12915f6e761 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -0,0 +1,975 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <linux/pci.h> +#include <linux/io.h> +#include <linux/list.h> +#include <linux/etherdevice.h> + +#include "wil6210.h" +#include "wmi.h" + +/** + * WMI event receiving - theory of operations + * + * When firmware about to report WMI event, it fills memory area + * in the mailbox and raises misc. IRQ. Thread interrupt handler invoked for + * the misc IRQ, function @wmi_recv_cmd called by thread IRQ handler. + * + * @wmi_recv_cmd reads event, allocates memory chunk  and attaches it to the + * event list @wil->pending_wmi_ev. Then, work queue @wil->wmi_wq wakes up + * and handles events within the @wmi_event_worker. Every event get detached + * from list, processed and deleted. + * + * Purpose for this mechanism is to release IRQ thread; otherwise, + * if WMI event handling involves another WMI command flow, this 2-nd flow + * won't be completed because of blocked IRQ thread. + */ + +/** + * Addressing - theory of operations + * + * There are several buses present on the WIL6210 card. + * Same memory areas are visible at different address on + * the different busses. There are 3 main bus masters: + *  - MAC CPU (ucode) + *  - User CPU (firmware) + *  - AHB (host) + * + * On the PCI bus, there is one BAR (BAR0) of 2Mb size, exposing + * AHB addresses starting from 0x880000 + * + * Internally, firmware uses addresses that allows faster access but + * are invisible from the host. To read from these addresses, alternative + * AHB address must be used. + * + * Memory mapping + * Linker address         PCI/Host address + *                        0x880000 .. 0xa80000  2Mb BAR0 + * 0x800000 .. 0x807000   0x900000 .. 0x907000  28k DCCM + * 0x840000 .. 0x857000   0x908000 .. 0x91f000  92k PERIPH + */ + +/** + * @fw_mapping provides memory remapping table + */ +static const struct { +	u32 from; /* linker address - from, inclusive */ +	u32 to;   /* linker address - to, exclusive */ +	u32 host; /* PCI/Host address - BAR0 + 0x880000 */ +} fw_mapping[] = { +	{0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ +	{0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ +	{0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ +	{0x880000, 0x88a000, 0x880000}, /* various RGF */ +	{0x8c0000, 0x932000, 0x8c0000}, /* trivial mapping for upper area */ +	/* +	 * 920000..930000 ucode code RAM +	 * 930000..932000 ucode data RAM +	 */ +}; + +/** + * return AHB address for given firmware/ucode internal (linker) address + * @x - internal address + * If address have no valid AHB mapping, return 0 + */ +static u32 wmi_addr_remap(u32 x) +{ +	uint i; + +	for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { +		if ((x >= fw_mapping[i].from) && (x < fw_mapping[i].to)) +			return x + fw_mapping[i].host - fw_mapping[i].from; +	} + +	return 0; +} + +/** + * Check address validity for WMI buffer; remap if needed + * @ptr - internal (linker) fw/ucode address + * + * Valid buffer should be DWORD aligned + * + * return address for accessing buffer from the host; + * if buffer is not valid, return NULL. + */ +void __iomem *wmi_buffer(struct wil6210_priv *wil, __le32 ptr_) +{ +	u32 off; +	u32 ptr = le32_to_cpu(ptr_); + +	if (ptr % 4) +		return NULL; + +	ptr = wmi_addr_remap(ptr); +	if (ptr < WIL6210_FW_HOST_OFF) +		return NULL; + +	off = HOSTADDR(ptr); +	if (off > WIL6210_MEM_SIZE - 4) +		return NULL; + +	return wil->csr + off; +} + +/** + * Check address validity + */ +void __iomem *wmi_addr(struct wil6210_priv *wil, u32 ptr) +{ +	u32 off; + +	if (ptr % 4) +		return NULL; + +	if (ptr < WIL6210_FW_HOST_OFF) +		return NULL; + +	off = HOSTADDR(ptr); +	if (off > WIL6210_MEM_SIZE - 4) +		return NULL; + +	return wil->csr + off; +} + +int wmi_read_hdr(struct wil6210_priv *wil, __le32 ptr, +		 struct wil6210_mbox_hdr *hdr) +{ +	void __iomem *src = wmi_buffer(wil, ptr); +	if (!src) +		return -EINVAL; + +	wil_memcpy_fromio_32(hdr, src, sizeof(*hdr)); + +	return 0; +} + +static int __wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ +	struct { +		struct wil6210_mbox_hdr hdr; +		struct wil6210_mbox_hdr_wmi wmi; +	} __packed cmd = { +		.hdr = { +			.type = WIL_MBOX_HDR_TYPE_WMI, +			.flags = 0, +			.len = cpu_to_le16(sizeof(cmd.wmi) + len), +		}, +		.wmi = { +			.id = cpu_to_le16(cmdid), +			.info1 = 0, +		}, +	}; +	struct wil6210_mbox_ring *r = &wil->mbox_ctl.tx; +	struct wil6210_mbox_ring_desc d_head; +	u32 next_head; +	void __iomem *dst; +	void __iomem *head = wmi_addr(wil, r->head); +	uint retry; + +	if (sizeof(cmd) + len > r->entry_size) { +		wil_err(wil, "WMI size too large: %d bytes, max is %d\n", +			(int)(sizeof(cmd) + len), r->entry_size); +		return -ERANGE; + +	} + +	might_sleep(); + +	if (!test_bit(wil_status_fwready, &wil->status)) { +		wil_err(wil, "FW not ready\n"); +		return -EAGAIN; +	} + +	if (!head) { +		wil_err(wil, "WMI head is garbage: 0x%08x\n", r->head); +		return -EINVAL; +	} +	/* read Tx head till it is not busy */ +	for (retry = 5; retry > 0; retry--) { +		wil_memcpy_fromio_32(&d_head, head, sizeof(d_head)); +		if (d_head.sync == 0) +			break; +		msleep(20); +	} +	if (d_head.sync != 0) { +		wil_err(wil, "WMI head busy\n"); +		return -EBUSY; +	} +	/* next head */ +	next_head = r->base + ((r->head - r->base + sizeof(d_head)) % r->size); +	wil_dbg_WMI(wil, "Head 0x%08x -> 0x%08x\n", r->head, next_head); +	/* wait till FW finish with previous command */ +	for (retry = 5; retry > 0; retry--) { +		r->tail = ioread32(wil->csr + HOST_MBOX + +				   offsetof(struct wil6210_mbox_ctl, tx.tail)); +		if (next_head != r->tail) +			break; +		msleep(20); +	} +	if (next_head == r->tail) { +		wil_err(wil, "WMI ring full\n"); +		return -EBUSY; +	} +	dst = wmi_buffer(wil, d_head.addr); +	if (!dst) { +		wil_err(wil, "invalid WMI buffer: 0x%08x\n", +			le32_to_cpu(d_head.addr)); +		return -EINVAL; +	} +	cmd.hdr.seq = cpu_to_le16(++wil->wmi_seq); +	/* set command */ +	wil_dbg_WMI(wil, "WMI command 0x%04x [%d]\n", cmdid, len); +	wil_hex_dump_WMI("Cmd ", DUMP_PREFIX_OFFSET, 16, 1, &cmd, +			 sizeof(cmd), true); +	wil_hex_dump_WMI("cmd ", DUMP_PREFIX_OFFSET, 16, 1, buf, +			 len, true); +	wil_memcpy_toio_32(dst, &cmd, sizeof(cmd)); +	wil_memcpy_toio_32(dst + sizeof(cmd), buf, len); +	/* mark entry as full */ +	iowrite32(1, wil->csr + HOSTADDR(r->head) + +		  offsetof(struct wil6210_mbox_ring_desc, sync)); +	/* advance next ptr */ +	iowrite32(r->head = next_head, wil->csr + HOST_MBOX + +		  offsetof(struct wil6210_mbox_ctl, tx.head)); + +	/* interrupt to FW */ +	iowrite32(SW_INT_MBOX, wil->csr + HOST_SW_INT); + +	return 0; +} + +int wmi_send(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len) +{ +	int rc; + +	mutex_lock(&wil->wmi_mutex); +	rc = __wmi_send(wil, cmdid, buf, len); +	mutex_unlock(&wil->wmi_mutex); + +	return rc; +} + +/*=== Event handlers ===*/ +static void wmi_evt_ready(struct wil6210_priv *wil, int id, void *d, int len) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; +	struct wmi_ready_event *evt = d; +	u32 ver = le32_to_cpu(evt->sw_version); + +	wil_dbg_WMI(wil, "FW ver. %d; MAC %pM\n", ver, evt->mac); + +	if (!is_valid_ether_addr(ndev->dev_addr)) { +		memcpy(ndev->dev_addr, evt->mac, ETH_ALEN); +		memcpy(ndev->perm_addr, evt->mac, ETH_ALEN); +	} +	snprintf(wdev->wiphy->fw_version, sizeof(wdev->wiphy->fw_version), +		 "%d", ver); +} + +static void wmi_evt_fw_ready(struct wil6210_priv *wil, int id, void *d, +			     int len) +{ +	wil_dbg_WMI(wil, "WMI: FW ready\n"); + +	set_bit(wil_status_fwready, &wil->status); +	/* reuse wmi_ready for the firmware ready indication */ +	complete(&wil->wmi_ready); +} + +static void wmi_evt_rx_mgmt(struct wil6210_priv *wil, int id, void *d, int len) +{ +	struct wmi_rx_mgmt_packet_event *data = d; +	struct wiphy *wiphy = wil_to_wiphy(wil); +	struct ieee80211_mgmt *rx_mgmt_frame = +			(struct ieee80211_mgmt *)data->payload; +	int ch_no = data->info.channel+1; +	u32 freq = ieee80211_channel_to_frequency(ch_no, +			IEEE80211_BAND_60GHZ); +	struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq); +	/* TODO convert LE to CPU */ +	s32 signal = 0; /* TODO */ +	__le16 fc = rx_mgmt_frame->frame_control; +	u32 d_len = le32_to_cpu(data->info.len); +	u16 d_status = le16_to_cpu(data->info.status); + +	wil_dbg_WMI(wil, "MGMT: channel %d MCS %d SNR %d\n", +		    data->info.channel, data->info.mcs, data->info.snr); +	wil_dbg_WMI(wil, "status 0x%04x len %d stype %04x\n", d_status, d_len, +		    le16_to_cpu(data->info.stype)); +	wil_dbg_WMI(wil, "qid %d mid %d cid %d\n", +		    data->info.qid, data->info.mid, data->info.cid); + +	if (!channel) { +		wil_err(wil, "Frame on unsupported channel\n"); +		return; +	} + +	if (ieee80211_is_beacon(fc) || ieee80211_is_probe_resp(fc)) { +		struct cfg80211_bss *bss; +		u64 tsf = le64_to_cpu(rx_mgmt_frame->u.beacon.timestamp); +		u16 cap = le16_to_cpu(rx_mgmt_frame->u.beacon.capab_info); +		u16 bi = le16_to_cpu(rx_mgmt_frame->u.beacon.beacon_int); +		const u8 *ie_buf = rx_mgmt_frame->u.beacon.variable; +		size_t ie_len = d_len - offsetof(struct ieee80211_mgmt, +						 u.beacon.variable); +		wil_dbg_WMI(wil, "Capability info : 0x%04x\n", cap); + +		bss = cfg80211_inform_bss(wiphy, channel, rx_mgmt_frame->bssid, +					  tsf, cap, bi, ie_buf, ie_len, +					  signal, GFP_KERNEL); +		if (bss) { +			wil_dbg_WMI(wil, "Added BSS %pM\n", +				    rx_mgmt_frame->bssid); +			cfg80211_put_bss(bss); +		} else { +			wil_err(wil, "cfg80211_inform_bss() failed\n"); +		} +	} +} + +static void wmi_evt_scan_complete(struct wil6210_priv *wil, int id, +				  void *d, int len) +{ +	if (wil->scan_request) { +		struct wmi_scan_complete_event *data = d; +		bool aborted = (data->status != 0); + +		wil_dbg_WMI(wil, "SCAN_COMPLETE(0x%08x)\n", data->status); +		cfg80211_scan_done(wil->scan_request, aborted); +		wil->scan_request = NULL; +	} else { +		wil_err(wil, "SCAN_COMPLETE while not scanning\n"); +	} +} + +static void wmi_evt_connect(struct wil6210_priv *wil, int id, void *d, int len) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct wireless_dev *wdev = wil->wdev; +	struct wmi_connect_event *evt = d; +	int ch; /* channel number */ +	struct station_info sinfo; +	u8 *assoc_req_ie, *assoc_resp_ie; +	size_t assoc_req_ielen, assoc_resp_ielen; +	/* capinfo(u16) + listen_interval(u16) + IEs */ +	const size_t assoc_req_ie_offset = sizeof(u16) * 2; +	/* capinfo(u16) + status_code(u16) + associd(u16) + IEs */ +	const size_t assoc_resp_ie_offset = sizeof(u16) * 3; + +	if (len < sizeof(*evt)) { +		wil_err(wil, "Connect event too short : %d bytes\n", len); +		return; +	} +	if (len != sizeof(*evt) + evt->beacon_ie_len + evt->assoc_req_len + +		   evt->assoc_resp_len) { +		wil_err(wil, +			"Connect event corrupted : %d != %d + %d + %d + %d\n", +			len, (int)sizeof(*evt), evt->beacon_ie_len, +			evt->assoc_req_len, evt->assoc_resp_len); +		return; +	} +	ch = evt->channel + 1; +	wil_dbg_WMI(wil, "Connect %pM channel [%d] cid %d\n", +		    evt->bssid, ch, evt->cid); +	wil_hex_dump_WMI("connect AI : ", DUMP_PREFIX_OFFSET, 16, 1, +			 evt->assoc_info, len - sizeof(*evt), true); + +	/* figure out IE's */ +	assoc_req_ie = &evt->assoc_info[evt->beacon_ie_len + +					assoc_req_ie_offset]; +	assoc_req_ielen = evt->assoc_req_len - assoc_req_ie_offset; +	if (evt->assoc_req_len <= assoc_req_ie_offset) { +		assoc_req_ie = NULL; +		assoc_req_ielen = 0; +	} + +	assoc_resp_ie = &evt->assoc_info[evt->beacon_ie_len + +					 evt->assoc_req_len + +					 assoc_resp_ie_offset]; +	assoc_resp_ielen = evt->assoc_resp_len - assoc_resp_ie_offset; +	if (evt->assoc_resp_len <= assoc_resp_ie_offset) { +		assoc_resp_ie = NULL; +		assoc_resp_ielen = 0; +	} + +	if ((wdev->iftype == NL80211_IFTYPE_STATION) || +	    (wdev->iftype == NL80211_IFTYPE_P2P_CLIENT)) { +		if (wdev->sme_state != CFG80211_SME_CONNECTING) { +			wil_err(wil, "Not in connecting state\n"); +			return; +		} +		del_timer_sync(&wil->connect_timer); +		cfg80211_connect_result(ndev, evt->bssid, +					assoc_req_ie, assoc_req_ielen, +					assoc_resp_ie, assoc_resp_ielen, +					WLAN_STATUS_SUCCESS, GFP_KERNEL); + +	} else if ((wdev->iftype == NL80211_IFTYPE_AP) || +		   (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { +		memset(&sinfo, 0, sizeof(sinfo)); + +		sinfo.generation = wil->sinfo_gen++; + +		if (assoc_req_ie) { +			sinfo.assoc_req_ies = assoc_req_ie; +			sinfo.assoc_req_ies_len = assoc_req_ielen; +			sinfo.filled |= STATION_INFO_ASSOC_REQ_IES; +		} + +		cfg80211_new_sta(ndev, evt->bssid, &sinfo, GFP_KERNEL); +	} +	set_bit(wil_status_fwconnected, &wil->status); + +	/* FIXME FW can transmit only ucast frames to peer */ +	/* FIXME real ring_id instead of hard coded 0 */ +	memcpy(wil->dst_addr[0], evt->bssid, ETH_ALEN); + +	wil->pending_connect_cid = evt->cid; +	queue_work(wil->wmi_wq_conn, &wil->wmi_connect_worker); +} + +static void wmi_evt_disconnect(struct wil6210_priv *wil, int id, +			       void *d, int len) +{ +	struct wmi_disconnect_event *evt = d; + +	wil_dbg_WMI(wil, "Disconnect %pM reason %d proto %d wmi\n", +		    evt->bssid, +		    evt->protocol_reason_status, evt->disconnect_reason); + +	wil->sinfo_gen++; + +	wil6210_disconnect(wil, evt->bssid); +	clear_bit(wil_status_dontscan, &wil->status); +} + +static void wmi_evt_notify(struct wil6210_priv *wil, int id, void *d, int len) +{ +	struct wmi_notify_req_done_event *evt = d; + +	if (len < sizeof(*evt)) { +		wil_err(wil, "Short NOTIFY event\n"); +		return; +	} + +	wil->stats.tsf = le64_to_cpu(evt->tsf); +	wil->stats.snr = le32_to_cpu(evt->snr_val); +	wil->stats.bf_mcs = le16_to_cpu(evt->bf_mcs); +	wil->stats.my_rx_sector = le16_to_cpu(evt->my_rx_sector); +	wil->stats.my_tx_sector = le16_to_cpu(evt->my_tx_sector); +	wil->stats.peer_rx_sector = le16_to_cpu(evt->other_rx_sector); +	wil->stats.peer_tx_sector = le16_to_cpu(evt->other_tx_sector); +	wil_dbg_WMI(wil, "Link status, MCS %d TSF 0x%016llx\n" +		    "BF status 0x%08x SNR 0x%08x\n" +		    "Tx Tpt %d goodput %d Rx goodput %d\n" +		    "Sectors(rx:tx) my %d:%d peer %d:%d\n", +		    wil->stats.bf_mcs, wil->stats.tsf, evt->status, +		    wil->stats.snr, le32_to_cpu(evt->tx_tpt), +		    le32_to_cpu(evt->tx_goodput), le32_to_cpu(evt->rx_goodput), +		    wil->stats.my_rx_sector, wil->stats.my_tx_sector, +		    wil->stats.peer_rx_sector, wil->stats.peer_tx_sector); +} + +/* + * Firmware reports EAPOL frame using WME event. + * Reconstruct Ethernet frame and deliver it via normal Rx + */ +static void wmi_evt_eapol_rx(struct wil6210_priv *wil, int id, +			     void *d, int len) +{ +	struct net_device *ndev = wil_to_ndev(wil); +	struct wmi_eapol_rx_event *evt = d; +	u16 eapol_len = le16_to_cpu(evt->eapol_len); +	int sz = eapol_len + ETH_HLEN; +	struct sk_buff *skb; +	struct ethhdr *eth; + +	wil_dbg_WMI(wil, "EAPOL len %d from %pM\n", eapol_len, +		    evt->src_mac); + +	if (eapol_len > 196) { /* TODO: revisit size limit */ +		wil_err(wil, "EAPOL too large\n"); +		return; +	} + +	skb = alloc_skb(sz, GFP_KERNEL); +	if (!skb) { +		wil_err(wil, "Failed to allocate skb\n"); +		return; +	} +	eth = (struct ethhdr *)skb_put(skb, ETH_HLEN); +	memcpy(eth->h_dest, ndev->dev_addr, ETH_ALEN); +	memcpy(eth->h_source, evt->src_mac, ETH_ALEN); +	eth->h_proto = cpu_to_be16(ETH_P_PAE); +	memcpy(skb_put(skb, eapol_len), evt->eapol, eapol_len); +	skb->protocol = eth_type_trans(skb, ndev); +	if (likely(netif_rx_ni(skb) == NET_RX_SUCCESS)) { +		ndev->stats.rx_packets++; +		ndev->stats.rx_bytes += skb->len; +	} else { +		ndev->stats.rx_dropped++; +	} +} + +static const struct { +	int eventid; +	void (*handler)(struct wil6210_priv *wil, int eventid, +			void *data, int data_len); +} wmi_evt_handlers[] = { +	{WMI_READY_EVENTID,		wmi_evt_ready}, +	{WMI_FW_READY_EVENTID,		wmi_evt_fw_ready}, +	{WMI_RX_MGMT_PACKET_EVENTID,	wmi_evt_rx_mgmt}, +	{WMI_SCAN_COMPLETE_EVENTID,	wmi_evt_scan_complete}, +	{WMI_CONNECT_EVENTID,		wmi_evt_connect}, +	{WMI_DISCONNECT_EVENTID,	wmi_evt_disconnect}, +	{WMI_NOTIFY_REQ_DONE_EVENTID,	wmi_evt_notify}, +	{WMI_EAPOL_RX_EVENTID,		wmi_evt_eapol_rx}, +}; + +/* + * Run in IRQ context + * Extract WMI command from mailbox. Queue it to the @wil->pending_wmi_ev + * that will be eventually handled by the @wmi_event_worker in the thread + * context of thread "wil6210_wmi" + */ +void wmi_recv_cmd(struct wil6210_priv *wil) +{ +	struct wil6210_mbox_ring_desc d_tail; +	struct wil6210_mbox_hdr hdr; +	struct wil6210_mbox_ring *r = &wil->mbox_ctl.rx; +	struct pending_wmi_event *evt; +	u8 *cmd; +	void __iomem *src; +	ulong flags; + +	for (;;) { +		u16 len; + +		r->head = ioread32(wil->csr + HOST_MBOX + +				   offsetof(struct wil6210_mbox_ctl, rx.head)); +		if (r->tail == r->head) +			return; + +		/* read cmd from tail */ +		wil_memcpy_fromio_32(&d_tail, wil->csr + HOSTADDR(r->tail), +				     sizeof(struct wil6210_mbox_ring_desc)); +		if (d_tail.sync == 0) { +			wil_err(wil, "Mbox evt not owned by FW?\n"); +			return; +		} + +		if (0 != wmi_read_hdr(wil, d_tail.addr, &hdr)) { +			wil_err(wil, "Mbox evt at 0x%08x?\n", +				le32_to_cpu(d_tail.addr)); +			return; +		} + +		len = le16_to_cpu(hdr.len); +		src = wmi_buffer(wil, d_tail.addr) + +		      sizeof(struct wil6210_mbox_hdr); +		evt = kmalloc(ALIGN(offsetof(struct pending_wmi_event, +					     event.wmi) + len, 4), +			      GFP_KERNEL); +		if (!evt) { +			wil_err(wil, "kmalloc for WMI event (%d) failed\n", +				len); +			return; +		} +		evt->event.hdr = hdr; +		cmd = (void *)&evt->event.wmi; +		wil_memcpy_fromio_32(cmd, src, len); +		/* mark entry as empty */ +		iowrite32(0, wil->csr + HOSTADDR(r->tail) + +			  offsetof(struct wil6210_mbox_ring_desc, sync)); +		/* indicate */ +		wil_dbg_WMI(wil, "Mbox evt %04x %04x %04x %02x\n", +			    le16_to_cpu(hdr.seq), len, le16_to_cpu(hdr.type), +			    hdr.flags); +		if ((hdr.type == WIL_MBOX_HDR_TYPE_WMI) && +		    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { +			wil_dbg_WMI(wil, "WMI event 0x%04x\n", +				    evt->event.wmi.id); +		} +		wil_hex_dump_WMI("evt ", DUMP_PREFIX_OFFSET, 16, 1, +				 &evt->event.hdr, sizeof(hdr) + len, true); + +		/* advance tail */ +		r->tail = r->base + ((r->tail - r->base + +			  sizeof(struct wil6210_mbox_ring_desc)) % r->size); +		iowrite32(r->tail, wil->csr + HOST_MBOX + +			  offsetof(struct wil6210_mbox_ctl, rx.tail)); + +		/* add to the pending list */ +		spin_lock_irqsave(&wil->wmi_ev_lock, flags); +		list_add_tail(&evt->list, &wil->pending_wmi_ev); +		spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); +		{ +			int q =	queue_work(wil->wmi_wq, +					   &wil->wmi_event_worker); +			wil_dbg_WMI(wil, "queue_work -> %d\n", q); +		} +	} +} + +int wmi_call(struct wil6210_priv *wil, u16 cmdid, void *buf, u16 len, +	     u16 reply_id, void *reply, u8 reply_size, int to_msec) +{ +	int rc; +	int remain; + +	mutex_lock(&wil->wmi_mutex); + +	rc = __wmi_send(wil, cmdid, buf, len); +	if (rc) +		goto out; + +	wil->reply_id = reply_id; +	wil->reply_buf = reply; +	wil->reply_size = reply_size; +	remain = wait_for_completion_timeout(&wil->wmi_ready, +			msecs_to_jiffies(to_msec)); +	if (0 == remain) { +		wil_err(wil, "wmi_call(0x%04x->0x%04x) timeout %d msec\n", +			cmdid, reply_id, to_msec); +		rc = -ETIME; +	} else { +		wil_dbg_WMI(wil, +			    "wmi_call(0x%04x->0x%04x) completed in %d msec\n", +			    cmdid, reply_id, +			    to_msec - jiffies_to_msecs(remain)); +	} +	wil->reply_id = 0; +	wil->reply_buf = NULL; +	wil->reply_size = 0; + out: +	mutex_unlock(&wil->wmi_mutex); + +	return rc; +} + +int wmi_echo(struct wil6210_priv *wil) +{ +	struct wmi_echo_cmd cmd = { +		.value = cpu_to_le32(0x12345678), +	}; + +	return wmi_call(wil, WMI_ECHO_CMDID, &cmd, sizeof(cmd), +			 WMI_ECHO_RSP_EVENTID, NULL, 0, 20); +} + +int wmi_set_mac_address(struct wil6210_priv *wil, void *addr) +{ +	struct wmi_set_mac_address_cmd cmd; + +	memcpy(cmd.mac, addr, ETH_ALEN); + +	wil_dbg_WMI(wil, "Set MAC %pM\n", addr); + +	return wmi_send(wil, WMI_SET_MAC_ADDRESS_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_bcon(struct wil6210_priv *wil, int bi, u8 wmi_nettype) +{ +	struct wmi_bcon_ctrl_cmd cmd = { +		.bcon_interval = cpu_to_le16(bi), +		.network_type = wmi_nettype, +		.disable_sec_offload = 1, +	}; + +	if (!wil->secure_pcp) +		cmd.disable_sec = 1; + +	return wmi_send(wil, WMI_BCON_CTRL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ssid(struct wil6210_priv *wil, u8 ssid_len, const void *ssid) +{ +	struct wmi_set_ssid_cmd cmd = { +		.ssid_len = cpu_to_le32(ssid_len), +	}; + +	if (ssid_len > sizeof(cmd.ssid)) +		return -EINVAL; + +	memcpy(cmd.ssid, ssid, ssid_len); + +	return wmi_send(wil, WMI_SET_SSID_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_ssid(struct wil6210_priv *wil, u8 *ssid_len, void *ssid) +{ +	int rc; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_set_ssid_cmd cmd; +	} __packed reply; +	int len; /* reply.cmd.ssid_len in CPU order */ + +	rc = wmi_call(wil, WMI_GET_SSID_CMDID, NULL, 0, WMI_GET_SSID_EVENTID, +		      &reply, sizeof(reply), 20); +	if (rc) +		return rc; + +	len = le32_to_cpu(reply.cmd.ssid_len); +	if (len > sizeof(reply.cmd.ssid)) +		return -EINVAL; + +	*ssid_len = len; +	memcpy(ssid, reply.cmd.ssid, len); + +	return 0; +} + +int wmi_set_channel(struct wil6210_priv *wil, int channel) +{ +	struct wmi_set_pcp_channel_cmd cmd = { +		.channel = channel - 1, +	}; + +	return wmi_send(wil, WMI_SET_PCP_CHANNEL_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_get_channel(struct wil6210_priv *wil, int *channel) +{ +	int rc; +	struct { +		struct wil6210_mbox_hdr_wmi wmi; +		struct wmi_set_pcp_channel_cmd cmd; +	} __packed reply; + +	rc = wmi_call(wil, WMI_GET_PCP_CHANNEL_CMDID, NULL, 0, +		      WMI_GET_PCP_CHANNEL_EVENTID, &reply, sizeof(reply), 20); +	if (rc) +		return rc; + +	if (reply.cmd.channel > 3) +		return -EINVAL; + +	*channel = reply.cmd.channel + 1; + +	return 0; +} + +int wmi_tx_eapol(struct wil6210_priv *wil, struct sk_buff *skb) +{ +	struct wmi_eapol_tx_cmd *cmd; +	struct ethhdr *eth; +	u16 eapol_len = skb->len - ETH_HLEN; +	void *eapol = skb->data + ETH_HLEN; +	uint i; +	int rc; + +	skb_set_mac_header(skb, 0); +	eth = eth_hdr(skb); +	wil_dbg_WMI(wil, "EAPOL %d bytes to %pM\n", eapol_len, eth->h_dest); +	for (i = 0; i < ARRAY_SIZE(wil->vring_tx); i++) { +		if (memcmp(wil->dst_addr[i], eth->h_dest, ETH_ALEN) == 0) +			goto found_dest; +	} + +	return -EINVAL; + + found_dest: +	/* find out eapol data & len */ +	cmd = kzalloc(sizeof(*cmd) + eapol_len, GFP_KERNEL); +	if (!cmd) +		return -EINVAL; + +	memcpy(cmd->dst_mac, eth->h_dest, ETH_ALEN); +	cmd->eapol_len = cpu_to_le16(eapol_len); +	memcpy(cmd->eapol, eapol, eapol_len); +	rc = wmi_send(wil, WMI_EAPOL_TX_CMDID, cmd, sizeof(*cmd) + eapol_len); +	kfree(cmd); + +	return rc; +} + +int wmi_del_cipher_key(struct wil6210_priv *wil, u8 key_index, +		       const void *mac_addr) +{ +	struct wmi_delete_cipher_key_cmd cmd = { +		.key_index = key_index, +	}; + +	if (mac_addr) +		memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + +	return wmi_send(wil, WMI_DELETE_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_add_cipher_key(struct wil6210_priv *wil, u8 key_index, +		       const void *mac_addr, int key_len, const void *key) +{ +	struct wmi_add_cipher_key_cmd cmd = { +		.key_index = key_index, +		.key_usage = WMI_KEY_USE_PAIRWISE, +		.key_len = key_len, +	}; + +	if (!key || (key_len > sizeof(cmd.key))) +		return -EINVAL; + +	memcpy(cmd.key, key, key_len); +	if (mac_addr) +		memcpy(cmd.mac, mac_addr, WMI_MAC_LEN); + +	return wmi_send(wil, WMI_ADD_CIPHER_KEY_CMDID, &cmd, sizeof(cmd)); +} + +int wmi_set_ie(struct wil6210_priv *wil, u8 type, u16 ie_len, const void *ie) +{ +	int rc; +	u16 len = sizeof(struct wmi_set_appie_cmd) + ie_len; +	struct wmi_set_appie_cmd *cmd = kzalloc(len, GFP_KERNEL); +	if (!cmd) { +		wil_err(wil, "kmalloc(%d) failed\n", len); +		return -ENOMEM; +	} + +	cmd->mgmt_frm_type = type; +	/* BUG: FW API define ieLen as u8. Will fix FW */ +	cmd->ie_len = cpu_to_le16(ie_len); +	memcpy(cmd->ie_info, ie, ie_len); +	rc = wmi_send(wil, WMI_SET_APPIE_CMDID, &cmd, len); +	kfree(cmd); + +	return rc; +} + +void wmi_event_flush(struct wil6210_priv *wil) +{ +	struct pending_wmi_event *evt, *t; + +	wil_dbg_WMI(wil, "%s()\n", __func__); + +	list_for_each_entry_safe(evt, t, &wil->pending_wmi_ev, list) { +		list_del(&evt->list); +		kfree(evt); +	} +} + +static bool wmi_evt_call_handler(struct wil6210_priv *wil, int id, +				 void *d, int len) +{ +	uint i; + +	for (i = 0; i < ARRAY_SIZE(wmi_evt_handlers); i++) { +		if (wmi_evt_handlers[i].eventid == id) { +			wmi_evt_handlers[i].handler(wil, id, d, len); +			return true; +		} +	} + +	return false; +} + +static void wmi_event_handle(struct wil6210_priv *wil, +			     struct wil6210_mbox_hdr *hdr) +{ +	u16 len = le16_to_cpu(hdr->len); + +	if ((hdr->type == WIL_MBOX_HDR_TYPE_WMI) && +	    (len >= sizeof(struct wil6210_mbox_hdr_wmi))) { +		struct wil6210_mbox_hdr_wmi *wmi = (void *)(&hdr[1]); +		void *evt_data = (void *)(&wmi[1]); +		u16 id = le16_to_cpu(wmi->id); +		/* check if someone waits for this event */ +		if (wil->reply_id && wil->reply_id == id) { +			if (wil->reply_buf) { +				memcpy(wil->reply_buf, wmi, +				       min(len, wil->reply_size)); +			} else { +				wmi_evt_call_handler(wil, id, evt_data, +						     len - sizeof(*wmi)); +			} +			wil_dbg_WMI(wil, "Complete WMI 0x%04x\n", id); +			complete(&wil->wmi_ready); +			return; +		} +		/* unsolicited event */ +		/* search for handler */ +		if (!wmi_evt_call_handler(wil, id, evt_data, +					  len - sizeof(*wmi))) { +			wil_err(wil, "Unhandled event 0x%04x\n", id); +		} +	} else { +		wil_err(wil, "Unknown event type\n"); +		print_hex_dump(KERN_ERR, "evt?? ", DUMP_PREFIX_OFFSET, 16, 1, +			       hdr, sizeof(*hdr) + len, true); +	} +} + +/* + * Retrieve next WMI event from the pending list + */ +static struct list_head *next_wmi_ev(struct wil6210_priv *wil) +{ +	ulong flags; +	struct list_head *ret = NULL; + +	spin_lock_irqsave(&wil->wmi_ev_lock, flags); + +	if (!list_empty(&wil->pending_wmi_ev)) { +		ret = wil->pending_wmi_ev.next; +		list_del(ret); +	} + +	spin_unlock_irqrestore(&wil->wmi_ev_lock, flags); + +	return ret; +} + +/* + * Handler for the WMI events + */ +void wmi_event_worker(struct work_struct *work) +{ +	struct wil6210_priv *wil = container_of(work, struct wil6210_priv, +						 wmi_event_worker); +	struct pending_wmi_event *evt; +	struct list_head *lh; + +	while ((lh = next_wmi_ev(wil)) != NULL) { +		evt = list_entry(lh, struct pending_wmi_event, list); +		wmi_event_handle(wil, &evt->event.hdr); +		kfree(evt); +	} +} + +void wmi_connect_worker(struct work_struct *work) +{ +	int rc; +	struct wil6210_priv *wil = container_of(work, struct wil6210_priv, +						wmi_connect_worker); + +	if (wil->pending_connect_cid < 0) { +		wil_err(wil, "No connection pending\n"); +		return; +	} + +	wil_dbg_WMI(wil, "Configure for connection CID %d\n", +		    wil->pending_connect_cid); + +	rc = wil_vring_init_tx(wil, 0, WIL6210_TX_RING_SIZE, +			       wil->pending_connect_cid, 0); +	wil->pending_connect_cid = -1; +	if (rc == 0) +		wil_link_on(wil); +} diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h new file mode 100644 index 00000000000..3bbf87572b0 --- /dev/null +++ b/drivers/net/wireless/ath/wil6210/wmi.h @@ -0,0 +1,1116 @@ +/* + * Copyright (c) 2012 Qualcomm Atheros, Inc. + * Copyright (c) 2006-2012 Wilocity . + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * This file contains the definitions of the WMI protocol specified in the + * Wireless Module Interface (WMI) for the Wilocity + * MARLON 60 Gigabit wireless solution. + * It includes definitions of all the commands and events. + * Commands are messages from the host to the WM. + * Events are messages from the WM to the host. + */ + +#ifndef __WILOCITY_WMI_H__ +#define __WILOCITY_WMI_H__ + +/* General */ + +#define WMI_MAC_LEN		(6) +#define WMI_PROX_RANGE_NUM	(3) + +/* List of Commands */ +enum wmi_command_id { +	WMI_CONNECT_CMDID		= 0x0001, +	WMI_DISCONNECT_CMDID		= 0x0003, +	WMI_START_SCAN_CMDID		= 0x0007, +	WMI_SET_BSS_FILTER_CMDID	= 0x0009, +	WMI_SET_PROBED_SSID_CMDID	= 0x000a, +	WMI_SET_LISTEN_INT_CMDID	= 0x000b, +	WMI_BCON_CTRL_CMDID		= 0x000f, +	WMI_ADD_CIPHER_KEY_CMDID	= 0x0016, +	WMI_DELETE_CIPHER_KEY_CMDID	= 0x0017, +	WMI_SET_APPIE_CMDID		= 0x003f, +	WMI_GET_APPIE_CMDID		= 0x0040, +	WMI_SET_WSC_STATUS_CMDID	= 0x0041, +	WMI_PXMT_RANGE_CFG_CMDID	= 0x0042, +	WMI_PXMT_SNR2_RANGE_CFG_CMDID	= 0x0043, +	WMI_FAST_MEM_ACC_MODE_CMDID	= 0x0300, +	WMI_MEM_READ_CMDID		= 0x0800, +	WMI_MEM_WR_CMDID		= 0x0801, +	WMI_ECHO_CMDID			= 0x0803, +	WMI_DEEP_ECHO_CMDID		= 0x0804, +	WMI_CONFIG_MAC_CMDID		= 0x0805, +	WMI_CONFIG_PHY_DEBUG_CMDID	= 0x0806, +	WMI_ADD_STATION_CMDID		= 0x0807, +	WMI_ADD_DEBUG_TX_PCKT_CMDID	= 0x0808, +	WMI_PHY_GET_STATISTICS_CMDID	= 0x0809, +	WMI_FS_TUNE_CMDID		= 0x080a, +	WMI_CORR_MEASURE_CMDID		= 0x080b, +	WMI_TEMP_SENSE_CMDID		= 0x080e, +	WMI_DC_CALIB_CMDID		= 0x080f, +	WMI_SEND_TONE_CMDID		= 0x0810, +	WMI_IQ_TX_CALIB_CMDID		= 0x0811, +	WMI_IQ_RX_CALIB_CMDID		= 0x0812, +	WMI_SET_UCODE_IDLE_CMDID	= 0x0813, +	WMI_SET_WORK_MODE_CMDID		= 0x0815, +	WMI_LO_LEAKAGE_CALIB_CMDID	= 0x0816, +	WMI_MARLON_R_ACTIVATE_CMDID	= 0x0817, +	WMI_MARLON_R_READ_CMDID		= 0x0818, +	WMI_MARLON_R_WRITE_CMDID	= 0x0819, +	WMI_MARLON_R_TXRX_SEL_CMDID	= 0x081a, +	MAC_IO_STATIC_PARAMS_CMDID	= 0x081b, +	MAC_IO_DYNAMIC_PARAMS_CMDID	= 0x081c, +	WMI_SILENT_RSSI_CALIB_CMDID	= 0x081d, +	WMI_CFG_RX_CHAIN_CMDID		= 0x0820, +	WMI_VRING_CFG_CMDID		= 0x0821, +	WMI_RX_ON_CMDID			= 0x0822, +	WMI_VRING_BA_EN_CMDID		= 0x0823, +	WMI_VRING_BA_DIS_CMDID		= 0x0824, +	WMI_RCP_ADDBA_RESP_CMDID	= 0x0825, +	WMI_RCP_DELBA_CMDID		= 0x0826, +	WMI_SET_SSID_CMDID		= 0x0827, +	WMI_GET_SSID_CMDID		= 0x0828, +	WMI_SET_PCP_CHANNEL_CMDID	= 0x0829, +	WMI_GET_PCP_CHANNEL_CMDID	= 0x082a, +	WMI_SW_TX_REQ_CMDID		= 0x082b, +	WMI_RX_OFF_CMDID		= 0x082c, +	WMI_READ_MAC_RXQ_CMDID		= 0x0830, +	WMI_READ_MAC_TXQ_CMDID		= 0x0831, +	WMI_WRITE_MAC_RXQ_CMDID		= 0x0832, +	WMI_WRITE_MAC_TXQ_CMDID		= 0x0833, +	WMI_WRITE_MAC_XQ_FIELD_CMDID	= 0x0834, +	WMI_MLME_PUSH_CMDID		= 0x0835, +	WMI_BEAMFORMING_MGMT_CMDID	= 0x0836, +	WMI_BF_TXSS_MGMT_CMDID		= 0x0837, +	WMI_BF_SM_MGMT_CMDID		= 0x0838, +	WMI_BF_RXSS_MGMT_CMDID		= 0x0839, +	WMI_SET_SECTORS_CMDID		= 0x0849, +	WMI_MAINTAIN_PAUSE_CMDID	= 0x0850, +	WMI_MAINTAIN_RESUME_CMDID	= 0x0851, +	WMI_RS_MGMT_CMDID		= 0x0852, +	WMI_RF_MGMT_CMDID		= 0x0853, +	/* Performance monitoring commands */ +	WMI_BF_CTRL_CMDID		= 0x0862, +	WMI_NOTIFY_REQ_CMDID		= 0x0863, +	WMI_GET_STATUS_CMDID		= 0x0864, +	WMI_UNIT_TEST_CMDID		= 0x0900, +	WMI_HICCUP_CMDID		= 0x0901, +	WMI_FLASH_READ_CMDID		= 0x0902, +	WMI_FLASH_WRITE_CMDID		= 0x0903, +	WMI_SECURITY_UNIT_TEST_CMDID	= 0x0904, + +	WMI_SET_MAC_ADDRESS_CMDID	= 0xf003, +	WMI_ABORT_SCAN_CMDID		= 0xf007, +	WMI_SET_PMK_CMDID		= 0xf028, + +	WMI_SET_PROMISCUOUS_MODE_CMDID	= 0xf041, +	WMI_GET_PMK_CMDID		= 0xf048, +	WMI_SET_PASSPHRASE_CMDID	= 0xf049, +	WMI_SEND_ASSOC_RES_CMDID	= 0xf04a, +	WMI_SET_ASSOC_REQ_RELAY_CMDID	= 0xf04b, +	WMI_EAPOL_TX_CMDID		= 0xf04c, +	WMI_MAC_ADDR_REQ_CMDID		= 0xf04d, +	WMI_FW_VER_CMDID		= 0xf04e, +}; + +/* + * Commands data structures + */ + +/* + * Frame Types + */ +enum wmi_mgmt_frame_type { +	WMI_FRAME_BEACON	= 0, +	WMI_FRAME_PROBE_REQ	= 1, +	WMI_FRAME_PROBE_RESP	= 2, +	WMI_FRAME_ASSOC_REQ	= 3, +	WMI_FRAME_ASSOC_RESP	= 4, +	WMI_NUM_MGMT_FRAME, +}; + +/* + * WMI_CONNECT_CMDID + */ +enum wmi_network_type { +	WMI_NETTYPE_INFRA		= 0x01, +	WMI_NETTYPE_ADHOC		= 0x02, +	WMI_NETTYPE_ADHOC_CREATOR	= 0x04, +	WMI_NETTYPE_AP			= 0x10, +	WMI_NETTYPE_P2P			= 0x20, +	WMI_NETTYPE_WBE			= 0x40, /* PCIE over 60g */ +}; + +enum wmi_dot11_auth_mode { +	WMI_AUTH11_OPEN			= 0x01, +	WMI_AUTH11_SHARED		= 0x02, +	WMI_AUTH11_LEAP			= 0x04, +	WMI_AUTH11_WSC			= 0x08, +}; + +enum wmi_auth_mode { +	WMI_AUTH_NONE			= 0x01, +	WMI_AUTH_WPA			= 0x02, +	WMI_AUTH_WPA2			= 0x04, +	WMI_AUTH_WPA_PSK		= 0x08, +	WMI_AUTH_WPA2_PSK		= 0x10, +	WMI_AUTH_WPA_CCKM		= 0x20, +	WMI_AUTH_WPA2_CCKM		= 0x40, +}; + +enum wmi_crypto_type { +	WMI_CRYPT_NONE			= 0x01, +	WMI_CRYPT_WEP			= 0x02, +	WMI_CRYPT_TKIP			= 0x04, +	WMI_CRYPT_AES			= 0x08, +	WMI_CRYPT_AES_GCMP		= 0x20, +}; + + +enum wmi_connect_ctrl_flag_bits { +	WMI_CONNECT_ASSOC_POLICY_USER		= 0x0001, +	WMI_CONNECT_SEND_REASSOC		= 0x0002, +	WMI_CONNECT_IGNORE_WPAx_GROUP_CIPHER	= 0x0004, +	WMI_CONNECT_PROFILE_MATCH_DONE		= 0x0008, +	WMI_CONNECT_IGNORE_AAC_BEACON		= 0x0010, +	WMI_CONNECT_CSA_FOLLOW_BSS		= 0x0020, +	WMI_CONNECT_DO_WPA_OFFLOAD		= 0x0040, +	WMI_CONNECT_DO_NOT_DEAUTH		= 0x0080, +}; + +#define WMI_MAX_SSID_LEN    (32) + +struct wmi_connect_cmd { +	u8 network_type; +	u8 dot11_auth_mode; +	u8 auth_mode; +	u8 pairwise_crypto_type; +	u8 pairwise_crypto_len; +	u8 group_crypto_type; +	u8 group_crypto_len; +	u8 ssid_len; +	u8 ssid[WMI_MAX_SSID_LEN]; +	u8 channel; +	u8 reserved0; +	u8 bssid[WMI_MAC_LEN]; +	__le32 ctrl_flags; +	u8 dst_mac[WMI_MAC_LEN]; +	u8 reserved1[2]; +} __packed; + + +/* + * WMI_RECONNECT_CMDID + */ +struct wmi_reconnect_cmd { +	u8 channel;			/* hint */ +	u8 reserved; +	u8 bssid[WMI_MAC_LEN];		/* mandatory if set */ +} __packed; + + +/* + * WMI_SET_PMK_CMDID + */ + +#define WMI_MIN_KEY_INDEX	(0) +#define WMI_MAX_KEY_INDEX	(3) +#define WMI_MAX_KEY_LEN		(32) +#define WMI_PASSPHRASE_LEN	(64) +#define WMI_PMK_LEN		(32) + +struct  wmi_set_pmk_cmd { +	u8 pmk[WMI_PMK_LEN]; +} __packed; + + +/* + * WMI_SET_PASSPHRASE_CMDID + */ +struct wmi_set_passphrase_cmd { +	u8 ssid[WMI_MAX_SSID_LEN]; +	u8 passphrase[WMI_PASSPHRASE_LEN]; +	u8 ssid_len; +	u8 passphrase_len; +} __packed; + +/* + * WMI_ADD_CIPHER_KEY_CMDID + */ +enum wmi_key_usage { +	WMI_KEY_USE_PAIRWISE	= 0, +	WMI_KEY_USE_GROUP	= 1, +	WMI_KEY_USE_TX		= 2,  /* default Tx Key - Static WEP only */ +}; + +struct wmi_add_cipher_key_cmd { +	u8 key_index; +	u8 key_type; +	u8 key_usage;		/* enum wmi_key_usage */ +	u8 key_len; +	u8 key_rsc[8];		/* key replay sequence counter */ +	u8 key[WMI_MAX_KEY_LEN]; +	u8 key_op_ctrl;		/* Additional Key Control information */ +	u8 mac[WMI_MAC_LEN]; +} __packed; + +/* + * WMI_DELETE_CIPHER_KEY_CMDID + */ +struct wmi_delete_cipher_key_cmd { +	u8 key_index; +	u8 mac[WMI_MAC_LEN]; +} __packed; + + +/* + * WMI_START_SCAN_CMDID + * + * Start L1 scan operation + * + * Returned events: + * - WMI_RX_MGMT_PACKET_EVENTID - for every probe resp. + * - WMI_SCAN_COMPLETE_EVENTID + */ +enum wmi_scan_type { +	WMI_LONG_SCAN		= 0, +	WMI_SHORT_SCAN		= 1, +}; + +struct wmi_start_scan_cmd { +	u8 reserved[8]; +	__le32 home_dwell_time;	/* Max duration in the home channel(ms) */ +	__le32 force_scan_interval;	/* Time interval between scans (ms)*/ +	u8 scan_type;		/* wmi_scan_type */ +	u8 num_channels;		/* how many channels follow */ +	struct { +		u8 channel; +		u8 reserved; +	} channel_list[0];	/* channels ID's */ +				/* 0 - 58320 MHz */ +				/* 1 - 60480 MHz */ +				/* 2 - 62640 MHz */ +} __packed; + +/* + * WMI_SET_PROBED_SSID_CMDID + */ +#define MAX_PROBED_SSID_INDEX   (15) + +enum wmi_ssid_flag { +	WMI_SSID_FLAG_DISABLE	= 0,	/* disables entry */ +	WMI_SSID_FLAG_SPECIFIC	= 1,	/* probes specified ssid */ +	WMI_SSID_FLAG_ANY	= 2,	/* probes for any ssid */ +}; + +struct wmi_probed_ssid_cmd { +	u8 entry_index;			/* 0 to MAX_PROBED_SSID_INDEX */ +	u8 flag;			/* enum wmi_ssid_flag */ +	u8 ssid_len; +	u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_APPIE_CMDID + * Add Application specified IE to a management frame + */ +struct wmi_set_appie_cmd { +	u8 mgmt_frm_type;	/* enum wmi_mgmt_frame_type */ +	u8 reserved; +	__le16 ie_len;	/* Length of the IE to be added to MGMT frame */ +	u8 ie_info[0]; +} __packed; + +#define WMI_MAX_IE_LEN (1024) + +struct wmi_pxmt_range_cfg_cmd { +	u8 dst_mac[WMI_MAC_LEN]; +	__le16 range; +} __packed; + +struct wmi_pxmt_snr2_range_cfg_cmd { +	s8 snr2range_arr[WMI_PROX_RANGE_NUM-1]; +} __packed; + +/* + * WMI_RF_MGMT_CMDID + */ +enum wmi_rf_mgmt_type { +	WMI_RF_MGMT_W_DISABLE	= 0, +	WMI_RF_MGMT_W_ENABLE	= 1, +	WMI_RF_MGMT_GET_STATUS	= 2, +}; + +struct wmi_rf_mgmt_cmd { +	__le32 rf_mgmt_type; +} __packed; + +/* + * WMI_SET_SSID_CMDID + */ +struct wmi_set_ssid_cmd { +	__le32 ssid_len; +	u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_SET_PCP_CHANNEL_CMDID + */ +struct wmi_set_pcp_channel_cmd { +	u8 channel; +	u8 reserved[3]; +} __packed; + +/* + * WMI_BCON_CTRL_CMDID + */ +struct wmi_bcon_ctrl_cmd { +	__le16 bcon_interval; +	__le16 frag_num; +	__le64 ss_mask; +	u8 network_type; +	u8 reserved; +	u8 disable_sec_offload; +	u8 disable_sec; +} __packed; + +/* + * WMI_SW_TX_REQ_CMDID + */ +struct wmi_sw_tx_req_cmd { +	u8 dst_mac[WMI_MAC_LEN]; +	__le16 len; +	u8 payload[0]; +} __packed; + +/* + * WMI_VRING_CFG_CMDID + */ + +struct wmi_sw_ring_cfg { +	__le64 ring_mem_base; +	__le16 ring_size; +	__le16 max_mpdu_size; +} __packed; + +struct wmi_vring_cfg_schd { +	__le16 priority; +	__le16 timeslot_us; +} __packed; + +enum wmi_vring_cfg_encap_trans_type { +	WMI_VRING_ENC_TYPE_802_3		= 0, +	WMI_VRING_ENC_TYPE_NATIVE_WIFI		= 1, +}; + +enum wmi_vring_cfg_ds_cfg { +	WMI_VRING_DS_PBSS			= 0, +	WMI_VRING_DS_STATION			= 1, +	WMI_VRING_DS_AP				= 2, +	WMI_VRING_DS_ADDR4			= 3, +}; + +enum wmi_vring_cfg_nwifi_ds_trans_type { +	WMI_NWIFI_TX_TRANS_MODE_NO		= 0, +	WMI_NWIFI_TX_TRANS_MODE_AP2PBSS		= 1, +	WMI_NWIFI_TX_TRANS_MODE_STA2PBSS	= 2, +}; + +enum wmi_vring_cfg_schd_params_priority { +	WMI_SCH_PRIO_REGULAR			= 0, +	WMI_SCH_PRIO_HIGH			= 1, +}; + +struct wmi_vring_cfg { +	struct wmi_sw_ring_cfg tx_sw_ring; +	u8 ringid;				/* 0-23 vrings */ + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 encap_trans_type; +	u8 ds_cfg;				/* 802.3 DS cfg */ +	u8 nwifi_ds_trans_type; + +	#define VRING_CFG_MAC_CTRL_LIFETIME_EN_POS (0) +	#define VRING_CFG_MAC_CTRL_LIFETIME_EN_LEN (1) +	#define VRING_CFG_MAC_CTRL_LIFETIME_EN_MSK (0x1) +	#define VRING_CFG_MAC_CTRL_AGGR_EN_POS (1) +	#define VRING_CFG_MAC_CTRL_AGGR_EN_LEN (1) +	#define VRING_CFG_MAC_CTRL_AGGR_EN_MSK (0x2) +	u8 mac_ctrl; + +	#define VRING_CFG_TO_RESOLUTION_VALUE_POS (0) +	#define VRING_CFG_TO_RESOLUTION_VALUE_LEN (6) +	#define VRING_CFG_TO_RESOLUTION_VALUE_MSK (0x3F) +	u8 to_resolution; +	u8 agg_max_wsize; +	struct wmi_vring_cfg_schd schd_params; +} __packed; + +enum wmi_vring_cfg_cmd_action { +	WMI_VRING_CMD_ADD			= 0, +	WMI_VRING_CMD_MODIFY			= 1, +	WMI_VRING_CMD_DELETE			= 2, +}; + +struct wmi_vring_cfg_cmd { +	__le32 action; +	struct wmi_vring_cfg vring_cfg; +} __packed; + +/* + * WMI_VRING_BA_EN_CMDID + */ +struct wmi_vring_ba_en_cmd { +	u8 ringid; +	u8 agg_max_wsize; +	__le16 ba_timeout; +} __packed; + +/* + * WMI_VRING_BA_DIS_CMDID + */ +struct wmi_vring_ba_dis_cmd { +	u8 ringid; +	u8 reserved; +	__le16 reason; +} __packed; + +/* + * WMI_NOTIFY_REQ_CMDID + */ +struct wmi_notify_req_cmd { +	u8 cid; +	u8 reserved[3]; +	__le32 interval_usec; +} __packed; + +/* + * WMI_CFG_RX_CHAIN_CMDID + */ +enum wmi_sniffer_cfg_mode { +	WMI_SNIFFER_OFF				= 0, +	WMI_SNIFFER_ON				= 1, +}; + +enum wmi_sniffer_cfg_phy_info_mode { +	WMI_SNIFFER_PHY_INFO_DISABLED		= 0, +	WMI_SNIFFER_PHY_INFO_ENABLED		= 1, +}; + +enum wmi_sniffer_cfg_phy_support { +	WMI_SNIFFER_CP				= 0, +	WMI_SNIFFER_DP				= 1, +	WMI_SNIFFER_BOTH_PHYS			= 2, +}; + +struct wmi_sniffer_cfg { +	__le32 mode;		/* enum wmi_sniffer_cfg_mode */ +	__le32 phy_info_mode;	/* enum wmi_sniffer_cfg_phy_info_mode */ +	__le32 phy_support;	/* enum wmi_sniffer_cfg_phy_support */ +	u8 channel; +	u8 reserved[3]; +} __packed; + +enum wmi_cfg_rx_chain_cmd_action { +	WMI_RX_CHAIN_ADD			= 0, +	WMI_RX_CHAIN_DEL			= 1, +}; + +enum wmi_cfg_rx_chain_cmd_decap_trans_type { +	WMI_DECAP_TYPE_802_3			= 0, +	WMI_DECAP_TYPE_NATIVE_WIFI		= 1, +}; + +enum wmi_cfg_rx_chain_cmd_nwifi_ds_trans_type { +	WMI_NWIFI_RX_TRANS_MODE_NO		= 0, +	WMI_NWIFI_RX_TRANS_MODE_PBSS2AP		= 1, +	WMI_NWIFI_RX_TRANS_MODE_PBSS2STA	= 2, +}; + +struct wmi_cfg_rx_chain_cmd { +	__le32 action; +	struct wmi_sw_ring_cfg rx_sw_ring; +	u8 mid; +	u8 decap_trans_type; + +	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_POS (0) +	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_LEN (1) +	#define L2_802_3_OFFLOAD_CTRL_VLAN_TAG_INSERTION_MSK (0x1) +	u8 l2_802_3_offload_ctrl; + +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_POS (0) +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_LEN (1) +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_QOS_MSK (0x1) +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_POS (1) +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_LEN (1) +	#define L2_NWIFI_OFFLOAD_CTRL_REMOVE_PN_MSK (0x2) +	u8 l2_nwifi_offload_ctrl; + +	u8 vlan_id; +	u8 nwifi_ds_trans_type; + +	#define L3_L4_CTRL_IPV4_CHECKSUM_EN_POS (0) +	#define L3_L4_CTRL_IPV4_CHECKSUM_EN_LEN (1) +	#define L3_L4_CTRL_IPV4_CHECKSUM_EN_MSK (0x1) +	#define L3_L4_CTRL_TCPIP_CHECKSUM_EN_POS (1) +	#define L3_L4_CTRL_TCPIP_CHECKSUM_EN_LEN (1) +	#define L3_L4_CTRL_TCPIP_CHECKSUM_EN_MSK (0x2) +	u8 l3_l4_ctrl; + +	#define RING_CTRL_OVERRIDE_PREFETCH_THRSH_POS (0) +	#define RING_CTRL_OVERRIDE_PREFETCH_THRSH_LEN (1) +	#define RING_CTRL_OVERRIDE_PREFETCH_THRSH_MSK (0x1) +	#define RING_CTRL_OVERRIDE_WB_THRSH_POS (1) +	#define RING_CTRL_OVERRIDE_WB_THRSH_LEN (1) +	#define RING_CTRL_OVERRIDE_WB_THRSH_MSK (0x2) +	#define RING_CTRL_OVERRIDE_ITR_THRSH_POS (2) +	#define RING_CTRL_OVERRIDE_ITR_THRSH_LEN (1) +	#define RING_CTRL_OVERRIDE_ITR_THRSH_MSK (0x4) +	#define RING_CTRL_OVERRIDE_HOST_THRSH_POS (3) +	#define RING_CTRL_OVERRIDE_HOST_THRSH_LEN (1) +	#define RING_CTRL_OVERRIDE_HOST_THRSH_MSK (0x8) +	u8 ring_ctrl; + +	__le16 prefetch_thrsh; +	__le16 wb_thrsh; +	__le32 itr_value; +	__le16 host_thrsh; +	u8 reserved[2]; +	struct wmi_sniffer_cfg sniffer_cfg; +} __packed; + +/* + * WMI_RCP_ADDBA_RESP_CMDID + */ +struct wmi_rcp_addba_resp_cmd { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 dialog_token; +	__le16 status_code; +	__le16 ba_param_set;	/* ieee80211_ba_parameterset field to send */ +	__le16 ba_timeout; +} __packed; + +/* + * WMI_RCP_DELBA_CMDID + */ +struct wmi_rcp_delba_cmd { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 reserved; +	__le16 reason; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_CMDID + */ +struct wmi_rcp_addba_req_cmd { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 dialog_token; +	/* ieee80211_ba_parameterset field as it received */ +	__le16 ba_param_set; +	__le16 ba_timeout; +	/* ieee80211_ba_seqstrl field as it received */ +	__le16 ba_seq_ctrl; +} __packed; + +/* + * WMI_SET_MAC_ADDRESS_CMDID + */ +struct wmi_set_mac_address_cmd { +	u8 mac[WMI_MAC_LEN]; +	u8 reserved[2]; +} __packed; + + +/* +* WMI_EAPOL_TX_CMDID +*/ +struct wmi_eapol_tx_cmd { +	u8 dst_mac[WMI_MAC_LEN]; +	__le16 eapol_len; +	u8 eapol[0]; +} __packed; + +/* + * WMI_ECHO_CMDID + * + * Check FW is alive + * + * WMI_DEEP_ECHO_CMDID + * + * Check FW and ucode are alive + * + * Returned event: WMI_ECHO_RSP_EVENTID + * same event for both commands + */ +struct wmi_echo_cmd { +	__le32 value; +} __packed; + +/* + * WMI Events + */ + +/* + * List of Events (target to host) + */ +enum wmi_event_id { +	WMI_IMM_RSP_EVENTID			= 0x0000, +	WMI_READY_EVENTID			= 0x1001, +	WMI_CONNECT_EVENTID			= 0x1002, +	WMI_DISCONNECT_EVENTID			= 0x1003, +	WMI_SCAN_COMPLETE_EVENTID		= 0x100a, +	WMI_REPORT_STATISTICS_EVENTID		= 0x100b, +	WMI_RD_MEM_RSP_EVENTID			= 0x1800, +	WMI_FW_READY_EVENTID			= 0x1801, +	WMI_EXIT_FAST_MEM_ACC_MODE_EVENTID	= 0x0200, +	WMI_ECHO_RSP_EVENTID			= 0x1803, +	WMI_CONFIG_MAC_DONE_EVENTID		= 0x1805, +	WMI_CONFIG_PHY_DEBUG_DONE_EVENTID	= 0x1806, +	WMI_ADD_STATION_DONE_EVENTID		= 0x1807, +	WMI_ADD_DEBUG_TX_PCKT_DONE_EVENTID	= 0x1808, +	WMI_PHY_GET_STATISTICS_EVENTID		= 0x1809, +	WMI_FS_TUNE_DONE_EVENTID		= 0x180a, +	WMI_CORR_MEASURE_DONE_EVENTID		= 0x180b, +	WMI_TEMP_SENSE_DONE_EVENTID		= 0x180e, +	WMI_DC_CALIB_DONE_EVENTID		= 0x180f, +	WMI_IQ_TX_CALIB_DONE_EVENTID		= 0x1811, +	WMI_IQ_RX_CALIB_DONE_EVENTID		= 0x1812, +	WMI_SET_WORK_MODE_DONE_EVENTID		= 0x1815, +	WMI_LO_LEAKAGE_CALIB_DONE_EVENTID	= 0x1816, +	WMI_MARLON_R_ACTIVATE_DONE_EVENTID	= 0x1817, +	WMI_MARLON_R_READ_DONE_EVENTID		= 0x1818, +	WMI_MARLON_R_WRITE_DONE_EVENTID		= 0x1819, +	WMI_MARLON_R_TXRX_SEL_DONE_EVENTID	= 0x181a, +	WMI_SILENT_RSSI_CALIB_DONE_EVENTID	= 0x181d, + +	WMI_CFG_RX_CHAIN_DONE_EVENTID		= 0x1820, +	WMI_VRING_CFG_DONE_EVENTID		= 0x1821, +	WMI_RX_ON_DONE_EVENTID			= 0x1822, +	WMI_BA_STATUS_EVENTID			= 0x1823, +	WMI_RCP_ADDBA_REQ_EVENTID		= 0x1824, +	WMI_ADDBA_RESP_SENT_EVENTID		= 0x1825, +	WMI_DELBA_EVENTID			= 0x1826, +	WMI_GET_SSID_EVENTID			= 0x1828, +	WMI_GET_PCP_CHANNEL_EVENTID		= 0x182a, +	WMI_SW_TX_COMPLETE_EVENTID		= 0x182b, +	WMI_RX_OFF_DONE_EVENTID			= 0x182c, + +	WMI_READ_MAC_RXQ_EVENTID		= 0x1830, +	WMI_READ_MAC_TXQ_EVENTID		= 0x1831, +	WMI_WRITE_MAC_RXQ_EVENTID		= 0x1832, +	WMI_WRITE_MAC_TXQ_EVENTID		= 0x1833, +	WMI_WRITE_MAC_XQ_FIELD_EVENTID		= 0x1834, + +	WMI_BEAFORMING_MGMT_DONE_EVENTID	= 0x1836, +	WMI_BF_TXSS_MGMT_DONE_EVENTID		= 0x1837, +	WMI_BF_RXSS_MGMT_DONE_EVENTID		= 0x1839, +	WMI_RS_MGMT_DONE_EVENTID		= 0x1852, +	WMI_RF_MGMT_STATUS_EVENTID		= 0x1853, +	WMI_BF_SM_MGMT_DONE_EVENTID		= 0x1838, +	WMI_RX_MGMT_PACKET_EVENTID		= 0x1840, + +	/* Performance monitoring events */ +	WMI_DATA_PORT_OPEN_EVENTID		= 0x1860, +	WMI_WBE_LINKDOWN_EVENTID		= 0x1861, + +	WMI_BF_CTRL_DONE_EVENTID		= 0x1862, +	WMI_NOTIFY_REQ_DONE_EVENTID		= 0x1863, +	WMI_GET_STATUS_DONE_EVENTID		= 0x1864, + +	WMI_UNIT_TEST_EVENTID			= 0x1900, +	WMI_FLASH_READ_DONE_EVENTID		= 0x1902, +	WMI_FLASH_WRITE_DONE_EVENTID		= 0x1903, + +	WMI_SET_CHANNEL_EVENTID			= 0x9000, +	WMI_ASSOC_REQ_EVENTID			= 0x9001, +	WMI_EAPOL_RX_EVENTID			= 0x9002, +	WMI_MAC_ADDR_RESP_EVENTID		= 0x9003, +	WMI_FW_VER_EVENTID			= 0x9004, +}; + +/* + * Events data structures + */ + +/* + * WMI_RF_MGMT_STATUS_EVENTID + */ +enum wmi_rf_status { +	WMI_RF_ENABLED			= 0, +	WMI_RF_DISABLED_HW		= 1, +	WMI_RF_DISABLED_SW		= 2, +	WMI_RF_DISABLED_HW_SW		= 3, +}; + +struct wmi_rf_mgmt_status_event { +	__le32 rf_status; +} __packed; + +/* + * WMI_GET_STATUS_DONE_EVENTID + */ +struct wmi_get_status_done_event { +	__le32 is_associated; +	u8 cid; +	u8 reserved0[3]; +	u8 bssid[WMI_MAC_LEN]; +	u8 channel; +	u8 reserved1; +	u8 network_type; +	u8 reserved2[3]; +	__le32 ssid_len; +	u8 ssid[WMI_MAX_SSID_LEN]; +	__le32 rf_status; +	__le32 is_secured; +} __packed; + +/* + * WMI_FW_VER_EVENTID + */ +struct wmi_fw_ver_event { +	u8 major; +	u8 minor; +	__le16 subminor; +	__le16 build; +} __packed; + +/* +* WMI_MAC_ADDR_RESP_EVENTID +*/ +struct wmi_mac_addr_resp_event { +	u8 mac[WMI_MAC_LEN]; +	u8 auth_mode; +	u8 crypt_mode; +	__le32 offload_mode; +} __packed; + +/* +* WMI_EAPOL_RX_EVENTID +*/ +struct wmi_eapol_rx_event { +	u8 src_mac[WMI_MAC_LEN]; +	__le16 eapol_len; +	u8 eapol[0]; +} __packed; + +/* +* WMI_READY_EVENTID +*/ +enum wmi_phy_capability { +	WMI_11A_CAPABILITY		= 1, +	WMI_11G_CAPABILITY		= 2, +	WMI_11AG_CAPABILITY		= 3, +	WMI_11NA_CAPABILITY		= 4, +	WMI_11NG_CAPABILITY		= 5, +	WMI_11NAG_CAPABILITY		= 6, +	WMI_11AD_CAPABILITY		= 7, +	WMI_11N_CAPABILITY_OFFSET = WMI_11NA_CAPABILITY - WMI_11A_CAPABILITY, +}; + +struct wmi_ready_event { +	__le32 sw_version; +	__le32 abi_version; +	u8 mac[WMI_MAC_LEN]; +	u8 phy_capability;		/* enum wmi_phy_capability */ +	u8 reserved; +} __packed; + +/* + * WMI_NOTIFY_REQ_DONE_EVENTID + */ +struct wmi_notify_req_done_event { +	__le32 status; +	__le64 tsf; +	__le32 snr_val; +	__le32 tx_tpt; +	__le32 tx_goodput; +	__le32 rx_goodput; +	__le16 bf_mcs; +	__le16 my_rx_sector; +	__le16 my_tx_sector; +	__le16 other_rx_sector; +	__le16 other_tx_sector; +	__le16 range; +} __packed; + +/* + * WMI_CONNECT_EVENTID + */ +struct wmi_connect_event { +	u8 channel; +	u8 reserved0; +	u8 bssid[WMI_MAC_LEN]; +	__le16 listen_interval; +	__le16 beacon_interval; +	u8 network_type; +	u8 reserved1[3]; +	u8 beacon_ie_len; +	u8 assoc_req_len; +	u8 assoc_resp_len; +	u8 cid; +	u8 reserved2[3]; +	u8 assoc_info[0]; +} __packed; + +/* + * WMI_DISCONNECT_EVENTID + */ +enum wmi_disconnect_reason { +	WMI_DIS_REASON_NO_NETWORK_AVAIL		= 1, +	WMI_DIS_REASON_LOST_LINK		= 2, /* bmiss */ +	WMI_DIS_REASON_DISCONNECT_CMD		= 3, +	WMI_DIS_REASON_BSS_DISCONNECTED		= 4, +	WMI_DIS_REASON_AUTH_FAILED		= 5, +	WMI_DIS_REASON_ASSOC_FAILED		= 6, +	WMI_DIS_REASON_NO_RESOURCES_AVAIL	= 7, +	WMI_DIS_REASON_CSERV_DISCONNECT		= 8, +	WMI_DIS_REASON_INVALID_PROFILE		= 10, +	WMI_DIS_REASON_DOT11H_CHANNEL_SWITCH	= 11, +	WMI_DIS_REASON_PROFILE_MISMATCH		= 12, +	WMI_DIS_REASON_CONNECTION_EVICTED	= 13, +	WMI_DIS_REASON_IBSS_MERGE		= 14, +}; + +struct wmi_disconnect_event { +	__le16 protocol_reason_status;	/* reason code, see 802.11 spec. */ +	u8 bssid[WMI_MAC_LEN];		/* set if known */ +	u8 disconnect_reason;		/* see wmi_disconnect_reason_e */ +	u8 assoc_resp_len; +	u8 assoc_info[0]; +} __packed; + +/* + * WMI_SCAN_COMPLETE_EVENTID + */ +struct wmi_scan_complete_event { +	__le32 status; +} __packed; + +/* + * WMI_BA_STATUS_EVENTID + */ +enum wmi_vring_ba_status { +	WMI_BA_AGREED			= 0, +	WMI_BA_NON_AGREED		= 1, +}; + +struct wmi_vring_ba_status_event { +	__le16 status; +	u8 reserved[2]; +	u8 ringid; +	u8 agg_wsize; +	__le16 ba_timeout; +} __packed; + +/* + * WMI_DELBA_EVENTID + */ +struct wmi_delba_event { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 from_initiator; +	__le16 reason; +} __packed; + +/* + * WMI_VRING_CFG_DONE_EVENTID + */ +enum wmi_vring_cfg_done_event_status { +	WMI_VRING_CFG_SUCCESS		= 0, +	WMI_VRING_CFG_FAILURE		= 1, +}; + +struct wmi_vring_cfg_done_event { +	u8 ringid; +	u8 status; +	u8 reserved[2]; +	__le32 tx_vring_tail_ptr; +} __packed; + +/* + * WMI_ADDBA_RESP_SENT_EVENTID + */ +enum wmi_rcp_addba_resp_sent_event_status { +	WMI_ADDBA_SUCCESS		= 0, +	WMI_ADDBA_FAIL			= 1, +}; + +struct wmi_rcp_addba_resp_sent_event { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 reserved; +	__le16 status; +} __packed; + +/* + * WMI_RCP_ADDBA_REQ_EVENTID + */ +struct wmi_rcp_addba_req_event { + +	#define CIDXTID_CID_POS (0) +	#define CIDXTID_CID_LEN (4) +	#define CIDXTID_CID_MSK (0xF) +	#define CIDXTID_TID_POS (4) +	#define CIDXTID_TID_LEN (4) +	#define CIDXTID_TID_MSK (0xF0) +	u8 cidxtid; + +	u8 dialog_token; +	__le16 ba_param_set;	/* ieee80211_ba_parameterset as it received */ +	__le16 ba_timeout; +	__le16 ba_seq_ctrl;	/* ieee80211_ba_seqstrl field as it received */ +} __packed; + +/* + * WMI_CFG_RX_CHAIN_DONE_EVENTID + */ +enum wmi_cfg_rx_chain_done_event_status { +	WMI_CFG_RX_CHAIN_SUCCESS	= 1, +}; + +struct wmi_cfg_rx_chain_done_event { +	__le32 rx_ring_tail_ptr;	/* Rx V-Ring Tail pointer */ +	__le32 status; +} __packed; + +/* + * WMI_WBE_LINKDOWN_EVENTID + */ +enum wmi_wbe_link_down_event_reason { +	WMI_WBE_REASON_USER_REQUEST	= 0, +	WMI_WBE_REASON_RX_DISASSOC	= 1, +	WMI_WBE_REASON_BAD_PHY_LINK	= 2, +}; + +struct wmi_wbe_link_down_event { +	u8 cid; +	u8 reserved[3]; +	__le32 reason; +} __packed; + +/* + * WMI_DATA_PORT_OPEN_EVENTID + */ +struct wmi_data_port_open_event { +	u8 cid; +	u8 reserved[3]; +} __packed; + +/* + * WMI_GET_PCP_CHANNEL_EVENTID + */ +struct wmi_get_pcp_channel_event { +	u8 channel; +	u8 reserved[3]; +} __packed; + +/* + * WMI_SW_TX_COMPLETE_EVENTID + */ +enum wmi_sw_tx_status { +	WMI_TX_SW_STATUS_SUCCESS		= 0, +	WMI_TX_SW_STATUS_FAILED_NO_RESOURCES	= 1, +	WMI_TX_SW_STATUS_FAILED_TX		= 2, +}; + +struct wmi_sw_tx_complete_event { +	u8 status;	/* enum wmi_sw_tx_status */ +	u8 reserved[3]; +} __packed; + +/* + * WMI_GET_SSID_EVENTID + */ +struct wmi_get_ssid_event { +	__le32 ssid_len; +	u8 ssid[WMI_MAX_SSID_LEN]; +} __packed; + +/* + * WMI_RX_MGMT_PACKET_EVENTID + */ +struct wmi_rx_mgmt_info { +	u8 mcs; +	s8 snr; +	__le16 range; +	__le16 stype; +	__le16 status; +	__le32 len; +	u8 qid; +	u8 mid; +	u8 cid; +	u8 channel;	/* From Radio MNGR */ +} __packed; + +struct wmi_rx_mgmt_packet_event { +	struct wmi_rx_mgmt_info info; +	u8 payload[0]; +} __packed; + +/* + * WMI_ECHO_RSP_EVENTID + */ +struct wmi_echo_event { +	__le32 echoed_value; +} __packed; + +#endif /* __WILOCITY_WMI_H__ */ diff --git a/drivers/net/wireless/b43/b43.h b/drivers/net/wireless/b43/b43.h index b298e5d68be..10e288d470e 100644 --- a/drivers/net/wireless/b43/b43.h +++ b/drivers/net/wireless/b43/b43.h @@ -7,6 +7,7 @@  #include <linux/hw_random.h>  #include <linux/bcma/bcma.h>  #include <linux/ssb/ssb.h> +#include <linux/completion.h>  #include <net/mac80211.h>  #include "debugfs.h" @@ -722,6 +723,10 @@ enum b43_firmware_file_type {  struct b43_request_fw_context {  	/* The device we are requesting the fw for. */  	struct b43_wldev *dev; +	/* a completion event structure needed if this call is asynchronous */ +	struct completion fw_load_complete; +	/* a pointer to the firmware object */ +	const struct firmware *blob;  	/* The type of firmware to request. */  	enum b43_firmware_file_type req_type;  	/* Error messages for each firmware type. */ diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 16ab280359b..806e34c1928 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2088,11 +2088,18 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error)  		b43warn(wl, text);  } +static void b43_fw_cb(const struct firmware *firmware, void *context) +{ +	struct b43_request_fw_context *ctx = context; + +	ctx->blob = firmware; +	complete(&ctx->fw_load_complete); +} +  int b43_do_request_fw(struct b43_request_fw_context *ctx,  		      const char *name, -		      struct b43_firmware_file *fw) +		      struct b43_firmware_file *fw, bool async)  { -	const struct firmware *blob;  	struct b43_fw_header *hdr;  	u32 size;  	int err; @@ -2131,11 +2138,31 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,  		B43_WARN_ON(1);  		return -ENOSYS;  	} -	err = request_firmware(&blob, ctx->fwname, ctx->dev->dev->dev); +	if (async) { +		/* do this part asynchronously */ +		init_completion(&ctx->fw_load_complete); +		err = request_firmware_nowait(THIS_MODULE, 1, ctx->fwname, +					      ctx->dev->dev->dev, GFP_KERNEL, +					      ctx, b43_fw_cb); +		if (err < 0) { +			pr_err("Unable to load firmware\n"); +			return err; +		} +		/* stall here until fw ready */ +		wait_for_completion(&ctx->fw_load_complete); +		if (ctx->blob) +			goto fw_ready; +	/* On some ARM systems, the async request will fail, but the next sync +	 * request works. For this reason, we dall through here +	 */ +	} +	err = request_firmware(&ctx->blob, ctx->fwname, +			       ctx->dev->dev->dev);  	if (err == -ENOENT) {  		snprintf(ctx->errors[ctx->req_type],  			 sizeof(ctx->errors[ctx->req_type]), -			 "Firmware file \"%s\" not found\n", ctx->fwname); +			 "Firmware file \"%s\" not found\n", +			 ctx->fwname);  		return err;  	} else if (err) {  		snprintf(ctx->errors[ctx->req_type], @@ -2144,14 +2171,15 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,  			 ctx->fwname, err);  		return err;  	} -	if (blob->size < sizeof(struct b43_fw_header)) +fw_ready: +	if (ctx->blob->size < sizeof(struct b43_fw_header))  		goto err_format; -	hdr = (struct b43_fw_header *)(blob->data); +	hdr = (struct b43_fw_header *)(ctx->blob->data);  	switch (hdr->type) {  	case B43_FW_TYPE_UCODE:  	case B43_FW_TYPE_PCM:  		size = be32_to_cpu(hdr->size); -		if (size != blob->size - sizeof(struct b43_fw_header)) +		if (size != ctx->blob->size - sizeof(struct b43_fw_header))  			goto err_format;  		/* fallthrough */  	case B43_FW_TYPE_IV: @@ -2162,7 +2190,7 @@ int b43_do_request_fw(struct b43_request_fw_context *ctx,  		goto err_format;  	} -	fw->data = blob; +	fw->data = ctx->blob;  	fw->filename = name;  	fw->type = ctx->req_type; @@ -2172,7 +2200,7 @@ err_format:  	snprintf(ctx->errors[ctx->req_type],  		 sizeof(ctx->errors[ctx->req_type]),  		 "Firmware file \"%s\" format error.\n", ctx->fwname); -	release_firmware(blob); +	release_firmware(ctx->blob);  	return -EPROTO;  } @@ -2223,7 +2251,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)  			goto err_no_ucode;  		}  	} -	err = b43_do_request_fw(ctx, filename, &fw->ucode); +	err = b43_do_request_fw(ctx, filename, &fw->ucode, true);  	if (err)  		goto err_load; @@ -2235,7 +2263,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)  	else  		goto err_no_pcm;  	fw->pcm_request_failed = false; -	err = b43_do_request_fw(ctx, filename, &fw->pcm); +	err = b43_do_request_fw(ctx, filename, &fw->pcm, false);  	if (err == -ENOENT) {  		/* We did not find a PCM file? Not fatal, but  		 * core rev <= 10 must do without hwcrypto then. */ @@ -2296,7 +2324,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)  	default:  		goto err_no_initvals;  	} -	err = b43_do_request_fw(ctx, filename, &fw->initvals); +	err = b43_do_request_fw(ctx, filename, &fw->initvals, false);  	if (err)  		goto err_load; @@ -2355,7 +2383,7 @@ static int b43_try_request_fw(struct b43_request_fw_context *ctx)  	default:  		goto err_no_initvals;  	} -	err = b43_do_request_fw(ctx, filename, &fw->initvals_band); +	err = b43_do_request_fw(ctx, filename, &fw->initvals_band, false);  	if (err)  		goto err_load; diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index 8c684cd3352..abac25ee958 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -137,9 +137,8 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on);  struct b43_request_fw_context; -int b43_do_request_fw(struct b43_request_fw_context *ctx, -		      const char *name, -		      struct b43_firmware_file *fw); +int b43_do_request_fw(struct b43_request_fw_context *ctx, const char *name, +		      struct b43_firmware_file *fw, bool async);  void b43_do_release_fw(struct b43_firmware_file *fw);  #endif /* B43_MAIN_H_ */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 1261a9b84e0..75464ad4fbd 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -3091,10 +3091,11 @@ brcmf_configure_wpaie(struct net_device *ndev, struct brcmf_vs_tlv *wpa_ie,  	len = wpa_ie->len + TLV_HDR_LEN;  	data = (u8 *)wpa_ie; -	offset = 0; +	offset = TLV_HDR_LEN;  	if (!is_rsn_ie)  		offset += VS_IE_FIXED_HDR_LEN; -	offset += WPA_IE_VERSION_LEN; +	else +		offset += WPA_IE_VERSION_LEN;  	/* check for multicast cipher suite */  	if (offset + WPA_IE_MIN_OUI_LEN > len) { diff --git a/drivers/net/wireless/brcm80211/brcmsmac/debug.h b/drivers/net/wireless/brcm80211/brcmsmac/debug.h index 796836b0f46..822781cf15d 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/debug.h +++ b/drivers/net/wireless/brcm80211/brcmsmac/debug.h @@ -1,5 +1,6 @@  /*   * Copyright (c) 2012 Broadcom Corporation + * Copyright (c) 2012 Canonical Ltd.   *   * Permission to use, copy, modify, and/or distribute this software for any   * purpose with or without fee is hereby granted, provided that the above diff --git a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c index 606b534347b..21a82423247 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c @@ -1343,13 +1343,13 @@ static bool wlc_lcnphy_rx_iq_cal_gain(struct brcms_phy *pi, u16 biq1_gain,  	wlc_lcnphy_rx_gain_override_enable(pi, true);  	wlc_lcnphy_start_tx_tone(pi, 2000, (40 >> 1), 0); -	usleep_range(500, 500); +	udelay(500);  	write_radio_reg(pi, RADIO_2064_REG112, 0);  	if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_l))  		return false;  	wlc_lcnphy_start_tx_tone(pi, 2000, 40, 0); -	usleep_range(500, 500); +	udelay(500);  	write_radio_reg(pi, RADIO_2064_REG112, 0);  	if (!wlc_lcnphy_rx_iq_est(pi, 1024, 32, &iq_est_h))  		return false; diff --git a/drivers/net/wireless/iwlegacy/3945-mac.c b/drivers/net/wireless/iwlegacy/3945-mac.c index d604b4036a7..3726cd6fcd7 100644 --- a/drivers/net/wireless/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/iwlegacy/3945-mac.c @@ -3273,7 +3273,7 @@ il3945_store_measurement(struct device *d, struct device_attribute *attr,  	if (count) {  		char *p = buffer; -		strncpy(buffer, buf, min(sizeof(buffer), count)); +		strlcpy(buffer, buf, sizeof(buffer));  		channel = simple_strtoul(p, NULL, 0);  		if (channel)  			params.channel = channel; diff --git a/drivers/net/wireless/iwlwifi/dvm/tx.c b/drivers/net/wireless/iwlwifi/dvm/tx.c index da21328ca8e..a790599fe2c 100644 --- a/drivers/net/wireless/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/iwlwifi/dvm/tx.c @@ -1151,13 +1151,6 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,  			next_reclaimed = ssn;  		} -		if (tid != IWL_TID_NON_QOS) { -			priv->tid_data[sta_id][tid].next_reclaimed = -				next_reclaimed; -			IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", -						  next_reclaimed); -		} -  		iwl_trans_reclaim(priv->trans, txq_id, ssn, &skbs);  		iwlagn_check_ratid_empty(priv, sta_id, tid); @@ -1208,11 +1201,28 @@ int iwlagn_rx_reply_tx(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb,  			if (!is_agg)  				iwlagn_non_agg_tx_status(priv, ctx, hdr->addr1); +			/* +			 * W/A for FW bug - the seq_ctl isn't updated when the +			 * queues are flushed. Fetch it from the packet itself +			 */ +			if (!is_agg && status == TX_STATUS_FAIL_FIFO_FLUSHED) { +				next_reclaimed = le16_to_cpu(hdr->seq_ctrl); +				next_reclaimed = +					SEQ_TO_SN(next_reclaimed + 0x10); +			} +  			is_offchannel_skb =  				(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN);  			freed++;  		} +		if (tid != IWL_TID_NON_QOS) { +			priv->tid_data[sta_id][tid].next_reclaimed = +				next_reclaimed; +			IWL_DEBUG_TX_REPLY(priv, "Next reclaimed packet:%d\n", +					   next_reclaimed); +		} +  		WARN_ON(!is_agg && freed != 1);  		/* diff --git a/drivers/net/wireless/iwlwifi/pcie/rx.c b/drivers/net/wireless/iwlwifi/pcie/rx.c index dad4c4aad91..8389cd38338 100644 --- a/drivers/net/wireless/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/iwlwifi/pcie/rx.c @@ -1166,6 +1166,7 @@ static irqreturn_t iwl_pcie_isr(int irq, void *data)  	else if (test_bit(STATUS_INT_ENABLED, &trans_pcie->status) &&  		 !trans_pcie->inta)  		iwl_enable_interrupts(trans); +	return IRQ_HANDLED;  none:  	/* re-enable interrupts here since we don't have anything to service. */ diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index a875499f894..efe525be27d 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1709,7 +1709,7 @@ static int mwifiex_set_ibss_params(struct mwifiex_private *priv,  						NL80211_CHAN_NO_HT)  			config_bands |= BAND_GN;  	} else { -		if (cfg80211_get_chandef_type(¶ms->chandef) != +		if (cfg80211_get_chandef_type(¶ms->chandef) ==  						NL80211_CHAN_NO_HT)  			config_bands = BAND_A;  		else diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index cb682561c43..60e88b58039 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -56,7 +56,6 @@ int mwifiex_copy_mcast_addr(struct mwifiex_multicast_list *mlist,   */  int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)  { -	bool cancel_flag = false;  	int status;  	struct cmd_ctrl_node *cmd_queued; @@ -70,14 +69,11 @@ int mwifiex_wait_queue_complete(struct mwifiex_adapter *adapter)  	atomic_inc(&adapter->cmd_pending);  	/* Wait for completion */ -	wait_event_interruptible(adapter->cmd_wait_q.wait, -				 *(cmd_queued->condition)); -	if (!*(cmd_queued->condition)) -		cancel_flag = true; - -	if (cancel_flag) { -		mwifiex_cancel_pending_ioctl(adapter); -		dev_dbg(adapter->dev, "cmd cancel\n"); +	status = wait_event_interruptible(adapter->cmd_wait_q.wait, +					  *(cmd_queued->condition)); +	if (status) { +		dev_err(adapter->dev, "cmd_wait_q terminated: %d\n", status); +		return status;  	}  	status = adapter->cmd_wait_q.status; @@ -496,8 +492,11 @@ int mwifiex_enable_hs(struct mwifiex_adapter *adapter)  		return false;  	} -	wait_event_interruptible(adapter->hs_activate_wait_q, -				 adapter->hs_activate_wait_q_woken); +	if (wait_event_interruptible(adapter->hs_activate_wait_q, +				     adapter->hs_activate_wait_q_woken)) { +		dev_err(adapter->dev, "hs_activate_wait_q terminated\n"); +		return false; +	}  	return true;  } diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index f221b95b90b..83564d36e80 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -4250,9 +4250,11 @@ static int mwl8k_cmd_update_stadb_add(struct ieee80211_hw *hw,  	p->amsdu_enabled = 0;  	rc = mwl8k_post_cmd(hw, &cmd->header); +	if (!rc) +		rc = p->station_id;  	kfree(cmd); -	return rc ? rc : p->station_id; +	return rc;  }  static int mwl8k_cmd_update_stadb_del(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c index e71c702e2eb..800a16526c8 100644 --- a/drivers/net/wireless/p54/p54usb.c +++ b/drivers/net/wireless/p54/p54usb.c @@ -47,6 +47,7 @@ static struct usb_device_id p54u_table[] = {  	{USB_DEVICE(0x0411, 0x0050)},	/* Buffalo WLI2-USB2-G54 */  	{USB_DEVICE(0x045e, 0x00c2)},	/* Microsoft MN-710 */  	{USB_DEVICE(0x0506, 0x0a11)},	/* 3COM 3CRWE254G72 */ +	{USB_DEVICE(0x0675, 0x0530)},	/* DrayTek Vigor 530 */  	{USB_DEVICE(0x06b9, 0x0120)},	/* Thomson SpeedTouch 120g */  	{USB_DEVICE(0x0707, 0xee06)},	/* SMC 2862W-G */  	{USB_DEVICE(0x07aa, 0x001c)},	/* Corega CG-WLUSB2GT */ @@ -82,6 +83,8 @@ static struct usb_device_id p54u_table[] = {  	{USB_DEVICE(0x06a9, 0x000e)},	/* Westell 802.11g USB (A90-211WG-01) */  	{USB_DEVICE(0x06b9, 0x0121)},	/* Thomson SpeedTouch 121g */  	{USB_DEVICE(0x0707, 0xee13)},   /* SMC 2862W-G version 2 */ +	{USB_DEVICE(0x0803, 0x4310)},	/* Zoom 4410a */ +	{USB_DEVICE(0x083a, 0x4503)},	/* T-Com Sinus 154 data II */  	{USB_DEVICE(0x083a, 0x4521)},   /* Siemens Gigaset USB Adapter 54 version 2 */  	{USB_DEVICE(0x083a, 0xc501)},	/* Zoom Wireless-G 4410 */  	{USB_DEVICE(0x083a, 0xf503)},	/* Accton FD7050E ver 1010ec  */ @@ -101,6 +104,7 @@ static struct usb_device_id p54u_table[] = {  	{USB_DEVICE(0x13B1, 0x000C)},	/* Linksys WUSB54AG */  	{USB_DEVICE(0x1413, 0x5400)},   /* Telsey 802.11g USB2.0 Adapter */  	{USB_DEVICE(0x1435, 0x0427)},	/* Inventel UR054G */ +	/* {USB_DEVICE(0x15a9, 0x0002)}, * Also SparkLAN WL-682 with 3887 */  	{USB_DEVICE(0x1668, 0x1050)},	/* Actiontec 802UIG-1 */  	{USB_DEVICE(0x1740, 0x1000)},	/* Senao NUB-350 */  	{USB_DEVICE(0x2001, 0x3704)},	/* DLink DWL-G122 rev A2 */ diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 4ffb6a584cd..44f8b3f3cbe 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -685,6 +685,14 @@ void rt2x00lib_rxdone(struct queue_entry *entry, gfp_t gfp)  	 * to mac80211.  	 */  	rx_status = IEEE80211_SKB_RXCB(entry->skb); + +	/* Ensure that all fields of rx_status are initialized +	 * properly. The skb->cb array was used for driver +	 * specific informations, so rx_status might contain +	 * garbage. +	 */ +	memset(rx_status, 0, sizeof(*rx_status)); +  	rx_status->mactime = rxdesc.timestamp;  	rx_status->band = rt2x00dev->curr_band;  	rx_status->freq = rt2x00dev->curr_freq; diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c index 3deacafdcd5..4261e8ecc4c 100644 --- a/drivers/net/wireless/rtlwifi/pci.c +++ b/drivers/net/wireless/rtlwifi/pci.c @@ -743,6 +743,8 @@ static void _rtl_pci_rx_interrupt(struct ieee80211_hw *hw)  done:  		bufferaddress = (*((dma_addr_t *)skb->cb)); +		if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) +			return;  		tmp_one = 1;  		rtlpriv->cfg->ops->set_desc((u8 *) pdesc, false,  					    HW_DESC_RXBUFF_ADDR, @@ -1115,6 +1117,10 @@ static int _rtl_pci_init_rx_ring(struct ieee80211_hw *hw)  					   PCI_DMA_FROMDEVICE);  			bufferaddress = (*((dma_addr_t *)skb->cb)); +			if (pci_dma_mapping_error(rtlpci->pdev, bufferaddress)) { +				dev_kfree_skb_any(skb); +				return 1; +			}  			rtlpriv->cfg->ops->set_desc((u8 *)entry, false,  						    HW_DESC_RXBUFF_ADDR,  						    (u8 *)&bufferaddress); diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c index 1d5d3604e3e..246e5352f2e 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c +++ b/drivers/net/wireless/rtlwifi/rtl8192c/phy_common.c @@ -692,7 +692,7 @@ u8 rtl92c_phy_sw_chnl(struct ieee80211_hw *hw)  	if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {  		rtl92c_phy_sw_chnl_callback(hw);  		RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, -			 "sw_chnl_inprogress false schdule workitem\n"); +			 "sw_chnl_inprogress false schedule workitem\n");  		rtlphy->sw_chnl_inprogress = false;  	} else {  		RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c index 17342475614..c31795e379f 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192ce/trx.c @@ -611,8 +611,14 @@ void rtl92ce_tx_fill_desc(struct ieee80211_hw *hw,  	dma_addr_t mapping = pci_map_single(rtlpci->pdev,  					    skb->data, skb->len,  					    PCI_DMA_TODEVICE); +  	u8 bw_40 = 0; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	rcu_read_lock();  	sta = get_sta(hw, mac->vif, mac->bssid);  	if (mac->opmode == NL80211_IFTYPE_STATION) { @@ -774,6 +780,11 @@ void rtl92ce_tx_fill_cmddesc(struct ieee80211_hw *hw,  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);  	__le16 fc = hdr->frame_control; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);  	if (firstseg) diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c index f9f3861046c..a0fbf284420 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192de/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192de/trx.c @@ -587,6 +587,11 @@ void rtl92de_tx_fill_desc(struct ieee80211_hw *hw,  	buf_len = skb->len;  	mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len,  				 PCI_DMA_TODEVICE); +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	CLEAR_PCI_TX_DESC_CONTENT(pdesc, sizeof(struct tx_desc_92d));  	if (ieee80211_is_nullfunc(fc) || ieee80211_is_ctl(fc)) {  		firstseg = true; @@ -740,6 +745,11 @@ void rtl92de_tx_fill_cmddesc(struct ieee80211_hw *hw,  	struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)(skb->data);  	__le16 fc = hdr->frame_control; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);  	if (firstseg)  		SET_TX_DESC_OFFSET(pdesc, USB_HWDESC_HEADER_LEN); diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c index 0e9f6ebf078..206561d7282 100644 --- a/drivers/net/wireless/rtlwifi/rtl8192se/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8192se/trx.c @@ -611,6 +611,11 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,  		    PCI_DMA_TODEVICE);  	u8 bw_40 = 0; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	if (mac->opmode == NL80211_IFTYPE_STATION) {  		bw_40 = mac->bw_40;  	} else if (mac->opmode == NL80211_IFTYPE_AP || @@ -763,6 +768,7 @@ void rtl92se_tx_fill_desc(struct ieee80211_hw *hw,  void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,  	bool firstseg, bool lastseg, struct sk_buff *skb)  { +	struct rtl_priv *rtlpriv = rtl_priv(hw);  	struct rtl_pci *rtlpci = rtl_pcidev(rtl_pcipriv(hw));  	struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));  	struct rtl_tcb_desc *tcb_desc = (struct rtl_tcb_desc *)(skb->cb); @@ -770,7 +776,12 @@ void rtl92se_tx_fill_cmddesc(struct ieee80211_hw *hw, u8 *pdesc,  	dma_addr_t mapping = pci_map_single(rtlpci->pdev, skb->data, skb->len,  			PCI_DMA_TODEVICE); -    /* Clear all status	*/ +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	} +	/* Clear all status	*/  	CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_CMDDESC_SIZE_RTL8192S);  	/* This bit indicate this packet is used for FW download. */ diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c index 39cc7938eed..3d8536bb0d2 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/phy.c @@ -1106,7 +1106,7 @@ u8 rtl8723ae_phy_sw_chnl(struct ieee80211_hw *hw)  	if (!(is_hal_stop(rtlhal)) && !(RT_CANNOT_IO(hw))) {  		rtl8723ae_phy_sw_chnl_callback(hw);  		RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, -			 "sw_chnl_inprogress false schdule workitem\n"); +			 "sw_chnl_inprogress false schedule workitem\n");  		rtlphy->sw_chnl_inprogress = false;  	} else {  		RT_TRACE(rtlpriv, COMP_CHAN, DBG_LOUD, diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c index 18b0bc51766..bb7cc90bafb 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/sw.c @@ -341,7 +341,7 @@ static struct rtl_hal_cfg rtl8723ae_hal_cfg = {  	.maps[RTL_RC_HT_RATEMCS15] = DESC92_RATEMCS15,  }; -static struct pci_device_id rtl8723ae_pci_ids[] __devinitdata = { +static struct pci_device_id rtl8723ae_pci_ids[] = {  	{RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8723, rtl8723ae_hal_cfg)},  	{},  }; diff --git a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c index 87331d826d7..a313be8c21d 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c +++ b/drivers/net/wireless/rtlwifi/rtl8723ae/trx.c @@ -387,6 +387,11 @@ void rtl8723ae_tx_fill_desc(struct ieee80211_hw *hw,  					    PCI_DMA_TODEVICE);  	u8 bw_40 = 0; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	if (mac->opmode == NL80211_IFTYPE_STATION) {  		bw_40 = mac->bw_40;  	} else if (mac->opmode == NL80211_IFTYPE_AP || @@ -542,6 +547,11 @@ void rtl8723ae_tx_fill_cmddesc(struct ieee80211_hw *hw,  					    PCI_DMA_TODEVICE);  	__le16 fc = hdr->frame_control; +	if (pci_dma_mapping_error(rtlpci->pdev, mapping)) { +		RT_TRACE(rtlpriv, COMP_SEND, DBG_TRACE, +			 "DMA mapping error"); +		return; +	}  	CLEAR_PCI_TX_DESC_CONTENT(pdesc, TX_DESC_SIZE);  	if (firstseg) diff --git a/drivers/net/wireless/rtlwifi/usb.c b/drivers/net/wireless/rtlwifi/usb.c index 29f0969e4ba..f2ecdeb3a90 100644 --- a/drivers/net/wireless/rtlwifi/usb.c +++ b/drivers/net/wireless/rtlwifi/usb.c @@ -210,17 +210,16 @@ static void _usb_writeN_sync(struct rtl_priv *rtlpriv, u32 addr, void *data,  	u16 index = REALTEK_USB_VENQT_CMD_IDX;  	int pipe = usb_sndctrlpipe(udev, 0); /* write_out */  	u8 *buffer; -	dma_addr_t dma_addr; -	wvalue = (u16)(addr&0x0000ffff); -	buffer = usb_alloc_coherent(udev, (size_t)len, GFP_ATOMIC, &dma_addr); +	wvalue = (u16)(addr & 0x0000ffff); +	buffer = kmalloc(len, GFP_ATOMIC);  	if (!buffer)  		return;  	memcpy(buffer, data, len);  	usb_control_msg(udev, pipe, request, reqtype, wvalue,  			index, buffer, len, 50); -	usb_free_coherent(udev, (size_t)len, buffer, dma_addr); +	kfree(buffer);  }  static void _rtl_usb_io_handler_init(struct device *dev, @@ -640,6 +639,7 @@ static int _rtl_usb_receive(struct ieee80211_hw *hw)  			RT_TRACE(rtlpriv, COMP_USB, DBG_EMERG,  				 "Failed to prep_rx_urb!!\n");  			err = PTR_ERR(skb); +			usb_free_urb(urb);  			goto err_out;  		} diff --git a/drivers/net/xen-netfront.c b/drivers/net/xen-netfront.c index c26e28b4bd9..7ffa43bd7cf 100644 --- a/drivers/net/xen-netfront.c +++ b/drivers/net/xen-netfront.c @@ -1015,29 +1015,10 @@ err:  		i = xennet_fill_frags(np, skb, &tmpq);  		/* -		 * Truesize approximates the size of true data plus -		 * any supervisor overheads. Adding hypervisor -		 * overheads has been shown to significantly reduce -		 * achievable bandwidth with the default receive -		 * buffer size. It is therefore not wise to account -		 * for it here. -		 * -		 * After alloc_skb(RX_COPY_THRESHOLD), truesize is set -		 * to RX_COPY_THRESHOLD + the supervisor -		 * overheads. Here, we add the size of the data pulled -		 * in xennet_fill_frags(). -		 * -		 * We also adjust for any unused space in the main -		 * data area by subtracting (RX_COPY_THRESHOLD - -		 * len). This is especially important with drivers -		 * which split incoming packets into header and data, -		 * using only 66 bytes of the main data area (see the -		 * e1000 driver for example.)  On such systems, -		 * without this last adjustement, our achievable -		 * receive throughout using the standard receive -		 * buffer size was cut by 25%(!!!). -		 */ -		skb->truesize += skb->data_len - RX_COPY_THRESHOLD; +                 * Truesize is the actual allocation size, even if the +                 * allocation is only partially used. +                 */ +		skb->truesize += PAGE_SIZE * skb_shinfo(skb)->nr_frags;  		skb->len += skb->data_len;  		if (rx->flags & XEN_NETRXF_csum_blank)  |