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