Commit f3c1e4cf authored by Andy Fleming's avatar Andy Fleming Committed by Jeff Garzik

[PATCH] update gianfar ethernet driver

parent 633980ae
...@@ -10,7 +10,9 @@ obj-$(CONFIG_E1000) += e1000/ ...@@ -10,7 +10,9 @@ obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/ obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/ obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar.o gianfar_ethtool.o gianfar_phy.o obj-$(CONFIG_GIANFAR) += gianfar_driver.o
gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o
# #
# link order important here # link order important here
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Andy Fleming * Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com) * Maintainer: Kumar Gala (kumar.gala@freescale.com)
* *
* Copyright 2004 Freescale Semiconductor, Inc * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -96,15 +96,6 @@ ...@@ -96,15 +96,6 @@
#include "gianfar.h" #include "gianfar.h"
#include "gianfar_phy.h" #include "gianfar_phy.h"
#ifdef CONFIG_NET_FASTROUTE
#include <linux/if_arp.h>
#include <net/ip.h>
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,41)
#define irqreturn_t void
#define IRQ_HANDLED
#endif
#define TX_TIMEOUT (1*HZ) #define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000 #define SKB_ALLOC_TIMEOUT 1000000
...@@ -117,9 +108,8 @@ ...@@ -117,9 +108,8 @@
#define RECEIVE(x) netif_rx(x) #define RECEIVE(x) netif_rx(x)
#endif #endif
#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.0, " const char gfar_driver_name[] = "Gianfar Ethernet";
char gfar_driver_name[] = "Gianfar Ethernet"; const char gfar_driver_version[] = "1.1";
char gfar_driver_version[] = "1.0";
int startup_gfar(struct net_device *dev); int startup_gfar(struct net_device *dev);
static int gfar_enet_open(struct net_device *dev); static int gfar_enet_open(struct net_device *dev);
...@@ -148,24 +138,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); ...@@ -148,24 +138,11 @@ static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
#ifdef CONFIG_GFAR_NAPI #ifdef CONFIG_GFAR_NAPI
static int gfar_poll(struct net_device *dev, int *budget); static int gfar_poll(struct net_device *dev, int *budget);
#endif #endif
#ifdef CONFIG_NET_FASTROUTE
static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst);
#endif
static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length);
#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
#else
static int gfar_clean_rx_ring(struct net_device *dev);
#endif
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length); static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, int length);
static void gfar_phy_startup_timer(unsigned long data);
extern struct ethtool_ops gfar_ethtool_ops; extern struct ethtool_ops gfar_ethtool_ops;
extern void gfar_gstrings_normon(struct net_device *dev, u32 stringset,
u8 * buf);
extern void gfar_fill_stats_normon(struct net_device *dev,
struct ethtool_stats *dummy, u64 * buf);
extern int gfar_stats_count_normon(struct net_device *dev);
MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_AUTHOR("Freescale Semiconductor, Inc");
MODULE_DESCRIPTION("Gianfar Ethernet Driver"); MODULE_DESCRIPTION("Gianfar Ethernet Driver");
...@@ -183,7 +160,7 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -183,7 +160,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
struct ocp_gfar_data *einfo; struct ocp_gfar_data *einfo;
int idx; int idx;
int err = 0; int err = 0;
struct ethtool_ops *dev_ethtool_ops; int dev_ethtool_ops = 0;
einfo = (struct ocp_gfar_data *) ocpdev->def->additions; einfo = (struct ocp_gfar_data *) ocpdev->def->additions;
...@@ -197,7 +174,8 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -197,7 +174,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
/* get a pointer to the register memory which can /* get a pointer to the register memory which can
* configure the PHYs. If it's different from this set, * configure the PHYs. If it's different from this set,
* get the device which has those regs */ * get the device which has those regs */
if ((einfo->phyregidx >= 0) && (einfo->phyregidx != ocpdev->def->index)) { if ((einfo->phyregidx >= 0) &&
(einfo->phyregidx != ocpdev->def->index)) {
mdiodev = ocp_find_device(OCP_ANY_ID, mdiodev = ocp_find_device(OCP_ANY_ID,
OCP_FUNC_GFAR, einfo->phyregidx); OCP_FUNC_GFAR, einfo->phyregidx);
...@@ -238,6 +216,8 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -238,6 +216,8 @@ static int gfar_probe(struct ocp_device *ocpdev)
goto phy_regs_fail; goto phy_regs_fail;
} }
spin_lock_init(&priv->lock);
ocp_set_drvdata(ocpdev, dev); ocp_set_drvdata(ocpdev, dev);
/* Stop the DMA engine now, in case it was running before */ /* Stop the DMA engine now, in case it was running before */
...@@ -269,15 +249,13 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -269,15 +249,13 @@ static int gfar_probe(struct ocp_device *ocpdev)
gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS); gfar_write(&priv->regs->ecntrl, ECNTRL_INIT_SETTINGS);
/* Copy the station address into the dev structure, */ /* Copy the station address into the dev structure, */
/* and into the address registers MAC_STNADDR1,2. */
/* Backwards, because little endian MACs are dumb. */
/* Don't set the regs if the firmware already did */
memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN); memcpy(dev->dev_addr, einfo->mac_addr, MAC_ADDR_LEN);
/* Set the dev->base_addr to the gfar reg region */ /* Set the dev->base_addr to the gfar reg region */
dev->base_addr = (unsigned long) (priv->regs); dev->base_addr = (unsigned long) (priv->regs);
SET_MODULE_OWNER(dev); SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &ocpdev->dev);
/* Fill in the dev structure */ /* Fill in the dev structure */
dev->open = gfar_enet_open; dev->open = gfar_enet_open;
...@@ -293,37 +271,16 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -293,37 +271,16 @@ static int gfar_probe(struct ocp_device *ocpdev)
dev->change_mtu = gfar_change_mtu; dev->change_mtu = gfar_change_mtu;
dev->mtu = 1500; dev->mtu = 1500;
dev->set_multicast_list = gfar_set_multi; dev->set_multicast_list = gfar_set_multi;
dev->flags |= IFF_MULTICAST;
dev_ethtool_ops = /* Index into the array of possible ethtool
(struct ethtool_ops *)kmalloc(sizeof(struct ethtool_ops), * ops to catch all 4 possibilities */
GFP_KERNEL); if((priv->einfo->flags & GFAR_HAS_RMON) == 0)
dev_ethtool_ops += 1;
if(dev_ethtool_ops == NULL) {
err = -ENOMEM;
goto ethtool_fail;
}
memcpy(dev_ethtool_ops, &gfar_ethtool_ops, sizeof(gfar_ethtool_ops));
/* If there is no RMON support in this device, we don't
* want to expose non-existant statistics */
if((priv->einfo->flags & GFAR_HAS_RMON) == 0) {
dev_ethtool_ops->get_strings = gfar_gstrings_normon;
dev_ethtool_ops->get_stats_count = gfar_stats_count_normon;
dev_ethtool_ops->get_ethtool_stats = gfar_fill_stats_normon;
}
if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0) {
dev_ethtool_ops->set_coalesce = NULL;
dev_ethtool_ops->get_coalesce = NULL;
}
dev->ethtool_ops = dev_ethtool_ops; if((priv->einfo->flags & GFAR_HAS_COALESCE) == 0)
dev_ethtool_ops += 2;
#ifdef CONFIG_NET_FASTROUTE dev->ethtool_ops = gfar_op_array[dev_ethtool_ops];
dev->accept_fastpath = gfar_accept_fastpath;
#endif
priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE; priv->rx_buffer_size = DEFAULT_RX_BUFFER_SIZE;
#ifdef CONFIG_GFAR_BUFSTASH #ifdef CONFIG_GFAR_BUFSTASH
...@@ -332,13 +289,12 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -332,13 +289,12 @@ static int gfar_probe(struct ocp_device *ocpdev)
priv->tx_ring_size = DEFAULT_TX_RING_SIZE; priv->tx_ring_size = DEFAULT_TX_RING_SIZE;
priv->rx_ring_size = DEFAULT_RX_RING_SIZE; priv->rx_ring_size = DEFAULT_RX_RING_SIZE;
/* Initially, coalescing is disabled */ priv->txcoalescing = DEFAULT_TX_COALESCE;
priv->txcoalescing = 0; priv->txcount = DEFAULT_TXCOUNT;
priv->txcount = 0; priv->txtime = DEFAULT_TXTIME;
priv->txtime = 0; priv->rxcoalescing = DEFAULT_RX_COALESCE;
priv->rxcoalescing = 0; priv->rxcount = DEFAULT_RXCOUNT;
priv->rxcount = 0; priv->rxtime = DEFAULT_RXTIME;
priv->rxtime = 0;
err = register_netdev(dev); err = register_netdev(dev);
...@@ -349,10 +305,10 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -349,10 +305,10 @@ static int gfar_probe(struct ocp_device *ocpdev)
} }
/* Print out the device info */ /* Print out the device info */
printk(DEVICE_NAME, dev->name); printk(KERN_INFO DEVICE_NAME, dev->name);
for (idx = 0; idx < 6; idx++) for (idx = 0; idx < 6; idx++)
printk("%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':'); printk(KERN_INFO "%2.2x%c", dev->dev_addr[idx], idx == 5 ? ' ' : ':');
printk("\n"); printk(KERN_INFO "\n");
/* Even more device info helps when determining which kernel */ /* Even more device info helps when determining which kernel */
/* provided which set of benchmarks. Since this is global for all */ /* provided which set of benchmarks. Since this is global for all */
...@@ -367,10 +323,7 @@ static int gfar_probe(struct ocp_device *ocpdev) ...@@ -367,10 +323,7 @@ static int gfar_probe(struct ocp_device *ocpdev)
return 0; return 0;
register_fail: register_fail:
kfree(dev_ethtool_ops);
ethtool_fail:
iounmap((void *) priv->phyregs); iounmap((void *) priv->phyregs);
phy_regs_fail: phy_regs_fail:
iounmap((void *) priv->regs); iounmap((void *) priv->regs);
...@@ -386,7 +339,6 @@ static void gfar_remove(struct ocp_device *ocpdev) ...@@ -386,7 +339,6 @@ static void gfar_remove(struct ocp_device *ocpdev)
ocp_set_drvdata(ocpdev, NULL); ocp_set_drvdata(ocpdev, NULL);
kfree(dev->ethtool_ops);
iounmap((void *) priv->regs); iounmap((void *) priv->regs);
iounmap((void *) priv->phyregs); iounmap((void *) priv->phyregs);
free_netdev(dev); free_netdev(dev);
...@@ -399,26 +351,90 @@ static int init_phy(struct net_device *dev) ...@@ -399,26 +351,90 @@ static int init_phy(struct net_device *dev)
{ {
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
struct phy_info *curphy; struct phy_info *curphy;
unsigned int timeout = PHY_INIT_TIMEOUT;
struct gfar *phyregs = priv->phyregs;
struct gfar_mii_info *mii_info;
int err;
priv->link = 1;
priv->oldlink = 0; priv->oldlink = 0;
priv->oldspeed = 0; priv->oldspeed = 0;
priv->olddplx = -1; priv->oldduplex = -1;
mii_info = kmalloc(sizeof(struct gfar_mii_info),
GFP_KERNEL);
if(NULL == mii_info) {
printk(KERN_ERR "%s: Could not allocate mii_info\n",
dev->name);
return -ENOMEM;
}
mii_info->speed = SPEED_1000;
mii_info->duplex = DUPLEX_FULL;
mii_info->pause = 0;
mii_info->link = 1;
mii_info->advertising = (ADVERTISED_10baseT_Half |
ADVERTISED_10baseT_Full |
ADVERTISED_100baseT_Half |
ADVERTISED_100baseT_Full |
ADVERTISED_1000baseT_Full);
mii_info->autoneg = 1;
mii_info->mii_id = priv->einfo->phyid;
mii_info->dev = dev;
mii_info->mdio_read = &read_phy_reg;
mii_info->mdio_write = &write_phy_reg;
priv->mii_info = mii_info;
/* Reset the management interface */
gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
/* Setup the MII Mgmt clock speed */
gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
/* Wait until the bus is free */
while ((gfar_read(&phyregs->miimind) & MIIMIND_BUSY) &&
timeout--)
cpu_relax();
if(timeout <= 0) {
printk(KERN_ERR "%s: The MII Bus is stuck!\n",
dev->name);
err = -1;
goto bus_fail;
}
/* get info for this PHY */ /* get info for this PHY */
curphy = get_phy_info(dev); curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) { if (curphy == NULL) {
printk(KERN_ERR "%s: No PHY found\n", dev->name); printk(KERN_ERR "%s: No PHY found\n", dev->name);
return -1; err = -1;
goto no_phy;
} }
priv->phyinfo = curphy; mii_info->phyinfo = curphy;
/* Run the commands which initialize the PHY */
if(curphy->init) {
err = curphy->init(priv->mii_info);
/* Run the commands which configure the PHY */ if (err)
phy_run_commands(dev, curphy->config); goto phy_init_fail;
}
return 0; return 0;
phy_init_fail:
no_phy:
bus_fail:
kfree(mii_info);
return err;
} }
static void init_registers(struct net_device *dev) static void init_registers(struct net_device *dev)
...@@ -494,7 +510,7 @@ void stop_gfar(struct net_device *dev) ...@@ -494,7 +510,7 @@ void stop_gfar(struct net_device *dev)
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */ /* Tell the kernel the link is down */
priv->link = 0; priv->mii_info->link = 0;
adjust_link(dev); adjust_link(dev);
/* Mask all interrupts */ /* Mask all interrupts */
...@@ -521,7 +537,12 @@ void stop_gfar(struct net_device *dev) ...@@ -521,7 +537,12 @@ void stop_gfar(struct net_device *dev)
gfar_write(&regs->maccfg1, tempval); gfar_write(&regs->maccfg1, tempval);
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) { if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
phy_run_commands(dev, priv->phyinfo->shutdown); /* Clear any pending interrupts */
mii_clear_phy_interrupt(priv->mii_info);
/* Disable PHY Interrupts */
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_DISABLED);
} }
spin_unlock_irqrestore(&priv->lock, flags); spin_unlock_irqrestore(&priv->lock, flags);
...@@ -543,15 +564,11 @@ void stop_gfar(struct net_device *dev) ...@@ -543,15 +564,11 @@ void stop_gfar(struct net_device *dev)
free_skb_resources(priv); free_skb_resources(priv);
dma_unmap_single(NULL, gfar_read(&regs->tbase), dma_free_coherent(NULL,
sizeof(struct txbd)*priv->tx_ring_size, sizeof(struct txbd8)*priv->tx_ring_size
DMA_BIDIRECTIONAL); + sizeof(struct rxbd8)*priv->rx_ring_size,
dma_unmap_single(NULL, gfar_read(&regs->rbase), priv->tx_bd_base,
sizeof(struct rxbd)*priv->rx_ring_size, gfar_read(&regs->tbase));
DMA_BIDIRECTIONAL);
/* Free the buffer descriptors */
kfree(priv->tx_bd_base);
} }
/* If there are any tx skbs or rx skbs still around, free them. /* If there are any tx skbs or rx skbs still around, free them.
...@@ -610,7 +627,8 @@ int startup_gfar(struct net_device *dev) ...@@ -610,7 +627,8 @@ int startup_gfar(struct net_device *dev)
{ {
struct txbd8 *txbdp; struct txbd8 *txbdp;
struct rxbd8 *rxbdp; struct rxbd8 *rxbdp;
unsigned long addr; dma_addr_t addr;
unsigned long vaddr;
int i; int i;
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs; struct gfar *regs = priv->regs;
...@@ -620,32 +638,27 @@ int startup_gfar(struct net_device *dev) ...@@ -620,32 +638,27 @@ int startup_gfar(struct net_device *dev)
gfar_write(&regs->imask, IMASK_INIT_CLEAR); gfar_write(&regs->imask, IMASK_INIT_CLEAR);
/* Allocate memory for the buffer descriptors */ /* Allocate memory for the buffer descriptors */
addr = vaddr = (unsigned long) dma_alloc_coherent(NULL,
(unsigned int) kmalloc(sizeof (struct txbd8) * priv->tx_ring_size + sizeof (struct txbd8) * priv->tx_ring_size +
sizeof (struct rxbd8) * priv->rx_ring_size, sizeof (struct rxbd8) * priv->rx_ring_size,
GFP_KERNEL); &addr, GFP_KERNEL);
if (addr == 0) { if (vaddr == 0) {
printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n", printk(KERN_ERR "%s: Could not allocate buffer descriptors!\n",
dev->name); dev->name);
return -ENOMEM; return -ENOMEM;
} }
priv->tx_bd_base = (struct txbd8 *) addr; priv->tx_bd_base = (struct txbd8 *) vaddr;
/* enet DMA only understands physical addresses */ /* enet DMA only understands physical addresses */
gfar_write(&regs->tbase, gfar_write(&regs->tbase, addr);
dma_map_single(NULL, (void *)addr,
sizeof(struct txbd8) * priv->tx_ring_size,
DMA_BIDIRECTIONAL));
/* Start the rx descriptor ring where the tx ring leaves off */ /* Start the rx descriptor ring where the tx ring leaves off */
addr = addr + sizeof (struct txbd8) * priv->tx_ring_size; addr = addr + sizeof (struct txbd8) * priv->tx_ring_size;
priv->rx_bd_base = (struct rxbd8 *) addr; vaddr = vaddr + sizeof (struct txbd8) * priv->tx_ring_size;
gfar_write(&regs->rbase, priv->rx_bd_base = (struct rxbd8 *) vaddr;
dma_map_single(NULL, (void *)addr, gfar_write(&regs->rbase, addr);
sizeof(struct rxbd8) * priv->rx_ring_size,
DMA_BIDIRECTIONAL));
/* Setup the skbuff rings */ /* Setup the skbuff rings */
priv->tx_skbuff = priv->tx_skbuff =
...@@ -755,39 +768,13 @@ int startup_gfar(struct net_device *dev) ...@@ -755,39 +768,13 @@ int startup_gfar(struct net_device *dev)
} }
} }
/* Grab the PHY interrupt */ /* Set up the PHY change work queue */
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) { INIT_WORK(&priv->tq, gfar_phy_change, dev);
if (request_irq(priv->einfo->interruptPHY, phy_interrupt,
SA_SHIRQ, "phy_interrupt", dev) < 0) {
printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
dev->name, priv->einfo->interruptPHY);
err = -1;
if (priv->einfo->flags & GFAR_HAS_MULTI_INTR)
goto phy_irq_fail;
else
goto tx_irq_fail;
}
} else {
init_timer(&priv->phy_info_timer); init_timer(&priv->phy_info_timer);
priv->phy_info_timer.function = &gfar_phy_timer; priv->phy_info_timer.function = &gfar_phy_startup_timer;
priv->phy_info_timer.data = (unsigned long) dev; priv->phy_info_timer.data = (unsigned long) priv->mii_info;
mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ); mod_timer(&priv->phy_info_timer, jiffies + HZ);
}
/* Set up the bottom half queue */
INIT_WORK(&priv->tq, (void (*)(void *))gfar_phy_change, dev);
/* Configure the PHY interrupt */
phy_run_commands(dev, priv->phyinfo->startup);
/* Tell the kernel the link is up, and determine the
* negotiated features (speed, duplex) */
adjust_link(dev);
if (priv->link == 0)
printk(KERN_INFO "%s: No link detected\n", dev->name);
/* Configure the coalescing support */ /* Configure the coalescing support */
if (priv->txcoalescing) if (priv->txcoalescing)
...@@ -827,8 +814,6 @@ int startup_gfar(struct net_device *dev) ...@@ -827,8 +814,6 @@ int startup_gfar(struct net_device *dev)
return 0; return 0;
phy_irq_fail:
free_irq(priv->einfo->interruptReceive, dev);
rx_irq_fail: rx_irq_fail:
free_irq(priv->einfo->interruptTransmit, dev); free_irq(priv->einfo->interruptTransmit, dev);
tx_irq_fail: tx_irq_fail:
...@@ -837,7 +822,17 @@ int startup_gfar(struct net_device *dev) ...@@ -837,7 +822,17 @@ int startup_gfar(struct net_device *dev)
rx_skb_fail: rx_skb_fail:
free_skb_resources(priv); free_skb_resources(priv);
tx_skb_fail: tx_skb_fail:
kfree(priv->tx_bd_base); dma_free_coherent(NULL,
sizeof(struct txbd8)*priv->tx_ring_size
+ sizeof(struct rxbd8)*priv->rx_ring_size,
priv->tx_bd_base,
gfar_read(&regs->tbase));
if (priv->mii_info->phyinfo->close)
priv->mii_info->phyinfo->close(priv->mii_info);
kfree(priv->mii_info);
return err; return err;
} }
...@@ -854,7 +849,7 @@ static int gfar_enet_open(struct net_device *dev) ...@@ -854,7 +849,7 @@ static int gfar_enet_open(struct net_device *dev)
err = init_phy(dev); err = init_phy(dev);
if (err) if(err)
return err; return err;
err = startup_gfar(dev); err = startup_gfar(dev);
...@@ -934,8 +929,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -934,8 +929,15 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Stops the kernel queue, and halts the controller */ /* Stops the kernel queue, and halts the controller */
static int gfar_close(struct net_device *dev) static int gfar_close(struct net_device *dev)
{ {
struct gfar_private *priv = netdev_priv(dev);
stop_gfar(dev); stop_gfar(dev);
/* Shutdown the PHY */
if (priv->mii_info->phyinfo->close)
priv->mii_info->phyinfo->close(priv->mii_info);
kfree(priv->mii_info);
netif_stop_queue(dev); netif_stop_queue(dev);
return 0; return 0;
...@@ -971,121 +973,6 @@ int gfar_set_mac_address(struct net_device *dev) ...@@ -971,121 +973,6 @@ int gfar_set_mac_address(struct net_device *dev)
return 0; return 0;
} }
/**********************************************************************
* gfar_accept_fastpath
*
* Used to authenticate to the kernel that a fast path entry can be
* added to device's routing table cache
*
* Input : pointer to ethernet interface network device structure and
* a pointer to the designated entry to be added to the cache.
* Output : zero upon success, negative upon failure
**********************************************************************/
#ifdef CONFIG_NET_FASTROUTE
static int gfar_accept_fastpath(struct net_device *dev, struct dst_entry *dst)
{
struct net_device *odev = dst->dev;
if ((dst->ops->protocol != __constant_htons(ETH_P_IP))
|| (odev->type != ARPHRD_ETHER)
|| (odev->accept_fastpath == NULL)) {
return -1;
}
return 0;
}
#endif
/* try_fastroute() -- Checks the fastroute cache to see if a given packet
* can be routed immediately to another device. If it can, we send it.
* If we used a fastroute, we return 1. Otherwise, we return 0.
* Returns 0 if CONFIG_NET_FASTROUTE is not on
*/
static inline int try_fastroute(struct sk_buff *skb, struct net_device *dev, int length)
{
#ifdef CONFIG_NET_FASTROUTE
struct ethhdr *eth;
struct iphdr *iph;
unsigned int hash;
struct rtable *rt;
struct net_device *odev;
struct gfar_private *priv = netdev_priv(dev);
unsigned int CPU_ID = smp_processor_id();
eth = (struct ethhdr *) (skb->data);
/* Only route ethernet IP packets */
if (eth->h_proto == __constant_htons(ETH_P_IP)) {
iph = (struct iphdr *) (skb->data + ETH_HLEN);
/* Generate the hash value */
hash = ((*(u8 *) &iph->daddr) ^ (*(u8 *) & iph->saddr)) & NETDEV_FASTROUTE_HMASK;
rt = (struct rtable *) (dev->fastpath[hash]);
if (rt != NULL
&& ((*(u32 *) &iph->daddr) == (*(u32 *) &rt->key.dst))
&& ((*(u32 *) &iph->saddr) == (*(u32 *) &rt->key.src))
&& !(rt->u.dst.obsolete)) {
odev = rt->u.dst.dev;
netdev_rx_stat[CPU_ID].fastroute_hit++;
/* Make sure the packet is:
* 1) IPv4
* 2) without any options (header length of 5)
* 3) Not a multicast packet
* 4) going to a valid destination
* 5) Not out of time-to-live
*/
if (iph->version == 4
&& iph->ihl == 5
&& (!(eth->h_dest[0] & 0x01))
&& neigh_is_valid(rt->u.dst.neighbour)
&& iph->ttl > 1) {
/* Fast Route Path: Taken if the outgoing device is ready to transmit the packet now */
if ((!netif_queue_stopped(odev))
&& (!spin_is_locked(odev->xmit_lock))
&& (skb->len <= (odev->mtu + ETH_HLEN + 2 + 4))) {
skb->pkt_type = PACKET_FASTROUTE;
skb->protocol = __constant_htons(ETH_P_IP);
ip_decrease_ttl(iph);
memcpy(eth->h_source, odev->dev_addr, MAC_ADDR_LEN);
memcpy(eth->h_dest, rt->u.dst.neighbour->ha, MAC_ADDR_LEN);
skb->dev = odev;
/* Prep the skb for the packet */
skb_put(skb, length);
if (odev->hard_start_xmit(skb, odev) != 0) {
panic("%s: FastRoute path corrupted", dev->name);
}
netdev_rx_stat[CPU_ID].fastroute_success++;
}
/* Semi Fast Route Path: Mark the packet as needing fast routing, but let the
* stack handle getting it to the device */
else {
skb->pkt_type = PACKET_FASTROUTE;
skb->nh.raw = skb->data + ETH_HLEN;
skb->protocol = __constant_htons(ETH_P_IP);
netdev_rx_stat[CPU_ID].fastroute_defer++;
/* Prep the skb for the packet */
skb_put(skb, length);
if(RECEIVE(skb) == NET_RX_DROP) {
priv->extra_stats.kernel_dropped++;
}
}
return 1;
}
}
}
#endif /* CONFIG_NET_FASTROUTE */
return 0;
}
static int gfar_change_mtu(struct net_device *dev, int new_mtu) static int gfar_change_mtu(struct net_device *dev, int new_mtu)
{ {
...@@ -1148,7 +1035,6 @@ static void gfar_timeout(struct net_device *dev) ...@@ -1148,7 +1035,6 @@ static void gfar_timeout(struct net_device *dev)
startup_gfar(dev); startup_gfar(dev);
} }
if (!netif_queue_stopped(dev))
netif_schedule(dev); netif_schedule(dev);
} }
...@@ -1315,7 +1201,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1315,7 +1201,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
#else #else
spin_lock(&priv->lock); spin_lock(&priv->lock);
gfar_clean_rx_ring(dev); gfar_clean_rx_ring(dev, priv->rx_ring_size);
/* If we are coalescing interrupts, update the timer */ /* If we are coalescing interrupts, update the timer */
/* Otherwise, clear it */ /* Otherwise, clear it */
...@@ -1336,7 +1222,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1336,7 +1222,7 @@ irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs)
/* gfar_process_frame() -- handle one incoming packet if skb /* gfar_process_frame() -- handle one incoming packet if skb
* isn't NULL. Try the fastroute before using the stack */ * isn't NULL. */
static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
int length) int length)
{ {
...@@ -1350,7 +1236,6 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, ...@@ -1350,7 +1236,6 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
priv->stats.rx_dropped++; priv->stats.rx_dropped++;
priv->extra_stats.rx_skbmissing++; priv->extra_stats.rx_skbmissing++;
} else { } else {
if(try_fastroute(skb, dev, length) == 0) {
/* Prep the skb for the packet */ /* Prep the skb for the packet */
skb_put(skb, length); skb_put(skb, length);
...@@ -1362,20 +1247,15 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, ...@@ -1362,20 +1247,15 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
priv->extra_stats.kernel_dropped++; priv->extra_stats.kernel_dropped++;
} }
} }
}
return 0; return 0;
} }
/* gfar_clean_rx_ring() -- Processes each frame in the rx ring /* gfar_clean_rx_ring() -- Processes each frame in the rx ring
* until all are gone (or, in the case of NAPI, the budget/quota * until the budget/quota has been reached. Returns the number
* has been reached). Returns the number of frames handled * of frames handled
*/ */
#ifdef CONFIG_GFAR_NAPI
static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit) static int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit)
#else
static int gfar_clean_rx_ring(struct net_device *dev)
#endif
{ {
struct rxbd8 *bdp; struct rxbd8 *bdp;
struct sk_buff *skb; struct sk_buff *skb;
...@@ -1386,12 +1266,7 @@ static int gfar_clean_rx_ring(struct net_device *dev) ...@@ -1386,12 +1266,7 @@ static int gfar_clean_rx_ring(struct net_device *dev)
/* Get the first full descriptor */ /* Get the first full descriptor */
bdp = priv->cur_rx; bdp = priv->cur_rx;
#ifdef CONFIG_GFAR_NAPI while (!((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))) {
#define GFAR_RXDONE() ((bdp->status & RXBD_EMPTY) || (--rx_work_limit < 0))
#else
#define GFAR_RXDONE() (bdp->status & RXBD_EMPTY)
#endif
while (!GFAR_RXDONE()) {
skb = priv->rx_skbuff[priv->skb_currx]; skb = priv->rx_skbuff[priv->skb_currx];
if (!(bdp->status & if (!(bdp->status &
...@@ -1407,7 +1282,6 @@ static int gfar_clean_rx_ring(struct net_device *dev) ...@@ -1407,7 +1282,6 @@ static int gfar_clean_rx_ring(struct net_device *dev)
gfar_process_frame(dev, skb, pkt_len); gfar_process_frame(dev, skb, pkt_len);
priv->stats.rx_bytes += pkt_len; priv->stats.rx_bytes += pkt_len;
} else { } else {
count_errors(bdp->status, priv); count_errors(bdp->status, priv);
...@@ -1462,7 +1336,6 @@ static int gfar_poll(struct net_device *dev, int *budget) ...@@ -1462,7 +1336,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
if (rx_work_limit > dev->quota) if (rx_work_limit > dev->quota)
rx_work_limit = dev->quota; rx_work_limit = dev->quota;
spin_lock(&priv->lock);
howmany = gfar_clean_rx_ring(dev, rx_work_limit); howmany = gfar_clean_rx_ring(dev, rx_work_limit);
dev->quota -= howmany; dev->quota -= howmany;
...@@ -1489,8 +1362,6 @@ static int gfar_poll(struct net_device *dev, int *budget) ...@@ -1489,8 +1362,6 @@ static int gfar_poll(struct net_device *dev, int *budget)
priv->rxclean = 1; priv->rxclean = 1;
} }
spin_unlock(priv->lock);
return (rx_work_limit < 0) ? 1 : 0; return (rx_work_limit < 0) ? 1 : 0;
} }
#endif #endif
...@@ -1586,10 +1457,14 @@ static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1586,10 +1457,14 @@ static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
struct net_device *dev = (struct net_device *) dev_id; struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
/* Run the commands which acknowledge the interrupt */ /* Clear the interrupt */
phy_run_commands(dev, priv->phyinfo->ack_int); mii_clear_phy_interrupt(priv->mii_info);
/* Schedule the bottom half */ /* Disable PHY interrupts */
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_DISABLED);
/* Schedule the phy change */
schedule_work(&priv->tq); schedule_work(&priv->tq);
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -1600,18 +1475,24 @@ static void gfar_phy_change(void *data) ...@@ -1600,18 +1475,24 @@ static void gfar_phy_change(void *data)
{ {
struct net_device *dev = (struct net_device *) data; struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
int timeout = HZ / 1000 + 1; int result = 0;
/* Delay to give the PHY a chance to change the /* Delay to give the PHY a chance to change the
* register state */ * register state */
set_current_state(TASK_UNINTERRUPTIBLE); msleep(1);
schedule_timeout(timeout);
/* Run the commands which check the link state */ /* Update the link, speed, duplex */
phy_run_commands(dev, priv->phyinfo->handle_int); result = priv->mii_info->phyinfo->read_status(priv->mii_info);
/* React to the change in state */ /* Adjust the known status as long as the link
* isn't still coming up */
if((0 == result) || (priv->mii_info->link == 0))
adjust_link(dev); adjust_link(dev);
/* Reenable interrupts, if needed */
if (priv->einfo->flags & GFAR_HAS_PHY_INTR)
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_ENABLED);
} }
/* Called every so often on systems that don't interrupt /* Called every so often on systems that don't interrupt
...@@ -1623,7 +1504,72 @@ static void gfar_phy_timer(unsigned long data) ...@@ -1623,7 +1504,72 @@ static void gfar_phy_timer(unsigned long data)
schedule_work(&priv->tq); schedule_work(&priv->tq);
mod_timer(&priv->phy_info_timer, jiffies + 2 * HZ); mod_timer(&priv->phy_info_timer, jiffies +
GFAR_PHY_CHANGE_TIME * HZ);
}
/* Keep trying aneg for some time
* If, after GFAR_AN_TIMEOUT seconds, it has not
* finished, we switch to forced.
* Either way, once the process has completed, we either
* request the interrupt, or switch the timer over to
* using gfar_phy_timer to check status */
static void gfar_phy_startup_timer(unsigned long data)
{
int result;
static int secondary = GFAR_AN_TIMEOUT;
struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
struct gfar_private *priv = netdev_priv(mii_info->dev);
/* Configure the Auto-negotiation */
result = mii_info->phyinfo->config_aneg(mii_info);
/* If autonegotiation failed to start, and
* we haven't timed out, reset the timer, and return */
if (result && secondary--) {
mod_timer(&priv->phy_info_timer, jiffies + HZ);
return;
} else if (result) {
/* Couldn't start autonegotiation.
* Try switching to forced */
mii_info->autoneg = 0;
result = mii_info->phyinfo->config_aneg(mii_info);
/* Forcing failed! Give up */
if(result) {
printk(KERN_ERR "%s: Forcing failed!\n",
mii_info->dev->name);
return;
}
}
/* Kill the timer so it can be restarted */
del_timer_sync(&priv->phy_info_timer);
/* Grab the PHY interrupt, if necessary/possible */
if (priv->einfo->flags & GFAR_HAS_PHY_INTR) {
if (request_irq(priv->einfo->interruptPHY,
phy_interrupt,
SA_SHIRQ,
"phy_interrupt",
mii_info->dev) < 0) {
printk(KERN_ERR "%s: Can't get IRQ %d (PHY)\n",
mii_info->dev->name,
priv->einfo->interruptPHY);
} else {
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_ENABLED);
return;
}
}
/* Start the timer again, this time in order to
* handle a change in status */
init_timer(&priv->phy_info_timer);
priv->phy_info_timer.function = &gfar_phy_timer;
priv->phy_info_timer.data = (unsigned long) mii_info->dev;
mod_timer(&priv->phy_info_timer, jiffies +
GFAR_PHY_CHANGE_TIME * HZ);
} }
/* Called every time the controller might need to be made /* Called every time the controller might need to be made
...@@ -1637,12 +1583,13 @@ static void adjust_link(struct net_device *dev) ...@@ -1637,12 +1583,13 @@ static void adjust_link(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
struct gfar *regs = priv->regs; struct gfar *regs = priv->regs;
u32 tempval; u32 tempval;
struct gfar_mii_info *mii_info = priv->mii_info;
if (priv->link) { if (mii_info->link) {
/* Now we make sure that we can be in full duplex mode. /* Now we make sure that we can be in full duplex mode.
* If not, we operate in half-duplex mode. */ * If not, we operate in half-duplex mode. */
if (priv->duplexity != priv->olddplx) { if (mii_info->duplex != priv->oldduplex) {
if (!(priv->duplexity)) { if (!(mii_info->duplex)) {
tempval = gfar_read(&regs->maccfg2); tempval = gfar_read(&regs->maccfg2);
tempval &= ~(MACCFG2_FULL_DUPLEX); tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(&regs->maccfg2, tempval); gfar_write(&regs->maccfg2, tempval);
...@@ -1658,11 +1605,11 @@ static void adjust_link(struct net_device *dev) ...@@ -1658,11 +1605,11 @@ static void adjust_link(struct net_device *dev)
dev->name); dev->name);
} }
priv->olddplx = priv->duplexity; priv->oldduplex = mii_info->duplex;
} }
if (priv->speed != priv->oldspeed) { if (mii_info->speed != priv->oldspeed) {
switch (priv->speed) { switch (mii_info->speed) {
case 1000: case 1000:
tempval = gfar_read(&regs->maccfg2); tempval = gfar_read(&regs->maccfg2);
tempval = tempval =
...@@ -1679,14 +1626,14 @@ static void adjust_link(struct net_device *dev) ...@@ -1679,14 +1626,14 @@ static void adjust_link(struct net_device *dev)
default: default:
printk(KERN_WARNING printk(KERN_WARNING
"%s: Ack! Speed (%d) is not 10/100/1000!\n", "%s: Ack! Speed (%d) is not 10/100/1000!\n",
dev->name, priv->speed); dev->name, mii_info->speed);
break; break;
} }
printk(KERN_INFO "%s: Speed %dBT\n", dev->name, printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
priv->speed); mii_info->speed);
priv->oldspeed = priv->speed; priv->oldspeed = mii_info->speed;
} }
if (!priv->oldlink) { if (!priv->oldlink) {
...@@ -1700,7 +1647,7 @@ static void adjust_link(struct net_device *dev) ...@@ -1700,7 +1647,7 @@ static void adjust_link(struct net_device *dev)
printk(KERN_INFO "%s: Link is down\n", dev->name); printk(KERN_INFO "%s: Link is down\n", dev->name);
priv->oldlink = 0; priv->oldlink = 0;
priv->oldspeed = 0; priv->oldspeed = 0;
priv->olddplx = -1; priv->oldduplex = -1;
netif_carrier_off(dev); netif_carrier_off(dev);
} }
} }
...@@ -1900,11 +1847,7 @@ static int __init gfar_init(void) ...@@ -1900,11 +1847,7 @@ static int __init gfar_init(void)
int rc; int rc;
rc = ocp_register_driver(&gfar_driver); rc = ocp_register_driver(&gfar_driver);
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
if (rc != 0) { if (rc != 0) {
#else
if (rc == 0) {
#endif
ocp_unregister_driver(&gfar_driver); ocp_unregister_driver(&gfar_driver);
return -ENODEV; return -ENODEV;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Andy Fleming * Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com) * Maintainer: Kumar Gala (kumar.gala@freescale.com)
* *
* Copyright 2004 Freescale Semiconductor, Inc * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
* *
* Still left to do: * Still left to do:
* -Add support for module parameters * -Add support for module parameters
* -Add support for ethtool -s
* -Add patch for ethtool phys id
*/ */
#ifndef __GIANFAR_H #ifndef __GIANFAR_H
#define __GIANFAR_H #define __GIANFAR_H
...@@ -42,15 +44,7 @@ ...@@ -42,15 +44,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,41)
#include <linux/workqueue.h> #include <linux/workqueue.h>
#else
#include <linux/tqueue.h>
#define work_struct tq_struct
#define schedule_work schedule_task
#endif
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <asm/ocp.h> #include <asm/ocp.h>
...@@ -70,8 +64,13 @@ ...@@ -70,8 +64,13 @@
#define MAC_ADDR_LEN 6 #define MAC_ADDR_LEN 6
extern char gfar_driver_name[]; #define PHY_INIT_TIMEOUT 100000
extern char gfar_driver_version[]; #define GFAR_PHY_CHANGE_TIME 2
#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, "
#define DRV_NAME "gfar-enet"
extern const char gfar_driver_name[];
extern const char gfar_driver_version[];
/* These need to be powers of 2 for this driver */ /* These need to be powers of 2 for this driver */
#ifdef CONFIG_GFAR_NAPI #ifdef CONFIG_GFAR_NAPI
...@@ -105,11 +104,13 @@ extern char gfar_driver_version[]; ...@@ -105,11 +104,13 @@ extern char gfar_driver_version[];
#define GFAR_100_TIME 2560 #define GFAR_100_TIME 2560
#define GFAR_10_TIME 25600 #define GFAR_10_TIME 25600
#define DEFAULT_TX_COALESCE 1
#define DEFAULT_TXCOUNT 16 #define DEFAULT_TXCOUNT 16
#define DEFAULT_TXTIME 32768 #define DEFAULT_TXTIME 400
#define DEFAULT_RX_COALESCE 1
#define DEFAULT_RXCOUNT 16 #define DEFAULT_RXCOUNT 16
#define DEFAULT_RXTIME 32768 #define DEFAULT_RXTIME 400
#define TBIPA_VALUE 0x1f #define TBIPA_VALUE 0x1f
#define MIIMCFG_INIT_VALUE 0x00000007 #define MIIMCFG_INIT_VALUE 0x00000007
...@@ -467,8 +468,7 @@ struct gfar { ...@@ -467,8 +468,7 @@ struct gfar {
* empty and completely full conditions. The empty/ready indicator in * empty and completely full conditions. The empty/ready indicator in
* the buffer descriptor determines the actual condition. * the buffer descriptor determines the actual condition.
*/ */
struct gfar_private struct gfar_private {
{
/* pointers to arrays of skbuffs for tx and rx */ /* pointers to arrays of skbuffs for tx and rx */
struct sk_buff ** tx_skbuff; struct sk_buff ** tx_skbuff;
struct sk_buff ** rx_skbuff; struct sk_buff ** rx_skbuff;
...@@ -496,7 +496,6 @@ struct gfar_private ...@@ -496,7 +496,6 @@ struct gfar_private
struct txbd8 *cur_tx; /* Next free ring entry */ struct txbd8 *cur_tx; /* Next free ring entry */
struct txbd8 *dirty_tx; /* The Ring entry to be freed. */ struct txbd8 *dirty_tx; /* The Ring entry to be freed. */
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */ struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
struct phy_info *phyinfo;
struct gfar *phyregs; struct gfar *phyregs;
struct work_struct tq; struct work_struct tq;
struct timer_list phy_info_timer; struct timer_list phy_info_timer;
...@@ -509,15 +508,14 @@ struct gfar_private ...@@ -509,15 +508,14 @@ struct gfar_private
unsigned int rx_ring_size; unsigned int rx_ring_size;
wait_queue_head_t rxcleanupq; wait_queue_head_t rxcleanupq;
unsigned int rxclean; unsigned int rxclean;
int link; /* current link state */
int oldlink;
int duplexity; /* Indicates negotiated duplex state */
int olddplx;
int speed; /* Indicates negotiated speed */
int oldspeed;
/* Info structure initialized by board setup code */ /* Info structure initialized by board setup code */
struct ocp_gfar_data *einfo; struct ocp_gfar_data *einfo;
struct gfar_mii_info *mii_info;
int oldspeed;
int oldduplex;
int oldlink;
}; };
extern inline u32 gfar_read(volatile unsigned *addr) extern inline u32 gfar_read(volatile unsigned *addr)
...@@ -532,6 +530,6 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val) ...@@ -532,6 +530,6 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val)
out_be32(addr, val); out_be32(addr, val);
} }
extern struct ethtool_ops *gfar_op_array[];
#endif /* __GIANFAR_H */ #endif /* __GIANFAR_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Andy Fleming * Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com) * Maintainer: Kumar Gala (kumar.gala@freescale.com)
* *
* Copyright 2004 Freescale Semiconductor, Inc * Copyright (c) 2003,2004 Freescale Semiconductor, Inc.
* *
* This software may be used and distributed according to * This software may be used and distributed according to
* the terms of the GNU Public License, Version 2, incorporated herein * the terms of the GNU Public License, Version 2, incorporated herein
...@@ -58,64 +58,64 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals); ...@@ -58,64 +58,64 @@ int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals);
void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo); void gfar_gdrvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo);
static char stat_gstrings[][ETH_GSTRING_LEN] = { static char stat_gstrings[][ETH_GSTRING_LEN] = {
"RX Dropped by Kernel", "rx-dropped-by-kernel",
"RX Large Frame Errors", "rx-large-frame-errors",
"RX Short Frame Errors", "rx-short-frame-errors",
"RX Non-Octet Errors", "rx-non-octet-errors",
"RX CRC Errors", "rx-crc-errors",
"RX Overrun Errors", "rx-overrun-errors",
"RX Busy Errors", "rx-busy-errors",
"RX Babbling Errors", "rx-babbling-errors",
"RX Truncated Frames", "rx-truncated-frames",
"Ethernet Bus Error", "ethernet-bus-error",
"TX Babbling Errors", "tx-babbling-errors",
"TX Underrun Errors", "tx-underrun-errors",
"RX SKB Missing Errors", "rx-skb-missing-errors",
"TX Timeout Errors", "tx-timeout-errors",
"tx&rx 64B frames", "tx-rx-64-frames",
"tx&rx 65-127B frames", "tx-rx-65-127-frames",
"tx&rx 128-255B frames", "tx-rx-128-255-frames",
"tx&rx 256-511B frames", "tx-rx-256-511-frames",
"tx&rx 512-1023B frames", "tx-rx-512-1023-frames",
"tx&rx 1024-1518B frames", "tx-rx-1024-1518-frames",
"tx&rx 1519-1522B Good VLAN", "tx-rx-1519-1522-good-vlan",
"RX bytes", "rx-bytes",
"RX Packets", "rx-packets",
"RX FCS Errors", "rx-fcs-errors",
"Receive Multicast Packet", "receive-multicast-packet",
"Receive Broadcast Packet", "receive-broadcast-packet",
"RX Control Frame Packets", "rx-control-frame-packets",
"RX Pause Frame Packets", "rx-pause-frame-packets",
"RX Unknown OP Code", "rx-unknown-op-code",
"RX Alignment Error", "rx-alignment-error",
"RX Frame Length Error", "rx-frame-length-error",
"RX Code Error", "rx-code-error",
"RX Carrier Sense Error", "rx-carrier-sense-error",
"RX Undersize Packets", "rx-undersize-packets",
"RX Oversize Packets", "rx-oversize-packets",
"RX Fragmented Frames", "rx-fragmented-frames",
"RX Jabber Frames", "rx-jabber-frames",
"RX Dropped Frames", "rx-dropped-frames",
"TX Byte Counter", "tx-byte-counter",
"TX Packets", "tx-packets",
"TX Multicast Packets", "tx-multicast-packets",
"TX Broadcast Packets", "tx-broadcast-packets",
"TX Pause Control Frames", "tx-pause-control-frames",
"TX Deferral Packets", "tx-deferral-packets",
"TX Excessive Deferral Packets", "tx-excessive-deferral-packets",
"TX Single Collision Packets", "tx-single-collision-packets",
"TX Multiple Collision Packets", "tx-multiple-collision-packets",
"TX Late Collision Packets", "tx-late-collision-packets",
"TX Excessive Collision Packets", "tx-excessive-collision-packets",
"TX Total Collision", "tx-total-collision",
"RESERVED", "reserved",
"TX Dropped Frames", "tx-dropped-frames",
"TX Jabber Frames", "tx-jabber-frames",
"TX FCS Errors", "tx-fcs-errors",
"TX Control Frames", "tx-control-frames",
"TX Oversize Frames", "tx-oversize-frames",
"TX Undersize Frames", "tx-undersize-frames",
"TX Fragmented Frames", "tx-fragmented-frames",
}; };
/* Fill in an array of 64-bit statistics from various sources. /* Fill in an array of 64-bit statistics from various sources.
...@@ -125,7 +125,7 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = { ...@@ -125,7 +125,7 @@ static char stat_gstrings[][ETH_GSTRING_LEN] = {
void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf) void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, u64 * buf)
{ {
int i; int i;
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
u32 *rmon = (u32 *) & priv->regs->rmon; u32 *rmon = (u32 *) & priv->regs->rmon;
u64 *extra = (u64 *) & priv->extra_stats; u64 *extra = (u64 *) & priv->extra_stats;
struct gfar_stats *stats = (struct gfar_stats *) buf; struct gfar_stats *stats = (struct gfar_stats *) buf;
...@@ -154,7 +154,7 @@ void gfar_fill_stats_normon(struct net_device *dev, ...@@ -154,7 +154,7 @@ void gfar_fill_stats_normon(struct net_device *dev,
struct ethtool_stats *dummy, u64 * buf) struct ethtool_stats *dummy, u64 * buf)
{ {
int i; int i;
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
u64 *extra = (u64 *) & priv->extra_stats; u64 *extra = (u64 *) & priv->extra_stats;
for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) { for (i = 0; i < GFAR_EXTRA_STATS_LEN; i++) {
...@@ -171,7 +171,7 @@ int gfar_stats_count_normon(struct net_device *dev) ...@@ -171,7 +171,7 @@ int gfar_stats_count_normon(struct net_device *dev)
void gfar_gdrvinfo(struct net_device *dev, struct void gfar_gdrvinfo(struct net_device *dev, struct
ethtool_drvinfo *drvinfo) ethtool_drvinfo *drvinfo)
{ {
strncpy(drvinfo->driver, gfar_driver_name, GFAR_INFOSTR_LEN); strncpy(drvinfo->driver, DRV_NAME, GFAR_INFOSTR_LEN);
strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN); strncpy(drvinfo->version, gfar_driver_version, GFAR_INFOSTR_LEN);
strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN); strncpy(drvinfo->fw_version, "N/A", GFAR_INFOSTR_LEN);
strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN); strncpy(drvinfo->bus_info, "N/A", GFAR_INFOSTR_LEN);
...@@ -184,7 +184,7 @@ void gfar_gdrvinfo(struct net_device *dev, struct ...@@ -184,7 +184,7 @@ void gfar_gdrvinfo(struct net_device *dev, struct
/* Return the current settings in the ethtool_cmd structure */ /* Return the current settings in the ethtool_cmd structure */
int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support = uint gigabit_support =
priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0; priv->einfo->flags & GFAR_HAS_GIGABIT ? SUPPORTED_1000baseT_Full : 0;
uint gigabit_advert = uint gigabit_advert =
...@@ -201,10 +201,10 @@ int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) ...@@ -201,10 +201,10 @@ int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
| ADVERTISED_100baseT_Full | ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg); | gigabit_advert | ADVERTISED_Autoneg);
cmd->speed = priv->speed; cmd->speed = priv->mii_info->speed;
cmd->duplex = priv->duplexity; cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII; cmd->port = PORT_MII;
cmd->phy_address = priv->einfo->phyid; cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL; cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE; cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount; cmd->maxtxpkt = priv->txcount;
...@@ -223,7 +223,7 @@ int gfar_reglen(struct net_device *dev) ...@@ -223,7 +223,7 @@ int gfar_reglen(struct net_device *dev)
void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf) void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regbuf)
{ {
int i; int i;
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
u32 *theregs = (u32 *) priv->regs; u32 *theregs = (u32 *) priv->regs;
u32 *buf = (u32 *) regbuf; u32 *buf = (u32 *) regbuf;
...@@ -231,13 +231,6 @@ void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regb ...@@ -231,13 +231,6 @@ void gfar_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *regb
buf[i] = theregs[i]; buf[i] = theregs[i];
} }
/* Return the link state 1 is up, 0 is down */
u32 gfar_get_link(struct net_device *dev)
{
struct gfar_private *priv = (struct gfar_private *) dev->priv;
return (u32) priv->link;
}
/* Fill in a buffer with the strings which correspond to the /* Fill in a buffer with the strings which correspond to the
* stats */ * stats */
void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf) void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf)
...@@ -252,7 +245,7 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use ...@@ -252,7 +245,7 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use
unsigned int count; unsigned int count;
/* The timer is different, depending on the interface speed */ /* The timer is different, depending on the interface speed */
switch (priv->speed) { switch (priv->mii_info->speed) {
case 1000: case 1000:
count = GFAR_GBIT_TIME; count = GFAR_GBIT_TIME;
break; break;
...@@ -276,7 +269,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic ...@@ -276,7 +269,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
unsigned int count; unsigned int count;
/* The timer is different, depending on the interface speed */ /* The timer is different, depending on the interface speed */
switch (priv->speed) { switch (priv->mii_info->speed) {
case 1000: case 1000:
count = GFAR_GBIT_TIME; count = GFAR_GBIT_TIME;
break; break;
...@@ -298,7 +291,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic ...@@ -298,7 +291,7 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic
* structure. */ * structure. */
int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime); cvals->rx_coalesce_usecs = gfar_ticks2usecs(priv, priv->rxtime);
cvals->rx_max_coalesced_frames = priv->rxcount; cvals->rx_max_coalesced_frames = priv->rxcount;
...@@ -344,7 +337,7 @@ int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) ...@@ -344,7 +337,7 @@ int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
*/ */
int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
/* Set up rx coalescing */ /* Set up rx coalescing */
if ((cvals->rx_coalesce_usecs == 0) || if ((cvals->rx_coalesce_usecs == 0) ||
...@@ -386,7 +379,7 @@ int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals) ...@@ -386,7 +379,7 @@ int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals)
* jumbo are ignored by the driver */ * jumbo are ignored by the driver */
void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE; rvals->rx_max_pending = GFAR_RX_MAX_RING_SIZE;
rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE; rvals->rx_mini_max_pending = GFAR_RX_MAX_RING_SIZE;
...@@ -409,7 +402,7 @@ void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals) ...@@ -409,7 +402,7 @@ void gfar_gringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals) int gfar_sringparam(struct net_device *dev, struct ethtool_ringparam *rvals)
{ {
u32 tempval; u32 tempval;
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
int err = 0; int err = 0;
if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE) if (rvals->rx_pending > GFAR_RX_MAX_RING_SIZE)
...@@ -473,7 +466,7 @@ struct ethtool_ops gfar_ethtool_ops = { ...@@ -473,7 +466,7 @@ struct ethtool_ops gfar_ethtool_ops = {
.get_drvinfo = gfar_gdrvinfo, .get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen, .get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs, .get_regs = gfar_get_regs,
.get_link = gfar_get_link, .get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce, .get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce, .set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam, .get_ringparam = gfar_gringparam,
...@@ -482,3 +475,51 @@ struct ethtool_ops gfar_ethtool_ops = { ...@@ -482,3 +475,51 @@ struct ethtool_ops gfar_ethtool_ops = {
.get_stats_count = gfar_stats_count, .get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats, .get_ethtool_stats = gfar_fill_stats,
}; };
struct ethtool_ops gfar_normon_nocoalesce_ethtool_ops = {
.get_settings = gfar_gsettings,
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
.get_link = ethtool_op_get_link,
.get_ringparam = gfar_gringparam,
.set_ringparam = gfar_sringparam,
.get_strings = gfar_gstrings_normon,
.get_stats_count = gfar_stats_count_normon,
.get_ethtool_stats = gfar_fill_stats_normon,
};
struct ethtool_ops gfar_nocoalesce_ethtool_ops = {
.get_settings = gfar_gsettings,
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
.get_link = ethtool_op_get_link,
.get_ringparam = gfar_gringparam,
.set_ringparam = gfar_sringparam,
.get_strings = gfar_gstrings,
.get_stats_count = gfar_stats_count,
.get_ethtool_stats = gfar_fill_stats,
};
struct ethtool_ops gfar_normon_ethtool_ops = {
.get_settings = gfar_gsettings,
.get_drvinfo = gfar_gdrvinfo,
.get_regs_len = gfar_reglen,
.get_regs = gfar_get_regs,
.get_link = ethtool_op_get_link,
.get_coalesce = gfar_gcoalesce,
.set_coalesce = gfar_scoalesce,
.get_ringparam = gfar_gringparam,
.set_ringparam = gfar_sringparam,
.get_strings = gfar_gstrings_normon,
.get_stats_count = gfar_stats_count_normon,
.get_ethtool_stats = gfar_fill_stats_normon,
};
struct ethtool_ops *gfar_op_array[] = {
&gfar_ethtool_ops,
&gfar_normon_ethtool_ops,
&gfar_nocoalesce_ethtool_ops,
&gfar_normon_nocoalesce_ethtool_ops
};
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Andy Fleming * Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com) * Maintainer: Kumar Gala (kumar.gala@freescale.com)
* *
* Copyright 2004 Freescale Semiconductor, Inc * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -38,21 +38,31 @@ ...@@ -38,21 +38,31 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/version.h> #include <linux/version.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/mii.h>
#include "gianfar.h" #include "gianfar.h"
#include "gianfar_phy.h" #include "gianfar_phy.h"
static void config_genmii_advert(struct gfar_mii_info *mii_info);
static void genmii_setup_forced(struct gfar_mii_info *mii_info);
static void genmii_restart_aneg(struct gfar_mii_info *mii_info);
static int gbit_config_aneg(struct gfar_mii_info *mii_info);
static int genmii_config_aneg(struct gfar_mii_info *mii_info);
static int genmii_update_link(struct gfar_mii_info *mii_info);
static int genmii_read_status(struct gfar_mii_info *mii_info);
u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum);
void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val);
/* Write value to the PHY for this device to the register at regnum, */ /* Write value to the PHY for this device to the register at regnum, */
/* waiting until the write is done before it returns. All PHY */ /* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */ /* configuration has to be done through the TSEC1 MIIM regs */
void write_phy_reg(struct net_device *dev, u16 regnum, u16 value) void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs; struct gfar *regbase = priv->phyregs;
struct ocp_gfar_data *einfo = priv->einfo;
/* Set the PHY address and the register address we want to write */ /* Set the PHY address and the register address we want to write */
gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum); gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */ /* Write out the value we want */
gfar_write(&regbase->miimcon, value); gfar_write(&regbase->miimcon, value);
...@@ -65,19 +75,18 @@ void write_phy_reg(struct net_device *dev, u16 regnum, u16 value) ...@@ -65,19 +75,18 @@ void write_phy_reg(struct net_device *dev, u16 regnum, u16 value)
/* Reads from register regnum in the PHY for device dev, */ /* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */ /* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */ /* configuration has to be done through the TSEC1 MIIM regs */
u16 read_phy_reg(struct net_device *dev, u16 regnum) int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs; struct gfar *regbase = priv->phyregs;
struct ocp_gfar_data *einfo = priv->einfo;
u16 value; u16 value;
/* Set the PHY address and the register address we want to read */ /* Set the PHY address and the register address we want to read */
gfar_write(&regbase->miimadd, ((einfo->phyid) << 8) | regnum); gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */ /* Clear miimcom, and then initiate a read */
gfar_write(&regbase->miimcom, 0); gfar_write(&regbase->miimcom, 0);
gfar_write(&regbase->miimcom, MIIM_READ_COMMAND); gfar_write(&regbase->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */ /* Wait for the transaction to finish */
while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY)) while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
...@@ -89,362 +98,557 @@ u16 read_phy_reg(struct net_device *dev, u16 regnum) ...@@ -89,362 +98,557 @@ u16 read_phy_reg(struct net_device *dev, u16 regnum)
return value; return value;
} }
/* returns which value to write to the control register. */ void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
/* For 10/100 the value is slightly different. */
u16 mii_cr_init(u16 mii_reg, struct net_device * dev)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; if(mii_info->phyinfo->ack_interrupt)
struct ocp_gfar_data *einfo = priv->einfo; mii_info->phyinfo->ack_interrupt(mii_info);
}
if (einfo->flags & GFAR_HAS_GIGABIT)
return MIIM_CONTROL_INIT; void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
else {
return MIIM_CR_INIT; mii_info->interrupts = interrupts;
if(mii_info->phyinfo->config_intr)
mii_info->phyinfo->config_intr(mii_info);
} }
#define BRIEF_GFAR_ERRORS
/* Wait for auto-negotiation to complete */ /* Writes MII_ADVERTISE with the appropriate values, after
u16 mii_parse_sr(u16 mii_reg, struct net_device * dev) * sanitizing advertise to make sure only supported features
* are advertised
*/
static void config_genmii_advert(struct gfar_mii_info *mii_info)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; u32 advertise;
u16 adv;
/* Only allow advertising what this PHY supports */
mii_info->advertising &= mii_info->phyinfo->features;
advertise = mii_info->advertising;
/* Setup standard advertisement */
adv = phy_read(mii_info, MII_ADVERTISE);
adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
if (advertise & ADVERTISED_10baseT_Half)
adv |= ADVERTISE_10HALF;
if (advertise & ADVERTISED_10baseT_Full)
adv |= ADVERTISE_10FULL;
if (advertise & ADVERTISED_100baseT_Half)
adv |= ADVERTISE_100HALF;
if (advertise & ADVERTISED_100baseT_Full)
adv |= ADVERTISE_100FULL;
phy_write(mii_info, MII_ADVERTISE, adv);
}
unsigned int timeout = GFAR_AN_TIMEOUT; static void genmii_setup_forced(struct gfar_mii_info *mii_info)
{
u16 ctrl;
u32 features = mii_info->phyinfo->features;
if (mii_reg & MIIM_STATUS_LINK) ctrl = phy_read(mii_info, MII_BMCR);
priv->link = 1;
else
priv->link = 0;
/* Only auto-negotiate if the link has just gone up */ ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
if (priv->link && !priv->oldlink) { ctrl |= BMCR_RESET;
while ((!(mii_reg & MIIM_STATUS_AN_DONE)) && timeout--)
mii_reg = read_phy_reg(dev, MIIM_STATUS);
#if defined(BRIEF_GFAR_ERRORS) switch(mii_info->speed) {
if (mii_reg & MIIM_STATUS_AN_DONE) case SPEED_1000:
printk(KERN_INFO "%s: Auto-negotiation done\n", if(features & (SUPPORTED_1000baseT_Half
dev->name); | SUPPORTED_1000baseT_Full)) {
else ctrl |= BMCR_SPEED1000;
printk(KERN_INFO "%s: Auto-negotiation timed out\n", break;
dev->name); }
#endif mii_info->speed = SPEED_100;
case SPEED_100:
if (features & (SUPPORTED_100baseT_Half
| SUPPORTED_100baseT_Full)) {
ctrl |= BMCR_SPEED100;
break;
} }
mii_info->speed = SPEED_10;
case SPEED_10:
if (features & (SUPPORTED_10baseT_Half
| SUPPORTED_10baseT_Full))
break;
default: /* Unsupported speed! */
printk(KERN_ERR "%s: Bad speed!\n",
mii_info->dev->name);
break;
}
phy_write(mii_info, MII_BMCR, ctrl);
}
/* Enable and Restart Autonegotiation */
static void genmii_restart_aneg(struct gfar_mii_info *mii_info)
{
u16 ctl;
ctl = phy_read(mii_info, MII_BMCR);
ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
phy_write(mii_info, MII_BMCR, ctl);
}
static int gbit_config_aneg(struct gfar_mii_info *mii_info)
{
u16 adv;
u32 advertise;
if(mii_info->autoneg) {
/* Configure the ADVERTISE register */
config_genmii_advert(mii_info);
advertise = mii_info->advertising;
adv = phy_read(mii_info, MII_1000BASETCONTROL);
adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP |
MII_1000BASETCONTROL_HALFDUPLEXCAP);
if (advertise & SUPPORTED_1000baseT_Half)
adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP;
if (advertise & SUPPORTED_1000baseT_Full)
adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP;
phy_write(mii_info, MII_1000BASETCONTROL, adv);
/* Start/Restart aneg */
genmii_restart_aneg(mii_info);
} else
genmii_setup_forced(mii_info);
return 0;
}
static int marvell_config_aneg(struct gfar_mii_info *mii_info)
{
/* The Marvell PHY has an errata which requires
* that certain registers get written in order
* to restart autonegotiation */
phy_write(mii_info, MII_BMCR, BMCR_RESET);
phy_write(mii_info, 0x1d, 0x1f);
phy_write(mii_info, 0x1e, 0x200c);
phy_write(mii_info, 0x1d, 0x5);
phy_write(mii_info, 0x1e, 0);
phy_write(mii_info, 0x1e, 0x100);
gbit_config_aneg(mii_info);
return 0;
}
static int genmii_config_aneg(struct gfar_mii_info *mii_info)
{
if (mii_info->autoneg) {
config_genmii_advert(mii_info);
genmii_restart_aneg(mii_info);
} else
genmii_setup_forced(mii_info);
return 0; return 0;
} }
/* Determine the speed and duplex which was negotiated */
u16 mii_parse_88E1011_psr(u16 mii_reg, struct net_device * dev) static int genmii_update_link(struct gfar_mii_info *mii_info)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; u16 status;
unsigned int speed;
/* Do a fake read */
phy_read(mii_info, MII_BMSR);
if (priv->link) { /* Read link and autonegotiation status */
if (mii_reg & MIIM_88E1011_PHYSTAT_DUPLEX) status = phy_read(mii_info, MII_BMSR);
priv->duplexity = 1; if ((status & BMSR_LSTATUS) == 0)
mii_info->link = 0;
else else
priv->duplexity = 0; mii_info->link = 1;
speed = (mii_reg & MIIM_88E1011_PHYSTAT_SPEED); /* If we are autonegotiating, and not done,
* return an error */
if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
return -EAGAIN;
switch (speed) { return 0;
case MIIM_88E1011_PHYSTAT_GBIT: }
priv->speed = 1000;
static int genmii_read_status(struct gfar_mii_info *mii_info)
{
u16 status;
int err;
/* Update the link, but return if there
* was an error */
err = genmii_update_link(mii_info);
if (err)
return err;
if (mii_info->autoneg) {
status = phy_read(mii_info, MII_LPA);
if (status & (LPA_10FULL | LPA_100FULL))
mii_info->duplex = DUPLEX_FULL;
else
mii_info->duplex = DUPLEX_HALF;
if (status & (LPA_100FULL | LPA_100HALF))
mii_info->speed = SPEED_100;
else
mii_info->speed = SPEED_10;
mii_info->pause = 0;
}
/* On non-aneg, we assume what we put in BMCR is the speed,
* though magic-aneg shouldn't prevent this case from occurring
*/
return 0;
}
static int marvell_read_status(struct gfar_mii_info *mii_info)
{
u16 status;
int err;
/* Update the link, but return if there
* was an error */
err = genmii_update_link(mii_info);
if (err)
return err;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if (mii_info->autoneg && mii_info->link) {
int speed;
status = phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS);
#if 0
/* If speed and duplex aren't resolved,
* return an error. Isn't this handled
* by checking aneg?
*/
if ((status & MII_M1011_PHY_SPEC_STATUS_RESOLVED) == 0)
return -EAGAIN;
#endif
/* Get the duplexity */
if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX)
mii_info->duplex = DUPLEX_FULL;
else
mii_info->duplex = DUPLEX_HALF;
/* Get the speed */
speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK;
switch(speed) {
case MII_M1011_PHY_SPEC_STATUS_1000:
mii_info->speed = SPEED_1000;
break; break;
case MIIM_88E1011_PHYSTAT_100: case MII_M1011_PHY_SPEC_STATUS_100:
priv->speed = 100; mii_info->speed = SPEED_100;
break; break;
default: default:
priv->speed = 10; mii_info->speed = SPEED_10;
break; break;
} }
} else { mii_info->pause = 0;
priv->speed = 0;
priv->duplexity = 0;
} }
return 0; return 0;
} }
u16 mii_parse_cis8201(u16 mii_reg, struct net_device * dev)
{
struct gfar_private *priv = (struct gfar_private *) dev->priv;
unsigned int speed;
if (priv->link) { static int cis820x_read_status(struct gfar_mii_info *mii_info)
if (mii_reg & MIIM_CIS8201_AUXCONSTAT_DUPLEX) {
priv->duplexity = 1; u16 status;
int err;
/* Update the link, but return if there
* was an error */
err = genmii_update_link(mii_info);
if (err)
return err;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if (mii_info->autoneg && mii_info->link) {
int speed;
status = phy_read(mii_info, MII_CIS8201_AUX_CONSTAT);
if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
mii_info->duplex = DUPLEX_FULL;
else else
priv->duplexity = 0; mii_info->duplex = DUPLEX_HALF;
speed = mii_reg & MIIM_CIS8201_AUXCONSTAT_SPEED; speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) { switch (speed) {
case MIIM_CIS8201_AUXCONSTAT_GBIT: case MII_CIS8201_AUXCONSTAT_GBIT:
priv->speed = 1000; mii_info->speed = SPEED_1000;
break; break;
case MIIM_CIS8201_AUXCONSTAT_100: case MII_CIS8201_AUXCONSTAT_100:
priv->speed = 100; mii_info->speed = SPEED_100;
break; break;
default: default:
priv->speed = 10; mii_info->speed = SPEED_10;
break; break;
} }
} else {
priv->speed = 0;
priv->duplexity = 0;
} }
return 0; return 0;
} }
u16 mii_parse_dm9161_scsr(u16 mii_reg, struct net_device * dev) static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{ {
struct gfar_private *priv = (struct gfar_private *) dev->priv; /* Clear the interrupts by reading the reg */
phy_read(mii_info, MII_M1011_IEVENT);
if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H)) return 0;
priv->speed = 100; }
static int marvell_config_intr(struct gfar_mii_info *mii_info)
{
if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT);
else else
priv->speed = 10; phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F)) return 0;
priv->duplexity = 1; }
static int cis820x_init(struct gfar_mii_info *mii_info)
{
phy_write(mii_info, MII_CIS8201_AUX_CONSTAT,
MII_CIS8201_AUXCONSTAT_INIT);
phy_write(mii_info, MII_CIS8201_EXT_CON1,
MII_CIS8201_EXTCON1_INIT);
return 0;
}
static int cis820x_ack_interrupt(struct gfar_mii_info *mii_info)
{
phy_read(mii_info, MII_CIS8201_ISTAT);
return 0;
}
static int cis820x_config_intr(struct gfar_mii_info *mii_info)
{
if(mii_info->interrupts == MII_INTERRUPT_ENABLED)
phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK);
else else
priv->duplexity = 0; phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0; return 0;
} }
u16 dm9161_wait(u16 mii_reg, struct net_device *dev) #define DM9161_DELAY 10
static int dm9161_read_status(struct gfar_mii_info *mii_info)
{ {
int timeout = HZ; u16 status;
int secondary = 10; int err;
u16 temp;
/* Update the link, but return if there
* was an error */
err = genmii_update_link(mii_info);
if (err)
return err;
/* If the link is up, read the speed and duplex */
/* If we aren't autonegotiating, assume speeds
* are as set */
if (mii_info->autoneg && mii_info->link) {
status = phy_read(mii_info, MII_DM9161_SCSR);
if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H))
mii_info->speed = SPEED_100;
else
mii_info->speed = SPEED_10;
do { if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
mii_info->duplex = DUPLEX_FULL;
else
mii_info->duplex = DUPLEX_HALF;
}
/* Davicom takes a bit to come up after a reset, return 0;
* so wait here for a bit */ }
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout(timeout);
temp = read_phy_reg(dev, MIIM_STATUS);
secondary--; static int dm9161_config_aneg(struct gfar_mii_info *mii_info)
} while ((!(temp & MIIM_STATUS_AN_DONE)) && secondary); {
struct dm9161_private *priv = mii_info->priv;
if(0 == priv->resetdone)
return -EAGAIN;
return 0; return 0;
} }
static struct phy_info phy_info_M88E1011S = { static void dm9161_timer(unsigned long data)
0x01410c6, {
"Marvell 88E1011S", struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
4, struct dm9161_private *priv = mii_info->priv;
(const struct phy_cmd[]) { /* config */ u16 status = phy_read(mii_info, MII_BMSR);
/* Reset and configure the PHY */
{MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init}, if (status & BMSR_ANEGCOMPLETE) {
{miim_end,} priv->resetdone = 1;
}, } else
(const struct phy_cmd[]) { /* startup */ mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
/* Status is read once to clear old link state */ }
{MIIM_STATUS, miim_read, NULL},
/* Auto-negotiate */
{MIIM_STATUS, miim_read, mii_parse_sr},
/* Read the status */
{MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
/* Clear the IEVENT register */
{MIIM_88E1011_IEVENT, miim_read, NULL},
/* Set up the mask */
{MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* ack_int */
/* Clear the interrupt */
{MIIM_88E1011_IEVENT, miim_read, NULL},
/* Disable interrupts */
{MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* handle_int */
/* Read the Status (2x to make sure link is right) */
{MIIM_STATUS, miim_read, NULL},
/* Check the status */
{MIIM_STATUS, miim_read, mii_parse_sr},
{MIIM_88E1011_PHY_STATUS, miim_read, mii_parse_88E1011_psr},
/* Enable Interrupts */
{MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_INIT, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* shutdown */
{MIIM_88E1011_IEVENT, miim_read, NULL},
{MIIM_88E1011_IMASK, MIIM_88E1011_IMASK_CLEAR, NULL},
{miim_end,}
},
};
/* Cicada 8204 */ static int dm9161_init(struct gfar_mii_info *mii_info)
static struct phy_info phy_info_cis8204 = { {
0x3f11, struct dm9161_private *priv;
/* Allocate the private data structure */
priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL);
if (NULL == priv)
return -ENOMEM;
mii_info->priv = priv;
/* Reset is not done yet */
priv->resetdone = 0;
/* Isolate the PHY */
phy_write(mii_info, MII_BMCR, BMCR_ISOLATE);
/* Do not bypass the scrambler/descrambler */
phy_write(mii_info, MII_DM9161_SCR, MII_DM9161_SCR_INIT);
/* Clear 10BTCSR to default */
phy_write(mii_info, MII_DM9161_10BTCSR, MII_DM9161_10BTCSR_INIT);
/* Reconnect the PHY, and enable Autonegotiation */
phy_write(mii_info, MII_BMCR, BMCR_ANENABLE);
/* Start a timer for DM9161_DELAY seconds to wait
* for the PHY to be ready */
init_timer(&priv->timer);
priv->timer.function = &dm9161_timer;
priv->timer.data = (unsigned long) mii_info;
mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
return 0;
}
static void dm9161_close(struct gfar_mii_info *mii_info)
{
struct dm9161_private *priv = mii_info->priv;
del_timer_sync(&priv->timer);
kfree(priv);
}
#if 0
static int dm9161_ack_interrupt(struct gfar_mii_info *mii_info)
{
phy_read(mii_info, MII_DM9161_INTR);
return 0;
}
#endif
/* Cicada 820x */
static struct phy_info phy_info_cis820x = {
0x000fc440,
"Cicada Cis8204", "Cicada Cis8204",
6, 0x000fffc0,
(const struct phy_cmd[]) { /* config */ .features = MII_GBIT_FEATURES,
/* Override PHY config settings */ .init = &cis820x_init,
{MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, .config_aneg = &gbit_config_aneg,
/* Set up the interface mode */ .read_status = &cis820x_read_status,
{MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL}, .ack_interrupt = &cis820x_ack_interrupt,
/* Configure some basic stuff */ .config_intr = &cis820x_config_intr,
{MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
{miim_end,}
},
(const struct phy_cmd[]) { /* startup */
/* Read the Status (2x to make sure link is right) */
{MIIM_STATUS, miim_read, NULL},
/* Auto-negotiate */
{MIIM_STATUS, miim_read, mii_parse_sr},
/* Read the status */
{MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
/* Clear the status register */
{MIIM_CIS8204_ISTAT, miim_read, NULL},
/* Enable interrupts */
{MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* ack_int */
/* Clear the status register */
{MIIM_CIS8204_ISTAT, miim_read, NULL},
/* Disable interrupts */
{MIIM_CIS8204_IMASK, 0x0, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* handle_int */
/* Read the Status (2x to make sure link is right) */
{MIIM_STATUS, miim_read, NULL},
/* Auto-negotiate */
{MIIM_STATUS, miim_read, mii_parse_sr},
/* Read the status */
{MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
/* Enable interrupts */
{MIIM_CIS8204_IMASK, MIIM_CIS8204_IMASK_MASK, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* shutdown */
/* Clear the status register */
{MIIM_CIS8204_ISTAT, miim_read, NULL},
/* Disable interrupts */
{MIIM_CIS8204_IMASK, 0x0, NULL},
{miim_end,}
},
}; };
/* Cicada 8201 */ static struct phy_info phy_info_dm9161 = {
static struct phy_info phy_info_cis8201 = { .phy_id = 0x0181b880,
0xfc41, .name = "Davicom DM9161E",
"CIS8201", .phy_id_mask = 0x0ffffff0,
4, .init = dm9161_init,
(const struct phy_cmd[]) { /* config */ .config_aneg = dm9161_config_aneg,
/* Override PHY config settings */ .read_status = dm9161_read_status,
{MIIM_CIS8201_AUX_CONSTAT, MIIM_CIS8201_AUXCONSTAT_INIT, NULL}, .close = dm9161_close,
/* Set up the interface mode */
{MIIM_CIS8201_EXT_CON1, MIIM_CIS8201_EXTCON1_INIT, NULL},
/* Configure some basic stuff */
{MIIM_CONTROL, MIIM_CONTROL_INIT, mii_cr_init},
{miim_end,}
},
(const struct phy_cmd[]) { /* startup */
/* Read the Status (2x to make sure link is right) */
{MIIM_STATUS, miim_read, NULL},
/* Auto-negotiate */
{MIIM_STATUS, miim_read, mii_parse_sr},
/* Read the status */
{MIIM_CIS8201_AUX_CONSTAT, miim_read, mii_parse_cis8201},
{miim_end,}
},
(const struct phy_cmd[]) { /* ack_int */
{miim_end,}
},
(const struct phy_cmd[]) { /* handle_int */
{miim_end,}
},
(const struct phy_cmd[]) { /* shutdown */
{miim_end,}
},
}; };
static struct phy_info phy_info_dm9161 = { static struct phy_info phy_info_marvell = {
0x0181b88, .phy_id = 0x01410c00,
"Davicom DM9161E", .phy_id_mask = 0xffffff00,
4, .name = "Marvell 88E1101",
(const struct phy_cmd[]) { /* config */ .features = MII_GBIT_FEATURES,
{MIIM_CONTROL, MIIM_DM9161_CR_STOP, NULL}, .config_aneg = &marvell_config_aneg,
/* Do not bypass the scrambler/descrambler */ .read_status = &marvell_read_status,
{MIIM_DM9161_SCR, MIIM_DM9161_SCR_INIT, NULL}, .ack_interrupt = &marvell_ack_interrupt,
/* Clear 10BTCSR to default */ .config_intr = &marvell_config_intr,
{MIIM_DM9161_10BTCSR, MIIM_DM9161_10BTCSR_INIT, NULL}, };
/* Configure some basic stuff */
{MIIM_CONTROL, MIIM_CR_INIT, NULL}, static struct phy_info phy_info_genmii= {
{miim_end,} .phy_id = 0x00000000,
}, .phy_id_mask = 0x00000000,
(const struct phy_cmd[]) { /* startup */ .name = "Generic MII",
/* Restart Auto Negotiation */ .features = MII_BASIC_FEATURES,
{MIIM_CONTROL, MIIM_DM9161_CR_RSTAN, NULL}, .config_aneg = genmii_config_aneg,
/* Status is read once to clear old link state */ .read_status = genmii_read_status,
{MIIM_STATUS, miim_read, dm9161_wait},
/* Auto-negotiate */
{MIIM_STATUS, miim_read, mii_parse_sr},
/* Read the status */
{MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
/* Clear any pending interrupts */
{MIIM_DM9161_INTR, miim_read, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* ack_int */
{MIIM_DM9161_INTR, miim_read, NULL},
{miim_end,}
},
(const struct phy_cmd[]) { /* handle_int */
{MIIM_STATUS, miim_read, NULL},
{MIIM_STATUS, miim_read, mii_parse_sr},
{MIIM_DM9161_SCSR, miim_read, mii_parse_dm9161_scsr},
{miim_end,}
},
(const struct phy_cmd[]) { /* shutdown */
{MIIM_DM9161_INTR, miim_read, NULL},
{miim_end,}
},
}; };
static struct phy_info *phy_info[] = { static struct phy_info *phy_info[] = {
&phy_info_cis8201, &phy_info_cis820x,
&phy_info_cis8204, &phy_info_marvell,
&phy_info_M88E1011S,
&phy_info_dm9161, &phy_info_dm9161,
&phy_info_genmii,
NULL NULL
}; };
u16 phy_read(struct gfar_mii_info *mii_info, u16 regnum)
{
u16 retval;
unsigned long flags;
spin_lock_irqsave(&mii_info->mdio_lock, flags);
retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum);
spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
return retval;
}
void phy_write(struct gfar_mii_info *mii_info, u16 regnum, u16 val)
{
unsigned long flags;
spin_lock_irqsave(&mii_info->mdio_lock, flags);
mii_info->mdio_write(mii_info->dev,
mii_info->mii_id,
regnum, val);
spin_unlock_irqrestore(&mii_info->mdio_lock, flags);
}
/* Use the PHY ID registers to determine what type of PHY is attached /* Use the PHY ID registers to determine what type of PHY is attached
* to device dev. return a struct phy_info structure describing that PHY * to device dev. return a struct phy_info structure describing that PHY
*/ */
struct phy_info * get_phy_info(struct net_device *dev) struct phy_info * get_phy_info(struct gfar_mii_info *mii_info)
{ {
u16 phy_reg; u16 phy_reg;
u32 phy_ID; u32 phy_ID;
int i; int i;
struct phy_info *theInfo = NULL; struct phy_info *theInfo = NULL;
struct net_device *dev = mii_info->dev;
/* Grab the bits from PHYIR1, and put them in the upper half */ /* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg = read_phy_reg(dev, MIIM_PHYIR1); phy_reg = phy_read(mii_info, MII_PHYSID1);
phy_ID = (phy_reg & 0xffff) << 16; phy_ID = (phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */ /* Grab the bits from PHYIR2, and put them in the lower half */
phy_reg = read_phy_reg(dev, MIIM_PHYIR2); phy_reg = phy_read(mii_info, MII_PHYSID2);
phy_ID |= (phy_reg & 0xffff); phy_ID |= (phy_reg & 0xffff);
/* loop through all the known PHY types, and find one that */ /* loop through all the known PHY types, and find one that */
/* matches the ID we read from the PHY. */ /* matches the ID we read from the PHY. */
for (i = 0; phy_info[i]; i++) for (i = 0; phy_info[i]; i++)
if (phy_info[i]->id == (phy_ID >> phy_info[i]->shift)) if (phy_info[i]->phy_id ==
(phy_ID & phy_info[i]->phy_id_mask)) {
theInfo = phy_info[i]; theInfo = phy_info[i];
break;
}
/* This shouldn't happen, as we have generic PHY support */
if (theInfo == NULL) { if (theInfo == NULL) {
printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID); printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
return NULL; return NULL;
...@@ -455,50 +659,3 @@ struct phy_info * get_phy_info(struct net_device *dev) ...@@ -455,50 +659,3 @@ struct phy_info * get_phy_info(struct net_device *dev)
return theInfo; return theInfo;
} }
/* Take a list of struct phy_cmd, and, depending on the values, either */
/* read or write, using a helper function if provided */
/* It is assumed that all lists of struct phy_cmd will be terminated by */
/* mii_end. */
void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd)
{
int i;
u16 result;
struct gfar_private *priv = (struct gfar_private *) dev->priv;
struct gfar *phyregs = priv->phyregs;
/* Reset the management interface */
gfar_write(&phyregs->miimcfg, MIIMCFG_RESET);
/* Setup the MII Mgmt clock speed */
gfar_write(&phyregs->miimcfg, MIIMCFG_INIT_VALUE);
/* Wait until the bus is free */
while (gfar_read(&phyregs->miimind) & MIIMIND_BUSY)
cpu_relax();
for (i = 0; cmd->mii_reg != miim_end; i++) {
/* The command is a read if mii_data is miim_read */
if (cmd->mii_data == miim_read) {
/* Read the value of the PHY reg */
result = read_phy_reg(dev, cmd->mii_reg);
/* If a function was supplied, we need to let it process */
/* the result. */
if (cmd->funct != NULL)
(*(cmd->funct)) (result, dev);
} else { /* Otherwise, it's a write */
/* If a function was supplied, it will provide
* the value to write */
/* Otherwise, the value was supplied in cmd->mii_data */
if (cmd->funct != NULL)
result = (*(cmd->funct)) (0, dev);
else
result = cmd->mii_data;
/* Write the appropriate value to the PHY reg */
write_phy_reg(dev, cmd->mii_reg, result);
}
cmd++;
}
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Author: Andy Fleming * Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com) * Maintainer: Kumar Gala (kumar.gala@freescale.com)
* *
* Copyright 2004 Freescale Semiconductor, Inc * Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
...@@ -19,135 +19,144 @@ ...@@ -19,135 +19,144 @@
#ifndef __GIANFAR_PHY_H #ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H #define __GIANFAR_PHY_H
#define miim_end ((u32)-2) #define MII_end ((u32)-2)
#define miim_read ((u32)-1) #define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001 #define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004 #define MIIMIND_NOTVALID 0x00000004
#define MIIM_CONTROL 0x00 #define GFAR_AN_TIMEOUT 2000
#define MIIM_CONTROL_RESET 0x00008000
#define MIIM_CONTROL_INIT 0x00001140
#define MIIM_ANEN 0x00001000
#define MIIM_CR 0x00
#define MIIM_CR_RST 0x00008000
#define MIIM_CR_INIT 0x00001000
#define MIIM_STATUS 0x1
#define MIIM_STATUS_AN_DONE 0x00000020
#define MIIM_STATUS_LINK 0x0004
#define MIIM_PHYIR1 0x2
#define MIIM_PHYIR2 0x3
#define GFAR_AN_TIMEOUT 0x000fffff
#define MIIM_ANLPBPA 0x5
#define MIIM_ANLPBPA_HALF 0x00000040
#define MIIM_ANLPBPA_FULL 0x00000020
#define MIIM_ANEX 0x6
#define MIIM_ANEX_NP 0x00000004
#define MIIM_ANEX_PRX 0x00000002
/* 1000BT control (Marvell & BCM54xx at least) */
#define MII_1000BASETCONTROL 0x09
#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200
#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100
/* Cicada Extended Control Register 1 */ /* Cicada Extended Control Register 1 */
#define MIIM_CIS8201_EXT_CON1 0x17 #define MII_CIS8201_EXT_CON1 0x17
#define MIIM_CIS8201_EXTCON1_INIT 0x0000 #define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */ /* Cicada Interrupt Mask Register */
#define MIIM_CIS8204_IMASK 0x19 #define MII_CIS8201_IMASK 0x19
#define MIIM_CIS8204_IMASK_IEN 0x8000 #define MII_CIS8201_IMASK_IEN 0x8000
#define MIIM_CIS8204_IMASK_SPEED 0x4000 #define MII_CIS8201_IMASK_SPEED 0x4000
#define MIIM_CIS8204_IMASK_LINK 0x2000 #define MII_CIS8201_IMASK_LINK 0x2000
#define MIIM_CIS8204_IMASK_DUPLEX 0x1000 #define MII_CIS8201_IMASK_DUPLEX 0x1000
#define MIIM_CIS8204_IMASK_MASK 0xf000 #define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */ /* Cicada Interrupt Status Register */
#define MIIM_CIS8204_ISTAT 0x1a #define MII_CIS8201_ISTAT 0x1a
#define MIIM_CIS8204_ISTAT_STATUS 0x8000 #define MII_CIS8201_ISTAT_STATUS 0x8000
#define MIIM_CIS8204_ISTAT_SPEED 0x4000 #define MII_CIS8201_ISTAT_SPEED 0x4000
#define MIIM_CIS8204_ISTAT_LINK 0x2000 #define MII_CIS8201_ISTAT_LINK 0x2000
#define MIIM_CIS8204_ISTAT_DUPLEX 0x1000 #define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */ /* Cicada Auxiliary Control/Status Register */
#define MIIM_CIS8201_AUX_CONSTAT 0x1c #define MII_CIS8201_AUX_CONSTAT 0x1c
#define MIIM_CIS8201_AUXCONSTAT_INIT 0x0004 #define MII_CIS8201_AUXCONSTAT_INIT 0x0004
#define MIIM_CIS8201_AUXCONSTAT_DUPLEX 0x0020 #define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
#define MIIM_CIS8201_AUXCONSTAT_SPEED 0x0018 #define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
#define MIIM_CIS8201_AUXCONSTAT_GBIT 0x0010 #define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
#define MIIM_CIS8201_AUXCONSTAT_100 0x0008 #define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */ /* 88E1011 PHY Status Register */
#define MIIM_88E1011_PHY_STATUS 0x11 #define MII_M1011_PHY_SPEC_STATUS 0x11
#define MIIM_88E1011_PHYSTAT_SPEED 0xc000 #define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
#define MIIM_88E1011_PHYSTAT_GBIT 0x8000 #define MII_M1011_PHY_SPEC_STATUS_100 0x4000
#define MIIM_88E1011_PHYSTAT_100 0x4000 #define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
#define MIIM_88E1011_PHYSTAT_DUPLEX 0x2000 #define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
#define MIIM_88E1011_PHYSTAT_LINK 0x0400 #define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
#define MIIM_88E1011_IEVENT 0x13 #define MII_M1011_IEVENT 0x13
#define MIIM_88E1011_IEVENT_CLEAR 0x0000 #define MII_M1011_IEVENT_CLEAR 0x0000
#define MIIM_88E1011_IMASK 0x12 #define MII_M1011_IMASK 0x12
#define MIIM_88E1011_IMASK_INIT 0x6400 #define MII_M1011_IMASK_INIT 0x6400
#define MIIM_88E1011_IMASK_CLEAR 0x0000 #define MII_M1011_IMASK_CLEAR 0x0000
/* DM9161 Control register values */ #define MII_DM9161_SCR 0x10
#define MIIM_DM9161_CR_STOP 0x0400 #define MII_DM9161_SCR_INIT 0x0610
#define MIIM_DM9161_CR_RSTAN 0x1200
#define MIIM_DM9161_SCR 0x10
#define MIIM_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */ /* DM9161 Specified Configuration and Status Register */
#define MIIM_DM9161_SCSR 0x11 #define MII_DM9161_SCSR 0x11
#define MIIM_DM9161_SCSR_100F 0x8000 #define MII_DM9161_SCSR_100F 0x8000
#define MIIM_DM9161_SCSR_100H 0x4000 #define MII_DM9161_SCSR_100H 0x4000
#define MIIM_DM9161_SCSR_10F 0x2000 #define MII_DM9161_SCSR_10F 0x2000
#define MIIM_DM9161_SCSR_10H 0x1000 #define MII_DM9161_SCSR_10H 0x1000
/* DM9161 Interrupt Register */ /* DM9161 Interrupt Register */
#define MIIM_DM9161_INTR 0x15 #define MII_DM9161_INTR 0x15
#define MIIM_DM9161_INTR_PEND 0x8000 #define MII_DM9161_INTR_PEND 0x8000
#define MIIM_DM9161_INTR_DPLX_MASK 0x0800 #define MII_DM9161_INTR_DPLX_MASK 0x0800
#define MIIM_DM9161_INTR_SPD_MASK 0x0400 #define MII_DM9161_INTR_SPD_MASK 0x0400
#define MIIM_DM9161_INTR_LINK_MASK 0x0200 #define MII_DM9161_INTR_LINK_MASK 0x0200
#define MIIM_DM9161_INTR_MASK 0x0100 #define MII_DM9161_INTR_MASK 0x0100
#define MIIM_DM9161_INTR_DPLX_CHANGE 0x0010 #define MII_DM9161_INTR_DPLX_CHANGE 0x0010
#define MIIM_DM9161_INTR_SPD_CHANGE 0x0008 #define MII_DM9161_INTR_SPD_CHANGE 0x0008
#define MIIM_DM9161_INTR_LINK_CHANGE 0x0004 #define MII_DM9161_INTR_LINK_CHANGE 0x0004
#define MIIM_DM9161_INTR_INIT 0x0000 #define MII_DM9161_INTR_INIT 0x0000
#define MIIM_DM9161_INTR_STOP \ #define MII_DM9161_INTR_STOP \
(MIIM_DM9161_INTR_DPLX_MASK | MIIM_DM9161_INTR_SPD_MASK \ (MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
| MIIM_DM9161_INTR_LINK_MASK | MIIM_DM9161_INTR_MASK) | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
/* DM9161 10BT Configuration/Status */ /* DM9161 10BT Configuration/Status */
#define MIIM_DM9161_10BTCSR 0x12 #define MII_DM9161_10BTCSR 0x12
#define MIIM_DM9161_10BTCSR_INIT 0x7800 #define MII_DM9161_10BTCSR_INIT 0x7800
#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \
SUPPORTED_10baseT_Full | \
SUPPORTED_100baseT_Half | \
SUPPORTED_100baseT_Full | \
SUPPORTED_Autoneg | \
SUPPORTED_TP | \
SUPPORTED_MII)
#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \
SUPPORTED_1000baseT_Half | \
SUPPORTED_1000baseT_Full)
#define MII_READ_COMMAND 0x00000001
#define MII_INTERRUPT_DISABLED 0x0
#define MII_INTERRUPT_ENABLED 0x1
/* Taken from mii_if_info and sungem_phy.h */
struct gfar_mii_info {
/* Information about the PHY type */
/* And management functions */
struct phy_info *phyinfo;
/* forced speed & duplex (no autoneg)
* partner speed & duplex & pause (autoneg)
*/
int speed;
int duplex;
int pause;
/* The most recently read link state */
int link;
#define MIIM_READ_COMMAND 0x00000001 /* Enabled Interrupts */
u32 interrupts;
/* u32 advertising;
* struct phy_cmd: A command for reading or writing a PHY register int autoneg;
* int mii_id;
* mii_reg: The register to read or write
* /* private data pointer */
* mii_data: For writes, the value to put in the register. /* For use by PHYs to maintain extra state */
* A value of -1 indicates this is a read. void *priv;
*
* funct: A function pointer which is invoked for each command. /* Provided by host chip */
* For reads, this function will be passed the value read struct net_device *dev;
* from the PHY, and process it.
* For writes, the result of this function will be written /* A lock to ensure that only one thing can read/write
* to the PHY register * the MDIO bus at a time */
*/ spinlock_t mdio_lock;
struct phy_cmd {
u32 mii_reg; /* Provided by ethernet driver */
u32 mii_data; int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
u16 (*funct) (u16 mii_reg, struct net_device * dev); void (*mdio_write) (struct net_device *dev, int mii_id, int reg, int val);
}; };
/* struct phy_info: a structure which defines attributes for a PHY /* struct phy_info: a structure which defines attributes for a PHY
...@@ -155,38 +164,50 @@ struct phy_cmd { ...@@ -155,38 +164,50 @@ struct phy_cmd {
* id will contain a number which represents the PHY. During * id will contain a number which represents the PHY. During
* startup, the driver will poll the PHY to find out what its * startup, the driver will poll the PHY to find out what its
* UID--as defined by registers 2 and 3--is. The 32-bit result * UID--as defined by registers 2 and 3--is. The 32-bit result
* gotten from the PHY will be shifted right by "shift" bits to * gotten from the PHY will be ANDed with phy_id_mask to
* discard any bits which may change based on revision numbers * discard any bits which may change based on revision numbers
* unimportant to functionality * unimportant to functionality
* *
* The struct phy_cmd entries represent pointers to an arrays of * There are 6 commands which take a gfar_mii_info structure.
* commands which tell the driver what to do to the PHY. * Each PHY must declare config_aneg, and read_status.
*/ */
struct phy_info { struct phy_info {
u32 id; u32 phy_id;
char *name; char *name;
unsigned int shift; unsigned int phy_id_mask;
/* Called to configure the PHY, and modify the controller u32 features;
* based on the results */
const struct phy_cmd *config;
/* Called when starting up the controller. Usually sets /* Called to initialize the PHY */
* up the interrupt for state changes */ int (*init)(struct gfar_mii_info *mii_info);
const struct phy_cmd *startup;
/* Called inside the interrupt handler to acknowledge /* Called to suspend the PHY for power */
* the interrupt */ int (*suspend)(struct gfar_mii_info *mii_info);
const struct phy_cmd *ack_int;
/* Called in the bottom half to handle the interrupt */ /* Reconfigures autonegotiation (or disables it) */
const struct phy_cmd *handle_int; int (*config_aneg)(struct gfar_mii_info *mii_info);
/* Called when bringing down the controller. Usually stops /* Determines the negotiated speed and duplex */
* the interrupts from being generated */ int (*read_status)(struct gfar_mii_info *mii_info);
const struct phy_cmd *shutdown;
/* Clears any pending interrupts */
int (*ack_interrupt)(struct gfar_mii_info *mii_info);
/* Enables or disables interrupts */
int (*config_intr)(struct gfar_mii_info *mii_info);
/* Clears up any memory if needed */
void (*close)(struct gfar_mii_info *mii_info);
}; };
struct phy_info *get_phy_info(struct net_device *dev); struct phy_info *get_phy_info(struct gfar_mii_info *mii_info);
void phy_run_commands(struct net_device *dev, const struct phy_cmd *cmd); int read_phy_reg(struct net_device *dev, int mii_id, int regnum);
void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value);
void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info);
void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts);
struct dm9161_private {
struct timer_list timer;
int resetdone;
};
#endif /* GIANFAR_PHY_H */ #endif /* GIANFAR_PHY_H */
...@@ -33,7 +33,8 @@ ...@@ -33,7 +33,8 @@
#define MII_NCONFIG 0x1c /* Network interface config */ #define MII_NCONFIG 0x1c /* Network interface config */
/* Basic mode control register. */ /* Basic mode control register. */
#define BMCR_RESV 0x007f /* Unused... */ #define BMCR_RESV 0x003f /* Unused... */
#define BMCR_SPEED1000 0x0040 /* MSB of Speed (1000) */
#define BMCR_CTST 0x0080 /* Collision test */ #define BMCR_CTST 0x0080 /* Collision test */
#define BMCR_FULLDPLX 0x0100 /* Full duplex */ #define BMCR_FULLDPLX 0x0100 /* Full duplex */
#define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */ #define BMCR_ANRESTART 0x0200 /* Auto negotiation restart */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment