diff options
Diffstat (limited to 'target/linux/coldfire/files-2.6.31/drivers')
3 files changed, 2072 insertions, 0 deletions
diff --git a/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.c b/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.c new file mode 100644 index 0000000000..18a2fb914a --- /dev/null +++ b/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.c @@ -0,0 +1,1561 @@ +/* + * Copyright 2007-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Author: Kurt Mahan, kmahan@freescale.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/phy.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> +#include <linux/bitops.h> + +#include <asm/coldfire.h> +#include <asm/mcfsim.h> + +#include <asm/dma.h> +#include <asm/MCD_dma.h> +#include <asm/m5485sram.h> +#include <asm/virtconvert.h> +#include <asm/irq.h> + +#include "fec_m547x.h" + +#ifdef CONFIG_FEC_548x_ENABLE_FEC2 +#define FEC_MAX_PORTS 2 +#define FEC_2 +#else +#define FEC_MAX_PORTS 1 +#undef FEC_2 +#endif + +#define VERSION "0.20" +MODULE_DESCRIPTION("DMA Fast Ethernet Controller driver ver " VERSION); + +/* fec private */ +struct fec_priv { + struct net_device *netdev; /* owning net device */ + void *fecpriv_txbuf[FEC_TX_BUF_NUMBER]; /* tx buffer ptrs */ + MCD_bufDescFec *fecpriv_txdesc; /* tx descriptor ptrs */ + volatile unsigned int fecpriv_current_tx; /* current tx desc index */ + volatile unsigned int fecpriv_next_tx; /* next tx desc index */ + unsigned int fecpriv_current_rx; /* current rx desc index */ + MCD_bufDescFec *fecpriv_rxdesc; /* rx descriptor ptrs */ + struct sk_buff *askb_rx[FEC_RX_BUF_NUMBER]; /* rx SKB ptrs */ + unsigned int fecpriv_initiator_rx; /* rx dma initiator */ + unsigned int fecpriv_initiator_tx; /* tx dma initiator */ + int fecpriv_fec_rx_channel; /* rx dma channel */ + int fecpriv_fec_tx_channel; /* tx dma channel */ + int fecpriv_rx_requestor; /* rx dma requestor */ + int fecpriv_tx_requestor; /* tx dma requestor */ + void *fecpriv_interrupt_fec_rx_handler; /* dma rx handler */ + void *fecpriv_interrupt_fec_tx_handler; /* dma tx handler */ + unsigned char *fecpriv_mac_addr; /* private fec mac addr */ + struct net_device_stats fecpriv_stat; /* stats ptr */ + spinlock_t fecpriv_lock; + int fecpriv_rxflag; + struct tasklet_struct fecpriv_tasklet_reinit; + int index; /* fec hw number */ + struct phy_device *phydev; + struct mii_bus *mdio_bus; + int duplex; + int link; + int speed; +}; + +struct net_device *fec_dev[FEC_MAX_PORTS]; + +/* FEC functions */ +static int __init fec_init(void); +static struct net_device_stats *fec_get_stat(struct net_device *dev); +static int fec_open(struct net_device *dev); +static int fec_close(struct net_device *nd); +static int fec_tx(struct sk_buff *skb, struct net_device *dev); +static void fec_set_multicast_list(struct net_device *nd); +static int fec_set_mac_address(struct net_device *dev, void *p); +static void fec_tx_timeout(struct net_device *dev); +static void fec_interrupt_fec_tx_handler(struct net_device *dev); +static void fec_interrupt_fec_rx_handler(struct net_device *dev); +static irqreturn_t fec_interrupt_handler(int irq, void *dev_id); +static void fec_interrupt_fec_tx_handler_fec0(void); +static void fec_interrupt_fec_rx_handler_fec0(void); +static void fec_interrupt_fec_reinit(unsigned long data); + +/* default fec0 address */ +unsigned char fec_mac_addr_fec0[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x50 }; + +#ifdef FEC_2 +/* default fec1 address */ +unsigned char fec_mac_addr_fec1[6] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x51 }; +#endif + +extern unsigned char uboot_enet0[]; +extern unsigned char uboot_enet1[]; + +#ifndef MODULE +int fec_str_to_mac(char *str_mac, unsigned char* addr); +int __init fec_mac_setup0(char *s); +#endif + + +#ifdef FEC_2 +void fec_interrupt_fec_tx_handler_fec1(void); +void fec_interrupt_fec_rx_handler_fec1(void); +#endif + +#ifndef MODULE +int __init fec_mac_setup1(char *s); +#endif + +module_init(fec_init); +/* module_exit(fec_cleanup); */ + +__setup("mac0=", fec_mac_setup0); + +#ifdef FEC_2 +__setup("mac1=", fec_mac_setup1); +#endif + +#define mk_mii_read(REG) (0x60020000 | ((REG & 0x1f) << 18)) +#define mk_mii_write(REG, VAL) (0x50020000 | ((REG & 0x1f) << 18) | \ + (VAL & 0xffff)) +/* ----------------------------------------------------------- */ +static int coldfire_fec_mdio_read(struct mii_bus *bus, + int phy_id, int reg) +{ + int ret; + struct net_device *dev = bus->priv; +#ifdef CONFIG_FEC_548x_SHARED_PHY + unsigned long base_addr = (unsigned long)FEC_BASE_ADDR_FEC0; +#else + unsigned long base_addr = (unsigned long) dev->base_addr; +#endif + int tries = 100; + + /* Clear the MII interrupt bit */ + FEC_EIR(base_addr) = FEC_EIR_MII; + + /* Write to the MII management frame register */ + FEC_MMFR(base_addr) = mk_mii_read(reg) | (phy_id << 23); + + /* Wait for the reading */ + while (!(FEC_EIR(base_addr) & FEC_EIR_MII)) { + udelay(10); + + if (!tries) { + printk(KERN_ERR "%s timeout\n", __func__); + return -ETIMEDOUT; + } + tries--; + } + + /* Clear the MII interrupt bit */ + FEC_EIR(base_addr) = FEC_EIR_MII; + ret = FEC_MMFR(base_addr) & 0x0000FFFF; + return ret; +} + +static int coldfire_fec_mdio_write(struct mii_bus *bus, + int phy_id, int reg, u16 data) +{ + int ret; + struct net_device *dev = bus->priv; +#ifdef CONFIG_FEC_548x_SHARED_PHY + unsigned long base_addr = (unsigned long)FEC_BASE_ADDR_FEC0; +#else + unsigned long base_addr = (unsigned long) dev->base_addr; +#endif + int tries = 100; + + printk(KERN_ERR "%s base_addr %x, phy_id %x, reg %x, data %x\n", + __func__, base_addr, phy_id, reg, data); + /* Clear the MII interrupt bit */ + FEC_EIR(base_addr) = FEC_EIR_MII; + + /* Write to the MII management frame register */ + FEC_MMFR(base_addr) = mk_mii_write(reg, data) | (phy_id << 23); + + /* Wait for the writing */ + while (!(FEC_EIR(base_addr) & FEC_EIR_MII)) { + udelay(10); + if (!tries) { + printk(KERN_ERR "%s timeout\n", __func__); + return -ETIMEDOUT; + } + tries--; + } + /* Clear the MII interrupt bit */ + FEC_EIR(base_addr) = FEC_EIR_MII; + ret = FEC_MMFR(base_addr) & 0x0000FFFF; + + return ret; +} + +static void fec_adjust_link(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = priv->phydev; + int new_state = 0; + + if (phydev->link != PHY_DOWN) { + if (phydev->duplex != priv->duplex) { + new_state = 1; + priv->duplex = phydev->duplex; + } + + if (phydev->speed != priv->speed) { + new_state = 1; + priv->speed = phydev->speed; + } + + if (priv->link == PHY_DOWN) { + new_state = 1; + priv->link = phydev->link; + } + } else if (priv->link) { + new_state = 1; + priv->link = PHY_DOWN; + priv->speed = 0; + priv->duplex = -1; + } + + if (new_state) + phy_print_status(phydev); +} + +static int coldfire_fec_init_phy(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + struct phy_device *phydev = NULL; + int i; + int startnode; + +#ifdef CONFIG_FEC_548x_SHARED_PHY + if (priv->index == 0) + startnode = 0; + else if (priv->index == 1) { + struct fec_priv *priv0 = netdev_priv(fec_dev[0]); + startnode = priv0->phydev->addr + 1; + } else + startnode = 0; +#else + startnode = 0; +#endif +#ifdef FEC_DEBUG + printk(KERN_ERR "%s priv->index %x, startnode %x\n", + __func__, priv->index, startnode); +#endif + /* search for connect PHY device */ + for (i = startnode; i < PHY_MAX_ADDR; i++) { + struct phy_device *const tmp_phydev = + priv->mdio_bus->phy_map[i]; + + if (!tmp_phydev) { +#ifdef FEC_DEBUG + printk(KERN_INFO "%s no PHY here at" + "mii_bus->phy_map[%d]\n", + __func__, i); +#endif + continue; /* no PHY here... */ + } + phydev = tmp_phydev; +#ifdef FEC_DEBUG + printk(KERN_INFO "%s find PHY here at" + "mii_bus->phy_map[%d]\n", + __func__, i); +#endif + break; /* found it */ + } + + /* now we are supposed to have a proper phydev, to attach to... */ + if (!phydev) { + printk(KERN_INFO "%s: Don't found any phy device at all\n", + dev->name); + return -ENODEV; + } + + priv->link = 0; + priv->speed = 0; + priv->duplex = 0; +#ifdef FEC_DEBUG + printk(KERN_INFO "%s phydev_busid %s\n", __func__, dev_name(&phydev->dev)); +#endif + phydev = phy_connect(dev, dev_name(&phydev->dev), + &fec_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (IS_ERR(phydev)) { + printk(KERN_ERR " %s phy_connect failed\n", __func__); + return PTR_ERR(phydev); + } + + printk(KERN_INFO "attached phy %i to driver %s\n", + phydev->addr, phydev->drv->name); + priv->phydev = phydev; + return 0; +} + +static int fec_mdio_register(struct net_device *dev, + int slot) +{ + int err = 0; + struct fec_priv *fp = netdev_priv(dev); + + fp->mdio_bus = mdiobus_alloc(); + if (!fp->mdio_bus) { + printk(KERN_ERR "ethernet mdiobus_alloc fail\n"); + return -ENOMEM; + } + + if (slot == 0) { + fp->mdio_bus->name = "Coldfire FEC MII 0 Bus"; + strcpy(fp->mdio_bus->id, "0"); + } else if (slot == 1) { + fp->mdio_bus->name = "Coldfire FEC MII 1 Bus"; + strcpy(fp->mdio_bus->id, "1"); + } else { + printk(KERN_ERR "Now coldfire can not" + "support more than 2 mii bus\n"); + } + + fp->mdio_bus->read = &coldfire_fec_mdio_read; + fp->mdio_bus->write = &coldfire_fec_mdio_write; + fp->mdio_bus->priv = dev; + err = mdiobus_register(fp->mdio_bus); + if (err) { + mdiobus_free(fp->mdio_bus); + printk(KERN_ERR "%s: ethernet mdiobus_register fail %d\n", + dev->name, err); + return -EIO; + } + + printk(KERN_INFO "mdiobus_register %s ok\n", + fp->mdio_bus->name); + return err; +} + +static const struct net_device_ops fec_netdev_ops = { + .ndo_open = fec_open, + .ndo_stop = fec_close, + .ndo_start_xmit = fec_tx, + .ndo_set_multicast_list = fec_set_multicast_list, + .ndo_tx_timeout = fec_tx_timeout, + .ndo_get_stats = fec_get_stat, + .ndo_validate_addr = eth_validate_addr, + .ndo_set_mac_address = fec_set_mac_address, +}; + +/* + * Initialize a FEC device + */ +int fec_enet_init(struct net_device *dev, int slot) +{ + struct fec_priv *fp = netdev_priv(dev); + int i; + + fp->index = slot; + fp->netdev = dev; + fec_dev[slot] = dev; + + if (slot == 0) { + /* disable fec0 */ + FEC_ECR(FEC_BASE_ADDR_FEC0) = FEC_ECR_DISABLE; + + /* setup the interrupt handler */ + dev->irq = 64 + ISC_FEC0; + + if (request_irq(dev->irq, fec_interrupt_handler, + IRQF_DISABLED, "ColdFire FEC 0", dev)) { + dev->irq = 0; + printk(KERN_ERR "Cannot allocate FEC0 IRQ\n"); + } else { + /* interrupt priority and level */ + MCF_ICR(ISC_FEC0) = ILP_FEC0; + } + + /* fec base address */ + dev->base_addr = FEC_BASE_ADDR_FEC0; + + /* requestor numbers */ + fp->fecpriv_rx_requestor = DMA_FEC0_RX; + fp->fecpriv_tx_requestor = DMA_FEC0_TX; + + /* fec0 handlers */ + fp->fecpriv_interrupt_fec_rx_handler = + fec_interrupt_fec_rx_handler_fec0; + fp->fecpriv_interrupt_fec_tx_handler = + fec_interrupt_fec_tx_handler_fec0; + + /* tx descriptors */ + fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC0; + + /* rx descriptors */ + fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC0; + + /* mac addr + if (uboot_enet0[0] || uboot_enet0[1] || uboot_enet0[2] || + uboot_enet0[3] || uboot_enet0[4] || uboot_enet0[5]) { + use uboot enet 0 addr + memcpy(fec_mac_addr_fec0, uboot_enet0, 6); + }*/ + fec_mac_addr_fec0[0] = + (FEC_PALR(FEC_BASE_ADDR_FEC0) >> 24) & 0xFF; + fec_mac_addr_fec0[1] = + (FEC_PALR(FEC_BASE_ADDR_FEC0) >> 16) & 0xFF; + fec_mac_addr_fec0[2] = + (FEC_PALR(FEC_BASE_ADDR_FEC0) >> 8) & 0xFF; + fec_mac_addr_fec0[3] = + (FEC_PALR(FEC_BASE_ADDR_FEC0)) & 0xFF; + fec_mac_addr_fec0[4] = + (FEC_PAUR(FEC_BASE_ADDR_FEC0) >> 24) & 0xFF; + fec_mac_addr_fec0[5] = + (FEC_PAUR(FEC_BASE_ADDR_FEC0) >> 16) & 0xFF; + + fp->fecpriv_mac_addr = fec_mac_addr_fec0; + } else { + /* disable fec1 */ + FEC_ECR(FEC_BASE_ADDR_FEC1) = FEC_ECR_DISABLE; +#ifdef FEC_2 + /* setup the interrupt handler */ + dev->irq = 64 + ISC_FEC1; + + if (request_irq(dev->irq, fec_interrupt_handler, + IRQF_DISABLED, "ColdFire FEC 1", dev)) { + dev->irq = 0; + printk(KERN_ERR "Cannot allocate FEC1 IRQ\n"); + } else { + /* interrupt priority and level */ + MCF_ICR(ISC_FEC1) = ILP_FEC1; + } + + /* fec base address */ + dev->base_addr = FEC_BASE_ADDR_FEC1; + + /* requestor numbers */ + fp->fecpriv_rx_requestor = DMA_FEC1_RX; + fp->fecpriv_tx_requestor = DMA_FEC1_TX; + + /* fec1 handlers */ + fp->fecpriv_interrupt_fec_rx_handler = + fec_interrupt_fec_rx_handler_fec1; + fp->fecpriv_interrupt_fec_tx_handler = + fec_interrupt_fec_tx_handler_fec1; + + /* tx descriptors */ + fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC1; + + /* rx descriptors */ + fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC1; + + /* mac addr + if (uboot_enet1[0] || uboot_enet1[1] || uboot_enet1[2] || + uboot_enet1[3] || uboot_enet1[4] || uboot_enet1[5]) { + use uboot enet 1 addr + memcpy(fec_mac_addr_fec1, uboot_enet1, 6); + }*/ + fec_mac_addr_fec1[0] = + (FEC_PALR(FEC_BASE_ADDR_FEC1) >> 24) & 0xFF; + fec_mac_addr_fec1[1] = + (FEC_PALR(FEC_BASE_ADDR_FEC1) >> 16) & 0xFF; + fec_mac_addr_fec1[2] = + (FEC_PALR(FEC_BASE_ADDR_FEC1) >> 8) & 0xFF; + fec_mac_addr_fec1[3] = + (FEC_PALR(FEC_BASE_ADDR_FEC1)) & 0xFF; + fec_mac_addr_fec1[4] = + (FEC_PAUR(FEC_BASE_ADDR_FEC1) >> 24) & 0xFF; + fec_mac_addr_fec1[5] = + (FEC_PAUR(FEC_BASE_ADDR_FEC1) >> 16) & 0xFF; + + fp->fecpriv_mac_addr = fec_mac_addr_fec1; +#endif + } + + /* clear MIB */ + memset((void *) (dev->base_addr + 0x200), 0, FEC_MIB_LEN); + + /* clear the statistics structure */ + memset((void *) &(fp->fecpriv_stat), 0, + sizeof(struct net_device_stats)); + + /* grab the FEC initiators */ + dma_set_initiator(fp->fecpriv_tx_requestor); + fp->fecpriv_initiator_tx = dma_get_initiator(fp->fecpriv_tx_requestor); + dma_set_initiator(fp->fecpriv_rx_requestor); + fp->fecpriv_initiator_rx = dma_get_initiator(fp->fecpriv_rx_requestor); + + /* reset the DMA channels */ + fp->fecpriv_fec_rx_channel = -1; + fp->fecpriv_fec_tx_channel = -1; + + for (i = 0; i < FEC_RX_BUF_NUMBER; i++) + fp->askb_rx[i] = NULL; + + /* initialize the pointers to the socket buffers */ + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) + fp->fecpriv_txbuf[i] = NULL; + + ether_setup(dev); + + dev->netdev_ops = &fec_netdev_ops; + dev->watchdog_timeo = FEC_TX_TIMEOUT * HZ; + + memcpy(dev->dev_addr, fp->fecpriv_mac_addr, ETH_ALEN); + + spin_lock_init(&fp->fecpriv_lock); + + /* Initialize FEC/I2C/IRQ Pin Assignment Register*/ + FEC_GPIO_PAR_FECI2CIRQ &= 0xF; + FEC_GPIO_PAR_FECI2CIRQ |= FEC_FECI2CIRQ; + + return 0; +} + +/* + * Module Initialization + */ +int __init fec_init(void) +{ + struct net_device *dev; + int i; + int err; + struct fec_priv *fep; + DECLARE_MAC_BUF(mac); + + printk(KERN_INFO "FEC ENET (DMA) Version %s\n", VERSION); + + for (i = 0; i < FEC_MAX_PORTS; i++) { + dev = alloc_etherdev(sizeof(struct fec_priv)); + if (!dev) + return -ENOMEM; + err = fec_enet_init(dev, i); + if (err) { + free_netdev(dev); + continue; + } + + fep = netdev_priv(dev); + FEC_MSCR(dev->base_addr) = FEC_MII_SPEED; +#ifdef CONFIG_FEC_548x_SHARED_PHY + if (i == 0) + err = fec_mdio_register(dev, i); + else { + struct fec_priv *priv0 = netdev_priv(fec_dev[0]); + fep->mdio_bus = priv0->mdio_bus; + printk(KERN_INFO "FEC%d SHARED the %s ok\n", + i, fep->mdio_bus->name); + } +#else + err = fec_mdio_register(dev, i); +#endif + if (err) { + printk(KERN_ERR "%s: ethernet fec_mdio_register\n", + dev->name); + free_netdev(dev); + return -ENOMEM; + } + + if (register_netdev(dev) != 0) { + free_netdev(dev); + return -EIO; + } + + printk(KERN_INFO "%s: ethernet %s\n", + dev->name, print_mac(mac, dev->dev_addr)); + } + return 0; +} + +/* + * Stop a device + */ +void fec_stop(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + + dma_remove_initiator(fp->fecpriv_initiator_tx); + dma_remove_initiator(fp->fecpriv_initiator_rx); + + if (dev->irq) + free_irq(dev->irq, dev); +} + +/************************************************************************ +* NAME: fec_open +* +* DESCRIPTION: This function performs the initialization of +* of FEC and corresponding KS8721 transiver +* +* RETURNS: If no error occurs, this function returns zero. +*************************************************************************/ +int fec_open(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + int fduplex; + int i; + int channel; + int error_code = -EBUSY; + + fp->link = 0; + fp->duplex = 0; + fp->speed = 0; + coldfire_fec_init_phy(dev); + phy_start(fp->phydev); + + /* Receive the DMA channels */ + channel = dma_set_channel_fec(fp->fecpriv_rx_requestor); + + if (channel == -1) { + printk(KERN_ERR "Dma channel cannot be reserved\n"); + goto ERRORS; + } + + fp->fecpriv_fec_rx_channel = channel; + + dma_connect(channel, (int) fp->fecpriv_interrupt_fec_rx_handler); + + channel = dma_set_channel_fec(fp->fecpriv_tx_requestor); + + if (channel == -1) { + printk(KERN_ERR "Dma channel cannot be reserved\n"); + goto ERRORS; + } + + fp->fecpriv_fec_tx_channel = channel; + + dma_connect(channel, (int) fp->fecpriv_interrupt_fec_tx_handler); + + /* init tasklet for controller reinitialization */ + tasklet_init(&fp->fecpriv_tasklet_reinit, + fec_interrupt_fec_reinit, (unsigned long) dev); + + /* Reset FIFOs */ + FEC_FECFRST(base_addr) |= FEC_SW_RST | FEC_RST_CTL; + FEC_FECFRST(base_addr) &= ~FEC_SW_RST; + + /* Reset and disable FEC */ + FEC_ECR(base_addr) = FEC_ECR_RESET; + + udelay(10); + + /* Clear all events */ + FEC_EIR(base_addr) = FEC_EIR_CLEAR; + + /* Reset FIFO status */ + FEC_FECTFSR(base_addr) = FEC_FECTFSR_MSK; + FEC_FECRFSR(base_addr) = FEC_FECRFSR_MSK; + + /* Set the default address */ + FEC_PALR(base_addr) = (fp->fecpriv_mac_addr[0] << 24) | + (fp->fecpriv_mac_addr[1] << 16) | + (fp->fecpriv_mac_addr[2] << 8) | + fp->fecpriv_mac_addr[3]; + FEC_PAUR(base_addr) = (fp->fecpriv_mac_addr[4] << 24) | + (fp->fecpriv_mac_addr[5] << 16) | 0x8808; + + /* Reset the group address descriptor */ + FEC_GALR(base_addr) = 0x00000000; + FEC_GAUR(base_addr) = 0x00000000; + + /* Reset the individual address descriptor */ + FEC_IALR(base_addr) = 0x00000000; + FEC_IAUR(base_addr) = 0x00000000; + + /* Set the receive control register */ + FEC_RCR(base_addr) = FEC_RCR_MAX_FRM_SIZE | FEC_RCR_MII; + + /* Set the receive FIFO control register */ + /*FEC_FECRFCR(base_addr) = + * FEC_FECRFCR_FRM | FEC_FECRFCR_GR | FEC_FECRFCR_MSK;*/ + FEC_FECRFCR(base_addr) = FEC_FECRFCR_FRM | FEC_FECRFCR_GR + | (FEC_FECRFCR_MSK + /* disable all but ...*/ + & ~FEC_FECRFCR_FAE + /* enable frame accept error*/ + & ~FEC_FECRFCR_RXW + /* enable receive wait condition*/ + /*& ~FEC_FECRFCR_UF*/ + /* enable FIFO underflow*/ + ); + + /* Set the receive FIFO alarm register */ + FEC_FECRFAR(base_addr) = FEC_FECRFAR_ALARM; + + /* Set the transmit FIFO control register */ + /*FEC_FECTFCR(base_addr) = + FEC_FECTFCR_FRM | FEC_FECTFCR_GR | FEC_FECTFCR_MSK;*/ + FEC_FECTFCR(base_addr) = FEC_FECTFCR_FRM | FEC_FECTFCR_GR + | (FEC_FECTFCR_MSK + /* disable all but ... */ + & ~FEC_FECTFCR_FAE + /* enable frame accept error */ + /* & ~FEC_FECTFCR_TXW */ + /*enable transmit wait condition*/ + /*& ~FEC_FECTFCR_UF*/ + /*enable FIFO underflow*/ + & ~FEC_FECTFCR_OF); + /* enable FIFO overflow */ + + /* Set the transmit FIFO alarm register */ + FEC_FECTFAR(base_addr) = FEC_FECTFAR_ALARM; + + /* Set the Tx FIFO watermark */ + FEC_FECTFWR(base_addr) = FEC_FECTFWR_XWMRK; + + /* Enable the transmitter to append the CRC */ + FEC_CTCWR(base_addr) = FEC_CTCWR_TFCW_CRC; + + /* Enable the ethernet interrupts */ + /*FEC_EIMR(base_addr) = FEC_EIMR_MASK;*/ + FEC_EIMR(base_addr) = FEC_EIMR_DISABLE + | FEC_EIR_LC + | FEC_EIR_RL + | FEC_EIR_HBERR + | FEC_EIR_XFUN + | FEC_EIR_XFERR + | FEC_EIR_RFERR; + +#if 0 + error_code = init_transceiver(base_addr, &fduplex); + if (error_code != 0) { + printk(KERN_ERR "Initialization of the " + "transceiver is failed\n"); + goto ERRORS; + } +#else + fduplex = 1; +#endif + if (fduplex) + /* Enable the full duplex mode */ + FEC_TCR(base_addr) = FEC_TCR_FDEN | FEC_TCR_HBC; + else + /* Disable reception of frames while transmitting */ + FEC_RCR(base_addr) |= FEC_RCR_DRT; + + /* Enable MIB */ + FEC_MIBC(base_addr) = FEC_MIBC_ENABLE; + + /* Enable FEC */ + FEC_ECR(base_addr) |= FEC_ECR_ETHEREN; + FEC_MSCR(dev->base_addr) = FEC_MII_SPEED; + /* Initialize tx descriptors and start DMA for the transmission */ + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) + fp->fecpriv_txdesc[i].statCtrl = MCD_FEC_INTERRUPT; + + fp->fecpriv_txdesc[i - 1].statCtrl |= MCD_FEC_WRAP; + + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) fp->fecpriv_txdesc, 0, + (unsigned char *) &(FEC_FECTFDR(base_addr)), 0, + FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx, + FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT, + MCD_NO_CSUM | MCD_NO_BYTE_SWAP); + + /* Initialize rx descriptors and start DMA for the reception */ + for (i = 0; i < FEC_RX_BUF_NUMBER; i++) { + fp->askb_rx[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, GFP_DMA); + if (!fp->askb_rx[i]) { + fp->fecpriv_rxdesc[i].dataPointer = 0; + fp->fecpriv_rxdesc[i].statCtrl = 0; + fp->fecpriv_rxdesc[i].length = 0; + } else { + skb_reserve(fp->askb_rx[i], 16); + fp->askb_rx[i]->dev = dev; + fp->fecpriv_rxdesc[i].dataPointer = + (unsigned int)virt_to_phys(fp->askb_rx[i]->tail); + fp->fecpriv_rxdesc[i].statCtrl = + MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT; + fp->fecpriv_rxdesc[i].length = FEC_MAXBUF_SIZE; + } + } + + fp->fecpriv_rxdesc[i - 1].statCtrl |= MCD_FEC_WRAP; + fp->fecpriv_current_rx = 0; + + MCD_startDma(fp->fecpriv_fec_rx_channel, (char *) fp->fecpriv_rxdesc, 0, + (unsigned char *) &(FEC_FECRFDR(base_addr)), 0, + FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_rx, + FEC_RX_DMA_PRI, MCD_FECRX_DMA | MCD_INTERRUPT, + MCD_NO_CSUM | MCD_NO_BYTE_SWAP); + + netif_start_queue(dev); + return 0; + +ERRORS: + + /* Remove the channels and return with the error code */ + if (fp->fecpriv_fec_rx_channel != -1) { + dma_disconnect(fp->fecpriv_fec_rx_channel); + dma_remove_channel_by_number(fp->fecpriv_fec_rx_channel); + fp->fecpriv_fec_rx_channel = -1; + } + + if (fp->fecpriv_fec_tx_channel != -1) { + dma_disconnect(fp->fecpriv_fec_tx_channel); + dma_remove_channel_by_number(fp->fecpriv_fec_tx_channel); + fp->fecpriv_fec_tx_channel = -1; + } + + return error_code; +} + +/************************************************************************ +* NAME: fec_close +* +* DESCRIPTION: This function performs the graceful stop of the +* transmission and disables FEC +* +* RETURNS: This function always returns zero. +*************************************************************************/ +int fec_close(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + unsigned long time; + int i; + + netif_stop_queue(dev); + phy_disconnect(fp->phydev); + phy_stop(fp->phydev); + /* Perform the graceful stop */ + FEC_TCR(base_addr) |= FEC_TCR_GTS; + + time = jiffies; + + /* Wait for the graceful stop */ + while (!(FEC_EIR(base_addr) & FEC_EIR_GRA) && jiffies - time < + (FEC_GR_TIMEOUT * HZ)) + schedule(); + + /* Disable FEC */ + FEC_ECR(base_addr) = FEC_ECR_DISABLE; + + /* Reset the DMA channels */ + spin_lock_irq(&fp->fecpriv_lock); + MCD_killDma(fp->fecpriv_fec_tx_channel); + spin_unlock_irq(&fp->fecpriv_lock); + dma_remove_channel_by_number(fp->fecpriv_fec_tx_channel); + dma_disconnect(fp->fecpriv_fec_tx_channel); + fp->fecpriv_fec_tx_channel = -1; + + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) { + if (fp->fecpriv_txbuf[i]) { + kfree(fp->fecpriv_txbuf[i]); + fp->fecpriv_txbuf[i] = NULL; + } + } + + spin_lock_irq(&fp->fecpriv_lock); + MCD_killDma(fp->fecpriv_fec_rx_channel); + spin_unlock_irq(&fp->fecpriv_lock); + + dma_remove_channel_by_number(fp->fecpriv_fec_rx_channel); + dma_disconnect(fp->fecpriv_fec_rx_channel); + fp->fecpriv_fec_rx_channel = -1; + + for (i = 0; i < FEC_RX_BUF_NUMBER; i++) { + if (fp->askb_rx[i]) { + kfree_skb(fp->askb_rx[i]); + fp->askb_rx[i] = NULL; + } + } + + return 0; +} + +/************************************************************************ +* +NAME: fec_get_stat +* +* RETURNS: This function returns the statistical information. +*************************************************************************/ +struct net_device_stats *fec_get_stat(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = dev->base_addr; + + /* Receive the statistical information */ + fp->fecpriv_stat.rx_packets = FECSTAT_RMON_R_PACKETS(base_addr); + fp->fecpriv_stat.tx_packets = FECSTAT_RMON_T_PACKETS(base_addr); + fp->fecpriv_stat.rx_bytes = FECSTAT_RMON_R_OCTETS(base_addr); + fp->fecpriv_stat.tx_bytes = FECSTAT_RMON_T_OCTETS(base_addr); + + fp->fecpriv_stat.multicast = FECSTAT_RMON_R_MC_PKT(base_addr); + fp->fecpriv_stat.collisions = FECSTAT_RMON_T_COL(base_addr); + + fp->fecpriv_stat.rx_length_errors = + FECSTAT_RMON_R_UNDERSIZE(base_addr) + + FECSTAT_RMON_R_OVERSIZE(base_addr) + + FECSTAT_RMON_R_FRAG(base_addr) + + FECSTAT_RMON_R_JAB(base_addr); + fp->fecpriv_stat.rx_crc_errors = FECSTAT_IEEE_R_CRC(base_addr); + fp->fecpriv_stat.rx_frame_errors = FECSTAT_IEEE_R_ALIGN(base_addr); + fp->fecpriv_stat.rx_over_errors = FECSTAT_IEEE_R_MACERR(base_addr); + + fp->fecpriv_stat.tx_carrier_errors = FECSTAT_IEEE_T_CSERR(base_addr); + fp->fecpriv_stat.tx_fifo_errors = FECSTAT_IEEE_T_MACERR(base_addr); + fp->fecpriv_stat.tx_window_errors = FECSTAT_IEEE_T_LCOL(base_addr); + + /* I hope that one frame doesn't have more than one error */ + fp->fecpriv_stat.rx_errors = fp->fecpriv_stat.rx_length_errors + + fp->fecpriv_stat.rx_crc_errors + + fp->fecpriv_stat.rx_frame_errors + + fp->fecpriv_stat.rx_over_errors + + fp->fecpriv_stat.rx_dropped; + fp->fecpriv_stat.tx_errors = fp->fecpriv_stat.tx_carrier_errors + + fp->fecpriv_stat.tx_fifo_errors + + fp->fecpriv_stat.tx_window_errors + + fp->fecpriv_stat.tx_aborted_errors + + fp->fecpriv_stat.tx_heartbeat_errors + + fp->fecpriv_stat.tx_dropped; + + return &fp->fecpriv_stat; +} + +/************************************************************************ +* NAME: fec_set_multicast_list +* +* DESCRIPTION: This function sets the frame filtering parameters +*************************************************************************/ +void fec_set_multicast_list(struct net_device *dev) +{ + struct dev_mc_list *dmi; + unsigned int crc, data; + int i, j, k; + unsigned long base_addr = (unsigned long) dev->base_addr; + + if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI) { + /* Allow all incoming frames */ + FEC_GALR(base_addr) = 0xFFFFFFFF; + FEC_GAUR(base_addr) = 0xFFFFFFFF; + return; + } + + /* Reset the group address register */ + FEC_GALR(base_addr) = 0x00000000; + FEC_GAUR(base_addr) = 0x00000000; + + /* Process all addresses */ + for (i = 0, dmi = dev->mc_list; i < dev->mc_count; + i++, dmi = dmi->next) { + /* Processing must be only for the group addresses */ + if (!(dmi->dmi_addr[0] & 1)) + continue; + + /* Calculate crc value for the current address */ + crc = 0xFFFFFFFF; + for (j = 0; j < dmi->dmi_addrlen; j++) { + for (k = 0, data = dmi->dmi_addr[j]; + k < 8; k++, data >>= 1) { + if ((crc ^ data) & 1) + crc = (crc >> 1) ^ FEC_CRCPOL; + else + crc >>= 1; + } + } + + /* Add this value */ + crc >>= 26; + crc &= 0x3F; + if (crc > 31) + FEC_GAUR(base_addr) |= 0x1 << (crc - 32); + else + FEC_GALR(base_addr) |= 0x1 << crc; + } +} + +/************************************************************************ +* NAME: fec_set_mac_address +* +* DESCRIPTION: This function sets the MAC address +*************************************************************************/ +int fec_set_mac_address(struct net_device *dev, void *p) +{ + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + struct sockaddr *addr = p; + + if (netif_running(dev)) + return -EBUSY; + + /* Copy a new address to the device structure */ + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + /* Copy a new address to the private structure */ + memcpy(fp->fecpriv_mac_addr, addr->sa_data, 6); + + /* Set the address to the registers */ + FEC_PALR(base_addr) = (fp->fecpriv_mac_addr[0] << 24) | + (fp->fecpriv_mac_addr[1] << 16) | + (fp->fecpriv_mac_addr[2] << 8) | + fp->fecpriv_mac_addr[3]; + FEC_PAUR(base_addr) = (fp->fecpriv_mac_addr[4] << 24) | + (fp->fecpriv_mac_addr[5] << 16) | + 0x8808; + + return 0; +} + +/************************************************************************ +* NAME: fec_tx +* +* DESCRIPTION: This function starts transmission of the frame using DMA +* +* RETURNS: This function always returns zero. +*************************************************************************/ +int fec_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + void *data, *data_aligned; + int offset; + + data = kmalloc(skb->len + 15, GFP_DMA | GFP_ATOMIC); + + if (!data) { + fp->fecpriv_stat.tx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + offset = (((unsigned long)virt_to_phys(data) + 15) & 0xFFFFFFF0) - + (unsigned long)virt_to_phys(data); + data_aligned = (void *)((unsigned long)data + offset); + memcpy(data_aligned, skb->data, skb->len); + + /* flush data cache before initializing + * the descriptor and starting DMA */ + + spin_lock_irq(&fp->fecpriv_lock); + + /* Initialize the descriptor */ + fp->fecpriv_txbuf[fp->fecpriv_next_tx] = data; + fp->fecpriv_txdesc[fp->fecpriv_next_tx].dataPointer + = (unsigned int) virt_to_phys(data_aligned); + fp->fecpriv_txdesc[fp->fecpriv_next_tx].length = skb->len; + fp->fecpriv_txdesc[fp->fecpriv_next_tx].statCtrl + |= (MCD_FEC_END_FRAME | MCD_FEC_BUF_READY); + fp->fecpriv_next_tx = (fp->fecpriv_next_tx + 1) & FEC_TX_INDEX_MASK; + + if (fp->fecpriv_txbuf[fp->fecpriv_current_tx] + && fp->fecpriv_current_tx == fp->fecpriv_next_tx) + netif_stop_queue(dev); + + spin_unlock_irq(&fp->fecpriv_lock); + + /* Tell the DMA to continue the transmission */ + MCD_continDma(fp->fecpriv_fec_tx_channel); + + dev_kfree_skb(skb); + + dev->trans_start = jiffies; + + return 0; +} + +/************************************************************************ +* NAME: fec_tx_timeout +* +* DESCRIPTION: If the interrupt processing of received frames was lost +* and DMA stopped the reception, this function clears +* the transmission descriptors and starts DMA +* +*************************************************************************/ +void fec_tx_timeout(struct net_device *dev) +{ + int i; + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + + spin_lock_irq(&fp->fecpriv_lock); + MCD_killDma(fp->fecpriv_fec_tx_channel); + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) { + if (fp->fecpriv_txbuf[i]) { + kfree(fp->fecpriv_txbuf[i]); + fp->fecpriv_txbuf[i] = NULL; + } + fp->fecpriv_txdesc[i].statCtrl = MCD_FEC_INTERRUPT; + } + fp->fecpriv_txdesc[i - 1].statCtrl |= MCD_FEC_WRAP; + + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + /* Reset FIFOs */ + FEC_FECFRST(base_addr) |= FEC_SW_RST; + FEC_FECFRST(base_addr) &= ~FEC_SW_RST; + + /* Reset and disable FEC */ + /* FEC_ECR(base_addr) = FEC_ECR_RESET; */ + + /* Enable FEC */ + FEC_ECR(base_addr) |= FEC_ECR_ETHEREN; + + MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) fp->fecpriv_txdesc, 0, + (unsigned char *) &(FEC_FECTFDR(base_addr)), 0, + FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx, + FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT, + MCD_NO_CSUM | MCD_NO_BYTE_SWAP); + + spin_unlock_irq(&fp->fecpriv_lock); + + netif_wake_queue(dev); + +} + +/************************************************************************ +* NAME: fec_interrupt_tx_handler +* +* DESCRIPTION: This function is called when the data +* transmission from the buffer to the FEC is completed. +* +*************************************************************************/ +void fec_interrupt_fec_tx_handler(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + + /* Release the socket buffer */ + if (fp->fecpriv_txbuf[fp->fecpriv_current_tx]) { + kfree(fp->fecpriv_txbuf[fp->fecpriv_current_tx]); + fp->fecpriv_txbuf[fp->fecpriv_current_tx] = NULL; + } + fp->fecpriv_current_tx = + (fp->fecpriv_current_tx + 1) & FEC_TX_INDEX_MASK; + + if (MCD_dmaStatus(fp->fecpriv_fec_tx_channel) == MCD_DONE) { + for (; fp->fecpriv_current_tx != fp->fecpriv_next_tx; + fp->fecpriv_current_tx = + (fp->fecpriv_current_tx + 1) + & FEC_TX_INDEX_MASK) { + if (fp->fecpriv_txbuf[fp->fecpriv_current_tx]) { + kfree(fp->fecpriv_txbuf[ + fp->fecpriv_current_tx]); + fp->fecpriv_txbuf[fp->fecpriv_current_tx] + = NULL; + } + } + } + + if (netif_queue_stopped(dev)) + netif_wake_queue(dev); +} + +/************************************************************************ +* NAME: fec_interrupt_rx_handler +* +* DESCRIPTION: This function is called when the data +* reception from the FEC to the reception buffer is completed. +* +*************************************************************************/ +void fec_interrupt_fec_rx_handler(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct sk_buff *skb; + int i; + + fp->fecpriv_rxflag = 1; + /* Some buffers can be missed */ + if (!(fp->fecpriv_rxdesc[fp->fecpriv_current_rx].statCtrl + & MCD_FEC_END_FRAME)) { + /* Find a valid index */ + for (i = 0; ((i < FEC_RX_BUF_NUMBER) && + !(fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].statCtrl + & MCD_FEC_END_FRAME)); i++, + (fp->fecpriv_current_rx = + (fp->fecpriv_current_rx + 1) + & FEC_RX_INDEX_MASK)) + ; + + if (i == FEC_RX_BUF_NUMBER) { + /* There are no data to process */ + /* Tell the DMA to continue the reception */ + MCD_continDma(fp->fecpriv_fec_rx_channel); + + fp->fecpriv_rxflag = 0; + + return; + } + } + + for (; fp->fecpriv_rxdesc[fp->fecpriv_current_rx].statCtrl + & MCD_FEC_END_FRAME; + fp->fecpriv_current_rx = (fp->fecpriv_current_rx + 1) + & FEC_RX_INDEX_MASK) { + if ((fp->fecpriv_rxdesc[fp->fecpriv_current_rx].length + <= FEC_MAXBUF_SIZE) && + (fp->fecpriv_rxdesc[fp->fecpriv_current_rx].length + > 4)) { + /* --tym-- */ + skb = fp->askb_rx[fp->fecpriv_current_rx]; + if (!skb) + fp->fecpriv_stat.rx_dropped++; + else { + /* + * flush data cache before initializing + * the descriptor and starting DMA + */ + skb_put(skb, + (fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].length - 4)); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + } + fp->fecpriv_rxdesc[fp->fecpriv_current_rx].statCtrl &= + ~MCD_FEC_END_FRAME; + /* allocate new skbuff */ + fp->askb_rx[fp->fecpriv_current_rx] = + alloc_skb(FEC_MAXBUF_SIZE + 16, + /*GFP_ATOMIC |*/ GFP_DMA); + if (!fp->askb_rx[fp->fecpriv_current_rx]) { + fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].dataPointer + = 0; + fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].length = 0; + fp->fecpriv_stat.rx_dropped++; + } else { + skb_reserve( + fp->askb_rx[fp->fecpriv_current_rx], 16); + fp->askb_rx[fp->fecpriv_current_rx]->dev = dev; + + /* + * flush data cache before initializing + * the descriptor and starting DMA + */ + + fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].dataPointer = + (unsigned int) virt_to_phys( + fp->askb_rx[ + fp->fecpriv_current_rx]->tail); + fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].length = + FEC_MAXBUF_SIZE; + fp->fecpriv_rxdesc[ + fp->fecpriv_current_rx].statCtrl |= + MCD_FEC_BUF_READY; + + /* + * flush data cache before initializing + * the descriptor and starting DMA + */ + } + } + + } + + /* Tell the DMA to continue the reception */ + MCD_continDma(fp->fecpriv_fec_rx_channel); + + fp->fecpriv_rxflag = 0; +} + +/************************************************************************ +* NAME: fec_interrupt_handler +* +* DESCRIPTION: This function is called when some special errors occur +* +*************************************************************************/ +irqreturn_t fec_interrupt_handler(int irq, void *dev_id) +{ + + struct net_device *dev = (struct net_device *)dev_id; + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + unsigned long events; + + /* Read and clear the events */ + events = FEC_EIR(base_addr) & FEC_EIMR(base_addr); + + if (events & FEC_EIR_HBERR) { + fp->fecpriv_stat.tx_heartbeat_errors++; + FEC_EIR(base_addr) = FEC_EIR_HBERR; + } + + /* receive/transmit FIFO error */ + if (((events & FEC_EIR_RFERR) != 0) + || ((events & FEC_EIR_XFERR) != 0)) { + /* kill DMA receive channel */ + MCD_killDma(fp->fecpriv_fec_rx_channel); + + /* kill running transmission by DMA */ + MCD_killDma(fp->fecpriv_fec_tx_channel); + + /* Reset FIFOs */ + FEC_FECFRST(base_addr) |= FEC_SW_RST; + FEC_FECFRST(base_addr) &= ~FEC_SW_RST; + + /* reset receive FIFO status register */ + FEC_FECRFSR(base_addr) = FEC_FECRFSR_FAE | + FEC_FECRFSR_RXW | + FEC_FECRFSR_UF; + + /* reset transmit FIFO status register */ + FEC_FECTFSR(base_addr) = FEC_FECTFSR_FAE | + FEC_FECTFSR_TXW | + FEC_FECTFSR_UF | + FEC_FECTFSR_OF; + + /* reset RFERR and XFERR event */ + FEC_EIR(base_addr) = FEC_EIR_RFERR | FEC_EIR_XFERR; + + /* stop queue */ + netif_stop_queue(dev); + + /* execute reinitialization as tasklet */ + tasklet_schedule(&fp->fecpriv_tasklet_reinit); + + fp->fecpriv_stat.rx_dropped++; + } + + /* transmit FIFO underrun */ + if ((events & FEC_EIR_XFUN) != 0) { + /* reset XFUN event */ + FEC_EIR(base_addr) = FEC_EIR_XFUN; + fp->fecpriv_stat.tx_aborted_errors++; + } + + /* late collision */ + if ((events & FEC_EIR_LC) != 0) { + /* reset LC event */ + FEC_EIR(base_addr) = FEC_EIR_LC; + fp->fecpriv_stat.tx_aborted_errors++; + } + + /* collision retry limit */ + if ((events & FEC_EIR_RL) != 0) { + /* reset RL event */ + FEC_EIR(base_addr) = FEC_EIR_RL; + fp->fecpriv_stat.tx_aborted_errors++; + } + return 0; +} + +/************************************************************************ +* NAME: fec_interrupt_reinit +* +* DESCRIPTION: This function is called from interrupt handler +* when controller must be reinitialized. +* +*************************************************************************/ +void fec_interrupt_fec_reinit(unsigned long data) +{ + int i; + struct net_device *dev = (struct net_device *)data; + struct fec_priv *fp = netdev_priv(dev); + unsigned long base_addr = (unsigned long) dev->base_addr; + + /* Initialize reception descriptors and start DMA for the reception */ + for (i = 0; i < FEC_RX_BUF_NUMBER; i++) { + if (!fp->askb_rx[i]) { + fp->askb_rx[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, + GFP_ATOMIC | GFP_DMA); + if (!fp->askb_rx[i]) { + fp->fecpriv_rxdesc[i].dataPointer = 0; + fp->fecpriv_rxdesc[i].statCtrl = 0; + fp->fecpriv_rxdesc[i].length = 0; + continue; + } + fp->askb_rx[i]->dev = dev; + skb_reserve(fp->askb_rx[i], 16); + } + fp->fecpriv_rxdesc[i].dataPointer = + (unsigned int) virt_to_phys(fp->askb_rx[i]->tail); + fp->fecpriv_rxdesc[i].statCtrl = + MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT; + fp->fecpriv_rxdesc[i].length = FEC_MAXBUF_SIZE; + } + + fp->fecpriv_rxdesc[i - 1].statCtrl |= MCD_FEC_WRAP; + fp->fecpriv_current_rx = 0; + + /* restart frame transmission */ + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) { + if (fp->fecpriv_txbuf[i]) { + kfree(fp->fecpriv_txbuf[i]); + fp->fecpriv_txbuf[i] = NULL; + fp->fecpriv_stat.tx_dropped++; + } + fp->fecpriv_txdesc[i].statCtrl = MCD_FEC_INTERRUPT; + } + fp->fecpriv_txdesc[i - 1].statCtrl |= MCD_FEC_WRAP; + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + /* flush entire data cache before restarting the DMA */ + + /* restart DMA from beginning */ + MCD_startDma(fp->fecpriv_fec_rx_channel, + (char *) fp->fecpriv_rxdesc, 0, + (unsigned char *) &(FEC_FECRFDR(base_addr)), 0, + FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_rx, + FEC_RX_DMA_PRI, MCD_FECRX_DMA | MCD_INTERRUPT, + MCD_NO_CSUM | MCD_NO_BYTE_SWAP); + + MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) fp->fecpriv_txdesc, 0, + (unsigned char *) &(FEC_FECTFDR(base_addr)), 0, + FEC_MAX_FRM_SIZE, 0, fp->fecpriv_initiator_tx, + FEC_TX_DMA_PRI, MCD_FECTX_DMA | MCD_INTERRUPT, + MCD_NO_CSUM | MCD_NO_BYTE_SWAP); + + /* Enable FEC */ + FEC_ECR(base_addr) |= FEC_ECR_ETHEREN; + + netif_wake_queue(dev); +} + +/************************************************************************ +* NAME: fec_interrupt_tx_handler_fec0 +* +* DESCRIPTION: This is the DMA interrupt handler using for FEC0 +* transmission. +* +*************************************************************************/ +void fec_interrupt_fec_tx_handler_fec0(void) +{ + fec_interrupt_fec_tx_handler(fec_dev[0]); +} + +#ifdef FEC_2 +/************************************************************************ +* NAME: fec_interrupt_tx_handler_fec1 +* +* DESCRIPTION: This is the DMA interrupt handler using for the FEC1 +* transmission. +* +*************************************************************************/ +void fec_interrupt_fec_tx_handler_fec1(void) +{ + fec_interrupt_fec_tx_handler(fec_dev[1]); +} +#endif + +/************************************************************************ +* NAME: fec_interrupt_rx_handler_fec0 +* +* DESCRIPTION: This is the DMA interrupt handler using for the FEC0 +* reception. +* +*************************************************************************/ +void fec_interrupt_fec_rx_handler_fec0(void) +{ + fec_interrupt_fec_rx_handler(fec_dev[0]); +} + +#ifdef FEC_2 +/************************************************************************ +* NAME: fec_interrupt_rx_handler_fec1 +* +* DESCRIPTION: This is the DMA interrupt handler using for the FEC1 +* reception. +* +*************************************************************************/ +void fec_interrupt_fec_rx_handler_fec1(void) +{ + fec_interrupt_fec_rx_handler(fec_dev[1]); +} + +#endif + +#ifndef MODULE +/************************************************************************ +* NAME: fec_mac_setup0 +* +* DESCRIPTION: This function sets the MAC address of FEC0 from command line +* +*************************************************************************/ +int __init fec_mac_setup0(char *s) +{ + if (!s || !*s) + return 1; + + if (fec_str_to_mac(s, fec_mac_addr_fec0)) + printk(KERN_ERR "The MAC address of FEC0 " + "cannot be set from command line"); + return 1; +} + +#ifdef FEC_2 + +/************************************************************************ +* NAME: fec_mac_setup1 +* +* DESCRIPTION: This function sets the MAC address of FEC1 from command line +* +*************************************************************************/ +int __init fec_mac_setup1(char *s) +{ + if (!s || !*s) + return 1; + + if (fec_str_to_mac(s, fec_mac_addr_fec1)) + printk(KERN_ERR "The MAC address of FEC1 " + "cannot be set from command line\n"); + return 1; +} +#endif + +/************************************************************************ +* NAME: fec_str_to_mac +* +* DESCRIPTION: This function interprets the character string into MAC addr +* +*************************************************************************/ +int fec_str_to_mac(char *str_mac, unsigned char* addr) +{ + unsigned long val; + char c; + unsigned long octet[6], *octetptr = octet; + int i; + +again: + val = 0; + while ((c = *str_mac) != '\0') { + if ((c >= '0') && (c <= '9')) { + val = (val * 16) + (c - '0'); + str_mac++; + continue; + } else if (((c >= 'a') && (c <= 'f')) + || ((c >= 'A') && (c <= 'F'))) { + val = (val << 4) + + (c + 10 - + (((c >= 'a') && (c <= 'f')) ? 'a' : 'A')); + str_mac++; + continue; + } + break; + } + if (*str_mac == ':') { + *octetptr++ = val, str_mac++; + if (octetptr >= octet + 6) + return 1; + goto again; + } + + /* Check for trailing characters */ + if (*str_mac && !(*str_mac == ' ')) + return 1; + + *octetptr++ = val; + + if ((octetptr - octet) == 6) { + for (i = 0; i <= 6; i++) + addr[i] = octet[i]; + } else + return 1; + + return 0; +} +#endif diff --git a/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.h b/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.h new file mode 100644 index 0000000000..61cb3f2fa8 --- /dev/null +++ b/target/linux/coldfire/files-2.6.31/drivers/net/fec_m547x.h @@ -0,0 +1,242 @@ + +#define FEC_BASE_ADDR_FEC0 ((unsigned int)MCF_MBAR + 0x9000) +#define FEC_BASE_ADDR_FEC1 ((unsigned int)MCF_MBAR + 0x9800) + +/* +#define FEC_INTC_IMRH_INT_MASK38 (0x00000040) +#define FEC_INTC_IMRH_INT_MASK39 (0x00000080) +#define FEC_INTC_ICR_FEC0 (0x30) +#define FEC_INTC_ICR_FEC1 (0x31) +*/ +#define FEC_FECI2CIRQ (0xFFC0) +#define FEC_GPIO_PAR_FECI2CIRQ \ + (*(volatile unsigned short *)((unsigned int)MCF_MBAR + 0xA44)) +/* +#define FEC_INTC_ICRn(x) \ +(*(volatile unsigned char *)(void*) +((unsigned int) MCF_MBAR + 0x000740+((x)*0x001))) +#define FEC_INTC_IMRH \ + *(volatile unsigned int*)((unsigned int)MCF_MBAR + 0x000708) +*/ +#define FEC_ECR_DISABLE (0x00000000) + +#define FEC_ECR(x) \ + (*(volatile unsigned int *)(x + 0x024)) +#define FEC_EIR(x) \ + (*(volatile unsigned int *)(x + 0x004)) +#define FEC_PALR(x) \ + (*(volatile unsigned int *)(x + 0x0E4)) +#define FEC_PAUR(x) \ + (*(volatile unsigned int *)(x + 0x0E8)) +#define FEC_IALR(x) \ + (*(volatile unsigned int *)(x + 0x11C)) +#define FEC_IAUR(x) \ + (*(volatile unsigned int *)(x + 0x118)) +#define FEC_GALR(x) \ + (*(volatile unsigned int *)(x + 0x124)) +#define FEC_GAUR(x) \ + (*(volatile unsigned int *)(x + 0x120)) +#define FEC_RCR(x) \ + (*(volatile unsigned int *)(x + 0x084)) +#define FEC_FECRFCR(x) \ + (*(volatile unsigned int *)(x + 0x18C)) +#define FEC_FECRFAR(x) \ + (*(volatile unsigned int *)(x + 0x198)) +#define FEC_FECTFCR(x) \ + (*(volatile unsigned int *)(x + 0x1AC)) +#define FEC_FECTFAR(x) \ + (*(volatile unsigned int *)(x + 0x1B8)) +#define FEC_FECTFWR(x) \ + (*(volatile unsigned int *)(x + 0x144)) +#define FEC_CTCWR(x) \ + (*(volatile unsigned int *)(x + 0x1C8)) +#define FEC_EIMR(x) \ + (*(volatile unsigned int *)(x + 0x008)) +#define FEC_TCR(x) \ + (*(volatile unsigned int *)(x + 0x0C4)) +#define FEC_MIBC(x) \ + (*(volatile unsigned int *)(x + 0x064)) +#define FEC_MSCR(x) \ + (*(volatile unsigned int *)(x + 0x044)) +#define FEC_FECTFDR(x) \ + (*(volatile unsigned int *)(x + 0x1A4)) +#define FEC_FECRFDR(x) \ + (*(volatile unsigned int *)(x + 0x184)) +#define FEC_FECTFSR(x) \ + (*(volatile unsigned int *)(x + 0x1A8)) +#define FEC_FECRFSR(x) \ + (*(volatile unsigned int *)(x + 0x188)) +#define FECSTAT_RMON_R_PACKETS(x) \ + (*(volatile unsigned int *)(x + 0x284)) +#define FECSTAT_RMON_T_PACKETS(x) \ + (*(volatile unsigned int *)(x + 0x204)) +#define FECSTAT_RMON_R_OCTETS(x) \ + (*(volatile unsigned int *)(x + 0x2C4)) +#define FECSTAT_RMON_T_OCTETS(x) \ + (*(volatile unsigned int *)(x + 0x244)) +#define FECSTAT_RMON_R_UNDERSIZE(x) \ + (*(volatile unsigned int *)(x + 0x294)) +#define FECSTAT_RMON_R_OVERSIZE(x) \ + (*(volatile unsigned int *)(x + 0x298)) +#define FECSTAT_RMON_R_FRAG(x) \ + (*(volatile unsigned int *)(x + 0x29C)) +#define FECSTAT_RMON_R_JAB(x) \ + (*(volatile unsigned int *)(x + 0x2A0)) +#define FECSTAT_RMON_R_MC_PKT(x) \ + (*(volatile unsigned int *)(x + 0x28C)) +#define FECSTAT_RMON_T_COL(x) \ + (*(volatile unsigned int *)(x + 0x224)) +#define FECSTAT_IEEE_R_ALIGN(x) \ + (*(volatile unsigned int *)(x + 0x2D4)) +#define FECSTAT_IEEE_R_CRC(x) \ + (*(volatile unsigned int *)(x + 0x2D0)) +#define FECSTAT_IEEE_R_MACERR(x) \ + (*(volatile unsigned int *)(x + 0x2D8)) +#define FECSTAT_IEEE_T_CSERR(x) \ + (*(volatile unsigned int *)(x + 0x268)) +#define FECSTAT_IEEE_T_MACERR(x) \ + (*(volatile unsigned int *)(x + 0x264)) +#define FECSTAT_IEEE_T_LCOL(x) \ + (*(volatile unsigned int *)(x + 0x25C)) +#define FECSTAT_IEEE_R_OCTETS_OK(x) \ + (*(volatile unsigned int *)(x + 0x2E0)) +#define FECSTAT_IEEE_T_OCTETS_OK(x) \ + (*(volatile unsigned int *)(x + 0x274)) +#define FECSTAT_IEEE_R_DROP(x) \ + (*(volatile unsigned int *)(x + 0x2C8)) +#define FECSTAT_IEEE_T_DROP(x) \ + (*(volatile unsigned int *)(x + 0x248)) +#define FECSTAT_IEEE_R_FRAME_OK(x) \ + (*(volatile unsigned int *)(x + 0x2CC)) +#define FECSTAT_IEEE_T_FRAME_OK(x) \ + (*(volatile unsigned int *)(x + 0x24C)) +#define FEC_MMFR(x) \ + (*(volatile unsigned int *)(x + 0x040)) +#define FEC_FECFRST(x) \ + (*(volatile unsigned int *)(x + 0x1C4)) + +#define FEC_MAX_FRM_SIZE (1518) +#define FEC_MAXBUF_SIZE (1520) + +/* Register values */ +#define FEC_ECR_RESET (0x00000001) +#define FEC_EIR_CLEAR (0xFFFFFFFF) +#define FEC_EIR_RL (0x00100000) +#define FEC_EIR_HBERR (0x80000000) +#define FEC_EIR_BABR (0x40000000) +/* babbling receive error */ +#define FEC_EIR_BABT (0x20000000) +/* babbling transmit error */ +#define FEC_EIR_TXF (0x08000000) +/* transmit frame interrupt */ +#define FEC_EIR_MII (0x00800000) +/* MII interrupt */ +#define FEC_EIR_LC (0x00200000) +/* late collision */ +#define FEC_EIR_XFUN (0x00080000) +/* transmit FIFO underrun */ +#define FEC_EIR_XFERR (0x00040000) +/* transmit FIFO error */ +#define FEC_EIR_RFERR (0x00020000) +/* receive FIFO error */ +#define FEC_RCR_MAX_FRM_SIZE (FEC_MAX_FRM_SIZE << 16) +#define FEC_RCR_MII (0x00000004) +#define FEC_FECRFCR_FAE (0x00400000) +/* frame accept error */ +#define FEC_FECRFCR_RXW (0x00200000) +/* receive wait condition */ +#define FEC_FECRFCR_UF (0x00100000) +/* receive FIFO underflow */ +#define FEC_FECRFCR_FRM (0x08000000) +#define FEC_FECRFCR_GR (0x7 << 24) + +#define FEC_EIMR_DISABLE (0x00000000) + +#define FEC_FECRFAR_ALARM (0x300) +#define FEC_FECTFCR_FRM (0x08000000) +#define FEC_FECTFCR_GR (0x7 << 24) +#define FEC_FECTFCR_FAE (0x00400000) +/* frame accept error */ +#define FEC_FECTFCR_TXW (0x00040000) +/* transmit wait condition */ +#define FEC_FECTFCR_UF (0x00100000) +/* transmit FIFO underflow */ +#define FEC_FECTFCR_OF (0x00080000) +/* transmit FIFO overflow */ + +#define FEC_FECTFAR_ALARM (0x100) +#define FEC_FECTFWR_XWMRK (0x00000000) + +#define FEC_FECTFSR_MSK (0xC0B00000) +#define FEC_FECTFSR_TXW (0x40000000) +/* transmit wait condition */ +#define FEC_FECTFSR_FAE (0x00800000) +/* frame accept error */ +#define FEC_FECTFSR_UF (0x00200000) +/* transmit FIFO underflow */ +#define FEC_FECTFSR_OF (0x00100000) +/* transmit FIFO overflow */ + +#define FEC_FECRFSR_MSK (0x80F00000) +#define FEC_FECRFSR_FAE (0x00800000) +/* frame accept error */ +#define FEC_FECRFSR_RXW (0x00400000) +/* receive wait condition */ +#define FEC_FECRFSR_UF (0x00200000) +/* receive FIFO underflow */ + +#define FEC_CTCWR_TFCW_CRC (0x03000000) +#define FEC_TCR_FDEN (0x00000004) +#define FEC_TCR_HBC (0x00000002) +#define FEC_RCR_DRT (0x00000002) +#define FEC_EIMR_MASK (FEC_EIR_RL | FEC_EIR_HBERR) +#define FEC_ECR_ETHEREN (0x00000002) +#define FEC_FECTFCR_MSK (0x00FC0000) +#define FEC_FECRFCR_MSK (0x00F80000) +#define FEC_EIR_GRA (0x10000000) +#define FEC_TCR_GTS (0x00000001) +#define FEC_MIBC_ENABLE (0x00000000) +#define FEC_MIB_LEN (228) +#define FEC_PHY_ADDR (0x01) + +#define FEC_RX_DMA_PRI (6) +#define FEC_TX_DMA_PRI (6) + +#define FEC_TX_BUF_NUMBER (8) +#define FEC_RX_BUF_NUMBER (64) + +#define FEC_TX_INDEX_MASK (0x7) +#define FEC_RX_INDEX_MASK (0x3f) + +#define FEC_RX_DESC_FEC0 SYS_SRAM_FEC_START +#define FEC_TX_DESC_FEC0 \ + (FEC_RX_DESC_FEC0 + FEC_RX_BUF_NUMBER * sizeof(MCD_bufDescFec)) + +#define FEC_RX_DESC_FEC1 \ + (SYS_SRAM_FEC_START + SYS_SRAM_FEC_SIZE/2) +#define FEC_TX_DESC_FEC1 \ + (FEC_RX_DESC_FEC1 + FEC_RX_BUF_NUMBER * sizeof(MCD_bufDescFec)) + +#define FEC_EIR_MII (0x00800000) +#define FEC_MMFR_READ (0x60020000) +#define FEC_MMFR_WRITE (0x50020000) + +#define FEC_FLAGS_RX (0x00000001) + +#define FEC_CRCPOL (0xEDB88320) + +#define FEC_MII_TIMEOUT (2) +#define FEC_GR_TIMEOUT (1) +#define FEC_TX_TIMEOUT (1) +#define FEC_RX_TIMEOUT (1) + +#define FEC_SW_RST 0x2000000 +#define FEC_RST_CTL 0x1000000 + +int fec_read_mii(unsigned int base_addr, unsigned int pa, unsigned int ra, + unsigned int *data); +int fec_write_mii(unsigned int base_addr, unsigned int pa, unsigned int ra, + unsigned int data); + +#define FEC_MII_SPEED \ + ((MCF_CLK / 2) / ((2500000 / 2) * 2)) diff --git a/target/linux/coldfire/files-2.6.31/drivers/net/phy/broadcom522x.c b/target/linux/coldfire/files-2.6.31/drivers/net/phy/broadcom522x.c new file mode 100644 index 0000000000..f20a519e5d --- /dev/null +++ b/target/linux/coldfire/files-2.6.31/drivers/net/phy/broadcom522x.c @@ -0,0 +1,269 @@ +/* + *Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved. + * Chenghu Wu <b16972@freescale.com> + * + * Driver for broadcom PHYs 522x + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/mii.h> +#include <linux/ethtool.h> +#include <linux/phy.h> +#include <linux/netdevice.h> + +/* DP83865 phy identifier values */ +#define BCM5222_PHY_ID 0x00406320 + +/* PHY Register */ +#define BCM5222_TIMEOUT 0x100 + +/* MII Registers */ +#define BCM5222_CTRL 0x00 +#define BCM5222_STATUS 0x01 +#define BCM5222_ID_HIGH 0x02 +#define BCM5222_ID_LOW 0x03 +#define BCM5222_AN_ADV 0x04 +#define BCM5222_AN_LP 0x05 +#define BCM5222_AN_EXP 0x06 +#define BCM5222_AN_NEXTPG 0x07 +#define BCM5222_AN_LP_NPTX 0x08 +#define BCM5222_AUX_CS 0x18 +#define BCM5222_AUX_STATUS 0x19 + +/* CONTROL Bits */ +#define BCM5222_CTRL_RESET 0x8000 +#define BCM5222_CTRL_LOOPBACK 0x4000 +#define BCM5222_CTRL_FORCE 0x2000 +#define BCM5222_CTRL_AUTOEN 0x1000 +#define BCM5222_CTRL_PWRDN 0x0800 +#define BCM5222_CTRL_ISOLATE 0x0400 +#define BCM5222_CTRL_RESTART 0x0200 +#define BCM5222_CTRL_DUPLEX 0x0100 +#define BCM5222_CTRL_COLLEN 0x0080 + +/* STATUS Bits */ +#define BCM5222_STATUS_100T4 0x8000 +#define BCM5222_STATUS_100TXFDX 0x4000 +#define BCM5222_STATUS_100TX 0x2000 +#define BCM5222_STATUS_10FDX 0x1000 +#define BCM5222_STATUS_10 0x0800 +#define BCM5222_STATUS_MF_PREAMBLE 0x0040 +#define BCM5222_STATUS_AN_COMPLETE 0x0020 +#define BCM5222_STATUS_REMOTE_FAULT 0x0010 +#define BCM5222_STATUS_AN_CAPABLE 0x0008 +#define BCM5222_STATUS_LINK 0x0004 +#define BCM5222_STATUS_JABBER 0x0002 +#define BCM5222_STATUS_EXT_CAP 0x0001 + +/* ID Values */ +#define BCM5222_ID_HIGH_VAL 0x0040 +#define BCM5222_ID_LOW_VAL 0x6320 + +/* Advertise Bits */ +#define BCM5222_AN_ADV_NEXTPG 0x8000 +#define BCM5222_AN_ADV_REMOTE_FAULT 0x2000 +#define BCM5222_AN_ADV_PAUSE 0x0400 +#define BCM5222_AN_ADV_100T4 0x0200 +#define BCM5222_AN_ADV_100TXFDX 0x0100 +#define BCM5222_AN_ADV_100TX 0x0080 +#define BCM5222_AN_ADV_10FDX 0x0040 +#define BCM5222_AN_ADV_10 0x0020 +#define BCM5222_AN_ADV_8023 0x0001 +#define BCM5222_AN_ADV_ALL \ + (BCM5222_AN_ADV_100TXFDX | \ + BCM5222_AN_ADV_100TXFDX | \ + BCM5222_AN_ADV_100TX | \ + BCM5222_AN_ADV_10FDX | \ + BCM5222_AN_ADV_10 | \ + BCM5222_AN_ADV_8023) + +/* AUX CTRL/STATUS Bits */ +#define BCM5222_AUX_CS_JABBER_DIS 0x8000 +#define BCM5222_AUX_CS_FORCE_LINK 0x4000 +#define BCM5222_AUX_CS_10M_TX_PWR 0x0100 +#define BCM5222_AUX_CS_HSQ_LSQ_MASK 0x00c0 +#define BCM5222_AUX_CS_EDGE_RATE_MASK 0x0030 +#define BCM5222_AUX_CS_AN_IND 0x0008 +#define BCM5222_AUX_CS_SPEED_FORCE 0x0004 +#define BCM5222_AUX_CS_SPEED 0x0002 +#define BCM5222_AUX_CS_DUPLEX 0x0001 + +/* AUX STATUS Bits */ +#define BCM5222_AUX_STATUS_AN_COMP 0x8000 +#define BCM5222_AUX_STATUS_AN_COMPACK 0x4000 +#define BCM5222_AUX_STATUS_AN_ACKDET 0x2000 +#define BCM5222_AUX_STATUS_AN_ABDET 0x1000 +#define BCM5222_AUX_STATUS_AN_PAUSE 0x0800 +#define BCM5222_AUX_STATUS_AN_HCDMASK 0x0700 +#define BCM5222_AUX_STATUS_AN_PDFAULT 0x0080 +#define BCM5222_AUX_STATUS_LP_RMTFAULT 0x0040 +#define BCM5222_AUX_STATUS_LP_PGRX 0x0020 +#define BCM5222_AUX_STATUS_LP_NEGABLE 0x0010 +#define BCM5222_AUX_STATUS_SPEED 0x0008 +#define BCM5222_AUX_STATUS_LINK 0x0004 +#define BCM5222_AUX_STATUS_AN_EN 0x0002 +#define BCM5222_AUX_STATUS_JABBER 0x0001 + +static int bcm5222_config_intr(struct phy_device *phydev) +{ + int err = 0; + printk(KERN_INFO "%s PHY_INTERRUPT %x\n", + __func__, phydev->interrupts); + + return err; +} + +static int bcm5222_ack_interrupt(struct phy_device *phydev) +{ + return 0; +} + +static int bcm5222_config_init(struct phy_device *phydev) +{ + return bcm5222_ack_interrupt(phydev); +} + +static int bcm5222_config_init_old(struct phy_device *phydev) +{ + int timeout; + int flag = 1; + int ret = phy_read(phydev, BCM5222_AUX_STATUS); + if (ret < 0) { + printk(KERN_INFO "%s MII_BCM5222_ISR %x\n", + __func__, ret); + } + /* + * reset + */ + phy_write(phydev, BCM5222_CTRL, BCM5222_CTRL_RESET); + + /* check that it cleared */ + ret = phy_read(phydev, BCM5222_CTRL); + printk(KERN_INFO "%s BCM5222_CTRL %x\n", + __func__, ret); + /*if reset bit is set, return */ + if (ret & BCM5222_CTRL_RESET) { + printk(KERN_ERR "%s %x = BCM5222_CTRL_RESET(%x)\n", + __func__, ret, BCM5222_CTRL_RESET); + return -ETIME; + } + + /* + * setup auto-negotiation + */ + + /* disable */ + phy_write(phydev, BCM5222_CTRL, 0); + ret = phy_read(phydev, BCM5222_CTRL); + printk(KERN_INFO "%s BCM5222_CTRL %x\n", + __func__, ret); + /* set the auto-negotiation advertisement register */ + phy_write(phydev, BCM5222_AN_ADV, BCM5222_AN_ADV_ALL); + ret = phy_read(phydev, BCM5222_AN_ADV); + printk(KERN_INFO "%s BCM5222_AN_ADV %x, BCM5222_AN_ADV_ALL %x\n", + __func__, ret, BCM5222_AN_ADV_ALL); + /* enable */ + phy_write(phydev, BCM5222_CTRL, BCM5222_CTRL_AUTOEN); + ret = phy_read(phydev, BCM5222_CTRL); + printk(KERN_INFO "%s BCM5222_CTRL %x\n", + __func__, ret); + printk(KERN_INFO "** wait for complete\n"); + + /* read aux status reg */ + ret = phy_read(phydev, BCM5222_AUX_STATUS); + /* Wait for the auto-negotiation completion */ + timeout = BCM5222_TIMEOUT; + while (!(ret & BCM5222_AUX_STATUS_AN_COMP)) { + if (!timeout--) { + flag = 0; + printk(KERN_INFO "BCM5222: TIMEOUT\n"); + break; + } + + mdelay(10); + /* Read PHY status register */ + ret = phy_read(phydev, BCM5222_AUX_STATUS); + } + + ret = phy_read(phydev, BCM5222_AUX_STATUS); + ret = phy_read(phydev, BCM5222_AN_ADV); + return 0; +} + +static int bcm5222_read_status(struct phy_device *phydev) +{ + int ret; + ret = phy_read(phydev, BCM5222_AUX_STATUS); + printk(KERN_INFO "%s ret %x\n", __func__, ret); + + if (ret & BCM5222_AUX_STATUS_LINK) + phydev->link = 1; + else + phydev->link = 0; + + if (ret & BCM5222_AUX_STATUS_SPEED) + phydev->speed = SPEED_100; + else + phydev->speed = SPEED_10; + + ret = phy_read(phydev, BCM5222_AUX_CS); + printk(KERN_INFO "%s ret %x\n", __func__, ret); + if (ret & BCM5222_AUX_CS_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + return 0; +} + +static int bcm5222_config_aneg(struct phy_device *phydev) +{ + phy_read(phydev, BCM5222_AUX_STATUS); + phy_read(phydev, BCM5222_AN_ADV); + return 0; +} + +static struct phy_driver bcm5222_driver = { + .phy_id = BCM5222_PHY_ID, + .phy_id_mask = 0xfffffff0, + .name = "Broadcom BCM5222", + .features = PHY_BASIC_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = bcm5222_config_init, + .config_aneg = genphy_config_aneg, + .read_status = genphy_read_status, + .ack_interrupt = bcm5222_ack_interrupt, + .config_intr = bcm5222_config_intr, + .driver = {.owner = THIS_MODULE,} +}; + +static int __init bcm5222_init(void) +{ + int ret; + + ret = phy_driver_register(&bcm5222_driver); + if (ret) + goto err1; + + return 0; +err1: + printk(KERN_INFO "register bcm5222 PHY driver fail\n"); + return ret; +} + +static void __exit bcm5222_exit(void) +{ + phy_driver_unregister(&bcm5222_driver); +} + +MODULE_DESCRIPTION("Broadcom PHY driver"); +MODULE_LICENSE("GPL v2"); + +module_init(bcm5222_init); +module_exit(bcm5222_exit); |