]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/net/phy/smsc.c
ed6da0256ac3074a6cfccee85d5e43552f35b046
[karo-tx-uboot.git] / drivers / net / phy / smsc.c
1 /*
2  * SMSC PHY drivers
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of
7  * the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
17  * MA 02111-1307 USA
18  *
19  * Base code from drivers/net/phy/davicom.c
20  *   Copyright 2010-2011 Freescale Semiconductor, Inc.
21  *   author Andy Fleming
22  *
23  * Some code get from linux kenrel
24  * Copyright (c) 2006 Herbert Valerio Riedel <hvr@gnu.org>
25  *
26  */
27 #include <miiphy.h>
28 #include <errno.h>
29
30 #define MII_LAN83C185_CTRL_STATUS       17 /* Mode/Status Register */
31 #define MII_LAN83C185_EDPWRDOWN         (1 << 13) /* EDPWRDOWN */
32 #define MII_LAN83C185_ENERGYON          (1 << 1)  /* ENERGYON */
33
34 static int smsc_parse_status(struct phy_device *phydev)
35 {
36         int bmcr;
37         int aneg_exp;
38         int mii_adv;
39         int lpa;
40
41         aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
42         if (aneg_exp < 0)
43                 return aneg_exp;
44
45         if (aneg_exp & EXPANSION_MFAULTS) {
46                 /* second read to clear latched status */
47                 aneg_exp = phy_read(phydev, MDIO_DEVAD_NONE, MII_EXPANSION);
48                 if (aneg_exp & EXPANSION_MFAULTS)
49                         return -EIO;
50         }
51
52         bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
53         if (bmcr < 0)
54                 return bmcr;
55         if (bmcr & BMCR_ANENABLE) {
56                 lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
57                 if (lpa < 0)
58                         return lpa;
59                 mii_adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
60                 if (mii_adv < 0)
61                         return mii_adv;
62                 lpa &= mii_adv;
63
64                 if (!(aneg_exp & EXPANSION_NWAY)) {
65                         /* parallel detection */
66                         phydev->duplex = DUPLEX_HALF;
67                         if (lpa & (LPA_100HALF | LPA_100FULL))
68                                 phydev->speed = SPEED_100;
69                         else
70                                 phydev->speed = SPEED_10;
71                 }
72
73                 if (lpa & (LPA_100FULL | LPA_100HALF)) {
74                         phydev->speed = SPEED_100;
75                         if (lpa & LPA_100FULL)
76                                 phydev->duplex = DUPLEX_FULL;
77                         else
78                                 phydev->duplex = DUPLEX_HALF;
79                 } else if (lpa & (LPA_10FULL | LPA_10HALF)) {
80                         phydev->speed = SPEED_10;
81                         if (lpa & LPA_10FULL)
82                                 phydev->duplex = DUPLEX_FULL;
83                         else
84                                 phydev->duplex = DUPLEX_HALF;
85                 } else {
86                         return -EINVAL;
87                 }
88         } else {
89                 if (bmcr & BMCR_SPEED100)
90                         phydev->speed = SPEED_100;
91                 else
92                         phydev->speed = SPEED_10;
93
94                 if (bmcr & BMCR_FULLDPLX)
95                         phydev->duplex = DUPLEX_FULL;
96                 else
97                         phydev->duplex = DUPLEX_HALF;
98         }
99         return 0;
100 }
101
102 static int smsc_startup(struct phy_device *phydev)
103 {
104         int ret;
105
106         if (!phydev->link) {
107                 /* Disable EDPD to wake up PHY */
108                 ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS);
109
110                 if (ret < 0)
111                         return ret;
112
113                 if (ret & MII_LAN83C185_EDPWRDOWN) {
114                         ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_LAN83C185_CTRL_STATUS,
115                                 ret & ~MII_LAN83C185_EDPWRDOWN);
116                         if (ret < 0)
117                                 return ret;
118
119                         /* Sleep 64 ms to allow ~5 link test pulses to be sent */
120                         udelay(64 * 1000);
121                 }
122         }
123
124         ret = genphy_update_link(phydev);
125         if (ret < 0)
126                 return ret;
127         return smsc_parse_status(phydev);
128 }
129
130 static struct phy_driver lan8700_driver = {
131         .name = "SMSC LAN8700",
132         .uid = 0x0007c0c0,
133         .mask = 0xffff0,
134         .features = PHY_BASIC_FEATURES,
135         .config = &genphy_config_aneg,
136         .startup = &smsc_startup,
137         .shutdown = &genphy_shutdown,
138 };
139
140 static struct phy_driver lan911x_driver = {
141         .name = "SMSC LAN911x Internal PHY",
142         .uid = 0x0007c0d0,
143         .mask = 0xffff0,
144         .features = PHY_BASIC_FEATURES,
145         .config = &genphy_config_aneg,
146         .startup = &smsc_startup,
147         .shutdown = &genphy_shutdown,
148 };
149
150 static struct phy_driver lan8710_driver = {
151         .name = "SMSC LAN8710/LAN8720",
152         .uid = 0x0007c0f0,
153         .mask = 0xffff0,
154         .features = PHY_GBIT_FEATURES,
155         .config = &genphy_config_aneg,
156         .startup = &smsc_startup,
157         .shutdown = &genphy_shutdown,
158 };
159
160 int phy_smsc_init(void)
161 {
162         phy_register(&lan8710_driver);
163         phy_register(&lan911x_driver);
164         phy_register(&lan8700_driver);
165
166         return 0;
167 }