162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* $Id: sunlance.c,v 1.112 2002/01/15 06:48:55 davem Exp $ 362306a36Sopenharmony_ci * lance.c: Linux/Sparc/Lance driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written 1995, 1996 by Miguel de Icaza 662306a36Sopenharmony_ci * Sources: 762306a36Sopenharmony_ci * The Linux depca driver 862306a36Sopenharmony_ci * The Linux lance driver. 962306a36Sopenharmony_ci * The Linux skeleton driver. 1062306a36Sopenharmony_ci * The NetBSD Sparc/Lance driver. 1162306a36Sopenharmony_ci * Theo de Raadt (deraadt@openbsd.org) 1262306a36Sopenharmony_ci * NCR92C990 Lan Controller manual 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * 1.4: 1562306a36Sopenharmony_ci * Added support to run with a ledma on the Sun4m 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * 1.5: 1862306a36Sopenharmony_ci * Added multiple card detection. 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * 4/17/96: Burst sizes and tpe selection on sun4m by Eddie C. Dost 2162306a36Sopenharmony_ci * (ecd@skynet.be) 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * 5/15/96: auto carrier detection on sun4m by Eddie C. Dost 2462306a36Sopenharmony_ci * (ecd@skynet.be) 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * 5/17/96: lebuffer on scsi/ether cards now work David S. Miller 2762306a36Sopenharmony_ci * (davem@caip.rutgers.edu) 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * 5/29/96: override option 'tpe-link-test?', if it is 'false', as 3062306a36Sopenharmony_ci * this disables auto carrier detection on sun4m. Eddie C. Dost 3162306a36Sopenharmony_ci * (ecd@skynet.be) 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * 1.7: 3462306a36Sopenharmony_ci * 6/26/96: Bug fix for multiple ledmas, miguel. 3562306a36Sopenharmony_ci * 3662306a36Sopenharmony_ci * 1.8: 3762306a36Sopenharmony_ci * Stole multicast code from depca.c, fixed lance_tx. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * 1.9: 4062306a36Sopenharmony_ci * 8/21/96: Fixed the multicast code (Pedro Roque) 4162306a36Sopenharmony_ci * 4262306a36Sopenharmony_ci * 8/28/96: Send fake packet in lance_open() if auto_select is true, 4362306a36Sopenharmony_ci * so we can detect the carrier loss condition in time. 4462306a36Sopenharmony_ci * Eddie C. Dost (ecd@skynet.be) 4562306a36Sopenharmony_ci * 4662306a36Sopenharmony_ci * 9/15/96: Align rx_buf so that eth_copy_and_sum() won't cause an 4762306a36Sopenharmony_ci * MNA trap during chksum_partial_copy(). (ecd@skynet.be) 4862306a36Sopenharmony_ci * 4962306a36Sopenharmony_ci * 11/17/96: Handle LE_C0_MERR in lance_interrupt(). (ecd@skynet.be) 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * 12/22/96: Don't loop forever in lance_rx() on incomplete packets. 5262306a36Sopenharmony_ci * This was the sun4c killer. Shit, stupid bug. 5362306a36Sopenharmony_ci * (ecd@skynet.be) 5462306a36Sopenharmony_ci * 5562306a36Sopenharmony_ci * 1.10: 5662306a36Sopenharmony_ci * 1/26/97: Modularize driver. (ecd@skynet.be) 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * 1.11: 5962306a36Sopenharmony_ci * 12/27/97: Added sun4d support. (jj@sunsite.mff.cuni.cz) 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * 1.12: 6262306a36Sopenharmony_ci * 11/3/99: Fixed SMP race in lance_start_xmit found by davem. 6362306a36Sopenharmony_ci * Anton Blanchard (anton@progsoc.uts.edu.au) 6462306a36Sopenharmony_ci * 2.00: 11/9/99: Massive overhaul and port to new SBUS driver interfaces. 6562306a36Sopenharmony_ci * David S. Miller (davem@redhat.com) 6662306a36Sopenharmony_ci * 2.01: 6762306a36Sopenharmony_ci * 11/08/01: Use library crc32 functions (Matt_Domsch@dell.com) 6862306a36Sopenharmony_ci * 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#undef DEBUG_DRIVER 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic char lancestr[] = "LANCE"; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#include <linux/module.h> 7662306a36Sopenharmony_ci#include <linux/kernel.h> 7762306a36Sopenharmony_ci#include <linux/types.h> 7862306a36Sopenharmony_ci#include <linux/fcntl.h> 7962306a36Sopenharmony_ci#include <linux/interrupt.h> 8062306a36Sopenharmony_ci#include <linux/ioport.h> 8162306a36Sopenharmony_ci#include <linux/in.h> 8262306a36Sopenharmony_ci#include <linux/string.h> 8362306a36Sopenharmony_ci#include <linux/delay.h> 8462306a36Sopenharmony_ci#include <linux/crc32.h> 8562306a36Sopenharmony_ci#include <linux/errno.h> 8662306a36Sopenharmony_ci#include <linux/socket.h> /* Used for the temporal inet entries and routing */ 8762306a36Sopenharmony_ci#include <linux/route.h> 8862306a36Sopenharmony_ci#include <linux/netdevice.h> 8962306a36Sopenharmony_ci#include <linux/etherdevice.h> 9062306a36Sopenharmony_ci#include <linux/skbuff.h> 9162306a36Sopenharmony_ci#include <linux/ethtool.h> 9262306a36Sopenharmony_ci#include <linux/bitops.h> 9362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 9462306a36Sopenharmony_ci#include <linux/of.h> 9562306a36Sopenharmony_ci#include <linux/platform_device.h> 9662306a36Sopenharmony_ci#include <linux/gfp.h> 9762306a36Sopenharmony_ci#include <linux/pgtable.h> 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci#include <asm/io.h> 10062306a36Sopenharmony_ci#include <asm/dma.h> 10162306a36Sopenharmony_ci#include <asm/byteorder.h> /* Used by the checksum routines */ 10262306a36Sopenharmony_ci#include <asm/idprom.h> 10362306a36Sopenharmony_ci#include <asm/prom.h> 10462306a36Sopenharmony_ci#include <asm/auxio.h> /* For tpe-link-test? setting */ 10562306a36Sopenharmony_ci#include <asm/irq.h> 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci#define DRV_NAME "sunlance" 10862306a36Sopenharmony_ci#define DRV_RELDATE "8/24/03" 10962306a36Sopenharmony_ci#define DRV_AUTHOR "Miguel de Icaza (miguel@nuclecu.unam.mx)" 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ciMODULE_AUTHOR(DRV_AUTHOR); 11262306a36Sopenharmony_ciMODULE_DESCRIPTION("Sun Lance ethernet driver"); 11362306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Define: 2^4 Tx buffers and 2^4 Rx buffers */ 11662306a36Sopenharmony_ci#ifndef LANCE_LOG_TX_BUFFERS 11762306a36Sopenharmony_ci#define LANCE_LOG_TX_BUFFERS 4 11862306a36Sopenharmony_ci#define LANCE_LOG_RX_BUFFERS 4 11962306a36Sopenharmony_ci#endif 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define LE_CSR0 0 12262306a36Sopenharmony_ci#define LE_CSR1 1 12362306a36Sopenharmony_ci#define LE_CSR2 2 12462306a36Sopenharmony_ci#define LE_CSR3 3 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci#define LE_MO_PROM 0x8000 /* Enable promiscuous mode */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci#define LE_C0_ERR 0x8000 /* Error: set if BAB, SQE, MISS or ME is set */ 12962306a36Sopenharmony_ci#define LE_C0_BABL 0x4000 /* BAB: Babble: tx timeout. */ 13062306a36Sopenharmony_ci#define LE_C0_CERR 0x2000 /* SQE: Signal quality error */ 13162306a36Sopenharmony_ci#define LE_C0_MISS 0x1000 /* MISS: Missed a packet */ 13262306a36Sopenharmony_ci#define LE_C0_MERR 0x0800 /* ME: Memory error */ 13362306a36Sopenharmony_ci#define LE_C0_RINT 0x0400 /* Received interrupt */ 13462306a36Sopenharmony_ci#define LE_C0_TINT 0x0200 /* Transmitter Interrupt */ 13562306a36Sopenharmony_ci#define LE_C0_IDON 0x0100 /* IFIN: Init finished. */ 13662306a36Sopenharmony_ci#define LE_C0_INTR 0x0080 /* Interrupt or error */ 13762306a36Sopenharmony_ci#define LE_C0_INEA 0x0040 /* Interrupt enable */ 13862306a36Sopenharmony_ci#define LE_C0_RXON 0x0020 /* Receiver on */ 13962306a36Sopenharmony_ci#define LE_C0_TXON 0x0010 /* Transmitter on */ 14062306a36Sopenharmony_ci#define LE_C0_TDMD 0x0008 /* Transmitter demand */ 14162306a36Sopenharmony_ci#define LE_C0_STOP 0x0004 /* Stop the card */ 14262306a36Sopenharmony_ci#define LE_C0_STRT 0x0002 /* Start the card */ 14362306a36Sopenharmony_ci#define LE_C0_INIT 0x0001 /* Init the card */ 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define LE_C3_BSWP 0x4 /* SWAP */ 14662306a36Sopenharmony_ci#define LE_C3_ACON 0x2 /* ALE Control */ 14762306a36Sopenharmony_ci#define LE_C3_BCON 0x1 /* Byte control */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* Receive message descriptor 1 */ 15062306a36Sopenharmony_ci#define LE_R1_OWN 0x80 /* Who owns the entry */ 15162306a36Sopenharmony_ci#define LE_R1_ERR 0x40 /* Error: if FRA, OFL, CRC or BUF is set */ 15262306a36Sopenharmony_ci#define LE_R1_FRA 0x20 /* FRA: Frame error */ 15362306a36Sopenharmony_ci#define LE_R1_OFL 0x10 /* OFL: Frame overflow */ 15462306a36Sopenharmony_ci#define LE_R1_CRC 0x08 /* CRC error */ 15562306a36Sopenharmony_ci#define LE_R1_BUF 0x04 /* BUF: Buffer error */ 15662306a36Sopenharmony_ci#define LE_R1_SOP 0x02 /* Start of packet */ 15762306a36Sopenharmony_ci#define LE_R1_EOP 0x01 /* End of packet */ 15862306a36Sopenharmony_ci#define LE_R1_POK 0x03 /* Packet is complete: SOP + EOP */ 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define LE_T1_OWN 0x80 /* Lance owns the packet */ 16162306a36Sopenharmony_ci#define LE_T1_ERR 0x40 /* Error summary */ 16262306a36Sopenharmony_ci#define LE_T1_EMORE 0x10 /* Error: more than one retry needed */ 16362306a36Sopenharmony_ci#define LE_T1_EONE 0x08 /* Error: one retry needed */ 16462306a36Sopenharmony_ci#define LE_T1_EDEF 0x04 /* Error: deferred */ 16562306a36Sopenharmony_ci#define LE_T1_SOP 0x02 /* Start of packet */ 16662306a36Sopenharmony_ci#define LE_T1_EOP 0x01 /* End of packet */ 16762306a36Sopenharmony_ci#define LE_T1_POK 0x03 /* Packet is complete: SOP + EOP */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci#define LE_T3_BUF 0x8000 /* Buffer error */ 17062306a36Sopenharmony_ci#define LE_T3_UFL 0x4000 /* Error underflow */ 17162306a36Sopenharmony_ci#define LE_T3_LCOL 0x1000 /* Error late collision */ 17262306a36Sopenharmony_ci#define LE_T3_CLOS 0x0800 /* Error carrier loss */ 17362306a36Sopenharmony_ci#define LE_T3_RTY 0x0400 /* Error retry */ 17462306a36Sopenharmony_ci#define LE_T3_TDR 0x03ff /* Time Domain Reflectometry counter */ 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) 17762306a36Sopenharmony_ci#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) 17862306a36Sopenharmony_ci#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) 17962306a36Sopenharmony_ci#define TX_NEXT(__x) (((__x)+1) & TX_RING_MOD_MASK) 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) 18262306a36Sopenharmony_ci#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) 18362306a36Sopenharmony_ci#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) 18462306a36Sopenharmony_ci#define RX_NEXT(__x) (((__x)+1) & RX_RING_MOD_MASK) 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define PKT_BUF_SZ 1544 18762306a36Sopenharmony_ci#define RX_BUFF_SIZE PKT_BUF_SZ 18862306a36Sopenharmony_ci#define TX_BUFF_SIZE PKT_BUF_SZ 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistruct lance_rx_desc { 19162306a36Sopenharmony_ci u16 rmd0; /* low address of packet */ 19262306a36Sopenharmony_ci u8 rmd1_bits; /* descriptor bits */ 19362306a36Sopenharmony_ci u8 rmd1_hadr; /* high address of packet */ 19462306a36Sopenharmony_ci s16 length; /* This length is 2s complement (negative)! 19562306a36Sopenharmony_ci * Buffer length 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci u16 mblength; /* This is the actual number of bytes received */ 19862306a36Sopenharmony_ci}; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistruct lance_tx_desc { 20162306a36Sopenharmony_ci u16 tmd0; /* low address of packet */ 20262306a36Sopenharmony_ci u8 tmd1_bits; /* descriptor bits */ 20362306a36Sopenharmony_ci u8 tmd1_hadr; /* high address of packet */ 20462306a36Sopenharmony_ci s16 length; /* Length is 2s complement (negative)! */ 20562306a36Sopenharmony_ci u16 misc; 20662306a36Sopenharmony_ci}; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci/* The LANCE initialization block, described in databook. */ 20962306a36Sopenharmony_ci/* On the Sparc, this block should be on a DMA region */ 21062306a36Sopenharmony_cistruct lance_init_block { 21162306a36Sopenharmony_ci u16 mode; /* Pre-set mode (reg. 15) */ 21262306a36Sopenharmony_ci u8 phys_addr[6]; /* Physical ethernet address */ 21362306a36Sopenharmony_ci u32 filter[2]; /* Multicast filter. */ 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Receive and transmit ring base, along with extra bits. */ 21662306a36Sopenharmony_ci u16 rx_ptr; /* receive descriptor addr */ 21762306a36Sopenharmony_ci u16 rx_len; /* receive len and high addr */ 21862306a36Sopenharmony_ci u16 tx_ptr; /* transmit descriptor addr */ 21962306a36Sopenharmony_ci u16 tx_len; /* transmit len and high addr */ 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci /* The Tx and Rx ring entries must aligned on 8-byte boundaries. */ 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 u8 tx_buf [TX_RING_SIZE][TX_BUFF_SIZE]; 22662306a36Sopenharmony_ci u8 pad[2]; /* align rx_buf for copy_and_sum(). */ 22762306a36Sopenharmony_ci u8 rx_buf [RX_RING_SIZE][RX_BUFF_SIZE]; 22862306a36Sopenharmony_ci}; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci#define libdesc_offset(rt, elem) \ 23162306a36Sopenharmony_ci((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem]))))) 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define libbuff_offset(rt, elem) \ 23462306a36Sopenharmony_ci((__u32)(((unsigned long)(&(((struct lance_init_block *)0)->rt[elem][0]))))) 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistruct lance_private { 23762306a36Sopenharmony_ci void __iomem *lregs; /* Lance RAP/RDP regs. */ 23862306a36Sopenharmony_ci void __iomem *dregs; /* DMA controller regs. */ 23962306a36Sopenharmony_ci struct lance_init_block __iomem *init_block_iomem; 24062306a36Sopenharmony_ci struct lance_init_block *init_block_mem; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci spinlock_t lock; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci int rx_new, tx_new; 24562306a36Sopenharmony_ci int rx_old, tx_old; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci struct platform_device *ledma; /* If set this points to ledma */ 24862306a36Sopenharmony_ci char tpe; /* cable-selection is TPE */ 24962306a36Sopenharmony_ci char auto_select; /* cable-selection by carrier */ 25062306a36Sopenharmony_ci char burst_sizes; /* ledma SBus burst sizes */ 25162306a36Sopenharmony_ci char pio_buffer; /* init block in PIO space? */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci unsigned short busmaster_regval; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci void (*init_ring)(struct net_device *); 25662306a36Sopenharmony_ci void (*rx)(struct net_device *); 25762306a36Sopenharmony_ci void (*tx)(struct net_device *); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci char *name; 26062306a36Sopenharmony_ci dma_addr_t init_block_dvma; 26162306a36Sopenharmony_ci struct net_device *dev; /* Backpointer */ 26262306a36Sopenharmony_ci struct platform_device *op; 26362306a36Sopenharmony_ci struct platform_device *lebuffer; 26462306a36Sopenharmony_ci struct timer_list multicast_timer; 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\ 26862306a36Sopenharmony_ci lp->tx_old+TX_RING_MOD_MASK-lp->tx_new:\ 26962306a36Sopenharmony_ci lp->tx_old - lp->tx_new-1) 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* Lance registers. */ 27262306a36Sopenharmony_ci#define RDP 0x00UL /* register data port */ 27362306a36Sopenharmony_ci#define RAP 0x02UL /* register address port */ 27462306a36Sopenharmony_ci#define LANCE_REG_SIZE 0x04UL 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci#define STOP_LANCE(__lp) \ 27762306a36Sopenharmony_cido { void __iomem *__base = (__lp)->lregs; \ 27862306a36Sopenharmony_ci sbus_writew(LE_CSR0, __base + RAP); \ 27962306a36Sopenharmony_ci sbus_writew(LE_C0_STOP, __base + RDP); \ 28062306a36Sopenharmony_ci} while (0) 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ciint sparc_lance_debug = 2; 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci/* The Lance uses 24 bit addresses */ 28562306a36Sopenharmony_ci/* On the Sun4c the DVMA will provide the remaining bytes for us */ 28662306a36Sopenharmony_ci/* On the Sun4m we have to instruct the ledma to provide them */ 28762306a36Sopenharmony_ci/* Even worse, on scsi/ether SBUS cards, the init block and the 28862306a36Sopenharmony_ci * transmit/receive buffers are addresses as offsets from absolute 28962306a36Sopenharmony_ci * zero on the lebuffer PIO area. -DaveM 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci#define LANCE_ADDR(x) ((long)(x) & ~0xff000000) 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci/* Load the CSR registers */ 29562306a36Sopenharmony_cistatic void load_csrs(struct lance_private *lp) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci u32 leptr; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci if (lp->pio_buffer) 30062306a36Sopenharmony_ci leptr = 0; 30162306a36Sopenharmony_ci else 30262306a36Sopenharmony_ci leptr = LANCE_ADDR(lp->init_block_dvma); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sbus_writew(LE_CSR1, lp->lregs + RAP); 30562306a36Sopenharmony_ci sbus_writew(leptr & 0xffff, lp->lregs + RDP); 30662306a36Sopenharmony_ci sbus_writew(LE_CSR2, lp->lregs + RAP); 30762306a36Sopenharmony_ci sbus_writew(leptr >> 16, lp->lregs + RDP); 30862306a36Sopenharmony_ci sbus_writew(LE_CSR3, lp->lregs + RAP); 30962306a36Sopenharmony_ci sbus_writew(lp->busmaster_regval, lp->lregs + RDP); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Point back to csr0 */ 31262306a36Sopenharmony_ci sbus_writew(LE_CSR0, lp->lregs + RAP); 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci/* Setup the Lance Rx and Tx rings */ 31662306a36Sopenharmony_cistatic void lance_init_ring_dvma(struct net_device *dev) 31762306a36Sopenharmony_ci{ 31862306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 31962306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 32062306a36Sopenharmony_ci dma_addr_t aib = lp->init_block_dvma; 32162306a36Sopenharmony_ci __u32 leptr; 32262306a36Sopenharmony_ci int i; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci /* Lock out other processes while setting up hardware */ 32562306a36Sopenharmony_ci netif_stop_queue(dev); 32662306a36Sopenharmony_ci lp->rx_new = lp->tx_new = 0; 32762306a36Sopenharmony_ci lp->rx_old = lp->tx_old = 0; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Copy the ethernet address to the lance init block 33062306a36Sopenharmony_ci * Note that on the sparc you need to swap the ethernet address. 33162306a36Sopenharmony_ci */ 33262306a36Sopenharmony_ci ib->phys_addr [0] = dev->dev_addr [1]; 33362306a36Sopenharmony_ci ib->phys_addr [1] = dev->dev_addr [0]; 33462306a36Sopenharmony_ci ib->phys_addr [2] = dev->dev_addr [3]; 33562306a36Sopenharmony_ci ib->phys_addr [3] = dev->dev_addr [2]; 33662306a36Sopenharmony_ci ib->phys_addr [4] = dev->dev_addr [5]; 33762306a36Sopenharmony_ci ib->phys_addr [5] = dev->dev_addr [4]; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* Setup the Tx ring entries */ 34062306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 34162306a36Sopenharmony_ci leptr = LANCE_ADDR(aib + libbuff_offset(tx_buf, i)); 34262306a36Sopenharmony_ci ib->btx_ring [i].tmd0 = leptr; 34362306a36Sopenharmony_ci ib->btx_ring [i].tmd1_hadr = leptr >> 16; 34462306a36Sopenharmony_ci ib->btx_ring [i].tmd1_bits = 0; 34562306a36Sopenharmony_ci ib->btx_ring [i].length = 0xf000; /* The ones required by tmd2 */ 34662306a36Sopenharmony_ci ib->btx_ring [i].misc = 0; 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Setup the Rx ring entries */ 35062306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 35162306a36Sopenharmony_ci leptr = LANCE_ADDR(aib + libbuff_offset(rx_buf, i)); 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci ib->brx_ring [i].rmd0 = leptr; 35462306a36Sopenharmony_ci ib->brx_ring [i].rmd1_hadr = leptr >> 16; 35562306a36Sopenharmony_ci ib->brx_ring [i].rmd1_bits = LE_R1_OWN; 35662306a36Sopenharmony_ci ib->brx_ring [i].length = -RX_BUFF_SIZE | 0xf000; 35762306a36Sopenharmony_ci ib->brx_ring [i].mblength = 0; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Setup the initialization block */ 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Setup rx descriptor pointer */ 36362306a36Sopenharmony_ci leptr = LANCE_ADDR(aib + libdesc_offset(brx_ring, 0)); 36462306a36Sopenharmony_ci ib->rx_len = (LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16); 36562306a36Sopenharmony_ci ib->rx_ptr = leptr; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* Setup tx descriptor pointer */ 36862306a36Sopenharmony_ci leptr = LANCE_ADDR(aib + libdesc_offset(btx_ring, 0)); 36962306a36Sopenharmony_ci ib->tx_len = (LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16); 37062306a36Sopenharmony_ci ib->tx_ptr = leptr; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void lance_init_ring_pio(struct net_device *dev) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 37662306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 37762306a36Sopenharmony_ci u32 leptr; 37862306a36Sopenharmony_ci int i; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* Lock out other processes while setting up hardware */ 38162306a36Sopenharmony_ci netif_stop_queue(dev); 38262306a36Sopenharmony_ci lp->rx_new = lp->tx_new = 0; 38362306a36Sopenharmony_ci lp->rx_old = lp->tx_old = 0; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Copy the ethernet address to the lance init block 38662306a36Sopenharmony_ci * Note that on the sparc you need to swap the ethernet address. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[1], &ib->phys_addr[0]); 38962306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[0], &ib->phys_addr[1]); 39062306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[3], &ib->phys_addr[2]); 39162306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[2], &ib->phys_addr[3]); 39262306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[5], &ib->phys_addr[4]); 39362306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[4], &ib->phys_addr[5]); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* Setup the Tx ring entries */ 39662306a36Sopenharmony_ci for (i = 0; i < TX_RING_SIZE; i++) { 39762306a36Sopenharmony_ci leptr = libbuff_offset(tx_buf, i); 39862306a36Sopenharmony_ci sbus_writew(leptr, &ib->btx_ring [i].tmd0); 39962306a36Sopenharmony_ci sbus_writeb(leptr >> 16,&ib->btx_ring [i].tmd1_hadr); 40062306a36Sopenharmony_ci sbus_writeb(0, &ib->btx_ring [i].tmd1_bits); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* The ones required by tmd2 */ 40362306a36Sopenharmony_ci sbus_writew(0xf000, &ib->btx_ring [i].length); 40462306a36Sopenharmony_ci sbus_writew(0, &ib->btx_ring [i].misc); 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Setup the Rx ring entries */ 40862306a36Sopenharmony_ci for (i = 0; i < RX_RING_SIZE; i++) { 40962306a36Sopenharmony_ci leptr = libbuff_offset(rx_buf, i); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci sbus_writew(leptr, &ib->brx_ring [i].rmd0); 41262306a36Sopenharmony_ci sbus_writeb(leptr >> 16,&ib->brx_ring [i].rmd1_hadr); 41362306a36Sopenharmony_ci sbus_writeb(LE_R1_OWN, &ib->brx_ring [i].rmd1_bits); 41462306a36Sopenharmony_ci sbus_writew(-RX_BUFF_SIZE|0xf000, 41562306a36Sopenharmony_ci &ib->brx_ring [i].length); 41662306a36Sopenharmony_ci sbus_writew(0, &ib->brx_ring [i].mblength); 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Setup the initialization block */ 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* Setup rx descriptor pointer */ 42262306a36Sopenharmony_ci leptr = libdesc_offset(brx_ring, 0); 42362306a36Sopenharmony_ci sbus_writew((LANCE_LOG_RX_BUFFERS << 13) | (leptr >> 16), 42462306a36Sopenharmony_ci &ib->rx_len); 42562306a36Sopenharmony_ci sbus_writew(leptr, &ib->rx_ptr); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Setup tx descriptor pointer */ 42862306a36Sopenharmony_ci leptr = libdesc_offset(btx_ring, 0); 42962306a36Sopenharmony_ci sbus_writew((LANCE_LOG_TX_BUFFERS << 13) | (leptr >> 16), 43062306a36Sopenharmony_ci &ib->tx_len); 43162306a36Sopenharmony_ci sbus_writew(leptr, &ib->tx_ptr); 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic void init_restart_ledma(struct lance_private *lp) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci u32 csr = sbus_readl(lp->dregs + DMA_CSR); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!(csr & DMA_HNDL_ERROR)) { 43962306a36Sopenharmony_ci /* E-Cache draining */ 44062306a36Sopenharmony_ci while (sbus_readl(lp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN) 44162306a36Sopenharmony_ci barrier(); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci csr = sbus_readl(lp->dregs + DMA_CSR); 44562306a36Sopenharmony_ci csr &= ~DMA_E_BURSTS; 44662306a36Sopenharmony_ci if (lp->burst_sizes & DMA_BURST32) 44762306a36Sopenharmony_ci csr |= DMA_E_BURST32; 44862306a36Sopenharmony_ci else 44962306a36Sopenharmony_ci csr |= DMA_E_BURST16; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci csr |= (DMA_DSBL_RD_DRN | DMA_DSBL_WR_INV | DMA_FIFO_INV); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (lp->tpe) 45462306a36Sopenharmony_ci csr |= DMA_EN_ENETAUI; 45562306a36Sopenharmony_ci else 45662306a36Sopenharmony_ci csr &= ~DMA_EN_ENETAUI; 45762306a36Sopenharmony_ci udelay(20); 45862306a36Sopenharmony_ci sbus_writel(csr, lp->dregs + DMA_CSR); 45962306a36Sopenharmony_ci udelay(200); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic int init_restart_lance(struct lance_private *lp) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci u16 regval = 0; 46562306a36Sopenharmony_ci int i; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci if (lp->dregs) 46862306a36Sopenharmony_ci init_restart_ledma(lp); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci sbus_writew(LE_CSR0, lp->lregs + RAP); 47162306a36Sopenharmony_ci sbus_writew(LE_C0_INIT, lp->lregs + RDP); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Wait for the lance to complete initialization */ 47462306a36Sopenharmony_ci for (i = 0; i < 100; i++) { 47562306a36Sopenharmony_ci regval = sbus_readw(lp->lregs + RDP); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (regval & (LE_C0_ERR | LE_C0_IDON)) 47862306a36Sopenharmony_ci break; 47962306a36Sopenharmony_ci barrier(); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci if (i == 100 || (regval & LE_C0_ERR)) { 48262306a36Sopenharmony_ci printk(KERN_ERR "LANCE unopened after %d ticks, csr0=%4.4x.\n", 48362306a36Sopenharmony_ci i, regval); 48462306a36Sopenharmony_ci if (lp->dregs) 48562306a36Sopenharmony_ci printk("dcsr=%8.8x\n", sbus_readl(lp->dregs + DMA_CSR)); 48662306a36Sopenharmony_ci return -1; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* Clear IDON by writing a "1", enable interrupts and start lance */ 49062306a36Sopenharmony_ci sbus_writew(LE_C0_IDON, lp->lregs + RDP); 49162306a36Sopenharmony_ci sbus_writew(LE_C0_INEA | LE_C0_STRT, lp->lregs + RDP); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (lp->dregs) { 49462306a36Sopenharmony_ci u32 csr = sbus_readl(lp->dregs + DMA_CSR); 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci csr |= DMA_INT_ENAB; 49762306a36Sopenharmony_ci sbus_writel(csr, lp->dregs + DMA_CSR); 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic void lance_rx_dvma(struct net_device *dev) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 50662306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 50762306a36Sopenharmony_ci struct lance_rx_desc *rd; 50862306a36Sopenharmony_ci u8 bits; 50962306a36Sopenharmony_ci int len, entry = lp->rx_new; 51062306a36Sopenharmony_ci struct sk_buff *skb; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci for (rd = &ib->brx_ring [entry]; 51362306a36Sopenharmony_ci !((bits = rd->rmd1_bits) & LE_R1_OWN); 51462306a36Sopenharmony_ci rd = &ib->brx_ring [entry]) { 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* We got an incomplete frame? */ 51762306a36Sopenharmony_ci if ((bits & LE_R1_POK) != LE_R1_POK) { 51862306a36Sopenharmony_ci dev->stats.rx_over_errors++; 51962306a36Sopenharmony_ci dev->stats.rx_errors++; 52062306a36Sopenharmony_ci } else if (bits & LE_R1_ERR) { 52162306a36Sopenharmony_ci /* Count only the end frame as a rx error, 52262306a36Sopenharmony_ci * not the beginning 52362306a36Sopenharmony_ci */ 52462306a36Sopenharmony_ci if (bits & LE_R1_BUF) dev->stats.rx_fifo_errors++; 52562306a36Sopenharmony_ci if (bits & LE_R1_CRC) dev->stats.rx_crc_errors++; 52662306a36Sopenharmony_ci if (bits & LE_R1_OFL) dev->stats.rx_over_errors++; 52762306a36Sopenharmony_ci if (bits & LE_R1_FRA) dev->stats.rx_frame_errors++; 52862306a36Sopenharmony_ci if (bits & LE_R1_EOP) dev->stats.rx_errors++; 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci len = (rd->mblength & 0xfff) - 4; 53162306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len + 2); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (!skb) { 53462306a36Sopenharmony_ci dev->stats.rx_dropped++; 53562306a36Sopenharmony_ci rd->mblength = 0; 53662306a36Sopenharmony_ci rd->rmd1_bits = LE_R1_OWN; 53762306a36Sopenharmony_ci lp->rx_new = RX_NEXT(entry); 53862306a36Sopenharmony_ci return; 53962306a36Sopenharmony_ci } 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci dev->stats.rx_bytes += len; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci skb_reserve(skb, 2); /* 16 byte align */ 54462306a36Sopenharmony_ci skb_put(skb, len); /* make room */ 54562306a36Sopenharmony_ci skb_copy_to_linear_data(skb, 54662306a36Sopenharmony_ci (unsigned char *)&(ib->rx_buf [entry][0]), 54762306a36Sopenharmony_ci len); 54862306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 54962306a36Sopenharmony_ci netif_rx(skb); 55062306a36Sopenharmony_ci dev->stats.rx_packets++; 55162306a36Sopenharmony_ci } 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Return the packet to the pool */ 55462306a36Sopenharmony_ci rd->mblength = 0; 55562306a36Sopenharmony_ci rd->rmd1_bits = LE_R1_OWN; 55662306a36Sopenharmony_ci entry = RX_NEXT(entry); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci lp->rx_new = entry; 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void lance_tx_dvma(struct net_device *dev) 56362306a36Sopenharmony_ci{ 56462306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 56562306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 56662306a36Sopenharmony_ci int i, j; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci spin_lock(&lp->lock); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci j = lp->tx_old; 57162306a36Sopenharmony_ci for (i = j; i != lp->tx_new; i = j) { 57262306a36Sopenharmony_ci struct lance_tx_desc *td = &ib->btx_ring [i]; 57362306a36Sopenharmony_ci u8 bits = td->tmd1_bits; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* If we hit a packet not owned by us, stop */ 57662306a36Sopenharmony_ci if (bits & LE_T1_OWN) 57762306a36Sopenharmony_ci break; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (bits & LE_T1_ERR) { 58062306a36Sopenharmony_ci u16 status = td->misc; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci dev->stats.tx_errors++; 58362306a36Sopenharmony_ci if (status & LE_T3_RTY) dev->stats.tx_aborted_errors++; 58462306a36Sopenharmony_ci if (status & LE_T3_LCOL) dev->stats.tx_window_errors++; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if (status & LE_T3_CLOS) { 58762306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 58862306a36Sopenharmony_ci if (lp->auto_select) { 58962306a36Sopenharmony_ci lp->tpe = 1 - lp->tpe; 59062306a36Sopenharmony_ci printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n", 59162306a36Sopenharmony_ci dev->name, lp->tpe?"TPE":"AUI"); 59262306a36Sopenharmony_ci STOP_LANCE(lp); 59362306a36Sopenharmony_ci lp->init_ring(dev); 59462306a36Sopenharmony_ci load_csrs(lp); 59562306a36Sopenharmony_ci init_restart_lance(lp); 59662306a36Sopenharmony_ci goto out; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci /* Buffer errors and underflows turn off the 60162306a36Sopenharmony_ci * transmitter, restart the adapter. 60262306a36Sopenharmony_ci */ 60362306a36Sopenharmony_ci if (status & (LE_T3_BUF|LE_T3_UFL)) { 60462306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n", 60762306a36Sopenharmony_ci dev->name); 60862306a36Sopenharmony_ci STOP_LANCE(lp); 60962306a36Sopenharmony_ci lp->init_ring(dev); 61062306a36Sopenharmony_ci load_csrs(lp); 61162306a36Sopenharmony_ci init_restart_lance(lp); 61262306a36Sopenharmony_ci goto out; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci } else if ((bits & LE_T1_POK) == LE_T1_POK) { 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * So we don't count the packet more than once. 61762306a36Sopenharmony_ci */ 61862306a36Sopenharmony_ci td->tmd1_bits = bits & ~(LE_T1_POK); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* One collision before packet was sent. */ 62162306a36Sopenharmony_ci if (bits & LE_T1_EONE) 62262306a36Sopenharmony_ci dev->stats.collisions++; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci /* More than one collision, be optimistic. */ 62562306a36Sopenharmony_ci if (bits & LE_T1_EMORE) 62662306a36Sopenharmony_ci dev->stats.collisions += 2; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci dev->stats.tx_packets++; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci j = TX_NEXT(j); 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci lp->tx_old = j; 63462306a36Sopenharmony_ciout: 63562306a36Sopenharmony_ci if (netif_queue_stopped(dev) && 63662306a36Sopenharmony_ci TX_BUFFS_AVAIL > 0) 63762306a36Sopenharmony_ci netif_wake_queue(dev); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci spin_unlock(&lp->lock); 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic void lance_piocopy_to_skb(struct sk_buff *skb, void __iomem *piobuf, int len) 64362306a36Sopenharmony_ci{ 64462306a36Sopenharmony_ci u16 *p16 = (u16 *) skb->data; 64562306a36Sopenharmony_ci u32 *p32; 64662306a36Sopenharmony_ci u8 *p8; 64762306a36Sopenharmony_ci void __iomem *pbuf = piobuf; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci /* We know here that both src and dest are on a 16bit boundary. */ 65062306a36Sopenharmony_ci *p16++ = sbus_readw(pbuf); 65162306a36Sopenharmony_ci p32 = (u32 *) p16; 65262306a36Sopenharmony_ci pbuf += 2; 65362306a36Sopenharmony_ci len -= 2; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci while (len >= 4) { 65662306a36Sopenharmony_ci *p32++ = sbus_readl(pbuf); 65762306a36Sopenharmony_ci pbuf += 4; 65862306a36Sopenharmony_ci len -= 4; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci p8 = (u8 *) p32; 66162306a36Sopenharmony_ci if (len >= 2) { 66262306a36Sopenharmony_ci p16 = (u16 *) p32; 66362306a36Sopenharmony_ci *p16++ = sbus_readw(pbuf); 66462306a36Sopenharmony_ci pbuf += 2; 66562306a36Sopenharmony_ci len -= 2; 66662306a36Sopenharmony_ci p8 = (u8 *) p16; 66762306a36Sopenharmony_ci } 66862306a36Sopenharmony_ci if (len >= 1) 66962306a36Sopenharmony_ci *p8 = sbus_readb(pbuf); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void lance_rx_pio(struct net_device *dev) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 67562306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 67662306a36Sopenharmony_ci struct lance_rx_desc __iomem *rd; 67762306a36Sopenharmony_ci unsigned char bits; 67862306a36Sopenharmony_ci int len, entry; 67962306a36Sopenharmony_ci struct sk_buff *skb; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci entry = lp->rx_new; 68262306a36Sopenharmony_ci for (rd = &ib->brx_ring [entry]; 68362306a36Sopenharmony_ci !((bits = sbus_readb(&rd->rmd1_bits)) & LE_R1_OWN); 68462306a36Sopenharmony_ci rd = &ib->brx_ring [entry]) { 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci /* We got an incomplete frame? */ 68762306a36Sopenharmony_ci if ((bits & LE_R1_POK) != LE_R1_POK) { 68862306a36Sopenharmony_ci dev->stats.rx_over_errors++; 68962306a36Sopenharmony_ci dev->stats.rx_errors++; 69062306a36Sopenharmony_ci } else if (bits & LE_R1_ERR) { 69162306a36Sopenharmony_ci /* Count only the end frame as a rx error, 69262306a36Sopenharmony_ci * not the beginning 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci if (bits & LE_R1_BUF) dev->stats.rx_fifo_errors++; 69562306a36Sopenharmony_ci if (bits & LE_R1_CRC) dev->stats.rx_crc_errors++; 69662306a36Sopenharmony_ci if (bits & LE_R1_OFL) dev->stats.rx_over_errors++; 69762306a36Sopenharmony_ci if (bits & LE_R1_FRA) dev->stats.rx_frame_errors++; 69862306a36Sopenharmony_ci if (bits & LE_R1_EOP) dev->stats.rx_errors++; 69962306a36Sopenharmony_ci } else { 70062306a36Sopenharmony_ci len = (sbus_readw(&rd->mblength) & 0xfff) - 4; 70162306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len + 2); 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci if (!skb) { 70462306a36Sopenharmony_ci dev->stats.rx_dropped++; 70562306a36Sopenharmony_ci sbus_writew(0, &rd->mblength); 70662306a36Sopenharmony_ci sbus_writeb(LE_R1_OWN, &rd->rmd1_bits); 70762306a36Sopenharmony_ci lp->rx_new = RX_NEXT(entry); 70862306a36Sopenharmony_ci return; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci dev->stats.rx_bytes += len; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci skb_reserve (skb, 2); /* 16 byte align */ 71462306a36Sopenharmony_ci skb_put(skb, len); /* make room */ 71562306a36Sopenharmony_ci lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len); 71662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 71762306a36Sopenharmony_ci netif_rx(skb); 71862306a36Sopenharmony_ci dev->stats.rx_packets++; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Return the packet to the pool */ 72262306a36Sopenharmony_ci sbus_writew(0, &rd->mblength); 72362306a36Sopenharmony_ci sbus_writeb(LE_R1_OWN, &rd->rmd1_bits); 72462306a36Sopenharmony_ci entry = RX_NEXT(entry); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci lp->rx_new = entry; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void lance_tx_pio(struct net_device *dev) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 73362306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 73462306a36Sopenharmony_ci int i, j; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci spin_lock(&lp->lock); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci j = lp->tx_old; 73962306a36Sopenharmony_ci for (i = j; i != lp->tx_new; i = j) { 74062306a36Sopenharmony_ci struct lance_tx_desc __iomem *td = &ib->btx_ring [i]; 74162306a36Sopenharmony_ci u8 bits = sbus_readb(&td->tmd1_bits); 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* If we hit a packet not owned by us, stop */ 74462306a36Sopenharmony_ci if (bits & LE_T1_OWN) 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci if (bits & LE_T1_ERR) { 74862306a36Sopenharmony_ci u16 status = sbus_readw(&td->misc); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci dev->stats.tx_errors++; 75162306a36Sopenharmony_ci if (status & LE_T3_RTY) dev->stats.tx_aborted_errors++; 75262306a36Sopenharmony_ci if (status & LE_T3_LCOL) dev->stats.tx_window_errors++; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (status & LE_T3_CLOS) { 75562306a36Sopenharmony_ci dev->stats.tx_carrier_errors++; 75662306a36Sopenharmony_ci if (lp->auto_select) { 75762306a36Sopenharmony_ci lp->tpe = 1 - lp->tpe; 75862306a36Sopenharmony_ci printk(KERN_NOTICE "%s: Carrier Lost, trying %s\n", 75962306a36Sopenharmony_ci dev->name, lp->tpe?"TPE":"AUI"); 76062306a36Sopenharmony_ci STOP_LANCE(lp); 76162306a36Sopenharmony_ci lp->init_ring(dev); 76262306a36Sopenharmony_ci load_csrs(lp); 76362306a36Sopenharmony_ci init_restart_lance(lp); 76462306a36Sopenharmony_ci goto out; 76562306a36Sopenharmony_ci } 76662306a36Sopenharmony_ci } 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci /* Buffer errors and underflows turn off the 76962306a36Sopenharmony_ci * transmitter, restart the adapter. 77062306a36Sopenharmony_ci */ 77162306a36Sopenharmony_ci if (status & (LE_T3_BUF|LE_T3_UFL)) { 77262306a36Sopenharmony_ci dev->stats.tx_fifo_errors++; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci printk(KERN_ERR "%s: Tx: ERR_BUF|ERR_UFL, restarting\n", 77562306a36Sopenharmony_ci dev->name); 77662306a36Sopenharmony_ci STOP_LANCE(lp); 77762306a36Sopenharmony_ci lp->init_ring(dev); 77862306a36Sopenharmony_ci load_csrs(lp); 77962306a36Sopenharmony_ci init_restart_lance(lp); 78062306a36Sopenharmony_ci goto out; 78162306a36Sopenharmony_ci } 78262306a36Sopenharmony_ci } else if ((bits & LE_T1_POK) == LE_T1_POK) { 78362306a36Sopenharmony_ci /* 78462306a36Sopenharmony_ci * So we don't count the packet more than once. 78562306a36Sopenharmony_ci */ 78662306a36Sopenharmony_ci sbus_writeb(bits & ~(LE_T1_POK), &td->tmd1_bits); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci /* One collision before packet was sent. */ 78962306a36Sopenharmony_ci if (bits & LE_T1_EONE) 79062306a36Sopenharmony_ci dev->stats.collisions++; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci /* More than one collision, be optimistic. */ 79362306a36Sopenharmony_ci if (bits & LE_T1_EMORE) 79462306a36Sopenharmony_ci dev->stats.collisions += 2; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci dev->stats.tx_packets++; 79762306a36Sopenharmony_ci } 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci j = TX_NEXT(j); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci lp->tx_old = j; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci if (netif_queue_stopped(dev) && 80462306a36Sopenharmony_ci TX_BUFFS_AVAIL > 0) 80562306a36Sopenharmony_ci netif_wake_queue(dev); 80662306a36Sopenharmony_ciout: 80762306a36Sopenharmony_ci spin_unlock(&lp->lock); 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic irqreturn_t lance_interrupt(int irq, void *dev_id) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct net_device *dev = dev_id; 81362306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 81462306a36Sopenharmony_ci int csr0; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci sbus_writew(LE_CSR0, lp->lregs + RAP); 81762306a36Sopenharmony_ci csr0 = sbus_readw(lp->lregs + RDP); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* Acknowledge all the interrupt sources ASAP */ 82062306a36Sopenharmony_ci sbus_writew(csr0 & (LE_C0_INTR | LE_C0_TINT | LE_C0_RINT), 82162306a36Sopenharmony_ci lp->lregs + RDP); 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if ((csr0 & LE_C0_ERR) != 0) { 82462306a36Sopenharmony_ci /* Clear the error condition */ 82562306a36Sopenharmony_ci sbus_writew((LE_C0_BABL | LE_C0_ERR | LE_C0_MISS | 82662306a36Sopenharmony_ci LE_C0_CERR | LE_C0_MERR), 82762306a36Sopenharmony_ci lp->lregs + RDP); 82862306a36Sopenharmony_ci } 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (csr0 & LE_C0_RINT) 83162306a36Sopenharmony_ci lp->rx(dev); 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci if (csr0 & LE_C0_TINT) 83462306a36Sopenharmony_ci lp->tx(dev); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (csr0 & LE_C0_BABL) 83762306a36Sopenharmony_ci dev->stats.tx_errors++; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci if (csr0 & LE_C0_MISS) 84062306a36Sopenharmony_ci dev->stats.rx_errors++; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (csr0 & LE_C0_MERR) { 84362306a36Sopenharmony_ci if (lp->dregs) { 84462306a36Sopenharmony_ci u32 addr = sbus_readl(lp->dregs + DMA_ADDR); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci printk(KERN_ERR "%s: Memory error, status %04x, addr %06x\n", 84762306a36Sopenharmony_ci dev->name, csr0, addr & 0xffffff); 84862306a36Sopenharmony_ci } else { 84962306a36Sopenharmony_ci printk(KERN_ERR "%s: Memory error, status %04x\n", 85062306a36Sopenharmony_ci dev->name, csr0); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci sbus_writew(LE_C0_STOP, lp->lregs + RDP); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci if (lp->dregs) { 85662306a36Sopenharmony_ci u32 dma_csr = sbus_readl(lp->dregs + DMA_CSR); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci dma_csr |= DMA_FIFO_INV; 85962306a36Sopenharmony_ci sbus_writel(dma_csr, lp->dregs + DMA_CSR); 86062306a36Sopenharmony_ci } 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci lp->init_ring(dev); 86362306a36Sopenharmony_ci load_csrs(lp); 86462306a36Sopenharmony_ci init_restart_lance(lp); 86562306a36Sopenharmony_ci netif_wake_queue(dev); 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci sbus_writew(LE_C0_INEA, lp->lregs + RDP); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return IRQ_HANDLED; 87162306a36Sopenharmony_ci} 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci/* Build a fake network packet and send it to ourselves. */ 87462306a36Sopenharmony_cistatic void build_fake_packet(struct lance_private *lp) 87562306a36Sopenharmony_ci{ 87662306a36Sopenharmony_ci struct net_device *dev = lp->dev; 87762306a36Sopenharmony_ci int i, entry; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci entry = lp->tx_new & TX_RING_MOD_MASK; 88062306a36Sopenharmony_ci if (lp->pio_buffer) { 88162306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 88262306a36Sopenharmony_ci u16 __iomem *packet = (u16 __iomem *) &(ib->tx_buf[entry][0]); 88362306a36Sopenharmony_ci struct ethhdr __iomem *eth = (struct ethhdr __iomem *) packet; 88462306a36Sopenharmony_ci for (i = 0; i < (ETH_ZLEN / sizeof(u16)); i++) 88562306a36Sopenharmony_ci sbus_writew(0, &packet[i]); 88662306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 88762306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[i], ð->h_dest[i]); 88862306a36Sopenharmony_ci sbus_writeb(dev->dev_addr[i], ð->h_source[i]); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci sbus_writew((-ETH_ZLEN) | 0xf000, &ib->btx_ring[entry].length); 89162306a36Sopenharmony_ci sbus_writew(0, &ib->btx_ring[entry].misc); 89262306a36Sopenharmony_ci sbus_writeb(LE_T1_POK|LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits); 89362306a36Sopenharmony_ci } else { 89462306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 89562306a36Sopenharmony_ci u16 *packet = (u16 *) &(ib->tx_buf[entry][0]); 89662306a36Sopenharmony_ci struct ethhdr *eth = (struct ethhdr *) packet; 89762306a36Sopenharmony_ci memset(packet, 0, ETH_ZLEN); 89862306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 89962306a36Sopenharmony_ci eth->h_dest[i] = dev->dev_addr[i]; 90062306a36Sopenharmony_ci eth->h_source[i] = dev->dev_addr[i]; 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci ib->btx_ring[entry].length = (-ETH_ZLEN) | 0xf000; 90362306a36Sopenharmony_ci ib->btx_ring[entry].misc = 0; 90462306a36Sopenharmony_ci ib->btx_ring[entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); 90562306a36Sopenharmony_ci } 90662306a36Sopenharmony_ci lp->tx_new = TX_NEXT(entry); 90762306a36Sopenharmony_ci} 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_cistatic int lance_open(struct net_device *dev) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 91262306a36Sopenharmony_ci int status = 0; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci STOP_LANCE(lp); 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (request_irq(dev->irq, lance_interrupt, IRQF_SHARED, 91762306a36Sopenharmony_ci lancestr, (void *) dev)) { 91862306a36Sopenharmony_ci printk(KERN_ERR "Lance: Can't get irq %d\n", dev->irq); 91962306a36Sopenharmony_ci return -EAGAIN; 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci /* On the 4m, setup the ledma to provide the upper bits for buffers */ 92362306a36Sopenharmony_ci if (lp->dregs) { 92462306a36Sopenharmony_ci u32 regval = lp->init_block_dvma & 0xff000000; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci sbus_writel(regval, lp->dregs + DMA_TEST); 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci /* Set mode and clear multicast filter only at device open, 93062306a36Sopenharmony_ci * so that lance_init_ring() called at any error will not 93162306a36Sopenharmony_ci * forget multicast filters. 93262306a36Sopenharmony_ci * 93362306a36Sopenharmony_ci * BTW it is common bug in all lance drivers! --ANK 93462306a36Sopenharmony_ci */ 93562306a36Sopenharmony_ci if (lp->pio_buffer) { 93662306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 93762306a36Sopenharmony_ci sbus_writew(0, &ib->mode); 93862306a36Sopenharmony_ci sbus_writel(0, &ib->filter[0]); 93962306a36Sopenharmony_ci sbus_writel(0, &ib->filter[1]); 94062306a36Sopenharmony_ci } else { 94162306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 94262306a36Sopenharmony_ci ib->mode = 0; 94362306a36Sopenharmony_ci ib->filter [0] = 0; 94462306a36Sopenharmony_ci ib->filter [1] = 0; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci lp->init_ring(dev); 94862306a36Sopenharmony_ci load_csrs(lp); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci netif_start_queue(dev); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci status = init_restart_lance(lp); 95362306a36Sopenharmony_ci if (!status && lp->auto_select) { 95462306a36Sopenharmony_ci build_fake_packet(lp); 95562306a36Sopenharmony_ci sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP); 95662306a36Sopenharmony_ci } 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci return status; 95962306a36Sopenharmony_ci} 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_cistatic int lance_close(struct net_device *dev) 96262306a36Sopenharmony_ci{ 96362306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci netif_stop_queue(dev); 96662306a36Sopenharmony_ci del_timer_sync(&lp->multicast_timer); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci STOP_LANCE(lp); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci free_irq(dev->irq, (void *) dev); 97162306a36Sopenharmony_ci return 0; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic int lance_reset(struct net_device *dev) 97562306a36Sopenharmony_ci{ 97662306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 97762306a36Sopenharmony_ci int status; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci STOP_LANCE(lp); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* On the 4m, reset the dma too */ 98262306a36Sopenharmony_ci if (lp->dregs) { 98362306a36Sopenharmony_ci u32 csr, addr; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci printk(KERN_ERR "resetting ledma\n"); 98662306a36Sopenharmony_ci csr = sbus_readl(lp->dregs + DMA_CSR); 98762306a36Sopenharmony_ci sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR); 98862306a36Sopenharmony_ci udelay(200); 98962306a36Sopenharmony_ci sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci addr = lp->init_block_dvma & 0xff000000; 99262306a36Sopenharmony_ci sbus_writel(addr, lp->dregs + DMA_TEST); 99362306a36Sopenharmony_ci } 99462306a36Sopenharmony_ci lp->init_ring(dev); 99562306a36Sopenharmony_ci load_csrs(lp); 99662306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 99762306a36Sopenharmony_ci status = init_restart_lance(lp); 99862306a36Sopenharmony_ci return status; 99962306a36Sopenharmony_ci} 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_cistatic void lance_piocopy_from_skb(void __iomem *dest, unsigned char *src, int len) 100262306a36Sopenharmony_ci{ 100362306a36Sopenharmony_ci void __iomem *piobuf = dest; 100462306a36Sopenharmony_ci u32 *p32; 100562306a36Sopenharmony_ci u16 *p16; 100662306a36Sopenharmony_ci u8 *p8; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci switch ((unsigned long)src & 0x3) { 100962306a36Sopenharmony_ci case 0: 101062306a36Sopenharmony_ci p32 = (u32 *) src; 101162306a36Sopenharmony_ci while (len >= 4) { 101262306a36Sopenharmony_ci sbus_writel(*p32, piobuf); 101362306a36Sopenharmony_ci p32++; 101462306a36Sopenharmony_ci piobuf += 4; 101562306a36Sopenharmony_ci len -= 4; 101662306a36Sopenharmony_ci } 101762306a36Sopenharmony_ci src = (char *) p32; 101862306a36Sopenharmony_ci break; 101962306a36Sopenharmony_ci case 1: 102062306a36Sopenharmony_ci case 3: 102162306a36Sopenharmony_ci p8 = (u8 *) src; 102262306a36Sopenharmony_ci while (len >= 4) { 102362306a36Sopenharmony_ci u32 val; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci val = p8[0] << 24; 102662306a36Sopenharmony_ci val |= p8[1] << 16; 102762306a36Sopenharmony_ci val |= p8[2] << 8; 102862306a36Sopenharmony_ci val |= p8[3]; 102962306a36Sopenharmony_ci sbus_writel(val, piobuf); 103062306a36Sopenharmony_ci p8 += 4; 103162306a36Sopenharmony_ci piobuf += 4; 103262306a36Sopenharmony_ci len -= 4; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci src = (char *) p8; 103562306a36Sopenharmony_ci break; 103662306a36Sopenharmony_ci case 2: 103762306a36Sopenharmony_ci p16 = (u16 *) src; 103862306a36Sopenharmony_ci while (len >= 4) { 103962306a36Sopenharmony_ci u32 val = p16[0]<<16 | p16[1]; 104062306a36Sopenharmony_ci sbus_writel(val, piobuf); 104162306a36Sopenharmony_ci p16 += 2; 104262306a36Sopenharmony_ci piobuf += 4; 104362306a36Sopenharmony_ci len -= 4; 104462306a36Sopenharmony_ci } 104562306a36Sopenharmony_ci src = (char *) p16; 104662306a36Sopenharmony_ci break; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci if (len >= 2) { 104962306a36Sopenharmony_ci u16 val = src[0] << 8 | src[1]; 105062306a36Sopenharmony_ci sbus_writew(val, piobuf); 105162306a36Sopenharmony_ci src += 2; 105262306a36Sopenharmony_ci piobuf += 2; 105362306a36Sopenharmony_ci len -= 2; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci if (len >= 1) 105662306a36Sopenharmony_ci sbus_writeb(src[0], piobuf); 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic void lance_piozero(void __iomem *dest, int len) 106062306a36Sopenharmony_ci{ 106162306a36Sopenharmony_ci void __iomem *piobuf = dest; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci if ((unsigned long)piobuf & 1) { 106462306a36Sopenharmony_ci sbus_writeb(0, piobuf); 106562306a36Sopenharmony_ci piobuf += 1; 106662306a36Sopenharmony_ci len -= 1; 106762306a36Sopenharmony_ci if (len == 0) 106862306a36Sopenharmony_ci return; 106962306a36Sopenharmony_ci } 107062306a36Sopenharmony_ci if (len == 1) { 107162306a36Sopenharmony_ci sbus_writeb(0, piobuf); 107262306a36Sopenharmony_ci return; 107362306a36Sopenharmony_ci } 107462306a36Sopenharmony_ci if ((unsigned long)piobuf & 2) { 107562306a36Sopenharmony_ci sbus_writew(0, piobuf); 107662306a36Sopenharmony_ci piobuf += 2; 107762306a36Sopenharmony_ci len -= 2; 107862306a36Sopenharmony_ci if (len == 0) 107962306a36Sopenharmony_ci return; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci while (len >= 4) { 108262306a36Sopenharmony_ci sbus_writel(0, piobuf); 108362306a36Sopenharmony_ci piobuf += 4; 108462306a36Sopenharmony_ci len -= 4; 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci if (len >= 2) { 108762306a36Sopenharmony_ci sbus_writew(0, piobuf); 108862306a36Sopenharmony_ci piobuf += 2; 108962306a36Sopenharmony_ci len -= 2; 109062306a36Sopenharmony_ci } 109162306a36Sopenharmony_ci if (len >= 1) 109262306a36Sopenharmony_ci sbus_writeb(0, piobuf); 109362306a36Sopenharmony_ci} 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_cistatic void lance_tx_timeout(struct net_device *dev, unsigned int txqueue) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci printk(KERN_ERR "%s: transmit timed out, status %04x, reset\n", 110062306a36Sopenharmony_ci dev->name, sbus_readw(lp->lregs + RDP)); 110162306a36Sopenharmony_ci lance_reset(dev); 110262306a36Sopenharmony_ci netif_wake_queue(dev); 110362306a36Sopenharmony_ci} 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb, struct net_device *dev) 110662306a36Sopenharmony_ci{ 110762306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 110862306a36Sopenharmony_ci int entry, skblen, len; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci skblen = skb->len; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci len = (skblen <= ETH_ZLEN) ? ETH_ZLEN : skblen; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci spin_lock_irq(&lp->lock); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci dev->stats.tx_bytes += len; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci entry = lp->tx_new & TX_RING_MOD_MASK; 111962306a36Sopenharmony_ci if (lp->pio_buffer) { 112062306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 112162306a36Sopenharmony_ci sbus_writew((-len) | 0xf000, &ib->btx_ring[entry].length); 112262306a36Sopenharmony_ci sbus_writew(0, &ib->btx_ring[entry].misc); 112362306a36Sopenharmony_ci lance_piocopy_from_skb(&ib->tx_buf[entry][0], skb->data, skblen); 112462306a36Sopenharmony_ci if (len != skblen) 112562306a36Sopenharmony_ci lance_piozero(&ib->tx_buf[entry][skblen], len - skblen); 112662306a36Sopenharmony_ci sbus_writeb(LE_T1_POK | LE_T1_OWN, &ib->btx_ring[entry].tmd1_bits); 112762306a36Sopenharmony_ci } else { 112862306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 112962306a36Sopenharmony_ci ib->btx_ring [entry].length = (-len) | 0xf000; 113062306a36Sopenharmony_ci ib->btx_ring [entry].misc = 0; 113162306a36Sopenharmony_ci skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen); 113262306a36Sopenharmony_ci if (len != skblen) 113362306a36Sopenharmony_ci memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen); 113462306a36Sopenharmony_ci ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN); 113562306a36Sopenharmony_ci } 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci lp->tx_new = TX_NEXT(entry); 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci if (TX_BUFFS_AVAIL <= 0) 114062306a36Sopenharmony_ci netif_stop_queue(dev); 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci /* Kick the lance: transmit now */ 114362306a36Sopenharmony_ci sbus_writew(LE_C0_INEA | LE_C0_TDMD, lp->lregs + RDP); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Read back CSR to invalidate the E-Cache. 114662306a36Sopenharmony_ci * This is needed, because DMA_DSBL_WR_INV is set. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci if (lp->dregs) 114962306a36Sopenharmony_ci sbus_readw(lp->lregs + RDP); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci spin_unlock_irq(&lp->lock); 115262306a36Sopenharmony_ci 115362306a36Sopenharmony_ci dev_kfree_skb(skb); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci return NETDEV_TX_OK; 115662306a36Sopenharmony_ci} 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci/* taken from the depca driver */ 115962306a36Sopenharmony_cistatic void lance_load_multicast(struct net_device *dev) 116062306a36Sopenharmony_ci{ 116162306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 116262306a36Sopenharmony_ci struct netdev_hw_addr *ha; 116362306a36Sopenharmony_ci u32 crc; 116462306a36Sopenharmony_ci u32 val; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* set all multicast bits */ 116762306a36Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) 116862306a36Sopenharmony_ci val = ~0; 116962306a36Sopenharmony_ci else 117062306a36Sopenharmony_ci val = 0; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci if (lp->pio_buffer) { 117362306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 117462306a36Sopenharmony_ci sbus_writel(val, &ib->filter[0]); 117562306a36Sopenharmony_ci sbus_writel(val, &ib->filter[1]); 117662306a36Sopenharmony_ci } else { 117762306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 117862306a36Sopenharmony_ci ib->filter [0] = val; 117962306a36Sopenharmony_ci ib->filter [1] = val; 118062306a36Sopenharmony_ci } 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) 118362306a36Sopenharmony_ci return; 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci /* Add addresses */ 118662306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 118762306a36Sopenharmony_ci crc = ether_crc_le(6, ha->addr); 118862306a36Sopenharmony_ci crc = crc >> 26; 118962306a36Sopenharmony_ci if (lp->pio_buffer) { 119062306a36Sopenharmony_ci struct lance_init_block __iomem *ib = lp->init_block_iomem; 119162306a36Sopenharmony_ci u16 __iomem *mcast_table = (u16 __iomem *) &ib->filter; 119262306a36Sopenharmony_ci u16 tmp = sbus_readw(&mcast_table[crc>>4]); 119362306a36Sopenharmony_ci tmp |= 1 << (crc & 0xf); 119462306a36Sopenharmony_ci sbus_writew(tmp, &mcast_table[crc>>4]); 119562306a36Sopenharmony_ci } else { 119662306a36Sopenharmony_ci struct lance_init_block *ib = lp->init_block_mem; 119762306a36Sopenharmony_ci u16 *mcast_table = (u16 *) &ib->filter; 119862306a36Sopenharmony_ci mcast_table [crc >> 4] |= 1 << (crc & 0xf); 119962306a36Sopenharmony_ci } 120062306a36Sopenharmony_ci } 120162306a36Sopenharmony_ci} 120262306a36Sopenharmony_ci 120362306a36Sopenharmony_cistatic void lance_set_multicast(struct net_device *dev) 120462306a36Sopenharmony_ci{ 120562306a36Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 120662306a36Sopenharmony_ci struct lance_init_block *ib_mem = lp->init_block_mem; 120762306a36Sopenharmony_ci struct lance_init_block __iomem *ib_iomem = lp->init_block_iomem; 120862306a36Sopenharmony_ci u16 mode; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci if (!netif_running(dev)) 121162306a36Sopenharmony_ci return; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci if (lp->tx_old != lp->tx_new) { 121462306a36Sopenharmony_ci mod_timer(&lp->multicast_timer, jiffies + 4); 121562306a36Sopenharmony_ci netif_wake_queue(dev); 121662306a36Sopenharmony_ci return; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci netif_stop_queue(dev); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci STOP_LANCE(lp); 122262306a36Sopenharmony_ci lp->init_ring(dev); 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci if (lp->pio_buffer) 122562306a36Sopenharmony_ci mode = sbus_readw(&ib_iomem->mode); 122662306a36Sopenharmony_ci else 122762306a36Sopenharmony_ci mode = ib_mem->mode; 122862306a36Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 122962306a36Sopenharmony_ci mode |= LE_MO_PROM; 123062306a36Sopenharmony_ci if (lp->pio_buffer) 123162306a36Sopenharmony_ci sbus_writew(mode, &ib_iomem->mode); 123262306a36Sopenharmony_ci else 123362306a36Sopenharmony_ci ib_mem->mode = mode; 123462306a36Sopenharmony_ci } else { 123562306a36Sopenharmony_ci mode &= ~LE_MO_PROM; 123662306a36Sopenharmony_ci if (lp->pio_buffer) 123762306a36Sopenharmony_ci sbus_writew(mode, &ib_iomem->mode); 123862306a36Sopenharmony_ci else 123962306a36Sopenharmony_ci ib_mem->mode = mode; 124062306a36Sopenharmony_ci lance_load_multicast(dev); 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci load_csrs(lp); 124362306a36Sopenharmony_ci init_restart_lance(lp); 124462306a36Sopenharmony_ci netif_wake_queue(dev); 124562306a36Sopenharmony_ci} 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cistatic void lance_set_multicast_retry(struct timer_list *t) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci struct lance_private *lp = from_timer(lp, t, multicast_timer); 125062306a36Sopenharmony_ci struct net_device *dev = lp->dev; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci lance_set_multicast(dev); 125362306a36Sopenharmony_ci} 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_cistatic void lance_free_hwresources(struct lance_private *lp) 125662306a36Sopenharmony_ci{ 125762306a36Sopenharmony_ci if (lp->lregs) 125862306a36Sopenharmony_ci of_iounmap(&lp->op->resource[0], lp->lregs, LANCE_REG_SIZE); 125962306a36Sopenharmony_ci if (lp->dregs) { 126062306a36Sopenharmony_ci struct platform_device *ledma = lp->ledma; 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci of_iounmap(&ledma->resource[0], lp->dregs, 126362306a36Sopenharmony_ci resource_size(&ledma->resource[0])); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci if (lp->init_block_iomem) { 126662306a36Sopenharmony_ci of_iounmap(&lp->lebuffer->resource[0], lp->init_block_iomem, 126762306a36Sopenharmony_ci sizeof(struct lance_init_block)); 126862306a36Sopenharmony_ci } else if (lp->init_block_mem) { 126962306a36Sopenharmony_ci dma_free_coherent(&lp->op->dev, 127062306a36Sopenharmony_ci sizeof(struct lance_init_block), 127162306a36Sopenharmony_ci lp->init_block_mem, 127262306a36Sopenharmony_ci lp->init_block_dvma); 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci/* Ethtool support... */ 127762306a36Sopenharmony_cistatic void sparc_lance_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 127862306a36Sopenharmony_ci{ 127962306a36Sopenharmony_ci strscpy(info->driver, "sunlance", sizeof(info->driver)); 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_cistatic const struct ethtool_ops sparc_lance_ethtool_ops = { 128362306a36Sopenharmony_ci .get_drvinfo = sparc_lance_get_drvinfo, 128462306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 128562306a36Sopenharmony_ci}; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_cistatic const struct net_device_ops sparc_lance_ops = { 128862306a36Sopenharmony_ci .ndo_open = lance_open, 128962306a36Sopenharmony_ci .ndo_stop = lance_close, 129062306a36Sopenharmony_ci .ndo_start_xmit = lance_start_xmit, 129162306a36Sopenharmony_ci .ndo_set_rx_mode = lance_set_multicast, 129262306a36Sopenharmony_ci .ndo_tx_timeout = lance_tx_timeout, 129362306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 129462306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 129562306a36Sopenharmony_ci}; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int sparc_lance_probe_one(struct platform_device *op, 129862306a36Sopenharmony_ci struct platform_device *ledma, 129962306a36Sopenharmony_ci struct platform_device *lebuffer) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct device_node *dp = op->dev.of_node; 130262306a36Sopenharmony_ci struct lance_private *lp; 130362306a36Sopenharmony_ci struct net_device *dev; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci dev = alloc_etherdev(sizeof(struct lance_private) + 8); 130662306a36Sopenharmony_ci if (!dev) 130762306a36Sopenharmony_ci return -ENOMEM; 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci lp = netdev_priv(dev); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci spin_lock_init(&lp->lock); 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci /* Copy the IDPROM ethernet address to the device structure, later we 131462306a36Sopenharmony_ci * will copy the address in the device structure to the lance 131562306a36Sopenharmony_ci * initialization block. 131662306a36Sopenharmony_ci */ 131762306a36Sopenharmony_ci eth_hw_addr_set(dev, idprom->id_ethaddr); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci /* Get the IO region */ 132062306a36Sopenharmony_ci lp->lregs = of_ioremap(&op->resource[0], 0, 132162306a36Sopenharmony_ci LANCE_REG_SIZE, lancestr); 132262306a36Sopenharmony_ci if (!lp->lregs) { 132362306a36Sopenharmony_ci printk(KERN_ERR "SunLance: Cannot map registers.\n"); 132462306a36Sopenharmony_ci goto fail; 132562306a36Sopenharmony_ci } 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci lp->ledma = ledma; 132862306a36Sopenharmony_ci if (lp->ledma) { 132962306a36Sopenharmony_ci lp->dregs = of_ioremap(&ledma->resource[0], 0, 133062306a36Sopenharmony_ci resource_size(&ledma->resource[0]), 133162306a36Sopenharmony_ci "ledma"); 133262306a36Sopenharmony_ci if (!lp->dregs) { 133362306a36Sopenharmony_ci printk(KERN_ERR "SunLance: Cannot map " 133462306a36Sopenharmony_ci "ledma registers.\n"); 133562306a36Sopenharmony_ci goto fail; 133662306a36Sopenharmony_ci } 133762306a36Sopenharmony_ci } 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci lp->op = op; 134062306a36Sopenharmony_ci lp->lebuffer = lebuffer; 134162306a36Sopenharmony_ci if (lebuffer) { 134262306a36Sopenharmony_ci /* sanity check */ 134362306a36Sopenharmony_ci if (lebuffer->resource[0].start & 7) { 134462306a36Sopenharmony_ci printk(KERN_ERR "SunLance: ERROR: Rx and Tx rings not on even boundary.\n"); 134562306a36Sopenharmony_ci goto fail; 134662306a36Sopenharmony_ci } 134762306a36Sopenharmony_ci lp->init_block_iomem = 134862306a36Sopenharmony_ci of_ioremap(&lebuffer->resource[0], 0, 134962306a36Sopenharmony_ci sizeof(struct lance_init_block), "lebuffer"); 135062306a36Sopenharmony_ci if (!lp->init_block_iomem) { 135162306a36Sopenharmony_ci printk(KERN_ERR "SunLance: Cannot map PIO buffer.\n"); 135262306a36Sopenharmony_ci goto fail; 135362306a36Sopenharmony_ci } 135462306a36Sopenharmony_ci lp->init_block_dvma = 0; 135562306a36Sopenharmony_ci lp->pio_buffer = 1; 135662306a36Sopenharmony_ci lp->init_ring = lance_init_ring_pio; 135762306a36Sopenharmony_ci lp->rx = lance_rx_pio; 135862306a36Sopenharmony_ci lp->tx = lance_tx_pio; 135962306a36Sopenharmony_ci } else { 136062306a36Sopenharmony_ci lp->init_block_mem = 136162306a36Sopenharmony_ci dma_alloc_coherent(&op->dev, 136262306a36Sopenharmony_ci sizeof(struct lance_init_block), 136362306a36Sopenharmony_ci &lp->init_block_dvma, GFP_ATOMIC); 136462306a36Sopenharmony_ci if (!lp->init_block_mem) 136562306a36Sopenharmony_ci goto fail; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci lp->pio_buffer = 0; 136862306a36Sopenharmony_ci lp->init_ring = lance_init_ring_dvma; 136962306a36Sopenharmony_ci lp->rx = lance_rx_dvma; 137062306a36Sopenharmony_ci lp->tx = lance_tx_dvma; 137162306a36Sopenharmony_ci } 137262306a36Sopenharmony_ci lp->busmaster_regval = of_getintprop_default(dp, "busmaster-regval", 137362306a36Sopenharmony_ci (LE_C3_BSWP | 137462306a36Sopenharmony_ci LE_C3_ACON | 137562306a36Sopenharmony_ci LE_C3_BCON)); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci lp->name = lancestr; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci lp->burst_sizes = 0; 138062306a36Sopenharmony_ci if (lp->ledma) { 138162306a36Sopenharmony_ci struct device_node *ledma_dp = ledma->dev.of_node; 138262306a36Sopenharmony_ci struct device_node *sbus_dp; 138362306a36Sopenharmony_ci unsigned int sbmask; 138462306a36Sopenharmony_ci const char *prop; 138562306a36Sopenharmony_ci u32 csr; 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci /* Find burst-size property for ledma */ 138862306a36Sopenharmony_ci lp->burst_sizes = of_getintprop_default(ledma_dp, 138962306a36Sopenharmony_ci "burst-sizes", 0); 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci /* ledma may be capable of fast bursts, but sbus may not. */ 139262306a36Sopenharmony_ci sbus_dp = ledma_dp->parent; 139362306a36Sopenharmony_ci sbmask = of_getintprop_default(sbus_dp, "burst-sizes", 139462306a36Sopenharmony_ci DMA_BURSTBITS); 139562306a36Sopenharmony_ci lp->burst_sizes &= sbmask; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* Get the cable-selection property */ 139862306a36Sopenharmony_ci prop = of_get_property(ledma_dp, "cable-selection", NULL); 139962306a36Sopenharmony_ci if (!prop || prop[0] == '\0') { 140062306a36Sopenharmony_ci struct device_node *nd; 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci printk(KERN_INFO "SunLance: using " 140362306a36Sopenharmony_ci "auto-carrier-detection.\n"); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci nd = of_find_node_by_path("/options"); 140662306a36Sopenharmony_ci if (!nd) 140762306a36Sopenharmony_ci goto no_link_test; 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci prop = of_get_property(nd, "tpe-link-test?", NULL); 141062306a36Sopenharmony_ci if (!prop) 141162306a36Sopenharmony_ci goto node_put; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (strcmp(prop, "true")) { 141462306a36Sopenharmony_ci printk(KERN_NOTICE "SunLance: warning: overriding option " 141562306a36Sopenharmony_ci "'tpe-link-test?'\n"); 141662306a36Sopenharmony_ci printk(KERN_NOTICE "SunLance: warning: mail any problems " 141762306a36Sopenharmony_ci "to ecd@skynet.be\n"); 141862306a36Sopenharmony_ci auxio_set_lte(AUXIO_LTE_ON); 141962306a36Sopenharmony_ci } 142062306a36Sopenharmony_cinode_put: 142162306a36Sopenharmony_ci of_node_put(nd); 142262306a36Sopenharmony_cino_link_test: 142362306a36Sopenharmony_ci lp->auto_select = 1; 142462306a36Sopenharmony_ci lp->tpe = 0; 142562306a36Sopenharmony_ci } else if (!strcmp(prop, "aui")) { 142662306a36Sopenharmony_ci lp->auto_select = 0; 142762306a36Sopenharmony_ci lp->tpe = 0; 142862306a36Sopenharmony_ci } else { 142962306a36Sopenharmony_ci lp->auto_select = 0; 143062306a36Sopenharmony_ci lp->tpe = 1; 143162306a36Sopenharmony_ci } 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci /* Reset ledma */ 143462306a36Sopenharmony_ci csr = sbus_readl(lp->dregs + DMA_CSR); 143562306a36Sopenharmony_ci sbus_writel(csr | DMA_RST_ENET, lp->dregs + DMA_CSR); 143662306a36Sopenharmony_ci udelay(200); 143762306a36Sopenharmony_ci sbus_writel(csr & ~DMA_RST_ENET, lp->dregs + DMA_CSR); 143862306a36Sopenharmony_ci } else 143962306a36Sopenharmony_ci lp->dregs = NULL; 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci lp->dev = dev; 144262306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &op->dev); 144362306a36Sopenharmony_ci dev->watchdog_timeo = 5*HZ; 144462306a36Sopenharmony_ci dev->ethtool_ops = &sparc_lance_ethtool_ops; 144562306a36Sopenharmony_ci dev->netdev_ops = &sparc_lance_ops; 144662306a36Sopenharmony_ci 144762306a36Sopenharmony_ci dev->irq = op->archdata.irqs[0]; 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci /* We cannot sleep if the chip is busy during a 145062306a36Sopenharmony_ci * multicast list update event, because such events 145162306a36Sopenharmony_ci * can occur from interrupts (ex. IPv6). So we 145262306a36Sopenharmony_ci * use a timer to try again later when necessary. -DaveM 145362306a36Sopenharmony_ci */ 145462306a36Sopenharmony_ci timer_setup(&lp->multicast_timer, lance_set_multicast_retry, 0); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci if (register_netdev(dev)) { 145762306a36Sopenharmony_ci printk(KERN_ERR "SunLance: Cannot register device.\n"); 145862306a36Sopenharmony_ci goto fail; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci platform_set_drvdata(op, lp); 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci printk(KERN_INFO "%s: LANCE %pM\n", 146462306a36Sopenharmony_ci dev->name, dev->dev_addr); 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci return 0; 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_cifail: 146962306a36Sopenharmony_ci lance_free_hwresources(lp); 147062306a36Sopenharmony_ci free_netdev(dev); 147162306a36Sopenharmony_ci return -ENODEV; 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic int sunlance_sbus_probe(struct platform_device *op) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci struct platform_device *parent = to_platform_device(op->dev.parent); 147762306a36Sopenharmony_ci struct device_node *parent_dp = parent->dev.of_node; 147862306a36Sopenharmony_ci int err; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (of_node_name_eq(parent_dp, "ledma")) { 148162306a36Sopenharmony_ci err = sparc_lance_probe_one(op, parent, NULL); 148262306a36Sopenharmony_ci } else if (of_node_name_eq(parent_dp, "lebuffer")) { 148362306a36Sopenharmony_ci err = sparc_lance_probe_one(op, NULL, parent); 148462306a36Sopenharmony_ci } else 148562306a36Sopenharmony_ci err = sparc_lance_probe_one(op, NULL, NULL); 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci return err; 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic int sunlance_sbus_remove(struct platform_device *op) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci struct lance_private *lp = platform_get_drvdata(op); 149362306a36Sopenharmony_ci struct net_device *net_dev = lp->dev; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci unregister_netdev(net_dev); 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_ci lance_free_hwresources(lp); 149862306a36Sopenharmony_ci 149962306a36Sopenharmony_ci free_netdev(net_dev); 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_ci return 0; 150262306a36Sopenharmony_ci} 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_cistatic const struct of_device_id sunlance_sbus_match[] = { 150562306a36Sopenharmony_ci { 150662306a36Sopenharmony_ci .name = "le", 150762306a36Sopenharmony_ci }, 150862306a36Sopenharmony_ci {}, 150962306a36Sopenharmony_ci}; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sunlance_sbus_match); 151262306a36Sopenharmony_ci 151362306a36Sopenharmony_cistatic struct platform_driver sunlance_sbus_driver = { 151462306a36Sopenharmony_ci .driver = { 151562306a36Sopenharmony_ci .name = "sunlance", 151662306a36Sopenharmony_ci .of_match_table = sunlance_sbus_match, 151762306a36Sopenharmony_ci }, 151862306a36Sopenharmony_ci .probe = sunlance_sbus_probe, 151962306a36Sopenharmony_ci .remove = sunlance_sbus_remove, 152062306a36Sopenharmony_ci}; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_cimodule_platform_driver(sunlance_sbus_driver); 1523