]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/net/phy/smsc.c
Update from 2013.01 to 2013.07
[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 get from linux kenrel
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 static int smsc_parse_status(struct phy_device *phydev)
21 {
22         int bmcr;
23         int aneg_exp;
24         int mii_adv;
25         int lpa;
26
27         aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
28         if (aneg_exp < 0)
29                 return aneg_exp;
30
31         if (aneg_exp & EXPANSION_MFAULTS) {
32                 /* second read to clear latched status */
33                 aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
34                 if (aneg_exp & EXPANSION_MFAULTS)
35                         return -EIO;
36         }
37
38         bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
39         if (bmcr < 0)
40                 return bmcr;
41         if (bmcr & BMCR_ANENABLE) {
42                 lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
43                 if (lpa < 0)
44                         return lpa;
45                 mii_adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
46                 if (mii_adv < 0)
47                         return mii_adv;
48                 lpa &= mii_adv;
49
50                 if (!(aneg_exp & EXPANSION_NWAY)) {
51                         /* parallel detection */
52                         phydev->duplex = DUPLEX_HALF;
53                         if (lpa & (LPA_100HALF | LPA_100FULL))
54                                 phydev->speed = SPEED_100;
55                         else
56                                 phydev->speed = SPEED_10;
57                 }
58
59                 if (lpa & (LPA_100FULL | LPA_100HALF)) {
60                         phydev->speed = SPEED_100;
61                         if (lpa & LPA_100FULL)
62                                 phydev->duplex = DUPLEX_FULL;
63                         else
64                                 phydev->duplex = DUPLEX_HALF;
65                 } else if (lpa & (LPA_10FULL | LPA_10HALF)) {
66                         phydev->speed = SPEED_10;
67                         if (lpa & LPA_10FULL)
68                                 phydev->duplex = DUPLEX_FULL;
69                         else
70                                 phydev->duplex = DUPLEX_HALF;
71                 } else {
72                         return -EINVAL;
73                 }
74         } else {
75                 if (bmcr & BMCR_SPEED100)
76                         phydev->speed = SPEED_100;
77                 else
78                         phydev->speed = SPEED_10;
79
80                 if (bmcr & BMCR_FULLDPLX)
81                         phydev->duplex = DUPLEX_FULL;
82                 else
83                         phydev->duplex = DUPLEX_HALF;
84         }
85         return 0;
86 }
87
88 static int smsc_startup(struct phy_device *phydev)
89 {
90         int ret;
91
92         if (!phydev->link) {
93                 /* Disable EDPD to wake up PHY */
94                 ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS);
95
96                 if (ret < 0)
97                         return ret;
98
99                 if (ret & MII_LAN83C185_EDPWRDOWN) {
100                         ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS,
101                                 ret & ~MII_LAN83C185_EDPWRDOWN);
102                         if (ret < 0)
103                                 return ret;
104
105                         /* Sleep 64 ms to allow ~5 link test pulses to be sent */
106                         udelay(64 * 1000);
107                 }
108         }
109
110         ret = genphy_update_link(phydev);
111         if (ret < 0)
112                 return ret;
113         return smsc_parse_status(phydev);
114 }
115
116 static int smsc_mdix_setup(struct phy_device *phydev)
117 {
118         int ret;
119         static const char *mdix = "_mdi";
120         char varname[strlen(phydev->bus->name) + strlen(mdix) + 1];
121         const char *val;
122
123         snprintf(varname, sizeof(varname), "%s%s", phydev->bus->name, mdix);
124
125         val = getenv(varname);
126         if (val) {
127                 if (strcasecmp(val, "auto") == 0) {
128                         ret = 0;
129                 } else if (strcasecmp(val, "mdix") == 0) {
130                         ret = 1;
131                 } else if (strcasecmp(val, "mdi") == 0) {
132                         ret = 2;
133                 } else {
134                         printf("Improper setting '%s' for %s\n", val, varname);
135                         printf("Expected one of: 'auto', 'mdi', 'mdix'\n");
136                         return -EINVAL;
137                 }
138         } else {
139                 ret = 0;
140         }
141         return ret;
142 }
143
144 static int smsc_config(struct phy_device *phydev)
145 {
146         int mdix = smsc_mdix_setup(phydev);
147
148         if (mdix < 0) {
149                 return mdix;
150         } else if (mdix) {
151                 int ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1b,
152                                 (1 << 15) | ((mdix & 1) << 13));
153                 if (ret) {
154                         printf("Failed to setup MDI/MDIX mode: %d\n", ret);
155                         return ret;
156                 }
157         }
158         return genphy_config_aneg(phydev);
159 }
160
161 static struct phy_driver lan8700_driver = {
162         .name = "SMSC LAN8700",
163         .uid = 0x0007c0c0,
164         .mask = 0xffff0,
165         .features = PHY_BASIC_FEATURES,
166         .config = &genphy_config_aneg,
167         .startup = &smsc_startup,
168         .shutdown = &genphy_shutdown,
169 };
170
171 static struct phy_driver lan911x_driver = {
172         .name = "SMSC LAN911x Internal PHY",
173         .uid = 0x0007c0d0,
174         .mask = 0xffff0,
175         .features = PHY_BASIC_FEATURES,
176         .config = &genphy_config_aneg,
177         .startup = &smsc_startup,
178         .shutdown = &genphy_shutdown,
179 };
180
181 static struct phy_driver lan8710_driver = {
182         .name = "SMSC LAN8710/LAN8720",
183         .uid = 0x0007c0f0,
184         .mask = 0xffff0,
185         .features = PHY_GBIT_FEATURES,
186         .config = &smsc_config,
187         .startup = &smsc_startup,
188         .shutdown = &genphy_shutdown,
189 };
190
191 int phy_smsc_init(void)
192 {
193         phy_register(&lan8710_driver);
194         phy_register(&lan911x_driver);
195         phy_register(&lan8700_driver);
196
197         return 0;
198 }