xref: /openbmc/linux/drivers/net/phy/smsc.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2c9e055acSHerbert Valerio Riedel /*
3c9e055acSHerbert Valerio Riedel  * drivers/net/phy/smsc.c
4c9e055acSHerbert Valerio Riedel  *
5c9e055acSHerbert Valerio Riedel  * Driver for SMSC PHYs
6c9e055acSHerbert Valerio Riedel  *
7c9e055acSHerbert Valerio Riedel  * Author: Herbert Valerio Riedel
8c9e055acSHerbert Valerio Riedel  *
9c9e055acSHerbert Valerio Riedel  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
10c9e055acSHerbert Valerio Riedel  *
1190b24cfbSSteve Glendinning  * Support added for SMSC LAN8187 and LAN8700 by steve.glendinning@shawell.net
124d9b1a02SSteve Glendinning  *
13c9e055acSHerbert Valerio Riedel  */
14c9e055acSHerbert Valerio Riedel 
15bedd8d78SMarco Felsch #include <linux/clk.h>
16c9e055acSHerbert Valerio Riedel #include <linux/kernel.h>
17c9e055acSHerbert Valerio Riedel #include <linux/module.h>
18c9e055acSHerbert Valerio Riedel #include <linux/mii.h>
19c9e055acSHerbert Valerio Riedel #include <linux/ethtool.h>
20c6e970a0SAndrew Lunn #include <linux/of.h>
21c9e055acSHerbert Valerio Riedel #include <linux/phy.h>
22c9e055acSHerbert Valerio Riedel #include <linux/netdevice.h>
23*8b305ee2STristram Ha #include <linux/crc16.h>
24*8b305ee2STristram Ha #include <linux/etherdevice.h>
2543c6759eSJavier Martinez Canillas #include <linux/smscphy.h>
26c9e055acSHerbert Valerio Riedel 
2705b35e7eSAndre Edich /* Vendor-specific PHY Definitions */
2805b35e7eSAndre Edich /* EDPD NLP / crossover time configuration */
2905b35e7eSAndre Edich #define PHY_EDPD_CONFIG			16
3005b35e7eSAndre Edich #define PHY_EDPD_CONFIG_EXT_CROSSOVER_	0x0001
3105b35e7eSAndre Edich 
3205b35e7eSAndre Edich /* Control/Status Indication Register */
3305b35e7eSAndre Edich #define SPECIAL_CTRL_STS		27
3405b35e7eSAndre Edich #define SPECIAL_CTRL_STS_OVRRD_AMDIX_	0x8000
3505b35e7eSAndre Edich #define SPECIAL_CTRL_STS_AMDIX_ENABLE_	0x4000
3605b35e7eSAndre Edich #define SPECIAL_CTRL_STS_AMDIX_STATE_	0x2000
3705b35e7eSAndre Edich 
381ce65869SHeiner Kallweit #define EDPD_MAX_WAIT_DFLT_MS		640
39657de1cfSHeiner Kallweit /* interval between phylib state machine runs in ms */
40657de1cfSHeiner Kallweit #define PHY_STATE_MACH_MS		1000
411ce65869SHeiner Kallweit 
42030a8902SAndrew Lunn struct smsc_hw_stat {
43030a8902SAndrew Lunn 	const char *string;
44030a8902SAndrew Lunn 	u8 reg;
45030a8902SAndrew Lunn 	u8 bits;
46030a8902SAndrew Lunn };
47030a8902SAndrew Lunn 
48030a8902SAndrew Lunn static struct smsc_hw_stat smsc_hw_stats[] = {
49030a8902SAndrew Lunn 	{ "phy_symbol_errors", 26, 16},
50030a8902SAndrew Lunn };
51030a8902SAndrew Lunn 
520a9c453eSTeresa Remmet struct smsc_phy_priv {
53fc281d78SHeiner Kallweit 	unsigned int edpd_enable:1;
54a6205110SHeiner Kallweit 	unsigned int edpd_mode_set_by_user:1;
551ce65869SHeiner Kallweit 	unsigned int edpd_max_wait_ms;
56*8b305ee2STristram Ha 	bool wol_arp;
570a9c453eSTeresa Remmet };
580a9c453eSTeresa Remmet 
smsc_phy_ack_interrupt(struct phy_device * phydev)59824ef51fSIoana Ciornei static int smsc_phy_ack_interrupt(struct phy_device *phydev)
60824ef51fSIoana Ciornei {
61824ef51fSIoana Ciornei 	int rc = phy_read(phydev, MII_LAN83C185_ISF);
62824ef51fSIoana Ciornei 
63824ef51fSIoana Ciornei 	return rc < 0 ? rc : 0;
64824ef51fSIoana Ciornei }
65824ef51fSIoana Ciornei 
smsc_phy_config_intr(struct phy_device * phydev)66a69e332bSHeiner Kallweit int smsc_phy_config_intr(struct phy_device *phydev)
67c9e055acSHerbert Valerio Riedel {
6873654945SMarco Felsch 	int rc;
6973654945SMarco Felsch 
7073654945SMarco Felsch 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
71824ef51fSIoana Ciornei 		rc = smsc_phy_ack_interrupt(phydev);
72824ef51fSIoana Ciornei 		if (rc)
73824ef51fSIoana Ciornei 			return rc;
74824ef51fSIoana Ciornei 
7558aac3a2SHeiner Kallweit 		rc = phy_write(phydev, MII_LAN83C185_IM,
7658aac3a2SHeiner Kallweit 			       MII_LAN83C185_ISF_INT_PHYLIB_EVENTS);
77824ef51fSIoana Ciornei 	} else {
787e8b617eSLukas Wunner 		rc = phy_write(phydev, MII_LAN83C185_IM, 0);
79824ef51fSIoana Ciornei 		if (rc)
80824ef51fSIoana Ciornei 			return rc;
81c9e055acSHerbert Valerio Riedel 
82824ef51fSIoana Ciornei 		rc = smsc_phy_ack_interrupt(phydev);
83c9e055acSHerbert Valerio Riedel 	}
84c9e055acSHerbert Valerio Riedel 
85c9e055acSHerbert Valerio Riedel 	return rc < 0 ? rc : 0;
86c9e055acSHerbert Valerio Riedel }
87a69e332bSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_config_intr);
88c9e055acSHerbert Valerio Riedel 
smsc_phy_config_edpd(struct phy_device * phydev)8989946e31SHeiner Kallweit static int smsc_phy_config_edpd(struct phy_device *phydev)
9089946e31SHeiner Kallweit {
9189946e31SHeiner Kallweit 	struct smsc_phy_priv *priv = phydev->priv;
9289946e31SHeiner Kallweit 
9389946e31SHeiner Kallweit 	if (priv->edpd_enable)
9489946e31SHeiner Kallweit 		return phy_set_bits(phydev, MII_LAN83C185_CTRL_STATUS,
9589946e31SHeiner Kallweit 				    MII_LAN83C185_EDPWRDOWN);
9689946e31SHeiner Kallweit 	else
9789946e31SHeiner Kallweit 		return phy_clear_bits(phydev, MII_LAN83C185_CTRL_STATUS,
9889946e31SHeiner Kallweit 				      MII_LAN83C185_EDPWRDOWN);
9989946e31SHeiner Kallweit }
10089946e31SHeiner Kallweit 
smsc_phy_handle_interrupt(struct phy_device * phydev)101a69e332bSHeiner Kallweit irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
10236b25c26SIoana Ciornei {
1037e8b617eSLukas Wunner 	int irq_status;
10436b25c26SIoana Ciornei 
10536b25c26SIoana Ciornei 	irq_status = phy_read(phydev, MII_LAN83C185_ISF);
10636b25c26SIoana Ciornei 	if (irq_status < 0) {
1071e7b81edSLukas Wunner 		if (irq_status != -ENODEV)
10836b25c26SIoana Ciornei 			phy_error(phydev);
1091e7b81edSLukas Wunner 
11036b25c26SIoana Ciornei 		return IRQ_NONE;
11136b25c26SIoana Ciornei 	}
11236b25c26SIoana Ciornei 
11358aac3a2SHeiner Kallweit 	if (!(irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS))
11436b25c26SIoana Ciornei 		return IRQ_NONE;
11536b25c26SIoana Ciornei 
11636b25c26SIoana Ciornei 	phy_trigger_machine(phydev);
11736b25c26SIoana Ciornei 
11836b25c26SIoana Ciornei 	return IRQ_HANDLED;
11936b25c26SIoana Ciornei }
120a69e332bSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_handle_interrupt);
12136b25c26SIoana Ciornei 
smsc_phy_config_init(struct phy_device * phydev)122a69e332bSHeiner Kallweit int smsc_phy_config_init(struct phy_device *phydev)
123c9e055acSHerbert Valerio Riedel {
1240a9c453eSTeresa Remmet 	struct smsc_phy_priv *priv = phydev->priv;
1250a9c453eSTeresa Remmet 
126d56417adSHeiner Kallweit 	if (!priv)
127436e3800SMarco Felsch 		return 0;
128436e3800SMarco Felsch 
129a6205110SHeiner Kallweit 	/* don't use EDPD in irq mode except overridden by user */
130a6205110SHeiner Kallweit 	if (!priv->edpd_mode_set_by_user && phydev->irq != PHY_POLL)
131d56417adSHeiner Kallweit 		priv->edpd_enable = false;
132d56417adSHeiner Kallweit 
13389946e31SHeiner Kallweit 	return smsc_phy_config_edpd(phydev);
13421009686SGwenhael Goavec-Merou }
135a69e332bSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_config_init);
13621009686SGwenhael Goavec-Merou 
smsc_phy_reset(struct phy_device * phydev)13721009686SGwenhael Goavec-Merou static int smsc_phy_reset(struct phy_device *phydev)
13821009686SGwenhael Goavec-Merou {
1396ded7cd6Strem 	int rc = phy_read(phydev, MII_LAN83C185_SPECIAL_MODES);
1406ded7cd6Strem 	if (rc < 0)
1416ded7cd6Strem 		return rc;
1426ded7cd6Strem 
1436ded7cd6Strem 	/* If the SMSC PHY is in power down mode, then set it
1446ded7cd6Strem 	 * in all capable mode before using it.
1456ded7cd6Strem 	 */
1466ded7cd6Strem 	if ((rc & MII_LAN83C185_MODE_MASK) == MII_LAN83C185_MODE_POWERDOWN) {
147fc0f7e33SManfred Schlaegl 		/* set "all capable" mode */
1486ded7cd6Strem 		rc |= MII_LAN83C185_MODE_ALL;
1496ded7cd6Strem 		phy_write(phydev, MII_LAN83C185_SPECIAL_MODES, rc);
1506ded7cd6Strem 	}
151fc0f7e33SManfred Schlaegl 
152fc0f7e33SManfred Schlaegl 	/* reset the phy */
153fc0f7e33SManfred Schlaegl 	return genphy_soft_reset(phydev);
154c9e055acSHerbert Valerio Riedel }
155c9e055acSHerbert Valerio Riedel 
lan87xx_config_aneg(struct phy_device * phydev)15605b35e7eSAndre Edich static int lan87xx_config_aneg(struct phy_device *phydev)
15705b35e7eSAndre Edich {
15805b35e7eSAndre Edich 	int rc;
15905b35e7eSAndre Edich 	int val;
16005b35e7eSAndre Edich 
16105b35e7eSAndre Edich 	switch (phydev->mdix_ctrl) {
16205b35e7eSAndre Edich 	case ETH_TP_MDI:
16305b35e7eSAndre Edich 		val = SPECIAL_CTRL_STS_OVRRD_AMDIX_;
16405b35e7eSAndre Edich 		break;
16505b35e7eSAndre Edich 	case ETH_TP_MDI_X:
16605b35e7eSAndre Edich 		val = SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
16705b35e7eSAndre Edich 			SPECIAL_CTRL_STS_AMDIX_STATE_;
16805b35e7eSAndre Edich 		break;
16905b35e7eSAndre Edich 	case ETH_TP_MDI_AUTO:
17005b35e7eSAndre Edich 		val = SPECIAL_CTRL_STS_AMDIX_ENABLE_;
17105b35e7eSAndre Edich 		break;
17205b35e7eSAndre Edich 	default:
17305b35e7eSAndre Edich 		return genphy_config_aneg(phydev);
17405b35e7eSAndre Edich 	}
17505b35e7eSAndre Edich 
17605b35e7eSAndre Edich 	rc = phy_read(phydev, SPECIAL_CTRL_STS);
17705b35e7eSAndre Edich 	if (rc < 0)
17805b35e7eSAndre Edich 		return rc;
17905b35e7eSAndre Edich 
18005b35e7eSAndre Edich 	rc &= ~(SPECIAL_CTRL_STS_OVRRD_AMDIX_ |
18105b35e7eSAndre Edich 		SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
18205b35e7eSAndre Edich 		SPECIAL_CTRL_STS_AMDIX_STATE_);
18305b35e7eSAndre Edich 	rc |= val;
18405b35e7eSAndre Edich 	phy_write(phydev, SPECIAL_CTRL_STS, rc);
18505b35e7eSAndre Edich 
18605b35e7eSAndre Edich 	phydev->mdix = phydev->mdix_ctrl;
18705b35e7eSAndre Edich 	return genphy_config_aneg(phydev);
18805b35e7eSAndre Edich }
18905b35e7eSAndre Edich 
lan95xx_config_aneg_ext(struct phy_device * phydev)190fdb5cc6aSAndre Edich static int lan95xx_config_aneg_ext(struct phy_device *phydev)
19105b35e7eSAndre Edich {
1924310e2f4SHeiner Kallweit 	if (phydev->phy_id == 0x0007c0f0) { /* LAN9500A or LAN9505A */
19305b35e7eSAndre Edich 		/* Extend Manual AutoMDIX timer */
1944310e2f4SHeiner Kallweit 		int rc = phy_set_bits(phydev, PHY_EDPD_CONFIG,
1954310e2f4SHeiner Kallweit 				      PHY_EDPD_CONFIG_EXT_CROSSOVER_);
1964310e2f4SHeiner Kallweit 
19705b35e7eSAndre Edich 		if (rc < 0)
19805b35e7eSAndre Edich 			return rc;
1994310e2f4SHeiner Kallweit 	}
20005b35e7eSAndre Edich 
20105b35e7eSAndre Edich 	return lan87xx_config_aneg(phydev);
20205b35e7eSAndre Edich }
20305b35e7eSAndre Edich 
204b629820dSMarek Vasut /*
205776829deSIgor Plyatov  * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable
206776829deSIgor Plyatov  * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to
207776829deSIgor Plyatov  * unstable detection of plugging in Ethernet cable.
208776829deSIgor Plyatov  * This workaround disables Energy Detect Power-Down mode and waiting for
209776829deSIgor Plyatov  * response on link pulses to detect presence of plugged Ethernet cable.
210776829deSIgor Plyatov  * The Energy Detect Power-Down mode is enabled again in the end of procedure to
211776829deSIgor Plyatov  * save approximately 220 mW of power if cable is unplugged.
2122642cc6cSLukas Wunner  * The workaround is only applicable to poll mode. Energy Detect Power-Down may
2132642cc6cSLukas Wunner  * not be used in interrupt mode lest link change detection becomes unreliable.
214b629820dSMarek Vasut  */
lan87xx_read_status(struct phy_device * phydev)215a69e332bSHeiner Kallweit int lan87xx_read_status(struct phy_device *phydev)
2164223dbffSPatrick Trantham {
2170a9c453eSTeresa Remmet 	struct smsc_phy_priv *priv = phydev->priv;
218c22c3bbfSHeiner Kallweit 	int err;
2190a9c453eSTeresa Remmet 
220c22c3bbfSHeiner Kallweit 	err = genphy_read_status(phydev);
221c22c3bbfSHeiner Kallweit 	if (err)
222c22c3bbfSHeiner Kallweit 		return err;
2230a9c453eSTeresa Remmet 
2241ce65869SHeiner Kallweit 	if (!phydev->link && priv && priv->edpd_enable &&
2251ce65869SHeiner Kallweit 	    priv->edpd_max_wait_ms) {
2261ce65869SHeiner Kallweit 		unsigned int max_wait = priv->edpd_max_wait_ms * 1000;
2271ce65869SHeiner Kallweit 		int rc;
2281ce65869SHeiner Kallweit 
2294223dbffSPatrick Trantham 		/* Disable EDPD to wake up PHY */
2301ce65869SHeiner Kallweit 		rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
231b629820dSMarek Vasut 		if (rc < 0)
232b629820dSMarek Vasut 			return rc;
233b629820dSMarek Vasut 
2344223dbffSPatrick Trantham 		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
2354223dbffSPatrick Trantham 			       rc & ~MII_LAN83C185_EDPWRDOWN);
236b629820dSMarek Vasut 		if (rc < 0)
237b629820dSMarek Vasut 			return rc;
238b629820dSMarek Vasut 
2396d61f483SDejin Zheng 		/* Wait max 640 ms to detect energy and the timeout is not
2406d61f483SDejin Zheng 		 * an actual error.
2416d61f483SDejin Zheng 		 */
2426d61f483SDejin Zheng 		read_poll_timeout(phy_read, rc,
2436d61f483SDejin Zheng 				  rc & MII_LAN83C185_ENERGYON || rc < 0,
2441ce65869SHeiner Kallweit 				  10000, max_wait, true, phydev,
2456d61f483SDejin Zheng 				  MII_LAN83C185_CTRL_STATUS);
246776829deSIgor Plyatov 		if (rc < 0)
247776829deSIgor Plyatov 			return rc;
2484223dbffSPatrick Trantham 
2494223dbffSPatrick Trantham 		/* Re-enable EDPD */
2504223dbffSPatrick Trantham 		rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
2514223dbffSPatrick Trantham 		if (rc < 0)
2524223dbffSPatrick Trantham 			return rc;
2534223dbffSPatrick Trantham 
2544223dbffSPatrick Trantham 		rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS,
2554223dbffSPatrick Trantham 			       rc | MII_LAN83C185_EDPWRDOWN);
2564223dbffSPatrick Trantham 		if (rc < 0)
2574223dbffSPatrick Trantham 			return rc;
258b629820dSMarek Vasut 	}
259b629820dSMarek Vasut 
2604223dbffSPatrick Trantham 	return err;
261698244acSGiuseppe Cavallaro }
262a69e332bSHeiner Kallweit EXPORT_SYMBOL_GPL(lan87xx_read_status);
263c9e055acSHerbert Valerio Riedel 
lan874x_phy_config_init(struct phy_device * phydev)264*8b305ee2STristram Ha static int lan874x_phy_config_init(struct phy_device *phydev)
265*8b305ee2STristram Ha {
266*8b305ee2STristram Ha 	u16 val;
267*8b305ee2STristram Ha 	int rc;
268*8b305ee2STristram Ha 
269*8b305ee2STristram Ha 	/* Setup LED2/nINT/nPME pin to function as nPME.  May need user option
270*8b305ee2STristram Ha 	 * to use LED1/nINT/nPME.
271*8b305ee2STristram Ha 	 */
272*8b305ee2STristram Ha 	val = MII_LAN874X_PHY_PME2_SET;
273*8b305ee2STristram Ha 
274*8b305ee2STristram Ha 	/* The bits MII_LAN874X_PHY_WOL_PFDA_FR, MII_LAN874X_PHY_WOL_WUFR,
275*8b305ee2STristram Ha 	 * MII_LAN874X_PHY_WOL_MPR, and MII_LAN874X_PHY_WOL_BCAST_FR need to
276*8b305ee2STristram Ha 	 * be cleared to de-assert PME signal after a WoL event happens, but
277*8b305ee2STristram Ha 	 * using PME auto clear gets around that.
278*8b305ee2STristram Ha 	 */
279*8b305ee2STristram Ha 	val |= MII_LAN874X_PHY_PME_SELF_CLEAR;
280*8b305ee2STristram Ha 	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
281*8b305ee2STristram Ha 			   val);
282*8b305ee2STristram Ha 	if (rc < 0)
283*8b305ee2STristram Ha 		return rc;
284*8b305ee2STristram Ha 
285*8b305ee2STristram Ha 	/* set nPME self clear delay time */
286*8b305ee2STristram Ha 	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_MCFGR,
287*8b305ee2STristram Ha 			   MII_LAN874X_PHY_PME_SELF_CLEAR_DELAY);
288*8b305ee2STristram Ha 	if (rc < 0)
289*8b305ee2STristram Ha 		return rc;
290*8b305ee2STristram Ha 
291*8b305ee2STristram Ha 	return smsc_phy_config_init(phydev);
292*8b305ee2STristram Ha }
293*8b305ee2STristram Ha 
lan874x_get_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)294*8b305ee2STristram Ha static void lan874x_get_wol(struct phy_device *phydev,
295*8b305ee2STristram Ha 			    struct ethtool_wolinfo *wol)
296*8b305ee2STristram Ha {
297*8b305ee2STristram Ha 	struct smsc_phy_priv *priv = phydev->priv;
298*8b305ee2STristram Ha 	int rc;
299*8b305ee2STristram Ha 
300*8b305ee2STristram Ha 	wol->supported = (WAKE_UCAST | WAKE_BCAST | WAKE_MAGIC |
301*8b305ee2STristram Ha 			  WAKE_ARP | WAKE_MCAST);
302*8b305ee2STristram Ha 	wol->wolopts = 0;
303*8b305ee2STristram Ha 
304*8b305ee2STristram Ha 	rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR);
305*8b305ee2STristram Ha 	if (rc < 0)
306*8b305ee2STristram Ha 		return;
307*8b305ee2STristram Ha 
308*8b305ee2STristram Ha 	if (rc & MII_LAN874X_PHY_WOL_PFDAEN)
309*8b305ee2STristram Ha 		wol->wolopts |= WAKE_UCAST;
310*8b305ee2STristram Ha 
311*8b305ee2STristram Ha 	if (rc & MII_LAN874X_PHY_WOL_BCSTEN)
312*8b305ee2STristram Ha 		wol->wolopts |= WAKE_BCAST;
313*8b305ee2STristram Ha 
314*8b305ee2STristram Ha 	if (rc & MII_LAN874X_PHY_WOL_MPEN)
315*8b305ee2STristram Ha 		wol->wolopts |= WAKE_MAGIC;
316*8b305ee2STristram Ha 
317*8b305ee2STristram Ha 	if (rc & MII_LAN874X_PHY_WOL_WUEN) {
318*8b305ee2STristram Ha 		if (priv->wol_arp)
319*8b305ee2STristram Ha 			wol->wolopts |= WAKE_ARP;
320*8b305ee2STristram Ha 		else
321*8b305ee2STristram Ha 			wol->wolopts |= WAKE_MCAST;
322*8b305ee2STristram Ha 	}
323*8b305ee2STristram Ha }
324*8b305ee2STristram Ha 
smsc_crc16(const u8 * buffer,size_t len)325*8b305ee2STristram Ha static u16 smsc_crc16(const u8 *buffer, size_t len)
326*8b305ee2STristram Ha {
327*8b305ee2STristram Ha 	return bitrev16(crc16(0xFFFF, buffer, len));
328*8b305ee2STristram Ha }
329*8b305ee2STristram Ha 
lan874x_chk_wol_pattern(const u8 pattern[],const u16 * mask,u8 len,u8 * data,u8 * datalen)330*8b305ee2STristram Ha static int lan874x_chk_wol_pattern(const u8 pattern[], const u16 *mask,
331*8b305ee2STristram Ha 				   u8 len, u8 *data, u8 *datalen)
332*8b305ee2STristram Ha {
333*8b305ee2STristram Ha 	size_t i, j, k;
334*8b305ee2STristram Ha 	int ret = 0;
335*8b305ee2STristram Ha 	u16 bits;
336*8b305ee2STristram Ha 
337*8b305ee2STristram Ha 	/* Pattern filtering can match up to 128 bytes of frame data.  There
338*8b305ee2STristram Ha 	 * are 8 registers to program the 16-bit masks, where each bit means
339*8b305ee2STristram Ha 	 * the byte will be compared.  The frame data will then go through a
340*8b305ee2STristram Ha 	 * CRC16 calculation for hardware comparison.  This helper function
341*8b305ee2STristram Ha 	 * makes sure only relevant frame data are included in this
342*8b305ee2STristram Ha 	 * calculation.  It provides a warning when the masks and expected
343*8b305ee2STristram Ha 	 * data size do not match.
344*8b305ee2STristram Ha 	 */
345*8b305ee2STristram Ha 	i = 0;
346*8b305ee2STristram Ha 	k = 0;
347*8b305ee2STristram Ha 	while (len > 0) {
348*8b305ee2STristram Ha 		bits = *mask;
349*8b305ee2STristram Ha 		for (j = 0; j < 16; j++, i++, len--) {
350*8b305ee2STristram Ha 			/* No more pattern. */
351*8b305ee2STristram Ha 			if (!len) {
352*8b305ee2STristram Ha 				/* The rest of bitmap is not empty. */
353*8b305ee2STristram Ha 				if (bits)
354*8b305ee2STristram Ha 					ret = i + 1;
355*8b305ee2STristram Ha 				break;
356*8b305ee2STristram Ha 			}
357*8b305ee2STristram Ha 			if (bits & 1)
358*8b305ee2STristram Ha 				data[k++] = pattern[i];
359*8b305ee2STristram Ha 			bits >>= 1;
360*8b305ee2STristram Ha 		}
361*8b305ee2STristram Ha 		mask++;
362*8b305ee2STristram Ha 	}
363*8b305ee2STristram Ha 	*datalen = k;
364*8b305ee2STristram Ha 	return ret;
365*8b305ee2STristram Ha }
366*8b305ee2STristram Ha 
lan874x_set_wol_pattern(struct phy_device * phydev,u16 val,const u8 data[],u8 datalen,const u16 * mask,u8 masklen)367*8b305ee2STristram Ha static int lan874x_set_wol_pattern(struct phy_device *phydev, u16 val,
368*8b305ee2STristram Ha 				   const u8 data[], u8 datalen,
369*8b305ee2STristram Ha 				   const u16 *mask, u8 masklen)
370*8b305ee2STristram Ha {
371*8b305ee2STristram Ha 	u16 crc, reg;
372*8b305ee2STristram Ha 	int rc;
373*8b305ee2STristram Ha 
374*8b305ee2STristram Ha 	/* Starting pattern offset is set before calling this function. */
375*8b305ee2STristram Ha 	val |= MII_LAN874X_PHY_WOL_FILTER_EN;
376*8b305ee2STristram Ha 	rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
377*8b305ee2STristram Ha 			   MII_LAN874X_PHY_MMD_WOL_WUF_CFGA, val);
378*8b305ee2STristram Ha 	if (rc < 0)
379*8b305ee2STristram Ha 		return rc;
380*8b305ee2STristram Ha 
381*8b305ee2STristram Ha 	crc = smsc_crc16(data, datalen);
382*8b305ee2STristram Ha 	rc = phy_write_mmd(phydev, MDIO_MMD_PCS,
383*8b305ee2STristram Ha 			   MII_LAN874X_PHY_MMD_WOL_WUF_CFGB, crc);
384*8b305ee2STristram Ha 	if (rc < 0)
385*8b305ee2STristram Ha 		return rc;
386*8b305ee2STristram Ha 
387*8b305ee2STristram Ha 	masklen = (masklen + 15) & ~0xf;
388*8b305ee2STristram Ha 	reg = MII_LAN874X_PHY_MMD_WOL_WUF_MASK7;
389*8b305ee2STristram Ha 	while (masklen >= 16) {
390*8b305ee2STristram Ha 		rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg, *mask);
391*8b305ee2STristram Ha 		if (rc < 0)
392*8b305ee2STristram Ha 			return rc;
393*8b305ee2STristram Ha 		reg--;
394*8b305ee2STristram Ha 		mask++;
395*8b305ee2STristram Ha 		masklen -= 16;
396*8b305ee2STristram Ha 	}
397*8b305ee2STristram Ha 
398*8b305ee2STristram Ha 	/* Clear out the rest of mask registers. */
399*8b305ee2STristram Ha 	while (reg != MII_LAN874X_PHY_MMD_WOL_WUF_MASK0) {
400*8b305ee2STristram Ha 		phy_write_mmd(phydev, MDIO_MMD_PCS, reg, 0);
401*8b305ee2STristram Ha 		reg--;
402*8b305ee2STristram Ha 	}
403*8b305ee2STristram Ha 	return rc;
404*8b305ee2STristram Ha }
405*8b305ee2STristram Ha 
lan874x_set_wol(struct phy_device * phydev,struct ethtool_wolinfo * wol)406*8b305ee2STristram Ha static int lan874x_set_wol(struct phy_device *phydev,
407*8b305ee2STristram Ha 			   struct ethtool_wolinfo *wol)
408*8b305ee2STristram Ha {
409*8b305ee2STristram Ha 	struct net_device *ndev = phydev->attached_dev;
410*8b305ee2STristram Ha 	struct smsc_phy_priv *priv = phydev->priv;
411*8b305ee2STristram Ha 	u16 val, val_wucsr;
412*8b305ee2STristram Ha 	u8 data[128];
413*8b305ee2STristram Ha 	u8 datalen;
414*8b305ee2STristram Ha 	int rc;
415*8b305ee2STristram Ha 
416*8b305ee2STristram Ha 	/* lan874x has only one WoL filter pattern */
417*8b305ee2STristram Ha 	if ((wol->wolopts & (WAKE_ARP | WAKE_MCAST)) ==
418*8b305ee2STristram Ha 	    (WAKE_ARP | WAKE_MCAST)) {
419*8b305ee2STristram Ha 		phydev_info(phydev,
420*8b305ee2STristram Ha 			    "lan874x WoL supports one of ARP|MCAST at a time\n");
421*8b305ee2STristram Ha 		return -EOPNOTSUPP;
422*8b305ee2STristram Ha 	}
423*8b305ee2STristram Ha 
424*8b305ee2STristram Ha 	rc = phy_read_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR);
425*8b305ee2STristram Ha 	if (rc < 0)
426*8b305ee2STristram Ha 		return rc;
427*8b305ee2STristram Ha 
428*8b305ee2STristram Ha 	val_wucsr = rc;
429*8b305ee2STristram Ha 
430*8b305ee2STristram Ha 	if (wol->wolopts & WAKE_UCAST)
431*8b305ee2STristram Ha 		val_wucsr |= MII_LAN874X_PHY_WOL_PFDAEN;
432*8b305ee2STristram Ha 	else
433*8b305ee2STristram Ha 		val_wucsr &= ~MII_LAN874X_PHY_WOL_PFDAEN;
434*8b305ee2STristram Ha 
435*8b305ee2STristram Ha 	if (wol->wolopts & WAKE_BCAST)
436*8b305ee2STristram Ha 		val_wucsr |= MII_LAN874X_PHY_WOL_BCSTEN;
437*8b305ee2STristram Ha 	else
438*8b305ee2STristram Ha 		val_wucsr &= ~MII_LAN874X_PHY_WOL_BCSTEN;
439*8b305ee2STristram Ha 
440*8b305ee2STristram Ha 	if (wol->wolopts & WAKE_MAGIC)
441*8b305ee2STristram Ha 		val_wucsr |= MII_LAN874X_PHY_WOL_MPEN;
442*8b305ee2STristram Ha 	else
443*8b305ee2STristram Ha 		val_wucsr &= ~MII_LAN874X_PHY_WOL_MPEN;
444*8b305ee2STristram Ha 
445*8b305ee2STristram Ha 	/* Need to use pattern matching */
446*8b305ee2STristram Ha 	if (wol->wolopts & (WAKE_ARP | WAKE_MCAST))
447*8b305ee2STristram Ha 		val_wucsr |= MII_LAN874X_PHY_WOL_WUEN;
448*8b305ee2STristram Ha 	else
449*8b305ee2STristram Ha 		val_wucsr &= ~MII_LAN874X_PHY_WOL_WUEN;
450*8b305ee2STristram Ha 
451*8b305ee2STristram Ha 	if (wol->wolopts & WAKE_ARP) {
452*8b305ee2STristram Ha 		const u8 pattern[2] = { 0x08, 0x06 };
453*8b305ee2STristram Ha 		const u16 mask[1] = { 0x0003 };
454*8b305ee2STristram Ha 
455*8b305ee2STristram Ha 		rc = lan874x_chk_wol_pattern(pattern, mask, 2, data,
456*8b305ee2STristram Ha 					     &datalen);
457*8b305ee2STristram Ha 		if (rc)
458*8b305ee2STristram Ha 			phydev_dbg(phydev, "pattern not valid at %d\n", rc);
459*8b305ee2STristram Ha 
460*8b305ee2STristram Ha 		/* Need to match broadcast destination address and provided
461*8b305ee2STristram Ha 		 * data pattern at offset 12.
462*8b305ee2STristram Ha 		 */
463*8b305ee2STristram Ha 		val = 12 | MII_LAN874X_PHY_WOL_FILTER_BCSTEN;
464*8b305ee2STristram Ha 		rc = lan874x_set_wol_pattern(phydev, val, data, datalen, mask,
465*8b305ee2STristram Ha 					     2);
466*8b305ee2STristram Ha 		if (rc < 0)
467*8b305ee2STristram Ha 			return rc;
468*8b305ee2STristram Ha 		priv->wol_arp = true;
469*8b305ee2STristram Ha 	}
470*8b305ee2STristram Ha 
471*8b305ee2STristram Ha 	if (wol->wolopts & WAKE_MCAST) {
472*8b305ee2STristram Ha 		/* Need to match multicast destination address. */
473*8b305ee2STristram Ha 		val = MII_LAN874X_PHY_WOL_FILTER_MCASTTEN;
474*8b305ee2STristram Ha 		rc = lan874x_set_wol_pattern(phydev, val, data, 0, NULL, 0);
475*8b305ee2STristram Ha 		if (rc < 0)
476*8b305ee2STristram Ha 			return rc;
477*8b305ee2STristram Ha 		priv->wol_arp = false;
478*8b305ee2STristram Ha 	}
479*8b305ee2STristram Ha 
480*8b305ee2STristram Ha 	if (wol->wolopts & (WAKE_MAGIC | WAKE_UCAST)) {
481*8b305ee2STristram Ha 		const u8 *mac = (const u8 *)ndev->dev_addr;
482*8b305ee2STristram Ha 		int i, reg;
483*8b305ee2STristram Ha 
484*8b305ee2STristram Ha 		reg = MII_LAN874X_PHY_MMD_WOL_RX_ADDRC;
485*8b305ee2STristram Ha 		for (i = 0; i < 6; i += 2, reg--) {
486*8b305ee2STristram Ha 			rc = phy_write_mmd(phydev, MDIO_MMD_PCS, reg,
487*8b305ee2STristram Ha 					   ((mac[i + 1] << 8) | mac[i]));
488*8b305ee2STristram Ha 			if (rc < 0)
489*8b305ee2STristram Ha 				return rc;
490*8b305ee2STristram Ha 		}
491*8b305ee2STristram Ha 	}
492*8b305ee2STristram Ha 
493*8b305ee2STristram Ha 	rc = phy_write_mmd(phydev, MDIO_MMD_PCS, MII_LAN874X_PHY_MMD_WOL_WUCSR,
494*8b305ee2STristram Ha 			   val_wucsr);
495*8b305ee2STristram Ha 	if (rc < 0)
496*8b305ee2STristram Ha 		return rc;
497*8b305ee2STristram Ha 
498*8b305ee2STristram Ha 	return 0;
499*8b305ee2STristram Ha }
500*8b305ee2STristram Ha 
smsc_get_sset_count(struct phy_device * phydev)501030a8902SAndrew Lunn static int smsc_get_sset_count(struct phy_device *phydev)
502030a8902SAndrew Lunn {
503030a8902SAndrew Lunn 	return ARRAY_SIZE(smsc_hw_stats);
504030a8902SAndrew Lunn }
505030a8902SAndrew Lunn 
smsc_get_strings(struct phy_device * phydev,u8 * data)506030a8902SAndrew Lunn static void smsc_get_strings(struct phy_device *phydev, u8 *data)
507030a8902SAndrew Lunn {
508030a8902SAndrew Lunn 	int i;
509030a8902SAndrew Lunn 
510030a8902SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++) {
5112da55390SArnd Bergmann 		strncpy(data + i * ETH_GSTRING_LEN,
512030a8902SAndrew Lunn 		       smsc_hw_stats[i].string, ETH_GSTRING_LEN);
513030a8902SAndrew Lunn 	}
514030a8902SAndrew Lunn }
515030a8902SAndrew Lunn 
smsc_get_stat(struct phy_device * phydev,int i)516030a8902SAndrew Lunn static u64 smsc_get_stat(struct phy_device *phydev, int i)
517030a8902SAndrew Lunn {
518030a8902SAndrew Lunn 	struct smsc_hw_stat stat = smsc_hw_stats[i];
519030a8902SAndrew Lunn 	int val;
520030a8902SAndrew Lunn 	u64 ret;
521030a8902SAndrew Lunn 
522030a8902SAndrew Lunn 	val = phy_read(phydev, stat.reg);
523030a8902SAndrew Lunn 	if (val < 0)
5246c3442f5SJisheng Zhang 		ret = U64_MAX;
525030a8902SAndrew Lunn 	else
526030a8902SAndrew Lunn 		ret = val;
527030a8902SAndrew Lunn 
528030a8902SAndrew Lunn 	return ret;
529030a8902SAndrew Lunn }
530030a8902SAndrew Lunn 
smsc_get_stats(struct phy_device * phydev,struct ethtool_stats * stats,u64 * data)531030a8902SAndrew Lunn static void smsc_get_stats(struct phy_device *phydev,
532030a8902SAndrew Lunn 			   struct ethtool_stats *stats, u64 *data)
533030a8902SAndrew Lunn {
534030a8902SAndrew Lunn 	int i;
535030a8902SAndrew Lunn 
536030a8902SAndrew Lunn 	for (i = 0; i < ARRAY_SIZE(smsc_hw_stats); i++)
537030a8902SAndrew Lunn 		data[i] = smsc_get_stat(phydev, i);
538030a8902SAndrew Lunn }
539030a8902SAndrew Lunn 
smsc_phy_get_edpd(struct phy_device * phydev,u16 * edpd)540657de1cfSHeiner Kallweit static int smsc_phy_get_edpd(struct phy_device *phydev, u16 *edpd)
541657de1cfSHeiner Kallweit {
542657de1cfSHeiner Kallweit 	struct smsc_phy_priv *priv = phydev->priv;
543657de1cfSHeiner Kallweit 
544657de1cfSHeiner Kallweit 	if (!priv)
545657de1cfSHeiner Kallweit 		return -EOPNOTSUPP;
546657de1cfSHeiner Kallweit 
547657de1cfSHeiner Kallweit 	if (!priv->edpd_enable)
548657de1cfSHeiner Kallweit 		*edpd = ETHTOOL_PHY_EDPD_DISABLE;
549657de1cfSHeiner Kallweit 	else if (!priv->edpd_max_wait_ms)
550657de1cfSHeiner Kallweit 		*edpd = ETHTOOL_PHY_EDPD_NO_TX;
551657de1cfSHeiner Kallweit 	else
552657de1cfSHeiner Kallweit 		*edpd = PHY_STATE_MACH_MS + priv->edpd_max_wait_ms;
553657de1cfSHeiner Kallweit 
554657de1cfSHeiner Kallweit 	return 0;
555657de1cfSHeiner Kallweit }
556657de1cfSHeiner Kallweit 
smsc_phy_set_edpd(struct phy_device * phydev,u16 edpd)557657de1cfSHeiner Kallweit static int smsc_phy_set_edpd(struct phy_device *phydev, u16 edpd)
558657de1cfSHeiner Kallweit {
559657de1cfSHeiner Kallweit 	struct smsc_phy_priv *priv = phydev->priv;
560657de1cfSHeiner Kallweit 
561657de1cfSHeiner Kallweit 	if (!priv)
562657de1cfSHeiner Kallweit 		return -EOPNOTSUPP;
563657de1cfSHeiner Kallweit 
564657de1cfSHeiner Kallweit 	switch (edpd) {
565657de1cfSHeiner Kallweit 	case ETHTOOL_PHY_EDPD_DISABLE:
566657de1cfSHeiner Kallweit 		priv->edpd_enable = false;
567657de1cfSHeiner Kallweit 		break;
568657de1cfSHeiner Kallweit 	case ETHTOOL_PHY_EDPD_NO_TX:
569657de1cfSHeiner Kallweit 		priv->edpd_enable = true;
570657de1cfSHeiner Kallweit 		priv->edpd_max_wait_ms = 0;
571657de1cfSHeiner Kallweit 		break;
572657de1cfSHeiner Kallweit 	case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
573657de1cfSHeiner Kallweit 		edpd = PHY_STATE_MACH_MS + EDPD_MAX_WAIT_DFLT_MS;
574657de1cfSHeiner Kallweit 		fallthrough;
575657de1cfSHeiner Kallweit 	default:
576657de1cfSHeiner Kallweit 		if (phydev->irq != PHY_POLL)
577657de1cfSHeiner Kallweit 			return -EOPNOTSUPP;
578657de1cfSHeiner Kallweit 		if (edpd < PHY_STATE_MACH_MS || edpd > PHY_STATE_MACH_MS + 1000)
579657de1cfSHeiner Kallweit 			return -EINVAL;
580657de1cfSHeiner Kallweit 		priv->edpd_enable = true;
581657de1cfSHeiner Kallweit 		priv->edpd_max_wait_ms = edpd - PHY_STATE_MACH_MS;
582657de1cfSHeiner Kallweit 	}
583657de1cfSHeiner Kallweit 
584657de1cfSHeiner Kallweit 	priv->edpd_mode_set_by_user = true;
585657de1cfSHeiner Kallweit 
586657de1cfSHeiner Kallweit 	return smsc_phy_config_edpd(phydev);
587657de1cfSHeiner Kallweit }
588657de1cfSHeiner Kallweit 
smsc_phy_get_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,void * data)589657de1cfSHeiner Kallweit int smsc_phy_get_tunable(struct phy_device *phydev,
590657de1cfSHeiner Kallweit 			 struct ethtool_tunable *tuna, void *data)
591657de1cfSHeiner Kallweit {
592657de1cfSHeiner Kallweit 	switch (tuna->id) {
593657de1cfSHeiner Kallweit 	case ETHTOOL_PHY_EDPD:
594657de1cfSHeiner Kallweit 		return smsc_phy_get_edpd(phydev, data);
595657de1cfSHeiner Kallweit 	default:
596657de1cfSHeiner Kallweit 		return -EOPNOTSUPP;
597657de1cfSHeiner Kallweit 	}
598657de1cfSHeiner Kallweit }
599657de1cfSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_get_tunable);
600657de1cfSHeiner Kallweit 
smsc_phy_set_tunable(struct phy_device * phydev,struct ethtool_tunable * tuna,const void * data)601657de1cfSHeiner Kallweit int smsc_phy_set_tunable(struct phy_device *phydev,
602657de1cfSHeiner Kallweit 			 struct ethtool_tunable *tuna, const void *data)
603657de1cfSHeiner Kallweit {
604657de1cfSHeiner Kallweit 	switch (tuna->id) {
605657de1cfSHeiner Kallweit 	case ETHTOOL_PHY_EDPD:
606657de1cfSHeiner Kallweit 		return smsc_phy_set_edpd(phydev, *(u16 *)data);
607657de1cfSHeiner Kallweit 	default:
608657de1cfSHeiner Kallweit 		return -EOPNOTSUPP;
609657de1cfSHeiner Kallweit 	}
610657de1cfSHeiner Kallweit }
611657de1cfSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_set_tunable);
612657de1cfSHeiner Kallweit 
smsc_phy_probe(struct phy_device * phydev)613a69e332bSHeiner Kallweit int smsc_phy_probe(struct phy_device *phydev)
6140a9c453eSTeresa Remmet {
6150a9c453eSTeresa Remmet 	struct device *dev = &phydev->mdio.dev;
6160a9c453eSTeresa Remmet 	struct smsc_phy_priv *priv;
6178af1a9afSHeiner Kallweit 	struct clk *refclk;
6180a9c453eSTeresa Remmet 
6190a9c453eSTeresa Remmet 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
6200a9c453eSTeresa Remmet 	if (!priv)
6210a9c453eSTeresa Remmet 		return -ENOMEM;
6220a9c453eSTeresa Remmet 
623fc281d78SHeiner Kallweit 	priv->edpd_enable = true;
6241ce65869SHeiner Kallweit 	priv->edpd_max_wait_ms = EDPD_MAX_WAIT_DFLT_MS;
6250a9c453eSTeresa Remmet 
62690c7dd32SHeiner Kallweit 	if (device_property_present(dev, "smsc,disable-energy-detect"))
627fc281d78SHeiner Kallweit 		priv->edpd_enable = false;
6280a9c453eSTeresa Remmet 
6290a9c453eSTeresa Remmet 	phydev->priv = priv;
6300a9c453eSTeresa Remmet 
631bedd8d78SMarco Felsch 	/* Make clk optional to keep DTB backward compatibility. */
6328af1a9afSHeiner Kallweit 	refclk = devm_clk_get_optional_enabled(dev, NULL);
6338af1a9afSHeiner Kallweit 	if (IS_ERR(refclk))
6348af1a9afSHeiner Kallweit 		return dev_err_probe(dev, PTR_ERR(refclk),
635a18caa97SMarco Felsch 				     "Failed to request clock\n");
636bedd8d78SMarco Felsch 
6378af1a9afSHeiner Kallweit 	return clk_set_rate(refclk, 50 * 1000 * 1000);
6380a9c453eSTeresa Remmet }
639a69e332bSHeiner Kallweit EXPORT_SYMBOL_GPL(smsc_phy_probe);
6400a9c453eSTeresa Remmet 
641d5bf9071SChristian Hohnstaedt static struct phy_driver smsc_phy_driver[] = {
642d5bf9071SChristian Hohnstaedt {
643c9e055acSHerbert Valerio Riedel 	.phy_id		= 0x0007c0a0, /* OUI=0x00800f, Model#=0x0a */
644c9e055acSHerbert Valerio Riedel 	.phy_id_mask	= 0xfffffff0,
645c9e055acSHerbert Valerio Riedel 	.name		= "SMSC LAN83C185",
646c9e055acSHerbert Valerio Riedel 
647dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
648c9e055acSHerbert Valerio Riedel 
6490a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
6500a9c453eSTeresa Remmet 
651c9e055acSHerbert Valerio Riedel 	/* basic functions */
65248c41b99SSteve Glendinning 	.config_init	= smsc_phy_config_init,
65321009686SGwenhael Goavec-Merou 	.soft_reset	= smsc_phy_reset,
654c9e055acSHerbert Valerio Riedel 
655c9e055acSHerbert Valerio Riedel 	/* IRQ related */
65648c41b99SSteve Glendinning 	.config_intr	= smsc_phy_config_intr,
65736b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
658c9e055acSHerbert Valerio Riedel 
659c64d2a9aSSteve Glendinning 	.suspend	= genphy_suspend,
660c64d2a9aSSteve Glendinning 	.resume		= genphy_resume,
661d5bf9071SChristian Hohnstaedt }, {
6624d9b1a02SSteve Glendinning 	.phy_id		= 0x0007c0b0, /* OUI=0x00800f, Model#=0x0b */
6634d9b1a02SSteve Glendinning 	.phy_id_mask	= 0xfffffff0,
6644d9b1a02SSteve Glendinning 	.name		= "SMSC LAN8187",
6654d9b1a02SSteve Glendinning 
666dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
6674d9b1a02SSteve Glendinning 
6680a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
6690a9c453eSTeresa Remmet 
6704d9b1a02SSteve Glendinning 	/* basic functions */
6714d9b1a02SSteve Glendinning 	.config_init	= smsc_phy_config_init,
67221009686SGwenhael Goavec-Merou 	.soft_reset	= smsc_phy_reset,
6734d9b1a02SSteve Glendinning 
6744d9b1a02SSteve Glendinning 	/* IRQ related */
6754d9b1a02SSteve Glendinning 	.config_intr	= smsc_phy_config_intr,
67636b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
6774d9b1a02SSteve Glendinning 
678030a8902SAndrew Lunn 	/* Statistics */
679030a8902SAndrew Lunn 	.get_sset_count = smsc_get_sset_count,
680030a8902SAndrew Lunn 	.get_strings	= smsc_get_strings,
681030a8902SAndrew Lunn 	.get_stats	= smsc_get_stats,
682030a8902SAndrew Lunn 
683c64d2a9aSSteve Glendinning 	.suspend	= genphy_suspend,
684c64d2a9aSSteve Glendinning 	.resume		= genphy_resume,
685d5bf9071SChristian Hohnstaedt }, {
68605b35e7eSAndre Edich 	/* This covers internal PHY (phy_id: 0x0007C0C3) for
68705b35e7eSAndre Edich 	 * LAN9500 (PID: 0x9500), LAN9514 (PID: 0xec00), LAN9505 (PID: 0x9505)
68805b35e7eSAndre Edich 	 */
6894d9b1a02SSteve Glendinning 	.phy_id		= 0x0007c0c0, /* OUI=0x00800f, Model#=0x0c */
6904d9b1a02SSteve Glendinning 	.phy_id_mask	= 0xfffffff0,
6914d9b1a02SSteve Glendinning 	.name		= "SMSC LAN8700",
6924d9b1a02SSteve Glendinning 
693dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
6944d9b1a02SSteve Glendinning 
6950a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
6960a9c453eSTeresa Remmet 
6974d9b1a02SSteve Glendinning 	/* basic functions */
698776829deSIgor Plyatov 	.read_status	= lan87xx_read_status,
6994d9b1a02SSteve Glendinning 	.config_init	= smsc_phy_config_init,
70021009686SGwenhael Goavec-Merou 	.soft_reset	= smsc_phy_reset,
70105b35e7eSAndre Edich 	.config_aneg	= lan87xx_config_aneg,
7024d9b1a02SSteve Glendinning 
7034d9b1a02SSteve Glendinning 	/* IRQ related */
7044d9b1a02SSteve Glendinning 	.config_intr	= smsc_phy_config_intr,
70536b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
7064d9b1a02SSteve Glendinning 
707030a8902SAndrew Lunn 	/* Statistics */
708030a8902SAndrew Lunn 	.get_sset_count = smsc_get_sset_count,
709030a8902SAndrew Lunn 	.get_strings	= smsc_get_strings,
710030a8902SAndrew Lunn 	.get_stats	= smsc_get_stats,
711030a8902SAndrew Lunn 
7123c4c3b3eSHeiner Kallweit 	.get_tunable	= smsc_phy_get_tunable,
7133c4c3b3eSHeiner Kallweit 	.set_tunable	= smsc_phy_set_tunable,
7143c4c3b3eSHeiner Kallweit 
715c64d2a9aSSteve Glendinning 	.suspend	= genphy_suspend,
716c64d2a9aSSteve Glendinning 	.resume		= genphy_resume,
717d5bf9071SChristian Hohnstaedt }, {
718fd9abb3dSSteve Glendinning 	.phy_id		= 0x0007c0d0, /* OUI=0x00800f, Model#=0x0d */
719fd9abb3dSSteve Glendinning 	.phy_id_mask	= 0xfffffff0,
720fd9abb3dSSteve Glendinning 	.name		= "SMSC LAN911x Internal PHY",
721fd9abb3dSSteve Glendinning 
722dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
723fd9abb3dSSteve Glendinning 
7240a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
7250a9c453eSTeresa Remmet 
726fd9abb3dSSteve Glendinning 	/* IRQ related */
727fd9abb3dSSteve Glendinning 	.config_intr	= smsc_phy_config_intr,
72836b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
729fd9abb3dSSteve Glendinning 
730c64d2a9aSSteve Glendinning 	.suspend	= genphy_suspend,
731c64d2a9aSSteve Glendinning 	.resume		= genphy_resume,
732d5bf9071SChristian Hohnstaedt }, {
73305b35e7eSAndre Edich 	/* This covers internal PHY (phy_id: 0x0007C0F0) for
73405b35e7eSAndre Edich 	 * LAN9500A (PID: 0x9E00), LAN9505A (PID: 0x9E01)
73505b35e7eSAndre Edich 	 */
736e072b639SSteve Glendinning 	.phy_id		= 0x0007c0f0, /* OUI=0x00800f, Model#=0x0f */
737e072b639SSteve Glendinning 	.phy_id_mask	= 0xfffffff0,
738e072b639SSteve Glendinning 	.name		= "SMSC LAN8710/LAN8720",
739e072b639SSteve Glendinning 
740dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
741e072b639SSteve Glendinning 
7420a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
7430a9c453eSTeresa Remmet 
744e072b639SSteve Glendinning 	/* basic functions */
7454223dbffSPatrick Trantham 	.read_status	= lan87xx_read_status,
7464257d583SPatrick Trantham 	.config_init	= smsc_phy_config_init,
74721009686SGwenhael Goavec-Merou 	.soft_reset	= smsc_phy_reset,
748fdb5cc6aSAndre Edich 	.config_aneg	= lan95xx_config_aneg_ext,
749e072b639SSteve Glendinning 
750e072b639SSteve Glendinning 	/* IRQ related */
751e072b639SSteve Glendinning 	.config_intr	= smsc_phy_config_intr,
75236b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
753e072b639SSteve Glendinning 
754030a8902SAndrew Lunn 	/* Statistics */
755030a8902SAndrew Lunn 	.get_sset_count = smsc_get_sset_count,
756030a8902SAndrew Lunn 	.get_strings	= smsc_get_strings,
757030a8902SAndrew Lunn 	.get_stats	= smsc_get_stats,
758030a8902SAndrew Lunn 
7593c4c3b3eSHeiner Kallweit 	.get_tunable	= smsc_phy_get_tunable,
7603c4c3b3eSHeiner Kallweit 	.set_tunable	= smsc_phy_set_tunable,
7613c4c3b3eSHeiner Kallweit 
762e072b639SSteve Glendinning 	.suspend	= genphy_suspend,
763e072b639SSteve Glendinning 	.resume		= genphy_resume,
76426706d43SJoshua Henderson }, {
76526706d43SJoshua Henderson 	.phy_id		= 0x0007c110,
76626706d43SJoshua Henderson 	.phy_id_mask	= 0xfffffff0,
76726706d43SJoshua Henderson 	.name		= "SMSC LAN8740",
76826706d43SJoshua Henderson 
769dcdecdcfSHeiner Kallweit 	/* PHY_BASIC_FEATURES */
77076db2d46SMartin Fuzzey 	.flags		= PHY_RST_AFTER_CLK_EN,
77126706d43SJoshua Henderson 
7720a9c453eSTeresa Remmet 	.probe		= smsc_phy_probe,
7730a9c453eSTeresa Remmet 
77426706d43SJoshua Henderson 	/* basic functions */
77526706d43SJoshua Henderson 	.read_status	= lan87xx_read_status,
776*8b305ee2STristram Ha 	.config_init	= lan874x_phy_config_init,
77726706d43SJoshua Henderson 	.soft_reset	= smsc_phy_reset,
77826706d43SJoshua Henderson 
77926706d43SJoshua Henderson 	/* IRQ related */
78026706d43SJoshua Henderson 	.config_intr	= smsc_phy_config_intr,
78136b25c26SIoana Ciornei 	.handle_interrupt = smsc_phy_handle_interrupt,
78226706d43SJoshua Henderson 
783030a8902SAndrew Lunn 	/* Statistics */
784030a8902SAndrew Lunn 	.get_sset_count = smsc_get_sset_count,
785030a8902SAndrew Lunn 	.get_strings	= smsc_get_strings,
786030a8902SAndrew Lunn 	.get_stats	= smsc_get_stats,
787030a8902SAndrew Lunn 
7883c4c3b3eSHeiner Kallweit 	.get_tunable	= smsc_phy_get_tunable,
7893c4c3b3eSHeiner Kallweit 	.set_tunable	= smsc_phy_set_tunable,
7903c4c3b3eSHeiner Kallweit 
791*8b305ee2STristram Ha 	/* WoL */
792*8b305ee2STristram Ha 	.set_wol	= lan874x_set_wol,
793*8b305ee2STristram Ha 	.get_wol	= lan874x_get_wol,
794*8b305ee2STristram Ha 
79526706d43SJoshua Henderson 	.suspend	= genphy_suspend,
79626706d43SJoshua Henderson 	.resume		= genphy_resume,
79753ad2286SYuiko Oshino }, {
79853ad2286SYuiko Oshino 	.phy_id		= 0x0007c130,	/* 0x0007c130 and 0x0007c131 */
799b2be0751SYuiko Oshino 	/* This mask (0xfffffff2) is to differentiate from
800b2be0751SYuiko Oshino 	 * LAN88xx (phy_id 0x0007c132)
801b2be0751SYuiko Oshino 	 * and allows future phy_id revisions.
802b2be0751SYuiko Oshino 	 */
80353ad2286SYuiko Oshino 	.phy_id_mask	= 0xfffffff2,
80453ad2286SYuiko Oshino 	.name		= "Microchip LAN8742",
80553ad2286SYuiko Oshino 
80653ad2286SYuiko Oshino 	/* PHY_BASIC_FEATURES */
80753ad2286SYuiko Oshino 	.flags		= PHY_RST_AFTER_CLK_EN,
80853ad2286SYuiko Oshino 
80953ad2286SYuiko Oshino 	.probe		= smsc_phy_probe,
81053ad2286SYuiko Oshino 
81153ad2286SYuiko Oshino 	/* basic functions */
81253ad2286SYuiko Oshino 	.read_status	= lan87xx_read_status,
813*8b305ee2STristram Ha 	.config_init	= lan874x_phy_config_init,
81453ad2286SYuiko Oshino 	.soft_reset	= smsc_phy_reset,
81553ad2286SYuiko Oshino 
81653ad2286SYuiko Oshino 	/* IRQ related */
81753ad2286SYuiko Oshino 	.config_intr	= smsc_phy_config_intr,
81853ad2286SYuiko Oshino 	.handle_interrupt = smsc_phy_handle_interrupt,
81953ad2286SYuiko Oshino 
82053ad2286SYuiko Oshino 	/* Statistics */
82153ad2286SYuiko Oshino 	.get_sset_count = smsc_get_sset_count,
82253ad2286SYuiko Oshino 	.get_strings	= smsc_get_strings,
82353ad2286SYuiko Oshino 	.get_stats	= smsc_get_stats,
82453ad2286SYuiko Oshino 
8253c4c3b3eSHeiner Kallweit 	.get_tunable	= smsc_phy_get_tunable,
8263c4c3b3eSHeiner Kallweit 	.set_tunable	= smsc_phy_set_tunable,
8273c4c3b3eSHeiner Kallweit 
828*8b305ee2STristram Ha 	/* WoL */
829*8b305ee2STristram Ha 	.set_wol	= lan874x_set_wol,
830*8b305ee2STristram Ha 	.get_wol	= lan874x_get_wol,
831*8b305ee2STristram Ha 
83253ad2286SYuiko Oshino 	.suspend	= genphy_suspend,
83353ad2286SYuiko Oshino 	.resume		= genphy_resume,
834d5bf9071SChristian Hohnstaedt } };
835e072b639SSteve Glendinning 
83650fd7150SJohan Hovold module_phy_driver(smsc_phy_driver);
837c9e055acSHerbert Valerio Riedel 
838c9e055acSHerbert Valerio Riedel MODULE_DESCRIPTION("SMSC PHY driver");
839c9e055acSHerbert Valerio Riedel MODULE_AUTHOR("Herbert Valerio Riedel");
840c9e055acSHerbert Valerio Riedel MODULE_LICENSE("GPL");
841c9e055acSHerbert Valerio Riedel 
842cf93c945SUwe Kleine-König static struct mdio_device_id __maybe_unused smsc_tbl[] = {
8434e4f10f6SDavid Woodhouse 	{ 0x0007c0a0, 0xfffffff0 },
8444e4f10f6SDavid Woodhouse 	{ 0x0007c0b0, 0xfffffff0 },
8454e4f10f6SDavid Woodhouse 	{ 0x0007c0c0, 0xfffffff0 },
8464e4f10f6SDavid Woodhouse 	{ 0x0007c0d0, 0xfffffff0 },
8474e4f10f6SDavid Woodhouse 	{ 0x0007c0f0, 0xfffffff0 },
84826706d43SJoshua Henderson 	{ 0x0007c110, 0xfffffff0 },
84953ad2286SYuiko Oshino 	{ 0x0007c130, 0xfffffff2 },
8504e4f10f6SDavid Woodhouse 	{ }
8514e4f10f6SDavid Woodhouse };
8524e4f10f6SDavid Woodhouse 
8534e4f10f6SDavid Woodhouse MODULE_DEVICE_TABLE(mdio, smsc_tbl);
854