162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Driver for SGI's IOC3 based Ethernet cards as found in the PCI card. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 1999, 2000, 01, 03, 06 Ralf Baechle 562306a36Sopenharmony_ci * Copyright (C) 1995, 1999, 2000, 2001 by Silicon Graphics, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * References: 862306a36Sopenharmony_ci * o IOC3 ASIC specification 4.51, 1996-04-18 962306a36Sopenharmony_ci * o IEEE 802.3 specification, 2000 edition 1062306a36Sopenharmony_ci * o DP38840A Specification, National Semiconductor, March 1997 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * To do: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * o Use prefetching for large packets. What is a good lower limit for 1562306a36Sopenharmony_ci * prefetching? 1662306a36Sopenharmony_ci * o Use hardware checksums. 1762306a36Sopenharmony_ci * o Which PHYs might possibly be attached to the IOC3 in real live, 1862306a36Sopenharmony_ci * which workarounds are required for them? Do we ever have Lucent's? 1962306a36Sopenharmony_ci * o For the 2.5 branch kill the mii-tool ioctls. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define IOC3_NAME "ioc3-eth" 2362306a36Sopenharmony_ci#define IOC3_VERSION "2.6.3-4" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <linux/delay.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/mm.h> 2862306a36Sopenharmony_ci#include <linux/errno.h> 2962306a36Sopenharmony_ci#include <linux/module.h> 3062306a36Sopenharmony_ci#include <linux/init.h> 3162306a36Sopenharmony_ci#include <linux/crc16.h> 3262306a36Sopenharmony_ci#include <linux/crc32.h> 3362306a36Sopenharmony_ci#include <linux/mii.h> 3462306a36Sopenharmony_ci#include <linux/in.h> 3562306a36Sopenharmony_ci#include <linux/io.h> 3662306a36Sopenharmony_ci#include <linux/ip.h> 3762306a36Sopenharmony_ci#include <linux/tcp.h> 3862306a36Sopenharmony_ci#include <linux/udp.h> 3962306a36Sopenharmony_ci#include <linux/gfp.h> 4062306a36Sopenharmony_ci#include <linux/netdevice.h> 4162306a36Sopenharmony_ci#include <linux/etherdevice.h> 4262306a36Sopenharmony_ci#include <linux/ethtool.h> 4362306a36Sopenharmony_ci#include <linux/skbuff.h> 4462306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4562306a36Sopenharmony_ci#include <linux/platform_device.h> 4662306a36Sopenharmony_ci#include <linux/nvmem-consumer.h> 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#include <net/ip.h> 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#include <asm/sn/ioc3.h> 5162306a36Sopenharmony_ci#include <asm/pci/bridge.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define CRC16_INIT 0 5462306a36Sopenharmony_ci#define CRC16_VALID 0xb001 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci/* Number of RX buffers. This is tunable in the range of 16 <= x < 512. 5762306a36Sopenharmony_ci * The value must be a power of two. 5862306a36Sopenharmony_ci */ 5962306a36Sopenharmony_ci#define RX_BUFFS 64 6062306a36Sopenharmony_ci#define RX_RING_ENTRIES 512 /* fixed in hardware */ 6162306a36Sopenharmony_ci#define RX_RING_MASK (RX_RING_ENTRIES - 1) 6262306a36Sopenharmony_ci#define RX_RING_SIZE (RX_RING_ENTRIES * sizeof(u64)) 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 128 TX buffers (not tunable) */ 6562306a36Sopenharmony_ci#define TX_RING_ENTRIES 128 6662306a36Sopenharmony_ci#define TX_RING_MASK (TX_RING_ENTRIES - 1) 6762306a36Sopenharmony_ci#define TX_RING_SIZE (TX_RING_ENTRIES * sizeof(struct ioc3_etxd)) 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/* IOC3 does dma transfers in 128 byte blocks */ 7062306a36Sopenharmony_ci#define IOC3_DMA_XFER_LEN 128UL 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Every RX buffer starts with 8 byte descriptor data */ 7362306a36Sopenharmony_ci#define RX_OFFSET (sizeof(struct ioc3_erxbuf) + NET_IP_ALIGN) 7462306a36Sopenharmony_ci#define RX_BUF_SIZE (13 * IOC3_DMA_XFER_LEN) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define ETCSR_FD ((21 << ETCSR_IPGR2_SHIFT) | (21 << ETCSR_IPGR1_SHIFT) | 21) 7762306a36Sopenharmony_ci#define ETCSR_HD ((17 << ETCSR_IPGR2_SHIFT) | (11 << ETCSR_IPGR1_SHIFT) | 21) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci/* Private per NIC data of the driver. */ 8062306a36Sopenharmony_cistruct ioc3_private { 8162306a36Sopenharmony_ci struct ioc3_ethregs *regs; 8262306a36Sopenharmony_ci struct device *dma_dev; 8362306a36Sopenharmony_ci u32 *ssram; 8462306a36Sopenharmony_ci unsigned long *rxr; /* pointer to receiver ring */ 8562306a36Sopenharmony_ci void *tx_ring; 8662306a36Sopenharmony_ci struct ioc3_etxd *txr; 8762306a36Sopenharmony_ci dma_addr_t rxr_dma; 8862306a36Sopenharmony_ci dma_addr_t txr_dma; 8962306a36Sopenharmony_ci struct sk_buff *rx_skbs[RX_RING_ENTRIES]; 9062306a36Sopenharmony_ci struct sk_buff *tx_skbs[TX_RING_ENTRIES]; 9162306a36Sopenharmony_ci int rx_ci; /* RX consumer index */ 9262306a36Sopenharmony_ci int rx_pi; /* RX producer index */ 9362306a36Sopenharmony_ci int tx_ci; /* TX consumer index */ 9462306a36Sopenharmony_ci int tx_pi; /* TX producer index */ 9562306a36Sopenharmony_ci int txqlen; 9662306a36Sopenharmony_ci u32 emcr, ehar_h, ehar_l; 9762306a36Sopenharmony_ci spinlock_t ioc3_lock; 9862306a36Sopenharmony_ci struct mii_if_info mii; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* Members used by autonegotiation */ 10162306a36Sopenharmony_ci struct timer_list ioc3_timer; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); 10562306a36Sopenharmony_cistatic void ioc3_set_multicast_list(struct net_device *dev); 10662306a36Sopenharmony_cistatic netdev_tx_t ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev); 10762306a36Sopenharmony_cistatic void ioc3_timeout(struct net_device *dev, unsigned int txqueue); 10862306a36Sopenharmony_cistatic inline unsigned int ioc3_hash(const unsigned char *addr); 10962306a36Sopenharmony_cistatic void ioc3_start(struct ioc3_private *ip); 11062306a36Sopenharmony_cistatic inline void ioc3_stop(struct ioc3_private *ip); 11162306a36Sopenharmony_cistatic void ioc3_init(struct net_device *dev); 11262306a36Sopenharmony_cistatic int ioc3_alloc_rx_bufs(struct net_device *dev); 11362306a36Sopenharmony_cistatic void ioc3_free_rx_bufs(struct ioc3_private *ip); 11462306a36Sopenharmony_cistatic inline void ioc3_clean_tx_ring(struct ioc3_private *ip); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct ethtool_ops ioc3_ethtool_ops; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic inline unsigned long aligned_rx_skb_addr(unsigned long addr) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci return (~addr + 1) & (IOC3_DMA_XFER_LEN - 1UL); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic inline int ioc3_alloc_skb(struct ioc3_private *ip, struct sk_buff **skb, 12462306a36Sopenharmony_ci struct ioc3_erxbuf **rxb, dma_addr_t *rxb_dma) 12562306a36Sopenharmony_ci{ 12662306a36Sopenharmony_ci struct sk_buff *new_skb; 12762306a36Sopenharmony_ci dma_addr_t d; 12862306a36Sopenharmony_ci int offset; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci new_skb = alloc_skb(RX_BUF_SIZE + IOC3_DMA_XFER_LEN - 1, GFP_ATOMIC); 13162306a36Sopenharmony_ci if (!new_skb) 13262306a36Sopenharmony_ci return -ENOMEM; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci /* ensure buffer is aligned to IOC3_DMA_XFER_LEN */ 13562306a36Sopenharmony_ci offset = aligned_rx_skb_addr((unsigned long)new_skb->data); 13662306a36Sopenharmony_ci if (offset) 13762306a36Sopenharmony_ci skb_reserve(new_skb, offset); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci d = dma_map_single(ip->dma_dev, new_skb->data, 14062306a36Sopenharmony_ci RX_BUF_SIZE, DMA_FROM_DEVICE); 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci if (dma_mapping_error(ip->dma_dev, d)) { 14362306a36Sopenharmony_ci dev_kfree_skb_any(new_skb); 14462306a36Sopenharmony_ci return -ENOMEM; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci *rxb_dma = d; 14762306a36Sopenharmony_ci *rxb = (struct ioc3_erxbuf *)new_skb->data; 14862306a36Sopenharmony_ci skb_reserve(new_skb, RX_OFFSET); 14962306a36Sopenharmony_ci *skb = new_skb; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#ifdef CONFIG_PCI_XTALK_BRIDGE 15562306a36Sopenharmony_cistatic inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci return (addr & ~PCI64_ATTR_BAR) | attr; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define ERBAR_VAL (ERBAR_BARRIER_BIT << ERBAR_RXBARR_SHIFT) 16162306a36Sopenharmony_ci#else 16262306a36Sopenharmony_cistatic inline unsigned long ioc3_map(dma_addr_t addr, unsigned long attr) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return addr; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci#define ERBAR_VAL 0 16862306a36Sopenharmony_ci#endif 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int ioc3eth_nvmem_match(struct device *dev, const void *data) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci const char *name = dev_name(dev); 17362306a36Sopenharmony_ci const char *prefix = data; 17462306a36Sopenharmony_ci int prefix_len; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci prefix_len = strlen(prefix); 17762306a36Sopenharmony_ci if (strlen(name) < (prefix_len + 3)) 17862306a36Sopenharmony_ci return 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (memcmp(prefix, name, prefix_len) != 0) 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* found nvmem device which is attached to our ioc3 18462306a36Sopenharmony_ci * now check for one wire family code 09, 89 and 91 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (memcmp(name + prefix_len, "09-", 3) == 0) 18762306a36Sopenharmony_ci return 1; 18862306a36Sopenharmony_ci if (memcmp(name + prefix_len, "89-", 3) == 0) 18962306a36Sopenharmony_ci return 1; 19062306a36Sopenharmony_ci if (memcmp(name + prefix_len, "91-", 3) == 0) 19162306a36Sopenharmony_ci return 1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int ioc3eth_get_mac_addr(struct resource *res, u8 mac_addr[6]) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct nvmem_device *nvmem; 19962306a36Sopenharmony_ci char prefix[24]; 20062306a36Sopenharmony_ci u8 prom[16]; 20162306a36Sopenharmony_ci int ret; 20262306a36Sopenharmony_ci int i; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci snprintf(prefix, sizeof(prefix), "ioc3-%012llx-", 20562306a36Sopenharmony_ci res->start & ~0xffff); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci nvmem = nvmem_device_find(prefix, ioc3eth_nvmem_match); 20862306a36Sopenharmony_ci if (IS_ERR(nvmem)) 20962306a36Sopenharmony_ci return PTR_ERR(nvmem); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ret = nvmem_device_read(nvmem, 0, 16, prom); 21262306a36Sopenharmony_ci nvmem_device_put(nvmem); 21362306a36Sopenharmony_ci if (ret < 0) 21462306a36Sopenharmony_ci return ret; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* check, if content is valid */ 21762306a36Sopenharmony_ci if (prom[0] != 0x0a || 21862306a36Sopenharmony_ci crc16(CRC16_INIT, prom, 13) != CRC16_VALID) 21962306a36Sopenharmony_ci return -EINVAL; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci for (i = 0; i < 6; i++) 22262306a36Sopenharmony_ci mac_addr[i] = prom[10 - i]; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return 0; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void __ioc3_set_mac_address(struct net_device *dev) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci writel((dev->dev_addr[5] << 8) | 23262306a36Sopenharmony_ci dev->dev_addr[4], 23362306a36Sopenharmony_ci &ip->regs->emar_h); 23462306a36Sopenharmony_ci writel((dev->dev_addr[3] << 24) | 23562306a36Sopenharmony_ci (dev->dev_addr[2] << 16) | 23662306a36Sopenharmony_ci (dev->dev_addr[1] << 8) | 23762306a36Sopenharmony_ci dev->dev_addr[0], 23862306a36Sopenharmony_ci &ip->regs->emar_l); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic int ioc3_set_mac_address(struct net_device *dev, void *addr) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 24462306a36Sopenharmony_ci struct sockaddr *sa = addr; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci eth_hw_addr_set(dev, sa->sa_data); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 24962306a36Sopenharmony_ci __ioc3_set_mac_address(dev); 25062306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci return 0; 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci/* Caller must hold the ioc3_lock ever for MII readers. This is also 25662306a36Sopenharmony_ci * used to protect the transmitter side but it's low contention. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic int ioc3_mdio_read(struct net_device *dev, int phy, int reg) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 26162306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci while (readl(®s->micr) & MICR_BUSY) 26462306a36Sopenharmony_ci ; 26562306a36Sopenharmony_ci writel((phy << MICR_PHYADDR_SHIFT) | reg | MICR_READTRIG, 26662306a36Sopenharmony_ci ®s->micr); 26762306a36Sopenharmony_ci while (readl(®s->micr) & MICR_BUSY) 26862306a36Sopenharmony_ci ; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return readl(®s->midr_r) & MIDR_DATA_MASK; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic void ioc3_mdio_write(struct net_device *dev, int phy, int reg, int data) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 27662306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci while (readl(®s->micr) & MICR_BUSY) 27962306a36Sopenharmony_ci ; 28062306a36Sopenharmony_ci writel(data, ®s->midr_w); 28162306a36Sopenharmony_ci writel((phy << MICR_PHYADDR_SHIFT) | reg, ®s->micr); 28262306a36Sopenharmony_ci while (readl(®s->micr) & MICR_BUSY) 28362306a36Sopenharmony_ci ; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int ioc3_mii_init(struct ioc3_private *ip); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic struct net_device_stats *ioc3_get_stats(struct net_device *dev) 28962306a36Sopenharmony_ci{ 29062306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 29162306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci dev->stats.collisions += readl(®s->etcdc) & ETCDC_COLLCNT_MASK; 29462306a36Sopenharmony_ci return &dev->stats; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic void ioc3_tcpudp_checksum(struct sk_buff *skb, u32 hwsum, int len) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci struct ethhdr *eh = eth_hdr(skb); 30062306a36Sopenharmony_ci unsigned int proto; 30162306a36Sopenharmony_ci unsigned char *cp; 30262306a36Sopenharmony_ci struct iphdr *ih; 30362306a36Sopenharmony_ci u32 csum, ehsum; 30462306a36Sopenharmony_ci u16 *ew; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Did hardware handle the checksum at all? The cases we can handle 30762306a36Sopenharmony_ci * are: 30862306a36Sopenharmony_ci * 30962306a36Sopenharmony_ci * - TCP and UDP checksums of IPv4 only. 31062306a36Sopenharmony_ci * - IPv6 would be doable but we keep that for later ... 31162306a36Sopenharmony_ci * - Only unfragmented packets. Did somebody already tell you 31262306a36Sopenharmony_ci * fragmentation is evil? 31362306a36Sopenharmony_ci * - don't care about packet size. Worst case when processing a 31462306a36Sopenharmony_ci * malformed packet we'll try to access the packet at ip header + 31562306a36Sopenharmony_ci * 64 bytes which is still inside the skb. Even in the unlikely 31662306a36Sopenharmony_ci * case where the checksum is right the higher layers will still 31762306a36Sopenharmony_ci * drop the packet as appropriate. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci if (eh->h_proto != htons(ETH_P_IP)) 32062306a36Sopenharmony_ci return; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci ih = (struct iphdr *)((char *)eh + ETH_HLEN); 32362306a36Sopenharmony_ci if (ip_is_fragment(ih)) 32462306a36Sopenharmony_ci return; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci proto = ih->protocol; 32762306a36Sopenharmony_ci if (proto != IPPROTO_TCP && proto != IPPROTO_UDP) 32862306a36Sopenharmony_ci return; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Same as tx - compute csum of pseudo header */ 33162306a36Sopenharmony_ci csum = hwsum + 33262306a36Sopenharmony_ci (ih->tot_len - (ih->ihl << 2)) + 33362306a36Sopenharmony_ci htons((u16)ih->protocol) + 33462306a36Sopenharmony_ci (ih->saddr >> 16) + (ih->saddr & 0xffff) + 33562306a36Sopenharmony_ci (ih->daddr >> 16) + (ih->daddr & 0xffff); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Sum up ethernet dest addr, src addr and protocol */ 33862306a36Sopenharmony_ci ew = (u16 *)eh; 33962306a36Sopenharmony_ci ehsum = ew[0] + ew[1] + ew[2] + ew[3] + ew[4] + ew[5] + ew[6]; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci ehsum = (ehsum & 0xffff) + (ehsum >> 16); 34262306a36Sopenharmony_ci ehsum = (ehsum & 0xffff) + (ehsum >> 16); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci csum += 0xffff ^ ehsum; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* In the next step we also subtract the 1's complement 34762306a36Sopenharmony_ci * checksum of the trailing ethernet CRC. 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_ci cp = (char *)eh + len; /* points at trailing CRC */ 35062306a36Sopenharmony_ci if (len & 1) { 35162306a36Sopenharmony_ci csum += 0xffff ^ (u16)((cp[1] << 8) | cp[0]); 35262306a36Sopenharmony_ci csum += 0xffff ^ (u16)((cp[3] << 8) | cp[2]); 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci csum += 0xffff ^ (u16)((cp[0] << 8) | cp[1]); 35562306a36Sopenharmony_ci csum += 0xffff ^ (u16)((cp[2] << 8) | cp[3]); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci csum = (csum & 0xffff) + (csum >> 16); 35962306a36Sopenharmony_ci csum = (csum & 0xffff) + (csum >> 16); 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (csum == 0xffff) 36262306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic inline void ioc3_rx(struct net_device *dev) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 36862306a36Sopenharmony_ci struct sk_buff *skb, *new_skb; 36962306a36Sopenharmony_ci int rx_entry, n_entry, len; 37062306a36Sopenharmony_ci struct ioc3_erxbuf *rxb; 37162306a36Sopenharmony_ci unsigned long *rxr; 37262306a36Sopenharmony_ci dma_addr_t d; 37362306a36Sopenharmony_ci u32 w0, err; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci rxr = ip->rxr; /* Ring base */ 37662306a36Sopenharmony_ci rx_entry = ip->rx_ci; /* RX consume index */ 37762306a36Sopenharmony_ci n_entry = ip->rx_pi; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci skb = ip->rx_skbs[rx_entry]; 38062306a36Sopenharmony_ci rxb = (struct ioc3_erxbuf *)(skb->data - RX_OFFSET); 38162306a36Sopenharmony_ci w0 = be32_to_cpu(rxb->w0); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci while (w0 & ERXBUF_V) { 38462306a36Sopenharmony_ci err = be32_to_cpu(rxb->err); /* It's valid ... */ 38562306a36Sopenharmony_ci if (err & ERXBUF_GOODPKT) { 38662306a36Sopenharmony_ci len = ((w0 >> ERXBUF_BYTECNT_SHIFT) & 0x7ff) - 4; 38762306a36Sopenharmony_ci skb_put(skb, len); 38862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci if (ioc3_alloc_skb(ip, &new_skb, &rxb, &d)) { 39162306a36Sopenharmony_ci /* Ouch, drop packet and just recycle packet 39262306a36Sopenharmony_ci * to keep the ring filled. 39362306a36Sopenharmony_ci */ 39462306a36Sopenharmony_ci dev->stats.rx_dropped++; 39562306a36Sopenharmony_ci new_skb = skb; 39662306a36Sopenharmony_ci d = rxr[rx_entry]; 39762306a36Sopenharmony_ci goto next; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (likely(dev->features & NETIF_F_RXCSUM)) 40162306a36Sopenharmony_ci ioc3_tcpudp_checksum(skb, 40262306a36Sopenharmony_ci w0 & ERXBUF_IPCKSUM_MASK, 40362306a36Sopenharmony_ci len); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci dma_unmap_single(ip->dma_dev, rxr[rx_entry], 40662306a36Sopenharmony_ci RX_BUF_SIZE, DMA_FROM_DEVICE); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci netif_rx(skb); 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci ip->rx_skbs[rx_entry] = NULL; /* Poison */ 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci dev->stats.rx_packets++; /* Statistics */ 41362306a36Sopenharmony_ci dev->stats.rx_bytes += len; 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci /* The frame is invalid and the skb never 41662306a36Sopenharmony_ci * reached the network layer so we can just 41762306a36Sopenharmony_ci * recycle it. 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci new_skb = skb; 42062306a36Sopenharmony_ci d = rxr[rx_entry]; 42162306a36Sopenharmony_ci dev->stats.rx_errors++; 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci if (err & ERXBUF_CRCERR) /* Statistics */ 42462306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 42562306a36Sopenharmony_ci if (err & ERXBUF_FRAMERR) 42662306a36Sopenharmony_ci dev->stats.rx_frame_errors++; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cinext: 42962306a36Sopenharmony_ci ip->rx_skbs[n_entry] = new_skb; 43062306a36Sopenharmony_ci rxr[n_entry] = cpu_to_be64(ioc3_map(d, PCI64_ATTR_BAR)); 43162306a36Sopenharmony_ci rxb->w0 = 0; /* Clear valid flag */ 43262306a36Sopenharmony_ci n_entry = (n_entry + 1) & RX_RING_MASK; /* Update erpir */ 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Now go on to the next ring entry. */ 43562306a36Sopenharmony_ci rx_entry = (rx_entry + 1) & RX_RING_MASK; 43662306a36Sopenharmony_ci skb = ip->rx_skbs[rx_entry]; 43762306a36Sopenharmony_ci rxb = (struct ioc3_erxbuf *)(skb->data - RX_OFFSET); 43862306a36Sopenharmony_ci w0 = be32_to_cpu(rxb->w0); 43962306a36Sopenharmony_ci } 44062306a36Sopenharmony_ci writel((n_entry << 3) | ERPIR_ARM, &ip->regs->erpir); 44162306a36Sopenharmony_ci ip->rx_pi = n_entry; 44262306a36Sopenharmony_ci ip->rx_ci = rx_entry; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic inline void ioc3_tx(struct net_device *dev) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 44862306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 44962306a36Sopenharmony_ci unsigned long packets, bytes; 45062306a36Sopenharmony_ci int tx_entry, o_entry; 45162306a36Sopenharmony_ci struct sk_buff *skb; 45262306a36Sopenharmony_ci u32 etcir; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci spin_lock(&ip->ioc3_lock); 45562306a36Sopenharmony_ci etcir = readl(®s->etcir); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci tx_entry = (etcir >> 7) & TX_RING_MASK; 45862306a36Sopenharmony_ci o_entry = ip->tx_ci; 45962306a36Sopenharmony_ci packets = 0; 46062306a36Sopenharmony_ci bytes = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci while (o_entry != tx_entry) { 46362306a36Sopenharmony_ci packets++; 46462306a36Sopenharmony_ci skb = ip->tx_skbs[o_entry]; 46562306a36Sopenharmony_ci bytes += skb->len; 46662306a36Sopenharmony_ci dev_consume_skb_irq(skb); 46762306a36Sopenharmony_ci ip->tx_skbs[o_entry] = NULL; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci o_entry = (o_entry + 1) & TX_RING_MASK; /* Next */ 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci etcir = readl(®s->etcir); /* More pkts sent? */ 47262306a36Sopenharmony_ci tx_entry = (etcir >> 7) & TX_RING_MASK; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci dev->stats.tx_packets += packets; 47662306a36Sopenharmony_ci dev->stats.tx_bytes += bytes; 47762306a36Sopenharmony_ci ip->txqlen -= packets; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci if (netif_queue_stopped(dev) && ip->txqlen < TX_RING_ENTRIES) 48062306a36Sopenharmony_ci netif_wake_queue(dev); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci ip->tx_ci = o_entry; 48362306a36Sopenharmony_ci spin_unlock(&ip->ioc3_lock); 48462306a36Sopenharmony_ci} 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci/* Deal with fatal IOC3 errors. This condition might be caused by a hard or 48762306a36Sopenharmony_ci * software problems, so we should try to recover 48862306a36Sopenharmony_ci * more gracefully if this ever happens. In theory we might be flooded 48962306a36Sopenharmony_ci * with such error interrupts if something really goes wrong, so we might 49062306a36Sopenharmony_ci * also consider to take the interface down. 49162306a36Sopenharmony_ci */ 49262306a36Sopenharmony_cistatic void ioc3_error(struct net_device *dev, u32 eisr) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci spin_lock(&ip->ioc3_lock); 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci if (eisr & EISR_RXOFLO) 49962306a36Sopenharmony_ci net_err_ratelimited("%s: RX overflow.\n", dev->name); 50062306a36Sopenharmony_ci if (eisr & EISR_RXBUFOFLO) 50162306a36Sopenharmony_ci net_err_ratelimited("%s: RX buffer overflow.\n", dev->name); 50262306a36Sopenharmony_ci if (eisr & EISR_RXMEMERR) 50362306a36Sopenharmony_ci net_err_ratelimited("%s: RX PCI error.\n", dev->name); 50462306a36Sopenharmony_ci if (eisr & EISR_RXPARERR) 50562306a36Sopenharmony_ci net_err_ratelimited("%s: RX SSRAM parity error.\n", dev->name); 50662306a36Sopenharmony_ci if (eisr & EISR_TXBUFUFLO) 50762306a36Sopenharmony_ci net_err_ratelimited("%s: TX buffer underflow.\n", dev->name); 50862306a36Sopenharmony_ci if (eisr & EISR_TXMEMERR) 50962306a36Sopenharmony_ci net_err_ratelimited("%s: TX PCI error.\n", dev->name); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci ioc3_stop(ip); 51262306a36Sopenharmony_ci ioc3_free_rx_bufs(ip); 51362306a36Sopenharmony_ci ioc3_clean_tx_ring(ip); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci ioc3_init(dev); 51662306a36Sopenharmony_ci if (ioc3_alloc_rx_bufs(dev)) { 51762306a36Sopenharmony_ci netdev_err(dev, "%s: rx buffer allocation failed\n", __func__); 51862306a36Sopenharmony_ci spin_unlock(&ip->ioc3_lock); 51962306a36Sopenharmony_ci return; 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci ioc3_start(ip); 52262306a36Sopenharmony_ci ioc3_mii_init(ip); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci netif_wake_queue(dev); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci spin_unlock(&ip->ioc3_lock); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci/* The interrupt handler does all of the Rx thread work and cleans up 53062306a36Sopenharmony_ci * after the Tx thread. 53162306a36Sopenharmony_ci */ 53262306a36Sopenharmony_cistatic irqreturn_t ioc3_interrupt(int irq, void *dev_id) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev_id); 53562306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 53662306a36Sopenharmony_ci u32 eisr; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci eisr = readl(®s->eisr); 53962306a36Sopenharmony_ci writel(eisr, ®s->eisr); 54062306a36Sopenharmony_ci readl(®s->eisr); /* Flush */ 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (eisr & (EISR_RXOFLO | EISR_RXBUFOFLO | EISR_RXMEMERR | 54362306a36Sopenharmony_ci EISR_RXPARERR | EISR_TXBUFUFLO | EISR_TXMEMERR)) 54462306a36Sopenharmony_ci ioc3_error(dev_id, eisr); 54562306a36Sopenharmony_ci if (eisr & EISR_RXTIMERINT) 54662306a36Sopenharmony_ci ioc3_rx(dev_id); 54762306a36Sopenharmony_ci if (eisr & EISR_TXEXPLICIT) 54862306a36Sopenharmony_ci ioc3_tx(dev_id); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci return IRQ_HANDLED; 55162306a36Sopenharmony_ci} 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_cistatic inline void ioc3_setup_duplex(struct ioc3_private *ip) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci if (ip->mii.full_duplex) { 56062306a36Sopenharmony_ci writel(ETCSR_FD, ®s->etcsr); 56162306a36Sopenharmony_ci ip->emcr |= EMCR_DUPLEX; 56262306a36Sopenharmony_ci } else { 56362306a36Sopenharmony_ci writel(ETCSR_HD, ®s->etcsr); 56462306a36Sopenharmony_ci ip->emcr &= ~EMCR_DUPLEX; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci writel(ip->emcr, ®s->emcr); 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic void ioc3_timer(struct timer_list *t) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct ioc3_private *ip = from_timer(ip, t, ioc3_timer); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Print the link status if it has changed */ 57662306a36Sopenharmony_ci mii_check_media(&ip->mii, 1, 0); 57762306a36Sopenharmony_ci ioc3_setup_duplex(ip); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci ip->ioc3_timer.expires = jiffies + ((12 * HZ) / 10); /* 1.2s */ 58062306a36Sopenharmony_ci add_timer(&ip->ioc3_timer); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/* Try to find a PHY. There is no apparent relation between the MII addresses 58462306a36Sopenharmony_ci * in the SGI documentation and what we find in reality, so we simply probe 58562306a36Sopenharmony_ci * for the PHY. 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_cistatic int ioc3_mii_init(struct ioc3_private *ip) 58862306a36Sopenharmony_ci{ 58962306a36Sopenharmony_ci u16 word; 59062306a36Sopenharmony_ci int i; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 59362306a36Sopenharmony_ci word = ioc3_mdio_read(ip->mii.dev, i, MII_PHYSID1); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (word != 0xffff && word != 0x0000) { 59662306a36Sopenharmony_ci ip->mii.phy_id = i; 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci ip->mii.phy_id = -1; 60162306a36Sopenharmony_ci return -ENODEV; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void ioc3_mii_start(struct ioc3_private *ip) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci ip->ioc3_timer.expires = jiffies + (12 * HZ) / 10; /* 1.2 sec. */ 60762306a36Sopenharmony_ci add_timer(&ip->ioc3_timer); 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic inline void ioc3_tx_unmap(struct ioc3_private *ip, int entry) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct ioc3_etxd *desc; 61362306a36Sopenharmony_ci u32 cmd, bufcnt, len; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci desc = &ip->txr[entry]; 61662306a36Sopenharmony_ci cmd = be32_to_cpu(desc->cmd); 61762306a36Sopenharmony_ci bufcnt = be32_to_cpu(desc->bufcnt); 61862306a36Sopenharmony_ci if (cmd & ETXD_B1V) { 61962306a36Sopenharmony_ci len = (bufcnt & ETXD_B1CNT_MASK) >> ETXD_B1CNT_SHIFT; 62062306a36Sopenharmony_ci dma_unmap_single(ip->dma_dev, be64_to_cpu(desc->p1), 62162306a36Sopenharmony_ci len, DMA_TO_DEVICE); 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci if (cmd & ETXD_B2V) { 62462306a36Sopenharmony_ci len = (bufcnt & ETXD_B2CNT_MASK) >> ETXD_B2CNT_SHIFT; 62562306a36Sopenharmony_ci dma_unmap_single(ip->dma_dev, be64_to_cpu(desc->p2), 62662306a36Sopenharmony_ci len, DMA_TO_DEVICE); 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci} 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_cistatic inline void ioc3_clean_tx_ring(struct ioc3_private *ip) 63162306a36Sopenharmony_ci{ 63262306a36Sopenharmony_ci struct sk_buff *skb; 63362306a36Sopenharmony_ci int i; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci for (i = 0; i < TX_RING_ENTRIES; i++) { 63662306a36Sopenharmony_ci skb = ip->tx_skbs[i]; 63762306a36Sopenharmony_ci if (skb) { 63862306a36Sopenharmony_ci ioc3_tx_unmap(ip, i); 63962306a36Sopenharmony_ci ip->tx_skbs[i] = NULL; 64062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 64162306a36Sopenharmony_ci } 64262306a36Sopenharmony_ci ip->txr[i].cmd = 0; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci ip->tx_pi = 0; 64562306a36Sopenharmony_ci ip->tx_ci = 0; 64662306a36Sopenharmony_ci} 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic void ioc3_free_rx_bufs(struct ioc3_private *ip) 64962306a36Sopenharmony_ci{ 65062306a36Sopenharmony_ci int rx_entry, n_entry; 65162306a36Sopenharmony_ci struct sk_buff *skb; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci n_entry = ip->rx_ci; 65462306a36Sopenharmony_ci rx_entry = ip->rx_pi; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci while (n_entry != rx_entry) { 65762306a36Sopenharmony_ci skb = ip->rx_skbs[n_entry]; 65862306a36Sopenharmony_ci if (skb) { 65962306a36Sopenharmony_ci dma_unmap_single(ip->dma_dev, 66062306a36Sopenharmony_ci be64_to_cpu(ip->rxr[n_entry]), 66162306a36Sopenharmony_ci RX_BUF_SIZE, DMA_FROM_DEVICE); 66262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 66362306a36Sopenharmony_ci } 66462306a36Sopenharmony_ci n_entry = (n_entry + 1) & RX_RING_MASK; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_cistatic int ioc3_alloc_rx_bufs(struct net_device *dev) 66962306a36Sopenharmony_ci{ 67062306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 67162306a36Sopenharmony_ci struct ioc3_erxbuf *rxb; 67262306a36Sopenharmony_ci dma_addr_t d; 67362306a36Sopenharmony_ci int i; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* Now the rx buffers. The RX ring may be larger but 67662306a36Sopenharmony_ci * we only allocate 16 buffers for now. Need to tune 67762306a36Sopenharmony_ci * this for performance and memory later. 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_ci for (i = 0; i < RX_BUFFS; i++) { 68062306a36Sopenharmony_ci if (ioc3_alloc_skb(ip, &ip->rx_skbs[i], &rxb, &d)) 68162306a36Sopenharmony_ci return -ENOMEM; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci rxb->w0 = 0; /* Clear valid flag */ 68462306a36Sopenharmony_ci ip->rxr[i] = cpu_to_be64(ioc3_map(d, PCI64_ATTR_BAR)); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci ip->rx_ci = 0; 68762306a36Sopenharmony_ci ip->rx_pi = RX_BUFFS; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci return 0; 69062306a36Sopenharmony_ci} 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_cistatic inline void ioc3_ssram_disc(struct ioc3_private *ip) 69362306a36Sopenharmony_ci{ 69462306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 69562306a36Sopenharmony_ci u32 *ssram0 = &ip->ssram[0x0000]; 69662306a36Sopenharmony_ci u32 *ssram1 = &ip->ssram[0x4000]; 69762306a36Sopenharmony_ci u32 pattern = 0x5555; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Assume the larger size SSRAM and enable parity checking */ 70062306a36Sopenharmony_ci writel(readl(®s->emcr) | (EMCR_BUFSIZ | EMCR_RAMPAR), ®s->emcr); 70162306a36Sopenharmony_ci readl(®s->emcr); /* Flush */ 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci writel(pattern, ssram0); 70462306a36Sopenharmony_ci writel(~pattern & IOC3_SSRAM_DM, ssram1); 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci if ((readl(ssram0) & IOC3_SSRAM_DM) != pattern || 70762306a36Sopenharmony_ci (readl(ssram1) & IOC3_SSRAM_DM) != (~pattern & IOC3_SSRAM_DM)) { 70862306a36Sopenharmony_ci /* set ssram size to 64 KB */ 70962306a36Sopenharmony_ci ip->emcr |= EMCR_RAMPAR; 71062306a36Sopenharmony_ci writel(readl(®s->emcr) & ~EMCR_BUFSIZ, ®s->emcr); 71162306a36Sopenharmony_ci } else { 71262306a36Sopenharmony_ci ip->emcr |= EMCR_BUFSIZ | EMCR_RAMPAR; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void ioc3_init(struct net_device *dev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 71962306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci del_timer_sync(&ip->ioc3_timer); /* Kill if running */ 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci writel(EMCR_RST, ®s->emcr); /* Reset */ 72462306a36Sopenharmony_ci readl(®s->emcr); /* Flush WB */ 72562306a36Sopenharmony_ci udelay(4); /* Give it time ... */ 72662306a36Sopenharmony_ci writel(0, ®s->emcr); 72762306a36Sopenharmony_ci readl(®s->emcr); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Misc registers */ 73062306a36Sopenharmony_ci writel(ERBAR_VAL, ®s->erbar); 73162306a36Sopenharmony_ci readl(®s->etcdc); /* Clear on read */ 73262306a36Sopenharmony_ci writel(15, ®s->ercsr); /* RX low watermark */ 73362306a36Sopenharmony_ci writel(0, ®s->ertr); /* Interrupt immediately */ 73462306a36Sopenharmony_ci __ioc3_set_mac_address(dev); 73562306a36Sopenharmony_ci writel(ip->ehar_h, ®s->ehar_h); 73662306a36Sopenharmony_ci writel(ip->ehar_l, ®s->ehar_l); 73762306a36Sopenharmony_ci writel(42, ®s->ersr); /* XXX should be random */ 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic void ioc3_start(struct ioc3_private *ip) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 74362306a36Sopenharmony_ci unsigned long ring; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Now the rx ring base, consume & produce registers. */ 74662306a36Sopenharmony_ci ring = ioc3_map(ip->rxr_dma, PCI64_ATTR_PREC); 74762306a36Sopenharmony_ci writel(ring >> 32, ®s->erbr_h); 74862306a36Sopenharmony_ci writel(ring & 0xffffffff, ®s->erbr_l); 74962306a36Sopenharmony_ci writel(ip->rx_ci << 3, ®s->ercir); 75062306a36Sopenharmony_ci writel((ip->rx_pi << 3) | ERPIR_ARM, ®s->erpir); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci ring = ioc3_map(ip->txr_dma, PCI64_ATTR_PREC); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci ip->txqlen = 0; /* nothing queued */ 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci /* Now the tx ring base, consume & produce registers. */ 75762306a36Sopenharmony_ci writel(ring >> 32, ®s->etbr_h); 75862306a36Sopenharmony_ci writel(ring & 0xffffffff, ®s->etbr_l); 75962306a36Sopenharmony_ci writel(ip->tx_pi << 7, ®s->etpir); 76062306a36Sopenharmony_ci writel(ip->tx_ci << 7, ®s->etcir); 76162306a36Sopenharmony_ci readl(®s->etcir); /* Flush */ 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci ip->emcr |= ((RX_OFFSET / 2) << EMCR_RXOFF_SHIFT) | EMCR_TXDMAEN | 76462306a36Sopenharmony_ci EMCR_TXEN | EMCR_RXDMAEN | EMCR_RXEN | EMCR_PADEN; 76562306a36Sopenharmony_ci writel(ip->emcr, ®s->emcr); 76662306a36Sopenharmony_ci writel(EISR_RXTIMERINT | EISR_RXOFLO | EISR_RXBUFOFLO | 76762306a36Sopenharmony_ci EISR_RXMEMERR | EISR_RXPARERR | EISR_TXBUFUFLO | 76862306a36Sopenharmony_ci EISR_TXEXPLICIT | EISR_TXMEMERR, ®s->eier); 76962306a36Sopenharmony_ci readl(®s->eier); 77062306a36Sopenharmony_ci} 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_cistatic inline void ioc3_stop(struct ioc3_private *ip) 77362306a36Sopenharmony_ci{ 77462306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci writel(0, ®s->emcr); /* Shutup */ 77762306a36Sopenharmony_ci writel(0, ®s->eier); /* Disable interrupts */ 77862306a36Sopenharmony_ci readl(®s->eier); /* Flush */ 77962306a36Sopenharmony_ci} 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic int ioc3_open(struct net_device *dev) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci ip->ehar_h = 0; 78662306a36Sopenharmony_ci ip->ehar_l = 0; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci ioc3_init(dev); 78962306a36Sopenharmony_ci if (ioc3_alloc_rx_bufs(dev)) { 79062306a36Sopenharmony_ci netdev_err(dev, "%s: rx buffer allocation failed\n", __func__); 79162306a36Sopenharmony_ci return -ENOMEM; 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci ioc3_start(ip); 79462306a36Sopenharmony_ci ioc3_mii_start(ip); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci netif_start_queue(dev); 79762306a36Sopenharmony_ci return 0; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int ioc3_close(struct net_device *dev) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci del_timer_sync(&ip->ioc3_timer); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci netif_stop_queue(dev); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci ioc3_stop(ip); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci ioc3_free_rx_bufs(ip); 81162306a36Sopenharmony_ci ioc3_clean_tx_ring(ip); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci return 0; 81462306a36Sopenharmony_ci} 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_cistatic const struct net_device_ops ioc3_netdev_ops = { 81762306a36Sopenharmony_ci .ndo_open = ioc3_open, 81862306a36Sopenharmony_ci .ndo_stop = ioc3_close, 81962306a36Sopenharmony_ci .ndo_start_xmit = ioc3_start_xmit, 82062306a36Sopenharmony_ci .ndo_tx_timeout = ioc3_timeout, 82162306a36Sopenharmony_ci .ndo_get_stats = ioc3_get_stats, 82262306a36Sopenharmony_ci .ndo_set_rx_mode = ioc3_set_multicast_list, 82362306a36Sopenharmony_ci .ndo_eth_ioctl = ioc3_ioctl, 82462306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 82562306a36Sopenharmony_ci .ndo_set_mac_address = ioc3_set_mac_address, 82662306a36Sopenharmony_ci}; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic int ioc3eth_probe(struct platform_device *pdev) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci u32 sw_physid1, sw_physid2, vendor, model, rev; 83162306a36Sopenharmony_ci struct ioc3_private *ip; 83262306a36Sopenharmony_ci struct net_device *dev; 83362306a36Sopenharmony_ci struct resource *regs; 83462306a36Sopenharmony_ci u8 mac_addr[6]; 83562306a36Sopenharmony_ci int err; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 83862306a36Sopenharmony_ci if (!regs) { 83962306a36Sopenharmony_ci dev_err(&pdev->dev, "Invalid resource\n"); 84062306a36Sopenharmony_ci return -EINVAL; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci /* get mac addr from one wire prom */ 84362306a36Sopenharmony_ci if (ioc3eth_get_mac_addr(regs, mac_addr)) 84462306a36Sopenharmony_ci return -EPROBE_DEFER; /* not available yet */ 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct ioc3_private)); 84762306a36Sopenharmony_ci if (!dev) 84862306a36Sopenharmony_ci return -ENOMEM; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci ip = netdev_priv(dev); 85362306a36Sopenharmony_ci ip->dma_dev = pdev->dev.parent; 85462306a36Sopenharmony_ci ip->regs = devm_platform_ioremap_resource(pdev, 0); 85562306a36Sopenharmony_ci if (IS_ERR(ip->regs)) { 85662306a36Sopenharmony_ci err = PTR_ERR(ip->regs); 85762306a36Sopenharmony_ci goto out_free; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci ip->ssram = devm_platform_ioremap_resource(pdev, 1); 86162306a36Sopenharmony_ci if (IS_ERR(ip->ssram)) { 86262306a36Sopenharmony_ci err = PTR_ERR(ip->ssram); 86362306a36Sopenharmony_ci goto out_free; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci dev->irq = platform_get_irq(pdev, 0); 86762306a36Sopenharmony_ci if (dev->irq < 0) { 86862306a36Sopenharmony_ci err = dev->irq; 86962306a36Sopenharmony_ci goto out_free; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci if (devm_request_irq(&pdev->dev, dev->irq, ioc3_interrupt, 87362306a36Sopenharmony_ci IRQF_SHARED, "ioc3-eth", dev)) { 87462306a36Sopenharmony_ci dev_err(&pdev->dev, "Can't get irq %d\n", dev->irq); 87562306a36Sopenharmony_ci err = -ENODEV; 87662306a36Sopenharmony_ci goto out_free; 87762306a36Sopenharmony_ci } 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci spin_lock_init(&ip->ioc3_lock); 88062306a36Sopenharmony_ci timer_setup(&ip->ioc3_timer, ioc3_timer, 0); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci ioc3_stop(ip); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Allocate rx ring. 4kb = 512 entries, must be 4kb aligned */ 88562306a36Sopenharmony_ci ip->rxr = dma_alloc_coherent(ip->dma_dev, RX_RING_SIZE, &ip->rxr_dma, 88662306a36Sopenharmony_ci GFP_KERNEL); 88762306a36Sopenharmony_ci if (!ip->rxr) { 88862306a36Sopenharmony_ci pr_err("ioc3-eth: rx ring allocation failed\n"); 88962306a36Sopenharmony_ci err = -ENOMEM; 89062306a36Sopenharmony_ci goto out_stop; 89162306a36Sopenharmony_ci } 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci /* Allocate tx rings. 16kb = 128 bufs, must be 16kb aligned */ 89462306a36Sopenharmony_ci ip->tx_ring = dma_alloc_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, 89562306a36Sopenharmony_ci &ip->txr_dma, GFP_KERNEL); 89662306a36Sopenharmony_ci if (!ip->tx_ring) { 89762306a36Sopenharmony_ci pr_err("ioc3-eth: tx ring allocation failed\n"); 89862306a36Sopenharmony_ci err = -ENOMEM; 89962306a36Sopenharmony_ci goto out_stop; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci /* Align TX ring */ 90262306a36Sopenharmony_ci ip->txr = PTR_ALIGN(ip->tx_ring, SZ_16K); 90362306a36Sopenharmony_ci ip->txr_dma = ALIGN(ip->txr_dma, SZ_16K); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci ioc3_init(dev); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci ip->mii.phy_id_mask = 0x1f; 90862306a36Sopenharmony_ci ip->mii.reg_num_mask = 0x1f; 90962306a36Sopenharmony_ci ip->mii.dev = dev; 91062306a36Sopenharmony_ci ip->mii.mdio_read = ioc3_mdio_read; 91162306a36Sopenharmony_ci ip->mii.mdio_write = ioc3_mdio_write; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci ioc3_mii_init(ip); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (ip->mii.phy_id == -1) { 91662306a36Sopenharmony_ci netdev_err(dev, "Didn't find a PHY, goodbye.\n"); 91762306a36Sopenharmony_ci err = -ENODEV; 91862306a36Sopenharmony_ci goto out_stop; 91962306a36Sopenharmony_ci } 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ioc3_mii_start(ip); 92262306a36Sopenharmony_ci ioc3_ssram_disc(ip); 92362306a36Sopenharmony_ci eth_hw_addr_set(dev, mac_addr); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci /* The IOC3-specific entries in the device structure. */ 92662306a36Sopenharmony_ci dev->watchdog_timeo = 5 * HZ; 92762306a36Sopenharmony_ci dev->netdev_ops = &ioc3_netdev_ops; 92862306a36Sopenharmony_ci dev->ethtool_ops = &ioc3_ethtool_ops; 92962306a36Sopenharmony_ci dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_RXCSUM; 93062306a36Sopenharmony_ci dev->features = NETIF_F_IP_CSUM | NETIF_F_HIGHDMA; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci sw_physid1 = ioc3_mdio_read(dev, ip->mii.phy_id, MII_PHYSID1); 93362306a36Sopenharmony_ci sw_physid2 = ioc3_mdio_read(dev, ip->mii.phy_id, MII_PHYSID2); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci err = register_netdev(dev); 93662306a36Sopenharmony_ci if (err) 93762306a36Sopenharmony_ci goto out_stop; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci mii_check_media(&ip->mii, 1, 1); 94062306a36Sopenharmony_ci ioc3_setup_duplex(ip); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci vendor = (sw_physid1 << 12) | (sw_physid2 >> 4); 94362306a36Sopenharmony_ci model = (sw_physid2 >> 4) & 0x3f; 94462306a36Sopenharmony_ci rev = sw_physid2 & 0xf; 94562306a36Sopenharmony_ci netdev_info(dev, "Using PHY %d, vendor 0x%x, model %d, rev %d.\n", 94662306a36Sopenharmony_ci ip->mii.phy_id, vendor, model, rev); 94762306a36Sopenharmony_ci netdev_info(dev, "IOC3 SSRAM has %d kbyte.\n", 94862306a36Sopenharmony_ci ip->emcr & EMCR_BUFSIZ ? 128 : 64); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci return 0; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ciout_stop: 95362306a36Sopenharmony_ci del_timer_sync(&ip->ioc3_timer); 95462306a36Sopenharmony_ci if (ip->rxr) 95562306a36Sopenharmony_ci dma_free_coherent(ip->dma_dev, RX_RING_SIZE, ip->rxr, 95662306a36Sopenharmony_ci ip->rxr_dma); 95762306a36Sopenharmony_ci if (ip->tx_ring) 95862306a36Sopenharmony_ci dma_free_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, ip->tx_ring, 95962306a36Sopenharmony_ci ip->txr_dma); 96062306a36Sopenharmony_ciout_free: 96162306a36Sopenharmony_ci free_netdev(dev); 96262306a36Sopenharmony_ci return err; 96362306a36Sopenharmony_ci} 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_cistatic int ioc3eth_remove(struct platform_device *pdev) 96662306a36Sopenharmony_ci{ 96762306a36Sopenharmony_ci struct net_device *dev = platform_get_drvdata(pdev); 96862306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci dma_free_coherent(ip->dma_dev, RX_RING_SIZE, ip->rxr, ip->rxr_dma); 97162306a36Sopenharmony_ci dma_free_coherent(ip->dma_dev, TX_RING_SIZE + SZ_16K - 1, ip->tx_ring, ip->txr_dma); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci unregister_netdev(dev); 97462306a36Sopenharmony_ci del_timer_sync(&ip->ioc3_timer); 97562306a36Sopenharmony_ci free_netdev(dev); 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci return 0; 97862306a36Sopenharmony_ci} 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_cistatic netdev_tx_t ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) 98262306a36Sopenharmony_ci{ 98362306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 98462306a36Sopenharmony_ci struct ioc3_etxd *desc; 98562306a36Sopenharmony_ci unsigned long data; 98662306a36Sopenharmony_ci unsigned int len; 98762306a36Sopenharmony_ci int produce; 98862306a36Sopenharmony_ci u32 w0 = 0; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* IOC3 has a fairly simple minded checksumming hardware which simply 99162306a36Sopenharmony_ci * adds up the 1's complement checksum for the entire packet and 99262306a36Sopenharmony_ci * inserts it at an offset which can be specified in the descriptor 99362306a36Sopenharmony_ci * into the transmit packet. This means we have to compensate for the 99462306a36Sopenharmony_ci * MAC header which should not be summed and the TCP/UDP pseudo headers 99562306a36Sopenharmony_ci * manually. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 99862306a36Sopenharmony_ci const struct iphdr *ih = ip_hdr(skb); 99962306a36Sopenharmony_ci const int proto = ntohs(ih->protocol); 100062306a36Sopenharmony_ci unsigned int csoff; 100162306a36Sopenharmony_ci u32 csum, ehsum; 100262306a36Sopenharmony_ci u16 *eh; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci /* The MAC header. skb->mac seem the logic approach 100562306a36Sopenharmony_ci * to find the MAC header - except it's a NULL pointer ... 100662306a36Sopenharmony_ci */ 100762306a36Sopenharmony_ci eh = (u16 *)skb->data; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* Sum up dest addr, src addr and protocol */ 101062306a36Sopenharmony_ci ehsum = eh[0] + eh[1] + eh[2] + eh[3] + eh[4] + eh[5] + eh[6]; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* Skip IP header; it's sum is always zero and was 101362306a36Sopenharmony_ci * already filled in by ip_output.c 101462306a36Sopenharmony_ci */ 101562306a36Sopenharmony_ci csum = csum_tcpudp_nofold(ih->saddr, ih->daddr, 101662306a36Sopenharmony_ci ih->tot_len - (ih->ihl << 2), 101762306a36Sopenharmony_ci proto, csum_fold(ehsum)); 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci csum = (csum & 0xffff) + (csum >> 16); /* Fold again */ 102062306a36Sopenharmony_ci csum = (csum & 0xffff) + (csum >> 16); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci csoff = ETH_HLEN + (ih->ihl << 2); 102362306a36Sopenharmony_ci if (proto == IPPROTO_UDP) { 102462306a36Sopenharmony_ci csoff += offsetof(struct udphdr, check); 102562306a36Sopenharmony_ci udp_hdr(skb)->check = csum; 102662306a36Sopenharmony_ci } 102762306a36Sopenharmony_ci if (proto == IPPROTO_TCP) { 102862306a36Sopenharmony_ci csoff += offsetof(struct tcphdr, check); 102962306a36Sopenharmony_ci tcp_hdr(skb)->check = csum; 103062306a36Sopenharmony_ci } 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT); 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci data = (unsigned long)skb->data; 103862306a36Sopenharmony_ci len = skb->len; 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_ci produce = ip->tx_pi; 104162306a36Sopenharmony_ci desc = &ip->txr[produce]; 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci if (len <= 104) { 104462306a36Sopenharmony_ci /* Short packet, let's copy it directly into the ring. */ 104562306a36Sopenharmony_ci skb_copy_from_linear_data(skb, desc->data, skb->len); 104662306a36Sopenharmony_ci if (len < ETH_ZLEN) { 104762306a36Sopenharmony_ci /* Very short packet, pad with zeros at the end. */ 104862306a36Sopenharmony_ci memset(desc->data + len, 0, ETH_ZLEN - len); 104962306a36Sopenharmony_ci len = ETH_ZLEN; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci desc->cmd = cpu_to_be32(len | ETXD_INTWHENDONE | ETXD_D0V | w0); 105262306a36Sopenharmony_ci desc->bufcnt = cpu_to_be32(len); 105362306a36Sopenharmony_ci } else if ((data ^ (data + len - 1)) & 0x4000) { 105462306a36Sopenharmony_ci unsigned long b2 = (data | 0x3fffUL) + 1UL; 105562306a36Sopenharmony_ci unsigned long s1 = b2 - data; 105662306a36Sopenharmony_ci unsigned long s2 = data + len - b2; 105762306a36Sopenharmony_ci dma_addr_t d1, d2; 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci desc->cmd = cpu_to_be32(len | ETXD_INTWHENDONE | 106062306a36Sopenharmony_ci ETXD_B1V | ETXD_B2V | w0); 106162306a36Sopenharmony_ci desc->bufcnt = cpu_to_be32((s1 << ETXD_B1CNT_SHIFT) | 106262306a36Sopenharmony_ci (s2 << ETXD_B2CNT_SHIFT)); 106362306a36Sopenharmony_ci d1 = dma_map_single(ip->dma_dev, skb->data, s1, DMA_TO_DEVICE); 106462306a36Sopenharmony_ci if (dma_mapping_error(ip->dma_dev, d1)) 106562306a36Sopenharmony_ci goto drop_packet; 106662306a36Sopenharmony_ci d2 = dma_map_single(ip->dma_dev, (void *)b2, s1, DMA_TO_DEVICE); 106762306a36Sopenharmony_ci if (dma_mapping_error(ip->dma_dev, d2)) { 106862306a36Sopenharmony_ci dma_unmap_single(ip->dma_dev, d1, len, DMA_TO_DEVICE); 106962306a36Sopenharmony_ci goto drop_packet; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci desc->p1 = cpu_to_be64(ioc3_map(d1, PCI64_ATTR_PREF)); 107262306a36Sopenharmony_ci desc->p2 = cpu_to_be64(ioc3_map(d2, PCI64_ATTR_PREF)); 107362306a36Sopenharmony_ci } else { 107462306a36Sopenharmony_ci dma_addr_t d; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci /* Normal sized packet that doesn't cross a page boundary. */ 107762306a36Sopenharmony_ci desc->cmd = cpu_to_be32(len | ETXD_INTWHENDONE | ETXD_B1V | w0); 107862306a36Sopenharmony_ci desc->bufcnt = cpu_to_be32(len << ETXD_B1CNT_SHIFT); 107962306a36Sopenharmony_ci d = dma_map_single(ip->dma_dev, skb->data, len, DMA_TO_DEVICE); 108062306a36Sopenharmony_ci if (dma_mapping_error(ip->dma_dev, d)) 108162306a36Sopenharmony_ci goto drop_packet; 108262306a36Sopenharmony_ci desc->p1 = cpu_to_be64(ioc3_map(d, PCI64_ATTR_PREF)); 108362306a36Sopenharmony_ci } 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci mb(); /* make sure all descriptor changes are visible */ 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci ip->tx_skbs[produce] = skb; /* Remember skb */ 108862306a36Sopenharmony_ci produce = (produce + 1) & TX_RING_MASK; 108962306a36Sopenharmony_ci ip->tx_pi = produce; 109062306a36Sopenharmony_ci writel(produce << 7, &ip->regs->etpir); /* Fire ... */ 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci ip->txqlen++; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci if (ip->txqlen >= (TX_RING_ENTRIES - 1)) 109562306a36Sopenharmony_ci netif_stop_queue(dev); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci return NETDEV_TX_OK; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_cidrop_packet: 110262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 110362306a36Sopenharmony_ci dev->stats.tx_dropped++; 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci return NETDEV_TX_OK; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic void ioc3_timeout(struct net_device *dev, unsigned int txqueue) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci netdev_err(dev, "transmit timed out, resetting\n"); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci ioc3_stop(ip); 111962306a36Sopenharmony_ci ioc3_free_rx_bufs(ip); 112062306a36Sopenharmony_ci ioc3_clean_tx_ring(ip); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci ioc3_init(dev); 112362306a36Sopenharmony_ci if (ioc3_alloc_rx_bufs(dev)) { 112462306a36Sopenharmony_ci netdev_err(dev, "%s: rx buffer allocation failed\n", __func__); 112562306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 112662306a36Sopenharmony_ci return; 112762306a36Sopenharmony_ci } 112862306a36Sopenharmony_ci ioc3_start(ip); 112962306a36Sopenharmony_ci ioc3_mii_init(ip); 113062306a36Sopenharmony_ci ioc3_mii_start(ip); 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci netif_wake_queue(dev); 113562306a36Sopenharmony_ci} 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci/* Given a multicast ethernet address, this routine calculates the 113862306a36Sopenharmony_ci * address's bit index in the logical address filter mask 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_cistatic inline unsigned int ioc3_hash(const unsigned char *addr) 114162306a36Sopenharmony_ci{ 114262306a36Sopenharmony_ci unsigned int temp = 0; 114362306a36Sopenharmony_ci int bits; 114462306a36Sopenharmony_ci u32 crc; 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, addr); 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_ci crc &= 0x3f; /* bit reverse lowest 6 bits for hash index */ 114962306a36Sopenharmony_ci for (bits = 6; --bits >= 0; ) { 115062306a36Sopenharmony_ci temp <<= 1; 115162306a36Sopenharmony_ci temp |= (crc & 0x1); 115262306a36Sopenharmony_ci crc >>= 1; 115362306a36Sopenharmony_ci } 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return temp; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_cistatic void ioc3_get_drvinfo(struct net_device *dev, 115962306a36Sopenharmony_ci struct ethtool_drvinfo *info) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci strscpy(info->driver, IOC3_NAME, sizeof(info->driver)); 116262306a36Sopenharmony_ci strscpy(info->version, IOC3_VERSION, sizeof(info->version)); 116362306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(to_pci_dev(dev->dev.parent)), 116462306a36Sopenharmony_ci sizeof(info->bus_info)); 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic int ioc3_get_link_ksettings(struct net_device *dev, 116862306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 116962306a36Sopenharmony_ci{ 117062306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 117362306a36Sopenharmony_ci mii_ethtool_get_link_ksettings(&ip->mii, cmd); 117462306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci return 0; 117762306a36Sopenharmony_ci} 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_cistatic int ioc3_set_link_ksettings(struct net_device *dev, 118062306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 118162306a36Sopenharmony_ci{ 118262306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 118362306a36Sopenharmony_ci int rc; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 118662306a36Sopenharmony_ci rc = mii_ethtool_set_link_ksettings(&ip->mii, cmd); 118762306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci return rc; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic int ioc3_nway_reset(struct net_device *dev) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 119562306a36Sopenharmony_ci int rc; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 119862306a36Sopenharmony_ci rc = mii_nway_restart(&ip->mii); 119962306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci return rc; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic u32 ioc3_get_link(struct net_device *dev) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 120762306a36Sopenharmony_ci int rc; 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 121062306a36Sopenharmony_ci rc = mii_link_ok(&ip->mii); 121162306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci return rc; 121462306a36Sopenharmony_ci} 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_cistatic const struct ethtool_ops ioc3_ethtool_ops = { 121762306a36Sopenharmony_ci .get_drvinfo = ioc3_get_drvinfo, 121862306a36Sopenharmony_ci .nway_reset = ioc3_nway_reset, 121962306a36Sopenharmony_ci .get_link = ioc3_get_link, 122062306a36Sopenharmony_ci .get_link_ksettings = ioc3_get_link_ksettings, 122162306a36Sopenharmony_ci .set_link_ksettings = ioc3_set_link_ksettings, 122262306a36Sopenharmony_ci}; 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic int ioc3_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 122762306a36Sopenharmony_ci int rc; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 123062306a36Sopenharmony_ci rc = generic_mii_ioctl(&ip->mii, if_mii(rq), cmd, NULL); 123162306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci return rc; 123462306a36Sopenharmony_ci} 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_cistatic void ioc3_set_multicast_list(struct net_device *dev) 123762306a36Sopenharmony_ci{ 123862306a36Sopenharmony_ci struct ioc3_private *ip = netdev_priv(dev); 123962306a36Sopenharmony_ci struct ioc3_ethregs *regs = ip->regs; 124062306a36Sopenharmony_ci struct netdev_hw_addr *ha; 124162306a36Sopenharmony_ci u64 ehar = 0; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci spin_lock_irq(&ip->ioc3_lock); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */ 124662306a36Sopenharmony_ci ip->emcr |= EMCR_PROMISC; 124762306a36Sopenharmony_ci writel(ip->emcr, ®s->emcr); 124862306a36Sopenharmony_ci readl(®s->emcr); 124962306a36Sopenharmony_ci } else { 125062306a36Sopenharmony_ci ip->emcr &= ~EMCR_PROMISC; 125162306a36Sopenharmony_ci writel(ip->emcr, ®s->emcr); /* Clear promiscuous. */ 125262306a36Sopenharmony_ci readl(®s->emcr); 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci if ((dev->flags & IFF_ALLMULTI) || 125562306a36Sopenharmony_ci (netdev_mc_count(dev) > 64)) { 125662306a36Sopenharmony_ci /* Too many for hashing to make sense or we want all 125762306a36Sopenharmony_ci * multicast packets anyway, so skip computing all the 125862306a36Sopenharmony_ci * hashes and just accept all packets. 125962306a36Sopenharmony_ci */ 126062306a36Sopenharmony_ci ip->ehar_h = 0xffffffff; 126162306a36Sopenharmony_ci ip->ehar_l = 0xffffffff; 126262306a36Sopenharmony_ci } else { 126362306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 126462306a36Sopenharmony_ci ehar |= (1UL << ioc3_hash(ha->addr)); 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci ip->ehar_h = ehar >> 32; 126762306a36Sopenharmony_ci ip->ehar_l = ehar & 0xffffffff; 126862306a36Sopenharmony_ci } 126962306a36Sopenharmony_ci writel(ip->ehar_h, ®s->ehar_h); 127062306a36Sopenharmony_ci writel(ip->ehar_l, ®s->ehar_l); 127162306a36Sopenharmony_ci } 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci spin_unlock_irq(&ip->ioc3_lock); 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic struct platform_driver ioc3eth_driver = { 127762306a36Sopenharmony_ci .probe = ioc3eth_probe, 127862306a36Sopenharmony_ci .remove = ioc3eth_remove, 127962306a36Sopenharmony_ci .driver = { 128062306a36Sopenharmony_ci .name = "ioc3-eth", 128162306a36Sopenharmony_ci } 128262306a36Sopenharmony_ci}; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_cimodule_platform_driver(ioc3eth_driver); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ciMODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); 128762306a36Sopenharmony_ciMODULE_DESCRIPTION("SGI IOC3 Ethernet driver"); 128862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1289