18c2ecf20Sopenharmony_ci/* sun3lance.c: Ethernet driver for SUN3 Lance chip */ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Sun3 Lance ethernet driver, by Sam Creasey (sammy@users.qual.net). 58c2ecf20Sopenharmony_ci This driver is a part of the linux kernel, and is thus distributed 68c2ecf20Sopenharmony_ci under the GNU General Public License. 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci The values used in LANCE_OBIO and LANCE_IRQ seem to be empirically 98c2ecf20Sopenharmony_ci true for the correct IRQ and address of the lance registers. They 108c2ecf20Sopenharmony_ci have not been widely tested, however. What we probably need is a 118c2ecf20Sopenharmony_ci "proper" way to search for a device in the sun3's prom, but, alas, 128c2ecf20Sopenharmony_ci linux has no such thing. 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci This driver is largely based on atarilance.c, by Roman Hodek. Other 158c2ecf20Sopenharmony_ci sources of inspiration were the NetBSD sun3 am7990 driver, and the 168c2ecf20Sopenharmony_ci linux sparc lance driver (sunlance.c). 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci There are more assumptions made throughout this driver, it almost 198c2ecf20Sopenharmony_ci certainly still needs work, but it does work at least for RARP/BOOTP and 208c2ecf20Sopenharmony_ci mounting the root NFS filesystem. 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci*/ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic const char version[] = 258c2ecf20Sopenharmony_ci"sun3lance.c: v1.2 1/12/2001 Sam Creasey (sammy@sammy.net)\n"; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <linux/stddef.h> 298c2ecf20Sopenharmony_ci#include <linux/kernel.h> 308c2ecf20Sopenharmony_ci#include <linux/string.h> 318c2ecf20Sopenharmony_ci#include <linux/errno.h> 328c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 338c2ecf20Sopenharmony_ci#include <linux/init.h> 348c2ecf20Sopenharmony_ci#include <linux/ioport.h> 358c2ecf20Sopenharmony_ci#include <linux/delay.h> 368c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 388c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 398c2ecf20Sopenharmony_ci#include <linux/bitops.h> 408c2ecf20Sopenharmony_ci#include <linux/pgtable.h> 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 438c2ecf20Sopenharmony_ci#include <asm/setup.h> 448c2ecf20Sopenharmony_ci#include <asm/irq.h> 458c2ecf20Sopenharmony_ci#include <asm/io.h> 468c2ecf20Sopenharmony_ci#include <asm/dvma.h> 478c2ecf20Sopenharmony_ci#include <asm/idprom.h> 488c2ecf20Sopenharmony_ci#include <asm/machines.h> 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 518c2ecf20Sopenharmony_ci#include <asm/sun3mmu.h> 528c2ecf20Sopenharmony_ci#else 538c2ecf20Sopenharmony_ci#include <asm/sun3xprom.h> 548c2ecf20Sopenharmony_ci#endif 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci/* sun3/60 addr/irq for the lance chip. If your sun is different, 578c2ecf20Sopenharmony_ci change this. */ 588c2ecf20Sopenharmony_ci#define LANCE_OBIO 0x120000 598c2ecf20Sopenharmony_ci#define LANCE_IRQ IRQ_AUTO_3 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/* Debug level: 628c2ecf20Sopenharmony_ci * 0 = silent, print only serious errors 638c2ecf20Sopenharmony_ci * 1 = normal, print error messages 648c2ecf20Sopenharmony_ci * 2 = debug, print debug infos 658c2ecf20Sopenharmony_ci * 3 = debug, print even more debug infos (packet data) 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#define LANCE_DEBUG 0 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#ifdef LANCE_DEBUG 718c2ecf20Sopenharmony_cistatic int lance_debug = LANCE_DEBUG; 728c2ecf20Sopenharmony_ci#else 738c2ecf20Sopenharmony_cistatic int lance_debug = 1; 748c2ecf20Sopenharmony_ci#endif 758c2ecf20Sopenharmony_cimodule_param(lance_debug, int, 0); 768c2ecf20Sopenharmony_ciMODULE_PARM_DESC(lance_debug, "SUN3 Lance debug level (0-3)"); 778c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define DPRINTK(n,a) \ 808c2ecf20Sopenharmony_ci do { \ 818c2ecf20Sopenharmony_ci if (lance_debug >= n) \ 828c2ecf20Sopenharmony_ci printk a; \ 838c2ecf20Sopenharmony_ci } while( 0 ) 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci/* we're only using 32k of memory, so we use 4 TX 878c2ecf20Sopenharmony_ci buffers and 16 RX buffers. These values are expressed as log2. */ 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define TX_LOG_RING_SIZE 3 908c2ecf20Sopenharmony_ci#define RX_LOG_RING_SIZE 5 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/* These are the derived values */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci#define TX_RING_SIZE (1 << TX_LOG_RING_SIZE) 958c2ecf20Sopenharmony_ci#define TX_RING_LEN_BITS (TX_LOG_RING_SIZE << 5) 968c2ecf20Sopenharmony_ci#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define RX_RING_SIZE (1 << RX_LOG_RING_SIZE) 998c2ecf20Sopenharmony_ci#define RX_RING_LEN_BITS (RX_LOG_RING_SIZE << 5) 1008c2ecf20Sopenharmony_ci#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* Definitions for packet buffer access: */ 1038c2ecf20Sopenharmony_ci#define PKT_BUF_SZ 1544 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/* Get the address of a packet buffer corresponding to a given buffer head */ 1068c2ecf20Sopenharmony_ci#define PKTBUF_ADDR(head) (void *)((unsigned long)(MEM) | (head)->base) 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci/* The LANCE Rx and Tx ring descriptors. */ 1108c2ecf20Sopenharmony_cistruct lance_rx_head { 1118c2ecf20Sopenharmony_ci unsigned short base; /* Low word of base addr */ 1128c2ecf20Sopenharmony_ci volatile unsigned char flag; 1138c2ecf20Sopenharmony_ci unsigned char base_hi; /* High word of base addr (unused) */ 1148c2ecf20Sopenharmony_ci short buf_length; /* This length is 2s complement! */ 1158c2ecf20Sopenharmony_ci volatile short msg_length; /* This length is "normal". */ 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistruct lance_tx_head { 1198c2ecf20Sopenharmony_ci unsigned short base; /* Low word of base addr */ 1208c2ecf20Sopenharmony_ci volatile unsigned char flag; 1218c2ecf20Sopenharmony_ci unsigned char base_hi; /* High word of base addr (unused) */ 1228c2ecf20Sopenharmony_ci short length; /* Length is 2s complement! */ 1238c2ecf20Sopenharmony_ci volatile short misc; 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* The LANCE initialization block, described in databook. */ 1278c2ecf20Sopenharmony_cistruct lance_init_block { 1288c2ecf20Sopenharmony_ci unsigned short mode; /* Pre-set mode */ 1298c2ecf20Sopenharmony_ci unsigned char hwaddr[6]; /* Physical ethernet address */ 1308c2ecf20Sopenharmony_ci unsigned int filter[2]; /* Multicast filter (unused). */ 1318c2ecf20Sopenharmony_ci /* Receive and transmit ring base, along with length bits. */ 1328c2ecf20Sopenharmony_ci unsigned short rdra; 1338c2ecf20Sopenharmony_ci unsigned short rlen; 1348c2ecf20Sopenharmony_ci unsigned short tdra; 1358c2ecf20Sopenharmony_ci unsigned short tlen; 1368c2ecf20Sopenharmony_ci unsigned short pad[4]; /* is thie needed? */ 1378c2ecf20Sopenharmony_ci}; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* The whole layout of the Lance shared memory */ 1408c2ecf20Sopenharmony_cistruct lance_memory { 1418c2ecf20Sopenharmony_ci struct lance_init_block init; 1428c2ecf20Sopenharmony_ci struct lance_tx_head tx_head[TX_RING_SIZE]; 1438c2ecf20Sopenharmony_ci struct lance_rx_head rx_head[RX_RING_SIZE]; 1448c2ecf20Sopenharmony_ci char rx_data[RX_RING_SIZE][PKT_BUF_SZ]; 1458c2ecf20Sopenharmony_ci char tx_data[TX_RING_SIZE][PKT_BUF_SZ]; 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* The driver's private device structure */ 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistruct lance_private { 1518c2ecf20Sopenharmony_ci volatile unsigned short *iobase; 1528c2ecf20Sopenharmony_ci struct lance_memory *mem; 1538c2ecf20Sopenharmony_ci int new_rx, new_tx; /* The next free ring entry */ 1548c2ecf20Sopenharmony_ci int old_tx, old_rx; /* ring entry to be processed */ 1558c2ecf20Sopenharmony_ci/* These two must be longs for set_bit() */ 1568c2ecf20Sopenharmony_ci long tx_full; 1578c2ecf20Sopenharmony_ci long lock; 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/* I/O register access macros */ 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci#define MEM lp->mem 1638c2ecf20Sopenharmony_ci#define DREG lp->iobase[0] 1648c2ecf20Sopenharmony_ci#define AREG lp->iobase[1] 1658c2ecf20Sopenharmony_ci#define REGA(a) (*( AREG = (a), &DREG )) 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* Definitions for the Lance */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci/* tx_head flags */ 1708c2ecf20Sopenharmony_ci#define TMD1_ENP 0x01 /* end of packet */ 1718c2ecf20Sopenharmony_ci#define TMD1_STP 0x02 /* start of packet */ 1728c2ecf20Sopenharmony_ci#define TMD1_DEF 0x04 /* deferred */ 1738c2ecf20Sopenharmony_ci#define TMD1_ONE 0x08 /* one retry needed */ 1748c2ecf20Sopenharmony_ci#define TMD1_MORE 0x10 /* more than one retry needed */ 1758c2ecf20Sopenharmony_ci#define TMD1_ERR 0x40 /* error summary */ 1768c2ecf20Sopenharmony_ci#define TMD1_OWN 0x80 /* ownership (set: chip owns) */ 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci#define TMD1_OWN_CHIP TMD1_OWN 1798c2ecf20Sopenharmony_ci#define TMD1_OWN_HOST 0 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci/* tx_head misc field */ 1828c2ecf20Sopenharmony_ci#define TMD3_TDR 0x03FF /* Time Domain Reflectometry counter */ 1838c2ecf20Sopenharmony_ci#define TMD3_RTRY 0x0400 /* failed after 16 retries */ 1848c2ecf20Sopenharmony_ci#define TMD3_LCAR 0x0800 /* carrier lost */ 1858c2ecf20Sopenharmony_ci#define TMD3_LCOL 0x1000 /* late collision */ 1868c2ecf20Sopenharmony_ci#define TMD3_UFLO 0x4000 /* underflow (late memory) */ 1878c2ecf20Sopenharmony_ci#define TMD3_BUFF 0x8000 /* buffering error (no ENP) */ 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* rx_head flags */ 1908c2ecf20Sopenharmony_ci#define RMD1_ENP 0x01 /* end of packet */ 1918c2ecf20Sopenharmony_ci#define RMD1_STP 0x02 /* start of packet */ 1928c2ecf20Sopenharmony_ci#define RMD1_BUFF 0x04 /* buffer error */ 1938c2ecf20Sopenharmony_ci#define RMD1_CRC 0x08 /* CRC error */ 1948c2ecf20Sopenharmony_ci#define RMD1_OFLO 0x10 /* overflow */ 1958c2ecf20Sopenharmony_ci#define RMD1_FRAM 0x20 /* framing error */ 1968c2ecf20Sopenharmony_ci#define RMD1_ERR 0x40 /* error summary */ 1978c2ecf20Sopenharmony_ci#define RMD1_OWN 0x80 /* ownership (set: ship owns) */ 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#define RMD1_OWN_CHIP RMD1_OWN 2008c2ecf20Sopenharmony_ci#define RMD1_OWN_HOST 0 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/* register names */ 2038c2ecf20Sopenharmony_ci#define CSR0 0 /* mode/status */ 2048c2ecf20Sopenharmony_ci#define CSR1 1 /* init block addr (low) */ 2058c2ecf20Sopenharmony_ci#define CSR2 2 /* init block addr (high) */ 2068c2ecf20Sopenharmony_ci#define CSR3 3 /* misc */ 2078c2ecf20Sopenharmony_ci#define CSR8 8 /* address filter */ 2088c2ecf20Sopenharmony_ci#define CSR15 15 /* promiscuous mode */ 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* CSR0 */ 2118c2ecf20Sopenharmony_ci/* (R=readable, W=writeable, S=set on write, C=clear on write) */ 2128c2ecf20Sopenharmony_ci#define CSR0_INIT 0x0001 /* initialize (RS) */ 2138c2ecf20Sopenharmony_ci#define CSR0_STRT 0x0002 /* start (RS) */ 2148c2ecf20Sopenharmony_ci#define CSR0_STOP 0x0004 /* stop (RS) */ 2158c2ecf20Sopenharmony_ci#define CSR0_TDMD 0x0008 /* transmit demand (RS) */ 2168c2ecf20Sopenharmony_ci#define CSR0_TXON 0x0010 /* transmitter on (R) */ 2178c2ecf20Sopenharmony_ci#define CSR0_RXON 0x0020 /* receiver on (R) */ 2188c2ecf20Sopenharmony_ci#define CSR0_INEA 0x0040 /* interrupt enable (RW) */ 2198c2ecf20Sopenharmony_ci#define CSR0_INTR 0x0080 /* interrupt active (R) */ 2208c2ecf20Sopenharmony_ci#define CSR0_IDON 0x0100 /* initialization done (RC) */ 2218c2ecf20Sopenharmony_ci#define CSR0_TINT 0x0200 /* transmitter interrupt (RC) */ 2228c2ecf20Sopenharmony_ci#define CSR0_RINT 0x0400 /* receiver interrupt (RC) */ 2238c2ecf20Sopenharmony_ci#define CSR0_MERR 0x0800 /* memory error (RC) */ 2248c2ecf20Sopenharmony_ci#define CSR0_MISS 0x1000 /* missed frame (RC) */ 2258c2ecf20Sopenharmony_ci#define CSR0_CERR 0x2000 /* carrier error (no heartbeat :-) (RC) */ 2268c2ecf20Sopenharmony_ci#define CSR0_BABL 0x4000 /* babble: tx-ed too many bits (RC) */ 2278c2ecf20Sopenharmony_ci#define CSR0_ERR 0x8000 /* error (RC) */ 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci/* CSR3 */ 2308c2ecf20Sopenharmony_ci#define CSR3_BCON 0x0001 /* byte control */ 2318c2ecf20Sopenharmony_ci#define CSR3_ACON 0x0002 /* ALE control */ 2328c2ecf20Sopenharmony_ci#define CSR3_BSWP 0x0004 /* byte swap (1=big endian) */ 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci/***************************** Prototypes *****************************/ 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int lance_probe( struct net_device *dev); 2378c2ecf20Sopenharmony_cistatic int lance_open( struct net_device *dev ); 2388c2ecf20Sopenharmony_cistatic void lance_init_ring( struct net_device *dev ); 2398c2ecf20Sopenharmony_cistatic netdev_tx_t lance_start_xmit(struct sk_buff *skb, 2408c2ecf20Sopenharmony_ci struct net_device *dev); 2418c2ecf20Sopenharmony_cistatic irqreturn_t lance_interrupt( int irq, void *dev_id); 2428c2ecf20Sopenharmony_cistatic int lance_rx( struct net_device *dev ); 2438c2ecf20Sopenharmony_cistatic int lance_close( struct net_device *dev ); 2448c2ecf20Sopenharmony_cistatic void set_multicast_list( struct net_device *dev ); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci/************************* End of Prototypes **************************/ 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistruct net_device * __init sun3lance_probe(int unit) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci struct net_device *dev; 2518c2ecf20Sopenharmony_ci static int found; 2528c2ecf20Sopenharmony_ci int err = -ENODEV; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci if (!MACH_IS_SUN3 && !MACH_IS_SUN3X) 2558c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* check that this machine has an onboard lance */ 2588c2ecf20Sopenharmony_ci switch(idprom->id_machtype) { 2598c2ecf20Sopenharmony_ci case SM_SUN3|SM_3_50: 2608c2ecf20Sopenharmony_ci case SM_SUN3|SM_3_60: 2618c2ecf20Sopenharmony_ci case SM_SUN3X|SM_3_80: 2628c2ecf20Sopenharmony_ci /* these machines have lance */ 2638c2ecf20Sopenharmony_ci break; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci default: 2668c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (found) 2708c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci dev = alloc_etherdev(sizeof(struct lance_private)); 2738c2ecf20Sopenharmony_ci if (!dev) 2748c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2758c2ecf20Sopenharmony_ci if (unit >= 0) { 2768c2ecf20Sopenharmony_ci sprintf(dev->name, "eth%d", unit); 2778c2ecf20Sopenharmony_ci netdev_boot_setup_check(dev); 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (!lance_probe(dev)) 2818c2ecf20Sopenharmony_ci goto out; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci err = register_netdev(dev); 2848c2ecf20Sopenharmony_ci if (err) 2858c2ecf20Sopenharmony_ci goto out1; 2868c2ecf20Sopenharmony_ci found = 1; 2878c2ecf20Sopenharmony_ci return dev; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ciout1: 2908c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 2918c2ecf20Sopenharmony_ci iounmap((void __iomem *)dev->base_addr); 2928c2ecf20Sopenharmony_ci#endif 2938c2ecf20Sopenharmony_ciout: 2948c2ecf20Sopenharmony_ci free_netdev(dev); 2958c2ecf20Sopenharmony_ci return ERR_PTR(err); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic const struct net_device_ops lance_netdev_ops = { 2998c2ecf20Sopenharmony_ci .ndo_open = lance_open, 3008c2ecf20Sopenharmony_ci .ndo_stop = lance_close, 3018c2ecf20Sopenharmony_ci .ndo_start_xmit = lance_start_xmit, 3028c2ecf20Sopenharmony_ci .ndo_set_rx_mode = set_multicast_list, 3038c2ecf20Sopenharmony_ci .ndo_set_mac_address = NULL, 3048c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 3058c2ecf20Sopenharmony_ci}; 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_cistatic int __init lance_probe( struct net_device *dev) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci unsigned long ioaddr; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci struct lance_private *lp; 3128c2ecf20Sopenharmony_ci int i; 3138c2ecf20Sopenharmony_ci static int did_version; 3148c2ecf20Sopenharmony_ci volatile unsigned short *ioaddr_probe; 3158c2ecf20Sopenharmony_ci unsigned short tmp1, tmp2; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 3188c2ecf20Sopenharmony_ci ioaddr = (unsigned long)ioremap(LANCE_OBIO, PAGE_SIZE); 3198c2ecf20Sopenharmony_ci if (!ioaddr) 3208c2ecf20Sopenharmony_ci return 0; 3218c2ecf20Sopenharmony_ci#else 3228c2ecf20Sopenharmony_ci ioaddr = SUN3X_LANCE; 3238c2ecf20Sopenharmony_ci#endif 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci /* test to see if there's really a lance here */ 3268c2ecf20Sopenharmony_ci /* (CSRO_INIT shouldn't be readable) */ 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ioaddr_probe = (volatile unsigned short *)ioaddr; 3298c2ecf20Sopenharmony_ci tmp1 = ioaddr_probe[0]; 3308c2ecf20Sopenharmony_ci tmp2 = ioaddr_probe[1]; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ioaddr_probe[1] = CSR0; 3338c2ecf20Sopenharmony_ci ioaddr_probe[0] = CSR0_INIT | CSR0_STOP; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if(ioaddr_probe[0] != CSR0_STOP) { 3368c2ecf20Sopenharmony_ci ioaddr_probe[0] = tmp1; 3378c2ecf20Sopenharmony_ci ioaddr_probe[1] = tmp2; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 3408c2ecf20Sopenharmony_ci iounmap((void __iomem *)ioaddr); 3418c2ecf20Sopenharmony_ci#endif 3428c2ecf20Sopenharmony_ci return 0; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci lp = netdev_priv(dev); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* XXX - leak? */ 3488c2ecf20Sopenharmony_ci MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000); 3498c2ecf20Sopenharmony_ci if (MEM == NULL) { 3508c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 3518c2ecf20Sopenharmony_ci iounmap((void __iomem *)ioaddr); 3528c2ecf20Sopenharmony_ci#endif 3538c2ecf20Sopenharmony_ci printk(KERN_WARNING "SUN3 Lance couldn't allocate DVMA memory\n"); 3548c2ecf20Sopenharmony_ci return 0; 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci lp->iobase = (volatile unsigned short *)ioaddr; 3588c2ecf20Sopenharmony_ci dev->base_addr = (unsigned long)ioaddr; /* informational only */ 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STOP; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (request_irq(LANCE_IRQ, lance_interrupt, 0, "SUN3 Lance", dev) < 0) { 3638c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 3648c2ecf20Sopenharmony_ci iounmap((void __iomem *)ioaddr); 3658c2ecf20Sopenharmony_ci#endif 3668c2ecf20Sopenharmony_ci dvma_free((void *)MEM); 3678c2ecf20Sopenharmony_ci printk(KERN_WARNING "SUN3 Lance unable to allocate IRQ\n"); 3688c2ecf20Sopenharmony_ci return 0; 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci dev->irq = (unsigned short)LANCE_IRQ; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci printk("%s: SUN3 Lance at io %#lx, mem %#lx, irq %d, hwaddr ", 3748c2ecf20Sopenharmony_ci dev->name, 3758c2ecf20Sopenharmony_ci (unsigned long)ioaddr, 3768c2ecf20Sopenharmony_ci (unsigned long)MEM, 3778c2ecf20Sopenharmony_ci dev->irq); 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci /* copy in the ethernet address from the prom */ 3808c2ecf20Sopenharmony_ci for(i = 0; i < 6 ; i++) 3818c2ecf20Sopenharmony_ci dev->dev_addr[i] = idprom->id_ethaddr[i]; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* tell the card it's ether address, bytes swapped */ 3848c2ecf20Sopenharmony_ci MEM->init.hwaddr[0] = dev->dev_addr[1]; 3858c2ecf20Sopenharmony_ci MEM->init.hwaddr[1] = dev->dev_addr[0]; 3868c2ecf20Sopenharmony_ci MEM->init.hwaddr[2] = dev->dev_addr[3]; 3878c2ecf20Sopenharmony_ci MEM->init.hwaddr[3] = dev->dev_addr[2]; 3888c2ecf20Sopenharmony_ci MEM->init.hwaddr[4] = dev->dev_addr[5]; 3898c2ecf20Sopenharmony_ci MEM->init.hwaddr[5] = dev->dev_addr[4]; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci printk("%pM\n", dev->dev_addr); 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci MEM->init.mode = 0x0000; 3948c2ecf20Sopenharmony_ci MEM->init.filter[0] = 0x00000000; 3958c2ecf20Sopenharmony_ci MEM->init.filter[1] = 0x00000000; 3968c2ecf20Sopenharmony_ci MEM->init.rdra = dvma_vtob(MEM->rx_head); 3978c2ecf20Sopenharmony_ci MEM->init.rlen = (RX_LOG_RING_SIZE << 13) | 3988c2ecf20Sopenharmony_ci (dvma_vtob(MEM->rx_head) >> 16); 3998c2ecf20Sopenharmony_ci MEM->init.tdra = dvma_vtob(MEM->tx_head); 4008c2ecf20Sopenharmony_ci MEM->init.tlen = (TX_LOG_RING_SIZE << 13) | 4018c2ecf20Sopenharmony_ci (dvma_vtob(MEM->tx_head) >> 16); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci DPRINTK(2, ("initaddr: %08lx rx_ring: %08lx tx_ring: %08lx\n", 4048c2ecf20Sopenharmony_ci dvma_vtob(&(MEM->init)), dvma_vtob(MEM->rx_head), 4058c2ecf20Sopenharmony_ci (dvma_vtob(MEM->tx_head)))); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci if (did_version++ == 0) 4088c2ecf20Sopenharmony_ci printk( version ); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci dev->netdev_ops = &lance_netdev_ops; 4118c2ecf20Sopenharmony_ci// KLUDGE -- REMOVE ME 4128c2ecf20Sopenharmony_ci set_bit(__LINK_STATE_PRESENT, &dev->state); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci return 1; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic int lance_open( struct net_device *dev ) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 4218c2ecf20Sopenharmony_ci int i; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: lance_open()\n", dev->name )); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STOP; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci lance_init_ring(dev); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci /* From now on, AREG is kept to point to CSR0 */ 4308c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_INIT; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci i = 1000000; 4338c2ecf20Sopenharmony_ci while (--i > 0) 4348c2ecf20Sopenharmony_ci if (DREG & CSR0_IDON) 4358c2ecf20Sopenharmony_ci break; 4368c2ecf20Sopenharmony_ci if (i <= 0 || (DREG & CSR0_ERR)) { 4378c2ecf20Sopenharmony_ci DPRINTK( 2, ( "lance_open(): opening %s failed, i=%d, csr0=%04x\n", 4388c2ecf20Sopenharmony_ci dev->name, i, DREG )); 4398c2ecf20Sopenharmony_ci DREG = CSR0_STOP; 4408c2ecf20Sopenharmony_ci return -EIO; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci DREG = CSR0_IDON | CSR0_STRT | CSR0_INEA; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci netif_start_queue(dev); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: LANCE is open, csr0 %04x\n", dev->name, DREG )); 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return 0; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci/* Initialize the LANCE Rx and Tx rings. */ 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic void lance_init_ring( struct net_device *dev ) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 4588c2ecf20Sopenharmony_ci int i; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci lp->lock = 0; 4618c2ecf20Sopenharmony_ci lp->tx_full = 0; 4628c2ecf20Sopenharmony_ci lp->new_rx = lp->new_tx = 0; 4638c2ecf20Sopenharmony_ci lp->old_rx = lp->old_tx = 0; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci for( i = 0; i < TX_RING_SIZE; i++ ) { 4668c2ecf20Sopenharmony_ci MEM->tx_head[i].base = dvma_vtob(MEM->tx_data[i]); 4678c2ecf20Sopenharmony_ci MEM->tx_head[i].flag = 0; 4688c2ecf20Sopenharmony_ci MEM->tx_head[i].base_hi = 4698c2ecf20Sopenharmony_ci (dvma_vtob(MEM->tx_data[i])) >>16; 4708c2ecf20Sopenharmony_ci MEM->tx_head[i].length = 0; 4718c2ecf20Sopenharmony_ci MEM->tx_head[i].misc = 0; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci for( i = 0; i < RX_RING_SIZE; i++ ) { 4758c2ecf20Sopenharmony_ci MEM->rx_head[i].base = dvma_vtob(MEM->rx_data[i]); 4768c2ecf20Sopenharmony_ci MEM->rx_head[i].flag = RMD1_OWN_CHIP; 4778c2ecf20Sopenharmony_ci MEM->rx_head[i].base_hi = 4788c2ecf20Sopenharmony_ci (dvma_vtob(MEM->rx_data[i])) >> 16; 4798c2ecf20Sopenharmony_ci MEM->rx_head[i].buf_length = -PKT_BUF_SZ | 0xf000; 4808c2ecf20Sopenharmony_ci MEM->rx_head[i].msg_length = 0; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci /* tell the card it's ether address, bytes swapped */ 4848c2ecf20Sopenharmony_ci MEM->init.hwaddr[0] = dev->dev_addr[1]; 4858c2ecf20Sopenharmony_ci MEM->init.hwaddr[1] = dev->dev_addr[0]; 4868c2ecf20Sopenharmony_ci MEM->init.hwaddr[2] = dev->dev_addr[3]; 4878c2ecf20Sopenharmony_ci MEM->init.hwaddr[3] = dev->dev_addr[2]; 4888c2ecf20Sopenharmony_ci MEM->init.hwaddr[4] = dev->dev_addr[5]; 4898c2ecf20Sopenharmony_ci MEM->init.hwaddr[5] = dev->dev_addr[4]; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci MEM->init.mode = 0x0000; 4928c2ecf20Sopenharmony_ci MEM->init.filter[0] = 0x00000000; 4938c2ecf20Sopenharmony_ci MEM->init.filter[1] = 0x00000000; 4948c2ecf20Sopenharmony_ci MEM->init.rdra = dvma_vtob(MEM->rx_head); 4958c2ecf20Sopenharmony_ci MEM->init.rlen = (RX_LOG_RING_SIZE << 13) | 4968c2ecf20Sopenharmony_ci (dvma_vtob(MEM->rx_head) >> 16); 4978c2ecf20Sopenharmony_ci MEM->init.tdra = dvma_vtob(MEM->tx_head); 4988c2ecf20Sopenharmony_ci MEM->init.tlen = (TX_LOG_RING_SIZE << 13) | 4998c2ecf20Sopenharmony_ci (dvma_vtob(MEM->tx_head) >> 16); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci /* tell the lance the address of its init block */ 5038c2ecf20Sopenharmony_ci REGA(CSR1) = dvma_vtob(&(MEM->init)); 5048c2ecf20Sopenharmony_ci REGA(CSR2) = dvma_vtob(&(MEM->init)) >> 16; 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3X 5078c2ecf20Sopenharmony_ci REGA(CSR3) = CSR3_BSWP | CSR3_ACON | CSR3_BCON; 5088c2ecf20Sopenharmony_ci#else 5098c2ecf20Sopenharmony_ci REGA(CSR3) = CSR3_BSWP; 5108c2ecf20Sopenharmony_ci#endif 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci} 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_cistatic netdev_tx_t 5168c2ecf20Sopenharmony_cilance_start_xmit(struct sk_buff *skb, struct net_device *dev) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 5198c2ecf20Sopenharmony_ci int entry, len; 5208c2ecf20Sopenharmony_ci struct lance_tx_head *head; 5218c2ecf20Sopenharmony_ci unsigned long flags; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci DPRINTK( 1, ( "%s: transmit start.\n", 5248c2ecf20Sopenharmony_ci dev->name)); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci /* Transmitter timeout, serious problems. */ 5278c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev)) { 5288c2ecf20Sopenharmony_ci int tickssofar = jiffies - dev_trans_start(dev); 5298c2ecf20Sopenharmony_ci if (tickssofar < HZ/5) 5308c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci DPRINTK( 1, ( "%s: transmit timed out, status %04x, resetting.\n", 5338c2ecf20Sopenharmony_ci dev->name, DREG )); 5348c2ecf20Sopenharmony_ci DREG = CSR0_STOP; 5358c2ecf20Sopenharmony_ci /* 5368c2ecf20Sopenharmony_ci * Always set BSWP after a STOP as STOP puts it back into 5378c2ecf20Sopenharmony_ci * little endian mode. 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_ci REGA(CSR3) = CSR3_BSWP; 5408c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if(lance_debug >= 2) { 5438c2ecf20Sopenharmony_ci int i; 5448c2ecf20Sopenharmony_ci printk("Ring data: old_tx %d new_tx %d%s new_rx %d\n", 5458c2ecf20Sopenharmony_ci lp->old_tx, lp->new_tx, 5468c2ecf20Sopenharmony_ci lp->tx_full ? " (full)" : "", 5478c2ecf20Sopenharmony_ci lp->new_rx ); 5488c2ecf20Sopenharmony_ci for( i = 0 ; i < RX_RING_SIZE; i++ ) 5498c2ecf20Sopenharmony_ci printk( "rx #%d: base=%04x blen=%04x mlen=%04x\n", 5508c2ecf20Sopenharmony_ci i, MEM->rx_head[i].base, 5518c2ecf20Sopenharmony_ci -MEM->rx_head[i].buf_length, 5528c2ecf20Sopenharmony_ci MEM->rx_head[i].msg_length); 5538c2ecf20Sopenharmony_ci for( i = 0 ; i < TX_RING_SIZE; i++ ) 5548c2ecf20Sopenharmony_ci printk("tx #%d: base=%04x len=%04x misc=%04x\n", 5558c2ecf20Sopenharmony_ci i, MEM->tx_head[i].base, 5568c2ecf20Sopenharmony_ci -MEM->tx_head[i].length, 5578c2ecf20Sopenharmony_ci MEM->tx_head[i].misc ); 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci lance_init_ring(dev); 5618c2ecf20Sopenharmony_ci REGA( CSR0 ) = CSR0_INEA | CSR0_INIT | CSR0_STRT; 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci netif_start_queue(dev); 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* Block a timer-based transmit from overlapping. This could better be 5708c2ecf20Sopenharmony_ci done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */ 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci /* Block a timer-based transmit from overlapping with us by 5738c2ecf20Sopenharmony_ci stopping the queue for a bit... */ 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (test_and_set_bit( 0, (void*)&lp->lock ) != 0) { 5788c2ecf20Sopenharmony_ci printk( "%s: tx queue lock!.\n", dev->name); 5798c2ecf20Sopenharmony_ci /* don't clear dev->tbusy flag. */ 5808c2ecf20Sopenharmony_ci return NETDEV_TX_BUSY; 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci AREG = CSR0; 5848c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: lance_start_xmit() called, csr0 %4.4x.\n", 5858c2ecf20Sopenharmony_ci dev->name, DREG )); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3X 5888c2ecf20Sopenharmony_ci /* this weirdness doesn't appear on sun3... */ 5898c2ecf20Sopenharmony_ci if(!(DREG & CSR0_INIT)) { 5908c2ecf20Sopenharmony_ci DPRINTK( 1, ("INIT not set, reinitializing...\n")); 5918c2ecf20Sopenharmony_ci REGA( CSR0 ) = CSR0_STOP; 5928c2ecf20Sopenharmony_ci lance_init_ring(dev); 5938c2ecf20Sopenharmony_ci REGA( CSR0 ) = CSR0_INIT | CSR0_STRT; 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci#endif 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci /* Fill in a Tx ring entry */ 5988c2ecf20Sopenharmony_ci#if 0 5998c2ecf20Sopenharmony_ci if (lance_debug >= 2) { 6008c2ecf20Sopenharmony_ci printk( "%s: TX pkt %d type 0x%04x" 6018c2ecf20Sopenharmony_ci " from %s to %s" 6028c2ecf20Sopenharmony_ci " data at 0x%08x len %d\n", 6038c2ecf20Sopenharmony_ci dev->name, lp->new_tx, ((u_short *)skb->data)[6], 6048c2ecf20Sopenharmony_ci DEV_ADDR(&skb->data[6]), DEV_ADDR(skb->data), 6058c2ecf20Sopenharmony_ci (int)skb->data, (int)skb->len ); 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci#endif 6088c2ecf20Sopenharmony_ci /* We're not prepared for the int until the last flags are set/reset. 6098c2ecf20Sopenharmony_ci * And the int may happen already after setting the OWN_CHIP... */ 6108c2ecf20Sopenharmony_ci local_irq_save(flags); 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Mask to ring buffer boundary. */ 6138c2ecf20Sopenharmony_ci entry = lp->new_tx; 6148c2ecf20Sopenharmony_ci head = &(MEM->tx_head[entry]); 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* Caution: the write order is important here, set the "ownership" bits 6178c2ecf20Sopenharmony_ci * last. 6188c2ecf20Sopenharmony_ci */ 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci /* the sun3's lance needs it's buffer padded to the minimum 6218c2ecf20Sopenharmony_ci size */ 6228c2ecf20Sopenharmony_ci len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN; 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci// head->length = -len; 6258c2ecf20Sopenharmony_ci head->length = (-len) | 0xf000; 6268c2ecf20Sopenharmony_ci head->misc = 0; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len); 6298c2ecf20Sopenharmony_ci if (len != skb->len) 6308c2ecf20Sopenharmony_ci memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci head->flag = TMD1_OWN_CHIP | TMD1_ENP | TMD1_STP; 6338c2ecf20Sopenharmony_ci lp->new_tx = (lp->new_tx + 1) & TX_RING_MOD_MASK; 6348c2ecf20Sopenharmony_ci dev->stats.tx_bytes += skb->len; 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci /* Trigger an immediate send poll. */ 6378c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_INEA | CSR0_TDMD | CSR0_STRT; 6388c2ecf20Sopenharmony_ci AREG = CSR0; 6398c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: lance_start_xmit() exiting, csr0 %4.4x.\n", 6408c2ecf20Sopenharmony_ci dev->name, DREG )); 6418c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci lp->lock = 0; 6448c2ecf20Sopenharmony_ci if ((MEM->tx_head[(entry+1) & TX_RING_MOD_MASK].flag & TMD1_OWN) == 6458c2ecf20Sopenharmony_ci TMD1_OWN_HOST) 6468c2ecf20Sopenharmony_ci netif_start_queue(dev); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci local_irq_restore(flags); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci/* The LANCE interrupt handler. */ 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_cistatic irqreturn_t lance_interrupt( int irq, void *dev_id) 6568c2ecf20Sopenharmony_ci{ 6578c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 6588c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 6598c2ecf20Sopenharmony_ci int csr0; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci still_more: 6628c2ecf20Sopenharmony_ci flush_cache_all(); 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci AREG = CSR0; 6658c2ecf20Sopenharmony_ci csr0 = DREG; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci /* ack interrupts */ 6688c2ecf20Sopenharmony_ci DREG = csr0 & (CSR0_TINT | CSR0_RINT | CSR0_IDON); 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci /* clear errors */ 6718c2ecf20Sopenharmony_ci if(csr0 & CSR0_ERR) 6728c2ecf20Sopenharmony_ci DREG = CSR0_BABL | CSR0_MERR | CSR0_CERR | CSR0_MISS; 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: interrupt csr0=%04x new csr=%04x.\n", 6768c2ecf20Sopenharmony_ci dev->name, csr0, DREG )); 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci if (csr0 & CSR0_TINT) { /* Tx-done interrupt */ 6798c2ecf20Sopenharmony_ci int old_tx = lp->old_tx; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci// if(lance_debug >= 3) { 6828c2ecf20Sopenharmony_ci// int i; 6838c2ecf20Sopenharmony_ci// 6848c2ecf20Sopenharmony_ci// printk("%s: tx int\n", dev->name); 6858c2ecf20Sopenharmony_ci// 6868c2ecf20Sopenharmony_ci// for(i = 0; i < TX_RING_SIZE; i++) 6878c2ecf20Sopenharmony_ci// printk("ring %d flag=%04x\n", i, 6888c2ecf20Sopenharmony_ci// MEM->tx_head[i].flag); 6898c2ecf20Sopenharmony_ci// } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci while( old_tx != lp->new_tx) { 6928c2ecf20Sopenharmony_ci struct lance_tx_head *head = &(MEM->tx_head[old_tx]); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci DPRINTK(3, ("on tx_ring %d\n", old_tx)); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci if (head->flag & TMD1_OWN_CHIP) 6978c2ecf20Sopenharmony_ci break; /* It still hasn't been Txed */ 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci if (head->flag & TMD1_ERR) { 7008c2ecf20Sopenharmony_ci int status = head->misc; 7018c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 7028c2ecf20Sopenharmony_ci if (status & TMD3_RTRY) dev->stats.tx_aborted_errors++; 7038c2ecf20Sopenharmony_ci if (status & TMD3_LCAR) dev->stats.tx_carrier_errors++; 7048c2ecf20Sopenharmony_ci if (status & TMD3_LCOL) dev->stats.tx_window_errors++; 7058c2ecf20Sopenharmony_ci if (status & (TMD3_UFLO | TMD3_BUFF)) { 7068c2ecf20Sopenharmony_ci dev->stats.tx_fifo_errors++; 7078c2ecf20Sopenharmony_ci printk("%s: Tx FIFO error\n", 7088c2ecf20Sopenharmony_ci dev->name); 7098c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STOP; 7108c2ecf20Sopenharmony_ci REGA(CSR3) = CSR3_BSWP; 7118c2ecf20Sopenharmony_ci lance_init_ring(dev); 7128c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STRT | CSR0_INEA; 7138c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7148c2ecf20Sopenharmony_ci } 7158c2ecf20Sopenharmony_ci } else if(head->flag & (TMD1_ENP | TMD1_STP)) { 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci head->flag &= ~(TMD1_ENP | TMD1_STP); 7188c2ecf20Sopenharmony_ci if(head->flag & (TMD1_ONE | TMD1_MORE)) 7198c2ecf20Sopenharmony_ci dev->stats.collisions++; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 7228c2ecf20Sopenharmony_ci DPRINTK(3, ("cleared tx ring %d\n", old_tx)); 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci old_tx = (old_tx +1) & TX_RING_MOD_MASK; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci lp->old_tx = old_tx; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev)) { 7328c2ecf20Sopenharmony_ci /* The ring is no longer full, clear tbusy. */ 7338c2ecf20Sopenharmony_ci netif_start_queue(dev); 7348c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if (csr0 & CSR0_RINT) /* Rx interrupt */ 7388c2ecf20Sopenharmony_ci lance_rx( dev ); 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* Log misc errors. */ 7418c2ecf20Sopenharmony_ci if (csr0 & CSR0_BABL) dev->stats.tx_errors++; /* Tx babble. */ 7428c2ecf20Sopenharmony_ci if (csr0 & CSR0_MISS) dev->stats.rx_errors++; /* Missed a Rx frame. */ 7438c2ecf20Sopenharmony_ci if (csr0 & CSR0_MERR) { 7448c2ecf20Sopenharmony_ci DPRINTK( 1, ( "%s: Bus master arbitration failure (?!?), " 7458c2ecf20Sopenharmony_ci "status %04x.\n", dev->name, csr0 )); 7468c2ecf20Sopenharmony_ci /* Restart the chip. */ 7478c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STOP; 7488c2ecf20Sopenharmony_ci REGA(CSR3) = CSR3_BSWP; 7498c2ecf20Sopenharmony_ci lance_init_ring(dev); 7508c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_STRT | CSR0_INEA; 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci /* Clear any other interrupt, and set interrupt enable. */ 7558c2ecf20Sopenharmony_ci// DREG = CSR0_BABL | CSR0_CERR | CSR0_MISS | CSR0_MERR | 7568c2ecf20Sopenharmony_ci// CSR0_IDON | CSR0_INEA; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci REGA(CSR0) = CSR0_INEA; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci if(DREG & (CSR0_RINT | CSR0_TINT)) { 7618c2ecf20Sopenharmony_ci DPRINTK(2, ("restarting interrupt, csr0=%#04x\n", DREG)); 7628c2ecf20Sopenharmony_ci goto still_more; 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: exiting interrupt, csr0=%#04x.\n", 7668c2ecf20Sopenharmony_ci dev->name, DREG )); 7678c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci/* get packet, toss into skbuff */ 7718c2ecf20Sopenharmony_cistatic int lance_rx( struct net_device *dev ) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 7748c2ecf20Sopenharmony_ci int entry = lp->new_rx; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* If we own the next entry, it's a new packet. Send it up. */ 7778c2ecf20Sopenharmony_ci while( (MEM->rx_head[entry].flag & RMD1_OWN) == RMD1_OWN_HOST ) { 7788c2ecf20Sopenharmony_ci struct lance_rx_head *head = &(MEM->rx_head[entry]); 7798c2ecf20Sopenharmony_ci int status = head->flag; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci if (status != (RMD1_ENP|RMD1_STP)) { /* There was an error. */ 7828c2ecf20Sopenharmony_ci /* There is a tricky error noted by John Murphy, 7838c2ecf20Sopenharmony_ci <murf@perftech.com> to Russ Nelson: Even with 7848c2ecf20Sopenharmony_ci full-sized buffers it's possible for a jabber packet to use two 7858c2ecf20Sopenharmony_ci buffers, with only the last correctly noting the error. */ 7868c2ecf20Sopenharmony_ci if (status & RMD1_ENP) /* Only count a general error at the */ 7878c2ecf20Sopenharmony_ci dev->stats.rx_errors++; /* end of a packet.*/ 7888c2ecf20Sopenharmony_ci if (status & RMD1_FRAM) dev->stats.rx_frame_errors++; 7898c2ecf20Sopenharmony_ci if (status & RMD1_OFLO) dev->stats.rx_over_errors++; 7908c2ecf20Sopenharmony_ci if (status & RMD1_CRC) dev->stats.rx_crc_errors++; 7918c2ecf20Sopenharmony_ci if (status & RMD1_BUFF) dev->stats.rx_fifo_errors++; 7928c2ecf20Sopenharmony_ci head->flag &= (RMD1_ENP|RMD1_STP); 7938c2ecf20Sopenharmony_ci } else { 7948c2ecf20Sopenharmony_ci /* Malloc up new buffer, compatible with net-3. */ 7958c2ecf20Sopenharmony_ci// short pkt_len = head->msg_length;// & 0xfff; 7968c2ecf20Sopenharmony_ci short pkt_len = (head->msg_length & 0xfff) - 4; 7978c2ecf20Sopenharmony_ci struct sk_buff *skb; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci if (pkt_len < 60) { 8008c2ecf20Sopenharmony_ci printk( "%s: Runt packet!\n", dev->name ); 8018c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci else { 8048c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, pkt_len + 2); 8058c2ecf20Sopenharmony_ci if (skb == NULL) { 8068c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 8078c2ecf20Sopenharmony_ci head->msg_length = 0; 8088c2ecf20Sopenharmony_ci head->flag |= RMD1_OWN_CHIP; 8098c2ecf20Sopenharmony_ci lp->new_rx = (lp->new_rx+1) & 8108c2ecf20Sopenharmony_ci RX_RING_MOD_MASK; 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci#if 0 8148c2ecf20Sopenharmony_ci if (lance_debug >= 3) { 8158c2ecf20Sopenharmony_ci u_char *data = PKTBUF_ADDR(head); 8168c2ecf20Sopenharmony_ci printk("%s: RX pkt %d type 0x%04x" 8178c2ecf20Sopenharmony_ci " from %pM to %pM", 8188c2ecf20Sopenharmony_ci dev->name, lp->new_tx, ((u_short *)data)[6], 8198c2ecf20Sopenharmony_ci &data[6], data); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci printk(" data %02x %02x %02x %02x %02x %02x %02x %02x " 8228c2ecf20Sopenharmony_ci "len %d at %08x\n", 8238c2ecf20Sopenharmony_ci data[15], data[16], data[17], data[18], 8248c2ecf20Sopenharmony_ci data[19], data[20], data[21], data[22], 8258c2ecf20Sopenharmony_ci pkt_len, data); 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci#endif 8288c2ecf20Sopenharmony_ci if (lance_debug >= 3) { 8298c2ecf20Sopenharmony_ci u_char *data = PKTBUF_ADDR(head); 8308c2ecf20Sopenharmony_ci printk( "%s: RX pkt %d type 0x%04x len %d\n ", dev->name, entry, ((u_short *)data)[6], pkt_len); 8318c2ecf20Sopenharmony_ci } 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci skb_reserve( skb, 2 ); /* 16 byte align */ 8358c2ecf20Sopenharmony_ci skb_put( skb, pkt_len ); /* Make room */ 8368c2ecf20Sopenharmony_ci skb_copy_to_linear_data(skb, 8378c2ecf20Sopenharmony_ci PKTBUF_ADDR(head), 8388c2ecf20Sopenharmony_ci pkt_len); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans( skb, dev ); 8418c2ecf20Sopenharmony_ci netif_rx( skb ); 8428c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 8438c2ecf20Sopenharmony_ci dev->stats.rx_bytes += pkt_len; 8448c2ecf20Sopenharmony_ci } 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci// head->buf_length = -PKT_BUF_SZ | 0xf000; 8488c2ecf20Sopenharmony_ci head->msg_length = 0; 8498c2ecf20Sopenharmony_ci head->flag = RMD1_OWN_CHIP; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci entry = lp->new_rx = (lp->new_rx +1) & RX_RING_MOD_MASK; 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci /* From lance.c (Donald Becker): */ 8558c2ecf20Sopenharmony_ci /* We should check that at least two ring entries are free. 8568c2ecf20Sopenharmony_ci If not, we should free one and mark stats->rx_dropped++. */ 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci return 0; 8598c2ecf20Sopenharmony_ci} 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_cistatic int lance_close( struct net_device *dev ) 8638c2ecf20Sopenharmony_ci{ 8648c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci netif_stop_queue(dev); 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci AREG = CSR0; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci DPRINTK( 2, ( "%s: Shutting down ethercard, status was %2.2x.\n", 8718c2ecf20Sopenharmony_ci dev->name, DREG )); 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci /* We stop the LANCE here -- it occasionally polls 8748c2ecf20Sopenharmony_ci memory if we don't. */ 8758c2ecf20Sopenharmony_ci DREG = CSR0_STOP; 8768c2ecf20Sopenharmony_ci return 0; 8778c2ecf20Sopenharmony_ci} 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci/* Set or clear the multicast filter for this adaptor. 8818c2ecf20Sopenharmony_ci num_addrs == -1 Promiscuous mode, receive all packets 8828c2ecf20Sopenharmony_ci num_addrs == 0 Normal mode, clear multicast list 8838c2ecf20Sopenharmony_ci num_addrs > 0 Multicast mode, receive normal and MC packets, and do 8848c2ecf20Sopenharmony_ci best-effort filtering. 8858c2ecf20Sopenharmony_ci */ 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci/* completely untested on a sun3 */ 8888c2ecf20Sopenharmony_cistatic void set_multicast_list( struct net_device *dev ) 8898c2ecf20Sopenharmony_ci{ 8908c2ecf20Sopenharmony_ci struct lance_private *lp = netdev_priv(dev); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci if(netif_queue_stopped(dev)) 8938c2ecf20Sopenharmony_ci /* Only possible if board is already started */ 8948c2ecf20Sopenharmony_ci return; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci /* We take the simple way out and always enable promiscuous mode. */ 8978c2ecf20Sopenharmony_ci DREG = CSR0_STOP; /* Temporarily stop the lance. */ 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) { 9008c2ecf20Sopenharmony_ci /* Log any net taps. */ 9018c2ecf20Sopenharmony_ci DPRINTK( 3, ( "%s: Promiscuous mode enabled.\n", dev->name )); 9028c2ecf20Sopenharmony_ci REGA( CSR15 ) = 0x8000; /* Set promiscuous mode */ 9038c2ecf20Sopenharmony_ci } else { 9048c2ecf20Sopenharmony_ci short multicast_table[4]; 9058c2ecf20Sopenharmony_ci int num_addrs = netdev_mc_count(dev); 9068c2ecf20Sopenharmony_ci int i; 9078c2ecf20Sopenharmony_ci /* We don't use the multicast table, but rely on upper-layer 9088c2ecf20Sopenharmony_ci * filtering. */ 9098c2ecf20Sopenharmony_ci memset( multicast_table, (num_addrs == 0) ? 0 : -1, 9108c2ecf20Sopenharmony_ci sizeof(multicast_table) ); 9118c2ecf20Sopenharmony_ci for( i = 0; i < 4; i++ ) 9128c2ecf20Sopenharmony_ci REGA( CSR8+i ) = multicast_table[i]; 9138c2ecf20Sopenharmony_ci REGA( CSR15 ) = 0; /* Unset promiscuous mode */ 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci /* 9178c2ecf20Sopenharmony_ci * Always set BSWP after a STOP as STOP puts it back into 9188c2ecf20Sopenharmony_ci * little endian mode. 9198c2ecf20Sopenharmony_ci */ 9208c2ecf20Sopenharmony_ci REGA( CSR3 ) = CSR3_BSWP; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* Resume normal operation and reset AREG to CSR0 */ 9238c2ecf20Sopenharmony_ci REGA( CSR0 ) = CSR0_IDON | CSR0_INEA | CSR0_STRT; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci#ifdef MODULE 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_cistatic struct net_device *sun3lance_dev; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ciint __init init_module(void) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci sun3lance_dev = sun3lance_probe(-1); 9348c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(sun3lance_dev); 9358c2ecf20Sopenharmony_ci} 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_civoid __exit cleanup_module(void) 9388c2ecf20Sopenharmony_ci{ 9398c2ecf20Sopenharmony_ci unregister_netdev(sun3lance_dev); 9408c2ecf20Sopenharmony_ci#ifdef CONFIG_SUN3 9418c2ecf20Sopenharmony_ci iounmap((void __iomem *)sun3lance_dev->base_addr); 9428c2ecf20Sopenharmony_ci#endif 9438c2ecf20Sopenharmony_ci free_netdev(sun3lance_dev); 9448c2ecf20Sopenharmony_ci} 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci#endif /* MODULE */ 9478c2ecf20Sopenharmony_ci 948