162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Lance ethernet driver for the MIPS processor based 462306a36Sopenharmony_ci * DECstation family 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * adopted from sunlance.c by Richard van den Berg 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (C) 2002, 2003, 2005, 2006 Maciej W. Rozycki 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * additional sources: 1262306a36Sopenharmony_ci * - PMAD-AA TURBOchannel Ethernet Module Functional Specification, 1362306a36Sopenharmony_ci * Revision 1.2 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * History: 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * v0.001: The kernel accepts the code and it shows the hardware address. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * v0.002: Removed most sparc stuff, left only some module and dma stuff. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * v0.003: Enhanced base address calculation from proposals by 2262306a36Sopenharmony_ci * Harald Koerfgen and Thomas Riemer. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * v0.004: lance-regs is pointing at the right addresses, added prom 2562306a36Sopenharmony_ci * check. First start of address mapping and DMA. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * v0.005: started to play around with LANCE-DMA. This driver will not 2862306a36Sopenharmony_ci * work for non IOASIC lances. HK 2962306a36Sopenharmony_ci * 3062306a36Sopenharmony_ci * v0.006: added pointer arrays to lance_private and setup routine for 3162306a36Sopenharmony_ci * them in dec_lance_init. HK 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * v0.007: Big shit. The LANCE seems to use a different DMA mechanism to 3462306a36Sopenharmony_ci * access the init block. This looks like one (short) word at a 3562306a36Sopenharmony_ci * time, but the smallest amount the IOASIC can transfer is a 3662306a36Sopenharmony_ci * (long) word. So we have a 2-2 padding here. Changed 3762306a36Sopenharmony_ci * lance_init_block accordingly. The 16-16 padding for the buffers 3862306a36Sopenharmony_ci * seems to be correct. HK 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * v0.008: mods to make PMAX_LANCE work. 01/09/1999 triemer 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * v0.009: Module support fixes, multiple interfaces support, various 4362306a36Sopenharmony_ci * bits. macro 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * v0.010: Fixes for the PMAD mapping of the LANCE buffer and for the 4662306a36Sopenharmony_ci * PMAX requirement to only use halfword accesses to the 4762306a36Sopenharmony_ci * buffer. macro 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * v0.011: Converted the PMAD to the driver model. macro 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include <linux/crc32.h> 5362306a36Sopenharmony_ci#include <linux/delay.h> 5462306a36Sopenharmony_ci#include <linux/errno.h> 5562306a36Sopenharmony_ci#include <linux/if_ether.h> 5662306a36Sopenharmony_ci#include <linux/init.h> 5762306a36Sopenharmony_ci#include <linux/kernel.h> 5862306a36Sopenharmony_ci#include <linux/module.h> 5962306a36Sopenharmony_ci#include <linux/netdevice.h> 6062306a36Sopenharmony_ci#include <linux/etherdevice.h> 6162306a36Sopenharmony_ci#include <linux/spinlock.h> 6262306a36Sopenharmony_ci#include <linux/stddef.h> 6362306a36Sopenharmony_ci#include <linux/string.h> 6462306a36Sopenharmony_ci#include <linux/tc.h> 6562306a36Sopenharmony_ci#include <linux/types.h> 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#include <asm/addrspace.h> 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#include <asm/dec/interrupts.h> 7062306a36Sopenharmony_ci#include <asm/dec/ioasic.h> 7162306a36Sopenharmony_ci#include <asm/dec/ioasic_addrs.h> 7262306a36Sopenharmony_ci#include <asm/dec/kn01.h> 7362306a36Sopenharmony_ci#include <asm/dec/machtype.h> 7462306a36Sopenharmony_ci#include <asm/dec/system.h> 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic const char version[] = 7762306a36Sopenharmony_ci"declance.c: v0.011 by Linux MIPS DECstation task force\n"; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ciMODULE_AUTHOR("Linux MIPS DECstation task force"); 8062306a36Sopenharmony_ciMODULE_DESCRIPTION("DEC LANCE (DECstation onboard, PMAD-xx) driver"); 8162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define __unused __attribute__ ((unused)) 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci/* 8662306a36Sopenharmony_ci * card types 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci#define ASIC_LANCE 1 8962306a36Sopenharmony_ci#define PMAD_LANCE 2 9062306a36Sopenharmony_ci#define PMAX_LANCE 3 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define LE_CSR0 0 9462306a36Sopenharmony_ci#define LE_CSR1 1 9562306a36Sopenharmony_ci#define LE_CSR2 2 9662306a36Sopenharmony_ci#define LE_CSR3 3 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */ 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */ 10162306a36Sopenharmony_ci#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */ 10262306a36Sopenharmony_ci#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */ 10362306a36Sopenharmony_ci#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */ 10462306a36Sopenharmony_ci#define LE_C0_MERR 0x0800 /* ME: Memory error */ 10562306a36Sopenharmony_ci#define LE_C0_RINT 0x0400 /* Received interrupt */ 10662306a36Sopenharmony_ci#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */ 10762306a36Sopenharmony_ci#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */ 10862306a36Sopenharmony_ci#define LE_C0_INTR 0x0080 /* Interrupt or error */ 10962306a36Sopenharmony_ci#define LE_C0_INEA 0x0040 /* Interrupt enable */ 11062306a36Sopenharmony_ci#define LE_C0_RXON 0x0020 /* Receiver on */ 11162306a36Sopenharmony_ci#define LE_C0_TXON 0x0010 /* Transmitter on */ 11262306a36Sopenharmony_ci#define LE_C0_TDMD 0x0008 /* Transmitter demand */ 11362306a36Sopenharmony_ci#define LE_C0_STOP 0x0004 /* Stop the card */ 11462306a36Sopenharmony_ci#define LE_C0_STRT 0x0002 /* Start the card */ 11562306a36Sopenharmony_ci#define LE_C0_INIT 0x0001 /* Init the card */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#define LE_C3_BSWP 0x4 /* SWAP */ 11862306a36Sopenharmony_ci#define LE_C3_ACON 0x2 /* ALE Control */ 11962306a36Sopenharmony_ci#define LE_C3_BCON 0x1 /* Byte control */ 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* Receive message descriptor 1 */ 12262306a36Sopenharmony_ci#define LE_R1_OWN 0x8000 /* Who owns the entry */ 12362306a36Sopenharmony_ci#define LE_R1_ERR 0x4000 /* Error: if FRA, OFL, CRC or BUF is set */ 12462306a36Sopenharmony_ci#define LE_R1_FRA 0x2000 /* FRA: Frame error */ 12562306a36Sopenharmony_ci#define LE_R1_OFL 0x1000 /* OFL: Frame overflow */ 12662306a36Sopenharmony_ci#define LE_R1_CRC 0x0800 /* CRC error */ 12762306a36Sopenharmony_ci#define LE_R1_BUF 0x0400 /* BUF: Buffer error */ 12862306a36Sopenharmony_ci#define LE_R1_SOP 0x0200 /* Start of packet */ 12962306a36Sopenharmony_ci#define LE_R1_EOP 0x0100 /* End of packet */ 13062306a36Sopenharmony_ci#define LE_R1_POK 0x0300 /* Packet is complete: SOP + EOP */ 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* Transmit message descriptor 1 */ 13362306a36Sopenharmony_ci#define LE_T1_OWN 0x8000 /* Lance owns the packet */ 13462306a36Sopenharmony_ci#define LE_T1_ERR 0x4000 /* Error summary */ 13562306a36Sopenharmony_ci#define LE_T1_EMORE 0x1000 /* Error: more than one retry needed */ 13662306a36Sopenharmony_ci#define LE_T1_EONE 0x0800 /* Error: one retry needed */ 13762306a36Sopenharmony_ci#define LE_T1_EDEF 0x0400 /* Error: deferred */ 13862306a36Sopenharmony_ci#define LE_T1_SOP 0x0200 /* Start of packet */ 13962306a36Sopenharmony_ci#define LE_T1_EOP 0x0100 /* End of packet */ 14062306a36Sopenharmony_ci#define LE_T1_POK 0x0300 /* Packet is complete: SOP + EOP */ 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#define LE_T3_BUF 0x8000 /* Buffer error */ 14362306a36Sopenharmony_ci#define LE_T3_UFL 0x4000 /* Error underflow */ 14462306a36Sopenharmony_ci#define LE_T3_LCOL 0x1000 /* Error late collision */ 14562306a36Sopenharmony_ci#define LE_T3_CLOS 0x0800 /* Error carrier loss */ 14662306a36Sopenharmony_ci#define LE_T3_RTY 0x0400 /* Error retry */ 14762306a36Sopenharmony_ci#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Define: 2^4 Tx buffers and 2^4 Rx buffers */ 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci#ifndef LANCE_LOG_TX_BUFFERS 15262306a36Sopenharmony_ci#define LANCE_LOG_TX_BUFFERS 4 15362306a36Sopenharmony_ci#define LANCE_LOG_RX_BUFFERS 4 15462306a36Sopenharmony_ci#endif 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) 15762306a36Sopenharmony_ci#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) 16062306a36Sopenharmony_ci#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci#define PKT_BUF_SZ 1536 16362306a36Sopenharmony_ci#define RX_BUFF_SIZE PKT_BUF_SZ 16462306a36Sopenharmony_ci#define TX_BUFF_SIZE PKT_BUF_SZ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci#undef TEST_HITS 16762306a36Sopenharmony_ci#define ZERO 0 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* 17062306a36Sopenharmony_ci * The DS2100/3100 have a linear 64 kB buffer which supports halfword 17162306a36Sopenharmony_ci * accesses only. Each halfword of the buffer is word-aligned in the 17262306a36Sopenharmony_ci * CPU address space. 17362306a36Sopenharmony_ci * 17462306a36Sopenharmony_ci * The PMAD-AA has a 128 kB buffer on-board. 17562306a36Sopenharmony_ci * 17662306a36Sopenharmony_ci * The IOASIC LANCE devices use a shared memory region. This region 17762306a36Sopenharmony_ci * as seen from the CPU is (max) 128 kB long and has to be on an 128 kB 17862306a36Sopenharmony_ci * boundary. The LANCE sees this as a 64 kB long continuous memory 17962306a36Sopenharmony_ci * region. 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * The LANCE's DMA address is used as an index in this buffer and DMA 18262306a36Sopenharmony_ci * takes place in bursts of eight 16-bit words which are packed into 18362306a36Sopenharmony_ci * four 32-bit words by the IOASIC. This leads to a strange padding: 18462306a36Sopenharmony_ci * 16 bytes of valid data followed by a 16 byte gap :-(. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistruct lance_rx_desc { 18862306a36Sopenharmony_ci unsigned short rmd0; /* low address of packet */ 18962306a36Sopenharmony_ci unsigned short rmd1; /* high address of packet 19062306a36Sopenharmony_ci and descriptor bits */ 19162306a36Sopenharmony_ci short length; /* 2s complement (negative!) 19262306a36Sopenharmony_ci of buffer length */ 19362306a36Sopenharmony_ci unsigned short mblength; /* actual number of bytes received */ 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct lance_tx_desc { 19762306a36Sopenharmony_ci unsigned short tmd0; /* low address of packet */ 19862306a36Sopenharmony_ci unsigned short tmd1; /* high address of packet 19962306a36Sopenharmony_ci and descriptor bits */ 20062306a36Sopenharmony_ci short length; /* 2s complement (negative!) 20162306a36Sopenharmony_ci of buffer length */ 20262306a36Sopenharmony_ci unsigned short misc; 20362306a36Sopenharmony_ci}; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* First part of the LANCE initialization block, described in databook. */ 20762306a36Sopenharmony_cistruct lance_init_block { 20862306a36Sopenharmony_ci unsigned short mode; /* pre-set mode (reg. 15) */ 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci unsigned short phys_addr[3]; /* physical ethernet address */ 21162306a36Sopenharmony_ci unsigned short filter[4]; /* multicast filter */ 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Receive and transmit ring base, along with extra bits. */ 21462306a36Sopenharmony_ci unsigned short rx_ptr; /* receive descriptor addr */ 21562306a36Sopenharmony_ci unsigned short rx_len; /* receive len and high addr */ 21662306a36Sopenharmony_ci unsigned short tx_ptr; /* transmit descriptor addr */ 21762306a36Sopenharmony_ci unsigned short tx_len; /* transmit len and high addr */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci short gap[4]; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* The buffer descriptors */ 22262306a36Sopenharmony_ci struct lance_rx_desc brx_ring[RX_RING_SIZE]; 22362306a36Sopenharmony_ci struct lance_tx_desc btx_ring[TX_RING_SIZE]; 22462306a36Sopenharmony_ci}; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci#define BUF_OFFSET_CPU sizeof(struct lance_init_block) 22762306a36Sopenharmony_ci#define BUF_OFFSET_LNC sizeof(struct lance_init_block) 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#define shift_off(off, type) \ 23062306a36Sopenharmony_ci (type == ASIC_LANCE || type == PMAX_LANCE ? off << 1 : off) 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci#define lib_off(rt, type) \ 23362306a36Sopenharmony_ci shift_off(offsetof(struct lance_init_block, rt), type) 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci#define lib_ptr(ib, rt, type) \ 23662306a36Sopenharmony_ci ((volatile u16 *)((u8 *)(ib) + lib_off(rt, type))) 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci#define rds_off(rt, type) \ 23962306a36Sopenharmony_ci shift_off(offsetof(struct lance_rx_desc, rt), type) 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci#define rds_ptr(rd, rt, type) \ 24262306a36Sopenharmony_ci ((volatile u16 *)((u8 *)(rd) + rds_off(rt, type))) 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci#define tds_off(rt, type) \ 24562306a36Sopenharmony_ci shift_off(offsetof(struct lance_tx_desc, rt), type) 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci#define tds_ptr(td, rt, type) \ 24862306a36Sopenharmony_ci ((volatile u16 *)((u8 *)(td) + tds_off(rt, type))) 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistruct lance_private { 25162306a36Sopenharmony_ci struct net_device *next; 25262306a36Sopenharmony_ci int type; 25362306a36Sopenharmony_ci int dma_irq; 25462306a36Sopenharmony_ci volatile struct lance_regs *ll; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci spinlock_t lock; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci int rx_new, tx_new; 25962306a36Sopenharmony_ci int rx_old, tx_old; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci unsigned short busmaster_regval; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci struct timer_list multicast_timer; 26462306a36Sopenharmony_ci struct net_device *dev; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Pointers to the ring buffers as seen from the CPU */ 26762306a36Sopenharmony_ci char *rx_buf_ptr_cpu[RX_RING_SIZE]; 26862306a36Sopenharmony_ci char *tx_buf_ptr_cpu[TX_RING_SIZE]; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* Pointers to the ring buffers as seen from the LANCE */ 27162306a36Sopenharmony_ci uint rx_buf_ptr_lnc[RX_RING_SIZE]; 27262306a36Sopenharmony_ci uint tx_buf_ptr_lnc[TX_RING_SIZE]; 27362306a36Sopenharmony_ci}; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ 27662306a36Sopenharmony_ci lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\ 27762306a36Sopenharmony_ci lp->tx_old - lp->tx_new-1) 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci/* The lance control ports are at an absolute address, machine and tc-slot 28062306a36Sopenharmony_ci * dependent. 28162306a36Sopenharmony_ci * DECstations do only 32-bit access and the LANCE uses 16 bit addresses, 28262306a36Sopenharmony_ci * so we have to give the structure an extra member making rap pointing 28362306a36Sopenharmony_ci * at the right address 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_cistruct lance_regs { 28662306a36Sopenharmony_ci volatile unsigned short rdp; /* register data port */ 28762306a36Sopenharmony_ci unsigned short pad; 28862306a36Sopenharmony_ci volatile unsigned short rap; /* register address port */ 28962306a36Sopenharmony_ci}; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ciint dec_lance_debug = 2; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver; 29462306a36Sopenharmony_cistatic struct net_device *root_lance_dev; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic inline void writereg(volatile unsigned short *regptr, short value) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci *regptr = value; 29962306a36Sopenharmony_ci iob(); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci/* Load the CSR registers */ 30362306a36Sopenharmony_cistatic void load_csrs(struct lance_private *lp) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 30662306a36Sopenharmony_ci uint leptr; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* The address space as seen from the LANCE 30962306a36Sopenharmony_ci * begins at address 0. HK 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci leptr = 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR1); 31462306a36Sopenharmony_ci writereg(&ll->rdp, (leptr & 0xFFFF)); 31562306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR2); 31662306a36Sopenharmony_ci writereg(&ll->rdp, leptr >> 16); 31762306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR3); 31862306a36Sopenharmony_ci writereg(&ll->rdp, lp->busmaster_regval); 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Point back to csr0 */ 32162306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/* 32562306a36Sopenharmony_ci * Our specialized copy routines 32662306a36Sopenharmony_ci * 32762306a36Sopenharmony_ci */ 32862306a36Sopenharmony_cistatic void cp_to_buf(const int type, void *to, const void *from, int len) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci unsigned short *tp; 33162306a36Sopenharmony_ci const unsigned short *fp; 33262306a36Sopenharmony_ci unsigned short clen; 33362306a36Sopenharmony_ci unsigned char *rtp; 33462306a36Sopenharmony_ci const unsigned char *rfp; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (type == PMAD_LANCE) { 33762306a36Sopenharmony_ci memcpy(to, from, len); 33862306a36Sopenharmony_ci } else if (type == PMAX_LANCE) { 33962306a36Sopenharmony_ci clen = len >> 1; 34062306a36Sopenharmony_ci tp = to; 34162306a36Sopenharmony_ci fp = from; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci while (clen--) { 34462306a36Sopenharmony_ci *tp++ = *fp++; 34562306a36Sopenharmony_ci tp++; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci clen = len & 1; 34962306a36Sopenharmony_ci rtp = (unsigned char *)tp; 35062306a36Sopenharmony_ci rfp = (const unsigned char *)fp; 35162306a36Sopenharmony_ci while (clen--) { 35262306a36Sopenharmony_ci *rtp++ = *rfp++; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci } else { 35562306a36Sopenharmony_ci /* 35662306a36Sopenharmony_ci * copy 16 Byte chunks 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci clen = len >> 4; 35962306a36Sopenharmony_ci tp = to; 36062306a36Sopenharmony_ci fp = from; 36162306a36Sopenharmony_ci while (clen--) { 36262306a36Sopenharmony_ci *tp++ = *fp++; 36362306a36Sopenharmony_ci *tp++ = *fp++; 36462306a36Sopenharmony_ci *tp++ = *fp++; 36562306a36Sopenharmony_ci *tp++ = *fp++; 36662306a36Sopenharmony_ci *tp++ = *fp++; 36762306a36Sopenharmony_ci *tp++ = *fp++; 36862306a36Sopenharmony_ci *tp++ = *fp++; 36962306a36Sopenharmony_ci *tp++ = *fp++; 37062306a36Sopenharmony_ci tp += 8; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* 37462306a36Sopenharmony_ci * do the rest, if any. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_ci clen = len & 15; 37762306a36Sopenharmony_ci rtp = (unsigned char *)tp; 37862306a36Sopenharmony_ci rfp = (const unsigned char *)fp; 37962306a36Sopenharmony_ci while (clen--) { 38062306a36Sopenharmony_ci *rtp++ = *rfp++; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci iob(); 38562306a36Sopenharmony_ci} 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_cistatic void cp_from_buf(const int type, void *to, const void *from, int len) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci unsigned short *tp; 39062306a36Sopenharmony_ci const unsigned short *fp; 39162306a36Sopenharmony_ci unsigned short clen; 39262306a36Sopenharmony_ci unsigned char *rtp; 39362306a36Sopenharmony_ci const unsigned char *rfp; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (type == PMAD_LANCE) { 39662306a36Sopenharmony_ci memcpy(to, from, len); 39762306a36Sopenharmony_ci } else if (type == PMAX_LANCE) { 39862306a36Sopenharmony_ci clen = len >> 1; 39962306a36Sopenharmony_ci tp = to; 40062306a36Sopenharmony_ci fp = from; 40162306a36Sopenharmony_ci while (clen--) { 40262306a36Sopenharmony_ci *tp++ = *fp++; 40362306a36Sopenharmony_ci fp++; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci clen = len & 1; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci rtp = (unsigned char *)tp; 40962306a36Sopenharmony_ci rfp = (const unsigned char *)fp; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci while (clen--) { 41262306a36Sopenharmony_ci *rtp++ = *rfp++; 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * copy 16 Byte chunks 41862306a36Sopenharmony_ci */ 41962306a36Sopenharmony_ci clen = len >> 4; 42062306a36Sopenharmony_ci tp = to; 42162306a36Sopenharmony_ci fp = from; 42262306a36Sopenharmony_ci while (clen--) { 42362306a36Sopenharmony_ci *tp++ = *fp++; 42462306a36Sopenharmony_ci *tp++ = *fp++; 42562306a36Sopenharmony_ci *tp++ = *fp++; 42662306a36Sopenharmony_ci *tp++ = *fp++; 42762306a36Sopenharmony_ci *tp++ = *fp++; 42862306a36Sopenharmony_ci *tp++ = *fp++; 42962306a36Sopenharmony_ci *tp++ = *fp++; 43062306a36Sopenharmony_ci *tp++ = *fp++; 43162306a36Sopenharmony_ci fp += 8; 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* 43562306a36Sopenharmony_ci * do the rest, if any. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_ci clen = len & 15; 43862306a36Sopenharmony_ci rtp = (unsigned char *)tp; 43962306a36Sopenharmony_ci rfp = (const unsigned char *)fp; 44062306a36Sopenharmony_ci while (clen--) { 44162306a36Sopenharmony_ci *rtp++ = *rfp++; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* Setup the Lance Rx and Tx rings */ 45062306a36Sopenharmony_cistatic void lance_init_ring(struct net_device *dev) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 45362306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 45462306a36Sopenharmony_ci uint leptr; 45562306a36Sopenharmony_ci int i; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Lock out other processes while setting up hardware */ 45862306a36Sopenharmony_ci netif_stop_queue(dev); 45962306a36Sopenharmony_ci lp->rx_new = lp->tx_new = 0; 46062306a36Sopenharmony_ci lp->rx_old = lp->tx_old = 0; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* Copy the ethernet address to the lance init block. 46362306a36Sopenharmony_ci * XXX bit 0 of the physical address registers has to be zero 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci *lib_ptr(ib, phys_addr[0], lp->type) = (dev->dev_addr[1] << 8) | 46662306a36Sopenharmony_ci dev->dev_addr[0]; 46762306a36Sopenharmony_ci *lib_ptr(ib, phys_addr[1], lp->type) = (dev->dev_addr[3] << 8) | 46862306a36Sopenharmony_ci dev->dev_addr[2]; 46962306a36Sopenharmony_ci *lib_ptr(ib, phys_addr[2], lp->type) = (dev->dev_addr[5] << 8) | 47062306a36Sopenharmony_ci dev->dev_addr[4]; 47162306a36Sopenharmony_ci /* Setup the initialization block */ 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Setup rx descriptor pointer */ 47462306a36Sopenharmony_ci leptr = offsetof(struct lance_init_block, brx_ring); 47562306a36Sopenharmony_ci *lib_ptr(ib, rx_len, lp->type) = (LANCE_LOG_RX_BUFFERS << 13) | 47662306a36Sopenharmony_ci (leptr >> 16); 47762306a36Sopenharmony_ci *lib_ptr(ib, rx_ptr, lp->type) = leptr; 47862306a36Sopenharmony_ci if (ZERO) 47962306a36Sopenharmony_ci printk("RX ptr: %8.8x(%8.8x)\n", 48062306a36Sopenharmony_ci leptr, (uint)lib_off(brx_ring, lp->type)); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Setup tx descriptor pointer */ 48362306a36Sopenharmony_ci leptr = offsetof(struct lance_init_block, btx_ring); 48462306a36Sopenharmony_ci *lib_ptr(ib, tx_len, lp->type) = (LANCE_LOG_TX_BUFFERS << 13) | 48562306a36Sopenharmony_ci (leptr >> 16); 48662306a36Sopenharmony_ci *lib_ptr(ib, tx_ptr, lp->type) = leptr; 48762306a36Sopenharmony_ci if (ZERO) 48862306a36Sopenharmony_ci printk("TX ptr: %8.8x(%8.8x)\n", 48962306a36Sopenharmony_ci leptr, (uint)lib_off(btx_ring, lp->type)); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci if (ZERO) 49262306a36Sopenharmony_ci printk("TX rings:\n"); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Setup the Tx ring entries */ 49562306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 49662306a36Sopenharmony_ci leptr = lp->tx_buf_ptr_lnc[i]; 49762306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[i].tmd0, lp->type) = leptr; 49862306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[i].tmd1, lp->type) = (leptr >> 16) & 49962306a36Sopenharmony_ci 0xff; 50062306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[i].length, lp->type) = 0xf000; 50162306a36Sopenharmony_ci /* The ones required by tmd2 */ 50262306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[i].misc, lp->type) = 0; 50362306a36Sopenharmony_ci if (i < 3 && ZERO) 50462306a36Sopenharmony_ci printk("%d: %8.8x(%p)\n", 50562306a36Sopenharmony_ci i, leptr, lp->tx_buf_ptr_cpu[i]); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Setup the Rx ring entries */ 50962306a36Sopenharmony_ci if (ZERO) 51062306a36Sopenharmony_ci printk("RX rings:\n"); 51162306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 51262306a36Sopenharmony_ci leptr = lp->rx_buf_ptr_lnc[i]; 51362306a36Sopenharmony_ci *lib_ptr(ib, brx_ring[i].rmd0, lp->type) = leptr; 51462306a36Sopenharmony_ci *lib_ptr(ib, brx_ring[i].rmd1, lp->type) = ((leptr >> 16) & 51562306a36Sopenharmony_ci 0xff) | 51662306a36Sopenharmony_ci LE_R1_OWN; 51762306a36Sopenharmony_ci *lib_ptr(ib, brx_ring[i].length, lp->type) = -RX_BUFF_SIZE | 51862306a36Sopenharmony_ci 0xf000; 51962306a36Sopenharmony_ci *lib_ptr(ib, brx_ring[i].mblength, lp->type) = 0; 52062306a36Sopenharmony_ci if (i < 3 && ZERO) 52162306a36Sopenharmony_ci printk("%d: %8.8x(%p)\n", 52262306a36Sopenharmony_ci i, leptr, lp->rx_buf_ptr_cpu[i]); 52362306a36Sopenharmony_ci } 52462306a36Sopenharmony_ci iob(); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic int init_restart_lance(struct lance_private *lp) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 53062306a36Sopenharmony_ci int i; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 53362306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_INIT); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Wait for the lance to complete initialization */ 53662306a36Sopenharmony_ci for (i = 0; (i < 100) && !(ll->rdp & LE_C0_IDON); i++) { 53762306a36Sopenharmony_ci udelay(10); 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if ((i == 100) || (ll->rdp & LE_C0_ERR)) { 54062306a36Sopenharmony_ci printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", 54162306a36Sopenharmony_ci i, ll->rdp); 54262306a36Sopenharmony_ci return -1; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci if ((ll->rdp & LE_C0_ERR)) { 54562306a36Sopenharmony_ci printk("LANCE unopened after %d ticks, csr0=%4.4x.\n", 54662306a36Sopenharmony_ci i, ll->rdp); 54762306a36Sopenharmony_ci return -1; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_IDON); 55062306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STRT); 55162306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_INEA); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic int lance_rx(struct net_device *dev) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 55962306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 56062306a36Sopenharmony_ci volatile u16 *rd; 56162306a36Sopenharmony_ci unsigned short bits; 56262306a36Sopenharmony_ci int entry, len; 56362306a36Sopenharmony_ci struct sk_buff *skb; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci#ifdef TEST_HITS 56662306a36Sopenharmony_ci { 56762306a36Sopenharmony_ci int i; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci printk("["); 57062306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 57162306a36Sopenharmony_ci if (i == lp->rx_new) 57262306a36Sopenharmony_ci printk("%s", *lib_ptr(ib, brx_ring[i].rmd1, 57362306a36Sopenharmony_ci lp->type) & 57462306a36Sopenharmony_ci LE_R1_OWN ? "_" : "X"); 57562306a36Sopenharmony_ci else 57662306a36Sopenharmony_ci printk("%s", *lib_ptr(ib, brx_ring[i].rmd1, 57762306a36Sopenharmony_ci lp->type) & 57862306a36Sopenharmony_ci LE_R1_OWN ? "." : "1"); 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci printk("]"); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci#endif 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci for (rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type); 58562306a36Sopenharmony_ci !((bits = *rds_ptr(rd, rmd1, lp->type)) & LE_R1_OWN); 58662306a36Sopenharmony_ci rd = lib_ptr(ib, brx_ring[lp->rx_new], lp->type)) { 58762306a36Sopenharmony_ci entry = lp->rx_new; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* We got an incomplete frame? */ 59062306a36Sopenharmony_ci if ((bits & LE_R1_POK) != LE_R1_POK) { 59162306a36Sopenharmony_ci dev->stats.rx_over_errors++; 59262306a36Sopenharmony_ci dev->stats.rx_errors++; 59362306a36Sopenharmony_ci } else if (bits & LE_R1_ERR) { 59462306a36Sopenharmony_ci /* Count only the end frame as a rx error, 59562306a36Sopenharmony_ci * not the beginning 59662306a36Sopenharmony_ci */ 59762306a36Sopenharmony_ci if (bits & LE_R1_BUF) 59862306a36Sopenharmony_ci dev->stats.rx_fifo_errors++; 59962306a36Sopenharmony_ci if (bits & LE_R1_CRC) 60062306a36Sopenharmony_ci dev->stats.rx_crc_errors++; 60162306a36Sopenharmony_ci if (bits & LE_R1_OFL) 60262306a36Sopenharmony_ci dev->stats.rx_over_errors++; 60362306a36Sopenharmony_ci if (bits & LE_R1_FRA) 60462306a36Sopenharmony_ci dev->stats.rx_frame_errors++; 60562306a36Sopenharmony_ci if (bits & LE_R1_EOP) 60662306a36Sopenharmony_ci dev->stats.rx_errors++; 60762306a36Sopenharmony_ci } else { 60862306a36Sopenharmony_ci len = (*rds_ptr(rd, mblength, lp->type) & 0xfff) - 4; 60962306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len + 2); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (!skb) { 61262306a36Sopenharmony_ci dev->stats.rx_dropped++; 61362306a36Sopenharmony_ci *rds_ptr(rd, mblength, lp->type) = 0; 61462306a36Sopenharmony_ci *rds_ptr(rd, rmd1, lp->type) = 61562306a36Sopenharmony_ci ((lp->rx_buf_ptr_lnc[entry] >> 16) & 61662306a36Sopenharmony_ci 0xff) | LE_R1_OWN; 61762306a36Sopenharmony_ci lp->rx_new = (entry + 1) & RX_RING_MOD_MASK; 61862306a36Sopenharmony_ci return 0; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci dev->stats.rx_bytes += len; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align */ 62362306a36Sopenharmony_ci skb_put(skb, len); /* make room */ 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci cp_from_buf(lp->type, skb->data, 62662306a36Sopenharmony_ci lp->rx_buf_ptr_cpu[entry], len); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 62962306a36Sopenharmony_ci netif_rx(skb); 63062306a36Sopenharmony_ci dev->stats.rx_packets++; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Return the packet to the pool */ 63462306a36Sopenharmony_ci *rds_ptr(rd, mblength, lp->type) = 0; 63562306a36Sopenharmony_ci *rds_ptr(rd, length, lp->type) = -RX_BUFF_SIZE | 0xf000; 63662306a36Sopenharmony_ci *rds_ptr(rd, rmd1, lp->type) = 63762306a36Sopenharmony_ci ((lp->rx_buf_ptr_lnc[entry] >> 16) & 0xff) | LE_R1_OWN; 63862306a36Sopenharmony_ci lp->rx_new = (entry + 1) & RX_RING_MOD_MASK; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci return 0; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void lance_tx(struct net_device *dev) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 64662306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 64762306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 64862306a36Sopenharmony_ci volatile u16 *td; 64962306a36Sopenharmony_ci int i, j; 65062306a36Sopenharmony_ci int status; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci j = lp->tx_old; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci spin_lock(&lp->lock); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci for (i = j; i != lp->tx_new; i = j) { 65762306a36Sopenharmony_ci td = lib_ptr(ib, btx_ring[i], lp->type); 65862306a36Sopenharmony_ci /* If we hit a packet not owned by us, stop */ 65962306a36Sopenharmony_ci if (*tds_ptr(td, tmd1, lp->type) & LE_T1_OWN) 66062306a36Sopenharmony_ci break; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci if (*tds_ptr(td, tmd1, lp->type) & LE_T1_ERR) { 66362306a36Sopenharmony_ci status = *tds_ptr(td, misc, lp->type); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci dev->stats.tx_errors++; 66662306a36Sopenharmony_ci if (status & LE_T3_RTY) 66762306a36Sopenharmony_ci dev->stats.tx_aborted_errors++; 66862306a36Sopenharmony_ci if (status & LE_T3_LCOL) 66962306a36Sopenharmony_ci dev->stats.tx_window_errors++; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (status & LE_T3_CLOS) { 67262306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 67362306a36Sopenharmony_ci printk("%s: Carrier Lost\n", dev->name); 67462306a36Sopenharmony_ci /* Stop the lance */ 67562306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 67662306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 67762306a36Sopenharmony_ci lance_init_ring(dev); 67862306a36Sopenharmony_ci load_csrs(lp); 67962306a36Sopenharmony_ci init_restart_lance(lp); 68062306a36Sopenharmony_ci goto out; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci /* Buffer errors and underflows turn off the 68362306a36Sopenharmony_ci * transmitter, restart the adapter. 68462306a36Sopenharmony_ci */ 68562306a36Sopenharmony_ci if (status & (LE_T3_BUF | LE_T3_UFL)) { 68662306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci printk("%s: Tx: ERR_BUF|ERR_UFL, restarting\n", 68962306a36Sopenharmony_ci dev->name); 69062306a36Sopenharmony_ci /* Stop the lance */ 69162306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 69262306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 69362306a36Sopenharmony_ci lance_init_ring(dev); 69462306a36Sopenharmony_ci load_csrs(lp); 69562306a36Sopenharmony_ci init_restart_lance(lp); 69662306a36Sopenharmony_ci goto out; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci } else if ((*tds_ptr(td, tmd1, lp->type) & LE_T1_POK) == 69962306a36Sopenharmony_ci LE_T1_POK) { 70062306a36Sopenharmony_ci /* 70162306a36Sopenharmony_ci * So we don't count the packet more than once. 70262306a36Sopenharmony_ci */ 70362306a36Sopenharmony_ci *tds_ptr(td, tmd1, lp->type) &= ~(LE_T1_POK); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* One collision before packet was sent. */ 70662306a36Sopenharmony_ci if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EONE) 70762306a36Sopenharmony_ci dev->stats.collisions++; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* More than one collision, be optimistic. */ 71062306a36Sopenharmony_ci if (*tds_ptr(td, tmd1, lp->type) & LE_T1_EMORE) 71162306a36Sopenharmony_ci dev->stats.collisions += 2; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci dev->stats.tx_packets++; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci j = (j + 1) & TX_RING_MOD_MASK; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci lp->tx_old = j; 71862306a36Sopenharmony_ciout: 71962306a36Sopenharmony_ci if (netif_queue_stopped(dev) && 72062306a36Sopenharmony_ci TX_BUFFS_AVAIL > 0) 72162306a36Sopenharmony_ci netif_wake_queue(dev); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci spin_unlock(&lp->lock); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic irqreturn_t lance_dma_merr_int(int irq, void *dev_id) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci struct net_device *dev = dev_id; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci printk(KERN_ERR "%s: DMA error\n", dev->name); 73162306a36Sopenharmony_ci return IRQ_HANDLED; 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic irqreturn_t lance_interrupt(int irq, void *dev_id) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct net_device *dev = dev_id; 73762306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 73862306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 73962306a36Sopenharmony_ci int csr0; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 74262306a36Sopenharmony_ci csr0 = ll->rdp; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* Acknowledge all the interrupt sources ASAP */ 74562306a36Sopenharmony_ci writereg(&ll->rdp, csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT)); 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if ((csr0 & LE_C0_ERR)) { 74862306a36Sopenharmony_ci /* Clear the error condition */ 74962306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | 75062306a36Sopenharmony_ci LE_C0_CERR | LE_C0_MERR); 75162306a36Sopenharmony_ci } 75262306a36Sopenharmony_ci if (csr0 & LE_C0_RINT) 75362306a36Sopenharmony_ci lance_rx(dev); 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci if (csr0 & LE_C0_TINT) 75662306a36Sopenharmony_ci lance_tx(dev); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci if (csr0 & LE_C0_BABL) 75962306a36Sopenharmony_ci dev->stats.tx_errors++; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci if (csr0 & LE_C0_MISS) 76262306a36Sopenharmony_ci dev->stats.rx_errors++; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (csr0 & LE_C0_MERR) { 76562306a36Sopenharmony_ci printk("%s: Memory error, status %04x\n", dev->name, csr0); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci lance_init_ring(dev); 77062306a36Sopenharmony_ci load_csrs(lp); 77162306a36Sopenharmony_ci init_restart_lance(lp); 77262306a36Sopenharmony_ci netif_wake_queue(dev); 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_INEA); 77662306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_INEA); 77762306a36Sopenharmony_ci return IRQ_HANDLED; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int lance_open(struct net_device *dev) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 78362306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 78462306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 78562306a36Sopenharmony_ci int status = 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* Stop the Lance */ 78862306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 78962306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci /* Set mode and clear multicast filter only at device open, 79262306a36Sopenharmony_ci * so that lance_init_ring() called at any error will not 79362306a36Sopenharmony_ci * forget multicast filters. 79462306a36Sopenharmony_ci * 79562306a36Sopenharmony_ci * BTW it is common bug in all lance drivers! --ANK 79662306a36Sopenharmony_ci */ 79762306a36Sopenharmony_ci *lib_ptr(ib, mode, lp->type) = 0; 79862306a36Sopenharmony_ci *lib_ptr(ib, filter[0], lp->type) = 0; 79962306a36Sopenharmony_ci *lib_ptr(ib, filter[1], lp->type) = 0; 80062306a36Sopenharmony_ci *lib_ptr(ib, filter[2], lp->type) = 0; 80162306a36Sopenharmony_ci *lib_ptr(ib, filter[3], lp->type) = 0; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci lance_init_ring(dev); 80462306a36Sopenharmony_ci load_csrs(lp); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci netif_start_queue(dev); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci /* Associate IRQ with lance_interrupt */ 80962306a36Sopenharmony_ci if (request_irq(dev->irq, lance_interrupt, 0, "lance", dev)) { 81062306a36Sopenharmony_ci printk("%s: Can't get IRQ %d\n", dev->name, dev->irq); 81162306a36Sopenharmony_ci return -EAGAIN; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci if (lp->dma_irq >= 0) { 81462306a36Sopenharmony_ci unsigned long flags; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (request_irq(lp->dma_irq, lance_dma_merr_int, IRQF_ONESHOT, 81762306a36Sopenharmony_ci "lance error", dev)) { 81862306a36Sopenharmony_ci free_irq(dev->irq, dev); 81962306a36Sopenharmony_ci printk("%s: Can't get DMA IRQ %d\n", dev->name, 82062306a36Sopenharmony_ci lp->dma_irq); 82162306a36Sopenharmony_ci return -EAGAIN; 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci spin_lock_irqsave(&ioasic_ssr_lock, flags); 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci fast_mb(); 82762306a36Sopenharmony_ci /* Enable I/O ASIC LANCE DMA. */ 82862306a36Sopenharmony_ci ioasic_write(IO_REG_SSR, 82962306a36Sopenharmony_ci ioasic_read(IO_REG_SSR) | IO_SSR_LANCE_DMA_EN); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci fast_mb(); 83262306a36Sopenharmony_ci spin_unlock_irqrestore(&ioasic_ssr_lock, flags); 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci status = init_restart_lance(lp); 83662306a36Sopenharmony_ci return status; 83762306a36Sopenharmony_ci} 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_cistatic int lance_close(struct net_device *dev) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 84262306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci netif_stop_queue(dev); 84562306a36Sopenharmony_ci del_timer_sync(&lp->multicast_timer); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci /* Stop the card */ 84862306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 84962306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci if (lp->dma_irq >= 0) { 85262306a36Sopenharmony_ci unsigned long flags; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci spin_lock_irqsave(&ioasic_ssr_lock, flags); 85562306a36Sopenharmony_ci 85662306a36Sopenharmony_ci fast_mb(); 85762306a36Sopenharmony_ci /* Disable I/O ASIC LANCE DMA. */ 85862306a36Sopenharmony_ci ioasic_write(IO_REG_SSR, 85962306a36Sopenharmony_ci ioasic_read(IO_REG_SSR) & ~IO_SSR_LANCE_DMA_EN); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci fast_iob(); 86262306a36Sopenharmony_ci spin_unlock_irqrestore(&ioasic_ssr_lock, flags); 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci free_irq(lp->dma_irq, dev); 86562306a36Sopenharmony_ci } 86662306a36Sopenharmony_ci free_irq(dev->irq, dev); 86762306a36Sopenharmony_ci return 0; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic inline int lance_reset(struct net_device *dev) 87162306a36Sopenharmony_ci{ 87262306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 87362306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 87462306a36Sopenharmony_ci int status; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci /* Stop the lance */ 87762306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 87862306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci lance_init_ring(dev); 88162306a36Sopenharmony_ci load_csrs(lp); 88262306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 88362306a36Sopenharmony_ci status = init_restart_lance(lp); 88462306a36Sopenharmony_ci return status; 88562306a36Sopenharmony_ci} 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_cistatic void lance_tx_timeout(struct net_device *dev, unsigned int txqueue) 88862306a36Sopenharmony_ci{ 88962306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 89062306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n", 89362306a36Sopenharmony_ci dev->name, ll->rdp); 89462306a36Sopenharmony_ci lance_reset(dev); 89562306a36Sopenharmony_ci netif_wake_queue(dev); 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev) 89962306a36Sopenharmony_ci{ 90062306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 90162306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 90262306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 90362306a36Sopenharmony_ci unsigned long flags; 90462306a36Sopenharmony_ci int entry, len; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci len = skb->len; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (len < ETH_ZLEN) { 90962306a36Sopenharmony_ci if (skb_padto(skb, ETH_ZLEN)) 91062306a36Sopenharmony_ci return NETDEV_TX_OK; 91162306a36Sopenharmony_ci len = ETH_ZLEN; 91262306a36Sopenharmony_ci } 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci dev->stats.tx_bytes += len; 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci spin_lock_irqsave(&lp->lock, flags); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci entry = lp->tx_new; 91962306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[entry].length, lp->type) = (-len); 92062306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[entry].misc, lp->type) = 0; 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci cp_to_buf(lp->type, lp->tx_buf_ptr_cpu[entry], skb->data, len); 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci /* Now, give the packet to the lance */ 92562306a36Sopenharmony_ci *lib_ptr(ib, btx_ring[entry].tmd1, lp->type) = 92662306a36Sopenharmony_ci ((lp->tx_buf_ptr_lnc[entry] >> 16) & 0xff) | 92762306a36Sopenharmony_ci (LE_T1_POK | LE_T1_OWN); 92862306a36Sopenharmony_ci lp->tx_new = (entry + 1) & TX_RING_MOD_MASK; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci if (TX_BUFFS_AVAIL <= 0) 93162306a36Sopenharmony_ci netif_stop_queue(dev); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Kick the lance: transmit now */ 93462306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_INEA | LE_C0_TDMD); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->lock, flags); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci dev_kfree_skb(skb); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return NETDEV_TX_OK; 94162306a36Sopenharmony_ci} 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_cistatic void lance_load_multicast(struct net_device *dev) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 94662306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 94762306a36Sopenharmony_ci struct netdev_hw_addr *ha; 94862306a36Sopenharmony_ci u32 crc; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* set all multicast bits */ 95162306a36Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) { 95262306a36Sopenharmony_ci *lib_ptr(ib, filter[0], lp->type) = 0xffff; 95362306a36Sopenharmony_ci *lib_ptr(ib, filter[1], lp->type) = 0xffff; 95462306a36Sopenharmony_ci *lib_ptr(ib, filter[2], lp->type) = 0xffff; 95562306a36Sopenharmony_ci *lib_ptr(ib, filter[3], lp->type) = 0xffff; 95662306a36Sopenharmony_ci return; 95762306a36Sopenharmony_ci } 95862306a36Sopenharmony_ci /* clear the multicast filter */ 95962306a36Sopenharmony_ci *lib_ptr(ib, filter[0], lp->type) = 0; 96062306a36Sopenharmony_ci *lib_ptr(ib, filter[1], lp->type) = 0; 96162306a36Sopenharmony_ci *lib_ptr(ib, filter[2], lp->type) = 0; 96262306a36Sopenharmony_ci *lib_ptr(ib, filter[3], lp->type) = 0; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci /* Add addresses */ 96562306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 96662306a36Sopenharmony_ci crc = ether_crc_le(ETH_ALEN, ha->addr); 96762306a36Sopenharmony_ci crc = crc >> 26; 96862306a36Sopenharmony_ci *lib_ptr(ib, filter[crc >> 4], lp->type) |= 1 << (crc & 0xf); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic void lance_set_multicast(struct net_device *dev) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 97562306a36Sopenharmony_ci volatile u16 *ib = (volatile u16 *)dev->mem_start; 97662306a36Sopenharmony_ci volatile struct lance_regs *ll = lp->ll; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci if (!netif_running(dev)) 97962306a36Sopenharmony_ci return; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (lp->tx_old != lp->tx_new) { 98262306a36Sopenharmony_ci mod_timer(&lp->multicast_timer, jiffies + 4 * HZ/100); 98362306a36Sopenharmony_ci netif_wake_queue(dev); 98462306a36Sopenharmony_ci return; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci netif_stop_queue(dev); 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci writereg(&ll->rap, LE_CSR0); 99062306a36Sopenharmony_ci writereg(&ll->rdp, LE_C0_STOP); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci lance_init_ring(dev); 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 99562306a36Sopenharmony_ci *lib_ptr(ib, mode, lp->type) |= LE_MO_PROM; 99662306a36Sopenharmony_ci } else { 99762306a36Sopenharmony_ci *lib_ptr(ib, mode, lp->type) &= ~LE_MO_PROM; 99862306a36Sopenharmony_ci lance_load_multicast(dev); 99962306a36Sopenharmony_ci } 100062306a36Sopenharmony_ci load_csrs(lp); 100162306a36Sopenharmony_ci init_restart_lance(lp); 100262306a36Sopenharmony_ci netif_wake_queue(dev); 100362306a36Sopenharmony_ci} 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic void lance_set_multicast_retry(struct timer_list *t) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci struct lance_private *lp = from_timer(lp, t, multicast_timer); 100862306a36Sopenharmony_ci struct net_device *dev = lp->dev; 100962306a36Sopenharmony_ci 101062306a36Sopenharmony_ci lance_set_multicast(dev); 101162306a36Sopenharmony_ci} 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = { 101462306a36Sopenharmony_ci .ndo_open = lance_open, 101562306a36Sopenharmony_ci .ndo_stop = lance_close, 101662306a36Sopenharmony_ci .ndo_start_xmit = lance_start_xmit, 101762306a36Sopenharmony_ci .ndo_tx_timeout = lance_tx_timeout, 101862306a36Sopenharmony_ci .ndo_set_rx_mode = lance_set_multicast, 101962306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 102062306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 102162306a36Sopenharmony_ci}; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_cistatic int dec_lance_probe(struct device *bdev, const int type) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci static unsigned version_printed; 102662306a36Sopenharmony_ci static const char fmt[] = "declance%d"; 102762306a36Sopenharmony_ci char name[10]; 102862306a36Sopenharmony_ci struct net_device *dev; 102962306a36Sopenharmony_ci struct lance_private *lp; 103062306a36Sopenharmony_ci volatile struct lance_regs *ll; 103162306a36Sopenharmony_ci resource_size_t start = 0, len = 0; 103262306a36Sopenharmony_ci int i, ret; 103362306a36Sopenharmony_ci unsigned long esar_base; 103462306a36Sopenharmony_ci unsigned char *esar; 103562306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 103662306a36Sopenharmony_ci const char *desc; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci if (dec_lance_debug && version_printed++ == 0) 103962306a36Sopenharmony_ci printk(version); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (bdev) 104262306a36Sopenharmony_ci snprintf(name, sizeof(name), "%s", dev_name(bdev)); 104362306a36Sopenharmony_ci else { 104462306a36Sopenharmony_ci i = 0; 104562306a36Sopenharmony_ci dev = root_lance_dev; 104662306a36Sopenharmony_ci while (dev) { 104762306a36Sopenharmony_ci i++; 104862306a36Sopenharmony_ci lp = netdev_priv(dev); 104962306a36Sopenharmony_ci dev = lp->next; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci snprintf(name, sizeof(name), fmt, i); 105262306a36Sopenharmony_ci } 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct lance_private)); 105562306a36Sopenharmony_ci if (!dev) { 105662306a36Sopenharmony_ci ret = -ENOMEM; 105762306a36Sopenharmony_ci goto err_out; 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* 106162306a36Sopenharmony_ci * alloc_etherdev ensures the data structures used by the LANCE 106262306a36Sopenharmony_ci * are aligned. 106362306a36Sopenharmony_ci */ 106462306a36Sopenharmony_ci lp = netdev_priv(dev); 106562306a36Sopenharmony_ci spin_lock_init(&lp->lock); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci lp->type = type; 106862306a36Sopenharmony_ci switch (type) { 106962306a36Sopenharmony_ci case ASIC_LANCE: 107062306a36Sopenharmony_ci dev->base_addr = CKSEG1ADDR(dec_kn_slot_base + IOASIC_LANCE); 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci /* buffer space for the on-board LANCE shared memory */ 107362306a36Sopenharmony_ci /* 107462306a36Sopenharmony_ci * FIXME: ugly hack! 107562306a36Sopenharmony_ci */ 107662306a36Sopenharmony_ci dev->mem_start = CKSEG1ADDR(0x00020000); 107762306a36Sopenharmony_ci dev->mem_end = dev->mem_start + 0x00020000; 107862306a36Sopenharmony_ci dev->irq = dec_interrupt[DEC_IRQ_LANCE]; 107962306a36Sopenharmony_ci esar_base = CKSEG1ADDR(dec_kn_slot_base + IOASIC_ESAR); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci /* Workaround crash with booting KN04 2.1k from Disk */ 108262306a36Sopenharmony_ci memset((void *)dev->mem_start, 0, 108362306a36Sopenharmony_ci dev->mem_end - dev->mem_start); 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci /* 108662306a36Sopenharmony_ci * setup the pointer arrays, this sucks [tm] :-( 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 108962306a36Sopenharmony_ci lp->rx_buf_ptr_cpu[i] = 109062306a36Sopenharmony_ci (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU + 109162306a36Sopenharmony_ci 2 * i * RX_BUFF_SIZE); 109262306a36Sopenharmony_ci lp->rx_buf_ptr_lnc[i] = 109362306a36Sopenharmony_ci (BUF_OFFSET_LNC + i * RX_BUFF_SIZE); 109462306a36Sopenharmony_ci } 109562306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 109662306a36Sopenharmony_ci lp->tx_buf_ptr_cpu[i] = 109762306a36Sopenharmony_ci (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU + 109862306a36Sopenharmony_ci 2 * RX_RING_SIZE * RX_BUFF_SIZE + 109962306a36Sopenharmony_ci 2 * i * TX_BUFF_SIZE); 110062306a36Sopenharmony_ci lp->tx_buf_ptr_lnc[i] = 110162306a36Sopenharmony_ci (BUF_OFFSET_LNC + 110262306a36Sopenharmony_ci RX_RING_SIZE * RX_BUFF_SIZE + 110362306a36Sopenharmony_ci i * TX_BUFF_SIZE); 110462306a36Sopenharmony_ci } 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci /* Setup I/O ASIC LANCE DMA. */ 110762306a36Sopenharmony_ci lp->dma_irq = dec_interrupt[DEC_IRQ_LANCE_MERR]; 110862306a36Sopenharmony_ci ioasic_write(IO_REG_LANCE_DMA_P, 110962306a36Sopenharmony_ci CPHYSADDR(dev->mem_start) << 3); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci break; 111262306a36Sopenharmony_ci#ifdef CONFIG_TC 111362306a36Sopenharmony_ci case PMAD_LANCE: 111462306a36Sopenharmony_ci dev_set_drvdata(bdev, dev); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci start = to_tc_dev(bdev)->resource.start; 111762306a36Sopenharmony_ci len = to_tc_dev(bdev)->resource.end - start + 1; 111862306a36Sopenharmony_ci if (!request_mem_region(start, len, dev_name(bdev))) { 111962306a36Sopenharmony_ci printk(KERN_ERR 112062306a36Sopenharmony_ci "%s: Unable to reserve MMIO resource\n", 112162306a36Sopenharmony_ci dev_name(bdev)); 112262306a36Sopenharmony_ci ret = -EBUSY; 112362306a36Sopenharmony_ci goto err_out_dev; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci dev->mem_start = CKSEG1ADDR(start); 112762306a36Sopenharmony_ci dev->mem_end = dev->mem_start + 0x100000; 112862306a36Sopenharmony_ci dev->base_addr = dev->mem_start + 0x100000; 112962306a36Sopenharmony_ci dev->irq = to_tc_dev(bdev)->interrupt; 113062306a36Sopenharmony_ci esar_base = dev->mem_start + 0x1c0002; 113162306a36Sopenharmony_ci lp->dma_irq = -1; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 113462306a36Sopenharmony_ci lp->rx_buf_ptr_cpu[i] = 113562306a36Sopenharmony_ci (char *)(dev->mem_start + BUF_OFFSET_CPU + 113662306a36Sopenharmony_ci i * RX_BUFF_SIZE); 113762306a36Sopenharmony_ci lp->rx_buf_ptr_lnc[i] = 113862306a36Sopenharmony_ci (BUF_OFFSET_LNC + i * RX_BUFF_SIZE); 113962306a36Sopenharmony_ci } 114062306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 114162306a36Sopenharmony_ci lp->tx_buf_ptr_cpu[i] = 114262306a36Sopenharmony_ci (char *)(dev->mem_start + BUF_OFFSET_CPU + 114362306a36Sopenharmony_ci RX_RING_SIZE * RX_BUFF_SIZE + 114462306a36Sopenharmony_ci i * TX_BUFF_SIZE); 114562306a36Sopenharmony_ci lp->tx_buf_ptr_lnc[i] = 114662306a36Sopenharmony_ci (BUF_OFFSET_LNC + 114762306a36Sopenharmony_ci RX_RING_SIZE * RX_BUFF_SIZE + 114862306a36Sopenharmony_ci i * TX_BUFF_SIZE); 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci break; 115262306a36Sopenharmony_ci#endif 115362306a36Sopenharmony_ci case PMAX_LANCE: 115462306a36Sopenharmony_ci dev->irq = dec_interrupt[DEC_IRQ_LANCE]; 115562306a36Sopenharmony_ci dev->base_addr = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE); 115662306a36Sopenharmony_ci dev->mem_start = CKSEG1ADDR(KN01_SLOT_BASE + KN01_LANCE_MEM); 115762306a36Sopenharmony_ci dev->mem_end = dev->mem_start + KN01_SLOT_SIZE; 115862306a36Sopenharmony_ci esar_base = CKSEG1ADDR(KN01_SLOT_BASE + KN01_ESAR + 1); 115962306a36Sopenharmony_ci lp->dma_irq = -1; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* 116262306a36Sopenharmony_ci * setup the pointer arrays, this sucks [tm] :-( 116362306a36Sopenharmony_ci */ 116462306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 116562306a36Sopenharmony_ci lp->rx_buf_ptr_cpu[i] = 116662306a36Sopenharmony_ci (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU + 116762306a36Sopenharmony_ci 2 * i * RX_BUFF_SIZE); 116862306a36Sopenharmony_ci lp->rx_buf_ptr_lnc[i] = 116962306a36Sopenharmony_ci (BUF_OFFSET_LNC + i * RX_BUFF_SIZE); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 117262306a36Sopenharmony_ci lp->tx_buf_ptr_cpu[i] = 117362306a36Sopenharmony_ci (char *)(dev->mem_start + 2 * BUF_OFFSET_CPU + 117462306a36Sopenharmony_ci 2 * RX_RING_SIZE * RX_BUFF_SIZE + 117562306a36Sopenharmony_ci 2 * i * TX_BUFF_SIZE); 117662306a36Sopenharmony_ci lp->tx_buf_ptr_lnc[i] = 117762306a36Sopenharmony_ci (BUF_OFFSET_LNC + 117862306a36Sopenharmony_ci RX_RING_SIZE * RX_BUFF_SIZE + 117962306a36Sopenharmony_ci i * TX_BUFF_SIZE); 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci break; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci default: 118562306a36Sopenharmony_ci printk(KERN_ERR "%s: declance_init called with unknown type\n", 118662306a36Sopenharmony_ci name); 118762306a36Sopenharmony_ci ret = -ENODEV; 118862306a36Sopenharmony_ci goto err_out_dev; 118962306a36Sopenharmony_ci } 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci ll = (struct lance_regs *) dev->base_addr; 119262306a36Sopenharmony_ci esar = (unsigned char *) esar_base; 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* prom checks */ 119562306a36Sopenharmony_ci /* First, check for test pattern */ 119662306a36Sopenharmony_ci if (esar[0x60] != 0xff && esar[0x64] != 0x00 && 119762306a36Sopenharmony_ci esar[0x68] != 0x55 && esar[0x6c] != 0xaa) { 119862306a36Sopenharmony_ci printk(KERN_ERR 119962306a36Sopenharmony_ci "%s: Ethernet station address prom not found!\n", 120062306a36Sopenharmony_ci name); 120162306a36Sopenharmony_ci ret = -ENODEV; 120262306a36Sopenharmony_ci goto err_out_resource; 120362306a36Sopenharmony_ci } 120462306a36Sopenharmony_ci /* Check the prom contents */ 120562306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 120662306a36Sopenharmony_ci if (esar[i * 4] != esar[0x3c - i * 4] && 120762306a36Sopenharmony_ci esar[i * 4] != esar[0x40 + i * 4] && 120862306a36Sopenharmony_ci esar[0x3c - i * 4] != esar[0x40 + i * 4]) { 120962306a36Sopenharmony_ci printk(KERN_ERR "%s: Something is wrong with the " 121062306a36Sopenharmony_ci "ethernet station address prom!\n", name); 121162306a36Sopenharmony_ci ret = -ENODEV; 121262306a36Sopenharmony_ci goto err_out_resource; 121362306a36Sopenharmony_ci } 121462306a36Sopenharmony_ci } 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci /* Copy the ethernet address to the device structure, later to the 121762306a36Sopenharmony_ci * lance initialization block so the lance gets it every time it's 121862306a36Sopenharmony_ci * (re)initialized. 121962306a36Sopenharmony_ci */ 122062306a36Sopenharmony_ci switch (type) { 122162306a36Sopenharmony_ci case ASIC_LANCE: 122262306a36Sopenharmony_ci desc = "IOASIC onboard LANCE"; 122362306a36Sopenharmony_ci break; 122462306a36Sopenharmony_ci case PMAD_LANCE: 122562306a36Sopenharmony_ci desc = "PMAD-AA"; 122662306a36Sopenharmony_ci break; 122762306a36Sopenharmony_ci case PMAX_LANCE: 122862306a36Sopenharmony_ci desc = "PMAX onboard LANCE"; 122962306a36Sopenharmony_ci break; 123062306a36Sopenharmony_ci } 123162306a36Sopenharmony_ci for (i = 0; i < 6; i++) 123262306a36Sopenharmony_ci addr[i] = esar[i * 4]; 123362306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci printk("%s: %s, addr = %pM, irq = %d\n", 123662306a36Sopenharmony_ci name, desc, dev->dev_addr, dev->irq); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci dev->netdev_ops = &lance_netdev_ops; 123962306a36Sopenharmony_ci dev->watchdog_timeo = 5*HZ; 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci /* lp->ll is the location of the registers for lance card */ 124262306a36Sopenharmony_ci lp->ll = ll; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci /* busmaster_regval (CSR3) should be zero according to the PMAD-AA 124562306a36Sopenharmony_ci * specification. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_ci lp->busmaster_regval = 0; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci dev->dma = 0; 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci /* We cannot sleep if the chip is busy during a 125262306a36Sopenharmony_ci * multicast list update event, because such events 125362306a36Sopenharmony_ci * can occur from interrupts (ex. IPv6). So we 125462306a36Sopenharmony_ci * use a timer to try again later when necessary. -DaveM 125562306a36Sopenharmony_ci */ 125662306a36Sopenharmony_ci lp->dev = dev; 125762306a36Sopenharmony_ci timer_setup(&lp->multicast_timer, lance_set_multicast_retry, 0); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci ret = register_netdev(dev); 126162306a36Sopenharmony_ci if (ret) { 126262306a36Sopenharmony_ci printk(KERN_ERR 126362306a36Sopenharmony_ci "%s: Unable to register netdev, aborting.\n", name); 126462306a36Sopenharmony_ci goto err_out_resource; 126562306a36Sopenharmony_ci } 126662306a36Sopenharmony_ci 126762306a36Sopenharmony_ci if (!bdev) { 126862306a36Sopenharmony_ci lp->next = root_lance_dev; 126962306a36Sopenharmony_ci root_lance_dev = dev; 127062306a36Sopenharmony_ci } 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci printk("%s: registered as %s.\n", name, dev->name); 127362306a36Sopenharmony_ci return 0; 127462306a36Sopenharmony_ci 127562306a36Sopenharmony_cierr_out_resource: 127662306a36Sopenharmony_ci if (bdev) 127762306a36Sopenharmony_ci release_mem_region(start, len); 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cierr_out_dev: 128062306a36Sopenharmony_ci free_netdev(dev); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cierr_out: 128362306a36Sopenharmony_ci return ret; 128462306a36Sopenharmony_ci} 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci/* Find all the lance cards on the system and initialize them */ 128762306a36Sopenharmony_cistatic int __init dec_lance_platform_probe(void) 128862306a36Sopenharmony_ci{ 128962306a36Sopenharmony_ci int count = 0; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (dec_interrupt[DEC_IRQ_LANCE] >= 0) { 129262306a36Sopenharmony_ci if (dec_interrupt[DEC_IRQ_LANCE_MERR] >= 0) { 129362306a36Sopenharmony_ci if (dec_lance_probe(NULL, ASIC_LANCE) >= 0) 129462306a36Sopenharmony_ci count++; 129562306a36Sopenharmony_ci } else if (!TURBOCHANNEL) { 129662306a36Sopenharmony_ci if (dec_lance_probe(NULL, PMAX_LANCE) >= 0) 129762306a36Sopenharmony_ci count++; 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci } 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci return (count > 0) ? 0 : -ENODEV; 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_cistatic void __exit dec_lance_platform_remove(void) 130562306a36Sopenharmony_ci{ 130662306a36Sopenharmony_ci while (root_lance_dev) { 130762306a36Sopenharmony_ci struct net_device *dev = root_lance_dev; 130862306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci unregister_netdev(dev); 131162306a36Sopenharmony_ci root_lance_dev = lp->next; 131262306a36Sopenharmony_ci free_netdev(dev); 131362306a36Sopenharmony_ci } 131462306a36Sopenharmony_ci} 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_ci#ifdef CONFIG_TC 131762306a36Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev); 131862306a36Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev); 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic const struct tc_device_id dec_lance_tc_table[] = { 132162306a36Sopenharmony_ci { "DEC ", "PMAD-AA " }, 132262306a36Sopenharmony_ci { } 132362306a36Sopenharmony_ci}; 132462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(tc, dec_lance_tc_table); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic struct tc_driver dec_lance_tc_driver = { 132762306a36Sopenharmony_ci .id_table = dec_lance_tc_table, 132862306a36Sopenharmony_ci .driver = { 132962306a36Sopenharmony_ci .name = "declance", 133062306a36Sopenharmony_ci .bus = &tc_bus_type, 133162306a36Sopenharmony_ci .probe = dec_lance_tc_probe, 133262306a36Sopenharmony_ci .remove = dec_lance_tc_remove, 133362306a36Sopenharmony_ci }, 133462306a36Sopenharmony_ci}; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic int dec_lance_tc_probe(struct device *dev) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci int status = dec_lance_probe(dev, PMAD_LANCE); 133962306a36Sopenharmony_ci if (!status) 134062306a36Sopenharmony_ci get_device(dev); 134162306a36Sopenharmony_ci return status; 134262306a36Sopenharmony_ci} 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_cistatic void dec_lance_remove(struct device *bdev) 134562306a36Sopenharmony_ci{ 134662306a36Sopenharmony_ci struct net_device *dev = dev_get_drvdata(bdev); 134762306a36Sopenharmony_ci resource_size_t start, len; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci unregister_netdev(dev); 135062306a36Sopenharmony_ci start = to_tc_dev(bdev)->resource.start; 135162306a36Sopenharmony_ci len = to_tc_dev(bdev)->resource.end - start + 1; 135262306a36Sopenharmony_ci release_mem_region(start, len); 135362306a36Sopenharmony_ci free_netdev(dev); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_cistatic int dec_lance_tc_remove(struct device *dev) 135762306a36Sopenharmony_ci{ 135862306a36Sopenharmony_ci put_device(dev); 135962306a36Sopenharmony_ci dec_lance_remove(dev); 136062306a36Sopenharmony_ci return 0; 136162306a36Sopenharmony_ci} 136262306a36Sopenharmony_ci#endif 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_cistatic int __init dec_lance_init(void) 136562306a36Sopenharmony_ci{ 136662306a36Sopenharmony_ci int status; 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci status = tc_register_driver(&dec_lance_tc_driver); 136962306a36Sopenharmony_ci if (!status) 137062306a36Sopenharmony_ci dec_lance_platform_probe(); 137162306a36Sopenharmony_ci return status; 137262306a36Sopenharmony_ci} 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_cistatic void __exit dec_lance_exit(void) 137562306a36Sopenharmony_ci{ 137662306a36Sopenharmony_ci dec_lance_platform_remove(); 137762306a36Sopenharmony_ci tc_unregister_driver(&dec_lance_tc_driver); 137862306a36Sopenharmony_ci} 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_cimodule_init(dec_lance_init); 138262306a36Sopenharmony_cimodule_exit(dec_lance_exit); 1383