18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/acorn/net/etherh.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2000-2002 Russell King 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * NS8390 I-cubed EtherH and ANT EtherM specific driver 88c2ecf20Sopenharmony_ci * Thanks to I-Cubed for information on their cards. 98c2ecf20Sopenharmony_ci * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton 108c2ecf20Sopenharmony_ci * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) 118c2ecf20Sopenharmony_ci * EtherM integration re-engineered by Russell King. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Changelog: 148c2ecf20Sopenharmony_ci * 08-12-1996 RMK 1.00 Created 158c2ecf20Sopenharmony_ci * RMK 1.03 Added support for EtherLan500 cards 168c2ecf20Sopenharmony_ci * 23-11-1997 RMK 1.04 Added media autodetection 178c2ecf20Sopenharmony_ci * 16-04-1998 RMK 1.05 Improved media autodetection 188c2ecf20Sopenharmony_ci * 10-02-2000 RMK 1.06 Updated for 2.3.43 198c2ecf20Sopenharmony_ci * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 208c2ecf20Sopenharmony_ci * 12-10-1999 CK/TEW EtherM driver first release 218c2ecf20Sopenharmony_ci * 21-12-2000 TTC EtherH/EtherM integration 228c2ecf20Sopenharmony_ci * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. 238c2ecf20Sopenharmony_ci * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. 248c2ecf20Sopenharmony_ci */ 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/module.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/types.h> 298c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 308c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 318c2ecf20Sopenharmony_ci#include <linux/ioport.h> 328c2ecf20Sopenharmony_ci#include <linux/in.h> 338c2ecf20Sopenharmony_ci#include <linux/string.h> 348c2ecf20Sopenharmony_ci#include <linux/errno.h> 358c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 368c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 378c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 388c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 398c2ecf20Sopenharmony_ci#include <linux/delay.h> 408c2ecf20Sopenharmony_ci#include <linux/device.h> 418c2ecf20Sopenharmony_ci#include <linux/init.h> 428c2ecf20Sopenharmony_ci#include <linux/bitops.h> 438c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci#include <asm/ecard.h> 468c2ecf20Sopenharmony_ci#include <asm/io.h> 478c2ecf20Sopenharmony_ci#include <asm/system_info.h> 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci#define EI_SHIFT(x) (ei_local->reg_offset[x]) 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#define ei_inb(_p) readb((void __iomem *)_p) 528c2ecf20Sopenharmony_ci#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p) 538c2ecf20Sopenharmony_ci#define ei_inb_p(_p) readb((void __iomem *)_p) 548c2ecf20Sopenharmony_ci#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define DRV_NAME "etherh" 578c2ecf20Sopenharmony_ci#define DRV_VERSION "1.11" 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic char version[] = 608c2ecf20Sopenharmony_ci "EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n"; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#include "lib8390.c" 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistruct etherh_priv { 658c2ecf20Sopenharmony_ci void __iomem *ioc_fast; 668c2ecf20Sopenharmony_ci void __iomem *memc; 678c2ecf20Sopenharmony_ci void __iomem *dma_base; 688c2ecf20Sopenharmony_ci unsigned int id; 698c2ecf20Sopenharmony_ci void __iomem *ctrl_port; 708c2ecf20Sopenharmony_ci unsigned char ctrl; 718c2ecf20Sopenharmony_ci u32 supported; 728c2ecf20Sopenharmony_ci}; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistruct etherh_data { 758c2ecf20Sopenharmony_ci unsigned long ns8390_offset; 768c2ecf20Sopenharmony_ci unsigned long dataport_offset; 778c2ecf20Sopenharmony_ci unsigned long ctrlport_offset; 788c2ecf20Sopenharmony_ci int ctrl_ioc; 798c2ecf20Sopenharmony_ci const char name[16]; 808c2ecf20Sopenharmony_ci u32 supported; 818c2ecf20Sopenharmony_ci unsigned char tx_start_page; 828c2ecf20Sopenharmony_ci unsigned char stop_page; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciMODULE_AUTHOR("Russell King"); 868c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("EtherH/EtherM driver"); 878c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define ETHERH500_DATAPORT 0x800 /* MEMC */ 908c2ecf20Sopenharmony_ci#define ETHERH500_NS8390 0x000 /* MEMC */ 918c2ecf20Sopenharmony_ci#define ETHERH500_CTRLPORT 0x800 /* IOC */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci#define ETHERH600_DATAPORT 0x040 /* MEMC */ 948c2ecf20Sopenharmony_ci#define ETHERH600_NS8390 0x800 /* MEMC */ 958c2ecf20Sopenharmony_ci#define ETHERH600_CTRLPORT 0x200 /* MEMC */ 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci#define ETHERH_CP_IE 1 988c2ecf20Sopenharmony_ci#define ETHERH_CP_IF 2 998c2ecf20Sopenharmony_ci#define ETHERH_CP_HEARTBEAT 2 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci#define ETHERH_TX_START_PAGE 1 1028c2ecf20Sopenharmony_ci#define ETHERH_STOP_PAGE 127 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* 1058c2ecf20Sopenharmony_ci * These came from CK/TEW 1068c2ecf20Sopenharmony_ci */ 1078c2ecf20Sopenharmony_ci#define ETHERM_DATAPORT 0x200 /* MEMC */ 1088c2ecf20Sopenharmony_ci#define ETHERM_NS8390 0x800 /* MEMC */ 1098c2ecf20Sopenharmony_ci#define ETHERM_CTRLPORT 0x23c /* MEMC */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#define ETHERM_TX_START_PAGE 64 1128c2ecf20Sopenharmony_ci#define ETHERM_STOP_PAGE 127 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* ------------------------------------------------------------------------ */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#define etherh_priv(dev) \ 1178c2ecf20Sopenharmony_ci ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device))) 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci unsigned char ctrl = eh->ctrl | mask; 1228c2ecf20Sopenharmony_ci eh->ctrl = ctrl; 1238c2ecf20Sopenharmony_ci writeb(ctrl, eh->ctrl_port); 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask) 1278c2ecf20Sopenharmony_ci{ 1288c2ecf20Sopenharmony_ci unsigned char ctrl = eh->ctrl & ~mask; 1298c2ecf20Sopenharmony_ci eh->ctrl = ctrl; 1308c2ecf20Sopenharmony_ci writeb(ctrl, eh->ctrl_port); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic inline unsigned int etherh_get_stat(struct etherh_priv *eh) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci return readb(eh->ctrl_port); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic void etherh_irq_enable(ecard_t *ec, int irqnr) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct etherh_priv *eh = ec->irq_data; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci etherh_set_ctrl(eh, ETHERH_CP_IE); 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void etherh_irq_disable(ecard_t *ec, int irqnr) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct etherh_priv *eh = ec->irq_data; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci etherh_clr_ctrl(eh, ETHERH_CP_IE); 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic expansioncard_ops_t etherh_ops = { 1568c2ecf20Sopenharmony_ci .irqenable = etherh_irq_enable, 1578c2ecf20Sopenharmony_ci .irqdisable = etherh_irq_disable, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic void 1648c2ecf20Sopenharmony_cietherh_setif(struct net_device *dev) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 1678c2ecf20Sopenharmony_ci unsigned long flags; 1688c2ecf20Sopenharmony_ci void __iomem *addr; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci local_irq_save(flags); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* set the interface type */ 1738c2ecf20Sopenharmony_ci switch (etherh_priv(dev)->id) { 1748c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN600: 1758c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN600A: 1768c2ecf20Sopenharmony_ci addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci switch (dev->if_port) { 1798c2ecf20Sopenharmony_ci case IF_PORT_10BASE2: 1808c2ecf20Sopenharmony_ci writeb((readb(addr) & 0xf8) | 1, addr); 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci case IF_PORT_10BASET: 1838c2ecf20Sopenharmony_ci writeb((readb(addr) & 0xf8), addr); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci break; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN500: 1898c2ecf20Sopenharmony_ci switch (dev->if_port) { 1908c2ecf20Sopenharmony_ci case IF_PORT_10BASE2: 1918c2ecf20Sopenharmony_ci etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF); 1928c2ecf20Sopenharmony_ci break; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci case IF_PORT_10BASET: 1958c2ecf20Sopenharmony_ci etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF); 1968c2ecf20Sopenharmony_ci break; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci default: 2018c2ecf20Sopenharmony_ci break; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci local_irq_restore(flags); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic int 2088c2ecf20Sopenharmony_cietherh_getifstat(struct net_device *dev) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 2118c2ecf20Sopenharmony_ci void __iomem *addr; 2128c2ecf20Sopenharmony_ci int stat = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (etherh_priv(dev)->id) { 2158c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN600: 2168c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN600A: 2178c2ecf20Sopenharmony_ci addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; 2188c2ecf20Sopenharmony_ci switch (dev->if_port) { 2198c2ecf20Sopenharmony_ci case IF_PORT_10BASE2: 2208c2ecf20Sopenharmony_ci stat = 1; 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case IF_PORT_10BASET: 2238c2ecf20Sopenharmony_ci stat = readb(addr) & 4; 2248c2ecf20Sopenharmony_ci break; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci break; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci case PROD_I3_ETHERLAN500: 2298c2ecf20Sopenharmony_ci switch (dev->if_port) { 2308c2ecf20Sopenharmony_ci case IF_PORT_10BASE2: 2318c2ecf20Sopenharmony_ci stat = 1; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci case IF_PORT_10BASET: 2348c2ecf20Sopenharmony_ci stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT; 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci break; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci default: 2408c2ecf20Sopenharmony_ci stat = 0; 2418c2ecf20Sopenharmony_ci break; 2428c2ecf20Sopenharmony_ci } 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci return stat != 0; 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Configure the interface. Note that we ignore the other 2498c2ecf20Sopenharmony_ci * parts of ifmap, since its mostly meaningless for this driver. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic int etherh_set_config(struct net_device *dev, struct ifmap *map) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci switch (map->port) { 2548c2ecf20Sopenharmony_ci case IF_PORT_10BASE2: 2558c2ecf20Sopenharmony_ci case IF_PORT_10BASET: 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * If the user explicitly sets the interface 2588c2ecf20Sopenharmony_ci * media type, turn off automedia detection. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci dev->flags &= ~IFF_AUTOMEDIA; 2618c2ecf20Sopenharmony_ci dev->if_port = map->port; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci etherh_setif(dev); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci/* 2748c2ecf20Sopenharmony_ci * Reset the 8390 (hard reset). Note that we can't actually do this. 2758c2ecf20Sopenharmony_ci */ 2768c2ecf20Sopenharmony_cistatic void 2778c2ecf20Sopenharmony_cietherh_reset(struct net_device *dev) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 2808c2ecf20Sopenharmony_ci void __iomem *addr = (void __iomem *)dev->base_addr; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* 2858c2ecf20Sopenharmony_ci * See if we need to change the interface type. 2868c2ecf20Sopenharmony_ci * Note that we use 'interface_num' as a flag 2878c2ecf20Sopenharmony_ci * to indicate that we need to change the media. 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { 2908c2ecf20Sopenharmony_ci ei_local->interface_num = 0; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (dev->if_port == IF_PORT_10BASET) 2938c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 2948c2ecf20Sopenharmony_ci else 2958c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci etherh_setif(dev); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* 3028c2ecf20Sopenharmony_ci * Write a block of data out to the 8390 3038c2ecf20Sopenharmony_ci */ 3048c2ecf20Sopenharmony_cistatic void 3058c2ecf20Sopenharmony_cietherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) 3068c2ecf20Sopenharmony_ci{ 3078c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 3088c2ecf20Sopenharmony_ci unsigned long dma_start; 3098c2ecf20Sopenharmony_ci void __iomem *dma_base, *addr; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (ei_local->dmaing) { 3128c2ecf20Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_block_input: " 3138c2ecf20Sopenharmony_ci " DMAstat %d irqlock %d\n", 3148c2ecf20Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * Make sure we have a round number of bytes if we're in word mode. 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci if (count & 1 && ei_local->word16) 3228c2ecf20Sopenharmony_ci count++; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci ei_local->dmaing = 1; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 3278c2ecf20Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci count = (count + 1) & ~1; 3308c2ecf20Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci writeb (0x42, addr + EN0_RCNTLO); 3338c2ecf20Sopenharmony_ci writeb (0x00, addr + EN0_RCNTHI); 3348c2ecf20Sopenharmony_ci writeb (0x42, addr + EN0_RSARLO); 3358c2ecf20Sopenharmony_ci writeb (0x00, addr + EN0_RSARHI); 3368c2ecf20Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci udelay (1); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 3418c2ecf20Sopenharmony_ci writeb (count, addr + EN0_RCNTLO); 3428c2ecf20Sopenharmony_ci writeb (count >> 8, addr + EN0_RCNTHI); 3438c2ecf20Sopenharmony_ci writeb (0, addr + EN0_RSARLO); 3448c2ecf20Sopenharmony_ci writeb (start_page, addr + EN0_RSARHI); 3458c2ecf20Sopenharmony_ci writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (ei_local->word16) 3488c2ecf20Sopenharmony_ci writesw (dma_base, buf, count >> 1); 3498c2ecf20Sopenharmony_ci else 3508c2ecf20Sopenharmony_ci writesb (dma_base, buf, count); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci dma_start = jiffies; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0) 3558c2ecf20Sopenharmony_ci if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */ 3568c2ecf20Sopenharmony_ci netdev_warn(dev, "timeout waiting for TX RDC\n"); 3578c2ecf20Sopenharmony_ci etherh_reset (dev); 3588c2ecf20Sopenharmony_ci __NS8390_init (dev, 1); 3598c2ecf20Sopenharmony_ci break; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 3638c2ecf20Sopenharmony_ci ei_local->dmaing = 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci/* 3678c2ecf20Sopenharmony_ci * Read a block of data from the 8390 3688c2ecf20Sopenharmony_ci */ 3698c2ecf20Sopenharmony_cistatic void 3708c2ecf20Sopenharmony_cietherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 3738c2ecf20Sopenharmony_ci unsigned char *buf; 3748c2ecf20Sopenharmony_ci void __iomem *dma_base, *addr; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (ei_local->dmaing) { 3778c2ecf20Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_block_input: " 3788c2ecf20Sopenharmony_ci " DMAstat %d irqlock %d\n", 3798c2ecf20Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 3808c2ecf20Sopenharmony_ci return; 3818c2ecf20Sopenharmony_ci } 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci ei_local->dmaing = 1; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 3868c2ecf20Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci buf = skb->data; 3898c2ecf20Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 3908c2ecf20Sopenharmony_ci writeb (count, addr + EN0_RCNTLO); 3918c2ecf20Sopenharmony_ci writeb (count >> 8, addr + EN0_RCNTHI); 3928c2ecf20Sopenharmony_ci writeb (ring_offset, addr + EN0_RSARLO); 3938c2ecf20Sopenharmony_ci writeb (ring_offset >> 8, addr + EN0_RSARHI); 3948c2ecf20Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (ei_local->word16) { 3978c2ecf20Sopenharmony_ci readsw (dma_base, buf, count >> 1); 3988c2ecf20Sopenharmony_ci if (count & 1) 3998c2ecf20Sopenharmony_ci buf[count - 1] = readb (dma_base); 4008c2ecf20Sopenharmony_ci } else 4018c2ecf20Sopenharmony_ci readsb (dma_base, buf, count); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 4048c2ecf20Sopenharmony_ci ei_local->dmaing = 0; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci/* 4088c2ecf20Sopenharmony_ci * Read a header from the 8390 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_cistatic void 4118c2ecf20Sopenharmony_cietherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) 4128c2ecf20Sopenharmony_ci{ 4138c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 4148c2ecf20Sopenharmony_ci void __iomem *dma_base, *addr; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci if (ei_local->dmaing) { 4178c2ecf20Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_get_header: " 4188c2ecf20Sopenharmony_ci " DMAstat %d irqlock %d\n", 4198c2ecf20Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 4208c2ecf20Sopenharmony_ci return; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ei_local->dmaing = 1; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 4268c2ecf20Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 4298c2ecf20Sopenharmony_ci writeb (sizeof (*hdr), addr + EN0_RCNTLO); 4308c2ecf20Sopenharmony_ci writeb (0, addr + EN0_RCNTHI); 4318c2ecf20Sopenharmony_ci writeb (0, addr + EN0_RSARLO); 4328c2ecf20Sopenharmony_ci writeb (ring_page, addr + EN0_RSARHI); 4338c2ecf20Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci if (ei_local->word16) 4368c2ecf20Sopenharmony_ci readsw (dma_base, hdr, sizeof (*hdr) >> 1); 4378c2ecf20Sopenharmony_ci else 4388c2ecf20Sopenharmony_ci readsb (dma_base, hdr, sizeof (*hdr)); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 4418c2ecf20Sopenharmony_ci ei_local->dmaing = 0; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci/* 4458c2ecf20Sopenharmony_ci * Open/initialize the board. This is called (in the current kernel) 4468c2ecf20Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run. 4478c2ecf20Sopenharmony_ci * 4488c2ecf20Sopenharmony_ci * This routine should set everything up anew at each open, even 4498c2ecf20Sopenharmony_ci * registers that "should" only need to be set once at boot, so that 4508c2ecf20Sopenharmony_ci * there is non-reboot way to recover if something goes wrong. 4518c2ecf20Sopenharmony_ci */ 4528c2ecf20Sopenharmony_cistatic int 4538c2ecf20Sopenharmony_cietherh_open(struct net_device *dev) 4548c2ecf20Sopenharmony_ci{ 4558c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev)) 4588c2ecf20Sopenharmony_ci return -EAGAIN; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* 4618c2ecf20Sopenharmony_ci * Make sure that we aren't going to change the 4628c2ecf20Sopenharmony_ci * media type on the next reset - we are about to 4638c2ecf20Sopenharmony_ci * do automedia manually now. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci ei_local->interface_num = 0; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci /* 4688c2ecf20Sopenharmony_ci * If we are doing automedia detection, do it now. 4698c2ecf20Sopenharmony_ci * This is more reliable than the 8390's detection. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci if (dev->flags & IFF_AUTOMEDIA) { 4728c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 4738c2ecf20Sopenharmony_ci etherh_setif(dev); 4748c2ecf20Sopenharmony_ci mdelay(1); 4758c2ecf20Sopenharmony_ci if (!etherh_getifstat(dev)) { 4768c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 4778c2ecf20Sopenharmony_ci etherh_setif(dev); 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci } else 4808c2ecf20Sopenharmony_ci etherh_setif(dev); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci etherh_reset(dev); 4838c2ecf20Sopenharmony_ci __ei_open(dev); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci return 0; 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci/* 4898c2ecf20Sopenharmony_ci * The inverse routine to etherh_open(). 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_cistatic int 4928c2ecf20Sopenharmony_cietherh_close(struct net_device *dev) 4938c2ecf20Sopenharmony_ci{ 4948c2ecf20Sopenharmony_ci __ei_close (dev); 4958c2ecf20Sopenharmony_ci free_irq (dev->irq, dev); 4968c2ecf20Sopenharmony_ci return 0; 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * Read the ethernet address string from the on board rom. 5018c2ecf20Sopenharmony_ci * This is an ascii string... 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_cistatic int etherh_addr(char *addr, struct expansion_card *ec) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct in_chunk_dir cd; 5068c2ecf20Sopenharmony_ci char *s; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (!ecard_readchunk(&cd, ec, 0xf5, 0)) { 5098c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: unable to read module description string\n", 5108c2ecf20Sopenharmony_ci dev_name(&ec->dev)); 5118c2ecf20Sopenharmony_ci goto no_addr; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci s = strchr(cd.d.string, '('); 5158c2ecf20Sopenharmony_ci if (s) { 5168c2ecf20Sopenharmony_ci int i; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for (i = 0; i < 6; i++) { 5198c2ecf20Sopenharmony_ci addr[i] = simple_strtoul(s + 1, &s, 0x10); 5208c2ecf20Sopenharmony_ci if (*s != (i == 5? ')' : ':')) 5218c2ecf20Sopenharmony_ci break; 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (i == 6) 5258c2ecf20Sopenharmony_ci return 0; 5268c2ecf20Sopenharmony_ci } 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci printk(KERN_ERR "%s: unable to parse MAC address: %s\n", 5298c2ecf20Sopenharmony_ci dev_name(&ec->dev), cd.d.string); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci no_addr: 5328c2ecf20Sopenharmony_ci return -ENODEV; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci/* 5368c2ecf20Sopenharmony_ci * Create an ethernet address from the system serial number. 5378c2ecf20Sopenharmony_ci */ 5388c2ecf20Sopenharmony_cistatic int __init etherm_addr(char *addr) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci unsigned int serial; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (system_serial_low == 0 && system_serial_high == 0) 5438c2ecf20Sopenharmony_ci return -ENODEV; 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ci serial = system_serial_low | system_serial_high; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci addr[0] = 0; 5488c2ecf20Sopenharmony_ci addr[1] = 0; 5498c2ecf20Sopenharmony_ci addr[2] = 0xa4; 5508c2ecf20Sopenharmony_ci addr[3] = 0x10 + (serial >> 24); 5518c2ecf20Sopenharmony_ci addr[4] = serial >> 16; 5528c2ecf20Sopenharmony_ci addr[5] = serial >> 8; 5538c2ecf20Sopenharmony_ci return 0; 5548c2ecf20Sopenharmony_ci} 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_cistatic void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 5598c2ecf20Sopenharmony_ci strlcpy(info->version, DRV_VERSION, sizeof(info->version)); 5608c2ecf20Sopenharmony_ci strlcpy(info->bus_info, dev_name(dev->dev.parent), 5618c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int etherh_get_link_ksettings(struct net_device *dev, 5658c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 5688c2ecf20Sopenharmony_ci etherh_priv(dev)->supported); 5698c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_10; 5708c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 5718c2ecf20Sopenharmony_ci cmd->base.port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC; 5728c2ecf20Sopenharmony_ci cmd->base.autoneg = (dev->flags & IFF_AUTOMEDIA ? AUTONEG_ENABLE : 5738c2ecf20Sopenharmony_ci AUTONEG_DISABLE); 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic int etherh_set_link_ksettings(struct net_device *dev, 5788c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 5798c2ecf20Sopenharmony_ci{ 5808c2ecf20Sopenharmony_ci switch (cmd->base.autoneg) { 5818c2ecf20Sopenharmony_ci case AUTONEG_ENABLE: 5828c2ecf20Sopenharmony_ci dev->flags |= IFF_AUTOMEDIA; 5838c2ecf20Sopenharmony_ci break; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci case AUTONEG_DISABLE: 5868c2ecf20Sopenharmony_ci switch (cmd->base.port) { 5878c2ecf20Sopenharmony_ci case PORT_TP: 5888c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci case PORT_BNC: 5928c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 5938c2ecf20Sopenharmony_ci break; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci default: 5968c2ecf20Sopenharmony_ci return -EINVAL; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci dev->flags &= ~IFF_AUTOMEDIA; 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci default: 6028c2ecf20Sopenharmony_ci return -EINVAL; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci etherh_setif(dev); 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci return 0; 6088c2ecf20Sopenharmony_ci} 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_cistatic u32 etherh_get_msglevel(struct net_device *dev) 6118c2ecf20Sopenharmony_ci{ 6128c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci return ei_local->msg_enable; 6158c2ecf20Sopenharmony_ci} 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_cistatic void etherh_set_msglevel(struct net_device *dev, u32 v) 6188c2ecf20Sopenharmony_ci{ 6198c2ecf20Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci ei_local->msg_enable = v; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_cistatic const struct ethtool_ops etherh_ethtool_ops = { 6258c2ecf20Sopenharmony_ci .get_drvinfo = etherh_get_drvinfo, 6268c2ecf20Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 6278c2ecf20Sopenharmony_ci .get_msglevel = etherh_get_msglevel, 6288c2ecf20Sopenharmony_ci .set_msglevel = etherh_set_msglevel, 6298c2ecf20Sopenharmony_ci .get_link_ksettings = etherh_get_link_ksettings, 6308c2ecf20Sopenharmony_ci .set_link_ksettings = etherh_set_link_ksettings, 6318c2ecf20Sopenharmony_ci}; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic const struct net_device_ops etherh_netdev_ops = { 6348c2ecf20Sopenharmony_ci .ndo_open = etherh_open, 6358c2ecf20Sopenharmony_ci .ndo_stop = etherh_close, 6368c2ecf20Sopenharmony_ci .ndo_set_config = etherh_set_config, 6378c2ecf20Sopenharmony_ci .ndo_start_xmit = __ei_start_xmit, 6388c2ecf20Sopenharmony_ci .ndo_tx_timeout = __ei_tx_timeout, 6398c2ecf20Sopenharmony_ci .ndo_get_stats = __ei_get_stats, 6408c2ecf20Sopenharmony_ci .ndo_set_rx_mode = __ei_set_multicast_list, 6418c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 6428c2ecf20Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 6438c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 6448c2ecf20Sopenharmony_ci .ndo_poll_controller = __ei_poll, 6458c2ecf20Sopenharmony_ci#endif 6468c2ecf20Sopenharmony_ci}; 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_cistatic u32 etherh_regoffsets[16]; 6498c2ecf20Sopenharmony_cistatic u32 etherm_regoffsets[16]; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic int 6528c2ecf20Sopenharmony_cietherh_probe(struct expansion_card *ec, const struct ecard_id *id) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci const struct etherh_data *data = id->data; 6558c2ecf20Sopenharmony_ci struct ei_device *ei_local; 6568c2ecf20Sopenharmony_ci struct net_device *dev; 6578c2ecf20Sopenharmony_ci struct etherh_priv *eh; 6588c2ecf20Sopenharmony_ci int ret; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci ret = ecard_request_resources(ec); 6618c2ecf20Sopenharmony_ci if (ret) 6628c2ecf20Sopenharmony_ci goto out; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci dev = ____alloc_ei_netdev(sizeof(struct etherh_priv)); 6658c2ecf20Sopenharmony_ci if (!dev) { 6668c2ecf20Sopenharmony_ci ret = -ENOMEM; 6678c2ecf20Sopenharmony_ci goto release; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci SET_NETDEV_DEV(dev, &ec->dev); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci dev->netdev_ops = ðerh_netdev_ops; 6738c2ecf20Sopenharmony_ci dev->irq = ec->irq; 6748c2ecf20Sopenharmony_ci dev->ethtool_ops = ðerh_ethtool_ops; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (data->supported & SUPPORTED_Autoneg) 6778c2ecf20Sopenharmony_ci dev->flags |= IFF_AUTOMEDIA; 6788c2ecf20Sopenharmony_ci if (data->supported & SUPPORTED_TP) { 6798c2ecf20Sopenharmony_ci dev->flags |= IFF_PORTSEL; 6808c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 6818c2ecf20Sopenharmony_ci } else if (data->supported & SUPPORTED_BNC) { 6828c2ecf20Sopenharmony_ci dev->flags |= IFF_PORTSEL; 6838c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 6848c2ecf20Sopenharmony_ci } else 6858c2ecf20Sopenharmony_ci dev->if_port = IF_PORT_UNKNOWN; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci eh = etherh_priv(dev); 6888c2ecf20Sopenharmony_ci eh->supported = data->supported; 6898c2ecf20Sopenharmony_ci eh->ctrl = 0; 6908c2ecf20Sopenharmony_ci eh->id = ec->cid.product; 6918c2ecf20Sopenharmony_ci eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE); 6928c2ecf20Sopenharmony_ci if (!eh->memc) { 6938c2ecf20Sopenharmony_ci ret = -ENOMEM; 6948c2ecf20Sopenharmony_ci goto free; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci eh->ctrl_port = eh->memc; 6988c2ecf20Sopenharmony_ci if (data->ctrl_ioc) { 6998c2ecf20Sopenharmony_ci eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE); 7008c2ecf20Sopenharmony_ci if (!eh->ioc_fast) { 7018c2ecf20Sopenharmony_ci ret = -ENOMEM; 7028c2ecf20Sopenharmony_ci goto free; 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci eh->ctrl_port = eh->ioc_fast; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset; 7088c2ecf20Sopenharmony_ci eh->dma_base = eh->memc + data->dataport_offset; 7098c2ecf20Sopenharmony_ci eh->ctrl_port += data->ctrlport_offset; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* 7128c2ecf20Sopenharmony_ci * IRQ and control port handling - only for non-NIC slot cards. 7138c2ecf20Sopenharmony_ci */ 7148c2ecf20Sopenharmony_ci if (ec->slot_no != 8) { 7158c2ecf20Sopenharmony_ci ecard_setirq(ec, ðerh_ops, eh); 7168c2ecf20Sopenharmony_ci } else { 7178c2ecf20Sopenharmony_ci /* 7188c2ecf20Sopenharmony_ci * If we're in the NIC slot, make sure the IRQ is enabled 7198c2ecf20Sopenharmony_ci */ 7208c2ecf20Sopenharmony_ci etherh_set_ctrl(eh, ETHERH_CP_IE); 7218c2ecf20Sopenharmony_ci } 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci ei_local = netdev_priv(dev); 7248c2ecf20Sopenharmony_ci spin_lock_init(&ei_local->page_lock); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (ec->cid.product == PROD_ANT_ETHERM) { 7278c2ecf20Sopenharmony_ci etherm_addr(dev->dev_addr); 7288c2ecf20Sopenharmony_ci ei_local->reg_offset = etherm_regoffsets; 7298c2ecf20Sopenharmony_ci } else { 7308c2ecf20Sopenharmony_ci etherh_addr(dev->dev_addr, ec); 7318c2ecf20Sopenharmony_ci ei_local->reg_offset = etherh_regoffsets; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci ei_local->name = dev->name; 7358c2ecf20Sopenharmony_ci ei_local->word16 = 1; 7368c2ecf20Sopenharmony_ci ei_local->tx_start_page = data->tx_start_page; 7378c2ecf20Sopenharmony_ci ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; 7388c2ecf20Sopenharmony_ci ei_local->stop_page = data->stop_page; 7398c2ecf20Sopenharmony_ci ei_local->reset_8390 = etherh_reset; 7408c2ecf20Sopenharmony_ci ei_local->block_input = etherh_block_input; 7418c2ecf20Sopenharmony_ci ei_local->block_output = etherh_block_output; 7428c2ecf20Sopenharmony_ci ei_local->get_8390_hdr = etherh_get_header; 7438c2ecf20Sopenharmony_ci ei_local->interface_num = 0; 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci etherh_reset(dev); 7468c2ecf20Sopenharmony_ci __NS8390_init(dev, 0); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci ret = register_netdev(dev); 7498c2ecf20Sopenharmony_ci if (ret) 7508c2ecf20Sopenharmony_ci goto free; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci netdev_info(dev, "%s in slot %d, %pM\n", 7538c2ecf20Sopenharmony_ci data->name, ec->slot_no, dev->dev_addr); 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, dev); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci return 0; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci free: 7608c2ecf20Sopenharmony_ci free_netdev(dev); 7618c2ecf20Sopenharmony_ci release: 7628c2ecf20Sopenharmony_ci ecard_release_resources(ec); 7638c2ecf20Sopenharmony_ci out: 7648c2ecf20Sopenharmony_ci return ret; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic void etherh_remove(struct expansion_card *ec) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct net_device *dev = ecard_get_drvdata(ec); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci ecard_set_drvdata(ec, NULL); 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci unregister_netdev(dev); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci free_netdev(dev); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci ecard_release_resources(ec); 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic struct etherh_data etherm_data = { 7818c2ecf20Sopenharmony_ci .ns8390_offset = ETHERM_NS8390, 7828c2ecf20Sopenharmony_ci .dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT, 7838c2ecf20Sopenharmony_ci .ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT, 7848c2ecf20Sopenharmony_ci .name = "ANT EtherM", 7858c2ecf20Sopenharmony_ci .supported = SUPPORTED_10baseT_Half, 7868c2ecf20Sopenharmony_ci .tx_start_page = ETHERM_TX_START_PAGE, 7878c2ecf20Sopenharmony_ci .stop_page = ETHERM_STOP_PAGE, 7888c2ecf20Sopenharmony_ci}; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cistatic struct etherh_data etherlan500_data = { 7918c2ecf20Sopenharmony_ci .ns8390_offset = ETHERH500_NS8390, 7928c2ecf20Sopenharmony_ci .dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT, 7938c2ecf20Sopenharmony_ci .ctrlport_offset = ETHERH500_CTRLPORT, 7948c2ecf20Sopenharmony_ci .ctrl_ioc = 1, 7958c2ecf20Sopenharmony_ci .name = "i3 EtherH 500", 7968c2ecf20Sopenharmony_ci .supported = SUPPORTED_10baseT_Half, 7978c2ecf20Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 7988c2ecf20Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 7998c2ecf20Sopenharmony_ci}; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_cistatic struct etherh_data etherlan600_data = { 8028c2ecf20Sopenharmony_ci .ns8390_offset = ETHERH600_NS8390, 8038c2ecf20Sopenharmony_ci .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT, 8048c2ecf20Sopenharmony_ci .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT, 8058c2ecf20Sopenharmony_ci .name = "i3 EtherH 600", 8068c2ecf20Sopenharmony_ci .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, 8078c2ecf20Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 8088c2ecf20Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 8098c2ecf20Sopenharmony_ci}; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_cistatic struct etherh_data etherlan600a_data = { 8128c2ecf20Sopenharmony_ci .ns8390_offset = ETHERH600_NS8390, 8138c2ecf20Sopenharmony_ci .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT, 8148c2ecf20Sopenharmony_ci .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT, 8158c2ecf20Sopenharmony_ci .name = "i3 EtherH 600A", 8168c2ecf20Sopenharmony_ci .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, 8178c2ecf20Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 8188c2ecf20Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 8198c2ecf20Sopenharmony_ci}; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic const struct ecard_id etherh_ids[] = { 8228c2ecf20Sopenharmony_ci { MANU_ANT, PROD_ANT_ETHERM, ðerm_data }, 8238c2ecf20Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN500, ðerlan500_data }, 8248c2ecf20Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN600, ðerlan600_data }, 8258c2ecf20Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN600A, ðerlan600a_data }, 8268c2ecf20Sopenharmony_ci { 0xffff, 0xffff } 8278c2ecf20Sopenharmony_ci}; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_cistatic struct ecard_driver etherh_driver = { 8308c2ecf20Sopenharmony_ci .probe = etherh_probe, 8318c2ecf20Sopenharmony_ci .remove = etherh_remove, 8328c2ecf20Sopenharmony_ci .id_table = etherh_ids, 8338c2ecf20Sopenharmony_ci .drv = { 8348c2ecf20Sopenharmony_ci .name = DRV_NAME, 8358c2ecf20Sopenharmony_ci }, 8368c2ecf20Sopenharmony_ci}; 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic int __init etherh_init(void) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci int i; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 8438c2ecf20Sopenharmony_ci etherh_regoffsets[i] = i << 2; 8448c2ecf20Sopenharmony_ci etherm_regoffsets[i] = i << 5; 8458c2ecf20Sopenharmony_ci } 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci return ecard_register_driver(ðerh_driver); 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void __exit etherh_exit(void) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci ecard_remove_driver(ðerh_driver); 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_cimodule_init(etherh_init); 8568c2ecf20Sopenharmony_cimodule_exit(etherh_exit); 857