lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Date: Fri, 29 Aug 2014 10:21:54 -0500 From: Tom Lendacky <thomas.lendacky@....com> To: <netdev@...r.kernel.org> CC: <davem@...emloft.net> Subject: [PATCH net-next v1 1/3] amd-xgbe: Move phy driver support into the network driver The phy driver support is not available as a generic phy device for use by other drivers, it is integral to the amd-xgbe device. For this reason, remove it from drivers/net/phy directory and make it part of the amd-xgbe driver. The driver will still make use of the phy library support for tracking state, etc. Signed-off-by: Tom Lendacky <thomas.lendacky@....com> --- drivers/net/ethernet/amd/Kconfig | 1 drivers/net/ethernet/amd/xgbe/Makefile | 2 drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 16 drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 10 drivers/net/ethernet/amd/xgbe/xgbe-main.c | 137 ++ drivers/net/ethernet/amd/xgbe/xgbe-mdio.c | 39 - drivers/net/ethernet/amd/xgbe/xgbe-phy.c | 1128 ++++++++++++++++++++ drivers/net/ethernet/amd/xgbe/xgbe.h | 59 + drivers/net/phy/Kconfig | 6 drivers/net/phy/Makefile | 1 drivers/net/phy/amd-xgbe-phy.c | 1457 -------------------------- 11 files changed, 1320 insertions(+), 1536 deletions(-) create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy.c delete mode 100644 drivers/net/phy/amd-xgbe-phy.c diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig index 8319c99..dbf4446 100644 --- a/drivers/net/ethernet/amd/Kconfig +++ b/drivers/net/ethernet/amd/Kconfig @@ -181,7 +181,6 @@ config AMD_XGBE tristate "AMD 10GbE Ethernet driver" depends on OF_NET select PHYLIB - select AMD_XGBE_PHY select BITREVERSE select CRC32 select PTP_1588_CLOCK diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile index 171a7e6..861cf15 100644 --- a/drivers/net/ethernet/amd/xgbe/Makefile +++ b/drivers/net/ethernet/amd/xgbe/Makefile @@ -2,7 +2,7 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \ xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \ - xgbe-ptp.o + xgbe-ptp.o xgbe-phy.o amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index dc84f71..61c0975 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -525,7 +525,7 @@ static void xgbe_adjust_link(struct net_device *netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); struct xgbe_hw_if *hw_if = &pdata->hw_if; - struct phy_device *phydev = pdata->phydev; + struct phy_device *phydev = pdata->phy.phydev; int new_state = 0; if (phydev == NULL) @@ -590,7 +590,7 @@ static void xgbe_adjust_link(struct net_device *netdev) static int xgbe_phy_init(struct xgbe_prv_data *pdata) { struct net_device *netdev = pdata->netdev; - struct phy_device *phydev = pdata->phydev; + struct phy_device *phydev = pdata->phy.phydev; int ret; pdata->phy_link = -1; @@ -623,10 +623,10 @@ err_phy_connect: static void xgbe_phy_exit(struct xgbe_prv_data *pdata) { - if (!pdata->phydev) + if (!pdata->phy.phydev) return; - phy_disconnect(pdata->phydev); + phy_disconnect(pdata->phy.phydev); } int xgbe_powerdown(struct net_device *netdev, unsigned int caller) @@ -644,7 +644,7 @@ int xgbe_powerdown(struct net_device *netdev, unsigned int caller) return -EINVAL; } - phy_stop(pdata->phydev); + phy_stop(pdata->phy.phydev); spin_lock_irqsave(&pdata->lock, flags); @@ -686,7 +686,7 @@ int xgbe_powerup(struct net_device *netdev, unsigned int caller) pdata->power_down = 0; - phy_start(pdata->phydev); + phy_start(pdata->phy.phydev); /* Enable Tx/Rx */ hw_if->powerup_tx(pdata); @@ -716,7 +716,7 @@ static int xgbe_start(struct xgbe_prv_data *pdata) hw_if->init(pdata); - phy_start(pdata->phydev); + phy_start(pdata->phy.phydev); hw_if->enable_tx(pdata); hw_if->enable_rx(pdata); @@ -738,7 +738,7 @@ static void xgbe_stop(struct xgbe_prv_data *pdata) DBGPR("-->xgbe_stop\n"); - phy_stop(pdata->phydev); + phy_stop(pdata->phy.phydev); netif_tx_stop_all_queues(netdev); xgbe_napi_disable(pdata, 1); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index a076aca..033ed55 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -250,7 +250,7 @@ static int xgbe_set_pauseparam(struct net_device *netdev, struct ethtool_pauseparam *pause) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - struct phy_device *phydev = pdata->phydev; + struct phy_device *phydev = pdata->phy.phydev; int ret = 0; DBGPR("-->xgbe_set_pauseparam\n"); @@ -287,10 +287,10 @@ static int xgbe_get_settings(struct net_device *netdev, DBGPR("-->xgbe_get_settings\n"); - if (!pdata->phydev) + if (!pdata->phy.phydev) return -ENODEV; - ret = phy_ethtool_gset(pdata->phydev, cmd); + ret = phy_ethtool_gset(pdata->phy.phydev, cmd); cmd->transceiver = XCVR_EXTERNAL; DBGPR("<--xgbe_get_settings\n"); @@ -302,13 +302,13 @@ static int xgbe_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd) { struct xgbe_prv_data *pdata = netdev_priv(netdev); - struct phy_device *phydev = pdata->phydev; + struct phy_device *phydev = pdata->phy.phydev; u32 speed; int ret; DBGPR("-->xgbe_set_settings\n"); - if (!pdata->phydev) + if (!pdata->phy.phydev) return -ENODEV; speed = ethtool_cmd_speed(cmd); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c index 8aa6a93..f9ffbfc 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c @@ -122,6 +122,8 @@ #include <linux/etherdevice.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_device.h> #include <linux/of_net.h> #include <linux/clk.h> @@ -223,7 +225,10 @@ static int xgbe_probe(struct platform_device *pdev) struct xgbe_desc_if *desc_if; struct net_device *netdev; struct device *dev = &pdev->dev; + struct device_node *phy_node; + struct platform_device *phy_pdev; struct resource *res; + const __be32 *property; const u8 *mac_addr; int ret; @@ -247,6 +252,21 @@ static int xgbe_probe(struct platform_device *pdev) mutex_init(&pdata->xpcs_mutex); spin_lock_init(&pdata->tstamp_lock); + /* Obtain a reference the phy device */ + phy_node = of_parse_phandle(dev->of_node, "phy-handle", 0); + if (!phy_node) { + dev_err(dev, "unable to parse phy-handle\n"); + ret = -EINVAL; + goto err_put; + } + + phy_pdev = of_find_device_by_node(phy_node); + if (!phy_pdev) { + dev_err(dev, "unable to locate phy device\n"); + ret = -EINVAL; + goto err_io; + } + /* Set and validate the number of descriptors for a ring */ BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT); pdata->tx_desc_count = XGBE_TX_DESC_CNT; @@ -300,12 +320,65 @@ static int xgbe_probe(struct platform_device *pdev) } DBGPR(" xpcs_regs = %p\n", pdata->xpcs_regs); - /* Set the DMA mask */ - if (!dev->dma_mask) - dev->dma_mask = &dev->coherent_dma_mask; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); - if (ret) { - dev_err(dev, "dma_set_mask_and_coherent failed\n"); + /* Obtain the mmio areas for the phy device */ + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 0); + pdata->rxtx_regs = devm_ioremap_resource(dev, res); + if (IS_ERR(pdata->rxtx_regs)) { + dev_err(dev, "rxtx ioremap failed\n"); + ret = PTR_ERR(pdata->rxtx_regs); + goto err_io; + } + DBGPR(" rxtx_regs = %p\n", pdata->rxtx_regs); + + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 1); + pdata->sir0_regs = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (IS_ERR(pdata->sir0_regs)) { + dev_err(dev, "sir0 ioremap failed\n"); + ret = PTR_ERR(pdata->sir0_regs); + goto err_io; + } + DBGPR(" sir0_regs = %p\n", pdata->sir0_regs); + + res = platform_get_resource(phy_pdev, IORESOURCE_MEM, 2); + pdata->sir1_regs = devm_ioremap_nocache(dev, res->start, + resource_size(res)); + if (IS_ERR(pdata->sir1_regs)) { + dev_err(dev, "sir1 ioremap failed\n"); + ret = PTR_ERR(pdata->sir1_regs); + goto err_io; + } + DBGPR(" sir1_regs = %p\n", pdata->sir1_regs); + + /* Get the device speed set property */ + property = of_get_property(phy_node, XGBE_PHY_SPEEDSET_PROPERTY, NULL); + if (property) + pdata->speed_set = be32_to_cpu(*property); + + switch (pdata->speed_set) { + case XGBE_PHY_SPEEDSET_1000_10000: + case XGBE_PHY_SPEEDSET_2500_10000: + break; + default: + dev_err(dev, "invalid amd,speed-set property\n"); + ret = -EINVAL; + goto err_io; + } + + /* Retrieve the MAC address */ + mac_addr = of_get_mac_address(dev->of_node); + if (!mac_addr) { + dev_err(dev, "invalid mac address for this device\n"); + ret = -EINVAL; + goto err_io; + } + memcpy(netdev->dev_addr, mac_addr, netdev->addr_len); + + /* Retrieve the PHY mode - it must be "xgmii" */ + pdata->phy_mode = of_get_phy_mode(dev->of_node); + if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) { + dev_err(dev, "invalid phy-mode specified for this device\n"); + ret = -EINVAL; goto err_io; } @@ -319,6 +392,16 @@ static int xgbe_probe(struct platform_device *pdev) pdata->awcache = XGBE_DMA_SYS_AWCACHE; } + /* Set the DMA mask */ + if (!dev->dma_mask) + dev->dma_mask = &dev->coherent_dma_mask; + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed\n"); + goto err_io; + } + + /* Get the device interrupt */ ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(dev, "platform_get_irq failed\n"); @@ -338,23 +421,6 @@ static int xgbe_probe(struct platform_device *pdev) /* Populate the hardware features */ xgbe_get_all_hw_features(pdata); - /* Retrieve the MAC address */ - mac_addr = of_get_mac_address(dev->of_node); - if (!mac_addr) { - dev_err(dev, "invalid mac address for this device\n"); - ret = -EINVAL; - goto err_io; - } - memcpy(netdev->dev_addr, mac_addr, netdev->addr_len); - - /* Retrieve the PHY mode - it must be "xgmii" */ - pdata->phy_mode = of_get_phy_mode(dev->of_node); - if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) { - dev_err(dev, "invalid phy-mode specified for this device\n"); - ret = -EINVAL; - goto err_io; - } - /* Set default configuration data */ xgbe_default_config(pdata); @@ -447,6 +513,8 @@ static int xgbe_probe(struct platform_device *pdev) xgbe_debugfs_init(pdata); + of_node_put(phy_node); + netdev_notice(netdev, "net device enabled\n"); DBGPR("<-- xgbe_probe\n"); @@ -460,6 +528,9 @@ err_bus_id: kfree(pdata->mii_bus_id); err_io: + of_node_put(phy_node); + +err_put: free_netdev(netdev); err_alloc: @@ -550,4 +621,22 @@ static struct platform_driver xgbe_driver = { .remove = xgbe_remove, }; -module_platform_driver(xgbe_driver); +static int __init xgbe_driver_init(void) +{ + int ret; + + ret = xgbe_phy_register(); + if (ret) + return ret; + + return platform_driver_register(&xgbe_driver); +} +module_init(xgbe_driver_init); + +static void __exit xgbe_driver_exit(void) +{ + platform_driver_unregister(&xgbe_driver); + + xgbe_phy_unregister(); +} +module_exit(xgbe_driver_exit); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c index 6d2221e..11c4912 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c @@ -206,25 +206,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata) int xgbe_mdio_register(struct xgbe_prv_data *pdata) { - struct device_node *phy_node; struct mii_bus *mii; struct phy_device *phydev; int ret = 0; DBGPR("-->xgbe_mdio_register\n"); - /* Retrieve the phy-handle */ - phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0); - if (!phy_node) { - dev_err(pdata->dev, "unable to parse phy-handle\n"); - return -EINVAL; - } - mii = mdiobus_alloc(); if (mii == NULL) { dev_err(pdata->dev, "mdiobus_alloc failed\n"); - ret = -ENOMEM; - goto err_node_get; + return -ENOMEM; } /* Register on the MDIO bus (don't probe any PHYs) */ @@ -250,24 +241,18 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) ret = phydev ? PTR_ERR(phydev) : -ENOLINK; goto err_mdiobus_register; } - request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, - MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS])); + phydev->priv = pdata; - of_node_get(phy_node); - phydev->dev.of_node = phy_node; ret = phy_device_register(phydev); if (ret) { dev_err(pdata->dev, "phy_device_register failed\n"); - of_node_put(phy_node); goto err_phy_device; } - /* Add a reference to the PHY driver so it can't be unloaded */ - pdata->phy_module = phydev->dev.driver ? - phydev->dev.driver->owner : NULL; - if (!try_module_get(pdata->phy_module)) { - dev_err(pdata->dev, "try_module_get failed\n"); - ret = -EIO; + /* Be sure the phy support was probed successfully */ + if (!pdata->phy.probed) { + dev_err(pdata->dev, "phy support not probed successfully\n"); + ret = -EINVAL; goto err_phy_device; } @@ -282,9 +267,7 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata) phydev->advertising &= ~ADVERTISED_Autoneg; } - pdata->phydev = phydev; - - of_node_put(phy_node); + pdata->phy.phydev = phydev; DBGPHY_REGS(pdata); @@ -301,9 +284,6 @@ err_mdiobus_register: err_mdiobus_alloc: mdiobus_free(mii); -err_node_get: - of_node_put(phy_node); - return ret; } @@ -311,10 +291,7 @@ void xgbe_mdio_unregister(struct xgbe_prv_data *pdata) { DBGPR("-->xgbe_mdio_unregister\n"); - pdata->phydev = NULL; - - module_put(pdata->phy_module); - pdata->phy_module = NULL; + pdata->phy.phydev = NULL; mdiobus_unregister(pdata->mii); pdata->mii->priv = NULL; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c new file mode 100644 index 0000000..7aca21d --- /dev/null +++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy.c @@ -0,0 +1,1128 @@ +/* + * AMD 10Gb Ethernet 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/module.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/workqueue.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/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/mm.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/mdio.h> +#include <linux/io.h> + +#include "xgbe.h" +#include "xgbe-common.h" + +#define XGBE_PHY_ID 0x000162d0 +#define XGBE_PHY_MASK 0xfffffff0 + +#define XGBE_AN_INT_CMPLT 0x01 +#define XGBE_AN_INC_LINK 0x02 +#define XGBE_AN_PG_RCV 0x04 + +#define XNP_MCF_NULL_MESSAGE 0x001 +#define XNP_ACK_PROCESSED (1 << 12) +#define XNP_MP_FORMATTED (1 << 13) +#define XNP_NP_EXCHANGE (1 << 15) + +#define XGBE_PHY_RATECHANGE_COUNT 500 + +#ifndef MDIO_PMA_10GBR_PMD_CTRL +#define MDIO_PMA_10GBR_PMD_CTRL 0x0096 +#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_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 + +#ifndef MDIO_PMA_10GBR_KR_TRAIN_START +#define MDIO_PMA_10GBR_KR_TRAIN_START 0x01 +#endif + +#ifndef MDIO_PMA_10GBR_KR_TRAIN_ENABLE +#define MDIO_PMA_10GBR_KR_TRAIN_ENABLE 0x02 +#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_DATARATE_INDEX 4 +#define SIR1_SPEED_DATARATE_WIDTH 2 +#define SIR1_SPEED_PI_SPD_SEL_INDEX 12 +#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4 +#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_CDR 0x7 +#define SPEED_10000_PLL 0x1 +#define SPEED_10000_RATE 0x0 +#define SPEED_10000_TXAMP 0xa +#define SPEED_10000_WORD 0x7 + +#define SPEED_2500_CDR 0x2 +#define SPEED_2500_PLL 0x0 +#define SPEED_2500_RATE 0x1 +#define SPEED_2500_TXAMP 0xf +#define SPEED_2500_WORD 0x1 + +#define SPEED_1000_CDR 0x2 +#define SPEED_1000_PLL 0x0 +#define SPEED_1000_RATE 0x3 +#define SPEED_1000_TXAMP 0xf +#define SPEED_1000_WORD 0x1 + +/* SerDes RxTx register offsets */ +#define RXTX_REG20 0x0050 +#define RXTX_REG114 0x01c8 + +/* SerDes RxTx register entry bit positions and sizes */ +#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_10000_BLWC 0 +#define RXTX_10000_PQ 0x1e + +#define RXTX_2500_BLWC 1 +#define RXTX_2500_PQ 0xa + +#define RXTX_1000_BLWC 1 +#define RXTX_1000_PQ 0xa + +/* 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(_pdata, _reg) \ + ioread16((_pdata)->sir0_regs + _reg) + +#define XSIR0_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XSIR0_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XSIR0_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->sir0_regs + _reg) + +#define XSIR0_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XSIR0_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XSIR0_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +#define XSIR1_IOREAD(_pdata, _reg) \ + ioread16((_pdata)->sir1_regs + _reg) + +#define XSIR1_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XSIR1_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XSIR1_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->sir1_regs + _reg) + +#define XSIR1_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XSIR1_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XSIR1_IOWRITE((_pdata), _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(_pdata, _reg) \ + ioread16((_pdata)->rxtx_regs + _reg) + +#define XRXTX_IOREAD_BITS(_pdata, _reg, _field) \ + GET_BITS(XRXTX_IOREAD((_pdata), _reg), \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH) + +#define XRXTX_IOWRITE(_pdata, _reg, _val) \ + iowrite16((_val), (_pdata)->rxtx_regs + _reg) + +#define XRXTX_IOWRITE_BITS(_pdata, _reg, _field, _val) \ +do { \ + u16 reg_val = XRXTX_IOREAD((_pdata), _reg); \ + SET_BITS(reg_val, \ + _reg##_##_field##_INDEX, \ + _reg##_##_field##_WIDTH, (_val)); \ + XRXTX_IOWRITE((_pdata), _reg, reg_val); \ +} while (0) + +static void xgbe_phy_an_enable_kr_training(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, + MDIO_PMA_10GBR_KR_TRAIN_ENABLE, + MDIO_PMA_10GBR_KR_TRAIN_ENABLE); +} + +static void xgbe_phy_an_disable_kr_training(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, + MDIO_PMA_10GBR_KR_TRAIN_ENABLE, 0); +} + +static void xgbe_phy_pcs_power_cycle(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int val; + + val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + + val |= MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, val); + + usleep_range(75, 100); + + val &= ~MDIO_CTRL1_LPOWER; + XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, val); +} + +static void xgbe_phy_serdes_start_ratechange(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Assert Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1); +} + +static void xgbe_phy_serdes_complete_ratechange(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int wait; + u16 status; + + /* Release Rx and Tx ratechange */ + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0); + + /* Wait for Rx and Tx ready */ + wait = XGBE_PHY_RATECHANGE_COUNT; + while (wait--) { + usleep_range(50, 75); + + status = XSIR0_IOREAD(pdata, SIR0_STATUS); + if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) && + XSIR_GET_BITS(status, SIR0_STATUS, TX_READY)) + return; + } + + netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n", + status); +} + +static void xgbe_phy_xgmii_mode(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Enable KR training */ + xgbe_phy_an_enable_kr_training(phydev); + + /* Set PCS to KR/10G speed */ + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE, + MDIO_PCS_CTRL2_10GBR); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL, + MDIO_CTRL1_SPEED10G); + + xgbe_phy_pcs_power_cycle(phydev); + + /* Set SerDes to 10G speed */ + xgbe_phy_serdes_start_ratechange(phydev); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_10000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_10000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_10000_PLL); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR); + + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_10000_PQ); + + xgbe_phy_serdes_complete_ratechange(phydev); +} + +static void xgbe_phy_gmii_2500_mode(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Disable KR training */ + xgbe_phy_an_disable_kr_training(phydev); + + /* Set PCS to KX/1G speed */ + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE, + MDIO_PCS_CTRL2_10GBX); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL, + MDIO_CTRL1_SPEED1G); + + xgbe_phy_pcs_power_cycle(phydev); + + /* Set SerDes to 2.5G speed */ + xgbe_phy_serdes_start_ratechange(phydev); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_2500_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_2500_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_2500_PLL); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR); + + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_2500_PQ); + + xgbe_phy_serdes_complete_ratechange(phydev); +} + +static void xgbe_phy_gmii_mode(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Disable KR training */ + xgbe_phy_an_disable_kr_training(phydev); + + /* Set PCS to KX/1G speed */ + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL2, MDIO_PCS_CTRL2_TYPE, + MDIO_PCS_CTRL2_10GBX); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_SPEEDSEL, + MDIO_CTRL1_SPEED1G); + + xgbe_phy_pcs_power_cycle(phydev); + + /* Set SerDes to 1G speed */ + xgbe_phy_serdes_start_ratechange(phydev); + + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, SPEED_1000_RATE); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, SPEED_1000_WORD); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, SPEED_1000_PLL); + XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR); + + XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC); + XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG, RXTX_1000_PQ); + + xgbe_phy_serdes_complete_ratechange(phydev); +} + +static enum xgbe_speed_mode xgbe_phy_cur_mode(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int val; + + val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2); + if ((val & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) + return XGBE_SPEED_MODE_KR; + + return XGBE_SPEED_MODE_KX; +} + +static bool xgbe_phy_in_kr_mode(struct phy_device *phydev) +{ + return (xgbe_phy_cur_mode(phydev) == XGBE_SPEED_MODE_KR); +} + +static bool xgbe_phy_in_kx_mode(struct phy_device *phydev) +{ + return (xgbe_phy_cur_mode(phydev) == XGBE_SPEED_MODE_KX); +} + +static void xgbe_phy_switch_mode(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* If we are in KR switch to KX, and vice-versa */ + if (xgbe_phy_in_kr_mode(phydev)) { + if (pdata->speed_set == XGBE_PHY_SPEEDSET_1000_10000) + xgbe_phy_gmii_mode(phydev); + else + xgbe_phy_gmii_2500_mode(phydev); + } else { + xgbe_phy_xgmii_mode(phydev); + } +} + +static void xgbe_phy_set_mode(struct phy_device *phydev, + enum xgbe_speed_mode mode) +{ + if (mode != xgbe_phy_cur_mode(phydev)) + xgbe_phy_switch_mode(phydev); +} + +static enum xgbe_an xgbe_phy_an_tx_training(struct phy_device *phydev, + enum xgbe_an_rx *state) +{ + struct xgbe_prv_data *pdata = phydev->priv; + int ad_val, lp_val, val; + + *state = XGBE_AN_RX_COMPLETE; + + /* If we're in KX mode then we're done */ + if (xgbe_phy_in_kx_mode(phydev)) + return XGBE_AN_EVENT; + + /* Enable/Disable FEC */ + ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2); + + val = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL); + + if ((ad_val & 0xc000) && (lp_val & 0xc000)) + val |= 0x01; + else + val &= ~0x01; + + XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, val); + + /* Start KR training */ + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, + MDIO_PMA_10GBR_KR_TRAIN_START, + MDIO_PMA_10GBR_KR_TRAIN_START); + + XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0); + + return XGBE_AN_EVENT; +} + +static enum xgbe_an xgbe_phy_an_tx_xnp(struct phy_device *phydev, + enum xgbe_an_rx *state) +{ + struct xgbe_prv_data *pdata = phydev->priv; + u16 msg; + + *state = XGBE_AN_RX_XNP; + + msg = XNP_MCF_NULL_MESSAGE; + msg |= XNP_MP_FORMATTED; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 2, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_XNP, msg); + + return XGBE_AN_EVENT; +} + +static enum xgbe_an xgbe_phy_an_rx_bpa(struct phy_device *phydev, + enum xgbe_an_rx *state) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int link_support; + int val, ad_val, lp_val; + + /* Read Base Ability register 2 first */ + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + + /* Check for a supported mode, otherwise restart in a different one */ + link_support = (xgbe_phy_in_kr_mode(phydev)) ? 0x80 : 0x20; + if (!(val & link_support)) + return XGBE_AN_INCOMPAT_LINK; + + /* Check Extended Next Page support */ + ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + + return ((ad_val & XNP_NP_EXCHANGE) || (lp_val & XNP_NP_EXCHANGE)) + ? xgbe_phy_an_tx_xnp(phydev, state) + : xgbe_phy_an_tx_training(phydev, state); +} + +static enum xgbe_an xgbe_phy_an_rx_xnp(struct phy_device *phydev, + enum xgbe_an_rx *state) +{ + struct xgbe_prv_data *pdata = phydev->priv; + int ad_val, lp_val; + + /* Check Extended Next Page support */ + ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + + return ((ad_val & XNP_NP_EXCHANGE) || (lp_val & XNP_NP_EXCHANGE)) + ? xgbe_phy_an_tx_xnp(phydev, state) + : xgbe_phy_an_tx_training(phydev, state); +} + +static enum xgbe_an xgbe_phy_an_start(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int val; + + /* Be sure we aren't looping trying to negotiate */ + if (xgbe_phy_in_kr_mode(phydev)) { + if (pdata->phy.kr_state != XGBE_AN_RX_READY) + return XGBE_AN_NO_LINK; + pdata->phy.kr_state = XGBE_AN_RX_BPA; + } else { + if (pdata->phy.kx_state != XGBE_AN_RX_READY) + return XGBE_AN_NO_LINK; + pdata->phy.kx_state = XGBE_AN_RX_BPA; + } + + /* Set up Advertisement register 3 first */ + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); + + if (phydev->supported & SUPPORTED_10000baseR_FEC) + val |= 0xc000; + else + val &= ~0xc000; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, val); + + /* Set up Advertisement register 2 next */ + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + + if (phydev->supported & SUPPORTED_10000baseKR_Full) + val |= 0x80; + else + val &= ~0x80; + + if ((phydev->supported & SUPPORTED_1000baseKX_Full) || + (phydev->supported & SUPPORTED_2500baseX_Full)) + val |= 0x20; + else + val &= ~0x20; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, val); + + /* Set up Advertisement register 1 last */ + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + + if (phydev->supported & SUPPORTED_Pause) + val |= 0x400; + else + val &= ~0x400; + + if (phydev->supported & SUPPORTED_Asym_Pause) + val |= 0x800; + else + val &= ~0x800; + + /* We don't intend to perform XNP */ + val &= ~XNP_NP_EXCHANGE; + + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE, val); + + /* Enable and start auto-negotiation */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_CTRL1); + val |= MDIO_AN_CTRL1_ENABLE; + val |= MDIO_AN_CTRL1_RESTART; + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_CTRL1, val); + + return XGBE_AN_EVENT; +} + +static enum xgbe_an xgbe_phy_an_event(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + enum xgbe_an new_state; + unsigned int val; + + val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_INT); + + new_state = XGBE_AN_EVENT; + if (val & XGBE_AN_PG_RCV) + new_state = XGBE_AN_PAGE_RECEIVED; + else if (val & XGBE_AN_INC_LINK) + new_state = XGBE_AN_INCOMPAT_LINK; + else if (val & XGBE_AN_INT_CMPLT) + new_state = XGBE_AN_COMPLETE; + + if (new_state != XGBE_AN_EVENT) + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + + return new_state; +} + +static enum xgbe_an xgbe_phy_an_page_received(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + enum xgbe_an_rx *state; + enum xgbe_an new_state; + + state = xgbe_phy_in_kr_mode(phydev) ? &pdata->phy.kr_state + : &pdata->phy.kx_state; + + switch (*state) { + case XGBE_AN_RX_BPA: + new_state = xgbe_phy_an_rx_bpa(phydev, state); + break; + + case XGBE_AN_RX_XNP: + new_state = xgbe_phy_an_rx_xnp(phydev, state); + break; + + default: + new_state = XGBE_AN_ERROR; + } + + return new_state; +} + +static enum xgbe_an xgbe_phy_an_incompat_link(struct phy_device *phydev) +{ + xgbe_phy_switch_mode(phydev); + + return XGBE_AN_START; +} + +static void xgbe_phy_an_state_machine(struct work_struct *work) +{ + struct xgbe_prv_data *pdata = container_of(work, + struct xgbe_prv_data, + phy.an_work); + struct phy_device *phydev = pdata->phy.phydev; + enum xgbe_an cur_state; + unsigned int sleep; + unsigned int an_supported = 0; + + while (1) { + mutex_lock(&pdata->phy.an_mutex); + + cur_state = pdata->phy.an_state; + + switch (pdata->phy.an_state) { + case XGBE_AN_START: + an_supported = 0; + pdata->phy.an_state = xgbe_phy_an_start(phydev); + break; + + case XGBE_AN_EVENT: + pdata->phy.an_state = xgbe_phy_an_event(phydev); + break; + + case XGBE_AN_PAGE_RECEIVED: + an_supported++; + pdata->phy.an_state = xgbe_phy_an_page_received(phydev); + break; + + case XGBE_AN_INCOMPAT_LINK: + pdata->phy.an_state = xgbe_phy_an_incompat_link(phydev); + break; + + case XGBE_AN_COMPLETE: + netdev_info(phydev->attached_dev, "%s successful\n", + an_supported ? "Auto negotiation" + : "Parallel detection"); + /* fall through */ + + case XGBE_AN_NO_LINK: + case XGBE_AN_EXIT: + goto exit_unlock; + + default: + pdata->phy.an_state = XGBE_AN_ERROR; + } + + if (pdata->phy.an_state == XGBE_AN_ERROR) { + netdev_err(phydev->attached_dev, + "error during auto-negotiation, state=%u\n", + cur_state); + goto exit_unlock; + } + + sleep = (pdata->phy.an_state == XGBE_AN_EVENT) ? 1 : 0; + + mutex_unlock(&pdata->phy.an_mutex); + + if (sleep) + usleep_range(20, 50); + } + +exit_unlock: + pdata->phy.an_result = pdata->phy.an_state; + pdata->phy.an_state = XGBE_AN_READY; + + mutex_unlock(&pdata->phy.an_mutex); +} + +static int xgbe_phy_soft_reset(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + unsigned int count, val; + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_RESET, + MDIO_CTRL1_RESET); + + count = 50; + do { + msleep(20); + val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1); + } while ((val & MDIO_CTRL1_RESET) && --count); + + if (val & MDIO_CTRL1_RESET) + return -ETIMEDOUT; + + return 0; +} + +static int xgbe_phy_config_init(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Initialize supported features */ + phydev->supported = SUPPORTED_Autoneg; + phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; + phydev->supported |= SUPPORTED_Backplane; + phydev->supported |= SUPPORTED_10000baseKR_Full | + SUPPORTED_10000baseR_FEC; + switch (pdata->speed_set) { + case XGBE_PHY_SPEEDSET_1000_10000: + phydev->supported |= SUPPORTED_1000baseKX_Full; + break; + case XGBE_PHY_SPEEDSET_2500_10000: + phydev->supported |= SUPPORTED_2500baseX_Full; + break; + } + phydev->advertising = phydev->supported; + + /* Turn off and clear interrupts */ + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); + XMDIO_WRITE(pdata, MDIO_MMD_AN, MDIO_AN_INT, 0); + + return 0; +} + +static int xgbe_phy_setup_forced(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Disable auto-negotiation */ + XMDIO_WRITE_BITS(pdata, MDIO_MMD_AN, MDIO_CTRL1, MDIO_AN_CTRL1_ENABLE, + 0); + + /* Validate/Set specified speed */ + switch (phydev->speed) { + case SPEED_10000: + xgbe_phy_xgmii_mode(phydev); + break; + + case SPEED_2500: + xgbe_phy_gmii_2500_mode(phydev); + break; + + case SPEED_1000: + xgbe_phy_gmii_mode(phydev); + break; + + default: + return -EINVAL; + } + + /* Validate duplex mode */ + if (phydev->duplex != DUPLEX_FULL) + return -EINVAL; + + phydev->pause = 0; + phydev->asym_pause = 0; + + return 0; +} + +static int xgbe_phy_config_aneg(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + u32 mmd_mask = phydev->c45_ids.devices_in_package; + + if (phydev->autoneg != AUTONEG_ENABLE) + return xgbe_phy_setup_forced(phydev); + + /* Make sure we have the AN MMD present */ + if (!(mmd_mask & MDIO_DEVS_AN)) + return -EINVAL; + + /* Start/Restart the auto-negotiation state machine */ + mutex_lock(&pdata->phy.an_mutex); + pdata->phy.an_result = XGBE_AN_READY; + pdata->phy.an_state = XGBE_AN_START; + pdata->phy.kr_state = XGBE_AN_RX_READY; + pdata->phy.kx_state = XGBE_AN_RX_READY; + mutex_unlock(&pdata->phy.an_mutex); + + queue_work(pdata->phy.an_workqueue, &pdata->phy.an_work); + + return 0; +} + +static int xgbe_phy_aneg_done(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + enum xgbe_an state; + + mutex_lock(&pdata->phy.an_mutex); + state = pdata->phy.an_result; + mutex_unlock(&pdata->phy.an_mutex); + + return (state == XGBE_AN_COMPLETE); +} + +static int xgbe_phy_update_link(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + enum xgbe_an state; + unsigned int check_again, autoneg, val; + int ret = 0; + + /* If we're doing auto-negotiation don't report link down */ + mutex_lock(&pdata->phy.an_mutex); + state = pdata->phy.an_state; + mutex_unlock(&pdata->phy.an_mutex); + + if (state != XGBE_AN_READY) { + phydev->link = 1; + return 0; + } + + /* Since the device can be in the wrong mode when a link is + * (re-)established (cable connected after the interface is + * up, etc.), the link status may report no link. If there + * is no link, try switching modes and checking the status + * again if auto negotiation is enabled. + */ + check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0; +again: + /* Link status is latched low, so read once to clear + * and then read again to get current state + */ + val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + val = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1); + + phydev->link = (val & MDIO_STAT1_LSTATUS) ? 1 : 0; + + if (!phydev->link) { + if (check_again) { + xgbe_phy_switch_mode(phydev); + check_again = 0; + goto again; + } + } + + autoneg = (phydev->link && !pdata->phy.link) ? 1 : 0; + pdata->phy.link = phydev->link; + + /* If link is (back) up, re-start auto-negotiation */ + if (autoneg) + ret = xgbe_phy_config_aneg(phydev); + + return ret; +} + +static int xgbe_phy_read_status(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + u32 mmd_mask = phydev->c45_ids.devices_in_package; + unsigned int ad_val, lp_val; + int ret; + + ret = xgbe_phy_update_link(phydev); + if (ret) + return ret; + + if (phydev->autoneg == AUTONEG_ENABLE) { + if (!(mmd_mask & MDIO_DEVS_AN)) + return -EINVAL; + + if (!xgbe_phy_aneg_done(phydev)) + return 0; + + /* Compare Advertisement and Link Partner register 1 */ + ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE); + lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA); + + ad_val &= lp_val; + phydev->pause = (ad_val & 0x400) ? 1 : 0; + phydev->asym_pause = (ad_val & 0x800) ? 1 : 0; + + /* Compare Advertisement and Link Partner register 2 */ + ad_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1); + lp_val = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1); + + ad_val &= lp_val; + if (ad_val & 0x80) { + phydev->speed = SPEED_10000; + xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KR); + } else { + switch (pdata->speed_set) { + case XGBE_PHY_SPEEDSET_1000_10000: + phydev->speed = SPEED_1000; + break; + + case XGBE_PHY_SPEEDSET_2500_10000: + phydev->speed = SPEED_2500; + break; + } + xgbe_phy_set_mode(phydev, XGBE_SPEED_MODE_KX); + } + + phydev->duplex = DUPLEX_FULL; + } else { + if (xgbe_phy_in_kr_mode(phydev)) { + phydev->speed = SPEED_10000; + } else { + switch (pdata->speed_set) { + case XGBE_PHY_SPEEDSET_1000_10000: + phydev->speed = SPEED_1000; + break; + + case 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 xgbe_phy_suspend(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + mutex_lock(&phydev->lock); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_LPOWER, + MDIO_CTRL1_LPOWER); + + mutex_unlock(&phydev->lock); + + return 0; +} + +static int xgbe_phy_resume(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + mutex_lock(&phydev->lock); + + XMDIO_WRITE_BITS(pdata, MDIO_MMD_PCS, MDIO_CTRL1, MDIO_CTRL1_LPOWER, + 0); + + mutex_unlock(&phydev->lock); + + return 0; +} + +static int xgbe_phy_probe(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + char *wq_name; + + if (!pdata) + return -EINVAL; + + wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", pdata->pdev->name); + if (!wq_name) + return -ENOMEM; + + pdata->phy.link = 1; + + mutex_init(&pdata->phy.an_mutex); + INIT_WORK(&pdata->phy.an_work, xgbe_phy_an_state_machine); + pdata->phy.an_workqueue = create_singlethread_workqueue(wq_name); + kfree(wq_name); + if (!pdata->phy.an_workqueue) + return -ENOMEM; + + pdata->phy.probed = 1; + + return 0; +} + +static void xgbe_phy_remove(struct phy_device *phydev) +{ + struct xgbe_prv_data *pdata = phydev->priv; + + /* Stop any in process auto-negotiation */ + mutex_lock(&pdata->phy.an_mutex); + pdata->phy.an_state = XGBE_AN_EXIT; + mutex_unlock(&pdata->phy.an_mutex); + + flush_workqueue(pdata->phy.an_workqueue); + destroy_workqueue(pdata->phy.an_workqueue); +} + +static int xgbe_phy_match_device(struct phy_device *phydev) +{ + return phydev->c45_ids.device_ids[MDIO_MMD_PCS] == XGBE_PHY_ID; +} + +static struct phy_driver xgbe_phy_driver[] = { + { + .phy_id = XGBE_PHY_ID, + .phy_id_mask = XGBE_PHY_MASK, + .name = "AMD XGBE PHY", + .features = 0, + .probe = xgbe_phy_probe, + .remove = xgbe_phy_remove, + .soft_reset = xgbe_phy_soft_reset, + .config_init = xgbe_phy_config_init, + .suspend = xgbe_phy_suspend, + .resume = xgbe_phy_resume, + .config_aneg = xgbe_phy_config_aneg, + .aneg_done = xgbe_phy_aneg_done, + .read_status = xgbe_phy_read_status, + .match_phy_device = xgbe_phy_match_device, + .driver = { + .owner = THIS_MODULE, + }, + }, +}; + +int xgbe_phy_register(void) +{ + return phy_drivers_register(xgbe_phy_driver, + ARRAY_SIZE(xgbe_phy_driver)); +} + +void xgbe_phy_unregister(void) +{ + phy_drivers_unregister(xgbe_phy_driver, + ARRAY_SIZE(xgbe_phy_driver)); +} + +static struct mdio_device_id __maybe_unused xgbe_phy_ids[] = { + { XGBE_PHY_ID, XGBE_PHY_MASK }, + { } +}; +MODULE_DEVICE_TABLE(mdio, xgbe_phy_ids); diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h index 07bf70a..36f8717 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe.h +++ b/drivers/net/ethernet/amd/xgbe/xgbe.h @@ -173,6 +173,11 @@ #define XGBE_DMA_CLOCK "dma_clk" #define XGBE_PTP_CLOCK "ptp_clk" +/* Device-tree property names/values */ +#define XGBE_PHY_SPEEDSET_PROPERTY "amd,speed-set" +#define XGBE_PHY_SPEEDSET_1000_10000 0 +#define XGBE_PHY_SPEEDSET_2500_10000 1 + /* Timestamp support - values based on 50MHz PTP clock * 50MHz => 20 nsec */ @@ -419,6 +424,48 @@ struct xgbe_mmc_stats { u64 rxwatchdogerror; }; +enum xgbe_an { + XGBE_AN_READY = 0, + XGBE_AN_START, + XGBE_AN_EVENT, + XGBE_AN_PAGE_RECEIVED, + XGBE_AN_INCOMPAT_LINK, + XGBE_AN_COMPLETE, + XGBE_AN_NO_LINK, + XGBE_AN_EXIT, + XGBE_AN_ERROR, +}; + +enum xgbe_an_rx { + XGBE_AN_RX_READY = 0, + XGBE_AN_RX_BPA, + XGBE_AN_RX_XNP, + XGBE_AN_RX_COMPLETE, +}; + +enum xgbe_speed_mode { + XGBE_SPEED_MODE_KR, + XGBE_SPEED_MODE_KX, +}; + +struct xgbe_phy { + struct phy_device *phydev; + + unsigned int probed; + + /* Maintain link status for re-starting auto-negotiation */ + unsigned int link; + + /* Auto-negotiation state machine support */ + struct mutex an_mutex; + enum xgbe_an an_result; + enum xgbe_an an_state; + enum xgbe_an_rx kr_state; + enum xgbe_an_rx kx_state; + struct work_struct an_work; + struct workqueue_struct *an_workqueue; +}; + struct xgbe_hw_if { int (*tx_complete)(struct xgbe_ring_desc *); @@ -573,6 +620,13 @@ struct xgbe_prv_data { void __iomem *xgmac_regs; /* XGMAC CSRs */ void __iomem *xpcs_regs; /* XPCS MMD registers */ + /* 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) */ + + unsigned int speed_set; + /* Overall device lock */ spinlock_t lock; @@ -631,11 +685,10 @@ struct xgbe_prv_data { unsigned int rx_pause; /* MDIO settings */ - struct module *phy_module; char *mii_bus_id; struct mii_bus *mii; int mdio_mmd; - struct phy_device *phydev; + struct xgbe_phy phy; int default_autoneg; int default_speed; @@ -705,6 +758,8 @@ struct ethtool_ops *xgbe_get_ethtool_ops(void); const struct dcbnl_rtnl_ops *xgbe_get_dcbnl_ops(void); #endif +int xgbe_phy_register(void); +void xgbe_phy_unregister(void); int xgbe_mdio_register(struct xgbe_prv_data *); void xgbe_mdio_unregister(struct xgbe_prv_data *); void xgbe_dump_phy_registers(struct xgbe_prv_data *); diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 28437ab..60a53bb 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -24,12 +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 - ---help--- - Currently supports the AMD 10GbE PHY - config MARVELL_PHY tristate "Drivers for Marvell PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index eb3b18b..21813d1 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -33,5 +33,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 f3230ee..0000000 --- a/drivers/net/phy/amd-xgbe-phy.c +++ /dev/null @@ -1,1457 +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/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> - - -MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@....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_AN_INT_CMPLT 0x01 -#define XGBE_AN_INC_LINK 0x02 -#define XGBE_AN_PG_RCV 0x04 - -#define XNP_MCF_NULL_MESSAGE 0x001 -#define XNP_ACK_PROCESSED (1 << 12) -#define XNP_MP_FORMATTED (1 << 13) -#define XNP_NP_EXCHANGE (1 << 15) - -#define XGBE_PHY_RATECHANGE_COUNT 500 - -#ifndef MDIO_PMA_10GBR_PMD_CTRL -#define MDIO_PMA_10GBR_PMD_CTRL 0x0096 -#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_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_DATARATE_INDEX 4 -#define SIR1_SPEED_DATARATE_WIDTH 2 -#define SIR1_SPEED_PI_SPD_SEL_INDEX 12 -#define SIR1_SPEED_PI_SPD_SEL_WIDTH 4 -#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_CDR 0x7 -#define SPEED_10000_PLL 0x1 -#define SPEED_10000_RATE 0x0 -#define SPEED_10000_TXAMP 0xa -#define SPEED_10000_WORD 0x7 - -#define SPEED_2500_CDR 0x2 -#define SPEED_2500_PLL 0x0 -#define SPEED_2500_RATE 0x1 -#define SPEED_2500_TXAMP 0xf -#define SPEED_2500_WORD 0x1 - -#define SPEED_1000_CDR 0x2 -#define SPEED_1000_PLL 0x0 -#define SPEED_1000_RATE 0x3 -#define SPEED_1000_TXAMP 0xf -#define SPEED_1000_WORD 0x1 - - -/* SerDes RxTx register offsets */ -#define RXTX_REG20 0x0050 -#define RXTX_REG114 0x01c8 - -/* SerDes RxTx register entry bit positions and sizes */ -#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_10000_BLWC 0 -#define RXTX_10000_PQ 0x1e - -#define RXTX_2500_BLWC 1 -#define RXTX_2500_PQ 0xa - -#define RXTX_1000_BLWC 1 -#define RXTX_1000_PQ 0xa - -/* 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) - - -enum amd_xgbe_phy_an { - AMD_XGBE_AN_READY = 0, - AMD_XGBE_AN_START, - AMD_XGBE_AN_EVENT, - AMD_XGBE_AN_PAGE_RECEIVED, - AMD_XGBE_AN_INCOMPAT_LINK, - AMD_XGBE_AN_COMPLETE, - AMD_XGBE_AN_NO_LINK, - AMD_XGBE_AN_EXIT, - AMD_XGBE_AN_ERROR, -}; - -enum amd_xgbe_phy_rx { - AMD_XGBE_RX_READY = 0, - AMD_XGBE_RX_BPA, - AMD_XGBE_RX_XNP, - AMD_XGBE_RX_COMPLETE, -}; - -enum amd_xgbe_phy_mode { - AMD_XGBE_MODE_KR, - AMD_XGBE_MODE_KX, -}; - -enum amd_xgbe_phy_speedset { - AMD_XGBE_PHY_SPEEDSET_1000_10000, - AMD_XGBE_PHY_SPEEDSET_2500_10000, -}; - -struct amd_xgbe_phy_priv { - struct platform_device *pdev; - 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) */ - - /* Maintain link status for re-starting auto-negotiation */ - unsigned int link; - enum amd_xgbe_phy_mode mode; - unsigned int speed_set; - - /* 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; -}; - -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 |= 0x02; - 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 &= ~0x02; - 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)) - return; - } - - netdev_dbg(phydev->attached_dev, "SerDes rx/tx not ready (%#hx)\n", - status); -} - -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, TXAMP, SPEED_10000_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR); - - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - priv->mode = AMD_XGBE_MODE_KR; - - 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, TXAMP, SPEED_2500_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR); - - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - priv->mode = AMD_XGBE_MODE_KX; - - 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, TXAMP, SPEED_1000_TXAMP); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL); - XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR); - - XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC); - XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ); - - amd_xgbe_phy_serdes_complete_ratechange(phydev); - - priv->mode = AMD_XGBE_MODE_KX; - - return 0; -} - -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 (priv->mode == AMD_XGBE_MODE_KR) { - 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 enum amd_xgbe_phy_an amd_xgbe_an_switch_mode(struct phy_device *phydev) -{ - int ret; - - ret = amd_xgbe_phy_switch_mode(phydev); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - return AMD_XGBE_AN_START; -} - -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 in KX mode then we're done */ - if (priv->mode == AMD_XGBE_MODE_KX) - return AMD_XGBE_AN_EVENT; - - /* 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; - - if ((ad_reg & 0xc000) && (lp_reg & 0xc000)) - ret |= 0x01; - else - ret &= ~0x01; - - 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; - - XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1); - - ret |= 0x01; - 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_EVENT; -} - -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_EVENT; -} - -static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev, - enum amd_xgbe_phy_rx *state) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - 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 = (priv->mode == AMD_XGBE_MODE_KR) ? 0x80 : 0x20; - if (!(ret & link_support)) - return amd_xgbe_an_switch_mode(phydev); - - /* 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_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_start(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - int ret; - - /* Be sure we aren't looping trying to negotiate */ - if (priv->mode == AMD_XGBE_MODE_KR) { - if (priv->kr_state != AMD_XGBE_RX_READY) - return AMD_XGBE_AN_NO_LINK; - priv->kr_state = AMD_XGBE_RX_BPA; - } else { - if (priv->kx_state != AMD_XGBE_RX_READY) - return AMD_XGBE_AN_NO_LINK; - priv->kx_state = AMD_XGBE_RX_BPA; - } - - /* Set up Advertisement register 3 first */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - if (phydev->supported & 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 AMD_XGBE_AN_ERROR; - - if (phydev->supported & SUPPORTED_10000baseKR_Full) - ret |= 0x80; - else - ret &= ~0x80; - - if ((phydev->supported & SUPPORTED_1000baseKX_Full) || - (phydev->supported & 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 AMD_XGBE_AN_ERROR; - - if (phydev->supported & SUPPORTED_Pause) - ret |= 0x400; - else - ret &= ~0x400; - - if (phydev->supported & 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); - - /* Enable and start auto-negotiation */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - ret |= MDIO_AN_CTRL1_ENABLE; - ret |= MDIO_AN_CTRL1_RESTART; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); - - return AMD_XGBE_AN_EVENT; -} - -static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev) -{ - enum amd_xgbe_phy_an new_state; - int ret; - - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT); - if (ret < 0) - return AMD_XGBE_AN_ERROR; - - new_state = AMD_XGBE_AN_EVENT; - if (ret & XGBE_AN_PG_RCV) - new_state = AMD_XGBE_AN_PAGE_RECEIVED; - else if (ret & XGBE_AN_INC_LINK) - new_state = AMD_XGBE_AN_INCOMPAT_LINK; - else if (ret & XGBE_AN_INT_CMPLT) - new_state = AMD_XGBE_AN_COMPLETE; - - if (new_state != AMD_XGBE_AN_EVENT) - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - - return new_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; - int ret; - - state = (priv->mode == AMD_XGBE_MODE_KR) ? &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) -{ - return amd_xgbe_an_switch_mode(phydev); -} - -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; - int sleep; - unsigned int an_supported = 0; - - while (1) { - mutex_lock(&priv->an_mutex); - - cur_state = priv->an_state; - - switch (priv->an_state) { - case AMD_XGBE_AN_START: - priv->an_state = amd_xgbe_an_start(phydev); - an_supported = 0; - break; - - case AMD_XGBE_AN_EVENT: - priv->an_state = amd_xgbe_an_event(phydev); - break; - - case AMD_XGBE_AN_PAGE_RECEIVED: - priv->an_state = amd_xgbe_an_page_received(phydev); - an_supported++; - break; - - case AMD_XGBE_AN_INCOMPAT_LINK: - priv->an_state = amd_xgbe_an_incompat_link(phydev); - break; - - case AMD_XGBE_AN_COMPLETE: - netdev_info(phydev->attached_dev, "%s successful\n", - an_supported ? "Auto negotiation" - : "Parallel detection"); - /* fall through */ - - case AMD_XGBE_AN_NO_LINK: - case AMD_XGBE_AN_EXIT: - goto exit_unlock; - - default: - priv->an_state = AMD_XGBE_AN_ERROR; - } - - if (priv->an_state == AMD_XGBE_AN_ERROR) { - netdev_err(phydev->attached_dev, - "error during auto-negotiation, state=%u\n", - cur_state); - goto exit_unlock; - } - - sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0; - - mutex_unlock(&priv->an_mutex); - - if (sleep) - usleep_range(20, 50); - } - -exit_unlock: - priv->an_result = priv->an_state; - priv->an_state = AMD_XGBE_AN_READY; - - mutex_unlock(&priv->an_mutex); -} - -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; - - return 0; -} - -static int amd_xgbe_phy_config_init(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - - /* Initialize supported features */ - phydev->supported = SUPPORTED_Autoneg; - phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause; - phydev->supported |= SUPPORTED_Backplane; - phydev->supported |= SUPPORTED_10000baseKR_Full | - SUPPORTED_10000baseR_FEC; - 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; - } - phydev->advertising = phydev->supported; - - /* Turn off and clear interrupts */ - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0); - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0); - - return 0; -} - -static int amd_xgbe_phy_setup_forced(struct phy_device *phydev) -{ - int ret; - - /* Disable auto-negotiation */ - ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1); - if (ret < 0) - return ret; - - ret &= ~MDIO_AN_CTRL1_ENABLE; - phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret); - - /* Validate/Set specified speed */ - switch (phydev->speed) { - case SPEED_10000: - ret = amd_xgbe_phy_xgmii_mode(phydev); - break; - - case SPEED_2500: - ret = amd_xgbe_phy_gmii_2500_mode(phydev); - break; - - case SPEED_1000: - ret = amd_xgbe_phy_gmii_mode(phydev); - 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; - - /* Get the current speed mode */ - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - return ret; - - /* Start/Restart the auto-negotiation state machine */ - mutex_lock(&priv->an_mutex); - priv->an_result = AMD_XGBE_AN_READY; - priv->an_state = AMD_XGBE_AN_START; - priv->kr_state = AMD_XGBE_RX_READY; - priv->kx_state = AMD_XGBE_RX_READY; - mutex_unlock(&priv->an_mutex); - - queue_work(priv->an_workqueue, &priv->an_work); - - return 0; -} - -static int amd_xgbe_phy_aneg_done(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_an state; - - mutex_lock(&priv->an_mutex); - state = priv->an_result; - mutex_unlock(&priv->an_mutex); - - return (state == AMD_XGBE_AN_COMPLETE); -} - -static int amd_xgbe_phy_update_link(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv = phydev->priv; - enum amd_xgbe_phy_an state; - unsigned int check_again, autoneg; - int ret; - - /* If we're doing auto-negotiation don't report link down */ - mutex_lock(&priv->an_mutex); - state = priv->an_state; - mutex_unlock(&priv->an_mutex); - - if (state != AMD_XGBE_AN_READY) { - phydev->link = 1; - return 0; - } - - /* Since the device can be in the wrong mode when a link is - * (re-)established (cable connected after the interface is - * up, etc.), the link status may report no link. If there - * is no link, try switching modes and checking the status - * again if auto negotiation is enabled. - */ - check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0; -again: - /* 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; - - if (!phydev->link) { - if (check_again) { - ret = amd_xgbe_phy_switch_mode(phydev); - if (ret < 0) - return ret; - check_again = 0; - goto again; - } - } - - autoneg = (phydev->link && !priv->link) ? 1 : 0; - priv->link = phydev->link; - if (autoneg) { - /* Link is (back) up, re-start auto-negotiation */ - ret = amd_xgbe_phy_config_aneg(phydev); - if (ret < 0) - return ret; - } - - 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, mode, ad_ret, lp_ret; - - ret = amd_xgbe_phy_update_link(phydev); - if (ret) - return ret; - - mode = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (mode < 0) - return mode; - mode &= MDIO_PCS_CTRL2_TYPE; - - if (phydev->autoneg == AUTONEG_ENABLE) { - 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; - if (mode != MDIO_PCS_CTRL2_10GBR) { - ret = amd_xgbe_phy_xgmii_mode(phydev); - if (ret < 0) - return ret; - } - } else { - int (*mode_fcn)(struct phy_device *); - - if (priv->speed_set == - AMD_XGBE_PHY_SPEEDSET_1000_10000) { - phydev->speed = SPEED_1000; - mode_fcn = amd_xgbe_phy_gmii_mode; - } else { - phydev->speed = SPEED_2500; - mode_fcn = amd_xgbe_phy_gmii_2500_mode; - } - - if (mode == MDIO_PCS_CTRL2_10GBR) { - ret = mode_fcn(phydev); - if (ret < 0) - return ret; - } - } - - phydev->duplex = DUPLEX_FULL; - } else { - if (mode == MDIO_PCS_CTRL2_10GBR) { - phydev->speed = SPEED_10000; - } else { - if (priv->speed_set == - AMD_XGBE_PHY_SPEEDSET_1000_10000) - phydev->speed = SPEED_1000; - else - phydev->speed = SPEED_2500; - } - phydev->duplex = DUPLEX_FULL; - phydev->pause = 0; - phydev->asym_pause = 0; - } - - return 0; -} - -static int amd_xgbe_phy_suspend(struct phy_device *phydev) -{ - int ret; - - mutex_lock(&phydev->lock); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - goto unlock; - - 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) -{ - int ret; - - mutex_lock(&phydev->lock); - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1); - if (ret < 0) - goto unlock; - - 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_probe(struct phy_device *phydev) -{ - struct amd_xgbe_phy_priv *priv; - struct platform_device *pdev; - struct device *dev; - char *wq_name; - const __be32 *property; - unsigned int speed_set; - int ret; - - if (!phydev->dev.of_node) - return -EINVAL; - - pdev = of_find_device_by_node(phydev->dev.of_node); - if (!pdev) - return -EINVAL; - dev = &pdev->dev; - - wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name); - if (!wq_name) { - ret = -ENOMEM; - goto err_pdev; - } - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - ret = -ENOMEM; - goto err_name; - } - - priv->pdev = pdev; - priv->dev = dev; - priv->phydev = phydev; - - /* Get the device mmio areas */ - priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - 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_priv; - } - - priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - 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(pdev, IORESOURCE_MEM, 2); - 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 device speed set property */ - speed_set = 0; - property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY, - NULL); - if (property) - speed_set = be32_to_cpu(*property); - - switch (speed_set) { - case 0: - priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000; - break; - case 1: - priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000; - break; - default: - dev_err(dev, "invalid amd,speed-set property\n"); - ret = -EINVAL; - goto err_sir1; - } - - priv->link = 1; - - ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL2); - if (ret < 0) - goto err_sir1; - if ((ret & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR) - priv->mode = AMD_XGBE_MODE_KR; - else - priv->mode = AMD_XGBE_MODE_KX; - - mutex_init(&priv->an_mutex); - INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine); - priv->an_workqueue = create_singlethread_workqueue(wq_name); - if (!priv->an_workqueue) { - ret = -ENOMEM; - goto err_sir1; - } - - phydev->priv = priv; - - kfree(wq_name); - of_dev_put(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_priv: - devm_kfree(dev, priv); - -err_name: - kfree(wq_name); - -err_pdev: - of_dev_put(pdev); - - 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; - - /* Stop any in process auto-negotiation */ - mutex_lock(&priv->an_mutex); - priv->an_state = AMD_XGBE_AN_EXIT; - mutex_unlock(&priv->an_mutex); - - 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, - .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, - }, - }, -}; - -static int __init amd_xgbe_phy_init(void) -{ - return phy_drivers_register(amd_xgbe_phy_driver, - ARRAY_SIZE(amd_xgbe_phy_driver)); -} - -static void __exit amd_xgbe_phy_exit(void) -{ - phy_drivers_unregister(amd_xgbe_phy_driver, - ARRAY_SIZE(amd_xgbe_phy_driver)); -} - -module_init(amd_xgbe_phy_init); -module_exit(amd_xgbe_phy_exit); - -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); -- To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to majordomo@...r.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists