162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2007 Atheros Corporation. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Derived from Intel e1000 driver 662306a36Sopenharmony_ci * Copyright(c) 1999 - 2005 Intel Corporation. All rights reserved. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/pci.h> 962306a36Sopenharmony_ci#include <linux/delay.h> 1062306a36Sopenharmony_ci#include <linux/mii.h> 1162306a36Sopenharmony_ci#include <linux/crc32.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "atl1c.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci/* 1662306a36Sopenharmony_ci * check_eeprom_exist 1762306a36Sopenharmony_ci * return 1 if eeprom exist 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ciint atl1c_check_eeprom_exist(struct atl1c_hw *hw) 2062306a36Sopenharmony_ci{ 2162306a36Sopenharmony_ci u32 data; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci AT_READ_REG(hw, REG_TWSI_DEBUG, &data); 2462306a36Sopenharmony_ci if (data & TWSI_DEBUG_DEV_EXIST) 2562306a36Sopenharmony_ci return 1; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci AT_READ_REG(hw, REG_MASTER_CTRL, &data); 2862306a36Sopenharmony_ci if (data & MASTER_CTRL_OTP_SEL) 2962306a36Sopenharmony_ci return 1; 3062306a36Sopenharmony_ci return 0; 3162306a36Sopenharmony_ci} 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civoid atl1c_hw_set_mac_addr(struct atl1c_hw *hw, u8 *mac_addr) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci u32 value; 3662306a36Sopenharmony_ci /* 3762306a36Sopenharmony_ci * 00-0B-6A-F6-00-DC 3862306a36Sopenharmony_ci * 0: 6AF600DC 1: 000B 3962306a36Sopenharmony_ci * low dword 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_ci value = mac_addr[2] << 24 | 4262306a36Sopenharmony_ci mac_addr[3] << 16 | 4362306a36Sopenharmony_ci mac_addr[4] << 8 | 4462306a36Sopenharmony_ci mac_addr[5]; 4562306a36Sopenharmony_ci AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 0, value); 4662306a36Sopenharmony_ci /* hight dword */ 4762306a36Sopenharmony_ci value = mac_addr[0] << 8 | 4862306a36Sopenharmony_ci mac_addr[1]; 4962306a36Sopenharmony_ci AT_WRITE_REG_ARRAY(hw, REG_MAC_STA_ADDR, 1, value); 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* read mac address from hardware register */ 5362306a36Sopenharmony_cistatic bool atl1c_read_current_addr(struct atl1c_hw *hw, u8 *eth_addr) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci u32 addr[2]; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci AT_READ_REG(hw, REG_MAC_STA_ADDR, &addr[0]); 5862306a36Sopenharmony_ci AT_READ_REG(hw, REG_MAC_STA_ADDR + 4, &addr[1]); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci *(u32 *) ð_addr[2] = htonl(addr[0]); 6162306a36Sopenharmony_ci *(u16 *) ð_addr[0] = htons((u16)addr[1]); 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci return is_valid_ether_addr(eth_addr); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* 6762306a36Sopenharmony_ci * atl1c_get_permanent_address 6862306a36Sopenharmony_ci * return 0 if get valid mac address, 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic int atl1c_get_permanent_address(struct atl1c_hw *hw) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u32 i; 7362306a36Sopenharmony_ci u32 otp_ctrl_data; 7462306a36Sopenharmony_ci u32 twsi_ctrl_data; 7562306a36Sopenharmony_ci u16 phy_data; 7662306a36Sopenharmony_ci bool raise_vol = false; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci /* MAC-address from BIOS is the 1st priority */ 7962306a36Sopenharmony_ci if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* init */ 8362306a36Sopenharmony_ci AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); 8462306a36Sopenharmony_ci if (atl1c_check_eeprom_exist(hw)) { 8562306a36Sopenharmony_ci if (hw->nic_type == athr_l1c || hw->nic_type == athr_l2c) { 8662306a36Sopenharmony_ci /* Enable OTP CLK */ 8762306a36Sopenharmony_ci if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) { 8862306a36Sopenharmony_ci otp_ctrl_data |= OTP_CTRL_CLK_EN; 8962306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); 9062306a36Sopenharmony_ci AT_WRITE_FLUSH(hw); 9162306a36Sopenharmony_ci msleep(1); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci /* raise voltage temporally for l2cb */ 9562306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { 9662306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); 9762306a36Sopenharmony_ci phy_data &= ~ANACTRL_HB_EN; 9862306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); 9962306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); 10062306a36Sopenharmony_ci phy_data |= VOLT_CTRL_SWLOWEST; 10162306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); 10262306a36Sopenharmony_ci udelay(20); 10362306a36Sopenharmony_ci raise_vol = true; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); 10762306a36Sopenharmony_ci twsi_ctrl_data |= TWSI_CTRL_SW_LDSTART; 10862306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_TWSI_CTRL, twsi_ctrl_data); 10962306a36Sopenharmony_ci for (i = 0; i < AT_TWSI_EEPROM_TIMEOUT; i++) { 11062306a36Sopenharmony_ci msleep(10); 11162306a36Sopenharmony_ci AT_READ_REG(hw, REG_TWSI_CTRL, &twsi_ctrl_data); 11262306a36Sopenharmony_ci if ((twsi_ctrl_data & TWSI_CTRL_SW_LDSTART) == 0) 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci if (i >= AT_TWSI_EEPROM_TIMEOUT) 11662306a36Sopenharmony_ci return -1; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci /* Disable OTP_CLK */ 11962306a36Sopenharmony_ci if ((hw->nic_type == athr_l1c || hw->nic_type == athr_l2c)) { 12062306a36Sopenharmony_ci otp_ctrl_data &= ~OTP_CTRL_CLK_EN; 12162306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); 12262306a36Sopenharmony_ci msleep(1); 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci if (raise_vol) { 12562306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_ANACTRL, &phy_data); 12662306a36Sopenharmony_ci phy_data |= ANACTRL_HB_EN; 12762306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, phy_data); 12862306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); 12962306a36Sopenharmony_ci phy_data &= ~VOLT_CTRL_SWLOWEST; 13062306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); 13162306a36Sopenharmony_ci udelay(20); 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (atl1c_read_current_addr(hw, hw->perm_mac_addr)) 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return -1; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cibool atl1c_read_eeprom(struct atl1c_hw *hw, u32 offset, u32 *p_value) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int i; 14362306a36Sopenharmony_ci bool ret = false; 14462306a36Sopenharmony_ci u32 otp_ctrl_data; 14562306a36Sopenharmony_ci u32 control; 14662306a36Sopenharmony_ci u32 data; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (offset & 3) 14962306a36Sopenharmony_ci return ret; /* address do not align */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci AT_READ_REG(hw, REG_OTP_CTRL, &otp_ctrl_data); 15262306a36Sopenharmony_ci if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) 15362306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_OTP_CTRL, 15462306a36Sopenharmony_ci (otp_ctrl_data | OTP_CTRL_CLK_EN)); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_EEPROM_DATA_LO, 0); 15762306a36Sopenharmony_ci control = (offset & EEPROM_CTRL_ADDR_MASK) << EEPROM_CTRL_ADDR_SHIFT; 15862306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_EEPROM_CTRL, control); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; i < 10; i++) { 16162306a36Sopenharmony_ci udelay(100); 16262306a36Sopenharmony_ci AT_READ_REG(hw, REG_EEPROM_CTRL, &control); 16362306a36Sopenharmony_ci if (control & EEPROM_CTRL_RW) 16462306a36Sopenharmony_ci break; 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci if (control & EEPROM_CTRL_RW) { 16762306a36Sopenharmony_ci AT_READ_REG(hw, REG_EEPROM_CTRL, &data); 16862306a36Sopenharmony_ci AT_READ_REG(hw, REG_EEPROM_DATA_LO, p_value); 16962306a36Sopenharmony_ci data = data & 0xFFFF; 17062306a36Sopenharmony_ci *p_value = swab32((data << 16) | (*p_value >> 16)); 17162306a36Sopenharmony_ci ret = true; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci if (!(otp_ctrl_data & OTP_CTRL_CLK_EN)) 17462306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_OTP_CTRL, otp_ctrl_data); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return ret; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * Reads the adapter's MAC address from the EEPROM 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ciint atl1c_read_mac_addr(struct atl1c_hw *hw) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci int err = 0; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci err = atl1c_get_permanent_address(hw); 18862306a36Sopenharmony_ci if (err) 18962306a36Sopenharmony_ci eth_random_addr(hw->perm_mac_addr); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci memcpy(hw->mac_addr, hw->perm_mac_addr, sizeof(hw->perm_mac_addr)); 19262306a36Sopenharmony_ci return err; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci/* 19662306a36Sopenharmony_ci * atl1c_hash_mc_addr 19762306a36Sopenharmony_ci * purpose 19862306a36Sopenharmony_ci * set hash value for a multicast address 19962306a36Sopenharmony_ci * hash calcu processing : 20062306a36Sopenharmony_ci * 1. calcu 32bit CRC for multicast address 20162306a36Sopenharmony_ci * 2. reverse crc with MSB to LSB 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ciu32 atl1c_hash_mc_addr(struct atl1c_hw *hw, u8 *mc_addr) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci u32 crc32; 20662306a36Sopenharmony_ci u32 value = 0; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci crc32 = ether_crc_le(6, mc_addr); 21062306a36Sopenharmony_ci for (i = 0; i < 32; i++) 21162306a36Sopenharmony_ci value |= (((crc32 >> i) & 1) << (31 - i)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci return value; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Sets the bit in the multicast table corresponding to the hash value. 21862306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 21962306a36Sopenharmony_ci * hash_value - Multicast address hash value 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_civoid atl1c_hash_set(struct atl1c_hw *hw, u32 hash_value) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci u32 hash_bit, hash_reg; 22462306a36Sopenharmony_ci u32 mta; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci /* 22762306a36Sopenharmony_ci * The HASH Table is a register array of 2 32-bit registers. 22862306a36Sopenharmony_ci * It is treated like an array of 64 bits. We want to set 22962306a36Sopenharmony_ci * bit BitArray[hash_value]. So we figure out what register 23062306a36Sopenharmony_ci * the bit is in, read it, OR in the new bit, then write 23162306a36Sopenharmony_ci * back the new value. The register is determined by the 23262306a36Sopenharmony_ci * upper bit of the hash value and the bit within that 23362306a36Sopenharmony_ci * register are determined by the lower 5 bits of the value. 23462306a36Sopenharmony_ci */ 23562306a36Sopenharmony_ci hash_reg = (hash_value >> 31) & 0x1; 23662306a36Sopenharmony_ci hash_bit = (hash_value >> 26) & 0x1F; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci mta = AT_READ_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg); 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci mta |= (1 << hash_bit); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci AT_WRITE_REG_ARRAY(hw, REG_RX_HASH_TABLE, hash_reg, mta); 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci/* 24662306a36Sopenharmony_ci * wait mdio module be idle 24762306a36Sopenharmony_ci * return true: idle 24862306a36Sopenharmony_ci * false: still busy 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_cibool atl1c_wait_mdio_idle(struct atl1c_hw *hw) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci u32 val; 25362306a36Sopenharmony_ci int i; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci for (i = 0; i < MDIO_MAX_AC_TO; i++) { 25662306a36Sopenharmony_ci AT_READ_REG(hw, REG_MDIO_CTRL, &val); 25762306a36Sopenharmony_ci if (!(val & (MDIO_CTRL_BUSY | MDIO_CTRL_START))) 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci udelay(10); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci return i != MDIO_MAX_AC_TO; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_civoid atl1c_stop_phy_polling(struct atl1c_hw *hw) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) 26862306a36Sopenharmony_ci return; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_CTRL, 0); 27162306a36Sopenharmony_ci atl1c_wait_mdio_idle(hw); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid atl1c_start_phy_polling(struct atl1c_hw *hw, u16 clk_sel) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci u32 val; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (!(hw->ctrl_flags & ATL1C_FPGA_VERSION)) 27962306a36Sopenharmony_ci return; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci val = MDIO_CTRL_SPRES_PRMBL | 28262306a36Sopenharmony_ci FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | 28362306a36Sopenharmony_ci FIELDX(MDIO_CTRL_REG, 1) | 28462306a36Sopenharmony_ci MDIO_CTRL_START | 28562306a36Sopenharmony_ci MDIO_CTRL_OP_READ; 28662306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_CTRL, val); 28762306a36Sopenharmony_ci atl1c_wait_mdio_idle(hw); 28862306a36Sopenharmony_ci val |= MDIO_CTRL_AP_EN; 28962306a36Sopenharmony_ci val &= ~MDIO_CTRL_START; 29062306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_CTRL, val); 29162306a36Sopenharmony_ci udelay(30); 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci/* 29662306a36Sopenharmony_ci * atl1c_read_phy_core 29762306a36Sopenharmony_ci * core function to read register in PHY via MDIO control register. 29862306a36Sopenharmony_ci * ext: extension register (see IEEE 802.3) 29962306a36Sopenharmony_ci * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) 30062306a36Sopenharmony_ci * reg: reg to read 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ciint atl1c_read_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, 30362306a36Sopenharmony_ci u16 reg, u16 *phy_data) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci u32 val; 30662306a36Sopenharmony_ci u16 clk_sel = MDIO_CTRL_CLK_25_4; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci atl1c_stop_phy_polling(hw); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci *phy_data = 0; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* only l2c_b2 & l1d_2 could use slow clock */ 31362306a36Sopenharmony_ci if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && 31462306a36Sopenharmony_ci hw->hibernate) 31562306a36Sopenharmony_ci clk_sel = MDIO_CTRL_CLK_25_128; 31662306a36Sopenharmony_ci if (ext) { 31762306a36Sopenharmony_ci val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); 31862306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_EXTN, val); 31962306a36Sopenharmony_ci val = MDIO_CTRL_SPRES_PRMBL | 32062306a36Sopenharmony_ci FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | 32162306a36Sopenharmony_ci MDIO_CTRL_START | 32262306a36Sopenharmony_ci MDIO_CTRL_MODE_EXT | 32362306a36Sopenharmony_ci MDIO_CTRL_OP_READ; 32462306a36Sopenharmony_ci } else { 32562306a36Sopenharmony_ci val = MDIO_CTRL_SPRES_PRMBL | 32662306a36Sopenharmony_ci FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | 32762306a36Sopenharmony_ci FIELDX(MDIO_CTRL_REG, reg) | 32862306a36Sopenharmony_ci MDIO_CTRL_START | 32962306a36Sopenharmony_ci MDIO_CTRL_OP_READ; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_CTRL, val); 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci if (!atl1c_wait_mdio_idle(hw)) 33462306a36Sopenharmony_ci return -1; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci AT_READ_REG(hw, REG_MDIO_CTRL, &val); 33762306a36Sopenharmony_ci *phy_data = (u16)FIELD_GETX(val, MDIO_CTRL_DATA); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci atl1c_start_phy_polling(hw, clk_sel); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/* 34562306a36Sopenharmony_ci * atl1c_write_phy_core 34662306a36Sopenharmony_ci * core function to write to register in PHY via MDIO control register. 34762306a36Sopenharmony_ci * ext: extension register (see IEEE 802.3) 34862306a36Sopenharmony_ci * dev: device address (see IEEE 802.3 DEVAD, PRTAD is fixed to 0) 34962306a36Sopenharmony_ci * reg: reg to write 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ciint atl1c_write_phy_core(struct atl1c_hw *hw, bool ext, u8 dev, 35262306a36Sopenharmony_ci u16 reg, u16 phy_data) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci u32 val; 35562306a36Sopenharmony_ci u16 clk_sel = MDIO_CTRL_CLK_25_4; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci atl1c_stop_phy_polling(hw); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* only l2c_b2 & l1d_2 could use slow clock */ 36162306a36Sopenharmony_ci if ((hw->nic_type == athr_l2c_b2 || hw->nic_type == athr_l1d_2) && 36262306a36Sopenharmony_ci hw->hibernate) 36362306a36Sopenharmony_ci clk_sel = MDIO_CTRL_CLK_25_128; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (ext) { 36662306a36Sopenharmony_ci val = FIELDX(MDIO_EXTN_DEVAD, dev) | FIELDX(MDIO_EXTN_REG, reg); 36762306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_EXTN, val); 36862306a36Sopenharmony_ci val = MDIO_CTRL_SPRES_PRMBL | 36962306a36Sopenharmony_ci FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | 37062306a36Sopenharmony_ci FIELDX(MDIO_CTRL_DATA, phy_data) | 37162306a36Sopenharmony_ci MDIO_CTRL_START | 37262306a36Sopenharmony_ci MDIO_CTRL_MODE_EXT; 37362306a36Sopenharmony_ci } else { 37462306a36Sopenharmony_ci val = MDIO_CTRL_SPRES_PRMBL | 37562306a36Sopenharmony_ci FIELDX(MDIO_CTRL_CLK_SEL, clk_sel) | 37662306a36Sopenharmony_ci FIELDX(MDIO_CTRL_DATA, phy_data) | 37762306a36Sopenharmony_ci FIELDX(MDIO_CTRL_REG, reg) | 37862306a36Sopenharmony_ci MDIO_CTRL_START; 37962306a36Sopenharmony_ci } 38062306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MDIO_CTRL, val); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci if (!atl1c_wait_mdio_idle(hw)) 38362306a36Sopenharmony_ci return -1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci atl1c_start_phy_polling(hw, clk_sel); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci return 0; 38862306a36Sopenharmony_ci} 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci/* 39162306a36Sopenharmony_ci * Reads the value from a PHY register 39262306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 39362306a36Sopenharmony_ci * reg_addr - address of the PHY register to read 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ciint atl1c_read_phy_reg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci return atl1c_read_phy_core(hw, false, 0, reg_addr, phy_data); 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* 40162306a36Sopenharmony_ci * Writes a value to a PHY register 40262306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 40362306a36Sopenharmony_ci * reg_addr - address of the PHY register to write 40462306a36Sopenharmony_ci * data - data to write to the PHY 40562306a36Sopenharmony_ci */ 40662306a36Sopenharmony_ciint atl1c_write_phy_reg(struct atl1c_hw *hw, u32 reg_addr, u16 phy_data) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci return atl1c_write_phy_core(hw, false, 0, reg_addr, phy_data); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci/* read from PHY extension register */ 41262306a36Sopenharmony_ciint atl1c_read_phy_ext(struct atl1c_hw *hw, u8 dev_addr, 41362306a36Sopenharmony_ci u16 reg_addr, u16 *phy_data) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci return atl1c_read_phy_core(hw, true, dev_addr, reg_addr, phy_data); 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci/* write to PHY extension register */ 41962306a36Sopenharmony_ciint atl1c_write_phy_ext(struct atl1c_hw *hw, u8 dev_addr, 42062306a36Sopenharmony_ci u16 reg_addr, u16 phy_data) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci return atl1c_write_phy_core(hw, true, dev_addr, reg_addr, phy_data); 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ciint atl1c_read_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 *phy_data) 42662306a36Sopenharmony_ci{ 42762306a36Sopenharmony_ci int err; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); 43062306a36Sopenharmony_ci if (unlikely(err)) 43162306a36Sopenharmony_ci return err; 43262306a36Sopenharmony_ci else 43362306a36Sopenharmony_ci err = atl1c_read_phy_reg(hw, MII_DBG_DATA, phy_data); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return err; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ciint atl1c_write_phy_dbg(struct atl1c_hw *hw, u16 reg_addr, u16 phy_data) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci int err; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci err = atl1c_write_phy_reg(hw, MII_DBG_ADDR, reg_addr); 44362306a36Sopenharmony_ci if (unlikely(err)) 44462306a36Sopenharmony_ci return err; 44562306a36Sopenharmony_ci else 44662306a36Sopenharmony_ci err = atl1c_write_phy_reg(hw, MII_DBG_DATA, phy_data); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* 45262306a36Sopenharmony_ci * Configures PHY autoneg and flow control advertisement settings 45362306a36Sopenharmony_ci * 45462306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 45562306a36Sopenharmony_ci */ 45662306a36Sopenharmony_cistatic int atl1c_phy_setup_adv(struct atl1c_hw *hw) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci u16 mii_adv_data = ADVERTISE_DEFAULT_CAP & ~ADVERTISE_ALL; 45962306a36Sopenharmony_ci u16 mii_giga_ctrl_data = GIGA_CR_1000T_DEFAULT_CAP & 46062306a36Sopenharmony_ci ~GIGA_CR_1000T_SPEED_MASK; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_10baseT_Half) 46362306a36Sopenharmony_ci mii_adv_data |= ADVERTISE_10HALF; 46462306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_10baseT_Full) 46562306a36Sopenharmony_ci mii_adv_data |= ADVERTISE_10FULL; 46662306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_100baseT_Half) 46762306a36Sopenharmony_ci mii_adv_data |= ADVERTISE_100HALF; 46862306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_100baseT_Full) 46962306a36Sopenharmony_ci mii_adv_data |= ADVERTISE_100FULL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_Autoneg) 47262306a36Sopenharmony_ci mii_adv_data |= ADVERTISE_10HALF | ADVERTISE_10FULL | 47362306a36Sopenharmony_ci ADVERTISE_100HALF | ADVERTISE_100FULL; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (hw->link_cap_flags & ATL1C_LINK_CAP_1000M) { 47662306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_1000baseT_Half) 47762306a36Sopenharmony_ci mii_giga_ctrl_data |= ADVERTISE_1000HALF; 47862306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_1000baseT_Full) 47962306a36Sopenharmony_ci mii_giga_ctrl_data |= ADVERTISE_1000FULL; 48062306a36Sopenharmony_ci if (hw->autoneg_advertised & ADVERTISED_Autoneg) 48162306a36Sopenharmony_ci mii_giga_ctrl_data |= ADVERTISE_1000HALF | 48262306a36Sopenharmony_ci ADVERTISE_1000FULL; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (atl1c_write_phy_reg(hw, MII_ADVERTISE, mii_adv_data) != 0 || 48662306a36Sopenharmony_ci atl1c_write_phy_reg(hw, MII_CTRL1000, mii_giga_ctrl_data) != 0) 48762306a36Sopenharmony_ci return -1; 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_civoid atl1c_phy_disable(struct atl1c_hw *hw) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci atl1c_power_saving(hw, 0); 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ciint atl1c_phy_reset(struct atl1c_hw *hw) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct atl1c_adapter *adapter = hw->adapter; 50062306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 50162306a36Sopenharmony_ci u16 phy_data; 50262306a36Sopenharmony_ci u32 phy_ctrl_data, lpi_ctrl; 50362306a36Sopenharmony_ci int err; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* reset PHY core */ 50662306a36Sopenharmony_ci AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl_data); 50762306a36Sopenharmony_ci phy_ctrl_data &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_PHY_IDDQ | 50862306a36Sopenharmony_ci GPHY_CTRL_GATE_25M_EN | GPHY_CTRL_PWDOWN_HW | GPHY_CTRL_CLS); 50962306a36Sopenharmony_ci phy_ctrl_data |= GPHY_CTRL_SEL_ANA_RST; 51062306a36Sopenharmony_ci if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) 51162306a36Sopenharmony_ci phy_ctrl_data |= (GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); 51262306a36Sopenharmony_ci else 51362306a36Sopenharmony_ci phy_ctrl_data &= ~(GPHY_CTRL_HIB_EN | GPHY_CTRL_HIB_PULSE); 51462306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data); 51562306a36Sopenharmony_ci AT_WRITE_FLUSH(hw); 51662306a36Sopenharmony_ci udelay(10); 51762306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl_data | GPHY_CTRL_EXT_RESET); 51862306a36Sopenharmony_ci AT_WRITE_FLUSH(hw); 51962306a36Sopenharmony_ci udelay(10 * GPHY_CTRL_EXT_RST_TO); /* delay 800us */ 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* switch clock */ 52262306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b) { 52362306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_CFGLPSPD, &phy_data); 52462306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_CFGLPSPD, 52562306a36Sopenharmony_ci phy_data & ~CFGLPSPD_RSTCNT_CLK125SW); 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* tx-half amplitude issue fix */ 52962306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { 53062306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_CABLE1TH_DET, &phy_data); 53162306a36Sopenharmony_ci phy_data |= CABLE1TH_DET_EN; 53262306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_CABLE1TH_DET, phy_data); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* clear bit3 of dbgport 3B to lower voltage */ 53662306a36Sopenharmony_ci if (!(hw->ctrl_flags & ATL1C_HIB_DISABLE)) { 53762306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2) { 53862306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_VOLT_CTRL, &phy_data); 53962306a36Sopenharmony_ci phy_data &= ~VOLT_CTRL_SWLOWEST; 54062306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_VOLT_CTRL, phy_data); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci /* power saving config */ 54362306a36Sopenharmony_ci phy_data = 54462306a36Sopenharmony_ci hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 ? 54562306a36Sopenharmony_ci L1D_LEGCYPS_DEF : L1C_LEGCYPS_DEF; 54662306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, phy_data); 54762306a36Sopenharmony_ci /* hib */ 54862306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, 54962306a36Sopenharmony_ci SYSMODCTRL_IECHOADJ_DEF); 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci /* disable pws */ 55262306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_LEGCYPS, &phy_data); 55362306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_LEGCYPS, 55462306a36Sopenharmony_ci phy_data & ~LEGCYPS_EN); 55562306a36Sopenharmony_ci /* disable hibernate */ 55662306a36Sopenharmony_ci atl1c_read_phy_dbg(hw, MIIDBG_HIBNEG, &phy_data); 55762306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_HIBNEG, 55862306a36Sopenharmony_ci phy_data & HIBNEG_PSHIB_EN); 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci /* disable AZ(EEE) by default */ 56162306a36Sopenharmony_ci if (hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2 || 56262306a36Sopenharmony_ci hw->nic_type == athr_l2c_b2) { 56362306a36Sopenharmony_ci AT_READ_REG(hw, REG_LPI_CTRL, &lpi_ctrl); 56462306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_LPI_CTRL, lpi_ctrl & ~LPI_CTRL_EN); 56562306a36Sopenharmony_ci atl1c_write_phy_ext(hw, MIIEXT_ANEG, MIIEXT_LOCAL_EEEADV, 0); 56662306a36Sopenharmony_ci atl1c_write_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL3, 56762306a36Sopenharmony_ci L2CB_CLDCTRL3); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci /* other debug port to set */ 57162306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_ANACTRL, ANACTRL_DEF); 57262306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_SRDSYSMOD, SRDSYSMOD_DEF); 57362306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_TST10BTCFG, TST10BTCFG_DEF); 57462306a36Sopenharmony_ci /* UNH-IOL test issue, set bit7 */ 57562306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_TST100BTCFG, 57662306a36Sopenharmony_ci TST100BTCFG_DEF | TST100BTCFG_LITCH_EN); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci /* set phy interrupt mask */ 57962306a36Sopenharmony_ci phy_data = IER_LINK_UP | IER_LINK_DOWN; 58062306a36Sopenharmony_ci err = atl1c_write_phy_reg(hw, MII_IER, phy_data); 58162306a36Sopenharmony_ci if (err) { 58262306a36Sopenharmony_ci if (netif_msg_hw(adapter)) 58362306a36Sopenharmony_ci dev_err(&pdev->dev, 58462306a36Sopenharmony_ci "Error enable PHY linkChange Interrupt\n"); 58562306a36Sopenharmony_ci return err; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ciint atl1c_phy_init(struct atl1c_hw *hw) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct atl1c_adapter *adapter = hw->adapter; 59362306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 59462306a36Sopenharmony_ci int ret_val; 59562306a36Sopenharmony_ci u16 mii_bmcr_data = BMCR_RESET; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (hw->nic_type == athr_mt) { 59862306a36Sopenharmony_ci hw->phy_configured = true; 59962306a36Sopenharmony_ci return 0; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if ((atl1c_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id1) != 0) || 60362306a36Sopenharmony_ci (atl1c_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id2) != 0)) { 60462306a36Sopenharmony_ci dev_err(&pdev->dev, "Error get phy ID\n"); 60562306a36Sopenharmony_ci return -1; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci switch (hw->media_type) { 60862306a36Sopenharmony_ci case MEDIA_TYPE_AUTO_SENSOR: 60962306a36Sopenharmony_ci ret_val = atl1c_phy_setup_adv(hw); 61062306a36Sopenharmony_ci if (ret_val) { 61162306a36Sopenharmony_ci if (netif_msg_link(adapter)) 61262306a36Sopenharmony_ci dev_err(&pdev->dev, 61362306a36Sopenharmony_ci "Error Setting up Auto-Negotiation\n"); 61462306a36Sopenharmony_ci return ret_val; 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; 61762306a36Sopenharmony_ci break; 61862306a36Sopenharmony_ci case MEDIA_TYPE_100M_FULL: 61962306a36Sopenharmony_ci mii_bmcr_data |= BMCR_SPEED100 | BMCR_FULLDPLX; 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case MEDIA_TYPE_100M_HALF: 62262306a36Sopenharmony_ci mii_bmcr_data |= BMCR_SPEED100; 62362306a36Sopenharmony_ci break; 62462306a36Sopenharmony_ci case MEDIA_TYPE_10M_FULL: 62562306a36Sopenharmony_ci mii_bmcr_data |= BMCR_FULLDPLX; 62662306a36Sopenharmony_ci break; 62762306a36Sopenharmony_ci case MEDIA_TYPE_10M_HALF: 62862306a36Sopenharmony_ci break; 62962306a36Sopenharmony_ci default: 63062306a36Sopenharmony_ci if (netif_msg_link(adapter)) 63162306a36Sopenharmony_ci dev_err(&pdev->dev, "Wrong Media type %d\n", 63262306a36Sopenharmony_ci hw->media_type); 63362306a36Sopenharmony_ci return -1; 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci ret_val = atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); 63762306a36Sopenharmony_ci if (ret_val) 63862306a36Sopenharmony_ci return ret_val; 63962306a36Sopenharmony_ci hw->phy_configured = true; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cibool atl1c_get_link_status(struct atl1c_hw *hw) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci u16 phy_data; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci if (hw->nic_type == athr_mt) { 64962306a36Sopenharmony_ci u32 spd; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci AT_READ_REG(hw, REG_MT_SPEED, &spd); 65262306a36Sopenharmony_ci return !!spd; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci /* MII_BMSR must be read twice */ 65662306a36Sopenharmony_ci atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); 65762306a36Sopenharmony_ci atl1c_read_phy_reg(hw, MII_BMSR, &phy_data); 65862306a36Sopenharmony_ci return !!(phy_data & BMSR_LSTATUS); 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* 66262306a36Sopenharmony_ci * Detects the current speed and duplex settings of the hardware. 66362306a36Sopenharmony_ci * 66462306a36Sopenharmony_ci * hw - Struct containing variables accessed by shared code 66562306a36Sopenharmony_ci * speed - Speed of the connection 66662306a36Sopenharmony_ci * duplex - Duplex setting of the connection 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ciint atl1c_get_speed_and_duplex(struct atl1c_hw *hw, u16 *speed, u16 *duplex) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci int err; 67162306a36Sopenharmony_ci u16 phy_data; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci if (hw->nic_type == athr_mt) { 67462306a36Sopenharmony_ci u32 spd; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci AT_READ_REG(hw, REG_MT_SPEED, &spd); 67762306a36Sopenharmony_ci *speed = spd; 67862306a36Sopenharmony_ci *duplex = FULL_DUPLEX; 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Read PHY Specific Status Register (17) */ 68362306a36Sopenharmony_ci err = atl1c_read_phy_reg(hw, MII_GIGA_PSSR, &phy_data); 68462306a36Sopenharmony_ci if (err) 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (!(phy_data & GIGA_PSSR_SPD_DPLX_RESOLVED)) 68862306a36Sopenharmony_ci return -1; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci switch (phy_data & GIGA_PSSR_SPEED) { 69162306a36Sopenharmony_ci case GIGA_PSSR_1000MBS: 69262306a36Sopenharmony_ci *speed = SPEED_1000; 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci case GIGA_PSSR_100MBS: 69562306a36Sopenharmony_ci *speed = SPEED_100; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci case GIGA_PSSR_10MBS: 69862306a36Sopenharmony_ci *speed = SPEED_10; 69962306a36Sopenharmony_ci break; 70062306a36Sopenharmony_ci default: 70162306a36Sopenharmony_ci return -1; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci if (phy_data & GIGA_PSSR_DPLX) 70562306a36Sopenharmony_ci *duplex = FULL_DUPLEX; 70662306a36Sopenharmony_ci else 70762306a36Sopenharmony_ci *duplex = HALF_DUPLEX; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci return 0; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci/* select one link mode to get lower power consumption */ 71362306a36Sopenharmony_ciint atl1c_phy_to_ps_link(struct atl1c_hw *hw) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci struct atl1c_adapter *adapter = hw->adapter; 71662306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 71762306a36Sopenharmony_ci int ret = 0; 71862306a36Sopenharmony_ci u16 autoneg_advertised = ADVERTISED_10baseT_Half; 71962306a36Sopenharmony_ci u16 save_autoneg_advertised; 72062306a36Sopenharmony_ci u16 mii_lpa_data; 72162306a36Sopenharmony_ci u16 speed = SPEED_0; 72262306a36Sopenharmony_ci u16 duplex = FULL_DUPLEX; 72362306a36Sopenharmony_ci int i; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci if (atl1c_get_link_status(hw)) { 72662306a36Sopenharmony_ci atl1c_read_phy_reg(hw, MII_LPA, &mii_lpa_data); 72762306a36Sopenharmony_ci if (mii_lpa_data & LPA_10FULL) 72862306a36Sopenharmony_ci autoneg_advertised = ADVERTISED_10baseT_Full; 72962306a36Sopenharmony_ci else if (mii_lpa_data & LPA_10HALF) 73062306a36Sopenharmony_ci autoneg_advertised = ADVERTISED_10baseT_Half; 73162306a36Sopenharmony_ci else if (mii_lpa_data & LPA_100HALF) 73262306a36Sopenharmony_ci autoneg_advertised = ADVERTISED_100baseT_Half; 73362306a36Sopenharmony_ci else if (mii_lpa_data & LPA_100FULL) 73462306a36Sopenharmony_ci autoneg_advertised = ADVERTISED_100baseT_Full; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci save_autoneg_advertised = hw->autoneg_advertised; 73762306a36Sopenharmony_ci hw->phy_configured = false; 73862306a36Sopenharmony_ci hw->autoneg_advertised = autoneg_advertised; 73962306a36Sopenharmony_ci if (atl1c_restart_autoneg(hw) != 0) { 74062306a36Sopenharmony_ci dev_dbg(&pdev->dev, "phy autoneg failed\n"); 74162306a36Sopenharmony_ci ret = -1; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci hw->autoneg_advertised = save_autoneg_advertised; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (mii_lpa_data) { 74662306a36Sopenharmony_ci for (i = 0; i < AT_SUSPEND_LINK_TIMEOUT; i++) { 74762306a36Sopenharmony_ci mdelay(100); 74862306a36Sopenharmony_ci if (atl1c_get_link_status(hw)) { 74962306a36Sopenharmony_ci if (atl1c_get_speed_and_duplex(hw, &speed, 75062306a36Sopenharmony_ci &duplex) != 0) 75162306a36Sopenharmony_ci dev_dbg(&pdev->dev, 75262306a36Sopenharmony_ci "get speed and duplex failed\n"); 75362306a36Sopenharmony_ci break; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci } else { 75862306a36Sopenharmony_ci speed = SPEED_10; 75962306a36Sopenharmony_ci duplex = HALF_DUPLEX; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci adapter->link_speed = speed; 76262306a36Sopenharmony_ci adapter->link_duplex = duplex; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return ret; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ciint atl1c_restart_autoneg(struct atl1c_hw *hw) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci int err = 0; 77062306a36Sopenharmony_ci u16 mii_bmcr_data = BMCR_RESET; 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci err = atl1c_phy_setup_adv(hw); 77362306a36Sopenharmony_ci if (err) 77462306a36Sopenharmony_ci return err; 77562306a36Sopenharmony_ci mii_bmcr_data |= BMCR_ANENABLE | BMCR_ANRESTART; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return atl1c_write_phy_reg(hw, MII_BMCR, mii_bmcr_data); 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ciint atl1c_power_saving(struct atl1c_hw *hw, u32 wufc) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct atl1c_adapter *adapter = hw->adapter; 78362306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 78462306a36Sopenharmony_ci u32 master_ctrl, mac_ctrl, phy_ctrl; 78562306a36Sopenharmony_ci u32 wol_ctrl, speed; 78662306a36Sopenharmony_ci u16 phy_data; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci wol_ctrl = 0; 78962306a36Sopenharmony_ci speed = adapter->link_speed == SPEED_1000 ? 79062306a36Sopenharmony_ci MAC_CTRL_SPEED_1000 : MAC_CTRL_SPEED_10_100; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci AT_READ_REG(hw, REG_MASTER_CTRL, &master_ctrl); 79362306a36Sopenharmony_ci AT_READ_REG(hw, REG_MAC_CTRL, &mac_ctrl); 79462306a36Sopenharmony_ci AT_READ_REG(hw, REG_GPHY_CTRL, &phy_ctrl); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci master_ctrl &= ~MASTER_CTRL_CLK_SEL_DIS; 79762306a36Sopenharmony_ci mac_ctrl = FIELD_SETX(mac_ctrl, MAC_CTRL_SPEED, speed); 79862306a36Sopenharmony_ci mac_ctrl &= ~(MAC_CTRL_DUPLX | MAC_CTRL_RX_EN | MAC_CTRL_TX_EN); 79962306a36Sopenharmony_ci if (adapter->link_duplex == FULL_DUPLEX) 80062306a36Sopenharmony_ci mac_ctrl |= MAC_CTRL_DUPLX; 80162306a36Sopenharmony_ci phy_ctrl &= ~(GPHY_CTRL_EXT_RESET | GPHY_CTRL_CLS); 80262306a36Sopenharmony_ci phy_ctrl |= GPHY_CTRL_SEL_ANA_RST | GPHY_CTRL_HIB_PULSE | 80362306a36Sopenharmony_ci GPHY_CTRL_HIB_EN; 80462306a36Sopenharmony_ci if (!wufc) { /* without WoL */ 80562306a36Sopenharmony_ci master_ctrl |= MASTER_CTRL_CLK_SEL_DIS; 80662306a36Sopenharmony_ci phy_ctrl |= GPHY_CTRL_PHY_IDDQ | GPHY_CTRL_PWDOWN_HW; 80762306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); 80862306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); 80962306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); 81062306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_WOL_CTRL, 0); 81162306a36Sopenharmony_ci hw->phy_configured = false; /* re-init PHY when resume */ 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci phy_ctrl |= GPHY_CTRL_EXT_RESET; 81562306a36Sopenharmony_ci if (wufc & AT_WUFC_MAG) { 81662306a36Sopenharmony_ci mac_ctrl |= MAC_CTRL_RX_EN | MAC_CTRL_BC_EN; 81762306a36Sopenharmony_ci wol_ctrl |= WOL_MAGIC_EN | WOL_MAGIC_PME_EN; 81862306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b && hw->revision_id == L2CB_V11) 81962306a36Sopenharmony_ci wol_ctrl |= WOL_PATTERN_EN | WOL_PATTERN_PME_EN; 82062306a36Sopenharmony_ci } 82162306a36Sopenharmony_ci if (wufc & AT_WUFC_LNKC) { 82262306a36Sopenharmony_ci wol_ctrl |= WOL_LINK_CHG_EN | WOL_LINK_CHG_PME_EN; 82362306a36Sopenharmony_ci if (atl1c_write_phy_reg(hw, MII_IER, IER_LINK_UP) != 0) { 82462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s: write phy MII_IER failed.\n", 82562306a36Sopenharmony_ci atl1c_driver_name); 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci /* clear PHY interrupt */ 82962306a36Sopenharmony_ci atl1c_read_phy_reg(hw, MII_ISR, &phy_data); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "%s: suspend MAC=%x,MASTER=%x,PHY=0x%x,WOL=%x\n", 83262306a36Sopenharmony_ci atl1c_driver_name, mac_ctrl, master_ctrl, phy_ctrl, wol_ctrl); 83362306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MASTER_CTRL, master_ctrl); 83462306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_MAC_CTRL, mac_ctrl); 83562306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_GPHY_CTRL, phy_ctrl); 83662306a36Sopenharmony_ci AT_WRITE_REG(hw, REG_WOL_CTRL, wol_ctrl); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 0; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci/* configure phy after Link change Event */ 84362306a36Sopenharmony_civoid atl1c_post_phy_linkchg(struct atl1c_hw *hw, u16 link_speed) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci u16 phy_val; 84662306a36Sopenharmony_ci bool adj_thresh = false; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci if (hw->nic_type == athr_l2c_b || hw->nic_type == athr_l2c_b2 || 84962306a36Sopenharmony_ci hw->nic_type == athr_l1d || hw->nic_type == athr_l1d_2) 85062306a36Sopenharmony_ci adj_thresh = true; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci if (link_speed != SPEED_0) { /* link up */ 85362306a36Sopenharmony_ci /* az with brcm, half-amp */ 85462306a36Sopenharmony_ci if (hw->nic_type == athr_l1d_2) { 85562306a36Sopenharmony_ci atl1c_read_phy_ext(hw, MIIEXT_PCS, MIIEXT_CLDCTRL6, 85662306a36Sopenharmony_ci &phy_val); 85762306a36Sopenharmony_ci phy_val = FIELD_GETX(phy_val, CLDCTRL6_CAB_LEN); 85862306a36Sopenharmony_ci phy_val = phy_val > CLDCTRL6_CAB_LEN_SHORT ? 85962306a36Sopenharmony_ci AZ_ANADECT_LONG : AZ_ANADECT_DEF; 86062306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_AZ_ANADECT, phy_val); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci /* threshold adjust */ 86362306a36Sopenharmony_ci if (adj_thresh && link_speed == SPEED_100 && hw->msi_lnkpatch) { 86462306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, L1D_MSE16DB_UP); 86562306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, 86662306a36Sopenharmony_ci L1D_SYSMODCTRL_IECHOADJ_DEF); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci } else { /* link down */ 86962306a36Sopenharmony_ci if (adj_thresh && hw->msi_lnkpatch) { 87062306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_SYSMODCTRL, 87162306a36Sopenharmony_ci SYSMODCTRL_IECHOADJ_DEF); 87262306a36Sopenharmony_ci atl1c_write_phy_dbg(hw, MIIDBG_MSE16DB, 87362306a36Sopenharmony_ci L1D_MSE16DB_DOWN); 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci} 877