1 /* smc-mca.c: A SMC Ultra ethernet driver for linux. */
3 Most of this driver, except for ultramca_probe is nearly
4 verbatim from smc-ultra.c by Donald Becker. The rest is
5 written and copyright 1996 by David Weis, weisd3458@uni.edu
7 This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
9 This driver uses the cards in the 8390-compatible, shared memory mode.
10 Most of the run-time complexity is handled by the generic code in
13 This driver enables the shared memory only when doing the actual data
14 transfers to avoid a bug in early version of the card that corrupted
15 data transferred by a AHA1542.
17 This driver does not support the programmed-I/O data transfer mode of
18 the EtherEZ. That support (if available) is smc-ez.c. Nor does it
19 use the non-8390-compatible "Altego" mode. (No support currently planned.)
23 Paul Gortmaker : multiple card support for module users.
24 David Weis : Micro Channel-ized it.
25 Tom Sightler : Added support for IBM PS/2 Ethernet Adapter/A
26 Christopher Turcksin : Changed MCA-probe so that multiple adapters are
27 found correctly (Jul 16, 1997)
28 Chris Beauregard : Tried to merge the two changes above (Dec 15, 1997)
29 Tom Sightler : Fixed minor detection bug caused by above merge
30 Tom Sightler : Added support for three more Western Digital
32 Tom Sightler : Added support for 2.2.x mca_find_unused_adapter
33 Hartmut Schmidt : - Modified parameter detection to handle each
34 card differently depending on a switch-list
35 - 'card_ver' removed from the adapter list
36 - Some minor bug fixes
40 #include <linux/module.h>
42 #include <linux/kernel.h>
43 #include <linux/sched.h>
44 #include <linux/errno.h>
45 #include <linux/string.h>
46 #include <linux/init.h>
48 #include <asm/system.h>
50 #include <linux/netdevice.h>
51 #include <linux/etherdevice.h>
54 #include <linux/mca.h>
56 int ultramca_probe(struct net_device
*dev
);
58 static int ultramca_open(struct net_device
*dev
);
59 static void ultramca_reset_8390(struct net_device
*dev
);
60 static void ultramca_get_8390_hdr(struct net_device
*dev
,
61 struct e8390_pkt_hdr
*hdr
,
63 static void ultramca_block_input(struct net_device
*dev
, int count
,
66 static void ultramca_block_output(struct net_device
*dev
, int count
,
67 const unsigned char *buf
,
68 const int start_page
);
69 static int ultramca_close_card(struct net_device
*dev
);
71 #define START_PG 0x00 /* First page of TX buffer */
73 #define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
74 #define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
75 #define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
76 #define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
77 #define ULTRA_IO_EXTENT 32
78 #define EN0_ERWCNT 0x08 /* Early receive warning count. */
80 #define _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A 0
81 #define _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A 1
82 #define _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A 2
83 #define _6fc1_WD_Starcard_PLUS_A_WD8003ST_A 3
84 #define _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A 4
85 #define _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A 5
86 #define _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A 6
87 #define _efe5_IBM_PS2_Adapter_A_for_Ethernet 7
89 struct smc_mca_adapters_t
{
94 static const struct smc_mca_adapters_t smc_mca_adapters
[] = {
95 { 0x61c8, "SMC Ethercard PLUS Elite/A BNC/AUI (WD8013EP/A)" },
96 { 0x61c9, "SMC Ethercard PLUS Elite/A UTP/AUI (WD8013WP/A)" },
97 { 0x6fc0, "WD Ethercard PLUS/A (WD8003E/A or WD8003ET/A)" },
98 { 0x6fc1, "WD Starcard PLUS/A (WD8003ST/A)" },
99 { 0x6fc2, "WD Ethercard PLUS 10T/A (WD8003W/A)" },
100 { 0xefd4, "IBM PS/2 Adapter/A for Ethernet UTP/AUI (WD8013WP/A)" },
101 { 0xefd5, "IBM PS/2 Adapter/A for Ethernet BNC/AUI (WD8013EP/A)" },
102 { 0xefe5, "IBM PS/2 Adapter/A for Ethernet" },
106 int __init
ultramca_probe(struct net_device
*dev
)
108 unsigned short ioaddr
;
109 unsigned char reg4
, num_pages
;
111 unsigned char pos2
= 0xff, pos3
= 0xff, pos4
= 0xff, pos5
= 0xff;
113 int adapter_found
= 0;
117 int base_addr
= dev
->base_addr
;
124 SET_MODULE_OWNER(dev
);
126 if (base_addr
|| irq
) {
127 printk(KERN_INFO
"Probing for SMC MCA adapter");
129 printk(KERN_INFO
" at I/O address 0x%04x%c",
130 base_addr
, irq
? ' ' : '\n');
133 printk(KERN_INFO
"using irq %d\n", irq
);
137 /* proper multicard detection by ZP Gu (zpg@castle.net) */
139 for (j
= 0; (smc_mca_adapters
[j
].name
!= NULL
) && !adapter_found
; j
++) {
140 slot
= mca_find_unused_adapter(smc_mca_adapters
[j
].id
, 0);
142 while((slot
!= MCA_NOTFOUND
) && !adapter_found
) {
146 /* If we're trying to match a specificied irq or
147 * io address, we'll reject the adapter
148 * found unless it's the one we're looking for
151 pos2
= mca_read_stored_pos(slot
, 2); /* io_addr */
152 pos3
= mca_read_stored_pos(slot
, 3); /* shared mem */
153 pos4
= mca_read_stored_pos(slot
, 4); /* ROM bios addr
155 pos5
= mca_read_stored_pos(slot
, 5); /* irq, media
158 /* Test the following conditions:
159 * - If an irq parameter is supplied, compare it
160 * with the irq of the adapter we found
161 * - If a base_addr paramater is given, compare it
162 * with the base_addr of the adapter we found
163 * - Check that the irq and the base_addr of the
164 * adapter we found is not already in use by
168 switch (j
) { /* j = card-idx (card array above) [hs] */
169 case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A
:
170 case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A
:
171 case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A
:
172 case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A
:
174 tbase
= addr_table
[(pos2
& 0xf0) >> 4].base_addr
;
175 tirq
= irq_table
[(pos5
& 0xc) >> 2].new_irq
;
178 case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A
:
179 case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A
:
180 case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A
:
181 case _efe5_IBM_PS2_Adapter_A_for_Ethernet
:
183 tbase
= ((pos2
& 0x0fe) * 0x10);
184 tirq
= irq_table
[(pos5
& 3)].old_irq
;
189 if(!tirq
|| !tbase
|| (irq
&& irq
!= tirq
) || (base_addr
&& tbase
!= base_addr
)) {
190 slot
= mca_find_unused_adapter(smc_mca_adapters
[j
].id
, slot
+ 1);
199 return ((base_addr
|| irq
) ? -ENXIO
: -ENODEV
);
204 printk(KERN_INFO
"%s: %s found in slot %d\n",
205 dev
->name
, smc_mca_adapters
[adapter
].name
, slot
+ 1);
207 mca_set_adapter_name(slot
, smc_mca_adapters
[adapter
].name
);
208 mca_mark_as_used(slot
);
211 dev
->base_addr
= ioaddr
= tbase
;
216 switch (j
) { /* 'j' = card-# in const array above [hs] */
217 case _61c8_SMC_Ethercard_PLUS_Elite_A_BNC_AUI_WD8013EP_A
:
218 case _61c9_SMC_Ethercard_PLUS_Elite_A_UTP_AUI_WD8013EP_A
:
220 for (i
= 0; i
< 16; i
++) { /* taking 16 counts
222 if (mem_table
[i
].mem_index
== (pos3
& ~MEM_MASK
)) {
223 dev
->mem_start
= mem_table
[i
].mem_start
;
224 num_pages
= mem_table
[i
].num_pages
;
229 case _6fc0_WD_Ethercard_PLUS_A_WD8003E_A_OR_WD8003ET_A
:
230 case _6fc1_WD_Starcard_PLUS_A_WD8003ST_A
:
231 case _6fc2_WD_Ethercard_PLUS_10T_A_WD8003W_A
:
232 case _efe5_IBM_PS2_Adapter_A_for_Ethernet
:
234 dev
->mem_start
= ((pos3
& 0xfc) * 0x1000);
238 case _efd4_IBM_PS2_Adapter_A_for_Ethernet_UTP_AUI_WD8013WP_A
:
239 case _efd5_IBM_PS2_Adapter_A_for_Ethernet_BNC_AUI_WD8013WP_A
:
241 /* courtesy of gamera@quartz.ocn.ne.jp, pos3 indicates
242 * the index of the 0x2000 step.
243 * beware different number of pages [hs]
245 dev
->mem_start
= 0xc0000 + (0x2000 * (pos3
& 0xf));
246 num_pages
= 0x20 + (2 * (pos3
& 0x10));
251 if (dev
->mem_start
== 0) /* sanity check, shouldn't happen */
254 if (!request_region(ioaddr
, ULTRA_IO_EXTENT
, dev
->name
))
257 reg4
= inb(ioaddr
+ 4) & 0x7f;
258 outb(reg4
, ioaddr
+ 4);
260 printk(KERN_INFO
"%s: Parameters: %#3x,", dev
->name
, ioaddr
);
262 for (i
= 0; i
< 6; i
++)
263 printk(KERN_INFO
" %2.2X", dev
->dev_addr
[i
] = inb(ioaddr
+ 8 + i
));
265 /* Switch from the station address to the alternate register set
266 * and read the useful registers there.
269 outb(0x80 | reg4
, ioaddr
+ 4);
271 /* Enable FINE16 mode to avoid BIOS ROM width mismatches @ reboot.
274 outb(0x80 | inb(ioaddr
+ 0x0c), ioaddr
+ 0x0c);
276 /* Switch back to the station address register set so that
277 * the MS-DOS driver can find the card after a warm boot.
280 outb(reg4
, ioaddr
+ 4);
282 /* Allocate dev->priv and fill in 8390 specific dev fields.
285 if (ethdev_init(dev
)) {
286 printk (KERN_INFO
", no memory for dev->priv.\n");
287 release_region(ioaddr
, ULTRA_IO_EXTENT
);
291 /* The 8390 isn't at the base address, so fake the offset
294 dev
->base_addr
= ioaddr
+ ULTRA_NIC_OFFSET
;
296 ei_status
.name
= "SMC Ultra MCA";
297 ei_status
.word16
= 1;
298 ei_status
.tx_start_page
= START_PG
;
299 ei_status
.rx_start_page
= START_PG
+ TX_PAGES
;
300 ei_status
.stop_page
= num_pages
;
302 dev
->rmem_start
= dev
->mem_start
+ TX_PAGES
* 256;
303 dev
->mem_end
= dev
->rmem_end
=
304 dev
->mem_start
+ (ei_status
.stop_page
- START_PG
) * 256;
306 printk(KERN_INFO
", IRQ %d memory %#lx-%#lx.\n",
307 dev
->irq
, dev
->mem_start
, dev
->mem_end
- 1);
309 ei_status
.reset_8390
= &ultramca_reset_8390
;
310 ei_status
.block_input
= &ultramca_block_input
;
311 ei_status
.block_output
= &ultramca_block_output
;
312 ei_status
.get_8390_hdr
= &ultramca_get_8390_hdr
;
314 ei_status
.priv
= slot
;
316 dev
->open
= &ultramca_open
;
317 dev
->stop
= &ultramca_close_card
;
323 static int ultramca_open(struct net_device
*dev
)
325 int ioaddr
= dev
->base_addr
- ULTRA_NIC_OFFSET
; /* ASIC addr */
328 if ((retval
= request_irq(dev
->irq
, ei_interrupt
, 0, dev
->name
, dev
)))
331 outb(ULTRA_MEMENB
, ioaddr
); /* Enable memory */
332 outb(0x80, ioaddr
+ 5); /* ??? */
333 outb(0x01, ioaddr
+ 6); /* Enable interrupts and memory. */
334 outb(0x04, ioaddr
+ 5); /* ??? */
336 /* Set the early receive warning level in window 0 high enough not
337 * to receive ERW interrupts.
340 /* outb_p(E8390_NODMA + E8390_PAGE0, dev->base_addr);
341 * outb(0xff, dev->base_addr + EN0_ERWCNT);
348 static void ultramca_reset_8390(struct net_device
*dev
)
350 int ioaddr
= dev
->base_addr
- ULTRA_NIC_OFFSET
; /* ASIC addr */
352 outb(ULTRA_RESET
, ioaddr
);
354 printk("resetting Ultra, t=%ld...", jiffies
);
357 outb(0x80, ioaddr
+ 5); /* ??? */
358 outb(0x01, ioaddr
+ 6); /* Enable interrupts and memory. */
361 printk("reset done\n");
365 /* Grab the 8390 specific header. Similar to the block_input routine, but
366 * we don't need to be concerned with ring wrap as the header will be at
367 * the start of a page, so we optimize accordingly.
370 static void ultramca_get_8390_hdr(struct net_device
*dev
, struct e8390_pkt_hdr
*hdr
, int ring_page
)
372 unsigned long hdr_start
= dev
->mem_start
+ ((ring_page
- START_PG
) << 8);
375 /* Officially this is what we are doing, but the readl() is faster */
376 memcpy_fromio(hdr
, hdr_start
, sizeof(struct e8390_pkt_hdr
));
378 ((unsigned int*)hdr
)[0] = readl(hdr_start
);
382 /* Block input and output are easy on shared memory ethercards, the only
383 * complication is when the ring buffer wraps.
386 static void ultramca_block_input(struct net_device
*dev
, int count
, struct sk_buff
*skb
, int ring_offset
)
388 unsigned long xfer_start
= dev
->mem_start
+ ring_offset
- (START_PG
<< 8);
390 if (xfer_start
+ count
> dev
->rmem_end
) {
391 /* We must wrap the input move. */
392 int semi_count
= dev
->rmem_end
- xfer_start
;
393 memcpy_fromio(skb
->data
, xfer_start
, semi_count
);
395 memcpy_fromio(skb
->data
+ semi_count
, dev
->rmem_start
, count
);
397 /* Packet is in one chunk -- we can copy + cksum. */
398 eth_io_copy_and_sum(skb
, xfer_start
, count
, 0);
403 static void ultramca_block_output(struct net_device
*dev
, int count
, const unsigned char *buf
,
406 unsigned long shmem
= dev
->mem_start
+ ((start_page
- START_PG
) << 8);
408 memcpy_toio(shmem
, buf
, count
);
411 static int ultramca_close_card(struct net_device
*dev
)
413 int ioaddr
= dev
->base_addr
- ULTRA_NIC_OFFSET
; /* ASIC addr */
415 netif_stop_queue(dev
);
418 printk("%s: Shutting down ethercard.\n", dev
->name
);
420 outb(0x00, ioaddr
+ 6); /* Disable interrupts. */
421 free_irq(dev
->irq
, dev
);
424 /* We should someday disable shared memory and change to 8-bit mode
433 #undef MODULE /* don't want to bother now! */
435 #define MAX_ULTRAMCA_CARDS 4 /* Max number of Ultra cards per module */
437 static struct net_device dev_ultra
[MAX_ULTRAMCA_CARDS
];
438 static int io
[MAX_ULTRAMCA_CARDS
];
439 static int irq
[MAX_ULTRAMCA_CARDS
];
441 MODULE_PARM(io
, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS
) "i");
442 MODULE_PARM(irq
, "1-" __MODULE_STRING(MAX_ULTRAMCA_CARDS
) "i");
444 int init_module(void)
446 int this_dev
, found
= 0;
448 for (this_dev
= 0; this_dev
< MAX_ULTRAMCA_CARDS
; this_dev
++) {
449 struct net_device
*dev
= &dev_ultra
[this_dev
];
450 dev
->irq
= irq
[this_dev
];
451 dev
->base_addr
= io
[this_dev
];
452 dev
->init
= ultramca_probe
;
454 if (register_netdev(dev
) != 0) {
455 if (found
!= 0) { /* Got at least one. */
458 printk(KERN_NOTICE
"smc-mca.c: No SMC Ultra card found (i/o = 0x%x).\n", io
[this_dev
]);
466 void cleanup_module(void)
470 for (this_dev
= 0; this_dev
< MAX_ULTRAMCA_CARDS
; this_dev
++) {
471 struct net_device
*dev
= &dev_ultra
[this_dev
];
472 if (dev
->priv
!= NULL
) {
473 void *priv
= dev
->priv
;
474 /* NB: ultra_close_card() does free_irq */
475 int ioaddr
= dev
->base_addr
- ULTRA_NIC_OFFSET
;
476 mca_mark_as_unused(ei_status
.priv
);
477 release_region(ioaddr
, ULTRA_IO_EXTENT
);
478 unregister_netdev(dev
);
487 * compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-mca.c"
489 * kept-new-versions: 5