xref: /openbmc/linux/drivers/net/ethernet/atheros/atl1c/atl1c_hw.c (revision 762f99f4f3cb41a775b5157dd761217beba65873)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
22b133ad6SJeff Kirsher /*
32b133ad6SJeff Kirsher  * Copyright(c) 2007 Atheros Corporation. All rights reserved.
42b133ad6SJeff Kirsher  *
52b133ad6SJeff Kirsher  * Derived from Intel e1000 driver
62b133ad6SJeff Kirsher  * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
72b133ad6SJeff Kirsher  */
82b133ad6SJeff Kirsher #include <linux/pci.h>
92b133ad6SJeff Kirsher #include <linux/delay.h>
102b133ad6SJeff Kirsher #include <linux/mii.h>
112b133ad6SJeff Kirsher #include <linux/crc32.h>
122b133ad6SJeff Kirsher 
132b133ad6SJeff Kirsher #include "atl1c.h"
142b133ad6SJeff Kirsher 
152b133ad6SJeff Kirsher /*
162b133ad6SJeff Kirsher  * check_eeprom_exist
172b133ad6SJeff Kirsher  * return 1 if eeprom exist
182b133ad6SJeff Kirsher  */
atl1c_check_eeprom_exist(struct atl1c_hw * hw)192b133ad6SJeff Kirsher int atl1c_check_eeprom_exist(struct atl1c_hw *hw)
202b133ad6SJeff Kirsher {
212b133ad6SJeff Kirsher 	u32 data;
222b133ad6SJeff Kirsher 
232b133ad6SJeff Kirsher 	AT_READ_REG(hw, REG_TWSI_DEBUG, &data);
242b133ad6SJeff Kirsher 	if (data & TWSI_DEBUG_DEV_EXIST)
252b133ad6SJeff Kirsher 		return 1;
262b133ad6SJeff Kirsher 
272b133ad6SJeff Kirsher 	AT_READ_REG(hw, REG_MASTER_CTRL, &data);
282b133ad6SJeff Kirsher 	if (data & MASTER_CTRL_OTP_SEL)
292b133ad6SJeff Kirsher 		return 1;
302b133ad6SJeff Kirsher 	return 0;
312b133ad6SJeff Kirsher }
322b133ad6SJeff Kirsher 
atl1c_hw_set_mac_addr(struct atl1c_hw * hw,u8 * mac_addr)33229e6b6eSHuang, Xiong void atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr)
342b133ad6SJeff Kirsher {
352b133ad6SJeff Kirsher 	u32 value;
362b133ad6SJeff Kirsher 	/*
372b133ad6SJeff Kirsher 	 * 00-0B-6A-F6-00-DC
382b133ad6SJeff Kirsher 	 * 0:  6AF600DC 1: 000B
392b133ad6SJeff Kirsher 	 * low dword
402b133ad6SJeff Kirsher 	 */
41229e6b6eSHuang, Xiong 	value = mac_addr[2] << 24 |
42229e6b6eSHuang, Xiong 		mac_addr[3] << 16 |
43229e6b6eSHuang, Xiong 		mac_addr[4] << 8  |
44229e6b6eSHuang, Xiong 		mac_addr[5];
452b133ad6SJeff Kirsher 	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
462b133ad6SJeff Kirsher 	/* hight dword */
47229e6b6eSHuang, Xiong 	value = mac_addr[0] << 8 |
48229e6b6eSHuang, Xiong 		mac_addr[1];
492b133ad6SJeff Kirsher 	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
502b133ad6SJeff Kirsher }
512b133ad6SJeff Kirsher 
52229e6b6eSHuang, Xiong /* read mac address from hardware register */
atl1c_read_current_addr(struct atl1c_hw * hw,u8 * eth_addr)53229e6b6eSHuang, Xiong static bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr)
54229e6b6eSHuang, Xiong {
55229e6b6eSHuang, Xiong 	u32 addr[2];
56229e6b6eSHuang, Xiong 
57229e6b6eSHuang, Xiong 	AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]);
58229e6b6eSHuang, Xiong 	AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]);
59229e6b6eSHuang, Xiong 
60229e6b6eSHuang, Xiong 	*(u32 *) &eth_addr[2] = htonl(addr[0]);
61229e6b6eSHuang, Xiong 	*(u16 *) &eth_addr[0] = htons((u16)addr[1]);
62229e6b6eSHuang, Xiong 
63229e6b6eSHuang, Xiong 	return is_valid_ether_addr(eth_addr);
64229e6b6eSHuang, Xiong }
65229e6b6eSHuang, Xiong 
662b133ad6SJeff Kirsher /*
672b133ad6SJeff Kirsher  * atl1c_get_permanent_address
682b133ad6SJeff Kirsher  * return 0 if get valid mac address,
692b133ad6SJeff Kirsher  */
atl1c_get_permanent_address(struct atl1c_hw * hw)702b133ad6SJeff Kirsher static int atl1c_get_permanent_address(struct atl1c_hw *hw)
712b133ad6SJeff Kirsher {
722b133ad6SJeff Kirsher 	u32 i;
732b133ad6SJeff Kirsher 	u32 otp_ctrl_data;
742b133ad6SJeff Kirsher 	u32 twsi_ctrl_data;
752b133ad6SJeff Kirsher 	u16 phy_data;
762b133ad6SJeff Kirsher 	bool raise_vol = false;
772b133ad6SJeff Kirsher 
78229e6b6eSHuang, Xiong 	/* MAC-address from BIOS is the 1st priority */
79229e6b6eSHuang, Xiong 	if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
80229e6b6eSHuang, Xiong 		return 0;
81229e6b6eSHuang, Xiong 
822b133ad6SJeff Kirsher 	/* init */
832b133ad6SJeff Kirsher 	AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
842b133ad6SJeff Kirsher 	if (atl1c_check_eeprom_exist(hw)) {
852b133ad6SJeff Kirsher 		if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
862b133ad6SJeff Kirsher 			/* Enable OTP CLK */
872b133ad6SJeff Kirsher 			if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) {
882b133ad6SJeff Kirsher 				otp_ctrl_data |= OTP_CTRL_CLK_EN;
892b133ad6SJeff Kirsher 				AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
902b133ad6SJeff Kirsher 				AT_WRITE_FLUSH(hw);
912b133ad6SJeff Kirsher 				msleep(1);
922b133ad6SJeff Kirsher 			}
932b133ad6SJeff Kirsher 		}
94229e6b6eSHuang, Xiong 		/* raise voltage temporally for l2cb */
95229e6b6eSHuang, Xiong 		if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
96229e6b6eSHuang, Xiong 			atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
97229e6b6eSHuang, Xiong 			phy_data &= ~ANACTRL_HB_EN;
98229e6b6eSHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
99229e6b6eSHuang, Xiong 			atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
100229e6b6eSHuang, Xiong 			phy_data |= VOLT_CTRL_SWLOWEST;
101229e6b6eSHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
1022b133ad6SJeff Kirsher 			udelay(20);
1032b133ad6SJeff Kirsher 			raise_vol = true;
1042b133ad6SJeff Kirsher 		}
1052b133ad6SJeff Kirsher 
1062b133ad6SJeff Kirsher 		AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
1072b133ad6SJeff Kirsher 		twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
1082b133ad6SJeff Kirsher 		AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
1092b133ad6SJeff Kirsher 		for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
1102b133ad6SJeff Kirsher 			msleep(10);
1112b133ad6SJeff Kirsher 			AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
1122b133ad6SJeff Kirsher 			if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
1132b133ad6SJeff Kirsher 				break;
1142b133ad6SJeff Kirsher 		}
1152b133ad6SJeff Kirsher 		if (i >= AT_TWSI_EEPROM_TIMEOUT)
1162b133ad6SJeff Kirsher 			return -1;
1172b133ad6SJeff Kirsher 	}
1182b133ad6SJeff Kirsher 	/* Disable OTP_CLK */
1192b133ad6SJeff Kirsher 	if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
1202b133ad6SJeff Kirsher 		otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
1212b133ad6SJeff Kirsher 		AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
1222b133ad6SJeff Kirsher 		msleep(1);
1232b133ad6SJeff Kirsher 	}
1242b133ad6SJeff Kirsher 	if (raise_vol) {
125229e6b6eSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
126229e6b6eSHuang, Xiong 		phy_data |= ANACTRL_HB_EN;
127229e6b6eSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
128229e6b6eSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
129229e6b6eSHuang, Xiong 		phy_data &= ~VOLT_CTRL_SWLOWEST;
130229e6b6eSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
1312b133ad6SJeff Kirsher 		udelay(20);
1322b133ad6SJeff Kirsher 	}
1332b133ad6SJeff Kirsher 
134229e6b6eSHuang, Xiong 	if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
1352b133ad6SJeff Kirsher 		return 0;
1362b133ad6SJeff Kirsher 
1372b133ad6SJeff Kirsher 	return -1;
1382b133ad6SJeff Kirsher }
1392b133ad6SJeff Kirsher 
atl1c_read_eeprom(struct atl1c_hw * hw,u32 offset,u32 * p_value)1402b133ad6SJeff Kirsher bool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
1412b133ad6SJeff Kirsher {
1422b133ad6SJeff Kirsher 	int i;
14355705639SPeter Senna Tschudin 	bool ret = false;
1442b133ad6SJeff Kirsher 	u32 otp_ctrl_data;
1452b133ad6SJeff Kirsher 	u32 control;
1462b133ad6SJeff Kirsher 	u32 data;
1472b133ad6SJeff Kirsher 
1482b133ad6SJeff Kirsher 	if (offset & 3)
1492b133ad6SJeff Kirsher 		return ret; /* address do not align */
1502b133ad6SJeff Kirsher 
1512b133ad6SJeff Kirsher 	AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
1522b133ad6SJeff Kirsher 	if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
1532b133ad6SJeff Kirsher 		AT_WRITE_REG(hw, REG_OTP_CTRL,
1542b133ad6SJeff Kirsher 				(otp_ctrl_data | OTP_CTRL_CLK_EN));
1552b133ad6SJeff Kirsher 
1562b133ad6SJeff Kirsher 	AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0);
1572b133ad6SJeff Kirsher 	control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT;
1582b133ad6SJeff Kirsher 	AT_WRITE_REG(hw, REG_EEPROM_CTRL, control);
1592b133ad6SJeff Kirsher 
1602b133ad6SJeff Kirsher 	for (i = 0; i < 10; i++) {
1612b133ad6SJeff Kirsher 		udelay(100);
1622b133ad6SJeff Kirsher 		AT_READ_REG(hw, REG_EEPROM_CTRL, &control);
1632b133ad6SJeff Kirsher 		if (control & EEPROM_CTRL_RW)
1642b133ad6SJeff Kirsher 			break;
1652b133ad6SJeff Kirsher 	}
1662b133ad6SJeff Kirsher 	if (control & EEPROM_CTRL_RW) {
1672b133ad6SJeff Kirsher 		AT_READ_REG(hw, REG_EEPROM_CTRL, &data);
1682b133ad6SJeff Kirsher 		AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value);
1692b133ad6SJeff Kirsher 		data = data & 0xFFFF;
1702b133ad6SJeff Kirsher 		*p_value = swab32((data << 16) | (*p_value >> 16));
1712b133ad6SJeff Kirsher 		ret = true;
1722b133ad6SJeff Kirsher 	}
1732b133ad6SJeff Kirsher 	if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
1742b133ad6SJeff Kirsher 		AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
1752b133ad6SJeff Kirsher 
1762b133ad6SJeff Kirsher 	return ret;
1772b133ad6SJeff Kirsher }
1782b133ad6SJeff Kirsher /*
1792b133ad6SJeff Kirsher  * Reads the adapter's MAC address from the EEPROM
1802b133ad6SJeff Kirsher  *
1812b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
1822b133ad6SJeff Kirsher  */
atl1c_read_mac_addr(struct atl1c_hw * hw)1832b133ad6SJeff Kirsher int atl1c_read_mac_addr(struct atl1c_hw *hw)
1842b133ad6SJeff Kirsher {
1852b133ad6SJeff Kirsher 	int err = 0;
1862b133ad6SJeff Kirsher 
1872b133ad6SJeff Kirsher 	err = atl1c_get_permanent_address(hw);
1882b133ad6SJeff Kirsher 	if (err)
1897efd26d0SJoe Perches 		eth_random_addr(hw->perm_mac_addr);
1902b133ad6SJeff Kirsher 
1912b133ad6SJeff Kirsher 	memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
1926a214fd4SDanny Kukawka 	return err;
1932b133ad6SJeff Kirsher }
1942b133ad6SJeff Kirsher 
1952b133ad6SJeff Kirsher /*
1962b133ad6SJeff Kirsher  * atl1c_hash_mc_addr
1972b133ad6SJeff Kirsher  *  purpose
1982b133ad6SJeff Kirsher  *      set hash value for a multicast address
1992b133ad6SJeff Kirsher  *      hash calcu processing :
2002b133ad6SJeff Kirsher  *          1. calcu 32bit CRC for multicast address
2012b133ad6SJeff Kirsher  *          2. reverse crc with MSB to LSB
2022b133ad6SJeff Kirsher  */
atl1c_hash_mc_addr(struct atl1c_hw * hw,u8 * mc_addr)2032b133ad6SJeff Kirsher u32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr)
2042b133ad6SJeff Kirsher {
2052b133ad6SJeff Kirsher 	u32 crc32;
2062b133ad6SJeff Kirsher 	u32 value = 0;
2072b133ad6SJeff Kirsher 	int i;
2082b133ad6SJeff Kirsher 
2092b133ad6SJeff Kirsher 	crc32 = ether_crc_le(6, mc_addr);
2102b133ad6SJeff Kirsher 	for (i = 0; i < 32; i++)
2112b133ad6SJeff Kirsher 		value |= (((crc32 >> i) & 1) << (31 - i));
2122b133ad6SJeff Kirsher 
2132b133ad6SJeff Kirsher 	return value;
2142b133ad6SJeff Kirsher }
2152b133ad6SJeff Kirsher 
2162b133ad6SJeff Kirsher /*
2172b133ad6SJeff Kirsher  * Sets the bit in the multicast table corresponding to the hash value.
2182b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
2192b133ad6SJeff Kirsher  * hash_value - Multicast address hash value
2202b133ad6SJeff Kirsher  */
atl1c_hash_set(struct atl1c_hw * hw,u32 hash_value)2212b133ad6SJeff Kirsher void atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
2222b133ad6SJeff Kirsher {
2232b133ad6SJeff Kirsher 	u32 hash_bit, hash_reg;
2242b133ad6SJeff Kirsher 	u32 mta;
2252b133ad6SJeff Kirsher 
2262b133ad6SJeff Kirsher 	/*
2272b133ad6SJeff Kirsher 	 * The HASH Table  is a register array of 2 32-bit registers.
2282b133ad6SJeff Kirsher 	 * It is treated like an array of 64 bits.  We want to set
2292b133ad6SJeff Kirsher 	 * bit BitArray[hash_value]. So we figure out what register
2302b133ad6SJeff Kirsher 	 * the bit is in, read it, OR in the new bit, then write
2312b133ad6SJeff Kirsher 	 * back the new value.  The register is determined by the
2322b133ad6SJeff Kirsher 	 * upper bit of the hash value and the bit within that
2332b133ad6SJeff Kirsher 	 * register are determined by the lower 5 bits of the value.
2342b133ad6SJeff Kirsher 	 */
2352b133ad6SJeff Kirsher 	hash_reg = (hash_value >> 31) & 0x1;
2362b133ad6SJeff Kirsher 	hash_bit = (hash_value >> 26) & 0x1F;
2372b133ad6SJeff Kirsher 
2382b133ad6SJeff Kirsher 	mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
2392b133ad6SJeff Kirsher 
2402b133ad6SJeff Kirsher 	mta |= (1 << hash_bit);
2412b133ad6SJeff Kirsher 
2422b133ad6SJeff Kirsher 	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
2432b133ad6SJeff Kirsher }
2442b133ad6SJeff Kirsher 
2452b133ad6SJeff Kirsher /*
246929a5e93SHuang, Xiong  * wait mdio module be idle
247929a5e93SHuang, Xiong  * return true: idle
248929a5e93SHuang, Xiong  *        false: still busy
249929a5e93SHuang, Xiong  */
atl1c_wait_mdio_idle(struct atl1c_hw * hw)250929a5e93SHuang, Xiong bool atl1c_wait_mdio_idle(struct atl1c_hw *hw)
251929a5e93SHuang, Xiong {
252929a5e93SHuang, Xiong 	u32 val;
253929a5e93SHuang, Xiong 	int i;
254929a5e93SHuang, Xiong 
255929a5e93SHuang, Xiong 	for (i = 0; i < MDIO_MAX_AC_TO; i++) {
256929a5e93SHuang, Xiong 		AT_READ_REG(hw, REG_MDIO_CTRL, &val);
257929a5e93SHuang, Xiong 		if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START)))
258929a5e93SHuang, Xiong 			break;
259929a5e93SHuang, Xiong 		udelay(10);
260929a5e93SHuang, Xiong 	}
261929a5e93SHuang, Xiong 
262929a5e93SHuang, Xiong 	return i != MDIO_MAX_AC_TO;
263929a5e93SHuang, Xiong }
264929a5e93SHuang, Xiong 
atl1c_stop_phy_polling(struct atl1c_hw * hw)265929a5e93SHuang, Xiong void atl1c_stop_phy_polling(struct atl1c_hw *hw)
266929a5e93SHuang, Xiong {
267929a5e93SHuang, Xiong 	if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
268929a5e93SHuang, Xiong 		return;
269929a5e93SHuang, Xiong 
270929a5e93SHuang, Xiong 	AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
271929a5e93SHuang, Xiong 	atl1c_wait_mdio_idle(hw);
272929a5e93SHuang, Xiong }
273929a5e93SHuang, Xiong 
atl1c_start_phy_polling(struct atl1c_hw * hw,u16 clk_sel)274929a5e93SHuang, Xiong void atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel)
275929a5e93SHuang, Xiong {
276929a5e93SHuang, Xiong 	u32 val;
277929a5e93SHuang, Xiong 
278929a5e93SHuang, Xiong 	if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
279929a5e93SHuang, Xiong 		return;
280929a5e93SHuang, Xiong 
281929a5e93SHuang, Xiong 	val = MDIO_CTRL_SPRES_PRMBL |
282929a5e93SHuang, Xiong 		FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
283929a5e93SHuang, Xiong 		FIELDX(MDIO_CTRL_REG, 1) |
284929a5e93SHuang, Xiong 		MDIO_CTRL_START |
285929a5e93SHuang, Xiong 		MDIO_CTRL_OP_READ;
286929a5e93SHuang, Xiong 	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
287929a5e93SHuang, Xiong 	atl1c_wait_mdio_idle(hw);
288929a5e93SHuang, Xiong 	val |= MDIO_CTRL_AP_EN;
289929a5e93SHuang, Xiong 	val &= ~MDIO_CTRL_START;
290929a5e93SHuang, Xiong 	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
291929a5e93SHuang, Xiong 	udelay(30);
292929a5e93SHuang, Xiong }
293929a5e93SHuang, Xiong 
294929a5e93SHuang, Xiong 
295929a5e93SHuang, Xiong /*
296929a5e93SHuang, Xiong  * atl1c_read_phy_core
297ad61dd30SStephen Boyd  * core function to read register in PHY via MDIO control register.
298929a5e93SHuang, Xiong  * ext: extension register (see IEEE 802.3)
299929a5e93SHuang, Xiong  * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
300929a5e93SHuang, Xiong  * reg: reg to read
301929a5e93SHuang, Xiong  */
atl1c_read_phy_core(struct atl1c_hw * hw,bool ext,u8 dev,u16 reg,u16 * phy_data)302929a5e93SHuang, Xiong int atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
303929a5e93SHuang, Xiong 			u16 reg, u16 *phy_data)
304929a5e93SHuang, Xiong {
305929a5e93SHuang, Xiong 	u32 val;
306929a5e93SHuang, Xiong 	u16 clk_sel = MDIO_CTRL_CLK_25_4;
307929a5e93SHuang, Xiong 
308929a5e93SHuang, Xiong 	atl1c_stop_phy_polling(hw);
309929a5e93SHuang, Xiong 
310929a5e93SHuang, Xiong 	*phy_data = 0;
311929a5e93SHuang, Xiong 
312929a5e93SHuang, Xiong 	/* only l2c_b2 & l1d_2 could use slow clock */
313929a5e93SHuang, Xiong 	if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
314929a5e93SHuang, Xiong 		hw->hibernate)
315929a5e93SHuang, Xiong 		clk_sel = MDIO_CTRL_CLK_25_128;
316929a5e93SHuang, Xiong 	if (ext) {
317929a5e93SHuang, Xiong 		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
318929a5e93SHuang, Xiong 		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
319929a5e93SHuang, Xiong 		val = MDIO_CTRL_SPRES_PRMBL |
320929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
321929a5e93SHuang, Xiong 			MDIO_CTRL_START |
322929a5e93SHuang, Xiong 			MDIO_CTRL_MODE_EXT |
323929a5e93SHuang, Xiong 			MDIO_CTRL_OP_READ;
324929a5e93SHuang, Xiong 	} else {
325929a5e93SHuang, Xiong 		val = MDIO_CTRL_SPRES_PRMBL |
326929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
327929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_REG, reg) |
328929a5e93SHuang, Xiong 			MDIO_CTRL_START |
329929a5e93SHuang, Xiong 			MDIO_CTRL_OP_READ;
330929a5e93SHuang, Xiong 	}
331929a5e93SHuang, Xiong 	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
332929a5e93SHuang, Xiong 
333929a5e93SHuang, Xiong 	if (!atl1c_wait_mdio_idle(hw))
334929a5e93SHuang, Xiong 		return -1;
335929a5e93SHuang, Xiong 
336929a5e93SHuang, Xiong 	AT_READ_REG(hw, REG_MDIO_CTRL, &val);
337929a5e93SHuang, Xiong 	*phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA);
338929a5e93SHuang, Xiong 
339929a5e93SHuang, Xiong 	atl1c_start_phy_polling(hw, clk_sel);
340929a5e93SHuang, Xiong 
341929a5e93SHuang, Xiong 	return 0;
342929a5e93SHuang, Xiong }
343929a5e93SHuang, Xiong 
344929a5e93SHuang, Xiong /*
345929a5e93SHuang, Xiong  * atl1c_write_phy_core
346dbedd44eSJoe Perches  * core function to write to register in PHY via MDIO control register.
347929a5e93SHuang, Xiong  * ext: extension register (see IEEE 802.3)
348929a5e93SHuang, Xiong  * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
349929a5e93SHuang, Xiong  * reg: reg to write
350929a5e93SHuang, Xiong  */
atl1c_write_phy_core(struct atl1c_hw * hw,bool ext,u8 dev,u16 reg,u16 phy_data)351929a5e93SHuang, Xiong int atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
352929a5e93SHuang, Xiong 			u16 reg, u16 phy_data)
353929a5e93SHuang, Xiong {
354929a5e93SHuang, Xiong 	u32 val;
355929a5e93SHuang, Xiong 	u16 clk_sel = MDIO_CTRL_CLK_25_4;
356929a5e93SHuang, Xiong 
357929a5e93SHuang, Xiong 	atl1c_stop_phy_polling(hw);
358929a5e93SHuang, Xiong 
359929a5e93SHuang, Xiong 
360929a5e93SHuang, Xiong 	/* only l2c_b2 & l1d_2 could use slow clock */
361929a5e93SHuang, Xiong 	if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
362929a5e93SHuang, Xiong 		hw->hibernate)
363929a5e93SHuang, Xiong 		clk_sel = MDIO_CTRL_CLK_25_128;
364929a5e93SHuang, Xiong 
365929a5e93SHuang, Xiong 	if (ext) {
366929a5e93SHuang, Xiong 		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
367929a5e93SHuang, Xiong 		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
368929a5e93SHuang, Xiong 		val = MDIO_CTRL_SPRES_PRMBL |
369929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
370929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_DATA, phy_data) |
371929a5e93SHuang, Xiong 			MDIO_CTRL_START |
372929a5e93SHuang, Xiong 			MDIO_CTRL_MODE_EXT;
373929a5e93SHuang, Xiong 	} else {
374929a5e93SHuang, Xiong 		val = MDIO_CTRL_SPRES_PRMBL |
375929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
376929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_DATA, phy_data) |
377929a5e93SHuang, Xiong 			FIELDX(MDIO_CTRL_REG, reg) |
378929a5e93SHuang, Xiong 			MDIO_CTRL_START;
379929a5e93SHuang, Xiong 	}
380929a5e93SHuang, Xiong 	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
381929a5e93SHuang, Xiong 
382929a5e93SHuang, Xiong 	if (!atl1c_wait_mdio_idle(hw))
383929a5e93SHuang, Xiong 		return -1;
384929a5e93SHuang, Xiong 
385929a5e93SHuang, Xiong 	atl1c_start_phy_polling(hw, clk_sel);
386929a5e93SHuang, Xiong 
387929a5e93SHuang, Xiong 	return 0;
388929a5e93SHuang, Xiong }
389929a5e93SHuang, Xiong 
390929a5e93SHuang, Xiong /*
3912b133ad6SJeff Kirsher  * Reads the value from a PHY register
3922b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
3932b133ad6SJeff Kirsher  * reg_addr - address of the PHY register to read
3942b133ad6SJeff Kirsher  */
atl1c_read_phy_reg(struct atl1c_hw * hw,u16 reg_addr,u16 * phy_data)3952b133ad6SJeff Kirsher int atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
3962b133ad6SJeff Kirsher {
397929a5e93SHuang, Xiong 	return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data);
3982b133ad6SJeff Kirsher }
3992b133ad6SJeff Kirsher 
4002b133ad6SJeff Kirsher /*
4012b133ad6SJeff Kirsher  * Writes a value to a PHY register
4022b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
4032b133ad6SJeff Kirsher  * reg_addr - address of the PHY register to write
4042b133ad6SJeff Kirsher  * data - data to write to the PHY
4052b133ad6SJeff Kirsher  */
atl1c_write_phy_reg(struct atl1c_hw * hw,u32 reg_addr,u16 phy_data)4062b133ad6SJeff Kirsher int atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
4072b133ad6SJeff Kirsher {
408929a5e93SHuang, Xiong 	return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data);
4092b133ad6SJeff Kirsher }
4102b133ad6SJeff Kirsher 
411929a5e93SHuang, Xiong /* read from PHY extension register */
atl1c_read_phy_ext(struct atl1c_hw * hw,u8 dev_addr,u16 reg_addr,u16 * phy_data)412929a5e93SHuang, Xiong int atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
413929a5e93SHuang, Xiong 			u16 reg_addr, u16 *phy_data)
414929a5e93SHuang, Xiong {
415929a5e93SHuang, Xiong 	return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data);
416929a5e93SHuang, Xiong }
4172b133ad6SJeff Kirsher 
418929a5e93SHuang, Xiong /* write to PHY extension register */
atl1c_write_phy_ext(struct atl1c_hw * hw,u8 dev_addr,u16 reg_addr,u16 phy_data)419929a5e93SHuang, Xiong int atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
420929a5e93SHuang, Xiong 			u16 reg_addr, u16 phy_data)
421929a5e93SHuang, Xiong {
422929a5e93SHuang, Xiong 	return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data);
4232b133ad6SJeff Kirsher }
4242b133ad6SJeff Kirsher 
atl1c_read_phy_dbg(struct atl1c_hw * hw,u16 reg_addr,u16 * phy_data)425ce5b972bSHuang, Xiong int atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
426ce5b972bSHuang, Xiong {
427ce5b972bSHuang, Xiong 	int err;
428ce5b972bSHuang, Xiong 
429ce5b972bSHuang, Xiong 	err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
430ce5b972bSHuang, Xiong 	if (unlikely(err))
431ce5b972bSHuang, Xiong 		return err;
432ce5b972bSHuang, Xiong 	else
433ce5b972bSHuang, Xiong 		err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data);
434ce5b972bSHuang, Xiong 
435ce5b972bSHuang, Xiong 	return err;
436ce5b972bSHuang, Xiong }
437ce5b972bSHuang, Xiong 
atl1c_write_phy_dbg(struct atl1c_hw * hw,u16 reg_addr,u16 phy_data)438ce5b972bSHuang, Xiong int atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data)
439ce5b972bSHuang, Xiong {
440ce5b972bSHuang, Xiong 	int err;
441ce5b972bSHuang, Xiong 
442ce5b972bSHuang, Xiong 	err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
443ce5b972bSHuang, Xiong 	if (unlikely(err))
444ce5b972bSHuang, Xiong 		return err;
445ce5b972bSHuang, Xiong 	else
446ce5b972bSHuang, Xiong 		err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
447ce5b972bSHuang, Xiong 
448ce5b972bSHuang, Xiong 	return err;
449ce5b972bSHuang, Xiong }
450ce5b972bSHuang, Xiong 
4512b133ad6SJeff Kirsher /*
4522b133ad6SJeff Kirsher  * Configures PHY autoneg and flow control advertisement settings
4532b133ad6SJeff Kirsher  *
4542b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
4552b133ad6SJeff Kirsher  */
atl1c_phy_setup_adv(struct atl1c_hw * hw)4562b133ad6SJeff Kirsher static int atl1c_phy_setup_adv(struct atl1c_hw *hw)
4572b133ad6SJeff Kirsher {
4582b133ad6SJeff Kirsher 	u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL;
4592b133ad6SJeff Kirsher 	u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP &
4602b133ad6SJeff Kirsher 				~GIGA_CR_1000T_SPEED_MASK;
4612b133ad6SJeff Kirsher 
4622b133ad6SJeff Kirsher 	if (hw->autoneg_advertised & ADVERTISED_10baseT_Half)
4632b133ad6SJeff Kirsher 		mii_adv_data |= ADVERTISE_10HALF;
4642b133ad6SJeff Kirsher 	if (hw->autoneg_advertised & ADVERTISED_10baseT_Full)
4652b133ad6SJeff Kirsher 		mii_adv_data |= ADVERTISE_10FULL;
4662b133ad6SJeff Kirsher 	if (hw->autoneg_advertised & ADVERTISED_100baseT_Half)
4672b133ad6SJeff Kirsher 		mii_adv_data |= ADVERTISE_100HALF;
4682b133ad6SJeff Kirsher 	if (hw->autoneg_advertised & ADVERTISED_100baseT_Full)
4692b133ad6SJeff Kirsher 		mii_adv_data |= ADVERTISE_100FULL;
4702b133ad6SJeff Kirsher 
4712b133ad6SJeff Kirsher 	if (hw->autoneg_advertised & ADVERTISED_Autoneg)
4722b133ad6SJeff Kirsher 		mii_adv_data |= ADVERTISE_10HALF  | ADVERTISE_10FULL |
4732b133ad6SJeff Kirsher 				ADVERTISE_100HALF | ADVERTISE_100FULL;
4742b133ad6SJeff Kirsher 
4752b133ad6SJeff Kirsher 	if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) {
4762b133ad6SJeff Kirsher 		if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half)
4772b133ad6SJeff Kirsher 			mii_giga_ctrl_data |= ADVERTISE_1000HALF;
4782b133ad6SJeff Kirsher 		if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full)
4792b133ad6SJeff Kirsher 			mii_giga_ctrl_data |= ADVERTISE_1000FULL;
4802b133ad6SJeff Kirsher 		if (hw->autoneg_advertised & ADVERTISED_Autoneg)
4812b133ad6SJeff Kirsher 			mii_giga_ctrl_data |= ADVERTISE_1000HALF |
4822b133ad6SJeff Kirsher 					ADVERTISE_1000FULL;
4832b133ad6SJeff Kirsher 	}
4842b133ad6SJeff Kirsher 
4852b133ad6SJeff Kirsher 	if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 ||
4862b133ad6SJeff Kirsher 	    atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0)
4872b133ad6SJeff Kirsher 		return -1;
4882b133ad6SJeff Kirsher 	return 0;
4892b133ad6SJeff Kirsher }
4902b133ad6SJeff Kirsher 
atl1c_phy_disable(struct atl1c_hw * hw)4912b133ad6SJeff Kirsher void atl1c_phy_disable(struct atl1c_hw *hw)
4922b133ad6SJeff Kirsher {
493319d013aSHuang, Xiong 	atl1c_power_saving(hw, 0);
4942b133ad6SJeff Kirsher }
4952b133ad6SJeff Kirsher 
4962b133ad6SJeff Kirsher 
atl1c_phy_reset(struct atl1c_hw * hw)4972b133ad6SJeff Kirsher int atl1c_phy_reset(struct atl1c_hw *hw)
4982b133ad6SJeff Kirsher {
4992b133ad6SJeff Kirsher 	struct atl1c_adapter *adapter = hw->adapter;
5002b133ad6SJeff Kirsher 	struct pci_dev *pdev = adapter->pdev;
5012b133ad6SJeff Kirsher 	u16 phy_data;
502ce5b972bSHuang, Xiong 	u32 phy_ctrl_data, lpi_ctrl;
5032b133ad6SJeff Kirsher 	int err;
5042b133ad6SJeff Kirsher 
505ce5b972bSHuang, Xiong 	/* reset PHY core */
506ce5b972bSHuang, Xiong 	AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data);
507ce5b972bSHuang, Xiong 	phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ |
508ce5b972bSHuang, Xiong 		GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS);
509ce5b972bSHuang, Xiong 	phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST;
510ce5b972bSHuang, Xiong 	if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE))
511ce5b972bSHuang, Xiong 		phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
512ce5b972bSHuang, Xiong 	else
513ce5b972bSHuang, Xiong 		phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
5142b133ad6SJeff Kirsher 	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
5152b133ad6SJeff Kirsher 	AT_WRITE_FLUSH(hw);
516ce5b972bSHuang, Xiong 	udelay(10);
517ce5b972bSHuang, Xiong 	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET);
5182b133ad6SJeff Kirsher 	AT_WRITE_FLUSH(hw);
519ce5b972bSHuang, Xiong 	udelay(10 * GPHY_CTRL_EXT_RST_TO);	/* delay 800us */
5202b133ad6SJeff Kirsher 
521ce5b972bSHuang, Xiong 	/* switch clock */
5222b133ad6SJeff Kirsher 	if (hw->nic_type == athr_l2c_b) {
523ce5b972bSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data);
524ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD,
525ce5b972bSHuang, Xiong 			phy_data & ~CFGLPSPD_RSTCNT_CLK125SW);
5262b133ad6SJeff Kirsher 	}
5272b133ad6SJeff Kirsher 
528ce5b972bSHuang, Xiong 	/* tx-half amplitude issue fix */
529ce5b972bSHuang, Xiong 	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
530ce5b972bSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data);
531ce5b972bSHuang, Xiong 		phy_data |= CABLE1TH_DET_EN;
532ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data);
5332b133ad6SJeff Kirsher 	}
534ce5b972bSHuang, Xiong 
535ce5b972bSHuang, Xiong 	/* clear bit3 of dbgport 3B to lower voltage */
536ce5b972bSHuang, Xiong 	if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) {
537ce5b972bSHuang, Xiong 		if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
538ce5b972bSHuang, Xiong 			atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
539ce5b972bSHuang, Xiong 			phy_data &= ~VOLT_CTRL_SWLOWEST;
540ce5b972bSHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
5412b133ad6SJeff Kirsher 		}
542ce5b972bSHuang, Xiong 		/* power saving config */
543ce5b972bSHuang, Xiong 		phy_data =
544ce5b972bSHuang, Xiong 			hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ?
545ce5b972bSHuang, Xiong 			L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF;
546ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data);
547ce5b972bSHuang, Xiong 		/* hib */
548ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
549ce5b972bSHuang, Xiong 			SYSMODCTRL_IECHOADJ_DEF);
550ce5b972bSHuang, Xiong 	} else {
551ce5b972bSHuang, Xiong 		/* disable pws */
552ce5b972bSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data);
553ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS,
554ce5b972bSHuang, Xiong 			phy_data & ~LEGCYPS_EN);
555ce5b972bSHuang, Xiong 		/* disable hibernate */
556ce5b972bSHuang, Xiong 		atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data);
557ce5b972bSHuang, Xiong 		atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG,
558ce5b972bSHuang, Xiong 			phy_data & HIBNEG_PSHIB_EN);
5592b133ad6SJeff Kirsher 	}
560ce5b972bSHuang, Xiong 	/* disable AZ(EEE) by default */
561ce5b972bSHuang, Xiong 	if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ||
562ce5b972bSHuang, Xiong 	    hw->nic_type == athr_l2c_b2) {
563ce5b972bSHuang, Xiong 		AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl);
564ce5b972bSHuang, Xiong 		AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN);
565ce5b972bSHuang, Xiong 		atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0);
566ce5b972bSHuang, Xiong 		atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3,
567ce5b972bSHuang, Xiong 			L2CB_CLDCTRL3);
568ce5b972bSHuang, Xiong 	}
569ce5b972bSHuang, Xiong 
570ce5b972bSHuang, Xiong 	/* other debug port to set */
571ce5b972bSHuang, Xiong 	atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF);
572ce5b972bSHuang, Xiong 	atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF);
573ce5b972bSHuang, Xiong 	atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF);
574ce5b972bSHuang, Xiong 	/* UNH-IOL test issue, set bit7 */
575ce5b972bSHuang, Xiong 	atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG,
576ce5b972bSHuang, Xiong 		TST100BTCFG_DEF | TST100BTCFG_LITCH_EN);
577ce5b972bSHuang, Xiong 
578ce5b972bSHuang, Xiong 	/* set phy interrupt mask */
579ce5b972bSHuang, Xiong 	phy_data = IER_LINK_UP | IER_LINK_DOWN;
580ce5b972bSHuang, Xiong 	err = atl1c_write_phy_reg(hw, MII_IER, phy_data);
5812b133ad6SJeff Kirsher 	if (err) {
5822b133ad6SJeff Kirsher 		if (netif_msg_hw(adapter))
5832b133ad6SJeff Kirsher 			dev_err(&pdev->dev,
5842b133ad6SJeff Kirsher 				"Error enable PHY linkChange Interrupt\n");
5852b133ad6SJeff Kirsher 		return err;
5862b133ad6SJeff Kirsher 	}
5872b133ad6SJeff Kirsher 	return 0;
5882b133ad6SJeff Kirsher }
5892b133ad6SJeff Kirsher 
atl1c_phy_init(struct atl1c_hw * hw)5902b133ad6SJeff Kirsher int atl1c_phy_init(struct atl1c_hw *hw)
5912b133ad6SJeff Kirsher {
59264699336SJoe Perches 	struct atl1c_adapter *adapter = hw->adapter;
5932b133ad6SJeff Kirsher 	struct pci_dev *pdev = adapter->pdev;
5942b133ad6SJeff Kirsher 	int ret_val;
5952b133ad6SJeff Kirsher 	u16 mii_bmcr_data = BMCR_RESET;
5962b133ad6SJeff Kirsher 
597*b9d233eaSGatis Peisenieks 	if (hw->nic_type == athr_mt) {
598*b9d233eaSGatis Peisenieks 		hw->phy_configured = true;
599*b9d233eaSGatis Peisenieks 		return 0;
600*b9d233eaSGatis Peisenieks 	}
601*b9d233eaSGatis Peisenieks 
6022b133ad6SJeff Kirsher 	if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
6032b133ad6SJeff Kirsher 		(atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
6042b133ad6SJeff Kirsher 		dev_err(&pdev->dev, "Error get phy ID\n");
6052b133ad6SJeff Kirsher 		return -1;
6062b133ad6SJeff Kirsher 	}
6072b133ad6SJeff Kirsher 	switch (hw->media_type) {
6082b133ad6SJeff Kirsher 	case MEDIA_TYPE_AUTO_SENSOR:
6092b133ad6SJeff Kirsher 		ret_val = atl1c_phy_setup_adv(hw);
6102b133ad6SJeff Kirsher 		if (ret_val) {
6112b133ad6SJeff Kirsher 			if (netif_msg_link(adapter))
6122b133ad6SJeff Kirsher 				dev_err(&pdev->dev,
6132b133ad6SJeff Kirsher 					"Error Setting up Auto-Negotiation\n");
6142b133ad6SJeff Kirsher 			return ret_val;
6152b133ad6SJeff Kirsher 		}
6162b133ad6SJeff Kirsher 		mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
6172b133ad6SJeff Kirsher 		break;
6182b133ad6SJeff Kirsher 	case MEDIA_TYPE_100M_FULL:
6192b133ad6SJeff Kirsher 		mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX;
6202b133ad6SJeff Kirsher 		break;
6212b133ad6SJeff Kirsher 	case MEDIA_TYPE_100M_HALF:
6222b133ad6SJeff Kirsher 		mii_bmcr_data |= BMCR_SPEED100;
6232b133ad6SJeff Kirsher 		break;
6242b133ad6SJeff Kirsher 	case MEDIA_TYPE_10M_FULL:
6252b133ad6SJeff Kirsher 		mii_bmcr_data |= BMCR_FULLDPLX;
6262b133ad6SJeff Kirsher 		break;
6272b133ad6SJeff Kirsher 	case MEDIA_TYPE_10M_HALF:
6282b133ad6SJeff Kirsher 		break;
6292b133ad6SJeff Kirsher 	default:
6302b133ad6SJeff Kirsher 		if (netif_msg_link(adapter))
6312b133ad6SJeff Kirsher 			dev_err(&pdev->dev, "Wrong Media type %d\n",
6322b133ad6SJeff Kirsher 				hw->media_type);
6332b133ad6SJeff Kirsher 		return -1;
6342b133ad6SJeff Kirsher 	}
6352b133ad6SJeff Kirsher 
6362b133ad6SJeff Kirsher 	ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
6372b133ad6SJeff Kirsher 	if (ret_val)
6382b133ad6SJeff Kirsher 		return ret_val;
6392b133ad6SJeff Kirsher 	hw->phy_configured = true;
6402b133ad6SJeff Kirsher 
6412b133ad6SJeff Kirsher 	return 0;
6422b133ad6SJeff Kirsher }
6432b133ad6SJeff Kirsher 
atl1c_get_link_status(struct atl1c_hw * hw)644ea0fbd05SGatis Peisenieks bool atl1c_get_link_status(struct atl1c_hw *hw)
645ea0fbd05SGatis Peisenieks {
646ea0fbd05SGatis Peisenieks 	u16 phy_data;
647ea0fbd05SGatis Peisenieks 
648ea0fbd05SGatis Peisenieks 	if (hw->nic_type == athr_mt) {
649ea0fbd05SGatis Peisenieks 		u32 spd;
650ea0fbd05SGatis Peisenieks 
651ea0fbd05SGatis Peisenieks 		AT_READ_REG(hw, REG_MT_SPEED, &spd);
652ea0fbd05SGatis Peisenieks 		return !!spd;
653ea0fbd05SGatis Peisenieks 	}
654ea0fbd05SGatis Peisenieks 
655ea0fbd05SGatis Peisenieks 	/* MII_BMSR must be read twice */
656ea0fbd05SGatis Peisenieks 	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
657ea0fbd05SGatis Peisenieks 	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
658ea0fbd05SGatis Peisenieks 	return !!(phy_data & BMSR_LSTATUS);
659ea0fbd05SGatis Peisenieks }
660ea0fbd05SGatis Peisenieks 
6612b133ad6SJeff Kirsher /*
6622b133ad6SJeff Kirsher  * Detects the current speed and duplex settings of the hardware.
6632b133ad6SJeff Kirsher  *
6642b133ad6SJeff Kirsher  * hw - Struct containing variables accessed by shared code
6652b133ad6SJeff Kirsher  * speed - Speed of the connection
6662b133ad6SJeff Kirsher  * duplex - Duplex setting of the connection
6672b133ad6SJeff Kirsher  */
atl1c_get_speed_and_duplex(struct atl1c_hw * hw,u16 * speed,u16 * duplex)6682b133ad6SJeff Kirsher int atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
6692b133ad6SJeff Kirsher {
6702b133ad6SJeff Kirsher 	int err;
6712b133ad6SJeff Kirsher 	u16 phy_data;
6722b133ad6SJeff Kirsher 
673f19d4997SGatis Peisenieks 	if (hw->nic_type == athr_mt) {
674f19d4997SGatis Peisenieks 		u32 spd;
675f19d4997SGatis Peisenieks 
676f19d4997SGatis Peisenieks 		AT_READ_REG(hw, REG_MT_SPEED, &spd);
677f19d4997SGatis Peisenieks 		*speed = spd;
678f19d4997SGatis Peisenieks 		*duplex = FULL_DUPLEX;
679f19d4997SGatis Peisenieks 		return 0;
680f19d4997SGatis Peisenieks 	}
681f19d4997SGatis Peisenieks 
6822b133ad6SJeff Kirsher 	/* Read   PHY Specific Status Register (17) */
6832b133ad6SJeff Kirsher 	err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
6842b133ad6SJeff Kirsher 	if (err)
6852b133ad6SJeff Kirsher 		return err;
6862b133ad6SJeff Kirsher 
6872b133ad6SJeff Kirsher 	if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED))
6882b133ad6SJeff Kirsher 		return -1;
6892b133ad6SJeff Kirsher 
6902b133ad6SJeff Kirsher 	switch (phy_data & GIGA_PSSR_SPEED) {
6912b133ad6SJeff Kirsher 	case GIGA_PSSR_1000MBS:
6922b133ad6SJeff Kirsher 		*speed = SPEED_1000;
6932b133ad6SJeff Kirsher 		break;
6942b133ad6SJeff Kirsher 	case GIGA_PSSR_100MBS:
6952b133ad6SJeff Kirsher 		*speed = SPEED_100;
6962b133ad6SJeff Kirsher 		break;
6972b133ad6SJeff Kirsher 	case  GIGA_PSSR_10MBS:
6982b133ad6SJeff Kirsher 		*speed = SPEED_10;
6992b133ad6SJeff Kirsher 		break;
7002b133ad6SJeff Kirsher 	default:
7012b133ad6SJeff Kirsher 		return -1;
7022b133ad6SJeff Kirsher 	}
7032b133ad6SJeff Kirsher 
7042b133ad6SJeff Kirsher 	if (phy_data & GIGA_PSSR_DPLX)
7052b133ad6SJeff Kirsher 		*duplex = FULL_DUPLEX;
7062b133ad6SJeff Kirsher 	else
7072b133ad6SJeff Kirsher 		*duplex = HALF_DUPLEX;
7082b133ad6SJeff Kirsher 
7092b133ad6SJeff Kirsher 	return 0;
7102b133ad6SJeff Kirsher }
7112b133ad6SJeff Kirsher 
712319d013aSHuang, Xiong /* select one link mode to get lower power consumption */
atl1c_phy_to_ps_link(struct atl1c_hw * hw)713319d013aSHuang, Xiong int atl1c_phy_to_ps_link(struct atl1c_hw *hw)
7142b133ad6SJeff Kirsher {
71564699336SJoe Perches 	struct atl1c_adapter *adapter = hw->adapter;
7162b133ad6SJeff Kirsher 	struct pci_dev *pdev = adapter->pdev;
7172b133ad6SJeff Kirsher 	int ret = 0;
7182b133ad6SJeff Kirsher 	u16 autoneg_advertised = ADVERTISED_10baseT_Half;
7192b133ad6SJeff Kirsher 	u16 save_autoneg_advertised;
7202b133ad6SJeff Kirsher 	u16 mii_lpa_data;
7212b133ad6SJeff Kirsher 	u16 speed = SPEED_0;
7222b133ad6SJeff Kirsher 	u16 duplex = FULL_DUPLEX;
7232b133ad6SJeff Kirsher 	int i;
7242b133ad6SJeff Kirsher 
725ea0fbd05SGatis Peisenieks 	if (atl1c_get_link_status(hw)) {
7262b133ad6SJeff Kirsher 		atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
7272b133ad6SJeff Kirsher 		if (mii_lpa_data & LPA_10FULL)
7282b133ad6SJeff Kirsher 			autoneg_advertised = ADVERTISED_10baseT_Full;
7292b133ad6SJeff Kirsher 		else if (mii_lpa_data & LPA_10HALF)
7302b133ad6SJeff Kirsher 			autoneg_advertised = ADVERTISED_10baseT_Half;
7312b133ad6SJeff Kirsher 		else if (mii_lpa_data & LPA_100HALF)
7322b133ad6SJeff Kirsher 			autoneg_advertised = ADVERTISED_100baseT_Half;
7332b133ad6SJeff Kirsher 		else if (mii_lpa_data & LPA_100FULL)
7342b133ad6SJeff Kirsher 			autoneg_advertised = ADVERTISED_100baseT_Full;
7352b133ad6SJeff Kirsher 
7362b133ad6SJeff Kirsher 		save_autoneg_advertised = hw->autoneg_advertised;
7372b133ad6SJeff Kirsher 		hw->phy_configured = false;
7382b133ad6SJeff Kirsher 		hw->autoneg_advertised = autoneg_advertised;
7392b133ad6SJeff Kirsher 		if (atl1c_restart_autoneg(hw) != 0) {
7402b133ad6SJeff Kirsher 			dev_dbg(&pdev->dev, "phy autoneg failed\n");
7412b133ad6SJeff Kirsher 			ret = -1;
7422b133ad6SJeff Kirsher 		}
7432b133ad6SJeff Kirsher 		hw->autoneg_advertised = save_autoneg_advertised;
7442b133ad6SJeff Kirsher 
7452b133ad6SJeff Kirsher 		if (mii_lpa_data) {
7462b133ad6SJeff Kirsher 			for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
7472b133ad6SJeff Kirsher 				mdelay(100);
748ea0fbd05SGatis Peisenieks 				if (atl1c_get_link_status(hw)) {
7492b133ad6SJeff Kirsher 					if (atl1c_get_speed_and_duplex(hw, &speed,
7502b133ad6SJeff Kirsher 									&duplex) != 0)
7512b133ad6SJeff Kirsher 						dev_dbg(&pdev->dev,
7522b133ad6SJeff Kirsher 							"get speed and duplex failed\n");
7532b133ad6SJeff Kirsher 					break;
7542b133ad6SJeff Kirsher 				}
7552b133ad6SJeff Kirsher 			}
7562b133ad6SJeff Kirsher 		}
7572b133ad6SJeff Kirsher 	} else {
7582b133ad6SJeff Kirsher 		speed = SPEED_10;
7592b133ad6SJeff Kirsher 		duplex = HALF_DUPLEX;
7602b133ad6SJeff Kirsher 	}
7612b133ad6SJeff Kirsher 	adapter->link_speed = speed;
7622b133ad6SJeff Kirsher 	adapter->link_duplex = duplex;
7632b133ad6SJeff Kirsher 
7642b133ad6SJeff Kirsher 	return ret;
7652b133ad6SJeff Kirsher }
7662b133ad6SJeff Kirsher 
atl1c_restart_autoneg(struct atl1c_hw * hw)7672b133ad6SJeff Kirsher int atl1c_restart_autoneg(struct atl1c_hw *hw)
7682b133ad6SJeff Kirsher {
7692b133ad6SJeff Kirsher 	int err = 0;
7702b133ad6SJeff Kirsher 	u16 mii_bmcr_data = BMCR_RESET;
7712b133ad6SJeff Kirsher 
7722b133ad6SJeff Kirsher 	err = atl1c_phy_setup_adv(hw);
7732b133ad6SJeff Kirsher 	if (err)
7742b133ad6SJeff Kirsher 		return err;
7752b133ad6SJeff Kirsher 	mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
7762b133ad6SJeff Kirsher 
7772b133ad6SJeff Kirsher 	return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
7782b133ad6SJeff Kirsher }
779319d013aSHuang, Xiong 
atl1c_power_saving(struct atl1c_hw * hw,u32 wufc)780319d013aSHuang, Xiong int atl1c_power_saving(struct atl1c_hw *hw, u32 wufc)
781319d013aSHuang, Xiong {
78264699336SJoe Perches 	struct atl1c_adapter *adapter = hw->adapter;
783319d013aSHuang, Xiong 	struct pci_dev *pdev = adapter->pdev;
784319d013aSHuang, Xiong 	u32 master_ctrl, mac_ctrl, phy_ctrl;
785319d013aSHuang, Xiong 	u32 wol_ctrl, speed;
786319d013aSHuang, Xiong 	u16 phy_data;
787319d013aSHuang, Xiong 
788319d013aSHuang, Xiong 	wol_ctrl = 0;
789319d013aSHuang, Xiong 	speed = adapter->link_speed == SPEED_1000 ?
790319d013aSHuang, Xiong 		MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100;
791319d013aSHuang, Xiong 
792319d013aSHuang, Xiong 	AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl);
793319d013aSHuang, Xiong 	AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl);
794319d013aSHuang, Xiong 	AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl);
795319d013aSHuang, Xiong 
796319d013aSHuang, Xiong 	master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS;
797319d013aSHuang, Xiong 	mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed);
798319d013aSHuang, Xiong 	mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN);
799319d013aSHuang, Xiong 	if (adapter->link_duplex == FULL_DUPLEX)
800319d013aSHuang, Xiong 		mac_ctrl |= MAC_CTRL_DUPLX;
801319d013aSHuang, Xiong 	phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS);
802319d013aSHuang, Xiong 	phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE |
803319d013aSHuang, Xiong 		GPHY_CTRL_HIB_EN;
804319d013aSHuang, Xiong 	if (!wufc) { /* without WoL */
805319d013aSHuang, Xiong 		master_ctrl |= MASTER_CTRL_CLK_SEL_DIS;
806319d013aSHuang, Xiong 		phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW;
807319d013aSHuang, Xiong 		AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
808319d013aSHuang, Xiong 		AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
809319d013aSHuang, Xiong 		AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
810319d013aSHuang, Xiong 		AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
811319d013aSHuang, Xiong 		hw->phy_configured = false; /* re-init PHY when resume */
812319d013aSHuang, Xiong 		return 0;
813319d013aSHuang, Xiong 	}
814319d013aSHuang, Xiong 	phy_ctrl |= GPHY_CTRL_EXT_RESET;
815319d013aSHuang, Xiong 	if (wufc & AT_WUFC_MAG) {
816319d013aSHuang, Xiong 		mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN;
817319d013aSHuang, Xiong 		wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
818319d013aSHuang, Xiong 		if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11)
819319d013aSHuang, Xiong 			wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN;
820319d013aSHuang, Xiong 	}
821319d013aSHuang, Xiong 	if (wufc & AT_WUFC_LNKC) {
822319d013aSHuang, Xiong 		wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
823319d013aSHuang, Xiong 		if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
8241051e9b3SMasanari Iida 			dev_dbg(&pdev->dev, "%s: write phy MII_IER failed.\n",
825319d013aSHuang, Xiong 				atl1c_driver_name);
826319d013aSHuang, Xiong 		}
827319d013aSHuang, Xiong 	}
828319d013aSHuang, Xiong 	/* clear PHY interrupt */
829319d013aSHuang, Xiong 	atl1c_read_phy_reg(hw, MII_ISR, &phy_data);
830319d013aSHuang, Xiong 
831319d013aSHuang, Xiong 	dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n",
832319d013aSHuang, Xiong 		atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl);
833319d013aSHuang, Xiong 	AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
834319d013aSHuang, Xiong 	AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
835319d013aSHuang, Xiong 	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
836319d013aSHuang, Xiong 	AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl);
837319d013aSHuang, Xiong 
838319d013aSHuang, Xiong 	return 0;
839319d013aSHuang, Xiong }
840903d7ce0SHuang, Xiong 
841903d7ce0SHuang, Xiong 
842903d7ce0SHuang, Xiong /* configure phy after Link change Event */
atl1c_post_phy_linkchg(struct atl1c_hw * hw,u16 link_speed)843903d7ce0SHuang, Xiong void atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed)
844903d7ce0SHuang, Xiong {
845903d7ce0SHuang, Xiong 	u16 phy_val;
846903d7ce0SHuang, Xiong 	bool adj_thresh = false;
847903d7ce0SHuang, Xiong 
848903d7ce0SHuang, Xiong 	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ||
849903d7ce0SHuang, Xiong 	    hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2)
850903d7ce0SHuang, Xiong 		adj_thresh = true;
851903d7ce0SHuang, Xiong 
852903d7ce0SHuang, Xiong 	if (link_speed != SPEED_0) { /* link up */
853903d7ce0SHuang, Xiong 		/* az with brcm, half-amp */
854903d7ce0SHuang, Xiong 		if (hw->nic_type == athr_l1d_2) {
855903d7ce0SHuang, Xiong 			atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6,
856903d7ce0SHuang, Xiong 				&phy_val);
857903d7ce0SHuang, Xiong 			phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN);
858903d7ce0SHuang, Xiong 			phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ?
859903d7ce0SHuang, Xiong 				AZ_ANADECT_LONG : AZ_ANADECT_DEF;
860903d7ce0SHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val);
861903d7ce0SHuang, Xiong 		}
862903d7ce0SHuang, Xiong 		/* threshold adjust */
863903d7ce0SHuang, Xiong 		if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) {
864903d7ce0SHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP);
865903d7ce0SHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
866903d7ce0SHuang, Xiong 				L1D_SYSMODCTRL_IECHOADJ_DEF);
867903d7ce0SHuang, Xiong 		}
868903d7ce0SHuang, Xiong 	} else { /* link down */
869903d7ce0SHuang, Xiong 		if (adj_thresh && hw->msi_lnkpatch) {
870903d7ce0SHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
871903d7ce0SHuang, Xiong 				SYSMODCTRL_IECHOADJ_DEF);
872903d7ce0SHuang, Xiong 			atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB,
873903d7ce0SHuang, Xiong 				L1D_MSE16DB_DOWN);
874903d7ce0SHuang, Xiong 		}
875903d7ce0SHuang, Xiong 	}
876903d7ce0SHuang, Xiong }
877