162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* cassini.c: Sun Microsystems Cassini(+) ethernet driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2004 Sun Microsystems Inc. 562306a36Sopenharmony_ci * Copyright (C) 2003 Adrian Sun (asun@darksunrising.com) 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This driver uses the sungem driver (c) David Miller 862306a36Sopenharmony_ci * (davem@redhat.com) as its basis. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * The cassini chip has a number of features that distinguish it from 1162306a36Sopenharmony_ci * the gem chip: 1262306a36Sopenharmony_ci * 4 transmit descriptor rings that are used for either QoS (VLAN) or 1362306a36Sopenharmony_ci * load balancing (non-VLAN mode) 1462306a36Sopenharmony_ci * batching of multiple packets 1562306a36Sopenharmony_ci * multiple CPU dispatching 1662306a36Sopenharmony_ci * page-based RX descriptor engine with separate completion rings 1762306a36Sopenharmony_ci * Gigabit support (GMII and PCS interface) 1862306a36Sopenharmony_ci * MIF link up/down detection works 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * RX is handled by page sized buffers that are attached as fragments to 2162306a36Sopenharmony_ci * the skb. here's what's done: 2262306a36Sopenharmony_ci * -- driver allocates pages at a time and keeps reference counts 2362306a36Sopenharmony_ci * on them. 2462306a36Sopenharmony_ci * -- the upper protocol layers assume that the header is in the skb 2562306a36Sopenharmony_ci * itself. as a result, cassini will copy a small amount (64 bytes) 2662306a36Sopenharmony_ci * to make them happy. 2762306a36Sopenharmony_ci * -- driver appends the rest of the data pages as frags to skbuffs 2862306a36Sopenharmony_ci * and increments the reference count 2962306a36Sopenharmony_ci * -- on page reclamation, the driver swaps the page with a spare page. 3062306a36Sopenharmony_ci * if that page is still in use, it frees its reference to that page, 3162306a36Sopenharmony_ci * and allocates a new page for use. otherwise, it just recycles the 3262306a36Sopenharmony_ci * page. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * NOTE: cassini can parse the header. however, it's not worth it 3562306a36Sopenharmony_ci * as long as the network stack requires a header copy. 3662306a36Sopenharmony_ci * 3762306a36Sopenharmony_ci * TX has 4 queues. currently these queues are used in a round-robin 3862306a36Sopenharmony_ci * fashion for load balancing. They can also be used for QoS. for that 3962306a36Sopenharmony_ci * to work, however, QoS information needs to be exposed down to the driver 4062306a36Sopenharmony_ci * level so that subqueues get targeted to particular transmit rings. 4162306a36Sopenharmony_ci * alternatively, the queues can be configured via use of the all-purpose 4262306a36Sopenharmony_ci * ioctl. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * RX DATA: the rx completion ring has all the info, but the rx desc 4562306a36Sopenharmony_ci * ring has all of the data. RX can conceivably come in under multiple 4662306a36Sopenharmony_ci * interrupts, but the INT# assignment needs to be set up properly by 4762306a36Sopenharmony_ci * the BIOS and conveyed to the driver. PCI BIOSes don't know how to do 4862306a36Sopenharmony_ci * that. also, the two descriptor rings are designed to distinguish between 4962306a36Sopenharmony_ci * encrypted and non-encrypted packets, but we use them for buffering 5062306a36Sopenharmony_ci * instead. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * by default, the selective clear mask is set up to process rx packets. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#include <linux/module.h> 5862306a36Sopenharmony_ci#include <linux/kernel.h> 5962306a36Sopenharmony_ci#include <linux/types.h> 6062306a36Sopenharmony_ci#include <linux/compiler.h> 6162306a36Sopenharmony_ci#include <linux/slab.h> 6262306a36Sopenharmony_ci#include <linux/delay.h> 6362306a36Sopenharmony_ci#include <linux/init.h> 6462306a36Sopenharmony_ci#include <linux/interrupt.h> 6562306a36Sopenharmony_ci#include <linux/vmalloc.h> 6662306a36Sopenharmony_ci#include <linux/ioport.h> 6762306a36Sopenharmony_ci#include <linux/pci.h> 6862306a36Sopenharmony_ci#include <linux/mm.h> 6962306a36Sopenharmony_ci#include <linux/highmem.h> 7062306a36Sopenharmony_ci#include <linux/list.h> 7162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#include <linux/netdevice.h> 7462306a36Sopenharmony_ci#include <linux/etherdevice.h> 7562306a36Sopenharmony_ci#include <linux/skbuff.h> 7662306a36Sopenharmony_ci#include <linux/ethtool.h> 7762306a36Sopenharmony_ci#include <linux/crc32.h> 7862306a36Sopenharmony_ci#include <linux/random.h> 7962306a36Sopenharmony_ci#include <linux/mii.h> 8062306a36Sopenharmony_ci#include <linux/ip.h> 8162306a36Sopenharmony_ci#include <linux/tcp.h> 8262306a36Sopenharmony_ci#include <linux/mutex.h> 8362306a36Sopenharmony_ci#include <linux/firmware.h> 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#include <net/checksum.h> 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#include <linux/atomic.h> 8862306a36Sopenharmony_ci#include <asm/io.h> 8962306a36Sopenharmony_ci#include <asm/byteorder.h> 9062306a36Sopenharmony_ci#include <linux/uaccess.h> 9162306a36Sopenharmony_ci#include <linux/jiffies.h> 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define CAS_NCPUS num_online_cpus() 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#define cas_skb_release(x) netif_rx(x) 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* select which firmware to use */ 9862306a36Sopenharmony_ci#define USE_HP_WORKAROUND 9962306a36Sopenharmony_ci#define HP_WORKAROUND_DEFAULT /* select which firmware to use as default */ 10062306a36Sopenharmony_ci#define CAS_HP_ALT_FIRMWARE cas_prog_null /* alternate firmware */ 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci#include "cassini.h" 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define USE_TX_COMPWB /* use completion writeback registers */ 10562306a36Sopenharmony_ci#define USE_CSMA_CD_PROTO /* standard CSMA/CD */ 10662306a36Sopenharmony_ci#define USE_RX_BLANK /* hw interrupt mitigation */ 10762306a36Sopenharmony_ci#undef USE_ENTROPY_DEV /* don't test for entropy device */ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* NOTE: these aren't useable unless PCI interrupts can be assigned. 11062306a36Sopenharmony_ci * also, we need to make cp->lock finer-grained. 11162306a36Sopenharmony_ci */ 11262306a36Sopenharmony_ci#undef USE_PCI_INTB 11362306a36Sopenharmony_ci#undef USE_PCI_INTC 11462306a36Sopenharmony_ci#undef USE_PCI_INTD 11562306a36Sopenharmony_ci#undef USE_QOS 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#undef USE_VPD_DEBUG /* debug vpd information if defined */ 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/* rx processing options */ 12062306a36Sopenharmony_ci#define USE_PAGE_ORDER /* specify to allocate large rx pages */ 12162306a36Sopenharmony_ci#define RX_DONT_BATCH 0 /* if 1, don't batch flows */ 12262306a36Sopenharmony_ci#define RX_COPY_ALWAYS 0 /* if 0, use frags */ 12362306a36Sopenharmony_ci#define RX_COPY_MIN 64 /* copy a little to make upper layers happy */ 12462306a36Sopenharmony_ci#undef RX_COUNT_BUFFERS /* define to calculate RX buffer stats */ 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define DRV_MODULE_NAME "cassini" 12762306a36Sopenharmony_ci#define DRV_MODULE_VERSION "1.6" 12862306a36Sopenharmony_ci#define DRV_MODULE_RELDATE "21 May 2008" 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define CAS_DEF_MSG_ENABLE \ 13162306a36Sopenharmony_ci (NETIF_MSG_DRV | \ 13262306a36Sopenharmony_ci NETIF_MSG_PROBE | \ 13362306a36Sopenharmony_ci NETIF_MSG_LINK | \ 13462306a36Sopenharmony_ci NETIF_MSG_TIMER | \ 13562306a36Sopenharmony_ci NETIF_MSG_IFDOWN | \ 13662306a36Sopenharmony_ci NETIF_MSG_IFUP | \ 13762306a36Sopenharmony_ci NETIF_MSG_RX_ERR | \ 13862306a36Sopenharmony_ci NETIF_MSG_TX_ERR) 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/* length of time before we decide the hardware is borked, 14162306a36Sopenharmony_ci * and dev->tx_timeout() should be called to fix the problem 14262306a36Sopenharmony_ci */ 14362306a36Sopenharmony_ci#define CAS_TX_TIMEOUT (HZ) 14462306a36Sopenharmony_ci#define CAS_LINK_TIMEOUT (22*HZ/10) 14562306a36Sopenharmony_ci#define CAS_LINK_FAST_TIMEOUT (1) 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* timeout values for state changing. these specify the number 14862306a36Sopenharmony_ci * of 10us delays to be used before giving up. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci#define STOP_TRIES_PHY 1000 15162306a36Sopenharmony_ci#define STOP_TRIES 5000 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci/* specify a minimum frame size to deal with some fifo issues 15462306a36Sopenharmony_ci * max mtu == 2 * page size - ethernet header - 64 - swivel = 15562306a36Sopenharmony_ci * 2 * page_size - 0x50 15662306a36Sopenharmony_ci */ 15762306a36Sopenharmony_ci#define CAS_MIN_FRAME 97 15862306a36Sopenharmony_ci#define CAS_1000MB_MIN_FRAME 255 15962306a36Sopenharmony_ci#define CAS_MIN_MTU 60 16062306a36Sopenharmony_ci#define CAS_MAX_MTU min(((cp->page_size << 1) - 0x50), 9000) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#if 1 16362306a36Sopenharmony_ci/* 16462306a36Sopenharmony_ci * Eliminate these and use separate atomic counters for each, to 16562306a36Sopenharmony_ci * avoid a race condition. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_ci#else 16862306a36Sopenharmony_ci#define CAS_RESET_MTU 1 16962306a36Sopenharmony_ci#define CAS_RESET_ALL 2 17062306a36Sopenharmony_ci#define CAS_RESET_SPARE 3 17162306a36Sopenharmony_ci#endif 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic char version[] = 17462306a36Sopenharmony_ci DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int cassini_debug = -1; /* -1 == use CAS_DEF_MSG_ENABLE as value */ 17762306a36Sopenharmony_cistatic int link_mode; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ciMODULE_AUTHOR("Adrian Sun (asun@darksunrising.com)"); 18062306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun Cassini(+) ethernet driver"); 18162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 18262306a36Sopenharmony_ciMODULE_FIRMWARE("sun/cassini.bin"); 18362306a36Sopenharmony_cimodule_param(cassini_debug, int, 0); 18462306a36Sopenharmony_ciMODULE_PARM_DESC(cassini_debug, "Cassini bitmapped debugging message enable value"); 18562306a36Sopenharmony_cimodule_param(link_mode, int, 0); 18662306a36Sopenharmony_ciMODULE_PARM_DESC(link_mode, "default link mode"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* 18962306a36Sopenharmony_ci * Work around for a PCS bug in which the link goes down due to the chip 19062306a36Sopenharmony_ci * being confused and never showing a link status of "up." 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci#define DEFAULT_LINKDOWN_TIMEOUT 5 19362306a36Sopenharmony_ci/* 19462306a36Sopenharmony_ci * Value in seconds, for user input. 19562306a36Sopenharmony_ci */ 19662306a36Sopenharmony_cistatic int linkdown_timeout = DEFAULT_LINKDOWN_TIMEOUT; 19762306a36Sopenharmony_cimodule_param(linkdown_timeout, int, 0); 19862306a36Sopenharmony_ciMODULE_PARM_DESC(linkdown_timeout, 19962306a36Sopenharmony_ci"min reset interval in sec. for PCS linkdown issue; disabled if not positive"); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci/* 20262306a36Sopenharmony_ci * value in 'ticks' (units used by jiffies). Set when we init the 20362306a36Sopenharmony_ci * module because 'HZ' in actually a function call on some flavors of 20462306a36Sopenharmony_ci * Linux. This will default to DEFAULT_LINKDOWN_TIMEOUT * HZ. 20562306a36Sopenharmony_ci */ 20662306a36Sopenharmony_cistatic int link_transition_timeout; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic u16 link_modes[] = { 21162306a36Sopenharmony_ci BMCR_ANENABLE, /* 0 : autoneg */ 21262306a36Sopenharmony_ci 0, /* 1 : 10bt half duplex */ 21362306a36Sopenharmony_ci BMCR_SPEED100, /* 2 : 100bt half duplex */ 21462306a36Sopenharmony_ci BMCR_FULLDPLX, /* 3 : 10bt full duplex */ 21562306a36Sopenharmony_ci BMCR_SPEED100|BMCR_FULLDPLX, /* 4 : 100bt full duplex */ 21662306a36Sopenharmony_ci CAS_BMCR_SPEED1000|BMCR_FULLDPLX /* 5 : 1000bt full duplex */ 21762306a36Sopenharmony_ci}; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic const struct pci_device_id cas_pci_tbl[] = { 22062306a36Sopenharmony_ci { PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_CASSINI, 22162306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 22262306a36Sopenharmony_ci { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SATURN, 22362306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 22462306a36Sopenharmony_ci { 0, } 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cas_pci_tbl); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void cas_set_link_modes(struct cas *cp); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic inline void cas_lock_tx(struct cas *cp) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci int i; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) 23662306a36Sopenharmony_ci spin_lock_nested(&cp->tx_lock[i], i); 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci/* WTZ: QA was finding deadlock problems with the previous 24062306a36Sopenharmony_ci * versions after long test runs with multiple cards per machine. 24162306a36Sopenharmony_ci * See if replacing cas_lock_all with safer versions helps. The 24262306a36Sopenharmony_ci * symptoms QA is reporting match those we'd expect if interrupts 24362306a36Sopenharmony_ci * aren't being properly restored, and we fixed a previous deadlock 24462306a36Sopenharmony_ci * with similar symptoms by using save/restore versions in other 24562306a36Sopenharmony_ci * places. 24662306a36Sopenharmony_ci */ 24762306a36Sopenharmony_ci#define cas_lock_all_save(cp, flags) \ 24862306a36Sopenharmony_cido { \ 24962306a36Sopenharmony_ci struct cas *xxxcp = (cp); \ 25062306a36Sopenharmony_ci spin_lock_irqsave(&xxxcp->lock, flags); \ 25162306a36Sopenharmony_ci cas_lock_tx(xxxcp); \ 25262306a36Sopenharmony_ci} while (0) 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic inline void cas_unlock_tx(struct cas *cp) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int i; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci for (i = N_TX_RINGS; i > 0; i--) 25962306a36Sopenharmony_ci spin_unlock(&cp->tx_lock[i - 1]); 26062306a36Sopenharmony_ci} 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci#define cas_unlock_all_restore(cp, flags) \ 26362306a36Sopenharmony_cido { \ 26462306a36Sopenharmony_ci struct cas *xxxcp = (cp); \ 26562306a36Sopenharmony_ci cas_unlock_tx(xxxcp); \ 26662306a36Sopenharmony_ci spin_unlock_irqrestore(&xxxcp->lock, flags); \ 26762306a36Sopenharmony_ci} while (0) 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic void cas_disable_irq(struct cas *cp, const int ring) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci /* Make sure we won't get any more interrupts */ 27262306a36Sopenharmony_ci if (ring == 0) { 27362306a36Sopenharmony_ci writel(0xFFFFFFFF, cp->regs + REG_INTR_MASK); 27462306a36Sopenharmony_ci return; 27562306a36Sopenharmony_ci } 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* disable completion interrupts and selectively mask */ 27862306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 27962306a36Sopenharmony_ci switch (ring) { 28062306a36Sopenharmony_ci#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD) 28162306a36Sopenharmony_ci#ifdef USE_PCI_INTB 28262306a36Sopenharmony_ci case 1: 28362306a36Sopenharmony_ci#endif 28462306a36Sopenharmony_ci#ifdef USE_PCI_INTC 28562306a36Sopenharmony_ci case 2: 28662306a36Sopenharmony_ci#endif 28762306a36Sopenharmony_ci#ifdef USE_PCI_INTD 28862306a36Sopenharmony_ci case 3: 28962306a36Sopenharmony_ci#endif 29062306a36Sopenharmony_ci writel(INTRN_MASK_CLEAR_ALL | INTRN_MASK_RX_EN, 29162306a36Sopenharmony_ci cp->regs + REG_PLUS_INTRN_MASK(ring)); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci#endif 29462306a36Sopenharmony_ci default: 29562306a36Sopenharmony_ci writel(INTRN_MASK_CLEAR_ALL, cp->regs + 29662306a36Sopenharmony_ci REG_PLUS_INTRN_MASK(ring)); 29762306a36Sopenharmony_ci break; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic inline void cas_mask_intr(struct cas *cp) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci int i; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci for (i = 0; i < N_RX_COMP_RINGS; i++) 30762306a36Sopenharmony_ci cas_disable_irq(cp, i); 30862306a36Sopenharmony_ci} 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_cistatic void cas_enable_irq(struct cas *cp, const int ring) 31162306a36Sopenharmony_ci{ 31262306a36Sopenharmony_ci if (ring == 0) { /* all but TX_DONE */ 31362306a36Sopenharmony_ci writel(INTR_TX_DONE, cp->regs + REG_INTR_MASK); 31462306a36Sopenharmony_ci return; 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 31862306a36Sopenharmony_ci switch (ring) { 31962306a36Sopenharmony_ci#if defined (USE_PCI_INTB) || defined(USE_PCI_INTC) || defined(USE_PCI_INTD) 32062306a36Sopenharmony_ci#ifdef USE_PCI_INTB 32162306a36Sopenharmony_ci case 1: 32262306a36Sopenharmony_ci#endif 32362306a36Sopenharmony_ci#ifdef USE_PCI_INTC 32462306a36Sopenharmony_ci case 2: 32562306a36Sopenharmony_ci#endif 32662306a36Sopenharmony_ci#ifdef USE_PCI_INTD 32762306a36Sopenharmony_ci case 3: 32862306a36Sopenharmony_ci#endif 32962306a36Sopenharmony_ci writel(INTRN_MASK_RX_EN, cp->regs + 33062306a36Sopenharmony_ci REG_PLUS_INTRN_MASK(ring)); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci#endif 33362306a36Sopenharmony_ci default: 33462306a36Sopenharmony_ci break; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci} 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic inline void cas_unmask_intr(struct cas *cp) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci int i; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for (i = 0; i < N_RX_COMP_RINGS; i++) 34462306a36Sopenharmony_ci cas_enable_irq(cp, i); 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic inline void cas_entropy_gather(struct cas *cp) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV 35062306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0) 35162306a36Sopenharmony_ci return; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci batch_entropy_store(readl(cp->regs + REG_ENTROPY_IV), 35462306a36Sopenharmony_ci readl(cp->regs + REG_ENTROPY_IV), 35562306a36Sopenharmony_ci sizeof(uint64_t)*8); 35662306a36Sopenharmony_ci#endif 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic inline void cas_entropy_reset(struct cas *cp) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV 36262306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_ENTROPY_DEV) == 0) 36362306a36Sopenharmony_ci return; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci writel(BIM_LOCAL_DEV_PAD | BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_EXT, 36662306a36Sopenharmony_ci cp->regs + REG_BIM_LOCAL_DEV_EN); 36762306a36Sopenharmony_ci writeb(ENTROPY_RESET_STC_MODE, cp->regs + REG_ENTROPY_RESET); 36862306a36Sopenharmony_ci writeb(0x55, cp->regs + REG_ENTROPY_RAND_REG); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci /* if we read back 0x0, we don't have an entropy device */ 37162306a36Sopenharmony_ci if (readb(cp->regs + REG_ENTROPY_RAND_REG) == 0) 37262306a36Sopenharmony_ci cp->cas_flags &= ~CAS_FLAG_ENTROPY_DEV; 37362306a36Sopenharmony_ci#endif 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* access to the phy. the following assumes that we've initialized the MIF to 37762306a36Sopenharmony_ci * be in frame rather than bit-bang mode 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic u16 cas_phy_read(struct cas *cp, int reg) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci u32 cmd; 38262306a36Sopenharmony_ci int limit = STOP_TRIES_PHY; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci cmd = MIF_FRAME_ST | MIF_FRAME_OP_READ; 38562306a36Sopenharmony_ci cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr); 38662306a36Sopenharmony_ci cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg); 38762306a36Sopenharmony_ci cmd |= MIF_FRAME_TURN_AROUND_MSB; 38862306a36Sopenharmony_ci writel(cmd, cp->regs + REG_MIF_FRAME); 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* poll for completion */ 39162306a36Sopenharmony_ci while (limit-- > 0) { 39262306a36Sopenharmony_ci udelay(10); 39362306a36Sopenharmony_ci cmd = readl(cp->regs + REG_MIF_FRAME); 39462306a36Sopenharmony_ci if (cmd & MIF_FRAME_TURN_AROUND_LSB) 39562306a36Sopenharmony_ci return cmd & MIF_FRAME_DATA_MASK; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci return 0xFFFF; /* -1 */ 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int cas_phy_write(struct cas *cp, int reg, u16 val) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int limit = STOP_TRIES_PHY; 40362306a36Sopenharmony_ci u32 cmd; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci cmd = MIF_FRAME_ST | MIF_FRAME_OP_WRITE; 40662306a36Sopenharmony_ci cmd |= CAS_BASE(MIF_FRAME_PHY_ADDR, cp->phy_addr); 40762306a36Sopenharmony_ci cmd |= CAS_BASE(MIF_FRAME_REG_ADDR, reg); 40862306a36Sopenharmony_ci cmd |= MIF_FRAME_TURN_AROUND_MSB; 40962306a36Sopenharmony_ci cmd |= val & MIF_FRAME_DATA_MASK; 41062306a36Sopenharmony_ci writel(cmd, cp->regs + REG_MIF_FRAME); 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* poll for completion */ 41362306a36Sopenharmony_ci while (limit-- > 0) { 41462306a36Sopenharmony_ci udelay(10); 41562306a36Sopenharmony_ci cmd = readl(cp->regs + REG_MIF_FRAME); 41662306a36Sopenharmony_ci if (cmd & MIF_FRAME_TURN_AROUND_LSB) 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci return -1; 42062306a36Sopenharmony_ci} 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_cistatic void cas_phy_powerup(struct cas *cp) 42362306a36Sopenharmony_ci{ 42462306a36Sopenharmony_ci u16 ctl = cas_phy_read(cp, MII_BMCR); 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci if ((ctl & BMCR_PDOWN) == 0) 42762306a36Sopenharmony_ci return; 42862306a36Sopenharmony_ci ctl &= ~BMCR_PDOWN; 42962306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, ctl); 43062306a36Sopenharmony_ci} 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_cistatic void cas_phy_powerdown(struct cas *cp) 43362306a36Sopenharmony_ci{ 43462306a36Sopenharmony_ci u16 ctl = cas_phy_read(cp, MII_BMCR); 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci if (ctl & BMCR_PDOWN) 43762306a36Sopenharmony_ci return; 43862306a36Sopenharmony_ci ctl |= BMCR_PDOWN; 43962306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, ctl); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci/* cp->lock held. note: the last put_page will free the buffer */ 44362306a36Sopenharmony_cistatic int cas_page_free(struct cas *cp, cas_page_t *page) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci dma_unmap_page(&cp->pdev->dev, page->dma_addr, cp->page_size, 44662306a36Sopenharmony_ci DMA_FROM_DEVICE); 44762306a36Sopenharmony_ci __free_pages(page->buffer, cp->page_order); 44862306a36Sopenharmony_ci kfree(page); 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci#ifdef RX_COUNT_BUFFERS 45362306a36Sopenharmony_ci#define RX_USED_ADD(x, y) ((x)->used += (y)) 45462306a36Sopenharmony_ci#define RX_USED_SET(x, y) ((x)->used = (y)) 45562306a36Sopenharmony_ci#else 45662306a36Sopenharmony_ci#define RX_USED_ADD(x, y) do { } while(0) 45762306a36Sopenharmony_ci#define RX_USED_SET(x, y) do { } while(0) 45862306a36Sopenharmony_ci#endif 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/* local page allocation routines for the receive buffers. jumbo pages 46162306a36Sopenharmony_ci * require at least 8K contiguous and 8K aligned buffers. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_cistatic cas_page_t *cas_page_alloc(struct cas *cp, const gfp_t flags) 46462306a36Sopenharmony_ci{ 46562306a36Sopenharmony_ci cas_page_t *page; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci page = kmalloc(sizeof(cas_page_t), flags); 46862306a36Sopenharmony_ci if (!page) 46962306a36Sopenharmony_ci return NULL; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci INIT_LIST_HEAD(&page->list); 47262306a36Sopenharmony_ci RX_USED_SET(page, 0); 47362306a36Sopenharmony_ci page->buffer = alloc_pages(flags, cp->page_order); 47462306a36Sopenharmony_ci if (!page->buffer) 47562306a36Sopenharmony_ci goto page_err; 47662306a36Sopenharmony_ci page->dma_addr = dma_map_page(&cp->pdev->dev, page->buffer, 0, 47762306a36Sopenharmony_ci cp->page_size, DMA_FROM_DEVICE); 47862306a36Sopenharmony_ci return page; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cipage_err: 48162306a36Sopenharmony_ci kfree(page); 48262306a36Sopenharmony_ci return NULL; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci/* initialize spare pool of rx buffers, but allocate during the open */ 48662306a36Sopenharmony_cistatic void cas_spare_init(struct cas *cp) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 48962306a36Sopenharmony_ci INIT_LIST_HEAD(&cp->rx_inuse_list); 49062306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 49362306a36Sopenharmony_ci INIT_LIST_HEAD(&cp->rx_spare_list); 49462306a36Sopenharmony_ci cp->rx_spares_needed = RX_SPARE_COUNT; 49562306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/* used on close. free all the spare buffers. */ 49962306a36Sopenharmony_cistatic void cas_spare_free(struct cas *cp) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct list_head list, *elem, *tmp; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci /* free spare buffers */ 50462306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 50562306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 50662306a36Sopenharmony_ci list_splice_init(&cp->rx_spare_list, &list); 50762306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 50862306a36Sopenharmony_ci list_for_each_safe(elem, tmp, &list) { 50962306a36Sopenharmony_ci cas_page_free(cp, list_entry(elem, cas_page_t, list)); 51062306a36Sopenharmony_ci } 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 51362306a36Sopenharmony_ci#if 1 51462306a36Sopenharmony_ci /* 51562306a36Sopenharmony_ci * Looks like Adrian had protected this with a different 51662306a36Sopenharmony_ci * lock than used everywhere else to manipulate this list. 51762306a36Sopenharmony_ci */ 51862306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 51962306a36Sopenharmony_ci list_splice_init(&cp->rx_inuse_list, &list); 52062306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 52162306a36Sopenharmony_ci#else 52262306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 52362306a36Sopenharmony_ci list_splice_init(&cp->rx_inuse_list, &list); 52462306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 52562306a36Sopenharmony_ci#endif 52662306a36Sopenharmony_ci list_for_each_safe(elem, tmp, &list) { 52762306a36Sopenharmony_ci cas_page_free(cp, list_entry(elem, cas_page_t, list)); 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci} 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* replenish spares if needed */ 53262306a36Sopenharmony_cistatic void cas_spare_recover(struct cas *cp, const gfp_t flags) 53362306a36Sopenharmony_ci{ 53462306a36Sopenharmony_ci struct list_head list, *elem, *tmp; 53562306a36Sopenharmony_ci int needed, i; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* check inuse list. if we don't need any more free buffers, 53862306a36Sopenharmony_ci * just free it 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* make a local copy of the list */ 54262306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 54362306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 54462306a36Sopenharmony_ci list_splice_init(&cp->rx_inuse_list, &list); 54562306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci list_for_each_safe(elem, tmp, &list) { 54862306a36Sopenharmony_ci cas_page_t *page = list_entry(elem, cas_page_t, list); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* 55162306a36Sopenharmony_ci * With the lockless pagecache, cassini buffering scheme gets 55262306a36Sopenharmony_ci * slightly less accurate: we might find that a page has an 55362306a36Sopenharmony_ci * elevated reference count here, due to a speculative ref, 55462306a36Sopenharmony_ci * and skip it as in-use. Ideally we would be able to reclaim 55562306a36Sopenharmony_ci * it. However this would be such a rare case, it doesn't 55662306a36Sopenharmony_ci * matter too much as we should pick it up the next time round. 55762306a36Sopenharmony_ci * 55862306a36Sopenharmony_ci * Importantly, if we find that the page has a refcount of 1 55962306a36Sopenharmony_ci * here (our refcount), then we know it is definitely not inuse 56062306a36Sopenharmony_ci * so we can reuse it. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci if (page_count(page->buffer) > 1) 56362306a36Sopenharmony_ci continue; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci list_del(elem); 56662306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 56762306a36Sopenharmony_ci if (cp->rx_spares_needed > 0) { 56862306a36Sopenharmony_ci list_add(elem, &cp->rx_spare_list); 56962306a36Sopenharmony_ci cp->rx_spares_needed--; 57062306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 57162306a36Sopenharmony_ci } else { 57262306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 57362306a36Sopenharmony_ci cas_page_free(cp, page); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* put any inuse buffers back on the list */ 57862306a36Sopenharmony_ci if (!list_empty(&list)) { 57962306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 58062306a36Sopenharmony_ci list_splice(&list, &cp->rx_inuse_list); 58162306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 58262306a36Sopenharmony_ci } 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 58562306a36Sopenharmony_ci needed = cp->rx_spares_needed; 58662306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 58762306a36Sopenharmony_ci if (!needed) 58862306a36Sopenharmony_ci return; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* we still need spares, so try to allocate some */ 59162306a36Sopenharmony_ci INIT_LIST_HEAD(&list); 59262306a36Sopenharmony_ci i = 0; 59362306a36Sopenharmony_ci while (i < needed) { 59462306a36Sopenharmony_ci cas_page_t *spare = cas_page_alloc(cp, flags); 59562306a36Sopenharmony_ci if (!spare) 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci list_add(&spare->list, &list); 59862306a36Sopenharmony_ci i++; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 60262306a36Sopenharmony_ci list_splice(&list, &cp->rx_spare_list); 60362306a36Sopenharmony_ci cp->rx_spares_needed -= i; 60462306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* pull a page from the list. */ 60862306a36Sopenharmony_cistatic cas_page_t *cas_page_dequeue(struct cas *cp) 60962306a36Sopenharmony_ci{ 61062306a36Sopenharmony_ci struct list_head *entry; 61162306a36Sopenharmony_ci int recover; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 61462306a36Sopenharmony_ci if (list_empty(&cp->rx_spare_list)) { 61562306a36Sopenharmony_ci /* try to do a quick recovery */ 61662306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 61762306a36Sopenharmony_ci cas_spare_recover(cp, GFP_ATOMIC); 61862306a36Sopenharmony_ci spin_lock(&cp->rx_spare_lock); 61962306a36Sopenharmony_ci if (list_empty(&cp->rx_spare_list)) { 62062306a36Sopenharmony_ci netif_err(cp, rx_err, cp->dev, 62162306a36Sopenharmony_ci "no spare buffers available\n"); 62262306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 62362306a36Sopenharmony_ci return NULL; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci entry = cp->rx_spare_list.next; 62862306a36Sopenharmony_ci list_del(entry); 62962306a36Sopenharmony_ci recover = ++cp->rx_spares_needed; 63062306a36Sopenharmony_ci spin_unlock(&cp->rx_spare_lock); 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci /* trigger the timer to do the recovery */ 63362306a36Sopenharmony_ci if ((recover & (RX_SPARE_RECOVER_VAL - 1)) == 0) { 63462306a36Sopenharmony_ci#if 1 63562306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 63662306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_spare); 63762306a36Sopenharmony_ci schedule_work(&cp->reset_task); 63862306a36Sopenharmony_ci#else 63962306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, CAS_RESET_SPARE); 64062306a36Sopenharmony_ci schedule_work(&cp->reset_task); 64162306a36Sopenharmony_ci#endif 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci return list_entry(entry, cas_page_t, list); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void cas_mif_poll(struct cas *cp, const int enable) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci u32 cfg; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci cfg = readl(cp->regs + REG_MIF_CFG); 65262306a36Sopenharmony_ci cfg &= (MIF_CFG_MDIO_0 | MIF_CFG_MDIO_1); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (cp->phy_type & CAS_PHY_MII_MDIO1) 65562306a36Sopenharmony_ci cfg |= MIF_CFG_PHY_SELECT; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci /* poll and interrupt on link status change. */ 65862306a36Sopenharmony_ci if (enable) { 65962306a36Sopenharmony_ci cfg |= MIF_CFG_POLL_EN; 66062306a36Sopenharmony_ci cfg |= CAS_BASE(MIF_CFG_POLL_REG, MII_BMSR); 66162306a36Sopenharmony_ci cfg |= CAS_BASE(MIF_CFG_POLL_PHY, cp->phy_addr); 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci writel((enable) ? ~(BMSR_LSTATUS | BMSR_ANEGCOMPLETE) : 0xFFFF, 66462306a36Sopenharmony_ci cp->regs + REG_MIF_MASK); 66562306a36Sopenharmony_ci writel(cfg, cp->regs + REG_MIF_CFG); 66662306a36Sopenharmony_ci} 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci/* Must be invoked under cp->lock */ 66962306a36Sopenharmony_cistatic void cas_begin_auto_negotiation(struct cas *cp, 67062306a36Sopenharmony_ci const struct ethtool_link_ksettings *ep) 67162306a36Sopenharmony_ci{ 67262306a36Sopenharmony_ci u16 ctl; 67362306a36Sopenharmony_ci#if 1 67462306a36Sopenharmony_ci int lcntl; 67562306a36Sopenharmony_ci int changed = 0; 67662306a36Sopenharmony_ci int oldstate = cp->lstate; 67762306a36Sopenharmony_ci int link_was_not_down = !(oldstate == link_down); 67862306a36Sopenharmony_ci#endif 67962306a36Sopenharmony_ci /* Setup link parameters */ 68062306a36Sopenharmony_ci if (!ep) 68162306a36Sopenharmony_ci goto start_aneg; 68262306a36Sopenharmony_ci lcntl = cp->link_cntl; 68362306a36Sopenharmony_ci if (ep->base.autoneg == AUTONEG_ENABLE) { 68462306a36Sopenharmony_ci cp->link_cntl = BMCR_ANENABLE; 68562306a36Sopenharmony_ci } else { 68662306a36Sopenharmony_ci u32 speed = ep->base.speed; 68762306a36Sopenharmony_ci cp->link_cntl = 0; 68862306a36Sopenharmony_ci if (speed == SPEED_100) 68962306a36Sopenharmony_ci cp->link_cntl |= BMCR_SPEED100; 69062306a36Sopenharmony_ci else if (speed == SPEED_1000) 69162306a36Sopenharmony_ci cp->link_cntl |= CAS_BMCR_SPEED1000; 69262306a36Sopenharmony_ci if (ep->base.duplex == DUPLEX_FULL) 69362306a36Sopenharmony_ci cp->link_cntl |= BMCR_FULLDPLX; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci#if 1 69662306a36Sopenharmony_ci changed = (lcntl != cp->link_cntl); 69762306a36Sopenharmony_ci#endif 69862306a36Sopenharmony_cistart_aneg: 69962306a36Sopenharmony_ci if (cp->lstate == link_up) { 70062306a36Sopenharmony_ci netdev_info(cp->dev, "PCS link down\n"); 70162306a36Sopenharmony_ci } else { 70262306a36Sopenharmony_ci if (changed) { 70362306a36Sopenharmony_ci netdev_info(cp->dev, "link configuration changed\n"); 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci cp->lstate = link_down; 70762306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_DOWN; 70862306a36Sopenharmony_ci if (!cp->hw_running) 70962306a36Sopenharmony_ci return; 71062306a36Sopenharmony_ci#if 1 71162306a36Sopenharmony_ci /* 71262306a36Sopenharmony_ci * WTZ: If the old state was link_up, we turn off the carrier 71362306a36Sopenharmony_ci * to replicate everything we do elsewhere on a link-down 71462306a36Sopenharmony_ci * event when we were already in a link-up state.. 71562306a36Sopenharmony_ci */ 71662306a36Sopenharmony_ci if (oldstate == link_up) 71762306a36Sopenharmony_ci netif_carrier_off(cp->dev); 71862306a36Sopenharmony_ci if (changed && link_was_not_down) { 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * WTZ: This branch will simply schedule a full reset after 72162306a36Sopenharmony_ci * we explicitly changed link modes in an ioctl. See if this 72262306a36Sopenharmony_ci * fixes the link-problems we were having for forced mode. 72362306a36Sopenharmony_ci */ 72462306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 72562306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_all); 72662306a36Sopenharmony_ci schedule_work(&cp->reset_task); 72762306a36Sopenharmony_ci cp->timer_ticks = 0; 72862306a36Sopenharmony_ci mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT); 72962306a36Sopenharmony_ci return; 73062306a36Sopenharmony_ci } 73162306a36Sopenharmony_ci#endif 73262306a36Sopenharmony_ci if (cp->phy_type & CAS_PHY_SERDES) { 73362306a36Sopenharmony_ci u32 val = readl(cp->regs + REG_PCS_MII_CTRL); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (cp->link_cntl & BMCR_ANENABLE) { 73662306a36Sopenharmony_ci val |= (PCS_MII_RESTART_AUTONEG | PCS_MII_AUTONEG_EN); 73762306a36Sopenharmony_ci cp->lstate = link_aneg; 73862306a36Sopenharmony_ci } else { 73962306a36Sopenharmony_ci if (cp->link_cntl & BMCR_FULLDPLX) 74062306a36Sopenharmony_ci val |= PCS_MII_CTRL_DUPLEX; 74162306a36Sopenharmony_ci val &= ~PCS_MII_AUTONEG_EN; 74262306a36Sopenharmony_ci cp->lstate = link_force_ok; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_CONFIG; 74562306a36Sopenharmony_ci writel(val, cp->regs + REG_PCS_MII_CTRL); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci } else { 74862306a36Sopenharmony_ci cas_mif_poll(cp, 0); 74962306a36Sopenharmony_ci ctl = cas_phy_read(cp, MII_BMCR); 75062306a36Sopenharmony_ci ctl &= ~(BMCR_FULLDPLX | BMCR_SPEED100 | 75162306a36Sopenharmony_ci CAS_BMCR_SPEED1000 | BMCR_ANENABLE); 75262306a36Sopenharmony_ci ctl |= cp->link_cntl; 75362306a36Sopenharmony_ci if (ctl & BMCR_ANENABLE) { 75462306a36Sopenharmony_ci ctl |= BMCR_ANRESTART; 75562306a36Sopenharmony_ci cp->lstate = link_aneg; 75662306a36Sopenharmony_ci } else { 75762306a36Sopenharmony_ci cp->lstate = link_force_ok; 75862306a36Sopenharmony_ci } 75962306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_CONFIG; 76062306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, ctl); 76162306a36Sopenharmony_ci cas_mif_poll(cp, 1); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci cp->timer_ticks = 0; 76562306a36Sopenharmony_ci mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 76962306a36Sopenharmony_cistatic int cas_reset_mii_phy(struct cas *cp) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci int limit = STOP_TRIES_PHY; 77262306a36Sopenharmony_ci u16 val; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, BMCR_RESET); 77562306a36Sopenharmony_ci udelay(100); 77662306a36Sopenharmony_ci while (--limit) { 77762306a36Sopenharmony_ci val = cas_phy_read(cp, MII_BMCR); 77862306a36Sopenharmony_ci if ((val & BMCR_RESET) == 0) 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci udelay(10); 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci return limit <= 0; 78362306a36Sopenharmony_ci} 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_cistatic void cas_saturn_firmware_init(struct cas *cp) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci const struct firmware *fw; 78862306a36Sopenharmony_ci const char fw_name[] = "sun/cassini.bin"; 78962306a36Sopenharmony_ci int err; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (PHY_NS_DP83065 != cp->phy_id) 79262306a36Sopenharmony_ci return; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci err = request_firmware(&fw, fw_name, &cp->pdev->dev); 79562306a36Sopenharmony_ci if (err) { 79662306a36Sopenharmony_ci pr_err("Failed to load firmware \"%s\"\n", 79762306a36Sopenharmony_ci fw_name); 79862306a36Sopenharmony_ci return; 79962306a36Sopenharmony_ci } 80062306a36Sopenharmony_ci if (fw->size < 2) { 80162306a36Sopenharmony_ci pr_err("bogus length %zu in \"%s\"\n", 80262306a36Sopenharmony_ci fw->size, fw_name); 80362306a36Sopenharmony_ci goto out; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci cp->fw_load_addr= fw->data[1] << 8 | fw->data[0]; 80662306a36Sopenharmony_ci cp->fw_size = fw->size - 2; 80762306a36Sopenharmony_ci cp->fw_data = vmalloc(cp->fw_size); 80862306a36Sopenharmony_ci if (!cp->fw_data) 80962306a36Sopenharmony_ci goto out; 81062306a36Sopenharmony_ci memcpy(cp->fw_data, &fw->data[2], cp->fw_size); 81162306a36Sopenharmony_ciout: 81262306a36Sopenharmony_ci release_firmware(fw); 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic void cas_saturn_firmware_load(struct cas *cp) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci int i; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (!cp->fw_data) 82062306a36Sopenharmony_ci return; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci cas_phy_powerdown(cp); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci /* expanded memory access mode */ 82562306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_MEM, 0x0); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* pointer configuration for new firmware */ 82862306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, 0x8ff9); 82962306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, 0xbd); 83062306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, 0x8ffa); 83162306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, 0x82); 83262306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, 0x8ffb); 83362306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, 0x0); 83462306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, 0x8ffc); 83562306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, 0x39); 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci /* download new firmware */ 83862306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_MEM, 0x1); 83962306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, cp->fw_load_addr); 84062306a36Sopenharmony_ci for (i = 0; i < cp->fw_size; i++) 84162306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, cp->fw_data[i]); 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* enable firmware */ 84462306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGE, 0x8ff8); 84562306a36Sopenharmony_ci cas_phy_write(cp, DP83065_MII_REGD, 0x1); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci/* phy initialization */ 85062306a36Sopenharmony_cistatic void cas_phy_init(struct cas *cp) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci u16 val; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* if we're in MII/GMII mode, set up phy */ 85562306a36Sopenharmony_ci if (CAS_PHY_MII(cp->phy_type)) { 85662306a36Sopenharmony_ci writel(PCS_DATAPATH_MODE_MII, 85762306a36Sopenharmony_ci cp->regs + REG_PCS_DATAPATH_MODE); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci cas_mif_poll(cp, 0); 86062306a36Sopenharmony_ci cas_reset_mii_phy(cp); /* take out of isolate mode */ 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci if (PHY_LUCENT_B0 == cp->phy_id) { 86362306a36Sopenharmony_ci /* workaround link up/down issue with lucent */ 86462306a36Sopenharmony_ci cas_phy_write(cp, LUCENT_MII_REG, 0x8000); 86562306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, 0x00f1); 86662306a36Sopenharmony_ci cas_phy_write(cp, LUCENT_MII_REG, 0x0); 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci } else if (PHY_BROADCOM_B0 == (cp->phy_id & 0xFFFFFFFC)) { 86962306a36Sopenharmony_ci /* workarounds for broadcom phy */ 87062306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG8, 0x0C20); 87162306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG7, 0x0012); 87262306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG5, 0x1804); 87362306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG7, 0x0013); 87462306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG5, 0x1204); 87562306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006); 87662306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG5, 0x0132); 87762306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG7, 0x8006); 87862306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG5, 0x0232); 87962306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG7, 0x201F); 88062306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG5, 0x0A20); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci } else if (PHY_BROADCOM_5411 == cp->phy_id) { 88362306a36Sopenharmony_ci val = cas_phy_read(cp, BROADCOM_MII_REG4); 88462306a36Sopenharmony_ci val = cas_phy_read(cp, BROADCOM_MII_REG4); 88562306a36Sopenharmony_ci if (val & 0x0080) { 88662306a36Sopenharmony_ci /* link workaround */ 88762306a36Sopenharmony_ci cas_phy_write(cp, BROADCOM_MII_REG4, 88862306a36Sopenharmony_ci val & ~0x0080); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci } else if (cp->cas_flags & CAS_FLAG_SATURN) { 89262306a36Sopenharmony_ci writel((cp->phy_type & CAS_PHY_MII_MDIO0) ? 89362306a36Sopenharmony_ci SATURN_PCFG_FSI : 0x0, 89462306a36Sopenharmony_ci cp->regs + REG_SATURN_PCFG); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci /* load firmware to address 10Mbps auto-negotiation 89762306a36Sopenharmony_ci * issue. NOTE: this will need to be changed if the 89862306a36Sopenharmony_ci * default firmware gets fixed. 89962306a36Sopenharmony_ci */ 90062306a36Sopenharmony_ci if (PHY_NS_DP83065 == cp->phy_id) { 90162306a36Sopenharmony_ci cas_saturn_firmware_load(cp); 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci cas_phy_powerup(cp); 90462306a36Sopenharmony_ci } 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* advertise capabilities */ 90762306a36Sopenharmony_ci val = cas_phy_read(cp, MII_BMCR); 90862306a36Sopenharmony_ci val &= ~BMCR_ANENABLE; 90962306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, val); 91062306a36Sopenharmony_ci udelay(10); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci cas_phy_write(cp, MII_ADVERTISE, 91362306a36Sopenharmony_ci cas_phy_read(cp, MII_ADVERTISE) | 91462306a36Sopenharmony_ci (ADVERTISE_10HALF | ADVERTISE_10FULL | 91562306a36Sopenharmony_ci ADVERTISE_100HALF | ADVERTISE_100FULL | 91662306a36Sopenharmony_ci CAS_ADVERTISE_PAUSE | 91762306a36Sopenharmony_ci CAS_ADVERTISE_ASYM_PAUSE)); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { 92062306a36Sopenharmony_ci /* make sure that we don't advertise half 92162306a36Sopenharmony_ci * duplex to avoid a chip issue 92262306a36Sopenharmony_ci */ 92362306a36Sopenharmony_ci val = cas_phy_read(cp, CAS_MII_1000_CTRL); 92462306a36Sopenharmony_ci val &= ~CAS_ADVERTISE_1000HALF; 92562306a36Sopenharmony_ci val |= CAS_ADVERTISE_1000FULL; 92662306a36Sopenharmony_ci cas_phy_write(cp, CAS_MII_1000_CTRL, val); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci } else { 93062306a36Sopenharmony_ci /* reset pcs for serdes */ 93162306a36Sopenharmony_ci u32 val; 93262306a36Sopenharmony_ci int limit; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci writel(PCS_DATAPATH_MODE_SERDES, 93562306a36Sopenharmony_ci cp->regs + REG_PCS_DATAPATH_MODE); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* enable serdes pins on saturn */ 93862306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_SATURN) 93962306a36Sopenharmony_ci writel(0, cp->regs + REG_SATURN_PCFG); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci /* Reset PCS unit. */ 94262306a36Sopenharmony_ci val = readl(cp->regs + REG_PCS_MII_CTRL); 94362306a36Sopenharmony_ci val |= PCS_MII_RESET; 94462306a36Sopenharmony_ci writel(val, cp->regs + REG_PCS_MII_CTRL); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci limit = STOP_TRIES; 94762306a36Sopenharmony_ci while (--limit > 0) { 94862306a36Sopenharmony_ci udelay(10); 94962306a36Sopenharmony_ci if ((readl(cp->regs + REG_PCS_MII_CTRL) & 95062306a36Sopenharmony_ci PCS_MII_RESET) == 0) 95162306a36Sopenharmony_ci break; 95262306a36Sopenharmony_ci } 95362306a36Sopenharmony_ci if (limit <= 0) 95462306a36Sopenharmony_ci netdev_warn(cp->dev, "PCS reset bit would not clear [%08x]\n", 95562306a36Sopenharmony_ci readl(cp->regs + REG_PCS_STATE_MACHINE)); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Make sure PCS is disabled while changing advertisement 95862306a36Sopenharmony_ci * configuration. 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci writel(0x0, cp->regs + REG_PCS_CFG); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci /* Advertise all capabilities except half-duplex. */ 96362306a36Sopenharmony_ci val = readl(cp->regs + REG_PCS_MII_ADVERT); 96462306a36Sopenharmony_ci val &= ~PCS_MII_ADVERT_HD; 96562306a36Sopenharmony_ci val |= (PCS_MII_ADVERT_FD | PCS_MII_ADVERT_SYM_PAUSE | 96662306a36Sopenharmony_ci PCS_MII_ADVERT_ASYM_PAUSE); 96762306a36Sopenharmony_ci writel(val, cp->regs + REG_PCS_MII_ADVERT); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci /* enable PCS */ 97062306a36Sopenharmony_ci writel(PCS_CFG_EN, cp->regs + REG_PCS_CFG); 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_ci /* pcs workaround: enable sync detect */ 97362306a36Sopenharmony_ci writel(PCS_SERDES_CTRL_SYNCD_EN, 97462306a36Sopenharmony_ci cp->regs + REG_PCS_SERDES_CTRL); 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci} 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic int cas_pcs_link_check(struct cas *cp) 98062306a36Sopenharmony_ci{ 98162306a36Sopenharmony_ci u32 stat, state_machine; 98262306a36Sopenharmony_ci int retval = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci /* The link status bit latches on zero, so you must 98562306a36Sopenharmony_ci * read it twice in such a case to see a transition 98662306a36Sopenharmony_ci * to the link being up. 98762306a36Sopenharmony_ci */ 98862306a36Sopenharmony_ci stat = readl(cp->regs + REG_PCS_MII_STATUS); 98962306a36Sopenharmony_ci if ((stat & PCS_MII_STATUS_LINK_STATUS) == 0) 99062306a36Sopenharmony_ci stat = readl(cp->regs + REG_PCS_MII_STATUS); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* The remote-fault indication is only valid 99362306a36Sopenharmony_ci * when autoneg has completed. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_ci if ((stat & (PCS_MII_STATUS_AUTONEG_COMP | 99662306a36Sopenharmony_ci PCS_MII_STATUS_REMOTE_FAULT)) == 99762306a36Sopenharmony_ci (PCS_MII_STATUS_AUTONEG_COMP | PCS_MII_STATUS_REMOTE_FAULT)) 99862306a36Sopenharmony_ci netif_info(cp, link, cp->dev, "PCS RemoteFault\n"); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci /* work around link detection issue by querying the PCS state 100162306a36Sopenharmony_ci * machine directly. 100262306a36Sopenharmony_ci */ 100362306a36Sopenharmony_ci state_machine = readl(cp->regs + REG_PCS_STATE_MACHINE); 100462306a36Sopenharmony_ci if ((state_machine & PCS_SM_LINK_STATE_MASK) != SM_LINK_STATE_UP) { 100562306a36Sopenharmony_ci stat &= ~PCS_MII_STATUS_LINK_STATUS; 100662306a36Sopenharmony_ci } else if (state_machine & PCS_SM_WORD_SYNC_STATE_MASK) { 100762306a36Sopenharmony_ci stat |= PCS_MII_STATUS_LINK_STATUS; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci if (stat & PCS_MII_STATUS_LINK_STATUS) { 101162306a36Sopenharmony_ci if (cp->lstate != link_up) { 101262306a36Sopenharmony_ci if (cp->opened) { 101362306a36Sopenharmony_ci cp->lstate = link_up; 101462306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_UP; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci cas_set_link_modes(cp); 101762306a36Sopenharmony_ci netif_carrier_on(cp->dev); 101862306a36Sopenharmony_ci } 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci } else if (cp->lstate == link_up) { 102162306a36Sopenharmony_ci cp->lstate = link_down; 102262306a36Sopenharmony_ci if (link_transition_timeout != 0 && 102362306a36Sopenharmony_ci cp->link_transition != LINK_TRANSITION_REQUESTED_RESET && 102462306a36Sopenharmony_ci !cp->link_transition_jiffies_valid) { 102562306a36Sopenharmony_ci /* 102662306a36Sopenharmony_ci * force a reset, as a workaround for the 102762306a36Sopenharmony_ci * link-failure problem. May want to move this to a 102862306a36Sopenharmony_ci * point a bit earlier in the sequence. If we had 102962306a36Sopenharmony_ci * generated a reset a short time ago, we'll wait for 103062306a36Sopenharmony_ci * the link timer to check the status until a 103162306a36Sopenharmony_ci * timer expires (link_transistion_jiffies_valid is 103262306a36Sopenharmony_ci * true when the timer is running.) Instead of using 103362306a36Sopenharmony_ci * a system timer, we just do a check whenever the 103462306a36Sopenharmony_ci * link timer is running - this clears the flag after 103562306a36Sopenharmony_ci * a suitable delay. 103662306a36Sopenharmony_ci */ 103762306a36Sopenharmony_ci retval = 1; 103862306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_REQUESTED_RESET; 103962306a36Sopenharmony_ci cp->link_transition_jiffies = jiffies; 104062306a36Sopenharmony_ci cp->link_transition_jiffies_valid = 1; 104162306a36Sopenharmony_ci } else { 104262306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_ON_FAILURE; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci netif_carrier_off(cp->dev); 104562306a36Sopenharmony_ci if (cp->opened) 104662306a36Sopenharmony_ci netif_info(cp, link, cp->dev, "PCS link down\n"); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci /* Cassini only: if you force a mode, there can be 104962306a36Sopenharmony_ci * sync problems on link down. to fix that, the following 105062306a36Sopenharmony_ci * things need to be checked: 105162306a36Sopenharmony_ci * 1) read serialink state register 105262306a36Sopenharmony_ci * 2) read pcs status register to verify link down. 105362306a36Sopenharmony_ci * 3) if link down and serial link == 0x03, then you need 105462306a36Sopenharmony_ci * to global reset the chip. 105562306a36Sopenharmony_ci */ 105662306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0) { 105762306a36Sopenharmony_ci /* should check to see if we're in a forced mode */ 105862306a36Sopenharmony_ci stat = readl(cp->regs + REG_PCS_SERDES_STATE); 105962306a36Sopenharmony_ci if (stat == 0x03) 106062306a36Sopenharmony_ci return 1; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci } else if (cp->lstate == link_down) { 106362306a36Sopenharmony_ci if (link_transition_timeout != 0 && 106462306a36Sopenharmony_ci cp->link_transition != LINK_TRANSITION_REQUESTED_RESET && 106562306a36Sopenharmony_ci !cp->link_transition_jiffies_valid) { 106662306a36Sopenharmony_ci /* force a reset, as a workaround for the 106762306a36Sopenharmony_ci * link-failure problem. May want to move 106862306a36Sopenharmony_ci * this to a point a bit earlier in the 106962306a36Sopenharmony_ci * sequence. 107062306a36Sopenharmony_ci */ 107162306a36Sopenharmony_ci retval = 1; 107262306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_REQUESTED_RESET; 107362306a36Sopenharmony_ci cp->link_transition_jiffies = jiffies; 107462306a36Sopenharmony_ci cp->link_transition_jiffies_valid = 1; 107562306a36Sopenharmony_ci } else { 107662306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_STILL_FAILED; 107762306a36Sopenharmony_ci } 107862306a36Sopenharmony_ci } 107962306a36Sopenharmony_ci 108062306a36Sopenharmony_ci return retval; 108162306a36Sopenharmony_ci} 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_cistatic int cas_pcs_interrupt(struct net_device *dev, 108462306a36Sopenharmony_ci struct cas *cp, u32 status) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci u32 stat = readl(cp->regs + REG_PCS_INTR_STATUS); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if ((stat & PCS_INTR_STATUS_LINK_CHANGE) == 0) 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci return cas_pcs_link_check(cp); 109162306a36Sopenharmony_ci} 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_cistatic int cas_txmac_interrupt(struct net_device *dev, 109462306a36Sopenharmony_ci struct cas *cp, u32 status) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci u32 txmac_stat = readl(cp->regs + REG_MAC_TX_STATUS); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (!txmac_stat) 109962306a36Sopenharmony_ci return 0; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, cp->dev, 110262306a36Sopenharmony_ci "txmac interrupt, txmac_stat: 0x%x\n", txmac_stat); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* Defer timer expiration is quite normal, 110562306a36Sopenharmony_ci * don't even log the event. 110662306a36Sopenharmony_ci */ 110762306a36Sopenharmony_ci if ((txmac_stat & MAC_TX_DEFER_TIMER) && 110862306a36Sopenharmony_ci !(txmac_stat & ~MAC_TX_DEFER_TIMER)) 110962306a36Sopenharmony_ci return 0; 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 111262306a36Sopenharmony_ci if (txmac_stat & MAC_TX_UNDERRUN) { 111362306a36Sopenharmony_ci netdev_err(dev, "TX MAC xmit underrun\n"); 111462306a36Sopenharmony_ci cp->net_stats[0].tx_fifo_errors++; 111562306a36Sopenharmony_ci } 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci if (txmac_stat & MAC_TX_MAX_PACKET_ERR) { 111862306a36Sopenharmony_ci netdev_err(dev, "TX MAC max packet size error\n"); 111962306a36Sopenharmony_ci cp->net_stats[0].tx_errors++; 112062306a36Sopenharmony_ci } 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* The rest are all cases of one of the 16-bit TX 112362306a36Sopenharmony_ci * counters expiring. 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_ci if (txmac_stat & MAC_TX_COLL_NORMAL) 112662306a36Sopenharmony_ci cp->net_stats[0].collisions += 0x10000; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci if (txmac_stat & MAC_TX_COLL_EXCESS) { 112962306a36Sopenharmony_ci cp->net_stats[0].tx_aborted_errors += 0x10000; 113062306a36Sopenharmony_ci cp->net_stats[0].collisions += 0x10000; 113162306a36Sopenharmony_ci } 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if (txmac_stat & MAC_TX_COLL_LATE) { 113462306a36Sopenharmony_ci cp->net_stats[0].tx_aborted_errors += 0x10000; 113562306a36Sopenharmony_ci cp->net_stats[0].collisions += 0x10000; 113662306a36Sopenharmony_ci } 113762306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* We do not keep track of MAC_TX_COLL_FIRST and 114062306a36Sopenharmony_ci * MAC_TX_PEAK_ATTEMPTS events. 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci return 0; 114362306a36Sopenharmony_ci} 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_cistatic void cas_load_firmware(struct cas *cp, cas_hp_inst_t *firmware) 114662306a36Sopenharmony_ci{ 114762306a36Sopenharmony_ci cas_hp_inst_t *inst; 114862306a36Sopenharmony_ci u32 val; 114962306a36Sopenharmony_ci int i; 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci i = 0; 115262306a36Sopenharmony_ci while ((inst = firmware) && inst->note) { 115362306a36Sopenharmony_ci writel(i, cp->regs + REG_HP_INSTR_RAM_ADDR); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci val = CAS_BASE(HP_INSTR_RAM_HI_VAL, inst->val); 115662306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_HI_MASK, inst->mask); 115762306a36Sopenharmony_ci writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_HI); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci val = CAS_BASE(HP_INSTR_RAM_MID_OUTARG, inst->outarg >> 10); 116062306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_OUTOP, inst->outop); 116162306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_FNEXT, inst->fnext); 116262306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_FOFF, inst->foff); 116362306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_SNEXT, inst->snext); 116462306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_SOFF, inst->soff); 116562306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_MID_OP, inst->op); 116662306a36Sopenharmony_ci writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_MID); 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci val = CAS_BASE(HP_INSTR_RAM_LOW_OUTMASK, inst->outmask); 116962306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTSHIFT, inst->outshift); 117062306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTEN, inst->outenab); 117162306a36Sopenharmony_ci val |= CAS_BASE(HP_INSTR_RAM_LOW_OUTARG, inst->outarg); 117262306a36Sopenharmony_ci writel(val, cp->regs + REG_HP_INSTR_RAM_DATA_LOW); 117362306a36Sopenharmony_ci ++firmware; 117462306a36Sopenharmony_ci ++i; 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic void cas_init_rx_dma(struct cas *cp) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci u64 desc_dma = cp->block_dvma; 118162306a36Sopenharmony_ci u32 val; 118262306a36Sopenharmony_ci int i, size; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci /* rx free descriptors */ 118562306a36Sopenharmony_ci val = CAS_BASE(RX_CFG_SWIVEL, RX_SWIVEL_OFF_VAL); 118662306a36Sopenharmony_ci val |= CAS_BASE(RX_CFG_DESC_RING, RX_DESC_RINGN_INDEX(0)); 118762306a36Sopenharmony_ci val |= CAS_BASE(RX_CFG_COMP_RING, RX_COMP_RINGN_INDEX(0)); 118862306a36Sopenharmony_ci if ((N_RX_DESC_RINGS > 1) && 118962306a36Sopenharmony_ci (cp->cas_flags & CAS_FLAG_REG_PLUS)) /* do desc 2 */ 119062306a36Sopenharmony_ci val |= CAS_BASE(RX_CFG_DESC_RING1, RX_DESC_RINGN_INDEX(1)); 119162306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_CFG); 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci val = (unsigned long) cp->init_rxds[0] - 119462306a36Sopenharmony_ci (unsigned long) cp->init_block; 119562306a36Sopenharmony_ci writel((desc_dma + val) >> 32, cp->regs + REG_RX_DB_HI); 119662306a36Sopenharmony_ci writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_DB_LOW); 119762306a36Sopenharmony_ci writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK); 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 120062306a36Sopenharmony_ci /* rx desc 2 is for IPSEC packets. however, 120162306a36Sopenharmony_ci * we don't it that for that purpose. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci val = (unsigned long) cp->init_rxds[1] - 120462306a36Sopenharmony_ci (unsigned long) cp->init_block; 120562306a36Sopenharmony_ci writel((desc_dma + val) >> 32, cp->regs + REG_PLUS_RX_DB1_HI); 120662306a36Sopenharmony_ci writel((desc_dma + val) & 0xffffffff, cp->regs + 120762306a36Sopenharmony_ci REG_PLUS_RX_DB1_LOW); 120862306a36Sopenharmony_ci writel(RX_DESC_RINGN_SIZE(1) - 4, cp->regs + 120962306a36Sopenharmony_ci REG_PLUS_RX_KICK1); 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci /* rx completion registers */ 121362306a36Sopenharmony_ci val = (unsigned long) cp->init_rxcs[0] - 121462306a36Sopenharmony_ci (unsigned long) cp->init_block; 121562306a36Sopenharmony_ci writel((desc_dma + val) >> 32, cp->regs + REG_RX_CB_HI); 121662306a36Sopenharmony_ci writel((desc_dma + val) & 0xffffffff, cp->regs + REG_RX_CB_LOW); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 121962306a36Sopenharmony_ci /* rx comp 2-4 */ 122062306a36Sopenharmony_ci for (i = 1; i < MAX_RX_COMP_RINGS; i++) { 122162306a36Sopenharmony_ci val = (unsigned long) cp->init_rxcs[i] - 122262306a36Sopenharmony_ci (unsigned long) cp->init_block; 122362306a36Sopenharmony_ci writel((desc_dma + val) >> 32, cp->regs + 122462306a36Sopenharmony_ci REG_PLUS_RX_CBN_HI(i)); 122562306a36Sopenharmony_ci writel((desc_dma + val) & 0xffffffff, cp->regs + 122662306a36Sopenharmony_ci REG_PLUS_RX_CBN_LOW(i)); 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci /* read selective clear regs to prevent spurious interrupts 123162306a36Sopenharmony_ci * on reset because complete == kick. 123262306a36Sopenharmony_ci * selective clear set up to prevent interrupts on resets 123362306a36Sopenharmony_ci */ 123462306a36Sopenharmony_ci readl(cp->regs + REG_INTR_STATUS_ALIAS); 123562306a36Sopenharmony_ci writel(INTR_RX_DONE | INTR_RX_BUF_UNAVAIL, cp->regs + REG_ALIAS_CLEAR); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci /* set up pause thresholds */ 123862306a36Sopenharmony_ci val = CAS_BASE(RX_PAUSE_THRESH_OFF, 123962306a36Sopenharmony_ci cp->rx_pause_off / RX_PAUSE_THRESH_QUANTUM); 124062306a36Sopenharmony_ci val |= CAS_BASE(RX_PAUSE_THRESH_ON, 124162306a36Sopenharmony_ci cp->rx_pause_on / RX_PAUSE_THRESH_QUANTUM); 124262306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_PAUSE_THRESH); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci /* zero out dma reassembly buffers */ 124562306a36Sopenharmony_ci for (i = 0; i < 64; i++) { 124662306a36Sopenharmony_ci writel(i, cp->regs + REG_RX_TABLE_ADDR); 124762306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_TABLE_DATA_LOW); 124862306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_TABLE_DATA_MID); 124962306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_TABLE_DATA_HI); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci /* make sure address register is 0 for normal operation */ 125362306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_CTRL_FIFO_ADDR); 125462306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_IPP_FIFO_ADDR); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci /* interrupt mitigation */ 125762306a36Sopenharmony_ci#ifdef USE_RX_BLANK 125862306a36Sopenharmony_ci val = CAS_BASE(RX_BLANK_INTR_TIME, RX_BLANK_INTR_TIME_VAL); 125962306a36Sopenharmony_ci val |= CAS_BASE(RX_BLANK_INTR_PKT, RX_BLANK_INTR_PKT_VAL); 126062306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_BLANK); 126162306a36Sopenharmony_ci#else 126262306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_BLANK); 126362306a36Sopenharmony_ci#endif 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci /* interrupt generation as a function of low water marks for 126662306a36Sopenharmony_ci * free desc and completion entries. these are used to trigger 126762306a36Sopenharmony_ci * housekeeping for rx descs. we don't use the free interrupt 126862306a36Sopenharmony_ci * as it's not very useful 126962306a36Sopenharmony_ci */ 127062306a36Sopenharmony_ci /* val = CAS_BASE(RX_AE_THRESH_FREE, RX_AE_FREEN_VAL(0)); */ 127162306a36Sopenharmony_ci val = CAS_BASE(RX_AE_THRESH_COMP, RX_AE_COMP_VAL); 127262306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_AE_THRESH); 127362306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 127462306a36Sopenharmony_ci val = CAS_BASE(RX_AE1_THRESH_FREE, RX_AE_FREEN_VAL(1)); 127562306a36Sopenharmony_ci writel(val, cp->regs + REG_PLUS_RX_AE1_THRESH); 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* Random early detect registers. useful for congestion avoidance. 127962306a36Sopenharmony_ci * this should be tunable. 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci writel(0x0, cp->regs + REG_RX_RED); 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_ci /* receive page sizes. default == 2K (0x800) */ 128462306a36Sopenharmony_ci val = 0; 128562306a36Sopenharmony_ci if (cp->page_size == 0x1000) 128662306a36Sopenharmony_ci val = 0x1; 128762306a36Sopenharmony_ci else if (cp->page_size == 0x2000) 128862306a36Sopenharmony_ci val = 0x2; 128962306a36Sopenharmony_ci else if (cp->page_size == 0x4000) 129062306a36Sopenharmony_ci val = 0x3; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci /* round mtu + offset. constrain to page size. */ 129362306a36Sopenharmony_ci size = cp->dev->mtu + 64; 129462306a36Sopenharmony_ci if (size > cp->page_size) 129562306a36Sopenharmony_ci size = cp->page_size; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (size <= 0x400) 129862306a36Sopenharmony_ci i = 0x0; 129962306a36Sopenharmony_ci else if (size <= 0x800) 130062306a36Sopenharmony_ci i = 0x1; 130162306a36Sopenharmony_ci else if (size <= 0x1000) 130262306a36Sopenharmony_ci i = 0x2; 130362306a36Sopenharmony_ci else 130462306a36Sopenharmony_ci i = 0x3; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci cp->mtu_stride = 1 << (i + 10); 130762306a36Sopenharmony_ci val = CAS_BASE(RX_PAGE_SIZE, val); 130862306a36Sopenharmony_ci val |= CAS_BASE(RX_PAGE_SIZE_MTU_STRIDE, i); 130962306a36Sopenharmony_ci val |= CAS_BASE(RX_PAGE_SIZE_MTU_COUNT, cp->page_size >> (i + 10)); 131062306a36Sopenharmony_ci val |= CAS_BASE(RX_PAGE_SIZE_MTU_OFF, 0x1); 131162306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_PAGE_SIZE); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* enable the header parser if desired */ 131462306a36Sopenharmony_ci if (&CAS_HP_FIRMWARE[0] == &cas_prog_null[0]) 131562306a36Sopenharmony_ci return; 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci val = CAS_BASE(HP_CFG_NUM_CPU, CAS_NCPUS > 63 ? 0 : CAS_NCPUS); 131862306a36Sopenharmony_ci val |= HP_CFG_PARSE_EN | HP_CFG_SYN_INC_MASK; 131962306a36Sopenharmony_ci val |= CAS_BASE(HP_CFG_TCP_THRESH, HP_TCP_THRESH_VAL); 132062306a36Sopenharmony_ci writel(val, cp->regs + REG_HP_CFG); 132162306a36Sopenharmony_ci} 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_cistatic inline void cas_rxc_init(struct cas_rx_comp *rxc) 132462306a36Sopenharmony_ci{ 132562306a36Sopenharmony_ci memset(rxc, 0, sizeof(*rxc)); 132662306a36Sopenharmony_ci rxc->word4 = cpu_to_le64(RX_COMP4_ZERO); 132762306a36Sopenharmony_ci} 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci/* NOTE: we use the ENC RX DESC ring for spares. the rx_page[0,1] 133062306a36Sopenharmony_ci * flipping is protected by the fact that the chip will not 133162306a36Sopenharmony_ci * hand back the same page index while it's being processed. 133262306a36Sopenharmony_ci */ 133362306a36Sopenharmony_cistatic inline cas_page_t *cas_page_spare(struct cas *cp, const int index) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci cas_page_t *page = cp->rx_pages[1][index]; 133662306a36Sopenharmony_ci cas_page_t *new; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci if (page_count(page->buffer) == 1) 133962306a36Sopenharmony_ci return page; 134062306a36Sopenharmony_ci 134162306a36Sopenharmony_ci new = cas_page_dequeue(cp); 134262306a36Sopenharmony_ci if (new) { 134362306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 134462306a36Sopenharmony_ci list_add(&page->list, &cp->rx_inuse_list); 134562306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci return new; 134862306a36Sopenharmony_ci} 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_ci/* this needs to be changed if we actually use the ENC RX DESC ring */ 135162306a36Sopenharmony_cistatic cas_page_t *cas_page_swap(struct cas *cp, const int ring, 135262306a36Sopenharmony_ci const int index) 135362306a36Sopenharmony_ci{ 135462306a36Sopenharmony_ci cas_page_t **page0 = cp->rx_pages[0]; 135562306a36Sopenharmony_ci cas_page_t **page1 = cp->rx_pages[1]; 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci /* swap if buffer is in use */ 135862306a36Sopenharmony_ci if (page_count(page0[index]->buffer) > 1) { 135962306a36Sopenharmony_ci cas_page_t *new = cas_page_spare(cp, index); 136062306a36Sopenharmony_ci if (new) { 136162306a36Sopenharmony_ci page1[index] = page0[index]; 136262306a36Sopenharmony_ci page0[index] = new; 136362306a36Sopenharmony_ci } 136462306a36Sopenharmony_ci } 136562306a36Sopenharmony_ci RX_USED_SET(page0[index], 0); 136662306a36Sopenharmony_ci return page0[index]; 136762306a36Sopenharmony_ci} 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_cistatic void cas_clean_rxds(struct cas *cp) 137062306a36Sopenharmony_ci{ 137162306a36Sopenharmony_ci /* only clean ring 0 as ring 1 is used for spare buffers */ 137262306a36Sopenharmony_ci struct cas_rx_desc *rxd = cp->init_rxds[0]; 137362306a36Sopenharmony_ci int i, size; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci /* release all rx flows */ 137662306a36Sopenharmony_ci for (i = 0; i < N_RX_FLOWS; i++) { 137762306a36Sopenharmony_ci struct sk_buff *skb; 137862306a36Sopenharmony_ci while ((skb = __skb_dequeue(&cp->rx_flows[i]))) { 137962306a36Sopenharmony_ci cas_skb_release(skb); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci /* initialize descriptors */ 138462306a36Sopenharmony_ci size = RX_DESC_RINGN_SIZE(0); 138562306a36Sopenharmony_ci for (i = 0; i < size; i++) { 138662306a36Sopenharmony_ci cas_page_t *page = cas_page_swap(cp, 0, i); 138762306a36Sopenharmony_ci rxd[i].buffer = cpu_to_le64(page->dma_addr); 138862306a36Sopenharmony_ci rxd[i].index = cpu_to_le64(CAS_BASE(RX_INDEX_NUM, i) | 138962306a36Sopenharmony_ci CAS_BASE(RX_INDEX_RING, 0)); 139062306a36Sopenharmony_ci } 139162306a36Sopenharmony_ci 139262306a36Sopenharmony_ci cp->rx_old[0] = RX_DESC_RINGN_SIZE(0) - 4; 139362306a36Sopenharmony_ci cp->rx_last[0] = 0; 139462306a36Sopenharmony_ci cp->cas_flags &= ~CAS_FLAG_RXD_POST(0); 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_cistatic void cas_clean_rxcs(struct cas *cp) 139862306a36Sopenharmony_ci{ 139962306a36Sopenharmony_ci int i, j; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci /* take ownership of rx comp descriptors */ 140262306a36Sopenharmony_ci memset(cp->rx_cur, 0, sizeof(*cp->rx_cur)*N_RX_COMP_RINGS); 140362306a36Sopenharmony_ci memset(cp->rx_new, 0, sizeof(*cp->rx_new)*N_RX_COMP_RINGS); 140462306a36Sopenharmony_ci for (i = 0; i < N_RX_COMP_RINGS; i++) { 140562306a36Sopenharmony_ci struct cas_rx_comp *rxc = cp->init_rxcs[i]; 140662306a36Sopenharmony_ci for (j = 0; j < RX_COMP_RINGN_SIZE(i); j++) { 140762306a36Sopenharmony_ci cas_rxc_init(rxc + j); 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci } 141062306a36Sopenharmony_ci} 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci#if 0 141362306a36Sopenharmony_ci/* When we get a RX fifo overflow, the RX unit is probably hung 141462306a36Sopenharmony_ci * so we do the following. 141562306a36Sopenharmony_ci * 141662306a36Sopenharmony_ci * If any part of the reset goes wrong, we return 1 and that causes the 141762306a36Sopenharmony_ci * whole chip to be reset. 141862306a36Sopenharmony_ci */ 141962306a36Sopenharmony_cistatic int cas_rxmac_reset(struct cas *cp) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct net_device *dev = cp->dev; 142262306a36Sopenharmony_ci int limit; 142362306a36Sopenharmony_ci u32 val; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* First, reset MAC RX. */ 142662306a36Sopenharmony_ci writel(cp->mac_rx_cfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG); 142762306a36Sopenharmony_ci for (limit = 0; limit < STOP_TRIES; limit++) { 142862306a36Sopenharmony_ci if (!(readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN)) 142962306a36Sopenharmony_ci break; 143062306a36Sopenharmony_ci udelay(10); 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci if (limit == STOP_TRIES) { 143362306a36Sopenharmony_ci netdev_err(dev, "RX MAC will not disable, resetting whole chip\n"); 143462306a36Sopenharmony_ci return 1; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci /* Second, disable RX DMA. */ 143862306a36Sopenharmony_ci writel(0, cp->regs + REG_RX_CFG); 143962306a36Sopenharmony_ci for (limit = 0; limit < STOP_TRIES; limit++) { 144062306a36Sopenharmony_ci if (!(readl(cp->regs + REG_RX_CFG) & RX_CFG_DMA_EN)) 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci udelay(10); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci if (limit == STOP_TRIES) { 144562306a36Sopenharmony_ci netdev_err(dev, "RX DMA will not disable, resetting whole chip\n"); 144662306a36Sopenharmony_ci return 1; 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci mdelay(5); 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci /* Execute RX reset command. */ 145262306a36Sopenharmony_ci writel(SW_RESET_RX, cp->regs + REG_SW_RESET); 145362306a36Sopenharmony_ci for (limit = 0; limit < STOP_TRIES; limit++) { 145462306a36Sopenharmony_ci if (!(readl(cp->regs + REG_SW_RESET) & SW_RESET_RX)) 145562306a36Sopenharmony_ci break; 145662306a36Sopenharmony_ci udelay(10); 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci if (limit == STOP_TRIES) { 145962306a36Sopenharmony_ci netdev_err(dev, "RX reset command will not execute, resetting whole chip\n"); 146062306a36Sopenharmony_ci return 1; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci /* reset driver rx state */ 146462306a36Sopenharmony_ci cas_clean_rxds(cp); 146562306a36Sopenharmony_ci cas_clean_rxcs(cp); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* Now, reprogram the rest of RX unit. */ 146862306a36Sopenharmony_ci cas_init_rx_dma(cp); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* re-enable */ 147162306a36Sopenharmony_ci val = readl(cp->regs + REG_RX_CFG); 147262306a36Sopenharmony_ci writel(val | RX_CFG_DMA_EN, cp->regs + REG_RX_CFG); 147362306a36Sopenharmony_ci writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK); 147462306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_RX_CFG); 147562306a36Sopenharmony_ci writel(val | MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG); 147662306a36Sopenharmony_ci return 0; 147762306a36Sopenharmony_ci} 147862306a36Sopenharmony_ci#endif 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_cistatic int cas_rxmac_interrupt(struct net_device *dev, struct cas *cp, 148162306a36Sopenharmony_ci u32 status) 148262306a36Sopenharmony_ci{ 148362306a36Sopenharmony_ci u32 stat = readl(cp->regs + REG_MAC_RX_STATUS); 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci if (!stat) 148662306a36Sopenharmony_ci return 0; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci netif_dbg(cp, intr, cp->dev, "rxmac interrupt, stat: 0x%x\n", stat); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* these are all rollovers */ 149162306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 149262306a36Sopenharmony_ci if (stat & MAC_RX_ALIGN_ERR) 149362306a36Sopenharmony_ci cp->net_stats[0].rx_frame_errors += 0x10000; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci if (stat & MAC_RX_CRC_ERR) 149662306a36Sopenharmony_ci cp->net_stats[0].rx_crc_errors += 0x10000; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci if (stat & MAC_RX_LEN_ERR) 149962306a36Sopenharmony_ci cp->net_stats[0].rx_length_errors += 0x10000; 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci if (stat & MAC_RX_OVERFLOW) { 150262306a36Sopenharmony_ci cp->net_stats[0].rx_over_errors++; 150362306a36Sopenharmony_ci cp->net_stats[0].rx_fifo_errors++; 150462306a36Sopenharmony_ci } 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* We do not track MAC_RX_FRAME_COUNT and MAC_RX_VIOL_ERR 150762306a36Sopenharmony_ci * events. 150862306a36Sopenharmony_ci */ 150962306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 151062306a36Sopenharmony_ci return 0; 151162306a36Sopenharmony_ci} 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic int cas_mac_interrupt(struct net_device *dev, struct cas *cp, 151462306a36Sopenharmony_ci u32 status) 151562306a36Sopenharmony_ci{ 151662306a36Sopenharmony_ci u32 stat = readl(cp->regs + REG_MAC_CTRL_STATUS); 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci if (!stat) 151962306a36Sopenharmony_ci return 0; 152062306a36Sopenharmony_ci 152162306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, cp->dev, 152262306a36Sopenharmony_ci "mac interrupt, stat: 0x%x\n", stat); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci /* This interrupt is just for pause frame and pause 152562306a36Sopenharmony_ci * tracking. It is useful for diagnostics and debug 152662306a36Sopenharmony_ci * but probably by default we will mask these events. 152762306a36Sopenharmony_ci */ 152862306a36Sopenharmony_ci if (stat & MAC_CTRL_PAUSE_STATE) 152962306a36Sopenharmony_ci cp->pause_entered++; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci if (stat & MAC_CTRL_PAUSE_RECEIVED) 153262306a36Sopenharmony_ci cp->pause_last_time_recvd = (stat >> 16); 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_ci return 0; 153562306a36Sopenharmony_ci} 153662306a36Sopenharmony_ci 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 153962306a36Sopenharmony_cistatic inline int cas_mdio_link_not_up(struct cas *cp) 154062306a36Sopenharmony_ci{ 154162306a36Sopenharmony_ci u16 val; 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci switch (cp->lstate) { 154462306a36Sopenharmony_ci case link_force_ret: 154562306a36Sopenharmony_ci netif_info(cp, link, cp->dev, "Autoneg failed again, keeping forced mode\n"); 154662306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, cp->link_fcntl); 154762306a36Sopenharmony_ci cp->timer_ticks = 5; 154862306a36Sopenharmony_ci cp->lstate = link_force_ok; 154962306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_CONFIG; 155062306a36Sopenharmony_ci break; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci case link_aneg: 155362306a36Sopenharmony_ci val = cas_phy_read(cp, MII_BMCR); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci /* Try forced modes. we try things in the following order: 155662306a36Sopenharmony_ci * 1000 full -> 100 full/half -> 10 half 155762306a36Sopenharmony_ci */ 155862306a36Sopenharmony_ci val &= ~(BMCR_ANRESTART | BMCR_ANENABLE); 155962306a36Sopenharmony_ci val |= BMCR_FULLDPLX; 156062306a36Sopenharmony_ci val |= (cp->cas_flags & CAS_FLAG_1000MB_CAP) ? 156162306a36Sopenharmony_ci CAS_BMCR_SPEED1000 : BMCR_SPEED100; 156262306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, val); 156362306a36Sopenharmony_ci cp->timer_ticks = 5; 156462306a36Sopenharmony_ci cp->lstate = link_force_try; 156562306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_CONFIG; 156662306a36Sopenharmony_ci break; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci case link_force_try: 156962306a36Sopenharmony_ci /* Downgrade from 1000 to 100 to 10 Mbps if necessary. */ 157062306a36Sopenharmony_ci val = cas_phy_read(cp, MII_BMCR); 157162306a36Sopenharmony_ci cp->timer_ticks = 5; 157262306a36Sopenharmony_ci if (val & CAS_BMCR_SPEED1000) { /* gigabit */ 157362306a36Sopenharmony_ci val &= ~CAS_BMCR_SPEED1000; 157462306a36Sopenharmony_ci val |= (BMCR_SPEED100 | BMCR_FULLDPLX); 157562306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, val); 157662306a36Sopenharmony_ci break; 157762306a36Sopenharmony_ci } 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci if (val & BMCR_SPEED100) { 158062306a36Sopenharmony_ci if (val & BMCR_FULLDPLX) /* fd failed */ 158162306a36Sopenharmony_ci val &= ~BMCR_FULLDPLX; 158262306a36Sopenharmony_ci else { /* 100Mbps failed */ 158362306a36Sopenharmony_ci val &= ~BMCR_SPEED100; 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, val); 158662306a36Sopenharmony_ci break; 158762306a36Sopenharmony_ci } 158862306a36Sopenharmony_ci break; 158962306a36Sopenharmony_ci default: 159062306a36Sopenharmony_ci break; 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci return 0; 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci/* must be invoked with cp->lock held */ 159762306a36Sopenharmony_cistatic int cas_mii_link_check(struct cas *cp, const u16 bmsr) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci int restart; 160062306a36Sopenharmony_ci 160162306a36Sopenharmony_ci if (bmsr & BMSR_LSTATUS) { 160262306a36Sopenharmony_ci /* Ok, here we got a link. If we had it due to a forced 160362306a36Sopenharmony_ci * fallback, and we were configured for autoneg, we 160462306a36Sopenharmony_ci * retry a short autoneg pass. If you know your hub is 160562306a36Sopenharmony_ci * broken, use ethtool ;) 160662306a36Sopenharmony_ci */ 160762306a36Sopenharmony_ci if ((cp->lstate == link_force_try) && 160862306a36Sopenharmony_ci (cp->link_cntl & BMCR_ANENABLE)) { 160962306a36Sopenharmony_ci cp->lstate = link_force_ret; 161062306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_CONFIG; 161162306a36Sopenharmony_ci cas_mif_poll(cp, 0); 161262306a36Sopenharmony_ci cp->link_fcntl = cas_phy_read(cp, MII_BMCR); 161362306a36Sopenharmony_ci cp->timer_ticks = 5; 161462306a36Sopenharmony_ci if (cp->opened) 161562306a36Sopenharmony_ci netif_info(cp, link, cp->dev, 161662306a36Sopenharmony_ci "Got link after fallback, retrying autoneg once...\n"); 161762306a36Sopenharmony_ci cas_phy_write(cp, MII_BMCR, 161862306a36Sopenharmony_ci cp->link_fcntl | BMCR_ANENABLE | 161962306a36Sopenharmony_ci BMCR_ANRESTART); 162062306a36Sopenharmony_ci cas_mif_poll(cp, 1); 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci } else if (cp->lstate != link_up) { 162362306a36Sopenharmony_ci cp->lstate = link_up; 162462306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_UP; 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (cp->opened) { 162762306a36Sopenharmony_ci cas_set_link_modes(cp); 162862306a36Sopenharmony_ci netif_carrier_on(cp->dev); 162962306a36Sopenharmony_ci } 163062306a36Sopenharmony_ci } 163162306a36Sopenharmony_ci return 0; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci /* link not up. if the link was previously up, we restart the 163562306a36Sopenharmony_ci * whole process 163662306a36Sopenharmony_ci */ 163762306a36Sopenharmony_ci restart = 0; 163862306a36Sopenharmony_ci if (cp->lstate == link_up) { 163962306a36Sopenharmony_ci cp->lstate = link_down; 164062306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_DOWN; 164162306a36Sopenharmony_ci 164262306a36Sopenharmony_ci netif_carrier_off(cp->dev); 164362306a36Sopenharmony_ci if (cp->opened) 164462306a36Sopenharmony_ci netif_info(cp, link, cp->dev, "Link down\n"); 164562306a36Sopenharmony_ci restart = 1; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci } else if (++cp->timer_ticks > 10) 164862306a36Sopenharmony_ci cas_mdio_link_not_up(cp); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci return restart; 165162306a36Sopenharmony_ci} 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_cistatic int cas_mif_interrupt(struct net_device *dev, struct cas *cp, 165462306a36Sopenharmony_ci u32 status) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci u32 stat = readl(cp->regs + REG_MIF_STATUS); 165762306a36Sopenharmony_ci u16 bmsr; 165862306a36Sopenharmony_ci 165962306a36Sopenharmony_ci /* check for a link change */ 166062306a36Sopenharmony_ci if (CAS_VAL(MIF_STATUS_POLL_STATUS, stat) == 0) 166162306a36Sopenharmony_ci return 0; 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_ci bmsr = CAS_VAL(MIF_STATUS_POLL_DATA, stat); 166462306a36Sopenharmony_ci return cas_mii_link_check(cp, bmsr); 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_cistatic int cas_pci_interrupt(struct net_device *dev, struct cas *cp, 166862306a36Sopenharmony_ci u32 status) 166962306a36Sopenharmony_ci{ 167062306a36Sopenharmony_ci u32 stat = readl(cp->regs + REG_PCI_ERR_STATUS); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci if (!stat) 167362306a36Sopenharmony_ci return 0; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci netdev_err(dev, "PCI error [%04x:%04x]", 167662306a36Sopenharmony_ci stat, readl(cp->regs + REG_BIM_DIAG)); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci /* cassini+ has this reserved */ 167962306a36Sopenharmony_ci if ((stat & PCI_ERR_BADACK) && 168062306a36Sopenharmony_ci ((cp->cas_flags & CAS_FLAG_REG_PLUS) == 0)) 168162306a36Sopenharmony_ci pr_cont(" <No ACK64# during ABS64 cycle>"); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci if (stat & PCI_ERR_DTRTO) 168462306a36Sopenharmony_ci pr_cont(" <Delayed transaction timeout>"); 168562306a36Sopenharmony_ci if (stat & PCI_ERR_OTHER) 168662306a36Sopenharmony_ci pr_cont(" <other>"); 168762306a36Sopenharmony_ci if (stat & PCI_ERR_BIM_DMA_WRITE) 168862306a36Sopenharmony_ci pr_cont(" <BIM DMA 0 write req>"); 168962306a36Sopenharmony_ci if (stat & PCI_ERR_BIM_DMA_READ) 169062306a36Sopenharmony_ci pr_cont(" <BIM DMA 0 read req>"); 169162306a36Sopenharmony_ci pr_cont("\n"); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (stat & PCI_ERR_OTHER) { 169462306a36Sopenharmony_ci int pci_errs; 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci /* Interrogate PCI config space for the 169762306a36Sopenharmony_ci * true cause. 169862306a36Sopenharmony_ci */ 169962306a36Sopenharmony_ci pci_errs = pci_status_get_and_clear_errors(cp->pdev); 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci netdev_err(dev, "PCI status errors[%04x]\n", pci_errs); 170262306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_PARITY) 170362306a36Sopenharmony_ci netdev_err(dev, "PCI parity error detected\n"); 170462306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_SIG_TARGET_ABORT) 170562306a36Sopenharmony_ci netdev_err(dev, "PCI target abort\n"); 170662306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_REC_TARGET_ABORT) 170762306a36Sopenharmony_ci netdev_err(dev, "PCI master acks target abort\n"); 170862306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_REC_MASTER_ABORT) 170962306a36Sopenharmony_ci netdev_err(dev, "PCI master abort\n"); 171062306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_SIG_SYSTEM_ERROR) 171162306a36Sopenharmony_ci netdev_err(dev, "PCI system error SERR#\n"); 171262306a36Sopenharmony_ci if (pci_errs & PCI_STATUS_DETECTED_PARITY) 171362306a36Sopenharmony_ci netdev_err(dev, "PCI parity error\n"); 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ci /* For all PCI errors, we should reset the chip. */ 171762306a36Sopenharmony_ci return 1; 171862306a36Sopenharmony_ci} 171962306a36Sopenharmony_ci 172062306a36Sopenharmony_ci/* All non-normal interrupt conditions get serviced here. 172162306a36Sopenharmony_ci * Returns non-zero if we should just exit the interrupt 172262306a36Sopenharmony_ci * handler right now (ie. if we reset the card which invalidates 172362306a36Sopenharmony_ci * all of the other original irq status bits). 172462306a36Sopenharmony_ci */ 172562306a36Sopenharmony_cistatic int cas_abnormal_irq(struct net_device *dev, struct cas *cp, 172662306a36Sopenharmony_ci u32 status) 172762306a36Sopenharmony_ci{ 172862306a36Sopenharmony_ci if (status & INTR_RX_TAG_ERROR) { 172962306a36Sopenharmony_ci /* corrupt RX tag framing */ 173062306a36Sopenharmony_ci netif_printk(cp, rx_err, KERN_DEBUG, cp->dev, 173162306a36Sopenharmony_ci "corrupt rx tag framing\n"); 173262306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 173362306a36Sopenharmony_ci cp->net_stats[0].rx_errors++; 173462306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 173562306a36Sopenharmony_ci goto do_reset; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci if (status & INTR_RX_LEN_MISMATCH) { 173962306a36Sopenharmony_ci /* length mismatch. */ 174062306a36Sopenharmony_ci netif_printk(cp, rx_err, KERN_DEBUG, cp->dev, 174162306a36Sopenharmony_ci "length mismatch for rx frame\n"); 174262306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 174362306a36Sopenharmony_ci cp->net_stats[0].rx_errors++; 174462306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 174562306a36Sopenharmony_ci goto do_reset; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci if (status & INTR_PCS_STATUS) { 174962306a36Sopenharmony_ci if (cas_pcs_interrupt(dev, cp, status)) 175062306a36Sopenharmony_ci goto do_reset; 175162306a36Sopenharmony_ci } 175262306a36Sopenharmony_ci 175362306a36Sopenharmony_ci if (status & INTR_TX_MAC_STATUS) { 175462306a36Sopenharmony_ci if (cas_txmac_interrupt(dev, cp, status)) 175562306a36Sopenharmony_ci goto do_reset; 175662306a36Sopenharmony_ci } 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (status & INTR_RX_MAC_STATUS) { 175962306a36Sopenharmony_ci if (cas_rxmac_interrupt(dev, cp, status)) 176062306a36Sopenharmony_ci goto do_reset; 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci if (status & INTR_MAC_CTRL_STATUS) { 176462306a36Sopenharmony_ci if (cas_mac_interrupt(dev, cp, status)) 176562306a36Sopenharmony_ci goto do_reset; 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci if (status & INTR_MIF_STATUS) { 176962306a36Sopenharmony_ci if (cas_mif_interrupt(dev, cp, status)) 177062306a36Sopenharmony_ci goto do_reset; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (status & INTR_PCI_ERROR_STATUS) { 177462306a36Sopenharmony_ci if (cas_pci_interrupt(dev, cp, status)) 177562306a36Sopenharmony_ci goto do_reset; 177662306a36Sopenharmony_ci } 177762306a36Sopenharmony_ci return 0; 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cido_reset: 178062306a36Sopenharmony_ci#if 1 178162306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 178262306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_all); 178362306a36Sopenharmony_ci netdev_err(dev, "reset called in cas_abnormal_irq [0x%x]\n", status); 178462306a36Sopenharmony_ci schedule_work(&cp->reset_task); 178562306a36Sopenharmony_ci#else 178662306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, CAS_RESET_ALL); 178762306a36Sopenharmony_ci netdev_err(dev, "reset called in cas_abnormal_irq\n"); 178862306a36Sopenharmony_ci schedule_work(&cp->reset_task); 178962306a36Sopenharmony_ci#endif 179062306a36Sopenharmony_ci return 1; 179162306a36Sopenharmony_ci} 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci/* NOTE: CAS_TABORT returns 1 or 2 so that it can be used when 179462306a36Sopenharmony_ci * determining whether to do a netif_stop/wakeup 179562306a36Sopenharmony_ci */ 179662306a36Sopenharmony_ci#define CAS_TABORT(x) (((x)->cas_flags & CAS_FLAG_TARGET_ABORT) ? 2 : 1) 179762306a36Sopenharmony_ci#define CAS_ROUND_PAGE(x) (((x) + PAGE_SIZE - 1) & PAGE_MASK) 179862306a36Sopenharmony_cistatic inline int cas_calc_tabort(struct cas *cp, const unsigned long addr, 179962306a36Sopenharmony_ci const int len) 180062306a36Sopenharmony_ci{ 180162306a36Sopenharmony_ci unsigned long off = addr + len; 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (CAS_TABORT(cp) == 1) 180462306a36Sopenharmony_ci return 0; 180562306a36Sopenharmony_ci if ((CAS_ROUND_PAGE(off) - off) > TX_TARGET_ABORT_LEN) 180662306a36Sopenharmony_ci return 0; 180762306a36Sopenharmony_ci return TX_TARGET_ABORT_LEN; 180862306a36Sopenharmony_ci} 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_cistatic inline void cas_tx_ringN(struct cas *cp, int ring, int limit) 181162306a36Sopenharmony_ci{ 181262306a36Sopenharmony_ci struct cas_tx_desc *txds; 181362306a36Sopenharmony_ci struct sk_buff **skbs; 181462306a36Sopenharmony_ci struct net_device *dev = cp->dev; 181562306a36Sopenharmony_ci int entry, count; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci spin_lock(&cp->tx_lock[ring]); 181862306a36Sopenharmony_ci txds = cp->init_txds[ring]; 181962306a36Sopenharmony_ci skbs = cp->tx_skbs[ring]; 182062306a36Sopenharmony_ci entry = cp->tx_old[ring]; 182162306a36Sopenharmony_ci 182262306a36Sopenharmony_ci count = TX_BUFF_COUNT(ring, entry, limit); 182362306a36Sopenharmony_ci while (entry != limit) { 182462306a36Sopenharmony_ci struct sk_buff *skb = skbs[entry]; 182562306a36Sopenharmony_ci dma_addr_t daddr; 182662306a36Sopenharmony_ci u32 dlen; 182762306a36Sopenharmony_ci int frag; 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (!skb) { 183062306a36Sopenharmony_ci /* this should never occur */ 183162306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 183262306a36Sopenharmony_ci continue; 183362306a36Sopenharmony_ci } 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci /* however, we might get only a partial skb release. */ 183662306a36Sopenharmony_ci count -= skb_shinfo(skb)->nr_frags + 183762306a36Sopenharmony_ci + cp->tx_tiny_use[ring][entry].nbufs + 1; 183862306a36Sopenharmony_ci if (count < 0) 183962306a36Sopenharmony_ci break; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci netif_printk(cp, tx_done, KERN_DEBUG, cp->dev, 184262306a36Sopenharmony_ci "tx[%d] done, slot %d\n", ring, entry); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci skbs[entry] = NULL; 184562306a36Sopenharmony_ci cp->tx_tiny_use[ring][entry].nbufs = 0; 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { 184862306a36Sopenharmony_ci struct cas_tx_desc *txd = txds + entry; 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci daddr = le64_to_cpu(txd->buffer); 185162306a36Sopenharmony_ci dlen = CAS_VAL(TX_DESC_BUFLEN, 185262306a36Sopenharmony_ci le64_to_cpu(txd->control)); 185362306a36Sopenharmony_ci dma_unmap_page(&cp->pdev->dev, daddr, dlen, 185462306a36Sopenharmony_ci DMA_TO_DEVICE); 185562306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci /* tiny buffer may follow */ 185862306a36Sopenharmony_ci if (cp->tx_tiny_use[ring][entry].used) { 185962306a36Sopenharmony_ci cp->tx_tiny_use[ring][entry].used = 0; 186062306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci } 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[ring]); 186562306a36Sopenharmony_ci cp->net_stats[ring].tx_packets++; 186662306a36Sopenharmony_ci cp->net_stats[ring].tx_bytes += skb->len; 186762306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[ring]); 186862306a36Sopenharmony_ci dev_consume_skb_irq(skb); 186962306a36Sopenharmony_ci } 187062306a36Sopenharmony_ci cp->tx_old[ring] = entry; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci /* this is wrong for multiple tx rings. the net device needs 187362306a36Sopenharmony_ci * multiple queues for this to do the right thing. we wait 187462306a36Sopenharmony_ci * for 2*packets to be available when using tiny buffers 187562306a36Sopenharmony_ci */ 187662306a36Sopenharmony_ci if (netif_queue_stopped(dev) && 187762306a36Sopenharmony_ci (TX_BUFFS_AVAIL(cp, ring) > CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1))) 187862306a36Sopenharmony_ci netif_wake_queue(dev); 187962306a36Sopenharmony_ci spin_unlock(&cp->tx_lock[ring]); 188062306a36Sopenharmony_ci} 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_cistatic void cas_tx(struct net_device *dev, struct cas *cp, 188362306a36Sopenharmony_ci u32 status) 188462306a36Sopenharmony_ci{ 188562306a36Sopenharmony_ci int limit, ring; 188662306a36Sopenharmony_ci#ifdef USE_TX_COMPWB 188762306a36Sopenharmony_ci u64 compwb = le64_to_cpu(cp->init_block->tx_compwb); 188862306a36Sopenharmony_ci#endif 188962306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, cp->dev, 189062306a36Sopenharmony_ci "tx interrupt, status: 0x%x, %llx\n", 189162306a36Sopenharmony_ci status, (unsigned long long)compwb); 189262306a36Sopenharmony_ci /* process all the rings */ 189362306a36Sopenharmony_ci for (ring = 0; ring < N_TX_RINGS; ring++) { 189462306a36Sopenharmony_ci#ifdef USE_TX_COMPWB 189562306a36Sopenharmony_ci /* use the completion writeback registers */ 189662306a36Sopenharmony_ci limit = (CAS_VAL(TX_COMPWB_MSB, compwb) << 8) | 189762306a36Sopenharmony_ci CAS_VAL(TX_COMPWB_LSB, compwb); 189862306a36Sopenharmony_ci compwb = TX_COMPWB_NEXT(compwb); 189962306a36Sopenharmony_ci#else 190062306a36Sopenharmony_ci limit = readl(cp->regs + REG_TX_COMPN(ring)); 190162306a36Sopenharmony_ci#endif 190262306a36Sopenharmony_ci if (cp->tx_old[ring] != limit) 190362306a36Sopenharmony_ci cas_tx_ringN(cp, ring, limit); 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci 190862306a36Sopenharmony_cistatic int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, 190962306a36Sopenharmony_ci int entry, const u64 *words, 191062306a36Sopenharmony_ci struct sk_buff **skbref) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci int dlen, hlen, len, i, alloclen; 191362306a36Sopenharmony_ci int off, swivel = RX_SWIVEL_OFF_VAL; 191462306a36Sopenharmony_ci struct cas_page *page; 191562306a36Sopenharmony_ci struct sk_buff *skb; 191662306a36Sopenharmony_ci void *crcaddr; 191762306a36Sopenharmony_ci __sum16 csum; 191862306a36Sopenharmony_ci char *p; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci hlen = CAS_VAL(RX_COMP2_HDR_SIZE, words[1]); 192162306a36Sopenharmony_ci dlen = CAS_VAL(RX_COMP1_DATA_SIZE, words[0]); 192262306a36Sopenharmony_ci len = hlen + dlen; 192362306a36Sopenharmony_ci 192462306a36Sopenharmony_ci if (RX_COPY_ALWAYS || (words[2] & RX_COMP3_SMALL_PKT)) 192562306a36Sopenharmony_ci alloclen = len; 192662306a36Sopenharmony_ci else 192762306a36Sopenharmony_ci alloclen = max(hlen, RX_COPY_MIN); 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci skb = netdev_alloc_skb(cp->dev, alloclen + swivel + cp->crc_size); 193062306a36Sopenharmony_ci if (skb == NULL) 193162306a36Sopenharmony_ci return -1; 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci *skbref = skb; 193462306a36Sopenharmony_ci skb_reserve(skb, swivel); 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci p = skb->data; 193762306a36Sopenharmony_ci crcaddr = NULL; 193862306a36Sopenharmony_ci if (hlen) { /* always copy header pages */ 193962306a36Sopenharmony_ci i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]); 194062306a36Sopenharmony_ci page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; 194162306a36Sopenharmony_ci off = CAS_VAL(RX_COMP2_HDR_OFF, words[1]) * 0x100 + 194262306a36Sopenharmony_ci swivel; 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci i = hlen; 194562306a36Sopenharmony_ci if (!dlen) /* attach FCS */ 194662306a36Sopenharmony_ci i += cp->crc_size; 194762306a36Sopenharmony_ci dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off, 194862306a36Sopenharmony_ci i, DMA_FROM_DEVICE); 194962306a36Sopenharmony_ci memcpy(p, page_address(page->buffer) + off, i); 195062306a36Sopenharmony_ci dma_sync_single_for_device(&cp->pdev->dev, 195162306a36Sopenharmony_ci page->dma_addr + off, i, 195262306a36Sopenharmony_ci DMA_FROM_DEVICE); 195362306a36Sopenharmony_ci RX_USED_ADD(page, 0x100); 195462306a36Sopenharmony_ci p += hlen; 195562306a36Sopenharmony_ci swivel = 0; 195662306a36Sopenharmony_ci } 195762306a36Sopenharmony_ci 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (alloclen < (hlen + dlen)) { 196062306a36Sopenharmony_ci skb_frag_t *frag = skb_shinfo(skb)->frags; 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci /* normal or jumbo packets. we use frags */ 196362306a36Sopenharmony_ci i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]); 196462306a36Sopenharmony_ci page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; 196562306a36Sopenharmony_ci off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel; 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci hlen = min(cp->page_size - off, dlen); 196862306a36Sopenharmony_ci if (hlen < 0) { 196962306a36Sopenharmony_ci netif_printk(cp, rx_err, KERN_DEBUG, cp->dev, 197062306a36Sopenharmony_ci "rx page overflow: %d\n", hlen); 197162306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 197262306a36Sopenharmony_ci return -1; 197362306a36Sopenharmony_ci } 197462306a36Sopenharmony_ci i = hlen; 197562306a36Sopenharmony_ci if (i == dlen) /* attach FCS */ 197662306a36Sopenharmony_ci i += cp->crc_size; 197762306a36Sopenharmony_ci dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off, 197862306a36Sopenharmony_ci i, DMA_FROM_DEVICE); 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ci /* make sure we always copy a header */ 198162306a36Sopenharmony_ci swivel = 0; 198262306a36Sopenharmony_ci if (p == (char *) skb->data) { /* not split */ 198362306a36Sopenharmony_ci memcpy(p, page_address(page->buffer) + off, 198462306a36Sopenharmony_ci RX_COPY_MIN); 198562306a36Sopenharmony_ci dma_sync_single_for_device(&cp->pdev->dev, 198662306a36Sopenharmony_ci page->dma_addr + off, i, 198762306a36Sopenharmony_ci DMA_FROM_DEVICE); 198862306a36Sopenharmony_ci off += RX_COPY_MIN; 198962306a36Sopenharmony_ci swivel = RX_COPY_MIN; 199062306a36Sopenharmony_ci RX_USED_ADD(page, cp->mtu_stride); 199162306a36Sopenharmony_ci } else { 199262306a36Sopenharmony_ci RX_USED_ADD(page, hlen); 199362306a36Sopenharmony_ci } 199462306a36Sopenharmony_ci skb_put(skb, alloclen); 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 199762306a36Sopenharmony_ci skb->data_len += hlen - swivel; 199862306a36Sopenharmony_ci skb->truesize += hlen - swivel; 199962306a36Sopenharmony_ci skb->len += hlen - swivel; 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci skb_frag_fill_page_desc(frag, page->buffer, off, hlen - swivel); 200262306a36Sopenharmony_ci __skb_frag_ref(frag); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci /* any more data? */ 200562306a36Sopenharmony_ci if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) { 200662306a36Sopenharmony_ci hlen = dlen; 200762306a36Sopenharmony_ci off = 0; 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]); 201062306a36Sopenharmony_ci page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; 201162306a36Sopenharmony_ci dma_sync_single_for_cpu(&cp->pdev->dev, 201262306a36Sopenharmony_ci page->dma_addr, 201362306a36Sopenharmony_ci hlen + cp->crc_size, 201462306a36Sopenharmony_ci DMA_FROM_DEVICE); 201562306a36Sopenharmony_ci dma_sync_single_for_device(&cp->pdev->dev, 201662306a36Sopenharmony_ci page->dma_addr, 201762306a36Sopenharmony_ci hlen + cp->crc_size, 201862306a36Sopenharmony_ci DMA_FROM_DEVICE); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 202162306a36Sopenharmony_ci skb->data_len += hlen; 202262306a36Sopenharmony_ci skb->len += hlen; 202362306a36Sopenharmony_ci frag++; 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci skb_frag_fill_page_desc(frag, page->buffer, 0, hlen); 202662306a36Sopenharmony_ci __skb_frag_ref(frag); 202762306a36Sopenharmony_ci RX_USED_ADD(page, hlen + cp->crc_size); 202862306a36Sopenharmony_ci } 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (cp->crc_size) 203162306a36Sopenharmony_ci crcaddr = page_address(page->buffer) + off + hlen; 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci } else { 203462306a36Sopenharmony_ci /* copying packet */ 203562306a36Sopenharmony_ci if (!dlen) 203662306a36Sopenharmony_ci goto end_copy_pkt; 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_ci i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]); 203962306a36Sopenharmony_ci page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; 204062306a36Sopenharmony_ci off = CAS_VAL(RX_COMP1_DATA_OFF, words[0]) + swivel; 204162306a36Sopenharmony_ci hlen = min(cp->page_size - off, dlen); 204262306a36Sopenharmony_ci if (hlen < 0) { 204362306a36Sopenharmony_ci netif_printk(cp, rx_err, KERN_DEBUG, cp->dev, 204462306a36Sopenharmony_ci "rx page overflow: %d\n", hlen); 204562306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 204662306a36Sopenharmony_ci return -1; 204762306a36Sopenharmony_ci } 204862306a36Sopenharmony_ci i = hlen; 204962306a36Sopenharmony_ci if (i == dlen) /* attach FCS */ 205062306a36Sopenharmony_ci i += cp->crc_size; 205162306a36Sopenharmony_ci dma_sync_single_for_cpu(&cp->pdev->dev, page->dma_addr + off, 205262306a36Sopenharmony_ci i, DMA_FROM_DEVICE); 205362306a36Sopenharmony_ci memcpy(p, page_address(page->buffer) + off, i); 205462306a36Sopenharmony_ci dma_sync_single_for_device(&cp->pdev->dev, 205562306a36Sopenharmony_ci page->dma_addr + off, i, 205662306a36Sopenharmony_ci DMA_FROM_DEVICE); 205762306a36Sopenharmony_ci if (p == (char *) skb->data) /* not split */ 205862306a36Sopenharmony_ci RX_USED_ADD(page, cp->mtu_stride); 205962306a36Sopenharmony_ci else 206062306a36Sopenharmony_ci RX_USED_ADD(page, i); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci /* any more data? */ 206362306a36Sopenharmony_ci if ((words[0] & RX_COMP1_SPLIT_PKT) && ((dlen -= hlen) > 0)) { 206462306a36Sopenharmony_ci p += hlen; 206562306a36Sopenharmony_ci i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]); 206662306a36Sopenharmony_ci page = cp->rx_pages[CAS_VAL(RX_INDEX_RING, i)][CAS_VAL(RX_INDEX_NUM, i)]; 206762306a36Sopenharmony_ci dma_sync_single_for_cpu(&cp->pdev->dev, 206862306a36Sopenharmony_ci page->dma_addr, 206962306a36Sopenharmony_ci dlen + cp->crc_size, 207062306a36Sopenharmony_ci DMA_FROM_DEVICE); 207162306a36Sopenharmony_ci memcpy(p, page_address(page->buffer), dlen + cp->crc_size); 207262306a36Sopenharmony_ci dma_sync_single_for_device(&cp->pdev->dev, 207362306a36Sopenharmony_ci page->dma_addr, 207462306a36Sopenharmony_ci dlen + cp->crc_size, 207562306a36Sopenharmony_ci DMA_FROM_DEVICE); 207662306a36Sopenharmony_ci RX_USED_ADD(page, dlen + cp->crc_size); 207762306a36Sopenharmony_ci } 207862306a36Sopenharmony_ciend_copy_pkt: 207962306a36Sopenharmony_ci if (cp->crc_size) 208062306a36Sopenharmony_ci crcaddr = skb->data + alloclen; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci skb_put(skb, alloclen); 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci csum = (__force __sum16)htons(CAS_VAL(RX_COMP4_TCP_CSUM, words[3])); 208662306a36Sopenharmony_ci if (cp->crc_size) { 208762306a36Sopenharmony_ci /* checksum includes FCS. strip it out. */ 208862306a36Sopenharmony_ci csum = csum_fold(csum_partial(crcaddr, cp->crc_size, 208962306a36Sopenharmony_ci csum_unfold(csum))); 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, cp->dev); 209262306a36Sopenharmony_ci if (skb->protocol == htons(ETH_P_IP)) { 209362306a36Sopenharmony_ci skb->csum = csum_unfold(~csum); 209462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_COMPLETE; 209562306a36Sopenharmony_ci } else 209662306a36Sopenharmony_ci skb_checksum_none_assert(skb); 209762306a36Sopenharmony_ci return len; 209862306a36Sopenharmony_ci} 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci/* we can handle up to 64 rx flows at a time. we do the same thing 210262306a36Sopenharmony_ci * as nonreassm except that we batch up the buffers. 210362306a36Sopenharmony_ci * NOTE: we currently just treat each flow as a bunch of packets that 210462306a36Sopenharmony_ci * we pass up. a better way would be to coalesce the packets 210562306a36Sopenharmony_ci * into a jumbo packet. to do that, we need to do the following: 210662306a36Sopenharmony_ci * 1) the first packet will have a clean split between header and 210762306a36Sopenharmony_ci * data. save both. 210862306a36Sopenharmony_ci * 2) each time the next flow packet comes in, extend the 210962306a36Sopenharmony_ci * data length and merge the checksums. 211062306a36Sopenharmony_ci * 3) on flow release, fix up the header. 211162306a36Sopenharmony_ci * 4) make sure the higher layer doesn't care. 211262306a36Sopenharmony_ci * because packets get coalesced, we shouldn't run into fragment count 211362306a36Sopenharmony_ci * issues. 211462306a36Sopenharmony_ci */ 211562306a36Sopenharmony_cistatic inline void cas_rx_flow_pkt(struct cas *cp, const u64 *words, 211662306a36Sopenharmony_ci struct sk_buff *skb) 211762306a36Sopenharmony_ci{ 211862306a36Sopenharmony_ci int flowid = CAS_VAL(RX_COMP3_FLOWID, words[2]) & (N_RX_FLOWS - 1); 211962306a36Sopenharmony_ci struct sk_buff_head *flow = &cp->rx_flows[flowid]; 212062306a36Sopenharmony_ci 212162306a36Sopenharmony_ci /* this is protected at a higher layer, so no need to 212262306a36Sopenharmony_ci * do any additional locking here. stick the buffer 212362306a36Sopenharmony_ci * at the end. 212462306a36Sopenharmony_ci */ 212562306a36Sopenharmony_ci __skb_queue_tail(flow, skb); 212662306a36Sopenharmony_ci if (words[0] & RX_COMP1_RELEASE_FLOW) { 212762306a36Sopenharmony_ci while ((skb = __skb_dequeue(flow))) { 212862306a36Sopenharmony_ci cas_skb_release(skb); 212962306a36Sopenharmony_ci } 213062306a36Sopenharmony_ci } 213162306a36Sopenharmony_ci} 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci/* put rx descriptor back on ring. if a buffer is in use by a higher 213462306a36Sopenharmony_ci * layer, this will need to put in a replacement. 213562306a36Sopenharmony_ci */ 213662306a36Sopenharmony_cistatic void cas_post_page(struct cas *cp, const int ring, const int index) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci cas_page_t *new; 213962306a36Sopenharmony_ci int entry; 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci entry = cp->rx_old[ring]; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci new = cas_page_swap(cp, ring, index); 214462306a36Sopenharmony_ci cp->init_rxds[ring][entry].buffer = cpu_to_le64(new->dma_addr); 214562306a36Sopenharmony_ci cp->init_rxds[ring][entry].index = 214662306a36Sopenharmony_ci cpu_to_le64(CAS_BASE(RX_INDEX_NUM, index) | 214762306a36Sopenharmony_ci CAS_BASE(RX_INDEX_RING, ring)); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci entry = RX_DESC_ENTRY(ring, entry + 1); 215062306a36Sopenharmony_ci cp->rx_old[ring] = entry; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (entry % 4) 215362306a36Sopenharmony_ci return; 215462306a36Sopenharmony_ci 215562306a36Sopenharmony_ci if (ring == 0) 215662306a36Sopenharmony_ci writel(entry, cp->regs + REG_RX_KICK); 215762306a36Sopenharmony_ci else if ((N_RX_DESC_RINGS > 1) && 215862306a36Sopenharmony_ci (cp->cas_flags & CAS_FLAG_REG_PLUS)) 215962306a36Sopenharmony_ci writel(entry, cp->regs + REG_PLUS_RX_KICK1); 216062306a36Sopenharmony_ci} 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci/* only when things are bad */ 216462306a36Sopenharmony_cistatic int cas_post_rxds_ringN(struct cas *cp, int ring, int num) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci unsigned int entry, last, count, released; 216762306a36Sopenharmony_ci int cluster; 216862306a36Sopenharmony_ci cas_page_t **page = cp->rx_pages[ring]; 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci entry = cp->rx_old[ring]; 217162306a36Sopenharmony_ci 217262306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, cp->dev, 217362306a36Sopenharmony_ci "rxd[%d] interrupt, done: %d\n", ring, entry); 217462306a36Sopenharmony_ci 217562306a36Sopenharmony_ci cluster = -1; 217662306a36Sopenharmony_ci count = entry & 0x3; 217762306a36Sopenharmony_ci last = RX_DESC_ENTRY(ring, num ? entry + num - 4: entry - 4); 217862306a36Sopenharmony_ci released = 0; 217962306a36Sopenharmony_ci while (entry != last) { 218062306a36Sopenharmony_ci /* make a new buffer if it's still in use */ 218162306a36Sopenharmony_ci if (page_count(page[entry]->buffer) > 1) { 218262306a36Sopenharmony_ci cas_page_t *new = cas_page_dequeue(cp); 218362306a36Sopenharmony_ci if (!new) { 218462306a36Sopenharmony_ci /* let the timer know that we need to 218562306a36Sopenharmony_ci * do this again 218662306a36Sopenharmony_ci */ 218762306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_RXD_POST(ring); 218862306a36Sopenharmony_ci if (!timer_pending(&cp->link_timer)) 218962306a36Sopenharmony_ci mod_timer(&cp->link_timer, jiffies + 219062306a36Sopenharmony_ci CAS_LINK_FAST_TIMEOUT); 219162306a36Sopenharmony_ci cp->rx_old[ring] = entry; 219262306a36Sopenharmony_ci cp->rx_last[ring] = num ? num - released : 0; 219362306a36Sopenharmony_ci return -ENOMEM; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci spin_lock(&cp->rx_inuse_lock); 219662306a36Sopenharmony_ci list_add(&page[entry]->list, &cp->rx_inuse_list); 219762306a36Sopenharmony_ci spin_unlock(&cp->rx_inuse_lock); 219862306a36Sopenharmony_ci cp->init_rxds[ring][entry].buffer = 219962306a36Sopenharmony_ci cpu_to_le64(new->dma_addr); 220062306a36Sopenharmony_ci page[entry] = new; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci } 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci if (++count == 4) { 220562306a36Sopenharmony_ci cluster = entry; 220662306a36Sopenharmony_ci count = 0; 220762306a36Sopenharmony_ci } 220862306a36Sopenharmony_ci released++; 220962306a36Sopenharmony_ci entry = RX_DESC_ENTRY(ring, entry + 1); 221062306a36Sopenharmony_ci } 221162306a36Sopenharmony_ci cp->rx_old[ring] = entry; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci if (cluster < 0) 221462306a36Sopenharmony_ci return 0; 221562306a36Sopenharmony_ci 221662306a36Sopenharmony_ci if (ring == 0) 221762306a36Sopenharmony_ci writel(cluster, cp->regs + REG_RX_KICK); 221862306a36Sopenharmony_ci else if ((N_RX_DESC_RINGS > 1) && 221962306a36Sopenharmony_ci (cp->cas_flags & CAS_FLAG_REG_PLUS)) 222062306a36Sopenharmony_ci writel(cluster, cp->regs + REG_PLUS_RX_KICK1); 222162306a36Sopenharmony_ci return 0; 222262306a36Sopenharmony_ci} 222362306a36Sopenharmony_ci 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci/* process a completion ring. packets are set up in three basic ways: 222662306a36Sopenharmony_ci * small packets: should be copied header + data in single buffer. 222762306a36Sopenharmony_ci * large packets: header and data in a single buffer. 222862306a36Sopenharmony_ci * split packets: header in a separate buffer from data. 222962306a36Sopenharmony_ci * data may be in multiple pages. data may be > 256 223062306a36Sopenharmony_ci * bytes but in a single page. 223162306a36Sopenharmony_ci * 223262306a36Sopenharmony_ci * NOTE: RX page posting is done in this routine as well. while there's 223362306a36Sopenharmony_ci * the capability of using multiple RX completion rings, it isn't 223462306a36Sopenharmony_ci * really worthwhile due to the fact that the page posting will 223562306a36Sopenharmony_ci * force serialization on the single descriptor ring. 223662306a36Sopenharmony_ci */ 223762306a36Sopenharmony_cistatic int cas_rx_ringN(struct cas *cp, int ring, int budget) 223862306a36Sopenharmony_ci{ 223962306a36Sopenharmony_ci struct cas_rx_comp *rxcs = cp->init_rxcs[ring]; 224062306a36Sopenharmony_ci int entry, drops; 224162306a36Sopenharmony_ci int npackets = 0; 224262306a36Sopenharmony_ci 224362306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, cp->dev, 224462306a36Sopenharmony_ci "rx[%d] interrupt, done: %d/%d\n", 224562306a36Sopenharmony_ci ring, 224662306a36Sopenharmony_ci readl(cp->regs + REG_RX_COMP_HEAD), cp->rx_new[ring]); 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_ci entry = cp->rx_new[ring]; 224962306a36Sopenharmony_ci drops = 0; 225062306a36Sopenharmony_ci while (1) { 225162306a36Sopenharmony_ci struct cas_rx_comp *rxc = rxcs + entry; 225262306a36Sopenharmony_ci struct sk_buff *skb; 225362306a36Sopenharmony_ci int type, len; 225462306a36Sopenharmony_ci u64 words[4]; 225562306a36Sopenharmony_ci int i, dring; 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci words[0] = le64_to_cpu(rxc->word1); 225862306a36Sopenharmony_ci words[1] = le64_to_cpu(rxc->word2); 225962306a36Sopenharmony_ci words[2] = le64_to_cpu(rxc->word3); 226062306a36Sopenharmony_ci words[3] = le64_to_cpu(rxc->word4); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci /* don't touch if still owned by hw */ 226362306a36Sopenharmony_ci type = CAS_VAL(RX_COMP1_TYPE, words[0]); 226462306a36Sopenharmony_ci if (type == 0) 226562306a36Sopenharmony_ci break; 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci /* hw hasn't cleared the zero bit yet */ 226862306a36Sopenharmony_ci if (words[3] & RX_COMP4_ZERO) { 226962306a36Sopenharmony_ci break; 227062306a36Sopenharmony_ci } 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci /* get info on the packet */ 227362306a36Sopenharmony_ci if (words[3] & (RX_COMP4_LEN_MISMATCH | RX_COMP4_BAD)) { 227462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[ring]); 227562306a36Sopenharmony_ci cp->net_stats[ring].rx_errors++; 227662306a36Sopenharmony_ci if (words[3] & RX_COMP4_LEN_MISMATCH) 227762306a36Sopenharmony_ci cp->net_stats[ring].rx_length_errors++; 227862306a36Sopenharmony_ci if (words[3] & RX_COMP4_BAD) 227962306a36Sopenharmony_ci cp->net_stats[ring].rx_crc_errors++; 228062306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[ring]); 228162306a36Sopenharmony_ci 228262306a36Sopenharmony_ci /* We'll just return it to Cassini. */ 228362306a36Sopenharmony_ci drop_it: 228462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[ring]); 228562306a36Sopenharmony_ci ++cp->net_stats[ring].rx_dropped; 228662306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[ring]); 228762306a36Sopenharmony_ci goto next; 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci len = cas_rx_process_pkt(cp, rxc, entry, words, &skb); 229162306a36Sopenharmony_ci if (len < 0) { 229262306a36Sopenharmony_ci ++drops; 229362306a36Sopenharmony_ci goto drop_it; 229462306a36Sopenharmony_ci } 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci /* see if it's a flow re-assembly or not. the driver 229762306a36Sopenharmony_ci * itself handles release back up. 229862306a36Sopenharmony_ci */ 229962306a36Sopenharmony_ci if (RX_DONT_BATCH || (type == 0x2)) { 230062306a36Sopenharmony_ci /* non-reassm: these always get released */ 230162306a36Sopenharmony_ci cas_skb_release(skb); 230262306a36Sopenharmony_ci } else { 230362306a36Sopenharmony_ci cas_rx_flow_pkt(cp, words, skb); 230462306a36Sopenharmony_ci } 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci spin_lock(&cp->stat_lock[ring]); 230762306a36Sopenharmony_ci cp->net_stats[ring].rx_packets++; 230862306a36Sopenharmony_ci cp->net_stats[ring].rx_bytes += len; 230962306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[ring]); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci next: 231262306a36Sopenharmony_ci npackets++; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci /* should it be released? */ 231562306a36Sopenharmony_ci if (words[0] & RX_COMP1_RELEASE_HDR) { 231662306a36Sopenharmony_ci i = CAS_VAL(RX_COMP2_HDR_INDEX, words[1]); 231762306a36Sopenharmony_ci dring = CAS_VAL(RX_INDEX_RING, i); 231862306a36Sopenharmony_ci i = CAS_VAL(RX_INDEX_NUM, i); 231962306a36Sopenharmony_ci cas_post_page(cp, dring, i); 232062306a36Sopenharmony_ci } 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci if (words[0] & RX_COMP1_RELEASE_DATA) { 232362306a36Sopenharmony_ci i = CAS_VAL(RX_COMP1_DATA_INDEX, words[0]); 232462306a36Sopenharmony_ci dring = CAS_VAL(RX_INDEX_RING, i); 232562306a36Sopenharmony_ci i = CAS_VAL(RX_INDEX_NUM, i); 232662306a36Sopenharmony_ci cas_post_page(cp, dring, i); 232762306a36Sopenharmony_ci } 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci if (words[0] & RX_COMP1_RELEASE_NEXT) { 233062306a36Sopenharmony_ci i = CAS_VAL(RX_COMP2_NEXT_INDEX, words[1]); 233162306a36Sopenharmony_ci dring = CAS_VAL(RX_INDEX_RING, i); 233262306a36Sopenharmony_ci i = CAS_VAL(RX_INDEX_NUM, i); 233362306a36Sopenharmony_ci cas_post_page(cp, dring, i); 233462306a36Sopenharmony_ci } 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci /* skip to the next entry */ 233762306a36Sopenharmony_ci entry = RX_COMP_ENTRY(ring, entry + 1 + 233862306a36Sopenharmony_ci CAS_VAL(RX_COMP1_SKIP, words[0])); 233962306a36Sopenharmony_ci#ifdef USE_NAPI 234062306a36Sopenharmony_ci if (budget && (npackets >= budget)) 234162306a36Sopenharmony_ci break; 234262306a36Sopenharmony_ci#endif 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci cp->rx_new[ring] = entry; 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci if (drops) 234762306a36Sopenharmony_ci netdev_info(cp->dev, "Memory squeeze, deferring packet\n"); 234862306a36Sopenharmony_ci return npackets; 234962306a36Sopenharmony_ci} 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci/* put completion entries back on the ring */ 235362306a36Sopenharmony_cistatic void cas_post_rxcs_ringN(struct net_device *dev, 235462306a36Sopenharmony_ci struct cas *cp, int ring) 235562306a36Sopenharmony_ci{ 235662306a36Sopenharmony_ci struct cas_rx_comp *rxc = cp->init_rxcs[ring]; 235762306a36Sopenharmony_ci int last, entry; 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci last = cp->rx_cur[ring]; 236062306a36Sopenharmony_ci entry = cp->rx_new[ring]; 236162306a36Sopenharmony_ci netif_printk(cp, intr, KERN_DEBUG, dev, 236262306a36Sopenharmony_ci "rxc[%d] interrupt, done: %d/%d\n", 236362306a36Sopenharmony_ci ring, readl(cp->regs + REG_RX_COMP_HEAD), entry); 236462306a36Sopenharmony_ci 236562306a36Sopenharmony_ci /* zero and re-mark descriptors */ 236662306a36Sopenharmony_ci while (last != entry) { 236762306a36Sopenharmony_ci cas_rxc_init(rxc + last); 236862306a36Sopenharmony_ci last = RX_COMP_ENTRY(ring, last + 1); 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci cp->rx_cur[ring] = last; 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci if (ring == 0) 237362306a36Sopenharmony_ci writel(last, cp->regs + REG_RX_COMP_TAIL); 237462306a36Sopenharmony_ci else if (cp->cas_flags & CAS_FLAG_REG_PLUS) 237562306a36Sopenharmony_ci writel(last, cp->regs + REG_PLUS_RX_COMPN_TAIL(ring)); 237662306a36Sopenharmony_ci} 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci 238062306a36Sopenharmony_ci/* cassini can use all four PCI interrupts for the completion ring. 238162306a36Sopenharmony_ci * rings 3 and 4 are identical 238262306a36Sopenharmony_ci */ 238362306a36Sopenharmony_ci#if defined(USE_PCI_INTC) || defined(USE_PCI_INTD) 238462306a36Sopenharmony_cistatic inline void cas_handle_irqN(struct net_device *dev, 238562306a36Sopenharmony_ci struct cas *cp, const u32 status, 238662306a36Sopenharmony_ci const int ring) 238762306a36Sopenharmony_ci{ 238862306a36Sopenharmony_ci if (status & (INTR_RX_COMP_FULL_ALT | INTR_RX_COMP_AF_ALT)) 238962306a36Sopenharmony_ci cas_post_rxcs_ringN(dev, cp, ring); 239062306a36Sopenharmony_ci} 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_cistatic irqreturn_t cas_interruptN(int irq, void *dev_id) 239362306a36Sopenharmony_ci{ 239462306a36Sopenharmony_ci struct net_device *dev = dev_id; 239562306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 239662306a36Sopenharmony_ci unsigned long flags; 239762306a36Sopenharmony_ci int ring = (irq == cp->pci_irq_INTC) ? 2 : 3; 239862306a36Sopenharmony_ci u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(ring)); 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci /* check for shared irq */ 240162306a36Sopenharmony_ci if (status == 0) 240262306a36Sopenharmony_ci return IRQ_NONE; 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 240562306a36Sopenharmony_ci if (status & INTR_RX_DONE_ALT) { /* handle rx separately */ 240662306a36Sopenharmony_ci#ifdef USE_NAPI 240762306a36Sopenharmony_ci cas_mask_intr(cp); 240862306a36Sopenharmony_ci napi_schedule(&cp->napi); 240962306a36Sopenharmony_ci#else 241062306a36Sopenharmony_ci cas_rx_ringN(cp, ring, 0); 241162306a36Sopenharmony_ci#endif 241262306a36Sopenharmony_ci status &= ~INTR_RX_DONE_ALT; 241362306a36Sopenharmony_ci } 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (status) 241662306a36Sopenharmony_ci cas_handle_irqN(dev, cp, status, ring); 241762306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 241862306a36Sopenharmony_ci return IRQ_HANDLED; 241962306a36Sopenharmony_ci} 242062306a36Sopenharmony_ci#endif 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci#ifdef USE_PCI_INTB 242362306a36Sopenharmony_ci/* everything but rx packets */ 242462306a36Sopenharmony_cistatic inline void cas_handle_irq1(struct cas *cp, const u32 status) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci if (status & INTR_RX_BUF_UNAVAIL_1) { 242762306a36Sopenharmony_ci /* Frame arrived, no free RX buffers available. 242862306a36Sopenharmony_ci * NOTE: we can get this on a link transition. */ 242962306a36Sopenharmony_ci cas_post_rxds_ringN(cp, 1, 0); 243062306a36Sopenharmony_ci spin_lock(&cp->stat_lock[1]); 243162306a36Sopenharmony_ci cp->net_stats[1].rx_dropped++; 243262306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[1]); 243362306a36Sopenharmony_ci } 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci if (status & INTR_RX_BUF_AE_1) 243662306a36Sopenharmony_ci cas_post_rxds_ringN(cp, 1, RX_DESC_RINGN_SIZE(1) - 243762306a36Sopenharmony_ci RX_AE_FREEN_VAL(1)); 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL)) 244062306a36Sopenharmony_ci cas_post_rxcs_ringN(cp, 1); 244162306a36Sopenharmony_ci} 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci/* ring 2 handles a few more events than 3 and 4 */ 244462306a36Sopenharmony_cistatic irqreturn_t cas_interrupt1(int irq, void *dev_id) 244562306a36Sopenharmony_ci{ 244662306a36Sopenharmony_ci struct net_device *dev = dev_id; 244762306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 244862306a36Sopenharmony_ci unsigned long flags; 244962306a36Sopenharmony_ci u32 status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1)); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci /* check for shared interrupt */ 245262306a36Sopenharmony_ci if (status == 0) 245362306a36Sopenharmony_ci return IRQ_NONE; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 245662306a36Sopenharmony_ci if (status & INTR_RX_DONE_ALT) { /* handle rx separately */ 245762306a36Sopenharmony_ci#ifdef USE_NAPI 245862306a36Sopenharmony_ci cas_mask_intr(cp); 245962306a36Sopenharmony_ci napi_schedule(&cp->napi); 246062306a36Sopenharmony_ci#else 246162306a36Sopenharmony_ci cas_rx_ringN(cp, 1, 0); 246262306a36Sopenharmony_ci#endif 246362306a36Sopenharmony_ci status &= ~INTR_RX_DONE_ALT; 246462306a36Sopenharmony_ci } 246562306a36Sopenharmony_ci if (status) 246662306a36Sopenharmony_ci cas_handle_irq1(cp, status); 246762306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 246862306a36Sopenharmony_ci return IRQ_HANDLED; 246962306a36Sopenharmony_ci} 247062306a36Sopenharmony_ci#endif 247162306a36Sopenharmony_ci 247262306a36Sopenharmony_cistatic inline void cas_handle_irq(struct net_device *dev, 247362306a36Sopenharmony_ci struct cas *cp, const u32 status) 247462306a36Sopenharmony_ci{ 247562306a36Sopenharmony_ci /* housekeeping interrupts */ 247662306a36Sopenharmony_ci if (status & INTR_ERROR_MASK) 247762306a36Sopenharmony_ci cas_abnormal_irq(dev, cp, status); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci if (status & INTR_RX_BUF_UNAVAIL) { 248062306a36Sopenharmony_ci /* Frame arrived, no free RX buffers available. 248162306a36Sopenharmony_ci * NOTE: we can get this on a link transition. 248262306a36Sopenharmony_ci */ 248362306a36Sopenharmony_ci cas_post_rxds_ringN(cp, 0, 0); 248462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 248562306a36Sopenharmony_ci cp->net_stats[0].rx_dropped++; 248662306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 248762306a36Sopenharmony_ci } else if (status & INTR_RX_BUF_AE) { 248862306a36Sopenharmony_ci cas_post_rxds_ringN(cp, 0, RX_DESC_RINGN_SIZE(0) - 248962306a36Sopenharmony_ci RX_AE_FREEN_VAL(0)); 249062306a36Sopenharmony_ci } 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci if (status & (INTR_RX_COMP_AF | INTR_RX_COMP_FULL)) 249362306a36Sopenharmony_ci cas_post_rxcs_ringN(dev, cp, 0); 249462306a36Sopenharmony_ci} 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_cistatic irqreturn_t cas_interrupt(int irq, void *dev_id) 249762306a36Sopenharmony_ci{ 249862306a36Sopenharmony_ci struct net_device *dev = dev_id; 249962306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 250062306a36Sopenharmony_ci unsigned long flags; 250162306a36Sopenharmony_ci u32 status = readl(cp->regs + REG_INTR_STATUS); 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci if (status == 0) 250462306a36Sopenharmony_ci return IRQ_NONE; 250562306a36Sopenharmony_ci 250662306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 250762306a36Sopenharmony_ci if (status & (INTR_TX_ALL | INTR_TX_INTME)) { 250862306a36Sopenharmony_ci cas_tx(dev, cp, status); 250962306a36Sopenharmony_ci status &= ~(INTR_TX_ALL | INTR_TX_INTME); 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci if (status & INTR_RX_DONE) { 251362306a36Sopenharmony_ci#ifdef USE_NAPI 251462306a36Sopenharmony_ci cas_mask_intr(cp); 251562306a36Sopenharmony_ci napi_schedule(&cp->napi); 251662306a36Sopenharmony_ci#else 251762306a36Sopenharmony_ci cas_rx_ringN(cp, 0, 0); 251862306a36Sopenharmony_ci#endif 251962306a36Sopenharmony_ci status &= ~INTR_RX_DONE; 252062306a36Sopenharmony_ci } 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci if (status) 252362306a36Sopenharmony_ci cas_handle_irq(dev, cp, status); 252462306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 252562306a36Sopenharmony_ci return IRQ_HANDLED; 252662306a36Sopenharmony_ci} 252762306a36Sopenharmony_ci 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci#ifdef USE_NAPI 253062306a36Sopenharmony_cistatic int cas_poll(struct napi_struct *napi, int budget) 253162306a36Sopenharmony_ci{ 253262306a36Sopenharmony_ci struct cas *cp = container_of(napi, struct cas, napi); 253362306a36Sopenharmony_ci struct net_device *dev = cp->dev; 253462306a36Sopenharmony_ci int i, enable_intr, credits; 253562306a36Sopenharmony_ci u32 status = readl(cp->regs + REG_INTR_STATUS); 253662306a36Sopenharmony_ci unsigned long flags; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 253962306a36Sopenharmony_ci cas_tx(dev, cp, status); 254062306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci /* NAPI rx packets. we spread the credits across all of the 254362306a36Sopenharmony_ci * rxc rings 254462306a36Sopenharmony_ci * 254562306a36Sopenharmony_ci * to make sure we're fair with the work we loop through each 254662306a36Sopenharmony_ci * ring N_RX_COMP_RING times with a request of 254762306a36Sopenharmony_ci * budget / N_RX_COMP_RINGS 254862306a36Sopenharmony_ci */ 254962306a36Sopenharmony_ci enable_intr = 1; 255062306a36Sopenharmony_ci credits = 0; 255162306a36Sopenharmony_ci for (i = 0; i < N_RX_COMP_RINGS; i++) { 255262306a36Sopenharmony_ci int j; 255362306a36Sopenharmony_ci for (j = 0; j < N_RX_COMP_RINGS; j++) { 255462306a36Sopenharmony_ci credits += cas_rx_ringN(cp, j, budget / N_RX_COMP_RINGS); 255562306a36Sopenharmony_ci if (credits >= budget) { 255662306a36Sopenharmony_ci enable_intr = 0; 255762306a36Sopenharmony_ci goto rx_comp; 255862306a36Sopenharmony_ci } 255962306a36Sopenharmony_ci } 256062306a36Sopenharmony_ci } 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_cirx_comp: 256362306a36Sopenharmony_ci /* final rx completion */ 256462306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 256562306a36Sopenharmony_ci if (status) 256662306a36Sopenharmony_ci cas_handle_irq(dev, cp, status); 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci#ifdef USE_PCI_INTB 256962306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 1) { 257062306a36Sopenharmony_ci status = readl(cp->regs + REG_PLUS_INTRN_STATUS(1)); 257162306a36Sopenharmony_ci if (status) 257262306a36Sopenharmony_ci cas_handle_irq1(dev, cp, status); 257362306a36Sopenharmony_ci } 257462306a36Sopenharmony_ci#endif 257562306a36Sopenharmony_ci 257662306a36Sopenharmony_ci#ifdef USE_PCI_INTC 257762306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 2) { 257862306a36Sopenharmony_ci status = readl(cp->regs + REG_PLUS_INTRN_STATUS(2)); 257962306a36Sopenharmony_ci if (status) 258062306a36Sopenharmony_ci cas_handle_irqN(dev, cp, status, 2); 258162306a36Sopenharmony_ci } 258262306a36Sopenharmony_ci#endif 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci#ifdef USE_PCI_INTD 258562306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 3) { 258662306a36Sopenharmony_ci status = readl(cp->regs + REG_PLUS_INTRN_STATUS(3)); 258762306a36Sopenharmony_ci if (status) 258862306a36Sopenharmony_ci cas_handle_irqN(dev, cp, status, 3); 258962306a36Sopenharmony_ci } 259062306a36Sopenharmony_ci#endif 259162306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 259262306a36Sopenharmony_ci if (enable_intr) { 259362306a36Sopenharmony_ci napi_complete(napi); 259462306a36Sopenharmony_ci cas_unmask_intr(cp); 259562306a36Sopenharmony_ci } 259662306a36Sopenharmony_ci return credits; 259762306a36Sopenharmony_ci} 259862306a36Sopenharmony_ci#endif 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 260162306a36Sopenharmony_cistatic void cas_netpoll(struct net_device *dev) 260262306a36Sopenharmony_ci{ 260362306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci cas_disable_irq(cp, 0); 260662306a36Sopenharmony_ci cas_interrupt(cp->pdev->irq, dev); 260762306a36Sopenharmony_ci cas_enable_irq(cp, 0); 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci#ifdef USE_PCI_INTB 261062306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 1) { 261162306a36Sopenharmony_ci /* cas_interrupt1(); */ 261262306a36Sopenharmony_ci } 261362306a36Sopenharmony_ci#endif 261462306a36Sopenharmony_ci#ifdef USE_PCI_INTC 261562306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 2) { 261662306a36Sopenharmony_ci /* cas_interruptN(); */ 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci#endif 261962306a36Sopenharmony_ci#ifdef USE_PCI_INTD 262062306a36Sopenharmony_ci if (N_RX_COMP_RINGS > 3) { 262162306a36Sopenharmony_ci /* cas_interruptN(); */ 262262306a36Sopenharmony_ci } 262362306a36Sopenharmony_ci#endif 262462306a36Sopenharmony_ci} 262562306a36Sopenharmony_ci#endif 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_cistatic void cas_tx_timeout(struct net_device *dev, unsigned int txqueue) 262862306a36Sopenharmony_ci{ 262962306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci netdev_err(dev, "transmit timed out, resetting\n"); 263262306a36Sopenharmony_ci if (!cp->hw_running) { 263362306a36Sopenharmony_ci netdev_err(dev, "hrm.. hw not running!\n"); 263462306a36Sopenharmony_ci return; 263562306a36Sopenharmony_ci } 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci netdev_err(dev, "MIF_STATE[%08x]\n", 263862306a36Sopenharmony_ci readl(cp->regs + REG_MIF_STATE_MACHINE)); 263962306a36Sopenharmony_ci 264062306a36Sopenharmony_ci netdev_err(dev, "MAC_STATE[%08x]\n", 264162306a36Sopenharmony_ci readl(cp->regs + REG_MAC_STATE_MACHINE)); 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_ci netdev_err(dev, "TX_STATE[%08x:%08x:%08x] FIFO[%08x:%08x:%08x] SM1[%08x] SM2[%08x]\n", 264462306a36Sopenharmony_ci readl(cp->regs + REG_TX_CFG), 264562306a36Sopenharmony_ci readl(cp->regs + REG_MAC_TX_STATUS), 264662306a36Sopenharmony_ci readl(cp->regs + REG_MAC_TX_CFG), 264762306a36Sopenharmony_ci readl(cp->regs + REG_TX_FIFO_PKT_CNT), 264862306a36Sopenharmony_ci readl(cp->regs + REG_TX_FIFO_WRITE_PTR), 264962306a36Sopenharmony_ci readl(cp->regs + REG_TX_FIFO_READ_PTR), 265062306a36Sopenharmony_ci readl(cp->regs + REG_TX_SM_1), 265162306a36Sopenharmony_ci readl(cp->regs + REG_TX_SM_2)); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci netdev_err(dev, "RX_STATE[%08x:%08x:%08x]\n", 265462306a36Sopenharmony_ci readl(cp->regs + REG_RX_CFG), 265562306a36Sopenharmony_ci readl(cp->regs + REG_MAC_RX_STATUS), 265662306a36Sopenharmony_ci readl(cp->regs + REG_MAC_RX_CFG)); 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci netdev_err(dev, "HP_STATE[%08x:%08x:%08x:%08x]\n", 265962306a36Sopenharmony_ci readl(cp->regs + REG_HP_STATE_MACHINE), 266062306a36Sopenharmony_ci readl(cp->regs + REG_HP_STATUS0), 266162306a36Sopenharmony_ci readl(cp->regs + REG_HP_STATUS1), 266262306a36Sopenharmony_ci readl(cp->regs + REG_HP_STATUS2)); 266362306a36Sopenharmony_ci 266462306a36Sopenharmony_ci#if 1 266562306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 266662306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_all); 266762306a36Sopenharmony_ci schedule_work(&cp->reset_task); 266862306a36Sopenharmony_ci#else 266962306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, CAS_RESET_ALL); 267062306a36Sopenharmony_ci schedule_work(&cp->reset_task); 267162306a36Sopenharmony_ci#endif 267262306a36Sopenharmony_ci} 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_cistatic inline int cas_intme(int ring, int entry) 267562306a36Sopenharmony_ci{ 267662306a36Sopenharmony_ci /* Algorithm: IRQ every 1/2 of descriptors. */ 267762306a36Sopenharmony_ci if (!(entry & ((TX_DESC_RINGN_SIZE(ring) >> 1) - 1))) 267862306a36Sopenharmony_ci return 1; 267962306a36Sopenharmony_ci return 0; 268062306a36Sopenharmony_ci} 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci 268362306a36Sopenharmony_cistatic void cas_write_txd(struct cas *cp, int ring, int entry, 268462306a36Sopenharmony_ci dma_addr_t mapping, int len, u64 ctrl, int last) 268562306a36Sopenharmony_ci{ 268662306a36Sopenharmony_ci struct cas_tx_desc *txd = cp->init_txds[ring] + entry; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci ctrl |= CAS_BASE(TX_DESC_BUFLEN, len); 268962306a36Sopenharmony_ci if (cas_intme(ring, entry)) 269062306a36Sopenharmony_ci ctrl |= TX_DESC_INTME; 269162306a36Sopenharmony_ci if (last) 269262306a36Sopenharmony_ci ctrl |= TX_DESC_EOF; 269362306a36Sopenharmony_ci txd->control = cpu_to_le64(ctrl); 269462306a36Sopenharmony_ci txd->buffer = cpu_to_le64(mapping); 269562306a36Sopenharmony_ci} 269662306a36Sopenharmony_ci 269762306a36Sopenharmony_cistatic inline void *tx_tiny_buf(struct cas *cp, const int ring, 269862306a36Sopenharmony_ci const int entry) 269962306a36Sopenharmony_ci{ 270062306a36Sopenharmony_ci return cp->tx_tiny_bufs[ring] + TX_TINY_BUF_LEN*entry; 270162306a36Sopenharmony_ci} 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_cistatic inline dma_addr_t tx_tiny_map(struct cas *cp, const int ring, 270462306a36Sopenharmony_ci const int entry, const int tentry) 270562306a36Sopenharmony_ci{ 270662306a36Sopenharmony_ci cp->tx_tiny_use[ring][tentry].nbufs++; 270762306a36Sopenharmony_ci cp->tx_tiny_use[ring][entry].used = 1; 270862306a36Sopenharmony_ci return cp->tx_tiny_dvma[ring] + TX_TINY_BUF_LEN*entry; 270962306a36Sopenharmony_ci} 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_cistatic inline int cas_xmit_tx_ringN(struct cas *cp, int ring, 271262306a36Sopenharmony_ci struct sk_buff *skb) 271362306a36Sopenharmony_ci{ 271462306a36Sopenharmony_ci struct net_device *dev = cp->dev; 271562306a36Sopenharmony_ci int entry, nr_frags, frag, tabort, tentry; 271662306a36Sopenharmony_ci dma_addr_t mapping; 271762306a36Sopenharmony_ci unsigned long flags; 271862306a36Sopenharmony_ci u64 ctrl; 271962306a36Sopenharmony_ci u32 len; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci spin_lock_irqsave(&cp->tx_lock[ring], flags); 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci /* This is a hard error, log it. */ 272462306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(cp, ring) <= 272562306a36Sopenharmony_ci CAS_TABORT(cp)*(skb_shinfo(skb)->nr_frags + 1)) { 272662306a36Sopenharmony_ci netif_stop_queue(dev); 272762306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->tx_lock[ring], flags); 272862306a36Sopenharmony_ci netdev_err(dev, "BUG! Tx Ring full when queue awake!\n"); 272962306a36Sopenharmony_ci return 1; 273062306a36Sopenharmony_ci } 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci ctrl = 0; 273362306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) { 273462306a36Sopenharmony_ci const u64 csum_start_off = skb_checksum_start_offset(skb); 273562306a36Sopenharmony_ci const u64 csum_stuff_off = csum_start_off + skb->csum_offset; 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci ctrl = TX_DESC_CSUM_EN | 273862306a36Sopenharmony_ci CAS_BASE(TX_DESC_CSUM_START, csum_start_off) | 273962306a36Sopenharmony_ci CAS_BASE(TX_DESC_CSUM_STUFF, csum_stuff_off); 274062306a36Sopenharmony_ci } 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_ci entry = cp->tx_new[ring]; 274362306a36Sopenharmony_ci cp->tx_skbs[ring][entry] = skb; 274462306a36Sopenharmony_ci 274562306a36Sopenharmony_ci nr_frags = skb_shinfo(skb)->nr_frags; 274662306a36Sopenharmony_ci len = skb_headlen(skb); 274762306a36Sopenharmony_ci mapping = dma_map_page(&cp->pdev->dev, virt_to_page(skb->data), 274862306a36Sopenharmony_ci offset_in_page(skb->data), len, DMA_TO_DEVICE); 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci tentry = entry; 275162306a36Sopenharmony_ci tabort = cas_calc_tabort(cp, (unsigned long) skb->data, len); 275262306a36Sopenharmony_ci if (unlikely(tabort)) { 275362306a36Sopenharmony_ci /* NOTE: len is always > tabort */ 275462306a36Sopenharmony_ci cas_write_txd(cp, ring, entry, mapping, len - tabort, 275562306a36Sopenharmony_ci ctrl | TX_DESC_SOF, 0); 275662306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 275762306a36Sopenharmony_ci 275862306a36Sopenharmony_ci skb_copy_from_linear_data_offset(skb, len - tabort, 275962306a36Sopenharmony_ci tx_tiny_buf(cp, ring, entry), tabort); 276062306a36Sopenharmony_ci mapping = tx_tiny_map(cp, ring, entry, tentry); 276162306a36Sopenharmony_ci cas_write_txd(cp, ring, entry, mapping, tabort, ctrl, 276262306a36Sopenharmony_ci (nr_frags == 0)); 276362306a36Sopenharmony_ci } else { 276462306a36Sopenharmony_ci cas_write_txd(cp, ring, entry, mapping, len, ctrl | 276562306a36Sopenharmony_ci TX_DESC_SOF, (nr_frags == 0)); 276662306a36Sopenharmony_ci } 276762306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci for (frag = 0; frag < nr_frags; frag++) { 277062306a36Sopenharmony_ci const skb_frag_t *fragp = &skb_shinfo(skb)->frags[frag]; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci len = skb_frag_size(fragp); 277362306a36Sopenharmony_ci mapping = skb_frag_dma_map(&cp->pdev->dev, fragp, 0, len, 277462306a36Sopenharmony_ci DMA_TO_DEVICE); 277562306a36Sopenharmony_ci 277662306a36Sopenharmony_ci tabort = cas_calc_tabort(cp, skb_frag_off(fragp), len); 277762306a36Sopenharmony_ci if (unlikely(tabort)) { 277862306a36Sopenharmony_ci /* NOTE: len is always > tabort */ 277962306a36Sopenharmony_ci cas_write_txd(cp, ring, entry, mapping, len - tabort, 278062306a36Sopenharmony_ci ctrl, 0); 278162306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 278262306a36Sopenharmony_ci memcpy_from_page(tx_tiny_buf(cp, ring, entry), 278362306a36Sopenharmony_ci skb_frag_page(fragp), 278462306a36Sopenharmony_ci skb_frag_off(fragp) + len - tabort, 278562306a36Sopenharmony_ci tabort); 278662306a36Sopenharmony_ci mapping = tx_tiny_map(cp, ring, entry, tentry); 278762306a36Sopenharmony_ci len = tabort; 278862306a36Sopenharmony_ci } 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci cas_write_txd(cp, ring, entry, mapping, len, ctrl, 279162306a36Sopenharmony_ci (frag + 1 == nr_frags)); 279262306a36Sopenharmony_ci entry = TX_DESC_NEXT(ring, entry); 279362306a36Sopenharmony_ci } 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci cp->tx_new[ring] = entry; 279662306a36Sopenharmony_ci if (TX_BUFFS_AVAIL(cp, ring) <= CAS_TABORT(cp)*(MAX_SKB_FRAGS + 1)) 279762306a36Sopenharmony_ci netif_stop_queue(dev); 279862306a36Sopenharmony_ci 279962306a36Sopenharmony_ci netif_printk(cp, tx_queued, KERN_DEBUG, dev, 280062306a36Sopenharmony_ci "tx[%d] queued, slot %d, skblen %d, avail %d\n", 280162306a36Sopenharmony_ci ring, entry, skb->len, TX_BUFFS_AVAIL(cp, ring)); 280262306a36Sopenharmony_ci writel(entry, cp->regs + REG_TX_KICKN(ring)); 280362306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->tx_lock[ring], flags); 280462306a36Sopenharmony_ci return 0; 280562306a36Sopenharmony_ci} 280662306a36Sopenharmony_ci 280762306a36Sopenharmony_cistatic netdev_tx_t cas_start_xmit(struct sk_buff *skb, struct net_device *dev) 280862306a36Sopenharmony_ci{ 280962306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_ci /* this is only used as a load-balancing hint, so it doesn't 281262306a36Sopenharmony_ci * need to be SMP safe 281362306a36Sopenharmony_ci */ 281462306a36Sopenharmony_ci static int ring; 281562306a36Sopenharmony_ci 281662306a36Sopenharmony_ci if (skb_padto(skb, cp->min_frame_size)) 281762306a36Sopenharmony_ci return NETDEV_TX_OK; 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci /* XXX: we need some higher-level QoS hooks to steer packets to 282062306a36Sopenharmony_ci * individual queues. 282162306a36Sopenharmony_ci */ 282262306a36Sopenharmony_ci if (cas_xmit_tx_ringN(cp, ring++ & N_TX_RINGS_MASK, skb)) 282362306a36Sopenharmony_ci return NETDEV_TX_BUSY; 282462306a36Sopenharmony_ci return NETDEV_TX_OK; 282562306a36Sopenharmony_ci} 282662306a36Sopenharmony_ci 282762306a36Sopenharmony_cistatic void cas_init_tx_dma(struct cas *cp) 282862306a36Sopenharmony_ci{ 282962306a36Sopenharmony_ci u64 desc_dma = cp->block_dvma; 283062306a36Sopenharmony_ci unsigned long off; 283162306a36Sopenharmony_ci u32 val; 283262306a36Sopenharmony_ci int i; 283362306a36Sopenharmony_ci 283462306a36Sopenharmony_ci /* set up tx completion writeback registers. must be 8-byte aligned */ 283562306a36Sopenharmony_ci#ifdef USE_TX_COMPWB 283662306a36Sopenharmony_ci off = offsetof(struct cas_init_block, tx_compwb); 283762306a36Sopenharmony_ci writel((desc_dma + off) >> 32, cp->regs + REG_TX_COMPWB_DB_HI); 283862306a36Sopenharmony_ci writel((desc_dma + off) & 0xffffffff, cp->regs + REG_TX_COMPWB_DB_LOW); 283962306a36Sopenharmony_ci#endif 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ci /* enable completion writebacks, enable paced mode, 284262306a36Sopenharmony_ci * disable read pipe, and disable pre-interrupt compwbs 284362306a36Sopenharmony_ci */ 284462306a36Sopenharmony_ci val = TX_CFG_COMPWB_Q1 | TX_CFG_COMPWB_Q2 | 284562306a36Sopenharmony_ci TX_CFG_COMPWB_Q3 | TX_CFG_COMPWB_Q4 | 284662306a36Sopenharmony_ci TX_CFG_DMA_RDPIPE_DIS | TX_CFG_PACED_MODE | 284762306a36Sopenharmony_ci TX_CFG_INTR_COMPWB_DIS; 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci /* write out tx ring info and tx desc bases */ 285062306a36Sopenharmony_ci for (i = 0; i < MAX_TX_RINGS; i++) { 285162306a36Sopenharmony_ci off = (unsigned long) cp->init_txds[i] - 285262306a36Sopenharmony_ci (unsigned long) cp->init_block; 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_ci val |= CAS_TX_RINGN_BASE(i); 285562306a36Sopenharmony_ci writel((desc_dma + off) >> 32, cp->regs + REG_TX_DBN_HI(i)); 285662306a36Sopenharmony_ci writel((desc_dma + off) & 0xffffffff, cp->regs + 285762306a36Sopenharmony_ci REG_TX_DBN_LOW(i)); 285862306a36Sopenharmony_ci /* don't zero out the kick register here as the system 285962306a36Sopenharmony_ci * will wedge 286062306a36Sopenharmony_ci */ 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci writel(val, cp->regs + REG_TX_CFG); 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci /* program max burst sizes. these numbers should be different 286562306a36Sopenharmony_ci * if doing QoS. 286662306a36Sopenharmony_ci */ 286762306a36Sopenharmony_ci#ifdef USE_QOS 286862306a36Sopenharmony_ci writel(0x800, cp->regs + REG_TX_MAXBURST_0); 286962306a36Sopenharmony_ci writel(0x1600, cp->regs + REG_TX_MAXBURST_1); 287062306a36Sopenharmony_ci writel(0x2400, cp->regs + REG_TX_MAXBURST_2); 287162306a36Sopenharmony_ci writel(0x4800, cp->regs + REG_TX_MAXBURST_3); 287262306a36Sopenharmony_ci#else 287362306a36Sopenharmony_ci writel(0x800, cp->regs + REG_TX_MAXBURST_0); 287462306a36Sopenharmony_ci writel(0x800, cp->regs + REG_TX_MAXBURST_1); 287562306a36Sopenharmony_ci writel(0x800, cp->regs + REG_TX_MAXBURST_2); 287662306a36Sopenharmony_ci writel(0x800, cp->regs + REG_TX_MAXBURST_3); 287762306a36Sopenharmony_ci#endif 287862306a36Sopenharmony_ci} 287962306a36Sopenharmony_ci 288062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 288162306a36Sopenharmony_cistatic inline void cas_init_dma(struct cas *cp) 288262306a36Sopenharmony_ci{ 288362306a36Sopenharmony_ci cas_init_tx_dma(cp); 288462306a36Sopenharmony_ci cas_init_rx_dma(cp); 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_cistatic void cas_process_mc_list(struct cas *cp) 288862306a36Sopenharmony_ci{ 288962306a36Sopenharmony_ci u16 hash_table[16]; 289062306a36Sopenharmony_ci u32 crc; 289162306a36Sopenharmony_ci struct netdev_hw_addr *ha; 289262306a36Sopenharmony_ci int i = 1; 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci memset(hash_table, 0, sizeof(hash_table)); 289562306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, cp->dev) { 289662306a36Sopenharmony_ci if (i <= CAS_MC_EXACT_MATCH_SIZE) { 289762306a36Sopenharmony_ci /* use the alternate mac address registers for the 289862306a36Sopenharmony_ci * first 15 multicast addresses 289962306a36Sopenharmony_ci */ 290062306a36Sopenharmony_ci writel((ha->addr[4] << 8) | ha->addr[5], 290162306a36Sopenharmony_ci cp->regs + REG_MAC_ADDRN(i*3 + 0)); 290262306a36Sopenharmony_ci writel((ha->addr[2] << 8) | ha->addr[3], 290362306a36Sopenharmony_ci cp->regs + REG_MAC_ADDRN(i*3 + 1)); 290462306a36Sopenharmony_ci writel((ha->addr[0] << 8) | ha->addr[1], 290562306a36Sopenharmony_ci cp->regs + REG_MAC_ADDRN(i*3 + 2)); 290662306a36Sopenharmony_ci i++; 290762306a36Sopenharmony_ci } 290862306a36Sopenharmony_ci else { 290962306a36Sopenharmony_ci /* use hw hash table for the next series of 291062306a36Sopenharmony_ci * multicast addresses 291162306a36Sopenharmony_ci */ 291262306a36Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, ha->addr); 291362306a36Sopenharmony_ci crc >>= 24; 291462306a36Sopenharmony_ci hash_table[crc >> 4] |= 1 << (15 - (crc & 0xf)); 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci } 291762306a36Sopenharmony_ci for (i = 0; i < 16; i++) 291862306a36Sopenharmony_ci writel(hash_table[i], cp->regs + REG_MAC_HASH_TABLEN(i)); 291962306a36Sopenharmony_ci} 292062306a36Sopenharmony_ci 292162306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 292262306a36Sopenharmony_cistatic u32 cas_setup_multicast(struct cas *cp) 292362306a36Sopenharmony_ci{ 292462306a36Sopenharmony_ci u32 rxcfg = 0; 292562306a36Sopenharmony_ci int i; 292662306a36Sopenharmony_ci 292762306a36Sopenharmony_ci if (cp->dev->flags & IFF_PROMISC) { 292862306a36Sopenharmony_ci rxcfg |= MAC_RX_CFG_PROMISC_EN; 292962306a36Sopenharmony_ci 293062306a36Sopenharmony_ci } else if (cp->dev->flags & IFF_ALLMULTI) { 293162306a36Sopenharmony_ci for (i=0; i < 16; i++) 293262306a36Sopenharmony_ci writel(0xFFFF, cp->regs + REG_MAC_HASH_TABLEN(i)); 293362306a36Sopenharmony_ci rxcfg |= MAC_RX_CFG_HASH_FILTER_EN; 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_ci } else { 293662306a36Sopenharmony_ci cas_process_mc_list(cp); 293762306a36Sopenharmony_ci rxcfg |= MAC_RX_CFG_HASH_FILTER_EN; 293862306a36Sopenharmony_ci } 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci return rxcfg; 294162306a36Sopenharmony_ci} 294262306a36Sopenharmony_ci 294362306a36Sopenharmony_ci/* must be invoked under cp->stat_lock[N_TX_RINGS] */ 294462306a36Sopenharmony_cistatic void cas_clear_mac_err(struct cas *cp) 294562306a36Sopenharmony_ci{ 294662306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_COLL_NORMAL); 294762306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_COLL_FIRST); 294862306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_COLL_EXCESS); 294962306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_COLL_LATE); 295062306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_TIMER_DEFER); 295162306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ATTEMPTS_PEAK); 295262306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_RECV_FRAME); 295362306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_LEN_ERR); 295462306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ALIGN_ERR); 295562306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_FCS_ERR); 295662306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_RX_CODE_ERR); 295762306a36Sopenharmony_ci} 295862306a36Sopenharmony_ci 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_cistatic void cas_mac_reset(struct cas *cp) 296162306a36Sopenharmony_ci{ 296262306a36Sopenharmony_ci int i; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci /* do both TX and RX reset */ 296562306a36Sopenharmony_ci writel(0x1, cp->regs + REG_MAC_TX_RESET); 296662306a36Sopenharmony_ci writel(0x1, cp->regs + REG_MAC_RX_RESET); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci /* wait for TX */ 296962306a36Sopenharmony_ci i = STOP_TRIES; 297062306a36Sopenharmony_ci while (i-- > 0) { 297162306a36Sopenharmony_ci if (readl(cp->regs + REG_MAC_TX_RESET) == 0) 297262306a36Sopenharmony_ci break; 297362306a36Sopenharmony_ci udelay(10); 297462306a36Sopenharmony_ci } 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci /* wait for RX */ 297762306a36Sopenharmony_ci i = STOP_TRIES; 297862306a36Sopenharmony_ci while (i-- > 0) { 297962306a36Sopenharmony_ci if (readl(cp->regs + REG_MAC_RX_RESET) == 0) 298062306a36Sopenharmony_ci break; 298162306a36Sopenharmony_ci udelay(10); 298262306a36Sopenharmony_ci } 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci if (readl(cp->regs + REG_MAC_TX_RESET) | 298562306a36Sopenharmony_ci readl(cp->regs + REG_MAC_RX_RESET)) 298662306a36Sopenharmony_ci netdev_err(cp->dev, "mac tx[%d]/rx[%d] reset failed [%08x]\n", 298762306a36Sopenharmony_ci readl(cp->regs + REG_MAC_TX_RESET), 298862306a36Sopenharmony_ci readl(cp->regs + REG_MAC_RX_RESET), 298962306a36Sopenharmony_ci readl(cp->regs + REG_MAC_STATE_MACHINE)); 299062306a36Sopenharmony_ci} 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 299462306a36Sopenharmony_cistatic void cas_init_mac(struct cas *cp) 299562306a36Sopenharmony_ci{ 299662306a36Sopenharmony_ci const unsigned char *e = &cp->dev->dev_addr[0]; 299762306a36Sopenharmony_ci int i; 299862306a36Sopenharmony_ci cas_mac_reset(cp); 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci /* setup core arbitration weight register */ 300162306a36Sopenharmony_ci writel(CAWR_RR_DIS, cp->regs + REG_CAWR); 300262306a36Sopenharmony_ci 300362306a36Sopenharmony_ci#if !defined(CONFIG_SPARC64) && !defined(CONFIG_ALPHA) 300462306a36Sopenharmony_ci /* set the infinite burst register for chips that don't have 300562306a36Sopenharmony_ci * pci issues. 300662306a36Sopenharmony_ci */ 300762306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) == 0) 300862306a36Sopenharmony_ci writel(INF_BURST_EN, cp->regs + REG_INF_BURST); 300962306a36Sopenharmony_ci#endif 301062306a36Sopenharmony_ci 301162306a36Sopenharmony_ci writel(0x1BF0, cp->regs + REG_MAC_SEND_PAUSE); 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci writel(0x00, cp->regs + REG_MAC_IPG0); 301462306a36Sopenharmony_ci writel(0x08, cp->regs + REG_MAC_IPG1); 301562306a36Sopenharmony_ci writel(0x04, cp->regs + REG_MAC_IPG2); 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci /* change later for 802.3z */ 301862306a36Sopenharmony_ci writel(0x40, cp->regs + REG_MAC_SLOT_TIME); 301962306a36Sopenharmony_ci 302062306a36Sopenharmony_ci /* min frame + FCS */ 302162306a36Sopenharmony_ci writel(ETH_ZLEN + 4, cp->regs + REG_MAC_FRAMESIZE_MIN); 302262306a36Sopenharmony_ci 302362306a36Sopenharmony_ci /* Ethernet payload + header + FCS + optional VLAN tag. NOTE: we 302462306a36Sopenharmony_ci * specify the maximum frame size to prevent RX tag errors on 302562306a36Sopenharmony_ci * oversized frames. 302662306a36Sopenharmony_ci */ 302762306a36Sopenharmony_ci writel(CAS_BASE(MAC_FRAMESIZE_MAX_BURST, 0x2000) | 302862306a36Sopenharmony_ci CAS_BASE(MAC_FRAMESIZE_MAX_FRAME, 302962306a36Sopenharmony_ci (CAS_MAX_MTU + ETH_HLEN + 4 + 4)), 303062306a36Sopenharmony_ci cp->regs + REG_MAC_FRAMESIZE_MAX); 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci /* NOTE: crc_size is used as a surrogate for half-duplex. 303362306a36Sopenharmony_ci * workaround saturn half-duplex issue by increasing preamble 303462306a36Sopenharmony_ci * size to 65 bytes. 303562306a36Sopenharmony_ci */ 303662306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_SATURN) && cp->crc_size) 303762306a36Sopenharmony_ci writel(0x41, cp->regs + REG_MAC_PA_SIZE); 303862306a36Sopenharmony_ci else 303962306a36Sopenharmony_ci writel(0x07, cp->regs + REG_MAC_PA_SIZE); 304062306a36Sopenharmony_ci writel(0x04, cp->regs + REG_MAC_JAM_SIZE); 304162306a36Sopenharmony_ci writel(0x10, cp->regs + REG_MAC_ATTEMPT_LIMIT); 304262306a36Sopenharmony_ci writel(0x8808, cp->regs + REG_MAC_CTRL_TYPE); 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci writel((e[5] | (e[4] << 8)) & 0x3ff, cp->regs + REG_MAC_RANDOM_SEED); 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ADDR_FILTER0); 304762306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ADDR_FILTER1); 304862306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ADDR_FILTER2); 304962306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ADDR_FILTER2_1_MASK); 305062306a36Sopenharmony_ci writel(0, cp->regs + REG_MAC_ADDR_FILTER0_MASK); 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ci /* setup mac address in perfect filter array */ 305362306a36Sopenharmony_ci for (i = 0; i < 45; i++) 305462306a36Sopenharmony_ci writel(0x0, cp->regs + REG_MAC_ADDRN(i)); 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci writel((e[4] << 8) | e[5], cp->regs + REG_MAC_ADDRN(0)); 305762306a36Sopenharmony_ci writel((e[2] << 8) | e[3], cp->regs + REG_MAC_ADDRN(1)); 305862306a36Sopenharmony_ci writel((e[0] << 8) | e[1], cp->regs + REG_MAC_ADDRN(2)); 305962306a36Sopenharmony_ci 306062306a36Sopenharmony_ci writel(0x0001, cp->regs + REG_MAC_ADDRN(42)); 306162306a36Sopenharmony_ci writel(0xc200, cp->regs + REG_MAC_ADDRN(43)); 306262306a36Sopenharmony_ci writel(0x0180, cp->regs + REG_MAC_ADDRN(44)); 306362306a36Sopenharmony_ci 306462306a36Sopenharmony_ci cp->mac_rx_cfg = cas_setup_multicast(cp); 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci spin_lock(&cp->stat_lock[N_TX_RINGS]); 306762306a36Sopenharmony_ci cas_clear_mac_err(cp); 306862306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[N_TX_RINGS]); 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci /* Setup MAC interrupts. We want to get all of the interesting 307162306a36Sopenharmony_ci * counter expiration events, but we do not want to hear about 307262306a36Sopenharmony_ci * normal rx/tx as the DMA engine tells us that. 307362306a36Sopenharmony_ci */ 307462306a36Sopenharmony_ci writel(MAC_TX_FRAME_XMIT, cp->regs + REG_MAC_TX_MASK); 307562306a36Sopenharmony_ci writel(MAC_RX_FRAME_RECV, cp->regs + REG_MAC_RX_MASK); 307662306a36Sopenharmony_ci 307762306a36Sopenharmony_ci /* Don't enable even the PAUSE interrupts for now, we 307862306a36Sopenharmony_ci * make no use of those events other than to record them. 307962306a36Sopenharmony_ci */ 308062306a36Sopenharmony_ci writel(0xffffffff, cp->regs + REG_MAC_CTRL_MASK); 308162306a36Sopenharmony_ci} 308262306a36Sopenharmony_ci 308362306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 308462306a36Sopenharmony_cistatic void cas_init_pause_thresholds(struct cas *cp) 308562306a36Sopenharmony_ci{ 308662306a36Sopenharmony_ci /* Calculate pause thresholds. Setting the OFF threshold to the 308762306a36Sopenharmony_ci * full RX fifo size effectively disables PAUSE generation 308862306a36Sopenharmony_ci */ 308962306a36Sopenharmony_ci if (cp->rx_fifo_size <= (2 * 1024)) { 309062306a36Sopenharmony_ci cp->rx_pause_off = cp->rx_pause_on = cp->rx_fifo_size; 309162306a36Sopenharmony_ci } else { 309262306a36Sopenharmony_ci int max_frame = (cp->dev->mtu + ETH_HLEN + 4 + 4 + 64) & ~63; 309362306a36Sopenharmony_ci if (max_frame * 3 > cp->rx_fifo_size) { 309462306a36Sopenharmony_ci cp->rx_pause_off = 7104; 309562306a36Sopenharmony_ci cp->rx_pause_on = 960; 309662306a36Sopenharmony_ci } else { 309762306a36Sopenharmony_ci int off = (cp->rx_fifo_size - (max_frame * 2)); 309862306a36Sopenharmony_ci int on = off - max_frame; 309962306a36Sopenharmony_ci cp->rx_pause_off = off; 310062306a36Sopenharmony_ci cp->rx_pause_on = on; 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci } 310362306a36Sopenharmony_ci} 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_cistatic int cas_vpd_match(const void __iomem *p, const char *str) 310662306a36Sopenharmony_ci{ 310762306a36Sopenharmony_ci int len = strlen(str) + 1; 310862306a36Sopenharmony_ci int i; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci for (i = 0; i < len; i++) { 311162306a36Sopenharmony_ci if (readb(p + i) != str[i]) 311262306a36Sopenharmony_ci return 0; 311362306a36Sopenharmony_ci } 311462306a36Sopenharmony_ci return 1; 311562306a36Sopenharmony_ci} 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci/* get the mac address by reading the vpd information in the rom. 311962306a36Sopenharmony_ci * also get the phy type and determine if there's an entropy generator. 312062306a36Sopenharmony_ci * NOTE: this is a bit convoluted for the following reasons: 312162306a36Sopenharmony_ci * 1) vpd info has order-dependent mac addresses for multinic cards 312262306a36Sopenharmony_ci * 2) the only way to determine the nic order is to use the slot 312362306a36Sopenharmony_ci * number. 312462306a36Sopenharmony_ci * 3) fiber cards don't have bridges, so their slot numbers don't 312562306a36Sopenharmony_ci * mean anything. 312662306a36Sopenharmony_ci * 4) we don't actually know we have a fiber card until after 312762306a36Sopenharmony_ci * the mac addresses are parsed. 312862306a36Sopenharmony_ci */ 312962306a36Sopenharmony_cistatic int cas_get_vpd_info(struct cas *cp, unsigned char *dev_addr, 313062306a36Sopenharmony_ci const int offset) 313162306a36Sopenharmony_ci{ 313262306a36Sopenharmony_ci void __iomem *p = cp->regs + REG_EXPANSION_ROM_RUN_START; 313362306a36Sopenharmony_ci void __iomem *base, *kstart; 313462306a36Sopenharmony_ci int i, len; 313562306a36Sopenharmony_ci int found = 0; 313662306a36Sopenharmony_ci#define VPD_FOUND_MAC 0x01 313762306a36Sopenharmony_ci#define VPD_FOUND_PHY 0x02 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci int phy_type = CAS_PHY_MII_MDIO0; /* default phy type */ 314062306a36Sopenharmony_ci int mac_off = 0; 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci#if defined(CONFIG_SPARC) 314362306a36Sopenharmony_ci const unsigned char *addr; 314462306a36Sopenharmony_ci#endif 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci /* give us access to the PROM */ 314762306a36Sopenharmony_ci writel(BIM_LOCAL_DEV_PROM | BIM_LOCAL_DEV_PAD, 314862306a36Sopenharmony_ci cp->regs + REG_BIM_LOCAL_DEV_EN); 314962306a36Sopenharmony_ci 315062306a36Sopenharmony_ci /* check for an expansion rom */ 315162306a36Sopenharmony_ci if (readb(p) != 0x55 || readb(p + 1) != 0xaa) 315262306a36Sopenharmony_ci goto use_random_mac_addr; 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci /* search for beginning of vpd */ 315562306a36Sopenharmony_ci base = NULL; 315662306a36Sopenharmony_ci for (i = 2; i < EXPANSION_ROM_SIZE; i++) { 315762306a36Sopenharmony_ci /* check for PCIR */ 315862306a36Sopenharmony_ci if ((readb(p + i + 0) == 0x50) && 315962306a36Sopenharmony_ci (readb(p + i + 1) == 0x43) && 316062306a36Sopenharmony_ci (readb(p + i + 2) == 0x49) && 316162306a36Sopenharmony_ci (readb(p + i + 3) == 0x52)) { 316262306a36Sopenharmony_ci base = p + (readb(p + i + 8) | 316362306a36Sopenharmony_ci (readb(p + i + 9) << 8)); 316462306a36Sopenharmony_ci break; 316562306a36Sopenharmony_ci } 316662306a36Sopenharmony_ci } 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_ci if (!base || (readb(base) != 0x82)) 316962306a36Sopenharmony_ci goto use_random_mac_addr; 317062306a36Sopenharmony_ci 317162306a36Sopenharmony_ci i = (readb(base + 1) | (readb(base + 2) << 8)) + 3; 317262306a36Sopenharmony_ci while (i < EXPANSION_ROM_SIZE) { 317362306a36Sopenharmony_ci if (readb(base + i) != 0x90) /* no vpd found */ 317462306a36Sopenharmony_ci goto use_random_mac_addr; 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci /* found a vpd field */ 317762306a36Sopenharmony_ci len = readb(base + i + 1) | (readb(base + i + 2) << 8); 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci /* extract keywords */ 318062306a36Sopenharmony_ci kstart = base + i + 3; 318162306a36Sopenharmony_ci p = kstart; 318262306a36Sopenharmony_ci while ((p - kstart) < len) { 318362306a36Sopenharmony_ci int klen = readb(p + 2); 318462306a36Sopenharmony_ci int j; 318562306a36Sopenharmony_ci char type; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci p += 3; 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci /* look for the following things: 319062306a36Sopenharmony_ci * -- correct length == 29 319162306a36Sopenharmony_ci * 3 (type) + 2 (size) + 319262306a36Sopenharmony_ci * 18 (strlen("local-mac-address") + 1) + 319362306a36Sopenharmony_ci * 6 (mac addr) 319462306a36Sopenharmony_ci * -- VPD Instance 'I' 319562306a36Sopenharmony_ci * -- VPD Type Bytes 'B' 319662306a36Sopenharmony_ci * -- VPD data length == 6 319762306a36Sopenharmony_ci * -- property string == local-mac-address 319862306a36Sopenharmony_ci * 319962306a36Sopenharmony_ci * -- correct length == 24 320062306a36Sopenharmony_ci * 3 (type) + 2 (size) + 320162306a36Sopenharmony_ci * 12 (strlen("entropy-dev") + 1) + 320262306a36Sopenharmony_ci * 7 (strlen("vms110") + 1) 320362306a36Sopenharmony_ci * -- VPD Instance 'I' 320462306a36Sopenharmony_ci * -- VPD Type String 'B' 320562306a36Sopenharmony_ci * -- VPD data length == 7 320662306a36Sopenharmony_ci * -- property string == entropy-dev 320762306a36Sopenharmony_ci * 320862306a36Sopenharmony_ci * -- correct length == 18 320962306a36Sopenharmony_ci * 3 (type) + 2 (size) + 321062306a36Sopenharmony_ci * 9 (strlen("phy-type") + 1) + 321162306a36Sopenharmony_ci * 4 (strlen("pcs") + 1) 321262306a36Sopenharmony_ci * -- VPD Instance 'I' 321362306a36Sopenharmony_ci * -- VPD Type String 'S' 321462306a36Sopenharmony_ci * -- VPD data length == 4 321562306a36Sopenharmony_ci * -- property string == phy-type 321662306a36Sopenharmony_ci * 321762306a36Sopenharmony_ci * -- correct length == 23 321862306a36Sopenharmony_ci * 3 (type) + 2 (size) + 321962306a36Sopenharmony_ci * 14 (strlen("phy-interface") + 1) + 322062306a36Sopenharmony_ci * 4 (strlen("pcs") + 1) 322162306a36Sopenharmony_ci * -- VPD Instance 'I' 322262306a36Sopenharmony_ci * -- VPD Type String 'S' 322362306a36Sopenharmony_ci * -- VPD data length == 4 322462306a36Sopenharmony_ci * -- property string == phy-interface 322562306a36Sopenharmony_ci */ 322662306a36Sopenharmony_ci if (readb(p) != 'I') 322762306a36Sopenharmony_ci goto next; 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_ci /* finally, check string and length */ 323062306a36Sopenharmony_ci type = readb(p + 3); 323162306a36Sopenharmony_ci if (type == 'B') { 323262306a36Sopenharmony_ci if ((klen == 29) && readb(p + 4) == 6 && 323362306a36Sopenharmony_ci cas_vpd_match(p + 5, 323462306a36Sopenharmony_ci "local-mac-address")) { 323562306a36Sopenharmony_ci if (mac_off++ > offset) 323662306a36Sopenharmony_ci goto next; 323762306a36Sopenharmony_ci 323862306a36Sopenharmony_ci /* set mac address */ 323962306a36Sopenharmony_ci for (j = 0; j < 6; j++) 324062306a36Sopenharmony_ci dev_addr[j] = 324162306a36Sopenharmony_ci readb(p + 23 + j); 324262306a36Sopenharmony_ci goto found_mac; 324362306a36Sopenharmony_ci } 324462306a36Sopenharmony_ci } 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci if (type != 'S') 324762306a36Sopenharmony_ci goto next; 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci#ifdef USE_ENTROPY_DEV 325062306a36Sopenharmony_ci if ((klen == 24) && 325162306a36Sopenharmony_ci cas_vpd_match(p + 5, "entropy-dev") && 325262306a36Sopenharmony_ci cas_vpd_match(p + 17, "vms110")) { 325362306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_ENTROPY_DEV; 325462306a36Sopenharmony_ci goto next; 325562306a36Sopenharmony_ci } 325662306a36Sopenharmony_ci#endif 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci if (found & VPD_FOUND_PHY) 325962306a36Sopenharmony_ci goto next; 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci if ((klen == 18) && readb(p + 4) == 4 && 326262306a36Sopenharmony_ci cas_vpd_match(p + 5, "phy-type")) { 326362306a36Sopenharmony_ci if (cas_vpd_match(p + 14, "pcs")) { 326462306a36Sopenharmony_ci phy_type = CAS_PHY_SERDES; 326562306a36Sopenharmony_ci goto found_phy; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci } 326862306a36Sopenharmony_ci 326962306a36Sopenharmony_ci if ((klen == 23) && readb(p + 4) == 4 && 327062306a36Sopenharmony_ci cas_vpd_match(p + 5, "phy-interface")) { 327162306a36Sopenharmony_ci if (cas_vpd_match(p + 19, "pcs")) { 327262306a36Sopenharmony_ci phy_type = CAS_PHY_SERDES; 327362306a36Sopenharmony_ci goto found_phy; 327462306a36Sopenharmony_ci } 327562306a36Sopenharmony_ci } 327662306a36Sopenharmony_cifound_mac: 327762306a36Sopenharmony_ci found |= VPD_FOUND_MAC; 327862306a36Sopenharmony_ci goto next; 327962306a36Sopenharmony_ci 328062306a36Sopenharmony_cifound_phy: 328162306a36Sopenharmony_ci found |= VPD_FOUND_PHY; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_cinext: 328462306a36Sopenharmony_ci p += klen; 328562306a36Sopenharmony_ci } 328662306a36Sopenharmony_ci i += len + 3; 328762306a36Sopenharmony_ci } 328862306a36Sopenharmony_ci 328962306a36Sopenharmony_ciuse_random_mac_addr: 329062306a36Sopenharmony_ci if (found & VPD_FOUND_MAC) 329162306a36Sopenharmony_ci goto done; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci#if defined(CONFIG_SPARC) 329462306a36Sopenharmony_ci addr = of_get_property(cp->of_node, "local-mac-address", NULL); 329562306a36Sopenharmony_ci if (addr != NULL) { 329662306a36Sopenharmony_ci memcpy(dev_addr, addr, ETH_ALEN); 329762306a36Sopenharmony_ci goto done; 329862306a36Sopenharmony_ci } 329962306a36Sopenharmony_ci#endif 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci /* Sun MAC prefix then 3 random bytes. */ 330262306a36Sopenharmony_ci pr_info("MAC address not found in ROM VPD\n"); 330362306a36Sopenharmony_ci dev_addr[0] = 0x08; 330462306a36Sopenharmony_ci dev_addr[1] = 0x00; 330562306a36Sopenharmony_ci dev_addr[2] = 0x20; 330662306a36Sopenharmony_ci get_random_bytes(dev_addr + 3, 3); 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_cidone: 330962306a36Sopenharmony_ci writel(0, cp->regs + REG_BIM_LOCAL_DEV_EN); 331062306a36Sopenharmony_ci return phy_type; 331162306a36Sopenharmony_ci} 331262306a36Sopenharmony_ci 331362306a36Sopenharmony_ci/* check pci invariants */ 331462306a36Sopenharmony_cistatic void cas_check_pci_invariants(struct cas *cp) 331562306a36Sopenharmony_ci{ 331662306a36Sopenharmony_ci struct pci_dev *pdev = cp->pdev; 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci cp->cas_flags = 0; 331962306a36Sopenharmony_ci if ((pdev->vendor == PCI_VENDOR_ID_SUN) && 332062306a36Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_SUN_CASSINI)) { 332162306a36Sopenharmony_ci if (pdev->revision >= CAS_ID_REVPLUS) 332262306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_REG_PLUS; 332362306a36Sopenharmony_ci if (pdev->revision < CAS_ID_REVPLUS02u) 332462306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_TARGET_ABORT; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci /* Original Cassini supports HW CSUM, but it's not 332762306a36Sopenharmony_ci * enabled by default as it can trigger TX hangs. 332862306a36Sopenharmony_ci */ 332962306a36Sopenharmony_ci if (pdev->revision < CAS_ID_REV2) 333062306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_NO_HW_CSUM; 333162306a36Sopenharmony_ci } else { 333262306a36Sopenharmony_ci /* Only sun has original cassini chips. */ 333362306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_REG_PLUS; 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_ci /* We use a flag because the same phy might be externally 333662306a36Sopenharmony_ci * connected. 333762306a36Sopenharmony_ci */ 333862306a36Sopenharmony_ci if ((pdev->vendor == PCI_VENDOR_ID_NS) && 333962306a36Sopenharmony_ci (pdev->device == PCI_DEVICE_ID_NS_SATURN)) 334062306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_SATURN; 334162306a36Sopenharmony_ci } 334262306a36Sopenharmony_ci} 334362306a36Sopenharmony_ci 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_cistatic int cas_check_invariants(struct cas *cp) 334662306a36Sopenharmony_ci{ 334762306a36Sopenharmony_ci struct pci_dev *pdev = cp->pdev; 334862306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 334962306a36Sopenharmony_ci u32 cfg; 335062306a36Sopenharmony_ci int i; 335162306a36Sopenharmony_ci 335262306a36Sopenharmony_ci /* get page size for rx buffers. */ 335362306a36Sopenharmony_ci cp->page_order = 0; 335462306a36Sopenharmony_ci#ifdef USE_PAGE_ORDER 335562306a36Sopenharmony_ci if (PAGE_SHIFT < CAS_JUMBO_PAGE_SHIFT) { 335662306a36Sopenharmony_ci /* see if we can allocate larger pages */ 335762306a36Sopenharmony_ci struct page *page = alloc_pages(GFP_ATOMIC, 335862306a36Sopenharmony_ci CAS_JUMBO_PAGE_SHIFT - 335962306a36Sopenharmony_ci PAGE_SHIFT); 336062306a36Sopenharmony_ci if (page) { 336162306a36Sopenharmony_ci __free_pages(page, CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT); 336262306a36Sopenharmony_ci cp->page_order = CAS_JUMBO_PAGE_SHIFT - PAGE_SHIFT; 336362306a36Sopenharmony_ci } else { 336462306a36Sopenharmony_ci printk("MTU limited to %d bytes\n", CAS_MAX_MTU); 336562306a36Sopenharmony_ci } 336662306a36Sopenharmony_ci } 336762306a36Sopenharmony_ci#endif 336862306a36Sopenharmony_ci cp->page_size = (PAGE_SIZE << cp->page_order); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci /* Fetch the FIFO configurations. */ 337162306a36Sopenharmony_ci cp->tx_fifo_size = readl(cp->regs + REG_TX_FIFO_SIZE) * 64; 337262306a36Sopenharmony_ci cp->rx_fifo_size = RX_FIFO_SIZE; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci /* finish phy determination. MDIO1 takes precedence over MDIO0 if 337562306a36Sopenharmony_ci * they're both connected. 337662306a36Sopenharmony_ci */ 337762306a36Sopenharmony_ci cp->phy_type = cas_get_vpd_info(cp, addr, PCI_SLOT(pdev->devfn)); 337862306a36Sopenharmony_ci eth_hw_addr_set(cp->dev, addr); 337962306a36Sopenharmony_ci if (cp->phy_type & CAS_PHY_SERDES) { 338062306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_1000MB_CAP; 338162306a36Sopenharmony_ci return 0; /* no more checking needed */ 338262306a36Sopenharmony_ci } 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci /* MII */ 338562306a36Sopenharmony_ci cfg = readl(cp->regs + REG_MIF_CFG); 338662306a36Sopenharmony_ci if (cfg & MIF_CFG_MDIO_1) { 338762306a36Sopenharmony_ci cp->phy_type = CAS_PHY_MII_MDIO1; 338862306a36Sopenharmony_ci } else if (cfg & MIF_CFG_MDIO_0) { 338962306a36Sopenharmony_ci cp->phy_type = CAS_PHY_MII_MDIO0; 339062306a36Sopenharmony_ci } 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci cas_mif_poll(cp, 0); 339362306a36Sopenharmony_ci writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE); 339462306a36Sopenharmony_ci 339562306a36Sopenharmony_ci for (i = 0; i < 32; i++) { 339662306a36Sopenharmony_ci u32 phy_id; 339762306a36Sopenharmony_ci int j; 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_ci for (j = 0; j < 3; j++) { 340062306a36Sopenharmony_ci cp->phy_addr = i; 340162306a36Sopenharmony_ci phy_id = cas_phy_read(cp, MII_PHYSID1) << 16; 340262306a36Sopenharmony_ci phy_id |= cas_phy_read(cp, MII_PHYSID2); 340362306a36Sopenharmony_ci if (phy_id && (phy_id != 0xFFFFFFFF)) { 340462306a36Sopenharmony_ci cp->phy_id = phy_id; 340562306a36Sopenharmony_ci goto done; 340662306a36Sopenharmony_ci } 340762306a36Sopenharmony_ci } 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci pr_err("MII phy did not respond [%08x]\n", 341062306a36Sopenharmony_ci readl(cp->regs + REG_MIF_STATE_MACHINE)); 341162306a36Sopenharmony_ci return -1; 341262306a36Sopenharmony_ci 341362306a36Sopenharmony_cidone: 341462306a36Sopenharmony_ci /* see if we can do gigabit */ 341562306a36Sopenharmony_ci cfg = cas_phy_read(cp, MII_BMSR); 341662306a36Sopenharmony_ci if ((cfg & CAS_BMSR_1000_EXTEND) && 341762306a36Sopenharmony_ci cas_phy_read(cp, CAS_MII_1000_EXTEND)) 341862306a36Sopenharmony_ci cp->cas_flags |= CAS_FLAG_1000MB_CAP; 341962306a36Sopenharmony_ci return 0; 342062306a36Sopenharmony_ci} 342162306a36Sopenharmony_ci 342262306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 342362306a36Sopenharmony_cistatic inline void cas_start_dma(struct cas *cp) 342462306a36Sopenharmony_ci{ 342562306a36Sopenharmony_ci int i; 342662306a36Sopenharmony_ci u32 val; 342762306a36Sopenharmony_ci int txfailed = 0; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci /* enable dma */ 343062306a36Sopenharmony_ci val = readl(cp->regs + REG_TX_CFG) | TX_CFG_DMA_EN; 343162306a36Sopenharmony_ci writel(val, cp->regs + REG_TX_CFG); 343262306a36Sopenharmony_ci val = readl(cp->regs + REG_RX_CFG) | RX_CFG_DMA_EN; 343362306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_CFG); 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_ci /* enable the mac */ 343662306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_TX_CFG) | MAC_TX_CFG_EN; 343762306a36Sopenharmony_ci writel(val, cp->regs + REG_MAC_TX_CFG); 343862306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_RX_CFG) | MAC_RX_CFG_EN; 343962306a36Sopenharmony_ci writel(val, cp->regs + REG_MAC_RX_CFG); 344062306a36Sopenharmony_ci 344162306a36Sopenharmony_ci i = STOP_TRIES; 344262306a36Sopenharmony_ci while (i-- > 0) { 344362306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_TX_CFG); 344462306a36Sopenharmony_ci if ((val & MAC_TX_CFG_EN)) 344562306a36Sopenharmony_ci break; 344662306a36Sopenharmony_ci udelay(10); 344762306a36Sopenharmony_ci } 344862306a36Sopenharmony_ci if (i < 0) txfailed = 1; 344962306a36Sopenharmony_ci i = STOP_TRIES; 345062306a36Sopenharmony_ci while (i-- > 0) { 345162306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_RX_CFG); 345262306a36Sopenharmony_ci if ((val & MAC_RX_CFG_EN)) { 345362306a36Sopenharmony_ci if (txfailed) { 345462306a36Sopenharmony_ci netdev_err(cp->dev, 345562306a36Sopenharmony_ci "enabling mac failed [tx:%08x:%08x]\n", 345662306a36Sopenharmony_ci readl(cp->regs + REG_MIF_STATE_MACHINE), 345762306a36Sopenharmony_ci readl(cp->regs + REG_MAC_STATE_MACHINE)); 345862306a36Sopenharmony_ci } 345962306a36Sopenharmony_ci goto enable_rx_done; 346062306a36Sopenharmony_ci } 346162306a36Sopenharmony_ci udelay(10); 346262306a36Sopenharmony_ci } 346362306a36Sopenharmony_ci netdev_err(cp->dev, "enabling mac failed [%s:%08x:%08x]\n", 346462306a36Sopenharmony_ci (txfailed ? "tx,rx" : "rx"), 346562306a36Sopenharmony_ci readl(cp->regs + REG_MIF_STATE_MACHINE), 346662306a36Sopenharmony_ci readl(cp->regs + REG_MAC_STATE_MACHINE)); 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_cienable_rx_done: 346962306a36Sopenharmony_ci cas_unmask_intr(cp); /* enable interrupts */ 347062306a36Sopenharmony_ci writel(RX_DESC_RINGN_SIZE(0) - 4, cp->regs + REG_RX_KICK); 347162306a36Sopenharmony_ci writel(0, cp->regs + REG_RX_COMP_TAIL); 347262306a36Sopenharmony_ci 347362306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_REG_PLUS) { 347462306a36Sopenharmony_ci if (N_RX_DESC_RINGS > 1) 347562306a36Sopenharmony_ci writel(RX_DESC_RINGN_SIZE(1) - 4, 347662306a36Sopenharmony_ci cp->regs + REG_PLUS_RX_KICK1); 347762306a36Sopenharmony_ci } 347862306a36Sopenharmony_ci} 347962306a36Sopenharmony_ci 348062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 348162306a36Sopenharmony_cistatic void cas_read_pcs_link_mode(struct cas *cp, int *fd, int *spd, 348262306a36Sopenharmony_ci int *pause) 348362306a36Sopenharmony_ci{ 348462306a36Sopenharmony_ci u32 val = readl(cp->regs + REG_PCS_MII_LPA); 348562306a36Sopenharmony_ci *fd = (val & PCS_MII_LPA_FD) ? 1 : 0; 348662306a36Sopenharmony_ci *pause = (val & PCS_MII_LPA_SYM_PAUSE) ? 0x01 : 0x00; 348762306a36Sopenharmony_ci if (val & PCS_MII_LPA_ASYM_PAUSE) 348862306a36Sopenharmony_ci *pause |= 0x10; 348962306a36Sopenharmony_ci *spd = 1000; 349062306a36Sopenharmony_ci} 349162306a36Sopenharmony_ci 349262306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 349362306a36Sopenharmony_cistatic void cas_read_mii_link_mode(struct cas *cp, int *fd, int *spd, 349462306a36Sopenharmony_ci int *pause) 349562306a36Sopenharmony_ci{ 349662306a36Sopenharmony_ci u32 val; 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci *fd = 0; 349962306a36Sopenharmony_ci *spd = 10; 350062306a36Sopenharmony_ci *pause = 0; 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_ci /* use GMII registers */ 350362306a36Sopenharmony_ci val = cas_phy_read(cp, MII_LPA); 350462306a36Sopenharmony_ci if (val & CAS_LPA_PAUSE) 350562306a36Sopenharmony_ci *pause = 0x01; 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci if (val & CAS_LPA_ASYM_PAUSE) 350862306a36Sopenharmony_ci *pause |= 0x10; 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_ci if (val & LPA_DUPLEX) 351162306a36Sopenharmony_ci *fd = 1; 351262306a36Sopenharmony_ci if (val & LPA_100) 351362306a36Sopenharmony_ci *spd = 100; 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { 351662306a36Sopenharmony_ci val = cas_phy_read(cp, CAS_MII_1000_STATUS); 351762306a36Sopenharmony_ci if (val & (CAS_LPA_1000FULL | CAS_LPA_1000HALF)) 351862306a36Sopenharmony_ci *spd = 1000; 351962306a36Sopenharmony_ci if (val & CAS_LPA_1000FULL) 352062306a36Sopenharmony_ci *fd = 1; 352162306a36Sopenharmony_ci } 352262306a36Sopenharmony_ci} 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci/* A link-up condition has occurred, initialize and enable the 352562306a36Sopenharmony_ci * rest of the chip. 352662306a36Sopenharmony_ci * 352762306a36Sopenharmony_ci * Must be invoked under cp->lock. 352862306a36Sopenharmony_ci */ 352962306a36Sopenharmony_cistatic void cas_set_link_modes(struct cas *cp) 353062306a36Sopenharmony_ci{ 353162306a36Sopenharmony_ci u32 val; 353262306a36Sopenharmony_ci int full_duplex, speed, pause; 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci full_duplex = 0; 353562306a36Sopenharmony_ci speed = 10; 353662306a36Sopenharmony_ci pause = 0; 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_ci if (CAS_PHY_MII(cp->phy_type)) { 353962306a36Sopenharmony_ci cas_mif_poll(cp, 0); 354062306a36Sopenharmony_ci val = cas_phy_read(cp, MII_BMCR); 354162306a36Sopenharmony_ci if (val & BMCR_ANENABLE) { 354262306a36Sopenharmony_ci cas_read_mii_link_mode(cp, &full_duplex, &speed, 354362306a36Sopenharmony_ci &pause); 354462306a36Sopenharmony_ci } else { 354562306a36Sopenharmony_ci if (val & BMCR_FULLDPLX) 354662306a36Sopenharmony_ci full_duplex = 1; 354762306a36Sopenharmony_ci 354862306a36Sopenharmony_ci if (val & BMCR_SPEED100) 354962306a36Sopenharmony_ci speed = 100; 355062306a36Sopenharmony_ci else if (val & CAS_BMCR_SPEED1000) 355162306a36Sopenharmony_ci speed = (cp->cas_flags & CAS_FLAG_1000MB_CAP) ? 355262306a36Sopenharmony_ci 1000 : 100; 355362306a36Sopenharmony_ci } 355462306a36Sopenharmony_ci cas_mif_poll(cp, 1); 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci } else { 355762306a36Sopenharmony_ci val = readl(cp->regs + REG_PCS_MII_CTRL); 355862306a36Sopenharmony_ci cas_read_pcs_link_mode(cp, &full_duplex, &speed, &pause); 355962306a36Sopenharmony_ci if ((val & PCS_MII_AUTONEG_EN) == 0) { 356062306a36Sopenharmony_ci if (val & PCS_MII_CTRL_DUPLEX) 356162306a36Sopenharmony_ci full_duplex = 1; 356262306a36Sopenharmony_ci } 356362306a36Sopenharmony_ci } 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_ci netif_info(cp, link, cp->dev, "Link up at %d Mbps, %s-duplex\n", 356662306a36Sopenharmony_ci speed, full_duplex ? "full" : "half"); 356762306a36Sopenharmony_ci 356862306a36Sopenharmony_ci val = MAC_XIF_TX_MII_OUTPUT_EN | MAC_XIF_LINK_LED; 356962306a36Sopenharmony_ci if (CAS_PHY_MII(cp->phy_type)) { 357062306a36Sopenharmony_ci val |= MAC_XIF_MII_BUFFER_OUTPUT_EN; 357162306a36Sopenharmony_ci if (!full_duplex) 357262306a36Sopenharmony_ci val |= MAC_XIF_DISABLE_ECHO; 357362306a36Sopenharmony_ci } 357462306a36Sopenharmony_ci if (full_duplex) 357562306a36Sopenharmony_ci val |= MAC_XIF_FDPLX_LED; 357662306a36Sopenharmony_ci if (speed == 1000) 357762306a36Sopenharmony_ci val |= MAC_XIF_GMII_MODE; 357862306a36Sopenharmony_ci writel(val, cp->regs + REG_MAC_XIF_CFG); 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_ci /* deal with carrier and collision detect. */ 358162306a36Sopenharmony_ci val = MAC_TX_CFG_IPG_EN; 358262306a36Sopenharmony_ci if (full_duplex) { 358362306a36Sopenharmony_ci val |= MAC_TX_CFG_IGNORE_CARRIER; 358462306a36Sopenharmony_ci val |= MAC_TX_CFG_IGNORE_COLL; 358562306a36Sopenharmony_ci } else { 358662306a36Sopenharmony_ci#ifndef USE_CSMA_CD_PROTO 358762306a36Sopenharmony_ci val |= MAC_TX_CFG_NEVER_GIVE_UP_EN; 358862306a36Sopenharmony_ci val |= MAC_TX_CFG_NEVER_GIVE_UP_LIM; 358962306a36Sopenharmony_ci#endif 359062306a36Sopenharmony_ci } 359162306a36Sopenharmony_ci /* val now set up for REG_MAC_TX_CFG */ 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_ci /* If gigabit and half-duplex, enable carrier extension 359462306a36Sopenharmony_ci * mode. increase slot time to 512 bytes as well. 359562306a36Sopenharmony_ci * else, disable it and make sure slot time is 64 bytes. 359662306a36Sopenharmony_ci * also activate checksum bug workaround 359762306a36Sopenharmony_ci */ 359862306a36Sopenharmony_ci if ((speed == 1000) && !full_duplex) { 359962306a36Sopenharmony_ci writel(val | MAC_TX_CFG_CARRIER_EXTEND, 360062306a36Sopenharmony_ci cp->regs + REG_MAC_TX_CFG); 360162306a36Sopenharmony_ci 360262306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_RX_CFG); 360362306a36Sopenharmony_ci val &= ~MAC_RX_CFG_STRIP_FCS; /* checksum workaround */ 360462306a36Sopenharmony_ci writel(val | MAC_RX_CFG_CARRIER_EXTEND, 360562306a36Sopenharmony_ci cp->regs + REG_MAC_RX_CFG); 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_ci writel(0x200, cp->regs + REG_MAC_SLOT_TIME); 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_ci cp->crc_size = 4; 361062306a36Sopenharmony_ci /* minimum size gigabit frame at half duplex */ 361162306a36Sopenharmony_ci cp->min_frame_size = CAS_1000MB_MIN_FRAME; 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci } else { 361462306a36Sopenharmony_ci writel(val, cp->regs + REG_MAC_TX_CFG); 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_ci /* checksum bug workaround. don't strip FCS when in 361762306a36Sopenharmony_ci * half-duplex mode 361862306a36Sopenharmony_ci */ 361962306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_RX_CFG); 362062306a36Sopenharmony_ci if (full_duplex) { 362162306a36Sopenharmony_ci val |= MAC_RX_CFG_STRIP_FCS; 362262306a36Sopenharmony_ci cp->crc_size = 0; 362362306a36Sopenharmony_ci cp->min_frame_size = CAS_MIN_MTU; 362462306a36Sopenharmony_ci } else { 362562306a36Sopenharmony_ci val &= ~MAC_RX_CFG_STRIP_FCS; 362662306a36Sopenharmony_ci cp->crc_size = 4; 362762306a36Sopenharmony_ci cp->min_frame_size = CAS_MIN_FRAME; 362862306a36Sopenharmony_ci } 362962306a36Sopenharmony_ci writel(val & ~MAC_RX_CFG_CARRIER_EXTEND, 363062306a36Sopenharmony_ci cp->regs + REG_MAC_RX_CFG); 363162306a36Sopenharmony_ci writel(0x40, cp->regs + REG_MAC_SLOT_TIME); 363262306a36Sopenharmony_ci } 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci if (netif_msg_link(cp)) { 363562306a36Sopenharmony_ci if (pause & 0x01) { 363662306a36Sopenharmony_ci netdev_info(cp->dev, "Pause is enabled (rxfifo: %d off: %d on: %d)\n", 363762306a36Sopenharmony_ci cp->rx_fifo_size, 363862306a36Sopenharmony_ci cp->rx_pause_off, 363962306a36Sopenharmony_ci cp->rx_pause_on); 364062306a36Sopenharmony_ci } else if (pause & 0x10) { 364162306a36Sopenharmony_ci netdev_info(cp->dev, "TX pause enabled\n"); 364262306a36Sopenharmony_ci } else { 364362306a36Sopenharmony_ci netdev_info(cp->dev, "Pause is disabled\n"); 364462306a36Sopenharmony_ci } 364562306a36Sopenharmony_ci } 364662306a36Sopenharmony_ci 364762306a36Sopenharmony_ci val = readl(cp->regs + REG_MAC_CTRL_CFG); 364862306a36Sopenharmony_ci val &= ~(MAC_CTRL_CFG_SEND_PAUSE_EN | MAC_CTRL_CFG_RECV_PAUSE_EN); 364962306a36Sopenharmony_ci if (pause) { /* symmetric or asymmetric pause */ 365062306a36Sopenharmony_ci val |= MAC_CTRL_CFG_SEND_PAUSE_EN; 365162306a36Sopenharmony_ci if (pause & 0x01) { /* symmetric pause */ 365262306a36Sopenharmony_ci val |= MAC_CTRL_CFG_RECV_PAUSE_EN; 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci } 365562306a36Sopenharmony_ci writel(val, cp->regs + REG_MAC_CTRL_CFG); 365662306a36Sopenharmony_ci cas_start_dma(cp); 365762306a36Sopenharmony_ci} 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 366062306a36Sopenharmony_cistatic void cas_init_hw(struct cas *cp, int restart_link) 366162306a36Sopenharmony_ci{ 366262306a36Sopenharmony_ci if (restart_link) 366362306a36Sopenharmony_ci cas_phy_init(cp); 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci cas_init_pause_thresholds(cp); 366662306a36Sopenharmony_ci cas_init_mac(cp); 366762306a36Sopenharmony_ci cas_init_dma(cp); 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci if (restart_link) { 367062306a36Sopenharmony_ci /* Default aneg parameters */ 367162306a36Sopenharmony_ci cp->timer_ticks = 0; 367262306a36Sopenharmony_ci cas_begin_auto_negotiation(cp, NULL); 367362306a36Sopenharmony_ci } else if (cp->lstate == link_up) { 367462306a36Sopenharmony_ci cas_set_link_modes(cp); 367562306a36Sopenharmony_ci netif_carrier_on(cp->dev); 367662306a36Sopenharmony_ci } 367762306a36Sopenharmony_ci} 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_ci/* Must be invoked under cp->lock. on earlier cassini boards, 368062306a36Sopenharmony_ci * SOFT_0 is tied to PCI reset. we use this to force a pci reset, 368162306a36Sopenharmony_ci * let it settle out, and then restore pci state. 368262306a36Sopenharmony_ci */ 368362306a36Sopenharmony_cistatic void cas_hard_reset(struct cas *cp) 368462306a36Sopenharmony_ci{ 368562306a36Sopenharmony_ci writel(BIM_LOCAL_DEV_SOFT_0, cp->regs + REG_BIM_LOCAL_DEV_EN); 368662306a36Sopenharmony_ci udelay(20); 368762306a36Sopenharmony_ci pci_restore_state(cp->pdev); 368862306a36Sopenharmony_ci} 368962306a36Sopenharmony_ci 369062306a36Sopenharmony_ci 369162306a36Sopenharmony_cistatic void cas_global_reset(struct cas *cp, int blkflag) 369262306a36Sopenharmony_ci{ 369362306a36Sopenharmony_ci int limit; 369462306a36Sopenharmony_ci 369562306a36Sopenharmony_ci /* issue a global reset. don't use RSTOUT. */ 369662306a36Sopenharmony_ci if (blkflag && !CAS_PHY_MII(cp->phy_type)) { 369762306a36Sopenharmony_ci /* For PCS, when the blkflag is set, we should set the 369862306a36Sopenharmony_ci * SW_REST_BLOCK_PCS_SLINK bit to prevent the results of 369962306a36Sopenharmony_ci * the last autonegotiation from being cleared. We'll 370062306a36Sopenharmony_ci * need some special handling if the chip is set into a 370162306a36Sopenharmony_ci * loopback mode. 370262306a36Sopenharmony_ci */ 370362306a36Sopenharmony_ci writel((SW_RESET_TX | SW_RESET_RX | SW_RESET_BLOCK_PCS_SLINK), 370462306a36Sopenharmony_ci cp->regs + REG_SW_RESET); 370562306a36Sopenharmony_ci } else { 370662306a36Sopenharmony_ci writel(SW_RESET_TX | SW_RESET_RX, cp->regs + REG_SW_RESET); 370762306a36Sopenharmony_ci } 370862306a36Sopenharmony_ci 370962306a36Sopenharmony_ci /* need to wait at least 3ms before polling register */ 371062306a36Sopenharmony_ci mdelay(3); 371162306a36Sopenharmony_ci 371262306a36Sopenharmony_ci limit = STOP_TRIES; 371362306a36Sopenharmony_ci while (limit-- > 0) { 371462306a36Sopenharmony_ci u32 val = readl(cp->regs + REG_SW_RESET); 371562306a36Sopenharmony_ci if ((val & (SW_RESET_TX | SW_RESET_RX)) == 0) 371662306a36Sopenharmony_ci goto done; 371762306a36Sopenharmony_ci udelay(10); 371862306a36Sopenharmony_ci } 371962306a36Sopenharmony_ci netdev_err(cp->dev, "sw reset failed\n"); 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_cidone: 372262306a36Sopenharmony_ci /* enable various BIM interrupts */ 372362306a36Sopenharmony_ci writel(BIM_CFG_DPAR_INTR_ENABLE | BIM_CFG_RMA_INTR_ENABLE | 372462306a36Sopenharmony_ci BIM_CFG_RTA_INTR_ENABLE, cp->regs + REG_BIM_CFG); 372562306a36Sopenharmony_ci 372662306a36Sopenharmony_ci /* clear out pci error status mask for handled errors. 372762306a36Sopenharmony_ci * we don't deal with DMA counter overflows as they happen 372862306a36Sopenharmony_ci * all the time. 372962306a36Sopenharmony_ci */ 373062306a36Sopenharmony_ci writel(0xFFFFFFFFU & ~(PCI_ERR_BADACK | PCI_ERR_DTRTO | 373162306a36Sopenharmony_ci PCI_ERR_OTHER | PCI_ERR_BIM_DMA_WRITE | 373262306a36Sopenharmony_ci PCI_ERR_BIM_DMA_READ), cp->regs + 373362306a36Sopenharmony_ci REG_PCI_ERR_STATUS_MASK); 373462306a36Sopenharmony_ci 373562306a36Sopenharmony_ci /* set up for MII by default to address mac rx reset timeout 373662306a36Sopenharmony_ci * issue 373762306a36Sopenharmony_ci */ 373862306a36Sopenharmony_ci writel(PCS_DATAPATH_MODE_MII, cp->regs + REG_PCS_DATAPATH_MODE); 373962306a36Sopenharmony_ci} 374062306a36Sopenharmony_ci 374162306a36Sopenharmony_cistatic void cas_reset(struct cas *cp, int blkflag) 374262306a36Sopenharmony_ci{ 374362306a36Sopenharmony_ci u32 val; 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci cas_mask_intr(cp); 374662306a36Sopenharmony_ci cas_global_reset(cp, blkflag); 374762306a36Sopenharmony_ci cas_mac_reset(cp); 374862306a36Sopenharmony_ci cas_entropy_reset(cp); 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci /* disable dma engines. */ 375162306a36Sopenharmony_ci val = readl(cp->regs + REG_TX_CFG); 375262306a36Sopenharmony_ci val &= ~TX_CFG_DMA_EN; 375362306a36Sopenharmony_ci writel(val, cp->regs + REG_TX_CFG); 375462306a36Sopenharmony_ci 375562306a36Sopenharmony_ci val = readl(cp->regs + REG_RX_CFG); 375662306a36Sopenharmony_ci val &= ~RX_CFG_DMA_EN; 375762306a36Sopenharmony_ci writel(val, cp->regs + REG_RX_CFG); 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci /* program header parser */ 376062306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_TARGET_ABORT) || 376162306a36Sopenharmony_ci (&CAS_HP_ALT_FIRMWARE[0] == &cas_prog_null[0])) { 376262306a36Sopenharmony_ci cas_load_firmware(cp, CAS_HP_FIRMWARE); 376362306a36Sopenharmony_ci } else { 376462306a36Sopenharmony_ci cas_load_firmware(cp, CAS_HP_ALT_FIRMWARE); 376562306a36Sopenharmony_ci } 376662306a36Sopenharmony_ci 376762306a36Sopenharmony_ci /* clear out error registers */ 376862306a36Sopenharmony_ci spin_lock(&cp->stat_lock[N_TX_RINGS]); 376962306a36Sopenharmony_ci cas_clear_mac_err(cp); 377062306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[N_TX_RINGS]); 377162306a36Sopenharmony_ci} 377262306a36Sopenharmony_ci 377362306a36Sopenharmony_ci/* Shut down the chip, must be called with pm_mutex held. */ 377462306a36Sopenharmony_cistatic void cas_shutdown(struct cas *cp) 377562306a36Sopenharmony_ci{ 377662306a36Sopenharmony_ci unsigned long flags; 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci /* Make us not-running to avoid timers respawning */ 377962306a36Sopenharmony_ci cp->hw_running = 0; 378062306a36Sopenharmony_ci 378162306a36Sopenharmony_ci del_timer_sync(&cp->link_timer); 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci /* Stop the reset task */ 378462306a36Sopenharmony_ci#if 0 378562306a36Sopenharmony_ci while (atomic_read(&cp->reset_task_pending_mtu) || 378662306a36Sopenharmony_ci atomic_read(&cp->reset_task_pending_spare) || 378762306a36Sopenharmony_ci atomic_read(&cp->reset_task_pending_all)) 378862306a36Sopenharmony_ci schedule(); 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci#else 379162306a36Sopenharmony_ci while (atomic_read(&cp->reset_task_pending)) 379262306a36Sopenharmony_ci schedule(); 379362306a36Sopenharmony_ci#endif 379462306a36Sopenharmony_ci /* Actually stop the chip */ 379562306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 379662306a36Sopenharmony_ci cas_reset(cp, 0); 379762306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_SATURN) 379862306a36Sopenharmony_ci cas_phy_powerdown(cp); 379962306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 380062306a36Sopenharmony_ci} 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_cistatic int cas_change_mtu(struct net_device *dev, int new_mtu) 380362306a36Sopenharmony_ci{ 380462306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci dev->mtu = new_mtu; 380762306a36Sopenharmony_ci if (!netif_running(dev) || !netif_device_present(dev)) 380862306a36Sopenharmony_ci return 0; 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_ci /* let the reset task handle it */ 381162306a36Sopenharmony_ci#if 1 381262306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 381362306a36Sopenharmony_ci if ((cp->phy_type & CAS_PHY_SERDES)) { 381462306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_all); 381562306a36Sopenharmony_ci } else { 381662306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_mtu); 381762306a36Sopenharmony_ci } 381862306a36Sopenharmony_ci schedule_work(&cp->reset_task); 381962306a36Sopenharmony_ci#else 382062306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, (cp->phy_type & CAS_PHY_SERDES) ? 382162306a36Sopenharmony_ci CAS_RESET_ALL : CAS_RESET_MTU); 382262306a36Sopenharmony_ci pr_err("reset called in cas_change_mtu\n"); 382362306a36Sopenharmony_ci schedule_work(&cp->reset_task); 382462306a36Sopenharmony_ci#endif 382562306a36Sopenharmony_ci 382662306a36Sopenharmony_ci flush_work(&cp->reset_task); 382762306a36Sopenharmony_ci return 0; 382862306a36Sopenharmony_ci} 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_cistatic void cas_clean_txd(struct cas *cp, int ring) 383162306a36Sopenharmony_ci{ 383262306a36Sopenharmony_ci struct cas_tx_desc *txd = cp->init_txds[ring]; 383362306a36Sopenharmony_ci struct sk_buff *skb, **skbs = cp->tx_skbs[ring]; 383462306a36Sopenharmony_ci u64 daddr, dlen; 383562306a36Sopenharmony_ci int i, size; 383662306a36Sopenharmony_ci 383762306a36Sopenharmony_ci size = TX_DESC_RINGN_SIZE(ring); 383862306a36Sopenharmony_ci for (i = 0; i < size; i++) { 383962306a36Sopenharmony_ci int frag; 384062306a36Sopenharmony_ci 384162306a36Sopenharmony_ci if (skbs[i] == NULL) 384262306a36Sopenharmony_ci continue; 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci skb = skbs[i]; 384562306a36Sopenharmony_ci skbs[i] = NULL; 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci for (frag = 0; frag <= skb_shinfo(skb)->nr_frags; frag++) { 384862306a36Sopenharmony_ci int ent = i & (size - 1); 384962306a36Sopenharmony_ci 385062306a36Sopenharmony_ci /* first buffer is never a tiny buffer and so 385162306a36Sopenharmony_ci * needs to be unmapped. 385262306a36Sopenharmony_ci */ 385362306a36Sopenharmony_ci daddr = le64_to_cpu(txd[ent].buffer); 385462306a36Sopenharmony_ci dlen = CAS_VAL(TX_DESC_BUFLEN, 385562306a36Sopenharmony_ci le64_to_cpu(txd[ent].control)); 385662306a36Sopenharmony_ci dma_unmap_page(&cp->pdev->dev, daddr, dlen, 385762306a36Sopenharmony_ci DMA_TO_DEVICE); 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci if (frag != skb_shinfo(skb)->nr_frags) { 386062306a36Sopenharmony_ci i++; 386162306a36Sopenharmony_ci 386262306a36Sopenharmony_ci /* next buffer might by a tiny buffer. 386362306a36Sopenharmony_ci * skip past it. 386462306a36Sopenharmony_ci */ 386562306a36Sopenharmony_ci ent = i & (size - 1); 386662306a36Sopenharmony_ci if (cp->tx_tiny_use[ring][ent].used) 386762306a36Sopenharmony_ci i++; 386862306a36Sopenharmony_ci } 386962306a36Sopenharmony_ci } 387062306a36Sopenharmony_ci dev_kfree_skb_any(skb); 387162306a36Sopenharmony_ci } 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci /* zero out tiny buf usage */ 387462306a36Sopenharmony_ci memset(cp->tx_tiny_use[ring], 0, size*sizeof(*cp->tx_tiny_use[ring])); 387562306a36Sopenharmony_ci} 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_ci/* freed on close */ 387862306a36Sopenharmony_cistatic inline void cas_free_rx_desc(struct cas *cp, int ring) 387962306a36Sopenharmony_ci{ 388062306a36Sopenharmony_ci cas_page_t **page = cp->rx_pages[ring]; 388162306a36Sopenharmony_ci int i, size; 388262306a36Sopenharmony_ci 388362306a36Sopenharmony_ci size = RX_DESC_RINGN_SIZE(ring); 388462306a36Sopenharmony_ci for (i = 0; i < size; i++) { 388562306a36Sopenharmony_ci if (page[i]) { 388662306a36Sopenharmony_ci cas_page_free(cp, page[i]); 388762306a36Sopenharmony_ci page[i] = NULL; 388862306a36Sopenharmony_ci } 388962306a36Sopenharmony_ci } 389062306a36Sopenharmony_ci} 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_cistatic void cas_free_rxds(struct cas *cp) 389362306a36Sopenharmony_ci{ 389462306a36Sopenharmony_ci int i; 389562306a36Sopenharmony_ci 389662306a36Sopenharmony_ci for (i = 0; i < N_RX_DESC_RINGS; i++) 389762306a36Sopenharmony_ci cas_free_rx_desc(cp, i); 389862306a36Sopenharmony_ci} 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci/* Must be invoked under cp->lock. */ 390162306a36Sopenharmony_cistatic void cas_clean_rings(struct cas *cp) 390262306a36Sopenharmony_ci{ 390362306a36Sopenharmony_ci int i; 390462306a36Sopenharmony_ci 390562306a36Sopenharmony_ci /* need to clean all tx rings */ 390662306a36Sopenharmony_ci memset(cp->tx_old, 0, sizeof(*cp->tx_old)*N_TX_RINGS); 390762306a36Sopenharmony_ci memset(cp->tx_new, 0, sizeof(*cp->tx_new)*N_TX_RINGS); 390862306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) 390962306a36Sopenharmony_ci cas_clean_txd(cp, i); 391062306a36Sopenharmony_ci 391162306a36Sopenharmony_ci /* zero out init block */ 391262306a36Sopenharmony_ci memset(cp->init_block, 0, sizeof(struct cas_init_block)); 391362306a36Sopenharmony_ci cas_clean_rxds(cp); 391462306a36Sopenharmony_ci cas_clean_rxcs(cp); 391562306a36Sopenharmony_ci} 391662306a36Sopenharmony_ci 391762306a36Sopenharmony_ci/* allocated on open */ 391862306a36Sopenharmony_cistatic inline int cas_alloc_rx_desc(struct cas *cp, int ring) 391962306a36Sopenharmony_ci{ 392062306a36Sopenharmony_ci cas_page_t **page = cp->rx_pages[ring]; 392162306a36Sopenharmony_ci int size, i = 0; 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci size = RX_DESC_RINGN_SIZE(ring); 392462306a36Sopenharmony_ci for (i = 0; i < size; i++) { 392562306a36Sopenharmony_ci if ((page[i] = cas_page_alloc(cp, GFP_KERNEL)) == NULL) 392662306a36Sopenharmony_ci return -1; 392762306a36Sopenharmony_ci } 392862306a36Sopenharmony_ci return 0; 392962306a36Sopenharmony_ci} 393062306a36Sopenharmony_ci 393162306a36Sopenharmony_cistatic int cas_alloc_rxds(struct cas *cp) 393262306a36Sopenharmony_ci{ 393362306a36Sopenharmony_ci int i; 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci for (i = 0; i < N_RX_DESC_RINGS; i++) { 393662306a36Sopenharmony_ci if (cas_alloc_rx_desc(cp, i) < 0) { 393762306a36Sopenharmony_ci cas_free_rxds(cp); 393862306a36Sopenharmony_ci return -1; 393962306a36Sopenharmony_ci } 394062306a36Sopenharmony_ci } 394162306a36Sopenharmony_ci return 0; 394262306a36Sopenharmony_ci} 394362306a36Sopenharmony_ci 394462306a36Sopenharmony_cistatic void cas_reset_task(struct work_struct *work) 394562306a36Sopenharmony_ci{ 394662306a36Sopenharmony_ci struct cas *cp = container_of(work, struct cas, reset_task); 394762306a36Sopenharmony_ci#if 0 394862306a36Sopenharmony_ci int pending = atomic_read(&cp->reset_task_pending); 394962306a36Sopenharmony_ci#else 395062306a36Sopenharmony_ci int pending_all = atomic_read(&cp->reset_task_pending_all); 395162306a36Sopenharmony_ci int pending_spare = atomic_read(&cp->reset_task_pending_spare); 395262306a36Sopenharmony_ci int pending_mtu = atomic_read(&cp->reset_task_pending_mtu); 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci if (pending_all == 0 && pending_spare == 0 && pending_mtu == 0) { 395562306a36Sopenharmony_ci /* We can have more tasks scheduled than actually 395662306a36Sopenharmony_ci * needed. 395762306a36Sopenharmony_ci */ 395862306a36Sopenharmony_ci atomic_dec(&cp->reset_task_pending); 395962306a36Sopenharmony_ci return; 396062306a36Sopenharmony_ci } 396162306a36Sopenharmony_ci#endif 396262306a36Sopenharmony_ci /* The link went down, we reset the ring, but keep 396362306a36Sopenharmony_ci * DMA stopped. Use this function for reset 396462306a36Sopenharmony_ci * on error as well. 396562306a36Sopenharmony_ci */ 396662306a36Sopenharmony_ci if (cp->hw_running) { 396762306a36Sopenharmony_ci unsigned long flags; 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci /* Make sure we don't get interrupts or tx packets */ 397062306a36Sopenharmony_ci netif_device_detach(cp->dev); 397162306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci if (cp->opened) { 397462306a36Sopenharmony_ci /* We call cas_spare_recover when we call cas_open. 397562306a36Sopenharmony_ci * but we do not initialize the lists cas_spare_recover 397662306a36Sopenharmony_ci * uses until cas_open is called. 397762306a36Sopenharmony_ci */ 397862306a36Sopenharmony_ci cas_spare_recover(cp, GFP_ATOMIC); 397962306a36Sopenharmony_ci } 398062306a36Sopenharmony_ci#if 1 398162306a36Sopenharmony_ci /* test => only pending_spare set */ 398262306a36Sopenharmony_ci if (!pending_all && !pending_mtu) 398362306a36Sopenharmony_ci goto done; 398462306a36Sopenharmony_ci#else 398562306a36Sopenharmony_ci if (pending == CAS_RESET_SPARE) 398662306a36Sopenharmony_ci goto done; 398762306a36Sopenharmony_ci#endif 398862306a36Sopenharmony_ci /* when pending == CAS_RESET_ALL, the following 398962306a36Sopenharmony_ci * call to cas_init_hw will restart auto negotiation. 399062306a36Sopenharmony_ci * Setting the second argument of cas_reset to 399162306a36Sopenharmony_ci * !(pending == CAS_RESET_ALL) will set this argument 399262306a36Sopenharmony_ci * to 1 (avoiding reinitializing the PHY for the normal 399362306a36Sopenharmony_ci * PCS case) when auto negotiation is not restarted. 399462306a36Sopenharmony_ci */ 399562306a36Sopenharmony_ci#if 1 399662306a36Sopenharmony_ci cas_reset(cp, !(pending_all > 0)); 399762306a36Sopenharmony_ci if (cp->opened) 399862306a36Sopenharmony_ci cas_clean_rings(cp); 399962306a36Sopenharmony_ci cas_init_hw(cp, (pending_all > 0)); 400062306a36Sopenharmony_ci#else 400162306a36Sopenharmony_ci cas_reset(cp, !(pending == CAS_RESET_ALL)); 400262306a36Sopenharmony_ci if (cp->opened) 400362306a36Sopenharmony_ci cas_clean_rings(cp); 400462306a36Sopenharmony_ci cas_init_hw(cp, pending == CAS_RESET_ALL); 400562306a36Sopenharmony_ci#endif 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_cidone: 400862306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 400962306a36Sopenharmony_ci netif_device_attach(cp->dev); 401062306a36Sopenharmony_ci } 401162306a36Sopenharmony_ci#if 1 401262306a36Sopenharmony_ci atomic_sub(pending_all, &cp->reset_task_pending_all); 401362306a36Sopenharmony_ci atomic_sub(pending_spare, &cp->reset_task_pending_spare); 401462306a36Sopenharmony_ci atomic_sub(pending_mtu, &cp->reset_task_pending_mtu); 401562306a36Sopenharmony_ci atomic_dec(&cp->reset_task_pending); 401662306a36Sopenharmony_ci#else 401762306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, 0); 401862306a36Sopenharmony_ci#endif 401962306a36Sopenharmony_ci} 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_cistatic void cas_link_timer(struct timer_list *t) 402262306a36Sopenharmony_ci{ 402362306a36Sopenharmony_ci struct cas *cp = from_timer(cp, t, link_timer); 402462306a36Sopenharmony_ci int mask, pending = 0, reset = 0; 402562306a36Sopenharmony_ci unsigned long flags; 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_ci if (link_transition_timeout != 0 && 402862306a36Sopenharmony_ci cp->link_transition_jiffies_valid && 402962306a36Sopenharmony_ci time_is_before_jiffies(cp->link_transition_jiffies + 403062306a36Sopenharmony_ci link_transition_timeout)) { 403162306a36Sopenharmony_ci /* One-second counter so link-down workaround doesn't 403262306a36Sopenharmony_ci * cause resets to occur so fast as to fool the switch 403362306a36Sopenharmony_ci * into thinking the link is down. 403462306a36Sopenharmony_ci */ 403562306a36Sopenharmony_ci cp->link_transition_jiffies_valid = 0; 403662306a36Sopenharmony_ci } 403762306a36Sopenharmony_ci 403862306a36Sopenharmony_ci if (!cp->hw_running) 403962306a36Sopenharmony_ci return; 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 404262306a36Sopenharmony_ci cas_lock_tx(cp); 404362306a36Sopenharmony_ci cas_entropy_gather(cp); 404462306a36Sopenharmony_ci 404562306a36Sopenharmony_ci /* If the link task is still pending, we just 404662306a36Sopenharmony_ci * reschedule the link timer 404762306a36Sopenharmony_ci */ 404862306a36Sopenharmony_ci#if 1 404962306a36Sopenharmony_ci if (atomic_read(&cp->reset_task_pending_all) || 405062306a36Sopenharmony_ci atomic_read(&cp->reset_task_pending_spare) || 405162306a36Sopenharmony_ci atomic_read(&cp->reset_task_pending_mtu)) 405262306a36Sopenharmony_ci goto done; 405362306a36Sopenharmony_ci#else 405462306a36Sopenharmony_ci if (atomic_read(&cp->reset_task_pending)) 405562306a36Sopenharmony_ci goto done; 405662306a36Sopenharmony_ci#endif 405762306a36Sopenharmony_ci 405862306a36Sopenharmony_ci /* check for rx cleaning */ 405962306a36Sopenharmony_ci if ((mask = (cp->cas_flags & CAS_FLAG_RXD_POST_MASK))) { 406062306a36Sopenharmony_ci int i, rmask; 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci for (i = 0; i < MAX_RX_DESC_RINGS; i++) { 406362306a36Sopenharmony_ci rmask = CAS_FLAG_RXD_POST(i); 406462306a36Sopenharmony_ci if ((mask & rmask) == 0) 406562306a36Sopenharmony_ci continue; 406662306a36Sopenharmony_ci 406762306a36Sopenharmony_ci /* post_rxds will do a mod_timer */ 406862306a36Sopenharmony_ci if (cas_post_rxds_ringN(cp, i, cp->rx_last[i]) < 0) { 406962306a36Sopenharmony_ci pending = 1; 407062306a36Sopenharmony_ci continue; 407162306a36Sopenharmony_ci } 407262306a36Sopenharmony_ci cp->cas_flags &= ~rmask; 407362306a36Sopenharmony_ci } 407462306a36Sopenharmony_ci } 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci if (CAS_PHY_MII(cp->phy_type)) { 407762306a36Sopenharmony_ci u16 bmsr; 407862306a36Sopenharmony_ci cas_mif_poll(cp, 0); 407962306a36Sopenharmony_ci bmsr = cas_phy_read(cp, MII_BMSR); 408062306a36Sopenharmony_ci /* WTZ: Solaris driver reads this twice, but that 408162306a36Sopenharmony_ci * may be due to the PCS case and the use of a 408262306a36Sopenharmony_ci * common implementation. Read it twice here to be 408362306a36Sopenharmony_ci * safe. 408462306a36Sopenharmony_ci */ 408562306a36Sopenharmony_ci bmsr = cas_phy_read(cp, MII_BMSR); 408662306a36Sopenharmony_ci cas_mif_poll(cp, 1); 408762306a36Sopenharmony_ci readl(cp->regs + REG_MIF_STATUS); /* avoid dups */ 408862306a36Sopenharmony_ci reset = cas_mii_link_check(cp, bmsr); 408962306a36Sopenharmony_ci } else { 409062306a36Sopenharmony_ci reset = cas_pcs_link_check(cp); 409162306a36Sopenharmony_ci } 409262306a36Sopenharmony_ci 409362306a36Sopenharmony_ci if (reset) 409462306a36Sopenharmony_ci goto done; 409562306a36Sopenharmony_ci 409662306a36Sopenharmony_ci /* check for tx state machine confusion */ 409762306a36Sopenharmony_ci if ((readl(cp->regs + REG_MAC_TX_STATUS) & MAC_TX_FRAME_XMIT) == 0) { 409862306a36Sopenharmony_ci u32 val = readl(cp->regs + REG_MAC_STATE_MACHINE); 409962306a36Sopenharmony_ci u32 wptr, rptr; 410062306a36Sopenharmony_ci int tlm = CAS_VAL(MAC_SM_TLM, val); 410162306a36Sopenharmony_ci 410262306a36Sopenharmony_ci if (((tlm == 0x5) || (tlm == 0x3)) && 410362306a36Sopenharmony_ci (CAS_VAL(MAC_SM_ENCAP_SM, val) == 0)) { 410462306a36Sopenharmony_ci netif_printk(cp, tx_err, KERN_DEBUG, cp->dev, 410562306a36Sopenharmony_ci "tx err: MAC_STATE[%08x]\n", val); 410662306a36Sopenharmony_ci reset = 1; 410762306a36Sopenharmony_ci goto done; 410862306a36Sopenharmony_ci } 410962306a36Sopenharmony_ci 411062306a36Sopenharmony_ci val = readl(cp->regs + REG_TX_FIFO_PKT_CNT); 411162306a36Sopenharmony_ci wptr = readl(cp->regs + REG_TX_FIFO_WRITE_PTR); 411262306a36Sopenharmony_ci rptr = readl(cp->regs + REG_TX_FIFO_READ_PTR); 411362306a36Sopenharmony_ci if ((val == 0) && (wptr != rptr)) { 411462306a36Sopenharmony_ci netif_printk(cp, tx_err, KERN_DEBUG, cp->dev, 411562306a36Sopenharmony_ci "tx err: TX_FIFO[%08x:%08x:%08x]\n", 411662306a36Sopenharmony_ci val, wptr, rptr); 411762306a36Sopenharmony_ci reset = 1; 411862306a36Sopenharmony_ci } 411962306a36Sopenharmony_ci 412062306a36Sopenharmony_ci if (reset) 412162306a36Sopenharmony_ci cas_hard_reset(cp); 412262306a36Sopenharmony_ci } 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_cidone: 412562306a36Sopenharmony_ci if (reset) { 412662306a36Sopenharmony_ci#if 1 412762306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending); 412862306a36Sopenharmony_ci atomic_inc(&cp->reset_task_pending_all); 412962306a36Sopenharmony_ci schedule_work(&cp->reset_task); 413062306a36Sopenharmony_ci#else 413162306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, CAS_RESET_ALL); 413262306a36Sopenharmony_ci pr_err("reset called in cas_link_timer\n"); 413362306a36Sopenharmony_ci schedule_work(&cp->reset_task); 413462306a36Sopenharmony_ci#endif 413562306a36Sopenharmony_ci } 413662306a36Sopenharmony_ci 413762306a36Sopenharmony_ci if (!pending) 413862306a36Sopenharmony_ci mod_timer(&cp->link_timer, jiffies + CAS_LINK_TIMEOUT); 413962306a36Sopenharmony_ci cas_unlock_tx(cp); 414062306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 414162306a36Sopenharmony_ci} 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci/* tiny buffers are used to avoid target abort issues with 414462306a36Sopenharmony_ci * older cassini's 414562306a36Sopenharmony_ci */ 414662306a36Sopenharmony_cistatic void cas_tx_tiny_free(struct cas *cp) 414762306a36Sopenharmony_ci{ 414862306a36Sopenharmony_ci struct pci_dev *pdev = cp->pdev; 414962306a36Sopenharmony_ci int i; 415062306a36Sopenharmony_ci 415162306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) { 415262306a36Sopenharmony_ci if (!cp->tx_tiny_bufs[i]) 415362306a36Sopenharmony_ci continue; 415462306a36Sopenharmony_ci 415562306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, TX_TINY_BUF_BLOCK, 415662306a36Sopenharmony_ci cp->tx_tiny_bufs[i], cp->tx_tiny_dvma[i]); 415762306a36Sopenharmony_ci cp->tx_tiny_bufs[i] = NULL; 415862306a36Sopenharmony_ci } 415962306a36Sopenharmony_ci} 416062306a36Sopenharmony_ci 416162306a36Sopenharmony_cistatic int cas_tx_tiny_alloc(struct cas *cp) 416262306a36Sopenharmony_ci{ 416362306a36Sopenharmony_ci struct pci_dev *pdev = cp->pdev; 416462306a36Sopenharmony_ci int i; 416562306a36Sopenharmony_ci 416662306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) { 416762306a36Sopenharmony_ci cp->tx_tiny_bufs[i] = 416862306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, TX_TINY_BUF_BLOCK, 416962306a36Sopenharmony_ci &cp->tx_tiny_dvma[i], GFP_KERNEL); 417062306a36Sopenharmony_ci if (!cp->tx_tiny_bufs[i]) { 417162306a36Sopenharmony_ci cas_tx_tiny_free(cp); 417262306a36Sopenharmony_ci return -1; 417362306a36Sopenharmony_ci } 417462306a36Sopenharmony_ci } 417562306a36Sopenharmony_ci return 0; 417662306a36Sopenharmony_ci} 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_ci 417962306a36Sopenharmony_cistatic int cas_open(struct net_device *dev) 418062306a36Sopenharmony_ci{ 418162306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 418262306a36Sopenharmony_ci int hw_was_up, err; 418362306a36Sopenharmony_ci unsigned long flags; 418462306a36Sopenharmony_ci 418562306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 418662306a36Sopenharmony_ci 418762306a36Sopenharmony_ci hw_was_up = cp->hw_running; 418862306a36Sopenharmony_ci 418962306a36Sopenharmony_ci /* The power-management mutex protects the hw_running 419062306a36Sopenharmony_ci * etc. state so it is safe to do this bit without cp->lock 419162306a36Sopenharmony_ci */ 419262306a36Sopenharmony_ci if (!cp->hw_running) { 419362306a36Sopenharmony_ci /* Reset the chip */ 419462306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 419562306a36Sopenharmony_ci /* We set the second arg to cas_reset to zero 419662306a36Sopenharmony_ci * because cas_init_hw below will have its second 419762306a36Sopenharmony_ci * argument set to non-zero, which will force 419862306a36Sopenharmony_ci * autonegotiation to start. 419962306a36Sopenharmony_ci */ 420062306a36Sopenharmony_ci cas_reset(cp, 0); 420162306a36Sopenharmony_ci cp->hw_running = 1; 420262306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 420362306a36Sopenharmony_ci } 420462306a36Sopenharmony_ci 420562306a36Sopenharmony_ci err = -ENOMEM; 420662306a36Sopenharmony_ci if (cas_tx_tiny_alloc(cp) < 0) 420762306a36Sopenharmony_ci goto err_unlock; 420862306a36Sopenharmony_ci 420962306a36Sopenharmony_ci /* alloc rx descriptors */ 421062306a36Sopenharmony_ci if (cas_alloc_rxds(cp) < 0) 421162306a36Sopenharmony_ci goto err_tx_tiny; 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci /* allocate spares */ 421462306a36Sopenharmony_ci cas_spare_init(cp); 421562306a36Sopenharmony_ci cas_spare_recover(cp, GFP_KERNEL); 421662306a36Sopenharmony_ci 421762306a36Sopenharmony_ci /* We can now request the interrupt as we know it's masked 421862306a36Sopenharmony_ci * on the controller. cassini+ has up to 4 interrupts 421962306a36Sopenharmony_ci * that can be used, but you need to do explicit pci interrupt 422062306a36Sopenharmony_ci * mapping to expose them 422162306a36Sopenharmony_ci */ 422262306a36Sopenharmony_ci if (request_irq(cp->pdev->irq, cas_interrupt, 422362306a36Sopenharmony_ci IRQF_SHARED, dev->name, (void *) dev)) { 422462306a36Sopenharmony_ci netdev_err(cp->dev, "failed to request irq !\n"); 422562306a36Sopenharmony_ci err = -EAGAIN; 422662306a36Sopenharmony_ci goto err_spare; 422762306a36Sopenharmony_ci } 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci#ifdef USE_NAPI 423062306a36Sopenharmony_ci napi_enable(&cp->napi); 423162306a36Sopenharmony_ci#endif 423262306a36Sopenharmony_ci /* init hw */ 423362306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 423462306a36Sopenharmony_ci cas_clean_rings(cp); 423562306a36Sopenharmony_ci cas_init_hw(cp, !hw_was_up); 423662306a36Sopenharmony_ci cp->opened = 1; 423762306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 423862306a36Sopenharmony_ci 423962306a36Sopenharmony_ci netif_start_queue(dev); 424062306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 424162306a36Sopenharmony_ci return 0; 424262306a36Sopenharmony_ci 424362306a36Sopenharmony_cierr_spare: 424462306a36Sopenharmony_ci cas_spare_free(cp); 424562306a36Sopenharmony_ci cas_free_rxds(cp); 424662306a36Sopenharmony_cierr_tx_tiny: 424762306a36Sopenharmony_ci cas_tx_tiny_free(cp); 424862306a36Sopenharmony_cierr_unlock: 424962306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 425062306a36Sopenharmony_ci return err; 425162306a36Sopenharmony_ci} 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_cistatic int cas_close(struct net_device *dev) 425462306a36Sopenharmony_ci{ 425562306a36Sopenharmony_ci unsigned long flags; 425662306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_ci#ifdef USE_NAPI 425962306a36Sopenharmony_ci napi_disable(&cp->napi); 426062306a36Sopenharmony_ci#endif 426162306a36Sopenharmony_ci /* Make sure we don't get distracted by suspend/resume */ 426262306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 426362306a36Sopenharmony_ci 426462306a36Sopenharmony_ci netif_stop_queue(dev); 426562306a36Sopenharmony_ci 426662306a36Sopenharmony_ci /* Stop traffic, mark us closed */ 426762306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 426862306a36Sopenharmony_ci cp->opened = 0; 426962306a36Sopenharmony_ci cas_reset(cp, 0); 427062306a36Sopenharmony_ci cas_phy_init(cp); 427162306a36Sopenharmony_ci cas_begin_auto_negotiation(cp, NULL); 427262306a36Sopenharmony_ci cas_clean_rings(cp); 427362306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 427462306a36Sopenharmony_ci 427562306a36Sopenharmony_ci free_irq(cp->pdev->irq, (void *) dev); 427662306a36Sopenharmony_ci cas_spare_free(cp); 427762306a36Sopenharmony_ci cas_free_rxds(cp); 427862306a36Sopenharmony_ci cas_tx_tiny_free(cp); 427962306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 428062306a36Sopenharmony_ci return 0; 428162306a36Sopenharmony_ci} 428262306a36Sopenharmony_ci 428362306a36Sopenharmony_cistatic struct { 428462306a36Sopenharmony_ci const char name[ETH_GSTRING_LEN]; 428562306a36Sopenharmony_ci} ethtool_cassini_statnames[] = { 428662306a36Sopenharmony_ci {"collisions"}, 428762306a36Sopenharmony_ci {"rx_bytes"}, 428862306a36Sopenharmony_ci {"rx_crc_errors"}, 428962306a36Sopenharmony_ci {"rx_dropped"}, 429062306a36Sopenharmony_ci {"rx_errors"}, 429162306a36Sopenharmony_ci {"rx_fifo_errors"}, 429262306a36Sopenharmony_ci {"rx_frame_errors"}, 429362306a36Sopenharmony_ci {"rx_length_errors"}, 429462306a36Sopenharmony_ci {"rx_over_errors"}, 429562306a36Sopenharmony_ci {"rx_packets"}, 429662306a36Sopenharmony_ci {"tx_aborted_errors"}, 429762306a36Sopenharmony_ci {"tx_bytes"}, 429862306a36Sopenharmony_ci {"tx_dropped"}, 429962306a36Sopenharmony_ci {"tx_errors"}, 430062306a36Sopenharmony_ci {"tx_fifo_errors"}, 430162306a36Sopenharmony_ci {"tx_packets"} 430262306a36Sopenharmony_ci}; 430362306a36Sopenharmony_ci#define CAS_NUM_STAT_KEYS ARRAY_SIZE(ethtool_cassini_statnames) 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_cistatic struct { 430662306a36Sopenharmony_ci const int offsets; /* neg. values for 2nd arg to cas_read_phy */ 430762306a36Sopenharmony_ci} ethtool_register_table[] = { 430862306a36Sopenharmony_ci {-MII_BMSR}, 430962306a36Sopenharmony_ci {-MII_BMCR}, 431062306a36Sopenharmony_ci {REG_CAWR}, 431162306a36Sopenharmony_ci {REG_INF_BURST}, 431262306a36Sopenharmony_ci {REG_BIM_CFG}, 431362306a36Sopenharmony_ci {REG_RX_CFG}, 431462306a36Sopenharmony_ci {REG_HP_CFG}, 431562306a36Sopenharmony_ci {REG_MAC_TX_CFG}, 431662306a36Sopenharmony_ci {REG_MAC_RX_CFG}, 431762306a36Sopenharmony_ci {REG_MAC_CTRL_CFG}, 431862306a36Sopenharmony_ci {REG_MAC_XIF_CFG}, 431962306a36Sopenharmony_ci {REG_MIF_CFG}, 432062306a36Sopenharmony_ci {REG_PCS_CFG}, 432162306a36Sopenharmony_ci {REG_SATURN_PCFG}, 432262306a36Sopenharmony_ci {REG_PCS_MII_STATUS}, 432362306a36Sopenharmony_ci {REG_PCS_STATE_MACHINE}, 432462306a36Sopenharmony_ci {REG_MAC_COLL_EXCESS}, 432562306a36Sopenharmony_ci {REG_MAC_COLL_LATE} 432662306a36Sopenharmony_ci}; 432762306a36Sopenharmony_ci#define CAS_REG_LEN ARRAY_SIZE(ethtool_register_table) 432862306a36Sopenharmony_ci#define CAS_MAX_REGS (sizeof (u32)*CAS_REG_LEN) 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_cistatic void cas_read_regs(struct cas *cp, u8 *ptr, int len) 433162306a36Sopenharmony_ci{ 433262306a36Sopenharmony_ci u8 *p; 433362306a36Sopenharmony_ci int i; 433462306a36Sopenharmony_ci unsigned long flags; 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 433762306a36Sopenharmony_ci for (i = 0, p = ptr; i < len ; i ++, p += sizeof(u32)) { 433862306a36Sopenharmony_ci u16 hval; 433962306a36Sopenharmony_ci u32 val; 434062306a36Sopenharmony_ci if (ethtool_register_table[i].offsets < 0) { 434162306a36Sopenharmony_ci hval = cas_phy_read(cp, 434262306a36Sopenharmony_ci -ethtool_register_table[i].offsets); 434362306a36Sopenharmony_ci val = hval; 434462306a36Sopenharmony_ci } else { 434562306a36Sopenharmony_ci val= readl(cp->regs+ethtool_register_table[i].offsets); 434662306a36Sopenharmony_ci } 434762306a36Sopenharmony_ci memcpy(p, (u8 *)&val, sizeof(u32)); 434862306a36Sopenharmony_ci } 434962306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 435062306a36Sopenharmony_ci} 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_cistatic struct net_device_stats *cas_get_stats(struct net_device *dev) 435362306a36Sopenharmony_ci{ 435462306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 435562306a36Sopenharmony_ci struct net_device_stats *stats = cp->net_stats; 435662306a36Sopenharmony_ci unsigned long flags; 435762306a36Sopenharmony_ci int i; 435862306a36Sopenharmony_ci unsigned long tmp; 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci /* we collate all of the stats into net_stats[N_TX_RING] */ 436162306a36Sopenharmony_ci if (!cp->hw_running) 436262306a36Sopenharmony_ci return stats + N_TX_RINGS; 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci /* collect outstanding stats */ 436562306a36Sopenharmony_ci /* WTZ: the Cassini spec gives these as 16 bit counters but 436662306a36Sopenharmony_ci * stored in 32-bit words. Added a mask of 0xffff to be safe, 436762306a36Sopenharmony_ci * in case the chip somehow puts any garbage in the other bits. 436862306a36Sopenharmony_ci * Also, counter usage didn't seem to mach what Adrian did 436962306a36Sopenharmony_ci * in the parts of the code that set these quantities. Made 437062306a36Sopenharmony_ci * that consistent. 437162306a36Sopenharmony_ci */ 437262306a36Sopenharmony_ci spin_lock_irqsave(&cp->stat_lock[N_TX_RINGS], flags); 437362306a36Sopenharmony_ci stats[N_TX_RINGS].rx_crc_errors += 437462306a36Sopenharmony_ci readl(cp->regs + REG_MAC_FCS_ERR) & 0xffff; 437562306a36Sopenharmony_ci stats[N_TX_RINGS].rx_frame_errors += 437662306a36Sopenharmony_ci readl(cp->regs + REG_MAC_ALIGN_ERR) &0xffff; 437762306a36Sopenharmony_ci stats[N_TX_RINGS].rx_length_errors += 437862306a36Sopenharmony_ci readl(cp->regs + REG_MAC_LEN_ERR) & 0xffff; 437962306a36Sopenharmony_ci#if 1 438062306a36Sopenharmony_ci tmp = (readl(cp->regs + REG_MAC_COLL_EXCESS) & 0xffff) + 438162306a36Sopenharmony_ci (readl(cp->regs + REG_MAC_COLL_LATE) & 0xffff); 438262306a36Sopenharmony_ci stats[N_TX_RINGS].tx_aborted_errors += tmp; 438362306a36Sopenharmony_ci stats[N_TX_RINGS].collisions += 438462306a36Sopenharmony_ci tmp + (readl(cp->regs + REG_MAC_COLL_NORMAL) & 0xffff); 438562306a36Sopenharmony_ci#else 438662306a36Sopenharmony_ci stats[N_TX_RINGS].tx_aborted_errors += 438762306a36Sopenharmony_ci readl(cp->regs + REG_MAC_COLL_EXCESS); 438862306a36Sopenharmony_ci stats[N_TX_RINGS].collisions += readl(cp->regs + REG_MAC_COLL_EXCESS) + 438962306a36Sopenharmony_ci readl(cp->regs + REG_MAC_COLL_LATE); 439062306a36Sopenharmony_ci#endif 439162306a36Sopenharmony_ci cas_clear_mac_err(cp); 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_ci /* saved bits that are unique to ring 0 */ 439462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[0]); 439562306a36Sopenharmony_ci stats[N_TX_RINGS].collisions += stats[0].collisions; 439662306a36Sopenharmony_ci stats[N_TX_RINGS].rx_over_errors += stats[0].rx_over_errors; 439762306a36Sopenharmony_ci stats[N_TX_RINGS].rx_frame_errors += stats[0].rx_frame_errors; 439862306a36Sopenharmony_ci stats[N_TX_RINGS].rx_fifo_errors += stats[0].rx_fifo_errors; 439962306a36Sopenharmony_ci stats[N_TX_RINGS].tx_aborted_errors += stats[0].tx_aborted_errors; 440062306a36Sopenharmony_ci stats[N_TX_RINGS].tx_fifo_errors += stats[0].tx_fifo_errors; 440162306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[0]); 440262306a36Sopenharmony_ci 440362306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) { 440462306a36Sopenharmony_ci spin_lock(&cp->stat_lock[i]); 440562306a36Sopenharmony_ci stats[N_TX_RINGS].rx_length_errors += 440662306a36Sopenharmony_ci stats[i].rx_length_errors; 440762306a36Sopenharmony_ci stats[N_TX_RINGS].rx_crc_errors += stats[i].rx_crc_errors; 440862306a36Sopenharmony_ci stats[N_TX_RINGS].rx_packets += stats[i].rx_packets; 440962306a36Sopenharmony_ci stats[N_TX_RINGS].tx_packets += stats[i].tx_packets; 441062306a36Sopenharmony_ci stats[N_TX_RINGS].rx_bytes += stats[i].rx_bytes; 441162306a36Sopenharmony_ci stats[N_TX_RINGS].tx_bytes += stats[i].tx_bytes; 441262306a36Sopenharmony_ci stats[N_TX_RINGS].rx_errors += stats[i].rx_errors; 441362306a36Sopenharmony_ci stats[N_TX_RINGS].tx_errors += stats[i].tx_errors; 441462306a36Sopenharmony_ci stats[N_TX_RINGS].rx_dropped += stats[i].rx_dropped; 441562306a36Sopenharmony_ci stats[N_TX_RINGS].tx_dropped += stats[i].tx_dropped; 441662306a36Sopenharmony_ci memset(stats + i, 0, sizeof(struct net_device_stats)); 441762306a36Sopenharmony_ci spin_unlock(&cp->stat_lock[i]); 441862306a36Sopenharmony_ci } 441962306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->stat_lock[N_TX_RINGS], flags); 442062306a36Sopenharmony_ci return stats + N_TX_RINGS; 442162306a36Sopenharmony_ci} 442262306a36Sopenharmony_ci 442362306a36Sopenharmony_ci 442462306a36Sopenharmony_cistatic void cas_set_multicast(struct net_device *dev) 442562306a36Sopenharmony_ci{ 442662306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 442762306a36Sopenharmony_ci u32 rxcfg, rxcfg_new; 442862306a36Sopenharmony_ci unsigned long flags; 442962306a36Sopenharmony_ci int limit = STOP_TRIES; 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_ci if (!cp->hw_running) 443262306a36Sopenharmony_ci return; 443362306a36Sopenharmony_ci 443462306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 443562306a36Sopenharmony_ci rxcfg = readl(cp->regs + REG_MAC_RX_CFG); 443662306a36Sopenharmony_ci 443762306a36Sopenharmony_ci /* disable RX MAC and wait for completion */ 443862306a36Sopenharmony_ci writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG); 443962306a36Sopenharmony_ci while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_EN) { 444062306a36Sopenharmony_ci if (!limit--) 444162306a36Sopenharmony_ci break; 444262306a36Sopenharmony_ci udelay(10); 444362306a36Sopenharmony_ci } 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci /* disable hash filter and wait for completion */ 444662306a36Sopenharmony_ci limit = STOP_TRIES; 444762306a36Sopenharmony_ci rxcfg &= ~(MAC_RX_CFG_PROMISC_EN | MAC_RX_CFG_HASH_FILTER_EN); 444862306a36Sopenharmony_ci writel(rxcfg & ~MAC_RX_CFG_EN, cp->regs + REG_MAC_RX_CFG); 444962306a36Sopenharmony_ci while (readl(cp->regs + REG_MAC_RX_CFG) & MAC_RX_CFG_HASH_FILTER_EN) { 445062306a36Sopenharmony_ci if (!limit--) 445162306a36Sopenharmony_ci break; 445262306a36Sopenharmony_ci udelay(10); 445362306a36Sopenharmony_ci } 445462306a36Sopenharmony_ci 445562306a36Sopenharmony_ci /* program hash filters */ 445662306a36Sopenharmony_ci cp->mac_rx_cfg = rxcfg_new = cas_setup_multicast(cp); 445762306a36Sopenharmony_ci rxcfg |= rxcfg_new; 445862306a36Sopenharmony_ci writel(rxcfg, cp->regs + REG_MAC_RX_CFG); 445962306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 446062306a36Sopenharmony_ci} 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_cistatic void cas_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 446362306a36Sopenharmony_ci{ 446462306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 446562306a36Sopenharmony_ci strscpy(info->driver, DRV_MODULE_NAME, sizeof(info->driver)); 446662306a36Sopenharmony_ci strscpy(info->version, DRV_MODULE_VERSION, sizeof(info->version)); 446762306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(cp->pdev), sizeof(info->bus_info)); 446862306a36Sopenharmony_ci} 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_cistatic int cas_get_link_ksettings(struct net_device *dev, 447162306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 447262306a36Sopenharmony_ci{ 447362306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 447462306a36Sopenharmony_ci u16 bmcr; 447562306a36Sopenharmony_ci int full_duplex, speed, pause; 447662306a36Sopenharmony_ci unsigned long flags; 447762306a36Sopenharmony_ci enum link_state linkstate = link_up; 447862306a36Sopenharmony_ci u32 supported, advertising; 447962306a36Sopenharmony_ci 448062306a36Sopenharmony_ci advertising = 0; 448162306a36Sopenharmony_ci supported = SUPPORTED_Autoneg; 448262306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_1000MB_CAP) { 448362306a36Sopenharmony_ci supported |= SUPPORTED_1000baseT_Full; 448462306a36Sopenharmony_ci advertising |= ADVERTISED_1000baseT_Full; 448562306a36Sopenharmony_ci } 448662306a36Sopenharmony_ci 448762306a36Sopenharmony_ci /* Record PHY settings if HW is on. */ 448862306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 448962306a36Sopenharmony_ci bmcr = 0; 449062306a36Sopenharmony_ci linkstate = cp->lstate; 449162306a36Sopenharmony_ci if (CAS_PHY_MII(cp->phy_type)) { 449262306a36Sopenharmony_ci cmd->base.port = PORT_MII; 449362306a36Sopenharmony_ci cmd->base.phy_address = cp->phy_addr; 449462306a36Sopenharmony_ci advertising |= ADVERTISED_TP | ADVERTISED_MII | 449562306a36Sopenharmony_ci ADVERTISED_10baseT_Half | 449662306a36Sopenharmony_ci ADVERTISED_10baseT_Full | 449762306a36Sopenharmony_ci ADVERTISED_100baseT_Half | 449862306a36Sopenharmony_ci ADVERTISED_100baseT_Full; 449962306a36Sopenharmony_ci 450062306a36Sopenharmony_ci supported |= 450162306a36Sopenharmony_ci (SUPPORTED_10baseT_Half | 450262306a36Sopenharmony_ci SUPPORTED_10baseT_Full | 450362306a36Sopenharmony_ci SUPPORTED_100baseT_Half | 450462306a36Sopenharmony_ci SUPPORTED_100baseT_Full | 450562306a36Sopenharmony_ci SUPPORTED_TP | SUPPORTED_MII); 450662306a36Sopenharmony_ci 450762306a36Sopenharmony_ci if (cp->hw_running) { 450862306a36Sopenharmony_ci cas_mif_poll(cp, 0); 450962306a36Sopenharmony_ci bmcr = cas_phy_read(cp, MII_BMCR); 451062306a36Sopenharmony_ci cas_read_mii_link_mode(cp, &full_duplex, 451162306a36Sopenharmony_ci &speed, &pause); 451262306a36Sopenharmony_ci cas_mif_poll(cp, 1); 451362306a36Sopenharmony_ci } 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci } else { 451662306a36Sopenharmony_ci cmd->base.port = PORT_FIBRE; 451762306a36Sopenharmony_ci cmd->base.phy_address = 0; 451862306a36Sopenharmony_ci supported |= SUPPORTED_FIBRE; 451962306a36Sopenharmony_ci advertising |= ADVERTISED_FIBRE; 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci if (cp->hw_running) { 452262306a36Sopenharmony_ci /* pcs uses the same bits as mii */ 452362306a36Sopenharmony_ci bmcr = readl(cp->regs + REG_PCS_MII_CTRL); 452462306a36Sopenharmony_ci cas_read_pcs_link_mode(cp, &full_duplex, 452562306a36Sopenharmony_ci &speed, &pause); 452662306a36Sopenharmony_ci } 452762306a36Sopenharmony_ci } 452862306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 452962306a36Sopenharmony_ci 453062306a36Sopenharmony_ci if (bmcr & BMCR_ANENABLE) { 453162306a36Sopenharmony_ci advertising |= ADVERTISED_Autoneg; 453262306a36Sopenharmony_ci cmd->base.autoneg = AUTONEG_ENABLE; 453362306a36Sopenharmony_ci cmd->base.speed = ((speed == 10) ? 453462306a36Sopenharmony_ci SPEED_10 : 453562306a36Sopenharmony_ci ((speed == 1000) ? 453662306a36Sopenharmony_ci SPEED_1000 : SPEED_100)); 453762306a36Sopenharmony_ci cmd->base.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF; 453862306a36Sopenharmony_ci } else { 453962306a36Sopenharmony_ci cmd->base.autoneg = AUTONEG_DISABLE; 454062306a36Sopenharmony_ci cmd->base.speed = ((bmcr & CAS_BMCR_SPEED1000) ? 454162306a36Sopenharmony_ci SPEED_1000 : 454262306a36Sopenharmony_ci ((bmcr & BMCR_SPEED100) ? 454362306a36Sopenharmony_ci SPEED_100 : SPEED_10)); 454462306a36Sopenharmony_ci cmd->base.duplex = (bmcr & BMCR_FULLDPLX) ? 454562306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 454662306a36Sopenharmony_ci } 454762306a36Sopenharmony_ci if (linkstate != link_up) { 454862306a36Sopenharmony_ci /* Force these to "unknown" if the link is not up and 454962306a36Sopenharmony_ci * autonogotiation in enabled. We can set the link 455062306a36Sopenharmony_ci * speed to 0, but not cmd->duplex, 455162306a36Sopenharmony_ci * because its legal values are 0 and 1. Ethtool will 455262306a36Sopenharmony_ci * print the value reported in parentheses after the 455362306a36Sopenharmony_ci * word "Unknown" for unrecognized values. 455462306a36Sopenharmony_ci * 455562306a36Sopenharmony_ci * If in forced mode, we report the speed and duplex 455662306a36Sopenharmony_ci * settings that we configured. 455762306a36Sopenharmony_ci */ 455862306a36Sopenharmony_ci if (cp->link_cntl & BMCR_ANENABLE) { 455962306a36Sopenharmony_ci cmd->base.speed = 0; 456062306a36Sopenharmony_ci cmd->base.duplex = 0xff; 456162306a36Sopenharmony_ci } else { 456262306a36Sopenharmony_ci cmd->base.speed = SPEED_10; 456362306a36Sopenharmony_ci if (cp->link_cntl & BMCR_SPEED100) { 456462306a36Sopenharmony_ci cmd->base.speed = SPEED_100; 456562306a36Sopenharmony_ci } else if (cp->link_cntl & CAS_BMCR_SPEED1000) { 456662306a36Sopenharmony_ci cmd->base.speed = SPEED_1000; 456762306a36Sopenharmony_ci } 456862306a36Sopenharmony_ci cmd->base.duplex = (cp->link_cntl & BMCR_FULLDPLX) ? 456962306a36Sopenharmony_ci DUPLEX_FULL : DUPLEX_HALF; 457062306a36Sopenharmony_ci } 457162306a36Sopenharmony_ci } 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 457462306a36Sopenharmony_ci supported); 457562306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 457662306a36Sopenharmony_ci advertising); 457762306a36Sopenharmony_ci 457862306a36Sopenharmony_ci return 0; 457962306a36Sopenharmony_ci} 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_cistatic int cas_set_link_ksettings(struct net_device *dev, 458262306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 458362306a36Sopenharmony_ci{ 458462306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 458562306a36Sopenharmony_ci unsigned long flags; 458662306a36Sopenharmony_ci u32 speed = cmd->base.speed; 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ci /* Verify the settings we care about. */ 458962306a36Sopenharmony_ci if (cmd->base.autoneg != AUTONEG_ENABLE && 459062306a36Sopenharmony_ci cmd->base.autoneg != AUTONEG_DISABLE) 459162306a36Sopenharmony_ci return -EINVAL; 459262306a36Sopenharmony_ci 459362306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE && 459462306a36Sopenharmony_ci ((speed != SPEED_1000 && 459562306a36Sopenharmony_ci speed != SPEED_100 && 459662306a36Sopenharmony_ci speed != SPEED_10) || 459762306a36Sopenharmony_ci (cmd->base.duplex != DUPLEX_HALF && 459862306a36Sopenharmony_ci cmd->base.duplex != DUPLEX_FULL))) 459962306a36Sopenharmony_ci return -EINVAL; 460062306a36Sopenharmony_ci 460162306a36Sopenharmony_ci /* Apply settings and restart link process. */ 460262306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 460362306a36Sopenharmony_ci cas_begin_auto_negotiation(cp, cmd); 460462306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 460562306a36Sopenharmony_ci return 0; 460662306a36Sopenharmony_ci} 460762306a36Sopenharmony_ci 460862306a36Sopenharmony_cistatic int cas_nway_reset(struct net_device *dev) 460962306a36Sopenharmony_ci{ 461062306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 461162306a36Sopenharmony_ci unsigned long flags; 461262306a36Sopenharmony_ci 461362306a36Sopenharmony_ci if ((cp->link_cntl & BMCR_ANENABLE) == 0) 461462306a36Sopenharmony_ci return -EINVAL; 461562306a36Sopenharmony_ci 461662306a36Sopenharmony_ci /* Restart link process. */ 461762306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 461862306a36Sopenharmony_ci cas_begin_auto_negotiation(cp, NULL); 461962306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 462062306a36Sopenharmony_ci 462162306a36Sopenharmony_ci return 0; 462262306a36Sopenharmony_ci} 462362306a36Sopenharmony_ci 462462306a36Sopenharmony_cistatic u32 cas_get_link(struct net_device *dev) 462562306a36Sopenharmony_ci{ 462662306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 462762306a36Sopenharmony_ci return cp->lstate == link_up; 462862306a36Sopenharmony_ci} 462962306a36Sopenharmony_ci 463062306a36Sopenharmony_cistatic u32 cas_get_msglevel(struct net_device *dev) 463162306a36Sopenharmony_ci{ 463262306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 463362306a36Sopenharmony_ci return cp->msg_enable; 463462306a36Sopenharmony_ci} 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_cistatic void cas_set_msglevel(struct net_device *dev, u32 value) 463762306a36Sopenharmony_ci{ 463862306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 463962306a36Sopenharmony_ci cp->msg_enable = value; 464062306a36Sopenharmony_ci} 464162306a36Sopenharmony_ci 464262306a36Sopenharmony_cistatic int cas_get_regs_len(struct net_device *dev) 464362306a36Sopenharmony_ci{ 464462306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 464562306a36Sopenharmony_ci return min_t(int, cp->casreg_len, CAS_MAX_REGS); 464662306a36Sopenharmony_ci} 464762306a36Sopenharmony_ci 464862306a36Sopenharmony_cistatic void cas_get_regs(struct net_device *dev, struct ethtool_regs *regs, 464962306a36Sopenharmony_ci void *p) 465062306a36Sopenharmony_ci{ 465162306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 465262306a36Sopenharmony_ci regs->version = 0; 465362306a36Sopenharmony_ci /* cas_read_regs handles locks (cp->lock). */ 465462306a36Sopenharmony_ci cas_read_regs(cp, p, regs->len / sizeof(u32)); 465562306a36Sopenharmony_ci} 465662306a36Sopenharmony_ci 465762306a36Sopenharmony_cistatic int cas_get_sset_count(struct net_device *dev, int sset) 465862306a36Sopenharmony_ci{ 465962306a36Sopenharmony_ci switch (sset) { 466062306a36Sopenharmony_ci case ETH_SS_STATS: 466162306a36Sopenharmony_ci return CAS_NUM_STAT_KEYS; 466262306a36Sopenharmony_ci default: 466362306a36Sopenharmony_ci return -EOPNOTSUPP; 466462306a36Sopenharmony_ci } 466562306a36Sopenharmony_ci} 466662306a36Sopenharmony_ci 466762306a36Sopenharmony_cistatic void cas_get_strings(struct net_device *dev, u32 stringset, u8 *data) 466862306a36Sopenharmony_ci{ 466962306a36Sopenharmony_ci memcpy(data, ðtool_cassini_statnames, 467062306a36Sopenharmony_ci CAS_NUM_STAT_KEYS * ETH_GSTRING_LEN); 467162306a36Sopenharmony_ci} 467262306a36Sopenharmony_ci 467362306a36Sopenharmony_cistatic void cas_get_ethtool_stats(struct net_device *dev, 467462306a36Sopenharmony_ci struct ethtool_stats *estats, u64 *data) 467562306a36Sopenharmony_ci{ 467662306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 467762306a36Sopenharmony_ci struct net_device_stats *stats = cas_get_stats(cp->dev); 467862306a36Sopenharmony_ci int i = 0; 467962306a36Sopenharmony_ci data[i++] = stats->collisions; 468062306a36Sopenharmony_ci data[i++] = stats->rx_bytes; 468162306a36Sopenharmony_ci data[i++] = stats->rx_crc_errors; 468262306a36Sopenharmony_ci data[i++] = stats->rx_dropped; 468362306a36Sopenharmony_ci data[i++] = stats->rx_errors; 468462306a36Sopenharmony_ci data[i++] = stats->rx_fifo_errors; 468562306a36Sopenharmony_ci data[i++] = stats->rx_frame_errors; 468662306a36Sopenharmony_ci data[i++] = stats->rx_length_errors; 468762306a36Sopenharmony_ci data[i++] = stats->rx_over_errors; 468862306a36Sopenharmony_ci data[i++] = stats->rx_packets; 468962306a36Sopenharmony_ci data[i++] = stats->tx_aborted_errors; 469062306a36Sopenharmony_ci data[i++] = stats->tx_bytes; 469162306a36Sopenharmony_ci data[i++] = stats->tx_dropped; 469262306a36Sopenharmony_ci data[i++] = stats->tx_errors; 469362306a36Sopenharmony_ci data[i++] = stats->tx_fifo_errors; 469462306a36Sopenharmony_ci data[i++] = stats->tx_packets; 469562306a36Sopenharmony_ci BUG_ON(i != CAS_NUM_STAT_KEYS); 469662306a36Sopenharmony_ci} 469762306a36Sopenharmony_ci 469862306a36Sopenharmony_cistatic const struct ethtool_ops cas_ethtool_ops = { 469962306a36Sopenharmony_ci .get_drvinfo = cas_get_drvinfo, 470062306a36Sopenharmony_ci .nway_reset = cas_nway_reset, 470162306a36Sopenharmony_ci .get_link = cas_get_link, 470262306a36Sopenharmony_ci .get_msglevel = cas_get_msglevel, 470362306a36Sopenharmony_ci .set_msglevel = cas_set_msglevel, 470462306a36Sopenharmony_ci .get_regs_len = cas_get_regs_len, 470562306a36Sopenharmony_ci .get_regs = cas_get_regs, 470662306a36Sopenharmony_ci .get_sset_count = cas_get_sset_count, 470762306a36Sopenharmony_ci .get_strings = cas_get_strings, 470862306a36Sopenharmony_ci .get_ethtool_stats = cas_get_ethtool_stats, 470962306a36Sopenharmony_ci .get_link_ksettings = cas_get_link_ksettings, 471062306a36Sopenharmony_ci .set_link_ksettings = cas_set_link_ksettings, 471162306a36Sopenharmony_ci}; 471262306a36Sopenharmony_ci 471362306a36Sopenharmony_cistatic int cas_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 471462306a36Sopenharmony_ci{ 471562306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 471662306a36Sopenharmony_ci struct mii_ioctl_data *data = if_mii(ifr); 471762306a36Sopenharmony_ci unsigned long flags; 471862306a36Sopenharmony_ci int rc = -EOPNOTSUPP; 471962306a36Sopenharmony_ci 472062306a36Sopenharmony_ci /* Hold the PM mutex while doing ioctl's or we may collide 472162306a36Sopenharmony_ci * with open/close and power management and oops. 472262306a36Sopenharmony_ci */ 472362306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 472462306a36Sopenharmony_ci switch (cmd) { 472562306a36Sopenharmony_ci case SIOCGMIIPHY: /* Get address of MII PHY in use. */ 472662306a36Sopenharmony_ci data->phy_id = cp->phy_addr; 472762306a36Sopenharmony_ci fallthrough; 472862306a36Sopenharmony_ci 472962306a36Sopenharmony_ci case SIOCGMIIREG: /* Read MII PHY register. */ 473062306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 473162306a36Sopenharmony_ci cas_mif_poll(cp, 0); 473262306a36Sopenharmony_ci data->val_out = cas_phy_read(cp, data->reg_num & 0x1f); 473362306a36Sopenharmony_ci cas_mif_poll(cp, 1); 473462306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 473562306a36Sopenharmony_ci rc = 0; 473662306a36Sopenharmony_ci break; 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_ci case SIOCSMIIREG: /* Write MII PHY register. */ 473962306a36Sopenharmony_ci spin_lock_irqsave(&cp->lock, flags); 474062306a36Sopenharmony_ci cas_mif_poll(cp, 0); 474162306a36Sopenharmony_ci rc = cas_phy_write(cp, data->reg_num & 0x1f, data->val_in); 474262306a36Sopenharmony_ci cas_mif_poll(cp, 1); 474362306a36Sopenharmony_ci spin_unlock_irqrestore(&cp->lock, flags); 474462306a36Sopenharmony_ci break; 474562306a36Sopenharmony_ci default: 474662306a36Sopenharmony_ci break; 474762306a36Sopenharmony_ci } 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 475062306a36Sopenharmony_ci return rc; 475162306a36Sopenharmony_ci} 475262306a36Sopenharmony_ci 475362306a36Sopenharmony_ci/* When this chip sits underneath an Intel 31154 bridge, it is the 475462306a36Sopenharmony_ci * only subordinate device and we can tweak the bridge settings to 475562306a36Sopenharmony_ci * reflect that fact. 475662306a36Sopenharmony_ci */ 475762306a36Sopenharmony_cistatic void cas_program_bridge(struct pci_dev *cas_pdev) 475862306a36Sopenharmony_ci{ 475962306a36Sopenharmony_ci struct pci_dev *pdev = cas_pdev->bus->self; 476062306a36Sopenharmony_ci u32 val; 476162306a36Sopenharmony_ci 476262306a36Sopenharmony_ci if (!pdev) 476362306a36Sopenharmony_ci return; 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ci if (pdev->vendor != 0x8086 || pdev->device != 0x537c) 476662306a36Sopenharmony_ci return; 476762306a36Sopenharmony_ci 476862306a36Sopenharmony_ci /* Clear bit 10 (Bus Parking Control) in the Secondary 476962306a36Sopenharmony_ci * Arbiter Control/Status Register which lives at offset 477062306a36Sopenharmony_ci * 0x41. Using a 32-bit word read/modify/write at 0x40 477162306a36Sopenharmony_ci * is much simpler so that's how we do this. 477262306a36Sopenharmony_ci */ 477362306a36Sopenharmony_ci pci_read_config_dword(pdev, 0x40, &val); 477462306a36Sopenharmony_ci val &= ~0x00040000; 477562306a36Sopenharmony_ci pci_write_config_dword(pdev, 0x40, val); 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci /* Max out the Multi-Transaction Timer settings since 477862306a36Sopenharmony_ci * Cassini is the only device present. 477962306a36Sopenharmony_ci * 478062306a36Sopenharmony_ci * The register is 16-bit and lives at 0x50. When the 478162306a36Sopenharmony_ci * settings are enabled, it extends the GRANT# signal 478262306a36Sopenharmony_ci * for a requestor after a transaction is complete. This 478362306a36Sopenharmony_ci * allows the next request to run without first needing 478462306a36Sopenharmony_ci * to negotiate the GRANT# signal back. 478562306a36Sopenharmony_ci * 478662306a36Sopenharmony_ci * Bits 12:10 define the grant duration: 478762306a36Sopenharmony_ci * 478862306a36Sopenharmony_ci * 1 -- 16 clocks 478962306a36Sopenharmony_ci * 2 -- 32 clocks 479062306a36Sopenharmony_ci * 3 -- 64 clocks 479162306a36Sopenharmony_ci * 4 -- 128 clocks 479262306a36Sopenharmony_ci * 5 -- 256 clocks 479362306a36Sopenharmony_ci * 479462306a36Sopenharmony_ci * All other values are illegal. 479562306a36Sopenharmony_ci * 479662306a36Sopenharmony_ci * Bits 09:00 define which REQ/GNT signal pairs get the 479762306a36Sopenharmony_ci * GRANT# signal treatment. We set them all. 479862306a36Sopenharmony_ci */ 479962306a36Sopenharmony_ci pci_write_config_word(pdev, 0x50, (5 << 10) | 0x3ff); 480062306a36Sopenharmony_ci 480162306a36Sopenharmony_ci /* The Read Prefecth Policy register is 16-bit and sits at 480262306a36Sopenharmony_ci * offset 0x52. It enables a "smart" pre-fetch policy. We 480362306a36Sopenharmony_ci * enable it and max out all of the settings since only one 480462306a36Sopenharmony_ci * device is sitting underneath and thus bandwidth sharing is 480562306a36Sopenharmony_ci * not an issue. 480662306a36Sopenharmony_ci * 480762306a36Sopenharmony_ci * The register has several 3 bit fields, which indicates a 480862306a36Sopenharmony_ci * multiplier applied to the base amount of prefetching the 480962306a36Sopenharmony_ci * chip would do. These fields are at: 481062306a36Sopenharmony_ci * 481162306a36Sopenharmony_ci * 15:13 --- ReRead Primary Bus 481262306a36Sopenharmony_ci * 12:10 --- FirstRead Primary Bus 481362306a36Sopenharmony_ci * 09:07 --- ReRead Secondary Bus 481462306a36Sopenharmony_ci * 06:04 --- FirstRead Secondary Bus 481562306a36Sopenharmony_ci * 481662306a36Sopenharmony_ci * Bits 03:00 control which REQ/GNT pairs the prefetch settings 481762306a36Sopenharmony_ci * get enabled on. Bit 3 is a grouped enabler which controls 481862306a36Sopenharmony_ci * all of the REQ/GNT pairs from [8:3]. Bits 2 to 0 control 481962306a36Sopenharmony_ci * the individual REQ/GNT pairs [2:0]. 482062306a36Sopenharmony_ci */ 482162306a36Sopenharmony_ci pci_write_config_word(pdev, 0x52, 482262306a36Sopenharmony_ci (0x7 << 13) | 482362306a36Sopenharmony_ci (0x7 << 10) | 482462306a36Sopenharmony_ci (0x7 << 7) | 482562306a36Sopenharmony_ci (0x7 << 4) | 482662306a36Sopenharmony_ci (0xf << 0)); 482762306a36Sopenharmony_ci 482862306a36Sopenharmony_ci /* Force cacheline size to 0x8 */ 482962306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 0x08); 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci /* Force latency timer to maximum setting so Cassini can 483262306a36Sopenharmony_ci * sit on the bus as long as it likes. 483362306a36Sopenharmony_ci */ 483462306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xff); 483562306a36Sopenharmony_ci} 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_cistatic const struct net_device_ops cas_netdev_ops = { 483862306a36Sopenharmony_ci .ndo_open = cas_open, 483962306a36Sopenharmony_ci .ndo_stop = cas_close, 484062306a36Sopenharmony_ci .ndo_start_xmit = cas_start_xmit, 484162306a36Sopenharmony_ci .ndo_get_stats = cas_get_stats, 484262306a36Sopenharmony_ci .ndo_set_rx_mode = cas_set_multicast, 484362306a36Sopenharmony_ci .ndo_eth_ioctl = cas_ioctl, 484462306a36Sopenharmony_ci .ndo_tx_timeout = cas_tx_timeout, 484562306a36Sopenharmony_ci .ndo_change_mtu = cas_change_mtu, 484662306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 484762306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 484862306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 484962306a36Sopenharmony_ci .ndo_poll_controller = cas_netpoll, 485062306a36Sopenharmony_ci#endif 485162306a36Sopenharmony_ci}; 485262306a36Sopenharmony_ci 485362306a36Sopenharmony_cistatic int cas_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 485462306a36Sopenharmony_ci{ 485562306a36Sopenharmony_ci static int cas_version_printed = 0; 485662306a36Sopenharmony_ci unsigned long casreg_len; 485762306a36Sopenharmony_ci struct net_device *dev; 485862306a36Sopenharmony_ci struct cas *cp; 485962306a36Sopenharmony_ci u16 pci_cmd; 486062306a36Sopenharmony_ci int i, err; 486162306a36Sopenharmony_ci u8 orig_cacheline_size = 0, cas_cacheline_size = 0; 486262306a36Sopenharmony_ci 486362306a36Sopenharmony_ci if (cas_version_printed++ == 0) 486462306a36Sopenharmony_ci pr_info("%s", version); 486562306a36Sopenharmony_ci 486662306a36Sopenharmony_ci err = pci_enable_device(pdev); 486762306a36Sopenharmony_ci if (err) { 486862306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot enable PCI device, aborting\n"); 486962306a36Sopenharmony_ci return err; 487062306a36Sopenharmony_ci } 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 487362306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot find proper PCI device " 487462306a36Sopenharmony_ci "base address, aborting\n"); 487562306a36Sopenharmony_ci err = -ENODEV; 487662306a36Sopenharmony_ci goto err_out_disable_pdev; 487762306a36Sopenharmony_ci } 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(*cp)); 488062306a36Sopenharmony_ci if (!dev) { 488162306a36Sopenharmony_ci err = -ENOMEM; 488262306a36Sopenharmony_ci goto err_out_disable_pdev; 488362306a36Sopenharmony_ci } 488462306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &pdev->dev); 488562306a36Sopenharmony_ci 488662306a36Sopenharmony_ci err = pci_request_regions(pdev, dev->name); 488762306a36Sopenharmony_ci if (err) { 488862306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); 488962306a36Sopenharmony_ci goto err_out_free_netdev; 489062306a36Sopenharmony_ci } 489162306a36Sopenharmony_ci pci_set_master(pdev); 489262306a36Sopenharmony_ci 489362306a36Sopenharmony_ci /* we must always turn on parity response or else parity 489462306a36Sopenharmony_ci * doesn't get generated properly. disable SERR/PERR as well. 489562306a36Sopenharmony_ci * in addition, we want to turn MWI on. 489662306a36Sopenharmony_ci */ 489762306a36Sopenharmony_ci pci_read_config_word(pdev, PCI_COMMAND, &pci_cmd); 489862306a36Sopenharmony_ci pci_cmd &= ~PCI_COMMAND_SERR; 489962306a36Sopenharmony_ci pci_cmd |= PCI_COMMAND_PARITY; 490062306a36Sopenharmony_ci pci_write_config_word(pdev, PCI_COMMAND, pci_cmd); 490162306a36Sopenharmony_ci if (pci_try_set_mwi(pdev)) 490262306a36Sopenharmony_ci pr_warn("Could not enable MWI for %s\n", pci_name(pdev)); 490362306a36Sopenharmony_ci 490462306a36Sopenharmony_ci cas_program_bridge(pdev); 490562306a36Sopenharmony_ci 490662306a36Sopenharmony_ci /* 490762306a36Sopenharmony_ci * On some architectures, the default cache line size set 490862306a36Sopenharmony_ci * by pci_try_set_mwi reduces perforamnce. We have to increase 490962306a36Sopenharmony_ci * it for this case. To start, we'll print some configuration 491062306a36Sopenharmony_ci * data. 491162306a36Sopenharmony_ci */ 491262306a36Sopenharmony_ci#if 1 491362306a36Sopenharmony_ci pci_read_config_byte(pdev, PCI_CACHE_LINE_SIZE, 491462306a36Sopenharmony_ci &orig_cacheline_size); 491562306a36Sopenharmony_ci if (orig_cacheline_size < CAS_PREF_CACHELINE_SIZE) { 491662306a36Sopenharmony_ci cas_cacheline_size = 491762306a36Sopenharmony_ci (CAS_PREF_CACHELINE_SIZE < SMP_CACHE_BYTES) ? 491862306a36Sopenharmony_ci CAS_PREF_CACHELINE_SIZE : SMP_CACHE_BYTES; 491962306a36Sopenharmony_ci if (pci_write_config_byte(pdev, 492062306a36Sopenharmony_ci PCI_CACHE_LINE_SIZE, 492162306a36Sopenharmony_ci cas_cacheline_size)) { 492262306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not set PCI cache " 492362306a36Sopenharmony_ci "line size\n"); 492462306a36Sopenharmony_ci goto err_out_free_res; 492562306a36Sopenharmony_ci } 492662306a36Sopenharmony_ci } 492762306a36Sopenharmony_ci#endif 492862306a36Sopenharmony_ci 492962306a36Sopenharmony_ci 493062306a36Sopenharmony_ci /* Configure DMA attributes. */ 493162306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 493262306a36Sopenharmony_ci if (err) { 493362306a36Sopenharmony_ci dev_err(&pdev->dev, "No usable DMA configuration, aborting\n"); 493462306a36Sopenharmony_ci goto err_out_free_res; 493562306a36Sopenharmony_ci } 493662306a36Sopenharmony_ci 493762306a36Sopenharmony_ci casreg_len = pci_resource_len(pdev, 0); 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci cp = netdev_priv(dev); 494062306a36Sopenharmony_ci cp->pdev = pdev; 494162306a36Sopenharmony_ci#if 1 494262306a36Sopenharmony_ci /* A value of 0 indicates we never explicitly set it */ 494362306a36Sopenharmony_ci cp->orig_cacheline_size = cas_cacheline_size ? orig_cacheline_size: 0; 494462306a36Sopenharmony_ci#endif 494562306a36Sopenharmony_ci cp->dev = dev; 494662306a36Sopenharmony_ci cp->msg_enable = (cassini_debug < 0) ? CAS_DEF_MSG_ENABLE : 494762306a36Sopenharmony_ci cassini_debug; 494862306a36Sopenharmony_ci 494962306a36Sopenharmony_ci#if defined(CONFIG_SPARC) 495062306a36Sopenharmony_ci cp->of_node = pci_device_to_OF_node(pdev); 495162306a36Sopenharmony_ci#endif 495262306a36Sopenharmony_ci 495362306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_UNKNOWN; 495462306a36Sopenharmony_ci cp->link_transition_jiffies_valid = 0; 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci spin_lock_init(&cp->lock); 495762306a36Sopenharmony_ci spin_lock_init(&cp->rx_inuse_lock); 495862306a36Sopenharmony_ci spin_lock_init(&cp->rx_spare_lock); 495962306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) { 496062306a36Sopenharmony_ci spin_lock_init(&cp->stat_lock[i]); 496162306a36Sopenharmony_ci spin_lock_init(&cp->tx_lock[i]); 496262306a36Sopenharmony_ci } 496362306a36Sopenharmony_ci spin_lock_init(&cp->stat_lock[N_TX_RINGS]); 496462306a36Sopenharmony_ci mutex_init(&cp->pm_mutex); 496562306a36Sopenharmony_ci 496662306a36Sopenharmony_ci timer_setup(&cp->link_timer, cas_link_timer, 0); 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci#if 1 496962306a36Sopenharmony_ci /* Just in case the implementation of atomic operations 497062306a36Sopenharmony_ci * change so that an explicit initialization is necessary. 497162306a36Sopenharmony_ci */ 497262306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending, 0); 497362306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending_all, 0); 497462306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending_spare, 0); 497562306a36Sopenharmony_ci atomic_set(&cp->reset_task_pending_mtu, 0); 497662306a36Sopenharmony_ci#endif 497762306a36Sopenharmony_ci INIT_WORK(&cp->reset_task, cas_reset_task); 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci /* Default link parameters */ 498062306a36Sopenharmony_ci if (link_mode >= 0 && link_mode < 6) 498162306a36Sopenharmony_ci cp->link_cntl = link_modes[link_mode]; 498262306a36Sopenharmony_ci else 498362306a36Sopenharmony_ci cp->link_cntl = BMCR_ANENABLE; 498462306a36Sopenharmony_ci cp->lstate = link_down; 498562306a36Sopenharmony_ci cp->link_transition = LINK_TRANSITION_LINK_DOWN; 498662306a36Sopenharmony_ci netif_carrier_off(cp->dev); 498762306a36Sopenharmony_ci cp->timer_ticks = 0; 498862306a36Sopenharmony_ci 498962306a36Sopenharmony_ci /* give us access to cassini registers */ 499062306a36Sopenharmony_ci cp->regs = pci_iomap(pdev, 0, casreg_len); 499162306a36Sopenharmony_ci if (!cp->regs) { 499262306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot map device registers, aborting\n"); 499362306a36Sopenharmony_ci goto err_out_free_res; 499462306a36Sopenharmony_ci } 499562306a36Sopenharmony_ci cp->casreg_len = casreg_len; 499662306a36Sopenharmony_ci 499762306a36Sopenharmony_ci pci_save_state(pdev); 499862306a36Sopenharmony_ci cas_check_pci_invariants(cp); 499962306a36Sopenharmony_ci cas_hard_reset(cp); 500062306a36Sopenharmony_ci cas_reset(cp, 0); 500162306a36Sopenharmony_ci if (cas_check_invariants(cp)) 500262306a36Sopenharmony_ci goto err_out_iounmap; 500362306a36Sopenharmony_ci if (cp->cas_flags & CAS_FLAG_SATURN) 500462306a36Sopenharmony_ci cas_saturn_firmware_init(cp); 500562306a36Sopenharmony_ci 500662306a36Sopenharmony_ci cp->init_block = 500762306a36Sopenharmony_ci dma_alloc_coherent(&pdev->dev, sizeof(struct cas_init_block), 500862306a36Sopenharmony_ci &cp->block_dvma, GFP_KERNEL); 500962306a36Sopenharmony_ci if (!cp->init_block) { 501062306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot allocate init block, aborting\n"); 501162306a36Sopenharmony_ci goto err_out_iounmap; 501262306a36Sopenharmony_ci } 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_ci for (i = 0; i < N_TX_RINGS; i++) 501562306a36Sopenharmony_ci cp->init_txds[i] = cp->init_block->txds[i]; 501662306a36Sopenharmony_ci 501762306a36Sopenharmony_ci for (i = 0; i < N_RX_DESC_RINGS; i++) 501862306a36Sopenharmony_ci cp->init_rxds[i] = cp->init_block->rxds[i]; 501962306a36Sopenharmony_ci 502062306a36Sopenharmony_ci for (i = 0; i < N_RX_COMP_RINGS; i++) 502162306a36Sopenharmony_ci cp->init_rxcs[i] = cp->init_block->rxcs[i]; 502262306a36Sopenharmony_ci 502362306a36Sopenharmony_ci for (i = 0; i < N_RX_FLOWS; i++) 502462306a36Sopenharmony_ci skb_queue_head_init(&cp->rx_flows[i]); 502562306a36Sopenharmony_ci 502662306a36Sopenharmony_ci dev->netdev_ops = &cas_netdev_ops; 502762306a36Sopenharmony_ci dev->ethtool_ops = &cas_ethtool_ops; 502862306a36Sopenharmony_ci dev->watchdog_timeo = CAS_TX_TIMEOUT; 502962306a36Sopenharmony_ci 503062306a36Sopenharmony_ci#ifdef USE_NAPI 503162306a36Sopenharmony_ci netif_napi_add(dev, &cp->napi, cas_poll); 503262306a36Sopenharmony_ci#endif 503362306a36Sopenharmony_ci dev->irq = pdev->irq; 503462306a36Sopenharmony_ci dev->dma = 0; 503562306a36Sopenharmony_ci 503662306a36Sopenharmony_ci /* Cassini features. */ 503762306a36Sopenharmony_ci if ((cp->cas_flags & CAS_FLAG_NO_HW_CSUM) == 0) 503862306a36Sopenharmony_ci dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; 503962306a36Sopenharmony_ci 504062306a36Sopenharmony_ci dev->features |= NETIF_F_HIGHDMA; 504162306a36Sopenharmony_ci 504262306a36Sopenharmony_ci /* MTU range: 60 - varies or 9000 */ 504362306a36Sopenharmony_ci dev->min_mtu = CAS_MIN_MTU; 504462306a36Sopenharmony_ci dev->max_mtu = CAS_MAX_MTU; 504562306a36Sopenharmony_ci 504662306a36Sopenharmony_ci if (register_netdev(dev)) { 504762306a36Sopenharmony_ci dev_err(&pdev->dev, "Cannot register net device, aborting\n"); 504862306a36Sopenharmony_ci goto err_out_free_consistent; 504962306a36Sopenharmony_ci } 505062306a36Sopenharmony_ci 505162306a36Sopenharmony_ci i = readl(cp->regs + REG_BIM_CFG); 505262306a36Sopenharmony_ci netdev_info(dev, "Sun Cassini%s (%sbit/%sMHz PCI/%s) Ethernet[%d] %pM\n", 505362306a36Sopenharmony_ci (cp->cas_flags & CAS_FLAG_REG_PLUS) ? "+" : "", 505462306a36Sopenharmony_ci (i & BIM_CFG_32BIT) ? "32" : "64", 505562306a36Sopenharmony_ci (i & BIM_CFG_66MHZ) ? "66" : "33", 505662306a36Sopenharmony_ci (cp->phy_type == CAS_PHY_SERDES) ? "Fi" : "Cu", pdev->irq, 505762306a36Sopenharmony_ci dev->dev_addr); 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci pci_set_drvdata(pdev, dev); 506062306a36Sopenharmony_ci cp->hw_running = 1; 506162306a36Sopenharmony_ci cas_entropy_reset(cp); 506262306a36Sopenharmony_ci cas_phy_init(cp); 506362306a36Sopenharmony_ci cas_begin_auto_negotiation(cp, NULL); 506462306a36Sopenharmony_ci return 0; 506562306a36Sopenharmony_ci 506662306a36Sopenharmony_cierr_out_free_consistent: 506762306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct cas_init_block), 506862306a36Sopenharmony_ci cp->init_block, cp->block_dvma); 506962306a36Sopenharmony_ci 507062306a36Sopenharmony_cierr_out_iounmap: 507162306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 507262306a36Sopenharmony_ci if (cp->hw_running) 507362306a36Sopenharmony_ci cas_shutdown(cp); 507462306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 507562306a36Sopenharmony_ci 507662306a36Sopenharmony_ci vfree(cp->fw_data); 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci pci_iounmap(pdev, cp->regs); 507962306a36Sopenharmony_ci 508062306a36Sopenharmony_ci 508162306a36Sopenharmony_cierr_out_free_res: 508262306a36Sopenharmony_ci pci_release_regions(pdev); 508362306a36Sopenharmony_ci 508462306a36Sopenharmony_ci /* Try to restore it in case the error occurred after we 508562306a36Sopenharmony_ci * set it. 508662306a36Sopenharmony_ci */ 508762306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, orig_cacheline_size); 508862306a36Sopenharmony_ci 508962306a36Sopenharmony_cierr_out_free_netdev: 509062306a36Sopenharmony_ci free_netdev(dev); 509162306a36Sopenharmony_ci 509262306a36Sopenharmony_cierr_out_disable_pdev: 509362306a36Sopenharmony_ci pci_disable_device(pdev); 509462306a36Sopenharmony_ci return -ENODEV; 509562306a36Sopenharmony_ci} 509662306a36Sopenharmony_ci 509762306a36Sopenharmony_cistatic void cas_remove_one(struct pci_dev *pdev) 509862306a36Sopenharmony_ci{ 509962306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 510062306a36Sopenharmony_ci struct cas *cp; 510162306a36Sopenharmony_ci if (!dev) 510262306a36Sopenharmony_ci return; 510362306a36Sopenharmony_ci 510462306a36Sopenharmony_ci cp = netdev_priv(dev); 510562306a36Sopenharmony_ci unregister_netdev(dev); 510662306a36Sopenharmony_ci 510762306a36Sopenharmony_ci vfree(cp->fw_data); 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 511062306a36Sopenharmony_ci cancel_work_sync(&cp->reset_task); 511162306a36Sopenharmony_ci if (cp->hw_running) 511262306a36Sopenharmony_ci cas_shutdown(cp); 511362306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 511462306a36Sopenharmony_ci 511562306a36Sopenharmony_ci#if 1 511662306a36Sopenharmony_ci if (cp->orig_cacheline_size) { 511762306a36Sopenharmony_ci /* Restore the cache line size if we had modified 511862306a36Sopenharmony_ci * it. 511962306a36Sopenharmony_ci */ 512062306a36Sopenharmony_ci pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 512162306a36Sopenharmony_ci cp->orig_cacheline_size); 512262306a36Sopenharmony_ci } 512362306a36Sopenharmony_ci#endif 512462306a36Sopenharmony_ci dma_free_coherent(&pdev->dev, sizeof(struct cas_init_block), 512562306a36Sopenharmony_ci cp->init_block, cp->block_dvma); 512662306a36Sopenharmony_ci pci_iounmap(pdev, cp->regs); 512762306a36Sopenharmony_ci free_netdev(dev); 512862306a36Sopenharmony_ci pci_release_regions(pdev); 512962306a36Sopenharmony_ci pci_disable_device(pdev); 513062306a36Sopenharmony_ci} 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_cistatic int __maybe_unused cas_suspend(struct device *dev_d) 513362306a36Sopenharmony_ci{ 513462306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 513562306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 513662306a36Sopenharmony_ci unsigned long flags; 513762306a36Sopenharmony_ci 513862306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 513962306a36Sopenharmony_ci 514062306a36Sopenharmony_ci /* If the driver is opened, we stop the DMA */ 514162306a36Sopenharmony_ci if (cp->opened) { 514262306a36Sopenharmony_ci netif_device_detach(dev); 514362306a36Sopenharmony_ci 514462306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 514562306a36Sopenharmony_ci 514662306a36Sopenharmony_ci /* We can set the second arg of cas_reset to 0 514762306a36Sopenharmony_ci * because on resume, we'll call cas_init_hw with 514862306a36Sopenharmony_ci * its second arg set so that autonegotiation is 514962306a36Sopenharmony_ci * restarted. 515062306a36Sopenharmony_ci */ 515162306a36Sopenharmony_ci cas_reset(cp, 0); 515262306a36Sopenharmony_ci cas_clean_rings(cp); 515362306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 515462306a36Sopenharmony_ci } 515562306a36Sopenharmony_ci 515662306a36Sopenharmony_ci if (cp->hw_running) 515762306a36Sopenharmony_ci cas_shutdown(cp); 515862306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 515962306a36Sopenharmony_ci 516062306a36Sopenharmony_ci return 0; 516162306a36Sopenharmony_ci} 516262306a36Sopenharmony_ci 516362306a36Sopenharmony_cistatic int __maybe_unused cas_resume(struct device *dev_d) 516462306a36Sopenharmony_ci{ 516562306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(dev_d); 516662306a36Sopenharmony_ci struct cas *cp = netdev_priv(dev); 516762306a36Sopenharmony_ci 516862306a36Sopenharmony_ci netdev_info(dev, "resuming\n"); 516962306a36Sopenharmony_ci 517062306a36Sopenharmony_ci mutex_lock(&cp->pm_mutex); 517162306a36Sopenharmony_ci cas_hard_reset(cp); 517262306a36Sopenharmony_ci if (cp->opened) { 517362306a36Sopenharmony_ci unsigned long flags; 517462306a36Sopenharmony_ci cas_lock_all_save(cp, flags); 517562306a36Sopenharmony_ci cas_reset(cp, 0); 517662306a36Sopenharmony_ci cp->hw_running = 1; 517762306a36Sopenharmony_ci cas_clean_rings(cp); 517862306a36Sopenharmony_ci cas_init_hw(cp, 1); 517962306a36Sopenharmony_ci cas_unlock_all_restore(cp, flags); 518062306a36Sopenharmony_ci 518162306a36Sopenharmony_ci netif_device_attach(dev); 518262306a36Sopenharmony_ci } 518362306a36Sopenharmony_ci mutex_unlock(&cp->pm_mutex); 518462306a36Sopenharmony_ci return 0; 518562306a36Sopenharmony_ci} 518662306a36Sopenharmony_ci 518762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(cas_pm_ops, cas_suspend, cas_resume); 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_cistatic struct pci_driver cas_driver = { 519062306a36Sopenharmony_ci .name = DRV_MODULE_NAME, 519162306a36Sopenharmony_ci .id_table = cas_pci_tbl, 519262306a36Sopenharmony_ci .probe = cas_init_one, 519362306a36Sopenharmony_ci .remove = cas_remove_one, 519462306a36Sopenharmony_ci .driver.pm = &cas_pm_ops, 519562306a36Sopenharmony_ci}; 519662306a36Sopenharmony_ci 519762306a36Sopenharmony_cistatic int __init cas_init(void) 519862306a36Sopenharmony_ci{ 519962306a36Sopenharmony_ci if (linkdown_timeout > 0) 520062306a36Sopenharmony_ci link_transition_timeout = linkdown_timeout * HZ; 520162306a36Sopenharmony_ci else 520262306a36Sopenharmony_ci link_transition_timeout = 0; 520362306a36Sopenharmony_ci 520462306a36Sopenharmony_ci return pci_register_driver(&cas_driver); 520562306a36Sopenharmony_ci} 520662306a36Sopenharmony_ci 520762306a36Sopenharmony_cistatic void __exit cas_cleanup(void) 520862306a36Sopenharmony_ci{ 520962306a36Sopenharmony_ci pci_unregister_driver(&cas_driver); 521062306a36Sopenharmony_ci} 521162306a36Sopenharmony_ci 521262306a36Sopenharmony_cimodule_init(cas_init); 521362306a36Sopenharmony_cimodule_exit(cas_cleanup); 5214