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 *) ð_addr[2] = htonl(addr[0]); 618c2ecf20Sopenharmony_ci *(u16 *) ð_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