162306a36Sopenharmony_ci/* cs89x0.c: A Crystal Semiconductor (Now Cirrus Logic) CS89[02]0
262306a36Sopenharmony_ci *           driver for linux.
362306a36Sopenharmony_ci * Written 1996 by Russell Nelson, with reference to skeleton.c
462306a36Sopenharmony_ci * written 1993-1994 by Donald Becker.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * This software may be used and distributed according to the terms
762306a36Sopenharmony_ci * of the GNU General Public License, incorporated herein by reference.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * The author may be reached at nelson@crynwr.com, Crynwr
1062306a36Sopenharmony_ci * Software, 521 Pleasant Valley Rd., Potsdam, NY 13676
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * Other contributors:
1362306a36Sopenharmony_ci * Mike Cruse        : mcruse@cti-ltd.com
1462306a36Sopenharmony_ci * Russ Nelson
1562306a36Sopenharmony_ci * Melody Lee        : ethernet@crystal.cirrus.com
1662306a36Sopenharmony_ci * Alan Cox
1762306a36Sopenharmony_ci * Andrew Morton
1862306a36Sopenharmony_ci * Oskar Schirmer    : oskar@scara.com
1962306a36Sopenharmony_ci * Deepak Saxena     : dsaxena@plexity.net
2062306a36Sopenharmony_ci * Dmitry Pervushin  : dpervushin@ru.mvista.com
2162306a36Sopenharmony_ci * Deepak Saxena     : dsaxena@plexity.net
2262306a36Sopenharmony_ci * Domenico Andreoli : cavokz@gmail.com
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * Set this to zero to disable DMA code
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * Note that even if DMA is turned off we still support the 'dma' and  'use_dma'
3062306a36Sopenharmony_ci * module options so we don't break any startup scripts.
3162306a36Sopenharmony_ci */
3262306a36Sopenharmony_ci#ifndef CONFIG_ISA_DMA_API
3362306a36Sopenharmony_ci#define ALLOW_DMA	0
3462306a36Sopenharmony_ci#else
3562306a36Sopenharmony_ci#define ALLOW_DMA	1
3662306a36Sopenharmony_ci#endif
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/*
3962306a36Sopenharmony_ci * Set this to zero to remove all the debug statements via
4062306a36Sopenharmony_ci * dead code elimination
4162306a36Sopenharmony_ci */
4262306a36Sopenharmony_ci#define DEBUGGING	1
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/* Sources:
4562306a36Sopenharmony_ci *	Crynwr packet driver epktisa.
4662306a36Sopenharmony_ci *	Crystal Semiconductor data sheets.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci#include <linux/module.h>
5262306a36Sopenharmony_ci#include <linux/printk.h>
5362306a36Sopenharmony_ci#include <linux/errno.h>
5462306a36Sopenharmony_ci#include <linux/netdevice.h>
5562306a36Sopenharmony_ci#include <linux/etherdevice.h>
5662306a36Sopenharmony_ci#include <linux/of.h>
5762306a36Sopenharmony_ci#include <linux/platform_device.h>
5862306a36Sopenharmony_ci#include <linux/kernel.h>
5962306a36Sopenharmony_ci#include <linux/types.h>
6062306a36Sopenharmony_ci#include <linux/fcntl.h>
6162306a36Sopenharmony_ci#include <linux/interrupt.h>
6262306a36Sopenharmony_ci#include <linux/ioport.h>
6362306a36Sopenharmony_ci#include <linux/in.h>
6462306a36Sopenharmony_ci#include <linux/jiffies.h>
6562306a36Sopenharmony_ci#include <linux/skbuff.h>
6662306a36Sopenharmony_ci#include <linux/spinlock.h>
6762306a36Sopenharmony_ci#include <linux/string.h>
6862306a36Sopenharmony_ci#include <linux/init.h>
6962306a36Sopenharmony_ci#include <linux/bitops.h>
7062306a36Sopenharmony_ci#include <linux/delay.h>
7162306a36Sopenharmony_ci#include <linux/gfp.h>
7262306a36Sopenharmony_ci#include <linux/io.h>
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci#include <net/Space.h>
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci#include <asm/irq.h>
7762306a36Sopenharmony_ci#include <linux/atomic.h>
7862306a36Sopenharmony_ci#if ALLOW_DMA
7962306a36Sopenharmony_ci#include <asm/dma.h>
8062306a36Sopenharmony_ci#endif
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci#include "cs89x0.h"
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci#define cs89_dbg(val, level, fmt, ...)				\
8562306a36Sopenharmony_cido {								\
8662306a36Sopenharmony_ci	if (val <= net_debug)					\
8762306a36Sopenharmony_ci		pr_##level(fmt, ##__VA_ARGS__);			\
8862306a36Sopenharmony_ci} while (0)
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic char version[] __initdata =
9162306a36Sopenharmony_ci	"v2.4.3-pre1 Russell Nelson <nelson@crynwr.com>, Andrew Morton";
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci#define DRV_NAME "cs89x0"
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci/* First, a few definitions that the brave might change.
9662306a36Sopenharmony_ci * A zero-terminated list of I/O addresses to be probed. Some special flags..
9762306a36Sopenharmony_ci * Addr & 1 = Read back the address port, look for signature and reset
9862306a36Sopenharmony_ci * the page window before probing
9962306a36Sopenharmony_ci * Addr & 3 = Reset the page window and probe
10062306a36Sopenharmony_ci * The CLPS eval board has the Cirrus chip at 0x80090300, in ARM IO space,
10162306a36Sopenharmony_ci * but it is possible that a Cirrus board could be plugged into the ISA
10262306a36Sopenharmony_ci * slots.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_ci/* The cs8900 has 4 IRQ pins, software selectable. cs8900_irq_map maps
10562306a36Sopenharmony_ci * them to system IRQ numbers. This mapping is card specific and is set to
10662306a36Sopenharmony_ci * the configuration of the Cirrus Eval board for this chip.
10762306a36Sopenharmony_ci */
10862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_ISA)
10962306a36Sopenharmony_cistatic unsigned int netcard_portlist[] __used __initdata = {
11062306a36Sopenharmony_ci	0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240,
11162306a36Sopenharmony_ci	0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_cistatic unsigned int cs8900_irq_map[] = {
11462306a36Sopenharmony_ci	10, 11, 12, 5
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci#endif
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci#if DEBUGGING
11962306a36Sopenharmony_cistatic unsigned int net_debug = DEBUGGING;
12062306a36Sopenharmony_ci#else
12162306a36Sopenharmony_ci#define net_debug 0	/* gcc will remove all the debug code for us */
12262306a36Sopenharmony_ci#endif
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/* The number of low I/O ports used by the ethercard. */
12562306a36Sopenharmony_ci#define NETCARD_IO_EXTENT	16
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci/* we allow the user to override various values normally set in the EEPROM */
12862306a36Sopenharmony_ci#define FORCE_RJ45	0x0001    /* pick one of these three */
12962306a36Sopenharmony_ci#define FORCE_AUI	0x0002
13062306a36Sopenharmony_ci#define FORCE_BNC	0x0004
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci#define FORCE_AUTO	0x0010    /* pick one of these three */
13362306a36Sopenharmony_ci#define FORCE_HALF	0x0020
13462306a36Sopenharmony_ci#define FORCE_FULL	0x0030
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* Information that need to be kept for each board. */
13762306a36Sopenharmony_cistruct net_local {
13862306a36Sopenharmony_ci	int chip_type;		/* one of: CS8900, CS8920, CS8920M */
13962306a36Sopenharmony_ci	char chip_revision;	/* revision letter of the chip ('A'...) */
14062306a36Sopenharmony_ci	int send_cmd;		/* the proper send command: TX_NOW, TX_AFTER_381, or TX_AFTER_ALL */
14162306a36Sopenharmony_ci	int auto_neg_cnf;	/* auto-negotiation word from EEPROM */
14262306a36Sopenharmony_ci	int adapter_cnf;	/* adapter configuration from EEPROM */
14362306a36Sopenharmony_ci	int isa_config;		/* ISA configuration from EEPROM */
14462306a36Sopenharmony_ci	int irq_map;		/* IRQ map from EEPROM */
14562306a36Sopenharmony_ci	int rx_mode;		/* what mode are we in? 0, RX_MULTCAST_ACCEPT, or RX_ALL_ACCEPT */
14662306a36Sopenharmony_ci	int curr_rx_cfg;	/* a copy of PP_RxCFG */
14762306a36Sopenharmony_ci	int linectl;		/* either 0 or LOW_RX_SQUELCH, depending on configuration. */
14862306a36Sopenharmony_ci	int send_underrun;	/* keep track of how many underruns in a row we get */
14962306a36Sopenharmony_ci	int force;		/* force various values; see FORCE* above. */
15062306a36Sopenharmony_ci	spinlock_t lock;
15162306a36Sopenharmony_ci	void __iomem *virt_addr;/* CS89x0 virtual address. */
15262306a36Sopenharmony_ci#if ALLOW_DMA
15362306a36Sopenharmony_ci	int use_dma;		/* Flag: we're using dma */
15462306a36Sopenharmony_ci	int dma;		/* DMA channel */
15562306a36Sopenharmony_ci	int dmasize;		/* 16 or 64 */
15662306a36Sopenharmony_ci	unsigned char *dma_buff;	/* points to the beginning of the buffer */
15762306a36Sopenharmony_ci	unsigned char *end_dma_buff;	/* points to the end of the buffer */
15862306a36Sopenharmony_ci	unsigned char *rx_dma_ptr;	/* points to the next packet  */
15962306a36Sopenharmony_ci#endif
16062306a36Sopenharmony_ci};
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci/* Example routines you must write ;->. */
16362306a36Sopenharmony_ci#define tx_done(dev) 1
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/*
16662306a36Sopenharmony_ci * Permit 'cs89x0_dma=N' in the kernel boot environment
16762306a36Sopenharmony_ci */
16862306a36Sopenharmony_ci#if !defined(MODULE)
16962306a36Sopenharmony_ci#if ALLOW_DMA
17062306a36Sopenharmony_cistatic int g_cs89x0_dma;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic int __init dma_fn(char *str)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	g_cs89x0_dma = simple_strtol(str, NULL, 0);
17562306a36Sopenharmony_ci	return 1;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci__setup("cs89x0_dma=", dma_fn);
17962306a36Sopenharmony_ci#endif	/* ALLOW_DMA */
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic int g_cs89x0_media__force;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic int __init media_fn(char *str)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	if (!strcmp(str, "rj45"))
18662306a36Sopenharmony_ci		g_cs89x0_media__force = FORCE_RJ45;
18762306a36Sopenharmony_ci	else if (!strcmp(str, "aui"))
18862306a36Sopenharmony_ci		g_cs89x0_media__force = FORCE_AUI;
18962306a36Sopenharmony_ci	else if (!strcmp(str, "bnc"))
19062306a36Sopenharmony_ci		g_cs89x0_media__force = FORCE_BNC;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	return 1;
19362306a36Sopenharmony_ci}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci__setup("cs89x0_media=", media_fn);
19662306a36Sopenharmony_ci#endif
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void readwords(struct net_local *lp, int portno, void *buf, int length)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	u8 *buf8 = (u8 *)buf;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	do {
20362306a36Sopenharmony_ci		u16 tmp16;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci		tmp16 = ioread16(lp->virt_addr + portno);
20662306a36Sopenharmony_ci		*buf8++ = (u8)tmp16;
20762306a36Sopenharmony_ci		*buf8++ = (u8)(tmp16 >> 8);
20862306a36Sopenharmony_ci	} while (--length);
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_cistatic void writewords(struct net_local *lp, int portno, void *buf, int length)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	u8 *buf8 = (u8 *)buf;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	do {
21662306a36Sopenharmony_ci		u16 tmp16;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci		tmp16 = *buf8++;
21962306a36Sopenharmony_ci		tmp16 |= (*buf8++) << 8;
22062306a36Sopenharmony_ci		iowrite16(tmp16, lp->virt_addr + portno);
22162306a36Sopenharmony_ci	} while (--length);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic u16
22562306a36Sopenharmony_cireadreg(struct net_device *dev, u16 regno)
22662306a36Sopenharmony_ci{
22762306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	iowrite16(regno, lp->virt_addr + ADD_PORT);
23062306a36Sopenharmony_ci	return ioread16(lp->virt_addr + DATA_PORT);
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void
23462306a36Sopenharmony_ciwritereg(struct net_device *dev, u16 regno, u16 value)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	iowrite16(regno, lp->virt_addr + ADD_PORT);
23962306a36Sopenharmony_ci	iowrite16(value, lp->virt_addr + DATA_PORT);
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_cistatic int __init
24362306a36Sopenharmony_ciwait_eeprom_ready(struct net_device *dev)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	unsigned long timeout = jiffies;
24662306a36Sopenharmony_ci	/* check to see if the EEPROM is ready,
24762306a36Sopenharmony_ci	 * a timeout is used just in case EEPROM is ready when
24862306a36Sopenharmony_ci	 * SI_BUSY in the PP_SelfST is clear
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	while (readreg(dev, PP_SelfST) & SI_BUSY)
25162306a36Sopenharmony_ci		if (time_after_eq(jiffies, timeout + 40))
25262306a36Sopenharmony_ci			return -1;
25362306a36Sopenharmony_ci	return 0;
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int __init
25762306a36Sopenharmony_ciget_eeprom_data(struct net_device *dev, int off, int len, int *buffer)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	int i;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	cs89_dbg(3, info, "EEPROM data from %x for %x:", off, len);
26262306a36Sopenharmony_ci	for (i = 0; i < len; i++) {
26362306a36Sopenharmony_ci		if (wait_eeprom_ready(dev) < 0)
26462306a36Sopenharmony_ci			return -1;
26562306a36Sopenharmony_ci		/* Now send the EEPROM read command and EEPROM location to read */
26662306a36Sopenharmony_ci		writereg(dev, PP_EECMD, (off + i) | EEPROM_READ_CMD);
26762306a36Sopenharmony_ci		if (wait_eeprom_ready(dev) < 0)
26862306a36Sopenharmony_ci			return -1;
26962306a36Sopenharmony_ci		buffer[i] = readreg(dev, PP_EEData);
27062306a36Sopenharmony_ci		cs89_dbg(3, cont, " %04x", buffer[i]);
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci	cs89_dbg(3, cont, "\n");
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic int  __init
27762306a36Sopenharmony_ciget_eeprom_cksum(int off, int len, int *buffer)
27862306a36Sopenharmony_ci{
27962306a36Sopenharmony_ci	int i, cksum;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	cksum = 0;
28262306a36Sopenharmony_ci	for (i = 0; i < len; i++)
28362306a36Sopenharmony_ci		cksum += buffer[i];
28462306a36Sopenharmony_ci	cksum &= 0xffff;
28562306a36Sopenharmony_ci	if (cksum == 0)
28662306a36Sopenharmony_ci		return 0;
28762306a36Sopenharmony_ci	return -1;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic void
29162306a36Sopenharmony_ciwrite_irq(struct net_device *dev, int chip_type, int irq)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	int i;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if (chip_type == CS8900) {
29662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_ISA)
29762306a36Sopenharmony_ci		/* Search the mapping table for the corresponding IRQ pin. */
29862306a36Sopenharmony_ci		for (i = 0; i != ARRAY_SIZE(cs8900_irq_map); i++)
29962306a36Sopenharmony_ci			if (cs8900_irq_map[i] == irq)
30062306a36Sopenharmony_ci				break;
30162306a36Sopenharmony_ci		/* Not found */
30262306a36Sopenharmony_ci		if (i == ARRAY_SIZE(cs8900_irq_map))
30362306a36Sopenharmony_ci			i = 3;
30462306a36Sopenharmony_ci#else
30562306a36Sopenharmony_ci		/* INTRQ0 pin is used for interrupt generation. */
30662306a36Sopenharmony_ci		i = 0;
30762306a36Sopenharmony_ci#endif
30862306a36Sopenharmony_ci		writereg(dev, PP_CS8900_ISAINT, i);
30962306a36Sopenharmony_ci	} else {
31062306a36Sopenharmony_ci		writereg(dev, PP_CS8920_ISAINT, irq);
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic void
31562306a36Sopenharmony_cicount_rx_errors(int status, struct net_device *dev)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	dev->stats.rx_errors++;
31862306a36Sopenharmony_ci	if (status & RX_RUNT)
31962306a36Sopenharmony_ci		dev->stats.rx_length_errors++;
32062306a36Sopenharmony_ci	if (status & RX_EXTRA_DATA)
32162306a36Sopenharmony_ci		dev->stats.rx_length_errors++;
32262306a36Sopenharmony_ci	if ((status & RX_CRC_ERROR) && !(status & (RX_EXTRA_DATA | RX_RUNT)))
32362306a36Sopenharmony_ci		/* per str 172 */
32462306a36Sopenharmony_ci		dev->stats.rx_crc_errors++;
32562306a36Sopenharmony_ci	if (status & RX_DRIBBLE)
32662306a36Sopenharmony_ci		dev->stats.rx_frame_errors++;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci/*********************************
33062306a36Sopenharmony_ci * This page contains DMA routines
33162306a36Sopenharmony_ci *********************************/
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#if ALLOW_DMA
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci#define dma_page_eq(ptr1, ptr2) ((long)(ptr1) >> 17 == (long)(ptr2) >> 17)
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistatic void
33862306a36Sopenharmony_ciget_dma_channel(struct net_device *dev)
33962306a36Sopenharmony_ci{
34062306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (lp->dma) {
34362306a36Sopenharmony_ci		dev->dma = lp->dma;
34462306a36Sopenharmony_ci		lp->isa_config |= ISA_RxDMA;
34562306a36Sopenharmony_ci	} else {
34662306a36Sopenharmony_ci		if ((lp->isa_config & ANY_ISA_DMA) == 0)
34762306a36Sopenharmony_ci			return;
34862306a36Sopenharmony_ci		dev->dma = lp->isa_config & DMA_NO_MASK;
34962306a36Sopenharmony_ci		if (lp->chip_type == CS8900)
35062306a36Sopenharmony_ci			dev->dma += 5;
35162306a36Sopenharmony_ci		if (dev->dma < 5 || dev->dma > 7) {
35262306a36Sopenharmony_ci			lp->isa_config &= ~ANY_ISA_DMA;
35362306a36Sopenharmony_ci			return;
35462306a36Sopenharmony_ci		}
35562306a36Sopenharmony_ci	}
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_cistatic void
35962306a36Sopenharmony_ciwrite_dma(struct net_device *dev, int chip_type, int dma)
36062306a36Sopenharmony_ci{
36162306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
36262306a36Sopenharmony_ci	if ((lp->isa_config & ANY_ISA_DMA) == 0)
36362306a36Sopenharmony_ci		return;
36462306a36Sopenharmony_ci	if (chip_type == CS8900)
36562306a36Sopenharmony_ci		writereg(dev, PP_CS8900_ISADMA, dma - 5);
36662306a36Sopenharmony_ci	else
36762306a36Sopenharmony_ci		writereg(dev, PP_CS8920_ISADMA, dma);
36862306a36Sopenharmony_ci}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_cistatic void
37162306a36Sopenharmony_ciset_dma_cfg(struct net_device *dev)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	if (lp->use_dma) {
37662306a36Sopenharmony_ci		if ((lp->isa_config & ANY_ISA_DMA) == 0) {
37762306a36Sopenharmony_ci			cs89_dbg(3, err, "set_dma_cfg(): no DMA\n");
37862306a36Sopenharmony_ci			return;
37962306a36Sopenharmony_ci		}
38062306a36Sopenharmony_ci		if (lp->isa_config & ISA_RxDMA) {
38162306a36Sopenharmony_ci			lp->curr_rx_cfg |= RX_DMA_ONLY;
38262306a36Sopenharmony_ci			cs89_dbg(3, info, "set_dma_cfg(): RX_DMA_ONLY\n");
38362306a36Sopenharmony_ci		} else {
38462306a36Sopenharmony_ci			lp->curr_rx_cfg |= AUTO_RX_DMA;	/* not that we support it... */
38562306a36Sopenharmony_ci			cs89_dbg(3, info, "set_dma_cfg(): AUTO_RX_DMA\n");
38662306a36Sopenharmony_ci		}
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_cistatic int
39162306a36Sopenharmony_cidma_bufcfg(struct net_device *dev)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
39462306a36Sopenharmony_ci	if (lp->use_dma)
39562306a36Sopenharmony_ci		return (lp->isa_config & ANY_ISA_DMA) ? RX_DMA_ENBL : 0;
39662306a36Sopenharmony_ci	else
39762306a36Sopenharmony_ci		return 0;
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int
40162306a36Sopenharmony_cidma_busctl(struct net_device *dev)
40262306a36Sopenharmony_ci{
40362306a36Sopenharmony_ci	int retval = 0;
40462306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
40562306a36Sopenharmony_ci	if (lp->use_dma) {
40662306a36Sopenharmony_ci		if (lp->isa_config & ANY_ISA_DMA)
40762306a36Sopenharmony_ci			retval |= RESET_RX_DMA; /* Reset the DMA pointer */
40862306a36Sopenharmony_ci		if (lp->isa_config & DMA_BURST)
40962306a36Sopenharmony_ci			retval |= DMA_BURST_MODE; /* Does ISA config specify DMA burst ? */
41062306a36Sopenharmony_ci		if (lp->dmasize == 64)
41162306a36Sopenharmony_ci			retval |= RX_DMA_SIZE_64K; /* did they ask for 64K? */
41262306a36Sopenharmony_ci		retval |= MEMORY_ON;	/* we need memory enabled to use DMA. */
41362306a36Sopenharmony_ci	}
41462306a36Sopenharmony_ci	return retval;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void
41862306a36Sopenharmony_cidma_rx(struct net_device *dev)
41962306a36Sopenharmony_ci{
42062306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
42162306a36Sopenharmony_ci	struct sk_buff *skb;
42262306a36Sopenharmony_ci	int status, length;
42362306a36Sopenharmony_ci	unsigned char *bp = lp->rx_dma_ptr;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	status = bp[0] + (bp[1] << 8);
42662306a36Sopenharmony_ci	length = bp[2] + (bp[3] << 8);
42762306a36Sopenharmony_ci	bp += 4;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	cs89_dbg(5, debug, "%s: receiving DMA packet at %lx, status %x, length %x\n",
43062306a36Sopenharmony_ci		 dev->name, (unsigned long)bp, status, length);
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if ((status & RX_OK) == 0) {
43362306a36Sopenharmony_ci		count_rx_errors(status, dev);
43462306a36Sopenharmony_ci		goto skip_this_frame;
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	/* Malloc up new buffer. */
43862306a36Sopenharmony_ci	skb = netdev_alloc_skb(dev, length + 2);
43962306a36Sopenharmony_ci	if (skb == NULL) {
44062306a36Sopenharmony_ci		dev->stats.rx_dropped++;
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci		/* AKPM: advance bp to the next frame */
44362306a36Sopenharmony_ciskip_this_frame:
44462306a36Sopenharmony_ci		bp += (length + 3) & ~3;
44562306a36Sopenharmony_ci		if (bp >= lp->end_dma_buff)
44662306a36Sopenharmony_ci			bp -= lp->dmasize * 1024;
44762306a36Sopenharmony_ci		lp->rx_dma_ptr = bp;
44862306a36Sopenharmony_ci		return;
44962306a36Sopenharmony_ci	}
45062306a36Sopenharmony_ci	skb_reserve(skb, 2);	/* longword align L3 header */
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	if (bp + length > lp->end_dma_buff) {
45362306a36Sopenharmony_ci		int semi_cnt = lp->end_dma_buff - bp;
45462306a36Sopenharmony_ci		skb_put_data(skb, bp, semi_cnt);
45562306a36Sopenharmony_ci		skb_put_data(skb, lp->dma_buff, length - semi_cnt);
45662306a36Sopenharmony_ci	} else {
45762306a36Sopenharmony_ci		skb_put_data(skb, bp, length);
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci	bp += (length + 3) & ~3;
46062306a36Sopenharmony_ci	if (bp >= lp->end_dma_buff)
46162306a36Sopenharmony_ci		bp -= lp->dmasize*1024;
46262306a36Sopenharmony_ci	lp->rx_dma_ptr = bp;
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	cs89_dbg(3, info, "%s: received %d byte DMA packet of type %x\n",
46562306a36Sopenharmony_ci		 dev->name, length,
46662306a36Sopenharmony_ci		 ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
46762306a36Sopenharmony_ci		  skb->data[ETH_ALEN + ETH_ALEN + 1]));
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
47062306a36Sopenharmony_ci	netif_rx(skb);
47162306a36Sopenharmony_ci	dev->stats.rx_packets++;
47262306a36Sopenharmony_ci	dev->stats.rx_bytes += length;
47362306a36Sopenharmony_ci}
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic void release_dma_buff(struct net_local *lp)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	if (lp->dma_buff) {
47862306a36Sopenharmony_ci		free_pages((unsigned long)(lp->dma_buff),
47962306a36Sopenharmony_ci			   get_order(lp->dmasize * 1024));
48062306a36Sopenharmony_ci		lp->dma_buff = NULL;
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci#endif	/* ALLOW_DMA */
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic void
48762306a36Sopenharmony_cicontrol_dc_dc(struct net_device *dev, int on_not_off)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
49062306a36Sopenharmony_ci	unsigned int selfcontrol;
49162306a36Sopenharmony_ci	unsigned long timenow = jiffies;
49262306a36Sopenharmony_ci	/* control the DC to DC convertor in the SelfControl register.
49362306a36Sopenharmony_ci	 * Note: This is hooked up to a general purpose pin, might not
49462306a36Sopenharmony_ci	 * always be a DC to DC convertor.
49562306a36Sopenharmony_ci	 */
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci	selfcontrol = HCB1_ENBL; /* Enable the HCB1 bit as an output */
49862306a36Sopenharmony_ci	if (((lp->adapter_cnf & A_CNF_DC_DC_POLARITY) != 0) ^ on_not_off)
49962306a36Sopenharmony_ci		selfcontrol |= HCB1;
50062306a36Sopenharmony_ci	else
50162306a36Sopenharmony_ci		selfcontrol &= ~HCB1;
50262306a36Sopenharmony_ci	writereg(dev, PP_SelfCTL, selfcontrol);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	/* Wait for the DC/DC converter to power up - 500ms */
50562306a36Sopenharmony_ci	while (time_before(jiffies, timenow + HZ))
50662306a36Sopenharmony_ci		;
50762306a36Sopenharmony_ci}
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/* send a test packet - return true if carrier bits are ok */
51062306a36Sopenharmony_cistatic int
51162306a36Sopenharmony_cisend_test_pkt(struct net_device *dev)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
51462306a36Sopenharmony_ci	char test_packet[] = {
51562306a36Sopenharmony_ci		0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0,
51662306a36Sopenharmony_ci		0, 46,		/* A 46 in network order */
51762306a36Sopenharmony_ci		0, 0,		/* DSAP=0 & SSAP=0 fields */
51862306a36Sopenharmony_ci		0xf3, 0		/* Control (Test Req + P bit set) */
51962306a36Sopenharmony_ci	};
52062306a36Sopenharmony_ci	unsigned long timenow = jiffies;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	writereg(dev, PP_LineCTL, readreg(dev, PP_LineCTL) | SERIAL_TX_ON);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	memcpy(test_packet,            dev->dev_addr, ETH_ALEN);
52562306a36Sopenharmony_ci	memcpy(test_packet + ETH_ALEN, dev->dev_addr, ETH_ALEN);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	iowrite16(TX_AFTER_ALL, lp->virt_addr + TX_CMD_PORT);
52862306a36Sopenharmony_ci	iowrite16(ETH_ZLEN, lp->virt_addr + TX_LEN_PORT);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	/* Test to see if the chip has allocated memory for the packet */
53162306a36Sopenharmony_ci	while (time_before(jiffies, timenow + 5))
53262306a36Sopenharmony_ci		if (readreg(dev, PP_BusST) & READY_FOR_TX_NOW)
53362306a36Sopenharmony_ci			break;
53462306a36Sopenharmony_ci	if (time_after_eq(jiffies, timenow + 5))
53562306a36Sopenharmony_ci		return 0;	/* this shouldn't happen */
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	/* Write the contents of the packet */
53862306a36Sopenharmony_ci	writewords(lp, TX_FRAME_PORT, test_packet, (ETH_ZLEN + 1) >> 1);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	cs89_dbg(1, debug, "Sending test packet ");
54162306a36Sopenharmony_ci	/* wait a couple of jiffies for packet to be received */
54262306a36Sopenharmony_ci	for (timenow = jiffies; time_before(jiffies, timenow + 3);)
54362306a36Sopenharmony_ci		;
54462306a36Sopenharmony_ci	if ((readreg(dev, PP_TxEvent) & TX_SEND_OK_BITS) == TX_OK) {
54562306a36Sopenharmony_ci		cs89_dbg(1, cont, "succeeded\n");
54662306a36Sopenharmony_ci		return 1;
54762306a36Sopenharmony_ci	}
54862306a36Sopenharmony_ci	cs89_dbg(1, cont, "failed\n");
54962306a36Sopenharmony_ci	return 0;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci#define DETECTED_NONE  0
55362306a36Sopenharmony_ci#define DETECTED_RJ45H 1
55462306a36Sopenharmony_ci#define DETECTED_RJ45F 2
55562306a36Sopenharmony_ci#define DETECTED_AUI   3
55662306a36Sopenharmony_ci#define DETECTED_BNC   4
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int
55962306a36Sopenharmony_cidetect_tp(struct net_device *dev)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
56262306a36Sopenharmony_ci	unsigned long timenow = jiffies;
56362306a36Sopenharmony_ci	int fdx;
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	cs89_dbg(1, debug, "%s: Attempting TP\n", dev->name);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	/* If connected to another full duplex capable 10-Base-T card
56862306a36Sopenharmony_ci	 * the link pulses seem to be lost when the auto detect bit in
56962306a36Sopenharmony_ci	 * the LineCTL is set.  To overcome this the auto detect bit will
57062306a36Sopenharmony_ci	 * be cleared whilst testing the 10-Base-T interface.  This would
57162306a36Sopenharmony_ci	 * not be necessary for the sparrow chip but is simpler to do it
57262306a36Sopenharmony_ci	 * anyway.
57362306a36Sopenharmony_ci	 */
57462306a36Sopenharmony_ci	writereg(dev, PP_LineCTL, lp->linectl & ~AUI_ONLY);
57562306a36Sopenharmony_ci	control_dc_dc(dev, 0);
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	/* Delay for the hardware to work out if the TP cable is present
57862306a36Sopenharmony_ci	 * - 150ms
57962306a36Sopenharmony_ci	 */
58062306a36Sopenharmony_ci	for (timenow = jiffies; time_before(jiffies, timenow + 15);)
58162306a36Sopenharmony_ci		;
58262306a36Sopenharmony_ci	if ((readreg(dev, PP_LineST) & LINK_OK) == 0)
58362306a36Sopenharmony_ci		return DETECTED_NONE;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	if (lp->chip_type == CS8900) {
58662306a36Sopenharmony_ci		switch (lp->force & 0xf0) {
58762306a36Sopenharmony_ci#if 0
58862306a36Sopenharmony_ci		case FORCE_AUTO:
58962306a36Sopenharmony_ci			pr_info("%s: cs8900 doesn't autonegotiate\n",
59062306a36Sopenharmony_ci				dev->name);
59162306a36Sopenharmony_ci			return DETECTED_NONE;
59262306a36Sopenharmony_ci#endif
59362306a36Sopenharmony_ci			/* CS8900 doesn't support AUTO, change to HALF*/
59462306a36Sopenharmony_ci		case FORCE_AUTO:
59562306a36Sopenharmony_ci			lp->force &= ~FORCE_AUTO;
59662306a36Sopenharmony_ci			lp->force |= FORCE_HALF;
59762306a36Sopenharmony_ci			break;
59862306a36Sopenharmony_ci		case FORCE_HALF:
59962306a36Sopenharmony_ci			break;
60062306a36Sopenharmony_ci		case FORCE_FULL:
60162306a36Sopenharmony_ci			writereg(dev, PP_TestCTL,
60262306a36Sopenharmony_ci				 readreg(dev, PP_TestCTL) | FDX_8900);
60362306a36Sopenharmony_ci			break;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci		fdx = readreg(dev, PP_TestCTL) & FDX_8900;
60662306a36Sopenharmony_ci	} else {
60762306a36Sopenharmony_ci		switch (lp->force & 0xf0) {
60862306a36Sopenharmony_ci		case FORCE_AUTO:
60962306a36Sopenharmony_ci			lp->auto_neg_cnf = AUTO_NEG_ENABLE;
61062306a36Sopenharmony_ci			break;
61162306a36Sopenharmony_ci		case FORCE_HALF:
61262306a36Sopenharmony_ci			lp->auto_neg_cnf = 0;
61362306a36Sopenharmony_ci			break;
61462306a36Sopenharmony_ci		case FORCE_FULL:
61562306a36Sopenharmony_ci			lp->auto_neg_cnf = RE_NEG_NOW | ALLOW_FDX;
61662306a36Sopenharmony_ci			break;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		writereg(dev, PP_AutoNegCTL, lp->auto_neg_cnf & AUTO_NEG_MASK);
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci		if ((lp->auto_neg_cnf & AUTO_NEG_BITS) == AUTO_NEG_ENABLE) {
62262306a36Sopenharmony_ci			pr_info("%s: negotiating duplex...\n", dev->name);
62362306a36Sopenharmony_ci			while (readreg(dev, PP_AutoNegST) & AUTO_NEG_BUSY) {
62462306a36Sopenharmony_ci				if (time_after(jiffies, timenow + 4000)) {
62562306a36Sopenharmony_ci					pr_err("**** Full / half duplex auto-negotiation timed out ****\n");
62662306a36Sopenharmony_ci					break;
62762306a36Sopenharmony_ci				}
62862306a36Sopenharmony_ci			}
62962306a36Sopenharmony_ci		}
63062306a36Sopenharmony_ci		fdx = readreg(dev, PP_AutoNegST) & FDX_ACTIVE;
63162306a36Sopenharmony_ci	}
63262306a36Sopenharmony_ci	if (fdx)
63362306a36Sopenharmony_ci		return DETECTED_RJ45F;
63462306a36Sopenharmony_ci	else
63562306a36Sopenharmony_ci		return DETECTED_RJ45H;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_cistatic int
63962306a36Sopenharmony_cidetect_bnc(struct net_device *dev)
64062306a36Sopenharmony_ci{
64162306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	cs89_dbg(1, debug, "%s: Attempting BNC\n", dev->name);
64462306a36Sopenharmony_ci	control_dc_dc(dev, 1);
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (send_test_pkt(dev))
64962306a36Sopenharmony_ci		return DETECTED_BNC;
65062306a36Sopenharmony_ci	else
65162306a36Sopenharmony_ci		return DETECTED_NONE;
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic int
65562306a36Sopenharmony_cidetect_aui(struct net_device *dev)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	cs89_dbg(1, debug, "%s: Attempting AUI\n", dev->name);
66062306a36Sopenharmony_ci	control_dc_dc(dev, 0);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	writereg(dev, PP_LineCTL, (lp->linectl & ~AUTO_AUI_10BASET) | AUI_ONLY);
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	if (send_test_pkt(dev))
66562306a36Sopenharmony_ci		return DETECTED_AUI;
66662306a36Sopenharmony_ci	else
66762306a36Sopenharmony_ci		return DETECTED_NONE;
66862306a36Sopenharmony_ci}
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci/* We have a good packet(s), get it/them out of the buffers. */
67162306a36Sopenharmony_cistatic void
67262306a36Sopenharmony_cinet_rx(struct net_device *dev)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
67562306a36Sopenharmony_ci	struct sk_buff *skb;
67662306a36Sopenharmony_ci	int status, length;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	status = ioread16(lp->virt_addr + RX_FRAME_PORT);
67962306a36Sopenharmony_ci	length = ioread16(lp->virt_addr + RX_FRAME_PORT);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if ((status & RX_OK) == 0) {
68262306a36Sopenharmony_ci		count_rx_errors(status, dev);
68362306a36Sopenharmony_ci		return;
68462306a36Sopenharmony_ci	}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	/* Malloc up new buffer. */
68762306a36Sopenharmony_ci	skb = netdev_alloc_skb(dev, length + 2);
68862306a36Sopenharmony_ci	if (skb == NULL) {
68962306a36Sopenharmony_ci		dev->stats.rx_dropped++;
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci	skb_reserve(skb, 2);	/* longword align L3 header */
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	readwords(lp, RX_FRAME_PORT, skb_put(skb, length), length >> 1);
69562306a36Sopenharmony_ci	if (length & 1)
69662306a36Sopenharmony_ci		skb->data[length-1] = ioread16(lp->virt_addr + RX_FRAME_PORT);
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	cs89_dbg(3, debug, "%s: received %d byte packet of type %x\n",
69962306a36Sopenharmony_ci		 dev->name, length,
70062306a36Sopenharmony_ci		 (skb->data[ETH_ALEN + ETH_ALEN] << 8) |
70162306a36Sopenharmony_ci		 skb->data[ETH_ALEN + ETH_ALEN + 1]);
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
70462306a36Sopenharmony_ci	netif_rx(skb);
70562306a36Sopenharmony_ci	dev->stats.rx_packets++;
70662306a36Sopenharmony_ci	dev->stats.rx_bytes += length;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci/* The typical workload of the driver:
71062306a36Sopenharmony_ci * Handle the network interface interrupts.
71162306a36Sopenharmony_ci */
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_cistatic irqreturn_t net_interrupt(int irq, void *dev_id)
71462306a36Sopenharmony_ci{
71562306a36Sopenharmony_ci	struct net_device *dev = dev_id;
71662306a36Sopenharmony_ci	struct net_local *lp;
71762306a36Sopenharmony_ci	int status;
71862306a36Sopenharmony_ci	int handled = 0;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci	lp = netdev_priv(dev);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci	/* we MUST read all the events out of the ISQ, otherwise we'll never
72362306a36Sopenharmony_ci	 * get interrupted again.  As a consequence, we can't have any limit
72462306a36Sopenharmony_ci	 * on the number of times we loop in the interrupt handler.  The
72562306a36Sopenharmony_ci	 * hardware guarantees that eventually we'll run out of events.  Of
72662306a36Sopenharmony_ci	 * course, if you're on a slow machine, and packets are arriving
72762306a36Sopenharmony_ci	 * faster than you can read them off, you're screwed.  Hasta la
72862306a36Sopenharmony_ci	 * vista, baby!
72962306a36Sopenharmony_ci	 */
73062306a36Sopenharmony_ci	while ((status = ioread16(lp->virt_addr + ISQ_PORT))) {
73162306a36Sopenharmony_ci		cs89_dbg(4, debug, "%s: event=%04x\n", dev->name, status);
73262306a36Sopenharmony_ci		handled = 1;
73362306a36Sopenharmony_ci		switch (status & ISQ_EVENT_MASK) {
73462306a36Sopenharmony_ci		case ISQ_RECEIVER_EVENT:
73562306a36Sopenharmony_ci			/* Got a packet(s). */
73662306a36Sopenharmony_ci			net_rx(dev);
73762306a36Sopenharmony_ci			break;
73862306a36Sopenharmony_ci		case ISQ_TRANSMITTER_EVENT:
73962306a36Sopenharmony_ci			dev->stats.tx_packets++;
74062306a36Sopenharmony_ci			netif_wake_queue(dev);	/* Inform upper layers. */
74162306a36Sopenharmony_ci			if ((status & (TX_OK |
74262306a36Sopenharmony_ci				       TX_LOST_CRS |
74362306a36Sopenharmony_ci				       TX_SQE_ERROR |
74462306a36Sopenharmony_ci				       TX_LATE_COL |
74562306a36Sopenharmony_ci				       TX_16_COL)) != TX_OK) {
74662306a36Sopenharmony_ci				if ((status & TX_OK) == 0)
74762306a36Sopenharmony_ci					dev->stats.tx_errors++;
74862306a36Sopenharmony_ci				if (status & TX_LOST_CRS)
74962306a36Sopenharmony_ci					dev->stats.tx_carrier_errors++;
75062306a36Sopenharmony_ci				if (status & TX_SQE_ERROR)
75162306a36Sopenharmony_ci					dev->stats.tx_heartbeat_errors++;
75262306a36Sopenharmony_ci				if (status & TX_LATE_COL)
75362306a36Sopenharmony_ci					dev->stats.tx_window_errors++;
75462306a36Sopenharmony_ci				if (status & TX_16_COL)
75562306a36Sopenharmony_ci					dev->stats.tx_aborted_errors++;
75662306a36Sopenharmony_ci			}
75762306a36Sopenharmony_ci			break;
75862306a36Sopenharmony_ci		case ISQ_BUFFER_EVENT:
75962306a36Sopenharmony_ci			if (status & READY_FOR_TX) {
76062306a36Sopenharmony_ci				/* we tried to transmit a packet earlier,
76162306a36Sopenharmony_ci				 * but inexplicably ran out of buffers.
76262306a36Sopenharmony_ci				 * That shouldn't happen since we only ever
76362306a36Sopenharmony_ci				 * load one packet.  Shrug.  Do the right
76462306a36Sopenharmony_ci				 * thing anyway.
76562306a36Sopenharmony_ci				 */
76662306a36Sopenharmony_ci				netif_wake_queue(dev);	/* Inform upper layers. */
76762306a36Sopenharmony_ci			}
76862306a36Sopenharmony_ci			if (status & TX_UNDERRUN) {
76962306a36Sopenharmony_ci				cs89_dbg(0, err, "%s: transmit underrun\n",
77062306a36Sopenharmony_ci					 dev->name);
77162306a36Sopenharmony_ci				lp->send_underrun++;
77262306a36Sopenharmony_ci				if (lp->send_underrun == 3)
77362306a36Sopenharmony_ci					lp->send_cmd = TX_AFTER_381;
77462306a36Sopenharmony_ci				else if (lp->send_underrun == 6)
77562306a36Sopenharmony_ci					lp->send_cmd = TX_AFTER_ALL;
77662306a36Sopenharmony_ci				/* transmit cycle is done, although
77762306a36Sopenharmony_ci				 * frame wasn't transmitted - this
77862306a36Sopenharmony_ci				 * avoids having to wait for the upper
77962306a36Sopenharmony_ci				 * layers to timeout on us, in the
78062306a36Sopenharmony_ci				 * event of a tx underrun
78162306a36Sopenharmony_ci				 */
78262306a36Sopenharmony_ci				netif_wake_queue(dev);	/* Inform upper layers. */
78362306a36Sopenharmony_ci			}
78462306a36Sopenharmony_ci#if ALLOW_DMA
78562306a36Sopenharmony_ci			if (lp->use_dma && (status & RX_DMA)) {
78662306a36Sopenharmony_ci				int count = readreg(dev, PP_DmaFrameCnt);
78762306a36Sopenharmony_ci				while (count) {
78862306a36Sopenharmony_ci					cs89_dbg(5, debug,
78962306a36Sopenharmony_ci						 "%s: receiving %d DMA frames\n",
79062306a36Sopenharmony_ci						 dev->name, count);
79162306a36Sopenharmony_ci					if (count > 1)
79262306a36Sopenharmony_ci						cs89_dbg(2, debug,
79362306a36Sopenharmony_ci							 "%s: receiving %d DMA frames\n",
79462306a36Sopenharmony_ci							 dev->name, count);
79562306a36Sopenharmony_ci					dma_rx(dev);
79662306a36Sopenharmony_ci					if (--count == 0)
79762306a36Sopenharmony_ci						count = readreg(dev, PP_DmaFrameCnt);
79862306a36Sopenharmony_ci					if (count > 0)
79962306a36Sopenharmony_ci						cs89_dbg(2, debug,
80062306a36Sopenharmony_ci							 "%s: continuing with %d DMA frames\n",
80162306a36Sopenharmony_ci							 dev->name, count);
80262306a36Sopenharmony_ci				}
80362306a36Sopenharmony_ci			}
80462306a36Sopenharmony_ci#endif
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		case ISQ_RX_MISS_EVENT:
80762306a36Sopenharmony_ci			dev->stats.rx_missed_errors += (status >> 6);
80862306a36Sopenharmony_ci			break;
80962306a36Sopenharmony_ci		case ISQ_TX_COL_EVENT:
81062306a36Sopenharmony_ci			dev->stats.collisions += (status >> 6);
81162306a36Sopenharmony_ci			break;
81262306a36Sopenharmony_ci		}
81362306a36Sopenharmony_ci	}
81462306a36Sopenharmony_ci	return IRQ_RETVAL(handled);
81562306a36Sopenharmony_ci}
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/* Open/initialize the board.  This is called (in the current kernel)
81862306a36Sopenharmony_ci   sometime after booting when the 'ifconfig' program is run.
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci   This routine should set everything up anew at each open, even
82162306a36Sopenharmony_ci   registers that "should" only need to be set once at boot, so that
82262306a36Sopenharmony_ci   there is non-reboot way to recover if something goes wrong.
82362306a36Sopenharmony_ci*/
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/* AKPM: do we need to do any locking here? */
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_cistatic int
82862306a36Sopenharmony_cinet_open(struct net_device *dev)
82962306a36Sopenharmony_ci{
83062306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
83162306a36Sopenharmony_ci	int result = 0;
83262306a36Sopenharmony_ci	int i;
83362306a36Sopenharmony_ci	int ret;
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	if (dev->irq < 2) {
83662306a36Sopenharmony_ci		/* Allow interrupts to be generated by the chip */
83762306a36Sopenharmony_ci/* Cirrus' release had this: */
83862306a36Sopenharmony_ci#if 0
83962306a36Sopenharmony_ci		writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL) | ENABLE_IRQ);
84062306a36Sopenharmony_ci#endif
84162306a36Sopenharmony_ci/* And 2.3.47 had this: */
84262306a36Sopenharmony_ci		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci		for (i = 2; i < CS8920_NO_INTS; i++) {
84562306a36Sopenharmony_ci			if ((1 << i) & lp->irq_map) {
84662306a36Sopenharmony_ci				if (request_irq(i, net_interrupt, 0, dev->name,
84762306a36Sopenharmony_ci						dev) == 0) {
84862306a36Sopenharmony_ci					dev->irq = i;
84962306a36Sopenharmony_ci					write_irq(dev, lp->chip_type, i);
85062306a36Sopenharmony_ci					/* writereg(dev, PP_BufCFG, GENERATE_SW_INTERRUPT); */
85162306a36Sopenharmony_ci					break;
85262306a36Sopenharmony_ci				}
85362306a36Sopenharmony_ci			}
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		if (i >= CS8920_NO_INTS) {
85762306a36Sopenharmony_ci			writereg(dev, PP_BusCTL, 0);	/* disable interrupts. */
85862306a36Sopenharmony_ci			pr_err("can't get an interrupt\n");
85962306a36Sopenharmony_ci			ret = -EAGAIN;
86062306a36Sopenharmony_ci			goto bad_out;
86162306a36Sopenharmony_ci		}
86262306a36Sopenharmony_ci	} else {
86362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_ISA)
86462306a36Sopenharmony_ci		if (((1 << dev->irq) & lp->irq_map) == 0) {
86562306a36Sopenharmony_ci			pr_err("%s: IRQ %d is not in our map of allowable IRQs, which is %x\n",
86662306a36Sopenharmony_ci			       dev->name, dev->irq, lp->irq_map);
86762306a36Sopenharmony_ci			ret = -EAGAIN;
86862306a36Sopenharmony_ci			goto bad_out;
86962306a36Sopenharmony_ci		}
87062306a36Sopenharmony_ci#endif
87162306a36Sopenharmony_ci/* FIXME: Cirrus' release had this: */
87262306a36Sopenharmony_ci		writereg(dev, PP_BusCTL, readreg(dev, PP_BusCTL)|ENABLE_IRQ);
87362306a36Sopenharmony_ci/* And 2.3.47 had this: */
87462306a36Sopenharmony_ci#if 0
87562306a36Sopenharmony_ci		writereg(dev, PP_BusCTL, ENABLE_IRQ | MEMORY_ON);
87662306a36Sopenharmony_ci#endif
87762306a36Sopenharmony_ci		write_irq(dev, lp->chip_type, dev->irq);
87862306a36Sopenharmony_ci		ret = request_irq(dev->irq, net_interrupt, 0, dev->name, dev);
87962306a36Sopenharmony_ci		if (ret) {
88062306a36Sopenharmony_ci			pr_err("request_irq(%d) failed\n", dev->irq);
88162306a36Sopenharmony_ci			goto bad_out;
88262306a36Sopenharmony_ci		}
88362306a36Sopenharmony_ci	}
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci#if ALLOW_DMA
88662306a36Sopenharmony_ci	if (lp->use_dma && (lp->isa_config & ANY_ISA_DMA)) {
88762306a36Sopenharmony_ci		unsigned long flags;
88862306a36Sopenharmony_ci		lp->dma_buff = (unsigned char *)__get_dma_pages(GFP_KERNEL,
88962306a36Sopenharmony_ci								get_order(lp->dmasize * 1024));
89062306a36Sopenharmony_ci		if (!lp->dma_buff) {
89162306a36Sopenharmony_ci			pr_err("%s: cannot get %dK memory for DMA\n",
89262306a36Sopenharmony_ci			       dev->name, lp->dmasize);
89362306a36Sopenharmony_ci			goto release_irq;
89462306a36Sopenharmony_ci		}
89562306a36Sopenharmony_ci		cs89_dbg(1, debug, "%s: dma %lx %lx\n",
89662306a36Sopenharmony_ci			 dev->name,
89762306a36Sopenharmony_ci			 (unsigned long)lp->dma_buff,
89862306a36Sopenharmony_ci			 (unsigned long)isa_virt_to_bus(lp->dma_buff));
89962306a36Sopenharmony_ci		if ((unsigned long)lp->dma_buff >= MAX_DMA_ADDRESS ||
90062306a36Sopenharmony_ci		    !dma_page_eq(lp->dma_buff,
90162306a36Sopenharmony_ci				 lp->dma_buff + lp->dmasize * 1024 - 1)) {
90262306a36Sopenharmony_ci			pr_err("%s: not usable as DMA buffer\n", dev->name);
90362306a36Sopenharmony_ci			goto release_irq;
90462306a36Sopenharmony_ci		}
90562306a36Sopenharmony_ci		memset(lp->dma_buff, 0, lp->dmasize * 1024);	/* Why? */
90662306a36Sopenharmony_ci		if (request_dma(dev->dma, dev->name)) {
90762306a36Sopenharmony_ci			pr_err("%s: cannot get dma channel %d\n",
90862306a36Sopenharmony_ci			       dev->name, dev->dma);
90962306a36Sopenharmony_ci			goto release_irq;
91062306a36Sopenharmony_ci		}
91162306a36Sopenharmony_ci		write_dma(dev, lp->chip_type, dev->dma);
91262306a36Sopenharmony_ci		lp->rx_dma_ptr = lp->dma_buff;
91362306a36Sopenharmony_ci		lp->end_dma_buff = lp->dma_buff + lp->dmasize * 1024;
91462306a36Sopenharmony_ci		spin_lock_irqsave(&lp->lock, flags);
91562306a36Sopenharmony_ci		disable_dma(dev->dma);
91662306a36Sopenharmony_ci		clear_dma_ff(dev->dma);
91762306a36Sopenharmony_ci		set_dma_mode(dev->dma, DMA_RX_MODE); /* auto_init as well */
91862306a36Sopenharmony_ci		set_dma_addr(dev->dma, isa_virt_to_bus(lp->dma_buff));
91962306a36Sopenharmony_ci		set_dma_count(dev->dma, lp->dmasize * 1024);
92062306a36Sopenharmony_ci		enable_dma(dev->dma);
92162306a36Sopenharmony_ci		spin_unlock_irqrestore(&lp->lock, flags);
92262306a36Sopenharmony_ci	}
92362306a36Sopenharmony_ci#endif	/* ALLOW_DMA */
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	/* set the Ethernet address */
92662306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN / 2; i++)
92762306a36Sopenharmony_ci		writereg(dev, PP_IA + i * 2,
92862306a36Sopenharmony_ci			 (dev->dev_addr[i * 2] |
92962306a36Sopenharmony_ci			  (dev->dev_addr[i * 2 + 1] << 8)));
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	/* while we're testing the interface, leave interrupts disabled */
93262306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, MEMORY_ON);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	/* Set the LineCTL quintuplet based on adapter configuration read from EEPROM */
93562306a36Sopenharmony_ci	if ((lp->adapter_cnf & A_CNF_EXTND_10B_2) &&
93662306a36Sopenharmony_ci	    (lp->adapter_cnf & A_CNF_LOW_RX_SQUELCH))
93762306a36Sopenharmony_ci		lp->linectl = LOW_RX_SQUELCH;
93862306a36Sopenharmony_ci	else
93962306a36Sopenharmony_ci		lp->linectl = 0;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	/* check to make sure that they have the "right" hardware available */
94262306a36Sopenharmony_ci	switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
94362306a36Sopenharmony_ci	case A_CNF_MEDIA_10B_T:
94462306a36Sopenharmony_ci		result = lp->adapter_cnf & A_CNF_10B_T;
94562306a36Sopenharmony_ci		break;
94662306a36Sopenharmony_ci	case A_CNF_MEDIA_AUI:
94762306a36Sopenharmony_ci		result = lp->adapter_cnf & A_CNF_AUI;
94862306a36Sopenharmony_ci		break;
94962306a36Sopenharmony_ci	case A_CNF_MEDIA_10B_2:
95062306a36Sopenharmony_ci		result = lp->adapter_cnf & A_CNF_10B_2;
95162306a36Sopenharmony_ci		break;
95262306a36Sopenharmony_ci	default:
95362306a36Sopenharmony_ci		result = lp->adapter_cnf & (A_CNF_10B_T |
95462306a36Sopenharmony_ci					    A_CNF_AUI |
95562306a36Sopenharmony_ci					    A_CNF_10B_2);
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	if (!result) {
95862306a36Sopenharmony_ci		pr_err("%s: EEPROM is configured for unavailable media\n",
95962306a36Sopenharmony_ci		       dev->name);
96062306a36Sopenharmony_cirelease_dma:
96162306a36Sopenharmony_ci#if ALLOW_DMA
96262306a36Sopenharmony_ci		free_dma(dev->dma);
96362306a36Sopenharmony_cirelease_irq:
96462306a36Sopenharmony_ci		release_dma_buff(lp);
96562306a36Sopenharmony_ci#endif
96662306a36Sopenharmony_ci		writereg(dev, PP_LineCTL,
96762306a36Sopenharmony_ci			 readreg(dev, PP_LineCTL) & ~(SERIAL_TX_ON | SERIAL_RX_ON));
96862306a36Sopenharmony_ci		free_irq(dev->irq, dev);
96962306a36Sopenharmony_ci		ret = -EAGAIN;
97062306a36Sopenharmony_ci		goto bad_out;
97162306a36Sopenharmony_ci	}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci	/* set the hardware to the configured choice */
97462306a36Sopenharmony_ci	switch (lp->adapter_cnf & A_CNF_MEDIA_TYPE) {
97562306a36Sopenharmony_ci	case A_CNF_MEDIA_10B_T:
97662306a36Sopenharmony_ci		result = detect_tp(dev);
97762306a36Sopenharmony_ci		if (result == DETECTED_NONE) {
97862306a36Sopenharmony_ci			pr_warn("%s: 10Base-T (RJ-45) has no cable\n",
97962306a36Sopenharmony_ci				dev->name);
98062306a36Sopenharmony_ci			if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
98162306a36Sopenharmony_ci				result = DETECTED_RJ45H; /* Yes! I don't care if I see a link pulse */
98262306a36Sopenharmony_ci		}
98362306a36Sopenharmony_ci		break;
98462306a36Sopenharmony_ci	case A_CNF_MEDIA_AUI:
98562306a36Sopenharmony_ci		result = detect_aui(dev);
98662306a36Sopenharmony_ci		if (result == DETECTED_NONE) {
98762306a36Sopenharmony_ci			pr_warn("%s: 10Base-5 (AUI) has no cable\n", dev->name);
98862306a36Sopenharmony_ci			if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
98962306a36Sopenharmony_ci				result = DETECTED_AUI; /* Yes! I don't care if I see a carrier */
99062306a36Sopenharmony_ci		}
99162306a36Sopenharmony_ci		break;
99262306a36Sopenharmony_ci	case A_CNF_MEDIA_10B_2:
99362306a36Sopenharmony_ci		result = detect_bnc(dev);
99462306a36Sopenharmony_ci		if (result == DETECTED_NONE) {
99562306a36Sopenharmony_ci			pr_warn("%s: 10Base-2 (BNC) has no cable\n", dev->name);
99662306a36Sopenharmony_ci			if (lp->auto_neg_cnf & IMM_BIT) /* check "ignore missing media" bit */
99762306a36Sopenharmony_ci				result = DETECTED_BNC; /* Yes! I don't care if I can xmit a packet */
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci		break;
100062306a36Sopenharmony_ci	case A_CNF_MEDIA_AUTO:
100162306a36Sopenharmony_ci		writereg(dev, PP_LineCTL, lp->linectl | AUTO_AUI_10BASET);
100262306a36Sopenharmony_ci		if (lp->adapter_cnf & A_CNF_10B_T) {
100362306a36Sopenharmony_ci			result = detect_tp(dev);
100462306a36Sopenharmony_ci			if (result != DETECTED_NONE)
100562306a36Sopenharmony_ci				break;
100662306a36Sopenharmony_ci		}
100762306a36Sopenharmony_ci		if (lp->adapter_cnf & A_CNF_AUI) {
100862306a36Sopenharmony_ci			result = detect_aui(dev);
100962306a36Sopenharmony_ci			if (result != DETECTED_NONE)
101062306a36Sopenharmony_ci				break;
101162306a36Sopenharmony_ci		}
101262306a36Sopenharmony_ci		if (lp->adapter_cnf & A_CNF_10B_2) {
101362306a36Sopenharmony_ci			result = detect_bnc(dev);
101462306a36Sopenharmony_ci			if (result != DETECTED_NONE)
101562306a36Sopenharmony_ci				break;
101662306a36Sopenharmony_ci		}
101762306a36Sopenharmony_ci		pr_err("%s: no media detected\n", dev->name);
101862306a36Sopenharmony_ci		goto release_dma;
101962306a36Sopenharmony_ci	}
102062306a36Sopenharmony_ci	switch (result) {
102162306a36Sopenharmony_ci	case DETECTED_NONE:
102262306a36Sopenharmony_ci		pr_err("%s: no network cable attached to configured media\n",
102362306a36Sopenharmony_ci		       dev->name);
102462306a36Sopenharmony_ci		goto release_dma;
102562306a36Sopenharmony_ci	case DETECTED_RJ45H:
102662306a36Sopenharmony_ci		pr_info("%s: using half-duplex 10Base-T (RJ-45)\n", dev->name);
102762306a36Sopenharmony_ci		break;
102862306a36Sopenharmony_ci	case DETECTED_RJ45F:
102962306a36Sopenharmony_ci		pr_info("%s: using full-duplex 10Base-T (RJ-45)\n", dev->name);
103062306a36Sopenharmony_ci		break;
103162306a36Sopenharmony_ci	case DETECTED_AUI:
103262306a36Sopenharmony_ci		pr_info("%s: using 10Base-5 (AUI)\n", dev->name);
103362306a36Sopenharmony_ci		break;
103462306a36Sopenharmony_ci	case DETECTED_BNC:
103562306a36Sopenharmony_ci		pr_info("%s: using 10Base-2 (BNC)\n", dev->name);
103662306a36Sopenharmony_ci		break;
103762306a36Sopenharmony_ci	}
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	/* Turn on both receive and transmit operations */
104062306a36Sopenharmony_ci	writereg(dev, PP_LineCTL,
104162306a36Sopenharmony_ci		 readreg(dev, PP_LineCTL) | SERIAL_RX_ON | SERIAL_TX_ON);
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci	/* Receive only error free packets addressed to this card */
104462306a36Sopenharmony_ci	lp->rx_mode = 0;
104562306a36Sopenharmony_ci	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT);
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	lp->curr_rx_cfg = RX_OK_ENBL | RX_CRC_ERROR_ENBL;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	if (lp->isa_config & STREAM_TRANSFER)
105062306a36Sopenharmony_ci		lp->curr_rx_cfg |= RX_STREAM_ENBL;
105162306a36Sopenharmony_ci#if ALLOW_DMA
105262306a36Sopenharmony_ci	set_dma_cfg(dev);
105362306a36Sopenharmony_ci#endif
105462306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, lp->curr_rx_cfg);
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	writereg(dev, PP_TxCFG, (TX_LOST_CRS_ENBL |
105762306a36Sopenharmony_ci				 TX_SQE_ERROR_ENBL |
105862306a36Sopenharmony_ci				 TX_OK_ENBL |
105962306a36Sopenharmony_ci				 TX_LATE_COL_ENBL |
106062306a36Sopenharmony_ci				 TX_JBR_ENBL |
106162306a36Sopenharmony_ci				 TX_ANY_COL_ENBL |
106262306a36Sopenharmony_ci				 TX_16_COL_ENBL));
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	writereg(dev, PP_BufCFG, (READY_FOR_TX_ENBL |
106562306a36Sopenharmony_ci				  RX_MISS_COUNT_OVRFLOW_ENBL |
106662306a36Sopenharmony_ci#if ALLOW_DMA
106762306a36Sopenharmony_ci				  dma_bufcfg(dev) |
106862306a36Sopenharmony_ci#endif
106962306a36Sopenharmony_ci				  TX_COL_COUNT_OVRFLOW_ENBL |
107062306a36Sopenharmony_ci				  TX_UNDERRUN_ENBL));
107162306a36Sopenharmony_ci
107262306a36Sopenharmony_ci	/* now that we've got our act together, enable everything */
107362306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, (ENABLE_IRQ
107462306a36Sopenharmony_ci				  | (dev->mem_start ? MEMORY_ON : 0) /* turn memory on */
107562306a36Sopenharmony_ci#if ALLOW_DMA
107662306a36Sopenharmony_ci				  | dma_busctl(dev)
107762306a36Sopenharmony_ci#endif
107862306a36Sopenharmony_ci			 ));
107962306a36Sopenharmony_ci	netif_start_queue(dev);
108062306a36Sopenharmony_ci	cs89_dbg(1, debug, "net_open() succeeded\n");
108162306a36Sopenharmony_ci	return 0;
108262306a36Sopenharmony_cibad_out:
108362306a36Sopenharmony_ci	return ret;
108462306a36Sopenharmony_ci}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci/* The inverse routine to net_open(). */
108762306a36Sopenharmony_cistatic int
108862306a36Sopenharmony_cinet_close(struct net_device *dev)
108962306a36Sopenharmony_ci{
109062306a36Sopenharmony_ci#if ALLOW_DMA
109162306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
109262306a36Sopenharmony_ci#endif
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	netif_stop_queue(dev);
109562306a36Sopenharmony_ci
109662306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, 0);
109762306a36Sopenharmony_ci	writereg(dev, PP_TxCFG, 0);
109862306a36Sopenharmony_ci	writereg(dev, PP_BufCFG, 0);
109962306a36Sopenharmony_ci	writereg(dev, PP_BusCTL, 0);
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	free_irq(dev->irq, dev);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci#if ALLOW_DMA
110462306a36Sopenharmony_ci	if (lp->use_dma && lp->dma) {
110562306a36Sopenharmony_ci		free_dma(dev->dma);
110662306a36Sopenharmony_ci		release_dma_buff(lp);
110762306a36Sopenharmony_ci	}
110862306a36Sopenharmony_ci#endif
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* Update the statistics here. */
111162306a36Sopenharmony_ci	return 0;
111262306a36Sopenharmony_ci}
111362306a36Sopenharmony_ci
111462306a36Sopenharmony_ci/* Get the current statistics.
111562306a36Sopenharmony_ci * This may be called with the card open or closed.
111662306a36Sopenharmony_ci */
111762306a36Sopenharmony_cistatic struct net_device_stats *
111862306a36Sopenharmony_cinet_get_stats(struct net_device *dev)
111962306a36Sopenharmony_ci{
112062306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
112162306a36Sopenharmony_ci	unsigned long flags;
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
112462306a36Sopenharmony_ci	/* Update the statistics from the device registers. */
112562306a36Sopenharmony_ci	dev->stats.rx_missed_errors += (readreg(dev, PP_RxMiss) >> 6);
112662306a36Sopenharmony_ci	dev->stats.collisions += (readreg(dev, PP_TxCol) >> 6);
112762306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci	return &dev->stats;
113062306a36Sopenharmony_ci}
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_cistatic void net_timeout(struct net_device *dev, unsigned int txqueue)
113362306a36Sopenharmony_ci{
113462306a36Sopenharmony_ci	/* If we get here, some higher level has decided we are broken.
113562306a36Sopenharmony_ci	   There should really be a "kick me" function call instead. */
113662306a36Sopenharmony_ci	cs89_dbg(0, err, "%s: transmit timed out, %s?\n",
113762306a36Sopenharmony_ci		 dev->name,
113862306a36Sopenharmony_ci		 tx_done(dev) ? "IRQ conflict" : "network cable problem");
113962306a36Sopenharmony_ci	/* Try to restart the adaptor. */
114062306a36Sopenharmony_ci	netif_wake_queue(dev);
114162306a36Sopenharmony_ci}
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cistatic netdev_tx_t net_send_packet(struct sk_buff *skb, struct net_device *dev)
114462306a36Sopenharmony_ci{
114562306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
114662306a36Sopenharmony_ci	unsigned long flags;
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci	cs89_dbg(3, debug, "%s: sent %d byte packet of type %x\n",
114962306a36Sopenharmony_ci		 dev->name, skb->len,
115062306a36Sopenharmony_ci		 ((skb->data[ETH_ALEN + ETH_ALEN] << 8) |
115162306a36Sopenharmony_ci		  skb->data[ETH_ALEN + ETH_ALEN + 1]));
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	/* keep the upload from being interrupted, since we
115462306a36Sopenharmony_ci	 * ask the chip to start transmitting before the
115562306a36Sopenharmony_ci	 * whole packet has been completely uploaded.
115662306a36Sopenharmony_ci	 */
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
115962306a36Sopenharmony_ci	netif_stop_queue(dev);
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* initiate a transmit sequence */
116262306a36Sopenharmony_ci	iowrite16(lp->send_cmd, lp->virt_addr + TX_CMD_PORT);
116362306a36Sopenharmony_ci	iowrite16(skb->len, lp->virt_addr + TX_LEN_PORT);
116462306a36Sopenharmony_ci
116562306a36Sopenharmony_ci	/* Test to see if the chip has allocated memory for the packet */
116662306a36Sopenharmony_ci	if ((readreg(dev, PP_BusST) & READY_FOR_TX_NOW) == 0) {
116762306a36Sopenharmony_ci		/* Gasp!  It hasn't.  But that shouldn't happen since
116862306a36Sopenharmony_ci		 * we're waiting for TxOk, so return 1 and requeue this packet.
116962306a36Sopenharmony_ci		 */
117062306a36Sopenharmony_ci
117162306a36Sopenharmony_ci		spin_unlock_irqrestore(&lp->lock, flags);
117262306a36Sopenharmony_ci		cs89_dbg(0, err, "Tx buffer not free!\n");
117362306a36Sopenharmony_ci		return NETDEV_TX_BUSY;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci	/* Write the contents of the packet */
117662306a36Sopenharmony_ci	writewords(lp, TX_FRAME_PORT, skb->data, (skb->len + 1) >> 1);
117762306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
117862306a36Sopenharmony_ci	dev->stats.tx_bytes += skb->len;
117962306a36Sopenharmony_ci	dev_consume_skb_any(skb);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	/* We DO NOT call netif_wake_queue() here.
118262306a36Sopenharmony_ci	 * We also DO NOT call netif_start_queue().
118362306a36Sopenharmony_ci	 *
118462306a36Sopenharmony_ci	 * Either of these would cause another bottom half run through
118562306a36Sopenharmony_ci	 * net_send_packet() before this packet has fully gone out.
118662306a36Sopenharmony_ci	 * That causes us to hit the "Gasp!" above and the send is rescheduled.
118762306a36Sopenharmony_ci	 * it runs like a dog.  We just return and wait for the Tx completion
118862306a36Sopenharmony_ci	 * interrupt handler to restart the netdevice layer
118962306a36Sopenharmony_ci	 */
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_ci	return NETDEV_TX_OK;
119262306a36Sopenharmony_ci}
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_cistatic void set_multicast_list(struct net_device *dev)
119562306a36Sopenharmony_ci{
119662306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
119762306a36Sopenharmony_ci	unsigned long flags;
119862306a36Sopenharmony_ci	u16 cfg;
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	spin_lock_irqsave(&lp->lock, flags);
120162306a36Sopenharmony_ci	if (dev->flags & IFF_PROMISC)
120262306a36Sopenharmony_ci		lp->rx_mode = RX_ALL_ACCEPT;
120362306a36Sopenharmony_ci	else if ((dev->flags & IFF_ALLMULTI) || !netdev_mc_empty(dev))
120462306a36Sopenharmony_ci		/* The multicast-accept list is initialized to accept-all,
120562306a36Sopenharmony_ci		 * and we rely on higher-level filtering for now.
120662306a36Sopenharmony_ci		 */
120762306a36Sopenharmony_ci		lp->rx_mode = RX_MULTCAST_ACCEPT;
120862306a36Sopenharmony_ci	else
120962306a36Sopenharmony_ci		lp->rx_mode = 0;
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	writereg(dev, PP_RxCTL, DEF_RX_ACCEPT | lp->rx_mode);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	/* in promiscuous mode, we accept errored packets,
121462306a36Sopenharmony_ci	 * so we have to enable interrupts on them also
121562306a36Sopenharmony_ci	 */
121662306a36Sopenharmony_ci	cfg = lp->curr_rx_cfg;
121762306a36Sopenharmony_ci	if (lp->rx_mode == RX_ALL_ACCEPT)
121862306a36Sopenharmony_ci		cfg |= RX_CRC_ERROR_ENBL | RX_RUNT_ENBL | RX_EXTRA_DATA_ENBL;
121962306a36Sopenharmony_ci	writereg(dev, PP_RxCFG, cfg);
122062306a36Sopenharmony_ci	spin_unlock_irqrestore(&lp->lock, flags);
122162306a36Sopenharmony_ci}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_cistatic int set_mac_address(struct net_device *dev, void *p)
122462306a36Sopenharmony_ci{
122562306a36Sopenharmony_ci	int i;
122662306a36Sopenharmony_ci	struct sockaddr *addr = p;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (netif_running(dev))
122962306a36Sopenharmony_ci		return -EBUSY;
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	eth_hw_addr_set(dev, addr->sa_data);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ci	cs89_dbg(0, debug, "%s: Setting MAC address to %pM\n",
123462306a36Sopenharmony_ci		 dev->name, dev->dev_addr);
123562306a36Sopenharmony_ci
123662306a36Sopenharmony_ci	/* set the Ethernet address */
123762306a36Sopenharmony_ci	for (i = 0; i < ETH_ALEN / 2; i++)
123862306a36Sopenharmony_ci		writereg(dev, PP_IA + i * 2,
123962306a36Sopenharmony_ci			 (dev->dev_addr[i * 2] |
124062306a36Sopenharmony_ci			  (dev->dev_addr[i * 2 + 1] << 8)));
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_ci	return 0;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
124662306a36Sopenharmony_ci/*
124762306a36Sopenharmony_ci * Polling receive - used by netconsole and other diagnostic tools
124862306a36Sopenharmony_ci * to allow network i/o with interrupts disabled.
124962306a36Sopenharmony_ci */
125062306a36Sopenharmony_cistatic void net_poll_controller(struct net_device *dev)
125162306a36Sopenharmony_ci{
125262306a36Sopenharmony_ci	disable_irq(dev->irq);
125362306a36Sopenharmony_ci	net_interrupt(dev->irq, dev);
125462306a36Sopenharmony_ci	enable_irq(dev->irq);
125562306a36Sopenharmony_ci}
125662306a36Sopenharmony_ci#endif
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_cistatic const struct net_device_ops net_ops = {
125962306a36Sopenharmony_ci	.ndo_open		= net_open,
126062306a36Sopenharmony_ci	.ndo_stop		= net_close,
126162306a36Sopenharmony_ci	.ndo_tx_timeout		= net_timeout,
126262306a36Sopenharmony_ci	.ndo_start_xmit		= net_send_packet,
126362306a36Sopenharmony_ci	.ndo_get_stats		= net_get_stats,
126462306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_multicast_list,
126562306a36Sopenharmony_ci	.ndo_set_mac_address	= set_mac_address,
126662306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER
126762306a36Sopenharmony_ci	.ndo_poll_controller	= net_poll_controller,
126862306a36Sopenharmony_ci#endif
126962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
127062306a36Sopenharmony_ci};
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_cistatic void __init reset_chip(struct net_device *dev)
127362306a36Sopenharmony_ci{
127462306a36Sopenharmony_ci#if !defined(CONFIG_MACH_MX31ADS)
127562306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
127662306a36Sopenharmony_ci	unsigned long reset_start_time;
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_ci	writereg(dev, PP_SelfCTL, readreg(dev, PP_SelfCTL) | POWER_ON_RESET);
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	/* wait 30 ms */
128162306a36Sopenharmony_ci	msleep(30);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	if (lp->chip_type != CS8900) {
128462306a36Sopenharmony_ci		/* Hardware problem requires PNP registers to be reconfigured after a reset */
128562306a36Sopenharmony_ci		iowrite16(PP_CS8920_ISAINT, lp->virt_addr + ADD_PORT);
128662306a36Sopenharmony_ci		iowrite8(dev->irq, lp->virt_addr + DATA_PORT);
128762306a36Sopenharmony_ci		iowrite8(0, lp->virt_addr + DATA_PORT + 1);
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci		iowrite16(PP_CS8920_ISAMemB, lp->virt_addr + ADD_PORT);
129062306a36Sopenharmony_ci		iowrite8((dev->mem_start >> 16) & 0xff,
129162306a36Sopenharmony_ci			 lp->virt_addr + DATA_PORT);
129262306a36Sopenharmony_ci		iowrite8((dev->mem_start >> 8) & 0xff,
129362306a36Sopenharmony_ci			 lp->virt_addr + DATA_PORT + 1);
129462306a36Sopenharmony_ci	}
129562306a36Sopenharmony_ci
129662306a36Sopenharmony_ci	/* Wait until the chip is reset */
129762306a36Sopenharmony_ci	reset_start_time = jiffies;
129862306a36Sopenharmony_ci	while ((readreg(dev, PP_SelfST) & INIT_DONE) == 0 &&
129962306a36Sopenharmony_ci	       time_before(jiffies, reset_start_time + 2))
130062306a36Sopenharmony_ci		;
130162306a36Sopenharmony_ci#endif /* !CONFIG_MACH_MX31ADS */
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci/* This is the real probe routine.
130562306a36Sopenharmony_ci * Linux has a history of friendly device probes on the ISA bus.
130662306a36Sopenharmony_ci * A good device probes avoids doing writes, and
130762306a36Sopenharmony_ci * verifies that the correct device exists and functions.
130862306a36Sopenharmony_ci * Return 0 on success.
130962306a36Sopenharmony_ci */
131062306a36Sopenharmony_cistatic int __init
131162306a36Sopenharmony_cics89x0_probe1(struct net_device *dev, void __iomem *ioaddr, int modular)
131262306a36Sopenharmony_ci{
131362306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
131462306a36Sopenharmony_ci	int i;
131562306a36Sopenharmony_ci	int tmp;
131662306a36Sopenharmony_ci	unsigned rev_type = 0;
131762306a36Sopenharmony_ci	int eeprom_buff[CHKSUM_LEN];
131862306a36Sopenharmony_ci	u8 addr[ETH_ALEN];
131962306a36Sopenharmony_ci	int retval;
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	/* Initialize the device structure. */
132262306a36Sopenharmony_ci	if (!modular) {
132362306a36Sopenharmony_ci		memset(lp, 0, sizeof(*lp));
132462306a36Sopenharmony_ci		spin_lock_init(&lp->lock);
132562306a36Sopenharmony_ci#ifndef MODULE
132662306a36Sopenharmony_ci#if ALLOW_DMA
132762306a36Sopenharmony_ci		if (g_cs89x0_dma) {
132862306a36Sopenharmony_ci			lp->use_dma = 1;
132962306a36Sopenharmony_ci			lp->dma = g_cs89x0_dma;
133062306a36Sopenharmony_ci			lp->dmasize = 16;	/* Could make this an option... */
133162306a36Sopenharmony_ci		}
133262306a36Sopenharmony_ci#endif
133362306a36Sopenharmony_ci		lp->force = g_cs89x0_media__force;
133462306a36Sopenharmony_ci#endif
133562306a36Sopenharmony_ci	}
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	pr_debug("PP_addr at %p[%x]: 0x%x\n",
133862306a36Sopenharmony_ci		 ioaddr, ADD_PORT, ioread16(ioaddr + ADD_PORT));
133962306a36Sopenharmony_ci	iowrite16(PP_ChipID, ioaddr + ADD_PORT);
134062306a36Sopenharmony_ci
134162306a36Sopenharmony_ci	tmp = ioread16(ioaddr + DATA_PORT);
134262306a36Sopenharmony_ci	if (tmp != CHIP_EISA_ID_SIG) {
134362306a36Sopenharmony_ci		pr_debug("%s: incorrect signature at %p[%x]: 0x%x!="
134462306a36Sopenharmony_ci			 CHIP_EISA_ID_SIG_STR "\n",
134562306a36Sopenharmony_ci			 dev->name, ioaddr, DATA_PORT, tmp);
134662306a36Sopenharmony_ci		retval = -ENODEV;
134762306a36Sopenharmony_ci		goto out1;
134862306a36Sopenharmony_ci	}
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	lp->virt_addr = ioaddr;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	/* get the chip type */
135362306a36Sopenharmony_ci	rev_type = readreg(dev, PRODUCT_ID_ADD);
135462306a36Sopenharmony_ci	lp->chip_type = rev_type & ~REVISON_BITS;
135562306a36Sopenharmony_ci	lp->chip_revision = ((rev_type & REVISON_BITS) >> 8) + 'A';
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	/* Check the chip type and revision in order to set the correct
135862306a36Sopenharmony_ci	 * send command.  CS8920 revision C and CS8900 revision F can use
135962306a36Sopenharmony_ci	 * the faster send.
136062306a36Sopenharmony_ci	 */
136162306a36Sopenharmony_ci	lp->send_cmd = TX_AFTER_381;
136262306a36Sopenharmony_ci	if (lp->chip_type == CS8900 && lp->chip_revision >= 'F')
136362306a36Sopenharmony_ci		lp->send_cmd = TX_NOW;
136462306a36Sopenharmony_ci	if (lp->chip_type != CS8900 && lp->chip_revision >= 'C')
136562306a36Sopenharmony_ci		lp->send_cmd = TX_NOW;
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	pr_info_once("%s\n", version);
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	pr_info("%s: cs89%c0%s rev %c found at %p ",
137062306a36Sopenharmony_ci		dev->name,
137162306a36Sopenharmony_ci		lp->chip_type == CS8900  ? '0' : '2',
137262306a36Sopenharmony_ci		lp->chip_type == CS8920M ? "M" : "",
137362306a36Sopenharmony_ci		lp->chip_revision,
137462306a36Sopenharmony_ci		lp->virt_addr);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	reset_chip(dev);
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci	/* Here we read the current configuration of the chip.
137962306a36Sopenharmony_ci	 * If there is no Extended EEPROM then the idea is to not disturb
138062306a36Sopenharmony_ci	 * the chip configuration, it should have been correctly setup by
138162306a36Sopenharmony_ci	 * automatic EEPROM read on reset. So, if the chip says it read
138262306a36Sopenharmony_ci	 * the EEPROM the driver will always do *something* instead of
138362306a36Sopenharmony_ci	 * complain that adapter_cnf is 0.
138462306a36Sopenharmony_ci	 */
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) ==
138762306a36Sopenharmony_ci	    (EEPROM_OK | EEPROM_PRESENT)) {
138862306a36Sopenharmony_ci		/* Load the MAC. */
138962306a36Sopenharmony_ci		for (i = 0; i < ETH_ALEN / 2; i++) {
139062306a36Sopenharmony_ci			unsigned int Addr;
139162306a36Sopenharmony_ci			Addr = readreg(dev, PP_IA + i * 2);
139262306a36Sopenharmony_ci			addr[i * 2] = Addr & 0xFF;
139362306a36Sopenharmony_ci			addr[i * 2 + 1] = Addr >> 8;
139462306a36Sopenharmony_ci		}
139562306a36Sopenharmony_ci		eth_hw_addr_set(dev, addr);
139662306a36Sopenharmony_ci
139762306a36Sopenharmony_ci		/* Load the Adapter Configuration.
139862306a36Sopenharmony_ci		 * Note:  Barring any more specific information from some
139962306a36Sopenharmony_ci		 * other source (ie EEPROM+Schematics), we would not know
140062306a36Sopenharmony_ci		 * how to operate a 10Base2 interface on the AUI port.
140162306a36Sopenharmony_ci		 * However, since we  do read the status of HCB1 and use
140262306a36Sopenharmony_ci		 * settings that always result in calls to control_dc_dc(dev,0)
140362306a36Sopenharmony_ci		 * a BNC interface should work if the enable pin
140462306a36Sopenharmony_ci		 * (dc/dc converter) is on HCB1.
140562306a36Sopenharmony_ci		 * It will be called AUI however.
140662306a36Sopenharmony_ci		 */
140762306a36Sopenharmony_ci
140862306a36Sopenharmony_ci		lp->adapter_cnf = 0;
140962306a36Sopenharmony_ci		i = readreg(dev, PP_LineCTL);
141062306a36Sopenharmony_ci		/* Preserve the setting of the HCB1 pin. */
141162306a36Sopenharmony_ci		if ((i & (HCB1 | HCB1_ENBL)) == (HCB1 | HCB1_ENBL))
141262306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_DC_DC_POLARITY;
141362306a36Sopenharmony_ci		/* Save the sqelch bit */
141462306a36Sopenharmony_ci		if ((i & LOW_RX_SQUELCH) == LOW_RX_SQUELCH)
141562306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_EXTND_10B_2 | A_CNF_LOW_RX_SQUELCH;
141662306a36Sopenharmony_ci		/* Check if the card is in 10Base-t only mode */
141762306a36Sopenharmony_ci		if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == 0)
141862306a36Sopenharmony_ci			lp->adapter_cnf |=  A_CNF_10B_T | A_CNF_MEDIA_10B_T;
141962306a36Sopenharmony_ci		/* Check if the card is in AUI only mode */
142062306a36Sopenharmony_ci		if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUI_ONLY)
142162306a36Sopenharmony_ci			lp->adapter_cnf |=  A_CNF_AUI | A_CNF_MEDIA_AUI;
142262306a36Sopenharmony_ci		/* Check if the card is in Auto mode. */
142362306a36Sopenharmony_ci		if ((i & (AUI_ONLY | AUTO_AUI_10BASET)) == AUTO_AUI_10BASET)
142462306a36Sopenharmony_ci			lp->adapter_cnf |=  A_CNF_AUI | A_CNF_10B_T |
142562306a36Sopenharmony_ci				A_CNF_MEDIA_AUI | A_CNF_MEDIA_10B_T | A_CNF_MEDIA_AUTO;
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci		cs89_dbg(1, info, "%s: PP_LineCTL=0x%x, adapter_cnf=0x%x\n",
142862306a36Sopenharmony_ci			 dev->name, i, lp->adapter_cnf);
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci		/* IRQ. Other chips already probe, see below. */
143162306a36Sopenharmony_ci		if (lp->chip_type == CS8900)
143262306a36Sopenharmony_ci			lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK;
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		pr_cont("[Cirrus EEPROM] ");
143562306a36Sopenharmony_ci	}
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	pr_cont("\n");
143862306a36Sopenharmony_ci
143962306a36Sopenharmony_ci	/* First check to see if an EEPROM is attached. */
144062306a36Sopenharmony_ci
144162306a36Sopenharmony_ci	if ((readreg(dev, PP_SelfST) & EEPROM_PRESENT) == 0)
144262306a36Sopenharmony_ci		pr_warn("No EEPROM, relying on command line....\n");
144362306a36Sopenharmony_ci	else if (get_eeprom_data(dev, START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
144462306a36Sopenharmony_ci		pr_warn("EEPROM read failed, relying on command line\n");
144562306a36Sopenharmony_ci	} else if (get_eeprom_cksum(START_EEPROM_DATA, CHKSUM_LEN, eeprom_buff) < 0) {
144662306a36Sopenharmony_ci		/* Check if the chip was able to read its own configuration starting
144762306a36Sopenharmony_ci		   at 0 in the EEPROM*/
144862306a36Sopenharmony_ci		if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) !=
144962306a36Sopenharmony_ci		    (EEPROM_OK | EEPROM_PRESENT))
145062306a36Sopenharmony_ci			pr_warn("Extended EEPROM checksum bad and no Cirrus EEPROM, relying on command line\n");
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	} else {
145362306a36Sopenharmony_ci		/* This reads an extended EEPROM that is not documented
145462306a36Sopenharmony_ci		 * in the CS8900 datasheet.
145562306a36Sopenharmony_ci		 */
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci		/* get transmission control word  but keep the autonegotiation bits */
145862306a36Sopenharmony_ci		if (!lp->auto_neg_cnf)
145962306a36Sopenharmony_ci			lp->auto_neg_cnf = eeprom_buff[AUTO_NEG_CNF_OFFSET / 2];
146062306a36Sopenharmony_ci		/* Store adapter configuration */
146162306a36Sopenharmony_ci		if (!lp->adapter_cnf)
146262306a36Sopenharmony_ci			lp->adapter_cnf = eeprom_buff[ADAPTER_CNF_OFFSET / 2];
146362306a36Sopenharmony_ci		/* Store ISA configuration */
146462306a36Sopenharmony_ci		lp->isa_config = eeprom_buff[ISA_CNF_OFFSET / 2];
146562306a36Sopenharmony_ci		dev->mem_start = eeprom_buff[PACKET_PAGE_OFFSET / 2] << 8;
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci		/* eeprom_buff has 32-bit ints, so we can't just memcpy it */
146862306a36Sopenharmony_ci		/* store the initial memory base address */
146962306a36Sopenharmony_ci		for (i = 0; i < ETH_ALEN / 2; i++) {
147062306a36Sopenharmony_ci			addr[i * 2] = eeprom_buff[i];
147162306a36Sopenharmony_ci			addr[i * 2 + 1] = eeprom_buff[i] >> 8;
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci		eth_hw_addr_set(dev, addr);
147462306a36Sopenharmony_ci		cs89_dbg(1, debug, "%s: new adapter_cnf: 0x%x\n",
147562306a36Sopenharmony_ci			 dev->name, lp->adapter_cnf);
147662306a36Sopenharmony_ci	}
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* allow them to force multiple transceivers.  If they force multiple, autosense */
147962306a36Sopenharmony_ci	{
148062306a36Sopenharmony_ci		int count = 0;
148162306a36Sopenharmony_ci		if (lp->force & FORCE_RJ45) {
148262306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_10B_T;
148362306a36Sopenharmony_ci			count++;
148462306a36Sopenharmony_ci		}
148562306a36Sopenharmony_ci		if (lp->force & FORCE_AUI) {
148662306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_AUI;
148762306a36Sopenharmony_ci			count++;
148862306a36Sopenharmony_ci		}
148962306a36Sopenharmony_ci		if (lp->force & FORCE_BNC) {
149062306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_10B_2;
149162306a36Sopenharmony_ci			count++;
149262306a36Sopenharmony_ci		}
149362306a36Sopenharmony_ci		if (count > 1)
149462306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_MEDIA_AUTO;
149562306a36Sopenharmony_ci		else if (lp->force & FORCE_RJ45)
149662306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_MEDIA_10B_T;
149762306a36Sopenharmony_ci		else if (lp->force & FORCE_AUI)
149862306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_MEDIA_AUI;
149962306a36Sopenharmony_ci		else if (lp->force & FORCE_BNC)
150062306a36Sopenharmony_ci			lp->adapter_cnf |= A_CNF_MEDIA_10B_2;
150162306a36Sopenharmony_ci	}
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	cs89_dbg(1, debug, "%s: after force 0x%x, adapter_cnf=0x%x\n",
150462306a36Sopenharmony_ci		 dev->name, lp->force, lp->adapter_cnf);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	/* FIXME: We don't let you set dc-dc polarity or low RX squelch from the command line: add it here */
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* FIXME: We don't let you set the IMM bit from the command line: add it to lp->auto_neg_cnf here */
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	/* FIXME: we don't set the Ethernet address on the command line.  Use
151162306a36Sopenharmony_ci	 * ifconfig IFACE hw ether AABBCCDDEEFF
151262306a36Sopenharmony_ci	 */
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	pr_info("media %s%s%s",
151562306a36Sopenharmony_ci		(lp->adapter_cnf & A_CNF_10B_T) ? "RJ-45," : "",
151662306a36Sopenharmony_ci		(lp->adapter_cnf & A_CNF_AUI) ? "AUI," : "",
151762306a36Sopenharmony_ci		(lp->adapter_cnf & A_CNF_10B_2) ? "BNC," : "");
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	lp->irq_map = 0xffff;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci	/* If this is a CS8900 then no pnp soft */
152262306a36Sopenharmony_ci	if (lp->chip_type != CS8900 &&
152362306a36Sopenharmony_ci	    /* Check if the ISA IRQ has been set  */
152462306a36Sopenharmony_ci	    (i = readreg(dev, PP_CS8920_ISAINT) & 0xff,
152562306a36Sopenharmony_ci	     (i != 0 && i < CS8920_NO_INTS))) {
152662306a36Sopenharmony_ci		if (!dev->irq)
152762306a36Sopenharmony_ci			dev->irq = i;
152862306a36Sopenharmony_ci	} else {
152962306a36Sopenharmony_ci		i = lp->isa_config & INT_NO_MASK;
153062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_ISA)
153162306a36Sopenharmony_ci		if (lp->chip_type == CS8900) {
153262306a36Sopenharmony_ci			/* Translate the IRQ using the IRQ mapping table. */
153362306a36Sopenharmony_ci			if (i >= ARRAY_SIZE(cs8900_irq_map))
153462306a36Sopenharmony_ci				pr_err("invalid ISA interrupt number %d\n", i);
153562306a36Sopenharmony_ci			else
153662306a36Sopenharmony_ci				i = cs8900_irq_map[i];
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci			lp->irq_map = CS8900_IRQ_MAP; /* fixed IRQ map for CS8900 */
153962306a36Sopenharmony_ci		} else {
154062306a36Sopenharmony_ci			int irq_map_buff[IRQ_MAP_LEN/2];
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_ci			if (get_eeprom_data(dev, IRQ_MAP_EEPROM_DATA,
154362306a36Sopenharmony_ci					    IRQ_MAP_LEN / 2,
154462306a36Sopenharmony_ci					    irq_map_buff) >= 0) {
154562306a36Sopenharmony_ci				if ((irq_map_buff[0] & 0xff) == PNP_IRQ_FRMT)
154662306a36Sopenharmony_ci					lp->irq_map = ((irq_map_buff[0] >> 8) |
154762306a36Sopenharmony_ci						       (irq_map_buff[1] << 8));
154862306a36Sopenharmony_ci			}
154962306a36Sopenharmony_ci		}
155062306a36Sopenharmony_ci#endif
155162306a36Sopenharmony_ci		if (!dev->irq)
155262306a36Sopenharmony_ci			dev->irq = i;
155362306a36Sopenharmony_ci	}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci	pr_cont(" IRQ %d", dev->irq);
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci#if ALLOW_DMA
155862306a36Sopenharmony_ci	if (lp->use_dma) {
155962306a36Sopenharmony_ci		get_dma_channel(dev);
156062306a36Sopenharmony_ci		pr_cont(", DMA %d", dev->dma);
156162306a36Sopenharmony_ci	} else
156262306a36Sopenharmony_ci#endif
156362306a36Sopenharmony_ci		pr_cont(", programmed I/O");
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	/* print the ethernet address. */
156662306a36Sopenharmony_ci	pr_cont(", MAC %pM\n", dev->dev_addr);
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci	dev->netdev_ops	= &net_ops;
156962306a36Sopenharmony_ci	dev->watchdog_timeo = HZ;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	cs89_dbg(0, info, "cs89x0_probe1() successful\n");
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci	retval = register_netdev(dev);
157462306a36Sopenharmony_ci	if (retval)
157562306a36Sopenharmony_ci		goto out2;
157662306a36Sopenharmony_ci	return 0;
157762306a36Sopenharmony_ciout2:
157862306a36Sopenharmony_ci	iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
157962306a36Sopenharmony_ciout1:
158062306a36Sopenharmony_ci	return retval;
158162306a36Sopenharmony_ci}
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_ISA)
158462306a36Sopenharmony_ci/*
158562306a36Sopenharmony_ci * This function converts the I/O port address used by the cs89x0_probe() and
158662306a36Sopenharmony_ci * init_module() functions to the I/O memory address used by the
158762306a36Sopenharmony_ci * cs89x0_probe1() function.
158862306a36Sopenharmony_ci */
158962306a36Sopenharmony_cistatic int __init
159062306a36Sopenharmony_cics89x0_ioport_probe(struct net_device *dev, unsigned long ioport, int modular)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev);
159362306a36Sopenharmony_ci	int ret;
159462306a36Sopenharmony_ci	void __iomem *io_mem;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	if (!lp)
159762306a36Sopenharmony_ci		return -ENOMEM;
159862306a36Sopenharmony_ci
159962306a36Sopenharmony_ci	dev->base_addr = ioport;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (!request_region(ioport, NETCARD_IO_EXTENT, DRV_NAME)) {
160262306a36Sopenharmony_ci		ret = -EBUSY;
160362306a36Sopenharmony_ci		goto out;
160462306a36Sopenharmony_ci	}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	io_mem = ioport_map(ioport & ~3, NETCARD_IO_EXTENT);
160762306a36Sopenharmony_ci	if (!io_mem) {
160862306a36Sopenharmony_ci		ret = -ENOMEM;
160962306a36Sopenharmony_ci		goto release;
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	/* if they give us an odd I/O address, then do ONE write to
161362306a36Sopenharmony_ci	 * the address port, to get it back to address zero, where we
161462306a36Sopenharmony_ci	 * expect to find the EISA signature word. An IO with a base of 0x3
161562306a36Sopenharmony_ci	 * will skip the test for the ADD_PORT.
161662306a36Sopenharmony_ci	 */
161762306a36Sopenharmony_ci	if (ioport & 1) {
161862306a36Sopenharmony_ci		cs89_dbg(1, info, "%s: odd ioaddr 0x%lx\n", dev->name, ioport);
161962306a36Sopenharmony_ci		if ((ioport & 2) != 2) {
162062306a36Sopenharmony_ci			if ((ioread16(io_mem + ADD_PORT) & ADD_MASK) !=
162162306a36Sopenharmony_ci			    ADD_SIG) {
162262306a36Sopenharmony_ci				pr_err("%s: bad signature 0x%x\n",
162362306a36Sopenharmony_ci				       dev->name, ioread16(io_mem + ADD_PORT));
162462306a36Sopenharmony_ci				ret = -ENODEV;
162562306a36Sopenharmony_ci				goto unmap;
162662306a36Sopenharmony_ci			}
162762306a36Sopenharmony_ci		}
162862306a36Sopenharmony_ci	}
162962306a36Sopenharmony_ci
163062306a36Sopenharmony_ci	ret = cs89x0_probe1(dev, io_mem, modular);
163162306a36Sopenharmony_ci	if (!ret)
163262306a36Sopenharmony_ci		goto out;
163362306a36Sopenharmony_ciunmap:
163462306a36Sopenharmony_ci	ioport_unmap(io_mem);
163562306a36Sopenharmony_cirelease:
163662306a36Sopenharmony_ci	release_region(ioport, NETCARD_IO_EXTENT);
163762306a36Sopenharmony_ciout:
163862306a36Sopenharmony_ci	return ret;
163962306a36Sopenharmony_ci}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci#ifndef MODULE
164262306a36Sopenharmony_ci/* Check for a network adaptor of this type, and return '0' iff one exists.
164362306a36Sopenharmony_ci * If dev->base_addr == 0, probe all likely locations.
164462306a36Sopenharmony_ci * If dev->base_addr == 1, always return failure.
164562306a36Sopenharmony_ci * If dev->base_addr == 2, allocate space for the device and return success
164662306a36Sopenharmony_ci * (detachable devices only).
164762306a36Sopenharmony_ci * Return 0 on success.
164862306a36Sopenharmony_ci */
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_cistruct net_device * __init cs89x0_probe(int unit)
165162306a36Sopenharmony_ci{
165262306a36Sopenharmony_ci	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
165362306a36Sopenharmony_ci	unsigned *port;
165462306a36Sopenharmony_ci	int err = 0;
165562306a36Sopenharmony_ci	int irq;
165662306a36Sopenharmony_ci	int io;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	if (!dev)
165962306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	sprintf(dev->name, "eth%d", unit);
166262306a36Sopenharmony_ci	netdev_boot_setup_check(dev);
166362306a36Sopenharmony_ci	io = dev->base_addr;
166462306a36Sopenharmony_ci	irq = dev->irq;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	cs89_dbg(0, info, "cs89x0_probe(0x%x)\n", io);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (io > 0x1ff)	{	/* Check a single specified location. */
166962306a36Sopenharmony_ci		err = cs89x0_ioport_probe(dev, io, 0);
167062306a36Sopenharmony_ci	} else if (io != 0) {	/* Don't probe at all. */
167162306a36Sopenharmony_ci		err = -ENXIO;
167262306a36Sopenharmony_ci	} else {
167362306a36Sopenharmony_ci		for (port = netcard_portlist; *port; port++) {
167462306a36Sopenharmony_ci			if (cs89x0_ioport_probe(dev, *port, 0) == 0)
167562306a36Sopenharmony_ci				break;
167662306a36Sopenharmony_ci			dev->irq = irq;
167762306a36Sopenharmony_ci		}
167862306a36Sopenharmony_ci		if (!*port)
167962306a36Sopenharmony_ci			err = -ENODEV;
168062306a36Sopenharmony_ci	}
168162306a36Sopenharmony_ci	if (err)
168262306a36Sopenharmony_ci		goto out;
168362306a36Sopenharmony_ci	return dev;
168462306a36Sopenharmony_ciout:
168562306a36Sopenharmony_ci	free_netdev(dev);
168662306a36Sopenharmony_ci	pr_warn("no cs8900 or cs8920 detected.  Be sure to disable PnP with SETUP\n");
168762306a36Sopenharmony_ci	return ERR_PTR(err);
168862306a36Sopenharmony_ci}
168962306a36Sopenharmony_ci#else
169062306a36Sopenharmony_cistatic struct net_device *dev_cs89x0;
169162306a36Sopenharmony_ci
169262306a36Sopenharmony_ci/* Support the 'debug' module parm even if we're compiled for non-debug to
169362306a36Sopenharmony_ci * avoid breaking someone's startup scripts
169462306a36Sopenharmony_ci */
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_cistatic int io;
169762306a36Sopenharmony_cistatic int irq;
169862306a36Sopenharmony_cistatic int debug;
169962306a36Sopenharmony_cistatic char media[8];
170062306a36Sopenharmony_cistatic int duplex = -1;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_cistatic int use_dma;			/* These generate unused var warnings if ALLOW_DMA = 0 */
170362306a36Sopenharmony_cistatic int dma;
170462306a36Sopenharmony_cistatic int dmasize = 16;		/* or 64 */
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_cimodule_param_hw(io, int, ioport, 0);
170762306a36Sopenharmony_cimodule_param_hw(irq, int, irq, 0);
170862306a36Sopenharmony_cimodule_param(debug, int, 0);
170962306a36Sopenharmony_cimodule_param_string(media, media, sizeof(media), 0);
171062306a36Sopenharmony_cimodule_param(duplex, int, 0);
171162306a36Sopenharmony_cimodule_param_hw(dma , int, dma, 0);
171262306a36Sopenharmony_cimodule_param(dmasize , int, 0);
171362306a36Sopenharmony_cimodule_param(use_dma , int, 0);
171462306a36Sopenharmony_ciMODULE_PARM_DESC(io, "cs89x0 I/O base address");
171562306a36Sopenharmony_ciMODULE_PARM_DESC(irq, "cs89x0 IRQ number");
171662306a36Sopenharmony_ci#if DEBUGGING
171762306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "cs89x0 debug level (0-6)");
171862306a36Sopenharmony_ci#else
171962306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "(ignored)");
172062306a36Sopenharmony_ci#endif
172162306a36Sopenharmony_ciMODULE_PARM_DESC(media, "Set cs89x0 adapter(s) media type(s) (rj45,bnc,aui)");
172262306a36Sopenharmony_ci/* No other value than -1 for duplex seems to be currently interpreted */
172362306a36Sopenharmony_ciMODULE_PARM_DESC(duplex, "(ignored)");
172462306a36Sopenharmony_ci#if ALLOW_DMA
172562306a36Sopenharmony_ciMODULE_PARM_DESC(dma , "cs89x0 ISA DMA channel; ignored if use_dma=0");
172662306a36Sopenharmony_ciMODULE_PARM_DESC(dmasize , "cs89x0 DMA size in kB (16,64); ignored if use_dma=0");
172762306a36Sopenharmony_ciMODULE_PARM_DESC(use_dma , "cs89x0 using DMA (0-1)");
172862306a36Sopenharmony_ci#else
172962306a36Sopenharmony_ciMODULE_PARM_DESC(dma , "(ignored)");
173062306a36Sopenharmony_ciMODULE_PARM_DESC(dmasize , "(ignored)");
173162306a36Sopenharmony_ciMODULE_PARM_DESC(use_dma , "(ignored)");
173262306a36Sopenharmony_ci#endif
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ciMODULE_AUTHOR("Mike Cruse, Russwll Nelson <nelson@crynwr.com>, Andrew Morton");
173562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci/*
173862306a36Sopenharmony_ci * media=t             - specify media type
173962306a36Sopenharmony_ci * or media=2
174062306a36Sopenharmony_ci * or media=aui
174162306a36Sopenharmony_ci * or medai=auto
174262306a36Sopenharmony_ci * duplex=0            - specify forced half/full/autonegotiate duplex
174362306a36Sopenharmony_ci * debug=#             - debug level
174462306a36Sopenharmony_ci *
174562306a36Sopenharmony_ci * Default Chip Configuration:
174662306a36Sopenharmony_ci * DMA Burst = enabled
174762306a36Sopenharmony_ci * IOCHRDY Enabled = enabled
174862306a36Sopenharmony_ci * UseSA = enabled
174962306a36Sopenharmony_ci * CS8900 defaults to half-duplex if not specified on command-line
175062306a36Sopenharmony_ci * CS8920 defaults to autoneg if not specified on command-line
175162306a36Sopenharmony_ci * Use reset defaults for other config parameters
175262306a36Sopenharmony_ci *
175362306a36Sopenharmony_ci * Assumptions:
175462306a36Sopenharmony_ci * media type specified is supported (circuitry is present)
175562306a36Sopenharmony_ci * if memory address is > 1MB, then required mem decode hw is present
175662306a36Sopenharmony_ci * if 10B-2, then agent other than driver will enable DC/DC converter
175762306a36Sopenharmony_ci * (hw or software util)
175862306a36Sopenharmony_ci */
175962306a36Sopenharmony_ci
176062306a36Sopenharmony_cistatic int __init cs89x0_isa_init_module(void)
176162306a36Sopenharmony_ci{
176262306a36Sopenharmony_ci	struct net_device *dev;
176362306a36Sopenharmony_ci	struct net_local *lp;
176462306a36Sopenharmony_ci	int ret = 0;
176562306a36Sopenharmony_ci
176662306a36Sopenharmony_ci#if DEBUGGING
176762306a36Sopenharmony_ci	net_debug = debug;
176862306a36Sopenharmony_ci#else
176962306a36Sopenharmony_ci	debug = 0;
177062306a36Sopenharmony_ci#endif
177162306a36Sopenharmony_ci	dev = alloc_etherdev(sizeof(struct net_local));
177262306a36Sopenharmony_ci	if (!dev)
177362306a36Sopenharmony_ci		return -ENOMEM;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	dev->irq = irq;
177662306a36Sopenharmony_ci	dev->base_addr = io;
177762306a36Sopenharmony_ci	lp = netdev_priv(dev);
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci#if ALLOW_DMA
178062306a36Sopenharmony_ci	if (use_dma) {
178162306a36Sopenharmony_ci		lp->use_dma = use_dma;
178262306a36Sopenharmony_ci		lp->dma = dma;
178362306a36Sopenharmony_ci		lp->dmasize = dmasize;
178462306a36Sopenharmony_ci	}
178562306a36Sopenharmony_ci#endif
178662306a36Sopenharmony_ci
178762306a36Sopenharmony_ci	spin_lock_init(&lp->lock);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	/* boy, they'd better get these right */
179062306a36Sopenharmony_ci	if (!strcmp(media, "rj45"))
179162306a36Sopenharmony_ci		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
179262306a36Sopenharmony_ci	else if (!strcmp(media, "aui"))
179362306a36Sopenharmony_ci		lp->adapter_cnf = A_CNF_MEDIA_AUI   | A_CNF_AUI;
179462306a36Sopenharmony_ci	else if (!strcmp(media, "bnc"))
179562306a36Sopenharmony_ci		lp->adapter_cnf = A_CNF_MEDIA_10B_2 | A_CNF_10B_2;
179662306a36Sopenharmony_ci	else
179762306a36Sopenharmony_ci		lp->adapter_cnf = A_CNF_MEDIA_10B_T | A_CNF_10B_T;
179862306a36Sopenharmony_ci
179962306a36Sopenharmony_ci	if (duplex == -1)
180062306a36Sopenharmony_ci		lp->auto_neg_cnf = AUTO_NEG_ENABLE;
180162306a36Sopenharmony_ci
180262306a36Sopenharmony_ci	if (io == 0) {
180362306a36Sopenharmony_ci		pr_err("Module autoprobing not allowed\n");
180462306a36Sopenharmony_ci		pr_err("Append io=0xNNN\n");
180562306a36Sopenharmony_ci		ret = -EPERM;
180662306a36Sopenharmony_ci		goto out;
180762306a36Sopenharmony_ci	} else if (io <= 0x1ff) {
180862306a36Sopenharmony_ci		ret = -ENXIO;
180962306a36Sopenharmony_ci		goto out;
181062306a36Sopenharmony_ci	}
181162306a36Sopenharmony_ci
181262306a36Sopenharmony_ci#if ALLOW_DMA
181362306a36Sopenharmony_ci	if (use_dma && dmasize != 16 && dmasize != 64) {
181462306a36Sopenharmony_ci		pr_err("dma size must be either 16K or 64K, not %dK\n",
181562306a36Sopenharmony_ci		       dmasize);
181662306a36Sopenharmony_ci		ret = -EPERM;
181762306a36Sopenharmony_ci		goto out;
181862306a36Sopenharmony_ci	}
181962306a36Sopenharmony_ci#endif
182062306a36Sopenharmony_ci	ret = cs89x0_ioport_probe(dev, io, 1);
182162306a36Sopenharmony_ci	if (ret)
182262306a36Sopenharmony_ci		goto out;
182362306a36Sopenharmony_ci
182462306a36Sopenharmony_ci	dev_cs89x0 = dev;
182562306a36Sopenharmony_ci	return 0;
182662306a36Sopenharmony_ciout:
182762306a36Sopenharmony_ci	free_netdev(dev);
182862306a36Sopenharmony_ci	return ret;
182962306a36Sopenharmony_ci}
183062306a36Sopenharmony_cimodule_init(cs89x0_isa_init_module);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic void __exit cs89x0_isa_cleanup_module(void)
183362306a36Sopenharmony_ci{
183462306a36Sopenharmony_ci	struct net_local *lp = netdev_priv(dev_cs89x0);
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	unregister_netdev(dev_cs89x0);
183762306a36Sopenharmony_ci	iowrite16(PP_ChipID, lp->virt_addr + ADD_PORT);
183862306a36Sopenharmony_ci	ioport_unmap(lp->virt_addr);
183962306a36Sopenharmony_ci	release_region(dev_cs89x0->base_addr, NETCARD_IO_EXTENT);
184062306a36Sopenharmony_ci	free_netdev(dev_cs89x0);
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_cimodule_exit(cs89x0_isa_cleanup_module);
184362306a36Sopenharmony_ci#endif /* MODULE */
184462306a36Sopenharmony_ci#endif /* CONFIG_CS89x0_ISA */
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_CS89x0_PLATFORM)
184762306a36Sopenharmony_cistatic int __init cs89x0_platform_probe(struct platform_device *pdev)
184862306a36Sopenharmony_ci{
184962306a36Sopenharmony_ci	struct net_device *dev = alloc_etherdev(sizeof(struct net_local));
185062306a36Sopenharmony_ci	void __iomem *virt_addr;
185162306a36Sopenharmony_ci	int err;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	if (!dev)
185462306a36Sopenharmony_ci		return -ENOMEM;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	dev->irq = platform_get_irq(pdev, 0);
185762306a36Sopenharmony_ci	if (dev->irq < 0) {
185862306a36Sopenharmony_ci		err = dev->irq;
185962306a36Sopenharmony_ci		goto free;
186062306a36Sopenharmony_ci	}
186162306a36Sopenharmony_ci
186262306a36Sopenharmony_ci	virt_addr = devm_platform_ioremap_resource(pdev, 0);
186362306a36Sopenharmony_ci	if (IS_ERR(virt_addr)) {
186462306a36Sopenharmony_ci		err = PTR_ERR(virt_addr);
186562306a36Sopenharmony_ci		goto free;
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	err = cs89x0_probe1(dev, virt_addr, 0);
186962306a36Sopenharmony_ci	if (err) {
187062306a36Sopenharmony_ci		dev_warn(&dev->dev, "no cs8900 or cs8920 detected\n");
187162306a36Sopenharmony_ci		goto free;
187262306a36Sopenharmony_ci	}
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	platform_set_drvdata(pdev, dev);
187562306a36Sopenharmony_ci	return 0;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_cifree:
187862306a36Sopenharmony_ci	free_netdev(dev);
187962306a36Sopenharmony_ci	return err;
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_cistatic int cs89x0_platform_remove(struct platform_device *pdev)
188362306a36Sopenharmony_ci{
188462306a36Sopenharmony_ci	struct net_device *dev = platform_get_drvdata(pdev);
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	/* This platform_get_resource() call will not return NULL, because
188762306a36Sopenharmony_ci	 * the same call in cs89x0_platform_probe() has returned a non NULL
188862306a36Sopenharmony_ci	 * value.
188962306a36Sopenharmony_ci	 */
189062306a36Sopenharmony_ci	unregister_netdev(dev);
189162306a36Sopenharmony_ci	free_netdev(dev);
189262306a36Sopenharmony_ci	return 0;
189362306a36Sopenharmony_ci}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused cs89x0_match[] = {
189662306a36Sopenharmony_ci	{ .compatible = "cirrus,cs8900", },
189762306a36Sopenharmony_ci	{ .compatible = "cirrus,cs8920", },
189862306a36Sopenharmony_ci	{ },
189962306a36Sopenharmony_ci};
190062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, cs89x0_match);
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic struct platform_driver cs89x0_driver = {
190362306a36Sopenharmony_ci	.driver	= {
190462306a36Sopenharmony_ci		.name		= DRV_NAME,
190562306a36Sopenharmony_ci		.of_match_table	= of_match_ptr(cs89x0_match),
190662306a36Sopenharmony_ci	},
190762306a36Sopenharmony_ci	.remove	= cs89x0_platform_remove,
190862306a36Sopenharmony_ci};
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_cimodule_platform_driver_probe(cs89x0_driver, cs89x0_platform_probe);
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci#endif /* CONFIG_CS89x0_PLATFORM */
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
191562306a36Sopenharmony_ciMODULE_DESCRIPTION("Crystal Semiconductor (Now Cirrus Logic) CS89[02]0 network driver");
191662306a36Sopenharmony_ciMODULE_AUTHOR("Russell Nelson <nelson@crynwr.com>");
1917