xref: /netbsd-src/sys/dev/pcmcia/if_ne_pcmcia.c (revision 4d7e773266e3c3f48566c86c0ad52d51c6454fd1)
1 /*	$NetBSD: if_ne_pcmcia.c,v 1.5 1997/11/02 00:27:21 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *	This product includes software developed by Marc Horowitz.
17  * 4. The name of the author may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
24  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/select.h>
35 #include <sys/device.h>
36 #include <sys/socket.h>
37 
38 #include <net/if_types.h>
39 #include <net/if.h>
40 #include <net/if_media.h>
41 #include <net/if_ether.h>
42 
43 #include <machine/bus.h>
44 #include <machine/intr.h>
45 
46 #include <dev/pcmcia/pcmciareg.h>
47 #include <dev/pcmcia/pcmciavar.h>
48 
49 #include <dev/ic/dp8390reg.h>
50 #include <dev/ic/dp8390var.h>
51 
52 #include <dev/ic/ne2000reg.h>
53 #include <dev/ic/ne2000var.h>
54 
55 #ifdef __BROKEN_INDIRECT_CONFIG
56 int ne_pcmcia_match __P((struct device *, void *, void *));
57 #else
58 int ne_pcmcia_match __P((struct device *, struct cfdata *, void *));
59 #endif
60 void ne_pcmcia_attach __P((struct device *, struct device *, void *));
61 
62 int	ne_pcmcia_enable __P((struct dp8390_softc *));
63 void	ne_pcmcia_disable __P((struct dp8390_softc *));
64 
65 struct ne_pcmcia_softc {
66 	struct ne2000_softc sc_ne2000;		/* real "ne2000" softc */
67 
68 	/* PCMCIA-specific goo */
69 	struct pcmcia_io_handle sc_pcioh;	/* PCMCIA i/o information */
70 	int sc_asic_io_window;			/* i/o window for ASIC */
71 	int sc_nic_io_window;			/* i/o window for NIC */
72 	struct pcmcia_function *sc_pf;		/* our PCMCIA function */
73 	void *sc_ih;				/* interrupt handle */
74 };
75 
76 struct cfattach ne_pcmcia_ca = {
77 	sizeof(struct ne_pcmcia_softc), ne_pcmcia_match, ne_pcmcia_attach
78 };
79 
80 struct ne2000dev {
81     char *name;
82     int32_t manufacturer;
83     int32_t product;
84     char *cis1_info0;
85     char *cis1_info1;
86     int function;
87     int enet_maddr;
88     unsigned char enet_vendor[3];
89 } ne2000devs[] = {
90     { "PreMax PE-200",
91       0xffff, 0xffff, "PMX   ", "PE-200", 0,
92       0x07f0, { 0x00, 0x20, 0xe0 } },
93     { "National Semiconductor InfoMover",
94       0x00a4, 0x0002, NULL, NULL, 0,
95       0x0ff0, { 0x08, 0x00, 0x5a } },
96     { "DEC DEPCM-BA",
97       0x0000, 0x0000, "DIGITAL", "DEPCM-XX", 0,
98       0x0ff0, { 0x00, 0x00, 0xe8 } },
99     /* this card might need pcic_alloc_iobase < 0x400, and/or
100        CIRRUS_PD672X on TI TravelMate 5000 needs it: */
101     { "Linksys EthernetCard",
102       0x0149, 0x0265, "LINKSYS", "E-CARD",
103       0, -1, { 0x00, 0x80, 0xc8 } },
104     { "Planet SmartCOM 2000",
105       /* This card doesn't have manufacturer and product id in CIS.  */
106       PCMCIA_MANUFACTURER_INVALID, PCMCIA_PRODUCT_INVALID,
107       "PCMCIA", "UE2212", 0,
108       0xff0, { 0x00, 0x00, 0xe8 } },
109 #if 0
110     /* the rest of these are stolen from the linux pcnet pcmcia device
111        driver.  Since I don't know the manfid or cis info strings for
112        any of them, they're not compiled in until I do. */
113 
114     { "Accton EN2212",
115       0x0000, 0x0000, NULL, NULL, 0,
116       0x0ff0, { 0x00, 0x00, 0xe8 } },
117     { "Allied Telesis LA-PCM",
118       0x0000, 0x0000, NULL, NULL, 0,
119       0x0ff0, { 0x00, 0x00, 0xf4 } },
120     { "APEX MultiCard",
121       0x0000, 0x0000, NULL, NULL, 0,
122       0x03f4, { 0x00, 0x20, 0xe5 } },
123     { "ASANTE FriendlyNet",
124       0x0000, 0x0000, NULL, NULL, 0,
125       0x4910, { 0x00, 0x00, 0x94 } },
126     { "Danpex EN-6200P2",
127       0x0000, 0x0000, NULL, NULL, 0,
128       0x0110, { 0x00, 0x40, 0xc7 } },
129     { "DataTrek NetCard",
130       0x0000, 0x0000, NULL, NULL, 0,
131       0x0ff0, { 0x00, 0x20, 0xe8 } },
132     { "Dayna CommuniCard E",
133       0x0000, 0x0000, NULL, NULL, 0,
134       0x0110, { 0x00, 0x80, 0x19 } },
135     { "D-Link DE-650",
136       0x0000, 0x0000, NULL, NULL, 0,
137       0x0040, { 0x00, 0x80, 0xc8 } },
138     { "EP-210 Ethernet",
139       0x0000, 0x0000, NULL, NULL, 0,
140       0x0110, { 0x00, 0x40, 0x33 } },
141     { "Epson EEN10B",
142       0x0000, 0x0000, NULL, NULL, 0,
143       0x0ff0, { 0x00, 0x00, 0x48 } },
144     { "ELECOM Laneed LD-CDWA",
145       0x0000, 0x0000, NULL, NULL, 0,
146       0x00b8, { 0x08, 0x00, 0x42 } },
147     { "Grey Cell GCS2220",
148       0x0000, 0x0000, NULL, NULL, 0,
149       0x0000, { 0x00, 0x47, 0x43 } },
150     { "Hypertec Ethernet",
151       0x0000, 0x0000, NULL, NULL, 0,
152       0x01c0, { 0x00, 0x40, 0x4c } },
153     { "IBM CCAE",
154       0x0000, 0x0000, NULL, NULL, 0,
155       0x0ff0, { 0x08, 0x00, 0x5a } },
156     { "IBM CCAE",
157       0x0000, 0x0000, NULL, NULL, 0,
158       0x0ff0, { 0x00, 0x04, 0xac } },
159     { "IBM CCAE",
160       0x0000, 0x0000, NULL, NULL, 0,
161       0x0ff0, { 0x00, 0x06, 0x29 } },
162     { "IBM FME",
163       0x0000, 0x0000, NULL, NULL, 0,
164       0x0374, { 0x00, 0x04, 0xac } },
165     { "IBM FME",
166       0x0000, 0x0000, NULL, NULL, 0,
167       0x0374, { 0x08, 0x00, 0x5a } },
168     { "I-O DATA PCLA/T",
169       0x0000, 0x0000, NULL, NULL, 0,
170       0x0ff0, { 0x00, 0xa0, 0xb0 } },
171     { "Katron PE-520",
172       0x0000, 0x0000, NULL, NULL, 0,
173       0x0110, { 0x00, 0x40, 0xf6 } },
174     { "Kingston KNE-PCM/x",
175       0x0000, 0x0000, NULL, NULL, 0,
176       0x0ff0, { 0x00, 0xc0, 0xf0 } },
177     { "Kingston KNE-PCM/x",
178       0x0000, 0x0000, NULL, NULL, 0,
179       0x0ff0, { 0xe2, 0x0c, 0x0f } },
180     { "Kingston KNE-PC2",
181       0x0000, 0x0000, NULL, NULL, 0,
182       0x0180, { 0x00, 0xc0, 0xf0 } },
183     { "Longshine LCS-8534",
184       0x0000, 0x0000, NULL, NULL, 0,
185       0x0000, { 0x08, 0x00, 0x00 } },
186     { "Maxtech PCN2000",
187       0x0000, 0x0000, NULL, NULL, 0,
188       0x5000, { 0x00, 0x00, 0xe8 } },
189     { "NDC Instant-Link",
190       0x0000, 0x0000, NULL, NULL, 0,
191       0x003a, { 0x00, 0x80, 0xc6 } },
192     { "NE2000 Compatible",
193       0x0000, 0x0000, NULL, NULL, 0,
194       0x0ff0, { 0x00, 0xa0, 0x0c } },
195     { "Network General Sniffer",
196       0x0000, 0x0000, NULL, NULL, 0,
197       0x0ff0, { 0x00, 0x00, 0x65 } },
198     { "Panasonic VEL211",
199       0x0000, 0x0000, NULL, NULL, 0,
200       0x0ff0, { 0x00, 0x80, 0x45 } },
201     { "RPTI EP400",
202       0x0000, 0x0000, NULL, NULL, 0,
203       0x0110, { 0x00, 0x40, 0x95 } },
204     { "SCM Ethernet",
205       0x0000, 0x0000, NULL, NULL, 0,
206       0x0ff0, { 0x00, 0x20, 0xcb } },
207     { "Socket EA",
208       0x0000, 0x0000, NULL, NULL, 0,
209       0x4000, { 0x00, 0xc0, 0x1b } },
210     { "Volktek NPL-402CT",
211       0x0000, 0x0000, NULL, NULL, 0,
212       0x0060, { 0x00, 0x40, 0x05 } },
213 #endif
214 };
215 
216 #define	NE2000_NDEVS	(sizeof(ne2000devs) / sizeof(ne2000devs[0]))
217 
218 #define ne2000_match(card, fct, n) \
219 ((((((card)->manufacturer != PCMCIA_MANUFACTURER_INVALID) && \
220     ((card)->manufacturer == ne2000devs[(n)].manufacturer) && \
221     ((card)->product != PCMCIA_PRODUCT_INVALID) && \
222     ((card)->product == ne2000devs[(n)].product)) || \
223    ((ne2000devs[(n)].cis1_info0) && (ne2000devs[(n)].cis1_info1) && \
224     (strcmp((card)->cis1_info[0], ne2000devs[(n)].cis1_info0) == 0) && \
225     (strcmp((card)->cis1_info[1], ne2000devs[(n)].cis1_info1) == 0))) && \
226   ((fct) == ne2000devs[(n)].function))? \
227  &ne2000devs[(n)]:NULL)
228 
229 int
230 ne_pcmcia_match(parent, match, aux)
231 	struct device *parent;
232 #ifdef __BROKEN_INDIRECT_CONFIG
233 	void *match;
234 #else
235 	struct cfdata *cf;
236 #endif
237 	void *aux;
238 {
239 	struct pcmcia_attach_args *pa = aux;
240 	int i;
241 
242 	for (i = 0; i < NE2000_NDEVS; i++) {
243 		if (ne2000_match(pa->card, pa->pf->number, i))
244 			return (1);
245 	}
246 
247 	return (0);
248 }
249 
250 void
251 ne_pcmcia_attach(parent, self, aux)
252 	struct device *parent, *self;
253 	void *aux;
254 {
255 	struct ne_pcmcia_softc *psc = (void *) self;
256 	struct ne2000_softc *nsc = &psc->sc_ne2000;
257 	struct dp8390_softc *dsc = &nsc->sc_dp8390;
258 	struct pcmcia_attach_args *pa = aux;
259 	struct pcmcia_config_entry *cfe;
260 	struct ne2000dev *ne_dev;
261 	struct pcmcia_mem_handle pcmh;
262 	bus_addr_t offset;
263 	int i, j, mwindow;
264 	u_int8_t myea[6], *enaddr = NULL;
265 
266 	psc->sc_pf = pa->pf;
267 	cfe = pa->pf->cfe_head.sqh_first;
268 
269 #if 0
270 	/*
271 	 * Some ne2000 driver's claim to have memory; others don't.
272 	 * Since I don't care, I don't check.
273 	 */
274 
275 	if (cfe->num_memspace != 1) {
276 		printf(": unexpected number of memory spaces "
277 		    " %d should be 1\n", cfe->num_memspace);
278 		return;
279 	}
280 #endif
281 
282 	if (cfe->num_iospace == 1) {
283 		if (cfe->iospace[0].length != NE2000_NPORTS) {
284 			printf(": unexpected I/O space configuration\n");
285 			return;
286 		}
287 	} else if (cfe->num_iospace == 2) {
288 		/*
289 		 * Some cards report a separate space for NIC and ASIC.
290 		 * This make some sense, but we must allocate a single
291 		 * NE2000_NPORTS-sized chunk, due to brain damaged
292 		 * address decoders on some of these cards.
293 		 */
294 		if ((cfe->iospace[0].length + cfe->iospace[1].length) !=
295 		    NE2000_NPORTS) {
296 			printf(": unexpected I/O space configuration\n");
297 			return;
298 		}
299 	} else {
300 		printf(": unexpected number of i/o spaces %d"
301 		    " should be 1 or 2\n", cfe->num_iospace);
302 	}
303 
304 	if (pcmcia_io_alloc(pa->pf, 0, NE2000_NPORTS, NE2000_NPORTS,
305 	    &psc->sc_pcioh)) {
306 		printf(": can't alloc i/o space\n");
307 		return;
308 	}
309 
310 	dsc->sc_regt = psc->sc_pcioh.iot;
311 	dsc->sc_regh = psc->sc_pcioh.ioh;
312 
313 	nsc->sc_asict = psc->sc_pcioh.iot;
314 	if (bus_space_subregion(dsc->sc_regt, dsc->sc_regh,
315 	    NE2000_ASIC_OFFSET, NE2000_ASIC_NPORTS,
316 	    &nsc->sc_asich)) {
317 		printf(": can't get subregion for asic\n");
318 		return;
319 	}
320 
321 	/* Set up power management hooks. */
322 	dsc->sc_enable = ne_pcmcia_enable;
323 	dsc->sc_disable = ne_pcmcia_disable;
324 
325 	/* Enable the card. */
326 	pcmcia_function_init(pa->pf, cfe);
327 	if (pcmcia_function_enable(pa->pf)) {
328 		printf(": function enable failed\n");
329 		return;
330 	}
331 
332 	/* some cards claim to be io16, but they're lying. */
333 	if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_IO8,
334 	    NE2000_NIC_OFFSET, NE2000_NIC_NPORTS,
335 	    &psc->sc_pcioh, &psc->sc_nic_io_window)) {
336 		printf(": can't map NIC i/o space\n");
337 		return;
338 	}
339 
340 	if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_IO16,
341 	    NE2000_ASIC_OFFSET, NE2000_ASIC_NPORTS,
342 	    &psc->sc_pcioh, &psc->sc_asic_io_window)) {
343 		printf(": can't map ASIC i/o space\n");
344 		return;
345 	}
346 
347 	printf("\n");
348 
349 	/*
350 	 * Read the station address from the board.
351 	 */
352 	for (i = 0; i < NE2000_NDEVS; i++) {
353 		if ((ne_dev = ne2000_match(pa->card, pa->pf->number, i))
354 		    != NULL) {
355 			if (ne_dev->enet_maddr >= 0) {
356 				if (pcmcia_mem_alloc(pa->pf,
357 				    ETHER_ADDR_LEN * 2, &pcmh)) {
358 					printf("%s: can't alloc mem for"
359 					    " enet addr\n",
360 					    dsc->sc_dev.dv_xname);
361 					return;
362 				}
363 				if (pcmcia_mem_map(pa->pf, PCMCIA_MEM_ATTR,
364 				    ne_dev->enet_maddr, ETHER_ADDR_LEN * 2,
365 				    &pcmh, &offset, &mwindow)) {
366 					printf("%s: can't map mem for"
367 					    " enet addr\n",
368 					    dsc->sc_dev.dv_xname);
369 					return;
370 				}
371 				for (j = 0; j < ETHER_ADDR_LEN; j++)
372 					myea[j] = bus_space_read_1(pcmh.memt,
373 					    pcmh.memh, offset + (j * 2));
374 				pcmcia_mem_unmap(pa->pf, mwindow);
375 				pcmcia_mem_free(pa->pf, &pcmh);
376 				enaddr = myea;
377 			}
378 			break;
379 		}
380 	}
381 
382 	if (enaddr != NULL) {
383 		/*
384 		 * Make sure this is what we expect.
385 		 */
386 		if (enaddr[0] != ne_dev->enet_vendor[0] ||
387 		    enaddr[1] != ne_dev->enet_vendor[1] ||
388 		    enaddr[2] != ne_dev->enet_vendor[2]) {
389 			printf("\n%s: enet addr has incorrect vendor code\n",
390 			    dsc->sc_dev.dv_xname);
391 			printf("%s: (%02x:%02x:%02x should be "
392 			    "%02x:%02x:%02x)\n", dsc->sc_dev.dv_xname,
393 			    enaddr[0], enaddr[1], enaddr[2],
394 			    ne_dev->enet_vendor[0],
395 			    ne_dev->enet_vendor[1],
396 			    ne_dev->enet_vendor[2]);
397 			return;
398 		}
399 	}
400 
401 	printf("%s: %s Ethernet\n", dsc->sc_dev.dv_xname, ne_dev->name);
402 
403 	ne2000_attach(nsc, enaddr);
404 
405 	pcmcia_function_disable(pa->pf);
406 }
407 
408 int
409 ne_pcmcia_enable(dsc)
410 	struct dp8390_softc *dsc;
411 {
412 	struct ne_pcmcia_softc *psc = (struct ne_pcmcia_softc *)dsc;
413 
414 	/* set up the interrupt */
415 	psc->sc_ih = pcmcia_intr_establish(psc->sc_pf, IPL_NET, dp8390_intr,
416 	    dsc);
417 	if (psc->sc_ih == NULL) {
418 		printf("%s: couldn't establish interrupt\n",
419 		    dsc->sc_dev.dv_xname);
420 		return (1);
421 	}
422 
423 	return (pcmcia_function_enable(psc->sc_pf));
424 }
425 
426 void
427 ne_pcmcia_disable(dsc)
428 	struct dp8390_softc *dsc;
429 {
430 	struct ne_pcmcia_softc *psc = (struct ne_pcmcia_softc *)dsc;
431 
432 	pcmcia_function_disable(psc->sc_pf);
433 
434 	pcmcia_intr_disestablish(psc->sc_pf, psc->sc_ih);
435 }
436