tg3: Add mdio bus registration

This patch introduces code to register and unregister the tg3 mdio bus
with the system.

Signed-off-by: Matt Carlson <mcarlson@broadcom.com>
Signed-off-by: Michael Chan <mchan@broadcom.com>
Signed-off-by: Benjamin Li <benli@broadcom.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Matt Carlson 2008-05-29 01:37:54 -07:00 committed by David S. Miller
parent dd47700310
commit 158d7abdae
3 changed files with 127 additions and 7 deletions

View File

@ -2228,6 +2228,7 @@ config VIA_VELOCITY
config TIGON3
tristate "Broadcom Tigon3 support"
depends on PCI
select PHYLIB
help
This driver supports Broadcom Tigon3 based gigabit Ethernet cards.

View File

@ -32,6 +32,7 @@
#include <linux/skbuff.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <linux/if_vlan.h>
#include <linux/ip.h>
#include <linux/tcp.h>
@ -835,6 +836,115 @@ static int tg3_bmcr_reset(struct tg3 *tp)
return 0;
}
static int tg3_mdio_read(struct mii_bus *bp, int mii_id, int reg)
{
struct tg3 *tp = (struct tg3 *)bp->priv;
u32 val;
if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_PAUSED)
return -EAGAIN;
if (tg3_readphy(tp, reg, &val))
return -EIO;
return val;
}
static int tg3_mdio_write(struct mii_bus *bp, int mii_id, int reg, u16 val)
{
struct tg3 *tp = (struct tg3 *)bp->priv;
if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_PAUSED)
return -EAGAIN;
if (tg3_writephy(tp, reg, val))
return -EIO;
return 0;
}
static int tg3_mdio_reset(struct mii_bus *bp)
{
return 0;
}
static void tg3_mdio_start(struct tg3 *tp)
{
if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) {
mutex_lock(&tp->mdio_bus.mdio_lock);
tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED;
mutex_unlock(&tp->mdio_bus.mdio_lock);
}
tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL;
tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
}
static void tg3_mdio_stop(struct tg3 *tp)
{
if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) {
mutex_lock(&tp->mdio_bus.mdio_lock);
tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_PAUSED;
mutex_unlock(&tp->mdio_bus.mdio_lock);
}
}
static int tg3_mdio_init(struct tg3 *tp)
{
int i;
u32 reg;
struct mii_bus *mdio_bus = &tp->mdio_bus;
tg3_mdio_start(tp);
if (!(tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB) ||
(tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED))
return 0;
memset(mdio_bus, 0, sizeof(*mdio_bus));
mdio_bus->name = "tg3 mdio bus";
snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%x",
(tp->pdev->bus->number << 8) | tp->pdev->devfn);
mdio_bus->priv = tp;
mdio_bus->dev = &tp->pdev->dev;
mdio_bus->read = &tg3_mdio_read;
mdio_bus->write = &tg3_mdio_write;
mdio_bus->reset = &tg3_mdio_reset;
mdio_bus->phy_mask = ~(1 << PHY_ADDR);
mdio_bus->irq = &tp->mdio_irq[0];
for (i = 0; i < PHY_MAX_ADDR; i++)
mdio_bus->irq[i] = PHY_POLL;
/* The bus registration will look for all the PHYs on the mdio bus.
* Unfortunately, it does not ensure the PHY is powered up before
* accessing the PHY ID registers. A chip reset is the
* quickest way to bring the device back to an operational state..
*/
if (tg3_readphy(tp, MII_BMCR, &reg) || (reg & BMCR_PDOWN))
tg3_bmcr_reset(tp);
i = mdiobus_register(mdio_bus);
if (!i)
tp->tg3_flags3 |= TG3_FLG3_MDIOBUS_INITED;
else
printk(KERN_WARNING "%s: mdiobus_reg failed (0x%x)\n",
tp->dev->name, i);
return i;
}
static void tg3_mdio_fini(struct tg3 *tp)
{
if (tp->tg3_flags3 & TG3_FLG3_MDIOBUS_INITED) {
tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_INITED;
mdiobus_unregister(&tp->mdio_bus);
tp->tg3_flags3 &= ~TG3_FLG3_MDIOBUS_PAUSED;
}
}
/* tp->lock is held. */
static void tg3_wait_for_event_ack(struct tg3 *tp)
{
@ -5386,6 +5496,8 @@ static int tg3_chip_reset(struct tg3 *tp)
tg3_nvram_lock(tp);
tg3_mdio_stop(tp);
/* No matching tg3_nvram_unlock() after this because
* chip reset below will undo the nvram lock.
*/
@ -5537,6 +5649,8 @@ static int tg3_chip_reset(struct tg3 *tp)
tw32_f(MAC_MODE, 0);
udelay(40);
tg3_mdio_start(tp);
err = tg3_poll_fw(tp);
if (err)
return err;
@ -7168,10 +7282,6 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
tw32_f(MAC_RX_MODE, tp->rx_mode);
udelay(10);
tp->mi_mode &= ~MAC_MI_MODE_AUTO_POLL;
tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
tw32(MAC_LED_CTRL, tp->led_ctrl);
tw32(MAC_MI_STAT, MAC_MI_STAT_LNKSTAT_ATTN_ENAB);
@ -11850,9 +11960,9 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_CHIP_REV(tp->pci_chip_rev_id) != CHIPREV_5700_BX)
tp->coalesce_mode |= HOSTCC_MODE_32BYTE;
/* Initialize MAC MI mode, polling disabled. */
tw32_f(MAC_MI_MODE, tp->mi_mode);
udelay(80);
err = tg3_mdio_init(tp);
if (err)
return err;
/* Initialize data/descriptor byte/word swapping. */
val = tr32(GRC_MODE);
@ -13052,6 +13162,10 @@ static void __devexit tg3_remove_one(struct pci_dev *pdev)
struct tg3 *tp = netdev_priv(dev);
flush_scheduled_work();
if (tp->tg3_flags3 & TG3_FLG3_USE_PHYLIB)
tg3_mdio_fini(tp);
unregister_netdev(dev);
if (tp->aperegs) {
iounmap(tp->aperegs);

View File

@ -2481,6 +2481,8 @@ struct tg3 {
#define TG3_FLG3_5761_5784_AX_FIXES 0x00000004
#define TG3_FLG3_5701_DMA_BUG 0x00000008
#define TG3_FLG3_USE_PHYLIB 0x00000010
#define TG3_FLG3_MDIOBUS_INITED 0x00000020
#define TG3_FLG3_MDIOBUS_PAUSED 0x00000040
struct timer_list timer;
u16 timer_counter;
@ -2521,6 +2523,9 @@ struct tg3 {
int msi_cap;
int pcix_cap;
struct mii_bus mdio_bus;
int mdio_irq[PHY_MAX_ADDR];
/* PHY info */
u32 phy_id;
#define PHY_ID_MASK 0xfffffff0