| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /*====================================================================== | 
 | 2 |  | 
 | 3 |     A PCMCIA ethernet driver for SMC91c92-based cards. | 
 | 4 |  | 
 | 5 |     This driver supports Megahertz PCMCIA ethernet cards; and | 
 | 6 |     Megahertz, Motorola, Ositech, and Psion Dacom ethernet/modem | 
 | 7 |     multifunction cards. | 
 | 8 |  | 
 | 9 |     Copyright (C) 1999 David A. Hinds -- dahinds@users.sourceforge.net | 
 | 10 |  | 
 | 11 |     smc91c92_cs.c 1.122 2002/10/25 06:26:39 | 
 | 12 |  | 
 | 13 |     This driver contains code written by Donald Becker | 
 | 14 |     (becker@scyld.com), Rowan Hughes (x-csrdh@jcu.edu.au), | 
 | 15 |     David Hinds (dahinds@users.sourceforge.net), and Erik Stahlman | 
 | 16 |     (erik@vt.edu).  Donald wrote the SMC 91c92 code using parts of | 
 | 17 |     Erik's SMC 91c94 driver.  Rowan wrote a similar driver, and I've | 
 | 18 |     incorporated some parts of his driver here.  I (Dave) wrote most | 
 | 19 |     of the PCMCIA glue code, and the Ositech support code.  Kelly | 
 | 20 |     Stephens (kstephen@holli.com) added support for the Motorola | 
 | 21 |     Mariner, with help from Allen Brost. | 
 | 22 |  | 
 | 23 |     This software may be used and distributed according to the terms of | 
 | 24 |     the GNU General Public License, incorporated herein by reference. | 
 | 25 |  | 
 | 26 | ======================================================================*/ | 
 | 27 |  | 
 | 28 | #include <linux/module.h> | 
 | 29 | #include <linux/kernel.h> | 
 | 30 | #include <linux/init.h> | 
 | 31 | #include <linux/slab.h> | 
 | 32 | #include <linux/string.h> | 
 | 33 | #include <linux/timer.h> | 
 | 34 | #include <linux/interrupt.h> | 
 | 35 | #include <linux/delay.h> | 
 | 36 | #include <linux/crc32.h> | 
 | 37 | #include <linux/netdevice.h> | 
 | 38 | #include <linux/etherdevice.h> | 
 | 39 | #include <linux/skbuff.h> | 
 | 40 | #include <linux/if_arp.h> | 
 | 41 | #include <linux/ioport.h> | 
 | 42 | #include <linux/ethtool.h> | 
 | 43 | #include <linux/mii.h> | 
| Marcelo Feitoza Parisi | 4851d3a | 2005-07-15 10:00:41 -0700 | [diff] [blame] | 44 | #include <linux/jiffies.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 45 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 46 | #include <pcmcia/cs_types.h> | 
 | 47 | #include <pcmcia/cs.h> | 
 | 48 | #include <pcmcia/cistpl.h> | 
 | 49 | #include <pcmcia/cisreg.h> | 
 | 50 | #include <pcmcia/ciscode.h> | 
 | 51 | #include <pcmcia/ds.h> | 
| Dominik Brodowski | 50db3fd | 2006-01-15 10:05:19 +0100 | [diff] [blame] | 52 | #include <pcmcia/ss.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 53 |  | 
 | 54 | #include <asm/io.h> | 
 | 55 | #include <asm/system.h> | 
 | 56 | #include <asm/uaccess.h> | 
 | 57 |  | 
 | 58 | /* Ositech Seven of Diamonds firmware */ | 
 | 59 | #include "ositech.h" | 
 | 60 |  | 
 | 61 | /*====================================================================*/ | 
 | 62 |  | 
| Arjan van de Ven | f71e130 | 2006-03-03 21:33:57 -0500 | [diff] [blame] | 63 | static const char *if_names[] = { "auto", "10baseT", "10base2"}; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 64 |  | 
 | 65 | /* Module parameters */ | 
 | 66 |  | 
 | 67 | MODULE_DESCRIPTION("SMC 91c92 series PCMCIA ethernet driver"); | 
 | 68 | MODULE_LICENSE("GPL"); | 
 | 69 |  | 
 | 70 | #define INT_MODULE_PARM(n, v) static int n = v; module_param(n, int, 0) | 
 | 71 |  | 
 | 72 | /* | 
 | 73 |   Transceiver/media type. | 
 | 74 |    0 = auto | 
 | 75 |    1 = 10baseT (and autoselect if #define AUTOSELECT), | 
 | 76 |    2 = AUI/10base2, | 
 | 77 | */ | 
 | 78 | INT_MODULE_PARM(if_port, 0); | 
 | 79 |  | 
 | 80 | #ifdef PCMCIA_DEBUG | 
 | 81 | INT_MODULE_PARM(pc_debug, PCMCIA_DEBUG); | 
 | 82 | static const char *version = | 
| Andy Gospodarek | d5b2069 | 2006-09-11 17:39:18 -0400 | [diff] [blame] | 83 | "smc91c92_cs.c 1.123 2006/11/09 Donald Becker, becker@scyld.com.\n"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 84 | #define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) | 
 | 85 | #else | 
 | 86 | #define DEBUG(n, args...) | 
 | 87 | #endif | 
 | 88 |  | 
 | 89 | #define DRV_NAME	"smc91c92_cs" | 
| Andy Gospodarek | d5b2069 | 2006-09-11 17:39:18 -0400 | [diff] [blame] | 90 | #define DRV_VERSION	"1.123" | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 91 |  | 
 | 92 | /*====================================================================*/ | 
 | 93 |  | 
 | 94 | /* Operational parameter that usually are not changed. */ | 
 | 95 |  | 
 | 96 | /* Time in jiffies before concluding Tx hung */ | 
 | 97 | #define TX_TIMEOUT		((400*HZ)/1000) | 
 | 98 |  | 
 | 99 | /* Maximum events (Rx packets, etc.) to handle at each interrupt. */ | 
 | 100 | #define INTR_WORK		4 | 
 | 101 |  | 
 | 102 | /* Times to check the check the chip before concluding that it doesn't | 
 | 103 |    currently have room for another Tx packet. */ | 
 | 104 | #define MEMORY_WAIT_TIME       	8 | 
 | 105 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 106 | struct smc_private { | 
| Dominik Brodowski | fd23823 | 2006-03-05 10:45:09 +0100 | [diff] [blame] | 107 | 	struct pcmcia_device	*p_dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 108 |     spinlock_t			lock; | 
 | 109 |     u_short			manfid; | 
 | 110 |     u_short			cardid; | 
 | 111 |     struct net_device_stats	stats; | 
 | 112 |     dev_node_t			node; | 
 | 113 |     struct sk_buff		*saved_skb; | 
 | 114 |     int				packets_waiting; | 
 | 115 |     void			__iomem *base; | 
 | 116 |     u_short			cfg; | 
 | 117 |     struct timer_list		media; | 
 | 118 |     int				watchdog, tx_err; | 
 | 119 |     u_short			media_status; | 
 | 120 |     u_short			fast_poll; | 
 | 121 |     u_short			link_status; | 
 | 122 |     struct mii_if_info		mii_if; | 
 | 123 |     int				duplex; | 
 | 124 |     int				rx_ovrn; | 
 | 125 | }; | 
 | 126 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 127 | struct smc_cfg_mem { | 
 | 128 |     tuple_t tuple; | 
 | 129 |     cisparse_t parse; | 
 | 130 |     u_char buf[255]; | 
 | 131 | }; | 
 | 132 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 133 | /* Special definitions for Megahertz multifunction cards */ | 
 | 134 | #define MEGAHERTZ_ISR		0x0380 | 
 | 135 |  | 
 | 136 | /* Special function registers for Motorola Mariner */ | 
 | 137 | #define MOT_LAN			0x0000 | 
 | 138 | #define MOT_UART		0x0020 | 
 | 139 | #define MOT_EEPROM		0x20 | 
 | 140 |  | 
 | 141 | #define MOT_NORMAL \ | 
 | 142 | (COR_LEVEL_REQ | COR_FUNC_ENA | COR_ADDR_DECODE | COR_IREQ_ENA) | 
 | 143 |  | 
 | 144 | /* Special function registers for Ositech cards */ | 
 | 145 | #define OSITECH_AUI_CTL		0x0c | 
 | 146 | #define OSITECH_PWRDOWN		0x0d | 
 | 147 | #define OSITECH_RESET		0x0e | 
 | 148 | #define OSITECH_ISR		0x0f | 
 | 149 | #define OSITECH_AUI_PWR		0x0c | 
 | 150 | #define OSITECH_RESET_ISR	0x0e | 
 | 151 |  | 
 | 152 | #define OSI_AUI_PWR		0x40 | 
 | 153 | #define OSI_LAN_PWRDOWN		0x02 | 
 | 154 | #define OSI_MODEM_PWRDOWN	0x01 | 
 | 155 | #define OSI_LAN_RESET		0x02 | 
 | 156 | #define OSI_MODEM_RESET		0x01 | 
 | 157 |  | 
 | 158 | /* Symbolic constants for the SMC91c9* series chips, from Erik Stahlman. */ | 
 | 159 | #define	BANK_SELECT		14		/* Window select register. */ | 
 | 160 | #define SMC_SELECT_BANK(x)  { outw(x, ioaddr + BANK_SELECT); } | 
 | 161 |  | 
 | 162 | /* Bank 0 registers. */ | 
 | 163 | #define	TCR 		0	/* transmit control register */ | 
 | 164 | #define	 TCR_CLEAR	0	/* do NOTHING */ | 
 | 165 | #define  TCR_ENABLE	0x0001	/* if this is 1, we can transmit */ | 
 | 166 | #define	 TCR_PAD_EN	0x0080	/* pads short packets to 64 bytes */ | 
 | 167 | #define  TCR_MONCSN	0x0400  /* Monitor Carrier. */ | 
 | 168 | #define  TCR_FDUPLX	0x0800  /* Full duplex mode. */ | 
 | 169 | #define	 TCR_NORMAL TCR_ENABLE | TCR_PAD_EN | 
 | 170 |  | 
 | 171 | #define EPH		2	/* Ethernet Protocol Handler report. */ | 
 | 172 | #define  EPH_TX_SUC	0x0001 | 
 | 173 | #define  EPH_SNGLCOL	0x0002 | 
 | 174 | #define  EPH_MULCOL	0x0004 | 
 | 175 | #define  EPH_LTX_MULT	0x0008 | 
 | 176 | #define  EPH_16COL	0x0010 | 
 | 177 | #define  EPH_SQET	0x0020 | 
 | 178 | #define  EPH_LTX_BRD	0x0040 | 
 | 179 | #define  EPH_TX_DEFR	0x0080 | 
 | 180 | #define  EPH_LAT_COL	0x0200 | 
 | 181 | #define  EPH_LOST_CAR	0x0400 | 
 | 182 | #define  EPH_EXC_DEF	0x0800 | 
 | 183 | #define  EPH_CTR_ROL	0x1000 | 
 | 184 | #define  EPH_RX_OVRN	0x2000 | 
 | 185 | #define  EPH_LINK_OK	0x4000 | 
 | 186 | #define  EPH_TX_UNRN	0x8000 | 
 | 187 | #define MEMINFO		8	/* Memory Information Register */ | 
 | 188 | #define MEMCFG		10	/* Memory Configuration Register */ | 
 | 189 |  | 
 | 190 | /* Bank 1 registers. */ | 
 | 191 | #define CONFIG			0 | 
 | 192 | #define  CFG_MII_SELECT		0x8000	/* 91C100 only */ | 
 | 193 | #define  CFG_NO_WAIT		0x1000 | 
 | 194 | #define  CFG_FULL_STEP		0x0400 | 
 | 195 | #define  CFG_SET_SQLCH		0x0200 | 
 | 196 | #define  CFG_AUI_SELECT	 	0x0100 | 
 | 197 | #define  CFG_16BIT		0x0080 | 
 | 198 | #define  CFG_DIS_LINK		0x0040 | 
 | 199 | #define  CFG_STATIC		0x0030 | 
 | 200 | #define  CFG_IRQ_SEL_1		0x0004 | 
 | 201 | #define  CFG_IRQ_SEL_0		0x0002 | 
 | 202 | #define BASE_ADDR		2 | 
 | 203 | #define	ADDR0			4 | 
 | 204 | #define	GENERAL			10 | 
 | 205 | #define	CONTROL			12 | 
 | 206 | #define  CTL_STORE		0x0001 | 
 | 207 | #define  CTL_RELOAD		0x0002 | 
 | 208 | #define  CTL_EE_SELECT		0x0004 | 
 | 209 | #define  CTL_TE_ENABLE		0x0020 | 
 | 210 | #define  CTL_CR_ENABLE		0x0040 | 
 | 211 | #define  CTL_LE_ENABLE		0x0080 | 
 | 212 | #define  CTL_AUTO_RELEASE	0x0800 | 
 | 213 | #define	 CTL_POWERDOWN		0x2000 | 
 | 214 |  | 
 | 215 | /* Bank 2 registers. */ | 
 | 216 | #define MMU_CMD		0 | 
 | 217 | #define	 MC_ALLOC	0x20  	/* or with number of 256 byte packets */ | 
 | 218 | #define	 MC_RESET	0x40 | 
 | 219 | #define  MC_RELEASE  	0x80  	/* remove and release the current rx packet */ | 
 | 220 | #define  MC_FREEPKT  	0xA0  	/* Release packet in PNR register */ | 
 | 221 | #define  MC_ENQUEUE	0xC0 	/* Enqueue the packet for transmit */ | 
 | 222 | #define	PNR_ARR		2 | 
 | 223 | #define FIFO_PORTS	4 | 
 | 224 | #define  FP_RXEMPTY	0x8000 | 
 | 225 | #define	POINTER		6 | 
 | 226 | #define  PTR_AUTO_INC	0x0040 | 
 | 227 | #define  PTR_READ	0x2000 | 
 | 228 | #define	 PTR_AUTOINC 	0x4000 | 
 | 229 | #define	 PTR_RCV	0x8000 | 
 | 230 | #define	DATA_1		8 | 
 | 231 | #define	INTERRUPT	12 | 
 | 232 | #define  IM_RCV_INT		0x1 | 
 | 233 | #define	 IM_TX_INT		0x2 | 
 | 234 | #define	 IM_TX_EMPTY_INT	0x4 | 
 | 235 | #define	 IM_ALLOC_INT		0x8 | 
 | 236 | #define	 IM_RX_OVRN_INT		0x10 | 
 | 237 | #define	 IM_EPH_INT		0x20 | 
 | 238 |  | 
 | 239 | #define	RCR		4 | 
 | 240 | enum RxCfg { RxAllMulti = 0x0004, RxPromisc = 0x0002, | 
 | 241 | 	     RxEnable = 0x0100, RxStripCRC = 0x0200}; | 
 | 242 | #define  RCR_SOFTRESET	0x8000 	/* resets the chip */ | 
 | 243 | #define	 RCR_STRIP_CRC	0x200	/* strips CRC */ | 
 | 244 | #define  RCR_ENABLE	0x100	/* IFF this is set, we can receive packets */ | 
 | 245 | #define  RCR_ALMUL	0x4 	/* receive all multicast packets */ | 
 | 246 | #define	 RCR_PROMISC	0x2	/* enable promiscuous mode */ | 
 | 247 |  | 
 | 248 | /* the normal settings for the RCR register : */ | 
 | 249 | #define	 RCR_NORMAL	(RCR_STRIP_CRC | RCR_ENABLE) | 
 | 250 | #define  RCR_CLEAR	0x0		/* set it to a base state */ | 
 | 251 | #define	COUNTER		6 | 
 | 252 |  | 
 | 253 | /* BANK 3 -- not the same values as in smc9194! */ | 
 | 254 | #define	MULTICAST0	0 | 
 | 255 | #define	MULTICAST2	2 | 
 | 256 | #define	MULTICAST4	4 | 
 | 257 | #define	MULTICAST6	6 | 
 | 258 | #define MGMT    	8 | 
 | 259 | #define REVISION	0x0a | 
 | 260 |  | 
 | 261 | /* Transmit status bits. */ | 
 | 262 | #define TS_SUCCESS 0x0001 | 
 | 263 | #define TS_16COL   0x0010 | 
 | 264 | #define TS_LATCOL  0x0200 | 
 | 265 | #define TS_LOSTCAR 0x0400 | 
 | 266 |  | 
 | 267 | /* Receive status bits. */ | 
 | 268 | #define RS_ALGNERR	0x8000 | 
 | 269 | #define RS_BADCRC	0x2000 | 
 | 270 | #define RS_ODDFRAME	0x1000 | 
 | 271 | #define RS_TOOLONG	0x0800 | 
 | 272 | #define RS_TOOSHORT	0x0400 | 
 | 273 | #define RS_MULTICAST	0x0001 | 
 | 274 | #define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) | 
 | 275 |  | 
 | 276 | #define set_bits(v, p) outw(inw(p)|(v), (p)) | 
 | 277 | #define mask_bits(v, p) outw(inw(p)&(v), (p)) | 
 | 278 |  | 
 | 279 | /*====================================================================*/ | 
 | 280 |  | 
| Dominik Brodowski | cc3b486 | 2005-11-14 21:23:14 +0100 | [diff] [blame] | 281 | static void smc91c92_detach(struct pcmcia_device *p_dev); | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 282 | static int smc91c92_config(struct pcmcia_device *link); | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 283 | static void smc91c92_release(struct pcmcia_device *link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 284 |  | 
 | 285 | static int smc_open(struct net_device *dev); | 
 | 286 | static int smc_close(struct net_device *dev); | 
 | 287 | static int smc_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); | 
 | 288 | static void smc_tx_timeout(struct net_device *dev); | 
 | 289 | static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev); | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 290 | static irqreturn_t smc_interrupt(int irq, void *dev_id); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 291 | static void smc_rx(struct net_device *dev); | 
 | 292 | static struct net_device_stats *smc_get_stats(struct net_device *dev); | 
 | 293 | static void set_rx_mode(struct net_device *dev); | 
 | 294 | static int s9k_config(struct net_device *dev, struct ifmap *map); | 
 | 295 | static void smc_set_xcvr(struct net_device *dev, int if_port); | 
 | 296 | static void smc_reset(struct net_device *dev); | 
 | 297 | static void media_check(u_long arg); | 
 | 298 | static void mdio_sync(kio_addr_t addr); | 
 | 299 | static int mdio_read(struct net_device *dev, int phy_id, int loc); | 
 | 300 | static void mdio_write(struct net_device *dev, int phy_id, int loc, int value); | 
 | 301 | static int smc_link_ok(struct net_device *dev); | 
| Jeff Garzik | 7282d49 | 2006-09-13 14:30:00 -0400 | [diff] [blame] | 302 | static const struct ethtool_ops ethtool_ops; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 303 |  | 
 | 304 | /*====================================================================== | 
 | 305 |  | 
 | 306 |   smc91c92_attach() creates an "instance" of the driver, allocating | 
 | 307 |   local data structures for one device.  The device is registered | 
 | 308 |   with Card Services. | 
 | 309 |  | 
 | 310 | ======================================================================*/ | 
 | 311 |  | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 312 | static int smc91c92_probe(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 313 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 |     struct smc_private *smc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 315 |     struct net_device *dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 316 |  | 
 | 317 |     DEBUG(0, "smc91c92_attach()\n"); | 
 | 318 |  | 
 | 319 |     /* Create new ethernet device */ | 
 | 320 |     dev = alloc_etherdev(sizeof(struct smc_private)); | 
 | 321 |     if (!dev) | 
| Dominik Brodowski | f8cfa61 | 2005-11-14 21:25:51 +0100 | [diff] [blame] | 322 | 	return -ENOMEM; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 323 |     smc = netdev_priv(dev); | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 324 |     smc->p_dev = link; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 325 |     link->priv = dev; | 
 | 326 |  | 
 | 327 |     spin_lock_init(&smc->lock); | 
 | 328 |     link->io.NumPorts1 = 16; | 
 | 329 |     link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; | 
 | 330 |     link->io.IOAddrLines = 4; | 
 | 331 |     link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; | 
 | 332 |     link->irq.IRQInfo1 = IRQ_LEVEL_ID; | 
 | 333 |     link->irq.Handler = &smc_interrupt; | 
 | 334 |     link->irq.Instance = dev; | 
 | 335 |     link->conf.Attributes = CONF_ENABLE_IRQ; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 336 |     link->conf.IntType = INT_MEMORY_AND_IO; | 
 | 337 |  | 
 | 338 |     /* The SMC91c92-specific entries in the device structure. */ | 
 | 339 |     SET_MODULE_OWNER(dev); | 
 | 340 |     dev->hard_start_xmit = &smc_start_xmit; | 
 | 341 |     dev->get_stats = &smc_get_stats; | 
 | 342 |     dev->set_config = &s9k_config; | 
 | 343 |     dev->set_multicast_list = &set_rx_mode; | 
 | 344 |     dev->open = &smc_open; | 
 | 345 |     dev->stop = &smc_close; | 
 | 346 |     dev->do_ioctl = &smc_ioctl; | 
 | 347 |     SET_ETHTOOL_OPS(dev, ðtool_ops); | 
 | 348 | #ifdef HAVE_TX_TIMEOUT | 
 | 349 |     dev->tx_timeout = smc_tx_timeout; | 
 | 350 |     dev->watchdog_timeo = TX_TIMEOUT; | 
 | 351 | #endif | 
 | 352 |  | 
 | 353 |     smc->mii_if.dev = dev; | 
 | 354 |     smc->mii_if.mdio_read = mdio_read; | 
 | 355 |     smc->mii_if.mdio_write = mdio_write; | 
 | 356 |     smc->mii_if.phy_id_mask = 0x1f; | 
 | 357 |     smc->mii_if.reg_num_mask = 0x1f; | 
 | 358 |  | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 359 |     return smc91c92_config(link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 360 | } /* smc91c92_attach */ | 
 | 361 |  | 
 | 362 | /*====================================================================== | 
 | 363 |  | 
 | 364 |     This deletes a driver "instance".  The device is de-registered | 
 | 365 |     with Card Services.  If it has been released, all local data | 
 | 366 |     structures are freed.  Otherwise, the structures will be freed | 
 | 367 |     when the device is released. | 
 | 368 |  | 
 | 369 | ======================================================================*/ | 
 | 370 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 371 | static void smc91c92_detach(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 372 | { | 
 | 373 |     struct net_device *dev = link->priv; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 374 |  | 
 | 375 |     DEBUG(0, "smc91c92_detach(0x%p)\n", link); | 
 | 376 |  | 
| Dominik Brodowski | fd23823 | 2006-03-05 10:45:09 +0100 | [diff] [blame] | 377 |     if (link->dev_node) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 378 | 	unregister_netdev(dev); | 
 | 379 |  | 
| Dominik Brodowski | e2d4096 | 2006-03-02 00:09:29 +0100 | [diff] [blame] | 380 |     smc91c92_release(link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 381 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 382 |     free_netdev(dev); | 
 | 383 | } /* smc91c92_detach */ | 
 | 384 |  | 
 | 385 | /*====================================================================*/ | 
 | 386 |  | 
 | 387 | static int cvt_ascii_address(struct net_device *dev, char *s) | 
 | 388 | { | 
 | 389 |     int i, j, da, c; | 
 | 390 |  | 
 | 391 |     if (strlen(s) != 12) | 
 | 392 | 	return -1; | 
 | 393 |     for (i = 0; i < 6; i++) { | 
 | 394 | 	da = 0; | 
 | 395 | 	for (j = 0; j < 2; j++) { | 
 | 396 | 	    c = *s++; | 
 | 397 | 	    da <<= 4; | 
 | 398 | 	    da += ((c >= '0') && (c <= '9')) ? | 
 | 399 | 		(c - '0') : ((c & 0x0f) + 9); | 
 | 400 | 	} | 
 | 401 | 	dev->dev_addr[i] = da; | 
 | 402 |     } | 
 | 403 |     return 0; | 
 | 404 | } | 
 | 405 |  | 
 | 406 | /*====================================================================*/ | 
 | 407 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 408 | static int first_tuple(struct pcmcia_device *handle, tuple_t *tuple, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 409 | 		cisparse_t *parse) | 
 | 410 | { | 
 | 411 | 	int i; | 
 | 412 |  | 
 | 413 | 	if ((i = pcmcia_get_first_tuple(handle, tuple)) != CS_SUCCESS || | 
 | 414 | 			(i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS) | 
 | 415 | 		return i; | 
 | 416 | 	return pcmcia_parse_tuple(handle, tuple, parse); | 
 | 417 | } | 
 | 418 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 419 | static int next_tuple(struct pcmcia_device *handle, tuple_t *tuple, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 420 | 		cisparse_t *parse) | 
 | 421 | { | 
 | 422 | 	int i; | 
 | 423 |  | 
 | 424 | 	if ((i = pcmcia_get_next_tuple(handle, tuple)) != CS_SUCCESS || | 
 | 425 | 			(i = pcmcia_get_tuple_data(handle, tuple)) != CS_SUCCESS) | 
 | 426 | 		return i; | 
 | 427 | 	return pcmcia_parse_tuple(handle, tuple, parse); | 
 | 428 | } | 
 | 429 |  | 
 | 430 | /*====================================================================== | 
 | 431 |  | 
 | 432 |     Configuration stuff for Megahertz cards | 
 | 433 |  | 
 | 434 |     mhz_3288_power() is used to power up a 3288's ethernet chip. | 
 | 435 |     mhz_mfc_config() handles socket setup for multifunction (1144 | 
 | 436 |     and 3288) cards.  mhz_setup() gets a card's hardware ethernet | 
 | 437 |     address. | 
 | 438 |  | 
 | 439 | ======================================================================*/ | 
 | 440 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 441 | static int mhz_3288_power(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 442 | { | 
 | 443 |     struct net_device *dev = link->priv; | 
 | 444 |     struct smc_private *smc = netdev_priv(dev); | 
 | 445 |     u_char tmp; | 
 | 446 |  | 
 | 447 |     /* Read the ISR twice... */ | 
 | 448 |     readb(smc->base+MEGAHERTZ_ISR); | 
 | 449 |     udelay(5); | 
 | 450 |     readb(smc->base+MEGAHERTZ_ISR); | 
 | 451 |  | 
 | 452 |     /* Pause 200ms... */ | 
 | 453 |     mdelay(200); | 
 | 454 |  | 
 | 455 |     /* Now read and write the COR... */ | 
 | 456 |     tmp = readb(smc->base + link->conf.ConfigBase + CISREG_COR); | 
 | 457 |     udelay(5); | 
 | 458 |     writeb(tmp, smc->base + link->conf.ConfigBase + CISREG_COR); | 
 | 459 |  | 
 | 460 |     return 0; | 
 | 461 | } | 
 | 462 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 463 | static int mhz_mfc_config(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 464 | { | 
 | 465 |     struct net_device *dev = link->priv; | 
 | 466 |     struct smc_private *smc = netdev_priv(dev); | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 467 |     struct smc_cfg_mem *cfg_mem; | 
 | 468 |     tuple_t *tuple; | 
 | 469 |     cisparse_t *parse; | 
 | 470 |     cistpl_cftable_entry_t *cf; | 
 | 471 |     u_char *buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 472 |     win_req_t req; | 
 | 473 |     memreq_t mem; | 
 | 474 |     int i, k; | 
 | 475 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 476 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 477 |     if (!cfg_mem) | 
 | 478 |         return CS_OUT_OF_RESOURCE; | 
 | 479 |  | 
 | 480 |     tuple = &cfg_mem->tuple; | 
 | 481 |     parse = &cfg_mem->parse; | 
 | 482 |     cf = &parse->cftable_entry; | 
 | 483 |     buf = cfg_mem->buf; | 
 | 484 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 485 |     link->conf.Attributes |= CONF_ENABLE_SPKR; | 
 | 486 |     link->conf.Status = CCSR_AUDIO_ENA; | 
 | 487 |     link->irq.Attributes = | 
 | 488 | 	IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT; | 
 | 489 |     link->io.IOAddrLines = 16; | 
 | 490 |     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; | 
 | 491 |     link->io.NumPorts2 = 8; | 
 | 492 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 493 |     tuple->Attributes = tuple->TupleOffset = 0; | 
 | 494 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 495 |     tuple->TupleDataMax = 255; | 
 | 496 |     tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 497 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 498 |     i = first_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 499 |     /* The Megahertz combo cards have modem-like CIS entries, so | 
 | 500 |        we have to explicitly try a bunch of port combinations. */ | 
 | 501 |     while (i == CS_SUCCESS) { | 
 | 502 | 	link->conf.ConfigIndex = cf->index; | 
 | 503 | 	link->io.BasePort2 = cf->io.win[0].base; | 
 | 504 | 	for (k = 0; k < 0x400; k += 0x10) { | 
 | 505 | 	    if (k & 0x80) continue; | 
 | 506 | 	    link->io.BasePort1 = k ^ 0x300; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 507 | 	    i = pcmcia_request_io(link, &link->io); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 508 | 	    if (i == CS_SUCCESS) break; | 
 | 509 | 	} | 
 | 510 | 	if (i == CS_SUCCESS) break; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 511 | 	i = next_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 512 |     } | 
 | 513 |     if (i != CS_SUCCESS) | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 514 | 	goto free_cfg_mem; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 515 |     dev->base_addr = link->io.BasePort1; | 
 | 516 |  | 
 | 517 |     /* Allocate a memory window, for accessing the ISR */ | 
 | 518 |     req.Attributes = WIN_DATA_WIDTH_8|WIN_MEMORY_TYPE_AM|WIN_ENABLE; | 
 | 519 |     req.Base = req.Size = 0; | 
 | 520 |     req.AccessSpeed = 0; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 521 |     i = pcmcia_request_window(&link, &req, &link->win); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 522 |     if (i != CS_SUCCESS) | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 523 | 	goto free_cfg_mem; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 524 |     smc->base = ioremap(req.Base, req.Size); | 
 | 525 |     mem.CardOffset = mem.Page = 0; | 
 | 526 |     if (smc->manfid == MANFID_MOTOROLA) | 
 | 527 | 	mem.CardOffset = link->conf.ConfigBase; | 
 | 528 |     i = pcmcia_map_mem_page(link->win, &mem); | 
 | 529 |  | 
 | 530 |     if ((i == CS_SUCCESS) | 
 | 531 | 	&& (smc->manfid == MANFID_MEGAHERTZ) | 
 | 532 | 	&& (smc->cardid == PRODID_MEGAHERTZ_EM3288)) | 
 | 533 | 	mhz_3288_power(link); | 
 | 534 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 535 | free_cfg_mem: | 
 | 536 |     kfree(cfg_mem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 537 |     return i; | 
 | 538 | } | 
 | 539 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 540 | static int mhz_setup(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 541 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 542 |     struct net_device *dev = link->priv; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 543 |     struct smc_cfg_mem *cfg_mem; | 
 | 544 |     tuple_t *tuple; | 
 | 545 |     cisparse_t *parse; | 
 | 546 |     u_char *buf, *station_addr; | 
 | 547 |     int rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 548 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 549 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 550 |     if (!cfg_mem) | 
 | 551 | 	return -1; | 
 | 552 |  | 
 | 553 |     tuple = &cfg_mem->tuple; | 
 | 554 |     parse = &cfg_mem->parse; | 
 | 555 |     buf = cfg_mem->buf; | 
 | 556 |  | 
 | 557 |     tuple->Attributes = tuple->TupleOffset = 0; | 
 | 558 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 559 |     tuple->TupleDataMax = 255; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 560 |  | 
 | 561 |     /* Read the station address from the CIS.  It is stored as the last | 
 | 562 |        (fourth) string in the Version 1 Version/ID tuple. */ | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 563 |     tuple->DesiredTuple = CISTPL_VERS_1; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 564 |     if (first_tuple(link, tuple, parse) != CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 565 | 	rc = -1; | 
 | 566 | 	goto free_cfg_mem; | 
 | 567 |     } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 568 |     /* Ugh -- the EM1144 card has two VERS_1 tuples!?! */ | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 569 |     if (next_tuple(link, tuple, parse) != CS_SUCCESS) | 
 | 570 | 	first_tuple(link, tuple, parse); | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 571 |     if (parse->version_1.ns > 3) { | 
 | 572 | 	station_addr = parse->version_1.str + parse->version_1.ofs[3]; | 
 | 573 | 	if (cvt_ascii_address(dev, station_addr) == 0) { | 
 | 574 | 		rc = 0; | 
 | 575 | 		goto free_cfg_mem; | 
 | 576 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 577 |     } | 
 | 578 |  | 
 | 579 |     /* Another possibility: for the EM3288, in a special tuple */ | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 580 |     tuple->DesiredTuple = 0x81; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 581 |     if (pcmcia_get_first_tuple(link, tuple) != CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 582 | 	rc = -1; | 
 | 583 | 	goto free_cfg_mem; | 
 | 584 |     } | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 585 |     if (pcmcia_get_tuple_data(link, tuple) != CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 586 | 	rc = -1; | 
 | 587 | 	goto free_cfg_mem; | 
 | 588 |     } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 589 |     buf[12] = '\0'; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 590 |     if (cvt_ascii_address(dev, buf) == 0) { | 
 | 591 | 	rc = 0; | 
 | 592 | 	goto free_cfg_mem; | 
 | 593 |    } | 
 | 594 |     rc = -1; | 
 | 595 | free_cfg_mem: | 
 | 596 |    kfree(cfg_mem); | 
 | 597 |    return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 598 | } | 
 | 599 |  | 
 | 600 | /*====================================================================== | 
 | 601 |  | 
 | 602 |     Configuration stuff for the Motorola Mariner | 
 | 603 |  | 
 | 604 |     mot_config() writes directly to the Mariner configuration | 
 | 605 |     registers because the CIS is just bogus. | 
 | 606 |  | 
 | 607 | ======================================================================*/ | 
 | 608 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 609 | static void mot_config(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 610 | { | 
 | 611 |     struct net_device *dev = link->priv; | 
 | 612 |     struct smc_private *smc = netdev_priv(dev); | 
 | 613 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 614 |     kio_addr_t iouart = link->io.BasePort2; | 
 | 615 |  | 
 | 616 |     /* Set UART base address and force map with COR bit 1 */ | 
 | 617 |     writeb(iouart & 0xff,        smc->base + MOT_UART + CISREG_IOBASE_0); | 
 | 618 |     writeb((iouart >> 8) & 0xff, smc->base + MOT_UART + CISREG_IOBASE_1); | 
 | 619 |     writeb(MOT_NORMAL,           smc->base + MOT_UART + CISREG_COR); | 
 | 620 |  | 
 | 621 |     /* Set SMC base address and force map with COR bit 1 */ | 
 | 622 |     writeb(ioaddr & 0xff,        smc->base + MOT_LAN + CISREG_IOBASE_0); | 
 | 623 |     writeb((ioaddr >> 8) & 0xff, smc->base + MOT_LAN + CISREG_IOBASE_1); | 
 | 624 |     writeb(MOT_NORMAL,           smc->base + MOT_LAN + CISREG_COR); | 
 | 625 |  | 
 | 626 |     /* Wait for things to settle down */ | 
 | 627 |     mdelay(100); | 
 | 628 | } | 
 | 629 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 630 | static int mot_setup(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 631 | { | 
 | 632 |     struct net_device *dev = link->priv; | 
 | 633 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 634 |     int i, wait, loop; | 
 | 635 |     u_int addr; | 
 | 636 |  | 
 | 637 |     /* Read Ethernet address from Serial EEPROM */ | 
 | 638 |  | 
 | 639 |     for (i = 0; i < 3; i++) { | 
 | 640 | 	SMC_SELECT_BANK(2); | 
 | 641 | 	outw(MOT_EEPROM + i, ioaddr + POINTER); | 
 | 642 | 	SMC_SELECT_BANK(1); | 
 | 643 | 	outw((CTL_RELOAD | CTL_EE_SELECT), ioaddr + CONTROL); | 
 | 644 |  | 
 | 645 | 	for (loop = wait = 0; loop < 200; loop++) { | 
 | 646 | 	    udelay(10); | 
 | 647 | 	    wait = ((CTL_RELOAD | CTL_STORE) & inw(ioaddr + CONTROL)); | 
 | 648 | 	    if (wait == 0) break; | 
 | 649 | 	} | 
 | 650 | 	 | 
 | 651 | 	if (wait) | 
 | 652 | 	    return -1; | 
 | 653 | 	 | 
 | 654 | 	addr = inw(ioaddr + GENERAL); | 
 | 655 | 	dev->dev_addr[2*i]   = addr & 0xff; | 
 | 656 | 	dev->dev_addr[2*i+1] = (addr >> 8) & 0xff; | 
 | 657 |     } | 
 | 658 |  | 
 | 659 |     return 0; | 
 | 660 | } | 
 | 661 |  | 
 | 662 | /*====================================================================*/ | 
 | 663 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 664 | static int smc_config(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 665 | { | 
 | 666 |     struct net_device *dev = link->priv; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 667 |     struct smc_cfg_mem *cfg_mem; | 
 | 668 |     tuple_t *tuple; | 
 | 669 |     cisparse_t *parse; | 
 | 670 |     cistpl_cftable_entry_t *cf; | 
 | 671 |     u_char *buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 672 |     int i; | 
 | 673 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 674 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 675 |     if (!cfg_mem) | 
 | 676 | 	return CS_OUT_OF_RESOURCE; | 
 | 677 |  | 
 | 678 |     tuple = &cfg_mem->tuple; | 
 | 679 |     parse = &cfg_mem->parse; | 
 | 680 |     cf = &parse->cftable_entry; | 
 | 681 |     buf = cfg_mem->buf; | 
 | 682 |  | 
 | 683 |     tuple->Attributes = tuple->TupleOffset = 0; | 
 | 684 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 685 |     tuple->TupleDataMax = 255; | 
 | 686 |     tuple->DesiredTuple = CISTPL_CFTABLE_ENTRY; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 687 |  | 
 | 688 |     link->io.NumPorts1 = 16; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 689 |     i = first_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 690 |     while (i != CS_NO_MORE_ITEMS) { | 
 | 691 | 	if (i == CS_SUCCESS) { | 
 | 692 | 	    link->conf.ConfigIndex = cf->index; | 
 | 693 | 	    link->io.BasePort1 = cf->io.win[0].base; | 
 | 694 | 	    link->io.IOAddrLines = cf->io.flags & CISTPL_IO_LINES_MASK; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 695 | 	    i = pcmcia_request_io(link, &link->io); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 696 | 	    if (i == CS_SUCCESS) break; | 
 | 697 | 	} | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 698 | 	i = next_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 699 |     } | 
 | 700 |     if (i == CS_SUCCESS) | 
 | 701 | 	dev->base_addr = link->io.BasePort1; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 702 |  | 
 | 703 |     kfree(cfg_mem); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 704 |     return i; | 
 | 705 | } | 
 | 706 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 707 | static int smc_setup(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 708 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 709 |     struct net_device *dev = link->priv; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 710 |     struct smc_cfg_mem *cfg_mem; | 
 | 711 |     tuple_t *tuple; | 
 | 712 |     cisparse_t *parse; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 713 |     cistpl_lan_node_id_t *node_id; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 714 |     u_char *buf, *station_addr; | 
 | 715 |     int i, rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 716 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 717 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 718 |     if (!cfg_mem) | 
 | 719 | 	return CS_OUT_OF_RESOURCE; | 
 | 720 |  | 
 | 721 |     tuple = &cfg_mem->tuple; | 
 | 722 |     parse = &cfg_mem->parse; | 
 | 723 |     buf = cfg_mem->buf; | 
 | 724 |  | 
 | 725 |     tuple->Attributes = tuple->TupleOffset = 0; | 
 | 726 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 727 |     tuple->TupleDataMax = 255; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 728 |  | 
 | 729 |     /* Check for a LAN function extension tuple */ | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 730 |     tuple->DesiredTuple = CISTPL_FUNCE; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 731 |     i = first_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 732 |     while (i == CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 733 | 	if (parse->funce.type == CISTPL_FUNCE_LAN_NODE_ID) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 734 | 	    break; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 735 | 	i = next_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 736 |     } | 
 | 737 |     if (i == CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 738 | 	node_id = (cistpl_lan_node_id_t *)parse->funce.data; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 739 | 	if (node_id->nb == 6) { | 
 | 740 | 	    for (i = 0; i < 6; i++) | 
 | 741 | 		dev->dev_addr[i] = node_id->id[i]; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 742 | 	    rc = 0; | 
 | 743 | 	    goto free_cfg_mem; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 744 | 	} | 
 | 745 |     } | 
 | 746 |     /* Try the third string in the Version 1 Version/ID tuple. */ | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 747 |     tuple->DesiredTuple = CISTPL_VERS_1; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 748 |     if (first_tuple(link, tuple, parse) != CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 749 | 	rc = -1; | 
 | 750 | 	goto free_cfg_mem; | 
 | 751 |     } | 
 | 752 |     station_addr = parse->version_1.str + parse->version_1.ofs[2]; | 
 | 753 |     if (cvt_ascii_address(dev, station_addr) == 0) { | 
 | 754 | 	rc = 0; | 
 | 755 | 	goto free_cfg_mem; | 
 | 756 |     } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 757 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 758 |     rc = -1; | 
 | 759 | free_cfg_mem: | 
 | 760 |     kfree(cfg_mem); | 
 | 761 |     return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 762 | } | 
 | 763 |  | 
 | 764 | /*====================================================================*/ | 
 | 765 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 766 | static int osi_config(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 767 | { | 
 | 768 |     struct net_device *dev = link->priv; | 
| Arjan van de Ven | f71e130 | 2006-03-03 21:33:57 -0500 | [diff] [blame] | 769 |     static const kio_addr_t com[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 }; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 770 |     int i, j; | 
 | 771 |  | 
 | 772 |     link->conf.Attributes |= CONF_ENABLE_SPKR; | 
 | 773 |     link->conf.Status = CCSR_AUDIO_ENA; | 
 | 774 |     link->irq.Attributes = | 
 | 775 | 	IRQ_TYPE_DYNAMIC_SHARING|IRQ_FIRST_SHARED|IRQ_HANDLE_PRESENT; | 
 | 776 |     link->io.NumPorts1 = 64; | 
 | 777 |     link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; | 
 | 778 |     link->io.NumPorts2 = 8; | 
 | 779 |     link->io.IOAddrLines = 16; | 
 | 780 |  | 
 | 781 |     /* Enable Hard Decode, LAN, Modem */ | 
 | 782 |     link->conf.ConfigIndex = 0x23; | 
 | 783 |  | 
 | 784 |     for (i = j = 0; j < 4; j++) { | 
 | 785 | 	link->io.BasePort2 = com[j]; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 786 | 	i = pcmcia_request_io(link, &link->io); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 787 | 	if (i == CS_SUCCESS) break; | 
 | 788 |     } | 
 | 789 |     if (i != CS_SUCCESS) { | 
 | 790 | 	/* Fallback: turn off hard decode */ | 
 | 791 | 	link->conf.ConfigIndex = 0x03; | 
 | 792 | 	link->io.NumPorts2 = 0; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 793 | 	i = pcmcia_request_io(link, &link->io); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 794 |     } | 
 | 795 |     dev->base_addr = link->io.BasePort1 + 0x10; | 
 | 796 |     return i; | 
 | 797 | } | 
 | 798 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 799 | static int osi_setup(struct pcmcia_device *link, u_short manfid, u_short cardid) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 800 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 801 |     struct net_device *dev = link->priv; | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 802 |     struct smc_cfg_mem *cfg_mem; | 
 | 803 |     tuple_t *tuple; | 
 | 804 |     u_char *buf; | 
 | 805 |     int i, rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 806 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 807 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 808 |     if (!cfg_mem) | 
 | 809 |         return -1; | 
 | 810 |  | 
 | 811 |     tuple = &cfg_mem->tuple; | 
 | 812 |     buf = cfg_mem->buf; | 
 | 813 |  | 
 | 814 |     tuple->Attributes = TUPLE_RETURN_COMMON; | 
 | 815 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 816 |     tuple->TupleDataMax = 255; | 
 | 817 |     tuple->TupleOffset = 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 818 |  | 
 | 819 |     /* Read the station address from tuple 0x90, subtuple 0x04 */ | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 820 |     tuple->DesiredTuple = 0x90; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 821 |     i = pcmcia_get_first_tuple(link, tuple); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 822 |     while (i == CS_SUCCESS) { | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 823 | 	i = pcmcia_get_tuple_data(link, tuple); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 824 | 	if ((i != CS_SUCCESS) || (buf[0] == 0x04)) | 
 | 825 | 	    break; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 826 | 	i = pcmcia_get_next_tuple(link, tuple); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 827 |     } | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 828 |     if (i != CS_SUCCESS) { | 
 | 829 | 	rc = -1; | 
 | 830 | 	goto free_cfg_mem; | 
 | 831 |     } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 832 |     for (i = 0; i < 6; i++) | 
 | 833 | 	dev->dev_addr[i] = buf[i+2]; | 
 | 834 |  | 
 | 835 |     if (((manfid == MANFID_OSITECH) && | 
 | 836 | 	 (cardid == PRODID_OSITECH_SEVEN)) || | 
 | 837 | 	((manfid == MANFID_PSION) && | 
 | 838 | 	 (cardid == PRODID_PSION_NET100))) { | 
 | 839 | 	/* Download the Seven of Diamonds firmware */ | 
 | 840 | 	for (i = 0; i < sizeof(__Xilinx7OD); i++) { | 
 | 841 | 	    outb(__Xilinx7OD[i], link->io.BasePort1+2); | 
 | 842 | 	    udelay(50); | 
 | 843 | 	} | 
 | 844 |     } else if (manfid == MANFID_OSITECH) { | 
 | 845 | 	/* Make sure both functions are powered up */ | 
 | 846 | 	set_bits(0x300, link->io.BasePort1 + OSITECH_AUI_PWR); | 
 | 847 | 	/* Now, turn on the interrupt for both card functions */ | 
 | 848 | 	set_bits(0x300, link->io.BasePort1 + OSITECH_RESET_ISR); | 
 | 849 | 	DEBUG(2, "AUI/PWR: %4.4x RESET/ISR: %4.4x\n", | 
 | 850 | 	      inw(link->io.BasePort1 + OSITECH_AUI_PWR), | 
 | 851 | 	      inw(link->io.BasePort1 + OSITECH_RESET_ISR)); | 
 | 852 |     } | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 853 |     rc = 0; | 
 | 854 | free_cfg_mem: | 
 | 855 |    kfree(cfg_mem); | 
 | 856 |    return rc; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 857 | } | 
 | 858 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 859 | static int smc91c92_suspend(struct pcmcia_device *link) | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 860 | { | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 861 | 	struct net_device *dev = link->priv; | 
 | 862 |  | 
| Dominik Brodowski | e2d4096 | 2006-03-02 00:09:29 +0100 | [diff] [blame] | 863 | 	if (link->open) | 
| Dominik Brodowski | 4bbed52 | 2006-01-15 11:18:12 +0100 | [diff] [blame] | 864 | 		netif_device_detach(dev); | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 865 |  | 
 | 866 | 	return 0; | 
 | 867 | } | 
 | 868 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 869 | static int smc91c92_resume(struct pcmcia_device *link) | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 870 | { | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 871 | 	struct net_device *dev = link->priv; | 
 | 872 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 873 | 	int i; | 
 | 874 |  | 
| Dominik Brodowski | e2d4096 | 2006-03-02 00:09:29 +0100 | [diff] [blame] | 875 | 	if ((smc->manfid == MANFID_MEGAHERTZ) && | 
 | 876 | 	    (smc->cardid == PRODID_MEGAHERTZ_EM3288)) | 
 | 877 | 		mhz_3288_power(link); | 
 | 878 | 	if (smc->manfid == MANFID_MOTOROLA) | 
 | 879 | 		mot_config(link); | 
 | 880 | 	if ((smc->manfid == MANFID_OSITECH) && | 
 | 881 | 	    (smc->cardid != PRODID_OSITECH_SEVEN)) { | 
 | 882 | 		/* Power up the card and enable interrupts */ | 
 | 883 | 		set_bits(0x0300, dev->base_addr-0x10+OSITECH_AUI_PWR); | 
 | 884 | 		set_bits(0x0300, dev->base_addr-0x10+OSITECH_RESET_ISR); | 
 | 885 | 	} | 
 | 886 | 	if (((smc->manfid == MANFID_OSITECH) && | 
 | 887 | 	     (smc->cardid == PRODID_OSITECH_SEVEN)) || | 
 | 888 | 	    ((smc->manfid == MANFID_PSION) && | 
 | 889 | 	     (smc->cardid == PRODID_PSION_NET100))) { | 
 | 890 | 		/* Download the Seven of Diamonds firmware */ | 
 | 891 | 		for (i = 0; i < sizeof(__Xilinx7OD); i++) { | 
 | 892 | 			outb(__Xilinx7OD[i], link->io.BasePort1+2); | 
 | 893 | 			udelay(50); | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 894 | 		} | 
| Dominik Brodowski | e2d4096 | 2006-03-02 00:09:29 +0100 | [diff] [blame] | 895 | 	} | 
 | 896 | 	if (link->open) { | 
 | 897 | 		smc_reset(dev); | 
 | 898 | 		netif_device_attach(dev); | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 899 | 	} | 
 | 900 |  | 
 | 901 | 	return 0; | 
 | 902 | } | 
 | 903 |  | 
 | 904 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 905 | /*====================================================================== | 
 | 906 |  | 
 | 907 |     This verifies that the chip is some SMC91cXX variant, and returns | 
 | 908 |     the revision code if successful.  Otherwise, it returns -ENODEV. | 
 | 909 |  | 
 | 910 | ======================================================================*/ | 
 | 911 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 912 | static int check_sig(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 913 | { | 
 | 914 |     struct net_device *dev = link->priv; | 
 | 915 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 916 |     int width; | 
 | 917 |     u_short s; | 
 | 918 |  | 
 | 919 |     SMC_SELECT_BANK(1); | 
 | 920 |     if (inw(ioaddr + BANK_SELECT) >> 8 != 0x33) { | 
 | 921 | 	/* Try powering up the chip */ | 
 | 922 | 	outw(0, ioaddr + CONTROL); | 
 | 923 | 	mdelay(55); | 
 | 924 |     } | 
 | 925 |  | 
 | 926 |     /* Try setting bus width */ | 
 | 927 |     width = (link->io.Attributes1 == IO_DATA_PATH_WIDTH_AUTO); | 
 | 928 |     s = inb(ioaddr + CONFIG); | 
 | 929 |     if (width) | 
 | 930 | 	s |= CFG_16BIT; | 
 | 931 |     else | 
 | 932 | 	s &= ~CFG_16BIT; | 
 | 933 |     outb(s, ioaddr + CONFIG); | 
 | 934 |  | 
 | 935 |     /* Check Base Address Register to make sure bus width is OK */ | 
 | 936 |     s = inw(ioaddr + BASE_ADDR); | 
 | 937 |     if ((inw(ioaddr + BANK_SELECT) >> 8 == 0x33) && | 
 | 938 | 	((s >> 8) != (s & 0xff))) { | 
 | 939 | 	SMC_SELECT_BANK(3); | 
 | 940 | 	s = inw(ioaddr + REVISION); | 
 | 941 | 	return (s & 0xff); | 
 | 942 |     } | 
 | 943 |  | 
 | 944 |     if (width) { | 
| Dominik Brodowski | 4bbed52 | 2006-01-15 11:18:12 +0100 | [diff] [blame] | 945 | 	    modconf_t mod = { | 
 | 946 | 		    .Attributes = CONF_IO_CHANGE_WIDTH, | 
 | 947 | 	    }; | 
 | 948 | 	    printk(KERN_INFO "smc91c92_cs: using 8-bit IO window.\n"); | 
| Dominik Brodowski | 50db3fd | 2006-01-15 10:05:19 +0100 | [diff] [blame] | 949 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 950 | 	    smc91c92_suspend(link); | 
 | 951 | 	    pcmcia_modify_configuration(link, &mod); | 
 | 952 | 	    smc91c92_resume(link); | 
| Dominik Brodowski | 4bbed52 | 2006-01-15 11:18:12 +0100 | [diff] [blame] | 953 | 	    return check_sig(link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 954 |     } | 
 | 955 |     return -ENODEV; | 
 | 956 | } | 
 | 957 |  | 
 | 958 | /*====================================================================== | 
 | 959 |  | 
 | 960 |     smc91c92_config() is scheduled to run after a CARD_INSERTION event | 
 | 961 |     is received, to configure the PCMCIA socket, and to make the | 
 | 962 |     ethernet device available to the system. | 
 | 963 |  | 
 | 964 | ======================================================================*/ | 
 | 965 |  | 
 | 966 | #define CS_EXIT_TEST(ret, svc, label) \ | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 967 | if (ret != CS_SUCCESS) { cs_error(link, svc, ret); goto label; } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 968 |  | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 969 | static int smc91c92_config(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 970 | { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 971 |     struct net_device *dev = link->priv; | 
 | 972 |     struct smc_private *smc = netdev_priv(dev); | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 973 |     struct smc_cfg_mem *cfg_mem; | 
 | 974 |     tuple_t *tuple; | 
 | 975 |     cisparse_t *parse; | 
 | 976 |     u_char *buf; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 977 |     char *name; | 
 | 978 |     int i, j, rev; | 
 | 979 |     kio_addr_t ioaddr; | 
 | 980 |     u_long mir; | 
 | 981 |  | 
 | 982 |     DEBUG(0, "smc91c92_config(0x%p)\n", link); | 
 | 983 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 984 |     cfg_mem = kmalloc(sizeof(struct smc_cfg_mem), GFP_KERNEL); | 
 | 985 |     if (!cfg_mem) | 
 | 986 | 	goto config_failed; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 987 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 988 |     tuple = &cfg_mem->tuple; | 
 | 989 |     parse = &cfg_mem->parse; | 
 | 990 |     buf = cfg_mem->buf; | 
 | 991 |  | 
 | 992 |     tuple->Attributes = tuple->TupleOffset = 0; | 
 | 993 |     tuple->TupleData = (cisdata_t *)buf; | 
 | 994 |     tuple->TupleDataMax = 64; | 
 | 995 |  | 
 | 996 |     tuple->DesiredTuple = CISTPL_CONFIG; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 997 |     i = first_tuple(link, tuple, parse); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 998 |     CS_EXIT_TEST(i, ParseTuple, config_failed); | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 999 |     link->conf.ConfigBase = parse->config.base; | 
 | 1000 |     link->conf.Present = parse->config.rmask[0]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1001 |  | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 1002 |     tuple->DesiredTuple = CISTPL_MANFID; | 
 | 1003 |     tuple->Attributes = TUPLE_RETURN_COMMON; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1004 |     if (first_tuple(link, tuple, parse) == CS_SUCCESS) { | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 1005 | 	smc->manfid = parse->manfid.manf; | 
 | 1006 | 	smc->cardid = parse->manfid.card; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1007 |     } | 
 | 1008 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1009 |     if ((smc->manfid == MANFID_OSITECH) && | 
 | 1010 | 	(smc->cardid != PRODID_OSITECH_SEVEN)) { | 
 | 1011 | 	i = osi_config(link); | 
 | 1012 |     } else if ((smc->manfid == MANFID_MOTOROLA) || | 
 | 1013 | 	       ((smc->manfid == MANFID_MEGAHERTZ) && | 
 | 1014 | 		((smc->cardid == PRODID_MEGAHERTZ_VARIOUS) || | 
 | 1015 | 		 (smc->cardid == PRODID_MEGAHERTZ_EM3288)))) { | 
 | 1016 | 	i = mhz_mfc_config(link); | 
 | 1017 |     } else { | 
 | 1018 | 	i = smc_config(link); | 
 | 1019 |     } | 
 | 1020 |     CS_EXIT_TEST(i, RequestIO, config_failed); | 
 | 1021 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1022 |     i = pcmcia_request_irq(link, &link->irq); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1023 |     CS_EXIT_TEST(i, RequestIRQ, config_failed); | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1024 |     i = pcmcia_request_configuration(link, &link->conf); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1025 |     CS_EXIT_TEST(i, RequestConfiguration, config_failed); | 
 | 1026 |  | 
 | 1027 |     if (smc->manfid == MANFID_MOTOROLA) | 
 | 1028 | 	mot_config(link); | 
 | 1029 |  | 
 | 1030 |     dev->irq = link->irq.AssignedIRQ; | 
 | 1031 |  | 
 | 1032 |     if ((if_port >= 0) && (if_port <= 2)) | 
 | 1033 | 	dev->if_port = if_port; | 
 | 1034 |     else | 
 | 1035 | 	printk(KERN_NOTICE "smc91c92_cs: invalid if_port requested\n"); | 
 | 1036 |  | 
 | 1037 |     switch (smc->manfid) { | 
 | 1038 |     case MANFID_OSITECH: | 
 | 1039 |     case MANFID_PSION: | 
 | 1040 | 	i = osi_setup(link, smc->manfid, smc->cardid); break; | 
 | 1041 |     case MANFID_SMC: | 
 | 1042 |     case MANFID_NEW_MEDIA: | 
 | 1043 | 	i = smc_setup(link); break; | 
 | 1044 |     case 0x128: /* For broken Megahertz cards */ | 
 | 1045 |     case MANFID_MEGAHERTZ: | 
 | 1046 | 	i = mhz_setup(link); break; | 
 | 1047 |     case MANFID_MOTOROLA: | 
 | 1048 |     default: /* get the hw address from EEPROM */ | 
 | 1049 | 	i = mot_setup(link); break; | 
 | 1050 |     } | 
 | 1051 |  | 
 | 1052 |     if (i != 0) { | 
 | 1053 | 	printk(KERN_NOTICE "smc91c92_cs: Unable to find hardware address.\n"); | 
 | 1054 | 	goto config_undo; | 
 | 1055 |     } | 
 | 1056 |  | 
 | 1057 |     smc->duplex = 0; | 
 | 1058 |     smc->rx_ovrn = 0; | 
 | 1059 |  | 
 | 1060 |     rev = check_sig(link); | 
 | 1061 |     name = "???"; | 
 | 1062 |     if (rev > 0) | 
 | 1063 | 	switch (rev >> 4) { | 
 | 1064 | 	case 3: name = "92"; break; | 
 | 1065 | 	case 4: name = ((rev & 15) >= 6) ? "96" : "94"; break; | 
 | 1066 | 	case 5: name = "95"; break; | 
 | 1067 | 	case 7: name = "100"; break; | 
 | 1068 | 	case 8: name = "100-FD"; break; | 
 | 1069 | 	case 9: name = "110"; break; | 
 | 1070 | 	} | 
 | 1071 |  | 
 | 1072 |     ioaddr = dev->base_addr; | 
 | 1073 |     if (rev > 0) { | 
 | 1074 | 	u_long mcr; | 
 | 1075 | 	SMC_SELECT_BANK(0); | 
 | 1076 | 	mir = inw(ioaddr + MEMINFO) & 0xff; | 
 | 1077 | 	if (mir == 0xff) mir++; | 
 | 1078 | 	/* Get scale factor for memory size */ | 
 | 1079 | 	mcr = ((rev >> 4) > 3) ? inw(ioaddr + MEMCFG) : 0x0200; | 
 | 1080 | 	mir *= 128 * (1<<((mcr >> 9) & 7)); | 
 | 1081 | 	SMC_SELECT_BANK(1); | 
 | 1082 | 	smc->cfg = inw(ioaddr + CONFIG) & ~CFG_AUI_SELECT; | 
 | 1083 | 	smc->cfg |= CFG_NO_WAIT | CFG_16BIT | CFG_STATIC; | 
 | 1084 | 	if (smc->manfid == MANFID_OSITECH) | 
 | 1085 | 	    smc->cfg |= CFG_IRQ_SEL_1 | CFG_IRQ_SEL_0; | 
 | 1086 | 	if ((rev >> 4) >= 7) | 
 | 1087 | 	    smc->cfg |= CFG_MII_SELECT; | 
 | 1088 |     } else | 
 | 1089 | 	mir = 0; | 
 | 1090 |  | 
 | 1091 |     if (smc->cfg & CFG_MII_SELECT) { | 
 | 1092 | 	SMC_SELECT_BANK(3); | 
 | 1093 |  | 
 | 1094 | 	for (i = 0; i < 32; i++) { | 
 | 1095 | 	    j = mdio_read(dev, i, 1); | 
 | 1096 | 	    if ((j != 0) && (j != 0xffff)) break; | 
 | 1097 | 	} | 
 | 1098 | 	smc->mii_if.phy_id = (i < 32) ? i : -1; | 
 | 1099 |  | 
 | 1100 | 	SMC_SELECT_BANK(0); | 
 | 1101 |     } | 
 | 1102 |  | 
| Dominik Brodowski | fd23823 | 2006-03-05 10:45:09 +0100 | [diff] [blame] | 1103 |     link->dev_node = &smc->node; | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1104 |     SET_NETDEV_DEV(dev, &handle_to_dev(link)); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1105 |  | 
 | 1106 |     if (register_netdev(dev) != 0) { | 
 | 1107 | 	printk(KERN_ERR "smc91c92_cs: register_netdev() failed\n"); | 
| Dominik Brodowski | fd23823 | 2006-03-05 10:45:09 +0100 | [diff] [blame] | 1108 | 	link->dev_node = NULL; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1109 | 	goto config_undo; | 
 | 1110 |     } | 
 | 1111 |  | 
 | 1112 |     strcpy(smc->node.dev_name, dev->name); | 
 | 1113 |  | 
 | 1114 |     printk(KERN_INFO "%s: smc91c%s rev %d: io %#3lx, irq %d, " | 
 | 1115 | 	   "hw_addr ", dev->name, name, (rev & 0x0f), dev->base_addr, | 
 | 1116 | 	   dev->irq); | 
 | 1117 |     for (i = 0; i < 6; i++) | 
 | 1118 | 	printk("%02X%s", dev->dev_addr[i], ((i<5) ? ":" : "\n")); | 
 | 1119 |  | 
 | 1120 |     if (rev > 0) { | 
 | 1121 | 	if (mir & 0x3ff) | 
 | 1122 | 	    printk(KERN_INFO "  %lu byte", mir); | 
 | 1123 | 	else | 
 | 1124 | 	    printk(KERN_INFO "  %lu kb", mir>>10); | 
 | 1125 | 	printk(" buffer, %s xcvr\n", (smc->cfg & CFG_MII_SELECT) ? | 
 | 1126 | 	       "MII" : if_names[dev->if_port]); | 
 | 1127 |     } | 
 | 1128 |  | 
 | 1129 |     if (smc->cfg & CFG_MII_SELECT) { | 
 | 1130 | 	if (smc->mii_if.phy_id != -1) { | 
 | 1131 | 	    DEBUG(0, "  MII transceiver at index %d, status %x.\n", | 
 | 1132 | 		  smc->mii_if.phy_id, j); | 
 | 1133 | 	} else { | 
 | 1134 |     	    printk(KERN_NOTICE "  No MII transceivers found!\n"); | 
 | 1135 | 	} | 
 | 1136 |     } | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 1137 |     kfree(cfg_mem); | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 1138 |     return 0; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1139 |  | 
 | 1140 | config_undo: | 
 | 1141 |     unregister_netdev(dev); | 
 | 1142 | config_failed:			/* CS_EXIT_TEST() calls jump to here... */ | 
 | 1143 |     smc91c92_release(link); | 
| Yum Rayan | 4638aef4 | 2005-05-05 15:14:10 -0700 | [diff] [blame] | 1144 |     kfree(cfg_mem); | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 1145 |     return -ENODEV; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1146 | } /* smc91c92_config */ | 
 | 1147 |  | 
 | 1148 | /*====================================================================== | 
 | 1149 |  | 
 | 1150 |     After a card is removed, smc91c92_release() will unregister the net | 
 | 1151 |     device, and release the PCMCIA configuration.  If the device is | 
 | 1152 |     still open, this will be postponed until it is closed. | 
 | 1153 |  | 
 | 1154 | ======================================================================*/ | 
 | 1155 |  | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1156 | static void smc91c92_release(struct pcmcia_device *link) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1157 | { | 
| Dominik Brodowski | 5f2a71f | 2006-01-15 09:32:39 +0100 | [diff] [blame] | 1158 | 	DEBUG(0, "smc91c92_release(0x%p)\n", link); | 
 | 1159 | 	if (link->win) { | 
 | 1160 | 		struct net_device *dev = link->priv; | 
 | 1161 | 		struct smc_private *smc = netdev_priv(dev); | 
 | 1162 | 		iounmap(smc->base); | 
 | 1163 | 	} | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1164 | 	pcmcia_disable_device(link); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1165 | } | 
 | 1166 |  | 
 | 1167 | /*====================================================================== | 
 | 1168 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1169 |     MII interface support for SMC91cXX based cards | 
 | 1170 | ======================================================================*/ | 
 | 1171 |  | 
 | 1172 | #define MDIO_SHIFT_CLK		0x04 | 
 | 1173 | #define MDIO_DATA_OUT		0x01 | 
 | 1174 | #define MDIO_DIR_WRITE		0x08 | 
 | 1175 | #define MDIO_DATA_WRITE0	(MDIO_DIR_WRITE) | 
 | 1176 | #define MDIO_DATA_WRITE1	(MDIO_DIR_WRITE | MDIO_DATA_OUT) | 
 | 1177 | #define MDIO_DATA_READ		0x02 | 
 | 1178 |  | 
 | 1179 | static void mdio_sync(kio_addr_t addr) | 
 | 1180 | { | 
 | 1181 |     int bits; | 
 | 1182 |     for (bits = 0; bits < 32; bits++) { | 
 | 1183 | 	outb(MDIO_DATA_WRITE1, addr); | 
 | 1184 | 	outb(MDIO_DATA_WRITE1 | MDIO_SHIFT_CLK, addr); | 
 | 1185 |     } | 
 | 1186 | } | 
 | 1187 |  | 
 | 1188 | static int mdio_read(struct net_device *dev, int phy_id, int loc) | 
 | 1189 | { | 
 | 1190 |     kio_addr_t addr = dev->base_addr + MGMT; | 
 | 1191 |     u_int cmd = (0x06<<10)|(phy_id<<5)|loc; | 
 | 1192 |     int i, retval = 0; | 
 | 1193 |  | 
 | 1194 |     mdio_sync(addr); | 
 | 1195 |     for (i = 13; i >= 0; i--) { | 
 | 1196 | 	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; | 
 | 1197 | 	outb(dat, addr); | 
 | 1198 | 	outb(dat | MDIO_SHIFT_CLK, addr); | 
 | 1199 |     } | 
 | 1200 |     for (i = 19; i > 0; i--) { | 
 | 1201 | 	outb(0, addr); | 
 | 1202 | 	retval = (retval << 1) | ((inb(addr) & MDIO_DATA_READ) != 0); | 
 | 1203 | 	outb(MDIO_SHIFT_CLK, addr); | 
 | 1204 |     } | 
 | 1205 |     return (retval>>1) & 0xffff; | 
 | 1206 | } | 
 | 1207 |  | 
 | 1208 | static void mdio_write(struct net_device *dev, int phy_id, int loc, int value) | 
 | 1209 | { | 
 | 1210 |     kio_addr_t addr = dev->base_addr + MGMT; | 
 | 1211 |     u_int cmd = (0x05<<28)|(phy_id<<23)|(loc<<18)|(1<<17)|value; | 
 | 1212 |     int i; | 
 | 1213 |  | 
 | 1214 |     mdio_sync(addr); | 
 | 1215 |     for (i = 31; i >= 0; i--) { | 
 | 1216 | 	int dat = (cmd&(1<<i)) ? MDIO_DATA_WRITE1 : MDIO_DATA_WRITE0; | 
 | 1217 | 	outb(dat, addr); | 
 | 1218 | 	outb(dat | MDIO_SHIFT_CLK, addr); | 
 | 1219 |     } | 
 | 1220 |     for (i = 1; i >= 0; i--) { | 
 | 1221 | 	outb(0, addr); | 
 | 1222 | 	outb(MDIO_SHIFT_CLK, addr); | 
 | 1223 |     } | 
 | 1224 | } | 
 | 1225 |  | 
 | 1226 | /*====================================================================== | 
 | 1227 |  | 
 | 1228 |     The driver core code, most of which should be common with a | 
 | 1229 |     non-PCMCIA implementation. | 
 | 1230 |  | 
 | 1231 | ======================================================================*/ | 
 | 1232 |  | 
 | 1233 | #ifdef PCMCIA_DEBUG | 
 | 1234 | static void smc_dump(struct net_device *dev) | 
 | 1235 | { | 
 | 1236 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1237 |     u_short i, w, save; | 
 | 1238 |     save = inw(ioaddr + BANK_SELECT); | 
 | 1239 |     for (w = 0; w < 4; w++) { | 
 | 1240 | 	SMC_SELECT_BANK(w); | 
 | 1241 | 	printk(KERN_DEBUG "bank %d: ", w); | 
 | 1242 | 	for (i = 0; i < 14; i += 2) | 
 | 1243 | 	    printk(" %04x", inw(ioaddr + i)); | 
 | 1244 | 	printk("\n"); | 
 | 1245 |     } | 
 | 1246 |     outw(save, ioaddr + BANK_SELECT); | 
 | 1247 | } | 
 | 1248 | #endif | 
 | 1249 |  | 
 | 1250 | static int smc_open(struct net_device *dev) | 
 | 1251 | { | 
 | 1252 |     struct smc_private *smc = netdev_priv(dev); | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1253 |     struct pcmcia_device *link = smc->p_dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1254 |  | 
 | 1255 | #ifdef PCMCIA_DEBUG | 
 | 1256 |     DEBUG(0, "%s: smc_open(%p), ID/Window %4.4x.\n", | 
 | 1257 | 	  dev->name, dev, inw(dev->base_addr + BANK_SELECT)); | 
 | 1258 |     if (pc_debug > 1) smc_dump(dev); | 
 | 1259 | #endif | 
 | 1260 |  | 
 | 1261 |     /* Check that the PCMCIA card is still here. */ | 
| Dominik Brodowski | 9940ec3 | 2006-03-05 11:04:33 +0100 | [diff] [blame] | 1262 |     if (!pcmcia_dev_present(link)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1263 | 	return -ENODEV; | 
 | 1264 |     /* Physical device present signature. */ | 
 | 1265 |     if (check_sig(link) < 0) { | 
 | 1266 | 	printk("smc91c92_cs: Yikes!  Bad chip signature!\n"); | 
 | 1267 | 	return -ENODEV; | 
 | 1268 |     } | 
 | 1269 |     link->open++; | 
 | 1270 |  | 
 | 1271 |     netif_start_queue(dev); | 
 | 1272 |     smc->saved_skb = NULL; | 
 | 1273 |     smc->packets_waiting = 0; | 
 | 1274 |  | 
 | 1275 |     smc_reset(dev); | 
 | 1276 |     init_timer(&smc->media); | 
 | 1277 |     smc->media.function = &media_check; | 
 | 1278 |     smc->media.data = (u_long) dev; | 
 | 1279 |     smc->media.expires = jiffies + HZ; | 
 | 1280 |     add_timer(&smc->media); | 
 | 1281 |  | 
 | 1282 |     return 0; | 
 | 1283 | } /* smc_open */ | 
 | 1284 |  | 
 | 1285 | /*====================================================================*/ | 
 | 1286 |  | 
 | 1287 | static int smc_close(struct net_device *dev) | 
 | 1288 | { | 
 | 1289 |     struct smc_private *smc = netdev_priv(dev); | 
| Dominik Brodowski | fba395e | 2006-03-31 17:21:06 +0200 | [diff] [blame] | 1290 |     struct pcmcia_device *link = smc->p_dev; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1291 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1292 |  | 
 | 1293 |     DEBUG(0, "%s: smc_close(), status %4.4x.\n", | 
 | 1294 | 	  dev->name, inw(ioaddr + BANK_SELECT)); | 
 | 1295 |  | 
 | 1296 |     netif_stop_queue(dev); | 
 | 1297 |  | 
 | 1298 |     /* Shut off all interrupts, and turn off the Tx and Rx sections. | 
 | 1299 |        Don't bother to check for chip present. */ | 
 | 1300 |     SMC_SELECT_BANK(2);	/* Nominally paranoia, but do no assume... */ | 
 | 1301 |     outw(0, ioaddr + INTERRUPT); | 
 | 1302 |     SMC_SELECT_BANK(0); | 
 | 1303 |     mask_bits(0xff00, ioaddr + RCR); | 
 | 1304 |     mask_bits(0xff00, ioaddr + TCR); | 
 | 1305 |  | 
 | 1306 |     /* Put the chip into power-down mode. */ | 
 | 1307 |     SMC_SELECT_BANK(1); | 
 | 1308 |     outw(CTL_POWERDOWN, ioaddr + CONTROL ); | 
 | 1309 |  | 
 | 1310 |     link->open--; | 
 | 1311 |     del_timer_sync(&smc->media); | 
 | 1312 |  | 
 | 1313 |     return 0; | 
 | 1314 | } /* smc_close */ | 
 | 1315 |  | 
 | 1316 | /*====================================================================== | 
 | 1317 |  | 
 | 1318 |    Transfer a packet to the hardware and trigger the packet send. | 
 | 1319 |    This may be called at either from either the Tx queue code | 
 | 1320 |    or the interrupt handler. | 
 | 1321 |  | 
 | 1322 | ======================================================================*/ | 
 | 1323 |  | 
 | 1324 | static void smc_hardware_send_packet(struct net_device * dev) | 
 | 1325 | { | 
 | 1326 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1327 |     struct sk_buff *skb = smc->saved_skb; | 
 | 1328 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1329 |     u_char packet_no; | 
 | 1330 |  | 
 | 1331 |     if (!skb) { | 
 | 1332 | 	printk(KERN_ERR "%s: In XMIT with no packet to send.\n", dev->name); | 
 | 1333 | 	return; | 
 | 1334 |     } | 
 | 1335 |  | 
 | 1336 |     /* There should be a packet slot waiting. */ | 
 | 1337 |     packet_no = inw(ioaddr + PNR_ARR) >> 8; | 
 | 1338 |     if (packet_no & 0x80) { | 
 | 1339 | 	/* If not, there is a hardware problem!  Likely an ejected card. */ | 
 | 1340 | 	printk(KERN_WARNING "%s: 91c92 hardware Tx buffer allocation" | 
 | 1341 | 	       " failed, status %#2.2x.\n", dev->name, packet_no); | 
 | 1342 | 	dev_kfree_skb_irq(skb); | 
 | 1343 | 	smc->saved_skb = NULL; | 
 | 1344 | 	netif_start_queue(dev); | 
 | 1345 | 	return; | 
 | 1346 |     } | 
 | 1347 |  | 
 | 1348 |     smc->stats.tx_bytes += skb->len; | 
 | 1349 |     /* The card should use the just-allocated buffer. */ | 
 | 1350 |     outw(packet_no, ioaddr + PNR_ARR); | 
 | 1351 |     /* point to the beginning of the packet */ | 
 | 1352 |     outw(PTR_AUTOINC , ioaddr + POINTER); | 
 | 1353 |  | 
 | 1354 |     /* Send the packet length (+6 for status, length and ctl byte) | 
 | 1355 |        and the status word (set to zeros). */ | 
 | 1356 |     { | 
 | 1357 | 	u_char *buf = skb->data; | 
 | 1358 | 	u_int length = skb->len; /* The chip will pad to ethernet min. */ | 
 | 1359 |  | 
 | 1360 | 	DEBUG(2, "%s: Trying to xmit packet of length %d.\n", | 
 | 1361 | 	      dev->name, length); | 
 | 1362 | 	 | 
 | 1363 | 	/* send the packet length: +6 for status word, length, and ctl */ | 
 | 1364 | 	outw(0, ioaddr + DATA_1); | 
 | 1365 | 	outw(length + 6, ioaddr + DATA_1); | 
 | 1366 | 	outsw(ioaddr + DATA_1, buf, length >> 1); | 
 | 1367 | 	 | 
 | 1368 | 	/* The odd last byte, if there is one, goes in the control word. */ | 
 | 1369 | 	outw((length & 1) ? 0x2000 | buf[length-1] : 0, ioaddr + DATA_1); | 
 | 1370 |     } | 
 | 1371 |  | 
 | 1372 |     /* Enable the Tx interrupts, both Tx (TxErr) and TxEmpty. */ | 
 | 1373 |     outw(((IM_TX_INT|IM_TX_EMPTY_INT)<<8) | | 
 | 1374 | 	 (inw(ioaddr + INTERRUPT) & 0xff00), | 
 | 1375 | 	 ioaddr + INTERRUPT); | 
 | 1376 |  | 
 | 1377 |     /* The chip does the rest of the work. */ | 
 | 1378 |     outw(MC_ENQUEUE , ioaddr + MMU_CMD); | 
 | 1379 |  | 
 | 1380 |     smc->saved_skb = NULL; | 
 | 1381 |     dev_kfree_skb_irq(skb); | 
 | 1382 |     dev->trans_start = jiffies; | 
 | 1383 |     netif_start_queue(dev); | 
 | 1384 |     return; | 
 | 1385 | } | 
 | 1386 |  | 
 | 1387 | /*====================================================================*/ | 
 | 1388 |  | 
 | 1389 | static void smc_tx_timeout(struct net_device *dev) | 
 | 1390 | { | 
 | 1391 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1392 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1393 |  | 
 | 1394 |     printk(KERN_NOTICE "%s: SMC91c92 transmit timed out, " | 
 | 1395 | 	   "Tx_status %2.2x status %4.4x.\n", | 
 | 1396 | 	   dev->name, inw(ioaddr)&0xff, inw(ioaddr + 2)); | 
 | 1397 |     smc->stats.tx_errors++; | 
 | 1398 |     smc_reset(dev); | 
 | 1399 |     dev->trans_start = jiffies; | 
 | 1400 |     smc->saved_skb = NULL; | 
 | 1401 |     netif_wake_queue(dev); | 
 | 1402 | } | 
 | 1403 |  | 
 | 1404 | static int smc_start_xmit(struct sk_buff *skb, struct net_device *dev) | 
 | 1405 | { | 
 | 1406 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1407 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1408 |     u_short num_pages; | 
 | 1409 |     short time_out, ir; | 
 | 1410 |  | 
 | 1411 |     netif_stop_queue(dev); | 
 | 1412 |  | 
 | 1413 |     DEBUG(2, "%s: smc_start_xmit(length = %d) called," | 
 | 1414 | 	  " status %4.4x.\n", dev->name, skb->len, inw(ioaddr + 2)); | 
 | 1415 |  | 
 | 1416 |     if (smc->saved_skb) { | 
 | 1417 | 	/* THIS SHOULD NEVER HAPPEN. */ | 
 | 1418 | 	smc->stats.tx_aborted_errors++; | 
 | 1419 | 	printk(KERN_DEBUG "%s: Internal error -- sent packet while busy.\n", | 
 | 1420 | 	       dev->name); | 
 | 1421 | 	return 1; | 
 | 1422 |     } | 
 | 1423 |     smc->saved_skb = skb; | 
 | 1424 |  | 
 | 1425 |     num_pages = skb->len >> 8; | 
 | 1426 |  | 
 | 1427 |     if (num_pages > 7) { | 
 | 1428 | 	printk(KERN_ERR "%s: Far too big packet error.\n", dev->name); | 
 | 1429 | 	dev_kfree_skb (skb); | 
 | 1430 | 	smc->saved_skb = NULL; | 
 | 1431 | 	smc->stats.tx_dropped++; | 
 | 1432 | 	return 0;		/* Do not re-queue this packet. */ | 
 | 1433 |     } | 
 | 1434 |     /* A packet is now waiting. */ | 
 | 1435 |     smc->packets_waiting++; | 
 | 1436 |  | 
 | 1437 |     SMC_SELECT_BANK(2);	/* Paranoia, we should always be in window 2 */ | 
 | 1438 |  | 
 | 1439 |     /* need MC_RESET to keep the memory consistent. errata? */ | 
 | 1440 |     if (smc->rx_ovrn) { | 
 | 1441 | 	outw(MC_RESET, ioaddr + MMU_CMD); | 
 | 1442 | 	smc->rx_ovrn = 0; | 
 | 1443 |     } | 
 | 1444 |  | 
 | 1445 |     /* Allocate the memory; send the packet now if we win. */ | 
 | 1446 |     outw(MC_ALLOC | num_pages, ioaddr + MMU_CMD); | 
 | 1447 |     for (time_out = MEMORY_WAIT_TIME; time_out >= 0; time_out--) { | 
 | 1448 | 	ir = inw(ioaddr+INTERRUPT); | 
 | 1449 | 	if (ir & IM_ALLOC_INT) { | 
 | 1450 | 	    /* Acknowledge the interrupt, send the packet. */ | 
 | 1451 | 	    outw((ir&0xff00) | IM_ALLOC_INT, ioaddr + INTERRUPT); | 
 | 1452 | 	    smc_hardware_send_packet(dev);	/* Send the packet now.. */ | 
 | 1453 | 	    return 0; | 
 | 1454 | 	} | 
 | 1455 |     } | 
 | 1456 |  | 
 | 1457 |     /* Otherwise defer until the Tx-space-allocated interrupt. */ | 
 | 1458 |     DEBUG(2, "%s: memory allocation deferred.\n", dev->name); | 
 | 1459 |     outw((IM_ALLOC_INT << 8) | (ir & 0xff00), ioaddr + INTERRUPT); | 
 | 1460 |  | 
 | 1461 |     return 0; | 
 | 1462 | } | 
 | 1463 |  | 
 | 1464 | /*====================================================================== | 
 | 1465 |  | 
 | 1466 |     Handle a Tx anomolous event.  Entered while in Window 2. | 
 | 1467 |  | 
 | 1468 | ======================================================================*/ | 
 | 1469 |  | 
 | 1470 | static void smc_tx_err(struct net_device * dev) | 
 | 1471 | { | 
 | 1472 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1473 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1474 |     int saved_packet = inw(ioaddr + PNR_ARR) & 0xff; | 
 | 1475 |     int packet_no = inw(ioaddr + FIFO_PORTS) & 0x7f; | 
 | 1476 |     int tx_status; | 
 | 1477 |  | 
 | 1478 |     /* select this as the packet to read from */ | 
 | 1479 |     outw(packet_no, ioaddr + PNR_ARR); | 
 | 1480 |  | 
 | 1481 |     /* read the first word from this packet */ | 
 | 1482 |     outw(PTR_AUTOINC | PTR_READ | 0, ioaddr + POINTER); | 
 | 1483 |  | 
 | 1484 |     tx_status = inw(ioaddr + DATA_1); | 
 | 1485 |  | 
 | 1486 |     smc->stats.tx_errors++; | 
 | 1487 |     if (tx_status & TS_LOSTCAR) smc->stats.tx_carrier_errors++; | 
 | 1488 |     if (tx_status & TS_LATCOL)  smc->stats.tx_window_errors++; | 
 | 1489 |     if (tx_status & TS_16COL) { | 
 | 1490 | 	smc->stats.tx_aborted_errors++; | 
 | 1491 | 	smc->tx_err++; | 
 | 1492 |     } | 
 | 1493 |  | 
 | 1494 |     if (tx_status & TS_SUCCESS) { | 
 | 1495 | 	printk(KERN_NOTICE "%s: Successful packet caused error " | 
 | 1496 | 	       "interrupt?\n", dev->name); | 
 | 1497 |     } | 
 | 1498 |     /* re-enable transmit */ | 
 | 1499 |     SMC_SELECT_BANK(0); | 
 | 1500 |     outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); | 
 | 1501 |     SMC_SELECT_BANK(2); | 
 | 1502 |  | 
 | 1503 |     outw(MC_FREEPKT, ioaddr + MMU_CMD); 	/* Free the packet memory. */ | 
 | 1504 |  | 
 | 1505 |     /* one less packet waiting for me */ | 
 | 1506 |     smc->packets_waiting--; | 
 | 1507 |  | 
 | 1508 |     outw(saved_packet, ioaddr + PNR_ARR); | 
 | 1509 |     return; | 
 | 1510 | } | 
 | 1511 |  | 
 | 1512 | /*====================================================================*/ | 
 | 1513 |  | 
 | 1514 | static void smc_eph_irq(struct net_device *dev) | 
 | 1515 | { | 
 | 1516 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1517 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1518 |     u_short card_stats, ephs; | 
 | 1519 |  | 
 | 1520 |     SMC_SELECT_BANK(0); | 
 | 1521 |     ephs = inw(ioaddr + EPH); | 
 | 1522 |     DEBUG(2, "%s: Ethernet protocol handler interrupt, status" | 
 | 1523 | 	  " %4.4x.\n", dev->name, ephs); | 
 | 1524 |     /* Could be a counter roll-over warning: update stats. */ | 
 | 1525 |     card_stats = inw(ioaddr + COUNTER); | 
 | 1526 |     /* single collisions */ | 
 | 1527 |     smc->stats.collisions += card_stats & 0xF; | 
 | 1528 |     card_stats >>= 4; | 
 | 1529 |     /* multiple collisions */ | 
 | 1530 |     smc->stats.collisions += card_stats & 0xF; | 
 | 1531 | #if 0 		/* These are for when linux supports these statistics */ | 
 | 1532 |     card_stats >>= 4;			/* deferred */ | 
 | 1533 |     card_stats >>= 4;			/* excess deferred */ | 
 | 1534 | #endif | 
 | 1535 |     /* If we had a transmit error we must re-enable the transmitter. */ | 
 | 1536 |     outw(inw(ioaddr + TCR) | TCR_ENABLE | smc->duplex, ioaddr + TCR); | 
 | 1537 |  | 
 | 1538 |     /* Clear a link error interrupt. */ | 
 | 1539 |     SMC_SELECT_BANK(1); | 
 | 1540 |     outw(CTL_AUTO_RELEASE | 0x0000, ioaddr + CONTROL); | 
 | 1541 |     outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, | 
 | 1542 | 	 ioaddr + CONTROL); | 
 | 1543 |     SMC_SELECT_BANK(2); | 
 | 1544 | } | 
 | 1545 |  | 
 | 1546 | /*====================================================================*/ | 
 | 1547 |  | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 1548 | static irqreturn_t smc_interrupt(int irq, void *dev_id) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1549 | { | 
 | 1550 |     struct net_device *dev = dev_id; | 
 | 1551 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1552 |     kio_addr_t ioaddr; | 
 | 1553 |     u_short saved_bank, saved_pointer, mask, status; | 
 | 1554 |     unsigned int handled = 1; | 
 | 1555 |     char bogus_cnt = INTR_WORK;		/* Work we are willing to do. */ | 
 | 1556 |  | 
 | 1557 |     if (!netif_device_present(dev)) | 
 | 1558 | 	return IRQ_NONE; | 
 | 1559 |  | 
 | 1560 |     ioaddr = dev->base_addr; | 
 | 1561 |  | 
 | 1562 |     DEBUG(3, "%s: SMC91c92 interrupt %d at %#x.\n", dev->name, | 
 | 1563 | 	  irq, ioaddr); | 
 | 1564 |  | 
 | 1565 |     smc->watchdog = 0; | 
 | 1566 |     saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 1567 |     if ((saved_bank & 0xff00) != 0x3300) { | 
 | 1568 | 	/* The device does not exist -- the card could be off-line, or | 
 | 1569 | 	   maybe it has been ejected. */ | 
 | 1570 | 	DEBUG(1, "%s: SMC91c92 interrupt %d for non-existent" | 
 | 1571 | 	      "/ejected device.\n", dev->name, irq); | 
 | 1572 | 	handled = 0; | 
 | 1573 | 	goto irq_done; | 
 | 1574 |     } | 
 | 1575 |  | 
 | 1576 |     SMC_SELECT_BANK(2); | 
 | 1577 |     saved_pointer = inw(ioaddr + POINTER); | 
 | 1578 |     mask = inw(ioaddr + INTERRUPT) >> 8; | 
 | 1579 |     /* clear all interrupts */ | 
 | 1580 |     outw(0, ioaddr + INTERRUPT); | 
 | 1581 |  | 
 | 1582 |     do { /* read the status flag, and mask it */ | 
 | 1583 | 	status = inw(ioaddr + INTERRUPT) & 0xff; | 
 | 1584 | 	DEBUG(3, "%s: Status is %#2.2x (mask %#2.2x).\n", dev->name, | 
 | 1585 | 	      status, mask); | 
 | 1586 | 	if ((status & mask) == 0) { | 
 | 1587 | 	    if (bogus_cnt == INTR_WORK) | 
 | 1588 | 		handled = 0; | 
 | 1589 | 	    break; | 
 | 1590 | 	} | 
 | 1591 | 	if (status & IM_RCV_INT) { | 
 | 1592 | 	    /* Got a packet(s). */ | 
 | 1593 | 	    smc_rx(dev); | 
 | 1594 | 	} | 
 | 1595 | 	if (status & IM_TX_INT) { | 
 | 1596 | 	    smc_tx_err(dev); | 
 | 1597 | 	    outw(IM_TX_INT, ioaddr + INTERRUPT); | 
 | 1598 | 	} | 
 | 1599 | 	status &= mask; | 
 | 1600 | 	if (status & IM_TX_EMPTY_INT) { | 
 | 1601 | 	    outw(IM_TX_EMPTY_INT, ioaddr + INTERRUPT); | 
 | 1602 | 	    mask &= ~IM_TX_EMPTY_INT; | 
 | 1603 | 	    smc->stats.tx_packets += smc->packets_waiting; | 
 | 1604 | 	    smc->packets_waiting = 0; | 
 | 1605 | 	} | 
 | 1606 | 	if (status & IM_ALLOC_INT) { | 
 | 1607 | 	    /* Clear this interrupt so it doesn't happen again */ | 
 | 1608 | 	    mask &= ~IM_ALLOC_INT; | 
 | 1609 | 	 | 
 | 1610 | 	    smc_hardware_send_packet(dev); | 
 | 1611 | 	 | 
 | 1612 | 	    /* enable xmit interrupts based on this */ | 
 | 1613 | 	    mask |= (IM_TX_EMPTY_INT | IM_TX_INT); | 
 | 1614 | 	 | 
 | 1615 | 	    /* and let the card send more packets to me */ | 
 | 1616 | 	    netif_wake_queue(dev); | 
 | 1617 | 	} | 
 | 1618 | 	if (status & IM_RX_OVRN_INT) { | 
 | 1619 | 	    smc->stats.rx_errors++; | 
 | 1620 | 	    smc->stats.rx_fifo_errors++; | 
 | 1621 | 	    if (smc->duplex) | 
 | 1622 | 		smc->rx_ovrn = 1; /* need MC_RESET outside smc_interrupt */ | 
 | 1623 | 	    outw(IM_RX_OVRN_INT, ioaddr + INTERRUPT); | 
 | 1624 | 	} | 
 | 1625 | 	if (status & IM_EPH_INT) | 
 | 1626 | 	    smc_eph_irq(dev); | 
 | 1627 |     } while (--bogus_cnt); | 
 | 1628 |  | 
 | 1629 |     DEBUG(3, "  Restoring saved registers mask %2.2x bank %4.4x" | 
 | 1630 | 	  " pointer %4.4x.\n", mask, saved_bank, saved_pointer); | 
 | 1631 |  | 
 | 1632 |     /* restore state register */ | 
 | 1633 |     outw((mask<<8), ioaddr + INTERRUPT); | 
 | 1634 |     outw(saved_pointer, ioaddr + POINTER); | 
 | 1635 |     SMC_SELECT_BANK(saved_bank); | 
 | 1636 |  | 
 | 1637 |     DEBUG(3, "%s: Exiting interrupt IRQ%d.\n", dev->name, irq); | 
 | 1638 |  | 
 | 1639 | irq_done: | 
 | 1640 |  | 
 | 1641 |     if ((smc->manfid == MANFID_OSITECH) && | 
 | 1642 | 	(smc->cardid != PRODID_OSITECH_SEVEN)) { | 
 | 1643 | 	/* Retrigger interrupt if needed */ | 
 | 1644 | 	mask_bits(0x00ff, ioaddr-0x10+OSITECH_RESET_ISR); | 
 | 1645 | 	set_bits(0x0300, ioaddr-0x10+OSITECH_RESET_ISR); | 
 | 1646 |     } | 
 | 1647 |     if (smc->manfid == MANFID_MOTOROLA) { | 
 | 1648 | 	u_char cor; | 
 | 1649 | 	cor = readb(smc->base + MOT_UART + CISREG_COR); | 
 | 1650 | 	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_UART + CISREG_COR); | 
 | 1651 | 	writeb(cor, smc->base + MOT_UART + CISREG_COR); | 
 | 1652 | 	cor = readb(smc->base + MOT_LAN + CISREG_COR); | 
 | 1653 | 	writeb(cor & ~COR_IREQ_ENA, smc->base + MOT_LAN + CISREG_COR); | 
 | 1654 | 	writeb(cor, smc->base + MOT_LAN + CISREG_COR); | 
 | 1655 |     } | 
 | 1656 | #ifdef DOES_NOT_WORK | 
 | 1657 |     if (smc->base != NULL) { /* Megahertz MFC's */ | 
 | 1658 | 	readb(smc->base+MEGAHERTZ_ISR); | 
 | 1659 | 	readb(smc->base+MEGAHERTZ_ISR); | 
 | 1660 |     } | 
 | 1661 | #endif | 
 | 1662 |     return IRQ_RETVAL(handled); | 
 | 1663 | } | 
 | 1664 |  | 
 | 1665 | /*====================================================================*/ | 
 | 1666 |  | 
 | 1667 | static void smc_rx(struct net_device *dev) | 
 | 1668 | { | 
 | 1669 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1670 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1671 |     int rx_status; | 
 | 1672 |     int packet_length;	/* Caution: not frame length, rather words | 
 | 1673 | 			   to transfer from the chip. */ | 
 | 1674 |  | 
 | 1675 |     /* Assertion: we are in Window 2. */ | 
 | 1676 |  | 
 | 1677 |     if (inw(ioaddr + FIFO_PORTS) & FP_RXEMPTY) { | 
 | 1678 | 	printk(KERN_ERR "%s: smc_rx() with nothing on Rx FIFO.\n", | 
 | 1679 | 	       dev->name); | 
 | 1680 | 	return; | 
 | 1681 |     } | 
 | 1682 |  | 
 | 1683 |     /*  Reset the read pointer, and read the status and packet length. */ | 
 | 1684 |     outw(PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + POINTER); | 
 | 1685 |     rx_status = inw(ioaddr + DATA_1); | 
 | 1686 |     packet_length = inw(ioaddr + DATA_1) & 0x07ff; | 
 | 1687 |  | 
 | 1688 |     DEBUG(2, "%s: Receive status %4.4x length %d.\n", | 
 | 1689 | 	  dev->name, rx_status, packet_length); | 
 | 1690 |  | 
 | 1691 |     if (!(rx_status & RS_ERRORS)) {		 | 
 | 1692 | 	/* do stuff to make a new packet */ | 
 | 1693 | 	struct sk_buff *skb; | 
 | 1694 | 	 | 
 | 1695 | 	/* Note: packet_length adds 5 or 6 extra bytes here! */ | 
 | 1696 | 	skb = dev_alloc_skb(packet_length+2); | 
 | 1697 | 	 | 
 | 1698 | 	if (skb == NULL) { | 
 | 1699 | 	    DEBUG(1, "%s: Low memory, packet dropped.\n", dev->name); | 
 | 1700 | 	    smc->stats.rx_dropped++; | 
 | 1701 | 	    outw(MC_RELEASE, ioaddr + MMU_CMD); | 
 | 1702 | 	    return; | 
 | 1703 | 	} | 
 | 1704 | 	 | 
 | 1705 | 	packet_length -= (rx_status & RS_ODDFRAME ? 5 : 6); | 
 | 1706 | 	skb_reserve(skb, 2); | 
 | 1707 | 	insw(ioaddr+DATA_1, skb_put(skb, packet_length), | 
 | 1708 | 	     (packet_length+1)>>1); | 
 | 1709 | 	skb->protocol = eth_type_trans(skb, dev); | 
 | 1710 | 	 | 
 | 1711 | 	skb->dev = dev; | 
 | 1712 | 	netif_rx(skb); | 
 | 1713 | 	dev->last_rx = jiffies; | 
 | 1714 | 	smc->stats.rx_packets++; | 
 | 1715 | 	smc->stats.rx_bytes += packet_length; | 
 | 1716 | 	if (rx_status & RS_MULTICAST) | 
 | 1717 | 	    smc->stats.multicast++; | 
 | 1718 |     } else { | 
 | 1719 | 	/* error ... */ | 
 | 1720 | 	smc->stats.rx_errors++; | 
 | 1721 | 	 | 
 | 1722 | 	if (rx_status & RS_ALGNERR)  smc->stats.rx_frame_errors++; | 
 | 1723 | 	if (rx_status & (RS_TOOSHORT | RS_TOOLONG)) | 
 | 1724 | 	    smc->stats.rx_length_errors++; | 
 | 1725 | 	if (rx_status & RS_BADCRC)	smc->stats.rx_crc_errors++; | 
 | 1726 |     } | 
 | 1727 |     /* Let the MMU free the memory of this packet. */ | 
 | 1728 |     outw(MC_RELEASE, ioaddr + MMU_CMD); | 
 | 1729 |  | 
 | 1730 |     return; | 
 | 1731 | } | 
 | 1732 |  | 
 | 1733 | /*====================================================================*/ | 
 | 1734 |  | 
 | 1735 | static struct net_device_stats *smc_get_stats(struct net_device *dev) | 
 | 1736 | { | 
 | 1737 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1738 |     /* Nothing to update - the 91c92 is a pretty primative chip. */ | 
 | 1739 |     return &smc->stats; | 
 | 1740 | } | 
 | 1741 |  | 
 | 1742 | /*====================================================================== | 
 | 1743 |  | 
 | 1744 |     Calculate values for the hardware multicast filter hash table. | 
 | 1745 |  | 
 | 1746 | ======================================================================*/ | 
 | 1747 |  | 
 | 1748 | static void fill_multicast_tbl(int count, struct dev_mc_list *addrs, | 
 | 1749 | 			       u_char *multicast_table) | 
 | 1750 | { | 
 | 1751 |     struct dev_mc_list	*mc_addr; | 
 | 1752 |  | 
| Komuro | bb53d6d | 2005-10-03 22:03:28 -0400 | [diff] [blame] | 1753 |     for (mc_addr = addrs;  mc_addr && count-- > 0;  mc_addr = mc_addr->next) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1754 | 	u_int position = ether_crc(6, mc_addr->dmi_addr); | 
 | 1755 | #ifndef final_version		/* Verify multicast address. */ | 
 | 1756 | 	if ((mc_addr->dmi_addr[0] & 1) == 0) | 
 | 1757 | 	    continue; | 
 | 1758 | #endif | 
 | 1759 | 	multicast_table[position >> 29] |= 1 << ((position >> 26) & 7); | 
 | 1760 |     } | 
 | 1761 | } | 
 | 1762 |  | 
 | 1763 | /*====================================================================== | 
 | 1764 |  | 
 | 1765 |     Set the receive mode. | 
 | 1766 |  | 
 | 1767 |     This routine is used by both the protocol level to notify us of | 
 | 1768 |     promiscuous/multicast mode changes, and by the open/reset code to | 
 | 1769 |     initialize the Rx registers.  We always set the multicast list and | 
 | 1770 |     leave the receiver running. | 
 | 1771 |  | 
 | 1772 | ======================================================================*/ | 
 | 1773 |  | 
 | 1774 | static void set_rx_mode(struct net_device *dev) | 
 | 1775 | { | 
 | 1776 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1777 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1778 |     u_int multicast_table[ 2 ] = { 0, }; | 
 | 1779 |     unsigned long flags; | 
 | 1780 |     u_short rx_cfg_setting; | 
 | 1781 |  | 
 | 1782 |     if (dev->flags & IFF_PROMISC) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1783 | 	rx_cfg_setting = RxStripCRC | RxEnable | RxPromisc | RxAllMulti; | 
 | 1784 |     } else if (dev->flags & IFF_ALLMULTI) | 
 | 1785 | 	rx_cfg_setting = RxStripCRC | RxEnable | RxAllMulti; | 
 | 1786 |     else { | 
 | 1787 | 	if (dev->mc_count)  { | 
 | 1788 | 	    fill_multicast_tbl(dev->mc_count, dev->mc_list, | 
 | 1789 | 			       (u_char *)multicast_table); | 
 | 1790 | 	} | 
 | 1791 | 	rx_cfg_setting = RxStripCRC | RxEnable; | 
 | 1792 |     } | 
 | 1793 |  | 
 | 1794 |     /* Load MC table and Rx setting into the chip without interrupts. */ | 
 | 1795 |     spin_lock_irqsave(&smc->lock, flags); | 
 | 1796 |     SMC_SELECT_BANK(3); | 
 | 1797 |     outl(multicast_table[0], ioaddr + MULTICAST0); | 
 | 1798 |     outl(multicast_table[1], ioaddr + MULTICAST4); | 
 | 1799 |     SMC_SELECT_BANK(0); | 
 | 1800 |     outw(rx_cfg_setting, ioaddr + RCR); | 
 | 1801 |     SMC_SELECT_BANK(2); | 
 | 1802 |     spin_unlock_irqrestore(&smc->lock, flags); | 
 | 1803 |  | 
 | 1804 |     return; | 
 | 1805 | } | 
 | 1806 |  | 
 | 1807 | /*====================================================================== | 
 | 1808 |  | 
 | 1809 |     Senses when a card's config changes. Here, it's coax or TP. | 
 | 1810 |  | 
 | 1811 | ======================================================================*/ | 
 | 1812 |  | 
 | 1813 | static int s9k_config(struct net_device *dev, struct ifmap *map) | 
 | 1814 | { | 
 | 1815 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1816 |     if ((map->port != (u_char)(-1)) && (map->port != dev->if_port)) { | 
 | 1817 | 	if (smc->cfg & CFG_MII_SELECT) | 
 | 1818 | 	    return -EOPNOTSUPP; | 
 | 1819 | 	else if (map->port > 2) | 
 | 1820 | 	    return -EINVAL; | 
 | 1821 | 	dev->if_port = map->port; | 
 | 1822 | 	printk(KERN_INFO "%s: switched to %s port\n", | 
 | 1823 | 	       dev->name, if_names[dev->if_port]); | 
 | 1824 | 	smc_reset(dev); | 
 | 1825 |     } | 
 | 1826 |     return 0; | 
 | 1827 | } | 
 | 1828 |  | 
 | 1829 | /*====================================================================== | 
 | 1830 |  | 
 | 1831 |     Reset the chip, reloading every register that might be corrupted. | 
 | 1832 |  | 
 | 1833 | ======================================================================*/ | 
 | 1834 |  | 
 | 1835 | /* | 
 | 1836 |   Set transceiver type, perhaps to something other than what the user | 
 | 1837 |   specified in dev->if_port. | 
 | 1838 | */ | 
 | 1839 | static void smc_set_xcvr(struct net_device *dev, int if_port) | 
 | 1840 | { | 
 | 1841 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1842 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1843 |     u_short saved_bank; | 
 | 1844 |  | 
 | 1845 |     saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 1846 |     SMC_SELECT_BANK(1); | 
 | 1847 |     if (if_port == 2) { | 
 | 1848 | 	outw(smc->cfg | CFG_AUI_SELECT, ioaddr + CONFIG); | 
 | 1849 | 	if ((smc->manfid == MANFID_OSITECH) && | 
 | 1850 | 	    (smc->cardid != PRODID_OSITECH_SEVEN)) | 
 | 1851 | 	    set_bits(OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); | 
 | 1852 | 	smc->media_status = ((dev->if_port == 0) ? 0x0001 : 0x0002); | 
 | 1853 |     } else { | 
 | 1854 | 	outw(smc->cfg, ioaddr + CONFIG); | 
 | 1855 | 	if ((smc->manfid == MANFID_OSITECH) && | 
 | 1856 | 	    (smc->cardid != PRODID_OSITECH_SEVEN)) | 
 | 1857 | 	    mask_bits(~OSI_AUI_PWR, ioaddr - 0x10 + OSITECH_AUI_PWR); | 
 | 1858 | 	smc->media_status = ((dev->if_port == 0) ? 0x0012 : 0x4001); | 
 | 1859 |     } | 
 | 1860 |     SMC_SELECT_BANK(saved_bank); | 
 | 1861 | } | 
 | 1862 |  | 
 | 1863 | static void smc_reset(struct net_device *dev) | 
 | 1864 | { | 
 | 1865 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1866 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1867 |     int i; | 
 | 1868 |  | 
 | 1869 |     DEBUG(0, "%s: smc91c92 reset called.\n", dev->name); | 
 | 1870 |  | 
 | 1871 |     /* The first interaction must be a write to bring the chip out | 
 | 1872 |        of sleep mode. */ | 
 | 1873 |     SMC_SELECT_BANK(0); | 
 | 1874 |     /* Reset the chip. */ | 
 | 1875 |     outw(RCR_SOFTRESET, ioaddr + RCR); | 
 | 1876 |     udelay(10); | 
 | 1877 |  | 
 | 1878 |     /* Clear the transmit and receive configuration registers. */ | 
 | 1879 |     outw(RCR_CLEAR, ioaddr + RCR); | 
 | 1880 |     outw(TCR_CLEAR, ioaddr + TCR); | 
 | 1881 |  | 
 | 1882 |     /* Set the Window 1 control, configuration and station addr registers. | 
 | 1883 |        No point in writing the I/O base register ;-> */ | 
 | 1884 |     SMC_SELECT_BANK(1); | 
| Andreas Mohr | d6e05ed | 2006-06-26 18:35:02 +0200 | [diff] [blame] | 1885 |     /* Automatically release successfully transmitted packets, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1886 |        Accept link errors, counter and Tx error interrupts. */ | 
 | 1887 |     outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE, | 
 | 1888 | 	 ioaddr + CONTROL); | 
 | 1889 |     smc_set_xcvr(dev, dev->if_port); | 
 | 1890 |     if ((smc->manfid == MANFID_OSITECH) && | 
 | 1891 | 	(smc->cardid != PRODID_OSITECH_SEVEN)) | 
 | 1892 | 	outw((dev->if_port == 2 ? OSI_AUI_PWR : 0) | | 
 | 1893 | 	     (inw(ioaddr-0x10+OSITECH_AUI_PWR) & 0xff00), | 
 | 1894 | 	     ioaddr - 0x10 + OSITECH_AUI_PWR); | 
 | 1895 |  | 
 | 1896 |     /* Fill in the physical address.  The databook is wrong about the order! */ | 
 | 1897 |     for (i = 0; i < 6; i += 2) | 
 | 1898 | 	outw((dev->dev_addr[i+1]<<8)+dev->dev_addr[i], | 
 | 1899 | 	     ioaddr + ADDR0 + i); | 
 | 1900 |  | 
 | 1901 |     /* Reset the MMU */ | 
 | 1902 |     SMC_SELECT_BANK(2); | 
 | 1903 |     outw(MC_RESET, ioaddr + MMU_CMD); | 
 | 1904 |     outw(0, ioaddr + INTERRUPT); | 
 | 1905 |  | 
 | 1906 |     /* Re-enable the chip. */ | 
 | 1907 |     SMC_SELECT_BANK(0); | 
 | 1908 |     outw(((smc->cfg & CFG_MII_SELECT) ? 0 : TCR_MONCSN) | | 
 | 1909 | 	 TCR_ENABLE | TCR_PAD_EN | smc->duplex, ioaddr + TCR); | 
 | 1910 |     set_rx_mode(dev); | 
 | 1911 |  | 
 | 1912 |     if (smc->cfg & CFG_MII_SELECT) { | 
 | 1913 | 	SMC_SELECT_BANK(3); | 
 | 1914 |  | 
 | 1915 | 	/* Reset MII */ | 
 | 1916 | 	mdio_write(dev, smc->mii_if.phy_id, 0, 0x8000); | 
 | 1917 |  | 
 | 1918 | 	/* Advertise 100F, 100H, 10F, 10H */ | 
 | 1919 | 	mdio_write(dev, smc->mii_if.phy_id, 4, 0x01e1); | 
 | 1920 |  | 
 | 1921 | 	/* Restart MII autonegotiation */ | 
 | 1922 | 	mdio_write(dev, smc->mii_if.phy_id, 0, 0x0000); | 
 | 1923 | 	mdio_write(dev, smc->mii_if.phy_id, 0, 0x1200); | 
 | 1924 |     } | 
 | 1925 |  | 
 | 1926 |     /* Enable interrupts. */ | 
 | 1927 |     SMC_SELECT_BANK(2); | 
 | 1928 |     outw((IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT) << 8, | 
 | 1929 | 	 ioaddr + INTERRUPT); | 
 | 1930 | } | 
 | 1931 |  | 
 | 1932 | /*====================================================================== | 
 | 1933 |  | 
 | 1934 |     Media selection timer routine | 
 | 1935 |  | 
 | 1936 | ======================================================================*/ | 
 | 1937 |  | 
 | 1938 | static void media_check(u_long arg) | 
 | 1939 | { | 
 | 1940 |     struct net_device *dev = (struct net_device *) arg; | 
 | 1941 |     struct smc_private *smc = netdev_priv(dev); | 
 | 1942 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 1943 |     u_short i, media, saved_bank; | 
 | 1944 |     u_short link; | 
 | 1945 |  | 
 | 1946 |     saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 1947 |  | 
 | 1948 |     if (!netif_device_present(dev)) | 
 | 1949 | 	goto reschedule; | 
 | 1950 |  | 
 | 1951 |     SMC_SELECT_BANK(2); | 
 | 1952 |  | 
 | 1953 |     /* need MC_RESET to keep the memory consistent. errata? */ | 
 | 1954 |     if (smc->rx_ovrn) { | 
 | 1955 | 	outw(MC_RESET, ioaddr + MMU_CMD); | 
 | 1956 | 	smc->rx_ovrn = 0; | 
 | 1957 |     } | 
 | 1958 |     i = inw(ioaddr + INTERRUPT); | 
 | 1959 |     SMC_SELECT_BANK(0); | 
 | 1960 |     media = inw(ioaddr + EPH) & EPH_LINK_OK; | 
 | 1961 |     SMC_SELECT_BANK(1); | 
 | 1962 |     media |= (inw(ioaddr + CONFIG) & CFG_AUI_SELECT) ? 2 : 1; | 
 | 1963 |  | 
 | 1964 |     /* Check for pending interrupt with watchdog flag set: with | 
 | 1965 |        this, we can limp along even if the interrupt is blocked */ | 
 | 1966 |     if (smc->watchdog++ && ((i>>8) & i)) { | 
 | 1967 | 	if (!smc->fast_poll) | 
 | 1968 | 	    printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); | 
| David Howells | 7d12e78 | 2006-10-05 14:55:46 +0100 | [diff] [blame] | 1969 | 	smc_interrupt(dev->irq, smc); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1970 | 	smc->fast_poll = HZ; | 
 | 1971 |     } | 
 | 1972 |     if (smc->fast_poll) { | 
 | 1973 | 	smc->fast_poll--; | 
 | 1974 | 	smc->media.expires = jiffies + HZ/100; | 
 | 1975 | 	add_timer(&smc->media); | 
 | 1976 | 	SMC_SELECT_BANK(saved_bank); | 
 | 1977 | 	return; | 
 | 1978 |     } | 
 | 1979 |  | 
 | 1980 |     if (smc->cfg & CFG_MII_SELECT) { | 
 | 1981 | 	if (smc->mii_if.phy_id < 0) | 
 | 1982 | 	    goto reschedule; | 
 | 1983 |  | 
 | 1984 | 	SMC_SELECT_BANK(3); | 
 | 1985 | 	link = mdio_read(dev, smc->mii_if.phy_id, 1); | 
 | 1986 | 	if (!link || (link == 0xffff)) { | 
 | 1987 |   	    printk(KERN_INFO "%s: MII is missing!\n", dev->name); | 
 | 1988 | 	    smc->mii_if.phy_id = -1; | 
 | 1989 | 	    goto reschedule; | 
 | 1990 | 	} | 
 | 1991 |  | 
 | 1992 | 	link &= 0x0004; | 
 | 1993 | 	if (link != smc->link_status) { | 
 | 1994 | 	    u_short p = mdio_read(dev, smc->mii_if.phy_id, 5); | 
 | 1995 | 	    printk(KERN_INFO "%s: %s link beat\n", dev->name, | 
 | 1996 | 		(link) ? "found" : "lost"); | 
 | 1997 | 	    smc->duplex = (((p & 0x0100) || ((p & 0x1c0) == 0x40)) | 
 | 1998 | 			   ? TCR_FDUPLX : 0); | 
 | 1999 | 	    if (link) { | 
 | 2000 | 	        printk(KERN_INFO "%s: autonegotiation complete: " | 
 | 2001 | 		       "%sbaseT-%cD selected\n", dev->name, | 
 | 2002 | 		       ((p & 0x0180) ? "100" : "10"), | 
 | 2003 | 		       (smc->duplex ? 'F' : 'H')); | 
 | 2004 | 	    } | 
 | 2005 | 	    SMC_SELECT_BANK(0); | 
 | 2006 | 	    outw(inw(ioaddr + TCR) | smc->duplex, ioaddr + TCR); | 
 | 2007 | 	    smc->link_status = link; | 
 | 2008 | 	} | 
 | 2009 | 	goto reschedule; | 
 | 2010 |     } | 
 | 2011 |  | 
 | 2012 |     /* Ignore collisions unless we've had no rx's recently */ | 
| Marcelo Feitoza Parisi | 4851d3a | 2005-07-15 10:00:41 -0700 | [diff] [blame] | 2013 |     if (time_after(jiffies, dev->last_rx + HZ)) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2014 | 	if (smc->tx_err || (smc->media_status & EPH_16COL)) | 
 | 2015 | 	    media |= EPH_16COL; | 
 | 2016 |     } | 
 | 2017 |     smc->tx_err = 0; | 
 | 2018 |  | 
 | 2019 |     if (media != smc->media_status) { | 
 | 2020 | 	if ((media & smc->media_status & 1) && | 
 | 2021 | 	    ((smc->media_status ^ media) & EPH_LINK_OK)) | 
 | 2022 | 	    printk(KERN_INFO "%s: %s link beat\n", dev->name, | 
 | 2023 | 		   (smc->media_status & EPH_LINK_OK ? "lost" : "found")); | 
 | 2024 | 	else if ((media & smc->media_status & 2) && | 
 | 2025 | 		 ((smc->media_status ^ media) & EPH_16COL)) | 
 | 2026 | 	    printk(KERN_INFO "%s: coax cable %s\n", dev->name, | 
 | 2027 | 		   (media & EPH_16COL ? "problem" : "ok")); | 
 | 2028 | 	if (dev->if_port == 0) { | 
 | 2029 | 	    if (media & 1) { | 
 | 2030 | 		if (media & EPH_LINK_OK) | 
 | 2031 | 		    printk(KERN_INFO "%s: flipped to 10baseT\n", | 
 | 2032 | 			   dev->name); | 
 | 2033 | 		else | 
 | 2034 | 		    smc_set_xcvr(dev, 2); | 
 | 2035 | 	    } else { | 
 | 2036 | 		if (media & EPH_16COL) | 
 | 2037 | 		    smc_set_xcvr(dev, 1); | 
 | 2038 | 		else | 
 | 2039 | 		    printk(KERN_INFO "%s: flipped to 10base2\n", | 
 | 2040 | 			   dev->name); | 
 | 2041 | 	    } | 
 | 2042 | 	} | 
 | 2043 | 	smc->media_status = media; | 
 | 2044 |     } | 
 | 2045 |  | 
 | 2046 | reschedule: | 
 | 2047 |     smc->media.expires = jiffies + HZ; | 
 | 2048 |     add_timer(&smc->media); | 
 | 2049 |     SMC_SELECT_BANK(saved_bank); | 
 | 2050 | } | 
 | 2051 |  | 
 | 2052 | static int smc_link_ok(struct net_device *dev) | 
 | 2053 | { | 
 | 2054 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 2055 |     struct smc_private *smc = netdev_priv(dev); | 
 | 2056 |  | 
 | 2057 |     if (smc->cfg & CFG_MII_SELECT) { | 
 | 2058 | 	return mii_link_ok(&smc->mii_if); | 
 | 2059 |     } else { | 
 | 2060 |         SMC_SELECT_BANK(0); | 
 | 2061 | 	return inw(ioaddr + EPH) & EPH_LINK_OK; | 
 | 2062 |     } | 
 | 2063 | } | 
 | 2064 |  | 
 | 2065 | static int smc_netdev_get_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) | 
 | 2066 | { | 
 | 2067 |     u16 tmp; | 
 | 2068 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 2069 |  | 
 | 2070 |     ecmd->supported = (SUPPORTED_TP | SUPPORTED_AUI | | 
 | 2071 | 	SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full); | 
 | 2072 | 		 | 
 | 2073 |     SMC_SELECT_BANK(1); | 
 | 2074 |     tmp = inw(ioaddr + CONFIG); | 
 | 2075 |     ecmd->port = (tmp & CFG_AUI_SELECT) ? PORT_AUI : PORT_TP; | 
 | 2076 |     ecmd->transceiver = XCVR_INTERNAL; | 
 | 2077 |     ecmd->speed = SPEED_10; | 
 | 2078 |     ecmd->phy_address = ioaddr + MGMT; | 
 | 2079 |  | 
 | 2080 |     SMC_SELECT_BANK(0); | 
 | 2081 |     tmp = inw(ioaddr + TCR); | 
 | 2082 |     ecmd->duplex = (tmp & TCR_FDUPLX) ? DUPLEX_FULL : DUPLEX_HALF; | 
 | 2083 |  | 
 | 2084 |     return 0; | 
 | 2085 | } | 
 | 2086 |  | 
 | 2087 | static int smc_netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) | 
 | 2088 | { | 
 | 2089 |     u16 tmp; | 
 | 2090 |     kio_addr_t ioaddr = dev->base_addr; | 
 | 2091 |  | 
 | 2092 |     if (ecmd->speed != SPEED_10) | 
 | 2093 |     	return -EINVAL; | 
 | 2094 |     if (ecmd->duplex != DUPLEX_HALF && ecmd->duplex != DUPLEX_FULL) | 
 | 2095 |     	return -EINVAL; | 
 | 2096 |     if (ecmd->port != PORT_TP && ecmd->port != PORT_AUI) | 
 | 2097 | 	return -EINVAL; | 
 | 2098 |     if (ecmd->transceiver != XCVR_INTERNAL) | 
 | 2099 |     	return -EINVAL; | 
 | 2100 |  | 
 | 2101 |     if (ecmd->port == PORT_AUI) | 
 | 2102 | 	smc_set_xcvr(dev, 1); | 
 | 2103 |     else | 
 | 2104 | 	smc_set_xcvr(dev, 0); | 
 | 2105 |  | 
 | 2106 |     SMC_SELECT_BANK(0); | 
 | 2107 |     tmp = inw(ioaddr + TCR); | 
 | 2108 |     if (ecmd->duplex == DUPLEX_FULL) | 
 | 2109 | 	tmp |= TCR_FDUPLX; | 
 | 2110 |     else | 
 | 2111 | 	tmp &= ~TCR_FDUPLX; | 
 | 2112 |     outw(tmp, ioaddr + TCR); | 
 | 2113 | 	 | 
 | 2114 |     return 0; | 
 | 2115 | } | 
 | 2116 |  | 
 | 2117 | static int check_if_running(struct net_device *dev) | 
 | 2118 | { | 
 | 2119 | 	if (!netif_running(dev)) | 
 | 2120 | 		return -EINVAL; | 
 | 2121 | 	return 0; | 
 | 2122 | } | 
 | 2123 |  | 
 | 2124 | static void smc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) | 
 | 2125 | { | 
 | 2126 | 	strcpy(info->driver, DRV_NAME); | 
 | 2127 | 	strcpy(info->version, DRV_VERSION); | 
 | 2128 | } | 
 | 2129 |  | 
 | 2130 | static int smc_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) | 
 | 2131 | { | 
 | 2132 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 2133 | 	kio_addr_t ioaddr = dev->base_addr; | 
 | 2134 | 	u16 saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 2135 | 	int ret; | 
 | 2136 |  | 
 | 2137 | 	SMC_SELECT_BANK(3); | 
 | 2138 | 	spin_lock_irq(&smc->lock); | 
 | 2139 | 	if (smc->cfg & CFG_MII_SELECT) | 
 | 2140 | 		ret = mii_ethtool_gset(&smc->mii_if, ecmd); | 
 | 2141 | 	else | 
 | 2142 | 		ret = smc_netdev_get_ecmd(dev, ecmd); | 
 | 2143 | 	spin_unlock_irq(&smc->lock); | 
 | 2144 | 	SMC_SELECT_BANK(saved_bank); | 
 | 2145 | 	return ret; | 
 | 2146 | } | 
 | 2147 |  | 
 | 2148 | static int smc_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) | 
 | 2149 | { | 
 | 2150 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 2151 | 	kio_addr_t ioaddr = dev->base_addr; | 
 | 2152 | 	u16 saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 2153 | 	int ret; | 
 | 2154 |  | 
 | 2155 | 	SMC_SELECT_BANK(3); | 
 | 2156 | 	spin_lock_irq(&smc->lock); | 
 | 2157 | 	if (smc->cfg & CFG_MII_SELECT) | 
 | 2158 | 		ret = mii_ethtool_sset(&smc->mii_if, ecmd); | 
 | 2159 | 	else | 
 | 2160 | 		ret = smc_netdev_set_ecmd(dev, ecmd); | 
 | 2161 | 	spin_unlock_irq(&smc->lock); | 
 | 2162 | 	SMC_SELECT_BANK(saved_bank); | 
 | 2163 | 	return ret; | 
 | 2164 | } | 
 | 2165 |  | 
 | 2166 | static u32 smc_get_link(struct net_device *dev) | 
 | 2167 | { | 
 | 2168 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 2169 | 	kio_addr_t ioaddr = dev->base_addr; | 
 | 2170 | 	u16 saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 2171 | 	u32 ret; | 
 | 2172 |  | 
 | 2173 | 	SMC_SELECT_BANK(3); | 
 | 2174 | 	spin_lock_irq(&smc->lock); | 
 | 2175 | 	ret = smc_link_ok(dev); | 
 | 2176 | 	spin_unlock_irq(&smc->lock); | 
 | 2177 | 	SMC_SELECT_BANK(saved_bank); | 
 | 2178 | 	return ret; | 
 | 2179 | } | 
 | 2180 |  | 
 | 2181 | #ifdef PCMCIA_DEBUG | 
 | 2182 | static u32 smc_get_msglevel(struct net_device *dev) | 
 | 2183 | { | 
 | 2184 | 	return pc_debug; | 
 | 2185 | } | 
 | 2186 |  | 
 | 2187 | static void smc_set_msglevel(struct net_device *dev, u32 val) | 
 | 2188 | { | 
 | 2189 | 	pc_debug = val; | 
 | 2190 | } | 
 | 2191 | #endif | 
 | 2192 |  | 
 | 2193 | static int smc_nway_reset(struct net_device *dev) | 
 | 2194 | { | 
 | 2195 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 2196 | 	if (smc->cfg & CFG_MII_SELECT) { | 
 | 2197 | 		kio_addr_t ioaddr = dev->base_addr; | 
 | 2198 | 		u16 saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 2199 | 		int res; | 
 | 2200 |  | 
 | 2201 | 		SMC_SELECT_BANK(3); | 
 | 2202 | 		res = mii_nway_restart(&smc->mii_if); | 
 | 2203 | 		SMC_SELECT_BANK(saved_bank); | 
 | 2204 |  | 
 | 2205 | 		return res; | 
 | 2206 | 	} else | 
 | 2207 | 		return -EOPNOTSUPP; | 
 | 2208 | } | 
 | 2209 |  | 
| Jeff Garzik | 7282d49 | 2006-09-13 14:30:00 -0400 | [diff] [blame] | 2210 | static const struct ethtool_ops ethtool_ops = { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2211 | 	.begin = check_if_running, | 
 | 2212 | 	.get_drvinfo = smc_get_drvinfo, | 
 | 2213 | 	.get_settings = smc_get_settings, | 
 | 2214 | 	.set_settings = smc_set_settings, | 
 | 2215 | 	.get_link = smc_get_link, | 
 | 2216 | #ifdef PCMCIA_DEBUG | 
 | 2217 | 	.get_msglevel = smc_get_msglevel, | 
 | 2218 | 	.set_msglevel = smc_set_msglevel, | 
 | 2219 | #endif | 
 | 2220 | 	.nway_reset = smc_nway_reset, | 
 | 2221 | }; | 
 | 2222 |  | 
 | 2223 | static int smc_ioctl (struct net_device *dev, struct ifreq *rq, int cmd) | 
 | 2224 | { | 
 | 2225 | 	struct smc_private *smc = netdev_priv(dev); | 
 | 2226 | 	struct mii_ioctl_data *mii = if_mii(rq); | 
 | 2227 | 	int rc = 0; | 
 | 2228 | 	u16 saved_bank; | 
 | 2229 | 	kio_addr_t ioaddr = dev->base_addr; | 
 | 2230 |  | 
 | 2231 | 	if (!netif_running(dev)) | 
 | 2232 | 		return -EINVAL; | 
 | 2233 |  | 
 | 2234 | 	spin_lock_irq(&smc->lock); | 
 | 2235 | 	saved_bank = inw(ioaddr + BANK_SELECT); | 
 | 2236 | 	SMC_SELECT_BANK(3); | 
 | 2237 | 	rc = generic_mii_ioctl(&smc->mii_if, mii, cmd, NULL); | 
 | 2238 | 	SMC_SELECT_BANK(saved_bank); | 
 | 2239 | 	spin_unlock_irq(&smc->lock); | 
 | 2240 | 	return rc; | 
 | 2241 | } | 
 | 2242 |  | 
| Dominik Brodowski | 5c67222 | 2005-06-27 16:28:27 -0700 | [diff] [blame] | 2243 | static struct pcmcia_device_id smc91c92_ids[] = { | 
 | 2244 | 	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0109, 0x0501), | 
 | 2245 | 	PCMCIA_PFC_DEVICE_MANF_CARD(0, 0x0140, 0x000a), | 
 | 2246 | 	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3288", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x04cd2988, 0x46a52d63), | 
 | 2247 | 	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "CC/XJEM3336", "DATA/FAX/CELL ETHERNET MODEM", 0xf510db04, 0x0143b773, 0x46a52d63), | 
 | 2248 | 	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "EM1144T", "PCMCIA MODEM", 0xf510db04, 0x856d66c8, 0xbd6c43ef), | 
 | 2249 | 	PCMCIA_PFC_DEVICE_PROD_ID123(0, "MEGAHERTZ", "XJEM1144/CCEM1144", "PCMCIA MODEM", 0xf510db04, 0x52d21e1e, 0xbd6c43ef), | 
 | 2250 | 	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Gateway 2000", "XJEM3336", 0xdd9989be, 0x662c394c), | 
 | 2251 | 	PCMCIA_PFC_DEVICE_PROD_ID12(0, "MEGAHERTZ", "XJEM1144/CCEM1144", 0xf510db04, 0x52d21e1e), | 
| Komuro | d277ad0 | 2005-07-28 01:07:24 -0700 | [diff] [blame] | 2252 | 	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Diamonds Modem+Ethernet", 0xc2f80cd, 0x656947b9), | 
 | 2253 | 	PCMCIA_PFC_DEVICE_PROD_ID12(0, "Ositech", "Trumpcard:Jack of Hearts Modem+Ethernet", 0xc2f80cd, 0xdc9ba5ed), | 
| Dominik Brodowski | 5c67222 | 2005-06-27 16:28:27 -0700 | [diff] [blame] | 2254 | 	PCMCIA_MFC_DEVICE_MANF_CARD(0, 0x016c, 0x0020), | 
 | 2255 | 	PCMCIA_DEVICE_MANF_CARD(0x016c, 0x0023), | 
 | 2256 | 	PCMCIA_DEVICE_PROD_ID123("BASICS by New Media Corporation", "Ethernet", "SMC91C94", 0x23c78a9d, 0x00b2e941, 0xcef397fb), | 
 | 2257 | 	PCMCIA_DEVICE_PROD_ID12("ARGOSY", "Fast Ethernet PCCard", 0x78f308dc, 0xdcea68bc), | 
 | 2258 | 	PCMCIA_DEVICE_PROD_ID12("dit Co., Ltd.", "PC Card-10/100BTX", 0xe59365c8, 0x6a2161d1), | 
 | 2259 | 	PCMCIA_DEVICE_PROD_ID12("DYNALINK", "L100C", 0x6a26d1cf, 0xc16ce9c5), | 
 | 2260 | 	PCMCIA_DEVICE_PROD_ID12("Farallon", "Farallon Enet", 0x58d93fc4, 0x244734e9), | 
 | 2261 | 	PCMCIA_DEVICE_PROD_ID12("Megahertz", "CC10BT/2", 0x33234748, 0x3c95b953), | 
 | 2262 | 	PCMCIA_DEVICE_PROD_ID12("MELCO/SMC", "LPC-TX", 0xa2cd8e6d, 0x42da662a), | 
| Komuro | d277ad0 | 2005-07-28 01:07:24 -0700 | [diff] [blame] | 2263 | 	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Four of Diamonds Ethernet", 0xc2f80cd, 0xb3466314), | 
 | 2264 | 	PCMCIA_DEVICE_PROD_ID12("Ositech", "Trumpcard:Seven of Diamonds Ethernet", 0xc2f80cd, 0x194b650a), | 
| Dominik Brodowski | 5c67222 | 2005-06-27 16:28:27 -0700 | [diff] [blame] | 2265 | 	PCMCIA_DEVICE_PROD_ID12("PCMCIA", "Fast Ethernet PCCard", 0x281f1c5d, 0xdcea68bc), | 
 | 2266 | 	PCMCIA_DEVICE_PROD_ID12("Psion", "10Mb Ethernet", 0x4ef00b21, 0x844be9e9), | 
 | 2267 | 	PCMCIA_DEVICE_PROD_ID12("SMC", "EtherEZ Ethernet 8020", 0xc4f8b18b, 0x4a0eeb2d), | 
 | 2268 | 	/* These conflict with other cards! */ | 
 | 2269 | 	/* PCMCIA_DEVICE_MANF_CARD(0x0186, 0x0100), */ | 
 | 2270 | 	/* PCMCIA_DEVICE_MANF_CARD(0x8a01, 0xc1ab), */ | 
 | 2271 | 	PCMCIA_DEVICE_NULL, | 
 | 2272 | }; | 
 | 2273 | MODULE_DEVICE_TABLE(pcmcia, smc91c92_ids); | 
 | 2274 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2275 | static struct pcmcia_driver smc91c92_cs_driver = { | 
 | 2276 | 	.owner		= THIS_MODULE, | 
 | 2277 | 	.drv		= { | 
 | 2278 | 		.name	= "smc91c92_cs", | 
 | 2279 | 	}, | 
| Dominik Brodowski | 15b99ac | 2006-03-31 17:26:06 +0200 | [diff] [blame] | 2280 | 	.probe		= smc91c92_probe, | 
| Dominik Brodowski | cc3b486 | 2005-11-14 21:23:14 +0100 | [diff] [blame] | 2281 | 	.remove		= smc91c92_detach, | 
| Dominik Brodowski | 5c67222 | 2005-06-27 16:28:27 -0700 | [diff] [blame] | 2282 | 	.id_table       = smc91c92_ids, | 
| Dominik Brodowski | 98e4c28 | 2005-11-14 21:21:18 +0100 | [diff] [blame] | 2283 | 	.suspend	= smc91c92_suspend, | 
 | 2284 | 	.resume		= smc91c92_resume, | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2285 | }; | 
 | 2286 |  | 
 | 2287 | static int __init init_smc91c92_cs(void) | 
 | 2288 | { | 
 | 2289 | 	return pcmcia_register_driver(&smc91c92_cs_driver); | 
 | 2290 | } | 
 | 2291 |  | 
 | 2292 | static void __exit exit_smc91c92_cs(void) | 
 | 2293 | { | 
 | 2294 | 	pcmcia_unregister_driver(&smc91c92_cs_driver); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 2295 | } | 
 | 2296 |  | 
 | 2297 | module_init(init_smc91c92_cs); | 
 | 2298 | module_exit(exit_smc91c92_cs); |