162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/drivers/acorn/net/etherh.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2000-2002 Russell King 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * NS8390 I-cubed EtherH and ANT EtherM specific driver 862306a36Sopenharmony_ci * Thanks to I-Cubed for information on their cards. 962306a36Sopenharmony_ci * EtherM conversion (C) 1999 Chris Kemp and Tim Watterton 1062306a36Sopenharmony_ci * EtherM integration (C) 2000 Aleph One Ltd (Tak-Shing Chan) 1162306a36Sopenharmony_ci * EtherM integration re-engineered by Russell King. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Changelog: 1462306a36Sopenharmony_ci * 08-12-1996 RMK 1.00 Created 1562306a36Sopenharmony_ci * RMK 1.03 Added support for EtherLan500 cards 1662306a36Sopenharmony_ci * 23-11-1997 RMK 1.04 Added media autodetection 1762306a36Sopenharmony_ci * 16-04-1998 RMK 1.05 Improved media autodetection 1862306a36Sopenharmony_ci * 10-02-2000 RMK 1.06 Updated for 2.3.43 1962306a36Sopenharmony_ci * 13-05-2000 RMK 1.07 Updated for 2.3.99-pre8 2062306a36Sopenharmony_ci * 12-10-1999 CK/TEW EtherM driver first release 2162306a36Sopenharmony_ci * 21-12-2000 TTC EtherH/EtherM integration 2262306a36Sopenharmony_ci * 25-12-2000 RMK 1.08 Clean integration of EtherM into this driver. 2362306a36Sopenharmony_ci * 03-01-2002 RMK 1.09 Always enable IRQs if we're in the nic slot. 2462306a36Sopenharmony_ci */ 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include <linux/module.h> 2762306a36Sopenharmony_ci#include <linux/kernel.h> 2862306a36Sopenharmony_ci#include <linux/types.h> 2962306a36Sopenharmony_ci#include <linux/fcntl.h> 3062306a36Sopenharmony_ci#include <linux/interrupt.h> 3162306a36Sopenharmony_ci#include <linux/ioport.h> 3262306a36Sopenharmony_ci#include <linux/in.h> 3362306a36Sopenharmony_ci#include <linux/string.h> 3462306a36Sopenharmony_ci#include <linux/errno.h> 3562306a36Sopenharmony_ci#include <linux/netdevice.h> 3662306a36Sopenharmony_ci#include <linux/etherdevice.h> 3762306a36Sopenharmony_ci#include <linux/ethtool.h> 3862306a36Sopenharmony_ci#include <linux/skbuff.h> 3962306a36Sopenharmony_ci#include <linux/delay.h> 4062306a36Sopenharmony_ci#include <linux/device.h> 4162306a36Sopenharmony_ci#include <linux/init.h> 4262306a36Sopenharmony_ci#include <linux/bitops.h> 4362306a36Sopenharmony_ci#include <linux/jiffies.h> 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include <asm/ecard.h> 4662306a36Sopenharmony_ci#include <asm/io.h> 4762306a36Sopenharmony_ci#include <asm/system_info.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define EI_SHIFT(x) (ei_local->reg_offset[x]) 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#define ei_inb(_p) readb((void __iomem *)_p) 5262306a36Sopenharmony_ci#define ei_outb(_v,_p) writeb(_v,(void __iomem *)_p) 5362306a36Sopenharmony_ci#define ei_inb_p(_p) readb((void __iomem *)_p) 5462306a36Sopenharmony_ci#define ei_outb_p(_v,_p) writeb(_v,(void __iomem *)_p) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define DRV_NAME "etherh" 5762306a36Sopenharmony_ci#define DRV_VERSION "1.11" 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic char version[] = 6062306a36Sopenharmony_ci "EtherH/EtherM Driver (c) 2002-2004 Russell King " DRV_VERSION "\n"; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#include "lib8390.c" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct etherh_priv { 6562306a36Sopenharmony_ci void __iomem *ioc_fast; 6662306a36Sopenharmony_ci void __iomem *memc; 6762306a36Sopenharmony_ci void __iomem *dma_base; 6862306a36Sopenharmony_ci unsigned int id; 6962306a36Sopenharmony_ci void __iomem *ctrl_port; 7062306a36Sopenharmony_ci unsigned char ctrl; 7162306a36Sopenharmony_ci u32 supported; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistruct etherh_data { 7562306a36Sopenharmony_ci unsigned long ns8390_offset; 7662306a36Sopenharmony_ci unsigned long dataport_offset; 7762306a36Sopenharmony_ci unsigned long ctrlport_offset; 7862306a36Sopenharmony_ci int ctrl_ioc; 7962306a36Sopenharmony_ci const char name[16]; 8062306a36Sopenharmony_ci u32 supported; 8162306a36Sopenharmony_ci unsigned char tx_start_page; 8262306a36Sopenharmony_ci unsigned char stop_page; 8362306a36Sopenharmony_ci}; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciMODULE_AUTHOR("Russell King"); 8662306a36Sopenharmony_ciMODULE_DESCRIPTION("EtherH/EtherM driver"); 8762306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define ETHERH500_DATAPORT 0x800 /* MEMC */ 9062306a36Sopenharmony_ci#define ETHERH500_NS8390 0x000 /* MEMC */ 9162306a36Sopenharmony_ci#define ETHERH500_CTRLPORT 0x800 /* IOC */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define ETHERH600_DATAPORT 0x040 /* MEMC */ 9462306a36Sopenharmony_ci#define ETHERH600_NS8390 0x800 /* MEMC */ 9562306a36Sopenharmony_ci#define ETHERH600_CTRLPORT 0x200 /* MEMC */ 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define ETHERH_CP_IE 1 9862306a36Sopenharmony_ci#define ETHERH_CP_IF 2 9962306a36Sopenharmony_ci#define ETHERH_CP_HEARTBEAT 2 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define ETHERH_TX_START_PAGE 1 10262306a36Sopenharmony_ci#define ETHERH_STOP_PAGE 127 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/* 10562306a36Sopenharmony_ci * These came from CK/TEW 10662306a36Sopenharmony_ci */ 10762306a36Sopenharmony_ci#define ETHERM_DATAPORT 0x200 /* MEMC */ 10862306a36Sopenharmony_ci#define ETHERM_NS8390 0x800 /* MEMC */ 10962306a36Sopenharmony_ci#define ETHERM_CTRLPORT 0x23c /* MEMC */ 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define ETHERM_TX_START_PAGE 64 11262306a36Sopenharmony_ci#define ETHERM_STOP_PAGE 127 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* ------------------------------------------------------------------------ */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci#define etherh_priv(dev) \ 11762306a36Sopenharmony_ci ((struct etherh_priv *)(((char *)netdev_priv(dev)) + sizeof(struct ei_device))) 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline void etherh_set_ctrl(struct etherh_priv *eh, unsigned char mask) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci unsigned char ctrl = eh->ctrl | mask; 12262306a36Sopenharmony_ci eh->ctrl = ctrl; 12362306a36Sopenharmony_ci writeb(ctrl, eh->ctrl_port); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic inline void etherh_clr_ctrl(struct etherh_priv *eh, unsigned char mask) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci unsigned char ctrl = eh->ctrl & ~mask; 12962306a36Sopenharmony_ci eh->ctrl = ctrl; 13062306a36Sopenharmony_ci writeb(ctrl, eh->ctrl_port); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic inline unsigned int etherh_get_stat(struct etherh_priv *eh) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return readb(eh->ctrl_port); 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic void etherh_irq_enable(ecard_t *ec, int irqnr) 14262306a36Sopenharmony_ci{ 14362306a36Sopenharmony_ci struct etherh_priv *eh = ec->irq_data; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci etherh_set_ctrl(eh, ETHERH_CP_IE); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void etherh_irq_disable(ecard_t *ec, int irqnr) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci struct etherh_priv *eh = ec->irq_data; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci etherh_clr_ctrl(eh, ETHERH_CP_IE); 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic expansioncard_ops_t etherh_ops = { 15662306a36Sopenharmony_ci .irqenable = etherh_irq_enable, 15762306a36Sopenharmony_ci .irqdisable = etherh_irq_disable, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_cistatic void 16462306a36Sopenharmony_cietherh_setif(struct net_device *dev) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci void __iomem *addr; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci local_irq_save(flags); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci /* set the interface type */ 17362306a36Sopenharmony_ci switch (etherh_priv(dev)->id) { 17462306a36Sopenharmony_ci case PROD_I3_ETHERLAN600: 17562306a36Sopenharmony_ci case PROD_I3_ETHERLAN600A: 17662306a36Sopenharmony_ci addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci switch (dev->if_port) { 17962306a36Sopenharmony_ci case IF_PORT_10BASE2: 18062306a36Sopenharmony_ci writeb((readb(addr) & 0xf8) | 1, addr); 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci case IF_PORT_10BASET: 18362306a36Sopenharmony_ci writeb((readb(addr) & 0xf8), addr); 18462306a36Sopenharmony_ci break; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci break; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci case PROD_I3_ETHERLAN500: 18962306a36Sopenharmony_ci switch (dev->if_port) { 19062306a36Sopenharmony_ci case IF_PORT_10BASE2: 19162306a36Sopenharmony_ci etherh_clr_ctrl(etherh_priv(dev), ETHERH_CP_IF); 19262306a36Sopenharmony_ci break; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci case IF_PORT_10BASET: 19562306a36Sopenharmony_ci etherh_set_ctrl(etherh_priv(dev), ETHERH_CP_IF); 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci break; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci default: 20162306a36Sopenharmony_ci break; 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci local_irq_restore(flags); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic int 20862306a36Sopenharmony_cietherh_getifstat(struct net_device *dev) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 21162306a36Sopenharmony_ci void __iomem *addr; 21262306a36Sopenharmony_ci int stat = 0; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (etherh_priv(dev)->id) { 21562306a36Sopenharmony_ci case PROD_I3_ETHERLAN600: 21662306a36Sopenharmony_ci case PROD_I3_ETHERLAN600A: 21762306a36Sopenharmony_ci addr = (void __iomem *)dev->base_addr + EN0_RCNTHI; 21862306a36Sopenharmony_ci switch (dev->if_port) { 21962306a36Sopenharmony_ci case IF_PORT_10BASE2: 22062306a36Sopenharmony_ci stat = 1; 22162306a36Sopenharmony_ci break; 22262306a36Sopenharmony_ci case IF_PORT_10BASET: 22362306a36Sopenharmony_ci stat = readb(addr) & 4; 22462306a36Sopenharmony_ci break; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci break; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci case PROD_I3_ETHERLAN500: 22962306a36Sopenharmony_ci switch (dev->if_port) { 23062306a36Sopenharmony_ci case IF_PORT_10BASE2: 23162306a36Sopenharmony_ci stat = 1; 23262306a36Sopenharmony_ci break; 23362306a36Sopenharmony_ci case IF_PORT_10BASET: 23462306a36Sopenharmony_ci stat = etherh_get_stat(etherh_priv(dev)) & ETHERH_CP_HEARTBEAT; 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci } 23762306a36Sopenharmony_ci break; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci default: 24062306a36Sopenharmony_ci stat = 0; 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return stat != 0; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Configure the interface. Note that we ignore the other 24962306a36Sopenharmony_ci * parts of ifmap, since its mostly meaningless for this driver. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_cistatic int etherh_set_config(struct net_device *dev, struct ifmap *map) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci switch (map->port) { 25462306a36Sopenharmony_ci case IF_PORT_10BASE2: 25562306a36Sopenharmony_ci case IF_PORT_10BASET: 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * If the user explicitly sets the interface 25862306a36Sopenharmony_ci * media type, turn off automedia detection. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci dev->flags &= ~IFF_AUTOMEDIA; 26162306a36Sopenharmony_ci dev->if_port = map->port; 26262306a36Sopenharmony_ci break; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci default: 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci etherh_setif(dev); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci/* 27462306a36Sopenharmony_ci * Reset the 8390 (hard reset). Note that we can't actually do this. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_cistatic void 27762306a36Sopenharmony_cietherh_reset(struct net_device *dev) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 28062306a36Sopenharmony_ci void __iomem *addr = (void __iomem *)dev->base_addr; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci writeb(E8390_NODMA+E8390_PAGE0+E8390_STOP, addr); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * See if we need to change the interface type. 28662306a36Sopenharmony_ci * Note that we use 'interface_num' as a flag 28762306a36Sopenharmony_ci * to indicate that we need to change the media. 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci if (dev->flags & IFF_AUTOMEDIA && ei_local->interface_num) { 29062306a36Sopenharmony_ci ei_local->interface_num = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (dev->if_port == IF_PORT_10BASET) 29362306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 29462306a36Sopenharmony_ci else 29562306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci etherh_setif(dev); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* 30262306a36Sopenharmony_ci * Write a block of data out to the 8390 30362306a36Sopenharmony_ci */ 30462306a36Sopenharmony_cistatic void 30562306a36Sopenharmony_cietherh_block_output (struct net_device *dev, int count, const unsigned char *buf, int start_page) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 30862306a36Sopenharmony_ci unsigned long dma_start; 30962306a36Sopenharmony_ci void __iomem *dma_base, *addr; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci if (ei_local->dmaing) { 31262306a36Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_block_input: " 31362306a36Sopenharmony_ci " DMAstat %d irqlock %d\n", 31462306a36Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 31562306a36Sopenharmony_ci return; 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* 31962306a36Sopenharmony_ci * Make sure we have a round number of bytes if we're in word mode. 32062306a36Sopenharmony_ci */ 32162306a36Sopenharmony_ci if (count & 1 && ei_local->word16) 32262306a36Sopenharmony_ci count++; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci ei_local->dmaing = 1; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 32762306a36Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci count = (count + 1) & ~1; 33062306a36Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci writeb (0x42, addr + EN0_RCNTLO); 33362306a36Sopenharmony_ci writeb (0x00, addr + EN0_RCNTHI); 33462306a36Sopenharmony_ci writeb (0x42, addr + EN0_RSARLO); 33562306a36Sopenharmony_ci writeb (0x00, addr + EN0_RSARHI); 33662306a36Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci udelay (1); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 34162306a36Sopenharmony_ci writeb (count, addr + EN0_RCNTLO); 34262306a36Sopenharmony_ci writeb (count >> 8, addr + EN0_RCNTHI); 34362306a36Sopenharmony_ci writeb (0, addr + EN0_RSARLO); 34462306a36Sopenharmony_ci writeb (start_page, addr + EN0_RSARHI); 34562306a36Sopenharmony_ci writeb (E8390_RWRITE | E8390_START, addr + E8390_CMD); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (ei_local->word16) 34862306a36Sopenharmony_ci writesw (dma_base, buf, count >> 1); 34962306a36Sopenharmony_ci else 35062306a36Sopenharmony_ci writesb (dma_base, buf, count); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dma_start = jiffies; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci while ((readb (addr + EN0_ISR) & ENISR_RDC) == 0) 35562306a36Sopenharmony_ci if (time_after(jiffies, dma_start + 2*HZ/100)) { /* 20ms */ 35662306a36Sopenharmony_ci netdev_warn(dev, "timeout waiting for TX RDC\n"); 35762306a36Sopenharmony_ci etherh_reset (dev); 35862306a36Sopenharmony_ci __NS8390_init (dev, 1); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 36362306a36Sopenharmony_ci ei_local->dmaing = 0; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci/* 36762306a36Sopenharmony_ci * Read a block of data from the 8390 36862306a36Sopenharmony_ci */ 36962306a36Sopenharmony_cistatic void 37062306a36Sopenharmony_cietherh_block_input (struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 37362306a36Sopenharmony_ci unsigned char *buf; 37462306a36Sopenharmony_ci void __iomem *dma_base, *addr; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (ei_local->dmaing) { 37762306a36Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_block_input: " 37862306a36Sopenharmony_ci " DMAstat %d irqlock %d\n", 37962306a36Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 38062306a36Sopenharmony_ci return; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci ei_local->dmaing = 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 38662306a36Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci buf = skb->data; 38962306a36Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 39062306a36Sopenharmony_ci writeb (count, addr + EN0_RCNTLO); 39162306a36Sopenharmony_ci writeb (count >> 8, addr + EN0_RCNTHI); 39262306a36Sopenharmony_ci writeb (ring_offset, addr + EN0_RSARLO); 39362306a36Sopenharmony_ci writeb (ring_offset >> 8, addr + EN0_RSARHI); 39462306a36Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci if (ei_local->word16) { 39762306a36Sopenharmony_ci readsw (dma_base, buf, count >> 1); 39862306a36Sopenharmony_ci if (count & 1) 39962306a36Sopenharmony_ci buf[count - 1] = readb (dma_base); 40062306a36Sopenharmony_ci } else 40162306a36Sopenharmony_ci readsb (dma_base, buf, count); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 40462306a36Sopenharmony_ci ei_local->dmaing = 0; 40562306a36Sopenharmony_ci} 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci/* 40862306a36Sopenharmony_ci * Read a header from the 8390 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic void 41162306a36Sopenharmony_cietherh_get_header (struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 41462306a36Sopenharmony_ci void __iomem *dma_base, *addr; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci if (ei_local->dmaing) { 41762306a36Sopenharmony_ci netdev_err(dev, "DMAing conflict in etherh_get_header: " 41862306a36Sopenharmony_ci " DMAstat %d irqlock %d\n", 41962306a36Sopenharmony_ci ei_local->dmaing, ei_local->irqlock); 42062306a36Sopenharmony_ci return; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ei_local->dmaing = 1; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci addr = (void __iomem *)dev->base_addr; 42662306a36Sopenharmony_ci dma_base = etherh_priv(dev)->dma_base; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci writeb (E8390_NODMA | E8390_PAGE0 | E8390_START, addr + E8390_CMD); 42962306a36Sopenharmony_ci writeb (sizeof (*hdr), addr + EN0_RCNTLO); 43062306a36Sopenharmony_ci writeb (0, addr + EN0_RCNTHI); 43162306a36Sopenharmony_ci writeb (0, addr + EN0_RSARLO); 43262306a36Sopenharmony_ci writeb (ring_page, addr + EN0_RSARHI); 43362306a36Sopenharmony_ci writeb (E8390_RREAD | E8390_START, addr + E8390_CMD); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci if (ei_local->word16) 43662306a36Sopenharmony_ci readsw (dma_base, hdr, sizeof (*hdr) >> 1); 43762306a36Sopenharmony_ci else 43862306a36Sopenharmony_ci readsb (dma_base, hdr, sizeof (*hdr)); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci writeb (ENISR_RDC, addr + EN0_ISR); 44162306a36Sopenharmony_ci ei_local->dmaing = 0; 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci/* 44562306a36Sopenharmony_ci * Open/initialize the board. This is called (in the current kernel) 44662306a36Sopenharmony_ci * sometime after booting when the 'ifconfig' program is run. 44762306a36Sopenharmony_ci * 44862306a36Sopenharmony_ci * This routine should set everything up anew at each open, even 44962306a36Sopenharmony_ci * registers that "should" only need to be set once at boot, so that 45062306a36Sopenharmony_ci * there is non-reboot way to recover if something goes wrong. 45162306a36Sopenharmony_ci */ 45262306a36Sopenharmony_cistatic int 45362306a36Sopenharmony_cietherh_open(struct net_device *dev) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (request_irq(dev->irq, __ei_interrupt, 0, dev->name, dev)) 45862306a36Sopenharmony_ci return -EAGAIN; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* 46162306a36Sopenharmony_ci * Make sure that we aren't going to change the 46262306a36Sopenharmony_ci * media type on the next reset - we are about to 46362306a36Sopenharmony_ci * do automedia manually now. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci ei_local->interface_num = 0; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * If we are doing automedia detection, do it now. 46962306a36Sopenharmony_ci * This is more reliable than the 8390's detection. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_ci if (dev->flags & IFF_AUTOMEDIA) { 47262306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 47362306a36Sopenharmony_ci etherh_setif(dev); 47462306a36Sopenharmony_ci mdelay(1); 47562306a36Sopenharmony_ci if (!etherh_getifstat(dev)) { 47662306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 47762306a36Sopenharmony_ci etherh_setif(dev); 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci } else 48062306a36Sopenharmony_ci etherh_setif(dev); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci etherh_reset(dev); 48362306a36Sopenharmony_ci __ei_open(dev); 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci return 0; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/* 48962306a36Sopenharmony_ci * The inverse routine to etherh_open(). 49062306a36Sopenharmony_ci */ 49162306a36Sopenharmony_cistatic int 49262306a36Sopenharmony_cietherh_close(struct net_device *dev) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci __ei_close (dev); 49562306a36Sopenharmony_ci free_irq (dev->irq, dev); 49662306a36Sopenharmony_ci return 0; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci/* 50062306a36Sopenharmony_ci * Read the ethernet address string from the on board rom. 50162306a36Sopenharmony_ci * This is an ascii string... 50262306a36Sopenharmony_ci */ 50362306a36Sopenharmony_cistatic int etherh_addr(char *addr, struct expansion_card *ec) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct in_chunk_dir cd; 50662306a36Sopenharmony_ci char *s; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci if (!ecard_readchunk(&cd, ec, 0xf5, 0)) { 50962306a36Sopenharmony_ci printk(KERN_ERR "%s: unable to read module description string\n", 51062306a36Sopenharmony_ci dev_name(&ec->dev)); 51162306a36Sopenharmony_ci goto no_addr; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci s = strchr(cd.d.string, '('); 51562306a36Sopenharmony_ci if (s) { 51662306a36Sopenharmony_ci int i; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci for (i = 0; i < 6; i++) { 51962306a36Sopenharmony_ci addr[i] = simple_strtoul(s + 1, &s, 0x10); 52062306a36Sopenharmony_ci if (*s != (i == 5? ')' : ':')) 52162306a36Sopenharmony_ci break; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci if (i == 6) 52562306a36Sopenharmony_ci return 0; 52662306a36Sopenharmony_ci } 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci printk(KERN_ERR "%s: unable to parse MAC address: %s\n", 52962306a36Sopenharmony_ci dev_name(&ec->dev), cd.d.string); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci no_addr: 53262306a36Sopenharmony_ci return -ENODEV; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci/* 53662306a36Sopenharmony_ci * Create an ethernet address from the system serial number. 53762306a36Sopenharmony_ci */ 53862306a36Sopenharmony_cistatic int __init etherm_addr(char *addr) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci unsigned int serial; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (system_serial_low == 0 && system_serial_high == 0) 54362306a36Sopenharmony_ci return -ENODEV; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci serial = system_serial_low | system_serial_high; 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci addr[0] = 0; 54862306a36Sopenharmony_ci addr[1] = 0; 54962306a36Sopenharmony_ci addr[2] = 0xa4; 55062306a36Sopenharmony_ci addr[3] = 0x10 + (serial >> 24); 55162306a36Sopenharmony_ci addr[4] = serial >> 16; 55262306a36Sopenharmony_ci addr[5] = serial >> 8; 55362306a36Sopenharmony_ci return 0; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic void etherh_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 55962306a36Sopenharmony_ci strscpy(info->version, DRV_VERSION, sizeof(info->version)); 56062306a36Sopenharmony_ci strscpy(info->bus_info, dev_name(dev->dev.parent), 56162306a36Sopenharmony_ci sizeof(info->bus_info)); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int etherh_get_link_ksettings(struct net_device *dev, 56562306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 56862306a36Sopenharmony_ci etherh_priv(dev)->supported); 56962306a36Sopenharmony_ci cmd->base.speed = SPEED_10; 57062306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_HALF; 57162306a36Sopenharmony_ci cmd->base.port = dev->if_port == IF_PORT_10BASET ? PORT_TP : PORT_BNC; 57262306a36Sopenharmony_ci cmd->base.autoneg = (dev->flags & IFF_AUTOMEDIA ? AUTONEG_ENABLE : 57362306a36Sopenharmony_ci AUTONEG_DISABLE); 57462306a36Sopenharmony_ci return 0; 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int etherh_set_link_ksettings(struct net_device *dev, 57862306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci switch (cmd->base.autoneg) { 58162306a36Sopenharmony_ci case AUTONEG_ENABLE: 58262306a36Sopenharmony_ci dev->flags |= IFF_AUTOMEDIA; 58362306a36Sopenharmony_ci break; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci case AUTONEG_DISABLE: 58662306a36Sopenharmony_ci switch (cmd->base.port) { 58762306a36Sopenharmony_ci case PORT_TP: 58862306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci case PORT_BNC: 59262306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 59362306a36Sopenharmony_ci break; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci default: 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci dev->flags &= ~IFF_AUTOMEDIA; 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci default: 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci etherh_setif(dev); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci} 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_cistatic u32 etherh_get_msglevel(struct net_device *dev) 61162306a36Sopenharmony_ci{ 61262306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci return ei_local->msg_enable; 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void etherh_set_msglevel(struct net_device *dev, u32 v) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci struct ei_device *ei_local = netdev_priv(dev); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ei_local->msg_enable = v; 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistatic const struct ethtool_ops etherh_ethtool_ops = { 62562306a36Sopenharmony_ci .get_drvinfo = etherh_get_drvinfo, 62662306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 62762306a36Sopenharmony_ci .get_msglevel = etherh_get_msglevel, 62862306a36Sopenharmony_ci .set_msglevel = etherh_set_msglevel, 62962306a36Sopenharmony_ci .get_link_ksettings = etherh_get_link_ksettings, 63062306a36Sopenharmony_ci .set_link_ksettings = etherh_set_link_ksettings, 63162306a36Sopenharmony_ci}; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic const struct net_device_ops etherh_netdev_ops = { 63462306a36Sopenharmony_ci .ndo_open = etherh_open, 63562306a36Sopenharmony_ci .ndo_stop = etherh_close, 63662306a36Sopenharmony_ci .ndo_set_config = etherh_set_config, 63762306a36Sopenharmony_ci .ndo_start_xmit = __ei_start_xmit, 63862306a36Sopenharmony_ci .ndo_tx_timeout = __ei_tx_timeout, 63962306a36Sopenharmony_ci .ndo_get_stats = __ei_get_stats, 64062306a36Sopenharmony_ci .ndo_set_rx_mode = __ei_set_multicast_list, 64162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 64262306a36Sopenharmony_ci .ndo_set_mac_address = eth_mac_addr, 64362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 64462306a36Sopenharmony_ci .ndo_poll_controller = __ei_poll, 64562306a36Sopenharmony_ci#endif 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic u32 etherh_regoffsets[16]; 64962306a36Sopenharmony_cistatic u32 etherm_regoffsets[16]; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistatic int 65262306a36Sopenharmony_cietherh_probe(struct expansion_card *ec, const struct ecard_id *id) 65362306a36Sopenharmony_ci{ 65462306a36Sopenharmony_ci const struct etherh_data *data = id->data; 65562306a36Sopenharmony_ci struct ei_device *ei_local; 65662306a36Sopenharmony_ci struct net_device *dev; 65762306a36Sopenharmony_ci struct etherh_priv *eh; 65862306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 65962306a36Sopenharmony_ci int ret; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci ret = ecard_request_resources(ec); 66262306a36Sopenharmony_ci if (ret) 66362306a36Sopenharmony_ci goto out; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci dev = ____alloc_ei_netdev(sizeof(struct etherh_priv)); 66662306a36Sopenharmony_ci if (!dev) { 66762306a36Sopenharmony_ci ret = -ENOMEM; 66862306a36Sopenharmony_ci goto release; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &ec->dev); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci dev->netdev_ops = ðerh_netdev_ops; 67462306a36Sopenharmony_ci dev->irq = ec->irq; 67562306a36Sopenharmony_ci dev->ethtool_ops = ðerh_ethtool_ops; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci if (data->supported & SUPPORTED_Autoneg) 67862306a36Sopenharmony_ci dev->flags |= IFF_AUTOMEDIA; 67962306a36Sopenharmony_ci if (data->supported & SUPPORTED_TP) { 68062306a36Sopenharmony_ci dev->flags |= IFF_PORTSEL; 68162306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASET; 68262306a36Sopenharmony_ci } else if (data->supported & SUPPORTED_BNC) { 68362306a36Sopenharmony_ci dev->flags |= IFF_PORTSEL; 68462306a36Sopenharmony_ci dev->if_port = IF_PORT_10BASE2; 68562306a36Sopenharmony_ci } else 68662306a36Sopenharmony_ci dev->if_port = IF_PORT_UNKNOWN; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci eh = etherh_priv(dev); 68962306a36Sopenharmony_ci eh->supported = data->supported; 69062306a36Sopenharmony_ci eh->ctrl = 0; 69162306a36Sopenharmony_ci eh->id = ec->cid.product; 69262306a36Sopenharmony_ci eh->memc = ecardm_iomap(ec, ECARD_RES_MEMC, 0, PAGE_SIZE); 69362306a36Sopenharmony_ci if (!eh->memc) { 69462306a36Sopenharmony_ci ret = -ENOMEM; 69562306a36Sopenharmony_ci goto free; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci eh->ctrl_port = eh->memc; 69962306a36Sopenharmony_ci if (data->ctrl_ioc) { 70062306a36Sopenharmony_ci eh->ioc_fast = ecardm_iomap(ec, ECARD_RES_IOCFAST, 0, PAGE_SIZE); 70162306a36Sopenharmony_ci if (!eh->ioc_fast) { 70262306a36Sopenharmony_ci ret = -ENOMEM; 70362306a36Sopenharmony_ci goto free; 70462306a36Sopenharmony_ci } 70562306a36Sopenharmony_ci eh->ctrl_port = eh->ioc_fast; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci dev->base_addr = (unsigned long)eh->memc + data->ns8390_offset; 70962306a36Sopenharmony_ci eh->dma_base = eh->memc + data->dataport_offset; 71062306a36Sopenharmony_ci eh->ctrl_port += data->ctrlport_offset; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* 71362306a36Sopenharmony_ci * IRQ and control port handling - only for non-NIC slot cards. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_ci if (ec->slot_no != 8) { 71662306a36Sopenharmony_ci ecard_setirq(ec, ðerh_ops, eh); 71762306a36Sopenharmony_ci } else { 71862306a36Sopenharmony_ci /* 71962306a36Sopenharmony_ci * If we're in the NIC slot, make sure the IRQ is enabled 72062306a36Sopenharmony_ci */ 72162306a36Sopenharmony_ci etherh_set_ctrl(eh, ETHERH_CP_IE); 72262306a36Sopenharmony_ci } 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci ei_local = netdev_priv(dev); 72562306a36Sopenharmony_ci spin_lock_init(&ei_local->page_lock); 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci if (ec->cid.product == PROD_ANT_ETHERM) { 72862306a36Sopenharmony_ci etherm_addr(addr); 72962306a36Sopenharmony_ci ei_local->reg_offset = etherm_regoffsets; 73062306a36Sopenharmony_ci } else { 73162306a36Sopenharmony_ci etherh_addr(addr, ec); 73262306a36Sopenharmony_ci ei_local->reg_offset = etherh_regoffsets; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci ei_local->name = dev->name; 73762306a36Sopenharmony_ci ei_local->word16 = 1; 73862306a36Sopenharmony_ci ei_local->tx_start_page = data->tx_start_page; 73962306a36Sopenharmony_ci ei_local->rx_start_page = ei_local->tx_start_page + TX_PAGES; 74062306a36Sopenharmony_ci ei_local->stop_page = data->stop_page; 74162306a36Sopenharmony_ci ei_local->reset_8390 = etherh_reset; 74262306a36Sopenharmony_ci ei_local->block_input = etherh_block_input; 74362306a36Sopenharmony_ci ei_local->block_output = etherh_block_output; 74462306a36Sopenharmony_ci ei_local->get_8390_hdr = etherh_get_header; 74562306a36Sopenharmony_ci ei_local->interface_num = 0; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci etherh_reset(dev); 74862306a36Sopenharmony_ci __NS8390_init(dev, 0); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = register_netdev(dev); 75162306a36Sopenharmony_ci if (ret) 75262306a36Sopenharmony_ci goto free; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci netdev_info(dev, "%s in slot %d, %pM\n", 75562306a36Sopenharmony_ci data->name, ec->slot_no, dev->dev_addr); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci ecard_set_drvdata(ec, dev); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci free: 76262306a36Sopenharmony_ci free_netdev(dev); 76362306a36Sopenharmony_ci release: 76462306a36Sopenharmony_ci ecard_release_resources(ec); 76562306a36Sopenharmony_ci out: 76662306a36Sopenharmony_ci return ret; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic void etherh_remove(struct expansion_card *ec) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct net_device *dev = ecard_get_drvdata(ec); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci ecard_set_drvdata(ec, NULL); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci unregister_netdev(dev); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci free_netdev(dev); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci ecard_release_resources(ec); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic struct etherh_data etherm_data = { 78362306a36Sopenharmony_ci .ns8390_offset = ETHERM_NS8390, 78462306a36Sopenharmony_ci .dataport_offset = ETHERM_NS8390 + ETHERM_DATAPORT, 78562306a36Sopenharmony_ci .ctrlport_offset = ETHERM_NS8390 + ETHERM_CTRLPORT, 78662306a36Sopenharmony_ci .name = "ANT EtherM", 78762306a36Sopenharmony_ci .supported = SUPPORTED_10baseT_Half, 78862306a36Sopenharmony_ci .tx_start_page = ETHERM_TX_START_PAGE, 78962306a36Sopenharmony_ci .stop_page = ETHERM_STOP_PAGE, 79062306a36Sopenharmony_ci}; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cistatic struct etherh_data etherlan500_data = { 79362306a36Sopenharmony_ci .ns8390_offset = ETHERH500_NS8390, 79462306a36Sopenharmony_ci .dataport_offset = ETHERH500_NS8390 + ETHERH500_DATAPORT, 79562306a36Sopenharmony_ci .ctrlport_offset = ETHERH500_CTRLPORT, 79662306a36Sopenharmony_ci .ctrl_ioc = 1, 79762306a36Sopenharmony_ci .name = "i3 EtherH 500", 79862306a36Sopenharmony_ci .supported = SUPPORTED_10baseT_Half, 79962306a36Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 80062306a36Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 80162306a36Sopenharmony_ci}; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic struct etherh_data etherlan600_data = { 80462306a36Sopenharmony_ci .ns8390_offset = ETHERH600_NS8390, 80562306a36Sopenharmony_ci .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT, 80662306a36Sopenharmony_ci .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT, 80762306a36Sopenharmony_ci .name = "i3 EtherH 600", 80862306a36Sopenharmony_ci .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, 80962306a36Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 81062306a36Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 81162306a36Sopenharmony_ci}; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_cistatic struct etherh_data etherlan600a_data = { 81462306a36Sopenharmony_ci .ns8390_offset = ETHERH600_NS8390, 81562306a36Sopenharmony_ci .dataport_offset = ETHERH600_NS8390 + ETHERH600_DATAPORT, 81662306a36Sopenharmony_ci .ctrlport_offset = ETHERH600_NS8390 + ETHERH600_CTRLPORT, 81762306a36Sopenharmony_ci .name = "i3 EtherH 600A", 81862306a36Sopenharmony_ci .supported = SUPPORTED_10baseT_Half | SUPPORTED_TP | SUPPORTED_BNC | SUPPORTED_Autoneg, 81962306a36Sopenharmony_ci .tx_start_page = ETHERH_TX_START_PAGE, 82062306a36Sopenharmony_ci .stop_page = ETHERH_STOP_PAGE, 82162306a36Sopenharmony_ci}; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_cistatic const struct ecard_id etherh_ids[] = { 82462306a36Sopenharmony_ci { MANU_ANT, PROD_ANT_ETHERM, ðerm_data }, 82562306a36Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN500, ðerlan500_data }, 82662306a36Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN600, ðerlan600_data }, 82762306a36Sopenharmony_ci { MANU_I3, PROD_I3_ETHERLAN600A, ðerlan600a_data }, 82862306a36Sopenharmony_ci { 0xffff, 0xffff } 82962306a36Sopenharmony_ci}; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic struct ecard_driver etherh_driver = { 83262306a36Sopenharmony_ci .probe = etherh_probe, 83362306a36Sopenharmony_ci .remove = etherh_remove, 83462306a36Sopenharmony_ci .id_table = etherh_ids, 83562306a36Sopenharmony_ci .drv = { 83662306a36Sopenharmony_ci .name = DRV_NAME, 83762306a36Sopenharmony_ci }, 83862306a36Sopenharmony_ci}; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_cistatic int __init etherh_init(void) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci int i; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 84562306a36Sopenharmony_ci etherh_regoffsets[i] = i << 2; 84662306a36Sopenharmony_ci etherm_regoffsets[i] = i << 5; 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci return ecard_register_driver(ðerh_driver); 85062306a36Sopenharmony_ci} 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_cistatic void __exit etherh_exit(void) 85362306a36Sopenharmony_ci{ 85462306a36Sopenharmony_ci ecard_remove_driver(ðerh_driver); 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cimodule_init(etherh_init); 85862306a36Sopenharmony_cimodule_exit(etherh_exit); 859