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		= &etherh_netdev_ops;
67462306a36Sopenharmony_ci	dev->irq		= ec->irq;
67562306a36Sopenharmony_ci	dev->ethtool_ops	= &etherh_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, &etherh_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,      &etherm_data       },
82562306a36Sopenharmony_ci	{ MANU_I3,  PROD_I3_ETHERLAN500,  &etherlan500_data  },
82662306a36Sopenharmony_ci	{ MANU_I3,  PROD_I3_ETHERLAN600,  &etherlan600_data  },
82762306a36Sopenharmony_ci	{ MANU_I3,  PROD_I3_ETHERLAN600A, &etherlan600a_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(&etherh_driver);
85062306a36Sopenharmony_ci}
85162306a36Sopenharmony_ci
85262306a36Sopenharmony_cistatic void __exit etherh_exit(void)
85362306a36Sopenharmony_ci{
85462306a36Sopenharmony_ci	ecard_remove_driver(&etherh_driver);
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cimodule_init(etherh_init);
85862306a36Sopenharmony_cimodule_exit(etherh_exit);
859