4 * SPDX-License-Identifier: GPL-2.0+
6 * Base code from drivers/net/phy/davicom.c
7 * Copyright 2010-2011 Freescale Semiconductor, Inc.
10 * Some code copied from linux kernel
11 * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
16 #define MII_LAN83C185_CTRL_STATUS 17 /* Mode/Status Register */
17 #define MII_LAN83C185_EDPWRDOWN (1 << 13) /* EDPWRDOWN */
18 #define MII_LAN83C185_ENERGYON (1 << 1) /* ENERGYON */
20 /* This code does not check the partner abilities. */
21 static int smsc_parse_status(struct phy_device *phydev)
28 aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
32 if (aneg_exp & EXPANSION_MFAULTS) {
33 /* second read to clear latched status */
34 aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
35 if (aneg_exp & EXPANSION_MFAULTS)
39 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
42 if (bmcr & BMCR_ANENABLE) {
43 lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
46 mii_adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
51 if (!(aneg_exp & EXPANSION_NWAY)) {
52 /* parallel detection */
53 phydev->duplex = DUPLEX_HALF;
54 if (lpa & (LPA_100HALF | LPA_100FULL))
55 phydev->speed = SPEED_100;
57 phydev->speed = SPEED_10;
60 if (lpa & (LPA_100FULL | LPA_100HALF)) {
61 phydev->speed = SPEED_100;
62 if (lpa & LPA_100FULL)
63 phydev->duplex = DUPLEX_FULL;
65 phydev->duplex = DUPLEX_HALF;
66 } else if (lpa & (LPA_10FULL | LPA_10HALF)) {
67 phydev->speed = SPEED_10;
69 phydev->duplex = DUPLEX_FULL;
71 phydev->duplex = DUPLEX_HALF;
76 if (bmcr & BMCR_SPEED100)
77 phydev->speed = SPEED_100;
79 phydev->speed = SPEED_10;
81 if (bmcr & BMCR_FULLDPLX)
82 phydev->duplex = DUPLEX_FULL;
84 phydev->duplex = DUPLEX_HALF;
89 static int smsc_startup(struct phy_device *phydev)
94 /* Disable EDPD to wake up PHY */
95 ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS);
100 if (ret & MII_LAN83C185_EDPWRDOWN) {
101 ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS,
102 ret & ~MII_LAN83C185_EDPWRDOWN);
106 /* Sleep 64 ms to allow ~5 link test pulses to be sent */
111 ret = genphy_update_link(phydev);
114 return smsc_parse_status(phydev);
117 static int smsc_mdix_setup(struct phy_device *phydev)
120 static const char *mdix = "_mdi";
121 char varname[strlen(phydev->bus->name) + strlen(mdix) + 1];
124 snprintf(varname, sizeof(varname), "%s%s", phydev->bus->name, mdix);
126 val = getenv(varname);
128 if (strcasecmp(val, "auto") == 0) {
130 } else if (strcasecmp(val, "mdix") == 0) {
132 } else if (strcasecmp(val, "mdi") == 0) {
135 printf("Improper setting '%s' for %s\n", val, varname);
136 printf("Expected one of: 'auto', 'mdi', 'mdix'\n");
145 static int smsc_config(struct phy_device *phydev)
147 int mdix = smsc_mdix_setup(phydev);
152 int ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1b,
153 (1 << 15) | ((mdix & 1) << 13));
155 printf("Failed to setup MDI/MDIX mode: %d\n", ret);
159 return genphy_config_aneg(phydev);
162 static struct phy_driver lan8700_driver = {
163 .name = "SMSC LAN8700",
166 .features = PHY_BASIC_FEATURES,
167 .config = &genphy_config_aneg,
168 .startup = &smsc_startup,
169 .shutdown = &genphy_shutdown,
172 static struct phy_driver lan911x_driver = {
173 .name = "SMSC LAN911x Internal PHY",
176 .features = PHY_BASIC_FEATURES,
177 .config = &genphy_config_aneg,
178 .startup = &smsc_startup,
179 .shutdown = &genphy_shutdown,
182 static struct phy_driver lan8710_driver = {
183 .name = "SMSC LAN8710/LAN8720",
186 .features = PHY_BASIC_FEATURES,
187 .config = &smsc_config,
188 .startup = &smsc_startup,
189 .shutdown = &genphy_shutdown,
192 int phy_smsc_init(void)
194 phy_register(&lan8710_driver);
195 phy_register(&lan911x_driver);
196 phy_register(&lan8700_driver);