xref: /linux/drivers/net/phy/teranetics.c (revision 94724d102c829d9f2105b1cb6e831c99174cd49d)
1f61687c0SShaohui Xie /*
2f61687c0SShaohui Xie  * Driver for Teranetics PHY
3f61687c0SShaohui Xie  *
4f61687c0SShaohui Xie  * Author: Shaohui Xie <Shaohui.Xie@freescale.com>
5f61687c0SShaohui Xie  *
6f61687c0SShaohui Xie  * Copyright 2015 Freescale Semiconductor, Inc.
7f61687c0SShaohui Xie  *
8f61687c0SShaohui Xie  * This file is licensed under the terms of the GNU General Public License
9f61687c0SShaohui Xie  * version 2.  This program is licensed "as is" without any warranty of any
10f61687c0SShaohui Xie  * kind, whether express or implied.
11f61687c0SShaohui Xie  */
12f61687c0SShaohui Xie 
13f61687c0SShaohui Xie #include <linux/kernel.h>
14f61687c0SShaohui Xie #include <linux/module.h>
15f61687c0SShaohui Xie #include <linux/mii.h>
16f61687c0SShaohui Xie #include <linux/ethtool.h>
17f61687c0SShaohui Xie #include <linux/mdio.h>
18f61687c0SShaohui Xie #include <linux/phy.h>
19f61687c0SShaohui Xie 
20f61687c0SShaohui Xie MODULE_DESCRIPTION("Teranetics PHY driver");
21f61687c0SShaohui Xie MODULE_AUTHOR("Shaohui Xie <Shaohui.Xie@freescale.com>");
22f61687c0SShaohui Xie MODULE_LICENSE("GPL v2");
23f61687c0SShaohui Xie 
24f61687c0SShaohui Xie #define PHY_ID_TN2020	0x00a19410
25f61687c0SShaohui Xie #define MDIO_PHYXS_LNSTAT_SYNC0	0x0001
26f61687c0SShaohui Xie #define MDIO_PHYXS_LNSTAT_SYNC1	0x0002
27f61687c0SShaohui Xie #define MDIO_PHYXS_LNSTAT_SYNC2	0x0004
28f61687c0SShaohui Xie #define MDIO_PHYXS_LNSTAT_SYNC3	0x0008
29f61687c0SShaohui Xie #define MDIO_PHYXS_LNSTAT_ALIGN 0x1000
30f61687c0SShaohui Xie 
31f61687c0SShaohui Xie #define MDIO_PHYXS_LANE_READY	(MDIO_PHYXS_LNSTAT_SYNC0 | \
32f61687c0SShaohui Xie 				MDIO_PHYXS_LNSTAT_SYNC1 | \
33f61687c0SShaohui Xie 				MDIO_PHYXS_LNSTAT_SYNC2 | \
34f61687c0SShaohui Xie 				MDIO_PHYXS_LNSTAT_SYNC3 | \
35f61687c0SShaohui Xie 				MDIO_PHYXS_LNSTAT_ALIGN)
36f61687c0SShaohui Xie 
37f61687c0SShaohui Xie static int teranetics_config_init(struct phy_device *phydev)
38f61687c0SShaohui Xie {
39f61687c0SShaohui Xie 	phydev->supported = SUPPORTED_10000baseT_Full;
40f61687c0SShaohui Xie 	phydev->advertising = SUPPORTED_10000baseT_Full;
41f61687c0SShaohui Xie 
42f61687c0SShaohui Xie 	return 0;
43f61687c0SShaohui Xie }
44f61687c0SShaohui Xie 
45f61687c0SShaohui Xie static int teranetics_soft_reset(struct phy_device *phydev)
46f61687c0SShaohui Xie {
47f61687c0SShaohui Xie 	return 0;
48f61687c0SShaohui Xie }
49f61687c0SShaohui Xie 
50f61687c0SShaohui Xie static int teranetics_aneg_done(struct phy_device *phydev)
51f61687c0SShaohui Xie {
52f61687c0SShaohui Xie 	int reg;
53f61687c0SShaohui Xie 
54*94724d10SShaohui Xie 	/* auto negotiation state can only be checked when using copper
55*94724d10SShaohui Xie 	 * port, if using fiber port, just lie it's done.
56*94724d10SShaohui Xie 	 */
57*94724d10SShaohui Xie 	if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) {
58f61687c0SShaohui Xie 		reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
59f61687c0SShaohui Xie 		return (reg < 0) ? reg : (reg & BMSR_ANEGCOMPLETE);
60f61687c0SShaohui Xie 	}
61f61687c0SShaohui Xie 
62*94724d10SShaohui Xie 	return 1;
63*94724d10SShaohui Xie }
64*94724d10SShaohui Xie 
65f61687c0SShaohui Xie static int teranetics_config_aneg(struct phy_device *phydev)
66f61687c0SShaohui Xie {
67f61687c0SShaohui Xie 	return 0;
68f61687c0SShaohui Xie }
69f61687c0SShaohui Xie 
70f61687c0SShaohui Xie static int teranetics_read_status(struct phy_device *phydev)
71f61687c0SShaohui Xie {
72f61687c0SShaohui Xie 	int reg;
73f61687c0SShaohui Xie 
74f61687c0SShaohui Xie 	phydev->link = 1;
75f61687c0SShaohui Xie 
76f61687c0SShaohui Xie 	phydev->speed = SPEED_10000;
77f61687c0SShaohui Xie 	phydev->duplex = DUPLEX_FULL;
78f61687c0SShaohui Xie 
79f61687c0SShaohui Xie 	if (!phy_read_mmd(phydev, MDIO_MMD_VEND1, 93)) {
80f61687c0SShaohui Xie 		reg = phy_read_mmd(phydev, MDIO_MMD_PHYXS, MDIO_PHYXS_LNSTAT);
81f61687c0SShaohui Xie 		if (reg < 0 ||
82f61687c0SShaohui Xie 		    !((reg & MDIO_PHYXS_LANE_READY) == MDIO_PHYXS_LANE_READY)) {
83f61687c0SShaohui Xie 			phydev->link = 0;
84f61687c0SShaohui Xie 			return 0;
85f61687c0SShaohui Xie 		}
86f61687c0SShaohui Xie 
87f61687c0SShaohui Xie 		reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_STAT1);
88f61687c0SShaohui Xie 		if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
89f61687c0SShaohui Xie 			phydev->link = 0;
90f61687c0SShaohui Xie 	}
91f61687c0SShaohui Xie 
92f61687c0SShaohui Xie 	return 0;
93f61687c0SShaohui Xie }
94f61687c0SShaohui Xie 
95f61687c0SShaohui Xie static int teranetics_match_phy_device(struct phy_device *phydev)
96f61687c0SShaohui Xie {
97f61687c0SShaohui Xie 	return phydev->c45_ids.device_ids[3] == PHY_ID_TN2020;
98f61687c0SShaohui Xie }
99f61687c0SShaohui Xie 
100f61687c0SShaohui Xie static struct phy_driver teranetics_driver[] = {
101f61687c0SShaohui Xie {
102f61687c0SShaohui Xie 	.phy_id		= PHY_ID_TN2020,
103f61687c0SShaohui Xie 	.phy_id_mask	= 0xffffffff,
104f61687c0SShaohui Xie 	.name		= "Teranetics TN2020",
105f61687c0SShaohui Xie 	.soft_reset	= teranetics_soft_reset,
106f61687c0SShaohui Xie 	.aneg_done	= teranetics_aneg_done,
107f61687c0SShaohui Xie 	.config_init    = teranetics_config_init,
108f61687c0SShaohui Xie 	.config_aneg    = teranetics_config_aneg,
109f61687c0SShaohui Xie 	.read_status	= teranetics_read_status,
110f61687c0SShaohui Xie 	.match_phy_device = teranetics_match_phy_device,
111f61687c0SShaohui Xie 	.driver		= { .owner = THIS_MODULE,},
112f61687c0SShaohui Xie },
113f61687c0SShaohui Xie };
114f61687c0SShaohui Xie 
115f61687c0SShaohui Xie static int __init teranetics_init(void)
116f61687c0SShaohui Xie {
117f61687c0SShaohui Xie 	return phy_drivers_register(teranetics_driver,
118f61687c0SShaohui Xie 				    ARRAY_SIZE(teranetics_driver));
119f61687c0SShaohui Xie }
120f61687c0SShaohui Xie 
121f61687c0SShaohui Xie static void __exit teranetics_exit(void)
122f61687c0SShaohui Xie {
123f61687c0SShaohui Xie 	return phy_drivers_unregister(teranetics_driver,
124f61687c0SShaohui Xie 				      ARRAY_SIZE(teranetics_driver));
125f61687c0SShaohui Xie }
126f61687c0SShaohui Xie 
127f61687c0SShaohui Xie module_init(teranetics_init);
128f61687c0SShaohui Xie module_exit(teranetics_exit);
129f61687c0SShaohui Xie 
130f61687c0SShaohui Xie static struct mdio_device_id __maybe_unused teranetics_tbl[] = {
131f61687c0SShaohui Xie 	{ PHY_ID_TN2020, 0xffffffff },
132f61687c0SShaohui Xie 	{ }
133f61687c0SShaohui Xie };
134f61687c0SShaohui Xie 
135f61687c0SShaohui Xie MODULE_DEVICE_TABLE(mdio, teranetics_tbl);
136