diff options
Diffstat (limited to 'drivers/net/phy')
-rw-r--r-- | drivers/net/phy/Kconfig | 15 | ||||
-rw-r--r-- | drivers/net/phy/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/phy/amd-xgbe-phy.c | 1901 | ||||
-rw-r--r-- | drivers/net/phy/bcm7xxx.c | 14 | ||||
-rw-r--r-- | drivers/net/phy/davicom.c | 13 | ||||
-rw-r--r-- | drivers/net/phy/dp83867.c | 239 | ||||
-rw-r--r-- | drivers/net/phy/fixed_phy.c | 9 | ||||
-rw-r--r-- | drivers/net/phy/icplus.c | 5 | ||||
-rw-r--r-- | drivers/net/phy/marvell.c | 10 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bcm-unimac.c | 51 | ||||
-rw-r--r-- | drivers/net/phy/mdio-bitbang.c | 7 | ||||
-rw-r--r-- | drivers/net/phy/mdio-gpio.c | 1 | ||||
-rw-r--r-- | drivers/net/phy/mdio-mux-gpio.c | 3 | ||||
-rw-r--r-- | drivers/net/phy/mdio_bus.c | 19 | ||||
-rw-r--r-- | drivers/net/phy/micrel.c | 53 | ||||
-rw-r--r-- | drivers/net/phy/phy.c | 66 | ||||
-rw-r--r-- | drivers/net/phy/phy_device.c | 22 | ||||
-rw-r--r-- | drivers/net/phy/realtek.c | 68 | ||||
-rw-r--r-- | drivers/net/phy/smsc.c | 31 | ||||
-rw-r--r-- | drivers/net/phy/vitesse.c | 14 |
20 files changed, 567 insertions, 1976 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 70641d2c0..cb86d7a01 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -24,13 +24,6 @@ config AMD_PHY ---help--- Currently supports the am79c874 -config AMD_XGBE_PHY - tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs" - depends on (OF || ACPI) && HAS_IOMEM - depends on ARM64 || COMPILE_TEST - ---help--- - Currently supports the AMD 10GbE PHY - config MARVELL_PHY tristate "Drivers for Marvell PHYs" ---help--- @@ -119,6 +112,11 @@ config MICREL_PHY ---help--- Supports the KSZ9021, VSC8201, KS8001 PHYs. +config DP83867_PHY + tristate "Drivers for Texas Instruments DP83867 Gigabit PHY" + ---help--- + Currently supports the DP83867 PHY. + config FIXED_PHY tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" depends on PHYLIB @@ -193,7 +191,7 @@ config MDIO_BUS_MUX_GPIO config MDIO_BUS_MUX_MMIOREG tristate "Support for MMIO device-controlled MDIO bus multiplexers" - depends on OF_MDIO + depends on OF_MDIO && HAS_IOMEM select MDIO_BUS_MUX help This module provides a driver for MDIO bus multiplexers that @@ -212,7 +210,6 @@ config MDIO_BCM_UNIMAC This hardware can be found in the Broadcom GENET Ethernet MAC controllers as well as some Broadcom Ethernet switches such as the Starfighter 2 switches. - endif # PHYLIB config MICREL_KS8995MA diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 501ea7699..fcc25a0c4 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o obj-$(CONFIG_NATIONAL_PHY) += national.o obj-$(CONFIG_DP83640_PHY) += dp83640.o +obj-$(CONFIG_DP83867_PHY) += dp83867.o obj-$(CONFIG_STE10XP) += ste10Xp.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o @@ -33,5 +34,4 @@ obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o -obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o diff --git a/drivers/net/phy/amd-xgbe-phy.c b/drivers/net/phy/amd-xgbe-phy.c deleted file mode 100644 index 34a75cba3..000000000 --- a/drivers/net/phy/amd-xgbe-phy.c +++ /dev/null @@ -1,1901 +0,0 @@ -/* - * AMD 10Gb Ethernet PHY driver - * - * This file is available to you under your choice of the following two - * licenses: - * - * License 1: GPLv2 - * - * Copyright (c) 2014 Advanced Micro Devices, Inc. - * - * 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/>. - * - * - * License 2: Modified BSD - * - * Copyright (c) 2014 Advanced Micro Devices, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Advanced Micro Devices, Inc. nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include <linux/kernel.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/workqueue.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/mii.h> -#include <linux/ethtool.h> -#include <linux/phy.h> -#include <linux/mdio.h> -#include <linux/io.h> -#include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/of_device.h> -#include <linux/uaccess.h> -#include <linux/bitops.h> -#include <linux/property.h> -#include <linux/acpi.h> -#include <linux/jiffies.h> - -MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>"); -MODULE_LICENSE("Dual BSD/GPL"); -MODULE_VERSION("1.0.0-a"); -MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver"); - -#define XGBE_PHY_ID 0x000162d0 -#define XGBE_PHY_MASK 0xfffffff0 - -#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set" -#define XGBE_PHY_BLWC_PROPERTY "amd,serdes-blwc" -#define XGBE_PHY_CDR_RATE_PROPERTY "amd,serdes-cdr-rate" -#define XGBE_PHY_PQ_SKEW_PROPERTY "amd,serdes-pq-skew" -#define XGBE_PHY_TX_AMP_PROPERTY "amd,serdes-tx-amp" -#define XGBE_PHY_DFE_CFG_PROPERTY "amd,serdes-dfe-tap-config" -#define XGBE_PHY_DFE_ENA_PROPERTY "amd,serdes-dfe-tap-enable" - -#define XGBE_PHY_SPEEDS 3 -#define XGBE_PHY_SPEED_1000 0 -#define XGBE_PHY_SPEED_2500 1 -#define XGBE_PHY_SPEED_10000 2 - -#define XGBE_AN_MS_TIMEOUT 500 - -#define XGBE_AN_INT_CMPLT 0x01 -#define XGBE_AN_INC_LINK 0x02 -#define XGBE_AN_PG_RCV 0x04 -#define XGBE_AN_INT_MASK 0x07 - -#define XNP_MCF_NULL_MESSAGE 0x001 -#define XNP_ACK_PROCESSED BIT(12) -#define XNP_MP_FORMATTED BIT(13) -#define XNP_NP_EXCHANGE BIT(15) - -#define XGBE_PHY_RATECHANGE_COUNT 500 - -#define XGBE_PHY_KR_TRAINING_START 0x01 -#define XGBE_PHY_KR_TRAINING_ENABLE 0x02 - -#define XGBE_PHY_FEC_ENABLE 0x01 -#define XGBE_PHY_FEC_FORWARD 0x02 -#define XGBE_PHY_FEC_MASK 0x03 - -#ifndef MDIO_PMA_10GBR_PMD_CTRL -#define MDIO_PMA_10GBR_PMD_CTRL 0x0096 -#endif - -#ifndef MDIO_PMA_10GBR_FEC_ABILITY -#define MDIO_PMA_10GBR_FEC_ABILITY 0x00aa -#endif - -#ifndef MDIO_PMA_10GBR_FEC_CTRL -#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab -#endif - -#ifndef MDIO_AN_XNP -#define MDIO_AN_XNP 0x0016 -#endif - -#ifndef MDIO_AN_LPX -#define MDIO_AN_LPX 0x0019 -#endif - -#ifndef MDIO_AN_INTMASK -#define MDIO_AN_INTMASK 0x8001 -#endif - -#ifndef MDIO_AN_INT -#define MDIO_AN_INT 0x8002 -#endif - -#ifndef MDIO_CTRL1_SPEED1G -#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100) -#endif - -/* SerDes integration register offsets */ -#define SIR0_KR_RT_1 0x002c -#define SIR0_STATUS 0x0040 -#define SIR1_SPEED 0x0000 - -/* SerDes integration register entry bit positions and sizes */ -#define SIR0_KR_RT_1_RESET_INDEX 11 -#define SIR0_KR_RT_1_RESET_WIDTH 1 -#define SIR0_STATUS_RX_READY_INDEX 0 -#define SIR0_STATUS_RX_READY_WIDTH 1 -#define SIR0_STATUS_TX_READY_INDEX 8 -#define SIR0_STATUS_TX_READY_WIDTH 1 -#define SIR1_SPEED_CDR_RATE_INDEX 12 -#define SIR1_SPEED_CDR_RATE_WIDTH 4 -#define SIR1_SPEED_DATARATE_INDEX 4 -#define SIR1_SPEED_DATARATE_WIDTH 2 -#define SIR1_SPEED_PLLSEL_INDEX 3 -#define SIR1_SPEED_PLLSEL_WIDTH 1 -#define SIR1_SPEED_RATECHANGE_INDEX 6 -#define SIR1_SPEED_RATECHANGE_WIDTH 1 -#define SIR1_SPEED_TXAMP_INDEX 8 -#define SIR1_SPEED_TXAMP_WIDTH 4 -#define SIR1_SPEED_WORDMODE_INDEX 0 -#define SIR1_SPEED_WORDMODE_WIDTH 3 - -#define SPEED_10000_BLWC 0 -#define SPEED_10000_CDR 0x7 -#define SPEED_10000_PLL 0x1 -#define SPEED_10000_PQ 0x12 -#define SPEED_10000_RATE 0x0 -#define SPEED_10000_TXAMP 0xa -#define SPEED_10000_WORD 0x7 -#define SPEED_10000_DFE_TAP_CONFIG 0x1 -#define SPEED_10000_DFE_TAP_ENABLE 0x7f - -#define SPEED_2500_BLWC 1 -#define SPEED_2500_CDR 0x2 -#define SPEED_2500_PLL 0x0 -#define SPEED_2500_PQ 0xa -#define SPEED_2500_RATE 0x1 -#define SPEED_2500_TXAMP 0xf -#define SPEED_2500_WORD 0x1 -#define SPEED_2500_DFE_TAP_CONFIG 0x3 -#define SPEED_2500_DFE_TAP_ENABLE 0x0 - -#define SPEED_1000_BLWC 1 -#define SPEED_1000_CDR 0x2 -#define SPEED_1000_PLL 0x0 -#define SPEED_1000_PQ 0xa -#define SPEED_1000_RATE 0x3 -#define SPEED_1000_TXAMP 0xf -#define SPEED_1000_WORD 0x1 -#define SPEED_1000_DFE_TAP_CONFIG 0x3 -#define SPEED_1000_DFE_TAP_ENABLE 0x0 - -/* SerDes RxTx register offsets */ -#define RXTX_REG6 0x0018 -#define RXTX_REG20 0x0050 -#define RXTX_REG22 0x0058 -#define RXTX_REG114 0x01c8 -#define RXTX_REG129 0x0204 - -/* SerDes RxTx register entry bit positions and sizes */ -#define RXTX_REG6_RESETB_RXD_INDEX 8 -#define RXTX_REG6_RESETB_RXD_WIDTH 1 -#define RXTX_REG20_BLWC_ENA_INDEX 2 -#define RXTX_REG20_BLWC_ENA_WIDTH 1 -#define RXTX_REG114_PQ_REG_INDEX 9 -#define RXTX_REG114_PQ_REG_WIDTH 7 -#define RXTX_REG129_RXDFE_CONFIG_INDEX 14 -#define RXTX_REG129_RXDFE_CONFIG_WIDTH 2 - -/* Bit setting and getting macros - * The get macro will extract the current bit field value from within - * the variable - * - * The set macro will clear the current bit field value within the - * variable and then set the bit field of the variable to the - * specified value - */ -#define GET_BITS(_var, _index, _width) \ - (((_var) >> (_index)) & ((0x1 << (_width)) - 1)) - -#define SET_BITS(_var, _index, _width, _val) \ -do { \ - (_var) &= ~(((0x1 << (_width)) - 1) << (_index)); \ - (_var) |= (((_val) & ((0x1 << (_width)) - 1)) << (_index)); \ -} while (0) - -#define XSIR_GET_BITS(_var, _prefix, _field) \ - GET_BITS((_var), \ - _prefix##_##_field##_INDEX, \ - _prefix##_##_field##_WIDTH) - -#define XSIR_SET_BITS(_var, _prefix, _field, _val) \ - SET_BITS((_var), \ - _prefix##_##_field##_INDEX, \ - _prefix##_##_field##_WIDTH, (_val)) - -/* Macros for reading or writing SerDes integration registers - * The ioread macros will get bit fields or full values using the - * register definitions formed using the input names - * - * The iowrite macros will set bit fields or full values using the - * register definitions formed using the input names - */ -#define XSIR0_IOREAD(_priv, _reg) \ - ioread16((_priv)->sir0_regs + _reg) - -#define XSIR0_IOREAD_BITS(_priv, _reg, _field) \ - GET_BITS(XSIR0_IOREAD((_priv), _reg), \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH) - -#define XSIR0_IOWRITE(_priv, _reg, _val) \ - iowrite16((_val), (_priv)->sir0_regs + _reg) - -#define XSIR0_IOWRITE_BITS(_priv, _reg, _field, _val) \ -do { \ - u16 reg_val = XSIR0_IOREAD((_priv), _reg); \ - SET_BITS(reg_val, \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH, (_val)); \ - XSIR0_IOWRITE((_priv), _reg, reg_val); \ -} while (0) - -#define XSIR1_IOREAD(_priv, _reg) \ - ioread16((_priv)->sir1_regs + _reg) - -#define XSIR1_IOREAD_BITS(_priv, _reg, _field) \ - GET_BITS(XSIR1_IOREAD((_priv), _reg), \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH) - -#define XSIR1_IOWRITE(_priv, _reg, _val) \ - iowrite16((_val), (_priv)->sir1_regs + _reg) - -#define XSIR1_IOWRITE_BITS(_priv, _reg, _field, _val) \ -do { \ - u16 reg_val = XSIR1_IOREAD((_priv), _reg); \ - SET_BITS(reg_val, \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH, (_val)); \ - XSIR1_IOWRITE((_priv), _reg, reg_val); \ -} while (0) - -/* Macros for reading or writing SerDes RxTx registers - * The ioread macros will get bit fields or full values using the - * register definitions formed using the input names - * - * The iowrite macros will set bit fields or full values using the - * register definitions formed using the input names - */ -#define XRXTX_IOREAD(_priv, _reg) \ - ioread16((_priv)->rxtx_regs + _reg) - -#define XRXTX_IOREAD_BITS(_priv, _reg, _field) \ - GET_BITS(XRXTX_IOREAD((_priv), _reg), \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH) - -#define XRXTX_IOWRITE(_priv, _reg, _val) \ - iowrite16((_val), (_priv)->rxtx_regs + _reg) - -#define XRXTX_IOWRITE_BITS(_priv, _reg, _field, _val) \ -do { \ - u16 reg_val = XRXTX_IOREAD((_priv), _reg); \ - SET_BITS(reg_val, \ - _reg##_##_field##_INDEX, \ - _reg##_##_field##_WIDTH, (_val)); \ - XRXTX_IOWRITE((_priv), _reg, reg_val); \ -} while (0) - -static const u32 amd_xgbe_phy_serdes_blwc[] = { - SPEED_1000_BLWC, - SPEED_2500_BLWC, - SPEED_10000_BLWC, -}; - -static const u32 amd_xgbe_phy_serdes_cdr_rate[] = { - SPEED_1000_CDR, - SPEED_2500_CDR, - SPEED_10000_CDR, -}; - -static const u32 amd_xgbe_phy_serdes_pq_skew[] = { - SPEED_1000_PQ, - SPEED_2500_PQ, - SPEED_10000_PQ, -}; - -static const u32 amd_xgbe_phy_serdes_tx_amp[] = { - SPEED_1000_TXAMP, - SPEED_2500_TXAMP, - SPEED_10000_TXAMP, -}; - -static const u32 amd_xgbe_phy_serdes_dfe_tap_cfg[] = { - SPEED_1000_DFE_TAP_CONFIG, - SPEED_2500_DFE_TAP_CONFIG, - SPEED_10000_DFE_TAP_CONFIG, -}; - -static const u32 amd_xgbe_phy_serdes_dfe_tap_ena[] = { - SPEED_1000_DFE_TAP_ENABLE, - SPEED_2500_DFE_TAP_ENABLE, - SPEED_10000_DFE_TAP_ENABLE, -}; - -enum amd_xgbe_phy_an { - AMD_XGBE_AN_READY = 0, - AMD_XGBE_AN_PAGE_RECEIVED, - AMD_XGBE_AN_INCOMPAT_LINK, - AMD_XGBE_AN_COMPLETE, - AMD_XGBE_AN_NO_LINK, - AMD_XGBE_AN_ERROR, -}; - -enum amd_xgbe_phy_rx { - AMD_XGBE_RX_BPA = 0, - AMD_XGBE_RX_XNP, - AMD_XGBE_RX_COMPLETE, - AMD_XGBE_RX_ERROR, -}; - -enum amd_xgbe_phy_mode { - AMD_XGBE_MODE_KR, - AMD_XGBE_MODE_KX, -}; - -enum amd_xgbe_phy_speedset { - AMD_XGBE_PHY_SPEEDSET_1000_10000 = 0, - AMD_XGBE_PHY_SPEEDSET_2500_10000, -}; - -struct amd_xgbe_phy_priv { - struct platform_device *pdev; - struct acpi_device *adev; - struct device *dev; - - struct phy_device *phydev; - - /* SerDes related mmio resources */ - struct resource *rxtx_res; - struct resource *sir0_res; - struct resource *sir1_res; - - /* SerDes related mmio registers */ - void __iomem *rxtx_regs; /* SerDes Rx/Tx CSRs */ - void __iomem *sir0_regs; /* SerDes integration registers (1/2) */ - void __iomem *sir1_regs; /* SerDes integration registers (2/2) */ - - int an_irq; - char an_irq_name[IFNAMSIZ + 32]; - struct work_struct an_irq_work; - unsigned int an_irq_allocated; - - unsigned int speed_set; - - /* SerDes UEFI configurable settings. - * Switching between modes/speeds requires new values for some - * SerDes settings. The values can be supplied as device - * properties in array format. The first array entry is for - * 1GbE, second for 2.5GbE and third for 10GbE - */ - u32 serdes_blwc[XGBE_PHY_SPEEDS]; - u32 serdes_cdr_rate[XGBE_PHY_SPEEDS]; - u32 serdes_pq_skew[XGBE_PHY_SPEEDS]; - u32 serdes_tx_amp[XGBE_PHY_SPEEDS]; - u32 serdes_dfe_tap_cfg[XGBE_PHY_SPEEDS]; - u32 serdes_dfe_tap_ena[XGBE_PHY_SPEEDS]; - - /* Auto-negotiation state machine support */ - struct mutex an_mutex; - enum amd_xgbe_phy_an an_result; - enum amd_xgbe_phy_an an_state; - enum amd_xgbe_phy_rx kr_state; - enum amd_xgbe_phy_rx kx_state; - struct work_struct an_work; - struct workqueue_struct *an_workqueue; - unsigned int an_supported; - unsigned int parallel_detect; - unsigned int fec_ability; - unsigned long an_start; - - unsigned int lpm_ctrl; /* CTRL1 for resume */ -}; - -static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev) -{ - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); - if (ret < 0) - return ret; - - ret |= XGBE_PHY_KR_TRAINING_ENABLE; - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); - - return 0; -} - -static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev) -{ - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); - if (ret < 0) - return ret; - - ret &= ~XGBE_PHY_KR_TRAINING_ENABLE; - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret); - - return 0; -} - -static int amd_xgbe_phy_pcs_power_cycle(struct phy_device *phydev) -{ - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret |= MDIO_CTRL1_LPOWER; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - usleep_range(75, 100); - - ret &= ~MDIO_CTRL1_LPOWER; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - return 0; -} - -static void amd_xgbe_phy_serdes_start_ratechange(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - - /* Assert Rx and Tx ratechange */ - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 1); -} - -static void amd_xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - unsigned int wait; - u16 status; - - /* Release Rx and Tx ratechange */ - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, RATECHANGE, 0); - - /* Wait for Rx and Tx ready */ - wait = XGBE_PHY_RATECHANGE_COUNT; - while (wait--) { - usleep_range(50, 75); - - status = XSIR0_IOREAD(priv, SIR0_STATUS); - if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && - XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) - goto rx_reset; - } - - netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n", - status); - -rx_reset: - /* Perform Rx reset for the DFE changes */ - XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 0); - XRXTX_IOWRITE_BITS(priv, RXTX_REG6, RESETB_RXD, 1); -} - -static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* Enable KR training */ - ret = amd_xgbe_an_enable_kr_training(phydev); - if (ret < 0) - return ret; - - /* Set PCS to KR/10G speed */ - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - return ret; - - ret &= ~MDIO_PCS_CTRL2_TYPE; - ret |= MDIO_PCS_CTRL2_10GBR; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret &= ~MDIO_CTRL1_SPEEDSEL; - ret |= MDIO_CTRL1_SPEED10G; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - ret = amd_xgbe_phy_pcs_power_cycle(phydev); - if (ret < 0) - return ret; - - /* Set SerDes to 10G speed */ - amd_xgbe_phy_serdes_start_ratechange(phydev); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, - priv->serdes_cdr_rate[XGBE_PHY_SPEED_10000]); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, - priv->serdes_tx_amp[XGBE_PHY_SPEED_10000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, - priv->serdes_blwc[XGBE_PHY_SPEED_10000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, - priv->serdes_pq_skew[XGBE_PHY_SPEED_10000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, - priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_10000]); - XRXTX_IOWRITE(priv, RXTX_REG22, - priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_10000]); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - return 0; -} - -static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* Disable KR training */ - ret = amd_xgbe_an_disable_kr_training(phydev); - if (ret < 0) - return ret; - - /* Set PCS to KX/1G speed */ - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - return ret; - - ret &= ~MDIO_PCS_CTRL2_TYPE; - ret |= MDIO_PCS_CTRL2_10GBX; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret &= ~MDIO_CTRL1_SPEEDSEL; - ret |= MDIO_CTRL1_SPEED1G; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - ret = amd_xgbe_phy_pcs_power_cycle(phydev); - if (ret < 0) - return ret; - - /* Set SerDes to 2.5G speed */ - amd_xgbe_phy_serdes_start_ratechange(phydev); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, - priv->serdes_cdr_rate[XGBE_PHY_SPEED_2500]); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, - priv->serdes_tx_amp[XGBE_PHY_SPEED_2500]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, - priv->serdes_blwc[XGBE_PHY_SPEED_2500]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, - priv->serdes_pq_skew[XGBE_PHY_SPEED_2500]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, - priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_2500]); - XRXTX_IOWRITE(priv, RXTX_REG22, - priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_2500]); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - return 0; -} - -static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* Disable KR training */ - ret = amd_xgbe_an_disable_kr_training(phydev); - if (ret < 0) - return ret; - - /* Set PCS to KX/1G speed */ - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - return ret; - - ret &= ~MDIO_PCS_CTRL2_TYPE; - ret |= MDIO_PCS_CTRL2_10GBX; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2, ret); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret &= ~MDIO_CTRL1_SPEEDSEL; - ret |= MDIO_CTRL1_SPEED1G; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - ret = amd_xgbe_phy_pcs_power_cycle(phydev); - if (ret < 0) - return ret; - - /* Set SerDes to 1G speed */ - amd_xgbe_phy_serdes_start_ratechange(phydev); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL); - - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE, - priv->serdes_cdr_rate[XGBE_PHY_SPEED_1000]); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, - priv->serdes_tx_amp[XGBE_PHY_SPEED_1000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, - priv->serdes_blwc[XGBE_PHY_SPEED_1000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, - priv->serdes_pq_skew[XGBE_PHY_SPEED_1000]); - XRXTX_IOWRITE_BITS(priv, RXTX_REG129, RXDFE_CONFIG, - priv->serdes_dfe_tap_cfg[XGBE_PHY_SPEED_1000]); - XRXTX_IOWRITE(priv, RXTX_REG22, - priv->serdes_dfe_tap_ena[XGBE_PHY_SPEED_1000]); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - return 0; -} - -static int amd_xgbe_phy_cur_mode(struct phy_device *phydev, - enum amd_xgbe_phy_mode *mode) -{ - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - return ret; - - if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) - *mode = AMD_XGBE_MODE_KR; - else - *mode = AMD_XGBE_MODE_KX; - - return 0; -} - -static bool amd_xgbe_phy_in_kr_mode(struct phy_device *phydev) -{ - enum amd_xgbe_phy_mode mode; - - if (amd_xgbe_phy_cur_mode(phydev, &mode)) - return false; - - return (mode == AMD_XGBE_MODE_KR); -} - -static int amd_xgbe_phy_switch_mode(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* If we are in KR switch to KX, and vice-versa */ - if (amd_xgbe_phy_in_kr_mode(phydev)) { - if (priv->speed_set == AMD_XGBE_PHY_SPEEDSET_1000_10000) - ret = amd_xgbe_phy_gmii_mode(phydev); - else - ret = amd_xgbe_phy_gmii_2500_mode(phydev); - } else { - ret = amd_xgbe_phy_xgmii_mode(phydev); - } - - return ret; -} - -static int amd_xgbe_phy_set_mode(struct phy_device *phydev, - enum amd_xgbe_phy_mode mode) -{ - enum amd_xgbe_phy_mode cur_mode; - int ret; - - ret = amd_xgbe_phy_cur_mode(phydev, &cur_mode); - if (ret) - return ret; - - if (mode != cur_mode) - ret = amd_xgbe_phy_switch_mode(phydev); - - return ret; -} - -static bool amd_xgbe_phy_use_xgmii_mode(struct phy_device *phydev) -{ - if (phydev->autoneg == AUTONEG_ENABLE) { - if (phydev->advertising & ADVERTISED_10000baseKR_Full) - return true; - } else { - if (phydev->speed == SPEED_10000) - return true; - } - - return false; -} - -static bool amd_xgbe_phy_use_gmii_2500_mode(struct phy_device *phydev) -{ - if (phydev->autoneg == AUTONEG_ENABLE) { - if (phydev->advertising & ADVERTISED_2500baseX_Full) - return true; - } else { - if (phydev->speed == SPEED_2500) - return true; - } - - return false; -} - -static bool amd_xgbe_phy_use_gmii_mode(struct phy_device *phydev) -{ - if (phydev->autoneg == AUTONEG_ENABLE) { - if (phydev->advertising & ADVERTISED_1000baseKX_Full) - return true; - } else { - if (phydev->speed == SPEED_1000) - return true; - } - - return false; -} - -static int amd_xgbe_phy_set_an(struct phy_device *phydev, bool enable, - bool restart) -{ - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret &= ~MDIO_AN_CTRL1_ENABLE; - - if (enable) - ret |= MDIO_AN_CTRL1_ENABLE; - - if (restart) - ret |= MDIO_AN_CTRL1_RESTART; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); - - return 0; -} - -static int amd_xgbe_phy_restart_an(struct phy_device *phydev) -{ - return amd_xgbe_phy_set_an(phydev, true, true); -} - -static int amd_xgbe_phy_disable_an(struct phy_device *phydev) -{ - return amd_xgbe_phy_set_an(phydev, false, false); -} - -static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev, - enum amd_xgbe_phy_rx *state) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ad_reg, lp_reg, ret; - - *state = AMD_XGBE_RX_COMPLETE; - - /* If we're not in KR mode then we're done */ - if (!amd_xgbe_phy_in_kr_mode(phydev)) - return AMD_XGBE_AN_PAGE_RECEIVED; - - /* Enable/Disable FEC */ - ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (ad_reg < 0) - return AMD_XGBE_AN_ERROR; - - lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 2); - if (lp_reg < 0) - return AMD_XGBE_AN_ERROR; - - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - ret &= ~XGBE_PHY_FEC_MASK; - if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) - ret |= priv->fec_ability; - - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret); - - /* Start KR training */ - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - if (ret & XGBE_PHY_KR_TRAINING_ENABLE) { - XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1); - - ret |= XGBE_PHY_KR_TRAINING_START; - phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, - ret); - - XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0); - } - - return AMD_XGBE_AN_PAGE_RECEIVED; -} - -static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev, - enum amd_xgbe_phy_rx *state) -{ - u16 msg; - - *state = AMD_XGBE_RX_XNP; - - msg = XNP_MCF_NULL_MESSAGE; - msg |= XNP_MP_FORMATTED; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg); - - return AMD_XGBE_AN_PAGE_RECEIVED; -} - -static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev, - enum amd_xgbe_phy_rx *state) -{ - unsigned int link_support; - int ret, ad_reg, lp_reg; - - /* Read Base Ability register 2 first */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - /* Check for a supported mode, otherwise restart in a different one */ - link_support = amd_xgbe_phy_in_kr_mode(phydev) ? 0x80 : 0x20; - if (!(ret & link_support)) - return AMD_XGBE_AN_INCOMPAT_LINK; - - /* Check Extended Next Page support */ - ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (ad_reg < 0) - return AMD_XGBE_AN_ERROR; - - lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA); - if (lp_reg < 0) - return AMD_XGBE_AN_ERROR; - - return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ? - amd_xgbe_an_tx_xnp(phydev, state) : - amd_xgbe_an_tx_training(phydev, state); -} - -static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev, - enum amd_xgbe_phy_rx *state) -{ - int ad_reg, lp_reg; - - /* Check Extended Next Page support */ - ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP); - if (ad_reg < 0) - return AMD_XGBE_AN_ERROR; - - lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPX); - if (lp_reg < 0) - return AMD_XGBE_AN_ERROR; - - return ((ad_reg & XNP_NP_EXCHANGE) || (lp_reg & XNP_NP_EXCHANGE)) ? - amd_xgbe_an_tx_xnp(phydev, state) : - amd_xgbe_an_tx_training(phydev, state); -} - -static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_rx *state; - unsigned long an_timeout; - int ret; - - if (!priv->an_start) { - priv->an_start = jiffies; - } else { - an_timeout = priv->an_start + - msecs_to_jiffies(XGBE_AN_MS_TIMEOUT); - if (time_after(jiffies, an_timeout)) { - /* Auto-negotiation timed out, reset state */ - priv->kr_state = AMD_XGBE_RX_BPA; - priv->kx_state = AMD_XGBE_RX_BPA; - - priv->an_start = jiffies; - } - } - - state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state - : &priv->kx_state; - - switch (*state) { - case AMD_XGBE_RX_BPA: - ret = amd_xgbe_an_rx_bpa(phydev, state); - break; - - case AMD_XGBE_RX_XNP: - ret = amd_xgbe_an_rx_xnp(phydev, state); - break; - - default: - ret = AMD_XGBE_AN_ERROR; - } - - return ret; -} - -static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* Be sure we aren't looping trying to negotiate */ - if (amd_xgbe_phy_in_kr_mode(phydev)) { - priv->kr_state = AMD_XGBE_RX_ERROR; - - if (!(phydev->advertising & SUPPORTED_1000baseKX_Full) && - !(phydev->advertising & SUPPORTED_2500baseX_Full)) - return AMD_XGBE_AN_NO_LINK; - - if (priv->kx_state != AMD_XGBE_RX_BPA) - return AMD_XGBE_AN_NO_LINK; - } else { - priv->kx_state = AMD_XGBE_RX_ERROR; - - if (!(phydev->advertising & SUPPORTED_10000baseKR_Full)) - return AMD_XGBE_AN_NO_LINK; - - if (priv->kr_state != AMD_XGBE_RX_BPA) - return AMD_XGBE_AN_NO_LINK; - } - - ret = amd_xgbe_phy_disable_an(phydev); - if (ret) - return AMD_XGBE_AN_ERROR; - - ret = amd_xgbe_phy_switch_mode(phydev); - if (ret) - return AMD_XGBE_AN_ERROR; - - ret = amd_xgbe_phy_restart_an(phydev); - if (ret) - return AMD_XGBE_AN_ERROR; - - return AMD_XGBE_AN_INCOMPAT_LINK; -} - -static irqreturn_t amd_xgbe_an_isr(int irq, void *data) -{ - struct amd_xgbe_phy_priv *priv = (struct amd_xgbe_phy_priv *)data; - - /* Interrupt reason must be read and cleared outside of IRQ context */ - disable_irq_nosync(priv->an_irq); - - queue_work(priv->an_workqueue, &priv->an_irq_work); - - return IRQ_HANDLED; -} - -static void amd_xgbe_an_irq_work(struct work_struct *work) -{ - struct amd_xgbe_phy_priv *priv = container_of(work, - struct amd_xgbe_phy_priv, - an_irq_work); - - /* Avoid a race between enabling the IRQ and exiting the work by - * waiting for the work to finish and then queueing it - */ - flush_work(&priv->an_work); - queue_work(priv->an_workqueue, &priv->an_work); -} - -static void amd_xgbe_an_state_machine(struct work_struct *work) -{ - struct amd_xgbe_phy_priv *priv = container_of(work, - struct amd_xgbe_phy_priv, - an_work); - struct phy_device *phydev = priv->phydev; - enum amd_xgbe_phy_an cur_state = priv->an_state; - int int_reg, int_mask; - - mutex_lock(&priv->an_mutex); - - /* Read the interrupt */ - int_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT); - if (!int_reg) - goto out; - -next_int: - if (int_reg < 0) { - priv->an_state = AMD_XGBE_AN_ERROR; - int_mask = XGBE_AN_INT_MASK; - } else if (int_reg & XGBE_AN_PG_RCV) { - priv->an_state = AMD_XGBE_AN_PAGE_RECEIVED; - int_mask = XGBE_AN_PG_RCV; - } else if (int_reg & XGBE_AN_INC_LINK) { - priv->an_state = AMD_XGBE_AN_INCOMPAT_LINK; - int_mask = XGBE_AN_INC_LINK; - } else if (int_reg & XGBE_AN_INT_CMPLT) { - priv->an_state = AMD_XGBE_AN_COMPLETE; - int_mask = XGBE_AN_INT_CMPLT; - } else { - priv->an_state = AMD_XGBE_AN_ERROR; - int_mask = 0; - } - - /* Clear the interrupt to be processed */ - int_reg &= ~int_mask; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, int_reg); - - priv->an_result = priv->an_state; - -again: - cur_state = priv->an_state; - - switch (priv->an_state) { - case AMD_XGBE_AN_READY: - priv->an_supported = 0; - break; - - case AMD_XGBE_AN_PAGE_RECEIVED: - priv->an_state = amd_xgbe_an_page_received(phydev); - priv->an_supported++; - break; - - case AMD_XGBE_AN_INCOMPAT_LINK: - priv->an_supported = 0; - priv->parallel_detect = 0; - priv->an_state = amd_xgbe_an_incompat_link(phydev); - break; - - case AMD_XGBE_AN_COMPLETE: - priv->parallel_detect = priv->an_supported ? 0 : 1; - netdev_dbg(phydev->attached_dev, "%s successful\n", - priv->an_supported ? "Auto negotiation" - : "Parallel detection"); - break; - - case AMD_XGBE_AN_NO_LINK: - break; - - default: - priv->an_state = AMD_XGBE_AN_ERROR; - } - - if (priv->an_state == AMD_XGBE_AN_NO_LINK) { - int_reg = 0; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - } else if (priv->an_state == AMD_XGBE_AN_ERROR) { - netdev_err(phydev->attached_dev, - "error during auto-negotiation, state=%u\n", - cur_state); - - int_reg = 0; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - } - - if (priv->an_state >= AMD_XGBE_AN_COMPLETE) { - priv->an_result = priv->an_state; - priv->an_state = AMD_XGBE_AN_READY; - priv->kr_state = AMD_XGBE_RX_BPA; - priv->kx_state = AMD_XGBE_RX_BPA; - priv->an_start = 0; - } - - if (cur_state != priv->an_state) - goto again; - - if (int_reg) - goto next_int; - -out: - enable_irq(priv->an_irq); - - mutex_unlock(&priv->an_mutex); -} - -static int amd_xgbe_an_init(struct phy_device *phydev) -{ - int ret; - - /* Set up Advertisement register 3 first */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (ret < 0) - return ret; - - if (phydev->advertising & SUPPORTED_10000baseR_FEC) - ret |= 0xc000; - else - ret &= ~0xc000; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret); - - /* Set up Advertisement register 2 next */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); - if (ret < 0) - return ret; - - if (phydev->advertising & SUPPORTED_10000baseKR_Full) - ret |= 0x80; - else - ret &= ~0x80; - - if ((phydev->advertising & SUPPORTED_1000baseKX_Full) || - (phydev->advertising & SUPPORTED_2500baseX_Full)) - ret |= 0x20; - else - ret &= ~0x20; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret); - - /* Set up Advertisement register 1 last */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (ret < 0) - return ret; - - if (phydev->advertising & SUPPORTED_Pause) - ret |= 0x400; - else - ret &= ~0x400; - - if (phydev->advertising & SUPPORTED_Asym_Pause) - ret |= 0x800; - else - ret &= ~0x800; - - /* We don't intend to perform XNP */ - ret &= ~XNP_NP_EXCHANGE; - - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret); - - return 0; -} - -static int amd_xgbe_phy_soft_reset(struct phy_device *phydev) -{ - int count, ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret |= MDIO_CTRL1_RESET; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - count = 50; - do { - msleep(20); - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - return ret; - } while ((ret & MDIO_CTRL1_RESET) && --count); - - if (ret & MDIO_CTRL1_RESET) - return -ETIMEDOUT; - - /* Disable auto-negotiation for now */ - ret = amd_xgbe_phy_disable_an(phydev); - if (ret < 0) - return ret; - - /* Clear auto-negotiation interrupts */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - - return 0; -} - -static int amd_xgbe_phy_config_init(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - struct net_device *netdev = phydev->attached_dev; - int ret; - - if (!priv->an_irq_allocated) { - /* Allocate the auto-negotiation workqueue and interrupt */ - snprintf(priv->an_irq_name, sizeof(priv->an_irq_name) - 1, - "%s-pcs", netdev_name(netdev)); - - priv->an_workqueue = - create_singlethread_workqueue(priv->an_irq_name); - if (!priv->an_workqueue) { - netdev_err(netdev, "phy workqueue creation failed\n"); - return -ENOMEM; - } - - ret = devm_request_irq(priv->dev, priv->an_irq, - amd_xgbe_an_isr, 0, priv->an_irq_name, - priv); - if (ret) { - netdev_err(netdev, "phy irq request failed\n"); - destroy_workqueue(priv->an_workqueue); - return ret; - } - - priv->an_irq_allocated = 1; - } - - /* Set initial mode - call the mode setting routines - * directly to insure we are properly configured - */ - if (amd_xgbe_phy_use_xgmii_mode(phydev)) - ret = amd_xgbe_phy_xgmii_mode(phydev); - else if (amd_xgbe_phy_use_gmii_mode(phydev)) - ret = amd_xgbe_phy_gmii_mode(phydev); - else if (amd_xgbe_phy_use_gmii_2500_mode(phydev)) - ret = amd_xgbe_phy_gmii_2500_mode(phydev); - else - ret = -EINVAL; - if (ret < 0) - return ret; - - /* Set up advertisement registers based on current settings */ - ret = amd_xgbe_an_init(phydev); - if (ret) - return ret; - - /* Enable auto-negotiation interrupts */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07); - - return 0; -} - -static int amd_xgbe_phy_setup_forced(struct phy_device *phydev) -{ - int ret; - - /* Disable auto-negotiation */ - ret = amd_xgbe_phy_disable_an(phydev); - if (ret < 0) - return ret; - - /* Validate/Set specified speed */ - switch (phydev->speed) { - case SPEED_10000: - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); - break; - - case SPEED_2500: - case SPEED_1000: - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); - break; - - default: - ret = -EINVAL; - } - - if (ret < 0) - return ret; - - /* Validate duplex mode */ - if (phydev->duplex != DUPLEX_FULL) - return -EINVAL; - - phydev->pause = 0; - phydev->asym_pause = 0; - - return 0; -} - -static int __amd_xgbe_phy_config_aneg(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - u32 mmd_mask = phydev->c45_ids.devices_in_package; - int ret; - - if (phydev->autoneg != AUTONEG_ENABLE) - return amd_xgbe_phy_setup_forced(phydev); - - /* Make sure we have the AN MMD present */ - if (!(mmd_mask & MDIO_DEVS_AN)) - return -EINVAL; - - /* Disable auto-negotiation interrupt */ - disable_irq(priv->an_irq); - - /* Start auto-negotiation in a supported mode */ - if (phydev->advertising & SUPPORTED_10000baseKR_Full) - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); - else if ((phydev->advertising & SUPPORTED_1000baseKX_Full) || - (phydev->advertising & SUPPORTED_2500baseX_Full)) - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); - else - ret = -EINVAL; - if (ret < 0) { - enable_irq(priv->an_irq); - return ret; - } - - /* Disable and stop any in progress auto-negotiation */ - ret = amd_xgbe_phy_disable_an(phydev); - if (ret < 0) - return ret; - - /* Clear any auto-negotitation interrupts */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - - priv->an_result = AMD_XGBE_AN_READY; - priv->an_state = AMD_XGBE_AN_READY; - priv->kr_state = AMD_XGBE_RX_BPA; - priv->kx_state = AMD_XGBE_RX_BPA; - - /* Re-enable auto-negotiation interrupt */ - enable_irq(priv->an_irq); - - /* Set up advertisement registers based on current settings */ - ret = amd_xgbe_an_init(phydev); - if (ret) - return ret; - - /* Enable and start auto-negotiation */ - return amd_xgbe_phy_restart_an(phydev); -} - -static int amd_xgbe_phy_config_aneg(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - mutex_lock(&priv->an_mutex); - - ret = __amd_xgbe_phy_config_aneg(phydev); - - mutex_unlock(&priv->an_mutex); - - return ret; -} - -static int amd_xgbe_phy_aneg_done(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - - return (priv->an_result == AMD_XGBE_AN_COMPLETE); -} - -static int amd_xgbe_phy_update_link(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* If we're doing auto-negotiation don't report link down */ - if (priv->an_state != AMD_XGBE_AN_READY) { - phydev->link = 1; - return 0; - } - - /* Link status is latched low, so read once to clear - * and then read again to get current state - */ - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); - if (ret < 0) - return ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_STAT1); - if (ret < 0) - return ret; - - phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0; - - return 0; -} - -static int amd_xgbe_phy_read_status(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - u32 mmd_mask = phydev->c45_ids.devices_in_package; - int ret, ad_ret, lp_ret; - - ret = amd_xgbe_phy_update_link(phydev); - if (ret) - return ret; - - if ((phydev->autoneg == AUTONEG_ENABLE) && - !priv->parallel_detect) { - if (!(mmd_mask & MDIO_DEVS_AN)) - return -EINVAL; - - if (!amd_xgbe_phy_aneg_done(phydev)) - return 0; - - /* Compare Advertisement and Link Partner register 1 */ - ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE); - if (ad_ret < 0) - return ad_ret; - lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA); - if (lp_ret < 0) - return lp_ret; - - ad_ret &= lp_ret; - phydev->pause = (ad_ret & 0x400) ? 1 : 0; - phydev->asym_pause = (ad_ret & 0x800) ? 1 : 0; - - /* Compare Advertisement and Link Partner register 2 */ - ad_ret = phy_read_mmd(phydev, MDIO_MMD_AN, - MDIO_AN_ADVERTISE + 1); - if (ad_ret < 0) - return ad_ret; - lp_ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA + 1); - if (lp_ret < 0) - return lp_ret; - - ad_ret &= lp_ret; - if (ad_ret & 0x80) { - phydev->speed = SPEED_10000; - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR); - if (ret) - return ret; - } else { - switch (priv->speed_set) { - case AMD_XGBE_PHY_SPEEDSET_1000_10000: - phydev->speed = SPEED_1000; - break; - - case AMD_XGBE_PHY_SPEEDSET_2500_10000: - phydev->speed = SPEED_2500; - break; - } - - ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX); - if (ret) - return ret; - } - - phydev->duplex = DUPLEX_FULL; - } else { - if (amd_xgbe_phy_in_kr_mode(phydev)) { - phydev->speed = SPEED_10000; - } else { - switch (priv->speed_set) { - case AMD_XGBE_PHY_SPEEDSET_1000_10000: - phydev->speed = SPEED_1000; - break; - - case AMD_XGBE_PHY_SPEEDSET_2500_10000: - phydev->speed = SPEED_2500; - break; - } - } - phydev->duplex = DUPLEX_FULL; - phydev->pause = 0; - phydev->asym_pause = 0; - } - - return 0; -} - -static int amd_xgbe_phy_suspend(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - mutex_lock(&phydev->lock); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - goto unlock; - - priv->lpm_ctrl = ret; - - ret |= MDIO_CTRL1_LPOWER; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret); - - ret = 0; - -unlock: - mutex_unlock(&phydev->lock); - - return ret; -} - -static int amd_xgbe_phy_resume(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - - mutex_lock(&phydev->lock); - - priv->lpm_ctrl &= ~MDIO_CTRL1_LPOWER; - phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, priv->lpm_ctrl); - - mutex_unlock(&phydev->lock); - - return 0; -} - -static unsigned int amd_xgbe_phy_resource_count(struct platform_device *pdev, - unsigned int type) -{ - unsigned int count; - int i; - - for (i = 0, count = 0; i < pdev->num_resources; i++) { - struct resource *r = &pdev->resource[i]; - - if (type == resource_type(r)) - count++; - } - - return count; -} - -static int amd_xgbe_phy_probe(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv; - struct platform_device *phy_pdev; - struct device *dev, *phy_dev; - unsigned int phy_resnum, phy_irqnum; - int ret; - - if (!phydev->bus || !phydev->bus->parent) - return -EINVAL; - - dev = phydev->bus->parent; - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - priv->pdev = to_platform_device(dev); - priv->adev = ACPI_COMPANION(dev); - priv->dev = dev; - priv->phydev = phydev; - mutex_init(&priv->an_mutex); - INIT_WORK(&priv->an_irq_work, amd_xgbe_an_irq_work); - INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine); - - if (!priv->adev || acpi_disabled) { - struct device_node *bus_node; - struct device_node *phy_node; - - bus_node = priv->dev->of_node; - phy_node = of_parse_phandle(bus_node, "phy-handle", 0); - if (!phy_node) { - dev_err(dev, "unable to parse phy-handle\n"); - ret = -EINVAL; - goto err_priv; - } - - phy_pdev = of_find_device_by_node(phy_node); - of_node_put(phy_node); - - if (!phy_pdev) { - dev_err(dev, "unable to obtain phy device\n"); - ret = -EINVAL; - goto err_priv; - } - - phy_resnum = 0; - phy_irqnum = 0; - } else { - /* In ACPI, the XGBE and PHY resources are the grouped - * together with the PHY resources at the end - */ - phy_pdev = priv->pdev; - phy_resnum = amd_xgbe_phy_resource_count(phy_pdev, - IORESOURCE_MEM) - 3; - phy_irqnum = amd_xgbe_phy_resource_count(phy_pdev, - IORESOURCE_IRQ) - 1; - } - phy_dev = &phy_pdev->dev; - - /* Get the device mmio areas */ - priv->rxtx_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, - phy_resnum++); - priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res); - if (IS_ERR(priv->rxtx_regs)) { - dev_err(dev, "rxtx ioremap failed\n"); - ret = PTR_ERR(priv->rxtx_regs); - goto err_put; - } - - priv->sir0_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, - phy_resnum++); - priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res); - if (IS_ERR(priv->sir0_regs)) { - dev_err(dev, "sir0 ioremap failed\n"); - ret = PTR_ERR(priv->sir0_regs); - goto err_rxtx; - } - - priv->sir1_res = platform_get_resource(phy_pdev, IORESOURCE_MEM, - phy_resnum++); - priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res); - if (IS_ERR(priv->sir1_regs)) { - dev_err(dev, "sir1 ioremap failed\n"); - ret = PTR_ERR(priv->sir1_regs); - goto err_sir0; - } - - /* Get the auto-negotiation interrupt */ - ret = platform_get_irq(phy_pdev, phy_irqnum); - if (ret < 0) { - dev_err(dev, "platform_get_irq failed\n"); - goto err_sir1; - } - priv->an_irq = ret; - - /* Get the device speed set property */ - ret = device_property_read_u32(phy_dev, XGBE_PHY_SPEEDSET_PROPERTY, - &priv->speed_set); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_SPEEDSET_PROPERTY); - goto err_sir1; - } - - switch (priv->speed_set) { - case AMD_XGBE_PHY_SPEEDSET_1000_10000: - case AMD_XGBE_PHY_SPEEDSET_2500_10000: - break; - default: - dev_err(dev, "invalid %s property\n", - XGBE_PHY_SPEEDSET_PROPERTY); - ret = -EINVAL; - goto err_sir1; - } - - if (device_property_present(phy_dev, XGBE_PHY_BLWC_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_BLWC_PROPERTY, - priv->serdes_blwc, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_BLWC_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_blwc, amd_xgbe_phy_serdes_blwc, - sizeof(priv->serdes_blwc)); - } - - if (device_property_present(phy_dev, XGBE_PHY_CDR_RATE_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_CDR_RATE_PROPERTY, - priv->serdes_cdr_rate, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_CDR_RATE_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_cdr_rate, amd_xgbe_phy_serdes_cdr_rate, - sizeof(priv->serdes_cdr_rate)); - } - - if (device_property_present(phy_dev, XGBE_PHY_PQ_SKEW_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_PQ_SKEW_PROPERTY, - priv->serdes_pq_skew, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_PQ_SKEW_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_pq_skew, amd_xgbe_phy_serdes_pq_skew, - sizeof(priv->serdes_pq_skew)); - } - - if (device_property_present(phy_dev, XGBE_PHY_TX_AMP_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_TX_AMP_PROPERTY, - priv->serdes_tx_amp, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_TX_AMP_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_tx_amp, amd_xgbe_phy_serdes_tx_amp, - sizeof(priv->serdes_tx_amp)); - } - - if (device_property_present(phy_dev, XGBE_PHY_DFE_CFG_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_DFE_CFG_PROPERTY, - priv->serdes_dfe_tap_cfg, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_DFE_CFG_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_dfe_tap_cfg, - amd_xgbe_phy_serdes_dfe_tap_cfg, - sizeof(priv->serdes_dfe_tap_cfg)); - } - - if (device_property_present(phy_dev, XGBE_PHY_DFE_ENA_PROPERTY)) { - ret = device_property_read_u32_array(phy_dev, - XGBE_PHY_DFE_ENA_PROPERTY, - priv->serdes_dfe_tap_ena, - XGBE_PHY_SPEEDS); - if (ret) { - dev_err(dev, "invalid %s property\n", - XGBE_PHY_DFE_ENA_PROPERTY); - goto err_sir1; - } - } else { - memcpy(priv->serdes_dfe_tap_ena, - amd_xgbe_phy_serdes_dfe_tap_ena, - sizeof(priv->serdes_dfe_tap_ena)); - } - - /* Initialize supported features */ - phydev->supported = SUPPORTED_Autoneg; - phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - phydev->supported |= SUPPORTED_Backplane; - phydev->supported |= SUPPORTED_10000baseKR_Full; - switch (priv->speed_set) { - case AMD_XGBE_PHY_SPEEDSET_1000_10000: - phydev->supported |= SUPPORTED_1000baseKX_Full; - break; - case AMD_XGBE_PHY_SPEEDSET_2500_10000: - phydev->supported |= SUPPORTED_2500baseX_Full; - break; - } - - ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_ABILITY); - if (ret < 0) - return ret; - priv->fec_ability = ret & XGBE_PHY_FEC_MASK; - if (priv->fec_ability & XGBE_PHY_FEC_ENABLE) - phydev->supported |= SUPPORTED_10000baseR_FEC; - - phydev->advertising = phydev->supported; - - phydev->priv = priv; - - if (!priv->adev || acpi_disabled) - platform_device_put(phy_pdev); - - return 0; - -err_sir1: - devm_iounmap(dev, priv->sir1_regs); - devm_release_mem_region(dev, priv->sir1_res->start, - resource_size(priv->sir1_res)); - -err_sir0: - devm_iounmap(dev, priv->sir0_regs); - devm_release_mem_region(dev, priv->sir0_res->start, - resource_size(priv->sir0_res)); - -err_rxtx: - devm_iounmap(dev, priv->rxtx_regs); - devm_release_mem_region(dev, priv->rxtx_res->start, - resource_size(priv->rxtx_res)); - -err_put: - if (!priv->adev || acpi_disabled) - platform_device_put(phy_pdev); - -err_priv: - devm_kfree(dev, priv); - - return ret; -} - -static void amd_xgbe_phy_remove(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - struct device *dev = priv->dev; - - if (priv->an_irq_allocated) { - devm_free_irq(dev, priv->an_irq, priv); - - flush_workqueue(priv->an_workqueue); - destroy_workqueue(priv->an_workqueue); - } - - /* Release resources */ - devm_iounmap(dev, priv->sir1_regs); - devm_release_mem_region(dev, priv->sir1_res->start, - resource_size(priv->sir1_res)); - - devm_iounmap(dev, priv->sir0_regs); - devm_release_mem_region(dev, priv->sir0_res->start, - resource_size(priv->sir0_res)); - - devm_iounmap(dev, priv->rxtx_regs); - devm_release_mem_region(dev, priv->rxtx_res->start, - resource_size(priv->rxtx_res)); - - devm_kfree(dev, priv); -} - -static int amd_xgbe_match_phy_device(struct phy_device *phydev) -{ - return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID; -} - -static struct phy_driver amd_xgbe_phy_driver[] = { - { - .phy_id = XGBE_PHY_ID, - .phy_id_mask = XGBE_PHY_MASK, - .name = "AMD XGBE PHY", - .features = 0, - .flags = PHY_IS_INTERNAL, - .probe = amd_xgbe_phy_probe, - .remove = amd_xgbe_phy_remove, - .soft_reset = amd_xgbe_phy_soft_reset, - .config_init = amd_xgbe_phy_config_init, - .suspend = amd_xgbe_phy_suspend, - .resume = amd_xgbe_phy_resume, - .config_aneg = amd_xgbe_phy_config_aneg, - .aneg_done = amd_xgbe_phy_aneg_done, - .read_status = amd_xgbe_phy_read_status, - .match_phy_device = amd_xgbe_match_phy_device, - .driver = { - .owner = THIS_MODULE, - }, - }, -}; - -module_phy_driver(amd_xgbe_phy_driver); - -static struct mdio_device_id __maybe_unused amd_xgbe_phy_ids[] = { - { XGBE_PHY_ID, XGBE_PHY_MASK }, - { } -}; -MODULE_DEVICE_TABLE(mdio, amd_xgbe_phy_ids); diff --git a/drivers/net/phy/bcm7xxx.c b/drivers/net/phy/bcm7xxx.c index b5dc59de0..6b701b3de 100644 --- a/drivers/net/phy/bcm7xxx.c +++ b/drivers/net/phy/bcm7xxx.c @@ -136,8 +136,8 @@ static int bcm7xxx_28nm_d0_afe_config_init(struct phy_device *phydev) /* AFE_RX_LP_COUNTER, set RX bandwidth to maximum */ phy_write_misc(phydev, AFE_RX_LP_COUNTER, 0x7fc0); - /* AFE_TX_CONFIG, set 1000BT Cfeed=110 for all ports */ - phy_write_misc(phydev, AFE_TX_CONFIG, 0x0061); + /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ + phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); @@ -167,6 +167,9 @@ static int bcm7xxx_28nm_e0_plus_afe_config_init(struct phy_device *phydev) /* AFE_RXCONFIG_1, provide more margin for INL/DNL measurement */ phy_write_misc(phydev, AFE_RXCONFIG_1, 0x9b2f); + /* AFE_TX_CONFIG, set 100BT Cfeed=011 to improve rise/fall time */ + phy_write_misc(phydev, AFE_TX_CONFIG, 0x431); + /* AFE_VDCA_ICTRL_0, set Iq=1101 instead of 0111 for AB symmetry */ phy_write_misc(phydev, AFE_VDCA_ICTRL_0, 0xa7da); @@ -243,6 +246,13 @@ static int bcm7xxx_28nm_config_init(struct phy_device *phydev) pr_info_once("%s: %s PHY revision: 0x%02x, patch: %d\n", dev_name(&phydev->dev), phydev->drv->name, rev, patch); + /* Dummy read to a register to workaround an issue upon reset where the + * internal inverter may not allow the first MDIO transaction to pass + * the MDIO management controller and make us return 0xffff for such + * reads. + */ + phy_read(phydev, MII_BMSR); + switch (rev) { case 0xb0: ret = bcm7xxx_28nm_b0_afe_config_init(phydev); diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c index 0d16c7d9e..2a328703b 100644 --- a/drivers/net/phy/davicom.c +++ b/drivers/net/phy/davicom.c @@ -158,6 +158,18 @@ static struct phy_driver dm91xx_driver[] = { .config_intr = dm9161_config_intr, .driver = { .owner = THIS_MODULE,}, }, { + .phy_id = 0x0181b8b0, + .name = "Davicom DM9161B/C", + .phy_id_mask = 0x0ffffff0, + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = dm9161_config_init, + .config_aneg = dm9161_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = dm9161_ack_interrupt, + .config_intr = dm9161_config_intr, + .driver = { .owner = THIS_MODULE,}, +}, { .phy_id = 0x0181b8a0, .name = "Davicom DM9161A", .phy_id_mask = 0x0ffffff0, @@ -186,6 +198,7 @@ module_phy_driver(dm91xx_driver); static struct mdio_device_id __maybe_unused davicom_tbl[] = { { 0x0181b880, 0x0ffffff0 }, + { 0x0181b8b0, 0x0ffffff0 }, { 0x0181b8a0, 0x0ffffff0 }, { 0x00181b80, 0x0ffffff0 }, { } diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c new file mode 100644 index 000000000..8a3bf5469 --- /dev/null +++ b/drivers/net/phy/dp83867.c @@ -0,0 +1,239 @@ +/* + * Driver for the Texas Instruments DP83867 PHY + * + * Copyright (C) 2015 Texas Instruments Inc. + * + * This program is free software; you can redistribute it 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. + * + * This program 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. + */ + +#include <linux/ethtool.h> +#include <linux/kernel.h> +#include <linux/mii.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy.h> + +#include <dt-bindings/net/ti-dp83867.h> + +#define DP83867_PHY_ID 0x2000a231 +#define DP83867_DEVADDR 0x1f + +#define MII_DP83867_PHYCTRL 0x10 +#define MII_DP83867_MICR 0x12 +#define MII_DP83867_ISR 0x13 +#define DP83867_CTRL 0x1f + +/* Extended Registers */ +#define DP83867_RGMIICTL 0x0032 +#define DP83867_RGMIIDCTL 0x0086 + +#define DP83867_SW_RESET BIT(15) +#define DP83867_SW_RESTART BIT(14) + +/* MICR Interrupt bits */ +#define MII_DP83867_MICR_AN_ERR_INT_EN BIT(15) +#define MII_DP83867_MICR_SPEED_CHNG_INT_EN BIT(14) +#define MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN BIT(13) +#define MII_DP83867_MICR_PAGE_RXD_INT_EN BIT(12) +#define MII_DP83867_MICR_AUTONEG_COMP_INT_EN BIT(11) +#define MII_DP83867_MICR_LINK_STS_CHNG_INT_EN BIT(10) +#define MII_DP83867_MICR_FALSE_CARRIER_INT_EN BIT(8) +#define MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN BIT(4) +#define MII_DP83867_MICR_WOL_INT_EN BIT(3) +#define MII_DP83867_MICR_XGMII_ERR_INT_EN BIT(2) +#define MII_DP83867_MICR_POL_CHNG_INT_EN BIT(1) +#define MII_DP83867_MICR_JABBER_INT_EN BIT(0) + +/* RGMIICTL bits */ +#define DP83867_RGMII_TX_CLK_DELAY_EN BIT(1) +#define DP83867_RGMII_RX_CLK_DELAY_EN BIT(0) + +/* PHY CTRL bits */ +#define DP83867_PHYCR_FIFO_DEPTH_SHIFT 14 + +/* RGMIIDCTL bits */ +#define DP83867_RGMII_TX_CLK_DELAY_SHIFT 4 + +struct dp83867_private { + int rx_id_delay; + int tx_id_delay; + int fifo_depth; +}; + +static int dp83867_ack_interrupt(struct phy_device *phydev) +{ + int err = phy_read(phydev, MII_DP83867_ISR); + + if (err < 0) + return err; + + return 0; +} + +static int dp83867_config_intr(struct phy_device *phydev) +{ + int micr_status; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { + micr_status = phy_read(phydev, MII_DP83867_MICR); + if (micr_status < 0) + return micr_status; + + micr_status |= + (MII_DP83867_MICR_AN_ERR_INT_EN | + MII_DP83867_MICR_SPEED_CHNG_INT_EN | + MII_DP83867_MICR_DUP_MODE_CHNG_INT_EN | + MII_DP83867_MICR_SLEEP_MODE_CHNG_INT_EN); + + return phy_write(phydev, MII_DP83867_MICR, micr_status); + } + + micr_status = 0x0; + return phy_write(phydev, MII_DP83867_MICR, micr_status); +} + +#ifdef CONFIG_OF_MDIO +static int dp83867_of_init(struct phy_device *phydev) +{ + struct dp83867_private *dp83867 = phydev->priv; + struct device *dev = &phydev->dev; + struct device_node *of_node = dev->of_node; + int ret; + + if (!of_node && dev->parent->of_node) + of_node = dev->parent->of_node; + + if (!phydev->dev.of_node) + return -ENODEV; + + ret = of_property_read_u32(of_node, "ti,rx-internal-delay", + &dp83867->rx_id_delay); + if (ret) + return ret; + + ret = of_property_read_u32(of_node, "ti,tx-internal-delay", + &dp83867->tx_id_delay); + if (ret) + return ret; + + ret = of_property_read_u32(of_node, "ti,fifo-depth", + &dp83867->fifo_depth); + if (ret) + return ret; + + return 0; +} +#else +static int dp83867_of_init(struct phy_device *phydev) +{ + return 0; +} +#endif /* CONFIG_OF_MDIO */ + +static int dp83867_config_init(struct phy_device *phydev) +{ + struct dp83867_private *dp83867; + int ret; + u16 val, delay; + + if (!phydev->priv) { + dp83867 = devm_kzalloc(&phydev->dev, sizeof(*dp83867), + GFP_KERNEL); + if (!dp83867) + return -ENOMEM; + + phydev->priv = dp83867; + ret = dp83867_of_init(phydev); + if (ret) + return ret; + } else { + dp83867 = (struct dp83867_private *)phydev->priv; + } + + if (phy_interface_is_rgmii(phydev)) { + ret = phy_write(phydev, MII_DP83867_PHYCTRL, + (dp83867->fifo_depth << DP83867_PHYCR_FIFO_DEPTH_SHIFT)); + if (ret) + return ret; + } + + if ((phydev->interface >= PHY_INTERFACE_MODE_RGMII_ID) && + (phydev->interface <= PHY_INTERFACE_MODE_RGMII_RXID)) { + val = phy_read_mmd_indirect(phydev, DP83867_RGMIICTL, + DP83867_DEVADDR, phydev->addr); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) + val |= (DP83867_RGMII_TX_CLK_DELAY_EN | DP83867_RGMII_RX_CLK_DELAY_EN); + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) + val |= DP83867_RGMII_TX_CLK_DELAY_EN; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) + val |= DP83867_RGMII_RX_CLK_DELAY_EN; + + phy_write_mmd_indirect(phydev, DP83867_RGMIICTL, + DP83867_DEVADDR, phydev->addr, val); + + delay = (dp83867->rx_id_delay | + (dp83867->tx_id_delay << DP83867_RGMII_TX_CLK_DELAY_SHIFT)); + + phy_write_mmd_indirect(phydev, DP83867_RGMIIDCTL, + DP83867_DEVADDR, phydev->addr, delay); + } + + return 0; +} + +static int dp83867_phy_reset(struct phy_device *phydev) +{ + int err; + + err = phy_write(phydev, DP83867_CTRL, DP83867_SW_RESET); + if (err < 0) + return err; + + return dp83867_config_init(phydev); +} + +static struct phy_driver dp83867_driver[] = { + { + .phy_id = DP83867_PHY_ID, + .phy_id_mask = 0xfffffff0, + .name = "TI DP83867", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + + .config_init = dp83867_config_init, + .soft_reset = dp83867_phy_reset, + + /* IRQ related */ + .ack_interrupt = dp83867_ack_interrupt, + .config_intr = dp83867_config_intr, + + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .suspend = genphy_suspend, + .resume = genphy_resume, + + .driver = {.owner = THIS_MODULE,} + }, +}; +module_phy_driver(dp83867_driver); + +static struct mdio_device_id __maybe_unused dp83867_tbl[] = { + { DP83867_PHY_ID, 0xfffffff0 }, + { } +}; + +MODULE_DEVICE_TABLE(mdio, dp83867_tbl); + +MODULE_DESCRIPTION("Texas Instruments DP83867 PHY driver"); +MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 1960b46ad..d7a65247f 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -290,6 +290,15 @@ struct phy_device *fixed_phy_register(unsigned int irq, return ERR_PTR(-EINVAL); } + /* propagate the fixed link values to struct phy_device */ + phy->link = status->link; + if (status->link) { + phy->speed = status->speed; + phy->duplex = status->duplex; + phy->pause = status->pause; + phy->asym_pause = status->asym_pause; + } + of_node_get(np); phy->dev.of_node = np; diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c index 8644f039d..0dbc445a5 100644 --- a/drivers/net/phy/icplus.c +++ b/drivers/net/phy/icplus.c @@ -139,10 +139,7 @@ static int ip1001_config_init(struct phy_device *phydev) if (c < 0) return c; - if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + if (phy_interface_is_rgmii(phydev)) { c = phy_read(phydev, IP10XX_SPEC_CTRL_STATUS); if (c < 0) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 1b1698f98..f721444c2 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -317,10 +317,7 @@ static int m88e1121_config_aneg(struct phy_device *phydev) if (err < 0) return err; - if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + if (phy_interface_is_rgmii(phydev)) { mscr = phy_read(phydev, MII_88E1121_PHY_MSCR_REG) & MII_88E1121_PHY_MSCR_DELAY_MASK; @@ -469,10 +466,7 @@ static int m88e1111_config_init(struct phy_device *phydev) int err; int temp; - if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || - (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { + if (phy_interface_is_rgmii(phydev)) { temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); if (temp < 0) diff --git a/drivers/net/phy/mdio-bcm-unimac.c b/drivers/net/phy/mdio-bcm-unimac.c index 414fdf1f3..6a52a7f0f 100644 --- a/drivers/net/phy/mdio-bcm-unimac.c +++ b/drivers/net/phy/mdio-bcm-unimac.c @@ -81,7 +81,13 @@ static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg) return -ETIMEDOUT; cmd = __raw_readl(priv->base + MDIO_CMD); - if (cmd & MDIO_READ_FAIL) + + /* Some broken devices are known not to release the line during + * turn-around, e.g: Broadcom BCM53125 external switches, so check for + * that condition here and ignore the MDIO controller read failure + * indication. + */ + if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL)) return -EIO; return cmd & 0xffff; @@ -114,6 +120,48 @@ static int unimac_mdio_write(struct mii_bus *bus, int phy_id, return 0; } +/* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with + * their internal MDIO management controller making them fail to successfully + * be read from or written to for the first transaction. We insert a dummy + * BMSR read here to make sure that phy_get_device() and get_phy_id() can + * correctly read the PHY MII_PHYSID1/2 registers and successfully register a + * PHY device for this peripheral. + * + * Once the PHY driver is registered, we can workaround subsequent reads from + * there (e.g: during system-wide power management). + * + * bus->reset is invoked before mdiobus_scan during mdiobus_register and is + * therefore the right location to stick that workaround. Since we do not want + * to read from non-existing PHYs, we either use bus->phy_mask or do a manual + * Device Tree scan to limit the search area. + */ +static int unimac_mdio_reset(struct mii_bus *bus) +{ + struct device_node *np = bus->dev.of_node; + struct device_node *child; + u32 read_mask = 0; + int addr; + + if (!np) { + read_mask = ~bus->phy_mask; + } else { + for_each_available_child_of_node(np, child) { + addr = of_mdio_parse_addr(&bus->dev, child); + if (addr < 0) + continue; + + read_mask |= 1 << addr; + } + } + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + if (read_mask & 1 << addr) + mdiobus_read(bus, addr, MII_BMSR); + } + + return 0; +} + static int unimac_mdio_probe(struct platform_device *pdev) { struct unimac_mdio_priv *priv; @@ -149,6 +197,7 @@ static int unimac_mdio_probe(struct platform_device *pdev) bus->parent = &pdev->dev; bus->read = unimac_mdio_read; bus->write = unimac_mdio_write; + bus->reset = unimac_mdio_reset; snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name); bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); diff --git a/drivers/net/phy/mdio-bitbang.c b/drivers/net/phy/mdio-bitbang.c index daec9b05d..61a543c78 100644 --- a/drivers/net/phy/mdio-bitbang.c +++ b/drivers/net/phy/mdio-bitbang.c @@ -165,8 +165,11 @@ static int mdiobb_read(struct mii_bus *bus, int phy, int reg) ctrl->ops->set_mdio_dir(ctrl, 0); - /* check the turnaround bit: the PHY should be driving it to zero */ - if (mdiobb_get_bit(ctrl) != 0) { + /* check the turnaround bit: the PHY should be driving it to zero, if this + * PHY is listed in phy_ignore_ta_mask as having broken TA, skip that + */ + if (mdiobb_get_bit(ctrl) != 0 && + !(bus->phy_ignore_ta_mask & (1 << phy))) { /* PHY didn't drive TA low -- flush any bits it * may be trying to send. */ diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 53d18150f..7dc21e56a 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c @@ -158,6 +158,7 @@ static struct mii_bus *mdio_gpio_bus_init(struct device *dev, new_bus->name = "GPIO Bitbanged MDIO", new_bus->phy_mask = pdata->phy_mask; + new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask; new_bus->irq = pdata->irqs; new_bus->parent = dev; diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c index 66edd99bc..7ddb1ab70 100644 --- a/drivers/net/phy/mdio-mux-gpio.c +++ b/drivers/net/phy/mdio-mux-gpio.c @@ -35,7 +35,8 @@ static int mdio_mux_gpio_switch_fn(int current_child, int desired_child, for (n = 0; n < s->gpios->ndescs; n++) values[n] = (desired_child >> n) & 1; - gpiod_set_array_cansleep(s->gpios->ndescs, s->gpios->desc, values); + gpiod_set_array_value_cansleep(s->gpios->ndescs, s->gpios->desc, + values); return 0; } diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 095ef3fe3..46a14cbb0 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -421,6 +421,8 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) { struct phy_device *phydev = to_phy_device(dev); struct phy_driver *phydrv = to_phy_driver(drv); + const int num_ids = ARRAY_SIZE(phydev->c45_ids.device_ids); + int i; if (of_driver_match_device(dev, drv)) return 1; @@ -428,8 +430,21 @@ static int mdio_bus_match(struct device *dev, struct device_driver *drv) if (phydrv->match_phy_device) return phydrv->match_phy_device(phydev); - return (phydrv->phy_id & phydrv->phy_id_mask) == - (phydev->phy_id & phydrv->phy_id_mask); + if (phydev->is_c45) { + for (i = 1; i < num_ids; i++) { + if (!(phydev->c45_ids.devices_in_package & (1 << i))) + continue; + + if ((phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->c45_ids.device_ids[i] & + phydrv->phy_id_mask)) + return 1; + } + return 0; + } else { + return (phydrv->phy_id & phydrv->phy_id_mask) == + (phydev->phy_id & phydrv->phy_id_mask); + } } #ifdef CONFIG_PM diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index ebdc357c5..499185eaf 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -288,9 +288,10 @@ static int kszphy_config_init(struct phy_device *phydev) } static int ksz9021_load_values_from_of(struct phy_device *phydev, - struct device_node *of_node, u16 reg, - char *field1, char *field2, - char *field3, char *field4) + const struct device_node *of_node, + u16 reg, + const char *field1, const char *field2, + const char *field3, const char *field4) { int val1 = -1; int val2 = -2; @@ -336,8 +337,8 @@ static int ksz9021_load_values_from_of(struct phy_device *phydev, static int ksz9021_config_init(struct phy_device *phydev) { - struct device *dev = &phydev->dev; - struct device_node *of_node = dev->of_node; + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; if (!of_node && dev->parent->of_node) of_node = dev->parent->of_node; @@ -365,6 +366,11 @@ static int ksz9021_config_init(struct phy_device *phydev) #define KSZ9031_PS_TO_REG 60 /* Extended registers */ +/* MMD Address 0x0 */ +#define MII_KSZ9031RN_FLP_BURST_TX_LO 3 +#define MII_KSZ9031RN_FLP_BURST_TX_HI 4 + +/* MMD Address 0x2 */ #define MII_KSZ9031RN_CONTROL_PAD_SKEW 4 #define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5 #define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6 @@ -389,9 +395,9 @@ static int ksz9031_extended_read(struct phy_device *phydev, } static int ksz9031_of_load_skew_values(struct phy_device *phydev, - struct device_node *of_node, + const struct device_node *of_node, u16 reg, size_t field_sz, - char *field[], u8 numfields) + const char *field[], u8 numfields) { int val[4] = {-1, -2, -3, -4}; int matches = 0; @@ -425,20 +431,36 @@ static int ksz9031_of_load_skew_values(struct phy_device *phydev, return ksz9031_extended_write(phydev, OP_DATA, 2, reg, newval); } +static int ksz9031_center_flp_timing(struct phy_device *phydev) +{ + int result; + + /* Center KSZ9031RNX FLP timing at 16ms. */ + result = ksz9031_extended_write(phydev, OP_DATA, 0, + MII_KSZ9031RN_FLP_BURST_TX_HI, 0x0006); + result = ksz9031_extended_write(phydev, OP_DATA, 0, + MII_KSZ9031RN_FLP_BURST_TX_LO, 0x1A80); + + if (result) + return result; + + return genphy_restart_aneg(phydev); +} + static int ksz9031_config_init(struct phy_device *phydev) { - struct device *dev = &phydev->dev; - struct device_node *of_node = dev->of_node; - char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; - char *rx_data_skews[4] = { + const struct device *dev = &phydev->dev; + const struct device_node *of_node = dev->of_node; + static const char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"}; + static const char *rx_data_skews[4] = { "rxd0-skew-ps", "rxd1-skew-ps", "rxd2-skew-ps", "rxd3-skew-ps" }; - char *tx_data_skews[4] = { + static const char *tx_data_skews[4] = { "txd0-skew-ps", "txd1-skew-ps", "txd2-skew-ps", "txd3-skew-ps" }; - char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; + static const char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"}; if (!of_node && dev->parent->of_node) of_node = dev->parent->of_node; @@ -460,7 +482,8 @@ static int ksz9031_config_init(struct phy_device *phydev) MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4, tx_data_skews, 4); } - return 0; + + return ksz9031_center_flp_timing(phydev); } #define KSZ8873MLL_GLOBAL_CONTROL_4 0x06 @@ -519,7 +542,7 @@ ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum, static int kszphy_probe(struct phy_device *phydev) { const struct kszphy_type *type = phydev->drv->driver_data; - struct device_node *np = phydev->dev.of_node; + const struct device_node *np = phydev->dev.of_node; struct kszphy_priv *priv; struct clk *clk; int ret; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 47cd57805..34fe339f4 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -58,6 +58,31 @@ static const char *phy_speed_to_str(int speed) } } +#define PHY_STATE_STR(_state) \ + case PHY_##_state: \ + return __stringify(_state); \ + +static const char *phy_state_to_str(enum phy_state st) +{ + switch (st) { + PHY_STATE_STR(DOWN) + PHY_STATE_STR(STARTING) + PHY_STATE_STR(READY) + PHY_STATE_STR(PENDING) + PHY_STATE_STR(UP) + PHY_STATE_STR(AN) + PHY_STATE_STR(RUNNING) + PHY_STATE_STR(NOLINK) + PHY_STATE_STR(FORCING) + PHY_STATE_STR(CHANGELINK) + PHY_STATE_STR(HALTED) + PHY_STATE_STR(RESUMING) + } + + return NULL; +} + + /** * phy_print_status - Convenience function to print out the current phy status * @phydev: the phy_device struct @@ -784,10 +809,14 @@ void phy_state_machine(struct work_struct *work) struct phy_device *phydev = container_of(dwork, struct phy_device, state_queue); bool needs_aneg = false, do_suspend = false; + enum phy_state old_state; int err = 0; + int old_link; mutex_lock(&phydev->lock); + old_state = phydev->state; + if (phydev->drv->link_change_notify) phydev->drv->link_change_notify(phydev); @@ -868,11 +897,18 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); break; case PHY_RUNNING: - /* Only register a CHANGE if we are - * polling or ignoring interrupts + /* Only register a CHANGE if we are polling or ignoring + * interrupts and link changed since latest checking. */ - if (!phy_interrupt_is_valid(phydev)) - phydev->state = PHY_CHANGELINK; + if (!phy_interrupt_is_valid(phydev)) { + old_link = phydev->link; + err = phy_read_status(phydev); + if (err) + break; + + if (old_link != phydev->link) + phydev->state = PHY_CHANGELINK; + } break; case PHY_CHANGELINK: err = phy_read_status(phydev); @@ -952,6 +988,9 @@ void phy_state_machine(struct work_struct *work) if (err < 0) phy_error(phydev); + dev_dbg(&phydev->dev, "PHY state change %s -> %s\n", + phy_state_to_str(old_state), phy_state_to_str(phydev->state)); + queue_delayed_work(system_power_efficient_wq, &phydev->state_queue, PHY_STATE_TIME * HZ); } @@ -999,10 +1038,14 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int value = -1; if (phydrv->read_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Read the content of the MMD's selected register */ - value = phydev->bus->read(phydev->bus, addr, MII_MMD_DATA); + value = bus->read(bus, addr, MII_MMD_DATA); + mutex_unlock(&bus->mdio_lock); } else { value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); } @@ -1032,10 +1075,14 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, struct phy_driver *phydrv = phydev->drv; if (phydrv->write_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Write the data into MMD's selected register */ - phydev->bus->write(phydev->bus, addr, MII_MMD_DATA, data); + bus->write(bus, addr, MII_MMD_DATA, data); + mutex_unlock(&bus->mdio_lock); } else { phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); } @@ -1062,8 +1109,7 @@ int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable) if ((phydev->duplex == DUPLEX_FULL) && ((phydev->interface == PHY_INTERFACE_MODE_MII) || (phydev->interface == PHY_INTERFACE_MODE_GMII) || - (phydev->interface >= PHY_INTERFACE_MODE_RGMII && - phydev->interface <= PHY_INTERFACE_MODE_RGMII_TXID) || + phy_interface_is_rgmii(phydev) || phy_is_internal(phydev))) { int eee_lp, eee_cap, eee_adv; u32 lp, cap, adv; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index d551df62e..55f01788d 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -176,7 +176,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, if (c45_ids) dev->c45_ids = *c45_ids; dev->bus = bus; - dev->dev.parent = bus->parent; + dev->dev.parent = &bus->dev; dev->dev.bus = &mdio_bus_type; dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); @@ -230,7 +230,7 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, for (i = 1; i < num_ids && c45_ids->devices_in_package == 0; i++) { - reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; +retry: reg_addr = MII_ADDR_C45 | i << 16 | MDIO_DEVS2; phy_reg = mdiobus_read(bus, addr, reg_addr); if (phy_reg < 0) return -EIO; @@ -242,12 +242,20 @@ static int get_phy_c45_ids(struct mii_bus *bus, int addr, u32 *phy_id, return -EIO; c45_ids->devices_in_package |= (phy_reg & 0xffff); - /* If mostly Fs, there is no device there, - * let's get out of here. - */ if ((c45_ids->devices_in_package & 0x1fffffff) == 0x1fffffff) { - *phy_id = 0xffffffff; - return 0; + if (i) { + /* If mostly Fs, there is no device there, + * then let's continue to probe more, as some + * 10G PHYs have zero Devices In package, + * e.g. Cortina CS4315/CS4340 PHY. + */ + i = 0; + goto retry; + } else { + /* no device there, let's get out of here */ + *phy_id = 0xffffffff; + return 0; + } } } diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c index 96a0f0fab..45353613b 100644 --- a/drivers/net/phy/realtek.c +++ b/drivers/net/phy/realtek.c @@ -22,8 +22,12 @@ #define RTL821x_INER 0x12 #define RTL821x_INER_INIT 0x6400 #define RTL821x_INSR 0x13 +#define RTL8211E_INER_LINK_STATUS 0x400 -#define RTL8211E_INER_LINK_STATUS 0x400 +#define RTL8211F_INER_LINK_STATUS 0x0010 +#define RTL8211F_INSR 0x1d +#define RTL8211F_PAGE_SELECT 0x1f +#define RTL8211F_TX_DELAY 0x100 MODULE_DESCRIPTION("Realtek PHY driver"); MODULE_AUTHOR("Johnson Leung"); @@ -38,6 +42,18 @@ static int rtl821x_ack_interrupt(struct phy_device *phydev) return (err < 0) ? err : 0; } +static int rtl8211f_ack_interrupt(struct phy_device *phydev) +{ + int err; + + phy_write(phydev, RTL8211F_PAGE_SELECT, 0xa43); + err = phy_read(phydev, RTL8211F_INSR); + /* restore to default page 0 */ + phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); + + return (err < 0) ? err : 0; +} + static int rtl8211b_config_intr(struct phy_device *phydev) { int err; @@ -64,6 +80,41 @@ static int rtl8211e_config_intr(struct phy_device *phydev) return err; } +static int rtl8211f_config_intr(struct phy_device *phydev) +{ + int err; + + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) + err = phy_write(phydev, RTL821x_INER, + RTL8211F_INER_LINK_STATUS); + else + err = phy_write(phydev, RTL821x_INER, 0); + + return err; +} + +static int rtl8211f_config_init(struct phy_device *phydev) +{ + int ret; + u16 reg; + + ret = genphy_config_init(phydev); + if (ret < 0) + return ret; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { + /* enable TXDLY */ + phy_write(phydev, RTL8211F_PAGE_SELECT, 0xd08); + reg = phy_read(phydev, 0x11); + reg |= RTL8211F_TX_DELAY; + phy_write(phydev, 0x11, reg); + /* restore to default page 0 */ + phy_write(phydev, RTL8211F_PAGE_SELECT, 0x0); + } + + return 0; +} + static struct phy_driver realtek_drvs[] = { { .phy_id = 0x00008201, @@ -98,6 +149,20 @@ static struct phy_driver realtek_drvs[] = { .suspend = genphy_suspend, .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, + }, { + .phy_id = 0x001cc916, + .name = "RTL8211F Gigabit Ethernet", + .phy_id_mask = 0x001fffff, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &genphy_config_aneg, + .config_init = &rtl8211f_config_init, + .read_status = &genphy_read_status, + .ack_interrupt = &rtl8211f_ack_interrupt, + .config_intr = &rtl8211f_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, + .driver = { .owner = THIS_MODULE }, }, }; @@ -106,6 +171,7 @@ module_phy_driver(realtek_drvs); static struct mdio_device_id __maybe_unused realtek_tbl[] = { { 0x001cc912, 0x001fffff }, { 0x001cc915, 0x001fffff }, + { 0x001cc916, 0x001fffff }, { } }; diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index c0f6479e1..70b089587 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -91,19 +91,18 @@ static int lan911x_config_init(struct phy_device *phydev) } /* - * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each - * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner - * does send the pulses within this interval, the PHY will remained powered - * down. - * - * This workaround will manually toggle the PHY on/off upon calls to read_status - * in order to generate link test pulses if the link is down. If a link partner - * is present, it will respond to the pulses, which will cause the ENERGYON bit - * to be set and will cause the EDPD mode to be exited. + * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable + * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to + * unstable detection of plugging in Ethernet cable. + * This workaround disables Energy Detect Power-Down mode and waiting for + * response on link pulses to detect presence of plugged Ethernet cable. + * The Energy Detect Power-Down mode is enabled again in the end of procedure to + * save approximately 220 mW of power if cable is unplugged. */ static int lan87xx_read_status(struct phy_device *phydev) { int err = genphy_read_status(phydev); + int i; if (!phydev->link) { /* Disable EDPD to wake up PHY */ @@ -116,8 +115,16 @@ static int lan87xx_read_status(struct phy_device *phydev) if (rc < 0) return rc; - /* Sleep 64 ms to allow ~5 link test pulses to be sent */ - msleep(64); + /* Wait max 640 ms to detect energy */ + for (i = 0; i < 64; i++) { + /* Sleep to allow link test pulses to be sent */ + msleep(10); + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + if (rc & MII_LAN83C185_ENERGYON) + break; + } /* Re-enable EDPD */ rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); @@ -191,7 +198,7 @@ static struct phy_driver smsc_phy_driver[] = { /* basic functions */ .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, + .read_status = lan87xx_read_status, .config_init = smsc_phy_config_init, .soft_reset = smsc_phy_reset, diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 76cad712d..17cad1851 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -66,6 +66,7 @@ #define PHY_ID_VSC8244 0x000fc6c0 #define PHY_ID_VSC8514 0x00070670 #define PHY_ID_VSC8574 0x000704a0 +#define PHY_ID_VSC8641 0x00070431 #define PHY_ID_VSC8662 0x00070660 #define PHY_ID_VSC8221 0x000fc550 #define PHY_ID_VSC8211 0x000fc4b0 @@ -272,6 +273,18 @@ static struct phy_driver vsc82xx_driver[] = { .config_intr = &vsc82xx_config_intr, .driver = { .owner = THIS_MODULE,}, }, { + .phy_id = PHY_ID_VSC8641, + .name = "Vitesse VSC8641", + .phy_id_mask = 0x000ffff0, + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &vsc824x_config_init, + .config_aneg = &vsc82x4_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &vsc824x_ack_interrupt, + .config_intr = &vsc82xx_config_intr, + .driver = { .owner = THIS_MODULE,}, +}, { .phy_id = PHY_ID_VSC8662, .name = "Vitesse VSC8662", .phy_id_mask = 0x000ffff0, @@ -318,6 +331,7 @@ static struct mdio_device_id __maybe_unused vitesse_tbl[] = { { PHY_ID_VSC8244, 0x000fffc0 }, { PHY_ID_VSC8514, 0x000ffff0 }, { PHY_ID_VSC8574, 0x000ffff0 }, + { PHY_ID_VSC8641, 0x000ffff0 }, { PHY_ID_VSC8662, 0x000ffff0 }, { PHY_ID_VSC8221, 0x000ffff0 }, { PHY_ID_VSC8211, 0x000ffff0 }, |