xref: /openbsd/sys/dev/pcmcia/if_ep_pcmcia.c (revision 891d7ab6)
1 /*	$OpenBSD: if_ep_pcmcia.c,v 1.41 2011/07/03 15:47:17 matthew Exp $	*/
2 /*	$NetBSD: if_ep_pcmcia.c,v 1.16 1998/08/17 23:20:40 thorpej Exp $  */
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10  * NASA Ames Research Center.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
36  *
37  * Redistribution and use in source and binary forms, with or without
38  * modification, are permitted provided that the following conditions
39  * are met:
40  * 1. Redistributions of source code must retain the above copyright
41  *    notice, this list of conditions and the following disclaimer.
42  * 2. Redistributions in binary form must reproduce the above copyright
43  *    notice, this list of conditions and the following disclaimer in the
44  *    documentation and/or other materials provided with the distribution.
45  * 3. All advertising materials mentioning features or use of this software
46  *    must display the following acknowledgement:
47  *	This product includes software developed by Marc Horowitz.
48  * 4. The name of the author may not be used to endorse or promote products
49  *    derived from this software without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
52  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
53  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
54  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
55  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
56  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
60  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61  */
62 
63 #include "bpfilter.h"
64 
65 #include <sys/param.h>
66 #include <sys/systm.h>
67 #include <sys/mbuf.h>
68 #include <sys/socket.h>
69 #include <sys/ioctl.h>
70 #include <sys/errno.h>
71 #include <sys/syslog.h>
72 #include <sys/selinfo.h>
73 #include <sys/timeout.h>
74 #include <sys/device.h>
75 
76 #include <net/if.h>
77 #include <net/if_dl.h>
78 #include <net/if_types.h>
79 #include <net/netisr.h>
80 #include <net/if_media.h>
81 
82 #ifdef INET
83 #include <netinet/in.h>
84 #include <netinet/in_systm.h>
85 #include <netinet/in_var.h>
86 #include <netinet/ip.h>
87 #include <netinet/if_ether.h>
88 #endif
89 
90 #if NBPFILTER > 0
91 #include <net/bpf.h>
92 #endif
93 
94 #include <machine/cpu.h>
95 #include <machine/bus.h>
96 
97 #include <dev/mii/mii.h>
98 #include <dev/mii/miivar.h>
99 
100 #include <dev/ic/elink3var.h>
101 #include <dev/ic/elink3reg.h>
102 
103 #include <dev/pcmcia/pcmciareg.h>
104 #include <dev/pcmcia/pcmciavar.h>
105 #include <dev/pcmcia/pcmciadevs.h>
106 
107 int	ep_pcmcia_match(struct device *, void *, void *);
108 void	ep_pcmcia_attach(struct device *, struct device *, void *);
109 int	ep_pcmcia_detach(struct device *, int);
110 int	ep_pcmcia_activate(struct device *, int);
111 
112 int	ep_pcmcia_get_enaddr(struct pcmcia_tuple *, void *);
113 #ifdef notyet
114 int	ep_pcmcia_enable(struct ep_softc *);
115 void	ep_pcmcia_disable(struct ep_softc *);
116 void	ep_pcmcia_disable1(struct ep_softc *);
117 #endif
118 
119 int	ep_pcmcia_enable1(struct ep_softc *);
120 
121 struct ep_pcmcia_softc {
122 	struct ep_softc sc_ep;			/* real "ep" softc */
123 
124 	/* PCMCIA-specific goo */
125 	struct pcmcia_io_handle sc_pcioh;	/* PCMCIA i/o space info */
126 	int sc_io_window;			/* our i/o window */
127 	struct pcmcia_function *sc_pf;		/* our PCMCIA function */
128 };
129 
130 struct cfattach ep_pcmcia_ca = {
131 	sizeof(struct ep_pcmcia_softc), ep_pcmcia_match, ep_pcmcia_attach,
132 	ep_pcmcia_detach, ep_pcmcia_activate
133 };
134 
135 struct ep_pcmcia_product {
136 	u_int16_t	epp_product;	/* PCMCIA product ID */
137 	u_short		epp_chipset;	/* 3Com chipset used */
138 	int		epp_flags;	/* initial softc flags */
139 	int		epp_expfunc;	/* expected function */
140 } ep_pcmcia_prod[] = {
141 	{ PCMCIA_PRODUCT_3COM_3C562,	EP_CHIPSET_3C509,
142 	  0,				0 },
143 
144 	{ PCMCIA_PRODUCT_3COM_3C589,	EP_CHIPSET_3C509,
145 	  0,				0 },
146 
147 	{ PCMCIA_PRODUCT_3COM_3CXEM556,	EP_CHIPSET_3C509,
148 	  0,				0 },
149 
150 	{ PCMCIA_PRODUCT_3COM_3CXEM556B,EP_CHIPSET_3C509,
151 	  0,				0 },
152 
153 	{ PCMCIA_PRODUCT_3COM_3C1,	EP_CHIPSET_3C509,
154 	  0,				0 },
155 
156 	{ PCMCIA_PRODUCT_3COM_3CCFEM556BI, EP_CHIPSET_ROADRUNNER,
157 	  EP_FLAGS_MII,			0 },
158 
159 	{ PCMCIA_PRODUCT_3COM_3C574,	EP_CHIPSET_ROADRUNNER,
160 	  EP_FLAGS_MII,			0 }
161 };
162 
163 struct ep_pcmcia_product *ep_pcmcia_lookup(struct pcmcia_attach_args *);
164 
165 struct ep_pcmcia_product *
166 ep_pcmcia_lookup(pa)
167 	struct pcmcia_attach_args *pa;
168 {
169 	int i;
170 
171 	for (i = 0; i < nitems(ep_pcmcia_prod); i++)
172 		if (pa->product == ep_pcmcia_prod[i].epp_product &&
173 		    pa->pf->number == ep_pcmcia_prod[i].epp_expfunc)
174 			return &ep_pcmcia_prod[i];
175 
176 	return (NULL);
177 }
178 
179 int
180 ep_pcmcia_match(parent, match, aux)
181 	struct device *parent;
182 	void *match, *aux;
183 {
184 	struct pcmcia_attach_args *pa = aux;
185 
186 	if (pa->manufacturer != PCMCIA_VENDOR_3COM)
187 		return (0);
188 
189 	if (ep_pcmcia_lookup(pa) != NULL)
190 		return (1);
191 
192 	return (0);
193 }
194 
195 #ifdef notdef
196 int
197 ep_pcmcia_enable(sc)
198 	struct ep_softc *sc;
199 {
200 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
201 	struct pcmcia_function *pf = psc->sc_pf;
202 
203 	/* establish the interrupt. */
204 	sc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, epintr,
205 	    sc, sc->sc_dev.dv_xname);
206 	if (sc->sc_ih == NULL) {
207 		printf("%s: couldn't establish interrupt\n",
208 		    sc->sc_dev.dv_xname);
209 		return (1);
210 	}
211 
212 	return (ep_pcmcia_enable1(sc));
213 }
214 #endif
215 
216 int
217 ep_pcmcia_enable1(sc)
218 	struct ep_softc *sc;
219 {
220 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
221 	struct pcmcia_function *pf = psc->sc_pf;
222 	int ret;
223 
224 	if ((ret = pcmcia_function_enable(pf)))
225 		return (ret);
226 
227 	if ((psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3C562) ||
228 	    (psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3CXEM556) ||
229 	    (psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3CXEM556B)) {
230 		int reg;
231 
232 		/* turn off the serial-disable bit */
233 
234 		reg = pcmcia_ccr_read(pf, PCMCIA_CCR_OPTION);
235 		if (reg & 0x08) {
236 			reg &= ~0x08;
237 			pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg);
238 		}
239 
240 	}
241 
242 	return (ret);
243 }
244 
245 #ifdef notyet
246 void
247 ep_pcmcia_disable(sc)
248 	struct ep_softc *sc;
249 {
250 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
251 
252 	pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih);
253 	ep_pcmcia_disable1(sc);
254 }
255 
256 void
257 ep_pcmcia_disable1(sc)
258 	struct ep_softc *sc;
259 {
260 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
261 
262 	pcmcia_function_disable(psc->sc_pf);
263 }
264 #endif
265 
266 void
267 ep_pcmcia_attach(parent, self, aux)
268 	struct device  *parent, *self;
269 	void           *aux;
270 {
271 	struct ep_pcmcia_softc *psc = (void *) self;
272 	struct ep_softc *sc = &psc->sc_ep;
273 	struct pcmcia_attach_args *pa = aux;
274 	struct pcmcia_config_entry *cfe;
275 	struct ep_pcmcia_product *epp;
276 	u_int8_t myla[ETHER_ADDR_LEN];
277 	u_int8_t *enaddr = NULL;
278 	const char *intrstr;
279 	int i;
280 
281 	psc->sc_pf = pa->pf;
282 	cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head);
283 
284 	/* Enable the card. */
285 	pcmcia_function_init(pa->pf, cfe);
286 	if (ep_pcmcia_enable1(sc))
287 		printf(": function enable failed\n");
288 
289 #ifdef notyet
290 	sc->enabled = 1;
291 #endif
292 
293 	if (cfe->num_memspace != 0)
294 		printf(": unexpected number of memory spaces %d should be 0\n",
295 		    cfe->num_memspace);
296 
297 	if (cfe->num_iospace != 1)
298 		printf(": unexpected number of I/O spaces %d should be 1\n",
299 		    cfe->num_iospace);
300 
301 	if (pa->product == PCMCIA_PRODUCT_3COM_3C562) {
302 		bus_addr_t maxaddr = (pa->pf->sc->iobase + pa->pf->sc->iosize);
303 
304 		for (i = pa->pf->sc->iobase; i < maxaddr; i += 0x10) {
305 			/*
306 			 * the 3c562 can only use 0x??00-0x??7f
307 			 * according to the Linux driver
308 			 */
309 			if (i & 0x80)
310 				continue;
311 			if (pcmcia_io_alloc(pa->pf, i, cfe->iospace[0].length,
312 			    cfe->iospace[0].length, &psc->sc_pcioh) == 0)
313 				break;
314 		}
315 		if (i >= maxaddr) {
316 			printf(": can't allocate i/o space\n");
317 			return;
318 		}
319 	} else {
320 		if (pcmcia_io_alloc(pa->pf, 0, cfe->iospace[0].length,
321 		    cfe->iospace[0].length, &psc->sc_pcioh))
322 			printf(": can't allocate i/o space\n");
323 	}
324 
325 	sc->sc_iot = psc->sc_pcioh.iot;
326 	sc->sc_ioh = psc->sc_pcioh.ioh;
327 
328 	if (pcmcia_io_map(pa->pf, ((cfe->flags & PCMCIA_CFE_IO16) ?
329 	    PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_IO8), 0, cfe->iospace[0].length,
330 	    &psc->sc_pcioh, &psc->sc_io_window)) {
331 		printf(": can't map i/o space\n");
332 		return;
333 	}
334 
335 	printf(" port 0x%lx/%d", psc->sc_pcioh.addr, psc->sc_pcioh.size);
336 
337 	switch (pa->product) {
338 	case PCMCIA_PRODUCT_3COM_3C562:
339 		/*
340 		 * 3c562a-c use this; 3c562d does it in the regular way.
341 		 * we might want to check the revision and produce a warning
342 		 * in the future.
343 		 */
344 		/* FALLTHROUGH */
345 	case PCMCIA_PRODUCT_3COM_3C574:
346 	case PCMCIA_PRODUCT_3COM_3CCFEM556BI:
347 		/*
348 		 * Apparently, some 3c574s do it this way, as well.
349 		 */
350 		if (pcmcia_scan_cis(parent, ep_pcmcia_get_enaddr, myla))
351 			enaddr = myla;
352 		break;
353 	}
354 
355 	sc->bustype = EP_BUS_PCMCIA;
356 
357 	epp = ep_pcmcia_lookup(pa);
358 	if (epp == NULL)
359 		panic("ep_pcmcia_attach: impossible");
360 
361 	sc->ep_flags = epp->epp_flags;
362 
363 #ifdef notyet
364 	sc->enable = ep_pcmcia_enable;
365 	sc->disable = ep_pcmcia_disable;
366 #endif
367 
368 	/* establish the interrupt. */
369 	sc->sc_ih = pcmcia_intr_establish(pa->pf, IPL_NET, epintr, sc,
370 	    sc->sc_dev.dv_xname);
371 	intrstr = pcmcia_intr_string(psc->sc_pf, sc->sc_ih);
372 	if (*intrstr)
373 		printf(", %s", intrstr);
374 
375 	printf(":");
376 
377 	epconfig(sc, epp->epp_chipset, enaddr);
378 
379 #ifdef notyet
380 	sc->enabled = 0;
381 
382 	ep_pcmcia_disable1(sc);
383 #endif
384 }
385 
386 int
387 ep_pcmcia_detach(dev, flags)
388 	struct device *dev;
389 	int flags;
390 {
391 	int rv;
392 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *)dev;
393 
394 	if ((rv = ep_detach(dev)) != 0)
395 		return (rv);
396 
397 	pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window);
398 	pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh);
399 
400 	return (0);
401 }
402 
403 int
404 ep_pcmcia_activate(dev, act)
405 	struct device *dev;
406 	int act;
407 {
408 	struct ep_pcmcia_softc *sc = (struct ep_pcmcia_softc *)dev;
409 	struct ep_softc *esc = &sc->sc_ep;
410 	struct ifnet *ifp = &esc->sc_arpcom.ac_if;
411 
412 	switch (act) {
413 	case DVACT_SUSPEND:
414 		ifp->if_timer = 0;
415 		if (ifp->if_flags & IFF_RUNNING)
416 			epstop(esc);
417 		if (sc->sc_ep.sc_ih)
418 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ep.sc_ih);
419 		sc->sc_ep.sc_ih = NULL;
420 		pcmcia_function_disable(sc->sc_pf);
421 		break;
422 	case DVACT_RESUME:
423 		pcmcia_function_enable(sc->sc_pf);
424 		sc->sc_ep.sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET,
425 		    epintr, sc, esc->sc_dev.dv_xname);
426 		if (ifp->if_flags & IFF_UP)
427 			epinit(esc);
428 		break;
429 	case DVACT_DEACTIVATE:
430 		if (sc->sc_ep.sc_ih)
431 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ep.sc_ih);
432 		sc->sc_ep.sc_ih = NULL;
433 		pcmcia_function_disable(sc->sc_pf);
434 		break;
435 	}
436 	return (0);
437 }
438 
439 int
440 ep_pcmcia_get_enaddr(tuple, arg)
441 	struct pcmcia_tuple *tuple;
442 	void *arg;
443 {
444 	u_int8_t *myla = arg;
445 	int i;
446 
447 	/* this is 3c562a-c magic */
448 	if (tuple->code == 0x88) {
449 		if (tuple->length < ETHER_ADDR_LEN)
450 			return (0);
451 
452 		for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
453 			myla[i] = pcmcia_tuple_read_1(tuple, i + 1);
454 			myla[i + 1] = pcmcia_tuple_read_1(tuple, i);
455 		}
456 
457 		return (1);
458 	}
459 	return (0);
460 }
461