162306a36Sopenharmony_ci/*======================================================================
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci    A PCMCIA ethernet driver for SMC91c92-based cards.
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci    This driver supports Megahertz PCMCIA ethernet cards; and
662306a36Sopenharmony_ci    Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem
762306a36Sopenharmony_ci    multifunction cards.
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci    Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci    smc91c92_cs.c 1.122 2002/10/25 06:26:39
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci    This driver contains code written by Donald Becker
1462306a36Sopenharmony_ci    (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au),
1562306a36Sopenharmony_ci    David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman
1662306a36Sopenharmony_ci    (erik@vt.edu).  Donald wrote the SMC 91c92 code using parts of
1762306a36Sopenharmony_ci    Erik's SMC 91c94 driver.  Rowan wrote a similar driver, and I've
1862306a36Sopenharmony_ci    incorporated some parts of his driver here.  I (Dave) wrote most
1962306a36Sopenharmony_ci    of the PCMCIA glue code, and the Ositech support code.  Kelly
2062306a36Sopenharmony_ci    Stephens (kstephen@holli.com) added support for the Motorola
2162306a36Sopenharmony_ci    Mariner, with help from Allen Brost.
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci    This software may be used and distributed according to the terms of
2462306a36Sopenharmony_ci    the GNU General Public License, incorporated herein by reference.
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci======================================================================*/
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <linux/kernel.h>
3262306a36Sopenharmony_ci#include <linux/slab.h>
3362306a36Sopenharmony_ci#include <linux/string.h>
3462306a36Sopenharmony_ci#include <linux/timer.h>
3562306a36Sopenharmony_ci#include <linux/interrupt.h>
3662306a36Sopenharmony_ci#include <linux/delay.h>
3762306a36Sopenharmony_ci#include <linux/crc32.h>
3862306a36Sopenharmony_ci#include <linux/netdevice.h>
3962306a36Sopenharmony_ci#include <linux/etherdevice.h>
4062306a36Sopenharmony_ci#include <linux/skbuff.h>
4162306a36Sopenharmony_ci#include <linux/if_arp.h>
4262306a36Sopenharmony_ci#include <linux/ioport.h>
4362306a36Sopenharmony_ci#include <linux/ethtool.h>
4462306a36Sopenharmony_ci#include <linux/mii.h>
4562306a36Sopenharmony_ci#include <linux/jiffies.h>
4662306a36Sopenharmony_ci#include <linux/firmware.h>
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#include <pcmcia/cistpl.h>
4962306a36Sopenharmony_ci#include <pcmcia/cisreg.h>
5062306a36Sopenharmony_ci#include <pcmcia/ciscode.h>
5162306a36Sopenharmony_ci#include <pcmcia/ds.h>
5262306a36Sopenharmony_ci#include <pcmcia/ss.h>
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci#include <asm/io.h>
5562306a36Sopenharmony_ci#include <linux/uaccess.h>
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/*====================================================================*/
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic const char *if_names[] = { "auto", "10baseT", "10base2"};
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci/* Firmware name */
6262306a36Sopenharmony_ci#define FIRMWARE_NAME		"ositech/Xilinx7OD.bin"
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci/* Module parameters */
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ciMODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver");
6762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
6862306a36Sopenharmony_ciMODULE_FIRMWARE(FIRMWARE_NAME);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci/*
7362306a36Sopenharmony_ci  Transceiver/media type.
7462306a36Sopenharmony_ci   0 = auto
7562306a36Sopenharmony_ci   1 = 10baseT (and autoselect if #define AUTOSELECT),
7662306a36Sopenharmony_ci   2 = AUI/10base2,
7762306a36Sopenharmony_ci*/
7862306a36Sopenharmony_ciINT_MODULE_PARM(if_port, 0);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci#define DRV_NAME	"smc91c92_cs"
8262306a36Sopenharmony_ci#define DRV_VERSION	"1.123"
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*====================================================================*/
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/* Operational parameter that usually are not changed. */
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Time in jiffies before concluding Tx hung */
8962306a36Sopenharmony_ci#define TX_TIMEOUT		((400*HZ)/1000)
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
9262306a36Sopenharmony_ci#define INTR_WORK		4
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/* Times to check the check the chip before concluding that it doesn't
9562306a36Sopenharmony_ci   currently have room for another Tx packet. */
9662306a36Sopenharmony_ci#define MEMORY_WAIT_TIME       	8
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistruct smc_private {
9962306a36Sopenharmony_ci	struct pcmcia_device	*p_dev;
10062306a36Sopenharmony_ci    spinlock_t			lock;
10162306a36Sopenharmony_ci    u_short			manfid;
10262306a36Sopenharmony_ci    u_short			cardid;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci    struct sk_buff		*saved_skb;
10562306a36Sopenharmony_ci    int				packets_waiting;
10662306a36Sopenharmony_ci    void			__iomem *base;
10762306a36Sopenharmony_ci    u_short			cfg;
10862306a36Sopenharmony_ci    struct timer_list		media;
10962306a36Sopenharmony_ci    int				watchdog, tx_err;
11062306a36Sopenharmony_ci    u_short			media_status;
11162306a36Sopenharmony_ci    u_short			fast_poll;
11262306a36Sopenharmony_ci    u_short			link_status;
11362306a36Sopenharmony_ci    struct mii_if_info		mii_if;
11462306a36Sopenharmony_ci    int				duplex;
11562306a36Sopenharmony_ci    int				rx_ovrn;
11662306a36Sopenharmony_ci    unsigned long		last_rx;
11762306a36Sopenharmony_ci};
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/* Special definitions for Megahertz multifunction cards */
12062306a36Sopenharmony_ci#define MEGAHERTZ_ISR		0x0380
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci/* Special function registers for Motorola Mariner */
12362306a36Sopenharmony_ci#define MOT_LAN			0x0000
12462306a36Sopenharmony_ci#define MOT_UART		0x0020
12562306a36Sopenharmony_ci#define MOT_EEPROM		0x20
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define MOT_NORMAL \
12862306a36Sopenharmony_ci(COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA)
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci/* Special function registers for Ositech cards */
13162306a36Sopenharmony_ci#define OSITECH_AUI_CTL		0x0c
13262306a36Sopenharmony_ci#define OSITECH_PWRDOWN		0x0d
13362306a36Sopenharmony_ci#define OSITECH_RESET		0x0e
13462306a36Sopenharmony_ci#define OSITECH_ISR		0x0f
13562306a36Sopenharmony_ci#define OSITECH_AUI_PWR		0x0c
13662306a36Sopenharmony_ci#define OSITECH_RESET_ISR	0x0e
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci#define OSI_AUI_PWR		0x40
13962306a36Sopenharmony_ci#define OSI_LAN_PWRDOWN		0x02
14062306a36Sopenharmony_ci#define OSI_MODEM_PWRDOWN	0x01
14162306a36Sopenharmony_ci#define OSI_LAN_RESET		0x02
14262306a36Sopenharmony_ci#define OSI_MODEM_RESET		0x01
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci/* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */
14562306a36Sopenharmony_ci#define	BANK_SELECT		14		/* Window select register. */
14662306a36Sopenharmony_ci#define SMC_SELECT_BANK(x)  { outw(x, ioaddr + BANK_SELECT); }
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci/* Bank 0 registers. */
14962306a36Sopenharmony_ci#define	TCR 		0	/* transmit control register */
15062306a36Sopenharmony_ci#define	 TCR_CLEAR	0	/* do NOTHING */
15162306a36Sopenharmony_ci#define  TCR_ENABLE	0x0001	/* if this is 1, we can transmit */
15262306a36Sopenharmony_ci#define	 TCR_PAD_EN	0x0080	/* pads short packets to 64 bytes */
15362306a36Sopenharmony_ci#define  TCR_MONCSN	0x0400  /* Monitor Carrier. */
15462306a36Sopenharmony_ci#define  TCR_FDUPLX	0x0800  /* Full duplex mode. */
15562306a36Sopenharmony_ci#define	 TCR_NORMAL TCR_ENABLE | TCR_PAD_EN
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci#define EPH		2	/* Ethernet Protocol Handler report. */
15862306a36Sopenharmony_ci#define  EPH_TX_SUC	0x0001
15962306a36Sopenharmony_ci#define  EPH_SNGLCOL	0x0002
16062306a36Sopenharmony_ci#define  EPH_MULCOL	0x0004
16162306a36Sopenharmony_ci#define  EPH_LTX_MULT	0x0008
16262306a36Sopenharmony_ci#define  EPH_16COL	0x0010
16362306a36Sopenharmony_ci#define  EPH_SQET	0x0020
16462306a36Sopenharmony_ci#define  EPH_LTX_BRD	0x0040
16562306a36Sopenharmony_ci#define  EPH_TX_DEFR	0x0080
16662306a36Sopenharmony_ci#define  EPH_LAT_COL	0x0200
16762306a36Sopenharmony_ci#define  EPH_LOST_CAR	0x0400
16862306a36Sopenharmony_ci#define  EPH_EXC_DEF	0x0800
16962306a36Sopenharmony_ci#define  EPH_CTR_ROL	0x1000
17062306a36Sopenharmony_ci#define  EPH_RX_OVRN	0x2000
17162306a36Sopenharmony_ci#define  EPH_LINK_OK	0x4000
17262306a36Sopenharmony_ci#define  EPH_TX_UNRN	0x8000
17362306a36Sopenharmony_ci#define MEMINFO		8	/* Memory Information Register */
17462306a36Sopenharmony_ci#define MEMCFG		10	/* Memory Configuration Register */
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci/* Bank 1 registers. */
17762306a36Sopenharmony_ci#define CONFIG			0
17862306a36Sopenharmony_ci#define  CFG_MII_SELECT		0x8000	/* 91C100 only */
17962306a36Sopenharmony_ci#define  CFG_NO_WAIT		0x1000
18062306a36Sopenharmony_ci#define  CFG_FULL_STEP		0x0400
18162306a36Sopenharmony_ci#define  CFG_SET_SQLCH		0x0200
18262306a36Sopenharmony_ci#define  CFG_AUI_SELECT	 	0x0100
18362306a36Sopenharmony_ci#define  CFG_16BIT		0x0080
18462306a36Sopenharmony_ci#define  CFG_DIS_LINK		0x0040
18562306a36Sopenharmony_ci#define  CFG_STATIC		0x0030
18662306a36Sopenharmony_ci#define  CFG_IRQ_SEL_1		0x0004
18762306a36Sopenharmony_ci#define  CFG_IRQ_SEL_0		0x0002
18862306a36Sopenharmony_ci#define BASE_ADDR		2
18962306a36Sopenharmony_ci#define	ADDR0			4
19062306a36Sopenharmony_ci#define	GENERAL			10
19162306a36Sopenharmony_ci#define	CONTROL			12
19262306a36Sopenharmony_ci#define  CTL_STORE		0x0001
19362306a36Sopenharmony_ci#define  CTL_RELOAD		0x0002
19462306a36Sopenharmony_ci#define  CTL_EE_SELECT		0x0004
19562306a36Sopenharmony_ci#define  CTL_TE_ENABLE		0x0020
19662306a36Sopenharmony_ci#define  CTL_CR_ENABLE		0x0040
19762306a36Sopenharmony_ci#define  CTL_LE_ENABLE		0x0080
19862306a36Sopenharmony_ci#define  CTL_AUTO_RELEASE	0x0800
19962306a36Sopenharmony_ci#define	 CTL_POWERDOWN		0x2000
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci/* Bank 2 registers. */
20262306a36Sopenharmony_ci#define MMU_CMD		0
20362306a36Sopenharmony_ci#define	 MC_ALLOC	0x20  	/* or with number of 256 byte packets */
20462306a36Sopenharmony_ci#define	 MC_RESET	0x40
20562306a36Sopenharmony_ci#define  MC_RELEASE  	0x80  	/* remove and release the current rx packet */
20662306a36Sopenharmony_ci#define  MC_FREEPKT  	0xA0  	/* Release packet in PNR register */
20762306a36Sopenharmony_ci#define  MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */
20862306a36Sopenharmony_ci#define	PNR_ARR		2
20962306a36Sopenharmony_ci#define FIFO_PORTS	4
21062306a36Sopenharmony_ci#define  FP_RXEMPTY	0x8000
21162306a36Sopenharmony_ci#define	POINTER		6
21262306a36Sopenharmony_ci#define  PTR_AUTO_INC	0x0040
21362306a36Sopenharmony_ci#define  PTR_READ	0x2000
21462306a36Sopenharmony_ci#define	 PTR_AUTOINC 	0x4000
21562306a36Sopenharmony_ci#define	 PTR_RCV	0x8000
21662306a36Sopenharmony_ci#define	DATA_1		8
21762306a36Sopenharmony_ci#define	INTERRUPT	12
21862306a36Sopenharmony_ci#define  IM_RCV_INT		0x1
21962306a36Sopenharmony_ci#define	 IM_TX_INT		0x2
22062306a36Sopenharmony_ci#define	 IM_TX_EMPTY_INT	0x4
22162306a36Sopenharmony_ci#define	 IM_ALLOC_INT		0x8
22262306a36Sopenharmony_ci#define	 IM_RX_OVRN_INT		0x10
22362306a36Sopenharmony_ci#define	 IM_EPH_INT		0x20
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci#define	RCR		4
22662306a36Sopenharmony_cienum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002,
22762306a36Sopenharmony_ci	     RxEnable = 0x0100, RxStripCRC = 0x0200};
22862306a36Sopenharmony_ci#define  RCR_SOFTRESET	0x8000 	/* resets the chip */
22962306a36Sopenharmony_ci#define	 RCR_STRIP_CRC	0x200	/* strips CRC */
23062306a36Sopenharmony_ci#define  RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */
23162306a36Sopenharmony_ci#define  RCR_ALMUL	0x4 	/* receive all multicast packets */
23262306a36Sopenharmony_ci#define	 RCR_PROMISC	0x2	/* enable promiscuous mode */
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci/* the normal settings for the RCR register : */
23562306a36Sopenharmony_ci#define	 RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE)
23662306a36Sopenharmony_ci#define  RCR_CLEAR	0x0		/* set it to a base state */
23762306a36Sopenharmony_ci#define	COUNTER		6
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci/* BANK 3 -- not the same values as in smc9194! */
24062306a36Sopenharmony_ci#define	MULTICAST0	0
24162306a36Sopenharmony_ci#define	MULTICAST2	2
24262306a36Sopenharmony_ci#define	MULTICAST4	4
24362306a36Sopenharmony_ci#define	MULTICAST6	6
24462306a36Sopenharmony_ci#define MGMT    	8
24562306a36Sopenharmony_ci#define REVISION	0x0a
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci/* Transmit status bits. */
24862306a36Sopenharmony_ci#define TS_SUCCESS 0x0001
24962306a36Sopenharmony_ci#define TS_16COL   0x0010
25062306a36Sopenharmony_ci#define TS_LATCOL  0x0200
25162306a36Sopenharmony_ci#define TS_LOSTCAR 0x0400
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/* Receive status bits. */
25462306a36Sopenharmony_ci#define RS_ALGNERR	0x8000
25562306a36Sopenharmony_ci#define RS_BADCRC	0x2000
25662306a36Sopenharmony_ci#define RS_ODDFRAME	0x1000
25762306a36Sopenharmony_ci#define RS_TOOLONG	0x0800
25862306a36Sopenharmony_ci#define RS_TOOSHORT	0x0400
25962306a36Sopenharmony_ci#define RS_MULTICAST	0x0001
26062306a36Sopenharmony_ci#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT)
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci#define set_bits(v, p) outw(inw(p)|(v), (p))
26362306a36Sopenharmony_ci#define mask_bits(v, p) outw(inw(p)&(v), (p))
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci/*====================================================================*/
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void smc91c92_detach(struct pcmcia_device *p_dev);
26862306a36Sopenharmony_cistatic int smc91c92_config(struct pcmcia_device *link);
26962306a36Sopenharmony_cistatic void smc91c92_release(struct pcmcia_device *link);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int smc_open(struct net_device *dev);
27262306a36Sopenharmony_cistatic int smc_close(struct net_device *dev);
27362306a36Sopenharmony_cistatic int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
27462306a36Sopenharmony_cistatic void smc_tx_timeout(struct net_device *dev, unsigned int txqueue);
27562306a36Sopenharmony_cistatic netdev_tx_t smc_start_xmit(struct sk_buff *skb,
27662306a36Sopenharmony_ci					struct net_device *dev);
27762306a36Sopenharmony_cistatic irqreturn_t smc_interrupt(int irq, void *dev_id);
27862306a36Sopenharmony_cistatic void smc_rx(struct net_device *dev);
27962306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev);
28062306a36Sopenharmony_cistatic int s9k_config(struct net_device *dev, struct ifmap *map);
28162306a36Sopenharmony_cistatic void smc_set_xcvr(struct net_device *dev, int if_port);
28262306a36Sopenharmony_cistatic void smc_reset(struct net_device *dev);
28362306a36Sopenharmony_cistatic void media_check(struct timer_list *t);
28462306a36Sopenharmony_cistatic void mdio_sync(unsigned int addr);
28562306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int loc);
28662306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int loc, int value);
28762306a36Sopenharmony_cistatic int smc_link_ok(struct net_device *dev);
28862306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_cistatic const struct net_device_ops smc_netdev_ops = {
29162306a36Sopenharmony_ci	.ndo_open		= smc_open,
29262306a36Sopenharmony_ci	.ndo_stop		= smc_close,
29362306a36Sopenharmony_ci	.ndo_start_xmit		= smc_start_xmit,
29462306a36Sopenharmony_ci	.ndo_tx_timeout 	= smc_tx_timeout,
29562306a36Sopenharmony_ci	.ndo_set_config 	= s9k_config,
29662306a36Sopenharmony_ci	.ndo_set_rx_mode	= set_rx_mode,
29762306a36Sopenharmony_ci	.ndo_eth_ioctl		= smc_ioctl,
29862306a36Sopenharmony_ci	.ndo_set_mac_address 	= eth_mac_addr,
29962306a36Sopenharmony_ci	.ndo_validate_addr	= eth_validate_addr,
30062306a36Sopenharmony_ci};
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int smc91c92_probe(struct pcmcia_device *link)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci    struct smc_private *smc;
30562306a36Sopenharmony_ci    struct net_device *dev;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci    dev_dbg(&link->dev, "smc91c92_attach()\n");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci    /* Create new ethernet device */
31062306a36Sopenharmony_ci    dev = alloc_etherdev(sizeof(struct smc_private));
31162306a36Sopenharmony_ci    if (!dev)
31262306a36Sopenharmony_ci	return -ENOMEM;
31362306a36Sopenharmony_ci    smc = netdev_priv(dev);
31462306a36Sopenharmony_ci    smc->p_dev = link;
31562306a36Sopenharmony_ci    link->priv = dev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci    spin_lock_init(&smc->lock);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci    /* The SMC91c92-specific entries in the device structure. */
32062306a36Sopenharmony_ci    dev->netdev_ops = &smc_netdev_ops;
32162306a36Sopenharmony_ci    dev->ethtool_ops = &ethtool_ops;
32262306a36Sopenharmony_ci    dev->watchdog_timeo = TX_TIMEOUT;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci    smc->mii_if.dev = dev;
32562306a36Sopenharmony_ci    smc->mii_if.mdio_read = mdio_read;
32662306a36Sopenharmony_ci    smc->mii_if.mdio_write = mdio_write;
32762306a36Sopenharmony_ci    smc->mii_if.phy_id_mask = 0x1f;
32862306a36Sopenharmony_ci    smc->mii_if.reg_num_mask = 0x1f;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci    return smc91c92_config(link);
33162306a36Sopenharmony_ci} /* smc91c92_attach */
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_cistatic void smc91c92_detach(struct pcmcia_device *link)
33462306a36Sopenharmony_ci{
33562306a36Sopenharmony_ci    struct net_device *dev = link->priv;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci    dev_dbg(&link->dev, "smc91c92_detach\n");
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci    unregister_netdev(dev);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci    smc91c92_release(link);
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci    free_netdev(dev);
34462306a36Sopenharmony_ci} /* smc91c92_detach */
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci/*====================================================================*/
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic int cvt_ascii_address(struct net_device *dev, char *s)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci    u8 mac[ETH_ALEN];
35162306a36Sopenharmony_ci    int i, j, da, c;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci    if (strlen(s) != 12)
35462306a36Sopenharmony_ci	return -1;
35562306a36Sopenharmony_ci    for (i = 0; i < 6; i++) {
35662306a36Sopenharmony_ci	da = 0;
35762306a36Sopenharmony_ci	for (j = 0; j < 2; j++) {
35862306a36Sopenharmony_ci	    c = *s++;
35962306a36Sopenharmony_ci	    da <<= 4;
36062306a36Sopenharmony_ci	    da += ((c >= '0') && (c <= '9')) ?
36162306a36Sopenharmony_ci		(c - '0') : ((c & 0x0f) + 9);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci	mac[i] = da;
36462306a36Sopenharmony_ci    }
36562306a36Sopenharmony_ci    eth_hw_addr_set(dev, mac);
36662306a36Sopenharmony_ci    return 0;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*====================================================================
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci    Configuration stuff for Megahertz cards
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci    mhz_3288_power() is used to power up a 3288's ethernet chip.
37462306a36Sopenharmony_ci    mhz_mfc_config() handles socket setup for multifunction (1144
37562306a36Sopenharmony_ci    and 3288) cards.  mhz_setup() gets a card's hardware ethernet
37662306a36Sopenharmony_ci    address.
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci======================================================================*/
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_cistatic int mhz_3288_power(struct pcmcia_device *link)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci    struct net_device *dev = link->priv;
38362306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
38462306a36Sopenharmony_ci    u_char tmp;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci    /* Read the ISR twice... */
38762306a36Sopenharmony_ci    readb(smc->base+MEGAHERTZ_ISR);
38862306a36Sopenharmony_ci    udelay(5);
38962306a36Sopenharmony_ci    readb(smc->base+MEGAHERTZ_ISR);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci    /* Pause 200ms... */
39262306a36Sopenharmony_ci    mdelay(200);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci    /* Now read and write the COR... */
39562306a36Sopenharmony_ci    tmp = readb(smc->base + link->config_base + CISREG_COR);
39662306a36Sopenharmony_ci    udelay(5);
39762306a36Sopenharmony_ci    writeb(tmp, smc->base + link->config_base + CISREG_COR);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci    return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int mhz_mfc_config_check(struct pcmcia_device *p_dev, void *priv_data)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	int k;
40562306a36Sopenharmony_ci	p_dev->io_lines = 16;
40662306a36Sopenharmony_ci	p_dev->resource[1]->start = p_dev->resource[0]->start;
40762306a36Sopenharmony_ci	p_dev->resource[1]->end = 8;
40862306a36Sopenharmony_ci	p_dev->resource[1]->flags &= ~IO_DATA_PATH_WIDTH;
40962306a36Sopenharmony_ci	p_dev->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
41062306a36Sopenharmony_ci	p_dev->resource[0]->end = 16;
41162306a36Sopenharmony_ci	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
41262306a36Sopenharmony_ci	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
41362306a36Sopenharmony_ci	for (k = 0; k < 0x400; k += 0x10) {
41462306a36Sopenharmony_ci		if (k & 0x80)
41562306a36Sopenharmony_ci			continue;
41662306a36Sopenharmony_ci		p_dev->resource[0]->start = k ^ 0x300;
41762306a36Sopenharmony_ci		if (!pcmcia_request_io(p_dev))
41862306a36Sopenharmony_ci			return 0;
41962306a36Sopenharmony_ci	}
42062306a36Sopenharmony_ci	return -ENODEV;
42162306a36Sopenharmony_ci}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_cistatic int mhz_mfc_config(struct pcmcia_device *link)
42462306a36Sopenharmony_ci{
42562306a36Sopenharmony_ci    struct net_device *dev = link->priv;
42662306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
42762306a36Sopenharmony_ci    unsigned int offset;
42862306a36Sopenharmony_ci    int i;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci    link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ |
43162306a36Sopenharmony_ci	    CONF_AUTO_SET_IO;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci    /* The Megahertz combo cards have modem-like CIS entries, so
43462306a36Sopenharmony_ci       we have to explicitly try a bunch of port combinations. */
43562306a36Sopenharmony_ci    if (pcmcia_loop_config(link, mhz_mfc_config_check, NULL))
43662306a36Sopenharmony_ci	    return -ENODEV;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci    dev->base_addr = link->resource[0]->start;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci    /* Allocate a memory window, for accessing the ISR */
44162306a36Sopenharmony_ci    link->resource[2]->flags = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE;
44262306a36Sopenharmony_ci    link->resource[2]->start = link->resource[2]->end = 0;
44362306a36Sopenharmony_ci    i = pcmcia_request_window(link, link->resource[2], 0);
44462306a36Sopenharmony_ci    if (i != 0)
44562306a36Sopenharmony_ci	    return -ENODEV;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci    smc->base = ioremap(link->resource[2]->start,
44862306a36Sopenharmony_ci		    resource_size(link->resource[2]));
44962306a36Sopenharmony_ci    offset = (smc->manfid == MANFID_MOTOROLA) ? link->config_base : 0;
45062306a36Sopenharmony_ci    i = pcmcia_map_mem_page(link, link->resource[2], offset);
45162306a36Sopenharmony_ci    if ((i == 0) &&
45262306a36Sopenharmony_ci	(smc->manfid == MANFID_MEGAHERTZ) &&
45362306a36Sopenharmony_ci	(smc->cardid == PRODID_MEGAHERTZ_EM3288))
45462306a36Sopenharmony_ci	    mhz_3288_power(link);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci    return 0;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_cistatic int pcmcia_get_versmac(struct pcmcia_device *p_dev,
46062306a36Sopenharmony_ci			      tuple_t *tuple,
46162306a36Sopenharmony_ci			      void *priv)
46262306a36Sopenharmony_ci{
46362306a36Sopenharmony_ci	struct net_device *dev = priv;
46462306a36Sopenharmony_ci	cisparse_t parse;
46562306a36Sopenharmony_ci	u8 *buf;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (pcmcia_parse_tuple(tuple, &parse))
46862306a36Sopenharmony_ci		return -EINVAL;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	buf = parse.version_1.str + parse.version_1.ofs[3];
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if ((parse.version_1.ns > 3) && (cvt_ascii_address(dev, buf) == 0))
47362306a36Sopenharmony_ci		return 0;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	return -EINVAL;
47662306a36Sopenharmony_ci};
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic int mhz_setup(struct pcmcia_device *link)
47962306a36Sopenharmony_ci{
48062306a36Sopenharmony_ci    struct net_device *dev = link->priv;
48162306a36Sopenharmony_ci    size_t len;
48262306a36Sopenharmony_ci    u8 *buf;
48362306a36Sopenharmony_ci    int rc;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci    /* Read the station address from the CIS.  It is stored as the last
48662306a36Sopenharmony_ci       (fourth) string in the Version 1 Version/ID tuple. */
48762306a36Sopenharmony_ci    if ((link->prod_id[3]) &&
48862306a36Sopenharmony_ci	(cvt_ascii_address(dev, link->prod_id[3]) == 0))
48962306a36Sopenharmony_ci	    return 0;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci    /* Workarounds for broken cards start here. */
49262306a36Sopenharmony_ci    /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */
49362306a36Sopenharmony_ci    if (!pcmcia_loop_tuple(link, CISTPL_VERS_1, pcmcia_get_versmac, dev))
49462306a36Sopenharmony_ci	    return 0;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci    /* Another possibility: for the EM3288, in a special tuple */
49762306a36Sopenharmony_ci    rc = -1;
49862306a36Sopenharmony_ci    len = pcmcia_get_tuple(link, 0x81, &buf);
49962306a36Sopenharmony_ci    if (buf && len >= 13) {
50062306a36Sopenharmony_ci	    buf[12] = '\0';
50162306a36Sopenharmony_ci	    if (cvt_ascii_address(dev, buf) == 0)
50262306a36Sopenharmony_ci		    rc = 0;
50362306a36Sopenharmony_ci    }
50462306a36Sopenharmony_ci    kfree(buf);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci    return rc;
50762306a36Sopenharmony_ci};
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci/*======================================================================
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci    Configuration stuff for the Motorola Mariner
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci    mot_config() writes directly to the Mariner configuration
51462306a36Sopenharmony_ci    registers because the CIS is just bogus.
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci======================================================================*/
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic void mot_config(struct pcmcia_device *link)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci    struct net_device *dev = link->priv;
52162306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
52262306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
52362306a36Sopenharmony_ci    unsigned int iouart = link->resource[1]->start;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci    /* Set UART base address and force map with COR bit 1 */
52662306a36Sopenharmony_ci    writeb(iouart & 0xff,        smc->base + MOT_UART + CISREG_IOBASE_0);
52762306a36Sopenharmony_ci    writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1);
52862306a36Sopenharmony_ci    writeb(MOT_NORMAL,           smc->base + MOT_UART + CISREG_COR);
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci    /* Set SMC base address and force map with COR bit 1 */
53162306a36Sopenharmony_ci    writeb(ioaddr & 0xff,        smc->base + MOT_LAN + CISREG_IOBASE_0);
53262306a36Sopenharmony_ci    writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1);
53362306a36Sopenharmony_ci    writeb(MOT_NORMAL,           smc->base + MOT_LAN + CISREG_COR);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci    /* Wait for things to settle down */
53662306a36Sopenharmony_ci    mdelay(100);
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic int mot_setup(struct pcmcia_device *link)
54062306a36Sopenharmony_ci{
54162306a36Sopenharmony_ci    struct net_device *dev = link->priv;
54262306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
54362306a36Sopenharmony_ci    int i, wait, loop;
54462306a36Sopenharmony_ci    u8 mac[ETH_ALEN];
54562306a36Sopenharmony_ci    u_int addr;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci    /* Read Ethernet address from Serial EEPROM */
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci    for (i = 0; i < 3; i++) {
55062306a36Sopenharmony_ci	SMC_SELECT_BANK(2);
55162306a36Sopenharmony_ci	outw(MOT_EEPROM + i, ioaddr + POINTER);
55262306a36Sopenharmony_ci	SMC_SELECT_BANK(1);
55362306a36Sopenharmony_ci	outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	for (loop = wait = 0; loop < 200; loop++) {
55662306a36Sopenharmony_ci	    udelay(10);
55762306a36Sopenharmony_ci	    wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL));
55862306a36Sopenharmony_ci	    if (wait == 0) break;
55962306a36Sopenharmony_ci	}
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (wait)
56262306a36Sopenharmony_ci	    return -1;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	addr = inw(ioaddr + GENERAL);
56562306a36Sopenharmony_ci	mac[2*i]   = addr & 0xff;
56662306a36Sopenharmony_ci	mac[2*i+1] = (addr >> 8) & 0xff;
56762306a36Sopenharmony_ci    }
56862306a36Sopenharmony_ci    eth_hw_addr_set(dev, mac);
56962306a36Sopenharmony_ci
57062306a36Sopenharmony_ci    return 0;
57162306a36Sopenharmony_ci}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci/*====================================================================*/
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_cistatic int smc_configcheck(struct pcmcia_device *p_dev, void *priv_data)
57662306a36Sopenharmony_ci{
57762306a36Sopenharmony_ci	p_dev->resource[0]->end = 16;
57862306a36Sopenharmony_ci	p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
57962306a36Sopenharmony_ci	p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci	return pcmcia_request_io(p_dev);
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic int smc_config(struct pcmcia_device *link)
58562306a36Sopenharmony_ci{
58662306a36Sopenharmony_ci    struct net_device *dev = link->priv;
58762306a36Sopenharmony_ci    int i;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci    link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_SET_IO;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci    i = pcmcia_loop_config(link, smc_configcheck, NULL);
59262306a36Sopenharmony_ci    if (!i)
59362306a36Sopenharmony_ci	    dev->base_addr = link->resource[0]->start;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci    return i;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_cistatic int smc_setup(struct pcmcia_device *link)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci    struct net_device *dev = link->priv;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci    /* Check for a LAN function extension tuple */
60462306a36Sopenharmony_ci    if (!pcmcia_get_mac_from_cis(link, dev))
60562306a36Sopenharmony_ci	    return 0;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci    /* Try the third string in the Version 1 Version/ID tuple. */
60862306a36Sopenharmony_ci    if (link->prod_id[2]) {
60962306a36Sopenharmony_ci	    if (cvt_ascii_address(dev, link->prod_id[2]) == 0)
61062306a36Sopenharmony_ci		    return 0;
61162306a36Sopenharmony_ci    }
61262306a36Sopenharmony_ci    return -1;
61362306a36Sopenharmony_ci}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci/*====================================================================*/
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int osi_config(struct pcmcia_device *link)
61862306a36Sopenharmony_ci{
61962306a36Sopenharmony_ci    struct net_device *dev = link->priv;
62062306a36Sopenharmony_ci    static const unsigned int com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
62162306a36Sopenharmony_ci    int i, j;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci    link->config_flags |= CONF_ENABLE_SPKR | CONF_ENABLE_IRQ;
62462306a36Sopenharmony_ci    link->resource[0]->end = 64;
62562306a36Sopenharmony_ci    link->resource[1]->flags |= IO_DATA_PATH_WIDTH_8;
62662306a36Sopenharmony_ci    link->resource[1]->end = 8;
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci    /* Enable Hard Decode, LAN, Modem */
62962306a36Sopenharmony_ci    link->io_lines = 16;
63062306a36Sopenharmony_ci    link->config_index = 0x23;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci    for (i = j = 0; j < 4; j++) {
63362306a36Sopenharmony_ci	link->resource[1]->start = com[j];
63462306a36Sopenharmony_ci	i = pcmcia_request_io(link);
63562306a36Sopenharmony_ci	if (i == 0)
63662306a36Sopenharmony_ci		break;
63762306a36Sopenharmony_ci    }
63862306a36Sopenharmony_ci    if (i != 0) {
63962306a36Sopenharmony_ci	/* Fallback: turn off hard decode */
64062306a36Sopenharmony_ci	link->config_index = 0x03;
64162306a36Sopenharmony_ci	link->resource[1]->end = 0;
64262306a36Sopenharmony_ci	i = pcmcia_request_io(link);
64362306a36Sopenharmony_ci    }
64462306a36Sopenharmony_ci    dev->base_addr = link->resource[0]->start + 0x10;
64562306a36Sopenharmony_ci    return i;
64662306a36Sopenharmony_ci}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_cistatic int osi_load_firmware(struct pcmcia_device *link)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	const struct firmware *fw;
65162306a36Sopenharmony_ci	int i, err;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	err = request_firmware(&fw, FIRMWARE_NAME, &link->dev);
65462306a36Sopenharmony_ci	if (err) {
65562306a36Sopenharmony_ci		pr_err("Failed to load firmware \"%s\"\n", FIRMWARE_NAME);
65662306a36Sopenharmony_ci		return err;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* Download the Seven of Diamonds firmware */
66062306a36Sopenharmony_ci	for (i = 0; i < fw->size; i++) {
66162306a36Sopenharmony_ci	    outb(fw->data[i], link->resource[0]->start + 2);
66262306a36Sopenharmony_ci	    udelay(50);
66362306a36Sopenharmony_ci	}
66462306a36Sopenharmony_ci	release_firmware(fw);
66562306a36Sopenharmony_ci	return err;
66662306a36Sopenharmony_ci}
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_cistatic int pcmcia_osi_mac(struct pcmcia_device *p_dev,
66962306a36Sopenharmony_ci			  tuple_t *tuple,
67062306a36Sopenharmony_ci			  void *priv)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct net_device *dev = priv;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (tuple->TupleDataLen < 8)
67562306a36Sopenharmony_ci		return -EINVAL;
67662306a36Sopenharmony_ci	if (tuple->TupleData[0] != 0x04)
67762306a36Sopenharmony_ci		return -EINVAL;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	eth_hw_addr_set(dev, &tuple->TupleData[2]);
68062306a36Sopenharmony_ci	return 0;
68162306a36Sopenharmony_ci};
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid)
68562306a36Sopenharmony_ci{
68662306a36Sopenharmony_ci    struct net_device *dev = link->priv;
68762306a36Sopenharmony_ci    int rc;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci    /* Read the station address from tuple 0x90, subtuple 0x04 */
69062306a36Sopenharmony_ci    if (pcmcia_loop_tuple(link, 0x90, pcmcia_osi_mac, dev))
69162306a36Sopenharmony_ci	    return -1;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci    if (((manfid == MANFID_OSITECH) &&
69462306a36Sopenharmony_ci	 (cardid == PRODID_OSITECH_SEVEN)) ||
69562306a36Sopenharmony_ci	((manfid == MANFID_PSION) &&
69662306a36Sopenharmony_ci	 (cardid == PRODID_PSION_NET100))) {
69762306a36Sopenharmony_ci	rc = osi_load_firmware(link);
69862306a36Sopenharmony_ci	if (rc)
69962306a36Sopenharmony_ci		return rc;
70062306a36Sopenharmony_ci    } else if (manfid == MANFID_OSITECH) {
70162306a36Sopenharmony_ci	/* Make sure both functions are powered up */
70262306a36Sopenharmony_ci	set_bits(0x300, link->resource[0]->start + OSITECH_AUI_PWR);
70362306a36Sopenharmony_ci	/* Now, turn on the interrupt for both card functions */
70462306a36Sopenharmony_ci	set_bits(0x300, link->resource[0]->start + OSITECH_RESET_ISR);
70562306a36Sopenharmony_ci	dev_dbg(&link->dev, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n",
70662306a36Sopenharmony_ci	      inw(link->resource[0]->start + OSITECH_AUI_PWR),
70762306a36Sopenharmony_ci	      inw(link->resource[0]->start + OSITECH_RESET_ISR));
70862306a36Sopenharmony_ci    }
70962306a36Sopenharmony_ci    return 0;
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_cistatic int smc91c92_suspend(struct pcmcia_device *link)
71362306a36Sopenharmony_ci{
71462306a36Sopenharmony_ci	struct net_device *dev = link->priv;
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	if (link->open)
71762306a36Sopenharmony_ci		netif_device_detach(dev);
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	return 0;
72062306a36Sopenharmony_ci}
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_cistatic int smc91c92_resume(struct pcmcia_device *link)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct net_device *dev = link->priv;
72562306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
72662306a36Sopenharmony_ci	int i;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if ((smc->manfid == MANFID_MEGAHERTZ) &&
72962306a36Sopenharmony_ci	    (smc->cardid == PRODID_MEGAHERTZ_EM3288))
73062306a36Sopenharmony_ci		mhz_3288_power(link);
73162306a36Sopenharmony_ci	if (smc->manfid == MANFID_MOTOROLA)
73262306a36Sopenharmony_ci		mot_config(link);
73362306a36Sopenharmony_ci	if ((smc->manfid == MANFID_OSITECH) &&
73462306a36Sopenharmony_ci	    (smc->cardid != PRODID_OSITECH_SEVEN)) {
73562306a36Sopenharmony_ci		/* Power up the card and enable interrupts */
73662306a36Sopenharmony_ci		set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR);
73762306a36Sopenharmony_ci		set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR);
73862306a36Sopenharmony_ci	}
73962306a36Sopenharmony_ci	if (((smc->manfid == MANFID_OSITECH) &&
74062306a36Sopenharmony_ci	     (smc->cardid == PRODID_OSITECH_SEVEN)) ||
74162306a36Sopenharmony_ci	    ((smc->manfid == MANFID_PSION) &&
74262306a36Sopenharmony_ci	     (smc->cardid == PRODID_PSION_NET100))) {
74362306a36Sopenharmony_ci		i = osi_load_firmware(link);
74462306a36Sopenharmony_ci		if (i) {
74562306a36Sopenharmony_ci			netdev_err(dev, "Failed to load firmware\n");
74662306a36Sopenharmony_ci			return i;
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci	}
74962306a36Sopenharmony_ci	if (link->open) {
75062306a36Sopenharmony_ci		smc_reset(dev);
75162306a36Sopenharmony_ci		netif_device_attach(dev);
75262306a36Sopenharmony_ci	}
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	return 0;
75562306a36Sopenharmony_ci}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci/*======================================================================
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci    This verifies that the chip is some SMC91cXX variant, and returns
76162306a36Sopenharmony_ci    the revision code if successful.  Otherwise, it returns -ENODEV.
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci======================================================================*/
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_cistatic int check_sig(struct pcmcia_device *link)
76662306a36Sopenharmony_ci{
76762306a36Sopenharmony_ci    struct net_device *dev = link->priv;
76862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
76962306a36Sopenharmony_ci    int width;
77062306a36Sopenharmony_ci    u_short s;
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
77362306a36Sopenharmony_ci    if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) {
77462306a36Sopenharmony_ci	/* Try powering up the chip */
77562306a36Sopenharmony_ci	outw(0, ioaddr + CONTROL);
77662306a36Sopenharmony_ci	mdelay(55);
77762306a36Sopenharmony_ci    }
77862306a36Sopenharmony_ci
77962306a36Sopenharmony_ci    /* Try setting bus width */
78062306a36Sopenharmony_ci    width = (link->resource[0]->flags == IO_DATA_PATH_WIDTH_AUTO);
78162306a36Sopenharmony_ci    s = inb(ioaddr + CONFIG);
78262306a36Sopenharmony_ci    if (width)
78362306a36Sopenharmony_ci	s |= CFG_16BIT;
78462306a36Sopenharmony_ci    else
78562306a36Sopenharmony_ci	s &= ~CFG_16BIT;
78662306a36Sopenharmony_ci    outb(s, ioaddr + CONFIG);
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci    /* Check Base Address Register to make sure bus width is OK */
78962306a36Sopenharmony_ci    s = inw(ioaddr + BASE_ADDR);
79062306a36Sopenharmony_ci    if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) &&
79162306a36Sopenharmony_ci	((s >> 8) != (s & 0xff))) {
79262306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
79362306a36Sopenharmony_ci	s = inw(ioaddr + REVISION);
79462306a36Sopenharmony_ci	return s & 0xff;
79562306a36Sopenharmony_ci    }
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci    if (width) {
79862306a36Sopenharmony_ci	    netdev_info(dev, "using 8-bit IO window\n");
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	    smc91c92_suspend(link);
80162306a36Sopenharmony_ci	    pcmcia_fixup_iowidth(link);
80262306a36Sopenharmony_ci	    smc91c92_resume(link);
80362306a36Sopenharmony_ci	    return check_sig(link);
80462306a36Sopenharmony_ci    }
80562306a36Sopenharmony_ci    return -ENODEV;
80662306a36Sopenharmony_ci}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_cistatic int smc91c92_config(struct pcmcia_device *link)
80962306a36Sopenharmony_ci{
81062306a36Sopenharmony_ci    struct net_device *dev = link->priv;
81162306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
81262306a36Sopenharmony_ci    char *name;
81362306a36Sopenharmony_ci    int i, rev, j = 0;
81462306a36Sopenharmony_ci    unsigned int ioaddr;
81562306a36Sopenharmony_ci    u_long mir;
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci    dev_dbg(&link->dev, "smc91c92_config\n");
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci    smc->manfid = link->manf_id;
82062306a36Sopenharmony_ci    smc->cardid = link->card_id;
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci    if ((smc->manfid == MANFID_OSITECH) &&
82362306a36Sopenharmony_ci	(smc->cardid != PRODID_OSITECH_SEVEN)) {
82462306a36Sopenharmony_ci	i = osi_config(link);
82562306a36Sopenharmony_ci    } else if ((smc->manfid == MANFID_MOTOROLA) ||
82662306a36Sopenharmony_ci	       ((smc->manfid == MANFID_MEGAHERTZ) &&
82762306a36Sopenharmony_ci		((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) ||
82862306a36Sopenharmony_ci		 (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) {
82962306a36Sopenharmony_ci	i = mhz_mfc_config(link);
83062306a36Sopenharmony_ci    } else {
83162306a36Sopenharmony_ci	i = smc_config(link);
83262306a36Sopenharmony_ci    }
83362306a36Sopenharmony_ci    if (i)
83462306a36Sopenharmony_ci	    goto config_failed;
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci    i = pcmcia_request_irq(link, smc_interrupt);
83762306a36Sopenharmony_ci    if (i)
83862306a36Sopenharmony_ci	    goto config_failed;
83962306a36Sopenharmony_ci    i = pcmcia_enable_device(link);
84062306a36Sopenharmony_ci    if (i)
84162306a36Sopenharmony_ci	    goto config_failed;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci    if (smc->manfid == MANFID_MOTOROLA)
84462306a36Sopenharmony_ci	mot_config(link);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci    dev->irq = link->irq;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci    if ((if_port >= 0) && (if_port <= 2))
84962306a36Sopenharmony_ci	dev->if_port = if_port;
85062306a36Sopenharmony_ci    else
85162306a36Sopenharmony_ci	dev_notice(&link->dev, "invalid if_port requested\n");
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci    switch (smc->manfid) {
85462306a36Sopenharmony_ci    case MANFID_OSITECH:
85562306a36Sopenharmony_ci    case MANFID_PSION:
85662306a36Sopenharmony_ci	i = osi_setup(link, smc->manfid, smc->cardid); break;
85762306a36Sopenharmony_ci    case MANFID_SMC:
85862306a36Sopenharmony_ci    case MANFID_NEW_MEDIA:
85962306a36Sopenharmony_ci	i = smc_setup(link); break;
86062306a36Sopenharmony_ci    case 0x128: /* For broken Megahertz cards */
86162306a36Sopenharmony_ci    case MANFID_MEGAHERTZ:
86262306a36Sopenharmony_ci	i = mhz_setup(link); break;
86362306a36Sopenharmony_ci    case MANFID_MOTOROLA:
86462306a36Sopenharmony_ci    default: /* get the hw address from EEPROM */
86562306a36Sopenharmony_ci	i = mot_setup(link); break;
86662306a36Sopenharmony_ci    }
86762306a36Sopenharmony_ci
86862306a36Sopenharmony_ci    if (i != 0) {
86962306a36Sopenharmony_ci	dev_notice(&link->dev, "Unable to find hardware address.\n");
87062306a36Sopenharmony_ci	goto config_failed;
87162306a36Sopenharmony_ci    }
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci    smc->duplex = 0;
87462306a36Sopenharmony_ci    smc->rx_ovrn = 0;
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci    rev = check_sig(link);
87762306a36Sopenharmony_ci    name = "???";
87862306a36Sopenharmony_ci    if (rev > 0)
87962306a36Sopenharmony_ci	switch (rev >> 4) {
88062306a36Sopenharmony_ci	case 3: name = "92"; break;
88162306a36Sopenharmony_ci	case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break;
88262306a36Sopenharmony_ci	case 5: name = "95"; break;
88362306a36Sopenharmony_ci	case 7: name = "100"; break;
88462306a36Sopenharmony_ci	case 8: name = "100-FD"; break;
88562306a36Sopenharmony_ci	case 9: name = "110"; break;
88662306a36Sopenharmony_ci	}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci    ioaddr = dev->base_addr;
88962306a36Sopenharmony_ci    if (rev > 0) {
89062306a36Sopenharmony_ci	u_long mcr;
89162306a36Sopenharmony_ci	SMC_SELECT_BANK(0);
89262306a36Sopenharmony_ci	mir = inw(ioaddr + MEMINFO) & 0xff;
89362306a36Sopenharmony_ci	if (mir == 0xff) mir++;
89462306a36Sopenharmony_ci	/* Get scale factor for memory size */
89562306a36Sopenharmony_ci	mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200;
89662306a36Sopenharmony_ci	mir *= 128 * (1<<((mcr >> 9) & 7));
89762306a36Sopenharmony_ci	SMC_SELECT_BANK(1);
89862306a36Sopenharmony_ci	smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT;
89962306a36Sopenharmony_ci	smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC;
90062306a36Sopenharmony_ci	if (smc->manfid == MANFID_OSITECH)
90162306a36Sopenharmony_ci	    smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0;
90262306a36Sopenharmony_ci	if ((rev >> 4) >= 7)
90362306a36Sopenharmony_ci	    smc->cfg |= CFG_MII_SELECT;
90462306a36Sopenharmony_ci    } else
90562306a36Sopenharmony_ci	mir = 0;
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci    if (smc->cfg & CFG_MII_SELECT) {
90862306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	for (i = 0; i < 32; i++) {
91162306a36Sopenharmony_ci	    j = mdio_read(dev, i, 1);
91262306a36Sopenharmony_ci	    if ((j != 0) && (j != 0xffff)) break;
91362306a36Sopenharmony_ci	}
91462306a36Sopenharmony_ci	smc->mii_if.phy_id = (i < 32) ? i : -1;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	SMC_SELECT_BANK(0);
91762306a36Sopenharmony_ci    }
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci    SET_NETDEV_DEV(dev, &link->dev);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci    if (register_netdev(dev) != 0) {
92262306a36Sopenharmony_ci	dev_err(&link->dev, "register_netdev() failed\n");
92362306a36Sopenharmony_ci	goto config_undo;
92462306a36Sopenharmony_ci    }
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci    netdev_info(dev, "smc91c%s rev %d: io %#3lx, irq %d, hw_addr %pM\n",
92762306a36Sopenharmony_ci		name, (rev & 0x0f), dev->base_addr, dev->irq, dev->dev_addr);
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci    if (rev > 0) {
93062306a36Sopenharmony_ci	if (mir & 0x3ff)
93162306a36Sopenharmony_ci	    netdev_info(dev, "  %lu byte", mir);
93262306a36Sopenharmony_ci	else
93362306a36Sopenharmony_ci	    netdev_info(dev, "  %lu kb", mir>>10);
93462306a36Sopenharmony_ci	pr_cont(" buffer, %s xcvr\n",
93562306a36Sopenharmony_ci		(smc->cfg & CFG_MII_SELECT) ? "MII" : if_names[dev->if_port]);
93662306a36Sopenharmony_ci    }
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci    if (smc->cfg & CFG_MII_SELECT) {
93962306a36Sopenharmony_ci	if (smc->mii_if.phy_id != -1) {
94062306a36Sopenharmony_ci	    netdev_dbg(dev, "  MII transceiver at index %d, status %x\n",
94162306a36Sopenharmony_ci		       smc->mii_if.phy_id, j);
94262306a36Sopenharmony_ci	} else {
94362306a36Sopenharmony_ci	    netdev_notice(dev, "  No MII transceivers found!\n");
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci    }
94662306a36Sopenharmony_ci    return 0;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ciconfig_undo:
94962306a36Sopenharmony_ci    unregister_netdev(dev);
95062306a36Sopenharmony_ciconfig_failed:
95162306a36Sopenharmony_ci    smc91c92_release(link);
95262306a36Sopenharmony_ci    free_netdev(dev);
95362306a36Sopenharmony_ci    return -ENODEV;
95462306a36Sopenharmony_ci} /* smc91c92_config */
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_cistatic void smc91c92_release(struct pcmcia_device *link)
95762306a36Sopenharmony_ci{
95862306a36Sopenharmony_ci	dev_dbg(&link->dev, "smc91c92_release\n");
95962306a36Sopenharmony_ci	if (link->resource[2]->end) {
96062306a36Sopenharmony_ci		struct net_device *dev = link->priv;
96162306a36Sopenharmony_ci		struct smc_private *smc = netdev_priv(dev);
96262306a36Sopenharmony_ci		iounmap(smc->base);
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci	pcmcia_disable_device(link);
96562306a36Sopenharmony_ci}
96662306a36Sopenharmony_ci
96762306a36Sopenharmony_ci/*======================================================================
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci    MII interface support for SMC91cXX based cards
97062306a36Sopenharmony_ci======================================================================*/
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_ci#define MDIO_SHIFT_CLK		0x04
97362306a36Sopenharmony_ci#define MDIO_DATA_OUT		0x01
97462306a36Sopenharmony_ci#define MDIO_DIR_WRITE		0x08
97562306a36Sopenharmony_ci#define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE)
97662306a36Sopenharmony_ci#define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT)
97762306a36Sopenharmony_ci#define MDIO_DATA_READ		0x02
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_cistatic void mdio_sync(unsigned int addr)
98062306a36Sopenharmony_ci{
98162306a36Sopenharmony_ci    int bits;
98262306a36Sopenharmony_ci    for (bits = 0; bits < 32; bits++) {
98362306a36Sopenharmony_ci	outb(MDIO_DATA_WRITE1, addr);
98462306a36Sopenharmony_ci	outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr);
98562306a36Sopenharmony_ci    }
98662306a36Sopenharmony_ci}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_cistatic int mdio_read(struct net_device *dev, int phy_id, int loc)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci    unsigned int addr = dev->base_addr + MGMT;
99162306a36Sopenharmony_ci    u_int cmd = (0x06<<10)|(phy_id<<5)|loc;
99262306a36Sopenharmony_ci    int i, retval = 0;
99362306a36Sopenharmony_ci
99462306a36Sopenharmony_ci    mdio_sync(addr);
99562306a36Sopenharmony_ci    for (i = 13; i >= 0; i--) {
99662306a36Sopenharmony_ci	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
99762306a36Sopenharmony_ci	outb(dat, addr);
99862306a36Sopenharmony_ci	outb(dat | MDIO_SHIFT_CLK, addr);
99962306a36Sopenharmony_ci    }
100062306a36Sopenharmony_ci    for (i = 19; i > 0; i--) {
100162306a36Sopenharmony_ci	outb(0, addr);
100262306a36Sopenharmony_ci	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0);
100362306a36Sopenharmony_ci	outb(MDIO_SHIFT_CLK, addr);
100462306a36Sopenharmony_ci    }
100562306a36Sopenharmony_ci    return (retval>>1) & 0xffff;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic void mdio_write(struct net_device *dev, int phy_id, int loc, int value)
100962306a36Sopenharmony_ci{
101062306a36Sopenharmony_ci    unsigned int addr = dev->base_addr + MGMT;
101162306a36Sopenharmony_ci    u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value;
101262306a36Sopenharmony_ci    int i;
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci    mdio_sync(addr);
101562306a36Sopenharmony_ci    for (i = 31; i >= 0; i--) {
101662306a36Sopenharmony_ci	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0;
101762306a36Sopenharmony_ci	outb(dat, addr);
101862306a36Sopenharmony_ci	outb(dat | MDIO_SHIFT_CLK, addr);
101962306a36Sopenharmony_ci    }
102062306a36Sopenharmony_ci    for (i = 1; i >= 0; i--) {
102162306a36Sopenharmony_ci	outb(0, addr);
102262306a36Sopenharmony_ci	outb(MDIO_SHIFT_CLK, addr);
102362306a36Sopenharmony_ci    }
102462306a36Sopenharmony_ci}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci/*======================================================================
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci    The driver core code, most of which should be common with a
102962306a36Sopenharmony_ci    non-PCMCIA implementation.
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci======================================================================*/
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci#ifdef PCMCIA_DEBUG
103462306a36Sopenharmony_cistatic void smc_dump(struct net_device *dev)
103562306a36Sopenharmony_ci{
103662306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
103762306a36Sopenharmony_ci    u_short i, w, save;
103862306a36Sopenharmony_ci    save = inw(ioaddr + BANK_SELECT);
103962306a36Sopenharmony_ci    for (w = 0; w < 4; w++) {
104062306a36Sopenharmony_ci	SMC_SELECT_BANK(w);
104162306a36Sopenharmony_ci	netdev_dbg(dev, "bank %d: ", w);
104262306a36Sopenharmony_ci	for (i = 0; i < 14; i += 2)
104362306a36Sopenharmony_ci	    pr_cont(" %04x", inw(ioaddr + i));
104462306a36Sopenharmony_ci	pr_cont("\n");
104562306a36Sopenharmony_ci    }
104662306a36Sopenharmony_ci    outw(save, ioaddr + BANK_SELECT);
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci#endif
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_cistatic int smc_open(struct net_device *dev)
105162306a36Sopenharmony_ci{
105262306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
105362306a36Sopenharmony_ci    struct pcmcia_device *link = smc->p_dev;
105462306a36Sopenharmony_ci
105562306a36Sopenharmony_ci    dev_dbg(&link->dev, "%s: smc_open(%p), ID/Window %4.4x.\n",
105662306a36Sopenharmony_ci	  dev->name, dev, inw(dev->base_addr + BANK_SELECT));
105762306a36Sopenharmony_ci#ifdef PCMCIA_DEBUG
105862306a36Sopenharmony_ci    smc_dump(dev);
105962306a36Sopenharmony_ci#endif
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci    /* Check that the PCMCIA card is still here. */
106262306a36Sopenharmony_ci    if (!pcmcia_dev_present(link))
106362306a36Sopenharmony_ci	return -ENODEV;
106462306a36Sopenharmony_ci    /* Physical device present signature. */
106562306a36Sopenharmony_ci    if (check_sig(link) < 0) {
106662306a36Sopenharmony_ci	netdev_info(dev, "Yikes!  Bad chip signature!\n");
106762306a36Sopenharmony_ci	return -ENODEV;
106862306a36Sopenharmony_ci    }
106962306a36Sopenharmony_ci    link->open++;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci    netif_start_queue(dev);
107262306a36Sopenharmony_ci    smc->saved_skb = NULL;
107362306a36Sopenharmony_ci    smc->packets_waiting = 0;
107462306a36Sopenharmony_ci
107562306a36Sopenharmony_ci    smc_reset(dev);
107662306a36Sopenharmony_ci    timer_setup(&smc->media, media_check, 0);
107762306a36Sopenharmony_ci    mod_timer(&smc->media, jiffies + HZ);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci    return 0;
108062306a36Sopenharmony_ci} /* smc_open */
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci/*====================================================================*/
108362306a36Sopenharmony_ci
108462306a36Sopenharmony_cistatic int smc_close(struct net_device *dev)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
108762306a36Sopenharmony_ci    struct pcmcia_device *link = smc->p_dev;
108862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci    dev_dbg(&link->dev, "%s: smc_close(), status %4.4x.\n",
109162306a36Sopenharmony_ci	  dev->name, inw(ioaddr + BANK_SELECT));
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci    netif_stop_queue(dev);
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci    /* Shut off all interrupts, and turn off the Tx and Rx sections.
109662306a36Sopenharmony_ci       Don't bother to check for chip present. */
109762306a36Sopenharmony_ci    SMC_SELECT_BANK(2);	/* Nominally paranoia, but do no assume... */
109862306a36Sopenharmony_ci    outw(0, ioaddr + INTERRUPT);
109962306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
110062306a36Sopenharmony_ci    mask_bits(0xff00, ioaddr + RCR);
110162306a36Sopenharmony_ci    mask_bits(0xff00, ioaddr + TCR);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci    /* Put the chip into power-down mode. */
110462306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
110562306a36Sopenharmony_ci    outw(CTL_POWERDOWN, ioaddr + CONTROL );
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_ci    link->open--;
110862306a36Sopenharmony_ci    del_timer_sync(&smc->media);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci    return 0;
111162306a36Sopenharmony_ci} /* smc_close */
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci/*======================================================================
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci   Transfer a packet to the hardware and trigger the packet send.
111662306a36Sopenharmony_ci   This may be called at either from either the Tx queue code
111762306a36Sopenharmony_ci   or the interrupt handler.
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci======================================================================*/
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_cistatic void smc_hardware_send_packet(struct net_device * dev)
112262306a36Sopenharmony_ci{
112362306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
112462306a36Sopenharmony_ci    struct sk_buff *skb = smc->saved_skb;
112562306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
112662306a36Sopenharmony_ci    u_char packet_no;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci    if (!skb) {
112962306a36Sopenharmony_ci	netdev_err(dev, "In XMIT with no packet to send\n");
113062306a36Sopenharmony_ci	return;
113162306a36Sopenharmony_ci    }
113262306a36Sopenharmony_ci
113362306a36Sopenharmony_ci    /* There should be a packet slot waiting. */
113462306a36Sopenharmony_ci    packet_no = inw(ioaddr + PNR_ARR) >> 8;
113562306a36Sopenharmony_ci    if (packet_no & 0x80) {
113662306a36Sopenharmony_ci	/* If not, there is a hardware problem!  Likely an ejected card. */
113762306a36Sopenharmony_ci	netdev_warn(dev, "hardware Tx buffer allocation failed, status %#2.2x\n",
113862306a36Sopenharmony_ci		    packet_no);
113962306a36Sopenharmony_ci	dev_kfree_skb_irq(skb);
114062306a36Sopenharmony_ci	smc->saved_skb = NULL;
114162306a36Sopenharmony_ci	netif_start_queue(dev);
114262306a36Sopenharmony_ci	return;
114362306a36Sopenharmony_ci    }
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_ci    dev->stats.tx_bytes += skb->len;
114662306a36Sopenharmony_ci    /* The card should use the just-allocated buffer. */
114762306a36Sopenharmony_ci    outw(packet_no, ioaddr + PNR_ARR);
114862306a36Sopenharmony_ci    /* point to the beginning of the packet */
114962306a36Sopenharmony_ci    outw(PTR_AUTOINC , ioaddr + POINTER);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci    /* Send the packet length (+6 for status, length and ctl byte)
115262306a36Sopenharmony_ci       and the status word (set to zeros). */
115362306a36Sopenharmony_ci    {
115462306a36Sopenharmony_ci	u_char *buf = skb->data;
115562306a36Sopenharmony_ci	u_int length = skb->len; /* The chip will pad to ethernet min. */
115662306a36Sopenharmony_ci
115762306a36Sopenharmony_ci	netdev_dbg(dev, "Trying to xmit packet of length %d\n", length);
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_ci	/* send the packet length: +6 for status word, length, and ctl */
116062306a36Sopenharmony_ci	outw(0, ioaddr + DATA_1);
116162306a36Sopenharmony_ci	outw(length + 6, ioaddr + DATA_1);
116262306a36Sopenharmony_ci	outsw(ioaddr + DATA_1, buf, length >> 1);
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	/* The odd last byte, if there is one, goes in the control word. */
116562306a36Sopenharmony_ci	outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1);
116662306a36Sopenharmony_ci    }
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci    /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */
116962306a36Sopenharmony_ci    outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) |
117062306a36Sopenharmony_ci	 (inw(ioaddr + INTERRUPT) & 0xff00),
117162306a36Sopenharmony_ci	 ioaddr + INTERRUPT);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci    /* The chip does the rest of the work. */
117462306a36Sopenharmony_ci    outw(MC_ENQUEUE , ioaddr + MMU_CMD);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci    smc->saved_skb = NULL;
117762306a36Sopenharmony_ci    dev_kfree_skb_irq(skb);
117862306a36Sopenharmony_ci    netif_trans_update(dev);
117962306a36Sopenharmony_ci    netif_start_queue(dev);
118062306a36Sopenharmony_ci}
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci/*====================================================================*/
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_cistatic void smc_tx_timeout(struct net_device *dev, unsigned int txqueue)
118562306a36Sopenharmony_ci{
118662306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
118762306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_ci    netdev_notice(dev, "transmit timed out, Tx_status %2.2x status %4.4x.\n",
119062306a36Sopenharmony_ci		  inw(ioaddr)&0xff, inw(ioaddr + 2));
119162306a36Sopenharmony_ci    dev->stats.tx_errors++;
119262306a36Sopenharmony_ci    smc_reset(dev);
119362306a36Sopenharmony_ci    netif_trans_update(dev); /* prevent tx timeout */
119462306a36Sopenharmony_ci    smc->saved_skb = NULL;
119562306a36Sopenharmony_ci    netif_wake_queue(dev);
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic netdev_tx_t smc_start_xmit(struct sk_buff *skb,
119962306a36Sopenharmony_ci					struct net_device *dev)
120062306a36Sopenharmony_ci{
120162306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
120262306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
120362306a36Sopenharmony_ci    u_short num_pages;
120462306a36Sopenharmony_ci    short time_out, ir;
120562306a36Sopenharmony_ci    unsigned long flags;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci    netif_stop_queue(dev);
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci    netdev_dbg(dev, "smc_start_xmit(length = %d) called, status %04x\n",
121062306a36Sopenharmony_ci	       skb->len, inw(ioaddr + 2));
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci    if (smc->saved_skb) {
121362306a36Sopenharmony_ci	/* THIS SHOULD NEVER HAPPEN. */
121462306a36Sopenharmony_ci	dev->stats.tx_aborted_errors++;
121562306a36Sopenharmony_ci	netdev_dbg(dev, "Internal error -- sent packet while busy\n");
121662306a36Sopenharmony_ci	return NETDEV_TX_BUSY;
121762306a36Sopenharmony_ci    }
121862306a36Sopenharmony_ci    smc->saved_skb = skb;
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci    num_pages = skb->len >> 8;
122162306a36Sopenharmony_ci
122262306a36Sopenharmony_ci    if (num_pages > 7) {
122362306a36Sopenharmony_ci	netdev_err(dev, "Far too big packet error: %d pages\n", num_pages);
122462306a36Sopenharmony_ci	dev_kfree_skb (skb);
122562306a36Sopenharmony_ci	smc->saved_skb = NULL;
122662306a36Sopenharmony_ci	dev->stats.tx_dropped++;
122762306a36Sopenharmony_ci	return NETDEV_TX_OK;		/* Do not re-queue this packet. */
122862306a36Sopenharmony_ci    }
122962306a36Sopenharmony_ci    /* A packet is now waiting. */
123062306a36Sopenharmony_ci    smc->packets_waiting++;
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci    spin_lock_irqsave(&smc->lock, flags);
123362306a36Sopenharmony_ci    SMC_SELECT_BANK(2);	/* Paranoia, we should always be in window 2 */
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci    /* need MC_RESET to keep the memory consistent. errata? */
123662306a36Sopenharmony_ci    if (smc->rx_ovrn) {
123762306a36Sopenharmony_ci	outw(MC_RESET, ioaddr + MMU_CMD);
123862306a36Sopenharmony_ci	smc->rx_ovrn = 0;
123962306a36Sopenharmony_ci    }
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci    /* Allocate the memory; send the packet now if we win. */
124262306a36Sopenharmony_ci    outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD);
124362306a36Sopenharmony_ci    for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) {
124462306a36Sopenharmony_ci	ir = inw(ioaddr+INTERRUPT);
124562306a36Sopenharmony_ci	if (ir & IM_ALLOC_INT) {
124662306a36Sopenharmony_ci	    /* Acknowledge the interrupt, send the packet. */
124762306a36Sopenharmony_ci	    outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT);
124862306a36Sopenharmony_ci	    smc_hardware_send_packet(dev);	/* Send the packet now.. */
124962306a36Sopenharmony_ci	    spin_unlock_irqrestore(&smc->lock, flags);
125062306a36Sopenharmony_ci	    return NETDEV_TX_OK;
125162306a36Sopenharmony_ci	}
125262306a36Sopenharmony_ci    }
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci    /* Otherwise defer until the Tx-space-allocated interrupt. */
125562306a36Sopenharmony_ci    netdev_dbg(dev, "memory allocation deferred.\n");
125662306a36Sopenharmony_ci    outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT);
125762306a36Sopenharmony_ci    spin_unlock_irqrestore(&smc->lock, flags);
125862306a36Sopenharmony_ci
125962306a36Sopenharmony_ci    return NETDEV_TX_OK;
126062306a36Sopenharmony_ci}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci/*======================================================================
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci    Handle a Tx anomalous event.  Entered while in Window 2.
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci======================================================================*/
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_cistatic void smc_tx_err(struct net_device * dev)
126962306a36Sopenharmony_ci{
127062306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
127162306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
127262306a36Sopenharmony_ci    int saved_packet = inw(ioaddr + PNR_ARR) & 0xff;
127362306a36Sopenharmony_ci    int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f;
127462306a36Sopenharmony_ci    int tx_status;
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci    /* select this as the packet to read from */
127762306a36Sopenharmony_ci    outw(packet_no, ioaddr + PNR_ARR);
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci    /* read the first word from this packet */
128062306a36Sopenharmony_ci    outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER);
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci    tx_status = inw(ioaddr + DATA_1);
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci    dev->stats.tx_errors++;
128562306a36Sopenharmony_ci    if (tx_status & TS_LOSTCAR) dev->stats.tx_carrier_errors++;
128662306a36Sopenharmony_ci    if (tx_status & TS_LATCOL)  dev->stats.tx_window_errors++;
128762306a36Sopenharmony_ci    if (tx_status & TS_16COL) {
128862306a36Sopenharmony_ci	dev->stats.tx_aborted_errors++;
128962306a36Sopenharmony_ci	smc->tx_err++;
129062306a36Sopenharmony_ci    }
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci    if (tx_status & TS_SUCCESS) {
129362306a36Sopenharmony_ci	netdev_notice(dev, "Successful packet caused error interrupt?\n");
129462306a36Sopenharmony_ci    }
129562306a36Sopenharmony_ci    /* re-enable transmit */
129662306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
129762306a36Sopenharmony_ci    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
129862306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci    outw(MC_FREEPKT, ioaddr + MMU_CMD); 	/* Free the packet memory. */
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci    /* one less packet waiting for me */
130362306a36Sopenharmony_ci    smc->packets_waiting--;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci    outw(saved_packet, ioaddr + PNR_ARR);
130662306a36Sopenharmony_ci}
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci/*====================================================================*/
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_cistatic void smc_eph_irq(struct net_device *dev)
131162306a36Sopenharmony_ci{
131262306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
131362306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
131462306a36Sopenharmony_ci    u_short card_stats, ephs;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
131762306a36Sopenharmony_ci    ephs = inw(ioaddr + EPH);
131862306a36Sopenharmony_ci    netdev_dbg(dev, "Ethernet protocol handler interrupt, status %4.4x.\n",
131962306a36Sopenharmony_ci	       ephs);
132062306a36Sopenharmony_ci    /* Could be a counter roll-over warning: update stats. */
132162306a36Sopenharmony_ci    card_stats = inw(ioaddr + COUNTER);
132262306a36Sopenharmony_ci    /* single collisions */
132362306a36Sopenharmony_ci    dev->stats.collisions += card_stats & 0xF;
132462306a36Sopenharmony_ci    card_stats >>= 4;
132562306a36Sopenharmony_ci    /* multiple collisions */
132662306a36Sopenharmony_ci    dev->stats.collisions += card_stats & 0xF;
132762306a36Sopenharmony_ci#if 0 		/* These are for when linux supports these statistics */
132862306a36Sopenharmony_ci    card_stats >>= 4;			/* deferred */
132962306a36Sopenharmony_ci    card_stats >>= 4;			/* excess deferred */
133062306a36Sopenharmony_ci#endif
133162306a36Sopenharmony_ci    /* If we had a transmit error we must re-enable the transmitter. */
133262306a36Sopenharmony_ci    outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR);
133362306a36Sopenharmony_ci
133462306a36Sopenharmony_ci    /* Clear a link error interrupt. */
133562306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
133662306a36Sopenharmony_ci    outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL);
133762306a36Sopenharmony_ci    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
133862306a36Sopenharmony_ci	 ioaddr + CONTROL);
133962306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/*====================================================================*/
134362306a36Sopenharmony_ci
134462306a36Sopenharmony_cistatic irqreturn_t smc_interrupt(int irq, void *dev_id)
134562306a36Sopenharmony_ci{
134662306a36Sopenharmony_ci    struct net_device *dev = dev_id;
134762306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
134862306a36Sopenharmony_ci    unsigned int ioaddr;
134962306a36Sopenharmony_ci    u_short saved_bank, saved_pointer, mask, status;
135062306a36Sopenharmony_ci    unsigned int handled = 1;
135162306a36Sopenharmony_ci    char bogus_cnt = INTR_WORK;		/* Work we are willing to do. */
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci    if (!netif_device_present(dev))
135462306a36Sopenharmony_ci	return IRQ_NONE;
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci    ioaddr = dev->base_addr;
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci    netdev_dbg(dev, "SMC91c92 interrupt %d at %#x.\n",
135962306a36Sopenharmony_ci	       irq, ioaddr);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci    spin_lock(&smc->lock);
136262306a36Sopenharmony_ci    smc->watchdog = 0;
136362306a36Sopenharmony_ci    saved_bank = inw(ioaddr + BANK_SELECT);
136462306a36Sopenharmony_ci    if ((saved_bank & 0xff00) != 0x3300) {
136562306a36Sopenharmony_ci	/* The device does not exist -- the card could be off-line, or
136662306a36Sopenharmony_ci	   maybe it has been ejected. */
136762306a36Sopenharmony_ci	netdev_dbg(dev, "SMC91c92 interrupt %d for non-existent/ejected device.\n",
136862306a36Sopenharmony_ci		   irq);
136962306a36Sopenharmony_ci	handled = 0;
137062306a36Sopenharmony_ci	goto irq_done;
137162306a36Sopenharmony_ci    }
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
137462306a36Sopenharmony_ci    saved_pointer = inw(ioaddr + POINTER);
137562306a36Sopenharmony_ci    mask = inw(ioaddr + INTERRUPT) >> 8;
137662306a36Sopenharmony_ci    /* clear all interrupts */
137762306a36Sopenharmony_ci    outw(0, ioaddr + INTERRUPT);
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci    do { /* read the status flag, and mask it */
138062306a36Sopenharmony_ci	status = inw(ioaddr + INTERRUPT) & 0xff;
138162306a36Sopenharmony_ci	netdev_dbg(dev, "Status is %#2.2x (mask %#2.2x).\n",
138262306a36Sopenharmony_ci		   status, mask);
138362306a36Sopenharmony_ci	if ((status & mask) == 0) {
138462306a36Sopenharmony_ci	    if (bogus_cnt == INTR_WORK)
138562306a36Sopenharmony_ci		handled = 0;
138662306a36Sopenharmony_ci	    break;
138762306a36Sopenharmony_ci	}
138862306a36Sopenharmony_ci	if (status & IM_RCV_INT) {
138962306a36Sopenharmony_ci	    /* Got a packet(s). */
139062306a36Sopenharmony_ci	    smc_rx(dev);
139162306a36Sopenharmony_ci	}
139262306a36Sopenharmony_ci	if (status & IM_TX_INT) {
139362306a36Sopenharmony_ci	    smc_tx_err(dev);
139462306a36Sopenharmony_ci	    outw(IM_TX_INT, ioaddr + INTERRUPT);
139562306a36Sopenharmony_ci	}
139662306a36Sopenharmony_ci	status &= mask;
139762306a36Sopenharmony_ci	if (status & IM_TX_EMPTY_INT) {
139862306a36Sopenharmony_ci	    outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT);
139962306a36Sopenharmony_ci	    mask &= ~IM_TX_EMPTY_INT;
140062306a36Sopenharmony_ci	    dev->stats.tx_packets += smc->packets_waiting;
140162306a36Sopenharmony_ci	    smc->packets_waiting = 0;
140262306a36Sopenharmony_ci	}
140362306a36Sopenharmony_ci	if (status & IM_ALLOC_INT) {
140462306a36Sopenharmony_ci	    /* Clear this interrupt so it doesn't happen again */
140562306a36Sopenharmony_ci	    mask &= ~IM_ALLOC_INT;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	    smc_hardware_send_packet(dev);
140862306a36Sopenharmony_ci
140962306a36Sopenharmony_ci	    /* enable xmit interrupts based on this */
141062306a36Sopenharmony_ci	    mask |= (IM_TX_EMPTY_INT | IM_TX_INT);
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	    /* and let the card send more packets to me */
141362306a36Sopenharmony_ci	    netif_wake_queue(dev);
141462306a36Sopenharmony_ci	}
141562306a36Sopenharmony_ci	if (status & IM_RX_OVRN_INT) {
141662306a36Sopenharmony_ci	    dev->stats.rx_errors++;
141762306a36Sopenharmony_ci	    dev->stats.rx_fifo_errors++;
141862306a36Sopenharmony_ci	    if (smc->duplex)
141962306a36Sopenharmony_ci		smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */
142062306a36Sopenharmony_ci	    outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT);
142162306a36Sopenharmony_ci	}
142262306a36Sopenharmony_ci	if (status & IM_EPH_INT)
142362306a36Sopenharmony_ci	    smc_eph_irq(dev);
142462306a36Sopenharmony_ci    } while (--bogus_cnt);
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci    netdev_dbg(dev, "  Restoring saved registers mask %2.2x bank %4.4x pointer %4.4x.\n",
142762306a36Sopenharmony_ci	       mask, saved_bank, saved_pointer);
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci    /* restore state register */
143062306a36Sopenharmony_ci    outw((mask<<8), ioaddr + INTERRUPT);
143162306a36Sopenharmony_ci    outw(saved_pointer, ioaddr + POINTER);
143262306a36Sopenharmony_ci    SMC_SELECT_BANK(saved_bank);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci    netdev_dbg(dev, "Exiting interrupt IRQ%d.\n", irq);
143562306a36Sopenharmony_ci
143662306a36Sopenharmony_ciirq_done:
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci    if ((smc->manfid == MANFID_OSITECH) &&
143962306a36Sopenharmony_ci	(smc->cardid != PRODID_OSITECH_SEVEN)) {
144062306a36Sopenharmony_ci	/* Retrigger interrupt if needed */
144162306a36Sopenharmony_ci	mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR);
144262306a36Sopenharmony_ci	set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR);
144362306a36Sopenharmony_ci    }
144462306a36Sopenharmony_ci    if (smc->manfid == MANFID_MOTOROLA) {
144562306a36Sopenharmony_ci	u_char cor;
144662306a36Sopenharmony_ci	cor = readb(smc->base + MOT_UART + CISREG_COR);
144762306a36Sopenharmony_ci	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR);
144862306a36Sopenharmony_ci	writeb(cor, smc->base + MOT_UART + CISREG_COR);
144962306a36Sopenharmony_ci	cor = readb(smc->base + MOT_LAN + CISREG_COR);
145062306a36Sopenharmony_ci	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR);
145162306a36Sopenharmony_ci	writeb(cor, smc->base + MOT_LAN + CISREG_COR);
145262306a36Sopenharmony_ci    }
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci    if ((smc->base != NULL) &&  /* Megahertz MFC's */
145562306a36Sopenharmony_ci	(smc->manfid == MANFID_MEGAHERTZ) &&
145662306a36Sopenharmony_ci	(smc->cardid == PRODID_MEGAHERTZ_EM3288)) {
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	u_char tmp;
145962306a36Sopenharmony_ci	tmp = readb(smc->base+MEGAHERTZ_ISR);
146062306a36Sopenharmony_ci	tmp = readb(smc->base+MEGAHERTZ_ISR);
146162306a36Sopenharmony_ci
146262306a36Sopenharmony_ci	/* Retrigger interrupt if needed */
146362306a36Sopenharmony_ci	writeb(tmp, smc->base + MEGAHERTZ_ISR);
146462306a36Sopenharmony_ci	writeb(tmp, smc->base + MEGAHERTZ_ISR);
146562306a36Sopenharmony_ci    }
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci    spin_unlock(&smc->lock);
146862306a36Sopenharmony_ci    return IRQ_RETVAL(handled);
146962306a36Sopenharmony_ci}
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci/*====================================================================*/
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic void smc_rx(struct net_device *dev)
147462306a36Sopenharmony_ci{
147562306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
147662306a36Sopenharmony_ci    int rx_status;
147762306a36Sopenharmony_ci    int packet_length;	/* Caution: not frame length, rather words
147862306a36Sopenharmony_ci			   to transfer from the chip. */
147962306a36Sopenharmony_ci
148062306a36Sopenharmony_ci    /* Assertion: we are in Window 2. */
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci    if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) {
148362306a36Sopenharmony_ci	netdev_err(dev, "smc_rx() with nothing on Rx FIFO\n");
148462306a36Sopenharmony_ci	return;
148562306a36Sopenharmony_ci    }
148662306a36Sopenharmony_ci
148762306a36Sopenharmony_ci    /*  Reset the read pointer, and read the status and packet length. */
148862306a36Sopenharmony_ci    outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER);
148962306a36Sopenharmony_ci    rx_status = inw(ioaddr + DATA_1);
149062306a36Sopenharmony_ci    packet_length = inw(ioaddr + DATA_1) & 0x07ff;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci    netdev_dbg(dev, "Receive status %4.4x length %d.\n",
149362306a36Sopenharmony_ci	       rx_status, packet_length);
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci    if (!(rx_status & RS_ERRORS)) {
149662306a36Sopenharmony_ci	/* do stuff to make a new packet */
149762306a36Sopenharmony_ci	struct sk_buff *skb;
149862306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	/* Note: packet_length adds 5 or 6 extra bytes here! */
150162306a36Sopenharmony_ci	skb = netdev_alloc_skb(dev, packet_length+2);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (skb == NULL) {
150462306a36Sopenharmony_ci	    netdev_dbg(dev, "Low memory, packet dropped.\n");
150562306a36Sopenharmony_ci	    dev->stats.rx_dropped++;
150662306a36Sopenharmony_ci	    outw(MC_RELEASE, ioaddr + MMU_CMD);
150762306a36Sopenharmony_ci	    return;
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6);
151162306a36Sopenharmony_ci	skb_reserve(skb, 2);
151262306a36Sopenharmony_ci	insw(ioaddr+DATA_1, skb_put(skb, packet_length),
151362306a36Sopenharmony_ci	     (packet_length+1)>>1);
151462306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, dev);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	netif_rx(skb);
151762306a36Sopenharmony_ci	smc->last_rx = jiffies;
151862306a36Sopenharmony_ci	dev->stats.rx_packets++;
151962306a36Sopenharmony_ci	dev->stats.rx_bytes += packet_length;
152062306a36Sopenharmony_ci	if (rx_status & RS_MULTICAST)
152162306a36Sopenharmony_ci	    dev->stats.multicast++;
152262306a36Sopenharmony_ci    } else {
152362306a36Sopenharmony_ci	/* error ... */
152462306a36Sopenharmony_ci	dev->stats.rx_errors++;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (rx_status & RS_ALGNERR)  dev->stats.rx_frame_errors++;
152762306a36Sopenharmony_ci	if (rx_status & (RS_TOOSHORT | RS_TOOLONG))
152862306a36Sopenharmony_ci	    dev->stats.rx_length_errors++;
152962306a36Sopenharmony_ci	if (rx_status & RS_BADCRC)	dev->stats.rx_crc_errors++;
153062306a36Sopenharmony_ci    }
153162306a36Sopenharmony_ci    /* Let the MMU free the memory of this packet. */
153262306a36Sopenharmony_ci    outw(MC_RELEASE, ioaddr + MMU_CMD);
153362306a36Sopenharmony_ci}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_ci/*======================================================================
153662306a36Sopenharmony_ci
153762306a36Sopenharmony_ci    Set the receive mode.
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci    This routine is used by both the protocol level to notify us of
154062306a36Sopenharmony_ci    promiscuous/multicast mode changes, and by the open/reset code to
154162306a36Sopenharmony_ci    initialize the Rx registers.  We always set the multicast list and
154262306a36Sopenharmony_ci    leave the receiver running.
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci======================================================================*/
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_cistatic void set_rx_mode(struct net_device *dev)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
154962306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
155062306a36Sopenharmony_ci    unsigned char multicast_table[8];
155162306a36Sopenharmony_ci    unsigned long flags;
155262306a36Sopenharmony_ci    u_short rx_cfg_setting;
155362306a36Sopenharmony_ci    int i;
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci    memset(multicast_table, 0, sizeof(multicast_table));
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_ci    if (dev->flags & IFF_PROMISC) {
155862306a36Sopenharmony_ci	rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti;
155962306a36Sopenharmony_ci    } else if (dev->flags & IFF_ALLMULTI)
156062306a36Sopenharmony_ci	rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti;
156162306a36Sopenharmony_ci    else {
156262306a36Sopenharmony_ci	if (!netdev_mc_empty(dev)) {
156362306a36Sopenharmony_ci	    struct netdev_hw_addr *ha;
156462306a36Sopenharmony_ci
156562306a36Sopenharmony_ci	    netdev_for_each_mc_addr(ha, dev) {
156662306a36Sopenharmony_ci		u_int position = ether_crc(6, ha->addr);
156762306a36Sopenharmony_ci		multicast_table[position >> 29] |= 1 << ((position >> 26) & 7);
156862306a36Sopenharmony_ci	    }
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci	rx_cfg_setting = RxStripCRC | RxEnable;
157162306a36Sopenharmony_ci    }
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci    /* Load MC table and Rx setting into the chip without interrupts. */
157462306a36Sopenharmony_ci    spin_lock_irqsave(&smc->lock, flags);
157562306a36Sopenharmony_ci    SMC_SELECT_BANK(3);
157662306a36Sopenharmony_ci    for (i = 0; i < 8; i++)
157762306a36Sopenharmony_ci	outb(multicast_table[i], ioaddr + MULTICAST0 + i);
157862306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
157962306a36Sopenharmony_ci    outw(rx_cfg_setting, ioaddr + RCR);
158062306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
158162306a36Sopenharmony_ci    spin_unlock_irqrestore(&smc->lock, flags);
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci/*======================================================================
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ci    Senses when a card's config changes. Here, it's coax or TP.
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci======================================================================*/
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_cistatic int s9k_config(struct net_device *dev, struct ifmap *map)
159162306a36Sopenharmony_ci{
159262306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
159362306a36Sopenharmony_ci    if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) {
159462306a36Sopenharmony_ci	if (smc->cfg & CFG_MII_SELECT)
159562306a36Sopenharmony_ci	    return -EOPNOTSUPP;
159662306a36Sopenharmony_ci	else if (map->port > 2)
159762306a36Sopenharmony_ci	    return -EINVAL;
159862306a36Sopenharmony_ci	dev->if_port = map->port;
159962306a36Sopenharmony_ci	netdev_info(dev, "switched to %s port\n", if_names[dev->if_port]);
160062306a36Sopenharmony_ci	smc_reset(dev);
160162306a36Sopenharmony_ci    }
160262306a36Sopenharmony_ci    return 0;
160362306a36Sopenharmony_ci}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci/*======================================================================
160662306a36Sopenharmony_ci
160762306a36Sopenharmony_ci    Reset the chip, reloading every register that might be corrupted.
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_ci======================================================================*/
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci/*
161262306a36Sopenharmony_ci  Set transceiver type, perhaps to something other than what the user
161362306a36Sopenharmony_ci  specified in dev->if_port.
161462306a36Sopenharmony_ci*/
161562306a36Sopenharmony_cistatic void smc_set_xcvr(struct net_device *dev, int if_port)
161662306a36Sopenharmony_ci{
161762306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
161862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
161962306a36Sopenharmony_ci    u_short saved_bank;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci    saved_bank = inw(ioaddr + BANK_SELECT);
162262306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
162362306a36Sopenharmony_ci    if (if_port == 2) {
162462306a36Sopenharmony_ci	outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG);
162562306a36Sopenharmony_ci	if ((smc->manfid == MANFID_OSITECH) &&
162662306a36Sopenharmony_ci	    (smc->cardid != PRODID_OSITECH_SEVEN))
162762306a36Sopenharmony_ci	    set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
162862306a36Sopenharmony_ci	smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002);
162962306a36Sopenharmony_ci    } else {
163062306a36Sopenharmony_ci	outw(smc->cfg, ioaddr + CONFIG);
163162306a36Sopenharmony_ci	if ((smc->manfid == MANFID_OSITECH) &&
163262306a36Sopenharmony_ci	    (smc->cardid != PRODID_OSITECH_SEVEN))
163362306a36Sopenharmony_ci	    mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR);
163462306a36Sopenharmony_ci	smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001);
163562306a36Sopenharmony_ci    }
163662306a36Sopenharmony_ci    SMC_SELECT_BANK(saved_bank);
163762306a36Sopenharmony_ci}
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_cistatic void smc_reset(struct net_device *dev)
164062306a36Sopenharmony_ci{
164162306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
164262306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
164362306a36Sopenharmony_ci    int i;
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci    netdev_dbg(dev, "smc91c92 reset called.\n");
164662306a36Sopenharmony_ci
164762306a36Sopenharmony_ci    /* The first interaction must be a write to bring the chip out
164862306a36Sopenharmony_ci       of sleep mode. */
164962306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
165062306a36Sopenharmony_ci    /* Reset the chip. */
165162306a36Sopenharmony_ci    outw(RCR_SOFTRESET, ioaddr + RCR);
165262306a36Sopenharmony_ci    udelay(10);
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci    /* Clear the transmit and receive configuration registers. */
165562306a36Sopenharmony_ci    outw(RCR_CLEAR, ioaddr + RCR);
165662306a36Sopenharmony_ci    outw(TCR_CLEAR, ioaddr + TCR);
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci    /* Set the Window 1 control, configuration and station addr registers.
165962306a36Sopenharmony_ci       No point in writing the I/O base register ;-> */
166062306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
166162306a36Sopenharmony_ci    /* Automatically release successfully transmitted packets,
166262306a36Sopenharmony_ci       Accept link errors, counter and Tx error interrupts. */
166362306a36Sopenharmony_ci    outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
166462306a36Sopenharmony_ci	 ioaddr + CONTROL);
166562306a36Sopenharmony_ci    smc_set_xcvr(dev, dev->if_port);
166662306a36Sopenharmony_ci    if ((smc->manfid == MANFID_OSITECH) &&
166762306a36Sopenharmony_ci	(smc->cardid != PRODID_OSITECH_SEVEN))
166862306a36Sopenharmony_ci	outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) |
166962306a36Sopenharmony_ci	     (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00),
167062306a36Sopenharmony_ci	     ioaddr - 0x10 + OSITECH_AUI_PWR);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci    /* Fill in the physical address.  The databook is wrong about the order! */
167362306a36Sopenharmony_ci    for (i = 0; i < 6; i += 2)
167462306a36Sopenharmony_ci	outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i],
167562306a36Sopenharmony_ci	     ioaddr + ADDR0 + i);
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci    /* Reset the MMU */
167862306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
167962306a36Sopenharmony_ci    outw(MC_RESET, ioaddr + MMU_CMD);
168062306a36Sopenharmony_ci    outw(0, ioaddr + INTERRUPT);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci    /* Re-enable the chip. */
168362306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
168462306a36Sopenharmony_ci    outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) |
168562306a36Sopenharmony_ci	 TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR);
168662306a36Sopenharmony_ci    set_rx_mode(dev);
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_ci    if (smc->cfg & CFG_MII_SELECT) {
168962306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	/* Reset MII */
169262306a36Sopenharmony_ci	mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	/* Advertise 100F, 100H, 10F, 10H */
169562306a36Sopenharmony_ci	mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1);
169662306a36Sopenharmony_ci
169762306a36Sopenharmony_ci	/* Restart MII autonegotiation */
169862306a36Sopenharmony_ci	mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000);
169962306a36Sopenharmony_ci	mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200);
170062306a36Sopenharmony_ci    }
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci    /* Enable interrupts. */
170362306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
170462306a36Sopenharmony_ci    outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8,
170562306a36Sopenharmony_ci	 ioaddr + INTERRUPT);
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ci
170862306a36Sopenharmony_ci/*======================================================================
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci    Media selection timer routine
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_ci======================================================================*/
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_cistatic void media_check(struct timer_list *t)
171562306a36Sopenharmony_ci{
171662306a36Sopenharmony_ci    struct smc_private *smc = from_timer(smc, t, media);
171762306a36Sopenharmony_ci    struct net_device *dev = smc->mii_if.dev;
171862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
171962306a36Sopenharmony_ci    u_short i, media, saved_bank;
172062306a36Sopenharmony_ci    u_short link;
172162306a36Sopenharmony_ci    unsigned long flags;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci    spin_lock_irqsave(&smc->lock, flags);
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci    saved_bank = inw(ioaddr + BANK_SELECT);
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci    if (!netif_device_present(dev))
172862306a36Sopenharmony_ci	goto reschedule;
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci    SMC_SELECT_BANK(2);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci    /* need MC_RESET to keep the memory consistent. errata? */
173362306a36Sopenharmony_ci    if (smc->rx_ovrn) {
173462306a36Sopenharmony_ci	outw(MC_RESET, ioaddr + MMU_CMD);
173562306a36Sopenharmony_ci	smc->rx_ovrn = 0;
173662306a36Sopenharmony_ci    }
173762306a36Sopenharmony_ci    i = inw(ioaddr + INTERRUPT);
173862306a36Sopenharmony_ci    SMC_SELECT_BANK(0);
173962306a36Sopenharmony_ci    media = inw(ioaddr + EPH) & EPH_LINK_OK;
174062306a36Sopenharmony_ci    SMC_SELECT_BANK(1);
174162306a36Sopenharmony_ci    media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci    SMC_SELECT_BANK(saved_bank);
174462306a36Sopenharmony_ci    spin_unlock_irqrestore(&smc->lock, flags);
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci    /* Check for pending interrupt with watchdog flag set: with
174762306a36Sopenharmony_ci       this, we can limp along even if the interrupt is blocked */
174862306a36Sopenharmony_ci    if (smc->watchdog++ && ((i>>8) & i)) {
174962306a36Sopenharmony_ci	if (!smc->fast_poll)
175062306a36Sopenharmony_ci	    netdev_info(dev, "interrupt(s) dropped!\n");
175162306a36Sopenharmony_ci	local_irq_save(flags);
175262306a36Sopenharmony_ci	smc_interrupt(dev->irq, dev);
175362306a36Sopenharmony_ci	local_irq_restore(flags);
175462306a36Sopenharmony_ci	smc->fast_poll = HZ;
175562306a36Sopenharmony_ci    }
175662306a36Sopenharmony_ci    if (smc->fast_poll) {
175762306a36Sopenharmony_ci	smc->fast_poll--;
175862306a36Sopenharmony_ci	smc->media.expires = jiffies + HZ/100;
175962306a36Sopenharmony_ci	add_timer(&smc->media);
176062306a36Sopenharmony_ci	return;
176162306a36Sopenharmony_ci    }
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci    spin_lock_irqsave(&smc->lock, flags);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci    saved_bank = inw(ioaddr + BANK_SELECT);
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ci    if (smc->cfg & CFG_MII_SELECT) {
176862306a36Sopenharmony_ci	if (smc->mii_if.phy_id < 0)
176962306a36Sopenharmony_ci	    goto reschedule;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
177262306a36Sopenharmony_ci	link = mdio_read(dev, smc->mii_if.phy_id, 1);
177362306a36Sopenharmony_ci	if (!link || (link == 0xffff)) {
177462306a36Sopenharmony_ci	    netdev_info(dev, "MII is missing!\n");
177562306a36Sopenharmony_ci	    smc->mii_if.phy_id = -1;
177662306a36Sopenharmony_ci	    goto reschedule;
177762306a36Sopenharmony_ci	}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_ci	link &= 0x0004;
178062306a36Sopenharmony_ci	if (link != smc->link_status) {
178162306a36Sopenharmony_ci	    u_short p = mdio_read(dev, smc->mii_if.phy_id, 5);
178262306a36Sopenharmony_ci	    netdev_info(dev, "%s link beat\n", link ? "found" : "lost");
178362306a36Sopenharmony_ci	    smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40))
178462306a36Sopenharmony_ci			   ? TCR_FDUPLX : 0);
178562306a36Sopenharmony_ci	    if (link) {
178662306a36Sopenharmony_ci		netdev_info(dev, "autonegotiation complete: "
178762306a36Sopenharmony_ci			    "%dbaseT-%cD selected\n",
178862306a36Sopenharmony_ci			    (p & 0x0180) ? 100 : 10, smc->duplex ? 'F' : 'H');
178962306a36Sopenharmony_ci	    }
179062306a36Sopenharmony_ci	    SMC_SELECT_BANK(0);
179162306a36Sopenharmony_ci	    outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR);
179262306a36Sopenharmony_ci	    smc->link_status = link;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci	goto reschedule;
179562306a36Sopenharmony_ci    }
179662306a36Sopenharmony_ci
179762306a36Sopenharmony_ci    /* Ignore collisions unless we've had no rx's recently */
179862306a36Sopenharmony_ci    if (time_after(jiffies, smc->last_rx + HZ)) {
179962306a36Sopenharmony_ci	if (smc->tx_err || (smc->media_status & EPH_16COL))
180062306a36Sopenharmony_ci	    media |= EPH_16COL;
180162306a36Sopenharmony_ci    }
180262306a36Sopenharmony_ci    smc->tx_err = 0;
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci    if (media != smc->media_status) {
180562306a36Sopenharmony_ci	if ((media & smc->media_status & 1) &&
180662306a36Sopenharmony_ci	    ((smc->media_status ^ media) & EPH_LINK_OK))
180762306a36Sopenharmony_ci	    netdev_info(dev, "%s link beat\n",
180862306a36Sopenharmony_ci			smc->media_status & EPH_LINK_OK ? "lost" : "found");
180962306a36Sopenharmony_ci	else if ((media & smc->media_status & 2) &&
181062306a36Sopenharmony_ci		 ((smc->media_status ^ media) & EPH_16COL))
181162306a36Sopenharmony_ci	    netdev_info(dev, "coax cable %s\n",
181262306a36Sopenharmony_ci			media & EPH_16COL ? "problem" : "ok");
181362306a36Sopenharmony_ci	if (dev->if_port == 0) {
181462306a36Sopenharmony_ci	    if (media & 1) {
181562306a36Sopenharmony_ci		if (media & EPH_LINK_OK)
181662306a36Sopenharmony_ci		    netdev_info(dev, "flipped to 10baseT\n");
181762306a36Sopenharmony_ci		else
181862306a36Sopenharmony_ci		    smc_set_xcvr(dev, 2);
181962306a36Sopenharmony_ci	    } else {
182062306a36Sopenharmony_ci		if (media & EPH_16COL)
182162306a36Sopenharmony_ci		    smc_set_xcvr(dev, 1);
182262306a36Sopenharmony_ci		else
182362306a36Sopenharmony_ci		    netdev_info(dev, "flipped to 10base2\n");
182462306a36Sopenharmony_ci	    }
182562306a36Sopenharmony_ci	}
182662306a36Sopenharmony_ci	smc->media_status = media;
182762306a36Sopenharmony_ci    }
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_cireschedule:
183062306a36Sopenharmony_ci    smc->media.expires = jiffies + HZ;
183162306a36Sopenharmony_ci    add_timer(&smc->media);
183262306a36Sopenharmony_ci    SMC_SELECT_BANK(saved_bank);
183362306a36Sopenharmony_ci    spin_unlock_irqrestore(&smc->lock, flags);
183462306a36Sopenharmony_ci}
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_cistatic int smc_link_ok(struct net_device *dev)
183762306a36Sopenharmony_ci{
183862306a36Sopenharmony_ci    unsigned int ioaddr = dev->base_addr;
183962306a36Sopenharmony_ci    struct smc_private *smc = netdev_priv(dev);
184062306a36Sopenharmony_ci
184162306a36Sopenharmony_ci    if (smc->cfg & CFG_MII_SELECT) {
184262306a36Sopenharmony_ci	return mii_link_ok(&smc->mii_if);
184362306a36Sopenharmony_ci    } else {
184462306a36Sopenharmony_ci        SMC_SELECT_BANK(0);
184562306a36Sopenharmony_ci	return inw(ioaddr + EPH) & EPH_LINK_OK;
184662306a36Sopenharmony_ci    }
184762306a36Sopenharmony_ci}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_cistatic void smc_netdev_get_ecmd(struct net_device *dev,
185062306a36Sopenharmony_ci				struct ethtool_link_ksettings *ecmd)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	u16 tmp;
185362306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
185462306a36Sopenharmony_ci	u32 supported;
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	supported = (SUPPORTED_TP | SUPPORTED_AUI |
185762306a36Sopenharmony_ci		     SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full);
185862306a36Sopenharmony_ci
185962306a36Sopenharmony_ci	SMC_SELECT_BANK(1);
186062306a36Sopenharmony_ci	tmp = inw(ioaddr + CONFIG);
186162306a36Sopenharmony_ci	ecmd->base.port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP;
186262306a36Sopenharmony_ci	ecmd->base.speed = SPEED_10;
186362306a36Sopenharmony_ci	ecmd->base.phy_address = ioaddr + MGMT;
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	SMC_SELECT_BANK(0);
186662306a36Sopenharmony_ci	tmp = inw(ioaddr + TCR);
186762306a36Sopenharmony_ci	ecmd->base.duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF;
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	ethtool_convert_legacy_u32_to_link_mode(ecmd->link_modes.supported,
187062306a36Sopenharmony_ci						supported);
187162306a36Sopenharmony_ci}
187262306a36Sopenharmony_ci
187362306a36Sopenharmony_cistatic int smc_netdev_set_ecmd(struct net_device *dev,
187462306a36Sopenharmony_ci			       const struct ethtool_link_ksettings *ecmd)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	u16 tmp;
187762306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	if (ecmd->base.speed != SPEED_10)
188062306a36Sopenharmony_ci		return -EINVAL;
188162306a36Sopenharmony_ci	if (ecmd->base.duplex != DUPLEX_HALF &&
188262306a36Sopenharmony_ci	    ecmd->base.duplex != DUPLEX_FULL)
188362306a36Sopenharmony_ci		return -EINVAL;
188462306a36Sopenharmony_ci	if (ecmd->base.port != PORT_TP && ecmd->base.port != PORT_AUI)
188562306a36Sopenharmony_ci		return -EINVAL;
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	if (ecmd->base.port == PORT_AUI)
188862306a36Sopenharmony_ci		smc_set_xcvr(dev, 1);
188962306a36Sopenharmony_ci	else
189062306a36Sopenharmony_ci		smc_set_xcvr(dev, 0);
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	SMC_SELECT_BANK(0);
189362306a36Sopenharmony_ci	tmp = inw(ioaddr + TCR);
189462306a36Sopenharmony_ci	if (ecmd->base.duplex == DUPLEX_FULL)
189562306a36Sopenharmony_ci		tmp |= TCR_FDUPLX;
189662306a36Sopenharmony_ci	else
189762306a36Sopenharmony_ci		tmp &= ~TCR_FDUPLX;
189862306a36Sopenharmony_ci	outw(tmp, ioaddr + TCR);
189962306a36Sopenharmony_ci
190062306a36Sopenharmony_ci	return 0;
190162306a36Sopenharmony_ci}
190262306a36Sopenharmony_ci
190362306a36Sopenharmony_cistatic int check_if_running(struct net_device *dev)
190462306a36Sopenharmony_ci{
190562306a36Sopenharmony_ci	if (!netif_running(dev))
190662306a36Sopenharmony_ci		return -EINVAL;
190762306a36Sopenharmony_ci	return 0;
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_cistatic void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
191162306a36Sopenharmony_ci{
191262306a36Sopenharmony_ci	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
191362306a36Sopenharmony_ci	strscpy(info->version, DRV_VERSION, sizeof(info->version));
191462306a36Sopenharmony_ci}
191562306a36Sopenharmony_ci
191662306a36Sopenharmony_cistatic int smc_get_link_ksettings(struct net_device *dev,
191762306a36Sopenharmony_ci				  struct ethtool_link_ksettings *ecmd)
191862306a36Sopenharmony_ci{
191962306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
192062306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
192162306a36Sopenharmony_ci	u16 saved_bank = inw(ioaddr + BANK_SELECT);
192262306a36Sopenharmony_ci	unsigned long flags;
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci	spin_lock_irqsave(&smc->lock, flags);
192562306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
192662306a36Sopenharmony_ci	if (smc->cfg & CFG_MII_SELECT)
192762306a36Sopenharmony_ci		mii_ethtool_get_link_ksettings(&smc->mii_if, ecmd);
192862306a36Sopenharmony_ci	else
192962306a36Sopenharmony_ci		smc_netdev_get_ecmd(dev, ecmd);
193062306a36Sopenharmony_ci	SMC_SELECT_BANK(saved_bank);
193162306a36Sopenharmony_ci	spin_unlock_irqrestore(&smc->lock, flags);
193262306a36Sopenharmony_ci	return 0;
193362306a36Sopenharmony_ci}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_cistatic int smc_set_link_ksettings(struct net_device *dev,
193662306a36Sopenharmony_ci				  const struct ethtool_link_ksettings *ecmd)
193762306a36Sopenharmony_ci{
193862306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
193962306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
194062306a36Sopenharmony_ci	u16 saved_bank = inw(ioaddr + BANK_SELECT);
194162306a36Sopenharmony_ci	int ret;
194262306a36Sopenharmony_ci	unsigned long flags;
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	spin_lock_irqsave(&smc->lock, flags);
194562306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
194662306a36Sopenharmony_ci	if (smc->cfg & CFG_MII_SELECT)
194762306a36Sopenharmony_ci		ret = mii_ethtool_set_link_ksettings(&smc->mii_if, ecmd);
194862306a36Sopenharmony_ci	else
194962306a36Sopenharmony_ci		ret = smc_netdev_set_ecmd(dev, ecmd);
195062306a36Sopenharmony_ci	SMC_SELECT_BANK(saved_bank);
195162306a36Sopenharmony_ci	spin_unlock_irqrestore(&smc->lock, flags);
195262306a36Sopenharmony_ci	return ret;
195362306a36Sopenharmony_ci}
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_cistatic u32 smc_get_link(struct net_device *dev)
195662306a36Sopenharmony_ci{
195762306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
195862306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
195962306a36Sopenharmony_ci	u16 saved_bank = inw(ioaddr + BANK_SELECT);
196062306a36Sopenharmony_ci	u32 ret;
196162306a36Sopenharmony_ci	unsigned long flags;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	spin_lock_irqsave(&smc->lock, flags);
196462306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
196562306a36Sopenharmony_ci	ret = smc_link_ok(dev);
196662306a36Sopenharmony_ci	SMC_SELECT_BANK(saved_bank);
196762306a36Sopenharmony_ci	spin_unlock_irqrestore(&smc->lock, flags);
196862306a36Sopenharmony_ci	return ret;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic int smc_nway_reset(struct net_device *dev)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
197462306a36Sopenharmony_ci	if (smc->cfg & CFG_MII_SELECT) {
197562306a36Sopenharmony_ci		unsigned int ioaddr = dev->base_addr;
197662306a36Sopenharmony_ci		u16 saved_bank = inw(ioaddr + BANK_SELECT);
197762306a36Sopenharmony_ci		int res;
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci		SMC_SELECT_BANK(3);
198062306a36Sopenharmony_ci		res = mii_nway_restart(&smc->mii_if);
198162306a36Sopenharmony_ci		SMC_SELECT_BANK(saved_bank);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci		return res;
198462306a36Sopenharmony_ci	} else
198562306a36Sopenharmony_ci		return -EOPNOTSUPP;
198662306a36Sopenharmony_ci}
198762306a36Sopenharmony_ci
198862306a36Sopenharmony_cistatic const struct ethtool_ops ethtool_ops = {
198962306a36Sopenharmony_ci	.begin = check_if_running,
199062306a36Sopenharmony_ci	.get_drvinfo = smc_get_drvinfo,
199162306a36Sopenharmony_ci	.get_link = smc_get_link,
199262306a36Sopenharmony_ci	.nway_reset = smc_nway_reset,
199362306a36Sopenharmony_ci	.get_link_ksettings = smc_get_link_ksettings,
199462306a36Sopenharmony_ci	.set_link_ksettings = smc_set_link_ksettings,
199562306a36Sopenharmony_ci};
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_cistatic int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct smc_private *smc = netdev_priv(dev);
200062306a36Sopenharmony_ci	struct mii_ioctl_data *mii = if_mii(rq);
200162306a36Sopenharmony_ci	int rc = 0;
200262306a36Sopenharmony_ci	u16 saved_bank;
200362306a36Sopenharmony_ci	unsigned int ioaddr = dev->base_addr;
200462306a36Sopenharmony_ci	unsigned long flags;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci	if (!netif_running(dev))
200762306a36Sopenharmony_ci		return -EINVAL;
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ci	spin_lock_irqsave(&smc->lock, flags);
201062306a36Sopenharmony_ci	saved_bank = inw(ioaddr + BANK_SELECT);
201162306a36Sopenharmony_ci	SMC_SELECT_BANK(3);
201262306a36Sopenharmony_ci	rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL);
201362306a36Sopenharmony_ci	SMC_SELECT_BANK(saved_bank);
201462306a36Sopenharmony_ci	spin_unlock_irqrestore(&smc->lock, flags);
201562306a36Sopenharmony_ci	return rc;
201662306a36Sopenharmony_ci}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_cistatic const struct pcmcia_device_id smc91c92_ids[] = {
201962306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501),
202062306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a),
202162306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63),
202262306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63),
202362306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef),
202462306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef),
202562306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c),
202662306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e),
202762306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9),
202862306a36Sopenharmony_ci	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed),
202962306a36Sopenharmony_ci	PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020),
203062306a36Sopenharmony_ci	PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023),
203162306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb),
203262306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc),
203362306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1),
203462306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5),
203562306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9),
203662306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953),
203762306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a),
203862306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314),
203962306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a),
204062306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc),
204162306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9),
204262306a36Sopenharmony_ci	PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d),
204362306a36Sopenharmony_ci	/* These conflict with other cards! */
204462306a36Sopenharmony_ci	/* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */
204562306a36Sopenharmony_ci	/* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */
204662306a36Sopenharmony_ci	PCMCIA_DEVICE_NULL,
204762306a36Sopenharmony_ci};
204862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pcmcia, smc91c92_ids);
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_cistatic struct pcmcia_driver smc91c92_cs_driver = {
205162306a36Sopenharmony_ci	.owner		= THIS_MODULE,
205262306a36Sopenharmony_ci	.name		= "smc91c92_cs",
205362306a36Sopenharmony_ci	.probe		= smc91c92_probe,
205462306a36Sopenharmony_ci	.remove		= smc91c92_detach,
205562306a36Sopenharmony_ci	.id_table       = smc91c92_ids,
205662306a36Sopenharmony_ci	.suspend	= smc91c92_suspend,
205762306a36Sopenharmony_ci	.resume		= smc91c92_resume,
205862306a36Sopenharmony_ci};
205962306a36Sopenharmony_cimodule_pcmcia_driver(smc91c92_cs_driver);
2060