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 = ðtool_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