xref: /openbsd-src/sys/dev/pcmcia/if_ep_pcmcia.c (revision 8b4b0e5989d8464d895bc87bc28811830534d79c)
1 /*	$OpenBSD: if_ep_pcmcia.c,v 1.52 2024/05/26 08:46:28 jsg 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 <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/timeout.h>
66 #include <sys/device.h>
67 
68 #include <net/if.h>
69 #include <net/if_media.h>
70 
71 #include <netinet/in.h>
72 #include <netinet/if_ether.h>
73 
74 #include <machine/bus.h>
75 
76 #include <dev/mii/miivar.h>
77 
78 #include <dev/ic/elink3var.h>
79 
80 #include <dev/pcmcia/pcmciareg.h>
81 #include <dev/pcmcia/pcmciavar.h>
82 #include <dev/pcmcia/pcmciadevs.h>
83 
84 int	ep_pcmcia_match(struct device *, void *, void *);
85 void	ep_pcmcia_attach(struct device *, struct device *, void *);
86 int	ep_pcmcia_detach(struct device *, int);
87 int	ep_pcmcia_activate(struct device *, int);
88 
89 int	ep_pcmcia_get_enaddr(struct pcmcia_tuple *, void *);
90 #ifdef notyet
91 int	ep_pcmcia_enable(struct ep_softc *);
92 void	ep_pcmcia_disable(struct ep_softc *);
93 void	ep_pcmcia_disable1(struct ep_softc *);
94 #endif
95 
96 int	ep_pcmcia_enable1(struct ep_softc *);
97 
98 struct ep_pcmcia_softc {
99 	struct ep_softc sc_ep;			/* real "ep" softc */
100 
101 	/* PCMCIA-specific goo */
102 	struct pcmcia_io_handle sc_pcioh;	/* PCMCIA i/o space info */
103 	int sc_io_window;			/* our i/o window */
104 	struct pcmcia_function *sc_pf;		/* our PCMCIA function */
105 };
106 
107 const struct cfattach ep_pcmcia_ca = {
108 	sizeof(struct ep_pcmcia_softc), ep_pcmcia_match, ep_pcmcia_attach,
109 	ep_pcmcia_detach, ep_pcmcia_activate
110 };
111 
112 struct ep_pcmcia_product {
113 	u_int16_t	epp_product;	/* PCMCIA product ID */
114 	u_short		epp_chipset;	/* 3Com chipset used */
115 	int		epp_flags;	/* initial softc flags */
116 	int		epp_expfunc;	/* expected function */
117 } ep_pcmcia_prod[] = {
118 	{ PCMCIA_PRODUCT_3COM_3C562,	EP_CHIPSET_3C509,
119 	  0,				0 },
120 
121 	{ PCMCIA_PRODUCT_3COM_3C589,	EP_CHIPSET_3C509,
122 	  0,				0 },
123 
124 	{ PCMCIA_PRODUCT_3COM_3CXEM556,	EP_CHIPSET_3C509,
125 	  0,				0 },
126 
127 	{ PCMCIA_PRODUCT_3COM_3CXEM556B,EP_CHIPSET_3C509,
128 	  0,				0 },
129 
130 	{ PCMCIA_PRODUCT_3COM_3C1,	EP_CHIPSET_3C509,
131 	  0,				0 },
132 
133 	{ PCMCIA_PRODUCT_3COM_3CCFEM556BI, EP_CHIPSET_ROADRUNNER,
134 	  EP_FLAGS_MII,			0 },
135 
136 	{ PCMCIA_PRODUCT_3COM_3C574,	EP_CHIPSET_ROADRUNNER,
137 	  EP_FLAGS_MII,			0 }
138 };
139 
140 struct ep_pcmcia_product *ep_pcmcia_lookup(struct pcmcia_attach_args *);
141 
142 struct ep_pcmcia_product *
ep_pcmcia_lookup(struct pcmcia_attach_args * pa)143 ep_pcmcia_lookup(struct pcmcia_attach_args *pa)
144 {
145 	int i;
146 
147 	for (i = 0; i < nitems(ep_pcmcia_prod); i++)
148 		if (pa->product == ep_pcmcia_prod[i].epp_product &&
149 		    pa->pf->number == ep_pcmcia_prod[i].epp_expfunc)
150 			return &ep_pcmcia_prod[i];
151 
152 	return (NULL);
153 }
154 
155 int
ep_pcmcia_match(struct device * parent,void * match,void * aux)156 ep_pcmcia_match(struct device *parent, void *match, void *aux)
157 {
158 	struct pcmcia_attach_args *pa = aux;
159 
160 	if (pa->manufacturer != PCMCIA_VENDOR_3COM)
161 		return (0);
162 
163 	if (ep_pcmcia_lookup(pa) != NULL)
164 		return (1);
165 
166 	return (0);
167 }
168 
169 #ifdef notdef
170 int
ep_pcmcia_enable(struct ep_softc * sc)171 ep_pcmcia_enable(struct ep_softc *sc)
172 {
173 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
174 	struct pcmcia_function *pf = psc->sc_pf;
175 
176 	/* establish the interrupt. */
177 	sc->sc_ih = pcmcia_intr_establish(pf, IPL_NET, epintr,
178 	    sc, sc->sc_dev.dv_xname);
179 	if (sc->sc_ih == NULL) {
180 		printf("%s: couldn't establish interrupt\n",
181 		    sc->sc_dev.dv_xname);
182 		return (1);
183 	}
184 
185 	return (ep_pcmcia_enable1(sc));
186 }
187 #endif
188 
189 int
ep_pcmcia_enable1(struct ep_softc * sc)190 ep_pcmcia_enable1(struct ep_softc *sc)
191 {
192 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
193 	struct pcmcia_function *pf = psc->sc_pf;
194 	int ret;
195 
196 	if ((ret = pcmcia_function_enable(pf)))
197 		return (ret);
198 
199 	if ((psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3C562) ||
200 	    (psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3CXEM556) ||
201 	    (psc->sc_pf->sc->card.product == PCMCIA_PRODUCT_3COM_3CXEM556B)) {
202 		int reg;
203 
204 		/* turn off the serial-disable bit */
205 
206 		reg = pcmcia_ccr_read(pf, PCMCIA_CCR_OPTION);
207 		if (reg & 0x08) {
208 			reg &= ~0x08;
209 			pcmcia_ccr_write(pf, PCMCIA_CCR_OPTION, reg);
210 		}
211 
212 	}
213 
214 	return (ret);
215 }
216 
217 #ifdef notyet
218 void
ep_pcmcia_disable(struct ep_softc * sc)219 ep_pcmcia_disable(struct ep_softc *sc)
220 {
221 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
222 
223 	pcmcia_intr_disestablish(psc->sc_pf, sc->sc_ih);
224 	ep_pcmcia_disable1(sc);
225 }
226 
227 void
ep_pcmcia_disable1(struct ep_softc * sc)228 ep_pcmcia_disable1(struct ep_softc *sc)
229 {
230 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *) sc;
231 
232 	pcmcia_function_disable(psc->sc_pf);
233 }
234 #endif
235 
236 void
ep_pcmcia_attach(struct device * parent,struct device * self,void * aux)237 ep_pcmcia_attach(struct device *parent, struct device *self, void *aux)
238 {
239 	struct ep_pcmcia_softc *psc = (void *) self;
240 	struct ep_softc *sc = &psc->sc_ep;
241 	struct pcmcia_attach_args *pa = aux;
242 	struct pcmcia_config_entry *cfe;
243 	struct ep_pcmcia_product *epp;
244 	u_int8_t myla[ETHER_ADDR_LEN];
245 	u_int8_t *enaddr = NULL;
246 	const char *intrstr;
247 	int i;
248 
249 	psc->sc_pf = pa->pf;
250 	cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head);
251 
252 	/* Enable the card. */
253 	pcmcia_function_init(pa->pf, cfe);
254 	if (ep_pcmcia_enable1(sc))
255 		printf(": function enable failed\n");
256 
257 #ifdef notyet
258 	sc->enabled = 1;
259 #endif
260 
261 	if (cfe->num_memspace != 0)
262 		printf(": unexpected number of memory spaces %d should be 0\n",
263 		    cfe->num_memspace);
264 
265 	if (cfe->num_iospace != 1)
266 		printf(": unexpected number of I/O spaces %d should be 1\n",
267 		    cfe->num_iospace);
268 
269 	if (pa->product == PCMCIA_PRODUCT_3COM_3C562) {
270 		bus_addr_t maxaddr = (pa->pf->sc->iobase + pa->pf->sc->iosize);
271 
272 		for (i = pa->pf->sc->iobase; i < maxaddr; i += 0x10) {
273 			/*
274 			 * the 3c562 can only use 0x??00-0x??7f
275 			 * according to the Linux driver
276 			 */
277 			if (i & 0x80)
278 				continue;
279 			if (pcmcia_io_alloc(pa->pf, i, cfe->iospace[0].length,
280 			    cfe->iospace[0].length, &psc->sc_pcioh) == 0)
281 				break;
282 		}
283 		if (i >= maxaddr) {
284 			printf(": can't allocate i/o space\n");
285 			return;
286 		}
287 	} else {
288 		if (pcmcia_io_alloc(pa->pf, 0, cfe->iospace[0].length,
289 		    cfe->iospace[0].length, &psc->sc_pcioh))
290 			printf(": can't allocate i/o space\n");
291 	}
292 
293 	sc->sc_iot = psc->sc_pcioh.iot;
294 	sc->sc_ioh = psc->sc_pcioh.ioh;
295 
296 	if (pcmcia_io_map(pa->pf, ((cfe->flags & PCMCIA_CFE_IO16) ?
297 	    PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_IO8), 0, cfe->iospace[0].length,
298 	    &psc->sc_pcioh, &psc->sc_io_window)) {
299 		printf(": can't map i/o space\n");
300 		return;
301 	}
302 
303 	printf(" port 0x%lx/%ld", psc->sc_pcioh.addr, psc->sc_pcioh.size);
304 
305 	switch (pa->product) {
306 	case PCMCIA_PRODUCT_3COM_3C562:
307 		/*
308 		 * 3c562a-c use this; 3c562d does it in the regular way.
309 		 * we might want to check the revision and produce a warning
310 		 * in the future.
311 		 */
312 		/* FALLTHROUGH */
313 	case PCMCIA_PRODUCT_3COM_3C574:
314 	case PCMCIA_PRODUCT_3COM_3CCFEM556BI:
315 		/*
316 		 * Apparently, some 3c574s do it this way, as well.
317 		 */
318 		if (pcmcia_scan_cis(parent, ep_pcmcia_get_enaddr, myla))
319 			enaddr = myla;
320 		break;
321 	}
322 
323 	sc->bustype = EP_BUS_PCMCIA;
324 
325 	epp = ep_pcmcia_lookup(pa);
326 	if (epp == NULL)
327 		panic("ep_pcmcia_attach: impossible");
328 
329 	sc->ep_flags = epp->epp_flags;
330 
331 #ifdef notyet
332 	sc->enable = ep_pcmcia_enable;
333 	sc->disable = ep_pcmcia_disable;
334 #endif
335 
336 	/* establish the interrupt. */
337 	sc->sc_ih = pcmcia_intr_establish(pa->pf, IPL_NET, epintr, sc,
338 	    sc->sc_dev.dv_xname);
339 	intrstr = pcmcia_intr_string(psc->sc_pf, sc->sc_ih);
340 	if (*intrstr)
341 		printf(", %s", intrstr);
342 
343 	printf(":");
344 
345 	epconfig(sc, epp->epp_chipset, enaddr);
346 
347 #ifdef notyet
348 	sc->enabled = 0;
349 
350 	ep_pcmcia_disable1(sc);
351 #endif
352 }
353 
354 int
ep_pcmcia_detach(struct device * dev,int flags)355 ep_pcmcia_detach(struct device *dev, int flags)
356 {
357 	int rv;
358 	struct ep_pcmcia_softc *psc = (struct ep_pcmcia_softc *)dev;
359 
360 	if ((rv = ep_detach(dev)) != 0)
361 		return (rv);
362 
363 	pcmcia_io_unmap(psc->sc_pf, psc->sc_io_window);
364 	pcmcia_io_free(psc->sc_pf, &psc->sc_pcioh);
365 
366 	return (0);
367 }
368 
369 int
ep_pcmcia_activate(struct device * dev,int act)370 ep_pcmcia_activate(struct device *dev, int act)
371 {
372 	struct ep_pcmcia_softc *sc = (struct ep_pcmcia_softc *)dev;
373 	struct ep_softc *esc = &sc->sc_ep;
374 	struct ifnet *ifp = &esc->sc_arpcom.ac_if;
375 
376 	switch (act) {
377 	case DVACT_SUSPEND:
378 		ifp->if_timer = 0;
379 		if (ifp->if_flags & IFF_RUNNING)
380 			epstop(esc);
381 		if (sc->sc_ep.sc_ih)
382 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ep.sc_ih);
383 		sc->sc_ep.sc_ih = NULL;
384 		pcmcia_function_disable(sc->sc_pf);
385 		break;
386 	case DVACT_RESUME:
387 		pcmcia_function_enable(sc->sc_pf);
388 		sc->sc_ep.sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET,
389 		    epintr, sc, esc->sc_dev.dv_xname);
390 		if (ifp->if_flags & IFF_UP)
391 			epinit(esc);
392 		break;
393 	case DVACT_DEACTIVATE:
394 		if (sc->sc_ep.sc_ih)
395 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ep.sc_ih);
396 		sc->sc_ep.sc_ih = NULL;
397 		pcmcia_function_disable(sc->sc_pf);
398 		break;
399 	}
400 	return (0);
401 }
402 
403 int
ep_pcmcia_get_enaddr(struct pcmcia_tuple * tuple,void * arg)404 ep_pcmcia_get_enaddr(struct pcmcia_tuple *tuple, void *arg)
405 {
406 	u_int8_t *myla = arg;
407 	int i;
408 
409 	/* this is 3c562a-c magic */
410 	if (tuple->code == 0x88) {
411 		if (tuple->length < ETHER_ADDR_LEN)
412 			return (0);
413 
414 		for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
415 			myla[i] = pcmcia_tuple_read_1(tuple, i + 1);
416 			myla[i + 1] = pcmcia_tuple_read_1(tuple, i);
417 		}
418 
419 		return (1);
420 	}
421 	return (0);
422 }
423