1167514Skmacy/**************************************************************************
2167514Skmacy
3189643SgnnCopyright (c) 2007-2009 Chelsio Inc.
4167514SkmacyAll rights reserved.
5167514Skmacy
6167514SkmacyRedistribution and use in source and binary forms, with or without
7167514Skmacymodification, are permitted provided that the following conditions are met:
8167514Skmacy
9167514Skmacy 1. Redistributions of source code must retain the above copyright notice,
10167514Skmacy    this list of conditions and the following disclaimer.
11167514Skmacy
12170076Skmacy 2. Neither the name of the Chelsio Corporation nor the names of its
13167514Skmacy    contributors may be used to endorse or promote products derived from
14167514Skmacy    this software without specific prior written permission.
15167514Skmacy
16167514SkmacyTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17167514SkmacyAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18167514SkmacyIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19167514SkmacyARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20167514SkmacyLIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21167514SkmacyCONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22167514SkmacySUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23167514SkmacyINTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24167514SkmacyCONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25167514SkmacyARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26167514SkmacyPOSSIBILITY OF SUCH DAMAGE.
27167514Skmacy
28167514Skmacy***************************************************************************/
29167514Skmacy
30167514Skmacy#include <sys/cdefs.h>
31167514Skmacy__FBSDID("$FreeBSD$");
32167514Skmacy
33170076Skmacy#include <cxgb_include.h>
34167514Skmacy
35171471Skmacy#undef msleep
36171471Skmacy#define msleep t3_os_sleep
37171471Skmacy
38167514Skmacy
39167514Skmacystatic inline int macidx(const struct cmac *mac)
40167514Skmacy{
41167514Skmacy	return mac->offset / (XGMAC0_1_BASE_ADDR - XGMAC0_0_BASE_ADDR);
42167514Skmacy}
43167514Skmacy
44197791Snp/*
45197791Snp * Returns a reasonable A_XGM_RESET_CTRL value for the mac specified.
46197791Snp */
47197791Snpstatic inline int xgm_reset_ctrl(const struct cmac *mac)
48197791Snp{
49197791Snp	adapter_t *adap = mac->adapter;
50197791Snp	int val = F_MAC_RESET_ | F_XGMAC_STOP_EN;
51197791Snp
52197791Snp	if (is_10G(adap)) {
53197791Snp		int cfg = t3_read_reg(adap, A_XGM_PORT_CFG + mac->offset);
54197791Snp
55197791Snp		val |= F_PCS_RESET_;
56197791Snp		if (G_PORTSPEED(cfg) != 3)	/* not running at 10G */
57197791Snp			val |= F_XG2G_RESET_;
58197791Snp	} else if (uses_xaui(adap))
59197791Snp		val |= F_PCS_RESET_ | F_XG2G_RESET_;
60197791Snp	else
61197791Snp		val |= F_RGMII_RESET_ | F_XG2G_RESET_;
62197791Snp
63197791Snp	return (val);
64197791Snp}
65197791Snp
66167514Skmacystatic void xaui_serdes_reset(struct cmac *mac)
67167514Skmacy{
68167514Skmacy	static const unsigned int clear[] = {
69167514Skmacy		F_PWRDN0 | F_PWRDN1,    F_RESETPLL01,    F_RESET0 | F_RESET1,
70167514Skmacy	     	F_PWRDN2 | F_PWRDN3,    F_RESETPLL23,    F_RESET2 | F_RESET3
71167514Skmacy	};
72167514Skmacy
73167514Skmacy	int i;
74167514Skmacy	adapter_t *adap = mac->adapter;
75167514Skmacy	u32 ctrl = A_XGM_SERDES_CTRL0 + mac->offset;
76167514Skmacy
77167514Skmacy	t3_write_reg(adap, ctrl, adap->params.vpd.xauicfg[macidx(mac)] |
78167514Skmacy		     F_RESET3 | F_RESET2 | F_RESET1 | F_RESET0 |
79167514Skmacy		     F_PWRDN3 | F_PWRDN2 | F_PWRDN1 | F_PWRDN0 |
80167514Skmacy		     F_RESETPLL23 | F_RESETPLL01);
81167514Skmacy	(void)t3_read_reg(adap, ctrl);
82167514Skmacy	udelay(15);
83167514Skmacy
84167514Skmacy	for (i = 0; i < ARRAY_SIZE(clear); i++) {
85167514Skmacy		t3_set_reg_field(adap, ctrl, clear[i], 0);
86167514Skmacy		udelay(15);
87167514Skmacy	}
88167514Skmacy}
89167514Skmacy
90176472Skmacy/**
91176472Skmacy *	t3b_pcs_reset - reset the PCS on T3B+ adapters
92176472Skmacy *	@mac: the XGMAC handle
93176472Skmacy *
94176472Skmacy *	Reset the XGMAC PCS block on T3B+ adapters.
95176472Skmacy */
96167514Skmacyvoid t3b_pcs_reset(struct cmac *mac)
97167514Skmacy{
98167514Skmacy	t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset,
99167514Skmacy			 F_PCS_RESET_, 0);
100211346Snp
101211346Snp	/* No delay required */
102211346Snp
103167514Skmacy	t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset, 0,
104167514Skmacy			 F_PCS_RESET_);
105167514Skmacy}
106167514Skmacy
107211346Snpvoid t3c_pcs_force_los(struct cmac *mac)
108211346Snp{
109211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT0 + mac->offset,
110211346Snp	    F_LOWSIGFORCEEN0 | F_LOWSIGFORCEVALUE0,
111211346Snp	    F_LOWSIGFORCEEN0 | F_LOWSIGFORCEVALUE0);
112211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT1 + mac->offset,
113211346Snp	    F_LOWSIGFORCEEN1 | F_LOWSIGFORCEVALUE1,
114211346Snp	    F_LOWSIGFORCEEN1 | F_LOWSIGFORCEVALUE1);
115211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT2 + mac->offset,
116211346Snp	    F_LOWSIGFORCEEN2 | F_LOWSIGFORCEVALUE2,
117211346Snp	    F_LOWSIGFORCEEN2 | F_LOWSIGFORCEVALUE2);
118211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT3 + mac->offset,
119211346Snp	    F_LOWSIGFORCEEN3 | F_LOWSIGFORCEVALUE3,
120211346Snp	    F_LOWSIGFORCEEN3 | F_LOWSIGFORCEVALUE3);
121211346Snp
122211346Snp	/* No delay required */
123211346Snp
124211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT0 + mac->offset,
125211346Snp	    F_LOWSIGFORCEEN0, 0);
126211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT1 + mac->offset,
127211346Snp	    F_LOWSIGFORCEEN1, 0);
128211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT2 + mac->offset,
129211346Snp	    F_LOWSIGFORCEEN2, 0);
130211346Snp	t3_set_reg_field(mac->adapter, A_XGM_SERDES_STAT3 + mac->offset,
131211346Snp	    F_LOWSIGFORCEEN3, 0);
132211346Snp}
133211346Snp
134176472Skmacy/**
135197791Snp *	t3_mac_init - initialize a MAC
136197791Snp *	@mac: the MAC to initialize
137176472Skmacy *
138197791Snp *	Initialize the given MAC.
139176472Skmacy */
140197791Snpint t3_mac_init(struct cmac *mac)
141167514Skmacy{
142167514Skmacy	static struct addr_val_pair mac_reset_avp[] = {
143167514Skmacy		{ A_XGM_TX_CTRL, 0 },
144167514Skmacy		{ A_XGM_RX_CTRL, 0 },
145167514Skmacy		{ A_XGM_RX_CFG, F_DISPAUSEFRAMES | F_EN1536BFRAMES |
146167514Skmacy		                F_RMFCS | F_ENJUMBO | F_ENHASHMCAST },
147167514Skmacy		{ A_XGM_RX_HASH_LOW, 0 },
148167514Skmacy		{ A_XGM_RX_HASH_HIGH, 0 },
149167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_1, 0 },
150167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_2, 0 },
151167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_3, 0 },
152167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_4, 0 },
153167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_5, 0 },
154167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_6, 0 },
155167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_7, 0 },
156167514Skmacy		{ A_XGM_RX_EXACT_MATCH_LOW_8, 0 },
157167514Skmacy		{ A_XGM_STAT_CTRL, F_CLRSTATS }
158167514Skmacy	};
159167514Skmacy	u32 val;
160167514Skmacy	adapter_t *adap = mac->adapter;
161167514Skmacy	unsigned int oft = mac->offset;
162167514Skmacy
163167514Skmacy	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_);
164167514Skmacy	(void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft);    /* flush */
165167514Skmacy
166167514Skmacy	t3_write_regs(adap, mac_reset_avp, ARRAY_SIZE(mac_reset_avp), oft);
167167514Skmacy	t3_set_reg_field(adap, A_XGM_RXFIFO_CFG + oft,
168167514Skmacy			 F_RXSTRFRWRD | F_DISERRFRAMES,
169167514Skmacy			 uses_xaui(adap) ? 0 : F_RXSTRFRWRD);
170176472Skmacy	t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + oft, 0, F_UNDERUNFIX);
171167514Skmacy
172167514Skmacy	if (uses_xaui(adap)) {
173167514Skmacy		if (adap->params.rev == 0) {
174167514Skmacy			t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0,
175167514Skmacy					 F_RXENABLE | F_TXENABLE);
176167514Skmacy			if (t3_wait_op_done(adap, A_XGM_SERDES_STATUS1 + oft,
177167514Skmacy					    F_CMULOCK, 1, 5, 2)) {
178167514Skmacy				CH_ERR(adap,
179167514Skmacy				       "MAC %d XAUI SERDES CMU lock failed\n",
180167514Skmacy				       macidx(mac));
181167514Skmacy				return -1;
182167514Skmacy			}
183167514Skmacy			t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0,
184167514Skmacy					 F_SERDESRESET_);
185167514Skmacy		} else
186167514Skmacy			xaui_serdes_reset(mac);
187167514Skmacy	}
188167514Skmacy
189170654Skmacy
190170654Skmacy	if (mac->multiport) {
191170654Skmacy		t3_write_reg(adap, A_XGM_RX_MAX_PKT_SIZE + oft,
192197791Snp			     V_RXMAXPKTSIZE(MAX_FRAME_SIZE - 4));
193170654Skmacy		t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + oft, 0,
194170654Skmacy				 F_DISPREAMBLE);
195170654Skmacy		t3_set_reg_field(adap, A_XGM_RX_CFG + oft, 0, F_COPYPREAMBLE |
196170654Skmacy				 F_ENNON802_3PREAMBLE);
197170654Skmacy		t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + oft,
198170654Skmacy				 V_TXFIFOTHRESH(M_TXFIFOTHRESH),
199170654Skmacy				 V_TXFIFOTHRESH(64));
200170654Skmacy		t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN);
201170654Skmacy		t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN);
202170654Skmacy	}
203180583Skmacy
204176472Skmacy	t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + oft,
205176472Skmacy			 V_RXMAXFRAMERSIZE(M_RXMAXFRAMERSIZE),
206176472Skmacy			 V_RXMAXFRAMERSIZE(MAX_FRAME_SIZE) | F_RXENFRAMER);
207180583Skmacy
208197791Snp	val = xgm_reset_ctrl(mac);
209167514Skmacy	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val);
210167514Skmacy	(void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft);  /* flush */
211167514Skmacy	if ((val & F_PCS_RESET_) && adap->params.rev) {
212171471Skmacy		msleep(1);
213167514Skmacy		t3b_pcs_reset(mac);
214167514Skmacy	}
215167514Skmacy
216167514Skmacy	memset(&mac->stats, 0, sizeof(mac->stats));
217167514Skmacy	return 0;
218167514Skmacy}
219167514Skmacy
220197791Snpstatic int t3_mac_reset(struct cmac *mac, int portspeed)
221167746Skmacy{
222197791Snp	u32 val, store_mps;
223167746Skmacy	adapter_t *adap = mac->adapter;
224167746Skmacy	unsigned int oft = mac->offset;
225181614Skmacy	int idx = macidx(mac);
226181614Skmacy	unsigned int store;
227167746Skmacy
228167746Skmacy	/* Stop egress traffic to xgm*/
229197791Snp	store_mps = t3_read_reg(adap, A_MPS_CFG);
230197791Snp	if (!idx)
231180583Skmacy		t3_set_reg_field(adap, A_MPS_CFG, F_PORT0ACTIVE, 0);
232167746Skmacy	else
233180583Skmacy		t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE, 0);
234167746Skmacy
235189643Sgnn	/* This will reduce the number of TXTOGGLES */
236189643Sgnn	/* Clear: to stop the NIC traffic */
237189643Sgnn	t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 0);
238189643Sgnn	/* Ensure TX drains */
239189643Sgnn	t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, 0);
240189643Sgnn
241167746Skmacy	/* PCS in reset */
242167746Skmacy	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_);
243167746Skmacy	(void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft);    /* flush */
244167746Skmacy
245181614Skmacy	/* Store A_TP_TX_DROP_CFG_CH0 */
246181614Skmacy	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
247197791Snp	store = t3_read_reg(adap, A_TP_PIO_DATA);
248181614Skmacy
249171471Skmacy	msleep(10);
250167746Skmacy
251181614Skmacy	/* Change DROP_CFG to 0xc0000011 */
252181614Skmacy	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
253181614Skmacy	t3_write_reg(adap, A_TP_PIO_DATA, 0xc0000011);
254181614Skmacy
255167746Skmacy	/* Check for xgm Rx fifo empty */
256181614Skmacy	/* Increased loop count to 1000 from 5 cover 1G and 100Mbps case */
257167746Skmacy	if (t3_wait_op_done(adap, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT + oft,
258197791Snp			    0x80000000, 1, 1000, 2) && portspeed < 0) {
259197791Snp		CH_ERR(adap, "MAC %d Rx fifo drain failed\n", idx);
260167746Skmacy		return -1;
261167746Skmacy	}
262167746Skmacy
263197791Snp	if (portspeed >= 0) {
264197791Snp		u32 intr = t3_read_reg(adap, A_XGM_INT_ENABLE + oft);
265167746Skmacy
266197791Snp		/*
267197791Snp		 * safespeedchange: wipes out pretty much all XGMAC registers.
268197791Snp		 */
269197791Snp
270197791Snp		t3_set_reg_field(adap, A_XGM_PORT_CFG + oft,
271197791Snp		    V_PORTSPEED(M_PORTSPEED) | F_SAFESPEEDCHANGE,
272197791Snp		    portspeed | F_SAFESPEEDCHANGE);
273197791Snp		(void) t3_read_reg(adap, A_XGM_PORT_CFG + oft);
274197791Snp		t3_set_reg_field(adap, A_XGM_PORT_CFG + oft,
275197791Snp		    F_SAFESPEEDCHANGE, 0);
276197791Snp		(void) t3_read_reg(adap, A_XGM_PORT_CFG + oft);
277197791Snp		t3_mac_init(mac);
278197791Snp
279197791Snp		t3_write_reg(adap, A_XGM_INT_ENABLE + oft, intr);
280197791Snp	} else {
281197791Snp
282197791Snp		t3_write_reg(adap, A_XGM_RESET_CTRL + oft, 0); /*MAC in reset*/
283197791Snp		(void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft);    /* flush */
284197791Snp
285197791Snp		val = xgm_reset_ctrl(mac);
286197791Snp		t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val);
287197791Snp		(void) t3_read_reg(adap, A_XGM_RESET_CTRL + oft);  /* flush */
288197791Snp		if ((val & F_PCS_RESET_) && adap->params.rev) {
289197791Snp			msleep(1);
290197791Snp			t3b_pcs_reset(mac);
291197791Snp		}
292197791Snp		t3_write_reg(adap, A_XGM_RX_CFG + oft,
293197791Snp			 F_DISPAUSEFRAMES | F_EN1536BFRAMES |
294197791Snp					F_RMFCS | F_ENJUMBO | F_ENHASHMCAST );
295167746Skmacy	}
296167746Skmacy
297181614Skmacy	/* Restore the DROP_CFG */
298181614Skmacy	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
299181614Skmacy	t3_write_reg(adap, A_TP_PIO_DATA, store);
300181614Skmacy
301181614Skmacy	/* Resume egress traffic to xgm */
302197791Snp	t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE | F_PORT0ACTIVE,
303197791Snp			 store_mps);
304167746Skmacy
305189643Sgnn	/*  Set: re-enable NIC traffic */
306197791Snp	t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, F_ENFORCEPKT);
307189643Sgnn
308167746Skmacy	return 0;
309167746Skmacy}
310167746Skmacy
311167514Skmacy/*
312167514Skmacy * Set the exact match register 'idx' to recognize the given Ethernet address.
313167514Skmacy */
314167514Skmacystatic void set_addr_filter(struct cmac *mac, int idx, const u8 *addr)
315167514Skmacy{
316167514Skmacy	u32 addr_lo, addr_hi;
317167514Skmacy	unsigned int oft = mac->offset + idx * 8;
318167514Skmacy
319167514Skmacy	addr_lo = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
320167514Skmacy	addr_hi = (addr[5] << 8) | addr[4];
321167514Skmacy
322167514Skmacy	t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1 + oft, addr_lo);
323167514Skmacy	t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_HIGH_1 + oft, addr_hi);
324167514Skmacy}
325167514Skmacy
326176472Skmacy/**
327176472Skmacy *	t3_mac_set_address - set one of the station's unicast MAC addresses
328176472Skmacy *	@mac: the MAC handle
329176472Skmacy *	@idx: index of the exact address match filter to use
330176472Skmacy *	@addr: the Ethernet address
331176472Skmacy *
332176472Skmacy *	Set one of the station's unicast MAC addresses.
333176472Skmacy */
334167514Skmacyint t3_mac_set_address(struct cmac *mac, unsigned int idx, u8 addr[6])
335167514Skmacy{
336170654Skmacy	if (mac->multiport)
337170654Skmacy		idx = mac->ext_port + idx * mac->adapter->params.nports;
338167514Skmacy	if (idx >= mac->nucast)
339167514Skmacy		return -EINVAL;
340167514Skmacy	set_addr_filter(mac, idx, addr);
341170654Skmacy	if (mac->multiport && idx < mac->adapter->params.nports)
342170654Skmacy		t3_vsc7323_set_addr(mac->adapter, addr, idx);
343167514Skmacy	return 0;
344167514Skmacy}
345167514Skmacy
346176472Skmacy/**
347176472Skmacy *	t3_mac_set_num_ucast - set the number of unicast addresses needed
348176472Skmacy *	@mac: the MAC handle
349176472Skmacy *	@n: number of unicast addresses needed
350176472Skmacy *
351176472Skmacy *	Specify the number of exact address filters that should be reserved for
352176472Skmacy *	unicast addresses.  Caller should reload the unicast and multicast
353176472Skmacy *	addresses after calling this.
354180583Skmacy *
355180583Skmacy *	Generally, this is 1 with the first one used for the station address,
356180583Skmacy *	and the rest are available for multicast addresses.
357167514Skmacy */
358170654Skmacyint t3_mac_set_num_ucast(struct cmac *mac, unsigned char n)
359167514Skmacy{
360167514Skmacy	if (n > EXACT_ADDR_FILTERS)
361167514Skmacy		return -EINVAL;
362167514Skmacy	mac->nucast = n;
363167514Skmacy	return 0;
364167514Skmacy}
365167514Skmacy
366189643Sgnnvoid t3_mac_disable_exact_filters(struct cmac *mac)
367170654Skmacy{
368170654Skmacy	unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_LOW_1;
369170654Skmacy
370170654Skmacy	for (i = 0; i < EXACT_ADDR_FILTERS; i++, reg += 8) {
371170654Skmacy		u32 v = t3_read_reg(mac->adapter, reg);
372170654Skmacy		t3_write_reg(mac->adapter, reg, v);
373170654Skmacy	}
374170654Skmacy	t3_read_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1); /* flush */
375170654Skmacy}
376170654Skmacy
377189643Sgnnvoid t3_mac_enable_exact_filters(struct cmac *mac)
378170654Skmacy{
379170654Skmacy	unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_HIGH_1;
380170654Skmacy
381170654Skmacy	for (i = 0; i < EXACT_ADDR_FILTERS; i++, reg += 8) {
382170654Skmacy		u32 v = t3_read_reg(mac->adapter, reg);
383170654Skmacy		t3_write_reg(mac->adapter, reg, v);
384170654Skmacy	}
385170654Skmacy	t3_read_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1); /* flush */
386170654Skmacy}
387170654Skmacy
388167514Skmacy/* Calculate the RX hash filter index of an Ethernet address */
389167514Skmacystatic int hash_hw_addr(const u8 *addr)
390167514Skmacy{
391167514Skmacy	int hash = 0, octet, bit, i = 0, c;
392167514Skmacy
393167514Skmacy	for (octet = 0; octet < 6; ++octet)
394167514Skmacy		for (c = addr[octet], bit = 0; bit < 8; c >>= 1, ++bit) {
395167514Skmacy			hash ^= (c & 1) << i;
396167514Skmacy			if (++i == 6)
397167514Skmacy				i = 0;
398167514Skmacy		}
399167514Skmacy	return hash;
400167514Skmacy}
401167514Skmacy
402176472Skmacy/**
403176472Skmacy *	t3_mac_set_rx_mode - set the Rx mode and address filters
404176472Skmacy *	@mac: the MAC to configure
405176472Skmacy *	@rm: structure containing the Rx mode and MAC addresses needed
406176472Skmacy *
407176472Skmacy *	Configures the MAC Rx mode (promiscuity, etc) and exact and hash
408176472Skmacy *	address filters.
409176472Skmacy */
410167514Skmacyint t3_mac_set_rx_mode(struct cmac *mac, struct t3_rx_mode *rm)
411167514Skmacy{
412170654Skmacy	u32 hash_lo, hash_hi;
413167514Skmacy	adapter_t *adap = mac->adapter;
414167514Skmacy	unsigned int oft = mac->offset;
415167514Skmacy
416167514Skmacy	if (promisc_rx_mode(rm))
417170654Skmacy		mac->promisc_map |= 1 << mac->ext_port;
418170654Skmacy	else
419170654Skmacy		mac->promisc_map &= ~(1 << mac->ext_port);
420170654Skmacy	t3_set_reg_field(adap, A_XGM_RX_CFG + oft, F_COPYALLFRAMES,
421170654Skmacy			 mac->promisc_map ? F_COPYALLFRAMES : 0);
422167514Skmacy
423170654Skmacy	if (allmulti_rx_mode(rm) || mac->multiport)
424167514Skmacy		hash_lo = hash_hi = 0xffffffff;
425167514Skmacy	else {
426167514Skmacy		u8 *addr;
427167514Skmacy		int exact_addr_idx = mac->nucast;
428167514Skmacy
429167514Skmacy		hash_lo = hash_hi = 0;
430167514Skmacy		while ((addr = t3_get_next_mcaddr(rm)))
431167514Skmacy			if (exact_addr_idx < EXACT_ADDR_FILTERS)
432167514Skmacy				set_addr_filter(mac, exact_addr_idx++, addr);
433167514Skmacy			else {
434167514Skmacy				int hash = hash_hw_addr(addr);
435167514Skmacy
436167514Skmacy				if (hash < 32)
437167514Skmacy					hash_lo |= (1 << hash);
438167514Skmacy				else
439167514Skmacy					hash_hi |= (1 << (hash - 32));
440167514Skmacy			}
441167514Skmacy	}
442167514Skmacy
443167514Skmacy	t3_write_reg(adap, A_XGM_RX_HASH_LOW + oft, hash_lo);
444167514Skmacy	t3_write_reg(adap, A_XGM_RX_HASH_HIGH + oft, hash_hi);
445167514Skmacy	return 0;
446167514Skmacy}
447167514Skmacy
448170654Skmacystatic int rx_fifo_hwm(int mtu)
449167514Skmacy{
450170654Skmacy	int hwm;
451170654Skmacy
452170654Skmacy	hwm = max(MAC_RXFIFO_SIZE - 3 * mtu, (MAC_RXFIFO_SIZE * 38) / 100);
453170654Skmacy	return min(hwm, MAC_RXFIFO_SIZE - 8192);
454170654Skmacy}
455170654Skmacy
456176472Skmacy/**
457176472Skmacy *	t3_mac_set_mtu - set the MAC MTU
458176472Skmacy *	@mac: the MAC to configure
459176472Skmacy *	@mtu: the MTU
460176472Skmacy *
461176472Skmacy *	Sets the MAC MTU and adjusts the FIFO PAUSE watermarks accordingly.
462176472Skmacy */
463180583Skmacyint t3_mac_set_mtu(struct cmac *mac, unsigned int mtu)
464170654Skmacy{
465211347Snp	int hwm, lwm;
466176472Skmacy	int ipg;
467176472Skmacy	unsigned int thres, v, reg;
468167514Skmacy	adapter_t *adap = mac->adapter;
469197791Snp	unsigned port_type = adap->params.vpd.port_type[macidx(mac)];
470197791Snp	unsigned int orig_mtu=mtu;
471167514Skmacy
472167514Skmacy	/*
473167514Skmacy	 * MAX_FRAME_SIZE inludes header + FCS, mtu doesn't.  The HW max
474167514Skmacy	 * packet size register includes header, but not FCS.
475167514Skmacy	 */
476167514Skmacy	mtu += 14;
477170654Skmacy	if (mac->multiport)
478170654Skmacy		mtu += 8;                             /* for preamble */
479167514Skmacy	if (mtu > MAX_FRAME_SIZE - 4)
480167514Skmacy		return -EINVAL;
481170654Skmacy	if (mac->multiport)
482170654Skmacy		return t3_vsc7323_set_mtu(adap, mtu - 4, mac->ext_port);
483167514Skmacy
484197791Snp	/* Modify the TX and RX fifo depth only if the card has a vsc8211 phy */
485197791Snp	if (port_type == 2) {
486197791Snp		int err = t3_vsc8211_fifo_depth(adap,orig_mtu,macidx(mac));
487197791Snp
488197791Snp		if (err)
489197791Snp			return err;
490197791Snp	}
491197791Snp
492176472Skmacy	if (adap->params.rev >= T3_REV_B2 &&
493170654Skmacy	    (t3_read_reg(adap, A_XGM_RX_CTRL + mac->offset) & F_RXEN)) {
494189643Sgnn		t3_mac_disable_exact_filters(mac);
495170654Skmacy		v = t3_read_reg(adap, A_XGM_RX_CFG + mac->offset);
496170654Skmacy		t3_set_reg_field(adap, A_XGM_RX_CFG + mac->offset,
497170654Skmacy				 F_ENHASHMCAST | F_COPYALLFRAMES, F_DISBCAST);
498170654Skmacy
499176472Skmacy		reg = adap->params.rev == T3_REV_B2 ?
500176472Skmacy			A_XGM_RX_MAX_PKT_SIZE_ERR_CNT : A_XGM_RXFIFO_CFG;
501180583Skmacy
502176472Skmacy		/* drain RX FIFO */
503176472Skmacy		if (t3_wait_op_done(adap, reg + mac->offset,
504176472Skmacy				    F_RXFIFO_EMPTY, 1, 20, 5)) {
505170654Skmacy			t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
506189643Sgnn			t3_mac_enable_exact_filters(mac);
507170654Skmacy			return -EIO;
508170654Skmacy		}
509176472Skmacy		t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
510176472Skmacy				 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),
511176472Skmacy				 V_RXMAXPKTSIZE(mtu));
512170654Skmacy		t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
513189643Sgnn		t3_mac_enable_exact_filters(mac);
514170654Skmacy	} else
515176472Skmacy		t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
516180583Skmacy				 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),
517180583Skmacy				 V_RXMAXPKTSIZE(mtu));
518167514Skmacy	/*
519167514Skmacy	 * Adjust the PAUSE frame watermarks.  We always set the LWM, and the
520167514Skmacy	 * HWM only if flow-control is enabled.
521167514Skmacy	 */
522170654Skmacy	hwm = rx_fifo_hwm(mtu);
523167746Skmacy	lwm = min(3 * (int) mtu, MAC_RXFIFO_SIZE /4);
524167514Skmacy	v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset);
525167514Skmacy	v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM);
526167514Skmacy	v |= V_RXFIFOPAUSELWM(lwm / 8);
527167514Skmacy	if (G_RXFIFOPAUSEHWM(v))
528167514Skmacy		v = (v & ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM)) |
529167514Skmacy		    V_RXFIFOPAUSEHWM(hwm / 8);
530170654Skmacy
531167514Skmacy	t3_write_reg(adap, A_XGM_RXFIFO_CFG + mac->offset, v);
532167514Skmacy
533167514Skmacy	/* Adjust the TX FIFO threshold based on the MTU */
534167514Skmacy	thres = (adap->params.vpd.cclk * 1000) / 15625;
535167514Skmacy	thres = (thres * mtu) / 1000;
536167514Skmacy	if (is_10G(adap))
537167514Skmacy		thres /= 10;
538167514Skmacy	thres = mtu > thres ? (mtu - thres + 7) / 8 : 0;
539167514Skmacy	thres = max(thres, 8U);                          /* need at least 8 */
540199239Snp	ipg = (port_type == 9 || adap->params.rev != T3_REV_C) ? 1 : 0;
541167514Skmacy	t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset,
542169978Skmacy			 V_TXFIFOTHRESH(M_TXFIFOTHRESH) | V_TXIPG(M_TXIPG),
543176472Skmacy			 V_TXFIFOTHRESH(thres) | V_TXIPG(ipg));
544167514Skmacy	return 0;
545167514Skmacy}
546167514Skmacy
547176472Skmacy/**
548176472Skmacy *	t3_mac_set_speed_duplex_fc - set MAC speed, duplex and flow control
549176472Skmacy *	@mac: the MAC to configure
550176472Skmacy *	@speed: the desired speed (10/100/1000/10000)
551176472Skmacy *	@duplex: the desired duplex
552176472Skmacy *	@fc: desired Tx/Rx PAUSE configuration
553176472Skmacy *
554176472Skmacy *	Set the MAC speed, duplex (actually only full-duplex is supported), and
555176472Skmacy *	flow control.  If a parameter value is negative the corresponding
556176472Skmacy *	MAC setting is left at its current value.
557176472Skmacy */
558167514Skmacyint t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc)
559167514Skmacy{
560167514Skmacy	u32 val;
561167514Skmacy	adapter_t *adap = mac->adapter;
562167514Skmacy	unsigned int oft = mac->offset;
563211347Snp	unsigned int pause_bits;
564167514Skmacy
565167514Skmacy	if (duplex >= 0 && duplex != DUPLEX_FULL)
566167514Skmacy		return -EINVAL;
567211347Snp
568211347Snp	pause_bits = MAC_RXFIFO_SIZE * 4 * 8;
569211347Snp	t3_write_reg(adap, A_XGM_TX_PAUSE_QUANTA + mac->offset,
570211347Snp		     pause_bits / 512);
571211347Snp	t3_write_reg(adap, A_XGM_PAUSE_TIMER + mac->offset,
572211347Snp		     (pause_bits >> (adap->params.rev == T3_REV_C ? 10 : 7)));
573211347Snp
574180583Skmacy	if (mac->multiport) {
575197791Snp		u32 rx_max_pkt_size =
576197791Snp		    G_RXMAXPKTSIZE(t3_read_reg(adap,
577197791Snp					       A_XGM_RX_MAX_PKT_SIZE + oft));
578171471Skmacy		val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft);
579171471Skmacy		val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM);
580197791Snp		val |= V_RXFIFOPAUSEHWM(rx_fifo_hwm(rx_max_pkt_size) / 8);
581171471Skmacy		t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val);
582171471Skmacy		t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN,
583171471Skmacy			  		F_TXPAUSEEN);
584211347Snp
585170654Skmacy		return t3_vsc7323_set_speed_fc(adap, speed, fc, mac->ext_port);
586171471Skmacy	}
587167514Skmacy	if (speed >= 0) {
588167514Skmacy		if (speed == SPEED_10)
589167514Skmacy			val = V_PORTSPEED(0);
590167514Skmacy		else if (speed == SPEED_100)
591167514Skmacy			val = V_PORTSPEED(1);
592167514Skmacy		else if (speed == SPEED_1000)
593167514Skmacy			val = V_PORTSPEED(2);
594167514Skmacy		else if (speed == SPEED_10000)
595167514Skmacy			val = V_PORTSPEED(3);
596167514Skmacy		else
597167514Skmacy			return -EINVAL;
598167514Skmacy
599197791Snp		if (!uses_xaui(adap)) /* T302 */
600197791Snp			t3_set_reg_field(adap, A_XGM_PORT_CFG + oft,
601197791Snp			    V_PORTSPEED(M_PORTSPEED), val);
602197791Snp		else {
603197791Snp			u32 old = t3_read_reg(adap, A_XGM_PORT_CFG + oft);
604197791Snp
605197791Snp			if ((old & V_PORTSPEED(M_PORTSPEED)) != val) {
606197791Snp				t3_mac_reset(mac, val);
607197791Snp				mac->was_reset = 1;
608197791Snp			}
609197791Snp		}
610167514Skmacy	}
611170654Skmacy
612167514Skmacy	val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft);
613167514Skmacy	val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM);
614197791Snp	if (fc & PAUSE_TX) {
615197791Snp		u32 rx_max_pkt_size =
616197791Snp		    G_RXMAXPKTSIZE(t3_read_reg(adap,
617197791Snp					       A_XGM_RX_MAX_PKT_SIZE + oft));
618197791Snp		val |= V_RXFIFOPAUSEHWM(rx_fifo_hwm(rx_max_pkt_size) / 8);
619197791Snp	}
620167514Skmacy	t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val);
621170654Skmacy
622167514Skmacy	t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN,
623171471Skmacy			(fc & PAUSE_RX) ? F_TXPAUSEEN : 0);
624167514Skmacy	return 0;
625167514Skmacy}
626167514Skmacy
627176472Skmacy/**
628176472Skmacy *	t3_mac_enable - enable the MAC in the given directions
629176472Skmacy *	@mac: the MAC to configure
630176472Skmacy *	@which: bitmap indicating which directions to enable
631176472Skmacy *
632176472Skmacy *	Enables the MAC for operation in the given directions.
633176472Skmacy *	%MAC_DIRECTION_TX enables the Tx direction, and %MAC_DIRECTION_RX
634176472Skmacy *	enables the Rx one.
635176472Skmacy */
636167514Skmacyint t3_mac_enable(struct cmac *mac, int which)
637167514Skmacy{
638167514Skmacy	int idx = macidx(mac);
639167514Skmacy	adapter_t *adap = mac->adapter;
640167514Skmacy	unsigned int oft = mac->offset;
641169978Skmacy	struct mac_stats *s = &mac->stats;
642167514Skmacy
643170654Skmacy	if (mac->multiport)
644170654Skmacy		return t3_vsc7323_enable(adap, mac->ext_port, which);
645170654Skmacy
646167514Skmacy	if (which & MAC_DIRECTION_TX) {
647167514Skmacy		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
648176472Skmacy		t3_write_reg(adap, A_TP_PIO_DATA,
649176472Skmacy			     adap->params.rev == T3_REV_C ?
650176472Skmacy			     0xc4ffff01 : 0xc0ede401);
651167514Skmacy		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE);
652176472Skmacy		t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx,
653176472Skmacy				 adap->params.rev == T3_REV_C ?
654176472Skmacy				 0 : 1 << idx);
655167746Skmacy
656171471Skmacy		t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN);
657171471Skmacy
658167746Skmacy		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CNT_CH0 + idx);
659169978Skmacy		mac->tx_mcnt = s->tx_frames;
660169978Skmacy		mac->tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap,
661169978Skmacy							       A_TP_PIO_DATA)));
662169978Skmacy		mac->tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
663169978Skmacy						A_XGM_TX_SPI4_SOP_EOP_CNT +
664169978Skmacy						oft)));
665169978Skmacy		mac->rx_mcnt = s->rx_frames;
666172096Skmacy		mac->rx_pause = s->rx_pause;
667169978Skmacy		mac->rx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
668169978Skmacy						A_XGM_RX_SPI4_SOP_EOP_CNT +
669169978Skmacy						oft)));
670172096Skmacy		mac->rx_ocnt = s->rx_fifo_ovfl;
671167746Skmacy		mac->txen = F_TXEN;
672167746Skmacy		mac->toggle_cnt = 0;
673167514Skmacy	}
674180583Skmacy	if (which & MAC_DIRECTION_RX)
675167514Skmacy		t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN);
676167514Skmacy	return 0;
677167514Skmacy}
678167514Skmacy
679176472Skmacy/**
680176472Skmacy *	t3_mac_disable - disable the MAC in the given directions
681176472Skmacy *	@mac: the MAC to configure
682176472Skmacy *	@which: bitmap indicating which directions to disable
683176472Skmacy *
684176472Skmacy *	Disables the MAC in the given directions.
685176472Skmacy *	%MAC_DIRECTION_TX disables the Tx direction, and %MAC_DIRECTION_RX
686176472Skmacy *	disables the Rx one.
687176472Skmacy */
688167514Skmacyint t3_mac_disable(struct cmac *mac, int which)
689167514Skmacy{
690167514Skmacy	adapter_t *adap = mac->adapter;
691167514Skmacy
692170654Skmacy	if (mac->multiport)
693170654Skmacy		return t3_vsc7323_disable(adap, mac->ext_port, which);
694170654Skmacy
695167514Skmacy	if (which & MAC_DIRECTION_TX) {
696167514Skmacy		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0);
697167746Skmacy		mac->txen = 0;
698167514Skmacy	}
699169978Skmacy	if (which & MAC_DIRECTION_RX) {
700197791Snp		int val = xgm_reset_ctrl(mac);
701172096Skmacy
702169978Skmacy		t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset,
703169978Skmacy				 F_PCS_RESET_, 0);
704171471Skmacy		msleep(100);
705167514Skmacy		t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0);
706169978Skmacy		t3_write_reg(mac->adapter, A_XGM_RESET_CTRL + mac->offset, val);
707169978Skmacy	}
708167514Skmacy	return 0;
709167514Skmacy}
710167514Skmacy
711167746Skmacyint t3b2_mac_watchdog_task(struct cmac *mac)
712167746Skmacy{
713167746Skmacy	int status;
714169978Skmacy	unsigned int tx_tcnt, tx_xcnt;
715167746Skmacy	adapter_t *adap = mac->adapter;
716169978Skmacy	struct mac_stats *s = &mac->stats;
717189643Sgnn	u64 tx_mcnt = s->tx_frames;
718167746Skmacy
719189643Sgnn	if (mac->multiport)
720189643Sgnn		tx_mcnt = t3_read_reg(adap, A_XGM_STAT_TX_FRAME_LOW);
721189643Sgnn
722169978Skmacy	status = 0;
723169978Skmacy	tx_xcnt = 1; /* By default tx_xcnt is making progress*/
724169978Skmacy	tx_tcnt = mac->tx_tcnt; /* If tx_mcnt is progressing ignore tx_tcnt*/
725172096Skmacy	if (tx_mcnt == mac->tx_mcnt && mac->rx_pause == s->rx_pause) {
726197791Snp		u32 cfg, active, enforcepkt;
727197791Snp
728169978Skmacy		tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
729189643Sgnn						      A_XGM_TX_SPI4_SOP_EOP_CNT +
730189643Sgnn						      mac->offset)));
731197791Snp		cfg = t3_read_reg(adap, A_MPS_CFG);
732197791Snp		active = macidx(mac) ? cfg & F_PORT1ACTIVE : cfg & F_PORT0ACTIVE;
733197791Snp		enforcepkt = cfg & F_ENFORCEPKT;
734197791Snp		if (active && enforcepkt && (tx_xcnt == 0)) {
735169978Skmacy			t3_write_reg(adap, A_TP_PIO_ADDR,
736169978Skmacy			     	A_TP_TX_DROP_CNT_CH0 + macidx(mac));
737169978Skmacy			tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap,
738169978Skmacy			      	A_TP_PIO_DATA)));
739189643Sgnn		} else
740185564Sgnn			goto out;
741189643Sgnn
742169978Skmacy	} else {
743169978Skmacy		mac->toggle_cnt = 0;
744185564Sgnn		goto out;
745169978Skmacy	}
746169978Skmacy
747172096Skmacy	if ((tx_tcnt != mac->tx_tcnt) && (mac->tx_xcnt == 0)) {
748167746Skmacy		if (mac->toggle_cnt > 4) {
749167746Skmacy			status = 2;
750169978Skmacy			goto out;
751167746Skmacy		} else {
752167746Skmacy			status = 1;
753169978Skmacy			goto out;
754169978Skmacy		}
755167746Skmacy	} else {
756167746Skmacy		mac->toggle_cnt = 0;
757169978Skmacy		goto out;
758169978Skmacy	}
759180583Skmacy
760180583Skmacyout:
761169978Skmacy	mac->tx_tcnt = tx_tcnt;
762169978Skmacy	mac->tx_xcnt = tx_xcnt;
763169978Skmacy	mac->tx_mcnt = s->tx_frames;
764172096Skmacy	mac->rx_pause = s->rx_pause;
765169978Skmacy	if (status == 1) {
766169978Skmacy		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0);
767169978Skmacy		t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset);  /* flush */
768169978Skmacy		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, mac->txen);
769169978Skmacy		t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset);  /* flush */
770169978Skmacy		mac->toggle_cnt++;
771169978Skmacy	} else if (status == 2) {
772197791Snp		t3_mac_reset(mac, -1);
773169978Skmacy		mac->toggle_cnt = 0;
774169978Skmacy	}
775167746Skmacy	return status;
776167746Skmacy}
777167746Skmacy
778176472Skmacy/**
779176472Skmacy *	t3_mac_update_stats - accumulate MAC statistics
780176472Skmacy *	@mac: the MAC handle
781176472Skmacy *
782176472Skmacy *	This function is called periodically to accumulate the current values
783176472Skmacy *	of the RMON counters into the port statistics.  Since the packet
784176472Skmacy *	counters are only 32 bits they can overflow in ~286 secs at 10G, so the
785176472Skmacy *	function should be called more frequently than that.  The byte counters
786176472Skmacy *	are 45-bit wide, they would overflow in ~7.8 hours.
787167514Skmacy */
788167514Skmacyconst struct mac_stats *t3_mac_update_stats(struct cmac *mac)
789167514Skmacy{
790167514Skmacy#define RMON_READ(mac, addr) t3_read_reg(mac->adapter, addr + mac->offset)
791167514Skmacy#define RMON_UPDATE(mac, name, reg) \
792167514Skmacy	(mac)->stats.name += (u64)RMON_READ(mac, A_XGM_STAT_##reg)
793167514Skmacy#define RMON_UPDATE64(mac, name, reg_lo, reg_hi) \
794167514Skmacy	(mac)->stats.name += RMON_READ(mac, A_XGM_STAT_##reg_lo) + \
795167514Skmacy			     ((u64)RMON_READ(mac, A_XGM_STAT_##reg_hi) << 32)
796167514Skmacy
797167514Skmacy	u32 v, lo;
798167514Skmacy
799171471Skmacy	if (mac->multiport)
800171471Skmacy		return t3_vsc7323_update_stats(mac);
801171471Skmacy
802167514Skmacy	RMON_UPDATE64(mac, rx_octets, RX_BYTES_LOW, RX_BYTES_HIGH);
803167514Skmacy	RMON_UPDATE64(mac, rx_frames, RX_FRAMES_LOW, RX_FRAMES_HIGH);
804167514Skmacy	RMON_UPDATE(mac, rx_mcast_frames, RX_MCAST_FRAMES);
805167514Skmacy	RMON_UPDATE(mac, rx_bcast_frames, RX_BCAST_FRAMES);
806167514Skmacy	RMON_UPDATE(mac, rx_fcs_errs, RX_CRC_ERR_FRAMES);
807167514Skmacy	RMON_UPDATE(mac, rx_pause, RX_PAUSE_FRAMES);
808167514Skmacy	RMON_UPDATE(mac, rx_jabber, RX_JABBER_FRAMES);
809167514Skmacy	RMON_UPDATE(mac, rx_short, RX_SHORT_FRAMES);
810167514Skmacy	RMON_UPDATE(mac, rx_symbol_errs, RX_SYM_CODE_ERR_FRAMES);
811167514Skmacy
812167514Skmacy	RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES);
813167514Skmacy
814167746Skmacy	v = RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT);
815167746Skmacy	if (mac->adapter->params.rev == T3_REV_B2)
816167746Skmacy		v &= 0x7fffffff;
817167746Skmacy	mac->stats.rx_too_long += v;
818167746Skmacy
819167514Skmacy	RMON_UPDATE(mac, rx_frames_64,        RX_64B_FRAMES);
820167514Skmacy	RMON_UPDATE(mac, rx_frames_65_127,    RX_65_127B_FRAMES);
821167514Skmacy	RMON_UPDATE(mac, rx_frames_128_255,   RX_128_255B_FRAMES);
822167514Skmacy	RMON_UPDATE(mac, rx_frames_256_511,   RX_256_511B_FRAMES);
823167514Skmacy	RMON_UPDATE(mac, rx_frames_512_1023,  RX_512_1023B_FRAMES);
824167514Skmacy	RMON_UPDATE(mac, rx_frames_1024_1518, RX_1024_1518B_FRAMES);
825167514Skmacy	RMON_UPDATE(mac, rx_frames_1519_max,  RX_1519_MAXB_FRAMES);
826167514Skmacy
827167514Skmacy	RMON_UPDATE64(mac, tx_octets, TX_BYTE_LOW, TX_BYTE_HIGH);
828167514Skmacy	RMON_UPDATE64(mac, tx_frames, TX_FRAME_LOW, TX_FRAME_HIGH);
829167514Skmacy	RMON_UPDATE(mac, tx_mcast_frames, TX_MCAST);
830167514Skmacy	RMON_UPDATE(mac, tx_bcast_frames, TX_BCAST);
831167514Skmacy	RMON_UPDATE(mac, tx_pause, TX_PAUSE);
832167514Skmacy	/* This counts error frames in general (bad FCS, underrun, etc). */
833167514Skmacy	RMON_UPDATE(mac, tx_underrun, TX_ERR_FRAMES);
834167514Skmacy
835167514Skmacy	RMON_UPDATE(mac, tx_frames_64,        TX_64B_FRAMES);
836167514Skmacy	RMON_UPDATE(mac, tx_frames_65_127,    TX_65_127B_FRAMES);
837167514Skmacy	RMON_UPDATE(mac, tx_frames_128_255,   TX_128_255B_FRAMES);
838167514Skmacy	RMON_UPDATE(mac, tx_frames_256_511,   TX_256_511B_FRAMES);
839167514Skmacy	RMON_UPDATE(mac, tx_frames_512_1023,  TX_512_1023B_FRAMES);
840167514Skmacy	RMON_UPDATE(mac, tx_frames_1024_1518, TX_1024_1518B_FRAMES);
841167514Skmacy	RMON_UPDATE(mac, tx_frames_1519_max,  TX_1519_MAXB_FRAMES);
842167514Skmacy
843167514Skmacy	/* The next stat isn't clear-on-read. */
844167514Skmacy	t3_write_reg(mac->adapter, A_TP_MIB_INDEX, mac->offset ? 51 : 50);
845167514Skmacy	v = t3_read_reg(mac->adapter, A_TP_MIB_RDATA);
846167514Skmacy	lo = (u32)mac->stats.rx_cong_drops;
847167514Skmacy	mac->stats.rx_cong_drops += (u64)(v - lo);
848167514Skmacy
849167514Skmacy	return &mac->stats;
850167514Skmacy}
851