]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/net/phy/smsc.c
karo: merge with Ka-Ro specific tree for secure boot support
[karo-tx-uboot.git] / drivers / net / phy / smsc.c
1 /*
2  * SMSC PHY drivers
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  *
6  * Base code from drivers/net/phy/davicom.c
7  *   Copyright 2010-2011 Freescale Semiconductor, Inc.
8  *   author Andy Fleming
9  *
10  * Some code copied from linux kernel
11  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
12  */
13 #include <miiphy.h>
14 #include <errno.h>
15
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 */
19
20 /* This code does not check the partner abilities. */
21 static int smsc_parse_status(struct phy_device *phydev)
22 {
23         int bmcr;
24         int aneg_exp;
25         int mii_adv;
26         int lpa;
27
28         aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
29         if (aneg_exp < 0)
30                 return aneg_exp;
31
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)
36                         return -EIO;
37         }
38
39         bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
40         if (bmcr < 0)
41                 return bmcr;
42         if (bmcr & BMCR_ANENABLE) {
43                 lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
44                 if (lpa < 0)
45                         return lpa;
46                 mii_adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
47                 if (mii_adv < 0)
48                         return mii_adv;
49                 lpa &= mii_adv;
50
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;
56                         else
57                                 phydev->speed = SPEED_10;
58                 }
59
60                 if (lpa & (LPA_100FULL | LPA_100HALF)) {
61                         phydev->speed = SPEED_100;
62                         if (lpa & LPA_100FULL)
63                                 phydev->duplex = DUPLEX_FULL;
64                         else
65                                 phydev->duplex = DUPLEX_HALF;
66                 } else if (lpa & (LPA_10FULL | LPA_10HALF)) {
67                         phydev->speed = SPEED_10;
68                         if (lpa & LPA_10FULL)
69                                 phydev->duplex = DUPLEX_FULL;
70                         else
71                                 phydev->duplex = DUPLEX_HALF;
72                 } else {
73                         return -EINVAL;
74                 }
75         } else {
76                 if (bmcr & BMCR_SPEED100)
77                         phydev->speed = SPEED_100;
78                 else
79                         phydev->speed = SPEED_10;
80
81                 if (bmcr & BMCR_FULLDPLX)
82                         phydev->duplex = DUPLEX_FULL;
83                 else
84                         phydev->duplex = DUPLEX_HALF;
85         }
86         return 0;
87 }
88
89 static int smsc_startup(struct phy_device *phydev)
90 {
91         int ret;
92
93         if (!phydev->link) {
94                 /* Disable EDPD to wake up PHY */
95                 ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS);
96
97                 if (ret < 0)
98                         return ret;
99
100                 if (ret & MII_LAN83C185_EDPWRDOWN) {
101                         ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS,
102                                 ret & ~MII_LAN83C185_EDPWRDOWN);
103                         if (ret < 0)
104                                 return ret;
105
106                         /* Sleep 64 ms to allow ~5 link test pulses to be sent */
107                         udelay(64 * 1000);
108                 }
109         }
110
111         ret = genphy_update_link(phydev);
112         if (ret < 0)
113                 return ret;
114         return smsc_parse_status(phydev);
115 }
116
117 static int smsc_mdix_setup(struct phy_device *phydev)
118 {
119         int ret;
120         static const char *mdix = "_mdi";
121         char varname[strlen(phydev->bus->name) + strlen(mdix) + 1];
122         const char *val;
123
124         snprintf(varname, sizeof(varname), "%s%s", phydev->bus->name, mdix);
125
126         val = getenv(varname);
127         if (val) {
128                 if (strcasecmp(val, "auto") == 0) {
129                         ret = 0;
130                 } else if (strcasecmp(val, "mdix") == 0) {
131                         ret = 1;
132                 } else if (strcasecmp(val, "mdi") == 0) {
133                         ret = 2;
134                 } else {
135                         printf("Improper setting '%s' for %s\n", val, varname);
136                         printf("Expected one of: 'auto', 'mdi', 'mdix'\n");
137                         return -EINVAL;
138                 }
139         } else {
140                 ret = 0;
141         }
142         return ret;
143 }
144
145 static int smsc_config(struct phy_device *phydev)
146 {
147         int mdix = smsc_mdix_setup(phydev);
148
149         if (mdix < 0) {
150                 return mdix;
151         } else if (mdix) {
152                 int ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1b,
153                                 (1 << 15) | ((mdix & 1) << 13));
154                 if (ret) {
155                         printf("Failed to setup MDI/MDIX mode: %d\n", ret);
156                         return ret;
157                 }
158         }
159         return genphy_config_aneg(phydev);
160 }
161
162 static struct phy_driver lan8700_driver = {
163         .name = "SMSC LAN8700",
164         .uid = 0x0007c0c0,
165         .mask = 0xffff0,
166         .features = PHY_BASIC_FEATURES,
167         .config = &genphy_config_aneg,
168         .startup = &smsc_startup,
169         .shutdown = &genphy_shutdown,
170 };
171
172 static struct phy_driver lan911x_driver = {
173         .name = "SMSC LAN911x Internal PHY",
174         .uid = 0x0007c0d0,
175         .mask = 0xffff0,
176         .features = PHY_BASIC_FEATURES,
177         .config = &genphy_config_aneg,
178         .startup = &smsc_startup,
179         .shutdown = &genphy_shutdown,
180 };
181
182 static struct phy_driver lan8710_driver = {
183         .name = "SMSC LAN8710/LAN8720",
184         .uid = 0x0007c0f0,
185         .mask = 0xffff0,
186         .features = PHY_BASIC_FEATURES,
187         .config = &smsc_config,
188         .startup = &smsc_startup,
189         .shutdown = &genphy_shutdown,
190 };
191
192 int phy_smsc_init(void)
193 {
194         phy_register(&lan8710_driver);
195         phy_register(&lan911x_driver);
196         phy_register(&lan8700_driver);
197
198         return 0;
199 }