162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * meth.c -- O2 Builtin 10/100 Ethernet driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2001-2003 Ilya Volynets 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <linux/errno.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/in.h> 1862306a36Sopenharmony_ci#include <linux/in6.h> 1962306a36Sopenharmony_ci#include <linux/device.h> /* struct device, et al */ 2062306a36Sopenharmony_ci#include <linux/netdevice.h> /* struct device, and other headers */ 2162306a36Sopenharmony_ci#include <linux/etherdevice.h> /* eth_type_trans */ 2262306a36Sopenharmony_ci#include <linux/ip.h> /* struct iphdr */ 2362306a36Sopenharmony_ci#include <linux/tcp.h> /* struct tcphdr */ 2462306a36Sopenharmony_ci#include <linux/skbuff.h> 2562306a36Sopenharmony_ci#include <linux/mii.h> /* MII definitions */ 2662306a36Sopenharmony_ci#include <linux/crc32.h> 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#include <asm/ip32/mace.h> 2962306a36Sopenharmony_ci#include <asm/ip32/ip32_ints.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include <asm/io.h> 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#include "meth.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#ifndef MFE_DEBUG 3662306a36Sopenharmony_ci#define MFE_DEBUG 0 3762306a36Sopenharmony_ci#endif 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#if MFE_DEBUG>=1 4062306a36Sopenharmony_ci#define DPRINTK(str,args...) printk(KERN_DEBUG "meth: %s: " str, __func__ , ## args) 4162306a36Sopenharmony_ci#define MFE_RX_DEBUG 2 4262306a36Sopenharmony_ci#else 4362306a36Sopenharmony_ci#define DPRINTK(str,args...) 4462306a36Sopenharmony_ci#define MFE_RX_DEBUG 0 4562306a36Sopenharmony_ci#endif 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic const char *meth_str="SGI O2 Fast Ethernet"; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* The maximum time waited (in jiffies) before assuming a Tx failed. (400ms) */ 5162306a36Sopenharmony_ci#define TX_TIMEOUT (400*HZ/1000) 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic int timeout = TX_TIMEOUT; 5462306a36Sopenharmony_cimodule_param(timeout, int, 0); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* 5762306a36Sopenharmony_ci * Maximum number of multicast addresses to filter (vs. Rx-all-multicast). 5862306a36Sopenharmony_ci * MACE Ethernet uses a 64 element hash table based on the Ethernet CRC. 5962306a36Sopenharmony_ci */ 6062306a36Sopenharmony_ci#define METH_MCF_LIMIT 32 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * This structure is private to each device. It is used to pass 6462306a36Sopenharmony_ci * packets in and out, so there is place for a packet 6562306a36Sopenharmony_ci */ 6662306a36Sopenharmony_cistruct meth_private { 6762306a36Sopenharmony_ci struct platform_device *pdev; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* in-memory copy of MAC Control register */ 7062306a36Sopenharmony_ci u64 mac_ctrl; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci /* in-memory copy of DMA Control register */ 7362306a36Sopenharmony_ci unsigned long dma_ctrl; 7462306a36Sopenharmony_ci /* address of PHY, used by mdio_* functions, initialized in mdio_probe */ 7562306a36Sopenharmony_ci unsigned long phy_addr; 7662306a36Sopenharmony_ci tx_packet *tx_ring; 7762306a36Sopenharmony_ci dma_addr_t tx_ring_dma; 7862306a36Sopenharmony_ci struct sk_buff *tx_skbs[TX_RING_ENTRIES]; 7962306a36Sopenharmony_ci dma_addr_t tx_skb_dmas[TX_RING_ENTRIES]; 8062306a36Sopenharmony_ci unsigned long tx_read, tx_write, tx_count; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci rx_packet *rx_ring[RX_RING_ENTRIES]; 8362306a36Sopenharmony_ci dma_addr_t rx_ring_dmas[RX_RING_ENTRIES]; 8462306a36Sopenharmony_ci struct sk_buff *rx_skbs[RX_RING_ENTRIES]; 8562306a36Sopenharmony_ci unsigned long rx_write; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* Multicast filter. */ 8862306a36Sopenharmony_ci u64 mcast_filter; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci spinlock_t meth_lock; 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic void meth_tx_timeout(struct net_device *dev, unsigned int txqueue); 9462306a36Sopenharmony_cistatic irqreturn_t meth_interrupt(int irq, void *dev_id); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/* global, initialized in ip32-setup.c */ 9762306a36Sopenharmony_cichar o2meth_eaddr[8]={0,0,0,0,0,0,0,0}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic inline void load_eaddr(struct net_device *dev) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci int i; 10262306a36Sopenharmony_ci u64 macaddr; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci DPRINTK("Loading MAC Address: %pM\n", dev->dev_addr); 10562306a36Sopenharmony_ci macaddr = 0; 10662306a36Sopenharmony_ci for (i = 0; i < 6; i++) 10762306a36Sopenharmony_ci macaddr |= (u64)dev->dev_addr[i] << ((5 - i) * 8); 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mace->eth.mac_addr = macaddr; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Waits for BUSY status of mdio bus to clear 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci#define WAIT_FOR_PHY(___rval) \ 11662306a36Sopenharmony_ci while ((___rval = mace->eth.phy_data) & MDIO_BUSY) { \ 11762306a36Sopenharmony_ci udelay(25); \ 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci/*read phy register, return value read */ 12062306a36Sopenharmony_cistatic unsigned long mdio_read(struct meth_private *priv, unsigned long phyreg) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci unsigned long rval; 12362306a36Sopenharmony_ci WAIT_FOR_PHY(rval); 12462306a36Sopenharmony_ci mace->eth.phy_regs = (priv->phy_addr << 5) | (phyreg & 0x1f); 12562306a36Sopenharmony_ci udelay(25); 12662306a36Sopenharmony_ci mace->eth.phy_trans_go = 1; 12762306a36Sopenharmony_ci udelay(25); 12862306a36Sopenharmony_ci WAIT_FOR_PHY(rval); 12962306a36Sopenharmony_ci return rval & MDIO_DATA_MASK; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_cistatic int mdio_probe(struct meth_private *priv) 13362306a36Sopenharmony_ci{ 13462306a36Sopenharmony_ci int i; 13562306a36Sopenharmony_ci unsigned long p2, p3, flags; 13662306a36Sopenharmony_ci /* check if phy is detected already */ 13762306a36Sopenharmony_ci if(priv->phy_addr>=0&&priv->phy_addr<32) 13862306a36Sopenharmony_ci return 0; 13962306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 14062306a36Sopenharmony_ci for (i=0;i<32;++i){ 14162306a36Sopenharmony_ci priv->phy_addr=i; 14262306a36Sopenharmony_ci p2=mdio_read(priv,2); 14362306a36Sopenharmony_ci p3=mdio_read(priv,3); 14462306a36Sopenharmony_ci#if MFE_DEBUG>=2 14562306a36Sopenharmony_ci switch ((p2<<12)|(p3>>4)){ 14662306a36Sopenharmony_ci case PHY_QS6612X: 14762306a36Sopenharmony_ci DPRINTK("PHY is QS6612X\n"); 14862306a36Sopenharmony_ci break; 14962306a36Sopenharmony_ci case PHY_ICS1889: 15062306a36Sopenharmony_ci DPRINTK("PHY is ICS1889\n"); 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci case PHY_ICS1890: 15362306a36Sopenharmony_ci DPRINTK("PHY is ICS1890\n"); 15462306a36Sopenharmony_ci break; 15562306a36Sopenharmony_ci case PHY_DP83840: 15662306a36Sopenharmony_ci DPRINTK("PHY is DP83840\n"); 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci#endif 16062306a36Sopenharmony_ci if(p2!=0xffff&&p2!=0x0000){ 16162306a36Sopenharmony_ci DPRINTK("PHY code: %x\n",(p2<<12)|(p3>>4)); 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 16662306a36Sopenharmony_ci if(priv->phy_addr<32) { 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci DPRINTK("Oopsie! PHY is not known!\n"); 17062306a36Sopenharmony_ci priv->phy_addr=-1; 17162306a36Sopenharmony_ci return -ENODEV; 17262306a36Sopenharmony_ci} 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_cistatic void meth_check_link(struct net_device *dev) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 17762306a36Sopenharmony_ci unsigned long mii_advertising = mdio_read(priv, 4); 17862306a36Sopenharmony_ci unsigned long mii_partner = mdio_read(priv, 5); 17962306a36Sopenharmony_ci unsigned long negotiated = mii_advertising & mii_partner; 18062306a36Sopenharmony_ci unsigned long duplex, speed; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (mii_partner == 0xffff) 18362306a36Sopenharmony_ci return; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci speed = (negotiated & 0x0380) ? METH_100MBIT : 0; 18662306a36Sopenharmony_ci duplex = ((negotiated & 0x0100) || (negotiated & 0x01C0) == 0x0040) ? 18762306a36Sopenharmony_ci METH_PHY_FDX : 0; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if ((priv->mac_ctrl & METH_PHY_FDX) ^ duplex) { 19062306a36Sopenharmony_ci DPRINTK("Setting %s-duplex\n", duplex ? "full" : "half"); 19162306a36Sopenharmony_ci if (duplex) 19262306a36Sopenharmony_ci priv->mac_ctrl |= METH_PHY_FDX; 19362306a36Sopenharmony_ci else 19462306a36Sopenharmony_ci priv->mac_ctrl &= ~METH_PHY_FDX; 19562306a36Sopenharmony_ci mace->eth.mac_ctrl = priv->mac_ctrl; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if ((priv->mac_ctrl & METH_100MBIT) ^ speed) { 19962306a36Sopenharmony_ci DPRINTK("Setting %dMbs mode\n", speed ? 100 : 10); 20062306a36Sopenharmony_ci if (duplex) 20162306a36Sopenharmony_ci priv->mac_ctrl |= METH_100MBIT; 20262306a36Sopenharmony_ci else 20362306a36Sopenharmony_ci priv->mac_ctrl &= ~METH_100MBIT; 20462306a36Sopenharmony_ci mace->eth.mac_ctrl = priv->mac_ctrl; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int meth_init_tx_ring(struct meth_private *priv) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci /* Init TX ring */ 21262306a36Sopenharmony_ci priv->tx_ring = dma_alloc_coherent(&priv->pdev->dev, 21362306a36Sopenharmony_ci TX_RING_BUFFER_SIZE, &priv->tx_ring_dma, GFP_ATOMIC); 21462306a36Sopenharmony_ci if (!priv->tx_ring) 21562306a36Sopenharmony_ci return -ENOMEM; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci priv->tx_count = priv->tx_read = priv->tx_write = 0; 21862306a36Sopenharmony_ci mace->eth.tx_ring_base = priv->tx_ring_dma; 21962306a36Sopenharmony_ci /* Now init skb save area */ 22062306a36Sopenharmony_ci memset(priv->tx_skbs, 0, sizeof(priv->tx_skbs)); 22162306a36Sopenharmony_ci memset(priv->tx_skb_dmas, 0, sizeof(priv->tx_skb_dmas)); 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic int meth_init_rx_ring(struct meth_private *priv) 22662306a36Sopenharmony_ci{ 22762306a36Sopenharmony_ci int i; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci for (i = 0; i < RX_RING_ENTRIES; i++) { 23062306a36Sopenharmony_ci priv->rx_skbs[i] = alloc_skb(METH_RX_BUFF_SIZE, 0); 23162306a36Sopenharmony_ci /* 8byte status vector + 3quad padding + 2byte padding, 23262306a36Sopenharmony_ci * to put data on 64bit aligned boundary */ 23362306a36Sopenharmony_ci skb_reserve(priv->rx_skbs[i],METH_RX_HEAD); 23462306a36Sopenharmony_ci priv->rx_ring[i]=(rx_packet*)(priv->rx_skbs[i]->head); 23562306a36Sopenharmony_ci /* I'll need to re-sync it after each RX */ 23662306a36Sopenharmony_ci priv->rx_ring_dmas[i] = 23762306a36Sopenharmony_ci dma_map_single(&priv->pdev->dev, priv->rx_ring[i], 23862306a36Sopenharmony_ci METH_RX_BUFF_SIZE, DMA_FROM_DEVICE); 23962306a36Sopenharmony_ci mace->eth.rx_fifo = priv->rx_ring_dmas[i]; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci priv->rx_write = 0; 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci} 24462306a36Sopenharmony_cistatic void meth_free_tx_ring(struct meth_private *priv) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci int i; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* Remove any pending skb */ 24962306a36Sopenharmony_ci for (i = 0; i < TX_RING_ENTRIES; i++) { 25062306a36Sopenharmony_ci dev_kfree_skb(priv->tx_skbs[i]); 25162306a36Sopenharmony_ci priv->tx_skbs[i] = NULL; 25262306a36Sopenharmony_ci } 25362306a36Sopenharmony_ci dma_free_coherent(&priv->pdev->dev, TX_RING_BUFFER_SIZE, priv->tx_ring, 25462306a36Sopenharmony_ci priv->tx_ring_dma); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/* Presumes RX DMA engine is stopped, and RX fifo ring is reset */ 25862306a36Sopenharmony_cistatic void meth_free_rx_ring(struct meth_private *priv) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci int i; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci for (i = 0; i < RX_RING_ENTRIES; i++) { 26362306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, priv->rx_ring_dmas[i], 26462306a36Sopenharmony_ci METH_RX_BUFF_SIZE, DMA_FROM_DEVICE); 26562306a36Sopenharmony_ci priv->rx_ring[i] = 0; 26662306a36Sopenharmony_ci priv->rx_ring_dmas[i] = 0; 26762306a36Sopenharmony_ci kfree_skb(priv->rx_skbs[i]); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciint meth_reset(struct net_device *dev) 27262306a36Sopenharmony_ci{ 27362306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Reset card */ 27662306a36Sopenharmony_ci mace->eth.mac_ctrl = SGI_MAC_RESET; 27762306a36Sopenharmony_ci udelay(1); 27862306a36Sopenharmony_ci mace->eth.mac_ctrl = 0; 27962306a36Sopenharmony_ci udelay(25); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Load ethernet address */ 28262306a36Sopenharmony_ci load_eaddr(dev); 28362306a36Sopenharmony_ci /* Should load some "errata", but later */ 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* Check for device */ 28662306a36Sopenharmony_ci if (mdio_probe(priv) < 0) { 28762306a36Sopenharmony_ci DPRINTK("Unable to find PHY\n"); 28862306a36Sopenharmony_ci return -ENODEV; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci /* Initial mode: 10 | Half-duplex | Accept normal packets */ 29262306a36Sopenharmony_ci priv->mac_ctrl = METH_ACCEPT_MCAST | METH_DEFAULT_IPG; 29362306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) 29462306a36Sopenharmony_ci priv->mac_ctrl |= METH_PROMISC; 29562306a36Sopenharmony_ci mace->eth.mac_ctrl = priv->mac_ctrl; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Autonegotiate speed and duplex mode */ 29862306a36Sopenharmony_ci meth_check_link(dev); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Now set dma control, but don't enable DMA, yet */ 30162306a36Sopenharmony_ci priv->dma_ctrl = (4 << METH_RX_OFFSET_SHIFT) | 30262306a36Sopenharmony_ci (RX_RING_ENTRIES << METH_RX_DEPTH_SHIFT); 30362306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/*============End Helper Routines=====================*/ 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci/* 31162306a36Sopenharmony_ci * Open and close 31262306a36Sopenharmony_ci */ 31362306a36Sopenharmony_cistatic int meth_open(struct net_device *dev) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 31662306a36Sopenharmony_ci int ret; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci priv->phy_addr = -1; /* No PHY is known yet... */ 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Initialize the hardware */ 32162306a36Sopenharmony_ci ret = meth_reset(dev); 32262306a36Sopenharmony_ci if (ret < 0) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Allocate the ring buffers */ 32662306a36Sopenharmony_ci ret = meth_init_tx_ring(priv); 32762306a36Sopenharmony_ci if (ret < 0) 32862306a36Sopenharmony_ci return ret; 32962306a36Sopenharmony_ci ret = meth_init_rx_ring(priv); 33062306a36Sopenharmony_ci if (ret < 0) 33162306a36Sopenharmony_ci goto out_free_tx_ring; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci ret = request_irq(dev->irq, meth_interrupt, 0, meth_str, dev); 33462306a36Sopenharmony_ci if (ret) { 33562306a36Sopenharmony_ci printk(KERN_ERR "%s: Can't get irq %d\n", dev->name, dev->irq); 33662306a36Sopenharmony_ci goto out_free_rx_ring; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Start DMA */ 34062306a36Sopenharmony_ci priv->dma_ctrl |= METH_DMA_TX_EN | /*METH_DMA_TX_INT_EN |*/ 34162306a36Sopenharmony_ci METH_DMA_RX_EN | METH_DMA_RX_INT_EN; 34262306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci DPRINTK("About to start queue\n"); 34562306a36Sopenharmony_ci netif_start_queue(dev); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciout_free_rx_ring: 35062306a36Sopenharmony_ci meth_free_rx_ring(priv); 35162306a36Sopenharmony_ciout_free_tx_ring: 35262306a36Sopenharmony_ci meth_free_tx_ring(priv); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return ret; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_cistatic int meth_release(struct net_device *dev) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci DPRINTK("Stopping queue\n"); 36262306a36Sopenharmony_ci netif_stop_queue(dev); /* can't transmit any more */ 36362306a36Sopenharmony_ci /* shut down DMA */ 36462306a36Sopenharmony_ci priv->dma_ctrl &= ~(METH_DMA_TX_EN | METH_DMA_TX_INT_EN | 36562306a36Sopenharmony_ci METH_DMA_RX_EN | METH_DMA_RX_INT_EN); 36662306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 36762306a36Sopenharmony_ci free_irq(dev->irq, dev); 36862306a36Sopenharmony_ci meth_free_tx_ring(priv); 36962306a36Sopenharmony_ci meth_free_rx_ring(priv); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci return 0; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci/* 37562306a36Sopenharmony_ci * Receive a packet: retrieve, encapsulate and pass over to upper levels 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_cistatic void meth_rx(struct net_device* dev, unsigned long int_status) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct sk_buff *skb; 38062306a36Sopenharmony_ci unsigned long status, flags; 38162306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 38262306a36Sopenharmony_ci unsigned long fifo_rptr = (int_status & METH_INT_RX_RPTR_MASK) >> 8; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 38562306a36Sopenharmony_ci priv->dma_ctrl &= ~METH_DMA_RX_INT_EN; 38662306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 38762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (int_status & METH_INT_RX_UNDERFLOW) { 39062306a36Sopenharmony_ci fifo_rptr = (fifo_rptr - 1) & 0x0f; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci while (priv->rx_write != fifo_rptr) { 39362306a36Sopenharmony_ci dma_unmap_single(&priv->pdev->dev, 39462306a36Sopenharmony_ci priv->rx_ring_dmas[priv->rx_write], 39562306a36Sopenharmony_ci METH_RX_BUFF_SIZE, DMA_FROM_DEVICE); 39662306a36Sopenharmony_ci status = priv->rx_ring[priv->rx_write]->status.raw; 39762306a36Sopenharmony_ci#if MFE_DEBUG 39862306a36Sopenharmony_ci if (!(status & METH_RX_ST_VALID)) { 39962306a36Sopenharmony_ci DPRINTK("Not received? status=%016lx\n",status); 40062306a36Sopenharmony_ci } 40162306a36Sopenharmony_ci#endif 40262306a36Sopenharmony_ci if ((!(status & METH_RX_STATUS_ERRORS)) && (status & METH_RX_ST_VALID)) { 40362306a36Sopenharmony_ci int len = (status & 0xffff) - 4; /* omit CRC */ 40462306a36Sopenharmony_ci /* length sanity check */ 40562306a36Sopenharmony_ci if (len < 60 || len > 1518) { 40662306a36Sopenharmony_ci printk(KERN_DEBUG "%s: bogus packet size: %ld, status=%#2Lx.\n", 40762306a36Sopenharmony_ci dev->name, priv->rx_write, 40862306a36Sopenharmony_ci priv->rx_ring[priv->rx_write]->status.raw); 40962306a36Sopenharmony_ci dev->stats.rx_errors++; 41062306a36Sopenharmony_ci dev->stats.rx_length_errors++; 41162306a36Sopenharmony_ci skb = priv->rx_skbs[priv->rx_write]; 41262306a36Sopenharmony_ci } else { 41362306a36Sopenharmony_ci skb = alloc_skb(METH_RX_BUFF_SIZE, GFP_ATOMIC); 41462306a36Sopenharmony_ci if (!skb) { 41562306a36Sopenharmony_ci /* Ouch! No memory! Drop packet on the floor */ 41662306a36Sopenharmony_ci DPRINTK("No mem: dropping packet\n"); 41762306a36Sopenharmony_ci dev->stats.rx_dropped++; 41862306a36Sopenharmony_ci skb = priv->rx_skbs[priv->rx_write]; 41962306a36Sopenharmony_ci } else { 42062306a36Sopenharmony_ci struct sk_buff *skb_c = priv->rx_skbs[priv->rx_write]; 42162306a36Sopenharmony_ci /* 8byte status vector + 3quad padding + 2byte padding, 42262306a36Sopenharmony_ci * to put data on 64bit aligned boundary */ 42362306a36Sopenharmony_ci skb_reserve(skb, METH_RX_HEAD); 42462306a36Sopenharmony_ci /* Write metadata, and then pass to the receive level */ 42562306a36Sopenharmony_ci skb_put(skb_c, len); 42662306a36Sopenharmony_ci priv->rx_skbs[priv->rx_write] = skb; 42762306a36Sopenharmony_ci skb_c->protocol = eth_type_trans(skb_c, dev); 42862306a36Sopenharmony_ci dev->stats.rx_packets++; 42962306a36Sopenharmony_ci dev->stats.rx_bytes += len; 43062306a36Sopenharmony_ci netif_rx(skb_c); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci } else { 43462306a36Sopenharmony_ci dev->stats.rx_errors++; 43562306a36Sopenharmony_ci skb=priv->rx_skbs[priv->rx_write]; 43662306a36Sopenharmony_ci#if MFE_DEBUG>0 43762306a36Sopenharmony_ci printk(KERN_WARNING "meth: RX error: status=0x%016lx\n",status); 43862306a36Sopenharmony_ci if(status&METH_RX_ST_RCV_CODE_VIOLATION) 43962306a36Sopenharmony_ci printk(KERN_WARNING "Receive Code Violation\n"); 44062306a36Sopenharmony_ci if(status&METH_RX_ST_CRC_ERR) 44162306a36Sopenharmony_ci printk(KERN_WARNING "CRC error\n"); 44262306a36Sopenharmony_ci if(status&METH_RX_ST_INV_PREAMBLE_CTX) 44362306a36Sopenharmony_ci printk(KERN_WARNING "Invalid Preamble Context\n"); 44462306a36Sopenharmony_ci if(status&METH_RX_ST_LONG_EVT_SEEN) 44562306a36Sopenharmony_ci printk(KERN_WARNING "Long Event Seen...\n"); 44662306a36Sopenharmony_ci if(status&METH_RX_ST_BAD_PACKET) 44762306a36Sopenharmony_ci printk(KERN_WARNING "Bad Packet\n"); 44862306a36Sopenharmony_ci if(status&METH_RX_ST_CARRIER_EVT_SEEN) 44962306a36Sopenharmony_ci printk(KERN_WARNING "Carrier Event Seen\n"); 45062306a36Sopenharmony_ci#endif 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci priv->rx_ring[priv->rx_write] = (rx_packet*)skb->head; 45362306a36Sopenharmony_ci priv->rx_ring[priv->rx_write]->status.raw = 0; 45462306a36Sopenharmony_ci priv->rx_ring_dmas[priv->rx_write] = 45562306a36Sopenharmony_ci dma_map_single(&priv->pdev->dev, 45662306a36Sopenharmony_ci priv->rx_ring[priv->rx_write], 45762306a36Sopenharmony_ci METH_RX_BUFF_SIZE, DMA_FROM_DEVICE); 45862306a36Sopenharmony_ci mace->eth.rx_fifo = priv->rx_ring_dmas[priv->rx_write]; 45962306a36Sopenharmony_ci ADVANCE_RX_PTR(priv->rx_write); 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 46262306a36Sopenharmony_ci /* In case there was underflow, and Rx DMA was disabled */ 46362306a36Sopenharmony_ci priv->dma_ctrl |= METH_DMA_RX_INT_EN | METH_DMA_RX_EN; 46462306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 46562306a36Sopenharmony_ci mace->eth.int_stat = METH_INT_RX_THRESHOLD; 46662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic int meth_tx_full(struct net_device *dev) 47062306a36Sopenharmony_ci{ 47162306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci return priv->tx_count >= TX_RING_ENTRIES - 1; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic void meth_tx_cleanup(struct net_device* dev, unsigned long int_status) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 47962306a36Sopenharmony_ci unsigned long status, flags; 48062306a36Sopenharmony_ci struct sk_buff *skb; 48162306a36Sopenharmony_ci unsigned long rptr = (int_status&TX_INFO_RPTR) >> 16; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci /* Stop DMA notification */ 48662306a36Sopenharmony_ci priv->dma_ctrl &= ~(METH_DMA_TX_INT_EN); 48762306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci while (priv->tx_read != rptr) { 49062306a36Sopenharmony_ci skb = priv->tx_skbs[priv->tx_read]; 49162306a36Sopenharmony_ci status = priv->tx_ring[priv->tx_read].header.raw; 49262306a36Sopenharmony_ci#if MFE_DEBUG>=1 49362306a36Sopenharmony_ci if (priv->tx_read == priv->tx_write) 49462306a36Sopenharmony_ci DPRINTK("Auchi! tx_read=%d,tx_write=%d,rptr=%d?\n", priv->tx_read, priv->tx_write,rptr); 49562306a36Sopenharmony_ci#endif 49662306a36Sopenharmony_ci if (status & METH_TX_ST_DONE) { 49762306a36Sopenharmony_ci if (status & METH_TX_ST_SUCCESS){ 49862306a36Sopenharmony_ci dev->stats.tx_packets++; 49962306a36Sopenharmony_ci dev->stats.tx_bytes += skb->len; 50062306a36Sopenharmony_ci } else { 50162306a36Sopenharmony_ci dev->stats.tx_errors++; 50262306a36Sopenharmony_ci#if MFE_DEBUG>=1 50362306a36Sopenharmony_ci DPRINTK("TX error: status=%016lx <",status); 50462306a36Sopenharmony_ci if(status & METH_TX_ST_SUCCESS) 50562306a36Sopenharmony_ci printk(" SUCCESS"); 50662306a36Sopenharmony_ci if(status & METH_TX_ST_TOOLONG) 50762306a36Sopenharmony_ci printk(" TOOLONG"); 50862306a36Sopenharmony_ci if(status & METH_TX_ST_UNDERRUN) 50962306a36Sopenharmony_ci printk(" UNDERRUN"); 51062306a36Sopenharmony_ci if(status & METH_TX_ST_EXCCOLL) 51162306a36Sopenharmony_ci printk(" EXCCOLL"); 51262306a36Sopenharmony_ci if(status & METH_TX_ST_DEFER) 51362306a36Sopenharmony_ci printk(" DEFER"); 51462306a36Sopenharmony_ci if(status & METH_TX_ST_LATECOLL) 51562306a36Sopenharmony_ci printk(" LATECOLL"); 51662306a36Sopenharmony_ci printk(" >\n"); 51762306a36Sopenharmony_ci#endif 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } else { 52062306a36Sopenharmony_ci DPRINTK("RPTR points us here, but packet not done?\n"); 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci dev_consume_skb_irq(skb); 52462306a36Sopenharmony_ci priv->tx_skbs[priv->tx_read] = NULL; 52562306a36Sopenharmony_ci priv->tx_ring[priv->tx_read].header.raw = 0; 52662306a36Sopenharmony_ci priv->tx_read = (priv->tx_read+1)&(TX_RING_ENTRIES-1); 52762306a36Sopenharmony_ci priv->tx_count--; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci /* wake up queue if it was stopped */ 53162306a36Sopenharmony_ci if (netif_queue_stopped(dev) && !meth_tx_full(dev)) { 53262306a36Sopenharmony_ci netif_wake_queue(dev); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci mace->eth.int_stat = METH_INT_TX_EMPTY | METH_INT_TX_PKT; 53662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic void meth_error(struct net_device* dev, unsigned status) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 54262306a36Sopenharmony_ci unsigned long flags; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci printk(KERN_WARNING "meth: error status: 0x%08x\n",status); 54562306a36Sopenharmony_ci /* check for errors too... */ 54662306a36Sopenharmony_ci if (status & (METH_INT_TX_LINK_FAIL)) 54762306a36Sopenharmony_ci printk(KERN_WARNING "meth: link failure\n"); 54862306a36Sopenharmony_ci /* Should I do full reset in this case? */ 54962306a36Sopenharmony_ci if (status & (METH_INT_MEM_ERROR)) 55062306a36Sopenharmony_ci printk(KERN_WARNING "meth: memory error\n"); 55162306a36Sopenharmony_ci if (status & (METH_INT_TX_ABORT)) 55262306a36Sopenharmony_ci printk(KERN_WARNING "meth: aborted\n"); 55362306a36Sopenharmony_ci if (status & (METH_INT_RX_OVERFLOW)) 55462306a36Sopenharmony_ci printk(KERN_WARNING "meth: Rx overflow\n"); 55562306a36Sopenharmony_ci if (status & (METH_INT_RX_UNDERFLOW)) { 55662306a36Sopenharmony_ci printk(KERN_WARNING "meth: Rx underflow\n"); 55762306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 55862306a36Sopenharmony_ci mace->eth.int_stat = METH_INT_RX_UNDERFLOW; 55962306a36Sopenharmony_ci /* more underflow interrupts will be delivered, 56062306a36Sopenharmony_ci * effectively throwing us into an infinite loop. 56162306a36Sopenharmony_ci * Thus I stop processing Rx in this case. */ 56262306a36Sopenharmony_ci priv->dma_ctrl &= ~METH_DMA_RX_EN; 56362306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 56462306a36Sopenharmony_ci DPRINTK("Disabled meth Rx DMA temporarily\n"); 56562306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci mace->eth.int_stat = METH_INT_ERROR; 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci/* 57162306a36Sopenharmony_ci * The typical interrupt entry point 57262306a36Sopenharmony_ci */ 57362306a36Sopenharmony_cistatic irqreturn_t meth_interrupt(int irq, void *dev_id) 57462306a36Sopenharmony_ci{ 57562306a36Sopenharmony_ci struct net_device *dev = (struct net_device *)dev_id; 57662306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 57762306a36Sopenharmony_ci unsigned long status; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci status = mace->eth.int_stat; 58062306a36Sopenharmony_ci while (status & 0xff) { 58162306a36Sopenharmony_ci /* First handle errors - if we get Rx underflow, 58262306a36Sopenharmony_ci * Rx DMA will be disabled, and Rx handler will reenable 58362306a36Sopenharmony_ci * it. I don't think it's possible to get Rx underflow, 58462306a36Sopenharmony_ci * without getting Rx interrupt */ 58562306a36Sopenharmony_ci if (status & METH_INT_ERROR) { 58662306a36Sopenharmony_ci meth_error(dev, status); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci if (status & (METH_INT_TX_EMPTY | METH_INT_TX_PKT)) { 58962306a36Sopenharmony_ci /* a transmission is over: free the skb */ 59062306a36Sopenharmony_ci meth_tx_cleanup(dev, status); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci if (status & METH_INT_RX_THRESHOLD) { 59362306a36Sopenharmony_ci if (!(priv->dma_ctrl & METH_DMA_RX_INT_EN)) 59462306a36Sopenharmony_ci break; 59562306a36Sopenharmony_ci /* send it to meth_rx for handling */ 59662306a36Sopenharmony_ci meth_rx(dev, status); 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci status = mace->eth.int_stat; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return IRQ_HANDLED; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci/* 60562306a36Sopenharmony_ci * Transmits packets that fit into TX descriptor (are <=120B) 60662306a36Sopenharmony_ci */ 60762306a36Sopenharmony_cistatic void meth_tx_short_prepare(struct meth_private *priv, 60862306a36Sopenharmony_ci struct sk_buff *skb) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci tx_packet *desc = &priv->tx_ring[priv->tx_write]; 61162306a36Sopenharmony_ci int len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci desc->header.raw = METH_TX_CMD_INT_EN | (len-1) | ((128-len) << 16); 61462306a36Sopenharmony_ci /* maybe I should set whole thing to 0 first... */ 61562306a36Sopenharmony_ci skb_copy_from_linear_data(skb, desc->data.dt + (120 - len), skb->len); 61662306a36Sopenharmony_ci if (skb->len < len) 61762306a36Sopenharmony_ci memset(desc->data.dt + 120 - len + skb->len, 0, len-skb->len); 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci#define TX_CATBUF1 BIT(25) 62062306a36Sopenharmony_cistatic void meth_tx_1page_prepare(struct meth_private *priv, 62162306a36Sopenharmony_ci struct sk_buff *skb) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci tx_packet *desc = &priv->tx_ring[priv->tx_write]; 62462306a36Sopenharmony_ci void *buffer_data = (void *)(((unsigned long)skb->data + 7) & ~7); 62562306a36Sopenharmony_ci int unaligned_len = (int)((unsigned long)buffer_data - (unsigned long)skb->data); 62662306a36Sopenharmony_ci int buffer_len = skb->len - unaligned_len; 62762306a36Sopenharmony_ci dma_addr_t catbuf; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | (skb->len - 1); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* unaligned part */ 63262306a36Sopenharmony_ci if (unaligned_len) { 63362306a36Sopenharmony_ci skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), 63462306a36Sopenharmony_ci unaligned_len); 63562306a36Sopenharmony_ci desc->header.raw |= (128 - unaligned_len) << 16; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci /* first page */ 63962306a36Sopenharmony_ci catbuf = dma_map_single(&priv->pdev->dev, buffer_data, buffer_len, 64062306a36Sopenharmony_ci DMA_TO_DEVICE); 64162306a36Sopenharmony_ci desc->data.cat_buf[0].form.start_addr = catbuf >> 3; 64262306a36Sopenharmony_ci desc->data.cat_buf[0].form.len = buffer_len - 1; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci#define TX_CATBUF2 BIT(26) 64562306a36Sopenharmony_cistatic void meth_tx_2page_prepare(struct meth_private *priv, 64662306a36Sopenharmony_ci struct sk_buff *skb) 64762306a36Sopenharmony_ci{ 64862306a36Sopenharmony_ci tx_packet *desc = &priv->tx_ring[priv->tx_write]; 64962306a36Sopenharmony_ci void *buffer1_data = (void *)(((unsigned long)skb->data + 7) & ~7); 65062306a36Sopenharmony_ci void *buffer2_data = (void *)PAGE_ALIGN((unsigned long)skb->data); 65162306a36Sopenharmony_ci int unaligned_len = (int)((unsigned long)buffer1_data - (unsigned long)skb->data); 65262306a36Sopenharmony_ci int buffer1_len = (int)((unsigned long)buffer2_data - (unsigned long)buffer1_data); 65362306a36Sopenharmony_ci int buffer2_len = skb->len - buffer1_len - unaligned_len; 65462306a36Sopenharmony_ci dma_addr_t catbuf1, catbuf2; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | TX_CATBUF2| (skb->len - 1); 65762306a36Sopenharmony_ci /* unaligned part */ 65862306a36Sopenharmony_ci if (unaligned_len){ 65962306a36Sopenharmony_ci skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), 66062306a36Sopenharmony_ci unaligned_len); 66162306a36Sopenharmony_ci desc->header.raw |= (128 - unaligned_len) << 16; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci /* first page */ 66562306a36Sopenharmony_ci catbuf1 = dma_map_single(&priv->pdev->dev, buffer1_data, buffer1_len, 66662306a36Sopenharmony_ci DMA_TO_DEVICE); 66762306a36Sopenharmony_ci desc->data.cat_buf[0].form.start_addr = catbuf1 >> 3; 66862306a36Sopenharmony_ci desc->data.cat_buf[0].form.len = buffer1_len - 1; 66962306a36Sopenharmony_ci /* second page */ 67062306a36Sopenharmony_ci catbuf2 = dma_map_single(&priv->pdev->dev, buffer2_data, buffer2_len, 67162306a36Sopenharmony_ci DMA_TO_DEVICE); 67262306a36Sopenharmony_ci desc->data.cat_buf[1].form.start_addr = catbuf2 >> 3; 67362306a36Sopenharmony_ci desc->data.cat_buf[1].form.len = buffer2_len - 1; 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cistatic void meth_add_to_tx_ring(struct meth_private *priv, struct sk_buff *skb) 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci /* Remember the skb, so we can free it at interrupt time */ 67962306a36Sopenharmony_ci priv->tx_skbs[priv->tx_write] = skb; 68062306a36Sopenharmony_ci if (skb->len <= 120) { 68162306a36Sopenharmony_ci /* Whole packet fits into descriptor */ 68262306a36Sopenharmony_ci meth_tx_short_prepare(priv, skb); 68362306a36Sopenharmony_ci } else if (PAGE_ALIGN((unsigned long)skb->data) != 68462306a36Sopenharmony_ci PAGE_ALIGN((unsigned long)skb->data + skb->len - 1)) { 68562306a36Sopenharmony_ci /* Packet crosses page boundary */ 68662306a36Sopenharmony_ci meth_tx_2page_prepare(priv, skb); 68762306a36Sopenharmony_ci } else { 68862306a36Sopenharmony_ci /* Packet is in one page */ 68962306a36Sopenharmony_ci meth_tx_1page_prepare(priv, skb); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci priv->tx_write = (priv->tx_write + 1) & (TX_RING_ENTRIES - 1); 69262306a36Sopenharmony_ci mace->eth.tx_info = priv->tx_write; 69362306a36Sopenharmony_ci priv->tx_count++; 69462306a36Sopenharmony_ci} 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci/* 69762306a36Sopenharmony_ci * Transmit a packet (called by the kernel) 69862306a36Sopenharmony_ci */ 69962306a36Sopenharmony_cistatic netdev_tx_t meth_tx(struct sk_buff *skb, struct net_device *dev) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 70262306a36Sopenharmony_ci unsigned long flags; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 70562306a36Sopenharmony_ci /* Stop DMA notification */ 70662306a36Sopenharmony_ci priv->dma_ctrl &= ~(METH_DMA_TX_INT_EN); 70762306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci meth_add_to_tx_ring(priv, skb); 71062306a36Sopenharmony_ci netif_trans_update(dev); /* save the timestamp */ 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* If TX ring is full, tell the upper layer to stop sending packets */ 71362306a36Sopenharmony_ci if (meth_tx_full(dev)) { 71462306a36Sopenharmony_ci printk(KERN_DEBUG "TX full: stopping\n"); 71562306a36Sopenharmony_ci netif_stop_queue(dev); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* Restart DMA notification */ 71962306a36Sopenharmony_ci priv->dma_ctrl |= METH_DMA_TX_INT_EN; 72062306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci return NETDEV_TX_OK; 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci/* 72862306a36Sopenharmony_ci * Deal with a transmit timeout. 72962306a36Sopenharmony_ci */ 73062306a36Sopenharmony_cistatic void meth_tx_timeout(struct net_device *dev, unsigned int txqueue) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 73362306a36Sopenharmony_ci unsigned long flags; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci printk(KERN_WARNING "%s: transmit timed out\n", dev->name); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci /* Protect against concurrent rx interrupts */ 73862306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock,flags); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Try to reset the interface. */ 74162306a36Sopenharmony_ci meth_reset(dev); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci dev->stats.tx_errors++; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Clear all rings */ 74662306a36Sopenharmony_ci meth_free_tx_ring(priv); 74762306a36Sopenharmony_ci meth_free_rx_ring(priv); 74862306a36Sopenharmony_ci meth_init_tx_ring(priv); 74962306a36Sopenharmony_ci meth_init_rx_ring(priv); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci /* Restart dma */ 75262306a36Sopenharmony_ci priv->dma_ctrl |= METH_DMA_TX_EN | METH_DMA_RX_EN | METH_DMA_RX_INT_EN; 75362306a36Sopenharmony_ci mace->eth.dma_ctrl = priv->dma_ctrl; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* Enable interrupt */ 75662306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 75962306a36Sopenharmony_ci netif_wake_queue(dev); 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci/* 76362306a36Sopenharmony_ci * Ioctl commands 76462306a36Sopenharmony_ci */ 76562306a36Sopenharmony_cistatic int meth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci /* XXX Not yet implemented */ 76862306a36Sopenharmony_ci switch(cmd) { 76962306a36Sopenharmony_ci case SIOCGMIIPHY: 77062306a36Sopenharmony_ci case SIOCGMIIREG: 77162306a36Sopenharmony_ci case SIOCSMIIREG: 77262306a36Sopenharmony_ci default: 77362306a36Sopenharmony_ci return -EOPNOTSUPP; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci} 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cistatic void meth_set_rx_mode(struct net_device *dev) 77862306a36Sopenharmony_ci{ 77962306a36Sopenharmony_ci struct meth_private *priv = netdev_priv(dev); 78062306a36Sopenharmony_ci unsigned long flags; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci netif_stop_queue(dev); 78362306a36Sopenharmony_ci spin_lock_irqsave(&priv->meth_lock, flags); 78462306a36Sopenharmony_ci priv->mac_ctrl &= ~METH_PROMISC; 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 78762306a36Sopenharmony_ci priv->mac_ctrl |= METH_PROMISC; 78862306a36Sopenharmony_ci priv->mcast_filter = 0xffffffffffffffffUL; 78962306a36Sopenharmony_ci } else if ((netdev_mc_count(dev) > METH_MCF_LIMIT) || 79062306a36Sopenharmony_ci (dev->flags & IFF_ALLMULTI)) { 79162306a36Sopenharmony_ci priv->mac_ctrl |= METH_ACCEPT_AMCAST; 79262306a36Sopenharmony_ci priv->mcast_filter = 0xffffffffffffffffUL; 79362306a36Sopenharmony_ci } else { 79462306a36Sopenharmony_ci struct netdev_hw_addr *ha; 79562306a36Sopenharmony_ci priv->mac_ctrl |= METH_ACCEPT_MCAST; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) 79862306a36Sopenharmony_ci set_bit((ether_crc(ETH_ALEN, ha->addr) >> 26), 79962306a36Sopenharmony_ci (volatile unsigned long *)&priv->mcast_filter); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* Write the changes to the chip registers. */ 80362306a36Sopenharmony_ci mace->eth.mac_ctrl = priv->mac_ctrl; 80462306a36Sopenharmony_ci mace->eth.mcast_filter = priv->mcast_filter; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Done! */ 80762306a36Sopenharmony_ci spin_unlock_irqrestore(&priv->meth_lock, flags); 80862306a36Sopenharmony_ci netif_wake_queue(dev); 80962306a36Sopenharmony_ci} 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_cistatic const struct net_device_ops meth_netdev_ops = { 81262306a36Sopenharmony_ci .ndo_open = meth_open, 81362306a36Sopenharmony_ci .ndo_stop = meth_release, 81462306a36Sopenharmony_ci .ndo_start_xmit = meth_tx, 81562306a36Sopenharmony_ci .ndo_eth_ioctl = meth_ioctl, 81662306a36Sopenharmony_ci .ndo_tx_timeout = meth_tx_timeout, 81762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 81862306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 81962306a36Sopenharmony_ci .ndo_set_rx_mode = meth_set_rx_mode, 82062306a36Sopenharmony_ci}; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci/* 82362306a36Sopenharmony_ci * The init function. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_cistatic int meth_probe(struct platform_device *pdev) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct net_device *dev; 82862306a36Sopenharmony_ci struct meth_private *priv; 82962306a36Sopenharmony_ci int err; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct meth_private)); 83262306a36Sopenharmony_ci if (!dev) 83362306a36Sopenharmony_ci return -ENOMEM; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci dev->netdev_ops = &meth_netdev_ops; 83662306a36Sopenharmony_ci dev->watchdog_timeo = timeout; 83762306a36Sopenharmony_ci dev->irq = MACE_ETHERNET_IRQ; 83862306a36Sopenharmony_ci dev->base_addr = (unsigned long)&mace->eth; 83962306a36Sopenharmony_ci eth_hw_addr_set(dev, o2meth_eaddr); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci priv = netdev_priv(dev); 84262306a36Sopenharmony_ci priv->pdev = pdev; 84362306a36Sopenharmony_ci spin_lock_init(&priv->meth_lock); 84462306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci err = register_netdev(dev); 84762306a36Sopenharmony_ci if (err) { 84862306a36Sopenharmony_ci free_netdev(dev); 84962306a36Sopenharmony_ci return err; 85062306a36Sopenharmony_ci } 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci printk(KERN_INFO "%s: SGI MACE Ethernet rev. %d\n", 85362306a36Sopenharmony_ci dev->name, (unsigned int)(mace->eth.mac_ctrl >> 29)); 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int meth_remove(struct platform_device *pdev) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci unregister_netdev(dev); 86262306a36Sopenharmony_ci free_netdev(dev); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic struct platform_driver meth_driver = { 86862306a36Sopenharmony_ci .probe = meth_probe, 86962306a36Sopenharmony_ci .remove = meth_remove, 87062306a36Sopenharmony_ci .driver = { 87162306a36Sopenharmony_ci .name = "meth", 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci}; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cimodule_platform_driver(meth_driver); 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ciMODULE_AUTHOR("Ilya Volynets <ilya@theIlya.com>"); 87862306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI O2 Builtin Fast Ethernet driver"); 87962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 88062306a36Sopenharmony_ciMODULE_ALIAS("platform:meth"); 881