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

[netdrvr gianfar] use new phy layer

Signed-off-by: default avatarAndy Fleming <afleming@freescale.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent acc4b985
...@@ -2075,6 +2075,8 @@ config SPIDER_NET ...@@ -2075,6 +2075,8 @@ config SPIDER_NET
config GIANFAR config GIANFAR
tristate "Gianfar Ethernet" tristate "Gianfar Ethernet"
depends on 85xx || 83xx depends on 85xx || 83xx
select PHYLIB
select PHYCONTROL
help help
This driver supports the Gigabit TSEC on the MPC85xx This driver supports the Gigabit TSEC on the MPC85xx
family of chips, and the FEC on the 8540 family of chips, and the FEC on the 8540
......
...@@ -13,7 +13,7 @@ obj-$(CONFIG_CHELSIO_T1) += chelsio/ ...@@ -13,7 +13,7 @@ obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_BONDING) += bonding/ obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o obj-$(CONFIG_GIANFAR) += gianfar_driver.o
gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_phy.o gianfar_driver-objs := gianfar.o gianfar_ethtool.o gianfar_mii.o
# #
# link order important here # link order important here
......
...@@ -29,12 +29,7 @@ ...@@ -29,12 +29,7 @@
* define the configuration needed by the board are defined in a * define the configuration needed by the board are defined in a
* board structure in arch/ppc/platforms (though I do not * board structure in arch/ppc/platforms (though I do not
* discount the possibility that other architectures could one * discount the possibility that other architectures could one
* day be supported. One assumption the driver currently makes * day be supported.
* is that the PHY is configured in such a way to advertise all
* capabilities. This is a sensible default, and on certain
* PHYs, changing this default encounters substantial errata
* issues. Future versions may remove this requirement, but for
* now, it is best for the firmware to ensure this is the case.
* *
* The Gianfar Ethernet Controller uses a ring of buffer * The Gianfar Ethernet Controller uses a ring of buffer
* descriptors. The beginning is indicated by a register * descriptors. The beginning is indicated by a register
...@@ -47,7 +42,7 @@ ...@@ -47,7 +42,7 @@
* corresponding bit in the IMASK register is also set (if * corresponding bit in the IMASK register is also set (if
* interrupt coalescing is active, then the interrupt may not * interrupt coalescing is active, then the interrupt may not
* happen immediately, but will wait until either a set number * happen immediately, but will wait until either a set number
* of frames or amount of time have passed.). In NAPI, the * of frames or amount of time have passed). In NAPI, the
* interrupt handler will signal there is work to be done, and * interrupt handler will signal there is work to be done, and
* exit. Without NAPI, the packet(s) will be handled * exit. Without NAPI, the packet(s) will be handled
* immediately. Both methods will start at the last known empty * immediately. Both methods will start at the last known empty
...@@ -75,6 +70,7 @@ ...@@ -75,6 +70,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -97,9 +93,11 @@ ...@@ -97,9 +93,11 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include "gianfar.h" #include "gianfar.h"
#include "gianfar_phy.h" #include "gianfar_mii.h"
#define TX_TIMEOUT (1*HZ) #define TX_TIMEOUT (1*HZ)
#define SKB_ALLOC_TIMEOUT 1000000 #define SKB_ALLOC_TIMEOUT 1000000
...@@ -113,9 +111,8 @@ ...@@ -113,9 +111,8 @@
#endif #endif
const char gfar_driver_name[] = "Gianfar Ethernet"; const char gfar_driver_name[] = "Gianfar Ethernet";
const char gfar_driver_version[] = "1.1"; const char gfar_driver_version[] = "1.2";
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);
static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev); static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev);
static void gfar_timeout(struct net_device *dev); static void gfar_timeout(struct net_device *dev);
...@@ -126,17 +123,13 @@ static int gfar_set_mac_address(struct net_device *dev); ...@@ -126,17 +123,13 @@ static int gfar_set_mac_address(struct net_device *dev);
static int gfar_change_mtu(struct net_device *dev, int new_mtu); static int gfar_change_mtu(struct net_device *dev, int new_mtu);
static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t gfar_error(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t gfar_transmit(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs); static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static void gfar_phy_change(void *data);
static void gfar_phy_timer(unsigned long data);
static void adjust_link(struct net_device *dev); static void adjust_link(struct net_device *dev);
static void init_registers(struct net_device *dev); static void init_registers(struct net_device *dev);
static int init_phy(struct net_device *dev); static int init_phy(struct net_device *dev);
static int gfar_probe(struct device *device); static int gfar_probe(struct device *device);
static int gfar_remove(struct device *device); static int gfar_remove(struct device *device);
void free_skb_resources(struct gfar_private *priv); static void free_skb_resources(struct gfar_private *priv);
static void gfar_set_multi(struct net_device *dev); static void gfar_set_multi(struct net_device *dev);
static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr); static void gfar_set_hash_for_addr(struct net_device *dev, u8 *addr);
#ifdef CONFIG_GFAR_NAPI #ifdef CONFIG_GFAR_NAPI
...@@ -144,7 +137,6 @@ static int gfar_poll(struct net_device *dev, int *budget); ...@@ -144,7 +137,6 @@ static int gfar_poll(struct net_device *dev, int *budget);
#endif #endif
int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
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);
static void gfar_vlan_rx_register(struct net_device *netdev, static void gfar_vlan_rx_register(struct net_device *netdev,
struct vlan_group *grp); struct vlan_group *grp);
static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid); static void gfar_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
...@@ -162,6 +154,9 @@ int gfar_uses_fcb(struct gfar_private *priv) ...@@ -162,6 +154,9 @@ int gfar_uses_fcb(struct gfar_private *priv)
else else
return 0; return 0;
} }
/* Set up the ethernet device structure, private data,
* and anything else we need before we start */
static int gfar_probe(struct device *device) static int gfar_probe(struct device *device)
{ {
u32 tempval; u32 tempval;
...@@ -175,7 +170,7 @@ static int gfar_probe(struct device *device) ...@@ -175,7 +170,7 @@ static int gfar_probe(struct device *device)
einfo = (struct gianfar_platform_data *) pdev->dev.platform_data; einfo = (struct gianfar_platform_data *) pdev->dev.platform_data;
if (einfo == NULL) { if (NULL == einfo) {
printk(KERN_ERR "gfar %d: Missing additional data!\n", printk(KERN_ERR "gfar %d: Missing additional data!\n",
pdev->id); pdev->id);
...@@ -185,7 +180,7 @@ static int gfar_probe(struct device *device) ...@@ -185,7 +180,7 @@ static int gfar_probe(struct device *device)
/* Create an ethernet device instance */ /* Create an ethernet device instance */
dev = alloc_etherdev(sizeof (*priv)); dev = alloc_etherdev(sizeof (*priv));
if (dev == NULL) if (NULL == dev)
return -ENOMEM; return -ENOMEM;
priv = netdev_priv(dev); priv = netdev_priv(dev);
...@@ -207,20 +202,11 @@ static int gfar_probe(struct device *device) ...@@ -207,20 +202,11 @@ static int gfar_probe(struct device *device)
priv->regs = (struct gfar *) priv->regs = (struct gfar *)
ioremap(r->start, sizeof (struct gfar)); ioremap(r->start, sizeof (struct gfar));
if (priv->regs == NULL) { if (NULL == priv->regs) {
err = -ENOMEM; err = -ENOMEM;
goto regs_fail; goto regs_fail;
} }
/* Set the PHY base address */
priv->phyregs = (struct gfar *)
ioremap(einfo->phy_reg_addr, sizeof (struct gfar));
if (priv->phyregs == NULL) {
err = -ENOMEM;
goto phy_regs_fail;
}
spin_lock_init(&priv->lock); spin_lock_init(&priv->lock);
dev_set_drvdata(device, dev); dev_set_drvdata(device, dev);
...@@ -386,12 +372,10 @@ static int gfar_probe(struct device *device) ...@@ -386,12 +372,10 @@ static int gfar_probe(struct device *device)
return 0; return 0;
register_fail: register_fail:
iounmap((void *) priv->phyregs);
phy_regs_fail:
iounmap((void *) priv->regs); iounmap((void *) priv->regs);
regs_fail: regs_fail:
free_netdev(dev); free_netdev(dev);
return -ENOMEM; return err;
} }
static int gfar_remove(struct device *device) static int gfar_remove(struct device *device)
...@@ -402,108 +386,41 @@ static int gfar_remove(struct device *device) ...@@ -402,108 +386,41 @@ static int gfar_remove(struct device *device)
dev_set_drvdata(device, NULL); dev_set_drvdata(device, NULL);
iounmap((void *) priv->regs); iounmap((void *) priv->regs);
iounmap((void *) priv->phyregs);
free_netdev(dev); free_netdev(dev);
return 0; return 0;
} }
/* Configure the PHY for dev. /* Initializes driver's PHY state, and attaches to the PHY.
* returns 0 if success. -1 if failure * Returns 0 on success.
*/ */
static int init_phy(struct net_device *dev) 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; uint gigabit_support =
unsigned int timeout = PHY_INIT_TIMEOUT; priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
struct gfar *phyregs = priv->phyregs; SUPPORTED_1000baseT_Full : 0;
struct gfar_mii_info *mii_info; struct phy_device *phydev;
int err;
priv->oldlink = 0; priv->oldlink = 0;
priv->oldspeed = 0; priv->oldspeed = 0;
priv->oldduplex = -1; priv->oldduplex = -1;
mii_info = kmalloc(sizeof(struct gfar_mii_info), phydev = phy_connect(dev, priv->einfo->bus_id, &adjust_link, 0);
GFP_KERNEL);
if(NULL == mii_info) {
if (netif_msg_ifup(priv))
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;
spin_lock_init(&mii_info->mdio_lock); if (IS_ERR(phydev)) {
printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name);
mii_info->mii_id = priv->einfo->phyid; return PTR_ERR(phydev);
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 */
curphy = get_phy_info(priv->mii_info);
if (curphy == NULL) {
if (netif_msg_ifup(priv))
printk(KERN_ERR "%s: No PHY found\n", dev->name);
err = -1;
goto no_phy;
} }
mii_info->phyinfo = curphy; /* Remove any features not supported by the controller */
phydev->supported &= (GFAR_SUPPORTED | gigabit_support);
phydev->advertising = phydev->supported;
/* Run the commands which initialize the PHY */ priv->phydev = phydev;
if(curphy->init) {
err = curphy->init(priv->mii_info);
if (err)
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)
...@@ -603,24 +520,13 @@ void stop_gfar(struct net_device *dev) ...@@ -603,24 +520,13 @@ void stop_gfar(struct net_device *dev)
struct gfar *regs = priv->regs; struct gfar *regs = priv->regs;
unsigned long flags; unsigned long flags;
phy_stop(priv->phydev);
/* Lock it down */ /* Lock it down */
spin_lock_irqsave(&priv->lock, flags); spin_lock_irqsave(&priv->lock, flags);
/* Tell the kernel the link is down */
priv->mii_info->link = 0;
adjust_link(dev);
gfar_halt(dev); gfar_halt(dev);
if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
/* 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);
/* Free the IRQs */ /* Free the IRQs */
...@@ -629,13 +535,7 @@ void stop_gfar(struct net_device *dev) ...@@ -629,13 +535,7 @@ void stop_gfar(struct net_device *dev)
free_irq(priv->interruptTransmit, dev); free_irq(priv->interruptTransmit, dev);
free_irq(priv->interruptReceive, dev); free_irq(priv->interruptReceive, dev);
} else { } else {
free_irq(priv->interruptTransmit, dev); free_irq(priv->interruptTransmit, dev);
}
if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
free_irq(priv->einfo->interruptPHY, dev);
} else {
del_timer_sync(&priv->phy_info_timer);
} }
free_skb_resources(priv); free_skb_resources(priv);
...@@ -649,7 +549,7 @@ void stop_gfar(struct net_device *dev) ...@@ -649,7 +549,7 @@ void stop_gfar(struct net_device *dev)
/* 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.
* Then free tx_skbuff and rx_skbuff */ * Then free tx_skbuff and rx_skbuff */
void free_skb_resources(struct gfar_private *priv) static void free_skb_resources(struct gfar_private *priv)
{ {
struct rxbd8 *rxbdp; struct rxbd8 *rxbdp;
struct txbd8 *txbdp; struct txbd8 *txbdp;
...@@ -770,7 +670,7 @@ int startup_gfar(struct net_device *dev) ...@@ -770,7 +670,7 @@ int startup_gfar(struct net_device *dev)
(struct sk_buff **) kmalloc(sizeof (struct sk_buff *) * (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
priv->tx_ring_size, GFP_KERNEL); priv->tx_ring_size, GFP_KERNEL);
if (priv->tx_skbuff == NULL) { if (NULL == priv->tx_skbuff) {
if (netif_msg_ifup(priv)) if (netif_msg_ifup(priv))
printk(KERN_ERR "%s: Could not allocate tx_skbuff\n", printk(KERN_ERR "%s: Could not allocate tx_skbuff\n",
dev->name); dev->name);
...@@ -785,7 +685,7 @@ int startup_gfar(struct net_device *dev) ...@@ -785,7 +685,7 @@ int startup_gfar(struct net_device *dev)
(struct sk_buff **) kmalloc(sizeof (struct sk_buff *) * (struct sk_buff **) kmalloc(sizeof (struct sk_buff *) *
priv->rx_ring_size, GFP_KERNEL); priv->rx_ring_size, GFP_KERNEL);
if (priv->rx_skbuff == NULL) { if (NULL == priv->rx_skbuff) {
if (netif_msg_ifup(priv)) if (netif_msg_ifup(priv))
printk(KERN_ERR "%s: Could not allocate rx_skbuff\n", printk(KERN_ERR "%s: Could not allocate rx_skbuff\n",
dev->name); dev->name);
...@@ -879,13 +779,7 @@ int startup_gfar(struct net_device *dev) ...@@ -879,13 +779,7 @@ int startup_gfar(struct net_device *dev)
} }
} }
/* Set up the PHY change work queue */ phy_start(priv->phydev);
INIT_WORK(&priv->tq, gfar_phy_change, dev);
init_timer(&priv->phy_info_timer);
priv->phy_info_timer.function = &gfar_phy_startup_timer;
priv->phy_info_timer.data = (unsigned long) priv->mii_info;
mod_timer(&priv->phy_info_timer, jiffies + HZ);
/* Configure the coalescing support */ /* Configure the coalescing support */
if (priv->txcoalescing) if (priv->txcoalescing)
...@@ -933,11 +827,6 @@ int startup_gfar(struct net_device *dev) ...@@ -933,11 +827,6 @@ int startup_gfar(struct net_device *dev)
priv->tx_bd_base, priv->tx_bd_base,
gfar_read(&regs->tbase0)); gfar_read(&regs->tbase0));
if (priv->mii_info->phyinfo->close)
priv->mii_info->phyinfo->close(priv->mii_info);
kfree(priv->mii_info);
return err; return err;
} }
...@@ -1035,7 +924,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -1035,7 +924,7 @@ static int gfar_start_xmit(struct sk_buff *skb, struct net_device *dev)
txbdp->status &= TXBD_WRAP; txbdp->status &= TXBD_WRAP;
/* Set up checksumming */ /* Set up checksumming */
if ((dev->features & NETIF_F_IP_CSUM) if ((dev->features & NETIF_F_IP_CSUM)
&& (CHECKSUM_HW == skb->ip_summed)) { && (CHECKSUM_HW == skb->ip_summed)) {
fcb = gfar_add_fcb(skb, txbdp); fcb = gfar_add_fcb(skb, txbdp);
gfar_tx_checksum(skb, fcb); gfar_tx_checksum(skb, fcb);
...@@ -1103,11 +992,9 @@ static int gfar_close(struct net_device *dev) ...@@ -1103,11 +992,9 @@ static int gfar_close(struct net_device *dev)
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
stop_gfar(dev); stop_gfar(dev);
/* Shutdown the PHY */ /* Disconnect from the PHY */
if (priv->mii_info->phyinfo->close) phy_disconnect(priv->phydev);
priv->mii_info->phyinfo->close(priv->mii_info); priv->phydev = NULL;
kfree(priv->mii_info);
netif_stop_queue(dev); netif_stop_queue(dev);
...@@ -1343,7 +1230,7 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) ...@@ -1343,7 +1230,7 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp)
while ((!skb) && timeout--) while ((!skb) && timeout--)
skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT); skb = dev_alloc_skb(priv->rx_buffer_size + RXBUF_ALIGNMENT);
if (skb == NULL) if (NULL == skb)
return NULL; return NULL;
/* We need the data buffer to be aligned properly. We will reserve /* We need the data buffer to be aligned properly. We will reserve
...@@ -1490,7 +1377,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb, ...@@ -1490,7 +1377,7 @@ static int gfar_process_frame(struct net_device *dev, struct sk_buff *skb,
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
struct rxfcb *fcb = NULL; struct rxfcb *fcb = NULL;
if (skb == NULL) { if (NULL == skb) {
if (netif_msg_rx_err(priv)) if (netif_msg_rx_err(priv))
printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name); printk(KERN_WARNING "%s: Missing skb!!.\n", dev->name);
priv->stats.rx_dropped++; priv->stats.rx_dropped++;
...@@ -1718,131 +1605,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -1718,131 +1605,9 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id, struct pt_regs *regs)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t phy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct net_device *dev = (struct net_device *) dev_id;
struct gfar_private *priv = netdev_priv(dev);
/* Clear the interrupt */
mii_clear_phy_interrupt(priv->mii_info);
/* Disable PHY interrupts */
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_DISABLED);
/* Schedule the phy change */
schedule_work(&priv->tq);
return IRQ_HANDLED;
}
/* Scheduled by the phy_interrupt/timer to handle PHY changes */
static void gfar_phy_change(void *data)
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
int result = 0;
/* Delay to give the PHY a chance to change the
* register state */
msleep(1);
/* Update the link, speed, duplex */
result = priv->mii_info->phyinfo->read_status(priv->mii_info);
/* 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);
/* Reenable interrupts, if needed */
if (priv->einfo->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR)
mii_configure_phy_interrupt(priv->mii_info,
MII_INTERRUPT_ENABLED);
}
/* Called every so often on systems that don't interrupt
* the core for PHY changes */
static void gfar_phy_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *) data;
struct gfar_private *priv = netdev_priv(dev);
schedule_work(&priv->tq);
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) {
if (netif_msg_link(priv))
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->board_flags & FSL_GIANFAR_BRD_HAS_PHY_INTR) {
if (request_irq(priv->einfo->interruptPHY,
phy_interrupt,
SA_SHIRQ,
"phy_interrupt",
mii_info->dev) < 0) {
if (netif_msg_intr(priv))
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
* aware of new link state. The PHY code conveys this * aware of new link state. The PHY code conveys this
* information through variables in the priv structure, and this * information through variables in the phydev structure, and this
* function converts those variables into the appropriate * function converts those variables into the appropriate
* register values, and can bring down the device if needed. * register values, and can bring down the device if needed.
*/ */
...@@ -1850,84 +1615,68 @@ static void adjust_link(struct net_device *dev) ...@@ -1850,84 +1615,68 @@ 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; unsigned long flags;
struct gfar_mii_info *mii_info = priv->mii_info; struct phy_device *phydev = priv->phydev;
int new_state = 0;
spin_lock_irqsave(&priv->lock, flags);
if (phydev->link) {
u32 tempval = gfar_read(&regs->maccfg2);
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 (mii_info->duplex != priv->oldduplex) { if (phydev->duplex != priv->oldduplex) {
if (!(mii_info->duplex)) { new_state = 1;
tempval = gfar_read(&regs->maccfg2); if (!(phydev->duplex))
tempval &= ~(MACCFG2_FULL_DUPLEX); tempval &= ~(MACCFG2_FULL_DUPLEX);
gfar_write(&regs->maccfg2, tempval); else
if (netif_msg_link(priv))
printk(KERN_INFO "%s: Half Duplex\n",
dev->name);
} else {
tempval = gfar_read(&regs->maccfg2);
tempval |= MACCFG2_FULL_DUPLEX; tempval |= MACCFG2_FULL_DUPLEX;
gfar_write(&regs->maccfg2, tempval);
if (netif_msg_link(priv)) priv->oldduplex = phydev->duplex;
printk(KERN_INFO "%s: Full Duplex\n",
dev->name);
}
priv->oldduplex = mii_info->duplex;
} }
if (mii_info->speed != priv->oldspeed) { if (phydev->speed != priv->oldspeed) {
switch (mii_info->speed) { new_state = 1;
switch (phydev->speed) {
case 1000: case 1000:
tempval = gfar_read(&regs->maccfg2);
tempval = tempval =
((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII); ((tempval & ~(MACCFG2_IF)) | MACCFG2_GMII);
gfar_write(&regs->maccfg2, tempval);
break; break;
case 100: case 100:
case 10: case 10:
tempval = gfar_read(&regs->maccfg2);
tempval = tempval =
((tempval & ~(MACCFG2_IF)) | MACCFG2_MII); ((tempval & ~(MACCFG2_IF)) | MACCFG2_MII);
gfar_write(&regs->maccfg2, tempval);
break; break;
default: default:
if (netif_msg_link(priv)) if (netif_msg_link(priv))
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, mii_info->speed); dev->name, phydev->speed);
break; break;
} }
if (netif_msg_link(priv)) priv->oldspeed = phydev->speed;
printk(KERN_INFO "%s: Speed %dBT\n", dev->name,
mii_info->speed);
priv->oldspeed = mii_info->speed;
} }
gfar_write(&regs->maccfg2, tempval);
if (!priv->oldlink) { if (!priv->oldlink) {
if (netif_msg_link(priv)) new_state = 1;
printk(KERN_INFO "%s: Link is up\n", dev->name);
priv->oldlink = 1; priv->oldlink = 1;
netif_carrier_on(dev);
netif_schedule(dev); netif_schedule(dev);
} }
} else { } else if (priv->oldlink) {
if (priv->oldlink) { new_state = 1;
if (netif_msg_link(priv)) priv->oldlink = 0;
printk(KERN_INFO "%s: Link is down\n", priv->oldspeed = 0;
dev->name); priv->oldduplex = -1;
priv->oldlink = 0;
priv->oldspeed = 0;
priv->oldduplex = -1;
netif_carrier_off(dev);
}
} }
}
if (new_state && netif_msg_link(priv))
phy_print_status(phydev);
spin_unlock_irqrestore(&priv->lock, flags);
}
/* Update the hash table based on the current list of multicast /* Update the hash table based on the current list of multicast
* addresses we subscribe to. Also, change the promiscuity of * addresses we subscribe to. Also, change the promiscuity of
...@@ -2122,12 +1871,23 @@ static struct device_driver gfar_driver = { ...@@ -2122,12 +1871,23 @@ static struct device_driver gfar_driver = {
static int __init gfar_init(void) static int __init gfar_init(void)
{ {
return driver_register(&gfar_driver); int err = gfar_mdio_init();
if (err)
return err;
err = driver_register(&gfar_driver);
if (err)
gfar_mdio_exit();
return err;
} }
static void __exit gfar_exit(void) static void __exit gfar_exit(void)
{ {
driver_unregister(&gfar_driver); driver_unregister(&gfar_driver);
gfar_mdio_exit();
} }
module_init(gfar_init); module_init(gfar_init);
......
...@@ -17,7 +17,6 @@ ...@@ -17,7 +17,6 @@
* *
* 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 * -Add patch for ethtool phys id
*/ */
#ifndef __GIANFAR_H #ifndef __GIANFAR_H
...@@ -37,7 +36,8 @@ ...@@ -37,7 +36,8 @@
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fsl_devices.h> #include <linux/mii.h>
#include <linux/phy.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/irq.h> #include <asm/irq.h>
...@@ -48,7 +48,8 @@ ...@@ -48,7 +48,8 @@
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include "gianfar_phy.h" #include <linux/fsl_devices.h>
#include "gianfar_mii.h"
/* The maximum number of packets to be handled in one call of gfar_poll */ /* The maximum number of packets to be handled in one call of gfar_poll */
#define GFAR_DEV_WEIGHT 64 #define GFAR_DEV_WEIGHT 64
...@@ -73,7 +74,7 @@ ...@@ -73,7 +74,7 @@
#define PHY_INIT_TIMEOUT 100000 #define PHY_INIT_TIMEOUT 100000
#define GFAR_PHY_CHANGE_TIME 2 #define GFAR_PHY_CHANGE_TIME 2
#define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.1, " #define DEVICE_NAME "%s: Gianfar Ethernet Controller Version 1.2, "
#define DRV_NAME "gfar-enet" #define DRV_NAME "gfar-enet"
extern const char gfar_driver_name[]; extern const char gfar_driver_name[];
extern const char gfar_driver_version[]; extern const char gfar_driver_version[];
...@@ -578,12 +579,7 @@ struct gfar { ...@@ -578,12 +579,7 @@ struct gfar {
u32 hafdup; /* 0x.50c - Half Duplex Register */ u32 hafdup; /* 0x.50c - Half Duplex Register */
u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */ u32 maxfrm; /* 0x.510 - Maximum Frame Length Register */
u8 res18[12]; u8 res18[12];
u32 miimcfg; /* 0x.520 - MII Management Configuration Register */ u8 gfar_mii_regs[24]; /* See gianfar_phy.h */
u32 miimcom; /* 0x.524 - MII Management Command Register */
u32 miimadd; /* 0x.528 - MII Management Address Register */
u32 miimcon; /* 0x.52c - MII Management Control Register */
u32 miimstat; /* 0x.530 - MII Management Status Register */
u32 miimind; /* 0x.534 - MII Management Indicator Register */
u8 res19[4]; u8 res19[4];
u32 ifstat; /* 0x.53c - Interface Status Register */ u32 ifstat; /* 0x.53c - Interface Status Register */
u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */ u32 macstnaddr1; /* 0x.540 - Station Address Part 1 Register */
...@@ -688,9 +684,6 @@ struct gfar_private { ...@@ -688,9 +684,6 @@ struct gfar_private {
struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */ struct gfar *regs; /* Pointer to the GFAR memory mapped Registers */
u32 *hash_regs[16]; u32 *hash_regs[16];
int hash_width; int hash_width;
struct gfar *phyregs;
struct work_struct tq;
struct timer_list phy_info_timer;
struct net_device_stats stats; /* linux network statistics */ struct net_device_stats stats; /* linux network statistics */
struct gfar_extra_stats extra_stats; struct gfar_extra_stats extra_stats;
spinlock_t lock; spinlock_t lock;
...@@ -710,7 +703,8 @@ struct gfar_private { ...@@ -710,7 +703,8 @@ struct gfar_private {
unsigned int interruptError; unsigned int interruptError;
struct gianfar_platform_data *einfo; struct gianfar_platform_data *einfo;
struct gfar_mii_info *mii_info; struct phy_device *phydev;
struct mii_bus *mii_bus;
int oldspeed; int oldspeed;
int oldduplex; int oldduplex;
int oldlink; int oldlink;
...@@ -732,4 +726,12 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val) ...@@ -732,4 +726,12 @@ extern inline void gfar_write(volatile unsigned *addr, u32 val)
extern struct ethtool_ops *gfar_op_array[]; extern struct ethtool_ops *gfar_op_array[];
extern irqreturn_t gfar_receive(int irq, void *dev_id, struct pt_regs *regs);
extern int startup_gfar(struct net_device *dev);
extern void stop_gfar(struct net_device *dev);
extern void gfar_halt(struct net_device *dev);
extern void gfar_phy_test(struct mii_bus *bus, struct phy_device *phydev,
int enable, u32 regnum, u32 read);
void gfar_setup_stashing(struct net_device *dev);
#endif /* __GIANFAR_H */ #endif /* __GIANFAR_H */
...@@ -39,17 +39,18 @@ ...@@ -39,17 +39,18 @@
#include <asm/types.h> #include <asm/types.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include "gianfar.h" #include "gianfar.h"
#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) #define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0))
extern int startup_gfar(struct net_device *dev);
extern void stop_gfar(struct net_device *dev);
extern void gfar_halt(struct net_device *dev);
extern void gfar_start(struct net_device *dev); extern void gfar_start(struct net_device *dev);
extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit); extern int gfar_clean_rx_ring(struct net_device *dev, int rx_work_limit);
#define GFAR_MAX_COAL_USECS 0xffff
#define GFAR_MAX_COAL_FRAMES 0xff
static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy, static void gfar_fill_stats(struct net_device *dev, struct ethtool_stats *dummy,
u64 * buf); u64 * buf);
static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf); static void gfar_gstrings(struct net_device *dev, u32 stringset, u8 * buf);
...@@ -182,38 +183,32 @@ static void gfar_gdrvinfo(struct net_device *dev, struct ...@@ -182,38 +183,32 @@ static void gfar_gdrvinfo(struct net_device *dev, struct
drvinfo->eedump_len = 0; drvinfo->eedump_len = 0;
} }
static int gfar_ssettings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct gfar_private *priv = netdev_priv(dev);
struct phy_device *phydev = priv->phydev;
if (NULL == phydev)
return -ENODEV;
return phy_ethtool_sset(phydev, cmd);
}
/* Return the current settings in the ethtool_cmd structure */ /* Return the current settings in the ethtool_cmd structure */
static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd) static int gfar_gsettings(struct net_device *dev, struct ethtool_cmd *cmd)
{ {
struct gfar_private *priv = netdev_priv(dev); struct gfar_private *priv = netdev_priv(dev);
uint gigabit_support = struct phy_device *phydev = priv->phydev;
priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
SUPPORTED_1000baseT_Full : 0; if (NULL == phydev)
uint gigabit_advert = return -ENODEV;
priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_GIGABIT ?
ADVERTISED_1000baseT_Full: 0;
cmd->supported = (SUPPORTED_10baseT_Half
| SUPPORTED_100baseT_Half
| SUPPORTED_100baseT_Full
| gigabit_support | SUPPORTED_Autoneg);
/* For now, we always advertise everything */
cmd->advertising = (ADVERTISED_10baseT_Half
| ADVERTISED_100baseT_Half
| ADVERTISED_100baseT_Full
| gigabit_advert | ADVERTISED_Autoneg);
cmd->speed = priv->mii_info->speed;
cmd->duplex = priv->mii_info->duplex;
cmd->port = PORT_MII;
cmd->phy_address = priv->mii_info->mii_id;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = AUTONEG_ENABLE;
cmd->maxtxpkt = priv->txcount; cmd->maxtxpkt = priv->txcount;
cmd->maxrxpkt = priv->rxcount; cmd->maxrxpkt = priv->rxcount;
return 0; return phy_ethtool_gset(phydev, cmd);
} }
/* Return the length of the register structure */ /* Return the length of the register structure */
...@@ -241,14 +236,14 @@ static unsigned int gfar_usecs2ticks(struct gfar_private *priv, unsigned int use ...@@ -241,14 +236,14 @@ 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->mii_info->speed) { switch (priv->phydev->speed) {
case 1000: case SPEED_1000:
count = GFAR_GBIT_TIME; count = GFAR_GBIT_TIME;
break; break;
case 100: case SPEED_100:
count = GFAR_100_TIME; count = GFAR_100_TIME;
break; break;
case 10: case SPEED_10:
default: default:
count = GFAR_10_TIME; count = GFAR_10_TIME;
break; break;
...@@ -265,14 +260,14 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, unsigned int tic ...@@ -265,14 +260,14 @@ 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->mii_info->speed) { switch (priv->phydev->speed) {
case 1000: case SPEED_1000:
count = GFAR_GBIT_TIME; count = GFAR_GBIT_TIME;
break; break;
case 100: case SPEED_100:
count = GFAR_100_TIME; count = GFAR_100_TIME;
break; break;
case 10: case SPEED_10:
default: default:
count = GFAR_10_TIME; count = GFAR_10_TIME;
break; break;
...@@ -292,6 +287,9 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals ...@@ -292,6 +287,9 @@ static int gfar_gcoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE)) if (!(priv->einfo->device_flags & FSL_GIANFAR_DEV_HAS_COALESCE))
return -EOPNOTSUPP; return -EOPNOTSUPP;
if (NULL == priv->phydev)
return -ENODEV;
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;
...@@ -348,6 +346,22 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals ...@@ -348,6 +346,22 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
else else
priv->rxcoalescing = 1; priv->rxcoalescing = 1;
if (NULL == priv->phydev)
return -ENODEV;
/* Check the bounds of the values */
if (cvals->rx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
pr_info("Coalescing is limited to %d microseconds\n",
GFAR_MAX_COAL_USECS);
return -EINVAL;
}
if (cvals->rx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
pr_info("Coalescing is limited to %d frames\n",
GFAR_MAX_COAL_FRAMES);
return -EINVAL;
}
priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs); priv->rxtime = gfar_usecs2ticks(priv, cvals->rx_coalesce_usecs);
priv->rxcount = cvals->rx_max_coalesced_frames; priv->rxcount = cvals->rx_max_coalesced_frames;
...@@ -358,6 +372,19 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals ...@@ -358,6 +372,19 @@ static int gfar_scoalesce(struct net_device *dev, struct ethtool_coalesce *cvals
else else
priv->txcoalescing = 1; priv->txcoalescing = 1;
/* Check the bounds of the values */
if (cvals->tx_coalesce_usecs > GFAR_MAX_COAL_USECS) {
pr_info("Coalescing is limited to %d microseconds\n",
GFAR_MAX_COAL_USECS);
return -EINVAL;
}
if (cvals->tx_max_coalesced_frames > GFAR_MAX_COAL_FRAMES) {
pr_info("Coalescing is limited to %d frames\n",
GFAR_MAX_COAL_FRAMES);
return -EINVAL;
}
priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs); priv->txtime = gfar_usecs2ticks(priv, cvals->tx_coalesce_usecs);
priv->txcount = cvals->tx_max_coalesced_frames; priv->txcount = cvals->tx_max_coalesced_frames;
...@@ -536,6 +563,7 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data) ...@@ -536,6 +563,7 @@ static void gfar_set_msglevel(struct net_device *dev, uint32_t data)
struct ethtool_ops gfar_ethtool_ops = { struct ethtool_ops gfar_ethtool_ops = {
.get_settings = gfar_gsettings, .get_settings = gfar_gsettings,
.set_settings = gfar_ssettings,
.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,
......
/*
* drivers/net/gianfar_mii.c
*
* Gianfar Ethernet Driver -- MIIM bus implementation
* Provides Bus interface for MIIM regs
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/unistd.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/version.h>
#include <asm/ocp.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include <linux/phy.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include "gianfar.h"
#include "gianfar_mii.h"
/* Write value to the PHY at mii_id at register regnum,
* on the bus, waiting until the write is done before returning.
* All PHY configuration is done through the TSEC1 MIIM regs */
int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
{
struct gfar_mii *regs = bus->priv;
/* Set the PHY address and the register address we want to write */
gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(&regs->miimcon, value);
/* Wait for the transaction to finish */
while (gfar_read(&regs->miimind) & MIIMIND_BUSY)
cpu_relax();
return 0;
}
/* Read the bus for PHY at addr mii_id, register regnum, and
* return the value. Clears miimcom first. All PHY
* configuration has to be done through the TSEC1 MIIM regs */
int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
{
struct gfar_mii *regs = bus->priv;
u16 value;
/* Set the PHY address and the register address we want to read */
gfar_write(&regs->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(&regs->miimcom, 0);
gfar_write(&regs->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
cpu_relax();
/* Grab the value of the register from miimstat */
value = gfar_read(&regs->miimstat);
return value;
}
/* Reset the MIIM registers, and wait for the bus to free */
int gfar_mdio_reset(struct mii_bus *bus)
{
struct gfar_mii *regs = bus->priv;
unsigned int timeout = PHY_INIT_TIMEOUT;
spin_lock_bh(&bus->mdio_lock);
/* Reset the management interface */
gfar_write(&regs->miimcfg, MIIMCFG_RESET);
/* Setup the MII Mgmt clock speed */
gfar_write(&regs->miimcfg, MIIMCFG_INIT_VALUE);
/* Wait until the bus is free */
while ((gfar_read(&regs->miimind) & MIIMIND_BUSY) &&
timeout--)
cpu_relax();
spin_unlock_bh(&bus->mdio_lock);
if(timeout <= 0) {
printk(KERN_ERR "%s: The MII Bus is stuck!\n",
bus->name);
return -EBUSY;
}
return 0;
}
int gfar_mdio_probe(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct gianfar_mdio_data *pdata;
struct gfar_mii *regs;
struct mii_bus *new_bus;
int err = 0;
if (NULL == dev)
return -EINVAL;
new_bus = kmalloc(sizeof(struct mii_bus), GFP_KERNEL);
if (NULL == new_bus)
return -ENOMEM;
new_bus->name = "Gianfar MII Bus",
new_bus->read = &gfar_mdio_read,
new_bus->write = &gfar_mdio_write,
new_bus->reset = &gfar_mdio_reset,
new_bus->id = pdev->id;
pdata = (struct gianfar_mdio_data *)pdev->dev.platform_data;
if (NULL == pdata) {
printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id);
return -ENODEV;
}
/* Set the PHY base address */
regs = (struct gfar_mii *) ioremap(pdata->paddr,
sizeof (struct gfar_mii));
if (NULL == regs) {
err = -ENOMEM;
goto reg_map_fail;
}
new_bus->priv = regs;
new_bus->irq = pdata->irq;
new_bus->dev = dev;
dev_set_drvdata(dev, new_bus);
err = mdiobus_register(new_bus);
if (0 != err) {
printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
new_bus->name);
goto bus_register_fail;
}
return 0;
bus_register_fail:
iounmap((void *) regs);
reg_map_fail:
kfree(new_bus);
return err;
}
int gfar_mdio_remove(struct device *dev)
{
struct mii_bus *bus = dev_get_drvdata(dev);
mdiobus_unregister(bus);
dev_set_drvdata(dev, NULL);
iounmap((void *) (&bus->priv));
bus->priv = NULL;
kfree(bus);
return 0;
}
static struct device_driver gianfar_mdio_driver = {
.name = "fsl-gianfar_mdio",
.bus = &platform_bus_type,
.probe = gfar_mdio_probe,
.remove = gfar_mdio_remove,
};
int __init gfar_mdio_init(void)
{
return driver_register(&gianfar_mdio_driver);
}
void __exit gfar_mdio_exit(void)
{
driver_unregister(&gianfar_mdio_driver);
}
/*
* drivers/net/gianfar_mii.h
*
* Gianfar Ethernet Driver -- MII Management Bus Implementation
* Driver for the MDIO bus controller in the Gianfar register space
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __GIANFAR_MII_H
#define __GIANFAR_MII_H
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
#define MII_READ_COMMAND 0x00000001
#define GFAR_SUPPORTED (SUPPORTED_10baseT_Half \
| SUPPORTED_100baseT_Half \
| SUPPORTED_100baseT_Full \
| SUPPORTED_Autoneg \
| SUPPORTED_MII)
struct gfar_mii {
u32 miimcfg; /* 0x.520 - MII Management Config Register */
u32 miimcom; /* 0x.524 - MII Management Command Register */
u32 miimadd; /* 0x.528 - MII Management Address Register */
u32 miimcon; /* 0x.52c - MII Management Control Register */
u32 miimstat; /* 0x.530 - MII Management Status Register */
u32 miimind; /* 0x.534 - MII Management Indicator Register */
};
int gfar_mdio_read(struct mii_bus *bus, int mii_id, int regnum);
int gfar_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value);
int __init gfar_mdio_init(void);
void __exit gfar_mdio_exit(void);
#endif /* GIANFAR_PHY_H */
/*
* drivers/net/gianfar_phy.c
*
* Gianfar Ethernet Driver -- PHY handling
* Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/crc32.h>
#include <linux/mii.h>
#include "gianfar.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, */
/* waiting until the write is done before it returns. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
/* Set the PHY address and the register address we want to write */
gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
/* Write out the value we want */
gfar_write(&regbase->miimcon, value);
/* Wait for the transaction to finish */
while (gfar_read(&regbase->miimind) & MIIMIND_BUSY)
cpu_relax();
}
/* Reads from register regnum in the PHY for device dev, */
/* returning the value. Clears miimcom first. All PHY */
/* configuration has to be done through the TSEC1 MIIM regs */
int read_phy_reg(struct net_device *dev, int mii_id, int regnum)
{
struct gfar_private *priv = netdev_priv(dev);
struct gfar *regbase = priv->phyregs;
u16 value;
/* Set the PHY address and the register address we want to read */
gfar_write(&regbase->miimadd, (mii_id << 8) | regnum);
/* Clear miimcom, and then initiate a read */
gfar_write(&regbase->miimcom, 0);
gfar_write(&regbase->miimcom, MII_READ_COMMAND);
/* Wait for the transaction to finish */
while (gfar_read(&regbase->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
cpu_relax();
/* Grab the value of the register from miimstat */
value = gfar_read(&regbase->miimstat);
return value;
}
void mii_clear_phy_interrupt(struct gfar_mii_info *mii_info)
{
if(mii_info->phyinfo->ack_interrupt)
mii_info->phyinfo->ack_interrupt(mii_info);
}
void mii_configure_phy_interrupt(struct gfar_mii_info *mii_info, u32 interrupts)
{
mii_info->interrupts = interrupts;
if(mii_info->phyinfo->config_intr)
mii_info->phyinfo->config_intr(mii_info);
}
/* Writes MII_ADVERTISE with the appropriate values, after
* sanitizing advertise to make sure only supported features
* are advertised
*/
static void config_genmii_advert(struct gfar_mii_info *mii_info)
{
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);
}
static void genmii_setup_forced(struct gfar_mii_info *mii_info)
{
u16 ctrl;
u32 features = mii_info->phyinfo->features;
ctrl = phy_read(mii_info, MII_BMCR);
ctrl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_SPEED1000|BMCR_ANENABLE);
ctrl |= BMCR_RESET;
switch(mii_info->speed) {
case SPEED_1000:
if(features & (SUPPORTED_1000baseT_Half
| SUPPORTED_1000baseT_Full)) {
ctrl |= BMCR_SPEED1000;
break;
}
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;
}
static int genmii_update_link(struct gfar_mii_info *mii_info)
{
u16 status;
/* Do a fake read */
phy_read(mii_info, MII_BMSR);
/* Read link and autonegotiation status */
status = phy_read(mii_info, MII_BMSR);
if ((status & BMSR_LSTATUS) == 0)
mii_info->link = 0;
else
mii_info->link = 1;
/* If we are autonegotiating, and not done,
* return an error */
if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE))
return -EAGAIN;
return 0;
}
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;
case MII_M1011_PHY_SPEC_STATUS_100:
mii_info->speed = SPEED_100;
break;
default:
mii_info->speed = SPEED_10;
break;
}
mii_info->pause = 0;
}
return 0;
}
static int cis820x_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_CIS8201_AUX_CONSTAT);
if (status & MII_CIS8201_AUXCONSTAT_DUPLEX)
mii_info->duplex = DUPLEX_FULL;
else
mii_info->duplex = DUPLEX_HALF;
speed = status & MII_CIS8201_AUXCONSTAT_SPEED;
switch (speed) {
case MII_CIS8201_AUXCONSTAT_GBIT:
mii_info->speed = SPEED_1000;
break;
case MII_CIS8201_AUXCONSTAT_100:
mii_info->speed = SPEED_100;
break;
default:
mii_info->speed = SPEED_10;
break;
}
}
return 0;
}
static int marvell_ack_interrupt(struct gfar_mii_info *mii_info)
{
/* Clear the interrupts by reading the reg */
phy_read(mii_info, MII_M1011_IEVENT);
return 0;
}
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
phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR);
return 0;
}
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
phy_write(mii_info, MII_CIS8201_IMASK, 0);
return 0;
}
#define DM9161_DELAY 10
static int dm9161_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) {
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;
if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F))
mii_info->duplex = DUPLEX_FULL;
else
mii_info->duplex = DUPLEX_HALF;
}
return 0;
}
static int dm9161_config_aneg(struct gfar_mii_info *mii_info)
{
struct dm9161_private *priv = mii_info->priv;
if(0 == priv->resetdone)
return -EAGAIN;
return 0;
}
static void dm9161_timer(unsigned long data)
{
struct gfar_mii_info *mii_info = (struct gfar_mii_info *)data;
struct dm9161_private *priv = mii_info->priv;
u16 status = phy_read(mii_info, MII_BMSR);
if (status & BMSR_ANEGCOMPLETE) {
priv->resetdone = 1;
} else
mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ);
}
static int dm9161_init(struct gfar_mii_info *mii_info)
{
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",
0x000fffc0,
.features = MII_GBIT_FEATURES,
.init = &cis820x_init,
.config_aneg = &gbit_config_aneg,
.read_status = &cis820x_read_status,
.ack_interrupt = &cis820x_ack_interrupt,
.config_intr = &cis820x_config_intr,
};
static struct phy_info phy_info_dm9161 = {
.phy_id = 0x0181b880,
.name = "Davicom DM9161E",
.phy_id_mask = 0x0ffffff0,
.init = dm9161_init,
.config_aneg = dm9161_config_aneg,
.read_status = dm9161_read_status,
.close = dm9161_close,
};
static struct phy_info phy_info_marvell = {
.phy_id = 0x01410c00,
.phy_id_mask = 0xffffff00,
.name = "Marvell 88E1101/88E1111",
.features = MII_GBIT_FEATURES,
.config_aneg = &marvell_config_aneg,
.read_status = &marvell_read_status,
.ack_interrupt = &marvell_ack_interrupt,
.config_intr = &marvell_config_intr,
};
static struct phy_info phy_info_genmii= {
.phy_id = 0x00000000,
.phy_id_mask = 0x00000000,
.name = "Generic MII",
.features = MII_BASIC_FEATURES,
.config_aneg = genmii_config_aneg,
.read_status = genmii_read_status,
};
static struct phy_info *phy_info[] = {
&phy_info_cis820x,
&phy_info_marvell,
&phy_info_dm9161,
&phy_info_genmii,
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
* to device dev. return a struct phy_info structure describing that PHY
*/
struct phy_info * get_phy_info(struct gfar_mii_info *mii_info)
{
u16 phy_reg;
u32 phy_ID;
int i;
struct phy_info *theInfo = NULL;
struct net_device *dev = mii_info->dev;
/* Grab the bits from PHYIR1, and put them in the upper half */
phy_reg = phy_read(mii_info, MII_PHYSID1);
phy_ID = (phy_reg & 0xffff) << 16;
/* Grab the bits from PHYIR2, and put them in the lower half */
phy_reg = phy_read(mii_info, MII_PHYSID2);
phy_ID |= (phy_reg & 0xffff);
/* loop through all the known PHY types, and find one that */
/* matches the ID we read from the PHY. */
for (i = 0; phy_info[i]; i++)
if (phy_info[i]->phy_id ==
(phy_ID & phy_info[i]->phy_id_mask)) {
theInfo = phy_info[i];
break;
}
/* This shouldn't happen, as we have generic PHY support */
if (theInfo == NULL) {
printk("%s: PHY id %x is not supported!\n", dev->name, phy_ID);
return NULL;
} else {
printk("%s: PHY is %s (%x)\n", dev->name, theInfo->name,
phy_ID);
}
return theInfo;
}
/*
* drivers/net/gianfar_phy.h
*
* Gianfar Ethernet Driver -- PHY handling
* Driver for FEC on MPC8540 and TSEC on MPC8540/MPC8560
* Based on 8260_io/fcc_enet.c
*
* Author: Andy Fleming
* Maintainer: Kumar Gala (kumar.gala@freescale.com)
*
* Copyright (c) 2002-2004 Freescale Semiconductor, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __GIANFAR_PHY_H
#define __GIANFAR_PHY_H
#define MII_end ((u32)-2)
#define MII_read ((u32)-1)
#define MIIMIND_BUSY 0x00000001
#define MIIMIND_NOTVALID 0x00000004
#define GFAR_AN_TIMEOUT 2000
/* 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 */
#define MII_CIS8201_EXT_CON1 0x17
#define MII_CIS8201_EXTCON1_INIT 0x0000
/* Cicada Interrupt Mask Register */
#define MII_CIS8201_IMASK 0x19
#define MII_CIS8201_IMASK_IEN 0x8000
#define MII_CIS8201_IMASK_SPEED 0x4000
#define MII_CIS8201_IMASK_LINK 0x2000
#define MII_CIS8201_IMASK_DUPLEX 0x1000
#define MII_CIS8201_IMASK_MASK 0xf000
/* Cicada Interrupt Status Register */
#define MII_CIS8201_ISTAT 0x1a
#define MII_CIS8201_ISTAT_STATUS 0x8000
#define MII_CIS8201_ISTAT_SPEED 0x4000
#define MII_CIS8201_ISTAT_LINK 0x2000
#define MII_CIS8201_ISTAT_DUPLEX 0x1000
/* Cicada Auxiliary Control/Status Register */
#define MII_CIS8201_AUX_CONSTAT 0x1c
#define MII_CIS8201_AUXCONSTAT_INIT 0x0004
#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020
#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018
#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010
#define MII_CIS8201_AUXCONSTAT_100 0x0008
/* 88E1011 PHY Status Register */
#define MII_M1011_PHY_SPEC_STATUS 0x11
#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000
#define MII_M1011_PHY_SPEC_STATUS_100 0x4000
#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000
#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000
#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800
#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400
#define MII_M1011_IEVENT 0x13
#define MII_M1011_IEVENT_CLEAR 0x0000
#define MII_M1011_IMASK 0x12
#define MII_M1011_IMASK_INIT 0x6400
#define MII_M1011_IMASK_CLEAR 0x0000
#define MII_DM9161_SCR 0x10
#define MII_DM9161_SCR_INIT 0x0610
/* DM9161 Specified Configuration and Status Register */
#define MII_DM9161_SCSR 0x11
#define MII_DM9161_SCSR_100F 0x8000
#define MII_DM9161_SCSR_100H 0x4000
#define MII_DM9161_SCSR_10F 0x2000
#define MII_DM9161_SCSR_10H 0x1000
/* DM9161 Interrupt Register */
#define MII_DM9161_INTR 0x15
#define MII_DM9161_INTR_PEND 0x8000
#define MII_DM9161_INTR_DPLX_MASK 0x0800
#define MII_DM9161_INTR_SPD_MASK 0x0400
#define MII_DM9161_INTR_LINK_MASK 0x0200
#define MII_DM9161_INTR_MASK 0x0100
#define MII_DM9161_INTR_DPLX_CHANGE 0x0010
#define MII_DM9161_INTR_SPD_CHANGE 0x0008
#define MII_DM9161_INTR_LINK_CHANGE 0x0004
#define MII_DM9161_INTR_INIT 0x0000
#define MII_DM9161_INTR_STOP \
(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \
| MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK)
/* DM9161 10BT Configuration/Status */
#define MII_DM9161_10BTCSR 0x12
#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;
/* Enabled Interrupts */
u32 interrupts;
u32 advertising;
int autoneg;
int mii_id;
/* private data pointer */
/* For use by PHYs to maintain extra state */
void *priv;
/* Provided by host chip */
struct net_device *dev;
/* A lock to ensure that only one thing can read/write
* the MDIO bus at a time */
spinlock_t mdio_lock;
/* Provided by ethernet driver */
int (*mdio_read) (struct net_device *dev, int mii_id, int reg);
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
*
* id will contain a number which represents the PHY. During
* 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
* gotten from the PHY will be ANDed with phy_id_mask to
* discard any bits which may change based on revision numbers
* unimportant to functionality
*
* There are 6 commands which take a gfar_mii_info structure.
* Each PHY must declare config_aneg, and read_status.
*/
struct phy_info {
u32 phy_id;
char *name;
unsigned int phy_id_mask;
u32 features;
/* Called to initialize the PHY */
int (*init)(struct gfar_mii_info *mii_info);
/* Called to suspend the PHY for power */
int (*suspend)(struct gfar_mii_info *mii_info);
/* Reconfigures autonegotiation (or disables it) */
int (*config_aneg)(struct gfar_mii_info *mii_info);
/* Determines the negotiated speed and duplex */
int (*read_status)(struct gfar_mii_info *mii_info);
/* 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 gfar_mii_info *mii_info);
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 */
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