Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

Feb 18-20, 2025
Register
Loading...
Note: File does not exist in v5.14.15.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Marvell 88Q2XXX automotive 100BASE-T1/1000BASE-T1 PHY driver
  4 */
  5#include <linux/ethtool_netlink.h>
  6#include <linux/marvell_phy.h>
  7#include <linux/phy.h>
  8
  9#define MDIO_MMD_AN_MV_STAT			32769
 10#define MDIO_MMD_AN_MV_STAT_ANEG		0x0100
 11#define MDIO_MMD_AN_MV_STAT_LOCAL_RX		0x1000
 12#define MDIO_MMD_AN_MV_STAT_REMOTE_RX		0x2000
 13#define MDIO_MMD_AN_MV_STAT_LOCAL_MASTER	0x4000
 14#define MDIO_MMD_AN_MV_STAT_MS_CONF_FAULT	0x8000
 15
 16#define MDIO_MMD_PCS_MV_100BT1_STAT1			33032
 17#define MDIO_MMD_PCS_MV_100BT1_STAT1_IDLE_ERROR	0x00FF
 18#define MDIO_MMD_PCS_MV_100BT1_STAT1_JABBER		0x0100
 19#define MDIO_MMD_PCS_MV_100BT1_STAT1_LINK		0x0200
 20#define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX		0x1000
 21#define MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX		0x2000
 22#define MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_MASTER	0x4000
 23
 24#define MDIO_MMD_PCS_MV_100BT1_STAT2		33033
 25#define MDIO_MMD_PCS_MV_100BT1_STAT2_JABBER	0x0001
 26#define MDIO_MMD_PCS_MV_100BT1_STAT2_POL	0x0002
 27#define MDIO_MMD_PCS_MV_100BT1_STAT2_LINK	0x0004
 28#define MDIO_MMD_PCS_MV_100BT1_STAT2_ANGE	0x0008
 29
 30static int mv88q2xxx_soft_reset(struct phy_device *phydev)
 31{
 32	int ret;
 33	int val;
 34
 35	ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
 36			    MDIO_PCS_1000BT1_CTRL, MDIO_PCS_1000BT1_CTRL_RESET);
 37	if (ret < 0)
 38		return ret;
 39
 40	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_PCS,
 41					 MDIO_PCS_1000BT1_CTRL, val,
 42					 !(val & MDIO_PCS_1000BT1_CTRL_RESET),
 43					 50000, 600000, true);
 44}
 45
 46static int mv88q2xxx_read_link_gbit(struct phy_device *phydev)
 47{
 48	int ret;
 49	bool link = false;
 50
 51	/* Read vendor specific Auto-Negotiation status register to get local
 52	 * and remote receiver status according to software initialization
 53	 * guide.
 54	 */
 55	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_MMD_AN_MV_STAT);
 56	if (ret < 0) {
 57		return ret;
 58	} else if ((ret & MDIO_MMD_AN_MV_STAT_LOCAL_RX) &&
 59		   (ret & MDIO_MMD_AN_MV_STAT_REMOTE_RX)) {
 60		/* The link state is latched low so that momentary link
 61		 * drops can be detected. Do not double-read the status
 62		 * in polling mode to detect such short link drops except
 63		 * the link was already down.
 64		 */
 65		if (!phy_polling_mode(phydev) || !phydev->link) {
 66			ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT);
 67			if (ret < 0)
 68				return ret;
 69			else if (ret & MDIO_PCS_1000BT1_STAT_LINK)
 70				link = true;
 71		}
 72
 73		if (!link) {
 74			ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_1000BT1_STAT);
 75			if (ret < 0)
 76				return ret;
 77			else if (ret & MDIO_PCS_1000BT1_STAT_LINK)
 78				link = true;
 79		}
 80	}
 81
 82	phydev->link = link;
 83
 84	return 0;
 85}
 86
 87static int mv88q2xxx_read_link_100m(struct phy_device *phydev)
 88{
 89	int ret;
 90
 91	/* The link state is latched low so that momentary link
 92	 * drops can be detected. Do not double-read the status
 93	 * in polling mode to detect such short link drops except
 94	 * the link was already down. In case we are not polling,
 95	 * we always read the realtime status.
 96	 */
 97	if (!phy_polling_mode(phydev) || !phydev->link) {
 98		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1);
 99		if (ret < 0)
100			return ret;
101		else if (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK)
102			goto out;
103	}
104
105	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_MMD_PCS_MV_100BT1_STAT1);
106	if (ret < 0)
107		return ret;
108
109out:
110	/* Check if we have link and if the remote and local receiver are ok */
111	if ((ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LINK) &&
112	    (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_LOCAL_RX) &&
113	    (ret & MDIO_MMD_PCS_MV_100BT1_STAT1_REMOTE_RX))
114		phydev->link = true;
115	else
116		phydev->link = false;
117
118	return 0;
119}
120
121static int mv88q2xxx_read_link(struct phy_device *phydev)
122{
123	int ret;
124
125	/* The 88Q2XXX PHYs do not have the PMA/PMD status register available,
126	 * therefore we need to read the link status from the vendor specific
127	 * registers depending on the speed.
128	 */
129	if (phydev->speed == SPEED_1000)
130		ret = mv88q2xxx_read_link_gbit(phydev);
131	else
132		ret = mv88q2xxx_read_link_100m(phydev);
133
134	return ret;
135}
136
137static int mv88q2xxx_read_status(struct phy_device *phydev)
138{
139	int ret;
140
141	ret = mv88q2xxx_read_link(phydev);
142	if (ret < 0)
143		return ret;
144
145	return genphy_c45_read_pma(phydev);
146}
147
148static int mv88q2xxx_get_features(struct phy_device *phydev)
149{
150	int ret;
151
152	ret = genphy_c45_pma_read_abilities(phydev);
153	if (ret)
154		return ret;
155
156	/* We need to read the baset1 extended abilities manually because the
157	 * PHY does not signalize it has the extended abilities register
158	 * available.
159	 */
160	ret = genphy_c45_pma_baset1_read_abilities(phydev);
161	if (ret)
162		return ret;
163
164	/* The PHY signalizes it supports autonegotiation. Unfortunately, so
165	 * far it was not possible to get a link even when following the init
166	 * sequence provided by Marvell. Disable it for now until a proper
167	 * workaround is found or a new PHY revision is released.
168	 */
169	linkmode_clear_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
170
171	return 0;
172}
173
174static int mv88q2xxx_config_aneg(struct phy_device *phydev)
175{
176	int ret;
177
178	ret = genphy_c45_config_aneg(phydev);
179	if (ret)
180		return ret;
181
182	return mv88q2xxx_soft_reset(phydev);
183}
184
185static int mv88q2xxx_config_init(struct phy_device *phydev)
186{
187	int ret;
188
189	/* The 88Q2XXX PHYs do have the extended ability register available, but
190	 * register MDIO_PMA_EXTABLE where they should signalize it does not
191	 * work according to specification. Therefore, we force it here.
192	 */
193	phydev->pma_extable = MDIO_PMA_EXTABLE_BT1;
194
195	/* Read the current PHY configuration */
196	ret = genphy_c45_read_pma(phydev);
197	if (ret)
198		return ret;
199
200	return mv88q2xxx_config_aneg(phydev);
201}
202
203static int mv88q2xxxx_get_sqi(struct phy_device *phydev)
204{
205	int ret;
206
207	if (phydev->speed == SPEED_100) {
208		/* Read the SQI from the vendor specific receiver status
209		 * register
210		 */
211		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0x8230);
212		if (ret < 0)
213			return ret;
214
215		ret = ret >> 12;
216	} else {
217		/* Read from vendor specific registers, they are not documented
218		 * but can be found in the Software Initialization Guide. Only
219		 * revisions >= A0 are supported.
220		 */
221		ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, 0xFC5D, 0x00FF, 0x00AC);
222		if (ret < 0)
223			return ret;
224
225		ret = phy_read_mmd(phydev, MDIO_MMD_PCS, 0xfc88);
226		if (ret < 0)
227			return ret;
228	}
229
230	return ret & 0x0F;
231}
232
233static int mv88q2xxxx_get_sqi_max(struct phy_device *phydev)
234{
235	return 15;
236}
237
238static struct phy_driver mv88q2xxx_driver[] = {
239	{
240		.phy_id			= MARVELL_PHY_ID_88Q2110,
241		.phy_id_mask		= MARVELL_PHY_ID_MASK,
242		.name			= "mv88q2110",
243		.get_features		= mv88q2xxx_get_features,
244		.config_aneg		= mv88q2xxx_config_aneg,
245		.config_init		= mv88q2xxx_config_init,
246		.read_status		= mv88q2xxx_read_status,
247		.soft_reset		= mv88q2xxx_soft_reset,
248		.set_loopback		= genphy_c45_loopback,
249		.get_sqi		= mv88q2xxxx_get_sqi,
250		.get_sqi_max		= mv88q2xxxx_get_sqi_max,
251	},
252};
253
254module_phy_driver(mv88q2xxx_driver);
255
256static struct mdio_device_id __maybe_unused mv88q2xxx_tbl[] = {
257	{ MARVELL_PHY_ID_88Q2110, MARVELL_PHY_ID_MASK },
258	{ /*sentinel*/ }
259};
260MODULE_DEVICE_TABLE(mdio, mv88q2xxx_tbl);
261
262MODULE_DESCRIPTION("Marvell 88Q2XXX 100/1000BASE-T1 Automotive Ethernet PHY driver");
263MODULE_LICENSE("GPL");