This is a fully functionnal ethernet driver for the MCF547x and MCF548x processors, tested on the M5484EVB board and on a custom board inpired by the M5484EVB board. It implements a FEC+DMA driver and a mdio driver.
Original work was made by freescale for 2.6.25, but never submitted for mainline, but cache support, mdio driver, phylib support, general cleanup and 2.6.30-3.6 ports are mine. Signed-off-by: Philippe De Muyter <p...@macqel.be> --- arch/m68k/include/asm/m54xxsim.h | 2 + arch/m68k/kernel/setup_no.c | 5 + drivers/net/ethernet/freescale/Kconfig | 26 +- drivers/net/ethernet/freescale/Makefile | 1 + drivers/net/ethernet/freescale/fec_m54xx.c | 1506 ++++++++++++++++++++++++++++ drivers/net/ethernet/freescale/fec_m54xx.h | 144 +++ 6 files changed, 1683 insertions(+), 1 deletions(-) create mode 100644 drivers/net/ethernet/freescale/fec_m54xx.c create mode 100644 drivers/net/ethernet/freescale/fec_m54xx.h diff --git a/arch/m68k/include/asm/m54xxsim.h b/arch/m68k/include/asm/m54xxsim.h index f5531d5..b4c81bf 100644 --- a/arch/m68k/include/asm/m54xxsim.h +++ b/arch/m68k/include/asm/m54xxsim.h @@ -46,6 +46,8 @@ #define MCF_IRQ_UART2 (MCFINT_VECBASE + 33) #define MCF_IRQ_UART3 (MCFINT_VECBASE + 32) #define MCF_IRQ_DMA (MCFINT_VECBASE + 48) /* DMA */ +#define MCF_IRQ_FEC0 (MCFINT_VECBASE + 39) /* FEC0 */ +#define MCF_IRQ_FEC1 (MCFINT_VECBASE + 38) /* FEC1 */ /* * Generic GPIO support diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c index 71fb299..10131b4 100644 --- a/arch/m68k/kernel/setup_no.c +++ b/arch/m68k/kernel/setup_no.c @@ -261,6 +261,11 @@ void __init setup_arch(char **cmdline_p) paging_init(); } +const char *machdep_get_mac_address(int i) +{ + return 0; +} + /* * Get CPU information for use by the procfs. */ diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 3574e14..64d8fc6 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig @@ -7,7 +7,7 @@ config NET_VENDOR_FREESCALE default y depends on FSL_SOC || QUICC_ENGINE || CPM1 || CPM2 || PPC_MPC512x || \ M523x || M527x || M5272 || M528x || M520x || M532x || \ - ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) + M54xx || ARCH_MXC || ARCH_MXS || (PPC_MPC52xx && PPC_BESTCOMM) ---help--- If you have a network (Ethernet) card belonging to this class, say Y and read the Ethernet-HOWTO, available from @@ -30,6 +30,30 @@ config FEC Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX processors. +config FEC_54xx + tristate "MCF547x/MCF548x Fast Ethernet Controller support" + depends on M54xx + default y + select CRC32 + select PHYLIB + select M54xx_DMA + help + The MCF547x and MCF548x have a built-in Fast Ethernet Controller. + This is not the same FEC controller as on other ColdFire as here + the DMA controller is not reserved to the FEC driver, but made + available for general DMA work. + Saying Y here will include support for this device in the kernel. + +config FEC2 + bool "Second FEC ethernet controller (on some ColdFire CPUs)" + depends on FEC || FEC_54xx + default y + help + Say Y here if you want to use the second built-in 10/100 Fast + ethernet controller on some Motorola ColdFire processors. On M54xx, + If your second ethernet port is not connected, saying N here will + free 2 DMA channels and allow you to use FEC io ports as GPIO's. + config FEC_MPC52xx tristate "FEC MPC52xx driver" depends on PPC_MPC52xx && PPC_BESTCOMM diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 1752488..05a5022 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile @@ -3,6 +3,7 @@ # obj-$(CONFIG_FEC) += fec.o +obj-$(CONFIG_FEC_54xx) += fec_m54xx.o obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx.o ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) obj-$(CONFIG_FEC_MPC52xx) += fec_mpc52xx_phy.o diff --git a/drivers/net/ethernet/freescale/fec_m54xx.c b/drivers/net/ethernet/freescale/fec_m54xx.c new file mode 100644 index 0000000..b28c89a --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_m54xx.c @@ -0,0 +1,1506 @@ +/* + * Performance and stability improvements: (C) Copyright 2008, + * Daniel Krueger, SYSTEC electronic GmbH + * + * Code crunched to get it to work on 2.6.24 -- FEC cleanup coming + * soon -- Kurt Mahan + * + * 2.6.30 and above port, cleanup, cache support, netdev_ops, mdio, + * phy & ethtool support, + * (C) Copyright 2010-2012 Philippe De Muyter <p...@macqel.be> Macq SA + */ +#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/crc32.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/m54xxdma_api.h> +#include <asm/m54xxsram.h> +#include <asm/irq.h> +#include <asm/cacheflush.h> +/* + * txd's and rxd's are in System SRAM which is uncached + * real buffers are in sdram which is cached + */ +#define flush_and_invalidate_dcache() flush_cache_all() + +#include "fec_m54xx.h" +#include <linux/phy.h> + +#ifdef CONFIG_FEC2 +#define FEC_MAX_PORTS 2 +#else +#define FEC_MAX_PORTS 1 +#endif + +#define FEC_MII_TIMEOUT 1000 /* us */ +#define FEC_TX_TIMEOUT (1 * HZ) + +#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 MAC_ADDR_LEN 6 + +#define VERSION "0.30" +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 */ + struct MCD_bufDescFec *fecpriv_txdesc; /* tx descriptor ptrs */ + /* fecpriv_current_tx changed only by fec_interrupt_fec_tx_handler */ + unsigned int fecpriv_current_tx; /* current tx desc index */ + /* fecpriv_next_tx changed only by fec_start_tx */ + unsigned int fecpriv_next_tx; /* next tx desc index */ + unsigned int fecpriv_current_rx; /* current rx desc index */ + struct 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 */ + 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 */ +#if 0 + int in_poll; +#endif + + int duplex; + int speed; + + /* phy link details */ + int phy_addr; + struct phy_device *phydev; + enum phy_state link; + + /* MDIO bus details */ + unsigned int phy_speed; + struct completion mdio_done; +}; + +/* default fec0 address */ +unsigned char fec0_addr[MAC_ADDR_LEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x50 }; + +#ifdef CONFIG_FEC2 +/* default fec1 address */ +unsigned char fec1_addr[MAC_ADDR_LEN] = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x51 }; +#endif + +extern const char *machdep_get_mac_address(int i); + +/* based on generic_adjust_link from fs_enet-main.c */ +static void fec_adjust_link(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct phy_device *phydev = fp->phydev; + int new_state = 0; + + if (phydev->link != PHY_DOWN) { + if (phydev->duplex != fp->duplex) { + unsigned long base_addr = (unsigned long) dev->base_addr; + u32 rcntrl; + u32 tcntrl; + + new_state = 1; + fp->duplex = phydev->duplex; + + rcntrl = readl(base_addr + FEC_RCR); + tcntrl = readl(base_addr + FEC_TCR); + + rcntrl &= ~FEC_RCR_DRT; + tcntrl &= ~FEC_TCR_FDEN; + if (phydev->duplex == DUPLEX_FULL) + /* Enable the full duplex mode */ + tcntrl |= FEC_TCR_FDEN; + else + /* Disable reception of frames while transmitting */ + rcntrl |= FEC_RCR_DRT; + + writel(rcntrl, base_addr + FEC_RCR); + writel(tcntrl, base_addr + FEC_TCR); + } + + if (phydev->speed != fp->speed) { + new_state = 1; + fp->speed = phydev->speed; + } + + if (fp->link == PHY_DOWN) { + new_state = 1; + fp->link = phydev->link; + } + + } else if (fp->link) { + new_state = 1; + fp->link = PHY_DOWN; + fp->speed = 0; + fp->duplex = -1; + } + + if (new_state /* && netif_msg_link(fp)*/) + phy_print_status(phydev); +} + +static int fec_init_phy(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[MII_BUS_ID_SIZE + 3]; + + snprintf(phy_id, sizeof(phy_id), "%x:%02x", 0xfec, fp->phy_addr); + + fp->link = PHY_DOWN; + fp->speed = 0; + fp->duplex = -1; + + phydev = phy_connect(dev, phy_id, &fec_adjust_link, 0, PHY_INTERFACE_MODE_MII); + if (IS_ERR(phydev)) { + dev_err(&dev->dev, "phy_connect failed\n"); + return PTR_ERR(phydev); + } + dev_info(&dev->dev, + "attached phy %i (OUI/model = %06x/%02x) to driver %s\n", + phydev->addr, (phydev->phy_id >> 10) & 0x3fffff, + (phydev->phy_id >> 4) & 0x3f, phydev->drv->name); + + fp->phydev = phydev; + + return 0; +} + +static int fec_phy_start(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + int err; + + /* + * on the freescale dev board, a DUAL phy is used : + * PHY 0 for FEC 0 and PHY 1 for FEC 1 + */ + fp->phy_addr = fp->index; + + err = fec_init_phy(dev); + if (err) { + dev_err(&dev->dev, "fec_init_phy failed\n"); + return err; + } + + /* reset phy - this also wakes it from PDOWN */ + phy_write(fp->phydev, MII_BMCR, BMCR_RESET); + phy_start(fp->phydev); + + return 0; +} + +/* ethtool interface -- copy of fec_mpc52xx.c */ +#if 0 +static void fec_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, DRIVER_NAME); +} +#endif + +static int fec_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + + if (!priv->phydev) + return -ENODEV; + + return phy_ethtool_gset(priv->phydev, cmd); +} + +static int fec_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + + if (!priv->phydev) + return -ENODEV; + + return phy_ethtool_sset(priv->phydev, cmd); +} + +#if 0 +static u32 fec_get_msglevel(struct net_device *dev) +{ + struct fec_priv *priv = netdev_priv(dev); + return priv->msg_enable; +} + +static void fec_set_msglevel(struct net_device *dev, u32 level) +{ + struct fec_priv *priv = netdev_priv(dev); + priv->msg_enable = level; +} +#endif + +static const struct ethtool_ops fec_ethtool_ops = { +#if 0 + .get_drvinfo = fec_get_drvinfo, +#endif + .get_settings = fec_get_settings, + .set_settings = fec_set_settings, + .get_link = ethtool_op_get_link, +#if 0 + .get_msglevel = fec_get_msglevel, + .set_msglevel = fec_set_msglevel, +#endif +}; + + +static int fec_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct fec_priv *priv = netdev_priv(dev); + + if (!priv->phydev) + return -ENOTSUPP; + + return phy_mii_ioctl(priv->phydev, rq, cmd); +} + +static int fec_mdio_transfer(struct mii_bus *bus, int phy_addr, + int reg, u32 value) +{ + struct fec_priv *priv = (struct fec_priv *)bus->priv; + unsigned long base_addr = (unsigned long) priv->netdev->base_addr; + unsigned long time_left; + + /* + * Set MII speed to 2.5 MHz + */ + writel((((MCF_BUSCLK + 4999999) / 5000000) & 0x3F) << 1, base_addr + FEC_MSCR); + + value |= (phy_addr << 23); + value |= (reg << 18); + value |= FEC_MMFR_ST | FEC_MMFR_TA; + + init_completion(&priv->mdio_done); + writel(value, base_addr + FEC_MMFR); + + /* wait for end of transfer, this takes about 23 us on lite5200b */ + time_left = wait_for_completion_timeout(&priv->mdio_done, + usecs_to_jiffies(FEC_MII_TIMEOUT)); + + writel(0, base_addr + FEC_MSCR); + + if (time_left == 0) { + pr_err("%s : timeout accessing phy%d/reg%d\n", + bus->name, phy_addr, reg); + return -ETIMEDOUT; + } + + return (value & FEC_MMFR_OP_READ) ? + (readl(base_addr + FEC_MMFR) & FEC_MMFR_DATA) : 0; +} + +static int fec_mdio_read(struct mii_bus *bus, int phy_addr, int reg) +{ + return fec_mdio_transfer(bus, phy_addr, reg, FEC_MMFR_OP_READ); +} + +static int fec_mdio_write(struct mii_bus *bus, int phy_addr, int reg, u16 data) +{ + return fec_mdio_transfer(bus, phy_addr, reg, data | FEC_MMFR_OP_WRITE); +} + +static int __init fec_mdio_init(struct net_device *dev) +{ + struct mii_bus *bus; + int err; + int i; + unsigned long addr; + + bus = mdiobus_alloc(); + if (bus == NULL) + return -ENOMEM; + + bus->name = "FEC 0 MII bus"; + bus->read = fec_mdio_read; + bus->write = fec_mdio_write; + /* + * on the freescale dev board, both PHY's (actually a DUAL phy) + * are connected via the MDIO bus of FEC0. + * PHY 0 for FEC 0 and PHY 1 for FEC 1 + */ + bus->phy_mask = ~((1 << FEC_MAX_PORTS) - 1); + + /* setup irqs */ + bus->irq = kmalloc(sizeof(bus->irq[0]) * PHY_MAX_ADDR, GFP_KERNEL); + if (bus->irq == NULL) { + err = -ENOMEM; + goto out_free; + } + for (i = 0; i < PHY_MAX_ADDR; i++) + bus->irq[i] = PHY_POLL; + + snprintf(bus->id, MII_BUS_ID_SIZE, "%x", 0xfec); + bus->priv = netdev_priv(dev); + + /* enable MII interrupt */ + addr = dev->base_addr + FEC_EIMR; + writel(readl(addr) | FEC_EIR_MII, addr); + + err = mdiobus_register(bus); + if (err) + goto out_unmap; + + return 0; + +out_unmap: +out_free: + kfree(bus->irq); + mdiobus_free(bus); + + return err; +} + +/************************************************************************ +* +NAME: fec_get_stats +* +* RETURNS: This function returns the statistical information. +*************************************************************************/ +struct net_device_stats *fec_get_stats(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct net_device_stats *statp = &fp->fecpriv_stat; + unsigned long base_addr = dev->base_addr; + + /* Receive the statistical information */ + statp->rx_packets = readl(base_addr + FECSTAT_RMON_R_PACKETS); + statp->tx_packets = readl(base_addr + FECSTAT_RMON_T_PACKETS); + statp->rx_bytes = readl(base_addr + FECSTAT_RMON_R_OCTETS); + statp->tx_bytes = readl(base_addr + FECSTAT_RMON_T_OCTETS); + + statp->multicast = readl(base_addr + FECSTAT_RMON_R_MC_PKT); + statp->collisions = readl(base_addr + FECSTAT_RMON_T_COL); + + statp->rx_length_errors = readl(base_addr + FECSTAT_RMON_R_UNDERSIZE) + + readl(base_addr + FECSTAT_RMON_R_OVERSIZE) + + readl(base_addr + FECSTAT_RMON_R_FRAG) + + readl(base_addr + FECSTAT_RMON_R_JAB); + statp->rx_crc_errors = readl(base_addr + FECSTAT_IEEE_R_CRC); + statp->rx_frame_errors = readl(base_addr + FECSTAT_IEEE_R_ALIGN); + statp->rx_over_errors = readl(base_addr + FECSTAT_IEEE_R_MACERR); + + statp->tx_carrier_errors = readl(base_addr + FECSTAT_IEEE_T_CSERR); + statp->tx_fifo_errors = readl(base_addr + FECSTAT_IEEE_T_MACERR); + statp->tx_window_errors = readl(base_addr + FECSTAT_IEEE_T_LCOL); + + /* I hope that one frame doesn't have more than one error */ + statp->rx_errors = statp->rx_length_errors + + statp->rx_crc_errors + + statp->rx_frame_errors + + statp->rx_over_errors + + statp->rx_dropped; + statp->tx_errors = statp->tx_carrier_errors + + statp->tx_fifo_errors + + statp->tx_window_errors + + statp->tx_aborted_errors + + statp->tx_heartbeat_errors + + statp->tx_dropped; + + return statp; +} + +/************************************************************************ +* NAME: fec_set_multicast_list +* +* DESCRIPTION: This function sets the frame filtering parameters +*************************************************************************/ +void fec_set_multicast_list(struct net_device *dev) +{ + unsigned int crc; + unsigned long base_addr = (unsigned long) dev->base_addr; + struct netdev_hw_addr *ha; + u32 gaur, galr; + + if (dev->flags & IFF_PROMISC || dev->flags & IFF_ALLMULTI) { + /* Allow all incoming frames */ + writel(0xFFFFFFFF, base_addr + FEC_GALR); + writel(0xFFFFFFFF, base_addr + FEC_GAUR); + return; + } + + /* Reset the group address register */ + galr = 0x00000000; + gaur = 0x00000000; + + /* Process all addresses */ + netdev_for_each_mc_addr(ha, dev) { + /* Calculate crc value for the current address */ + crc = ether_crc_le(MAC_ADDR_LEN, ha->addr) >> 26; + + /* Add this value */ + crc &= 0x3F; + if (crc > 31) + gaur |= 0x1 << (crc - 32); + else + galr |= 0x1 << crc; + } + writel(galr, base_addr + FEC_GALR); + writel(gaur, base_addr + FEC_GAUR); +} + +/************************************************************************ +* 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, MAC_ADDR_LEN); + + /* Set the address to the registers */ + writel((dev->dev_addr[0] << 24) | (dev->dev_addr[1] << 16) | + (dev->dev_addr[2] << 8) | dev->dev_addr[3], base_addr + FEC_PALR); + writel((dev->dev_addr[4] << 24) | (dev->dev_addr[5] << 16) | 0x8808, + base_addr + FEC_PAUR); + + return 0; +} + +/************************************************************************ +* NAME: fec_start_tx +* +* DESCRIPTION: This function starts transmission of the frame using DMA +* +* RETURNS: This function always returns zero. +*************************************************************************/ +int fec_start_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct MCD_bufDescFec *txd = fp->fecpriv_txdesc; + int n = fp->fecpriv_next_tx; + 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 */ + flush_dcache_range(virt_to_phys(data_aligned), skb->len); + + spin_lock_irq(&fp->fecpriv_lock); + + /* Initialize the descriptor */ + fp->fecpriv_txbuf[n] = data; + txd[n].dataPointer = (unsigned int) virt_to_phys(data_aligned); + txd[n].length = skb->len; + txd[n].statCtrl |= (MCD_FEC_END_FRAME | MCD_FEC_BUF_READY); + n = (n + 1) & FEC_TX_INDEX_MASK; + fp->fecpriv_next_tx = n; + + if (fp->fecpriv_txbuf[fp->fecpriv_current_tx] && fp->fecpriv_current_tx == n) + 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); + struct MCD_bufDescFec *txd = fp->fecpriv_txdesc; + unsigned long base_addr = (unsigned long) dev->base_addr; + u32 fecfrst; + + 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; + } + txd[i].statCtrl = MCD_FEC_INTERRUPT; + } + txd[i - 1].statCtrl |= MCD_FEC_WRAP; + + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + /* Reset FIFOs */ + fecfrst = FEC_SW_RST | FEC_RST_CTL; + writel(fecfrst, base_addr + FEC_FECFRST); + fecfrst &= ~FEC_SW_RST; + writel(fecfrst, base_addr + FEC_FECFRST); + +#if 0 + /* Reset and disable FEC */ + writel(FEC_ECR_RESET, base_addr + FEC_ECR); +#endif + + /* Enable FEC */ + writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR); + + MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) txd, 0, + (unsigned char *) (base_addr + FEC_FECTFDR), 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(void *voidp) +{ + struct net_device *dev = voidp; + struct fec_priv *fp = netdev_priv(dev); + void **txb = fp->fecpriv_txbuf; + int c = fp->fecpriv_current_tx; + + /* Release the socket buffer */ + if (txb[c]) { + kfree(txb[c]); + txb[c] = NULL; + } + c = (c + 1) & FEC_TX_INDEX_MASK; + + if (MCD_dmaStatus(fp->fecpriv_fec_tx_channel) == MCD_DONE) { + for (; c != fp->fecpriv_next_tx; c = (c + 1) & FEC_TX_INDEX_MASK) { + if (txb[c]) { + kfree(txb[c]); + txb[c] = NULL; + } + } + } + fp->fecpriv_current_tx = c; + + 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(void *voidp) +{ + struct net_device *dev = voidp; + struct fec_priv *fp = netdev_priv(dev); + struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc; + struct sk_buff **rxskb = fp->askb_rx; + struct sk_buff *skb; + int i; + int c = fp->fecpriv_current_rx; + + fp->fecpriv_rxflag = 1; + /* Some buffers can be missed */ + if (!(rxd[c].statCtrl & MCD_FEC_END_FRAME)) { + /* Find a valid index */ + for (i = 0; i < FEC_RX_BUF_NUMBER && !(rxd[c].statCtrl & MCD_FEC_END_FRAME); i++, c = (c + 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 (; rxd[c].statCtrl & MCD_FEC_END_FRAME; c = (c + 1) & FEC_RX_INDEX_MASK) { + if ((rxd[c].length <= FEC_MAXBUF_SIZE) && + (rxd[c].length > 4)) { /* --tym-- */ + skb = rxskb[c]; + if (!skb) + fp->fecpriv_stat.rx_dropped++; + else { + /* + * invalidate data cache before initializing + * the descriptor and starting DMAs + */ + cache_clear(virt_to_phys(skb->data), skb->len); + + skb_put(skb, rxd[c].length - 4); + skb->protocol = eth_type_trans(skb, dev); + netif_rx(skb); + } + rxd[c].statCtrl &= ~MCD_FEC_END_FRAME; + /* allocate new skbuff */ + rxskb[c] = alloc_skb(FEC_MAXBUF_SIZE + 16, /*GFP_ATOMIC |*/ GFP_DMA); + if (!rxskb[c]) { + rxd[c].dataPointer = 0; + rxd[c].length = 0; + fp->fecpriv_stat.rx_dropped++; + } else { +#if 0 +if (!fp->in_poll) + pr_debug("rxskb[%d] = %p\n", c, rxskb[c]); +#endif + skb_reserve(rxskb[c], 16); + rxskb[c]->dev = dev; + + /* + * invalidate data cache before initializing + * the descriptor and starting DMAs + */ + cache_clear(virt_to_phys(rxskb[c]->data), FEC_MAXBUF_SIZE); + + rxd[c].dataPointer = (unsigned int) virt_to_phys(rxskb[c]->data); + rxd[c].length = FEC_MAXBUF_SIZE; + rxd[c].statCtrl |= MCD_FEC_BUF_READY; + } + } + } + + fp->fecpriv_current_rx = c; + + /* 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; + u32 fecfrst; + + /* Read and clear the events */ + events = readl(base_addr + FEC_EIR) & readl(base_addr + FEC_EIMR); + + if (events & FEC_EIR_HBERR) { + fp->fecpriv_stat.tx_heartbeat_errors++; + writel(FEC_EIR_HBERR, base_addr + FEC_EIR); + } + + /* receive/transmit FIFO error */ + if (events & (FEC_EIR_RFERR | FEC_EIR_XFERR)) { + /* 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 */ + fecfrst = FEC_SW_RST | FEC_RST_CTL; + writel(fecfrst, base_addr + FEC_FECFRST); + fecfrst &= ~FEC_SW_RST; + writel(fecfrst, base_addr + FEC_FECFRST); + + /* reset receive FIFO status register */ + writel(FEC_FECRFSR_FAE | + FEC_FECRFSR_RXW | + FEC_FECRFSR_UF, + base_addr + FEC_FECRFSR); + + /* reset transmit FIFO status register */ + writel(FEC_FECTFSR_FAE | + FEC_FECTFSR_TXW | + FEC_FECTFSR_UF | + FEC_FECTFSR_OF, + base_addr + FEC_FECTFSR); + + /* reset RFERR and XFERR event */ + writel(FEC_EIR_RFERR | FEC_EIR_XFERR, base_addr + FEC_EIR); + + /* 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) { + /* reset XFUN event */ + writel(FEC_EIR_XFUN, base_addr + FEC_EIR); + fp->fecpriv_stat.tx_aborted_errors++; + } + + /* late collision */ + if (events & FEC_EIR_LC) { + /* reset LC event */ + writel(FEC_EIR_LC, base_addr + FEC_EIR); + fp->fecpriv_stat.tx_aborted_errors++; + } + + /* collision retry limit */ + if (events & FEC_EIR_RL) { + /* reset RL event */ + writel(FEC_EIR_RL, base_addr + FEC_EIR); + fp->fecpriv_stat.tx_aborted_errors++; + } + + /* MII transfer completed */ + if (events & FEC_EIR_MII) { + complete(&fp->mdio_done); + writel(FEC_EIR_MII, base_addr + FEC_EIR); + } + 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); + struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc; + struct MCD_bufDescFec *txd = fp->fecpriv_txdesc; + struct sk_buff **rxskb = fp->askb_rx; + unsigned long base_addr = (unsigned long) dev->base_addr; + +#if 0 +if (!fp->in_poll) + pr_debug("fec_interrupt_fec_reinit\n"); +#endif + /* Initialize reception descriptors and start DMA for the reception */ + for (i = 0; i < FEC_RX_BUF_NUMBER; i++) { + if (!rxskb[i]) { + rxskb[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, GFP_ATOMIC | GFP_DMA); + if (!rxskb[i]) { + rxd[i].dataPointer = 0; + rxd[i].statCtrl = 0; + rxd[i].length = 0; + continue; + } +#if 0 +if (!fp->in_poll) + pr_debug("rxskb[%d] = %p\n", i, rxskb[i]); +#endif + rxskb[i]->dev = dev; + skb_reserve(rxskb[i], 16); + } + rxd[i].dataPointer = (unsigned int) virt_to_phys(rxskb[i]->data); + rxd[i].statCtrl = MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT; + rxd[i].length = FEC_MAXBUF_SIZE; + } + + rxd[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++; + } + txd[i].statCtrl = MCD_FEC_INTERRUPT; + } + txd[i - 1].statCtrl |= MCD_FEC_WRAP; + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + /* flush entire data cache before restarting the DMA */ + flush_and_invalidate_dcache(); + + /* restart DMA from beginning */ + MCD_startDma(fp->fecpriv_fec_rx_channel, (char *) rxd, 0, + (unsigned char *) (base_addr + FEC_FECRFDR), 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 *) txd, 0, + (unsigned char *) (base_addr + FEC_FECTFDR), 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 */ + writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR); + + netif_wake_queue(dev); +} + +/************************************************************************ +* NAME: fec_open +* +* DESCRIPTION: This function performs the initialization of FEC +* +* RETURNS: If no error occurs, this function returns zero. +*************************************************************************/ +int fec_open(struct net_device *dev) +{ + struct fec_priv *fp = netdev_priv(dev); + struct MCD_bufDescFec *rxd = fp->fecpriv_rxdesc; + struct MCD_bufDescFec *txd = fp->fecpriv_txdesc; + struct sk_buff **rxskb = fp->askb_rx; + unsigned long base_addr = (unsigned long) dev->base_addr; + int i; + int channel; + int error_code = -EBUSY; + u32 fecfrst; + u32 rcntl; + + /* Receive the DMA channels */ + channel = dma_set_channel_fec(fp->fecpriv_rx_requestor); + + if (channel == -1) { + pr_err("Rx Dma channel cannot be reserved\n"); + goto ERRORS; + } + + fp->fecpriv_fec_rx_channel = channel; + + dma_connect(channel, fec_interrupt_fec_rx_handler, dev); + + channel = dma_set_channel_fec(fp->fecpriv_tx_requestor); + + if (channel == -1) { + pr_err("Tx Dma channel cannot be reserved\n"); + goto ERRORS; + } + + fp->fecpriv_fec_tx_channel = channel; + + dma_connect(channel, fec_interrupt_fec_tx_handler, dev); + + /* init tasklet for controller reinitialization */ + tasklet_init(&fp->fecpriv_tasklet_reinit, fec_interrupt_fec_reinit, + (unsigned long) dev); + + /* Reset FIFOs */ + fecfrst = FEC_SW_RST | FEC_RST_CTL; + writel(fecfrst, base_addr + FEC_FECFRST); + fecfrst &= ~FEC_SW_RST; + writel(fecfrst, base_addr + FEC_FECFRST); + + /* Reset and disable FEC */ + writel(FEC_ECR_RESET, base_addr + FEC_ECR); + + udelay(10); + + /* Clear all events */ + writel(FEC_EIR_CLEAR, base_addr + FEC_EIR); + + /* Reset FIFO status */ + writel(FEC_FECTFSR_MSK, base_addr + FEC_FECTFSR); + writel(FEC_FECRFSR_MSK, base_addr + FEC_FECRFSR); + + /* Set the default address */ + writel((dev->dev_addr[0] << 24) | (dev->dev_addr[1] << 16) | + (dev->dev_addr[2] << 8) | dev->dev_addr[3], base_addr + FEC_PALR); + writel((dev->dev_addr[4] << 24) | (dev->dev_addr[5] << 16) | 0x8808, + base_addr + FEC_PAUR); + + /* Reset the group address descriptor */ + writel(0x00000000, base_addr + FEC_GALR); + writel(0x00000000, base_addr + FEC_GAUR); + + /* Reset the individual address descriptor */ + writel(0x00000000, base_addr + FEC_IALR); + writel(0x00000000, base_addr + FEC_IAUR); + + /* Set the receive control register */ + rcntl = FEC_RCR_MAX_FRM_SIZE | FEC_RCR_MII; + writel(rcntl, base_addr + FEC_RCR); + + /* Set the receive FIFO control register */ + writel(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 */ +#if 0 + & ~FEC_FECRFCR_UF /* enable FIFO underflow */ +#endif + ), base_addr + FEC_FECRFCR); + + /* Set the receive FIFO alarm register */ + writel(FEC_FECRFAR_ALARM, base_addr + FEC_FECRFAR); + + /* Set the transmit FIFO control register */ + writel(FEC_FECTFCR_FRM | FEC_FECTFCR_GR + | (FEC_FECTFCR_MSK /* disable all but ... */ + & ~FEC_FECTFCR_FAE /* enable frame accept error */ +#if 0 + & ~FEC_FECTFCR_TXW /* enable transmit wait condition */ + & ~FEC_FECTFCR_UF /* enable FIFO underflow */ +#endif + & ~FEC_FECTFCR_OF), /* enable FIFO overflow */ + base_addr + FEC_FECTFCR); + + /* Set the transmit FIFO alarm register */ + writel(FEC_FECTFAR_ALARM, base_addr + FEC_FECTFAR); + + /* Set the Tx FIFO watermark */ + writel(FEC_FECTFWR_XWMRK, base_addr + FEC_FECTFWR); + + /* Enable the transmitter to append the CRC */ + writel(FEC_CTCWR_TFCW_CRC, base_addr + FEC_CTCWR); + + /* Enable the ethernet interrupts */ /* FIXME : too late for mdio bus */ + writel(FEC_EIMR_DISABLE + | FEC_EIR_LC + | FEC_EIR_RL + | FEC_EIR_HBERR + | FEC_EIR_XFUN + | FEC_EIR_XFERR + | FEC_EIR_RFERR + | FEC_EIR_MII, + base_addr + FEC_EIMR); + + if (fp->phydev->duplex == DUPLEX_FULL) + /* Enable the full duplex mode */ + writel(FEC_TCR_FDEN | FEC_TCR_HBC, base_addr + FEC_TCR); + else { + /* Disable reception of frames while transmitting */ + rcntl |= FEC_RCR_DRT; + writel(rcntl, base_addr + FEC_RCR); + } + + /* Enable MIB */ + writel(FEC_MIBC_ENABLE, base_addr + FEC_MIBC); + + /* Enable FEC */ + writel(FEC_ECR_ETHEREN, base_addr + FEC_ECR); + + /* Initialize tx descriptors and start DMA for the transmission */ + for (i = 0; i < FEC_TX_BUF_NUMBER; i++) + txd[i].statCtrl = MCD_FEC_INTERRUPT; + + txd[i - 1].statCtrl |= MCD_FEC_WRAP; + + fp->fecpriv_current_tx = fp->fecpriv_next_tx = 0; + + MCD_startDma(fp->fecpriv_fec_tx_channel, (char *) txd, 0, + (unsigned char *) (base_addr + FEC_FECTFDR), 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++) { + rxskb[i] = alloc_skb(FEC_MAXBUF_SIZE + 16, GFP_DMA); + if (!rxskb[i]) { + rxd[i].dataPointer = 0; + rxd[i].statCtrl = 0; + rxd[i].length = 0; + } else { +#if 0 +if (!fp->in_poll) + pr_debug("rxskb[%d] = %p\n", i, rxskb[i]); +#endif + skb_reserve(rxskb[i], 16); + rxskb[i]->dev = dev; + rxd[i].dataPointer = (unsigned int) virt_to_phys(rxskb[i]->data); + rxd[i].statCtrl = MCD_FEC_BUF_READY | MCD_FEC_INTERRUPT; + rxd[i].length = FEC_MAXBUF_SIZE; + } + } + + rxd[i - 1].statCtrl |= MCD_FEC_WRAP; + fp->fecpriv_current_rx = 0; + + /* flush entire data cache before restarting the DMA */ + flush_and_invalidate_dcache(); + + MCD_startDma(fp->fecpriv_fec_rx_channel, (char *) rxd, 0, + (unsigned char *) (base_addr + FEC_FECRFDR), 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; + unsigned long addr; + + netif_stop_queue(dev); + + /* Perform the graceful stop */ + addr = dev->base_addr + FEC_TCR; + writel(readl(addr) | FEC_TCR_GTS, addr); + + time = jiffies; + + /* Wait for the graceful stop */ + while (!(readl(base_addr + FEC_EIR) & FEC_EIR_GRA) && jiffies - time < FEC_GR_TIMEOUT * HZ) + schedule(); + + /* Disable FEC */ + writel(FEC_ECR_DISABLE, base_addr + FEC_ECR); + + /* 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; +} + +#ifndef MODULE +/************************************************************************ +* 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[MAC_ADDR_LEN], *octetptr = octet; + int i; + +again: + val = 0; + while ((c = *str_mac) != '\0') { + c = hex_to_bin(c); + if (c >= 0) { + val = (val * 16) + c; + str_mac++; + continue; + } + break; + } + if (*str_mac == ':') { + *octetptr++ = val, str_mac++; + if (octetptr >= octet + MAC_ADDR_LEN) + return 1; + goto again; + } + + /* Check for trailing characters */ + if (*str_mac && !(*str_mac == ' ')) + return 1; + + *octetptr++ = val; + + if ((octetptr - octet) == MAC_ADDR_LEN) { + for (i = 0; i <= MAC_ADDR_LEN; i++) + addr[i] = octet[i]; + } else + return 1; + + return 0; +} + +/************************************************************************ +* 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, fec0_addr)) + pr_err("Invalid MAC address from command line for FEC0"); + return 1; +} +__setup("mac0=", fec_mac_setup0); + +#ifdef CONFIG_FEC2 +/************************************************************************ +* 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, fec1_addr)) + pr_err("Invalid MAC address from command line for FEC1"); + return 1; +} +__setup("mac1=", fec_mac_setup1); +#endif +#endif + +#undef CONFIG_NET_POLL_CONTROLLER +#ifdef CONFIG_NET_POLL_CONTROLLER +/* + * Polling 'interrupt' - used by things like netconsole to send skbs + * without having to re-enable interrupts. It's not called while + * the interrupt routine is executing. + */ + +static void fec_poll(struct net_device *dev) +{ +#if 0 + struct fec_priv *fp = netdev_priv(dev); +#endif + /* disable_irq here is not very nice, but with the lockless + interrupt handler we have no other choice. */ +#if 0 + fp->in_poll = 1; +#endif + disable_irq(112); + disable_irq(dev->irq); + fec_interrupt_handler(dev->irq, dev); + fec_interrupt_fec_rx_handler(dev); + fec_interrupt_fec_tx_handler(dev); + enable_irq(dev->irq); + enable_irq(112); +#if 0 + fp->in_poll = 0; +#endif +} +#endif + +static const struct net_device_ops fec_netdev_ops = { + .ndo_open = fec_open, + .ndo_stop = fec_close, + .ndo_start_xmit = fec_start_tx, + .ndo_set_rx_mode = fec_set_multicast_list, + .ndo_set_mac_address = fec_set_mac_address, + .ndo_validate_addr = eth_validate_addr, + .ndo_tx_timeout = fec_tx_timeout, + .ndo_get_stats = fec_get_stats, + .ndo_do_ioctl = fec_ioctl, +#ifdef CONFIG_NET_POLL_CONTROLLER + .ndo_poll_controller = fec_poll, +#endif +}; + +/* + * Initialize a FEC device + */ +int fec_enet_init(struct net_device *dev) +{ + static int index; + struct fec_priv *fp = netdev_priv(dev); + const void *mac_addr; + int i; + unsigned long addr; + + fp->index = index; +#if 0 + fp->in_poll = 0; +#endif + fp->netdev = dev; + + if (index == 0) { + /* disable fec0 */ + writel(FEC_ECR_DISABLE, FEC_BASE_ADDR_FEC0 + FEC_ECR); + + /* setup the interrupt handler */ + dev->irq = MCF_IRQ_FEC0; + + if (request_irq(dev->irq, fec_interrupt_handler, + IRQF_DISABLED, "ColdFire FEC 0", dev)) { + dev->irq = 0; + pr_err("Cannot allocate FEC0 IRQ\n"); +#if 0 + } else { + /* interrupt priority and level */ + MCF_ICR(ISC_FEC0) = ILP_FEC0; +#endif + } + + /* 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; + + /* tx descriptors */ + fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC0; + + /* rx descriptors */ + fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC0; + + /* mac addr */ + mac_addr = machdep_get_mac_address(index); + if (mac_addr) + memcpy(fec0_addr, mac_addr, MAC_ADDR_LEN); + fp->fecpriv_mac_addr = fec0_addr; + } else { + /* disable fec1 */ + writel(FEC_ECR_DISABLE, FEC_BASE_ADDR_FEC1 + FEC_ECR); +#ifdef CONFIG_FEC2 + /* setup the interrupt handler */ + dev->irq = MCF_IRQ_FEC1; + + if (request_irq(dev->irq, fec_interrupt_handler, + IRQF_DISABLED, "ColdFire FEC 1", dev)) { + dev->irq = 0; + pr_err("Cannot allocate FEC1 IRQ\n"); +#if 0 + } else { + /* interrupt priority and level */ + MCF_ICR(ISC_FEC1) = ILP_FEC1; +#endif + } + + /* 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; + + /* tx descriptors */ + fp->fecpriv_txdesc = (void *)FEC_TX_DESC_FEC1; + + /* rx descriptors */ + fp->fecpriv_rxdesc = (void *)FEC_RX_DESC_FEC1; + + /* mac addr */ + mac_addr = machdep_get_mac_address(index); + if (mac_addr) + memcpy(fec1_addr, mac_addr, MAC_ADDR_LEN); + fp->fecpriv_mac_addr = fec1_addr; +#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->watchdog_timeo = FEC_TX_TIMEOUT; + dev->netdev_ops = &fec_netdev_ops; + dev->ethtool_ops = &fec_ethtool_ops; + + + memcpy(dev->dev_addr, fp->fecpriv_mac_addr, ETH_ALEN); + + spin_lock_init(&fp->fecpriv_lock); + + /* Initialize FEC/I2C/IRQ Pin Assignment Register */ + writew((readw(FEC_GPIO_PAR_FECI2CIRQ) & 0xF) | FEC_FECI2CIRQ, + FEC_GPIO_PAR_FECI2CIRQ); + + /* set MII mode (as opposed to 7-wires mode) */ + addr = dev->base_addr + FEC_RCR; + writel(readl(addr) | FEC_RCR_MII, addr); + + index++; + 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); +} + +/* + * Module Initialization + */ +int __init fec_init(void) +{ + struct net_device *dev; + int i; + int err; + + pr_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; + netif_carrier_off(dev); + err = fec_enet_init(dev); + if (err) { + free_netdev(dev); + continue; + } + if (register_netdev(dev) != 0) { + free_netdev(dev); + return -EIO; + } + + /* + * On the freescale dev board, E0MDC & E0MDIO are + * connected to a dual phy. E1MDC & E1MDIO are unused. + * In other words, only fec 0 can communicate with + * the dual phy. + */ + if (i == 0) + fec_mdio_init(dev); + + fec_phy_start(dev); + + pr_info("%s: ethernet %pM\n", + dev->name, dev->dev_addr); + } + return 0; +} + +/* module_exit(fec_cleanup); */ +module_init(fec_init); + +MODULE_LICENSE("GPL"); diff --git a/drivers/net/ethernet/freescale/fec_m54xx.h b/drivers/net/ethernet/freescale/fec_m54xx.h new file mode 100644 index 0000000..022ee32 --- /dev/null +++ b/drivers/net/ethernet/freescale/fec_m54xx.h @@ -0,0 +1,144 @@ + +#define FEC_BASE_ADDR_FEC0 ((unsigned int)MCF_MBAR + 0x9000) +#define FEC_BASE_ADDR_FEC1 ((unsigned int)MCF_MBAR + 0x9800) + +#define FEC_GPIO_PAR_FECI2CIRQ ((unsigned int)MCF_MBAR + 0xA44) +#define FEC_FECI2CIRQ (0xFFC0) + +#define FEC_ECR_DISABLE (0x00000000) + +#define FEC_EIR 0x004 +#define FEC_EIMR 0x008 +#define FEC_ECR 0x024 +#define FEC_MMFR 0x040 +#define FEC_MSCR 0x044 +#define FEC_MIBC 0x064 +#define FEC_RCR 0x084 +#define FEC_TCR 0x0C4 +#define FEC_PALR 0x0E4 +#define FEC_PAUR 0x0E8 +#define FEC_IAUR 0x118 +#define FEC_IALR 0x11C +#define FEC_GAUR 0x120 +#define FEC_GALR 0x124 +#define FEC_FECTFWR 0x144 +#define FEC_FECRFDR 0x184 +#define FEC_FECRFSR 0x188 +#define FEC_FECRFCR 0x18C +#define FEC_FECRFAR 0x198 +#define FEC_FECTFDR 0x1A4 +#define FEC_FECTFSR 0x1A8 +#define FEC_FECTFCR 0x1AC +#define FEC_FECTFAR 0x1B8 +#define FEC_FECFRST 0x1C4 +#define FEC_CTCWR 0x1C8 +#define FECSTAT_RMON_T_PACKETS 0x204 +#define FECSTAT_RMON_T_COL 0x224 +#define FECSTAT_RMON_T_OCTETS 0x244 +#define FECSTAT_IEEE_T_DROP 0x248 +#define FECSTAT_IEEE_T_FRAME_OK 0x24C +#define FECSTAT_IEEE_T_LCOL 0x25C +#define FECSTAT_IEEE_T_MACERR 0x264 +#define FECSTAT_IEEE_T_CSERR 0x268 +#define FECSTAT_IEEE_T_OCTETS_OK 0x274 +#define FECSTAT_RMON_R_PACKETS 0x284 +#define FECSTAT_RMON_R_MC_PKT 0x28C +#define FECSTAT_RMON_R_UNDERSIZE 0x294 +#define FECSTAT_RMON_R_OVERSIZE 0x298 +#define FECSTAT_RMON_R_FRAG 0x29C +#define FECSTAT_RMON_R_JAB 0x2A0 +#define FECSTAT_RMON_R_OCTETS 0x2C4 +#define FECSTAT_IEEE_R_DROP 0x2C8 +#define FECSTAT_IEEE_R_FRAME_OK 0x2CC +#define FECSTAT_IEEE_R_CRC 0x2D0 +#define FECSTAT_IEEE_R_ALIGN 0x2D4 +#define FECSTAT_IEEE_R_MACERR 0x2D8 +#define FECSTAT_IEEE_R_OCTETS_OK 0x2E0 + +#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_GRA (0x10000000) +#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_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_RX_DESC_FEC0 (SYS_SRAM_FEC_START) +#define FEC_TX_DESC_FEC0 (FEC_RX_DESC_FEC0 + FEC_RX_BUF_NUMBER * sizeof(struct 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(struct MCD_bufDescFec)) + +#define FEC_MMFR_ST (0x40000000) +#define FEC_MMFR_OP_READ (0x20000000) +#define FEC_MMFR_OP_WRITE (0x10020000) +#define FEC_MMFR_TA (0x00020000) +#define FEC_MMFR_DATA (0x0000ffff) /* PHY data mask */ + +#define FEC_FLAGS_RX (0x00000001) + +#define FEC_CRCPOL (0xEDB88320) + +#define FEC_GR_TIMEOUT (1) + +#define FEC_SW_RST 0x2000000 +#define FEC_RST_CTL 0x1000000 -- 1.7.1 _______________________________________________ uClinux-dev mailing list uClinux-dev@uclinux.org http://mailman.uclinux.org/mailman/listinfo/uclinux-dev This message was resent by uclinux-dev@uclinux.org To unsubscribe see: http://mailman.uclinux.org/mailman/options/uclinux-dev