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