11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds
31da177e4SLinus Torvalds mii.c: MII interface library
41da177e4SLinus Torvalds
51da177e4SLinus Torvalds Maintained by Jeff Garzik <jgarzik@pobox.com>
61da177e4SLinus Torvalds Copyright 2001,2002 Jeff Garzik
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds Various code came from myson803.c and other files by
91da177e4SLinus Torvalds Donald Becker. Copyright:
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds Written 1998-2002 by Donald Becker.
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds This software may be used and distributed according
141da177e4SLinus Torvalds to the terms of the GNU General Public License (GPL),
151da177e4SLinus Torvalds incorporated herein by reference. Drivers based on
161da177e4SLinus Torvalds or derived from this code fall under the GPL and must
171da177e4SLinus Torvalds retain the authorship, copyright and license notice.
181da177e4SLinus Torvalds This file is not a complete program and may only be
191da177e4SLinus Torvalds used when the entire operating system is licensed
201da177e4SLinus Torvalds under the GPL.
211da177e4SLinus Torvalds
221da177e4SLinus Torvalds The author may be reached as becker@scyld.com, or C/O
231da177e4SLinus Torvalds Scyld Computing Corporation
241da177e4SLinus Torvalds 410 Severn Ave., Suite 210
251da177e4SLinus Torvalds Annapolis MD 21403
261da177e4SLinus Torvalds
271da177e4SLinus Torvalds
281da177e4SLinus Torvalds */
291da177e4SLinus Torvalds
301da177e4SLinus Torvalds #include <linux/kernel.h>
311da177e4SLinus Torvalds #include <linux/module.h>
321da177e4SLinus Torvalds #include <linux/netdevice.h>
331da177e4SLinus Torvalds #include <linux/ethtool.h>
349c4df53bSBen Hutchings #include <linux/mii.h>
355974700cSBen Hutchings
mii_get_an(struct mii_if_info * mii,u16 addr)365974700cSBen Hutchings static u32 mii_get_an(struct mii_if_info *mii, u16 addr)
375974700cSBen Hutchings {
385974700cSBen Hutchings int advert;
395974700cSBen Hutchings
405974700cSBen Hutchings advert = mii->mdio_read(mii->dev, mii->phy_id, addr);
415974700cSBen Hutchings
4237f07023SMatt Carlson return mii_lpa_to_ethtool_lpa_t(advert);
435974700cSBen Hutchings }
441da177e4SLinus Torvalds
4532684ec6SRandy Dunlap /**
4632684ec6SRandy Dunlap * mii_ethtool_gset - get settings that are specified in @ecmd
4732684ec6SRandy Dunlap * @mii: MII interface
4832684ec6SRandy Dunlap * @ecmd: requested ethtool_cmd
4932684ec6SRandy Dunlap *
508ae6dacaSDavid Decotigny * The @ecmd parameter is expected to have been cleared before calling
518ae6dacaSDavid Decotigny * mii_ethtool_gset().
5232684ec6SRandy Dunlap */
mii_ethtool_gset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)53*2274af1dSPavel Skripkin void mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
541da177e4SLinus Torvalds {
551da177e4SLinus Torvalds struct net_device *dev = mii->dev;
565974700cSBen Hutchings u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
575974700cSBen Hutchings u32 nego;
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds ecmd->supported =
601da177e4SLinus Torvalds (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
611da177e4SLinus Torvalds SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
621da177e4SLinus Torvalds SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
631da177e4SLinus Torvalds if (mii->supports_gmii)
641da177e4SLinus Torvalds ecmd->supported |= SUPPORTED_1000baseT_Half |
651da177e4SLinus Torvalds SUPPORTED_1000baseT_Full;
661da177e4SLinus Torvalds
671da177e4SLinus Torvalds /* only supports twisted-pair */
681da177e4SLinus Torvalds ecmd->port = PORT_MII;
691da177e4SLinus Torvalds
701da177e4SLinus Torvalds /* only supports internal transceiver */
711da177e4SLinus Torvalds ecmd->transceiver = XCVR_INTERNAL;
721da177e4SLinus Torvalds
731da177e4SLinus Torvalds /* this isn't fully supported at higher layers */
741da177e4SLinus Torvalds ecmd->phy_address = mii->phy_id;
759c4df53bSBen Hutchings ecmd->mdio_support = ETH_MDIO_SUPPORTS_C22;
761da177e4SLinus Torvalds
771da177e4SLinus Torvalds ecmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
781da177e4SLinus Torvalds
791da177e4SLinus Torvalds bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
805974700cSBen Hutchings bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
811da177e4SLinus Torvalds if (mii->supports_gmii) {
825974700cSBen Hutchings ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
835974700cSBen Hutchings stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
841da177e4SLinus Torvalds }
851da177e4SLinus Torvalds
865974700cSBen Hutchings ecmd->advertising |= mii_get_an(mii, MII_ADVERTISE);
8728011cf1SMatt Carlson if (mii->supports_gmii)
8837f07023SMatt Carlson ecmd->advertising |=
8937f07023SMatt Carlson mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
905974700cSBen Hutchings
918027c85cSŁukasz Stelmach if (bmcr & BMCR_ANENABLE) {
928027c85cSŁukasz Stelmach ecmd->advertising |= ADVERTISED_Autoneg;
938027c85cSŁukasz Stelmach ecmd->autoneg = AUTONEG_ENABLE;
948027c85cSŁukasz Stelmach
955974700cSBen Hutchings if (bmsr & BMSR_ANEGCOMPLETE) {
965974700cSBen Hutchings ecmd->lp_advertising = mii_get_an(mii, MII_LPA);
975974700cSBen Hutchings ecmd->lp_advertising |=
9837f07023SMatt Carlson mii_stat1000_to_ethtool_lpa_t(stat1000);
991da177e4SLinus Torvalds } else {
1005974700cSBen Hutchings ecmd->lp_advertising = 0;
1015974700cSBen Hutchings }
1025974700cSBen Hutchings
1035974700cSBen Hutchings nego = ecmd->advertising & ecmd->lp_advertising;
1045974700cSBen Hutchings
1055974700cSBen Hutchings if (nego & (ADVERTISED_1000baseT_Full |
1065974700cSBen Hutchings ADVERTISED_1000baseT_Half)) {
10770739497SDavid Decotigny ethtool_cmd_speed_set(ecmd, SPEED_1000);
1085974700cSBen Hutchings ecmd->duplex = !!(nego & ADVERTISED_1000baseT_Full);
1095974700cSBen Hutchings } else if (nego & (ADVERTISED_100baseT_Full |
1105974700cSBen Hutchings ADVERTISED_100baseT_Half)) {
11170739497SDavid Decotigny ethtool_cmd_speed_set(ecmd, SPEED_100);
1125974700cSBen Hutchings ecmd->duplex = !!(nego & ADVERTISED_100baseT_Full);
1135974700cSBen Hutchings } else {
11470739497SDavid Decotigny ethtool_cmd_speed_set(ecmd, SPEED_10);
1155974700cSBen Hutchings ecmd->duplex = !!(nego & ADVERTISED_10baseT_Full);
1161da177e4SLinus Torvalds }
1171da177e4SLinus Torvalds } else {
1181da177e4SLinus Torvalds ecmd->autoneg = AUTONEG_DISABLE;
1191da177e4SLinus Torvalds
12070739497SDavid Decotigny ethtool_cmd_speed_set(ecmd,
12170739497SDavid Decotigny ((bmcr & BMCR_SPEED1000 &&
12270739497SDavid Decotigny (bmcr & BMCR_SPEED100) == 0) ?
12370739497SDavid Decotigny SPEED_1000 :
12470739497SDavid Decotigny ((bmcr & BMCR_SPEED100) ?
12570739497SDavid Decotigny SPEED_100 : SPEED_10)));
1261da177e4SLinus Torvalds ecmd->duplex = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF;
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds
1295974700cSBen Hutchings mii->full_duplex = ecmd->duplex;
1305974700cSBen Hutchings
1311da177e4SLinus Torvalds /* ignore maxtxpkt, maxrxpkt for now */
1321da177e4SLinus Torvalds }
1331da177e4SLinus Torvalds
13432684ec6SRandy Dunlap /**
135bc8ee596SPhilippe Reynes * mii_ethtool_get_link_ksettings - get settings that are specified in @cmd
136bc8ee596SPhilippe Reynes * @mii: MII interface
137bc8ee596SPhilippe Reynes * @cmd: requested ethtool_link_ksettings
138bc8ee596SPhilippe Reynes *
139bc8ee596SPhilippe Reynes * The @cmd parameter is expected to have been cleared before calling
140bc8ee596SPhilippe Reynes * mii_ethtool_get_link_ksettings().
141bc8ee596SPhilippe Reynes */
mii_ethtool_get_link_ksettings(struct mii_if_info * mii,struct ethtool_link_ksettings * cmd)14282c01a84Syuval.shaia@oracle.com void mii_ethtool_get_link_ksettings(struct mii_if_info *mii,
143bc8ee596SPhilippe Reynes struct ethtool_link_ksettings *cmd)
144bc8ee596SPhilippe Reynes {
145bc8ee596SPhilippe Reynes struct net_device *dev = mii->dev;
146bc8ee596SPhilippe Reynes u16 bmcr, bmsr, ctrl1000 = 0, stat1000 = 0;
147bc8ee596SPhilippe Reynes u32 nego, supported, advertising, lp_advertising;
148bc8ee596SPhilippe Reynes
149bc8ee596SPhilippe Reynes supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
150bc8ee596SPhilippe Reynes SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
151bc8ee596SPhilippe Reynes SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
152bc8ee596SPhilippe Reynes if (mii->supports_gmii)
153bc8ee596SPhilippe Reynes supported |= SUPPORTED_1000baseT_Half |
154bc8ee596SPhilippe Reynes SUPPORTED_1000baseT_Full;
155bc8ee596SPhilippe Reynes
156bc8ee596SPhilippe Reynes /* only supports twisted-pair */
157bc8ee596SPhilippe Reynes cmd->base.port = PORT_MII;
158bc8ee596SPhilippe Reynes
159bc8ee596SPhilippe Reynes /* this isn't fully supported at higher layers */
160bc8ee596SPhilippe Reynes cmd->base.phy_address = mii->phy_id;
161bc8ee596SPhilippe Reynes cmd->base.mdio_support = ETH_MDIO_SUPPORTS_C22;
162bc8ee596SPhilippe Reynes
163bc8ee596SPhilippe Reynes advertising = ADVERTISED_TP | ADVERTISED_MII;
164bc8ee596SPhilippe Reynes
165bc8ee596SPhilippe Reynes bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
166bc8ee596SPhilippe Reynes bmsr = mii->mdio_read(dev, mii->phy_id, MII_BMSR);
167bc8ee596SPhilippe Reynes if (mii->supports_gmii) {
168bc8ee596SPhilippe Reynes ctrl1000 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
169bc8ee596SPhilippe Reynes stat1000 = mii->mdio_read(dev, mii->phy_id, MII_STAT1000);
170bc8ee596SPhilippe Reynes }
171bc8ee596SPhilippe Reynes
172bc8ee596SPhilippe Reynes advertising |= mii_get_an(mii, MII_ADVERTISE);
173bc8ee596SPhilippe Reynes if (mii->supports_gmii)
174bc8ee596SPhilippe Reynes advertising |= mii_ctrl1000_to_ethtool_adv_t(ctrl1000);
175bc8ee596SPhilippe Reynes
1768027c85cSŁukasz Stelmach if (bmcr & BMCR_ANENABLE) {
1778027c85cSŁukasz Stelmach advertising |= ADVERTISED_Autoneg;
1788027c85cSŁukasz Stelmach cmd->base.autoneg = AUTONEG_ENABLE;
1798027c85cSŁukasz Stelmach
180bc8ee596SPhilippe Reynes if (bmsr & BMSR_ANEGCOMPLETE) {
181bc8ee596SPhilippe Reynes lp_advertising = mii_get_an(mii, MII_LPA);
182bc8ee596SPhilippe Reynes lp_advertising |=
183bc8ee596SPhilippe Reynes mii_stat1000_to_ethtool_lpa_t(stat1000);
184bc8ee596SPhilippe Reynes } else {
185bc8ee596SPhilippe Reynes lp_advertising = 0;
186bc8ee596SPhilippe Reynes }
187bc8ee596SPhilippe Reynes
188bc8ee596SPhilippe Reynes nego = advertising & lp_advertising;
189bc8ee596SPhilippe Reynes
190bc8ee596SPhilippe Reynes if (nego & (ADVERTISED_1000baseT_Full |
191bc8ee596SPhilippe Reynes ADVERTISED_1000baseT_Half)) {
192bc8ee596SPhilippe Reynes cmd->base.speed = SPEED_1000;
193bc8ee596SPhilippe Reynes cmd->base.duplex = !!(nego & ADVERTISED_1000baseT_Full);
194bc8ee596SPhilippe Reynes } else if (nego & (ADVERTISED_100baseT_Full |
195bc8ee596SPhilippe Reynes ADVERTISED_100baseT_Half)) {
196bc8ee596SPhilippe Reynes cmd->base.speed = SPEED_100;
197bc8ee596SPhilippe Reynes cmd->base.duplex = !!(nego & ADVERTISED_100baseT_Full);
198bc8ee596SPhilippe Reynes } else {
199bc8ee596SPhilippe Reynes cmd->base.speed = SPEED_10;
200bc8ee596SPhilippe Reynes cmd->base.duplex = !!(nego & ADVERTISED_10baseT_Full);
201bc8ee596SPhilippe Reynes }
202bc8ee596SPhilippe Reynes } else {
203bc8ee596SPhilippe Reynes cmd->base.autoneg = AUTONEG_DISABLE;
204bc8ee596SPhilippe Reynes
205bc8ee596SPhilippe Reynes cmd->base.speed = ((bmcr & BMCR_SPEED1000 &&
206bc8ee596SPhilippe Reynes (bmcr & BMCR_SPEED100) == 0) ?
207bc8ee596SPhilippe Reynes SPEED_1000 :
208bc8ee596SPhilippe Reynes ((bmcr & BMCR_SPEED100) ?
209bc8ee596SPhilippe Reynes SPEED_100 : SPEED_10));
210bc8ee596SPhilippe Reynes cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ?
211bc8ee596SPhilippe Reynes DUPLEX_FULL : DUPLEX_HALF;
212dc0b2c9cSArnd Bergmann
213dc0b2c9cSArnd Bergmann lp_advertising = 0;
214bc8ee596SPhilippe Reynes }
215bc8ee596SPhilippe Reynes
216bc8ee596SPhilippe Reynes mii->full_duplex = cmd->base.duplex;
217bc8ee596SPhilippe Reynes
218bc8ee596SPhilippe Reynes ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
219bc8ee596SPhilippe Reynes supported);
220bc8ee596SPhilippe Reynes ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising,
221bc8ee596SPhilippe Reynes advertising);
222bc8ee596SPhilippe Reynes ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.lp_advertising,
223bc8ee596SPhilippe Reynes lp_advertising);
224bc8ee596SPhilippe Reynes
225bc8ee596SPhilippe Reynes /* ignore maxtxpkt, maxrxpkt for now */
226bc8ee596SPhilippe Reynes }
227bc8ee596SPhilippe Reynes
228bc8ee596SPhilippe Reynes /**
22932684ec6SRandy Dunlap * mii_ethtool_sset - set settings that are specified in @ecmd
23032684ec6SRandy Dunlap * @mii: MII interface
23132684ec6SRandy Dunlap * @ecmd: requested ethtool_cmd
23232684ec6SRandy Dunlap *
23332684ec6SRandy Dunlap * Returns 0 for success, negative on error.
23432684ec6SRandy Dunlap */
mii_ethtool_sset(struct mii_if_info * mii,struct ethtool_cmd * ecmd)2351da177e4SLinus Torvalds int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd)
2361da177e4SLinus Torvalds {
2371da177e4SLinus Torvalds struct net_device *dev = mii->dev;
23825db0338SDavid Decotigny u32 speed = ethtool_cmd_speed(ecmd);
2391da177e4SLinus Torvalds
24025db0338SDavid Decotigny if (speed != SPEED_10 &&
24125db0338SDavid Decotigny speed != SPEED_100 &&
24225db0338SDavid Decotigny speed != SPEED_1000)
2431da177e4SLinus Torvalds return -EINVAL;
2441da177e4SLinus Torvalds if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL)
2451da177e4SLinus Torvalds return -EINVAL;
2461da177e4SLinus Torvalds if (ecmd->port != PORT_MII)
2471da177e4SLinus Torvalds return -EINVAL;
2481da177e4SLinus Torvalds if (ecmd->transceiver != XCVR_INTERNAL)
2491da177e4SLinus Torvalds return -EINVAL;
2501da177e4SLinus Torvalds if (ecmd->phy_address != mii->phy_id)
2511da177e4SLinus Torvalds return -EINVAL;
2521da177e4SLinus Torvalds if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
2531da177e4SLinus Torvalds return -EINVAL;
25425db0338SDavid Decotigny if ((speed == SPEED_1000) && (!mii->supports_gmii))
2551da177e4SLinus Torvalds return -EINVAL;
2561da177e4SLinus Torvalds
2571da177e4SLinus Torvalds /* ignore supported, maxtxpkt, maxrxpkt */
2581da177e4SLinus Torvalds
2591da177e4SLinus Torvalds if (ecmd->autoneg == AUTONEG_ENABLE) {
2601da177e4SLinus Torvalds u32 bmcr, advert, tmp;
2611da177e4SLinus Torvalds u32 advert2 = 0, tmp2 = 0;
2621da177e4SLinus Torvalds
2631da177e4SLinus Torvalds if ((ecmd->advertising & (ADVERTISED_10baseT_Half |
2641da177e4SLinus Torvalds ADVERTISED_10baseT_Full |
2651da177e4SLinus Torvalds ADVERTISED_100baseT_Half |
2661da177e4SLinus Torvalds ADVERTISED_100baseT_Full |
2671da177e4SLinus Torvalds ADVERTISED_1000baseT_Half |
2681da177e4SLinus Torvalds ADVERTISED_1000baseT_Full)) == 0)
2691da177e4SLinus Torvalds return -EINVAL;
2701da177e4SLinus Torvalds
2711da177e4SLinus Torvalds /* advertise only what has been requested */
2721da177e4SLinus Torvalds advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
2731da177e4SLinus Torvalds tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
2741da177e4SLinus Torvalds if (mii->supports_gmii) {
2751da177e4SLinus Torvalds advert2 = mii->mdio_read(dev, mii->phy_id, MII_CTRL1000);
2761da177e4SLinus Torvalds tmp2 = advert2 & ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
2771da177e4SLinus Torvalds }
27837f07023SMatt Carlson tmp |= ethtool_adv_to_mii_adv_t(ecmd->advertising);
27928011cf1SMatt Carlson
28028011cf1SMatt Carlson if (mii->supports_gmii)
28137f07023SMatt Carlson tmp2 |=
28237f07023SMatt Carlson ethtool_adv_to_mii_ctrl1000_t(ecmd->advertising);
2831da177e4SLinus Torvalds if (advert != tmp) {
2841da177e4SLinus Torvalds mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
2851da177e4SLinus Torvalds mii->advertising = tmp;
2861da177e4SLinus Torvalds }
2871da177e4SLinus Torvalds if ((mii->supports_gmii) && (advert2 != tmp2))
2881da177e4SLinus Torvalds mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
2891da177e4SLinus Torvalds
2901da177e4SLinus Torvalds /* turn on autonegotiation, and force a renegotiate */
2911da177e4SLinus Torvalds bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
2921da177e4SLinus Torvalds bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
2931da177e4SLinus Torvalds mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
2941da177e4SLinus Torvalds
2951da177e4SLinus Torvalds mii->force_media = 0;
2961da177e4SLinus Torvalds } else {
2971da177e4SLinus Torvalds u32 bmcr, tmp;
2981da177e4SLinus Torvalds
2991da177e4SLinus Torvalds /* turn off auto negotiation, set speed and duplexity */
3001da177e4SLinus Torvalds bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
3011da177e4SLinus Torvalds tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
3021da177e4SLinus Torvalds BMCR_SPEED1000 | BMCR_FULLDPLX);
30325db0338SDavid Decotigny if (speed == SPEED_1000)
3041da177e4SLinus Torvalds tmp |= BMCR_SPEED1000;
30525db0338SDavid Decotigny else if (speed == SPEED_100)
3061da177e4SLinus Torvalds tmp |= BMCR_SPEED100;
3071da177e4SLinus Torvalds if (ecmd->duplex == DUPLEX_FULL) {
3081da177e4SLinus Torvalds tmp |= BMCR_FULLDPLX;
3091da177e4SLinus Torvalds mii->full_duplex = 1;
3101da177e4SLinus Torvalds } else
3111da177e4SLinus Torvalds mii->full_duplex = 0;
3121da177e4SLinus Torvalds if (bmcr != tmp)
3131da177e4SLinus Torvalds mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
3141da177e4SLinus Torvalds
3151da177e4SLinus Torvalds mii->force_media = 1;
3161da177e4SLinus Torvalds }
3171da177e4SLinus Torvalds return 0;
3181da177e4SLinus Torvalds }
3191da177e4SLinus Torvalds
32032684ec6SRandy Dunlap /**
321bc8ee596SPhilippe Reynes * mii_ethtool_set_link_ksettings - set settings that are specified in @cmd
322bc8ee596SPhilippe Reynes * @mii: MII interfaces
323bc8ee596SPhilippe Reynes * @cmd: requested ethtool_link_ksettings
324bc8ee596SPhilippe Reynes *
325bc8ee596SPhilippe Reynes * Returns 0 for success, negative on error.
326bc8ee596SPhilippe Reynes */
mii_ethtool_set_link_ksettings(struct mii_if_info * mii,const struct ethtool_link_ksettings * cmd)327bc8ee596SPhilippe Reynes int mii_ethtool_set_link_ksettings(struct mii_if_info *mii,
328bc8ee596SPhilippe Reynes const struct ethtool_link_ksettings *cmd)
329bc8ee596SPhilippe Reynes {
330bc8ee596SPhilippe Reynes struct net_device *dev = mii->dev;
331bc8ee596SPhilippe Reynes u32 speed = cmd->base.speed;
332bc8ee596SPhilippe Reynes
333bc8ee596SPhilippe Reynes if (speed != SPEED_10 &&
334bc8ee596SPhilippe Reynes speed != SPEED_100 &&
335bc8ee596SPhilippe Reynes speed != SPEED_1000)
336bc8ee596SPhilippe Reynes return -EINVAL;
337bc8ee596SPhilippe Reynes if (cmd->base.duplex != DUPLEX_HALF && cmd->base.duplex != DUPLEX_FULL)
338bc8ee596SPhilippe Reynes return -EINVAL;
339bc8ee596SPhilippe Reynes if (cmd->base.port != PORT_MII)
340bc8ee596SPhilippe Reynes return -EINVAL;
341bc8ee596SPhilippe Reynes if (cmd->base.phy_address != mii->phy_id)
342bc8ee596SPhilippe Reynes return -EINVAL;
343bc8ee596SPhilippe Reynes if (cmd->base.autoneg != AUTONEG_DISABLE &&
344bc8ee596SPhilippe Reynes cmd->base.autoneg != AUTONEG_ENABLE)
345bc8ee596SPhilippe Reynes return -EINVAL;
346bc8ee596SPhilippe Reynes if ((speed == SPEED_1000) && (!mii->supports_gmii))
347bc8ee596SPhilippe Reynes return -EINVAL;
348bc8ee596SPhilippe Reynes
349bc8ee596SPhilippe Reynes /* ignore supported, maxtxpkt, maxrxpkt */
350bc8ee596SPhilippe Reynes
351bc8ee596SPhilippe Reynes if (cmd->base.autoneg == AUTONEG_ENABLE) {
352bc8ee596SPhilippe Reynes u32 bmcr, advert, tmp;
353bc8ee596SPhilippe Reynes u32 advert2 = 0, tmp2 = 0;
354bc8ee596SPhilippe Reynes u32 advertising;
355bc8ee596SPhilippe Reynes
356bc8ee596SPhilippe Reynes ethtool_convert_link_mode_to_legacy_u32(
357bc8ee596SPhilippe Reynes &advertising, cmd->link_modes.advertising);
358bc8ee596SPhilippe Reynes
359bc8ee596SPhilippe Reynes if ((advertising & (ADVERTISED_10baseT_Half |
360bc8ee596SPhilippe Reynes ADVERTISED_10baseT_Full |
361bc8ee596SPhilippe Reynes ADVERTISED_100baseT_Half |
362bc8ee596SPhilippe Reynes ADVERTISED_100baseT_Full |
363bc8ee596SPhilippe Reynes ADVERTISED_1000baseT_Half |
364bc8ee596SPhilippe Reynes ADVERTISED_1000baseT_Full)) == 0)
365bc8ee596SPhilippe Reynes return -EINVAL;
366bc8ee596SPhilippe Reynes
367bc8ee596SPhilippe Reynes /* advertise only what has been requested */
368bc8ee596SPhilippe Reynes advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
369bc8ee596SPhilippe Reynes tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
370bc8ee596SPhilippe Reynes if (mii->supports_gmii) {
371bc8ee596SPhilippe Reynes advert2 = mii->mdio_read(dev, mii->phy_id,
372bc8ee596SPhilippe Reynes MII_CTRL1000);
373bc8ee596SPhilippe Reynes tmp2 = advert2 &
374bc8ee596SPhilippe Reynes ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL);
375bc8ee596SPhilippe Reynes }
376bc8ee596SPhilippe Reynes tmp |= ethtool_adv_to_mii_adv_t(advertising);
377bc8ee596SPhilippe Reynes
378bc8ee596SPhilippe Reynes if (mii->supports_gmii)
379bc8ee596SPhilippe Reynes tmp2 |= ethtool_adv_to_mii_ctrl1000_t(advertising);
380bc8ee596SPhilippe Reynes if (advert != tmp) {
381bc8ee596SPhilippe Reynes mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
382bc8ee596SPhilippe Reynes mii->advertising = tmp;
383bc8ee596SPhilippe Reynes }
384bc8ee596SPhilippe Reynes if ((mii->supports_gmii) && (advert2 != tmp2))
385bc8ee596SPhilippe Reynes mii->mdio_write(dev, mii->phy_id, MII_CTRL1000, tmp2);
386bc8ee596SPhilippe Reynes
387bc8ee596SPhilippe Reynes /* turn on autonegotiation, and force a renegotiate */
388bc8ee596SPhilippe Reynes bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
389bc8ee596SPhilippe Reynes bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
390bc8ee596SPhilippe Reynes mii->mdio_write(dev, mii->phy_id, MII_BMCR, bmcr);
391bc8ee596SPhilippe Reynes
392bc8ee596SPhilippe Reynes mii->force_media = 0;
393bc8ee596SPhilippe Reynes } else {
394bc8ee596SPhilippe Reynes u32 bmcr, tmp;
395bc8ee596SPhilippe Reynes
396bc8ee596SPhilippe Reynes /* turn off auto negotiation, set speed and duplexity */
397bc8ee596SPhilippe Reynes bmcr = mii->mdio_read(dev, mii->phy_id, MII_BMCR);
398bc8ee596SPhilippe Reynes tmp = bmcr & ~(BMCR_ANENABLE | BMCR_SPEED100 |
399bc8ee596SPhilippe Reynes BMCR_SPEED1000 | BMCR_FULLDPLX);
400bc8ee596SPhilippe Reynes if (speed == SPEED_1000)
401bc8ee596SPhilippe Reynes tmp |= BMCR_SPEED1000;
402bc8ee596SPhilippe Reynes else if (speed == SPEED_100)
403bc8ee596SPhilippe Reynes tmp |= BMCR_SPEED100;
404bc8ee596SPhilippe Reynes if (cmd->base.duplex == DUPLEX_FULL) {
405bc8ee596SPhilippe Reynes tmp |= BMCR_FULLDPLX;
406bc8ee596SPhilippe Reynes mii->full_duplex = 1;
407bc8ee596SPhilippe Reynes } else {
408bc8ee596SPhilippe Reynes mii->full_duplex = 0;
409bc8ee596SPhilippe Reynes }
410bc8ee596SPhilippe Reynes if (bmcr != tmp)
411bc8ee596SPhilippe Reynes mii->mdio_write(dev, mii->phy_id, MII_BMCR, tmp);
412bc8ee596SPhilippe Reynes
413bc8ee596SPhilippe Reynes mii->force_media = 1;
414bc8ee596SPhilippe Reynes }
415bc8ee596SPhilippe Reynes return 0;
416bc8ee596SPhilippe Reynes }
417bc8ee596SPhilippe Reynes
418bc8ee596SPhilippe Reynes /**
41932684ec6SRandy Dunlap * mii_check_gmii_support - check if the MII supports Gb interfaces
42032684ec6SRandy Dunlap * @mii: the MII interface
42132684ec6SRandy Dunlap */
mii_check_gmii_support(struct mii_if_info * mii)42243ec6e95SDale Farnsworth int mii_check_gmii_support(struct mii_if_info *mii)
42343ec6e95SDale Farnsworth {
42443ec6e95SDale Farnsworth int reg;
42543ec6e95SDale Farnsworth
42643ec6e95SDale Farnsworth reg = mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
42743ec6e95SDale Farnsworth if (reg & BMSR_ESTATEN) {
42843ec6e95SDale Farnsworth reg = mii->mdio_read(mii->dev, mii->phy_id, MII_ESTATUS);
42943ec6e95SDale Farnsworth if (reg & (ESTATUS_1000_TFULL | ESTATUS_1000_THALF))
43043ec6e95SDale Farnsworth return 1;
43143ec6e95SDale Farnsworth }
43243ec6e95SDale Farnsworth
43343ec6e95SDale Farnsworth return 0;
43443ec6e95SDale Farnsworth }
43543ec6e95SDale Farnsworth
43632684ec6SRandy Dunlap /**
43732684ec6SRandy Dunlap * mii_link_ok - is link status up/ok
43832684ec6SRandy Dunlap * @mii: the MII interface
43932684ec6SRandy Dunlap *
44032684ec6SRandy Dunlap * Returns 1 if the MII reports link status up/ok, 0 otherwise.
44132684ec6SRandy Dunlap */
mii_link_ok(struct mii_if_info * mii)4421da177e4SLinus Torvalds int mii_link_ok (struct mii_if_info *mii)
4431da177e4SLinus Torvalds {
4441da177e4SLinus Torvalds /* first, a dummy read, needed to latch some MII phys */
4451da177e4SLinus Torvalds mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR);
4461da177e4SLinus Torvalds if (mii->mdio_read(mii->dev, mii->phy_id, MII_BMSR) & BMSR_LSTATUS)
4471da177e4SLinus Torvalds return 1;
4481da177e4SLinus Torvalds return 0;
4491da177e4SLinus Torvalds }
4501da177e4SLinus Torvalds
45132684ec6SRandy Dunlap /**
45232684ec6SRandy Dunlap * mii_nway_restart - restart NWay (autonegotiation) for this interface
45332684ec6SRandy Dunlap * @mii: the MII interface
45432684ec6SRandy Dunlap *
45532684ec6SRandy Dunlap * Returns 0 on success, negative on error.
45632684ec6SRandy Dunlap */
mii_nway_restart(struct mii_if_info * mii)4571da177e4SLinus Torvalds int mii_nway_restart (struct mii_if_info *mii)
4581da177e4SLinus Torvalds {
4591da177e4SLinus Torvalds int bmcr;
4601da177e4SLinus Torvalds int r = -EINVAL;
4611da177e4SLinus Torvalds
4621da177e4SLinus Torvalds /* if autoneg is off, it's an error */
4631da177e4SLinus Torvalds bmcr = mii->mdio_read(mii->dev, mii->phy_id, MII_BMCR);
4641da177e4SLinus Torvalds
4651da177e4SLinus Torvalds if (bmcr & BMCR_ANENABLE) {
4661da177e4SLinus Torvalds bmcr |= BMCR_ANRESTART;
4671da177e4SLinus Torvalds mii->mdio_write(mii->dev, mii->phy_id, MII_BMCR, bmcr);
4681da177e4SLinus Torvalds r = 0;
4691da177e4SLinus Torvalds }
4701da177e4SLinus Torvalds
4711da177e4SLinus Torvalds return r;
4721da177e4SLinus Torvalds }
4731da177e4SLinus Torvalds
47432684ec6SRandy Dunlap /**
47532684ec6SRandy Dunlap * mii_check_link - check MII link status
47632684ec6SRandy Dunlap * @mii: MII interface
47732684ec6SRandy Dunlap *
47832684ec6SRandy Dunlap * If the link status changed (previous != current), call
47932684ec6SRandy Dunlap * netif_carrier_on() if current link status is Up or call
48032684ec6SRandy Dunlap * netif_carrier_off() if current link status is Down.
48132684ec6SRandy Dunlap */
mii_check_link(struct mii_if_info * mii)4821da177e4SLinus Torvalds void mii_check_link (struct mii_if_info *mii)
4831da177e4SLinus Torvalds {
4841da177e4SLinus Torvalds int cur_link = mii_link_ok(mii);
4851da177e4SLinus Torvalds int prev_link = netif_carrier_ok(mii->dev);
4861da177e4SLinus Torvalds
4871da177e4SLinus Torvalds if (cur_link && !prev_link)
4881da177e4SLinus Torvalds netif_carrier_on(mii->dev);
4891da177e4SLinus Torvalds else if (prev_link && !cur_link)
4901da177e4SLinus Torvalds netif_carrier_off(mii->dev);
4911da177e4SLinus Torvalds }
4921da177e4SLinus Torvalds
49332684ec6SRandy Dunlap /**
4945bdc7380SBen Hutchings * mii_check_media - check the MII interface for a carrier/speed/duplex change
49532684ec6SRandy Dunlap * @mii: the MII interface
49632684ec6SRandy Dunlap * @ok_to_print: OK to print link up/down messages
49732684ec6SRandy Dunlap * @init_media: OK to save duplex mode in @mii
49832684ec6SRandy Dunlap *
49932684ec6SRandy Dunlap * Returns 1 if the duplex mode changed, 0 if not.
50032684ec6SRandy Dunlap * If the media type is forced, always returns 0.
50132684ec6SRandy Dunlap */
mii_check_media(struct mii_if_info * mii,unsigned int ok_to_print,unsigned int init_media)5021da177e4SLinus Torvalds unsigned int mii_check_media (struct mii_if_info *mii,
5031da177e4SLinus Torvalds unsigned int ok_to_print,
5041da177e4SLinus Torvalds unsigned int init_media)
5051da177e4SLinus Torvalds {
5061da177e4SLinus Torvalds unsigned int old_carrier, new_carrier;
5071da177e4SLinus Torvalds int advertise, lpa, media, duplex;
5081da177e4SLinus Torvalds int lpa2 = 0;
5091da177e4SLinus Torvalds
5101da177e4SLinus Torvalds /* check current and old link status */
5111da177e4SLinus Torvalds old_carrier = netif_carrier_ok(mii->dev) ? 1 : 0;
5121da177e4SLinus Torvalds new_carrier = (unsigned int) mii_link_ok(mii);
5131da177e4SLinus Torvalds
5141da177e4SLinus Torvalds /* if carrier state did not change, this is a "bounce",
5151da177e4SLinus Torvalds * just exit as everything is already set correctly
5161da177e4SLinus Torvalds */
5171da177e4SLinus Torvalds if ((!init_media) && (old_carrier == new_carrier))
5181da177e4SLinus Torvalds return 0; /* duplex did not change */
5191da177e4SLinus Torvalds
5201da177e4SLinus Torvalds /* no carrier, nothing much to do */
5211da177e4SLinus Torvalds if (!new_carrier) {
5221da177e4SLinus Torvalds netif_carrier_off(mii->dev);
5231da177e4SLinus Torvalds if (ok_to_print)
524967faf3bSJoe Perches netdev_info(mii->dev, "link down\n");
5251da177e4SLinus Torvalds return 0; /* duplex did not change */
5261da177e4SLinus Torvalds }
5271da177e4SLinus Torvalds
5281da177e4SLinus Torvalds /*
5291da177e4SLinus Torvalds * we have carrier, see who's on the other end
5301da177e4SLinus Torvalds */
5311da177e4SLinus Torvalds netif_carrier_on(mii->dev);
5321da177e4SLinus Torvalds
5335bdc7380SBen Hutchings if (mii->force_media) {
5345bdc7380SBen Hutchings if (ok_to_print)
5355bdc7380SBen Hutchings netdev_info(mii->dev, "link up\n");
5365bdc7380SBen Hutchings return 0; /* duplex did not change */
5375bdc7380SBen Hutchings }
5385bdc7380SBen Hutchings
5391da177e4SLinus Torvalds /* get MII advertise and LPA values */
5401da177e4SLinus Torvalds if ((!init_media) && (mii->advertising))
5411da177e4SLinus Torvalds advertise = mii->advertising;
5421da177e4SLinus Torvalds else {
5431da177e4SLinus Torvalds advertise = mii->mdio_read(mii->dev, mii->phy_id, MII_ADVERTISE);
5441da177e4SLinus Torvalds mii->advertising = advertise;
5451da177e4SLinus Torvalds }
5461da177e4SLinus Torvalds lpa = mii->mdio_read(mii->dev, mii->phy_id, MII_LPA);
5471da177e4SLinus Torvalds if (mii->supports_gmii)
5481da177e4SLinus Torvalds lpa2 = mii->mdio_read(mii->dev, mii->phy_id, MII_STAT1000);
5491da177e4SLinus Torvalds
5501da177e4SLinus Torvalds /* figure out media and duplex from advertise and LPA values */
5511da177e4SLinus Torvalds media = mii_nway_result(lpa & advertise);
5521da177e4SLinus Torvalds duplex = (media & ADVERTISE_FULL) ? 1 : 0;
5531da177e4SLinus Torvalds if (lpa2 & LPA_1000FULL)
5541da177e4SLinus Torvalds duplex = 1;
5551da177e4SLinus Torvalds
5561da177e4SLinus Torvalds if (ok_to_print)
557967faf3bSJoe Perches netdev_info(mii->dev, "link up, %uMbps, %s-duplex, lpa 0x%04X\n",
558967faf3bSJoe Perches lpa2 & (LPA_1000FULL | LPA_1000HALF) ? 1000 :
559967faf3bSJoe Perches media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ?
560967faf3bSJoe Perches 100 : 10,
5611da177e4SLinus Torvalds duplex ? "full" : "half",
5621da177e4SLinus Torvalds lpa);
5631da177e4SLinus Torvalds
5641da177e4SLinus Torvalds if ((init_media) || (mii->full_duplex != duplex)) {
5651da177e4SLinus Torvalds mii->full_duplex = duplex;
5661da177e4SLinus Torvalds return 1; /* duplex changed */
5671da177e4SLinus Torvalds }
5681da177e4SLinus Torvalds
5691da177e4SLinus Torvalds return 0; /* duplex did not change */
5701da177e4SLinus Torvalds }
5711da177e4SLinus Torvalds
57232684ec6SRandy Dunlap /**
57332684ec6SRandy Dunlap * generic_mii_ioctl - main MII ioctl interface
57432684ec6SRandy Dunlap * @mii_if: the MII interface
57532684ec6SRandy Dunlap * @mii_data: MII ioctl data structure
57632684ec6SRandy Dunlap * @cmd: MII ioctl command
57732684ec6SRandy Dunlap * @duplex_chg_out: pointer to @duplex_changed status if there was no
57832684ec6SRandy Dunlap * ioctl error
57932684ec6SRandy Dunlap *
58032684ec6SRandy Dunlap * Returns 0 on success, negative on error.
58132684ec6SRandy Dunlap */
generic_mii_ioctl(struct mii_if_info * mii_if,struct mii_ioctl_data * mii_data,int cmd,unsigned int * duplex_chg_out)5821da177e4SLinus Torvalds int generic_mii_ioctl(struct mii_if_info *mii_if,
5831da177e4SLinus Torvalds struct mii_ioctl_data *mii_data, int cmd,
5841da177e4SLinus Torvalds unsigned int *duplex_chg_out)
5851da177e4SLinus Torvalds {
5861da177e4SLinus Torvalds int rc = 0;
5871da177e4SLinus Torvalds unsigned int duplex_changed = 0;
5881da177e4SLinus Torvalds
5891da177e4SLinus Torvalds if (duplex_chg_out)
5901da177e4SLinus Torvalds *duplex_chg_out = 0;
5911da177e4SLinus Torvalds
5921da177e4SLinus Torvalds mii_data->phy_id &= mii_if->phy_id_mask;
5931da177e4SLinus Torvalds mii_data->reg_num &= mii_if->reg_num_mask;
5941da177e4SLinus Torvalds
5951da177e4SLinus Torvalds switch(cmd) {
5961da177e4SLinus Torvalds case SIOCGMIIPHY:
5971da177e4SLinus Torvalds mii_data->phy_id = mii_if->phy_id;
598df561f66SGustavo A. R. Silva fallthrough;
5991da177e4SLinus Torvalds
6001da177e4SLinus Torvalds case SIOCGMIIREG:
6011da177e4SLinus Torvalds mii_data->val_out =
6021da177e4SLinus Torvalds mii_if->mdio_read(mii_if->dev, mii_data->phy_id,
6031da177e4SLinus Torvalds mii_data->reg_num);
6041da177e4SLinus Torvalds break;
6051da177e4SLinus Torvalds
6061da177e4SLinus Torvalds case SIOCSMIIREG: {
6071da177e4SLinus Torvalds u16 val = mii_data->val_in;
6081da177e4SLinus Torvalds
6091da177e4SLinus Torvalds if (mii_data->phy_id == mii_if->phy_id) {
6101da177e4SLinus Torvalds switch(mii_data->reg_num) {
6111da177e4SLinus Torvalds case MII_BMCR: {
6121da177e4SLinus Torvalds unsigned int new_duplex = 0;
6131da177e4SLinus Torvalds if (val & (BMCR_RESET|BMCR_ANENABLE))
6141da177e4SLinus Torvalds mii_if->force_media = 0;
6151da177e4SLinus Torvalds else
6161da177e4SLinus Torvalds mii_if->force_media = 1;
6171da177e4SLinus Torvalds if (mii_if->force_media &&
6181da177e4SLinus Torvalds (val & BMCR_FULLDPLX))
6191da177e4SLinus Torvalds new_duplex = 1;
6201da177e4SLinus Torvalds if (mii_if->full_duplex != new_duplex) {
6211da177e4SLinus Torvalds duplex_changed = 1;
6221da177e4SLinus Torvalds mii_if->full_duplex = new_duplex;
6231da177e4SLinus Torvalds }
6241da177e4SLinus Torvalds break;
6251da177e4SLinus Torvalds }
6261da177e4SLinus Torvalds case MII_ADVERTISE:
6271da177e4SLinus Torvalds mii_if->advertising = val;
6281da177e4SLinus Torvalds break;
6291da177e4SLinus Torvalds default:
6301da177e4SLinus Torvalds /* do nothing */
6311da177e4SLinus Torvalds break;
6321da177e4SLinus Torvalds }
6331da177e4SLinus Torvalds }
6341da177e4SLinus Torvalds
6351da177e4SLinus Torvalds mii_if->mdio_write(mii_if->dev, mii_data->phy_id,
6361da177e4SLinus Torvalds mii_data->reg_num, val);
6371da177e4SLinus Torvalds break;
6381da177e4SLinus Torvalds }
6391da177e4SLinus Torvalds
6401da177e4SLinus Torvalds default:
6411da177e4SLinus Torvalds rc = -EOPNOTSUPP;
6421da177e4SLinus Torvalds break;
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds
6451da177e4SLinus Torvalds if ((rc == 0) && (duplex_chg_out) && (duplex_changed))
6461da177e4SLinus Torvalds *duplex_chg_out = 1;
6471da177e4SLinus Torvalds
6481da177e4SLinus Torvalds return rc;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds
6511da177e4SLinus Torvalds MODULE_AUTHOR ("Jeff Garzik <jgarzik@pobox.com>");
6521da177e4SLinus Torvalds MODULE_DESCRIPTION ("MII hardware support library");
6531da177e4SLinus Torvalds MODULE_LICENSE("GPL");
6541da177e4SLinus Torvalds
6551da177e4SLinus Torvalds EXPORT_SYMBOL(mii_link_ok);
6561da177e4SLinus Torvalds EXPORT_SYMBOL(mii_nway_restart);
6571da177e4SLinus Torvalds EXPORT_SYMBOL(mii_ethtool_gset);
658bc8ee596SPhilippe Reynes EXPORT_SYMBOL(mii_ethtool_get_link_ksettings);
6591da177e4SLinus Torvalds EXPORT_SYMBOL(mii_ethtool_sset);
660bc8ee596SPhilippe Reynes EXPORT_SYMBOL(mii_ethtool_set_link_ksettings);
6611da177e4SLinus Torvalds EXPORT_SYMBOL(mii_check_link);
6621da177e4SLinus Torvalds EXPORT_SYMBOL(mii_check_media);
66343ec6e95SDale Farnsworth EXPORT_SYMBOL(mii_check_gmii_support);
6641da177e4SLinus Torvalds EXPORT_SYMBOL(generic_mii_ioctl);
6651da177e4SLinus Torvalds
666