Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.28-rc3 473 lines 11 kB view raw
1/* 2 * drivers/net/phy/marvell.c 3 * 4 * Driver for Marvell PHYs 5 * 6 * Author: Andy Fleming 7 * 8 * Copyright (c) 2004 Freescale Semiconductor, Inc. 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU General Public License as published by the 12 * Free Software Foundation; either version 2 of the License, or (at your 13 * option) any later version. 14 * 15 */ 16#include <linux/kernel.h> 17#include <linux/string.h> 18#include <linux/errno.h> 19#include <linux/unistd.h> 20#include <linux/slab.h> 21#include <linux/interrupt.h> 22#include <linux/init.h> 23#include <linux/delay.h> 24#include <linux/netdevice.h> 25#include <linux/etherdevice.h> 26#include <linux/skbuff.h> 27#include <linux/spinlock.h> 28#include <linux/mm.h> 29#include <linux/module.h> 30#include <linux/mii.h> 31#include <linux/ethtool.h> 32#include <linux/phy.h> 33 34#include <asm/io.h> 35#include <asm/irq.h> 36#include <asm/uaccess.h> 37 38#define MII_M1011_IEVENT 0x13 39#define MII_M1011_IEVENT_CLEAR 0x0000 40 41#define MII_M1011_IMASK 0x12 42#define MII_M1011_IMASK_INIT 0x6400 43#define MII_M1011_IMASK_CLEAR 0x0000 44 45#define MII_M1011_PHY_SCR 0x10 46#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 47 48#define MII_M1145_PHY_EXT_CR 0x14 49#define MII_M1145_RGMII_RX_DELAY 0x0080 50#define MII_M1145_RGMII_TX_DELAY 0x0002 51 52#define M1145_DEV_FLAGS_RESISTANCE 0x00000001 53 54#define MII_M1111_PHY_LED_CONTROL 0x18 55#define MII_M1111_PHY_LED_DIRECT 0x4100 56#define MII_M1111_PHY_LED_COMBINE 0x411c 57#define MII_M1111_PHY_EXT_CR 0x14 58#define MII_M1111_RX_DELAY 0x80 59#define MII_M1111_TX_DELAY 0x2 60#define MII_M1111_PHY_EXT_SR 0x1b 61 62#define MII_M1111_HWCFG_MODE_MASK 0xf 63#define MII_M1111_HWCFG_MODE_COPPER_RGMII 0xb 64#define MII_M1111_HWCFG_MODE_FIBER_RGMII 0x3 65#define MII_M1111_HWCFG_MODE_SGMII_NO_CLK 0x4 66#define MII_M1111_HWCFG_FIBER_COPPER_AUTO 0x8000 67#define MII_M1111_HWCFG_FIBER_COPPER_RES 0x2000 68 69#define MII_M1111_COPPER 0 70#define MII_M1111_FIBER 1 71 72#define MII_M1011_PHY_STATUS 0x11 73#define MII_M1011_PHY_STATUS_1000 0x8000 74#define MII_M1011_PHY_STATUS_100 0x4000 75#define MII_M1011_PHY_STATUS_SPD_MASK 0xc000 76#define MII_M1011_PHY_STATUS_FULLDUPLEX 0x2000 77#define MII_M1011_PHY_STATUS_RESOLVED 0x0800 78#define MII_M1011_PHY_STATUS_LINK 0x0400 79 80 81MODULE_DESCRIPTION("Marvell PHY driver"); 82MODULE_AUTHOR("Andy Fleming"); 83MODULE_LICENSE("GPL"); 84 85static int marvell_ack_interrupt(struct phy_device *phydev) 86{ 87 int err; 88 89 /* Clear the interrupts by reading the reg */ 90 err = phy_read(phydev, MII_M1011_IEVENT); 91 92 if (err < 0) 93 return err; 94 95 return 0; 96} 97 98static int marvell_config_intr(struct phy_device *phydev) 99{ 100 int err; 101 102 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 103 err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); 104 else 105 err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); 106 107 return err; 108} 109 110static int marvell_config_aneg(struct phy_device *phydev) 111{ 112 int err; 113 114 /* The Marvell PHY has an errata which requires 115 * that certain registers get written in order 116 * to restart autonegotiation */ 117 err = phy_write(phydev, MII_BMCR, BMCR_RESET); 118 119 if (err < 0) 120 return err; 121 122 err = phy_write(phydev, 0x1d, 0x1f); 123 if (err < 0) 124 return err; 125 126 err = phy_write(phydev, 0x1e, 0x200c); 127 if (err < 0) 128 return err; 129 130 err = phy_write(phydev, 0x1d, 0x5); 131 if (err < 0) 132 return err; 133 134 err = phy_write(phydev, 0x1e, 0); 135 if (err < 0) 136 return err; 137 138 err = phy_write(phydev, 0x1e, 0x100); 139 if (err < 0) 140 return err; 141 142 err = phy_write(phydev, MII_M1011_PHY_SCR, 143 MII_M1011_PHY_SCR_AUTO_CROSS); 144 if (err < 0) 145 return err; 146 147 err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, 148 MII_M1111_PHY_LED_DIRECT); 149 if (err < 0) 150 return err; 151 152 err = genphy_config_aneg(phydev); 153 154 return err; 155} 156 157static int m88e1111_config_init(struct phy_device *phydev) 158{ 159 int err; 160 int temp; 161 162 /* Enable Fiber/Copper auto selection */ 163 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 164 temp &= ~MII_M1111_HWCFG_FIBER_COPPER_AUTO; 165 phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 166 167 temp = phy_read(phydev, MII_BMCR); 168 temp |= BMCR_RESET; 169 phy_write(phydev, MII_BMCR, temp); 170 171 if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) || 172 (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) || 173 (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) || 174 (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) { 175 176 temp = phy_read(phydev, MII_M1111_PHY_EXT_CR); 177 if (temp < 0) 178 return temp; 179 180 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 181 temp |= (MII_M1111_RX_DELAY | MII_M1111_TX_DELAY); 182 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) { 183 temp &= ~MII_M1111_TX_DELAY; 184 temp |= MII_M1111_RX_DELAY; 185 } else if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) { 186 temp &= ~MII_M1111_RX_DELAY; 187 temp |= MII_M1111_TX_DELAY; 188 } 189 190 err = phy_write(phydev, MII_M1111_PHY_EXT_CR, temp); 191 if (err < 0) 192 return err; 193 194 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 195 if (temp < 0) 196 return temp; 197 198 temp &= ~(MII_M1111_HWCFG_MODE_MASK); 199 200 if (temp & MII_M1111_HWCFG_FIBER_COPPER_RES) 201 temp |= MII_M1111_HWCFG_MODE_FIBER_RGMII; 202 else 203 temp |= MII_M1111_HWCFG_MODE_COPPER_RGMII; 204 205 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 206 if (err < 0) 207 return err; 208 } 209 210 if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { 211 temp = phy_read(phydev, MII_M1111_PHY_EXT_SR); 212 if (temp < 0) 213 return temp; 214 215 temp &= ~(MII_M1111_HWCFG_MODE_MASK); 216 temp |= MII_M1111_HWCFG_MODE_SGMII_NO_CLK; 217 218 err = phy_write(phydev, MII_M1111_PHY_EXT_SR, temp); 219 if (err < 0) 220 return err; 221 } 222 223 err = phy_write(phydev, MII_BMCR, BMCR_RESET); 224 if (err < 0) 225 return err; 226 227 return 0; 228} 229 230static int m88e1145_config_init(struct phy_device *phydev) 231{ 232 int err; 233 234 /* Take care of errata E0 & E1 */ 235 err = phy_write(phydev, 0x1d, 0x001b); 236 if (err < 0) 237 return err; 238 239 err = phy_write(phydev, 0x1e, 0x418f); 240 if (err < 0) 241 return err; 242 243 err = phy_write(phydev, 0x1d, 0x0016); 244 if (err < 0) 245 return err; 246 247 err = phy_write(phydev, 0x1e, 0xa2da); 248 if (err < 0) 249 return err; 250 251 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) { 252 int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); 253 if (temp < 0) 254 return temp; 255 256 temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); 257 258 err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); 259 if (err < 0) 260 return err; 261 262 if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) { 263 err = phy_write(phydev, 0x1d, 0x0012); 264 if (err < 0) 265 return err; 266 267 temp = phy_read(phydev, 0x1e); 268 if (temp < 0) 269 return temp; 270 271 temp &= 0xf03f; 272 temp |= 2 << 9; /* 36 ohm */ 273 temp |= 2 << 6; /* 39 ohm */ 274 275 err = phy_write(phydev, 0x1e, temp); 276 if (err < 0) 277 return err; 278 279 err = phy_write(phydev, 0x1d, 0x3); 280 if (err < 0) 281 return err; 282 283 err = phy_write(phydev, 0x1e, 0x8000); 284 if (err < 0) 285 return err; 286 } 287 } 288 289 return 0; 290} 291 292/* marvell_read_status 293 * 294 * Generic status code does not detect Fiber correctly! 295 * Description: 296 * Check the link, then figure out the current state 297 * by comparing what we advertise with what the link partner 298 * advertises. Start by checking the gigabit possibilities, 299 * then move on to 10/100. 300 */ 301static int marvell_read_status(struct phy_device *phydev) 302{ 303 int adv; 304 int err; 305 int lpa; 306 int status = 0; 307 308 /* Update the link, but return if there 309 * was an error */ 310 err = genphy_update_link(phydev); 311 if (err) 312 return err; 313 314 if (AUTONEG_ENABLE == phydev->autoneg) { 315 status = phy_read(phydev, MII_M1011_PHY_STATUS); 316 if (status < 0) 317 return status; 318 319 lpa = phy_read(phydev, MII_LPA); 320 if (lpa < 0) 321 return lpa; 322 323 adv = phy_read(phydev, MII_ADVERTISE); 324 if (adv < 0) 325 return adv; 326 327 lpa &= adv; 328 329 if (status & MII_M1011_PHY_STATUS_FULLDUPLEX) 330 phydev->duplex = DUPLEX_FULL; 331 else 332 phydev->duplex = DUPLEX_HALF; 333 334 status = status & MII_M1011_PHY_STATUS_SPD_MASK; 335 phydev->pause = phydev->asym_pause = 0; 336 337 switch (status) { 338 case MII_M1011_PHY_STATUS_1000: 339 phydev->speed = SPEED_1000; 340 break; 341 342 case MII_M1011_PHY_STATUS_100: 343 phydev->speed = SPEED_100; 344 break; 345 346 default: 347 phydev->speed = SPEED_10; 348 break; 349 } 350 351 if (phydev->duplex == DUPLEX_FULL) { 352 phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0; 353 phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0; 354 } 355 } else { 356 int bmcr = phy_read(phydev, MII_BMCR); 357 358 if (bmcr < 0) 359 return bmcr; 360 361 if (bmcr & BMCR_FULLDPLX) 362 phydev->duplex = DUPLEX_FULL; 363 else 364 phydev->duplex = DUPLEX_HALF; 365 366 if (bmcr & BMCR_SPEED1000) 367 phydev->speed = SPEED_1000; 368 else if (bmcr & BMCR_SPEED100) 369 phydev->speed = SPEED_100; 370 else 371 phydev->speed = SPEED_10; 372 373 phydev->pause = phydev->asym_pause = 0; 374 } 375 376 return 0; 377} 378 379static struct phy_driver marvell_drivers[] = { 380 { 381 .phy_id = 0x01410c60, 382 .phy_id_mask = 0xfffffff0, 383 .name = "Marvell 88E1101", 384 .features = PHY_GBIT_FEATURES, 385 .flags = PHY_HAS_INTERRUPT, 386 .config_aneg = &marvell_config_aneg, 387 .read_status = &genphy_read_status, 388 .ack_interrupt = &marvell_ack_interrupt, 389 .config_intr = &marvell_config_intr, 390 .driver = { .owner = THIS_MODULE }, 391 }, 392 { 393 .phy_id = 0x01410c90, 394 .phy_id_mask = 0xfffffff0, 395 .name = "Marvell 88E1112", 396 .features = PHY_GBIT_FEATURES, 397 .flags = PHY_HAS_INTERRUPT, 398 .config_init = &m88e1111_config_init, 399 .config_aneg = &marvell_config_aneg, 400 .read_status = &genphy_read_status, 401 .ack_interrupt = &marvell_ack_interrupt, 402 .config_intr = &marvell_config_intr, 403 .driver = { .owner = THIS_MODULE }, 404 }, 405 { 406 .phy_id = 0x01410cc0, 407 .phy_id_mask = 0xfffffff0, 408 .name = "Marvell 88E1111", 409 .features = PHY_GBIT_FEATURES, 410 .flags = PHY_HAS_INTERRUPT, 411 .config_init = &m88e1111_config_init, 412 .config_aneg = &marvell_config_aneg, 413 .read_status = &marvell_read_status, 414 .ack_interrupt = &marvell_ack_interrupt, 415 .config_intr = &marvell_config_intr, 416 .driver = { .owner = THIS_MODULE }, 417 }, 418 { 419 .phy_id = 0x01410cd0, 420 .phy_id_mask = 0xfffffff0, 421 .name = "Marvell 88E1145", 422 .features = PHY_GBIT_FEATURES, 423 .flags = PHY_HAS_INTERRUPT, 424 .config_init = &m88e1145_config_init, 425 .config_aneg = &marvell_config_aneg, 426 .read_status = &genphy_read_status, 427 .ack_interrupt = &marvell_ack_interrupt, 428 .config_intr = &marvell_config_intr, 429 .driver = { .owner = THIS_MODULE }, 430 }, 431 { 432 .phy_id = 0x01410e30, 433 .phy_id_mask = 0xfffffff0, 434 .name = "Marvell 88E1240", 435 .features = PHY_GBIT_FEATURES, 436 .flags = PHY_HAS_INTERRUPT, 437 .config_init = &m88e1111_config_init, 438 .config_aneg = &marvell_config_aneg, 439 .read_status = &genphy_read_status, 440 .ack_interrupt = &marvell_ack_interrupt, 441 .config_intr = &marvell_config_intr, 442 .driver = { .owner = THIS_MODULE }, 443 }, 444}; 445 446static int __init marvell_init(void) 447{ 448 int ret; 449 int i; 450 451 for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) { 452 ret = phy_driver_register(&marvell_drivers[i]); 453 454 if (ret) { 455 while (i-- > 0) 456 phy_driver_unregister(&marvell_drivers[i]); 457 return ret; 458 } 459 } 460 461 return 0; 462} 463 464static void __exit marvell_exit(void) 465{ 466 int i; 467 468 for (i = 0; i < ARRAY_SIZE(marvell_drivers); i++) 469 phy_driver_unregister(&marvell_drivers[i]); 470} 471 472module_init(marvell_init); 473module_exit(marvell_exit);