Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: drivers/infiniband/core/iwcm.c drivers/net/chelsio/cxgb2.c drivers/net/wireless/bcm43xx/bcm43xx_main.c drivers/net/wireless/prism54/islpci_eth.c drivers/usb/core/hub.h drivers/usb/input/hid-core.c net/core/netpoll.c Fix up merge failures with Linus's head and fix new compilation failures. Signed-Off-By: David Howells <dhowells@redhat.com>
This commit is contained in:
@@ -56,13 +56,19 @@ config SMSC_PHY
|
||||
---help---
|
||||
Currently supports the LAN83C185 PHY
|
||||
|
||||
config BROADCOM_PHY
|
||||
tristate "Drivers for Broadcom PHYs"
|
||||
depends on PHYLIB
|
||||
---help---
|
||||
Currently supports the BCM5411, BCM5421 and BCM5461 PHYs.
|
||||
|
||||
config FIXED_PHY
|
||||
tristate "Drivers for PHY emulation on fixed speed/link"
|
||||
depends on PHYLIB
|
||||
---help---
|
||||
Adds the driver to PHY layer to cover the boards that do not have any PHY bound,
|
||||
but with the ability to manipulate with speed/link in software. The relavant MII
|
||||
speed/duplex parameters could be effectively handled in user-specified fuction.
|
||||
but with the ability to manipulate the speed/link in software. The relevant MII
|
||||
speed/duplex parameters could be effectively handled in a user-specified function.
|
||||
Currently tested with mpc866ads.
|
||||
|
||||
config FIXED_MII_10_FDX
|
||||
|
@@ -10,4 +10,5 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
|
||||
obj-$(CONFIG_QSEMI_PHY) += qsemi.o
|
||||
obj-$(CONFIG_SMSC_PHY) += smsc.o
|
||||
obj-$(CONFIG_VITESSE_PHY) += vitesse.o
|
||||
obj-$(CONFIG_BROADCOM_PHY) += broadcom.o
|
||||
obj-$(CONFIG_FIXED_PHY) += fixed.o
|
||||
|
175
drivers/net/phy/broadcom.c
Normal file
175
drivers/net/phy/broadcom.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* drivers/net/phy/broadcom.c
|
||||
*
|
||||
* Broadcom BCM5411, BCM5421 and BCM5461 Gigabit Ethernet
|
||||
* transceivers.
|
||||
*
|
||||
* Copyright (c) 2006 Maciej W. Rozycki
|
||||
*
|
||||
* Inspired by code written by Amy Fong.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#define MII_BCM54XX_ECR 0x10 /* BCM54xx extended control register */
|
||||
#define MII_BCM54XX_ECR_IM 0x1000 /* Interrupt mask */
|
||||
#define MII_BCM54XX_ECR_IF 0x0800 /* Interrupt force */
|
||||
|
||||
#define MII_BCM54XX_ESR 0x11 /* BCM54xx extended status register */
|
||||
#define MII_BCM54XX_ESR_IS 0x1000 /* Interrupt status */
|
||||
|
||||
#define MII_BCM54XX_ISR 0x1a /* BCM54xx interrupt status register */
|
||||
#define MII_BCM54XX_IMR 0x1b /* BCM54xx interrupt mask register */
|
||||
#define MII_BCM54XX_INT_CRCERR 0x0001 /* CRC error */
|
||||
#define MII_BCM54XX_INT_LINK 0x0002 /* Link status changed */
|
||||
#define MII_BCM54XX_INT_SPEED 0x0004 /* Link speed change */
|
||||
#define MII_BCM54XX_INT_DUPLEX 0x0008 /* Duplex mode changed */
|
||||
#define MII_BCM54XX_INT_LRS 0x0010 /* Local receiver status changed */
|
||||
#define MII_BCM54XX_INT_RRS 0x0020 /* Remote receiver status changed */
|
||||
#define MII_BCM54XX_INT_SSERR 0x0040 /* Scrambler synchronization error */
|
||||
#define MII_BCM54XX_INT_UHCD 0x0080 /* Unsupported HCD negotiated */
|
||||
#define MII_BCM54XX_INT_NHCD 0x0100 /* No HCD */
|
||||
#define MII_BCM54XX_INT_NHCDL 0x0200 /* No HCD link */
|
||||
#define MII_BCM54XX_INT_ANPR 0x0400 /* Auto-negotiation page received */
|
||||
#define MII_BCM54XX_INT_LC 0x0800 /* All counters below 128 */
|
||||
#define MII_BCM54XX_INT_HC 0x1000 /* Counter above 32768 */
|
||||
#define MII_BCM54XX_INT_MDIX 0x2000 /* MDIX status change */
|
||||
#define MII_BCM54XX_INT_PSERR 0x4000 /* Pair swap error */
|
||||
|
||||
MODULE_DESCRIPTION("Broadcom PHY driver");
|
||||
MODULE_AUTHOR("Maciej W. Rozycki");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static int bcm54xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int reg, err;
|
||||
|
||||
reg = phy_read(phydev, MII_BCM54XX_ECR);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
/* Mask interrupts globally. */
|
||||
reg |= MII_BCM54XX_ECR_IM;
|
||||
err = phy_write(phydev, MII_BCM54XX_ECR, reg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Unmask events we are interested in. */
|
||||
reg = ~(MII_BCM54XX_INT_DUPLEX |
|
||||
MII_BCM54XX_INT_SPEED |
|
||||
MII_BCM54XX_INT_LINK);
|
||||
err = phy_write(phydev, MII_BCM54XX_IMR, reg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm54xx_ack_interrupt(struct phy_device *phydev)
|
||||
{
|
||||
int reg;
|
||||
|
||||
/* Clear pending interrupts. */
|
||||
reg = phy_read(phydev, MII_BCM54XX_ISR);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm54xx_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
int reg, err;
|
||||
|
||||
reg = phy_read(phydev, MII_BCM54XX_ECR);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
|
||||
reg &= ~MII_BCM54XX_ECR_IM;
|
||||
else
|
||||
reg |= MII_BCM54XX_ECR_IM;
|
||||
|
||||
err = phy_write(phydev, MII_BCM54XX_ECR, reg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct phy_driver bcm5411_driver = {
|
||||
.phy_id = 0x00206070,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM5411",
|
||||
.features = PHY_GBIT_FEATURES,
|
||||
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
|
||||
.config_init = bcm54xx_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.ack_interrupt = bcm54xx_ack_interrupt,
|
||||
.config_intr = bcm54xx_config_intr,
|
||||
.driver = { .owner = THIS_MODULE },
|
||||
};
|
||||
|
||||
static struct phy_driver bcm5421_driver = {
|
||||
.phy_id = 0x002060e0,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM5421",
|
||||
.features = PHY_GBIT_FEATURES,
|
||||
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
|
||||
.config_init = bcm54xx_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.ack_interrupt = bcm54xx_ack_interrupt,
|
||||
.config_intr = bcm54xx_config_intr,
|
||||
.driver = { .owner = THIS_MODULE },
|
||||
};
|
||||
|
||||
static struct phy_driver bcm5461_driver = {
|
||||
.phy_id = 0x002060c0,
|
||||
.phy_id_mask = 0xfffffff0,
|
||||
.name = "Broadcom BCM5461",
|
||||
.features = PHY_GBIT_FEATURES,
|
||||
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
|
||||
.config_init = bcm54xx_config_init,
|
||||
.config_aneg = genphy_config_aneg,
|
||||
.read_status = genphy_read_status,
|
||||
.ack_interrupt = bcm54xx_ack_interrupt,
|
||||
.config_intr = bcm54xx_config_intr,
|
||||
.driver = { .owner = THIS_MODULE },
|
||||
};
|
||||
|
||||
static int __init broadcom_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = phy_driver_register(&bcm5411_driver);
|
||||
if (ret)
|
||||
goto out_5411;
|
||||
ret = phy_driver_register(&bcm5421_driver);
|
||||
if (ret)
|
||||
goto out_5421;
|
||||
ret = phy_driver_register(&bcm5461_driver);
|
||||
if (ret)
|
||||
goto out_5461;
|
||||
return ret;
|
||||
|
||||
out_5461:
|
||||
phy_driver_unregister(&bcm5421_driver);
|
||||
out_5421:
|
||||
phy_driver_unregister(&bcm5411_driver);
|
||||
out_5411:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit broadcom_exit(void)
|
||||
{
|
||||
phy_driver_unregister(&bcm5461_driver);
|
||||
phy_driver_unregister(&bcm5421_driver);
|
||||
phy_driver_unregister(&bcm5411_driver);
|
||||
}
|
||||
|
||||
module_init(broadcom_init);
|
||||
module_exit(broadcom_exit);
|
@@ -7,6 +7,7 @@
|
||||
* Author: Andy Fleming
|
||||
*
|
||||
* Copyright (c) 2004 Freescale Semiconductor, Inc.
|
||||
* Copyright (c) 2006 Maciej W. Rozycki
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
@@ -32,6 +33,8 @@
|
||||
#include <linux/mii.h>
|
||||
#include <linux/ethtool.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/irq.h>
|
||||
@@ -484,6 +487,9 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat)
|
||||
{
|
||||
struct phy_device *phydev = phy_dat;
|
||||
|
||||
if (PHY_HALTED == phydev->state)
|
||||
return IRQ_NONE; /* It can't be ours. */
|
||||
|
||||
/* The MDIO bus is not allowed to be written in interrupt
|
||||
* context, so we need to disable the irq here. A work
|
||||
* queue will write the PHY to disable and clear the
|
||||
@@ -577,6 +583,13 @@ int phy_stop_interrupts(struct phy_device *phydev)
|
||||
if (err)
|
||||
phy_error(phydev);
|
||||
|
||||
/*
|
||||
* Finish any pending work; we might have been scheduled
|
||||
* to be called from keventd ourselves, though.
|
||||
*/
|
||||
if (!current_is_keventd())
|
||||
flush_scheduled_work();
|
||||
|
||||
free_irq(phydev->irq, phydev);
|
||||
|
||||
return err;
|
||||
@@ -604,7 +617,8 @@ static void phy_change(struct work_struct *work)
|
||||
enable_irq(phydev->irq);
|
||||
|
||||
/* Reenable interrupts */
|
||||
err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
|
||||
if (PHY_HALTED != phydev->state)
|
||||
err = phy_config_interrupt(phydev, PHY_INTERRUPT_ENABLED);
|
||||
|
||||
if (err)
|
||||
goto irq_enable_err;
|
||||
@@ -625,18 +639,24 @@ void phy_stop(struct phy_device *phydev)
|
||||
if (PHY_HALTED == phydev->state)
|
||||
goto out_unlock;
|
||||
|
||||
if (phydev->irq != PHY_POLL) {
|
||||
/* Clear any pending interrupts */
|
||||
phy_clear_interrupt(phydev);
|
||||
phydev->state = PHY_HALTED;
|
||||
|
||||
if (phydev->irq != PHY_POLL) {
|
||||
/* Disable PHY Interrupts */
|
||||
phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
|
||||
}
|
||||
|
||||
phydev->state = PHY_HALTED;
|
||||
/* Clear any pending interrupts */
|
||||
phy_clear_interrupt(phydev);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&phydev->lock);
|
||||
|
||||
/*
|
||||
* Cannot call flush_scheduled_work() here as desired because
|
||||
* of rtnl_lock(), but PHY_HALTED shall guarantee phy_change()
|
||||
* will not reenable interrupts.
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -694,60 +714,57 @@ static void phy_timer(unsigned long data)
|
||||
|
||||
break;
|
||||
case PHY_AN:
|
||||
err = phy_read_status(phydev);
|
||||
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
/* If the link is down, give up on
|
||||
* negotiation for now */
|
||||
if (!phydev->link) {
|
||||
phydev->state = PHY_NOLINK;
|
||||
netif_carrier_off(phydev->attached_dev);
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check if negotiation is done. Break
|
||||
* if there's an error */
|
||||
err = phy_aneg_done(phydev);
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
/* If auto-negotiation is done, we change to
|
||||
* either RUNNING, or NOLINK */
|
||||
/* If AN is done, we're running */
|
||||
if (err > 0) {
|
||||
err = phy_read_status(phydev);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
||||
if (phydev->link) {
|
||||
phydev->state = PHY_RUNNING;
|
||||
netif_carrier_on(phydev->attached_dev);
|
||||
} else {
|
||||
phydev->state = PHY_NOLINK;
|
||||
netif_carrier_off(phydev->attached_dev);
|
||||
}
|
||||
|
||||
phydev->state = PHY_RUNNING;
|
||||
netif_carrier_on(phydev->attached_dev);
|
||||
phydev->adjust_link(phydev->attached_dev);
|
||||
|
||||
} else if (0 == phydev->link_timeout--) {
|
||||
/* The counter expired, so either we
|
||||
* switch to forced mode, or the
|
||||
* magic_aneg bit exists, and we try aneg
|
||||
* again */
|
||||
if (!(phydev->drv->flags & PHY_HAS_MAGICANEG)) {
|
||||
int idx;
|
||||
|
||||
/* We'll start from the
|
||||
* fastest speed, and work
|
||||
* our way down */
|
||||
idx = phy_find_valid(0,
|
||||
phydev->supported);
|
||||
|
||||
phydev->speed = settings[idx].speed;
|
||||
phydev->duplex = settings[idx].duplex;
|
||||
|
||||
phydev->autoneg = AUTONEG_DISABLE;
|
||||
phydev->state = PHY_FORCING;
|
||||
phydev->link_timeout =
|
||||
PHY_FORCE_TIMEOUT;
|
||||
|
||||
pr_info("Trying %d/%s\n",
|
||||
phydev->speed,
|
||||
DUPLEX_FULL ==
|
||||
phydev->duplex ?
|
||||
"FULL" : "HALF");
|
||||
}
|
||||
int idx;
|
||||
|
||||
needs_aneg = 1;
|
||||
/* If we have the magic_aneg bit,
|
||||
* we try again */
|
||||
if (phydev->drv->flags & PHY_HAS_MAGICANEG)
|
||||
break;
|
||||
|
||||
/* The timer expired, and we still
|
||||
* don't have a setting, so we try
|
||||
* forcing it until we find one that
|
||||
* works, starting from the fastest speed,
|
||||
* and working our way down */
|
||||
idx = phy_find_valid(0, phydev->supported);
|
||||
|
||||
phydev->speed = settings[idx].speed;
|
||||
phydev->duplex = settings[idx].duplex;
|
||||
|
||||
phydev->autoneg = AUTONEG_DISABLE;
|
||||
|
||||
pr_info("Trying %d/%s\n", phydev->speed,
|
||||
DUPLEX_FULL ==
|
||||
phydev->duplex ?
|
||||
"FULL" : "HALF");
|
||||
}
|
||||
break;
|
||||
case PHY_NOLINK:
|
||||
@@ -763,7 +780,7 @@ static void phy_timer(unsigned long data)
|
||||
}
|
||||
break;
|
||||
case PHY_FORCING:
|
||||
err = phy_read_status(phydev);
|
||||
err = genphy_update_link(phydev);
|
||||
|
||||
if (err)
|
||||
break;
|
||||
|
@@ -59,6 +59,7 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
|
||||
dev->duplex = -1;
|
||||
dev->pause = dev->asym_pause = 0;
|
||||
dev->link = 1;
|
||||
dev->interface = PHY_INTERFACE_MODE_GMII;
|
||||
|
||||
dev->autoneg = AUTONEG_ENABLE;
|
||||
|
||||
@@ -137,11 +138,12 @@ void phy_prepare_link(struct phy_device *phydev,
|
||||
* the desired functionality.
|
||||
*/
|
||||
struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
|
||||
void (*handler)(struct net_device *), u32 flags)
|
||||
void (*handler)(struct net_device *), u32 flags,
|
||||
u32 interface)
|
||||
{
|
||||
struct phy_device *phydev;
|
||||
|
||||
phydev = phy_attach(dev, phy_id, flags);
|
||||
phydev = phy_attach(dev, phy_id, flags, interface);
|
||||
|
||||
if (IS_ERR(phydev))
|
||||
return phydev;
|
||||
@@ -186,7 +188,7 @@ static int phy_compare_id(struct device *dev, void *data)
|
||||
}
|
||||
|
||||
struct phy_device *phy_attach(struct net_device *dev,
|
||||
const char *phy_id, u32 flags)
|
||||
const char *phy_id, u32 flags, u32 interface)
|
||||
{
|
||||
struct bus_type *bus = &mdio_bus_type;
|
||||
struct phy_device *phydev;
|
||||
@@ -231,6 +233,20 @@ struct phy_device *phy_attach(struct net_device *dev,
|
||||
|
||||
phydev->dev_flags = flags;
|
||||
|
||||
phydev->interface = interface;
|
||||
|
||||
/* Do initial configuration here, now that
|
||||
* we have certain key parameters
|
||||
* (dev_flags and interface) */
|
||||
if (phydev->drv->config_init) {
|
||||
int err;
|
||||
|
||||
err = phydev->drv->config_init(phydev);
|
||||
|
||||
if (err < 0)
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return phydev;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_attach);
|
||||
@@ -427,6 +443,7 @@ int genphy_update_link(struct phy_device *phydev)
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(genphy_update_link);
|
||||
|
||||
/* genphy_read_status
|
||||
*
|
||||
@@ -611,13 +628,8 @@ static int phy_probe(struct device *dev)
|
||||
|
||||
spin_unlock(&phydev->lock);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (phydev->drv->config_init)
|
||||
err = phydev->drv->config_init(phydev);
|
||||
|
||||
return err;
|
||||
|
||||
}
|
||||
|
||||
static int phy_remove(struct device *dev)
|
||||
|
Reference in New Issue
Block a user