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