^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Driver for Teranetics PHY
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright 2015 Freescale Semiconductor, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/mii.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/ethtool.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/mdio.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/phy.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) MODULE_DESCRIPTION("Teranetics PHY driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define PHY_ID_TN2020 0x00a19410
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #define MDIO_PHYXS_LNSTAT_SYNC0 0x0001
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define MDIO_PHYXS_LNSTAT_SYNC1 0x0002
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define MDIO_PHYXS_LNSTAT_SYNC2 0x0004
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define MDIO_PHYXS_LNSTAT_SYNC3 0x0008
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define MDIO_PHYXS_LNSTAT_ALIGN 0x1000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define MDIO_PHYXS_LANE_READY (MDIO_PHYXS_LNSTAT_SYNC0 | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) MDIO_PHYXS_LNSTAT_SYNC1 | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) MDIO_PHYXS_LNSTAT_SYNC2 | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) MDIO_PHYXS_LNSTAT_SYNC3 | \
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) MDIO_PHYXS_LNSTAT_ALIGN)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static int teranetics_aneg_done(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /* auto negotiation state can only be checked when using copper
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) * port, if using fiber port, just lie it's done.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) return genphy_c45_aneg_done(phydev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) return 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) static int teranetics_read_status(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) int reg;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) phydev->link = 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) phydev->speed = SPEED_10000;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) phydev->duplex = DUPLEX_FULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) if (reg < 0 ||
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) phydev->link = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) phydev->link = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) static int teranetics_match_phy_device(struct phy_device *phydev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) static struct phy_driver teranetics_driver[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) .phy_id = PHY_ID_TN2020,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) .phy_id_mask = 0xffffffff,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) .name = "Teranetics TN2020",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) .features = PHY_10GBIT_FEATURES,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) .aneg_done = teranetics_aneg_done,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) .config_aneg = gen10g_config_aneg,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) .read_status = teranetics_read_status,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) .match_phy_device = teranetics_match_phy_device,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) module_phy_driver(teranetics_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) static struct mdio_device_id __maybe_unused teranetics_tbl[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) { PHY_ID_TN2020, 0xffffffff },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) { }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) MODULE_DEVICE_TABLE(mdio, teranetics_tbl);