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