18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Broadcom GENET MDIO routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2014-2017 Broadcom 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/acpi.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/delay.h> 118c2ecf20Sopenharmony_ci#include <linux/wait.h> 128c2ecf20Sopenharmony_ci#include <linux/mii.h> 138c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 148c2ecf20Sopenharmony_ci#include <linux/bitops.h> 158c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 168c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 178c2ecf20Sopenharmony_ci#include <linux/phy.h> 188c2ecf20Sopenharmony_ci#include <linux/phy_fixed.h> 198c2ecf20Sopenharmony_ci#include <linux/brcmphy.h> 208c2ecf20Sopenharmony_ci#include <linux/of.h> 218c2ecf20Sopenharmony_ci#include <linux/of_net.h> 228c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 238c2ecf20Sopenharmony_ci#include <linux/platform_data/bcmgenet.h> 248c2ecf20Sopenharmony_ci#include <linux/platform_data/mdio-bcm-unimac.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include "bcmgenet.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci/* setup netdev link state when PHY link status change and 308c2ecf20Sopenharmony_ci * update UMAC and RGMII block when link up 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_civoid bcmgenet_mii_setup(struct net_device *dev) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 358c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 368c2ecf20Sopenharmony_ci u32 reg, cmd_bits = 0; 378c2ecf20Sopenharmony_ci bool status_changed = false; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (priv->old_link != phydev->link) { 408c2ecf20Sopenharmony_ci status_changed = true; 418c2ecf20Sopenharmony_ci priv->old_link = phydev->link; 428c2ecf20Sopenharmony_ci } 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (phydev->link) { 458c2ecf20Sopenharmony_ci /* check speed/duplex/pause changes */ 468c2ecf20Sopenharmony_ci if (priv->old_speed != phydev->speed) { 478c2ecf20Sopenharmony_ci status_changed = true; 488c2ecf20Sopenharmony_ci priv->old_speed = phydev->speed; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (priv->old_duplex != phydev->duplex) { 528c2ecf20Sopenharmony_ci status_changed = true; 538c2ecf20Sopenharmony_ci priv->old_duplex = phydev->duplex; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (priv->old_pause != phydev->pause) { 578c2ecf20Sopenharmony_ci status_changed = true; 588c2ecf20Sopenharmony_ci priv->old_pause = phydev->pause; 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* done if nothing has changed */ 628c2ecf20Sopenharmony_ci if (!status_changed) 638c2ecf20Sopenharmony_ci return; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci /* speed */ 668c2ecf20Sopenharmony_ci if (phydev->speed == SPEED_1000) 678c2ecf20Sopenharmony_ci cmd_bits = UMAC_SPEED_1000; 688c2ecf20Sopenharmony_ci else if (phydev->speed == SPEED_100) 698c2ecf20Sopenharmony_ci cmd_bits = UMAC_SPEED_100; 708c2ecf20Sopenharmony_ci else 718c2ecf20Sopenharmony_ci cmd_bits = UMAC_SPEED_10; 728c2ecf20Sopenharmony_ci cmd_bits <<= CMD_SPEED_SHIFT; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* duplex */ 758c2ecf20Sopenharmony_ci if (phydev->duplex != DUPLEX_FULL) 768c2ecf20Sopenharmony_ci cmd_bits |= CMD_HD_EN; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci /* pause capability */ 798c2ecf20Sopenharmony_ci if (!phydev->pause) 808c2ecf20Sopenharmony_ci cmd_bits |= CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci /* 838c2ecf20Sopenharmony_ci * Program UMAC and RGMII block based on established 848c2ecf20Sopenharmony_ci * link speed, duplex, and pause. The speed set in 858c2ecf20Sopenharmony_ci * umac->cmd tell RGMII block which clock to use for 868c2ecf20Sopenharmony_ci * transmit -- 25MHz(100Mbps) or 125MHz(1Gbps). 878c2ecf20Sopenharmony_ci * Receive clock is provided by the PHY. 888c2ecf20Sopenharmony_ci */ 898c2ecf20Sopenharmony_ci reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); 908c2ecf20Sopenharmony_ci reg &= ~OOB_DISABLE; 918c2ecf20Sopenharmony_ci reg |= RGMII_LINK; 928c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci reg = bcmgenet_umac_readl(priv, UMAC_CMD); 958c2ecf20Sopenharmony_ci reg &= ~((CMD_SPEED_MASK << CMD_SPEED_SHIFT) | 968c2ecf20Sopenharmony_ci CMD_HD_EN | 978c2ecf20Sopenharmony_ci CMD_RX_PAUSE_IGNORE | CMD_TX_PAUSE_IGNORE); 988c2ecf20Sopenharmony_ci reg |= cmd_bits; 998c2ecf20Sopenharmony_ci if (reg & CMD_SW_RESET) { 1008c2ecf20Sopenharmony_ci reg &= ~CMD_SW_RESET; 1018c2ecf20Sopenharmony_ci bcmgenet_umac_writel(priv, reg, UMAC_CMD); 1028c2ecf20Sopenharmony_ci udelay(2); 1038c2ecf20Sopenharmony_ci reg |= CMD_TX_EN | CMD_RX_EN; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci bcmgenet_umac_writel(priv, reg, UMAC_CMD); 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci priv->eee.eee_active = phy_init_eee(phydev, 0) >= 0; 1088c2ecf20Sopenharmony_ci bcmgenet_eee_enable_set(dev, 1098c2ecf20Sopenharmony_ci priv->eee.eee_enabled && priv->eee.eee_active, 1108c2ecf20Sopenharmony_ci priv->eee.tx_lpi_enabled); 1118c2ecf20Sopenharmony_ci } else { 1128c2ecf20Sopenharmony_ci /* done if nothing has changed */ 1138c2ecf20Sopenharmony_ci if (!status_changed) 1148c2ecf20Sopenharmony_ci return; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* needed for MoCA fixed PHY to reflect correct link status */ 1178c2ecf20Sopenharmony_ci netif_carrier_off(dev); 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci phy_print_status(phydev); 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic int bcmgenet_fixed_phy_link_update(struct net_device *dev, 1258c2ecf20Sopenharmony_ci struct fixed_phy_status *status) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv; 1288c2ecf20Sopenharmony_ci u32 reg; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci if (dev && dev->phydev && status) { 1318c2ecf20Sopenharmony_ci priv = netdev_priv(dev); 1328c2ecf20Sopenharmony_ci reg = bcmgenet_umac_readl(priv, UMAC_MODE); 1338c2ecf20Sopenharmony_ci status->link = !!(reg & MODE_LINK_STATUS); 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_civoid bcmgenet_phy_power_set(struct net_device *dev, bool enable) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 1428c2ecf20Sopenharmony_ci u32 reg = 0; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* EXT_GPHY_CTRL is only valid for GENETv4 and onward */ 1458c2ecf20Sopenharmony_ci if (GENET_IS_V4(priv)) { 1468c2ecf20Sopenharmony_ci reg = bcmgenet_ext_readl(priv, EXT_GPHY_CTRL); 1478c2ecf20Sopenharmony_ci if (enable) { 1488c2ecf20Sopenharmony_ci reg &= ~EXT_CK25_DIS; 1498c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); 1508c2ecf20Sopenharmony_ci mdelay(1); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci reg &= ~(EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN); 1538c2ecf20Sopenharmony_ci reg |= EXT_GPHY_RESET; 1548c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); 1558c2ecf20Sopenharmony_ci mdelay(1); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci reg &= ~EXT_GPHY_RESET; 1588c2ecf20Sopenharmony_ci } else { 1598c2ecf20Sopenharmony_ci reg |= EXT_CFG_IDDQ_BIAS | EXT_CFG_PWR_DOWN | 1608c2ecf20Sopenharmony_ci EXT_GPHY_RESET; 1618c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); 1628c2ecf20Sopenharmony_ci mdelay(1); 1638c2ecf20Sopenharmony_ci reg |= EXT_CK25_DIS; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_GPHY_CTRL); 1668c2ecf20Sopenharmony_ci udelay(60); 1678c2ecf20Sopenharmony_ci } else { 1688c2ecf20Sopenharmony_ci mdelay(1); 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void bcmgenet_moca_phy_setup(struct bcmgenet_priv *priv) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci if (priv->hw_params->flags & GENET_HAS_MOCA_LINK_DET) 1758c2ecf20Sopenharmony_ci fixed_phy_set_link_update(priv->dev->phydev, 1768c2ecf20Sopenharmony_ci bcmgenet_fixed_phy_link_update); 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ciint bcmgenet_mii_config(struct net_device *dev, bool init) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 1828c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 1838c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 1848c2ecf20Sopenharmony_ci const char *phy_name = NULL; 1858c2ecf20Sopenharmony_ci u32 id_mode_dis = 0; 1868c2ecf20Sopenharmony_ci u32 port_ctrl; 1878c2ecf20Sopenharmony_ci u32 reg; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci switch (priv->phy_interface) { 1908c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_INTERNAL: 1918c2ecf20Sopenharmony_ci phy_name = "internal PHY"; 1928c2ecf20Sopenharmony_ci fallthrough; 1938c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MOCA: 1948c2ecf20Sopenharmony_ci /* Irrespective of the actually configured PHY speed (100 or 1958c2ecf20Sopenharmony_ci * 1000) GENETv4 only has an internal GPHY so we will just end 1968c2ecf20Sopenharmony_ci * up masking the Gigabit features from what we support, not 1978c2ecf20Sopenharmony_ci * switching to the EPHY 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ci if (GENET_IS_V4(priv)) 2008c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_INT_GPHY; 2018c2ecf20Sopenharmony_ci else 2028c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_INT_EPHY; 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci if (!phy_name) { 2058c2ecf20Sopenharmony_ci phy_name = "MoCA"; 2068c2ecf20Sopenharmony_ci if (!GENET_IS_V5(priv)) 2078c2ecf20Sopenharmony_ci port_ctrl |= LED_ACT_SOURCE_MAC; 2088c2ecf20Sopenharmony_ci bcmgenet_moca_phy_setup(priv); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_MII: 2138c2ecf20Sopenharmony_ci phy_name = "external MII"; 2148c2ecf20Sopenharmony_ci phy_set_max_speed(phydev, SPEED_100); 2158c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_EPHY; 2168c2ecf20Sopenharmony_ci break; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_REVMII: 2198c2ecf20Sopenharmony_ci phy_name = "external RvMII"; 2208c2ecf20Sopenharmony_ci /* of_mdiobus_register took care of reading the 'max-speed' 2218c2ecf20Sopenharmony_ci * PHY property for us, effectively limiting the PHY supported 2228c2ecf20Sopenharmony_ci * capabilities, use that knowledge to also configure the 2238c2ecf20Sopenharmony_ci * Reverse MII interface correctly. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 2268c2ecf20Sopenharmony_ci dev->phydev->supported)) 2278c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_RVMII_50; 2288c2ecf20Sopenharmony_ci else 2298c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_RVMII_25; 2308c2ecf20Sopenharmony_ci break; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII: 2338c2ecf20Sopenharmony_ci /* RGMII_NO_ID: TXC transitions at the same time as TXD 2348c2ecf20Sopenharmony_ci * (requires PCB or receiver-side delay) 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * ID is implicitly disabled for 100Mbps (RG)MII operation. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci phy_name = "external RGMII (no delay)"; 2398c2ecf20Sopenharmony_ci id_mode_dis = BIT(16); 2408c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_GPHY; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_TXID: 2448c2ecf20Sopenharmony_ci /* RGMII_TXID: Add 2ns delay on TXC (90 degree shift) */ 2458c2ecf20Sopenharmony_ci phy_name = "external RGMII (TX delay)"; 2468c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_GPHY; 2478c2ecf20Sopenharmony_ci break; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci case PHY_INTERFACE_MODE_RGMII_RXID: 2508c2ecf20Sopenharmony_ci phy_name = "external RGMII (RX delay)"; 2518c2ecf20Sopenharmony_ci port_ctrl = PORT_MODE_EXT_GPHY; 2528c2ecf20Sopenharmony_ci break; 2538c2ecf20Sopenharmony_ci default: 2548c2ecf20Sopenharmony_ci dev_err(kdev, "unknown phy mode: %d\n", priv->phy_interface); 2558c2ecf20Sopenharmony_ci return -EINVAL; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci bcmgenet_sys_writel(priv, port_ctrl, SYS_PORT_CTRL); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci priv->ext_phy = !priv->internal_phy && 2618c2ecf20Sopenharmony_ci (priv->phy_interface != PHY_INTERFACE_MODE_MOCA); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* This is an external PHY (xMII), so we need to enable the RGMII 2648c2ecf20Sopenharmony_ci * block for the interface to work 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci if (priv->ext_phy) { 2678c2ecf20Sopenharmony_ci reg = bcmgenet_ext_readl(priv, EXT_RGMII_OOB_CTRL); 2688c2ecf20Sopenharmony_ci reg &= ~ID_MODE_DIS; 2698c2ecf20Sopenharmony_ci reg |= id_mode_dis; 2708c2ecf20Sopenharmony_ci if (GENET_IS_V1(priv) || GENET_IS_V2(priv) || GENET_IS_V3(priv)) 2718c2ecf20Sopenharmony_ci reg |= RGMII_MODE_EN_V123; 2728c2ecf20Sopenharmony_ci else 2738c2ecf20Sopenharmony_ci reg |= RGMII_MODE_EN; 2748c2ecf20Sopenharmony_ci bcmgenet_ext_writel(priv, reg, EXT_RGMII_OOB_CTRL); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (init) 2788c2ecf20Sopenharmony_ci dev_info(kdev, "configuring instance for %s\n", phy_name); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci return 0; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ciint bcmgenet_mii_probe(struct net_device *dev) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 2868c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 2878c2ecf20Sopenharmony_ci struct device_node *dn = kdev->of_node; 2888c2ecf20Sopenharmony_ci struct phy_device *phydev; 2898c2ecf20Sopenharmony_ci u32 phy_flags = 0; 2908c2ecf20Sopenharmony_ci int ret; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Communicate the integrated PHY revision */ 2938c2ecf20Sopenharmony_ci if (priv->internal_phy) 2948c2ecf20Sopenharmony_ci phy_flags = priv->gphy_rev; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Initialize link state variables that bcmgenet_mii_setup() uses */ 2978c2ecf20Sopenharmony_ci priv->old_link = -1; 2988c2ecf20Sopenharmony_ci priv->old_speed = -1; 2998c2ecf20Sopenharmony_ci priv->old_duplex = -1; 3008c2ecf20Sopenharmony_ci priv->old_pause = -1; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (dn) { 3038c2ecf20Sopenharmony_ci phydev = of_phy_connect(dev, priv->phy_dn, bcmgenet_mii_setup, 3048c2ecf20Sopenharmony_ci phy_flags, priv->phy_interface); 3058c2ecf20Sopenharmony_ci if (!phydev) { 3068c2ecf20Sopenharmony_ci pr_err("could not attach to PHY\n"); 3078c2ecf20Sopenharmony_ci return -ENODEV; 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci } else { 3108c2ecf20Sopenharmony_ci if (has_acpi_companion(kdev)) { 3118c2ecf20Sopenharmony_ci char mdio_bus_id[MII_BUS_ID_SIZE]; 3128c2ecf20Sopenharmony_ci struct mii_bus *unimacbus; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d", 3158c2ecf20Sopenharmony_ci UNIMAC_MDIO_DRV_NAME, priv->pdev->id); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci unimacbus = mdio_find_bus(mdio_bus_id); 3188c2ecf20Sopenharmony_ci if (!unimacbus) { 3198c2ecf20Sopenharmony_ci pr_err("Unable to find mii\n"); 3208c2ecf20Sopenharmony_ci return -ENODEV; 3218c2ecf20Sopenharmony_ci } 3228c2ecf20Sopenharmony_ci phydev = phy_find_first(unimacbus); 3238c2ecf20Sopenharmony_ci put_device(&unimacbus->dev); 3248c2ecf20Sopenharmony_ci if (!phydev) { 3258c2ecf20Sopenharmony_ci pr_err("Unable to find PHY\n"); 3268c2ecf20Sopenharmony_ci return -ENODEV; 3278c2ecf20Sopenharmony_ci } 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci phydev = dev->phydev; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci phydev->dev_flags = phy_flags; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci ret = phy_connect_direct(dev, phydev, bcmgenet_mii_setup, 3348c2ecf20Sopenharmony_ci priv->phy_interface); 3358c2ecf20Sopenharmony_ci if (ret) { 3368c2ecf20Sopenharmony_ci pr_err("could not attach to PHY\n"); 3378c2ecf20Sopenharmony_ci return -ENODEV; 3388c2ecf20Sopenharmony_ci } 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* Configure port multiplexer based on what the probed PHY device since 3428c2ecf20Sopenharmony_ci * reading the 'max-speed' property determines the maximum supported 3438c2ecf20Sopenharmony_ci * PHY speed which is needed for bcmgenet_mii_config() to configure 3448c2ecf20Sopenharmony_ci * things appropriately. 3458c2ecf20Sopenharmony_ci */ 3468c2ecf20Sopenharmony_ci ret = bcmgenet_mii_config(dev, true); 3478c2ecf20Sopenharmony_ci if (ret) { 3488c2ecf20Sopenharmony_ci phy_disconnect(dev->phydev); 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci linkmode_copy(phydev->advertising, phydev->supported); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* The internal PHY has its link interrupts routed to the 3558c2ecf20Sopenharmony_ci * Ethernet MAC ISRs. On GENETv5 there is a hardware issue 3568c2ecf20Sopenharmony_ci * that prevents the signaling of link UP interrupts when 3578c2ecf20Sopenharmony_ci * the link operates at 10Mbps, so fallback to polling for 3588c2ecf20Sopenharmony_ci * those versions of GENET. 3598c2ecf20Sopenharmony_ci */ 3608c2ecf20Sopenharmony_ci if (priv->internal_phy && !GENET_IS_V5(priv)) 3618c2ecf20Sopenharmony_ci dev->phydev->irq = PHY_IGNORE_INTERRUPT; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic struct device_node *bcmgenet_mii_of_find_mdio(struct bcmgenet_priv *priv) 3678c2ecf20Sopenharmony_ci{ 3688c2ecf20Sopenharmony_ci struct device_node *dn = priv->pdev->dev.of_node; 3698c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 3708c2ecf20Sopenharmony_ci char *compat; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci compat = kasprintf(GFP_KERNEL, "brcm,genet-mdio-v%d", priv->version); 3738c2ecf20Sopenharmony_ci if (!compat) 3748c2ecf20Sopenharmony_ci return NULL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci priv->mdio_dn = of_get_compatible_child(dn, compat); 3778c2ecf20Sopenharmony_ci kfree(compat); 3788c2ecf20Sopenharmony_ci if (!priv->mdio_dn) { 3798c2ecf20Sopenharmony_ci dev_err(kdev, "unable to find MDIO bus node\n"); 3808c2ecf20Sopenharmony_ci return NULL; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return priv->mdio_dn; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic void bcmgenet_mii_pdata_init(struct bcmgenet_priv *priv, 3878c2ecf20Sopenharmony_ci struct unimac_mdio_pdata *ppd) 3888c2ecf20Sopenharmony_ci{ 3898c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 3908c2ecf20Sopenharmony_ci struct bcmgenet_platform_data *pd = kdev->platform_data; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Internal or external PHY with MDIO access 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci if (pd->phy_address >= 0 && pd->phy_address < PHY_MAX_ADDR) 3978c2ecf20Sopenharmony_ci ppd->phy_mask = 1 << pd->phy_address; 3988c2ecf20Sopenharmony_ci else 3998c2ecf20Sopenharmony_ci ppd->phy_mask = 0; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int bcmgenet_mii_wait(void *wait_func_data) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = wait_func_data; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci wait_event_timeout(priv->wq, 4088c2ecf20Sopenharmony_ci !(bcmgenet_umac_readl(priv, UMAC_MDIO_CMD) 4098c2ecf20Sopenharmony_ci & MDIO_START_BUSY), 4108c2ecf20Sopenharmony_ci HZ / 100); 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic int bcmgenet_mii_register(struct bcmgenet_priv *priv) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct platform_device *pdev = priv->pdev; 4178c2ecf20Sopenharmony_ci struct bcmgenet_platform_data *pdata = pdev->dev.platform_data; 4188c2ecf20Sopenharmony_ci struct device_node *dn = pdev->dev.of_node; 4198c2ecf20Sopenharmony_ci struct unimac_mdio_pdata ppd; 4208c2ecf20Sopenharmony_ci struct platform_device *ppdev; 4218c2ecf20Sopenharmony_ci struct resource *pres, res; 4228c2ecf20Sopenharmony_ci int id, ret; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci pres = platform_get_resource(pdev, IORESOURCE_MEM, 0); 4258c2ecf20Sopenharmony_ci if (!pres) { 4268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid resource\n"); 4278c2ecf20Sopenharmony_ci return -EINVAL; 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci memset(&res, 0, sizeof(res)); 4308c2ecf20Sopenharmony_ci memset(&ppd, 0, sizeof(ppd)); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci ppd.wait_func = bcmgenet_mii_wait; 4338c2ecf20Sopenharmony_ci ppd.wait_func_data = priv; 4348c2ecf20Sopenharmony_ci ppd.bus_name = "bcmgenet MII bus"; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* Unimac MDIO bus controller starts at UniMAC offset + MDIO_CMD 4378c2ecf20Sopenharmony_ci * and is 2 * 32-bits word long, 8 bytes total. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_ci res.start = pres->start + GENET_UMAC_OFF + UMAC_MDIO_CMD; 4408c2ecf20Sopenharmony_ci res.end = res.start + 8; 4418c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci if (dn) 4448c2ecf20Sopenharmony_ci id = of_alias_get_id(dn, "eth"); 4458c2ecf20Sopenharmony_ci else 4468c2ecf20Sopenharmony_ci id = pdev->id; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci ppdev = platform_device_alloc(UNIMAC_MDIO_DRV_NAME, id); 4498c2ecf20Sopenharmony_ci if (!ppdev) 4508c2ecf20Sopenharmony_ci return -ENOMEM; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Retain this platform_device pointer for later cleanup */ 4538c2ecf20Sopenharmony_ci priv->mii_pdev = ppdev; 4548c2ecf20Sopenharmony_ci ppdev->dev.parent = &pdev->dev; 4558c2ecf20Sopenharmony_ci if (dn) 4568c2ecf20Sopenharmony_ci ppdev->dev.of_node = bcmgenet_mii_of_find_mdio(priv); 4578c2ecf20Sopenharmony_ci else if (pdata) 4588c2ecf20Sopenharmony_ci bcmgenet_mii_pdata_init(priv, &ppd); 4598c2ecf20Sopenharmony_ci else 4608c2ecf20Sopenharmony_ci ppd.phy_mask = ~0; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci ret = platform_device_add_resources(ppdev, &res, 1); 4638c2ecf20Sopenharmony_ci if (ret) 4648c2ecf20Sopenharmony_ci goto out; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci ret = platform_device_add_data(ppdev, &ppd, sizeof(ppd)); 4678c2ecf20Sopenharmony_ci if (ret) 4688c2ecf20Sopenharmony_ci goto out; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = platform_device_add(ppdev); 4718c2ecf20Sopenharmony_ci if (ret) 4728c2ecf20Sopenharmony_ci goto out; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ciout: 4768c2ecf20Sopenharmony_ci platform_device_put(ppdev); 4778c2ecf20Sopenharmony_ci return ret; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int bcmgenet_phy_interface_init(struct bcmgenet_priv *priv) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 4838c2ecf20Sopenharmony_ci int phy_mode = device_get_phy_mode(kdev); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (phy_mode < 0) { 4868c2ecf20Sopenharmony_ci dev_err(kdev, "invalid PHY mode property\n"); 4878c2ecf20Sopenharmony_ci return phy_mode; 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci priv->phy_interface = phy_mode; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* We need to specifically look up whether this PHY interface is 4938c2ecf20Sopenharmony_ci * internal or not *before* we even try to probe the PHY driver 4948c2ecf20Sopenharmony_ci * over MDIO as we may have shut down the internal PHY for power 4958c2ecf20Sopenharmony_ci * saving purposes. 4968c2ecf20Sopenharmony_ci */ 4978c2ecf20Sopenharmony_ci if (priv->phy_interface == PHY_INTERFACE_MODE_INTERNAL) 4988c2ecf20Sopenharmony_ci priv->internal_phy = true; 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return 0; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int bcmgenet_mii_of_init(struct bcmgenet_priv *priv) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct device_node *dn = priv->pdev->dev.of_node; 5068c2ecf20Sopenharmony_ci struct phy_device *phydev; 5078c2ecf20Sopenharmony_ci int ret; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci /* Fetch the PHY phandle */ 5108c2ecf20Sopenharmony_ci priv->phy_dn = of_parse_phandle(dn, "phy-handle", 0); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci /* In the case of a fixed PHY, the DT node associated 5138c2ecf20Sopenharmony_ci * to the PHY is the Ethernet MAC DT node. 5148c2ecf20Sopenharmony_ci */ 5158c2ecf20Sopenharmony_ci if (!priv->phy_dn && of_phy_is_fixed_link(dn)) { 5168c2ecf20Sopenharmony_ci ret = of_phy_register_fixed_link(dn); 5178c2ecf20Sopenharmony_ci if (ret) 5188c2ecf20Sopenharmony_ci return ret; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci priv->phy_dn = of_node_get(dn); 5218c2ecf20Sopenharmony_ci } 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Get the link mode */ 5248c2ecf20Sopenharmony_ci ret = bcmgenet_phy_interface_init(priv); 5258c2ecf20Sopenharmony_ci if (ret) 5268c2ecf20Sopenharmony_ci return ret; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci /* Make sure we initialize MoCA PHYs with a link down */ 5298c2ecf20Sopenharmony_ci if (priv->phy_interface == PHY_INTERFACE_MODE_MOCA) { 5308c2ecf20Sopenharmony_ci phydev = of_phy_find_device(dn); 5318c2ecf20Sopenharmony_ci if (phydev) { 5328c2ecf20Sopenharmony_ci phydev->link = 0; 5338c2ecf20Sopenharmony_ci put_device(&phydev->mdio.dev); 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci return 0; 5388c2ecf20Sopenharmony_ci} 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_cistatic int bcmgenet_mii_pd_init(struct bcmgenet_priv *priv) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 5438c2ecf20Sopenharmony_ci struct bcmgenet_platform_data *pd = kdev->platform_data; 5448c2ecf20Sopenharmony_ci char phy_name[MII_BUS_ID_SIZE + 3]; 5458c2ecf20Sopenharmony_ci char mdio_bus_id[MII_BUS_ID_SIZE]; 5468c2ecf20Sopenharmony_ci struct phy_device *phydev; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci snprintf(mdio_bus_id, MII_BUS_ID_SIZE, "%s-%d", 5498c2ecf20Sopenharmony_ci UNIMAC_MDIO_DRV_NAME, priv->pdev->id); 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (pd->phy_interface != PHY_INTERFACE_MODE_MOCA && pd->mdio_enabled) { 5528c2ecf20Sopenharmony_ci snprintf(phy_name, MII_BUS_ID_SIZE, PHY_ID_FMT, 5538c2ecf20Sopenharmony_ci mdio_bus_id, pd->phy_address); 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* 5568c2ecf20Sopenharmony_ci * Internal or external PHY with MDIO access 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci phydev = phy_attach(priv->dev, phy_name, pd->phy_interface); 5598c2ecf20Sopenharmony_ci if (!phydev) { 5608c2ecf20Sopenharmony_ci dev_err(kdev, "failed to register PHY device\n"); 5618c2ecf20Sopenharmony_ci return -ENODEV; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci } else { 5648c2ecf20Sopenharmony_ci /* 5658c2ecf20Sopenharmony_ci * MoCA port or no MDIO access. 5668c2ecf20Sopenharmony_ci * Use fixed PHY to represent the link layer. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_ci struct fixed_phy_status fphy_status = { 5698c2ecf20Sopenharmony_ci .link = 1, 5708c2ecf20Sopenharmony_ci .speed = pd->phy_speed, 5718c2ecf20Sopenharmony_ci .duplex = pd->phy_duplex, 5728c2ecf20Sopenharmony_ci .pause = 0, 5738c2ecf20Sopenharmony_ci .asym_pause = 0, 5748c2ecf20Sopenharmony_ci }; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci phydev = fixed_phy_register(PHY_POLL, &fphy_status, NULL); 5778c2ecf20Sopenharmony_ci if (IS_ERR(phydev)) { 5788c2ecf20Sopenharmony_ci dev_err(kdev, "failed to register fixed PHY device\n"); 5798c2ecf20Sopenharmony_ci return -ENODEV; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci /* Make sure we initialize MoCA PHYs with a link down */ 5838c2ecf20Sopenharmony_ci phydev->link = 0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci priv->phy_interface = pd->phy_interface; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci return 0; 5908c2ecf20Sopenharmony_ci} 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cistatic int bcmgenet_mii_bus_init(struct bcmgenet_priv *priv) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct device *kdev = &priv->pdev->dev; 5958c2ecf20Sopenharmony_ci struct device_node *dn = kdev->of_node; 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci if (dn) 5988c2ecf20Sopenharmony_ci return bcmgenet_mii_of_init(priv); 5998c2ecf20Sopenharmony_ci else if (has_acpi_companion(kdev)) 6008c2ecf20Sopenharmony_ci return bcmgenet_phy_interface_init(priv); 6018c2ecf20Sopenharmony_ci else 6028c2ecf20Sopenharmony_ci return bcmgenet_mii_pd_init(priv); 6038c2ecf20Sopenharmony_ci} 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ciint bcmgenet_mii_init(struct net_device *dev) 6068c2ecf20Sopenharmony_ci{ 6078c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 6088c2ecf20Sopenharmony_ci int ret; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci ret = bcmgenet_mii_register(priv); 6118c2ecf20Sopenharmony_ci if (ret) 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci ret = bcmgenet_mii_bus_init(priv); 6158c2ecf20Sopenharmony_ci if (ret) 6168c2ecf20Sopenharmony_ci goto out; 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci return 0; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ciout: 6218c2ecf20Sopenharmony_ci bcmgenet_mii_exit(dev); 6228c2ecf20Sopenharmony_ci return ret; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_civoid bcmgenet_mii_exit(struct net_device *dev) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct bcmgenet_priv *priv = netdev_priv(dev); 6288c2ecf20Sopenharmony_ci struct device_node *dn = priv->pdev->dev.of_node; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (of_phy_is_fixed_link(dn)) 6318c2ecf20Sopenharmony_ci of_phy_deregister_fixed_link(dn); 6328c2ecf20Sopenharmony_ci of_node_put(priv->phy_dn); 6338c2ecf20Sopenharmony_ci clk_prepare_enable(priv->clk); 6348c2ecf20Sopenharmony_ci platform_device_unregister(priv->mii_pdev); 6358c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 6368c2ecf20Sopenharmony_ci} 637