1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun mii.c: MII interface library
4*4882a593Smuzhiyun
5*4882a593Smuzhiyun Maintained by Jeff Garzik <jgarzik@pobox.com>
6*4882a593Smuzhiyun Copyright 2001,2002 Jeff Garzik
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun Various code came from myson803.c and other files by
9*4882a593Smuzhiyun Donald Becker. Copyright:
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun Written 1998-2002 by Donald Becker.
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun This software may be used and distributed according
14*4882a593Smuzhiyun to the terms of the GNU General Public License (GPL),
15*4882a593Smuzhiyun incorporated herein by reference. Drivers based on
16*4882a593Smuzhiyun or derived from this code fall under the GPL and must
17*4882a593Smuzhiyun retain the authorship, copyright and license notice.
18*4882a593Smuzhiyun This file is not a complete program and may only be
19*4882a593Smuzhiyun used when the entire operating system is licensed
20*4882a593Smuzhiyun under the GPL.
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun The author may be reached as becker@scyld.com, or C/O
23*4882a593Smuzhiyun Scyld Computing Corporation
24*4882a593Smuzhiyun 410 Severn Ave., Suite 210
25*4882a593Smuzhiyun Annapolis MD 21403
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun */
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun #include <linux/kernel.h>
31*4882a593Smuzhiyun #include <linux/module.h>
32*4882a593Smuzhiyun #include <linux/netdevice.h>
33*4882a593Smuzhiyun #include <linux/ethtool.h>
34*4882a593Smuzhiyun #include <linux/mii.h>
35*4882a593Smuzhiyun
mii_get_an(struct mii_if_info * mii,u16 addr)36*4882a593Smuzhiyun static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
37*4882a593Smuzhiyun {
38*4882a593Smuzhiyun int advert;
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun return mii_lpa_to_ethtool_lpa_t(advert);
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /**
46*4882a593Smuzhiyun * mii_ethtool_gset - get settings that are specified in @ecmd
47*4882a593Smuzhiyun * @mii: MII interface
48*4882a593Smuzhiyun * @ecmd: requested ethtool_cmd
49*4882a593Smuzhiyun *
50*4882a593Smuzhiyun * The @ecmd parameter is expected to have been cleared before calling
51*4882a593Smuzhiyun * mii_ethtool_gset().
52*4882a593Smuzhiyun *
53*4882a593Smuzhiyun * Returns 0 for success, negative on error.
54*4882a593Smuzhiyun */
mii_ethtool_gset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)55*4882a593Smuzhiyun int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun struct net_device *dev = mii->dev;
58*4882a593Smuzhiyun u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
59*4882a593Smuzhiyun u32 nego;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun ecmd->supported =
62*4882a593Smuzhiyun (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
63*4882a593Smuzhiyun SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
64*4882a593Smuzhiyun SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
65*4882a593Smuzhiyun if (mii->supports_gmii)
66*4882a593Smuzhiyun ecmd->supported |= SUPPORTED_1000baseT_Half |
67*4882a593Smuzhiyun SUPPORTED_1000baseT_Full;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /* only supports twisted-pair */
70*4882a593Smuzhiyun ecmd->port = PORT_MII;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun /* only supports internal transceiver */
73*4882a593Smuzhiyun ecmd->transceiver = XCVR_INTERNAL;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun /* this isn't fully supported at higher layers */
76*4882a593Smuzhiyun ecmd->phy_address = mii->phy_id;
77*4882a593Smuzhiyun ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
80*4882a593Smuzhiyun
81*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
82*4882a593Smuzhiyun bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
83*4882a593Smuzhiyun if (mii->supports_gmii) {
84*4882a593Smuzhiyun ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
85*4882a593Smuzhiyun stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun if (bmcr & BMCR_ANENABLE) {
88*4882a593Smuzhiyun ecmd->advertising |= ADVERTISED_Autoneg;
89*4882a593Smuzhiyun ecmd->autoneg = AUTONEG_ENABLE;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
92*4882a593Smuzhiyun if (mii->supports_gmii)
93*4882a593Smuzhiyun ecmd->advertising |=
94*4882a593Smuzhiyun mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun if (bmsr & BMSR_ANEGCOMPLETE) {
97*4882a593Smuzhiyun ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
98*4882a593Smuzhiyun ecmd->lp_advertising |=
99*4882a593Smuzhiyun mii_stat1000_to_ethtool_lpa_t(stat1000);
100*4882a593Smuzhiyun } else {
101*4882a593Smuzhiyun ecmd->lp_advertising = 0;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun nego = ecmd->advertising & ecmd->lp_advertising;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun if (nego & (ADVERTISED_1000baseT_Full |
107*4882a593Smuzhiyun ADVERTISED_1000baseT_Half)) {
108*4882a593Smuzhiyun ethtool_cmd_speed_set(ecmd, SPEED_1000);
109*4882a593Smuzhiyun ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
110*4882a593Smuzhiyun } else if (nego & (ADVERTISED_100baseT_Full |
111*4882a593Smuzhiyun ADVERTISED_100baseT_Half)) {
112*4882a593Smuzhiyun ethtool_cmd_speed_set(ecmd, SPEED_100);
113*4882a593Smuzhiyun ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
114*4882a593Smuzhiyun } else {
115*4882a593Smuzhiyun ethtool_cmd_speed_set(ecmd, SPEED_10);
116*4882a593Smuzhiyun ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun } else {
119*4882a593Smuzhiyun ecmd->autoneg = AUTONEG_DISABLE;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun ethtool_cmd_speed_set(ecmd,
122*4882a593Smuzhiyun ((bmcr & BMCR_SPEED1000 &&
123*4882a593Smuzhiyun (bmcr & BMCR_SPEED100) == 0) ?
124*4882a593Smuzhiyun SPEED_1000 :
125*4882a593Smuzhiyun ((bmcr & BMCR_SPEED100) ?
126*4882a593Smuzhiyun SPEED_100 : SPEED_10)));
127*4882a593Smuzhiyun ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun mii->full_duplex = ecmd->duplex;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun /* ignore maxtxpkt, maxrxpkt for now */
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun return 0;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun /**
138*4882a593Smuzhiyun * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
139*4882a593Smuzhiyun * @mii: MII interface
140*4882a593Smuzhiyun * @cmd: requested ethtool_link_ksettings
141*4882a593Smuzhiyun *
142*4882a593Smuzhiyun * The @cmd parameter is expected to have been cleared before calling
143*4882a593Smuzhiyun * mii_ethtool_get_link_ksettings().
144*4882a593Smuzhiyun */
mii_ethtool_get_link_ksettings(struct mii_if_info * mii,struct ethtool_link_ksettings * cmd)145*4882a593Smuzhiyun void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
146*4882a593Smuzhiyun struct ethtool_link_ksettings *cmd)
147*4882a593Smuzhiyun {
148*4882a593Smuzhiyun struct net_device *dev = mii->dev;
149*4882a593Smuzhiyun u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
150*4882a593Smuzhiyun u32 nego, supported, advertising, lp_advertising;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
153*4882a593Smuzhiyun SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
154*4882a593Smuzhiyun SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
155*4882a593Smuzhiyun if (mii->supports_gmii)
156*4882a593Smuzhiyun supported |= SUPPORTED_1000baseT_Half |
157*4882a593Smuzhiyun SUPPORTED_1000baseT_Full;
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun /* only supports twisted-pair */
160*4882a593Smuzhiyun cmd->base.port = PORT_MII;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun /* this isn't fully supported at higher layers */
163*4882a593Smuzhiyun cmd->base.phy_address = mii->phy_id;
164*4882a593Smuzhiyun cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun advertising = ADVERTISED_TP | ADVERTISED_MII;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
169*4882a593Smuzhiyun bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
170*4882a593Smuzhiyun if (mii->supports_gmii) {
171*4882a593Smuzhiyun ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
172*4882a593Smuzhiyun stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
173*4882a593Smuzhiyun }
174*4882a593Smuzhiyun if (bmcr & BMCR_ANENABLE) {
175*4882a593Smuzhiyun advertising |= ADVERTISED_Autoneg;
176*4882a593Smuzhiyun cmd->base.autoneg = AUTONEG_ENABLE;
177*4882a593Smuzhiyun
178*4882a593Smuzhiyun advertising |= mii_get_an(mii, MII_ADVERTISE);
179*4882a593Smuzhiyun if (mii->supports_gmii)
180*4882a593Smuzhiyun advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun if (bmsr & BMSR_ANEGCOMPLETE) {
183*4882a593Smuzhiyun lp_advertising = mii_get_an(mii, MII_LPA);
184*4882a593Smuzhiyun lp_advertising |=
185*4882a593Smuzhiyun mii_stat1000_to_ethtool_lpa_t(stat1000);
186*4882a593Smuzhiyun } else {
187*4882a593Smuzhiyun lp_advertising = 0;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun nego = advertising & lp_advertising;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun if (nego & (ADVERTISED_1000baseT_Full |
193*4882a593Smuzhiyun ADVERTISED_1000baseT_Half)) {
194*4882a593Smuzhiyun cmd->base.speed = SPEED_1000;
195*4882a593Smuzhiyun cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
196*4882a593Smuzhiyun } else if (nego & (ADVERTISED_100baseT_Full |
197*4882a593Smuzhiyun ADVERTISED_100baseT_Half)) {
198*4882a593Smuzhiyun cmd->base.speed = SPEED_100;
199*4882a593Smuzhiyun cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
200*4882a593Smuzhiyun } else {
201*4882a593Smuzhiyun cmd->base.speed = SPEED_10;
202*4882a593Smuzhiyun cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun } else {
205*4882a593Smuzhiyun cmd->base.autoneg = AUTONEG_DISABLE;
206*4882a593Smuzhiyun
207*4882a593Smuzhiyun cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
208*4882a593Smuzhiyun (bmcr & BMCR_SPEED100) == 0) ?
209*4882a593Smuzhiyun SPEED_1000 :
210*4882a593Smuzhiyun ((bmcr & BMCR_SPEED100) ?
211*4882a593Smuzhiyun SPEED_100 : SPEED_10));
212*4882a593Smuzhiyun cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
213*4882a593Smuzhiyun DUPLEX_FULL : DUPLEX_HALF;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun lp_advertising = 0;
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun mii->full_duplex = cmd->base.duplex;
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
221*4882a593Smuzhiyun supported);
222*4882a593Smuzhiyun ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
223*4882a593Smuzhiyun advertising);
224*4882a593Smuzhiyun ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
225*4882a593Smuzhiyun lp_advertising);
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun /* ignore maxtxpkt, maxrxpkt for now */
228*4882a593Smuzhiyun }
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /**
231*4882a593Smuzhiyun * mii_ethtool_sset - set settings that are specified in @ecmd
232*4882a593Smuzhiyun * @mii: MII interface
233*4882a593Smuzhiyun * @ecmd: requested ethtool_cmd
234*4882a593Smuzhiyun *
235*4882a593Smuzhiyun * Returns 0 for success, negative on error.
236*4882a593Smuzhiyun */
mii_ethtool_sset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)237*4882a593Smuzhiyun int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun struct net_device *dev = mii->dev;
240*4882a593Smuzhiyun u32 speed = ethtool_cmd_speed(ecmd);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun if (speed != SPEED_10 &&
243*4882a593Smuzhiyun speed != SPEED_100 &&
244*4882a593Smuzhiyun speed != SPEED_1000)
245*4882a593Smuzhiyun return -EINVAL;
246*4882a593Smuzhiyun if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
247*4882a593Smuzhiyun return -EINVAL;
248*4882a593Smuzhiyun if (ecmd->port != PORT_MII)
249*4882a593Smuzhiyun return -EINVAL;
250*4882a593Smuzhiyun if (ecmd->transceiver != XCVR_INTERNAL)
251*4882a593Smuzhiyun return -EINVAL;
252*4882a593Smuzhiyun if (ecmd->phy_address != mii->phy_id)
253*4882a593Smuzhiyun return -EINVAL;
254*4882a593Smuzhiyun if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
255*4882a593Smuzhiyun return -EINVAL;
256*4882a593Smuzhiyun if ((speed == SPEED_1000) && (!mii->supports_gmii))
257*4882a593Smuzhiyun return -EINVAL;
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun /* ignore supported, maxtxpkt, maxrxpkt */
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun if (ecmd->autoneg == AUTONEG_ENABLE) {
262*4882a593Smuzhiyun u32 bmcr, advert, tmp;
263*4882a593Smuzhiyun u32 advert2 = 0, tmp2 = 0;
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
266*4882a593Smuzhiyun ADVERTISED_10baseT_Full |
267*4882a593Smuzhiyun ADVERTISED_100baseT_Half |
268*4882a593Smuzhiyun ADVERTISED_100baseT_Full |
269*4882a593Smuzhiyun ADVERTISED_1000baseT_Half |
270*4882a593Smuzhiyun ADVERTISED_1000baseT_Full)) == 0)
271*4882a593Smuzhiyun return -EINVAL;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /* advertise only what has been requested */
274*4882a593Smuzhiyun advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
275*4882a593Smuzhiyun tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
276*4882a593Smuzhiyun if (mii->supports_gmii) {
277*4882a593Smuzhiyun advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
278*4882a593Smuzhiyun tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun if (mii->supports_gmii)
283*4882a593Smuzhiyun tmp2 |=
284*4882a593Smuzhiyun ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
285*4882a593Smuzhiyun if (advert != tmp) {
286*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
287*4882a593Smuzhiyun mii->advertising = tmp;
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun if ((mii->supports_gmii) && (advert2 != tmp2))
290*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun /* turn on autonegotiation, and force a renegotiate */
293*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
294*4882a593Smuzhiyun bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
295*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun mii->force_media = 0;
298*4882a593Smuzhiyun } else {
299*4882a593Smuzhiyun u32 bmcr, tmp;
300*4882a593Smuzhiyun
301*4882a593Smuzhiyun /* turn off auto negotiation, set speed and duplexity */
302*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
303*4882a593Smuzhiyun tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
304*4882a593Smuzhiyun BMCR_SPEED1000 | BMCR_FULLDPLX);
305*4882a593Smuzhiyun if (speed == SPEED_1000)
306*4882a593Smuzhiyun tmp |= BMCR_SPEED1000;
307*4882a593Smuzhiyun else if (speed == SPEED_100)
308*4882a593Smuzhiyun tmp |= BMCR_SPEED100;
309*4882a593Smuzhiyun if (ecmd->duplex == DUPLEX_FULL) {
310*4882a593Smuzhiyun tmp |= BMCR_FULLDPLX;
311*4882a593Smuzhiyun mii->full_duplex = 1;
312*4882a593Smuzhiyun } else
313*4882a593Smuzhiyun mii->full_duplex = 0;
314*4882a593Smuzhiyun if (bmcr != tmp)
315*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun mii->force_media = 1;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun return 0;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun /**
323*4882a593Smuzhiyun * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
324*4882a593Smuzhiyun * @mii: MII interfaces
325*4882a593Smuzhiyun * @cmd: requested ethtool_link_ksettings
326*4882a593Smuzhiyun *
327*4882a593Smuzhiyun * Returns 0 for success, negative on error.
328*4882a593Smuzhiyun */
mii_ethtool_set_link_ksettings(struct mii_if_info * mii,const struct ethtool_link_ksettings * cmd)329*4882a593Smuzhiyun int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
330*4882a593Smuzhiyun const struct ethtool_link_ksettings *cmd)
331*4882a593Smuzhiyun {
332*4882a593Smuzhiyun struct net_device *dev = mii->dev;
333*4882a593Smuzhiyun u32 speed = cmd->base.speed;
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun if (speed != SPEED_10 &&
336*4882a593Smuzhiyun speed != SPEED_100 &&
337*4882a593Smuzhiyun speed != SPEED_1000)
338*4882a593Smuzhiyun return -EINVAL;
339*4882a593Smuzhiyun if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
340*4882a593Smuzhiyun return -EINVAL;
341*4882a593Smuzhiyun if (cmd->base.port != PORT_MII)
342*4882a593Smuzhiyun return -EINVAL;
343*4882a593Smuzhiyun if (cmd->base.phy_address != mii->phy_id)
344*4882a593Smuzhiyun return -EINVAL;
345*4882a593Smuzhiyun if (cmd->base.autoneg != AUTONEG_DISABLE &&
346*4882a593Smuzhiyun cmd->base.autoneg != AUTONEG_ENABLE)
347*4882a593Smuzhiyun return -EINVAL;
348*4882a593Smuzhiyun if ((speed == SPEED_1000) && (!mii->supports_gmii))
349*4882a593Smuzhiyun return -EINVAL;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun /* ignore supported, maxtxpkt, maxrxpkt */
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun if (cmd->base.autoneg == AUTONEG_ENABLE) {
354*4882a593Smuzhiyun u32 bmcr, advert, tmp;
355*4882a593Smuzhiyun u32 advert2 = 0, tmp2 = 0;
356*4882a593Smuzhiyun u32 advertising;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun ethtool_convert_link_mode_to_legacy_u32(
359*4882a593Smuzhiyun &advertising, cmd->link_modes.advertising);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun if ((advertising & (ADVERTISED_10baseT_Half |
362*4882a593Smuzhiyun ADVERTISED_10baseT_Full |
363*4882a593Smuzhiyun ADVERTISED_100baseT_Half |
364*4882a593Smuzhiyun ADVERTISED_100baseT_Full |
365*4882a593Smuzhiyun ADVERTISED_1000baseT_Half |
366*4882a593Smuzhiyun ADVERTISED_1000baseT_Full)) == 0)
367*4882a593Smuzhiyun return -EINVAL;
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /* advertise only what has been requested */
370*4882a593Smuzhiyun advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
371*4882a593Smuzhiyun tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
372*4882a593Smuzhiyun if (mii->supports_gmii) {
373*4882a593Smuzhiyun advert2 = mii->mdio_read(dev, mii->phy_id,
374*4882a593Smuzhiyun MII_CTRL1000);
375*4882a593Smuzhiyun tmp2 = advert2 &
376*4882a593Smuzhiyun ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun tmp |= ethtool_adv_to_mii_adv_t(advertising);
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun if (mii->supports_gmii)
381*4882a593Smuzhiyun tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
382*4882a593Smuzhiyun if (advert != tmp) {
383*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
384*4882a593Smuzhiyun mii->advertising = tmp;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun if ((mii->supports_gmii) && (advert2 != tmp2))
387*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
388*4882a593Smuzhiyun
389*4882a593Smuzhiyun /* turn on autonegotiation, and force a renegotiate */
390*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
391*4882a593Smuzhiyun bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
392*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun mii->force_media = 0;
395*4882a593Smuzhiyun } else {
396*4882a593Smuzhiyun u32 bmcr, tmp;
397*4882a593Smuzhiyun
398*4882a593Smuzhiyun /* turn off auto negotiation, set speed and duplexity */
399*4882a593Smuzhiyun bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
400*4882a593Smuzhiyun tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
401*4882a593Smuzhiyun BMCR_SPEED1000 | BMCR_FULLDPLX);
402*4882a593Smuzhiyun if (speed == SPEED_1000)
403*4882a593Smuzhiyun tmp |= BMCR_SPEED1000;
404*4882a593Smuzhiyun else if (speed == SPEED_100)
405*4882a593Smuzhiyun tmp |= BMCR_SPEED100;
406*4882a593Smuzhiyun if (cmd->base.duplex == DUPLEX_FULL) {
407*4882a593Smuzhiyun tmp |= BMCR_FULLDPLX;
408*4882a593Smuzhiyun mii->full_duplex = 1;
409*4882a593Smuzhiyun } else {
410*4882a593Smuzhiyun mii->full_duplex = 0;
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun if (bmcr != tmp)
413*4882a593Smuzhiyun mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun mii->force_media = 1;
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun return 0;
418*4882a593Smuzhiyun }
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun /**
421*4882a593Smuzhiyun * mii_check_gmii_support - check if the MII supports Gb interfaces
422*4882a593Smuzhiyun * @mii: the MII interface
423*4882a593Smuzhiyun */
mii_check_gmii_support(struct mii_if_info * mii)424*4882a593Smuzhiyun int mii_check_gmii_support(struct mii_if_info *mii)
425*4882a593Smuzhiyun {
426*4882a593Smuzhiyun int reg;
427*4882a593Smuzhiyun
428*4882a593Smuzhiyun reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
429*4882a593Smuzhiyun if (reg & BMSR_ESTATEN) {
430*4882a593Smuzhiyun reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
431*4882a593Smuzhiyun if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
432*4882a593Smuzhiyun return 1;
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun return 0;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun /**
439*4882a593Smuzhiyun * mii_link_ok - is link status up/ok
440*4882a593Smuzhiyun * @mii: the MII interface
441*4882a593Smuzhiyun *
442*4882a593Smuzhiyun * Returns 1 if the MII reports link status up/ok, 0 otherwise.
443*4882a593Smuzhiyun */
mii_link_ok(struct mii_if_info * mii)444*4882a593Smuzhiyun int mii_link_ok (struct mii_if_info *mii)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun /* first, a dummy read, needed to latch some MII phys */
447*4882a593Smuzhiyun mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
448*4882a593Smuzhiyun if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
449*4882a593Smuzhiyun return 1;
450*4882a593Smuzhiyun return 0;
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun /**
454*4882a593Smuzhiyun * mii_nway_restart - restart NWay (autonegotiation) for this interface
455*4882a593Smuzhiyun * @mii: the MII interface
456*4882a593Smuzhiyun *
457*4882a593Smuzhiyun * Returns 0 on success, negative on error.
458*4882a593Smuzhiyun */
mii_nway_restart(struct mii_if_info * mii)459*4882a593Smuzhiyun int mii_nway_restart (struct mii_if_info *mii)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun int bmcr;
462*4882a593Smuzhiyun int r = -EINVAL;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun /* if autoneg is off, it's an error */
465*4882a593Smuzhiyun bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
466*4882a593Smuzhiyun
467*4882a593Smuzhiyun if (bmcr & BMCR_ANENABLE) {
468*4882a593Smuzhiyun bmcr |= BMCR_ANRESTART;
469*4882a593Smuzhiyun mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
470*4882a593Smuzhiyun r = 0;
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun
473*4882a593Smuzhiyun return r;
474*4882a593Smuzhiyun }
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun /**
477*4882a593Smuzhiyun * mii_check_link - check MII link status
478*4882a593Smuzhiyun * @mii: MII interface
479*4882a593Smuzhiyun *
480*4882a593Smuzhiyun * If the link status changed (previous != current), call
481*4882a593Smuzhiyun * netif_carrier_on() if current link status is Up or call
482*4882a593Smuzhiyun * netif_carrier_off() if current link status is Down.
483*4882a593Smuzhiyun */
mii_check_link(struct mii_if_info * mii)484*4882a593Smuzhiyun void mii_check_link (struct mii_if_info *mii)
485*4882a593Smuzhiyun {
486*4882a593Smuzhiyun int cur_link = mii_link_ok(mii);
487*4882a593Smuzhiyun int prev_link = netif_carrier_ok(mii->dev);
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun if (cur_link && !prev_link)
490*4882a593Smuzhiyun netif_carrier_on(mii->dev);
491*4882a593Smuzhiyun else if (prev_link && !cur_link)
492*4882a593Smuzhiyun netif_carrier_off(mii->dev);
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun
495*4882a593Smuzhiyun /**
496*4882a593Smuzhiyun * mii_check_media - check the MII interface for a carrier/speed/duplex change
497*4882a593Smuzhiyun * @mii: the MII interface
498*4882a593Smuzhiyun * @ok_to_print: OK to print link up/down messages
499*4882a593Smuzhiyun * @init_media: OK to save duplex mode in @mii
500*4882a593Smuzhiyun *
501*4882a593Smuzhiyun * Returns 1 if the duplex mode changed, 0 if not.
502*4882a593Smuzhiyun * If the media type is forced, always returns 0.
503*4882a593Smuzhiyun */
mii_check_media(struct mii_if_info * mii,unsigned int ok_to_print,unsigned int init_media)504*4882a593Smuzhiyun unsigned int mii_check_media (struct mii_if_info *mii,
505*4882a593Smuzhiyun unsigned int ok_to_print,
506*4882a593Smuzhiyun unsigned int init_media)
507*4882a593Smuzhiyun {
508*4882a593Smuzhiyun unsigned int old_carrier, new_carrier;
509*4882a593Smuzhiyun int advertise, lpa, media, duplex;
510*4882a593Smuzhiyun int lpa2 = 0;
511*4882a593Smuzhiyun
512*4882a593Smuzhiyun /* check current and old link status */
513*4882a593Smuzhiyun old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
514*4882a593Smuzhiyun new_carrier = (unsigned int) mii_link_ok(mii);
515*4882a593Smuzhiyun
516*4882a593Smuzhiyun /* if carrier state did not change, this is a "bounce",
517*4882a593Smuzhiyun * just exit as everything is already set correctly
518*4882a593Smuzhiyun */
519*4882a593Smuzhiyun if ((!init_media) && (old_carrier == new_carrier))
520*4882a593Smuzhiyun return 0; /* duplex did not change */
521*4882a593Smuzhiyun
522*4882a593Smuzhiyun /* no carrier, nothing much to do */
523*4882a593Smuzhiyun if (!new_carrier) {
524*4882a593Smuzhiyun netif_carrier_off(mii->dev);
525*4882a593Smuzhiyun if (ok_to_print)
526*4882a593Smuzhiyun netdev_info(mii->dev, "link down\n");
527*4882a593Smuzhiyun return 0; /* duplex did not change */
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun /*
531*4882a593Smuzhiyun * we have carrier, see who's on the other end
532*4882a593Smuzhiyun */
533*4882a593Smuzhiyun netif_carrier_on(mii->dev);
534*4882a593Smuzhiyun
535*4882a593Smuzhiyun if (mii->force_media) {
536*4882a593Smuzhiyun if (ok_to_print)
537*4882a593Smuzhiyun netdev_info(mii->dev, "link up\n");
538*4882a593Smuzhiyun return 0; /* duplex did not change */
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun
541*4882a593Smuzhiyun /* get MII advertise and LPA values */
542*4882a593Smuzhiyun if ((!init_media) && (mii->advertising))
543*4882a593Smuzhiyun advertise = mii->advertising;
544*4882a593Smuzhiyun else {
545*4882a593Smuzhiyun advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
546*4882a593Smuzhiyun mii->advertising = advertise;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
549*4882a593Smuzhiyun if (mii->supports_gmii)
550*4882a593Smuzhiyun lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun /* figure out media and duplex from advertise and LPA values */
553*4882a593Smuzhiyun media = mii_nway_result(lpa & advertise);
554*4882a593Smuzhiyun duplex = (media & ADVERTISE_FULL) ? 1 : 0;
555*4882a593Smuzhiyun if (lpa2 & LPA_1000FULL)
556*4882a593Smuzhiyun duplex = 1;
557*4882a593Smuzhiyun
558*4882a593Smuzhiyun if (ok_to_print)
559*4882a593Smuzhiyun netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
560*4882a593Smuzhiyun lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
561*4882a593Smuzhiyun media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
562*4882a593Smuzhiyun 100 : 10,
563*4882a593Smuzhiyun duplex ? "full" : "half",
564*4882a593Smuzhiyun lpa);
565*4882a593Smuzhiyun
566*4882a593Smuzhiyun if ((init_media) || (mii->full_duplex != duplex)) {
567*4882a593Smuzhiyun mii->full_duplex = duplex;
568*4882a593Smuzhiyun return 1; /* duplex changed */
569*4882a593Smuzhiyun }
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun return 0; /* duplex did not change */
572*4882a593Smuzhiyun }
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun /**
575*4882a593Smuzhiyun * generic_mii_ioctl - main MII ioctl interface
576*4882a593Smuzhiyun * @mii_if: the MII interface
577*4882a593Smuzhiyun * @mii_data: MII ioctl data structure
578*4882a593Smuzhiyun * @cmd: MII ioctl command
579*4882a593Smuzhiyun * @duplex_chg_out: pointer to @duplex_changed status if there was no
580*4882a593Smuzhiyun * ioctl error
581*4882a593Smuzhiyun *
582*4882a593Smuzhiyun * Returns 0 on success, negative on error.
583*4882a593Smuzhiyun */
generic_mii_ioctl(struct mii_if_info * mii_if,struct mii_ioctl_data * mii_data,int cmd,unsigned int * duplex_chg_out)584*4882a593Smuzhiyun int generic_mii_ioctl(struct mii_if_info *mii_if,
585*4882a593Smuzhiyun struct mii_ioctl_data *mii_data, int cmd,
586*4882a593Smuzhiyun unsigned int *duplex_chg_out)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun int rc = 0;
589*4882a593Smuzhiyun unsigned int duplex_changed = 0;
590*4882a593Smuzhiyun
591*4882a593Smuzhiyun if (duplex_chg_out)
592*4882a593Smuzhiyun *duplex_chg_out = 0;
593*4882a593Smuzhiyun
594*4882a593Smuzhiyun mii_data->phy_id &= mii_if->phy_id_mask;
595*4882a593Smuzhiyun mii_data->reg_num &= mii_if->reg_num_mask;
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun switch(cmd) {
598*4882a593Smuzhiyun case SIOCGMIIPHY:
599*4882a593Smuzhiyun mii_data->phy_id = mii_if->phy_id;
600*4882a593Smuzhiyun fallthrough;
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun case SIOCGMIIREG:
603*4882a593Smuzhiyun mii_data->val_out =
604*4882a593Smuzhiyun mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
605*4882a593Smuzhiyun mii_data->reg_num);
606*4882a593Smuzhiyun break;
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun case SIOCSMIIREG: {
609*4882a593Smuzhiyun u16 val = mii_data->val_in;
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun if (mii_data->phy_id == mii_if->phy_id) {
612*4882a593Smuzhiyun switch(mii_data->reg_num) {
613*4882a593Smuzhiyun case MII_BMCR: {
614*4882a593Smuzhiyun unsigned int new_duplex = 0;
615*4882a593Smuzhiyun if (val & (BMCR_RESET|BMCR_ANENABLE))
616*4882a593Smuzhiyun mii_if->force_media = 0;
617*4882a593Smuzhiyun else
618*4882a593Smuzhiyun mii_if->force_media = 1;
619*4882a593Smuzhiyun if (mii_if->force_media &&
620*4882a593Smuzhiyun (val & BMCR_FULLDPLX))
621*4882a593Smuzhiyun new_duplex = 1;
622*4882a593Smuzhiyun if (mii_if->full_duplex != new_duplex) {
623*4882a593Smuzhiyun duplex_changed = 1;
624*4882a593Smuzhiyun mii_if->full_duplex = new_duplex;
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun break;
627*4882a593Smuzhiyun }
628*4882a593Smuzhiyun case MII_ADVERTISE:
629*4882a593Smuzhiyun mii_if->advertising = val;
630*4882a593Smuzhiyun break;
631*4882a593Smuzhiyun default:
632*4882a593Smuzhiyun /* do nothing */
633*4882a593Smuzhiyun break;
634*4882a593Smuzhiyun }
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
637*4882a593Smuzhiyun mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
638*4882a593Smuzhiyun mii_data->reg_num, val);
639*4882a593Smuzhiyun break;
640*4882a593Smuzhiyun }
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun default:
643*4882a593Smuzhiyun rc = -EOPNOTSUPP;
644*4882a593Smuzhiyun break;
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun
647*4882a593Smuzhiyun if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
648*4882a593Smuzhiyun *duplex_chg_out = 1;
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun return rc;
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun
653*4882a593Smuzhiyun MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
654*4882a593Smuzhiyun MODULE_DESCRIPTION ("MII hardware support library");
655*4882a593Smuzhiyun MODULE_LICENSE("GPL");
656*4882a593Smuzhiyun
657*4882a593Smuzhiyun EXPORT_SYMBOL(mii_link_ok);
658*4882a593Smuzhiyun EXPORT_SYMBOL(mii_nway_restart);
659*4882a593Smuzhiyun EXPORT_SYMBOL(mii_ethtool_gset);
660*4882a593Smuzhiyun EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
661*4882a593Smuzhiyun EXPORT_SYMBOL(mii_ethtool_sset);
662*4882a593Smuzhiyun EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
663*4882a593Smuzhiyun EXPORT_SYMBOL(mii_check_link);
664*4882a593Smuzhiyun EXPORT_SYMBOL(mii_check_media);
665*4882a593Smuzhiyun EXPORT_SYMBOL(mii_check_gmii_support);
666*4882a593Smuzhiyun EXPORT_SYMBOL(generic_mii_ioctl);
667*4882a593Smuzhiyun
668