18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright(c) 2007 Atheros Corporation. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Derived from Intel e1000 driver
68c2ecf20Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <linux/pci.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/mii.h>
118c2ecf20Sopenharmony_ci#include <linux/crc32.h>
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include "atl1c.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci/*
168c2ecf20Sopenharmony_ci * check_eeprom_exist
178c2ecf20Sopenharmony_ci * return 1 if eeprom exist
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ciint atl1c_check_eeprom_exist(struct atl1c_hw *hw)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	u32 data;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_TWSI_DEBUG, &data);
248c2ecf20Sopenharmony_ci	if (data & TWSI_DEBUG_DEV_EXIST)
258c2ecf20Sopenharmony_ci		return 1;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MASTER_CTRL, &data);
288c2ecf20Sopenharmony_ci	if (data & MASTER_CTRL_OTP_SEL)
298c2ecf20Sopenharmony_ci		return 1;
308c2ecf20Sopenharmony_ci	return 0;
318c2ecf20Sopenharmony_ci}
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_civoid atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	u32 value;
368c2ecf20Sopenharmony_ci	/*
378c2ecf20Sopenharmony_ci	 * 00-0B-6A-F6-00-DC
388c2ecf20Sopenharmony_ci	 * 0:  6AF600DC 1: 000B
398c2ecf20Sopenharmony_ci	 * low dword
408c2ecf20Sopenharmony_ci	 */
418c2ecf20Sopenharmony_ci	value = mac_addr[2] << 24 |
428c2ecf20Sopenharmony_ci		mac_addr[3] << 16 |
438c2ecf20Sopenharmony_ci		mac_addr[4] << 8  |
448c2ecf20Sopenharmony_ci		mac_addr[5];
458c2ecf20Sopenharmony_ci	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value);
468c2ecf20Sopenharmony_ci	/* hight dword */
478c2ecf20Sopenharmony_ci	value = mac_addr[0] << 8 |
488c2ecf20Sopenharmony_ci		mac_addr[1];
498c2ecf20Sopenharmony_ci	AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci/* read mac address from hardware register */
538c2ecf20Sopenharmony_cistatic bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	u32 addr[2];
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]);
588c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	*(u32 *) &eth_addr[2] = htonl(addr[0]);
618c2ecf20Sopenharmony_ci	*(u16 *) &eth_addr[0] = htons((u16)addr[1]);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	return is_valid_ether_addr(eth_addr);
648c2ecf20Sopenharmony_ci}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci/*
678c2ecf20Sopenharmony_ci * atl1c_get_permanent_address
688c2ecf20Sopenharmony_ci * return 0 if get valid mac address,
698c2ecf20Sopenharmony_ci */
708c2ecf20Sopenharmony_cistatic int atl1c_get_permanent_address(struct atl1c_hw *hw)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	u32 i;
738c2ecf20Sopenharmony_ci	u32 otp_ctrl_data;
748c2ecf20Sopenharmony_ci	u32 twsi_ctrl_data;
758c2ecf20Sopenharmony_ci	u16 phy_data;
768c2ecf20Sopenharmony_ci	bool raise_vol = false;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* MAC-address from BIOS is the 1st priority */
798c2ecf20Sopenharmony_ci	if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
808c2ecf20Sopenharmony_ci		return 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* init */
838c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
848c2ecf20Sopenharmony_ci	if (atl1c_check_eeprom_exist(hw)) {
858c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) {
868c2ecf20Sopenharmony_ci			/* Enable OTP CLK */
878c2ecf20Sopenharmony_ci			if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) {
888c2ecf20Sopenharmony_ci				otp_ctrl_data |= OTP_CTRL_CLK_EN;
898c2ecf20Sopenharmony_ci				AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
908c2ecf20Sopenharmony_ci				AT_WRITE_FLUSH(hw);
918c2ecf20Sopenharmony_ci				msleep(1);
928c2ecf20Sopenharmony_ci			}
938c2ecf20Sopenharmony_ci		}
948c2ecf20Sopenharmony_ci		/* raise voltage temporally for l2cb */
958c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
968c2ecf20Sopenharmony_ci			atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
978c2ecf20Sopenharmony_ci			phy_data &= ~ANACTRL_HB_EN;
988c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
998c2ecf20Sopenharmony_ci			atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
1008c2ecf20Sopenharmony_ci			phy_data |= VOLT_CTRL_SWLOWEST;
1018c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
1028c2ecf20Sopenharmony_ci			udelay(20);
1038c2ecf20Sopenharmony_ci			raise_vol = true;
1048c2ecf20Sopenharmony_ci		}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
1078c2ecf20Sopenharmony_ci		twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART;
1088c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data);
1098c2ecf20Sopenharmony_ci		for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) {
1108c2ecf20Sopenharmony_ci			msleep(10);
1118c2ecf20Sopenharmony_ci			AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data);
1128c2ecf20Sopenharmony_ci			if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0)
1138c2ecf20Sopenharmony_ci				break;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci		if (i >= AT_TWSI_EEPROM_TIMEOUT)
1168c2ecf20Sopenharmony_ci			return -1;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	/* Disable OTP_CLK */
1198c2ecf20Sopenharmony_ci	if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) {
1208c2ecf20Sopenharmony_ci		otp_ctrl_data &= ~OTP_CTRL_CLK_EN;
1218c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
1228c2ecf20Sopenharmony_ci		msleep(1);
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci	if (raise_vol) {
1258c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data);
1268c2ecf20Sopenharmony_ci		phy_data |= ANACTRL_HB_EN;
1278c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data);
1288c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
1298c2ecf20Sopenharmony_ci		phy_data &= ~VOLT_CTRL_SWLOWEST;
1308c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
1318c2ecf20Sopenharmony_ci		udelay(20);
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (atl1c_read_current_addr(hw, hw->perm_mac_addr))
1358c2ecf20Sopenharmony_ci		return 0;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return -1;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cibool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int i;
1438c2ecf20Sopenharmony_ci	bool ret = false;
1448c2ecf20Sopenharmony_ci	u32 otp_ctrl_data;
1458c2ecf20Sopenharmony_ci	u32 control;
1468c2ecf20Sopenharmony_ci	u32 data;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	if (offset & 3)
1498c2ecf20Sopenharmony_ci		return ret; /* address do not align */
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data);
1528c2ecf20Sopenharmony_ci	if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
1538c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_OTP_CTRL,
1548c2ecf20Sopenharmony_ci				(otp_ctrl_data | OTP_CTRL_CLK_EN));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0);
1578c2ecf20Sopenharmony_ci	control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT;
1588c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_EEPROM_CTRL, control);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	for (i = 0; i < 10; i++) {
1618c2ecf20Sopenharmony_ci		udelay(100);
1628c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_EEPROM_CTRL, &control);
1638c2ecf20Sopenharmony_ci		if (control & EEPROM_CTRL_RW)
1648c2ecf20Sopenharmony_ci			break;
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci	if (control & EEPROM_CTRL_RW) {
1678c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_EEPROM_CTRL, &data);
1688c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value);
1698c2ecf20Sopenharmony_ci		data = data & 0xFFFF;
1708c2ecf20Sopenharmony_ci		*p_value = swab32((data << 16) | (*p_value >> 16));
1718c2ecf20Sopenharmony_ci		ret = true;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	if (!(otp_ctrl_data & OTP_CTRL_CLK_EN))
1748c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return ret;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci/*
1798c2ecf20Sopenharmony_ci * Reads the adapter's MAC address from the EEPROM
1808c2ecf20Sopenharmony_ci *
1818c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
1828c2ecf20Sopenharmony_ci */
1838c2ecf20Sopenharmony_ciint atl1c_read_mac_addr(struct atl1c_hw *hw)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	int err = 0;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	err = atl1c_get_permanent_address(hw);
1888c2ecf20Sopenharmony_ci	if (err)
1898c2ecf20Sopenharmony_ci		eth_random_addr(hw->perm_mac_addr);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr));
1928c2ecf20Sopenharmony_ci	return err;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/*
1968c2ecf20Sopenharmony_ci * atl1c_hash_mc_addr
1978c2ecf20Sopenharmony_ci *  purpose
1988c2ecf20Sopenharmony_ci *      set hash value for a multicast address
1998c2ecf20Sopenharmony_ci *      hash calcu processing :
2008c2ecf20Sopenharmony_ci *          1. calcu 32bit CRC for multicast address
2018c2ecf20Sopenharmony_ci *          2. reverse crc with MSB to LSB
2028c2ecf20Sopenharmony_ci */
2038c2ecf20Sopenharmony_ciu32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr)
2048c2ecf20Sopenharmony_ci{
2058c2ecf20Sopenharmony_ci	u32 crc32;
2068c2ecf20Sopenharmony_ci	u32 value = 0;
2078c2ecf20Sopenharmony_ci	int i;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	crc32 = ether_crc_le(6, mc_addr);
2108c2ecf20Sopenharmony_ci	for (i = 0; i < 32; i++)
2118c2ecf20Sopenharmony_ci		value |= (((crc32 >> i) & 1) << (31 - i));
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	return value;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/*
2178c2ecf20Sopenharmony_ci * Sets the bit in the multicast table corresponding to the hash value.
2188c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
2198c2ecf20Sopenharmony_ci * hash_value - Multicast address hash value
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_civoid atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	u32 hash_bit, hash_reg;
2248c2ecf20Sopenharmony_ci	u32 mta;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/*
2278c2ecf20Sopenharmony_ci	 * The HASH Table  is a register array of 2 32-bit registers.
2288c2ecf20Sopenharmony_ci	 * It is treated like an array of 64 bits.  We want to set
2298c2ecf20Sopenharmony_ci	 * bit BitArray[hash_value]. So we figure out what register
2308c2ecf20Sopenharmony_ci	 * the bit is in, read it, OR in the new bit, then write
2318c2ecf20Sopenharmony_ci	 * back the new value.  The register is determined by the
2328c2ecf20Sopenharmony_ci	 * upper bit of the hash value and the bit within that
2338c2ecf20Sopenharmony_ci	 * register are determined by the lower 5 bits of the value.
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	hash_reg = (hash_value >> 31) & 0x1;
2368c2ecf20Sopenharmony_ci	hash_bit = (hash_value >> 26) & 0x1F;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	mta |= (1 << hash_bit);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta);
2438c2ecf20Sopenharmony_ci}
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/*
2468c2ecf20Sopenharmony_ci * wait mdio module be idle
2478c2ecf20Sopenharmony_ci * return true: idle
2488c2ecf20Sopenharmony_ci *        false: still busy
2498c2ecf20Sopenharmony_ci */
2508c2ecf20Sopenharmony_cibool atl1c_wait_mdio_idle(struct atl1c_hw *hw)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	u32 val;
2538c2ecf20Sopenharmony_ci	int i;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	for (i = 0; i < MDIO_MAX_AC_TO; i++) {
2568c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_MDIO_CTRL, &val);
2578c2ecf20Sopenharmony_ci		if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START)))
2588c2ecf20Sopenharmony_ci			break;
2598c2ecf20Sopenharmony_ci		udelay(10);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	return i != MDIO_MAX_AC_TO;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_civoid atl1c_stop_phy_polling(struct atl1c_hw *hw)
2668c2ecf20Sopenharmony_ci{
2678c2ecf20Sopenharmony_ci	if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
2688c2ecf20Sopenharmony_ci		return;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MDIO_CTRL, 0);
2718c2ecf20Sopenharmony_ci	atl1c_wait_mdio_idle(hw);
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_civoid atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	u32 val;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION))
2798c2ecf20Sopenharmony_ci		return;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	val = MDIO_CTRL_SPRES_PRMBL |
2828c2ecf20Sopenharmony_ci		FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
2838c2ecf20Sopenharmony_ci		FIELDX(MDIO_CTRL_REG, 1) |
2848c2ecf20Sopenharmony_ci		MDIO_CTRL_START |
2858c2ecf20Sopenharmony_ci		MDIO_CTRL_OP_READ;
2868c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
2878c2ecf20Sopenharmony_ci	atl1c_wait_mdio_idle(hw);
2888c2ecf20Sopenharmony_ci	val |= MDIO_CTRL_AP_EN;
2898c2ecf20Sopenharmony_ci	val &= ~MDIO_CTRL_START;
2908c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
2918c2ecf20Sopenharmony_ci	udelay(30);
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci/*
2968c2ecf20Sopenharmony_ci * atl1c_read_phy_core
2978c2ecf20Sopenharmony_ci * core function to read register in PHY via MDIO control register.
2988c2ecf20Sopenharmony_ci * ext: extension register (see IEEE 802.3)
2998c2ecf20Sopenharmony_ci * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
3008c2ecf20Sopenharmony_ci * reg: reg to read
3018c2ecf20Sopenharmony_ci */
3028c2ecf20Sopenharmony_ciint atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
3038c2ecf20Sopenharmony_ci			u16 reg, u16 *phy_data)
3048c2ecf20Sopenharmony_ci{
3058c2ecf20Sopenharmony_ci	u32 val;
3068c2ecf20Sopenharmony_ci	u16 clk_sel = MDIO_CTRL_CLK_25_4;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	atl1c_stop_phy_polling(hw);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	*phy_data = 0;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* only l2c_b2 & l1d_2 could use slow clock */
3138c2ecf20Sopenharmony_ci	if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
3148c2ecf20Sopenharmony_ci		hw->hibernate)
3158c2ecf20Sopenharmony_ci		clk_sel = MDIO_CTRL_CLK_25_128;
3168c2ecf20Sopenharmony_ci	if (ext) {
3178c2ecf20Sopenharmony_ci		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
3188c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
3198c2ecf20Sopenharmony_ci		val = MDIO_CTRL_SPRES_PRMBL |
3208c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
3218c2ecf20Sopenharmony_ci			MDIO_CTRL_START |
3228c2ecf20Sopenharmony_ci			MDIO_CTRL_MODE_EXT |
3238c2ecf20Sopenharmony_ci			MDIO_CTRL_OP_READ;
3248c2ecf20Sopenharmony_ci	} else {
3258c2ecf20Sopenharmony_ci		val = MDIO_CTRL_SPRES_PRMBL |
3268c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
3278c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_REG, reg) |
3288c2ecf20Sopenharmony_ci			MDIO_CTRL_START |
3298c2ecf20Sopenharmony_ci			MDIO_CTRL_OP_READ;
3308c2ecf20Sopenharmony_ci	}
3318c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (!atl1c_wait_mdio_idle(hw))
3348c2ecf20Sopenharmony_ci		return -1;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MDIO_CTRL, &val);
3378c2ecf20Sopenharmony_ci	*phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	atl1c_start_phy_polling(hw, clk_sel);
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci/*
3458c2ecf20Sopenharmony_ci * atl1c_write_phy_core
3468c2ecf20Sopenharmony_ci * core function to write to register in PHY via MDIO control register.
3478c2ecf20Sopenharmony_ci * ext: extension register (see IEEE 802.3)
3488c2ecf20Sopenharmony_ci * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0)
3498c2ecf20Sopenharmony_ci * reg: reg to write
3508c2ecf20Sopenharmony_ci */
3518c2ecf20Sopenharmony_ciint atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev,
3528c2ecf20Sopenharmony_ci			u16 reg, u16 phy_data)
3538c2ecf20Sopenharmony_ci{
3548c2ecf20Sopenharmony_ci	u32 val;
3558c2ecf20Sopenharmony_ci	u16 clk_sel = MDIO_CTRL_CLK_25_4;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	atl1c_stop_phy_polling(hw);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	/* only l2c_b2 & l1d_2 could use slow clock */
3618c2ecf20Sopenharmony_ci	if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) &&
3628c2ecf20Sopenharmony_ci		hw->hibernate)
3638c2ecf20Sopenharmony_ci		clk_sel = MDIO_CTRL_CLK_25_128;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (ext) {
3668c2ecf20Sopenharmony_ci		val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg);
3678c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_MDIO_EXTN, val);
3688c2ecf20Sopenharmony_ci		val = MDIO_CTRL_SPRES_PRMBL |
3698c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
3708c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_DATA, phy_data) |
3718c2ecf20Sopenharmony_ci			MDIO_CTRL_START |
3728c2ecf20Sopenharmony_ci			MDIO_CTRL_MODE_EXT;
3738c2ecf20Sopenharmony_ci	} else {
3748c2ecf20Sopenharmony_ci		val = MDIO_CTRL_SPRES_PRMBL |
3758c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) |
3768c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_DATA, phy_data) |
3778c2ecf20Sopenharmony_ci			FIELDX(MDIO_CTRL_REG, reg) |
3788c2ecf20Sopenharmony_ci			MDIO_CTRL_START;
3798c2ecf20Sopenharmony_ci	}
3808c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MDIO_CTRL, val);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	if (!atl1c_wait_mdio_idle(hw))
3838c2ecf20Sopenharmony_ci		return -1;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	atl1c_start_phy_polling(hw, clk_sel);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	return 0;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci/*
3918c2ecf20Sopenharmony_ci * Reads the value from a PHY register
3928c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
3938c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to read
3948c2ecf20Sopenharmony_ci */
3958c2ecf20Sopenharmony_ciint atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci/*
4018c2ecf20Sopenharmony_ci * Writes a value to a PHY register
4028c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
4038c2ecf20Sopenharmony_ci * reg_addr - address of the PHY register to write
4048c2ecf20Sopenharmony_ci * data - data to write to the PHY
4058c2ecf20Sopenharmony_ci */
4068c2ecf20Sopenharmony_ciint atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data);
4098c2ecf20Sopenharmony_ci}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci/* read from PHY extension register */
4128c2ecf20Sopenharmony_ciint atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
4138c2ecf20Sopenharmony_ci			u16 reg_addr, u16 *phy_data)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data);
4168c2ecf20Sopenharmony_ci}
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci/* write to PHY extension register */
4198c2ecf20Sopenharmony_ciint atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr,
4208c2ecf20Sopenharmony_ci			u16 reg_addr, u16 phy_data)
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data);
4238c2ecf20Sopenharmony_ci}
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ciint atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data)
4268c2ecf20Sopenharmony_ci{
4278c2ecf20Sopenharmony_ci	int err;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
4308c2ecf20Sopenharmony_ci	if (unlikely(err))
4318c2ecf20Sopenharmony_ci		return err;
4328c2ecf20Sopenharmony_ci	else
4338c2ecf20Sopenharmony_ci		err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return err;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ciint atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	int err;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr);
4438c2ecf20Sopenharmony_ci	if (unlikely(err))
4448c2ecf20Sopenharmony_ci		return err;
4458c2ecf20Sopenharmony_ci	else
4468c2ecf20Sopenharmony_ci		err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return err;
4498c2ecf20Sopenharmony_ci}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/*
4528c2ecf20Sopenharmony_ci * Configures PHY autoneg and flow control advertisement settings
4538c2ecf20Sopenharmony_ci *
4548c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
4558c2ecf20Sopenharmony_ci */
4568c2ecf20Sopenharmony_cistatic int atl1c_phy_setup_adv(struct atl1c_hw *hw)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL;
4598c2ecf20Sopenharmony_ci	u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP &
4608c2ecf20Sopenharmony_ci				~GIGA_CR_1000T_SPEED_MASK;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (hw->autoneg_advertised & ADVERTISED_10baseT_Half)
4638c2ecf20Sopenharmony_ci		mii_adv_data |= ADVERTISE_10HALF;
4648c2ecf20Sopenharmony_ci	if (hw->autoneg_advertised & ADVERTISED_10baseT_Full)
4658c2ecf20Sopenharmony_ci		mii_adv_data |= ADVERTISE_10FULL;
4668c2ecf20Sopenharmony_ci	if (hw->autoneg_advertised & ADVERTISED_100baseT_Half)
4678c2ecf20Sopenharmony_ci		mii_adv_data |= ADVERTISE_100HALF;
4688c2ecf20Sopenharmony_ci	if (hw->autoneg_advertised & ADVERTISED_100baseT_Full)
4698c2ecf20Sopenharmony_ci		mii_adv_data |= ADVERTISE_100FULL;
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (hw->autoneg_advertised & ADVERTISED_Autoneg)
4728c2ecf20Sopenharmony_ci		mii_adv_data |= ADVERTISE_10HALF  | ADVERTISE_10FULL |
4738c2ecf20Sopenharmony_ci				ADVERTISE_100HALF | ADVERTISE_100FULL;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) {
4768c2ecf20Sopenharmony_ci		if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half)
4778c2ecf20Sopenharmony_ci			mii_giga_ctrl_data |= ADVERTISE_1000HALF;
4788c2ecf20Sopenharmony_ci		if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full)
4798c2ecf20Sopenharmony_ci			mii_giga_ctrl_data |= ADVERTISE_1000FULL;
4808c2ecf20Sopenharmony_ci		if (hw->autoneg_advertised & ADVERTISED_Autoneg)
4818c2ecf20Sopenharmony_ci			mii_giga_ctrl_data |= ADVERTISE_1000HALF |
4828c2ecf20Sopenharmony_ci					ADVERTISE_1000FULL;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 ||
4868c2ecf20Sopenharmony_ci	    atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0)
4878c2ecf20Sopenharmony_ci		return -1;
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_civoid atl1c_phy_disable(struct atl1c_hw *hw)
4928c2ecf20Sopenharmony_ci{
4938c2ecf20Sopenharmony_ci	atl1c_power_saving(hw, 0);
4948c2ecf20Sopenharmony_ci}
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ciint atl1c_phy_reset(struct atl1c_hw *hw)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = hw->adapter;
5008c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
5018c2ecf20Sopenharmony_ci	u16 phy_data;
5028c2ecf20Sopenharmony_ci	u32 phy_ctrl_data, lpi_ctrl;
5038c2ecf20Sopenharmony_ci	int err;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* reset PHY core */
5068c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data);
5078c2ecf20Sopenharmony_ci	phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ |
5088c2ecf20Sopenharmony_ci		GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS);
5098c2ecf20Sopenharmony_ci	phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST;
5108c2ecf20Sopenharmony_ci	if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE))
5118c2ecf20Sopenharmony_ci		phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
5128c2ecf20Sopenharmony_ci	else
5138c2ecf20Sopenharmony_ci		phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE);
5148c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data);
5158c2ecf20Sopenharmony_ci	AT_WRITE_FLUSH(hw);
5168c2ecf20Sopenharmony_ci	udelay(10);
5178c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET);
5188c2ecf20Sopenharmony_ci	AT_WRITE_FLUSH(hw);
5198c2ecf20Sopenharmony_ci	udelay(10 * GPHY_CTRL_EXT_RST_TO);	/* delay 800us */
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	/* switch clock */
5228c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b) {
5238c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data);
5248c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD,
5258c2ecf20Sopenharmony_ci			phy_data & ~CFGLPSPD_RSTCNT_CLK125SW);
5268c2ecf20Sopenharmony_ci	}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	/* tx-half amplitude issue fix */
5298c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
5308c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data);
5318c2ecf20Sopenharmony_ci		phy_data |= CABLE1TH_DET_EN;
5328c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data);
5338c2ecf20Sopenharmony_ci	}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	/* clear bit3 of dbgport 3B to lower voltage */
5368c2ecf20Sopenharmony_ci	if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) {
5378c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) {
5388c2ecf20Sopenharmony_ci			atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data);
5398c2ecf20Sopenharmony_ci			phy_data &= ~VOLT_CTRL_SWLOWEST;
5408c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data);
5418c2ecf20Sopenharmony_ci		}
5428c2ecf20Sopenharmony_ci		/* power saving config */
5438c2ecf20Sopenharmony_ci		phy_data =
5448c2ecf20Sopenharmony_ci			hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ?
5458c2ecf20Sopenharmony_ci			L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF;
5468c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data);
5478c2ecf20Sopenharmony_ci		/* hib */
5488c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
5498c2ecf20Sopenharmony_ci			SYSMODCTRL_IECHOADJ_DEF);
5508c2ecf20Sopenharmony_ci	} else {
5518c2ecf20Sopenharmony_ci		/* disable pws */
5528c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data);
5538c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS,
5548c2ecf20Sopenharmony_ci			phy_data & ~LEGCYPS_EN);
5558c2ecf20Sopenharmony_ci		/* disable hibernate */
5568c2ecf20Sopenharmony_ci		atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data);
5578c2ecf20Sopenharmony_ci		atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG,
5588c2ecf20Sopenharmony_ci			phy_data & HIBNEG_PSHIB_EN);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	/* disable AZ(EEE) by default */
5618c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ||
5628c2ecf20Sopenharmony_ci	    hw->nic_type == athr_l2c_b2) {
5638c2ecf20Sopenharmony_ci		AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl);
5648c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN);
5658c2ecf20Sopenharmony_ci		atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0);
5668c2ecf20Sopenharmony_ci		atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3,
5678c2ecf20Sopenharmony_ci			L2CB_CLDCTRL3);
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* other debug port to set */
5718c2ecf20Sopenharmony_ci	atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF);
5728c2ecf20Sopenharmony_ci	atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF);
5738c2ecf20Sopenharmony_ci	atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF);
5748c2ecf20Sopenharmony_ci	/* UNH-IOL test issue, set bit7 */
5758c2ecf20Sopenharmony_ci	atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG,
5768c2ecf20Sopenharmony_ci		TST100BTCFG_DEF | TST100BTCFG_LITCH_EN);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	/* set phy interrupt mask */
5798c2ecf20Sopenharmony_ci	phy_data = IER_LINK_UP | IER_LINK_DOWN;
5808c2ecf20Sopenharmony_ci	err = atl1c_write_phy_reg(hw, MII_IER, phy_data);
5818c2ecf20Sopenharmony_ci	if (err) {
5828c2ecf20Sopenharmony_ci		if (netif_msg_hw(adapter))
5838c2ecf20Sopenharmony_ci			dev_err(&pdev->dev,
5848c2ecf20Sopenharmony_ci				"Error enable PHY linkChange Interrupt\n");
5858c2ecf20Sopenharmony_ci		return err;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci	return 0;
5888c2ecf20Sopenharmony_ci}
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ciint atl1c_phy_init(struct atl1c_hw *hw)
5918c2ecf20Sopenharmony_ci{
5928c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = hw->adapter;
5938c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
5948c2ecf20Sopenharmony_ci	int ret_val;
5958c2ecf20Sopenharmony_ci	u16 mii_bmcr_data = BMCR_RESET;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) ||
5988c2ecf20Sopenharmony_ci		(atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) {
5998c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Error get phy ID\n");
6008c2ecf20Sopenharmony_ci		return -1;
6018c2ecf20Sopenharmony_ci	}
6028c2ecf20Sopenharmony_ci	switch (hw->media_type) {
6038c2ecf20Sopenharmony_ci	case MEDIA_TYPE_AUTO_SENSOR:
6048c2ecf20Sopenharmony_ci		ret_val = atl1c_phy_setup_adv(hw);
6058c2ecf20Sopenharmony_ci		if (ret_val) {
6068c2ecf20Sopenharmony_ci			if (netif_msg_link(adapter))
6078c2ecf20Sopenharmony_ci				dev_err(&pdev->dev,
6088c2ecf20Sopenharmony_ci					"Error Setting up Auto-Negotiation\n");
6098c2ecf20Sopenharmony_ci			return ret_val;
6108c2ecf20Sopenharmony_ci		}
6118c2ecf20Sopenharmony_ci		mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
6128c2ecf20Sopenharmony_ci		break;
6138c2ecf20Sopenharmony_ci	case MEDIA_TYPE_100M_FULL:
6148c2ecf20Sopenharmony_ci		mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX;
6158c2ecf20Sopenharmony_ci		break;
6168c2ecf20Sopenharmony_ci	case MEDIA_TYPE_100M_HALF:
6178c2ecf20Sopenharmony_ci		mii_bmcr_data |= BMCR_SPEED100;
6188c2ecf20Sopenharmony_ci		break;
6198c2ecf20Sopenharmony_ci	case MEDIA_TYPE_10M_FULL:
6208c2ecf20Sopenharmony_ci		mii_bmcr_data |= BMCR_FULLDPLX;
6218c2ecf20Sopenharmony_ci		break;
6228c2ecf20Sopenharmony_ci	case MEDIA_TYPE_10M_HALF:
6238c2ecf20Sopenharmony_ci		break;
6248c2ecf20Sopenharmony_ci	default:
6258c2ecf20Sopenharmony_ci		if (netif_msg_link(adapter))
6268c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "Wrong Media type %d\n",
6278c2ecf20Sopenharmony_ci				hw->media_type);
6288c2ecf20Sopenharmony_ci		return -1;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
6328c2ecf20Sopenharmony_ci	if (ret_val)
6338c2ecf20Sopenharmony_ci		return ret_val;
6348c2ecf20Sopenharmony_ci	hw->phy_configured = true;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	return 0;
6378c2ecf20Sopenharmony_ci}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci/*
6408c2ecf20Sopenharmony_ci * Detects the current speed and duplex settings of the hardware.
6418c2ecf20Sopenharmony_ci *
6428c2ecf20Sopenharmony_ci * hw - Struct containing variables accessed by shared code
6438c2ecf20Sopenharmony_ci * speed - Speed of the connection
6448c2ecf20Sopenharmony_ci * duplex - Duplex setting of the connection
6458c2ecf20Sopenharmony_ci */
6468c2ecf20Sopenharmony_ciint atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	int err;
6498c2ecf20Sopenharmony_ci	u16 phy_data;
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	/* Read   PHY Specific Status Register (17) */
6528c2ecf20Sopenharmony_ci	err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data);
6538c2ecf20Sopenharmony_ci	if (err)
6548c2ecf20Sopenharmony_ci		return err;
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED))
6578c2ecf20Sopenharmony_ci		return -1;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	switch (phy_data & GIGA_PSSR_SPEED) {
6608c2ecf20Sopenharmony_ci	case GIGA_PSSR_1000MBS:
6618c2ecf20Sopenharmony_ci		*speed = SPEED_1000;
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case GIGA_PSSR_100MBS:
6648c2ecf20Sopenharmony_ci		*speed = SPEED_100;
6658c2ecf20Sopenharmony_ci		break;
6668c2ecf20Sopenharmony_ci	case  GIGA_PSSR_10MBS:
6678c2ecf20Sopenharmony_ci		*speed = SPEED_10;
6688c2ecf20Sopenharmony_ci		break;
6698c2ecf20Sopenharmony_ci	default:
6708c2ecf20Sopenharmony_ci		return -1;
6718c2ecf20Sopenharmony_ci	}
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	if (phy_data & GIGA_PSSR_DPLX)
6748c2ecf20Sopenharmony_ci		*duplex = FULL_DUPLEX;
6758c2ecf20Sopenharmony_ci	else
6768c2ecf20Sopenharmony_ci		*duplex = HALF_DUPLEX;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	return 0;
6798c2ecf20Sopenharmony_ci}
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci/* select one link mode to get lower power consumption */
6828c2ecf20Sopenharmony_ciint atl1c_phy_to_ps_link(struct atl1c_hw *hw)
6838c2ecf20Sopenharmony_ci{
6848c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = hw->adapter;
6858c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
6868c2ecf20Sopenharmony_ci	int ret = 0;
6878c2ecf20Sopenharmony_ci	u16 autoneg_advertised = ADVERTISED_10baseT_Half;
6888c2ecf20Sopenharmony_ci	u16 save_autoneg_advertised;
6898c2ecf20Sopenharmony_ci	u16 phy_data;
6908c2ecf20Sopenharmony_ci	u16 mii_lpa_data;
6918c2ecf20Sopenharmony_ci	u16 speed = SPEED_0;
6928c2ecf20Sopenharmony_ci	u16 duplex = FULL_DUPLEX;
6938c2ecf20Sopenharmony_ci	int i;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
6968c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
6978c2ecf20Sopenharmony_ci	if (phy_data & BMSR_LSTATUS) {
6988c2ecf20Sopenharmony_ci		atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data);
6998c2ecf20Sopenharmony_ci		if (mii_lpa_data & LPA_10FULL)
7008c2ecf20Sopenharmony_ci			autoneg_advertised = ADVERTISED_10baseT_Full;
7018c2ecf20Sopenharmony_ci		else if (mii_lpa_data & LPA_10HALF)
7028c2ecf20Sopenharmony_ci			autoneg_advertised = ADVERTISED_10baseT_Half;
7038c2ecf20Sopenharmony_ci		else if (mii_lpa_data & LPA_100HALF)
7048c2ecf20Sopenharmony_ci			autoneg_advertised = ADVERTISED_100baseT_Half;
7058c2ecf20Sopenharmony_ci		else if (mii_lpa_data & LPA_100FULL)
7068c2ecf20Sopenharmony_ci			autoneg_advertised = ADVERTISED_100baseT_Full;
7078c2ecf20Sopenharmony_ci
7088c2ecf20Sopenharmony_ci		save_autoneg_advertised = hw->autoneg_advertised;
7098c2ecf20Sopenharmony_ci		hw->phy_configured = false;
7108c2ecf20Sopenharmony_ci		hw->autoneg_advertised = autoneg_advertised;
7118c2ecf20Sopenharmony_ci		if (atl1c_restart_autoneg(hw) != 0) {
7128c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "phy autoneg failed\n");
7138c2ecf20Sopenharmony_ci			ret = -1;
7148c2ecf20Sopenharmony_ci		}
7158c2ecf20Sopenharmony_ci		hw->autoneg_advertised = save_autoneg_advertised;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (mii_lpa_data) {
7188c2ecf20Sopenharmony_ci			for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) {
7198c2ecf20Sopenharmony_ci				mdelay(100);
7208c2ecf20Sopenharmony_ci				atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
7218c2ecf20Sopenharmony_ci				atl1c_read_phy_reg(hw, MII_BMSR, &phy_data);
7228c2ecf20Sopenharmony_ci				if (phy_data & BMSR_LSTATUS) {
7238c2ecf20Sopenharmony_ci					if (atl1c_get_speed_and_duplex(hw, &speed,
7248c2ecf20Sopenharmony_ci									&duplex) != 0)
7258c2ecf20Sopenharmony_ci						dev_dbg(&pdev->dev,
7268c2ecf20Sopenharmony_ci							"get speed and duplex failed\n");
7278c2ecf20Sopenharmony_ci					break;
7288c2ecf20Sopenharmony_ci				}
7298c2ecf20Sopenharmony_ci			}
7308c2ecf20Sopenharmony_ci		}
7318c2ecf20Sopenharmony_ci	} else {
7328c2ecf20Sopenharmony_ci		speed = SPEED_10;
7338c2ecf20Sopenharmony_ci		duplex = HALF_DUPLEX;
7348c2ecf20Sopenharmony_ci	}
7358c2ecf20Sopenharmony_ci	adapter->link_speed = speed;
7368c2ecf20Sopenharmony_ci	adapter->link_duplex = duplex;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci	return ret;
7398c2ecf20Sopenharmony_ci}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ciint atl1c_restart_autoneg(struct atl1c_hw *hw)
7428c2ecf20Sopenharmony_ci{
7438c2ecf20Sopenharmony_ci	int err = 0;
7448c2ecf20Sopenharmony_ci	u16 mii_bmcr_data = BMCR_RESET;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	err = atl1c_phy_setup_adv(hw);
7478c2ecf20Sopenharmony_ci	if (err)
7488c2ecf20Sopenharmony_ci		return err;
7498c2ecf20Sopenharmony_ci	mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data);
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ciint atl1c_power_saving(struct atl1c_hw *hw, u32 wufc)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	struct atl1c_adapter *adapter = hw->adapter;
7578c2ecf20Sopenharmony_ci	struct pci_dev *pdev = adapter->pdev;
7588c2ecf20Sopenharmony_ci	u32 master_ctrl, mac_ctrl, phy_ctrl;
7598c2ecf20Sopenharmony_ci	u32 wol_ctrl, speed;
7608c2ecf20Sopenharmony_ci	u16 phy_data;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	wol_ctrl = 0;
7638c2ecf20Sopenharmony_ci	speed = adapter->link_speed == SPEED_1000 ?
7648c2ecf20Sopenharmony_ci		MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100;
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl);
7678c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl);
7688c2ecf20Sopenharmony_ci	AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS;
7718c2ecf20Sopenharmony_ci	mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed);
7728c2ecf20Sopenharmony_ci	mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN);
7738c2ecf20Sopenharmony_ci	if (adapter->link_duplex == FULL_DUPLEX)
7748c2ecf20Sopenharmony_ci		mac_ctrl |= MAC_CTRL_DUPLX;
7758c2ecf20Sopenharmony_ci	phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS);
7768c2ecf20Sopenharmony_ci	phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE |
7778c2ecf20Sopenharmony_ci		GPHY_CTRL_HIB_EN;
7788c2ecf20Sopenharmony_ci	if (!wufc) { /* without WoL */
7798c2ecf20Sopenharmony_ci		master_ctrl |= MASTER_CTRL_CLK_SEL_DIS;
7808c2ecf20Sopenharmony_ci		phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW;
7818c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
7828c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
7838c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
7848c2ecf20Sopenharmony_ci		AT_WRITE_REG(hw, REG_WOL_CTRL, 0);
7858c2ecf20Sopenharmony_ci		hw->phy_configured = false; /* re-init PHY when resume */
7868c2ecf20Sopenharmony_ci		return 0;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci	phy_ctrl |= GPHY_CTRL_EXT_RESET;
7898c2ecf20Sopenharmony_ci	if (wufc & AT_WUFC_MAG) {
7908c2ecf20Sopenharmony_ci		mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN;
7918c2ecf20Sopenharmony_ci		wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN;
7928c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11)
7938c2ecf20Sopenharmony_ci			wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci	if (wufc & AT_WUFC_LNKC) {
7968c2ecf20Sopenharmony_ci		wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN;
7978c2ecf20Sopenharmony_ci		if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) {
7988c2ecf20Sopenharmony_ci			dev_dbg(&pdev->dev, "%s: write phy MII_IER failed.\n",
7998c2ecf20Sopenharmony_ci				atl1c_driver_name);
8008c2ecf20Sopenharmony_ci		}
8018c2ecf20Sopenharmony_ci	}
8028c2ecf20Sopenharmony_ci	/* clear PHY interrupt */
8038c2ecf20Sopenharmony_ci	atl1c_read_phy_reg(hw, MII_ISR, &phy_data);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n",
8068c2ecf20Sopenharmony_ci		atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl);
8078c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl);
8088c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl);
8098c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl);
8108c2ecf20Sopenharmony_ci	AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	return 0;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci/* configure phy after Link change Event */
8178c2ecf20Sopenharmony_civoid atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed)
8188c2ecf20Sopenharmony_ci{
8198c2ecf20Sopenharmony_ci	u16 phy_val;
8208c2ecf20Sopenharmony_ci	bool adj_thresh = false;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 ||
8238c2ecf20Sopenharmony_ci	    hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2)
8248c2ecf20Sopenharmony_ci		adj_thresh = true;
8258c2ecf20Sopenharmony_ci
8268c2ecf20Sopenharmony_ci	if (link_speed != SPEED_0) { /* link up */
8278c2ecf20Sopenharmony_ci		/* az with brcm, half-amp */
8288c2ecf20Sopenharmony_ci		if (hw->nic_type == athr_l1d_2) {
8298c2ecf20Sopenharmony_ci			atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6,
8308c2ecf20Sopenharmony_ci				&phy_val);
8318c2ecf20Sopenharmony_ci			phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN);
8328c2ecf20Sopenharmony_ci			phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ?
8338c2ecf20Sopenharmony_ci				AZ_ANADECT_LONG : AZ_ANADECT_DEF;
8348c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val);
8358c2ecf20Sopenharmony_ci		}
8368c2ecf20Sopenharmony_ci		/* threshold adjust */
8378c2ecf20Sopenharmony_ci		if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) {
8388c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP);
8398c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
8408c2ecf20Sopenharmony_ci				L1D_SYSMODCTRL_IECHOADJ_DEF);
8418c2ecf20Sopenharmony_ci		}
8428c2ecf20Sopenharmony_ci	} else { /* link down */
8438c2ecf20Sopenharmony_ci		if (adj_thresh && hw->msi_lnkpatch) {
8448c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL,
8458c2ecf20Sopenharmony_ci				SYSMODCTRL_IECHOADJ_DEF);
8468c2ecf20Sopenharmony_ci			atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB,
8478c2ecf20Sopenharmony_ci				L1D_MSE16DB_DOWN);
8488c2ecf20Sopenharmony_ci		}
8498c2ecf20Sopenharmony_ci	}
8508c2ecf20Sopenharmony_ci}
851