18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for Teranetics PHY 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Shaohui Xie <Shaohui.Xie@freescale.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright 2015 Freescale Semiconductor, Inc. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/mii.h> 138c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 148c2ecf20Sopenharmony_ci#include <linux/mdio.h> 158c2ecf20Sopenharmony_ci#include <linux/phy.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Teranetics PHY driver"); 188c2ecf20Sopenharmony_ciMODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>"); 198c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define PHY_ID_TN2020 0x00a19410 228c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LNSTAT_SYNC0 0x0001 238c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LNSTAT_SYNC1 0x0002 248c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LNSTAT_SYNC2 0x0004 258c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LNSTAT_SYNC3 0x0008 268c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LNSTAT_ALIGN 0x1000 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \ 298c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT_SYNC1 | \ 308c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT_SYNC2 | \ 318c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT_SYNC3 | \ 328c2ecf20Sopenharmony_ci MDIO_PHYXS_LNSTAT_ALIGN) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic int teranetics_aneg_done(struct phy_device *phydev) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci /* auto negotiation state can only be checked when using copper 378c2ecf20Sopenharmony_ci * port, if using fiber port, just lie it's done. 388c2ecf20Sopenharmony_ci */ 398c2ecf20Sopenharmony_ci if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) 408c2ecf20Sopenharmony_ci return genphy_c45_aneg_done(phydev); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci return 1; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic int teranetics_read_status(struct phy_device *phydev) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int reg; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci phydev->link = 1; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci phydev->speed = SPEED_10000; 528c2ecf20Sopenharmony_ci phydev->duplex = DUPLEX_FULL; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) { 558c2ecf20Sopenharmony_ci reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT); 568c2ecf20Sopenharmony_ci if (reg < 0 || 578c2ecf20Sopenharmony_ci !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) { 588c2ecf20Sopenharmony_ci phydev->link = 0; 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1); 638c2ecf20Sopenharmony_ci if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS)) 648c2ecf20Sopenharmony_ci phydev->link = 0; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_cistatic int teranetics_match_phy_device(struct phy_device *phydev) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020; 738c2ecf20Sopenharmony_ci} 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic struct phy_driver teranetics_driver[] = { 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci .phy_id = PHY_ID_TN2020, 788c2ecf20Sopenharmony_ci .phy_id_mask = 0xffffffff, 798c2ecf20Sopenharmony_ci .name = "Teranetics TN2020", 808c2ecf20Sopenharmony_ci .features = PHY_10GBIT_FEATURES, 818c2ecf20Sopenharmony_ci .aneg_done = teranetics_aneg_done, 828c2ecf20Sopenharmony_ci .config_aneg = gen10g_config_aneg, 838c2ecf20Sopenharmony_ci .read_status = teranetics_read_status, 848c2ecf20Sopenharmony_ci .match_phy_device = teranetics_match_phy_device, 858c2ecf20Sopenharmony_ci}, 868c2ecf20Sopenharmony_ci}; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cimodule_phy_driver(teranetics_driver); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic struct mdio_device_id __maybe_unused teranetics_tbl[] = { 918c2ecf20Sopenharmony_ci { PHY_ID_TN2020, 0xffffffff }, 928c2ecf20Sopenharmony_ci { } 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(mdio, teranetics_tbl); 96