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