18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2005-2008 Chelsio, Inc. All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
58c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
118c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
128c2ecf20Sopenharmony_ci *     conditions are met:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
158c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
168c2ecf20Sopenharmony_ci *        disclaimer.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
198c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
208c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
218c2ecf20Sopenharmony_ci *        provided with the distribution.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
308c2ecf20Sopenharmony_ci * SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci#include "common.h"
338c2ecf20Sopenharmony_ci#include "regs.h"
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci/*
368c2ecf20Sopenharmony_ci * # of exact address filters.  The first one is used for the station address,
378c2ecf20Sopenharmony_ci * the rest are available for multicast addresses.
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_ci#define EXACT_ADDR_FILTERS 8
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic inline int macidx(const struct cmac *mac)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	return mac->offset / (XGMAC0_1_BASE_ADDR - XGMAC0_0_BASE_ADDR);
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic void xaui_serdes_reset(struct cmac *mac)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	static const unsigned int clear[] = {
498c2ecf20Sopenharmony_ci		F_PWRDN0 | F_PWRDN1, F_RESETPLL01, F_RESET0 | F_RESET1,
508c2ecf20Sopenharmony_ci		F_PWRDN2 | F_PWRDN3, F_RESETPLL23, F_RESET2 | F_RESET3
518c2ecf20Sopenharmony_ci	};
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	int i;
548c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
558c2ecf20Sopenharmony_ci	u32 ctrl = A_XGM_SERDES_CTRL0 + mac->offset;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	t3_write_reg(adap, ctrl, adap->params.vpd.xauicfg[macidx(mac)] |
588c2ecf20Sopenharmony_ci		     F_RESET3 | F_RESET2 | F_RESET1 | F_RESET0 |
598c2ecf20Sopenharmony_ci		     F_PWRDN3 | F_PWRDN2 | F_PWRDN1 | F_PWRDN0 |
608c2ecf20Sopenharmony_ci		     F_RESETPLL23 | F_RESETPLL01);
618c2ecf20Sopenharmony_ci	t3_read_reg(adap, ctrl);
628c2ecf20Sopenharmony_ci	udelay(15);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clear); i++) {
658c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, ctrl, clear[i], 0);
668c2ecf20Sopenharmony_ci		udelay(15);
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_civoid t3b_pcs_reset(struct cmac *mac)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset,
738c2ecf20Sopenharmony_ci			 F_PCS_RESET_, 0);
748c2ecf20Sopenharmony_ci	udelay(20);
758c2ecf20Sopenharmony_ci	t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset, 0,
768c2ecf20Sopenharmony_ci			 F_PCS_RESET_);
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint t3_mac_reset(struct cmac *mac)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	static const struct addr_val_pair mac_reset_avp[] = {
828c2ecf20Sopenharmony_ci		{A_XGM_TX_CTRL, 0},
838c2ecf20Sopenharmony_ci		{A_XGM_RX_CTRL, 0},
848c2ecf20Sopenharmony_ci		{A_XGM_RX_CFG, F_DISPAUSEFRAMES | F_EN1536BFRAMES |
858c2ecf20Sopenharmony_ci		 F_RMFCS | F_ENJUMBO | F_ENHASHMCAST},
868c2ecf20Sopenharmony_ci		{A_XGM_RX_HASH_LOW, 0},
878c2ecf20Sopenharmony_ci		{A_XGM_RX_HASH_HIGH, 0},
888c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_1, 0},
898c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_2, 0},
908c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_3, 0},
918c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_4, 0},
928c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_5, 0},
938c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_6, 0},
948c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_7, 0},
958c2ecf20Sopenharmony_ci		{A_XGM_RX_EXACT_MATCH_LOW_8, 0},
968c2ecf20Sopenharmony_ci		{A_XGM_STAT_CTRL, F_CLRSTATS}
978c2ecf20Sopenharmony_ci	};
988c2ecf20Sopenharmony_ci	u32 val;
998c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
1008c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_);
1038c2ecf20Sopenharmony_ci	t3_read_reg(adap, A_XGM_RESET_CTRL + oft);	/* flush */
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	t3_write_regs(adap, mac_reset_avp, ARRAY_SIZE(mac_reset_avp), oft);
1068c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_RXFIFO_CFG + oft,
1078c2ecf20Sopenharmony_ci			 F_RXSTRFRWRD | F_DISERRFRAMES,
1088c2ecf20Sopenharmony_ci			 uses_xaui(adap) ? 0 : F_RXSTRFRWRD);
1098c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + oft, 0, F_UNDERUNFIX);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (uses_xaui(adap)) {
1128c2ecf20Sopenharmony_ci		if (adap->params.rev == 0) {
1138c2ecf20Sopenharmony_ci			t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0,
1148c2ecf20Sopenharmony_ci					 F_RXENABLE | F_TXENABLE);
1158c2ecf20Sopenharmony_ci			if (t3_wait_op_done(adap, A_XGM_SERDES_STATUS1 + oft,
1168c2ecf20Sopenharmony_ci					    F_CMULOCK, 1, 5, 2)) {
1178c2ecf20Sopenharmony_ci				CH_ERR(adap,
1188c2ecf20Sopenharmony_ci				       "MAC %d XAUI SERDES CMU lock failed\n",
1198c2ecf20Sopenharmony_ci				       macidx(mac));
1208c2ecf20Sopenharmony_ci				return -1;
1218c2ecf20Sopenharmony_ci			}
1228c2ecf20Sopenharmony_ci			t3_set_reg_field(adap, A_XGM_SERDES_CTRL + oft, 0,
1238c2ecf20Sopenharmony_ci					 F_SERDESRESET_);
1248c2ecf20Sopenharmony_ci		} else
1258c2ecf20Sopenharmony_ci			xaui_serdes_reset(mac);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + oft,
1298c2ecf20Sopenharmony_ci			 V_RXMAXFRAMERSIZE(M_RXMAXFRAMERSIZE),
1308c2ecf20Sopenharmony_ci			 V_RXMAXFRAMERSIZE(MAX_FRAME_SIZE) | F_RXENFRAMER);
1318c2ecf20Sopenharmony_ci	val = F_MAC_RESET_ | F_XGMAC_STOP_EN;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	if (is_10G(adap))
1348c2ecf20Sopenharmony_ci		val |= F_PCS_RESET_;
1358c2ecf20Sopenharmony_ci	else if (uses_xaui(adap))
1368c2ecf20Sopenharmony_ci		val |= F_PCS_RESET_ | F_XG2G_RESET_;
1378c2ecf20Sopenharmony_ci	else
1388c2ecf20Sopenharmony_ci		val |= F_RGMII_RESET_ | F_XG2G_RESET_;
1398c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val);
1408c2ecf20Sopenharmony_ci	t3_read_reg(adap, A_XGM_RESET_CTRL + oft);	/* flush */
1418c2ecf20Sopenharmony_ci	if ((val & F_PCS_RESET_) && adap->params.rev) {
1428c2ecf20Sopenharmony_ci		msleep(1);
1438c2ecf20Sopenharmony_ci		t3b_pcs_reset(mac);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	memset(&mac->stats, 0, sizeof(mac->stats));
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic int t3b2_mac_reset(struct cmac *mac)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
1538c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset, store;
1548c2ecf20Sopenharmony_ci	int idx = macidx(mac);
1558c2ecf20Sopenharmony_ci	u32 val;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	if (!macidx(mac))
1588c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_MPS_CFG, F_PORT0ACTIVE, 0);
1598c2ecf20Sopenharmony_ci	else
1608c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE, 0);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* Stop NIC traffic to reduce the number of TXTOGGLES */
1638c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 0);
1648c2ecf20Sopenharmony_ci	/* Ensure TX drains */
1658c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, 0);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_);
1688c2ecf20Sopenharmony_ci	t3_read_reg(adap, A_XGM_RESET_CTRL + oft);    /* flush */
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Store A_TP_TX_DROP_CFG_CH0 */
1718c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
1728c2ecf20Sopenharmony_ci	store = t3_read_reg(adap, A_TP_TX_DROP_CFG_CH0 + idx);
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	msleep(10);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* Change DROP_CFG to 0xc0000011 */
1778c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
1788c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_TP_PIO_DATA, 0xc0000011);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* Check for xgm Rx fifo empty */
1818c2ecf20Sopenharmony_ci	/* Increased loop count to 1000 from 5 cover 1G and 100Mbps case */
1828c2ecf20Sopenharmony_ci	if (t3_wait_op_done(adap, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT + oft,
1838c2ecf20Sopenharmony_ci			    0x80000000, 1, 1000, 2)) {
1848c2ecf20Sopenharmony_ci		CH_ERR(adap, "MAC %d Rx fifo drain failed\n",
1858c2ecf20Sopenharmony_ci		       macidx(mac));
1868c2ecf20Sopenharmony_ci		return -1;
1878c2ecf20Sopenharmony_ci	}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, 0);
1908c2ecf20Sopenharmony_ci	t3_read_reg(adap, A_XGM_RESET_CTRL + oft);    /* flush */
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	val = F_MAC_RESET_;
1938c2ecf20Sopenharmony_ci	if (is_10G(adap))
1948c2ecf20Sopenharmony_ci		val |= F_PCS_RESET_;
1958c2ecf20Sopenharmony_ci	else if (uses_xaui(adap))
1968c2ecf20Sopenharmony_ci		val |= F_PCS_RESET_ | F_XG2G_RESET_;
1978c2ecf20Sopenharmony_ci	else
1988c2ecf20Sopenharmony_ci		val |= F_RGMII_RESET_ | F_XG2G_RESET_;
1998c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val);
2008c2ecf20Sopenharmony_ci	t3_read_reg(adap, A_XGM_RESET_CTRL + oft);  /* flush */
2018c2ecf20Sopenharmony_ci	if ((val & F_PCS_RESET_) && adap->params.rev) {
2028c2ecf20Sopenharmony_ci		msleep(1);
2038c2ecf20Sopenharmony_ci		t3b_pcs_reset(mac);
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RX_CFG + oft,
2068c2ecf20Sopenharmony_ci		     F_DISPAUSEFRAMES | F_EN1536BFRAMES |
2078c2ecf20Sopenharmony_ci		     F_RMFCS | F_ENJUMBO | F_ENHASHMCAST);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	/* Restore the DROP_CFG */
2108c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
2118c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_TP_PIO_DATA, store);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!idx)
2148c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT0ACTIVE);
2158c2ecf20Sopenharmony_ci	else
2168c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT1ACTIVE);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	/* re-enable nic traffic */
2198c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 1);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	/*  Set: re-enable NIC traffic */
2228c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_MPS_CFG, F_ENFORCEPKT, 1);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return 0;
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci/*
2288c2ecf20Sopenharmony_ci * Set the exact match register 'idx' to recognize the given Ethernet address.
2298c2ecf20Sopenharmony_ci */
2308c2ecf20Sopenharmony_cistatic void set_addr_filter(struct cmac *mac, int idx, const u8 * addr)
2318c2ecf20Sopenharmony_ci{
2328c2ecf20Sopenharmony_ci	u32 addr_lo, addr_hi;
2338c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset + idx * 8;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	addr_lo = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
2368c2ecf20Sopenharmony_ci	addr_hi = (addr[5] << 8) | addr[4];
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1 + oft, addr_lo);
2398c2ecf20Sopenharmony_ci	t3_write_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_HIGH_1 + oft, addr_hi);
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci/* Set one of the station's unicast MAC addresses. */
2438c2ecf20Sopenharmony_ciint t3_mac_set_address(struct cmac *mac, unsigned int idx, u8 addr[6])
2448c2ecf20Sopenharmony_ci{
2458c2ecf20Sopenharmony_ci	if (idx >= mac->nucast)
2468c2ecf20Sopenharmony_ci		return -EINVAL;
2478c2ecf20Sopenharmony_ci	set_addr_filter(mac, idx, addr);
2488c2ecf20Sopenharmony_ci	return 0;
2498c2ecf20Sopenharmony_ci}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci/*
2528c2ecf20Sopenharmony_ci * Specify the number of exact address filters that should be reserved for
2538c2ecf20Sopenharmony_ci * unicast addresses.  Caller should reload the unicast and multicast addresses
2548c2ecf20Sopenharmony_ci * after calling this.
2558c2ecf20Sopenharmony_ci */
2568c2ecf20Sopenharmony_ciint t3_mac_set_num_ucast(struct cmac *mac, int n)
2578c2ecf20Sopenharmony_ci{
2588c2ecf20Sopenharmony_ci	if (n > EXACT_ADDR_FILTERS)
2598c2ecf20Sopenharmony_ci		return -EINVAL;
2608c2ecf20Sopenharmony_ci	mac->nucast = n;
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_civoid t3_mac_disable_exact_filters(struct cmac *mac)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_LOW_1;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	for (i = 0; i < EXACT_ADDR_FILTERS; i++, reg += 8) {
2698c2ecf20Sopenharmony_ci		u32 v = t3_read_reg(mac->adapter, reg);
2708c2ecf20Sopenharmony_ci		t3_write_reg(mac->adapter, reg, v);
2718c2ecf20Sopenharmony_ci	}
2728c2ecf20Sopenharmony_ci	t3_read_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1);	/* flush */
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_civoid t3_mac_enable_exact_filters(struct cmac *mac)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	unsigned int i, reg = mac->offset + A_XGM_RX_EXACT_MATCH_HIGH_1;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	for (i = 0; i < EXACT_ADDR_FILTERS; i++, reg += 8) {
2808c2ecf20Sopenharmony_ci		u32 v = t3_read_reg(mac->adapter, reg);
2818c2ecf20Sopenharmony_ci		t3_write_reg(mac->adapter, reg, v);
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci	t3_read_reg(mac->adapter, A_XGM_RX_EXACT_MATCH_LOW_1);	/* flush */
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci/* Calculate the RX hash filter index of an Ethernet address */
2878c2ecf20Sopenharmony_cistatic int hash_hw_addr(const u8 * addr)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	int hash = 0, octet, bit, i = 0, c;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	for (octet = 0; octet < 6; ++octet)
2928c2ecf20Sopenharmony_ci		for (c = addr[octet], bit = 0; bit < 8; c >>= 1, ++bit) {
2938c2ecf20Sopenharmony_ci			hash ^= (c & 1) << i;
2948c2ecf20Sopenharmony_ci			if (++i == 6)
2958c2ecf20Sopenharmony_ci				i = 0;
2968c2ecf20Sopenharmony_ci		}
2978c2ecf20Sopenharmony_ci	return hash;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ciint t3_mac_set_rx_mode(struct cmac *mac, struct net_device *dev)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	u32 val, hash_lo, hash_hi;
3038c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
3048c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	val = t3_read_reg(adap, A_XGM_RX_CFG + oft) & ~F_COPYALLFRAMES;
3078c2ecf20Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
3088c2ecf20Sopenharmony_ci		val |= F_COPYALLFRAMES;
3098c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RX_CFG + oft, val);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	if (dev->flags & IFF_ALLMULTI)
3128c2ecf20Sopenharmony_ci		hash_lo = hash_hi = 0xffffffff;
3138c2ecf20Sopenharmony_ci	else {
3148c2ecf20Sopenharmony_ci		struct netdev_hw_addr *ha;
3158c2ecf20Sopenharmony_ci		int exact_addr_idx = mac->nucast;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci		hash_lo = hash_hi = 0;
3188c2ecf20Sopenharmony_ci		netdev_for_each_mc_addr(ha, dev)
3198c2ecf20Sopenharmony_ci			if (exact_addr_idx < EXACT_ADDR_FILTERS)
3208c2ecf20Sopenharmony_ci				set_addr_filter(mac, exact_addr_idx++,
3218c2ecf20Sopenharmony_ci						ha->addr);
3228c2ecf20Sopenharmony_ci			else {
3238c2ecf20Sopenharmony_ci				int hash = hash_hw_addr(ha->addr);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci				if (hash < 32)
3268c2ecf20Sopenharmony_ci					hash_lo |= (1 << hash);
3278c2ecf20Sopenharmony_ci				else
3288c2ecf20Sopenharmony_ci					hash_hi |= (1 << (hash - 32));
3298c2ecf20Sopenharmony_ci			}
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RX_HASH_LOW + oft, hash_lo);
3338c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RX_HASH_HIGH + oft, hash_hi);
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_cistatic int rx_fifo_hwm(int mtu)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	int hwm;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	hwm = max(MAC_RXFIFO_SIZE - 3 * mtu, (MAC_RXFIFO_SIZE * 38) / 100);
3428c2ecf20Sopenharmony_ci	return min(hwm, MAC_RXFIFO_SIZE - 8192);
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ciint t3_mac_set_mtu(struct cmac *mac, unsigned int mtu)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	int hwm, lwm, divisor;
3488c2ecf20Sopenharmony_ci	int ipg;
3498c2ecf20Sopenharmony_ci	unsigned int thres, v, reg;
3508c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * MAX_FRAME_SIZE inludes header + FCS, mtu doesn't.  The HW max
3548c2ecf20Sopenharmony_ci	 * packet size register includes header, but not FCS.
3558c2ecf20Sopenharmony_ci	 */
3568c2ecf20Sopenharmony_ci	mtu += 14;
3578c2ecf20Sopenharmony_ci	if (mtu > 1536)
3588c2ecf20Sopenharmony_ci		mtu += 4;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (mtu > MAX_FRAME_SIZE - 4)
3618c2ecf20Sopenharmony_ci		return -EINVAL;
3628c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset, mtu);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (adap->params.rev >= T3_REV_B2 &&
3658c2ecf20Sopenharmony_ci	    (t3_read_reg(adap, A_XGM_RX_CTRL + mac->offset) & F_RXEN)) {
3668c2ecf20Sopenharmony_ci		t3_mac_disable_exact_filters(mac);
3678c2ecf20Sopenharmony_ci		v = t3_read_reg(adap, A_XGM_RX_CFG + mac->offset);
3688c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_XGM_RX_CFG + mac->offset,
3698c2ecf20Sopenharmony_ci				 F_ENHASHMCAST | F_COPYALLFRAMES, F_DISBCAST);
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		reg = adap->params.rev == T3_REV_B2 ?
3728c2ecf20Sopenharmony_ci			A_XGM_RX_MAX_PKT_SIZE_ERR_CNT : A_XGM_RXFIFO_CFG;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		/* drain RX FIFO */
3758c2ecf20Sopenharmony_ci		if (t3_wait_op_done(adap, reg + mac->offset,
3768c2ecf20Sopenharmony_ci				    F_RXFIFO_EMPTY, 1, 20, 5)) {
3778c2ecf20Sopenharmony_ci			t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
3788c2ecf20Sopenharmony_ci			t3_mac_enable_exact_filters(mac);
3798c2ecf20Sopenharmony_ci			return -EIO;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
3828c2ecf20Sopenharmony_ci				 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),
3838c2ecf20Sopenharmony_ci				 V_RXMAXPKTSIZE(mtu));
3848c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_RX_CFG + mac->offset, v);
3858c2ecf20Sopenharmony_ci		t3_mac_enable_exact_filters(mac);
3868c2ecf20Sopenharmony_ci	} else
3878c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_XGM_RX_MAX_PKT_SIZE + mac->offset,
3888c2ecf20Sopenharmony_ci				 V_RXMAXPKTSIZE(M_RXMAXPKTSIZE),
3898c2ecf20Sopenharmony_ci				 V_RXMAXPKTSIZE(mtu));
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/*
3928c2ecf20Sopenharmony_ci	 * Adjust the PAUSE frame watermarks.  We always set the LWM, and the
3938c2ecf20Sopenharmony_ci	 * HWM only if flow-control is enabled.
3948c2ecf20Sopenharmony_ci	 */
3958c2ecf20Sopenharmony_ci	hwm = rx_fifo_hwm(mtu);
3968c2ecf20Sopenharmony_ci	lwm = min(3 * (int)mtu, MAC_RXFIFO_SIZE / 4);
3978c2ecf20Sopenharmony_ci	v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset);
3988c2ecf20Sopenharmony_ci	v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM);
3998c2ecf20Sopenharmony_ci	v |= V_RXFIFOPAUSELWM(lwm / 8);
4008c2ecf20Sopenharmony_ci	if (G_RXFIFOPAUSEHWM(v))
4018c2ecf20Sopenharmony_ci		v = (v & ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM)) |
4028c2ecf20Sopenharmony_ci		    V_RXFIFOPAUSEHWM(hwm / 8);
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RXFIFO_CFG + mac->offset, v);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	/* Adjust the TX FIFO threshold based on the MTU */
4078c2ecf20Sopenharmony_ci	thres = (adap->params.vpd.cclk * 1000) / 15625;
4088c2ecf20Sopenharmony_ci	thres = (thres * mtu) / 1000;
4098c2ecf20Sopenharmony_ci	if (is_10G(adap))
4108c2ecf20Sopenharmony_ci		thres /= 10;
4118c2ecf20Sopenharmony_ci	thres = mtu > thres ? (mtu - thres + 7) / 8 : 0;
4128c2ecf20Sopenharmony_ci	thres = max(thres, 8U);	/* need at least 8 */
4138c2ecf20Sopenharmony_ci	ipg = (adap->params.rev == T3_REV_C) ? 0 : 1;
4148c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset,
4158c2ecf20Sopenharmony_ci			 V_TXFIFOTHRESH(M_TXFIFOTHRESH) | V_TXIPG(M_TXIPG),
4168c2ecf20Sopenharmony_ci			 V_TXFIFOTHRESH(thres) | V_TXIPG(ipg));
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (adap->params.rev > 0) {
4198c2ecf20Sopenharmony_ci		divisor = (adap->params.rev == T3_REV_C) ? 64 : 8;
4208c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_PAUSE_TIMER + mac->offset,
4218c2ecf20Sopenharmony_ci			     (hwm - lwm) * 4 / divisor);
4228c2ecf20Sopenharmony_ci	}
4238c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_TX_PAUSE_QUANTA + mac->offset,
4248c2ecf20Sopenharmony_ci		     MAC_RXFIFO_SIZE * 4 * 8 / 512);
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ciint t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	u32 val;
4318c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
4328c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (duplex >= 0 && duplex != DUPLEX_FULL)
4358c2ecf20Sopenharmony_ci		return -EINVAL;
4368c2ecf20Sopenharmony_ci	if (speed >= 0) {
4378c2ecf20Sopenharmony_ci		if (speed == SPEED_10)
4388c2ecf20Sopenharmony_ci			val = V_PORTSPEED(0);
4398c2ecf20Sopenharmony_ci		else if (speed == SPEED_100)
4408c2ecf20Sopenharmony_ci			val = V_PORTSPEED(1);
4418c2ecf20Sopenharmony_ci		else if (speed == SPEED_1000)
4428c2ecf20Sopenharmony_ci			val = V_PORTSPEED(2);
4438c2ecf20Sopenharmony_ci		else if (speed == SPEED_10000)
4448c2ecf20Sopenharmony_ci			val = V_PORTSPEED(3);
4458c2ecf20Sopenharmony_ci		else
4468c2ecf20Sopenharmony_ci			return -EINVAL;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_XGM_PORT_CFG + oft,
4498c2ecf20Sopenharmony_ci				 V_PORTSPEED(M_PORTSPEED), val);
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft);
4538c2ecf20Sopenharmony_ci	val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM);
4548c2ecf20Sopenharmony_ci	if (fc & PAUSE_TX) {
4558c2ecf20Sopenharmony_ci		u32 rx_max_pkt_size =
4568c2ecf20Sopenharmony_ci		    G_RXMAXPKTSIZE(t3_read_reg(adap,
4578c2ecf20Sopenharmony_ci					       A_XGM_RX_MAX_PKT_SIZE + oft));
4588c2ecf20Sopenharmony_ci		val |= V_RXFIFOPAUSEHWM(rx_fifo_hwm(rx_max_pkt_size) / 8);
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val);
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN,
4638c2ecf20Sopenharmony_ci			 (fc & PAUSE_RX) ? F_TXPAUSEEN : 0);
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ciint t3_mac_enable(struct cmac *mac, int which)
4688c2ecf20Sopenharmony_ci{
4698c2ecf20Sopenharmony_ci	int idx = macidx(mac);
4708c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
4718c2ecf20Sopenharmony_ci	unsigned int oft = mac->offset;
4728c2ecf20Sopenharmony_ci	struct mac_stats *s = &mac->stats;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (which & MAC_DIRECTION_TX) {
4758c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx);
4768c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_TP_PIO_DATA,
4778c2ecf20Sopenharmony_ci			     adap->params.rev == T3_REV_C ?
4788c2ecf20Sopenharmony_ci			     0xc4ffff01 : 0xc0ede401);
4798c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE);
4808c2ecf20Sopenharmony_ci		t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx,
4818c2ecf20Sopenharmony_ci				 adap->params.rev == T3_REV_C ? 0 : 1 << idx);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN);
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CNT_CH0 + idx);
4868c2ecf20Sopenharmony_ci		mac->tx_mcnt = s->tx_frames;
4878c2ecf20Sopenharmony_ci		mac->tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap,
4888c2ecf20Sopenharmony_ci							A_TP_PIO_DATA)));
4898c2ecf20Sopenharmony_ci		mac->tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
4908c2ecf20Sopenharmony_ci						A_XGM_TX_SPI4_SOP_EOP_CNT +
4918c2ecf20Sopenharmony_ci						oft)));
4928c2ecf20Sopenharmony_ci		mac->rx_mcnt = s->rx_frames;
4938c2ecf20Sopenharmony_ci		mac->rx_pause = s->rx_pause;
4948c2ecf20Sopenharmony_ci		mac->rx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
4958c2ecf20Sopenharmony_ci						A_XGM_RX_SPI4_SOP_EOP_CNT +
4968c2ecf20Sopenharmony_ci						oft)));
4978c2ecf20Sopenharmony_ci		mac->rx_ocnt = s->rx_fifo_ovfl;
4988c2ecf20Sopenharmony_ci		mac->txen = F_TXEN;
4998c2ecf20Sopenharmony_ci		mac->toggle_cnt = 0;
5008c2ecf20Sopenharmony_ci	}
5018c2ecf20Sopenharmony_ci	if (which & MAC_DIRECTION_RX)
5028c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN);
5038c2ecf20Sopenharmony_ci	return 0;
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ciint t3_mac_disable(struct cmac *mac, int which)
5078c2ecf20Sopenharmony_ci{
5088c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (which & MAC_DIRECTION_TX) {
5118c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0);
5128c2ecf20Sopenharmony_ci		mac->txen = 0;
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	if (which & MAC_DIRECTION_RX) {
5158c2ecf20Sopenharmony_ci		int val = F_MAC_RESET_;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset,
5188c2ecf20Sopenharmony_ci				 F_PCS_RESET_, 0);
5198c2ecf20Sopenharmony_ci		msleep(100);
5208c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0);
5218c2ecf20Sopenharmony_ci		if (is_10G(adap))
5228c2ecf20Sopenharmony_ci			val |= F_PCS_RESET_;
5238c2ecf20Sopenharmony_ci		else if (uses_xaui(adap))
5248c2ecf20Sopenharmony_ci			val |= F_PCS_RESET_ | F_XG2G_RESET_;
5258c2ecf20Sopenharmony_ci		else
5268c2ecf20Sopenharmony_ci			val |= F_RGMII_RESET_ | F_XG2G_RESET_;
5278c2ecf20Sopenharmony_ci		t3_write_reg(mac->adapter, A_XGM_RESET_CTRL + mac->offset, val);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci	return 0;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ciint t3b2_mac_watchdog_task(struct cmac *mac)
5338c2ecf20Sopenharmony_ci{
5348c2ecf20Sopenharmony_ci	struct adapter *adap = mac->adapter;
5358c2ecf20Sopenharmony_ci	struct mac_stats *s = &mac->stats;
5368c2ecf20Sopenharmony_ci	unsigned int tx_tcnt, tx_xcnt;
5378c2ecf20Sopenharmony_ci	u64 tx_mcnt = s->tx_frames;
5388c2ecf20Sopenharmony_ci	int status;
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	status = 0;
5418c2ecf20Sopenharmony_ci	tx_xcnt = 1;		/* By default tx_xcnt is making progress */
5428c2ecf20Sopenharmony_ci	tx_tcnt = mac->tx_tcnt;	/* If tx_mcnt is progressing ignore tx_tcnt */
5438c2ecf20Sopenharmony_ci	if (tx_mcnt == mac->tx_mcnt && mac->rx_pause == s->rx_pause) {
5448c2ecf20Sopenharmony_ci		tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap,
5458c2ecf20Sopenharmony_ci						A_XGM_TX_SPI4_SOP_EOP_CNT +
5468c2ecf20Sopenharmony_ci					       	mac->offset)));
5478c2ecf20Sopenharmony_ci		if (tx_xcnt == 0) {
5488c2ecf20Sopenharmony_ci			t3_write_reg(adap, A_TP_PIO_ADDR,
5498c2ecf20Sopenharmony_ci				     A_TP_TX_DROP_CNT_CH0 + macidx(mac));
5508c2ecf20Sopenharmony_ci			tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap,
5518c2ecf20Sopenharmony_ci						      A_TP_PIO_DATA)));
5528c2ecf20Sopenharmony_ci		} else {
5538c2ecf20Sopenharmony_ci			goto out;
5548c2ecf20Sopenharmony_ci		}
5558c2ecf20Sopenharmony_ci	} else {
5568c2ecf20Sopenharmony_ci		mac->toggle_cnt = 0;
5578c2ecf20Sopenharmony_ci		goto out;
5588c2ecf20Sopenharmony_ci	}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	if ((tx_tcnt != mac->tx_tcnt) && (mac->tx_xcnt == 0)) {
5618c2ecf20Sopenharmony_ci		if (mac->toggle_cnt > 4) {
5628c2ecf20Sopenharmony_ci			status = 2;
5638c2ecf20Sopenharmony_ci			goto out;
5648c2ecf20Sopenharmony_ci		} else {
5658c2ecf20Sopenharmony_ci			status = 1;
5668c2ecf20Sopenharmony_ci			goto out;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci	} else {
5698c2ecf20Sopenharmony_ci		mac->toggle_cnt = 0;
5708c2ecf20Sopenharmony_ci		goto out;
5718c2ecf20Sopenharmony_ci	}
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ciout:
5748c2ecf20Sopenharmony_ci	mac->tx_tcnt = tx_tcnt;
5758c2ecf20Sopenharmony_ci	mac->tx_xcnt = tx_xcnt;
5768c2ecf20Sopenharmony_ci	mac->tx_mcnt = s->tx_frames;
5778c2ecf20Sopenharmony_ci	mac->rx_pause = s->rx_pause;
5788c2ecf20Sopenharmony_ci	if (status == 1) {
5798c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0);
5808c2ecf20Sopenharmony_ci		t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset);  /* flush */
5818c2ecf20Sopenharmony_ci		t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, mac->txen);
5828c2ecf20Sopenharmony_ci		t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset);  /* flush */
5838c2ecf20Sopenharmony_ci		mac->toggle_cnt++;
5848c2ecf20Sopenharmony_ci	} else if (status == 2) {
5858c2ecf20Sopenharmony_ci		t3b2_mac_reset(mac);
5868c2ecf20Sopenharmony_ci		mac->toggle_cnt = 0;
5878c2ecf20Sopenharmony_ci	}
5888c2ecf20Sopenharmony_ci	return status;
5898c2ecf20Sopenharmony_ci}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci/*
5928c2ecf20Sopenharmony_ci * This function is called periodically to accumulate the current values of the
5938c2ecf20Sopenharmony_ci * RMON counters into the port statistics.  Since the packet counters are only
5948c2ecf20Sopenharmony_ci * 32 bits they can overflow in ~286 secs at 10G, so the function should be
5958c2ecf20Sopenharmony_ci * called more frequently than that.  The byte counters are 45-bit wide, they
5968c2ecf20Sopenharmony_ci * would overflow in ~7.8 hours.
5978c2ecf20Sopenharmony_ci */
5988c2ecf20Sopenharmony_ciconst struct mac_stats *t3_mac_update_stats(struct cmac *mac)
5998c2ecf20Sopenharmony_ci{
6008c2ecf20Sopenharmony_ci#define RMON_READ(mac, addr) t3_read_reg(mac->adapter, addr + mac->offset)
6018c2ecf20Sopenharmony_ci#define RMON_UPDATE(mac, name, reg) \
6028c2ecf20Sopenharmony_ci	(mac)->stats.name += (u64)RMON_READ(mac, A_XGM_STAT_##reg)
6038c2ecf20Sopenharmony_ci#define RMON_UPDATE64(mac, name, reg_lo, reg_hi) \
6048c2ecf20Sopenharmony_ci	(mac)->stats.name += RMON_READ(mac, A_XGM_STAT_##reg_lo) + \
6058c2ecf20Sopenharmony_ci			     ((u64)RMON_READ(mac, A_XGM_STAT_##reg_hi) << 32)
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	u32 v, lo;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	RMON_UPDATE64(mac, rx_octets, RX_BYTES_LOW, RX_BYTES_HIGH);
6108c2ecf20Sopenharmony_ci	RMON_UPDATE64(mac, rx_frames, RX_FRAMES_LOW, RX_FRAMES_HIGH);
6118c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_mcast_frames, RX_MCAST_FRAMES);
6128c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_bcast_frames, RX_BCAST_FRAMES);
6138c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_fcs_errs, RX_CRC_ERR_FRAMES);
6148c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_pause, RX_PAUSE_FRAMES);
6158c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_jabber, RX_JABBER_FRAMES);
6168c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_short, RX_SHORT_FRAMES);
6178c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_symbol_errs, RX_SYM_CODE_ERR_FRAMES);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES);
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	v = RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT);
6228c2ecf20Sopenharmony_ci	if (mac->adapter->params.rev == T3_REV_B2)
6238c2ecf20Sopenharmony_ci		v &= 0x7fffffff;
6248c2ecf20Sopenharmony_ci	mac->stats.rx_too_long += v;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_64, RX_64B_FRAMES);
6278c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_65_127, RX_65_127B_FRAMES);
6288c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_128_255, RX_128_255B_FRAMES);
6298c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_256_511, RX_256_511B_FRAMES);
6308c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_512_1023, RX_512_1023B_FRAMES);
6318c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_1024_1518, RX_1024_1518B_FRAMES);
6328c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, rx_frames_1519_max, RX_1519_MAXB_FRAMES);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	RMON_UPDATE64(mac, tx_octets, TX_BYTE_LOW, TX_BYTE_HIGH);
6358c2ecf20Sopenharmony_ci	RMON_UPDATE64(mac, tx_frames, TX_FRAME_LOW, TX_FRAME_HIGH);
6368c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_mcast_frames, TX_MCAST);
6378c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_bcast_frames, TX_BCAST);
6388c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_pause, TX_PAUSE);
6398c2ecf20Sopenharmony_ci	/* This counts error frames in general (bad FCS, underrun, etc). */
6408c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_underrun, TX_ERR_FRAMES);
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_64, TX_64B_FRAMES);
6438c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_65_127, TX_65_127B_FRAMES);
6448c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_128_255, TX_128_255B_FRAMES);
6458c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_256_511, TX_256_511B_FRAMES);
6468c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_512_1023, TX_512_1023B_FRAMES);
6478c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_1024_1518, TX_1024_1518B_FRAMES);
6488c2ecf20Sopenharmony_ci	RMON_UPDATE(mac, tx_frames_1519_max, TX_1519_MAXB_FRAMES);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci	/* The next stat isn't clear-on-read. */
6518c2ecf20Sopenharmony_ci	t3_write_reg(mac->adapter, A_TP_MIB_INDEX, mac->offset ? 51 : 50);
6528c2ecf20Sopenharmony_ci	v = t3_read_reg(mac->adapter, A_TP_MIB_RDATA);
6538c2ecf20Sopenharmony_ci	lo = (u32) mac->stats.rx_cong_drops;
6548c2ecf20Sopenharmony_ci	mac->stats.rx_cong_drops += (u64) (v - lo);
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	return &mac->stats;
6578c2ecf20Sopenharmony_ci}
658