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