xref: /netbsd-src/sys/dev/pcmcia/mhzc.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*	$NetBSD: mhzc.c,v 1.40 2007/10/19 12:01:06 ad Exp $	*/
2 
3 /*-
4  * Copyright (c) 1999, 2000, 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
9  * NASA Ames Research Center, and by Charles M. Hannum.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgement:
21  *	This product includes software developed by the NetBSD
22  *	Foundation, Inc. and its contributors.
23  * 4. Neither the name of The NetBSD Foundation nor the names of its
24  *    contributors may be used to endorse or promote products derived
25  *    from this software without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37  * POSSIBILITY OF SUCH DAMAGE.
38  */
39 
40 /*
41  * Device driver for the Megaherz X-JACK Ethernet/Modem combo cards.
42  *
43  * Many thanks to Chuck Cranor for having the patience to sift through
44  * the Linux smc91c92_cs.c driver to find the magic details to get this
45  * working!
46  */
47 
48 #include <sys/cdefs.h>
49 __KERNEL_RCSID(0, "$NetBSD: mhzc.c,v 1.40 2007/10/19 12:01:06 ad Exp $");
50 
51 #include "opt_inet.h"
52 #include "bpfilter.h"
53 
54 #include <sys/param.h>
55 #include <sys/systm.h>
56 #include <sys/mbuf.h>
57 #include <sys/socket.h>
58 #include <sys/ioctl.h>
59 #include <sys/errno.h>
60 #include <sys/syslog.h>
61 #include <sys/select.h>
62 #include <sys/tty.h>
63 #include <sys/device.h>
64 #include <sys/kernel.h>
65 #include <sys/proc.h>
66 
67 #include <net/if.h>
68 #include <net/if_dl.h>
69 #include <net/if_ether.h>
70 #include <net/if_media.h>
71 
72 #ifdef INET
73 #include <netinet/in.h>
74 #include <netinet/in_systm.h>
75 #include <netinet/in_var.h>
76 #include <netinet/ip.h>
77 #include <netinet/if_inarp.h>
78 #endif
79 
80 
81 #if NBPFILTER > 0
82 #include <net/bpf.h>
83 #include <net/bpfdesc.h>
84 #endif
85 
86 #include <sys/intr.h>
87 #include <sys/bus.h>
88 
89 #include <dev/ic/comreg.h>
90 #include <dev/ic/comvar.h>
91 
92 #include <dev/mii/mii.h>
93 #include <dev/mii/miivar.h>
94 
95 #include <dev/ic/smc91cxxreg.h>
96 #include <dev/ic/smc91cxxvar.h>
97 
98 #include <dev/pcmcia/pcmciareg.h>
99 #include <dev/pcmcia/pcmciavar.h>
100 #include <dev/pcmcia/pcmciadevs.h>
101 
102 #include "mhzc.h"
103 
104 struct mhzc_softc {
105 	struct device sc_dev;		/* generic device glue */
106 
107 	struct pcmcia_function *sc_pf;	/* our PCMCIA function */
108 	void *sc_ih;			/* interrupt handle */
109 
110 	const struct mhzc_product *sc_product;
111 
112 	/*
113 	 * Data for the Modem portion.
114 	 */
115 	struct device *sc_modem;
116 	struct pcmcia_io_handle sc_modem_pcioh;
117 	int sc_modem_io_window;
118 
119 	/*
120 	 * Data for the Ethernet portion.
121 	 */
122 	struct device *sc_ethernet;
123 	struct pcmcia_io_handle sc_ethernet_pcioh;
124 	int sc_ethernet_io_window;
125 
126 	int sc_flags;
127 };
128 
129 /* sc_flags */
130 #define	MHZC_MODEM_MAPPED	0x01
131 #define	MHZC_ETHERNET_MAPPED	0x02
132 #define	MHZC_MODEM_ENABLED	0x04
133 #define	MHZC_ETHERNET_ENABLED	0x08
134 #define	MHZC_MODEM_ALLOCED	0x10
135 #define	MHZC_ETHERNET_ALLOCED	0x20
136 
137 int	mhzc_match(struct device *, struct cfdata *, void *);
138 void	mhzc_attach(struct device *, struct device *, void *);
139 int	mhzc_detach(struct device *, int);
140 int	mhzc_activate(struct device *, enum devact);
141 
142 CFATTACH_DECL(mhzc, sizeof(struct mhzc_softc),
143     mhzc_match, mhzc_attach, mhzc_detach, mhzc_activate);
144 
145 int	mhzc_em3336_enaddr(struct mhzc_softc *, u_int8_t *);
146 int	mhzc_em3336_enable(struct mhzc_softc *);
147 
148 const struct mhzc_product {
149 	struct pcmcia_product mp_product;
150 
151 	/* Get the Ethernet address for this card. */
152 	int		(*mp_enaddr)(struct mhzc_softc *, u_int8_t *);
153 
154 	/* Perform any special `enable' magic. */
155 	int		(*mp_enable)(struct mhzc_softc *);
156 } mhzc_products[] = {
157 	{ { PCMCIA_VENDOR_MEGAHERTZ, PCMCIA_PRODUCT_MEGAHERTZ_EM3336,
158 	    PCMCIA_CIS_INVALID },
159 	  mhzc_em3336_enaddr,		mhzc_em3336_enable },
160 };
161 static const size_t mhzc_nproducts =
162     sizeof(mhzc_products) / sizeof(mhzc_products[0]);
163 
164 int	mhzc_print(void *, const char *);
165 
166 int	mhzc_check_cfe(struct mhzc_softc *, struct pcmcia_config_entry *);
167 int	mhzc_alloc_ethernet(struct mhzc_softc *, struct pcmcia_config_entry *);
168 
169 int	mhzc_enable(struct mhzc_softc *, int);
170 void	mhzc_disable(struct mhzc_softc *, int);
171 
172 int	mhzc_intr(void *);
173 
174 int
175 mhzc_match(struct device *parent, struct cfdata *match,
176     void *aux)
177 {
178 	struct pcmcia_attach_args *pa = aux;
179 
180 	if (pcmcia_product_lookup(pa, mhzc_products, mhzc_nproducts,
181 	    sizeof(mhzc_products[0]), NULL))
182 		return (2);		/* beat `com' */
183 	return (0);
184 }
185 
186 void
187 mhzc_attach(struct device *parent, struct device *self, void *aux)
188 {
189 	struct mhzc_softc *sc = (void *)self;
190 	struct pcmcia_attach_args *pa = aux;
191 	struct pcmcia_config_entry *cfe;
192 	int error;
193 
194 	sc->sc_pf = pa->pf;
195 
196 	sc->sc_product = pcmcia_product_lookup(pa, mhzc_products,
197 	    mhzc_nproducts, sizeof(mhzc_products[0]), NULL);
198 	if (!sc->sc_product)
199 		panic("mhzc_attach: impossible");
200 
201 	/*
202 	 * The address decoders on these cards are wacky.  The configuration
203 	 * entries are set up to look like serial ports, and have no
204 	 * information about the Ethernet portion.  In order to talk to
205 	 * the Modem portion, the I/O address must have bit 0x80 set.
206 	 * In order to talk to the Ethernet portion, the I/O address must
207 	 * have the 0x80 bit clear.
208 	 *
209 	 * The standard configuration entries conveniently have 0x80 set
210 	 * in them, and have a length of 8 (a 16550's size, convenient!),
211 	 * so we use those to set up the Modem portion.
212 	 *
213 	 * Once we have the Modem's address established, we search for
214 	 * an address suitable for the Ethernet portion.  We do this by
215 	 * rounding up to the next 16-byte aligned address where 0x80
216 	 * isn't set (the SMC Ethernet chip has a 16-byte address size)
217 	 * and attemping to allocate a 16-byte region until we succeed.
218 	 *
219 	 * Sure would have been nice if Megahertz had made the card a
220 	 * proper multi-function device.
221 	 */
222 	SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) {
223 		if (mhzc_check_cfe(sc, cfe)) {
224 			/* Found one! */
225 			break;
226 		}
227 	}
228 	if (cfe == NULL) {
229 		aprint_error("%s: unable to find suitable config table entry\n",
230 		    self->dv_xname);
231 		goto fail;
232 	}
233 
234 	if (mhzc_alloc_ethernet(sc, cfe) == 0) {
235 		aprint_error("%s: unable to allocate space for Ethernet portion\n",
236 		    self->dv_xname);
237 		goto fail;
238 	}
239 
240 	/* Enable the card. */
241 	pcmcia_function_init(pa->pf, cfe);
242 
243 	if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_IO8, &sc->sc_modem_pcioh,
244 	    &sc->sc_modem_io_window)) {
245 		aprint_error("%s: unable to map I/O space\n",
246 		    sc->sc_dev.dv_xname);
247 		goto fail;
248 	}
249 	sc->sc_flags |= MHZC_MODEM_MAPPED;
250 
251 	if (pcmcia_io_map(sc->sc_pf, PCMCIA_WIDTH_AUTO, &sc->sc_ethernet_pcioh,
252 	    &sc->sc_ethernet_io_window)) {
253 		aprint_error("%s: unable to map I/O space\n",
254 		    sc->sc_dev.dv_xname);
255 		goto fail;
256 	}
257 	sc->sc_flags |= MHZC_ETHERNET_MAPPED;
258 
259 	error = mhzc_enable(sc, MHZC_MODEM_ENABLED|MHZC_ETHERNET_ENABLED);
260 	if (error)
261 		goto fail;
262 
263 	/*XXXUNCONST*/
264 	sc->sc_modem = config_found(self, __UNCONST("com"), mhzc_print);
265 	/*XXXUNCONST*/
266 	sc->sc_ethernet = config_found(self, __UNCONST("sm"), mhzc_print);
267 
268 	mhzc_disable(sc, MHZC_MODEM_ENABLED|MHZC_ETHERNET_ENABLED);
269 	return;
270 
271 fail:
272 	/* I/O spaces will be freed by detach. */
273 	;
274 }
275 
276 int
277 mhzc_check_cfe(sc, cfe)
278 	struct mhzc_softc *sc;
279 	struct pcmcia_config_entry *cfe;
280 {
281 
282 	if (cfe->num_memspace != 0)
283 		return (0);
284 
285 	if (cfe->num_iospace != 1)
286 		return (0);
287 
288 	if (pcmcia_io_alloc(sc->sc_pf,
289 	    cfe->iospace[0].start,
290 	    cfe->iospace[0].length,
291 	    cfe->iospace[0].length,
292 	    &sc->sc_modem_pcioh) == 0) {
293 		/* Found one for the modem! */
294 		sc->sc_flags |= MHZC_MODEM_ALLOCED;
295 		return (1);
296 	}
297 
298 	return (0);
299 }
300 
301 int
302 mhzc_alloc_ethernet(sc, cfe)
303 	struct mhzc_softc *sc;
304 	struct pcmcia_config_entry *cfe;
305 {
306 	bus_addr_t addr, maxaddr;
307 
308 	addr = cfe->iospace[0].start + cfe->iospace[0].length;
309 	maxaddr = 0x1000;
310 
311 	/*
312 	 * Now round it up so that it starts on a 16-byte boundary.
313 	 */
314 	addr = roundup(addr, 0x10);
315 
316 	for (; (addr + 0x10) < maxaddr; addr += 0x10) {
317 		if (addr & 0x80)
318 			continue;
319 		if (pcmcia_io_alloc(sc->sc_pf, addr, 0x10, 0x10,
320 		    &sc->sc_ethernet_pcioh) == 0) {
321 			/* Found one for the ethernet! */
322 			sc->sc_flags |= MHZC_ETHERNET_ALLOCED;
323 			return (1);
324 		}
325 	}
326 
327 	return (0);
328 }
329 
330 int
331 mhzc_print(aux, pnp)
332 	void *aux;
333 	const char *pnp;
334 {
335 	const char *name = aux;
336 
337 	if (pnp)
338 		aprint_normal("%s at %s(*)",  name, pnp);
339 
340 	return (UNCONF);
341 }
342 
343 int
344 mhzc_detach(self, flags)
345 	struct device *self;
346 	int flags;
347 {
348 	struct mhzc_softc *sc = (void *)self;
349 	int rv;
350 
351 	if (sc->sc_ethernet != NULL) {
352 		rv = config_detach(sc->sc_ethernet, flags);
353 		if (rv != 0)
354 			return (rv);
355 		sc->sc_ethernet = NULL;
356 	}
357 
358 	if (sc->sc_modem != NULL) {
359 		rv = config_detach(sc->sc_modem, flags);
360 		if (rv != 0)
361 			return (rv);
362 		sc->sc_modem = NULL;
363 	}
364 
365 	/* Unmap our i/o windows. */
366 	if (sc->sc_flags & MHZC_MODEM_MAPPED)
367 		pcmcia_io_unmap(sc->sc_pf, sc->sc_modem_io_window);
368 	if (sc->sc_flags & MHZC_ETHERNET_MAPPED)
369 		pcmcia_io_unmap(sc->sc_pf, sc->sc_ethernet_io_window);
370 
371 	/* Free our i/o spaces. */
372 	if (sc->sc_flags & MHZC_ETHERNET_ALLOCED)
373 		pcmcia_io_free(sc->sc_pf, &sc->sc_modem_pcioh);
374 	if (sc->sc_flags & MHZC_MODEM_ALLOCED)
375 		pcmcia_io_free(sc->sc_pf, &sc->sc_ethernet_pcioh);
376 
377 	sc->sc_flags = 0;
378 
379 	return (0);
380 }
381 
382 int
383 mhzc_activate(self, act)
384 	struct device *self;
385 	enum devact act;
386 {
387 	struct mhzc_softc *sc = (void *)self;
388 	int s, rv = 0;
389 
390 	s = splhigh();
391 	switch (act) {
392 	case DVACT_ACTIVATE:
393 		rv = EOPNOTSUPP;
394 		break;
395 
396 	case DVACT_DEACTIVATE:
397 		if (sc->sc_ethernet != NULL) {
398 			rv = config_deactivate(sc->sc_ethernet);
399 			if (rv != 0)
400 				goto out;
401 		}
402 
403 		if (sc->sc_modem != NULL) {
404 			rv = config_deactivate(sc->sc_modem);
405 			if (rv != 0)
406 				goto out;
407 		}
408 		break;
409 	}
410  out:
411 	splx(s);
412 	return (rv);
413 }
414 
415 int
416 mhzc_intr(arg)
417 	void *arg;
418 {
419 	struct mhzc_softc *sc = arg;
420 	int rval = 0;
421 
422 #if NCOM_MHZC > 0
423 	if (sc->sc_modem != NULL &&
424 	    (sc->sc_flags & MHZC_MODEM_ENABLED) != 0)
425 		rval |= comintr(sc->sc_modem);
426 #endif
427 
428 #if NSM_MHZC > 0
429 	if (sc->sc_ethernet != NULL &&
430 	    (sc->sc_flags & MHZC_ETHERNET_ENABLED) != 0)
431 		rval |= smc91cxx_intr(sc->sc_ethernet);
432 #endif
433 
434 	return (rval);
435 }
436 
437 int
438 mhzc_enable(sc, flag)
439 	struct mhzc_softc *sc;
440 	int flag;
441 {
442 	int error;
443 
444 	if ((sc->sc_flags & flag) == flag) {
445 		printf("%s: already enabled\n", sc->sc_dev.dv_xname);
446 		return (0);
447 	}
448 
449 	if ((sc->sc_flags & (MHZC_MODEM_ENABLED|MHZC_ETHERNET_ENABLED)) != 0) {
450 		sc->sc_flags |= flag;
451 		return (0);
452 	}
453 
454 	/*
455 	 * Establish our interrupt handler.
456 	 *
457 	 * XXX Note, we establish this at IPL_NET.  This is suboptimal
458 	 * XXX the Modem portion, but is necessary to make the Ethernet
459 	 * XXX portion have the correct interrupt level semantics.
460 	 *
461 	 * XXX Eventually we should use the `enabled' bits in the
462 	 * XXX flags word to determine which level we should be at.
463 	 */
464 	sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_NET,
465 	    mhzc_intr, sc);
466 	if (!sc->sc_ih)
467 		return (EIO);
468 
469 	error = pcmcia_function_enable(sc->sc_pf);
470 	if (error) {
471 		pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
472 		sc->sc_ih = 0;
473 		return (error);
474 	}
475 
476 	/*
477 	 * Perform any special enable magic necessary.
478 	 */
479 	if (sc->sc_product->mp_enable != NULL &&
480 	    (*sc->sc_product->mp_enable)(sc) != 0) {
481 		pcmcia_function_disable(sc->sc_pf);
482 		pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
483 		return (1);
484 	}
485 
486 	sc->sc_flags |= flag;
487 	return (0);
488 }
489 
490 void
491 mhzc_disable(sc, flag)
492 	struct mhzc_softc *sc;
493 	int flag;
494 {
495 
496 	if ((sc->sc_flags & flag) == 0) {
497 		printf("%s: already disabled\n", sc->sc_dev.dv_xname);
498 		return;
499 	}
500 
501 	sc->sc_flags &= ~flag;
502 	if ((sc->sc_flags & (MHZC_MODEM_ENABLED|MHZC_ETHERNET_ENABLED)) != 0)
503 		return;
504 
505 	pcmcia_function_disable(sc->sc_pf);
506 	pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
507 	sc->sc_ih = 0;
508 }
509 
510 /*****************************************************************************
511  * Megahertz EM3336 (and compatibles) support
512  *****************************************************************************/
513 
514 int	mhzc_em3336_lannid_ciscallback(struct pcmcia_tuple *, void *);
515 int	mhzc_em3336_ascii_enaddr(const char *cisstr, u_int8_t *);
516 
517 int
518 mhzc_em3336_enaddr(sc, myla)
519 	struct mhzc_softc *sc;
520 	u_int8_t *myla;
521 {
522 
523 	/* Get the station address from CIS tuple 0x81. */
524 	if (pcmcia_scan_cis(device_parent(&sc->sc_dev),
525 	    mhzc_em3336_lannid_ciscallback, myla) != 1) {
526 		printf("%s: unable to get Ethernet address from CIS\n",
527 		    sc->sc_dev.dv_xname);
528 		return (0);
529 	}
530 
531 	return (1);
532 }
533 
534 int
535 mhzc_em3336_enable(sc)
536 	struct mhzc_softc *sc;
537 {
538 	struct pcmcia_mem_handle memh;
539 	bus_size_t memoff;
540 	int memwin, reg;
541 
542 	/*
543 	 * Bring the chip to live by touching its registers in the correct
544 	 * way (as per my reference... the Linux smc91c92_cs.c driver by
545 	 * David A. Hinds).
546 	 */
547 
548 	/* Map the ISRPOWEREG. */
549 	if (pcmcia_mem_alloc(sc->sc_pf, 0x1000, &memh) != 0) {
550 		printf("%s: unable to allocate memory space\n",
551 		    sc->sc_dev.dv_xname);
552 		return (1);
553 	}
554 
555 	if (pcmcia_mem_map(sc->sc_pf, PCMCIA_MEM_ATTR, 0, 0x1000,
556 	    &memh, &memoff, &memwin)) {
557 		printf("%s: unable to map memory space\n",
558 		    sc->sc_dev.dv_xname);
559 		pcmcia_mem_free(sc->sc_pf, &memh);
560 		return (1);
561 	}
562 
563 	/*
564 	 * The magic sequence:
565 	 *
566 	 *	- read/write the CCR option register.
567 	 *	- read the ISRPOWEREG 2 times.
568 	 *	- read/write the CCR option register again.
569 	 */
570 
571 	reg = pcmcia_ccr_read(sc->sc_pf, PCMCIA_CCR_OPTION);
572 	pcmcia_ccr_write(sc->sc_pf, PCMCIA_CCR_OPTION, reg);
573 
574 	reg = bus_space_read_1(memh.memt, memh.memh, 0x380);
575 	delay(5);
576 	reg = bus_space_read_1(memh.memt, memh.memh, 0x380);
577 
578 	tsleep(&mhzc_em3336_enable, PWAIT, "mhz3en", hz * 200 / 1000);
579 
580 	reg = pcmcia_ccr_read(sc->sc_pf, PCMCIA_CCR_OPTION);
581 	delay(5);
582 	pcmcia_ccr_write(sc->sc_pf, PCMCIA_CCR_OPTION, reg);
583 
584 	pcmcia_mem_unmap(sc->sc_pf, memwin);
585 	pcmcia_mem_free(sc->sc_pf, &memh);
586 
587 	return (0);
588 }
589 
590 int
591 mhzc_em3336_lannid_ciscallback(tuple, arg)
592 	struct pcmcia_tuple *tuple;
593 	void *arg;
594 {
595 	u_int8_t *myla = arg, addr_str[ETHER_ADDR_LEN * 2];
596 	int i;
597 
598 	if (tuple->code == 0x81) {
599 		/*
600 		 * We have a string-encoded address.  Length includes
601 		 * terminating 0xff.
602 		 */
603 		if (tuple->length != (ETHER_ADDR_LEN * 2) + 1)
604 			return (0);
605 
606 		for (i = 0; i < tuple->length - 1; i++)
607 			addr_str[i] = pcmcia_tuple_read_1(tuple, i);
608 
609 		/*
610 		 * Decode the string into `myla'.
611 		 */
612 		return (mhzc_em3336_ascii_enaddr(addr_str, myla));
613 	}
614 	return (0);
615 }
616 
617 /* XXX This should be shared w/ if_sm_pcmcia.c */
618 int
619 mhzc_em3336_ascii_enaddr(cisstr, myla)
620 	const char *cisstr;
621 	u_int8_t *myla;
622 {
623 	u_int8_t digit;
624 	int i;
625 
626 	memset(myla, 0, ETHER_ADDR_LEN);
627 
628 	for (i = 0, digit = 0; i < (ETHER_ADDR_LEN * 2); i++) {
629 		if (cisstr[i] >= '0' && cisstr[i] <= '9')
630 			digit |= cisstr[i] - '0';
631 		else if (cisstr[i] >= 'a' && cisstr[i] <= 'f')
632 			digit |= (cisstr[i] - 'a') + 10;
633 		else if (cisstr[i] >= 'A' && cisstr[i] <= 'F')
634 			digit |= (cisstr[i] - 'A') + 10;
635 		else {
636 			/* Bogus digit!! */
637 			return (0);
638 		}
639 
640 		/* Compensate for ordering of digits. */
641 		if (i & 1) {
642 			myla[i >> 1] = digit;
643 			digit = 0;
644 		} else
645 			digit <<= 4;
646 	}
647 
648 	return (1);
649 }
650 
651 /****** Here begins the com attachment code. ******/
652 
653 #if NCOM_MHZC > 0
654 int	com_mhzc_match(struct device *, struct cfdata *, void *);
655 void	com_mhzc_attach(struct device *, struct device *, void *);
656 int	com_mhzc_detach(struct device *, int);
657 
658 /* No mhzc-specific goo in the softc; it's all in the parent. */
659 CFATTACH_DECL(com_mhzc, sizeof(struct com_softc),
660     com_mhzc_match, com_mhzc_attach, com_detach, com_activate);
661 
662 int	com_mhzc_enable(struct com_softc *);
663 void	com_mhzc_disable(struct com_softc *);
664 
665 int
666 com_mhzc_match(struct device *parent, struct cfdata *match,
667     void *aux)
668 {
669 	extern struct cfdriver com_cd;
670 	const char *name = aux;
671 
672 	/* Device is always present. */
673 	if (strcmp(name, com_cd.cd_name) == 0)
674 		return (1);
675 
676 	return (0);
677 }
678 
679 void
680 com_mhzc_attach(struct device *parent, struct device *self, void *aux)
681 {
682 	struct com_softc *sc = (void *)self;
683 	struct mhzc_softc *msc = (void *)parent;
684 
685 	aprint_normal("\n");
686 
687 	COM_INIT_REGS(sc->sc_regs,
688 	    msc->sc_modem_pcioh.iot,
689 	    msc->sc_modem_pcioh.ioh,
690 	    -1);
691 
692 	sc->enabled = 1;
693 
694 	sc->sc_frequency = COM_FREQ;
695 
696 	sc->enable = com_mhzc_enable;
697 	sc->disable = com_mhzc_disable;
698 
699 	aprint_normal("%s", sc->sc_dev.dv_xname);
700 
701 	com_attach_subr(sc);
702 
703 	sc->enabled = 0;
704 }
705 
706 int
707 com_mhzc_enable(sc)
708 	struct com_softc *sc;
709 {
710 
711 	return (mhzc_enable((struct mhzc_softc *)device_parent(&sc->sc_dev),
712 	    MHZC_MODEM_ENABLED));
713 }
714 
715 void
716 com_mhzc_disable(sc)
717 	struct com_softc *sc;
718 {
719 
720 	mhzc_disable((struct mhzc_softc *)device_parent(&sc->sc_dev),
721 	    MHZC_MODEM_ENABLED);
722 }
723 
724 #endif /* NCOM_MHZC > 0 */
725 
726 /****** Here begins the sm attachment code. ******/
727 
728 #if NSM_MHZC > 0
729 int	sm_mhzc_match(struct device *, struct cfdata *, void *);
730 void	sm_mhzc_attach(struct device *, struct device *, void *);
731 
732 /* No mhzc-specific goo in the softc; it's all in the parent. */
733 CFATTACH_DECL(sm_mhzc, sizeof(struct smc91cxx_softc),
734     sm_mhzc_match, sm_mhzc_attach, smc91cxx_detach, smc91cxx_activate);
735 
736 int	sm_mhzc_enable(struct smc91cxx_softc *);
737 void	sm_mhzc_disable(struct smc91cxx_softc *);
738 
739 int
740 sm_mhzc_match(struct device *parent, struct cfdata *match,
741     void *aux)
742 {
743 	extern struct cfdriver sm_cd;
744 	const char *name = aux;
745 
746 	/* Device is always present. */
747 	if (strcmp(name, sm_cd.cd_name) == 0)
748 		return (1);
749 
750 	return (0);
751 }
752 
753 void
754 sm_mhzc_attach(struct device *parent, struct device *self, void *aux)
755 {
756 	struct smc91cxx_softc *sc = (void *)self;
757 	struct mhzc_softc *msc = (void *)parent;
758 	u_int8_t myla[ETHER_ADDR_LEN];
759 
760 	aprint_normal("\n");
761 
762 	sc->sc_bst = msc->sc_ethernet_pcioh.iot;
763 	sc->sc_bsh = msc->sc_ethernet_pcioh.ioh;
764 
765 	sc->sc_enable = sm_mhzc_enable;
766 	sc->sc_disable = sm_mhzc_disable;
767 
768 	if ((*msc->sc_product->mp_enaddr)(msc, myla) != 1)
769 		return;
770 
771 	/* Perform generic initialization. */
772 	smc91cxx_attach(sc, myla);
773 }
774 
775 int
776 sm_mhzc_enable(sc)
777 	struct smc91cxx_softc *sc;
778 {
779 
780 	return (mhzc_enable((struct mhzc_softc *)device_parent(&sc->sc_dev),
781 	    MHZC_ETHERNET_ENABLED));
782 }
783 
784 void
785 sm_mhzc_disable(sc)
786 	struct smc91cxx_softc *sc;
787 {
788 
789 	mhzc_disable((struct mhzc_softc *)device_parent(&sc->sc_dev),
790 	    MHZC_ETHERNET_ENABLED);
791 }
792 
793 #endif /* NSM_MHZC > 0 */
794