| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2013 Johannes Berg <johannes@sipsolutions.net> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  This file is free software: you may copy, redistribute and/or modify it | 
					
						
							|  |  |  |  *  under the terms of the GNU General Public License as published by the | 
					
						
							|  |  |  |  *  Free Software Foundation, either version 2 of the License, or (at your | 
					
						
							|  |  |  |  *  option) any later version. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  This file is distributed in the hope that it will be useful, but | 
					
						
							|  |  |  |  *  WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
					
						
							|  |  |  |  *  General Public License for more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *  You should have received a copy of the GNU General Public License | 
					
						
							|  |  |  |  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This file incorporates work covered by the following copyright and | 
					
						
							|  |  |  |  * permission notice: | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (c) 2012 Qualcomm Atheros, Inc. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission to use, copy, modify, and/or distribute this software for any | 
					
						
							|  |  |  |  * purpose with or without fee is hereby granted, provided that the above | 
					
						
							|  |  |  |  * copyright notice and this permission notice appear in all copies. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
					
						
							|  |  |  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
					
						
							|  |  |  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | 
					
						
							|  |  |  |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
					
						
							|  |  |  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | 
					
						
							|  |  |  |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 
					
						
							|  |  |  |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #include <linux/etherdevice.h>
 | 
					
						
							|  |  |  | #include <linux/delay.h>
 | 
					
						
							|  |  |  | #include <linux/pci.h>
 | 
					
						
							|  |  |  | #include <linux/mdio.h>
 | 
					
						
							|  |  |  | #include "reg.h"
 | 
					
						
							|  |  |  | #include "hw.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline bool alx_is_rev_a(u8 rev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return rev == ALX_REV_A0 || rev == ALX_REV_A1; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_wait_mdio_idle(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ALX_MDIO_MAX_AC_TO; i++) { | 
					
						
							|  |  |  | 		val = alx_read_mem32(hw, ALX_MDIO); | 
					
						
							|  |  |  | 		if (!(val & ALX_MDIO_BUSY)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		udelay(10); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -ETIMEDOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_read_phy_core(struct alx_hw *hw, bool ext, u8 dev, | 
					
						
							|  |  |  | 			     u16 reg, u16 *phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val, clk_sel; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	*phy_data = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* use slow clock when it's in hibernation status */ | 
					
						
							|  |  |  | 	clk_sel = hw->link_speed != SPEED_UNKNOWN ? | 
					
						
							|  |  |  | 			ALX_MDIO_CLK_SEL_25MD4 : | 
					
						
							|  |  |  | 			ALX_MDIO_CLK_SEL_25MD128; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ext) { | 
					
						
							|  |  |  | 		val = dev << ALX_MDIO_EXTN_DEVAD_SHIFT | | 
					
						
							|  |  |  | 		      reg << ALX_MDIO_EXTN_REG_SHIFT; | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MDIO_EXTN, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		val = ALX_MDIO_SPRES_PRMBL | ALX_MDIO_START | | 
					
						
							|  |  |  | 		      ALX_MDIO_MODE_EXT | ALX_MDIO_OP_READ | | 
					
						
							|  |  |  | 		      clk_sel << ALX_MDIO_CLK_SEL_SHIFT; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		val = ALX_MDIO_SPRES_PRMBL | | 
					
						
							|  |  |  | 		      clk_sel << ALX_MDIO_CLK_SEL_SHIFT | | 
					
						
							|  |  |  | 		      reg << ALX_MDIO_REG_SHIFT | | 
					
						
							|  |  |  | 		      ALX_MDIO_START | ALX_MDIO_OP_READ; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MDIO, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = alx_wait_mdio_idle(hw); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MDIO); | 
					
						
							|  |  |  | 	*phy_data = ALX_GET_FIELD(val, ALX_MDIO_DATA); | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_write_phy_core(struct alx_hw *hw, bool ext, u8 dev, | 
					
						
							|  |  |  | 			      u16 reg, u16 phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val, clk_sel; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* use slow clock when it's in hibernation status */ | 
					
						
							|  |  |  | 	clk_sel = hw->link_speed != SPEED_UNKNOWN ? | 
					
						
							|  |  |  | 			ALX_MDIO_CLK_SEL_25MD4 : | 
					
						
							|  |  |  | 			ALX_MDIO_CLK_SEL_25MD128; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ext) { | 
					
						
							|  |  |  | 		val = dev << ALX_MDIO_EXTN_DEVAD_SHIFT | | 
					
						
							|  |  |  | 		      reg << ALX_MDIO_EXTN_REG_SHIFT; | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MDIO_EXTN, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		val = ALX_MDIO_SPRES_PRMBL | | 
					
						
							|  |  |  | 		      clk_sel << ALX_MDIO_CLK_SEL_SHIFT | | 
					
						
							|  |  |  | 		      phy_data << ALX_MDIO_DATA_SHIFT | | 
					
						
							|  |  |  | 		      ALX_MDIO_START | ALX_MDIO_MODE_EXT; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		val = ALX_MDIO_SPRES_PRMBL | | 
					
						
							|  |  |  | 		      clk_sel << ALX_MDIO_CLK_SEL_SHIFT | | 
					
						
							|  |  |  | 		      reg << ALX_MDIO_REG_SHIFT | | 
					
						
							|  |  |  | 		      phy_data << ALX_MDIO_DATA_SHIFT | | 
					
						
							|  |  |  | 		      ALX_MDIO_START; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MDIO, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return alx_wait_mdio_idle(hw); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return alx_read_phy_core(hw, false, 0, reg, phy_data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return alx_write_phy_core(hw, false, 0, reg, phy_data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return alx_read_phy_core(hw, true, dev, reg, pdata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return alx_write_phy_core(hw, true, dev, reg, data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_read_phy_dbg(struct alx_hw *hw, u16 reg, u16 *pdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = __alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, reg); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return __alx_read_phy_reg(hw, ALX_MII_DBG_DATA, pdata); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __alx_write_phy_dbg(struct alx_hw *hw, u16 reg, u16 data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = __alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, reg); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return __alx_write_phy_reg(hw, ALX_MII_DBG_DATA, data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_read_phy_reg(struct alx_hw *hw, u16 reg, u16 *phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_read_phy_reg(hw, reg, phy_data); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_write_phy_reg(struct alx_hw *hw, u16 reg, u16 phy_data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_write_phy_reg(hw, reg, phy_data); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_read_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 *pdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_read_phy_ext(hw, dev, reg, pdata); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_write_phy_ext(struct alx_hw *hw, u8 dev, u16 reg, u16 data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_write_phy_ext(hw, dev, reg, data); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_read_phy_dbg(struct alx_hw *hw, u16 reg, u16 *pdata) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_read_phy_dbg(hw, reg, pdata); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_write_phy_dbg(struct alx_hw *hw, u16 reg, u16 data) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock(&hw->mdio_lock); | 
					
						
							|  |  |  | 	err = __alx_write_phy_dbg(hw, reg, data); | 
					
						
							|  |  |  | 	spin_unlock(&hw->mdio_lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u16 alx_get_phy_config(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 	u16 phy_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_PHY_CTRL); | 
					
						
							|  |  |  | 	/* phy in reset */ | 
					
						
							|  |  |  | 	if ((val & ALX_PHY_CTRL_DSPRST_OUT) == 0) | 
					
						
							|  |  |  | 		return ALX_DRV_PHY_UNKNOWN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_DRV); | 
					
						
							|  |  |  | 	val = ALX_GET_FIELD(val, ALX_DRV_PHY); | 
					
						
							|  |  |  | 	if (ALX_DRV_PHY_UNKNOWN == val) | 
					
						
							|  |  |  | 		return ALX_DRV_PHY_UNKNOWN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_read_phy_reg(hw, ALX_MII_DBG_ADDR, &phy_val); | 
					
						
							|  |  |  | 	if (ALX_PHY_INITED == phy_val) | 
					
						
							|  |  |  | 		return val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ALX_DRV_PHY_UNKNOWN; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool alx_wait_reg(struct alx_hw *hw, u32 reg, u32 wait, u32 *val) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 read; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ALX_SLD_MAX_TO; i++) { | 
					
						
							|  |  |  | 		read = alx_read_mem32(hw, reg); | 
					
						
							|  |  |  | 		if ((read & wait) == 0) { | 
					
						
							|  |  |  | 			if (val) | 
					
						
							|  |  |  | 				*val = read; | 
					
						
							|  |  |  | 			return true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		mdelay(1); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool alx_read_macaddr(struct alx_hw *hw, u8 *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 mac0, mac1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mac0 = alx_read_mem32(hw, ALX_STAD0); | 
					
						
							|  |  |  | 	mac1 = alx_read_mem32(hw, ALX_STAD1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* addr should be big-endian */ | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:18 +02:00
										 |  |  | 	put_unaligned(cpu_to_be32(mac0), (__be32 *)(addr + 2)); | 
					
						
							|  |  |  | 	put_unaligned(cpu_to_be16(mac1), (__be16 *)addr); | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return is_valid_ether_addr(addr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_get_perm_macaddr(struct alx_hw *hw, u8 *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* try to get it from register first */ | 
					
						
							|  |  |  | 	if (alx_read_macaddr(hw, addr)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* try to load from efuse */ | 
					
						
							|  |  |  | 	if (!alx_wait_reg(hw, ALX_SLD, ALX_SLD_STAT | ALX_SLD_START, &val)) | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_SLD, val | ALX_SLD_START); | 
					
						
							|  |  |  | 	if (!alx_wait_reg(hw, ALX_SLD, ALX_SLD_START, NULL)) | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	if (alx_read_macaddr(hw, addr)) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* try to load from flash/eeprom (if present) */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_EFLD); | 
					
						
							|  |  |  | 	if (val & (ALX_EFLD_F_EXIST | ALX_EFLD_E_EXIST)) { | 
					
						
							|  |  |  | 		if (!alx_wait_reg(hw, ALX_EFLD, | 
					
						
							|  |  |  | 				  ALX_EFLD_STAT | ALX_EFLD_START, &val)) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_EFLD, val | ALX_EFLD_START); | 
					
						
							|  |  |  | 		if (!alx_wait_reg(hw, ALX_EFLD, ALX_EFLD_START, NULL)) | 
					
						
							|  |  |  | 			return -EIO; | 
					
						
							|  |  |  | 		if (alx_read_macaddr(hw, addr)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -EIO; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_set_macaddr(struct alx_hw *hw, const u8 *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* for example: 00-0B-6A-F6-00-DC * STAD0=6AF600DC, STAD1=000B */ | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:18 +02:00
										 |  |  | 	val = be32_to_cpu(get_unaligned((__be32 *)(addr + 2))); | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 	alx_write_mem32(hw, ALX_STAD0, val); | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:18 +02:00
										 |  |  | 	val = be16_to_cpu(get_unaligned((__be16 *)addr)); | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 	alx_write_mem32(hw, ALX_STAD1, val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void alx_reset_osc(struct alx_hw *hw, u8 rev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val, val2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear Internal OSC settings, switching OSC by hw itself */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MISC3); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MISC3, | 
					
						
							|  |  |  | 			(val & ~ALX_MISC3_25M_BY_SW) | | 
					
						
							|  |  |  | 			ALX_MISC3_25M_NOTO_INTNL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* 25M clk from chipset may be unstable 1s after de-assert of
 | 
					
						
							|  |  |  | 	 * PERST, driver need re-calibrate before enter Sleep for WoL | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MISC); | 
					
						
							|  |  |  | 	if (rev >= ALX_REV_B0) { | 
					
						
							|  |  |  | 		/* restore over current protection def-val,
 | 
					
						
							|  |  |  | 		 * this val could be reset by MAC-RST | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		ALX_SET_FIELD(val, ALX_MISC_PSW_OCP, ALX_MISC_PSW_OCP_DEF); | 
					
						
							|  |  |  | 		/* a 0->1 change will update the internal val of osc */ | 
					
						
							|  |  |  | 		val &= ~ALX_MISC_INTNLOSC_OPEN; | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MISC, val); | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN); | 
					
						
							|  |  |  | 		/* hw will automatically dis OSC after cab. */ | 
					
						
							|  |  |  | 		val2 = alx_read_mem32(hw, ALX_MSIC2); | 
					
						
							|  |  |  | 		val2 &= ~ALX_MSIC2_CALB_START; | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MSIC2, val2); | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MSIC2, val2 | ALX_MSIC2_CALB_START); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		val &= ~ALX_MISC_INTNLOSC_OPEN; | 
					
						
							|  |  |  | 		/* disable isolate for rev A devices */ | 
					
						
							|  |  |  | 		if (alx_is_rev_a(rev)) | 
					
						
							|  |  |  | 			val &= ~ALX_MISC_ISO_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MISC, val | ALX_MISC_INTNLOSC_OPEN); | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MISC, val); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	udelay(20); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int alx_stop_mac(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 rxq, txq, val; | 
					
						
							|  |  |  | 	u16 i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rxq = alx_read_mem32(hw, ALX_RXQ0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RXQ0, rxq & ~ALX_RXQ0_EN); | 
					
						
							|  |  |  | 	txq = alx_read_mem32(hw, ALX_TXQ0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TXQ0, txq & ~ALX_TXQ0_EN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	udelay(40); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hw->rx_ctrl &= ~(ALX_MAC_CTRL_RX_EN | ALX_MAC_CTRL_TX_EN); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ALX_DMA_MAC_RST_TO; i++) { | 
					
						
							|  |  |  | 		val = alx_read_mem32(hw, ALX_MAC_STS); | 
					
						
							|  |  |  | 		if (!(val & ALX_MAC_STS_IDLE)) | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		udelay(10); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return -ETIMEDOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_reset_mac(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val, pmctrl; | 
					
						
							|  |  |  | 	int i, ret; | 
					
						
							|  |  |  | 	u8 rev; | 
					
						
							|  |  |  | 	bool a_cr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pmctrl = 0; | 
					
						
							|  |  |  | 	rev = alx_hw_revision(hw); | 
					
						
							|  |  |  | 	a_cr = alx_is_rev_a(rev) && alx_hw_with_cr(hw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* disable all interrupts, RXQ/TXQ */ | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MSIX_MASK, 0xFFFFFFFF); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_IMR, 0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_ISR, ALX_ISR_DIS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = alx_stop_mac(hw); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* mac reset workaroud */ | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RFD_PIDX, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* dis l0s/l1 before mac reset */ | 
					
						
							|  |  |  | 	if (a_cr) { | 
					
						
							|  |  |  | 		pmctrl = alx_read_mem32(hw, ALX_PMCTRL); | 
					
						
							|  |  |  | 		if (pmctrl & (ALX_PMCTRL_L1_EN | ALX_PMCTRL_L0S_EN)) | 
					
						
							|  |  |  | 			alx_write_mem32(hw, ALX_PMCTRL, | 
					
						
							|  |  |  | 					pmctrl & ~(ALX_PMCTRL_L1_EN | | 
					
						
							|  |  |  | 						   ALX_PMCTRL_L0S_EN)); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* reset whole mac safely */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MASTER); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MASTER, | 
					
						
							|  |  |  | 			val | ALX_MASTER_DMA_MAC_RST | ALX_MASTER_OOB_DIS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* make sure it's real idle */ | 
					
						
							|  |  |  | 	udelay(10); | 
					
						
							|  |  |  | 	for (i = 0; i < ALX_DMA_MAC_RST_TO; i++) { | 
					
						
							|  |  |  | 		val = alx_read_mem32(hw, ALX_RFD_PIDX); | 
					
						
							|  |  |  | 		if (val == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		udelay(10); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (; i < ALX_DMA_MAC_RST_TO; i++) { | 
					
						
							|  |  |  | 		val = alx_read_mem32(hw, ALX_MASTER); | 
					
						
							|  |  |  | 		if ((val & ALX_MASTER_DMA_MAC_RST) == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		udelay(10); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (i == ALX_DMA_MAC_RST_TO) | 
					
						
							|  |  |  | 		return -EIO; | 
					
						
							|  |  |  | 	udelay(10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (a_cr) { | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_MASTER, val | ALX_MASTER_PCLKSEL_SRDS); | 
					
						
							|  |  |  | 		/* restore l0s / l1 */ | 
					
						
							|  |  |  | 		if (pmctrl & (ALX_PMCTRL_L1_EN | ALX_PMCTRL_L0S_EN)) | 
					
						
							|  |  |  | 			alx_write_mem32(hw, ALX_PMCTRL, pmctrl); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_reset_osc(hw, rev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear Internal OSC settings, switching OSC by hw itself,
 | 
					
						
							|  |  |  | 	 * disable isolate for rev A devices | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MISC3); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MISC3, | 
					
						
							|  |  |  | 			(val & ~ALX_MISC3_25M_BY_SW) | | 
					
						
							|  |  |  | 			ALX_MISC3_25M_NOTO_INTNL); | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MISC); | 
					
						
							|  |  |  | 	val &= ~ALX_MISC_INTNLOSC_OPEN; | 
					
						
							|  |  |  | 	if (alx_is_rev_a(rev)) | 
					
						
							|  |  |  | 		val &= ~ALX_MISC_ISO_EN; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MISC, val); | 
					
						
							|  |  |  | 	udelay(20); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* driver control speed/duplex, hash-alg */ | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_SERDES); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_SERDES, | 
					
						
							|  |  |  | 			val | ALX_SERDES_MACCLK_SLWDWN | | 
					
						
							|  |  |  | 			ALX_SERDES_PHYCLK_SLWDWN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_reset_phy(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 	u16 phy_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* (DSP)reset PHY core */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_PHY_CTRL); | 
					
						
							|  |  |  | 	val &= ~(ALX_PHY_CTRL_DSPRST_OUT | ALX_PHY_CTRL_IDDQ | | 
					
						
							|  |  |  | 		 ALX_PHY_CTRL_GATE_25M | ALX_PHY_CTRL_POWER_DOWN | | 
					
						
							|  |  |  | 		 ALX_PHY_CTRL_CLS); | 
					
						
							|  |  |  | 	val |= ALX_PHY_CTRL_RST_ANALOG; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val |= (ALX_PHY_CTRL_HIB_PULSE | ALX_PHY_CTRL_HIB_EN); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_PHY_CTRL, val); | 
					
						
							|  |  |  | 	udelay(10); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_PHY_CTRL, val | ALX_PHY_CTRL_DSPRST_OUT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ALX_PHY_CTRL_DSPRST_TO; i++) | 
					
						
							|  |  |  | 		udelay(10); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* phy power saving & hib */ | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_LEGCYPS, ALX_LEGCYPS_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_SYSMODCTRL, | 
					
						
							|  |  |  | 			  ALX_SYSMODCTRL_IECHOADJ_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_VDRVBIAS, | 
					
						
							|  |  |  | 			  ALX_VDRVBIAS_DEF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* EEE advertisement */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_LPI_CTRL); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_LPI_CTRL, val & ~ALX_LPI_CTRL_EN); | 
					
						
							|  |  |  | 	alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_LOCAL_EEEADV, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* phy power saving */ | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_TST10BTCFG, ALX_TST10BTCFG_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_SRDSYSMOD, ALX_SRDSYSMOD_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_TST100BTCFG, ALX_TST100BTCFG_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_ANACTRL, ALX_ANACTRL_DEF); | 
					
						
							|  |  |  | 	alx_read_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, &phy_val); | 
					
						
							|  |  |  | 	alx_write_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, | 
					
						
							|  |  |  | 			  phy_val & ~ALX_GREENCFG2_GATE_DFSE_EN); | 
					
						
							|  |  |  | 	/* rtl8139c, 120m issue */ | 
					
						
							|  |  |  | 	alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_NLP78, | 
					
						
							|  |  |  | 			  ALX_MIIEXT_NLP78_120M_DEF); | 
					
						
							|  |  |  | 	alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_S3DIG10, | 
					
						
							|  |  |  | 			  ALX_MIIEXT_S3DIG10_DEF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (hw->lnk_patch) { | 
					
						
							|  |  |  | 		/* Turn off half amplitude */ | 
					
						
							|  |  |  | 		alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL3, | 
					
						
							|  |  |  | 				 &phy_val); | 
					
						
							|  |  |  | 		alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL3, | 
					
						
							|  |  |  | 				  phy_val | ALX_CLDCTRL3_BP_CABLE1TH_DET_GT); | 
					
						
							|  |  |  | 		/* Turn off Green feature */ | 
					
						
							|  |  |  | 		alx_read_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, &phy_val); | 
					
						
							|  |  |  | 		alx_write_phy_dbg(hw, ALX_MIIDBG_GREENCFG2, | 
					
						
							|  |  |  | 				  phy_val | ALX_GREENCFG2_BP_GREEN); | 
					
						
							|  |  |  | 		/* Turn off half Bias */ | 
					
						
							|  |  |  | 		alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL5, | 
					
						
							|  |  |  | 				 &phy_val); | 
					
						
							|  |  |  | 		alx_write_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL5, | 
					
						
							|  |  |  | 				  phy_val | ALX_CLDCTRL5_BP_VD_HLFBIAS); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set phy interrupt mask */ | 
					
						
							|  |  |  | 	alx_write_phy_reg(hw, ALX_MII_IER, ALX_IER_LINK_UP | ALX_IER_LINK_DOWN); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ALX_PCI_CMD (PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_reset_pcie(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 rev = alx_hw_revision(hw); | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 	u16 val16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Workaround for PCI problem when BIOS sets MMRBC incorrectly. */ | 
					
						
							|  |  |  | 	pci_read_config_word(hw->pdev, PCI_COMMAND, &val16); | 
					
						
							|  |  |  | 	if (!(val16 & ALX_PCI_CMD) || (val16 & PCI_COMMAND_INTX_DISABLE)) { | 
					
						
							|  |  |  | 		val16 = (val16 | ALX_PCI_CMD) & ~PCI_COMMAND_INTX_DISABLE; | 
					
						
							|  |  |  | 		pci_write_config_word(hw->pdev, PCI_COMMAND, val16); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear WoL setting/status */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_WOL0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_WOL0, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_PDLL_TRNS1); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_PDLL_TRNS1, val & ~ALX_PDLL_TRNS1_D3PLLOFF_EN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* mask some pcie error bits */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_UE_SVRT); | 
					
						
							|  |  |  | 	val &= ~(ALX_UE_SVRT_DLPROTERR | ALX_UE_SVRT_FCPROTERR); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_UE_SVRT, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* wol 25M & pclk */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MASTER); | 
					
						
							|  |  |  | 	if (alx_is_rev_a(rev) && alx_hw_with_cr(hw)) { | 
					
						
							|  |  |  | 		if ((val & ALX_MASTER_WAKEN_25M) == 0 || | 
					
						
							|  |  |  | 		    (val & ALX_MASTER_PCLKSEL_SRDS) == 0) | 
					
						
							|  |  |  | 			alx_write_mem32(hw, ALX_MASTER, | 
					
						
							|  |  |  | 					val | ALX_MASTER_PCLKSEL_SRDS | | 
					
						
							|  |  |  | 					ALX_MASTER_WAKEN_25M); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		if ((val & ALX_MASTER_WAKEN_25M) == 0 || | 
					
						
							|  |  |  | 		    (val & ALX_MASTER_PCLKSEL_SRDS) != 0) | 
					
						
							|  |  |  | 			alx_write_mem32(hw, ALX_MASTER, | 
					
						
							|  |  |  | 					(val & ~ALX_MASTER_PCLKSEL_SRDS) | | 
					
						
							|  |  |  | 					ALX_MASTER_WAKEN_25M); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ASPM setting */ | 
					
						
							|  |  |  | 	alx_enable_aspm(hw, true, true); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	udelay(10); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_start_mac(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 mac, txq, rxq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rxq = alx_read_mem32(hw, ALX_RXQ0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RXQ0, rxq | ALX_RXQ0_EN); | 
					
						
							|  |  |  | 	txq = alx_read_mem32(hw, ALX_TXQ0); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TXQ0, txq | ALX_TXQ0_EN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mac = hw->rx_ctrl; | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 	if (hw->duplex == DUPLEX_FULL) | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		mac |= ALX_MAC_CTRL_FULLD; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		mac &= ~ALX_MAC_CTRL_FULLD; | 
					
						
							|  |  |  | 	ALX_SET_FIELD(mac, ALX_MAC_CTRL_SPEED, | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		      hw->link_speed == SPEED_1000 ? ALX_MAC_CTRL_SPEED_1000 : | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 						     ALX_MAC_CTRL_SPEED_10_100); | 
					
						
							|  |  |  | 	mac |= ALX_MAC_CTRL_TX_EN | ALX_MAC_CTRL_RX_EN; | 
					
						
							|  |  |  | 	hw->rx_ctrl = mac; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MAC_CTRL, mac); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_cfg_mac_flowcontrol(struct alx_hw *hw, u8 fc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (fc & ALX_FC_RX) | 
					
						
							|  |  |  | 		hw->rx_ctrl |= ALX_MAC_CTRL_RXFC_EN; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		hw->rx_ctrl &= ~ALX_MAC_CTRL_RXFC_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (fc & ALX_FC_TX) | 
					
						
							|  |  |  | 		hw->rx_ctrl |= ALX_MAC_CTRL_TXFC_EN; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		hw->rx_ctrl &= ~ALX_MAC_CTRL_TXFC_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MAC_CTRL, hw->rx_ctrl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_enable_aspm(struct alx_hw *hw, bool l0s_en, bool l1_en) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 pmctrl; | 
					
						
							|  |  |  | 	u8 rev = alx_hw_revision(hw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pmctrl = alx_read_mem32(hw, ALX_PMCTRL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ALX_SET_FIELD(pmctrl, ALX_PMCTRL_LCKDET_TIMER, | 
					
						
							|  |  |  | 		      ALX_PMCTRL_LCKDET_TIMER_DEF); | 
					
						
							|  |  |  | 	pmctrl |= ALX_PMCTRL_RCVR_WT_1US | | 
					
						
							|  |  |  | 		  ALX_PMCTRL_L1_CLKSW_EN | | 
					
						
							|  |  |  | 		  ALX_PMCTRL_L1_SRDSRX_PWD; | 
					
						
							|  |  |  | 	ALX_SET_FIELD(pmctrl, ALX_PMCTRL_L1REQ_TO, ALX_PMCTRL_L1REG_TO_DEF); | 
					
						
							|  |  |  | 	ALX_SET_FIELD(pmctrl, ALX_PMCTRL_L1_TIMER, ALX_PMCTRL_L1_TIMER_16US); | 
					
						
							|  |  |  | 	pmctrl &= ~(ALX_PMCTRL_L1_SRDS_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_L1_SRDSPLL_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_L1_BUFSRX_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_SADLY_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_HOTRST_WTEN| | 
					
						
							|  |  |  | 		    ALX_PMCTRL_L0S_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_L1_EN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_ASPM_FCEN | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_TXL1_AFTER_L0S | | 
					
						
							|  |  |  | 		    ALX_PMCTRL_RXL1_AFTER_L0S); | 
					
						
							|  |  |  | 	if (alx_is_rev_a(rev) && alx_hw_with_cr(hw)) | 
					
						
							|  |  |  | 		pmctrl |= ALX_PMCTRL_L1_SRDS_EN | ALX_PMCTRL_L1_SRDSPLL_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (l0s_en) | 
					
						
							|  |  |  | 		pmctrl |= (ALX_PMCTRL_L0S_EN | ALX_PMCTRL_ASPM_FCEN); | 
					
						
							|  |  |  | 	if (l1_en) | 
					
						
							|  |  |  | 		pmctrl |= (ALX_PMCTRL_L1_EN | ALX_PMCTRL_ASPM_FCEN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_PMCTRL, pmctrl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static u32 ethadv_to_hw_cfg(struct alx_hw *hw, u32 ethadv_cfg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 cfg = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ethadv_cfg & ADVERTISED_Autoneg) { | 
					
						
							|  |  |  | 		cfg |= ALX_DRV_PHY_AUTO; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_10baseT_Half) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_10; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_10baseT_Full) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_10 | ALX_DRV_PHY_DUPLEX; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_100baseT_Half) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_100; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_100baseT_Full) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_1000baseT_Half) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_1000; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_1000baseT_Full) | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_Pause) | 
					
						
							|  |  |  | 			cfg |= ADVERTISE_PAUSE_CAP; | 
					
						
							|  |  |  | 		if (ethadv_cfg & ADVERTISED_Asym_Pause) | 
					
						
							|  |  |  | 			cfg |= ADVERTISE_PAUSE_ASYM; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		switch (ethadv_cfg) { | 
					
						
							|  |  |  | 		case ADVERTISED_10baseT_Half: | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_10; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case ADVERTISED_100baseT_Half: | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_100; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case ADVERTISED_10baseT_Full: | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_10 | ALX_DRV_PHY_DUPLEX; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case ADVERTISED_100baseT_Full: | 
					
						
							|  |  |  | 			cfg |= ALX_DRV_PHY_100 | ALX_DRV_PHY_DUPLEX; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cfg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_setup_speed_duplex(struct alx_hw *hw, u32 ethadv, u8 flowctrl) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 adv, giga, cr; | 
					
						
							|  |  |  | 	u32 val; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, 0); | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_DRV); | 
					
						
							|  |  |  | 	ALX_SET_FIELD(val, ALX_DRV_PHY, 0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ethadv & ADVERTISED_Autoneg) { | 
					
						
							|  |  |  | 		adv = ADVERTISE_CSMA; | 
					
						
							|  |  |  | 		adv |= ethtool_adv_to_mii_adv_t(ethadv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (flowctrl & ALX_FC_ANEG) { | 
					
						
							|  |  |  | 			if (flowctrl & ALX_FC_RX) { | 
					
						
							|  |  |  | 				adv |= ADVERTISED_Pause; | 
					
						
							|  |  |  | 				if (!(flowctrl & ALX_FC_TX)) | 
					
						
							|  |  |  | 					adv |= ADVERTISED_Asym_Pause; | 
					
						
							|  |  |  | 			} else if (flowctrl & ALX_FC_TX) { | 
					
						
							|  |  |  | 				adv |= ADVERTISED_Asym_Pause; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		giga = 0; | 
					
						
							|  |  |  | 		if (alx_hw_giga(hw)) | 
					
						
							|  |  |  | 			giga = ethtool_adv_to_mii_ctrl1000_t(ethadv); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		cr = BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (alx_write_phy_reg(hw, MII_ADVERTISE, adv) || | 
					
						
							|  |  |  | 		    alx_write_phy_reg(hw, MII_CTRL1000, giga) || | 
					
						
							|  |  |  | 		    alx_write_phy_reg(hw, MII_BMCR, cr)) | 
					
						
							|  |  |  | 			err = -EBUSY; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		cr = BMCR_RESET; | 
					
						
							|  |  |  | 		if (ethadv == ADVERTISED_100baseT_Half || | 
					
						
							|  |  |  | 		    ethadv == ADVERTISED_100baseT_Full) | 
					
						
							|  |  |  | 			cr |= BMCR_SPEED100; | 
					
						
							|  |  |  | 		if (ethadv == ADVERTISED_10baseT_Full || | 
					
						
							|  |  |  | 		    ethadv == ADVERTISED_100baseT_Full) | 
					
						
							|  |  |  | 			cr |= BMCR_FULLDPLX; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = alx_write_phy_reg(hw, MII_BMCR, cr); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!err) { | 
					
						
							|  |  |  | 		alx_write_phy_reg(hw, ALX_MII_DBG_ADDR, ALX_PHY_INITED); | 
					
						
							|  |  |  | 		val |= ethadv_to_hw_cfg(hw, ethadv); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_DRV, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_post_phy_link(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 phy_val, len, agc; | 
					
						
							|  |  |  | 	u8 revid = alx_hw_revision(hw); | 
					
						
							|  |  |  | 	bool adj_th = revid == ALX_REV_B0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (revid != ALX_REV_B0 && !alx_is_rev_a(revid)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* 1000BT/AZ, wrong cable length */ | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 	if (hw->link_speed != SPEED_UNKNOWN) { | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		alx_read_phy_ext(hw, ALX_MIIEXT_PCS, ALX_MIIEXT_CLDCTRL6, | 
					
						
							|  |  |  | 				 &phy_val); | 
					
						
							|  |  |  | 		len = ALX_GET_FIELD(phy_val, ALX_CLDCTRL6_CAB_LEN); | 
					
						
							|  |  |  | 		alx_read_phy_dbg(hw, ALX_MIIDBG_AGC, &phy_val); | 
					
						
							|  |  |  | 		agc = ALX_GET_FIELD(phy_val, ALX_AGC_2_VGA); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		if ((hw->link_speed == SPEED_1000 && | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		     (len > ALX_CLDCTRL6_CAB_LEN_SHORT1G || | 
					
						
							|  |  |  | 		      (len == 0 && agc > ALX_AGC_LONG1G_LIMT))) || | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		    (hw->link_speed == SPEED_100 && | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		     (len > ALX_CLDCTRL6_CAB_LEN_SHORT100M || | 
					
						
							|  |  |  | 		      (len == 0 && agc > ALX_AGC_LONG100M_LIMT)))) { | 
					
						
							|  |  |  | 			alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT, | 
					
						
							|  |  |  | 					  ALX_AZ_ANADECT_LONG); | 
					
						
							|  |  |  | 			alx_read_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE, | 
					
						
							|  |  |  | 					 &phy_val); | 
					
						
							|  |  |  | 			alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE, | 
					
						
							|  |  |  | 					  phy_val | ALX_AFE_10BT_100M_TH); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			alx_write_phy_dbg(hw, ALX_MIIDBG_AZ_ANADECT, | 
					
						
							|  |  |  | 					  ALX_AZ_ANADECT_DEF); | 
					
						
							|  |  |  | 			alx_read_phy_ext(hw, ALX_MIIEXT_ANEG, | 
					
						
							|  |  |  | 					 ALX_MIIEXT_AFE, &phy_val); | 
					
						
							|  |  |  | 			alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE, | 
					
						
							|  |  |  | 					  phy_val & ~ALX_AFE_10BT_100M_TH); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* threshold adjust */ | 
					
						
							|  |  |  | 		if (adj_th && hw->lnk_patch) { | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 			if (hw->link_speed == SPEED_100) { | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 				alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB, | 
					
						
							|  |  |  | 						  ALX_MSE16DB_UP); | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 			} else if (hw->link_speed == SPEED_1000) { | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 				/*
 | 
					
						
							|  |  |  | 				 * Giga link threshold, raise the tolerance of | 
					
						
							|  |  |  | 				 * noise 50% | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				alx_read_phy_dbg(hw, ALX_MIIDBG_MSE20DB, | 
					
						
							|  |  |  | 						 &phy_val); | 
					
						
							|  |  |  | 				ALX_SET_FIELD(phy_val, ALX_MSE20DB_TH, | 
					
						
							|  |  |  | 					      ALX_MSE20DB_TH_HI); | 
					
						
							|  |  |  | 				alx_write_phy_dbg(hw, ALX_MIIDBG_MSE20DB, | 
					
						
							|  |  |  | 						  phy_val); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		alx_read_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE, | 
					
						
							|  |  |  | 				 &phy_val); | 
					
						
							|  |  |  | 		alx_write_phy_ext(hw, ALX_MIIEXT_ANEG, ALX_MIIEXT_AFE, | 
					
						
							|  |  |  | 				  phy_val & ~ALX_AFE_10BT_100M_TH); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (adj_th && hw->lnk_patch) { | 
					
						
							|  |  |  | 			alx_write_phy_dbg(hw, ALX_MIIDBG_MSE16DB, | 
					
						
							|  |  |  | 					  ALX_MSE16DB_DOWN); | 
					
						
							|  |  |  | 			alx_read_phy_dbg(hw, ALX_MIIDBG_MSE20DB, &phy_val); | 
					
						
							|  |  |  | 			ALX_SET_FIELD(phy_val, ALX_MSE20DB_TH, | 
					
						
							|  |  |  | 				      ALX_MSE20DB_TH_DEF); | 
					
						
							|  |  |  | 			alx_write_phy_dbg(hw, ALX_MIIDBG_MSE20DB, phy_val); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool alx_phy_configured(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 cfg, hw_cfg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cfg = ethadv_to_hw_cfg(hw, hw->adv_cfg); | 
					
						
							|  |  |  | 	cfg = ALX_GET_FIELD(cfg, ALX_DRV_PHY); | 
					
						
							|  |  |  | 	hw_cfg = alx_get_phy_config(hw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (hw_cfg == ALX_DRV_PHY_UNKNOWN) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return cfg == hw_cfg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | int alx_read_phy_link(struct alx_hw *hw) | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct pci_dev *pdev = hw->pdev; | 
					
						
							|  |  |  | 	u16 bmsr, giga; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = alx_read_phy_reg(hw, MII_BMSR, &bmsr); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = alx_read_phy_reg(hw, MII_BMSR, &bmsr); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(bmsr & BMSR_LSTATUS)) { | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		hw->link_speed = SPEED_UNKNOWN; | 
					
						
							|  |  |  | 		hw->duplex = DUPLEX_UNKNOWN; | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* speed/duplex result is saved in PHY Specific Status Register */ | 
					
						
							|  |  |  | 	err = alx_read_phy_reg(hw, ALX_MII_GIGA_PSSR, &giga); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(giga & ALX_GIGA_PSSR_SPD_DPLX_RESOLVED)) | 
					
						
							|  |  |  | 		goto wrong_speed; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (giga & ALX_GIGA_PSSR_SPEED) { | 
					
						
							|  |  |  | 	case ALX_GIGA_PSSR_1000MBS: | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		hw->link_speed = SPEED_1000; | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case ALX_GIGA_PSSR_100MBS: | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		hw->link_speed = SPEED_100; | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	case ALX_GIGA_PSSR_10MBS: | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 		hw->link_speed = SPEED_10; | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		goto wrong_speed; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-29 19:23:17 +02:00
										 |  |  | 	hw->duplex = (giga & ALX_GIGA_PSSR_DPLX) ? DUPLEX_FULL : DUPLEX_HALF; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2013-06-17 22:44:02 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | wrong_speed: | 
					
						
							|  |  |  | 	dev_err(&pdev->dev, "invalid PHY speed/duplex: 0x%x\n", giga); | 
					
						
							|  |  |  | 	return -EINVAL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int alx_clear_phy_intr(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16 isr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear interrupt status by reading it */ | 
					
						
							|  |  |  | 	return alx_read_phy_reg(hw, ALX_MII_ISR, &isr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_disable_rss(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 ctrl = alx_read_mem32(hw, ALX_RXQ0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ctrl &= ~ALX_RXQ0_RSS_HASH_EN; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RXQ0, ctrl); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void alx_configure_basic(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 val, raw_mtu, max_payload; | 
					
						
							|  |  |  | 	u16 val16; | 
					
						
							|  |  |  | 	u8 chip_rev = alx_hw_revision(hw); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_set_macaddr(hw, hw->mac_addr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_CLK_GATE, ALX_CLK_GATE_ALL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* idle timeout to switch clk_125M */ | 
					
						
							|  |  |  | 	if (chip_rev >= ALX_REV_B0) | 
					
						
							|  |  |  | 		alx_write_mem32(hw, ALX_IDLE_DECISN_TIMER, | 
					
						
							|  |  |  | 				ALX_IDLE_DECISN_TIMER_DEF); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_SMB_TIMER, hw->smb_timer * 500UL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_MASTER); | 
					
						
							|  |  |  | 	val |= ALX_MASTER_IRQMOD2_EN | | 
					
						
							|  |  |  | 	       ALX_MASTER_IRQMOD1_EN | | 
					
						
							|  |  |  | 	       ALX_MASTER_SYSALVTIMER_EN; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MASTER, val); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_IRQ_MODU_TIMER, | 
					
						
							|  |  |  | 			(hw->imt >> 1) << ALX_IRQ_MODU_TIMER1_SHIFT); | 
					
						
							|  |  |  | 	/* intr re-trig timeout */ | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_INT_RETRIG, ALX_INT_RETRIG_TO); | 
					
						
							|  |  |  | 	/* tpd threshold to trig int */ | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TINT_TPD_THRSHLD, hw->ith_tpd); | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TINT_TIMER, hw->imt); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	raw_mtu = hw->mtu + ETH_HLEN; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_MTU, raw_mtu + 8); | 
					
						
							|  |  |  | 	if (raw_mtu > ALX_MTU_JUMBO_TH) | 
					
						
							|  |  |  | 		hw->rx_ctrl &= ~ALX_MAC_CTRL_FAST_PAUSE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((raw_mtu + 8) < ALX_TXQ1_JUMBO_TSO_TH) | 
					
						
							|  |  |  | 		val = (raw_mtu + 8 + 7) >> 3; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		val = ALX_TXQ1_JUMBO_TSO_TH >> 3; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TXQ1, val | ALX_TXQ1_ERRLGPKT_DROP_EN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	max_payload = pcie_get_readrq(hw->pdev) >> 8; | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * if BIOS had changed the default dma read max length, | 
					
						
							|  |  |  | 	 * restore it to default value | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (max_payload < ALX_DEV_CTRL_MAXRRS_MIN) | 
					
						
							|  |  |  | 		pcie_set_readrq(hw->pdev, 128 << ALX_DEV_CTRL_MAXRRS_MIN); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = ALX_TXQ_TPD_BURSTPREF_DEF << ALX_TXQ0_TPD_BURSTPREF_SHIFT | | 
					
						
							|  |  |  | 	      ALX_TXQ0_MODE_ENHANCE | ALX_TXQ0_LSO_8023_EN | | 
					
						
							|  |  |  | 	      ALX_TXQ0_SUPT_IPOPT | | 
					
						
							|  |  |  | 	      ALX_TXQ_TXF_BURST_PREF_DEF << ALX_TXQ0_TXF_BURST_PREF_SHIFT; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_TXQ0, val); | 
					
						
							|  |  |  | 	val = ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q1_NUMPREF_SHIFT | | 
					
						
							|  |  |  | 	      ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q2_NUMPREF_SHIFT | | 
					
						
							|  |  |  | 	      ALX_TXQ_TPD_BURSTPREF_DEF << ALX_HQTPD_Q3_NUMPREF_SHIFT | | 
					
						
							|  |  |  | 	      ALX_HQTPD_BURST_EN; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_HQTPD, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* rxq, flow control */ | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_SRAM5); | 
					
						
							|  |  |  | 	val = ALX_GET_FIELD(val, ALX_SRAM_RXF_LEN) << 3; | 
					
						
							|  |  |  | 	if (val > ALX_SRAM_RXF_LEN_8K) { | 
					
						
							|  |  |  | 		val16 = ALX_MTU_STD_ALGN >> 3; | 
					
						
							|  |  |  | 		val = (val - ALX_RXQ2_RXF_FLOW_CTRL_RSVD) >> 3; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		val16 = ALX_MTU_STD_ALGN >> 3; | 
					
						
							|  |  |  | 		val = (val - ALX_MTU_STD_ALGN) >> 3; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RXQ2, | 
					
						
							|  |  |  | 			val16 << ALX_RXQ2_RXF_XOFF_THRESH_SHIFT | | 
					
						
							|  |  |  | 			val << ALX_RXQ2_RXF_XON_THRESH_SHIFT); | 
					
						
							|  |  |  | 	val = ALX_RXQ0_NUM_RFD_PREF_DEF << ALX_RXQ0_NUM_RFD_PREF_SHIFT | | 
					
						
							|  |  |  | 	      ALX_RXQ0_RSS_MODE_DIS << ALX_RXQ0_RSS_MODE_SHIFT | | 
					
						
							|  |  |  | 	      ALX_RXQ0_IDT_TBL_SIZE_DEF << ALX_RXQ0_IDT_TBL_SIZE_SHIFT | | 
					
						
							|  |  |  | 	      ALX_RXQ0_RSS_HSTYP_ALL | ALX_RXQ0_RSS_HASH_EN | | 
					
						
							|  |  |  | 	      ALX_RXQ0_IPV6_PARSE_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (alx_hw_giga(hw)) | 
					
						
							|  |  |  | 		ALX_SET_FIELD(val, ALX_RXQ0_ASPM_THRESH, | 
					
						
							|  |  |  | 			      ALX_RXQ0_ASPM_THRESH_100M); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_RXQ0, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = alx_read_mem32(hw, ALX_DMA); | 
					
						
							|  |  |  | 	val = ALX_DMA_RORDER_MODE_OUT << ALX_DMA_RORDER_MODE_SHIFT | | 
					
						
							|  |  |  | 	      ALX_DMA_RREQ_PRI_DATA | | 
					
						
							|  |  |  | 	      max_payload << ALX_DMA_RREQ_BLEN_SHIFT | | 
					
						
							|  |  |  | 	      ALX_DMA_WDLY_CNT_DEF << ALX_DMA_WDLY_CNT_SHIFT | | 
					
						
							|  |  |  | 	      ALX_DMA_RDLY_CNT_DEF << ALX_DMA_RDLY_CNT_SHIFT | | 
					
						
							|  |  |  | 	      (hw->dma_chnl - 1) << ALX_DMA_RCHNL_SEL_SHIFT; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_DMA, val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* default multi-tx-q weights */ | 
					
						
							|  |  |  | 	val = ALX_WRR_PRI_RESTRICT_NONE << ALX_WRR_PRI_SHIFT | | 
					
						
							|  |  |  | 	      4 << ALX_WRR_PRI0_SHIFT | | 
					
						
							|  |  |  | 	      4 << ALX_WRR_PRI1_SHIFT | | 
					
						
							|  |  |  | 	      4 << ALX_WRR_PRI2_SHIFT | | 
					
						
							|  |  |  | 	      4 << ALX_WRR_PRI3_SHIFT; | 
					
						
							|  |  |  | 	alx_write_mem32(hw, ALX_WRR, val); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool alx_get_phy_info(struct alx_hw *hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u16  devs1, devs2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (alx_read_phy_reg(hw, MII_PHYSID1, &hw->phy_id[0]) || | 
					
						
							|  |  |  | 	    alx_read_phy_reg(hw, MII_PHYSID2, &hw->phy_id[1])) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* since we haven't PMA/PMD status2 register, we can't
 | 
					
						
							|  |  |  | 	 * use mdio45_probe function for prtad and mmds. | 
					
						
							|  |  |  | 	 * use fixed MMD3 to get mmds. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (alx_read_phy_ext(hw, 3, MDIO_DEVS1, &devs1) || | 
					
						
							|  |  |  | 	    alx_read_phy_ext(hw, 3, MDIO_DEVS2, &devs2)) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 	hw->mdio.mmds = devs1 | devs2 << 16; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } |