Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Driver for Teranetics PHY
0004  *
0005  * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
0006  *
0007  * Copyright 2015 Freescale Semiconductor, Inc.
0008  */
0009 
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/mii.h>
0013 #include <linux/ethtool.h>
0014 #include <linux/mdio.h>
0015 #include <linux/phy.h>
0016 
0017 MODULE_DESCRIPTION("Teranetics PHY driver");
0018 MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
0019 MODULE_LICENSE("GPL v2");
0020 
0021 #define PHY_ID_TN2020   0x00a19410
0022 #define MDIO_PHYXS_LNSTAT_SYNC0 0x0001
0023 #define MDIO_PHYXS_LNSTAT_SYNC1 0x0002
0024 #define MDIO_PHYXS_LNSTAT_SYNC2 0x0004
0025 #define MDIO_PHYXS_LNSTAT_SYNC3 0x0008
0026 #define MDIO_PHYXS_LNSTAT_ALIGN 0x1000
0027 
0028 #define MDIO_PHYXS_LANE_READY   (MDIO_PHYXS_LNSTAT_SYNC0 | \
0029                 MDIO_PHYXS_LNSTAT_SYNC1 | \
0030                 MDIO_PHYXS_LNSTAT_SYNC2 | \
0031                 MDIO_PHYXS_LNSTAT_SYNC3 | \
0032                 MDIO_PHYXS_LNSTAT_ALIGN)
0033 
0034 static int teranetics_aneg_done(struct phy_device *phydev)
0035 {
0036     /* auto negotiation state can only be checked when using copper
0037      * port, if using fiber port, just lie it's done.
0038      */
0039     if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93))
0040         return genphy_c45_aneg_done(phydev);
0041 
0042     return 1;
0043 }
0044 
0045 static int teranetics_read_status(struct phy_device *phydev)
0046 {
0047     int reg;
0048 
0049     phydev->link = 1;
0050 
0051     phydev->speed = SPEED_10000;
0052     phydev->duplex = DUPLEX_FULL;
0053 
0054     if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) {
0055         reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT);
0056         if (reg < 0 ||
0057             !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) {
0058             phydev->link = 0;
0059             return 0;
0060         }
0061 
0062         reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
0063         if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
0064             phydev->link = 0;
0065     }
0066 
0067     return 0;
0068 }
0069 
0070 static int teranetics_match_phy_device(struct phy_device *phydev)
0071 {
0072     return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
0073 }
0074 
0075 static struct phy_driver teranetics_driver[] = {
0076 {
0077     .phy_id     = PHY_ID_TN2020,
0078     .phy_id_mask    = 0xffffffff,
0079     .name       = "Teranetics TN2020",
0080     .features       = PHY_10GBIT_FEATURES,
0081     .aneg_done  = teranetics_aneg_done,
0082     .config_aneg    = gen10g_config_aneg,
0083     .read_status    = teranetics_read_status,
0084     .match_phy_device = teranetics_match_phy_device,
0085 },
0086 };
0087 
0088 module_phy_driver(teranetics_driver);
0089 
0090 static struct mdio_device_id __maybe_unused teranetics_tbl[] = {
0091     { PHY_ID_TN2020, 0xffffffff },
0092     { }
0093 };
0094 
0095 MODULE_DEVICE_TABLE(mdio, teranetics_tbl);