mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 21:51:40 +00:00
forcedeth: sideband management fix
This patch contains a fix that implements proper communication with the sideband management unit. Also, it makes sure that the speed is correctly set for gigabit phys in the case where sideband mgmt unit initialized the phy. Refer to bug #7684 for more details. Signed-Off-By: Ayaz Abdulla <aabdulla@nvidia.com> Signed-off-by: Jeff Garzik <jeff@garzik.org>
This commit is contained in:
parent
e6331173c8
commit
f35723ec48
@ -234,6 +234,7 @@ enum {
|
|||||||
#define NVREG_XMITCTL_HOST_SEMA_MASK 0x0000f000
|
#define NVREG_XMITCTL_HOST_SEMA_MASK 0x0000f000
|
||||||
#define NVREG_XMITCTL_HOST_SEMA_ACQ 0x0000f000
|
#define NVREG_XMITCTL_HOST_SEMA_ACQ 0x0000f000
|
||||||
#define NVREG_XMITCTL_HOST_LOADED 0x00004000
|
#define NVREG_XMITCTL_HOST_LOADED 0x00004000
|
||||||
|
#define NVREG_XMITCTL_TX_PATH_EN 0x01000000
|
||||||
NvRegTransmitterStatus = 0x088,
|
NvRegTransmitterStatus = 0x088,
|
||||||
#define NVREG_XMITSTAT_BUSY 0x01
|
#define NVREG_XMITSTAT_BUSY 0x01
|
||||||
|
|
||||||
@ -249,6 +250,7 @@ enum {
|
|||||||
#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
|
#define NVREG_OFFLOAD_NORMAL RX_NIC_BUFSIZE
|
||||||
NvRegReceiverControl = 0x094,
|
NvRegReceiverControl = 0x094,
|
||||||
#define NVREG_RCVCTL_START 0x01
|
#define NVREG_RCVCTL_START 0x01
|
||||||
|
#define NVREG_RCVCTL_RX_PATH_EN 0x01000000
|
||||||
NvRegReceiverStatus = 0x98,
|
NvRegReceiverStatus = 0x98,
|
||||||
#define NVREG_RCVSTAT_BUSY 0x01
|
#define NVREG_RCVSTAT_BUSY 0x01
|
||||||
|
|
||||||
@ -1169,16 +1171,21 @@ static void nv_start_rx(struct net_device *dev)
|
|||||||
{
|
{
|
||||||
struct fe_priv *np = netdev_priv(dev);
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 rx_ctrl = readl(base + NvRegReceiverControl);
|
||||||
|
|
||||||
dprintk(KERN_DEBUG "%s: nv_start_rx\n", dev->name);
|
dprintk(KERN_DEBUG "%s: nv_start_rx\n", dev->name);
|
||||||
/* Already running? Stop it. */
|
/* Already running? Stop it. */
|
||||||
if (readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) {
|
if ((readl(base + NvRegReceiverControl) & NVREG_RCVCTL_START) && !np->mac_in_use) {
|
||||||
writel(0, base + NvRegReceiverControl);
|
rx_ctrl &= ~NVREG_RCVCTL_START;
|
||||||
|
writel(rx_ctrl, base + NvRegReceiverControl);
|
||||||
pci_push(base);
|
pci_push(base);
|
||||||
}
|
}
|
||||||
writel(np->linkspeed, base + NvRegLinkSpeed);
|
writel(np->linkspeed, base + NvRegLinkSpeed);
|
||||||
pci_push(base);
|
pci_push(base);
|
||||||
writel(NVREG_RCVCTL_START, base + NvRegReceiverControl);
|
rx_ctrl |= NVREG_RCVCTL_START;
|
||||||
|
if (np->mac_in_use)
|
||||||
|
rx_ctrl &= ~NVREG_RCVCTL_RX_PATH_EN;
|
||||||
|
writel(rx_ctrl, base + NvRegReceiverControl);
|
||||||
dprintk(KERN_DEBUG "%s: nv_start_rx to duplex %d, speed 0x%08x.\n",
|
dprintk(KERN_DEBUG "%s: nv_start_rx to duplex %d, speed 0x%08x.\n",
|
||||||
dev->name, np->duplex, np->linkspeed);
|
dev->name, np->duplex, np->linkspeed);
|
||||||
pci_push(base);
|
pci_push(base);
|
||||||
@ -1186,39 +1193,59 @@ static void nv_start_rx(struct net_device *dev)
|
|||||||
|
|
||||||
static void nv_stop_rx(struct net_device *dev)
|
static void nv_stop_rx(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 rx_ctrl = readl(base + NvRegReceiverControl);
|
||||||
|
|
||||||
dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
|
dprintk(KERN_DEBUG "%s: nv_stop_rx\n", dev->name);
|
||||||
writel(0, base + NvRegReceiverControl);
|
if (!np->mac_in_use)
|
||||||
|
rx_ctrl &= ~NVREG_RCVCTL_START;
|
||||||
|
else
|
||||||
|
rx_ctrl |= NVREG_RCVCTL_RX_PATH_EN;
|
||||||
|
writel(rx_ctrl, base + NvRegReceiverControl);
|
||||||
reg_delay(dev, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
|
reg_delay(dev, NvRegReceiverStatus, NVREG_RCVSTAT_BUSY, 0,
|
||||||
NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
|
NV_RXSTOP_DELAY1, NV_RXSTOP_DELAY1MAX,
|
||||||
KERN_INFO "nv_stop_rx: ReceiverStatus remained busy");
|
KERN_INFO "nv_stop_rx: ReceiverStatus remained busy");
|
||||||
|
|
||||||
udelay(NV_RXSTOP_DELAY2);
|
udelay(NV_RXSTOP_DELAY2);
|
||||||
writel(0, base + NvRegLinkSpeed);
|
if (!np->mac_in_use)
|
||||||
|
writel(0, base + NvRegLinkSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nv_start_tx(struct net_device *dev)
|
static void nv_start_tx(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 tx_ctrl = readl(base + NvRegTransmitterControl);
|
||||||
|
|
||||||
dprintk(KERN_DEBUG "%s: nv_start_tx\n", dev->name);
|
dprintk(KERN_DEBUG "%s: nv_start_tx\n", dev->name);
|
||||||
writel(NVREG_XMITCTL_START, base + NvRegTransmitterControl);
|
tx_ctrl |= NVREG_XMITCTL_START;
|
||||||
|
if (np->mac_in_use)
|
||||||
|
tx_ctrl &= ~NVREG_XMITCTL_TX_PATH_EN;
|
||||||
|
writel(tx_ctrl, base + NvRegTransmitterControl);
|
||||||
pci_push(base);
|
pci_push(base);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nv_stop_tx(struct net_device *dev)
|
static void nv_stop_tx(struct net_device *dev)
|
||||||
{
|
{
|
||||||
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
u8 __iomem *base = get_hwbase(dev);
|
||||||
|
u32 tx_ctrl = readl(base + NvRegTransmitterControl);
|
||||||
|
|
||||||
dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
|
dprintk(KERN_DEBUG "%s: nv_stop_tx\n", dev->name);
|
||||||
writel(0, base + NvRegTransmitterControl);
|
if (!np->mac_in_use)
|
||||||
|
tx_ctrl &= ~NVREG_XMITCTL_START;
|
||||||
|
else
|
||||||
|
tx_ctrl |= NVREG_XMITCTL_TX_PATH_EN;
|
||||||
|
writel(tx_ctrl, base + NvRegTransmitterControl);
|
||||||
reg_delay(dev, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
|
reg_delay(dev, NvRegTransmitterStatus, NVREG_XMITSTAT_BUSY, 0,
|
||||||
NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
|
NV_TXSTOP_DELAY1, NV_TXSTOP_DELAY1MAX,
|
||||||
KERN_INFO "nv_stop_tx: TransmitterStatus remained busy");
|
KERN_INFO "nv_stop_tx: TransmitterStatus remained busy");
|
||||||
|
|
||||||
udelay(NV_TXSTOP_DELAY2);
|
udelay(NV_TXSTOP_DELAY2);
|
||||||
writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV, base + NvRegTransmitPoll);
|
if (!np->mac_in_use)
|
||||||
|
writel(readl(base + NvRegTransmitPoll) & NVREG_TRANSMITPOLL_MAC_ADDR_REV,
|
||||||
|
base + NvRegTransmitPoll);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void nv_txrx_reset(struct net_device *dev)
|
static void nv_txrx_reset(struct net_device *dev)
|
||||||
@ -4148,20 +4175,6 @@ static int nv_mgmt_acquire_sema(struct net_device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Indicate to mgmt unit whether driver is loaded or not */
|
|
||||||
static void nv_mgmt_driver_loaded(struct net_device *dev, int loaded)
|
|
||||||
{
|
|
||||||
u8 __iomem *base = get_hwbase(dev);
|
|
||||||
u32 tx_ctrl;
|
|
||||||
|
|
||||||
tx_ctrl = readl(base + NvRegTransmitterControl);
|
|
||||||
if (loaded)
|
|
||||||
tx_ctrl |= NVREG_XMITCTL_HOST_LOADED;
|
|
||||||
else
|
|
||||||
tx_ctrl &= ~NVREG_XMITCTL_HOST_LOADED;
|
|
||||||
writel(tx_ctrl, base + NvRegTransmitterControl);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int nv_open(struct net_device *dev)
|
static int nv_open(struct net_device *dev)
|
||||||
{
|
{
|
||||||
struct fe_priv *np = netdev_priv(dev);
|
struct fe_priv *np = netdev_priv(dev);
|
||||||
@ -4659,33 +4672,24 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
|||||||
writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
|
writel(NVREG_MIISTAT_MASK, base + NvRegMIIStatus);
|
||||||
|
|
||||||
if (id->driver_data & DEV_HAS_MGMT_UNIT) {
|
if (id->driver_data & DEV_HAS_MGMT_UNIT) {
|
||||||
writel(0x1, base + 0x204); pci_push(base);
|
|
||||||
msleep(500);
|
|
||||||
/* management unit running on the mac? */
|
/* management unit running on the mac? */
|
||||||
np->mac_in_use = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST;
|
if (readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_PHY_INIT) {
|
||||||
if (np->mac_in_use) {
|
np->mac_in_use = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_MGMT_ST;
|
||||||
u32 mgmt_sync;
|
dprintk(KERN_INFO "%s: mgmt unit is running. mac in use %x.\n", pci_name(pci_dev), np->mac_in_use);
|
||||||
/* management unit setup the phy already? */
|
for (i = 0; i < 5000; i++) {
|
||||||
mgmt_sync = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK;
|
msleep(1);
|
||||||
if (mgmt_sync == NVREG_XMITCTL_SYNC_NOT_READY) {
|
if (nv_mgmt_acquire_sema(dev)) {
|
||||||
if (!nv_mgmt_acquire_sema(dev)) {
|
/* management unit setup the phy already? */
|
||||||
for (i = 0; i < 5000; i++) {
|
if ((readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK) ==
|
||||||
msleep(1);
|
NVREG_XMITCTL_SYNC_PHY_INIT) {
|
||||||
mgmt_sync = readl(base + NvRegTransmitterControl) & NVREG_XMITCTL_SYNC_MASK;
|
/* phy is inited by mgmt unit */
|
||||||
if (mgmt_sync == NVREG_XMITCTL_SYNC_NOT_READY)
|
phyinitialized = 1;
|
||||||
continue;
|
dprintk(KERN_INFO "%s: Phy already initialized by mgmt unit.\n", pci_name(pci_dev));
|
||||||
if (mgmt_sync == NVREG_XMITCTL_SYNC_PHY_INIT)
|
} else {
|
||||||
phyinitialized = 1;
|
/* we need to init the phy */
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else {
|
break;
|
||||||
/* we need to init the phy */
|
|
||||||
}
|
}
|
||||||
} else if (mgmt_sync == NVREG_XMITCTL_SYNC_PHY_INIT) {
|
|
||||||
/* phy is inited by SMU */
|
|
||||||
phyinitialized = 1;
|
|
||||||
} else {
|
|
||||||
/* we need to init the phy */
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4724,10 +4728,12 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
|||||||
if (!phyinitialized) {
|
if (!phyinitialized) {
|
||||||
/* reset it */
|
/* reset it */
|
||||||
phy_init(dev);
|
phy_init(dev);
|
||||||
}
|
} else {
|
||||||
|
/* see if it is a gigabit phy */
|
||||||
if (id->driver_data & DEV_HAS_MGMT_UNIT) {
|
u32 mii_status = mii_rw(dev, np->phyaddr, MII_BMSR, MII_READ);
|
||||||
nv_mgmt_driver_loaded(dev, 1);
|
if (mii_status & PHY_GIGABIT) {
|
||||||
|
np->gigabit = PHY_GIGABIT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set default link speed settings */
|
/* set default link speed settings */
|
||||||
@ -4749,8 +4755,6 @@ static int __devinit nv_probe(struct pci_dev *pci_dev, const struct pci_device_i
|
|||||||
out_error:
|
out_error:
|
||||||
if (phystate_orig)
|
if (phystate_orig)
|
||||||
writel(phystate|NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
|
writel(phystate|NVREG_ADAPTCTL_RUNNING, base + NvRegAdapterControl);
|
||||||
if (np->mac_in_use)
|
|
||||||
nv_mgmt_driver_loaded(dev, 0);
|
|
||||||
pci_set_drvdata(pci_dev, NULL);
|
pci_set_drvdata(pci_dev, NULL);
|
||||||
out_freering:
|
out_freering:
|
||||||
free_rings(dev);
|
free_rings(dev);
|
||||||
@ -4780,9 +4784,6 @@ static void __devexit nv_remove(struct pci_dev *pci_dev)
|
|||||||
writel(np->orig_mac[0], base + NvRegMacAddrA);
|
writel(np->orig_mac[0], base + NvRegMacAddrA);
|
||||||
writel(np->orig_mac[1], base + NvRegMacAddrB);
|
writel(np->orig_mac[1], base + NvRegMacAddrB);
|
||||||
|
|
||||||
if (np->mac_in_use)
|
|
||||||
nv_mgmt_driver_loaded(dev, 0);
|
|
||||||
|
|
||||||
/* free all structures */
|
/* free all structures */
|
||||||
free_rings(dev);
|
free_rings(dev);
|
||||||
iounmap(get_hwbase(dev));
|
iounmap(get_hwbase(dev));
|
||||||
|
Loading…
Reference in New Issue
Block a user