1 /* $NetBSD: mhzc.c,v 1.47 2009/11/13 01:14:35 dyoung Exp $ */
4 * Copyright (c) 1999, 2000, 2004 The NetBSD Foundation, Inc.
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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
34 * Device driver for the Megaherz X-JACK Ethernet/Modem combo cards.
36 * Many thanks to Chuck Cranor for having the patience to sift through
37 * the Linux smc91c92_cs.c driver to find the magic details to get this
41 #include <sys/cdefs.h>
42 __KERNEL_RCSID(0, "$NetBSD: mhzc.c,v 1.47 2009/11/13 01:14:35 dyoung Exp $");
47 #include <sys/param.h>
48 #include <sys/systm.h>
50 #include <sys/socket.h>
51 #include <sys/ioctl.h>
52 #include <sys/errno.h>
53 #include <sys/syslog.h>
54 #include <sys/select.h>
56 #include <sys/device.h>
57 #include <sys/kernel.h>
61 #include <net/if_dl.h>
62 #include <net/if_ether.h>
63 #include <net/if_media.h>
66 #include <netinet/in.h>
67 #include <netinet/in_systm.h>
68 #include <netinet/in_var.h>
69 #include <netinet/ip.h>
70 #include <netinet/if_inarp.h>
76 #include <net/bpfdesc.h>
82 #include <dev/ic/comreg.h>
83 #include <dev/ic/comvar.h>
85 #include <dev/mii/mii.h>
86 #include <dev/mii/miivar.h>
88 #include <dev/ic/smc91cxxreg.h>
89 #include <dev/ic/smc91cxxvar.h>
91 #include <dev/pcmcia/pcmciareg.h>
92 #include <dev/pcmcia/pcmciavar.h>
93 #include <dev/pcmcia/pcmciadevs.h>
98 device_t sc_dev
; /* generic device glue */
100 struct pcmcia_function
*sc_pf
; /* our PCMCIA function */
101 void *sc_ih
; /* interrupt handle */
103 const struct mhzc_product
*sc_product
;
106 * Data for the Modem portion.
109 struct pcmcia_io_handle sc_modem_pcioh
;
110 int sc_modem_io_window
;
113 * Data for the Ethernet portion.
115 device_t sc_ethernet
;
116 struct pcmcia_io_handle sc_ethernet_pcioh
;
117 int sc_ethernet_io_window
;
123 #define MHZC_MODEM_MAPPED 0x01
124 #define MHZC_ETHERNET_MAPPED 0x02
125 #define MHZC_MODEM_ENABLED 0x04
126 #define MHZC_ETHERNET_ENABLED 0x08
127 #define MHZC_MODEM_ALLOCED 0x10
128 #define MHZC_ETHERNET_ALLOCED 0x20
130 int mhzc_match(device_t
, cfdata_t
, void *);
131 void mhzc_attach(device_t
, device_t
, void *);
132 void mhzc_childdet(device_t
, device_t
);
133 int mhzc_detach(device_t
, int);
135 CFATTACH_DECL2_NEW(mhzc
, sizeof(struct mhzc_softc
),
136 mhzc_match
, mhzc_attach
, mhzc_detach
, NULL
, NULL
, mhzc_childdet
);
138 int mhzc_em3336_enaddr(struct mhzc_softc
*, u_int8_t
*);
139 int mhzc_em3336_enable(struct mhzc_softc
*);
141 const struct mhzc_product
{
142 struct pcmcia_product mp_product
;
144 /* Get the Ethernet address for this card. */
145 int (*mp_enaddr
)(struct mhzc_softc
*, u_int8_t
*);
147 /* Perform any special `enable' magic. */
148 int (*mp_enable
)(struct mhzc_softc
*);
149 } mhzc_products
[] = {
150 { { PCMCIA_VENDOR_MEGAHERTZ
, PCMCIA_PRODUCT_MEGAHERTZ_EM3336
,
151 PCMCIA_CIS_INVALID
},
152 mhzc_em3336_enaddr
, mhzc_em3336_enable
},
154 static const size_t mhzc_nproducts
=
155 sizeof(mhzc_products
) / sizeof(mhzc_products
[0]);
157 int mhzc_print(void *, const char *);
159 int mhzc_check_cfe(struct mhzc_softc
*, struct pcmcia_config_entry
*);
160 int mhzc_alloc_ethernet(struct mhzc_softc
*, struct pcmcia_config_entry
*);
162 int mhzc_enable(struct mhzc_softc
*, int);
163 void mhzc_disable(struct mhzc_softc
*, int);
165 int mhzc_intr(void *);
168 mhzc_match(device_t parent
, cfdata_t match
, void *aux
)
170 struct pcmcia_attach_args
*pa
= aux
;
172 if (pcmcia_product_lookup(pa
, mhzc_products
, mhzc_nproducts
,
173 sizeof(mhzc_products
[0]), NULL
))
174 return (2); /* beat `com' */
179 mhzc_attach(device_t parent
, device_t self
, void *aux
)
181 struct mhzc_softc
*sc
= device_private(self
);
182 struct pcmcia_attach_args
*pa
= aux
;
183 struct pcmcia_config_entry
*cfe
;
189 sc
->sc_product
= pcmcia_product_lookup(pa
, mhzc_products
,
190 mhzc_nproducts
, sizeof(mhzc_products
[0]), NULL
);
192 panic("mhzc_attach: impossible");
195 * The address decoders on these cards are wacky. The configuration
196 * entries are set up to look like serial ports, and have no
197 * information about the Ethernet portion. In order to talk to
198 * the Modem portion, the I/O address must have bit 0x80 set.
199 * In order to talk to the Ethernet portion, the I/O address must
200 * have the 0x80 bit clear.
202 * The standard configuration entries conveniently have 0x80 set
203 * in them, and have a length of 8 (a 16550's size, convenient!),
204 * so we use those to set up the Modem portion.
206 * Once we have the Modem's address established, we search for
207 * an address suitable for the Ethernet portion. We do this by
208 * rounding up to the next 16-byte aligned address where 0x80
209 * isn't set (the SMC Ethernet chip has a 16-byte address size)
210 * and attemping to allocate a 16-byte region until we succeed.
212 * Sure would have been nice if Megahertz had made the card a
213 * proper multi-function device.
215 SIMPLEQ_FOREACH(cfe
, &pa
->pf
->cfe_head
, cfe_list
) {
216 if (mhzc_check_cfe(sc
, cfe
)) {
222 aprint_error_dev(self
, "unable to find suitable config table entry\n");
226 if (mhzc_alloc_ethernet(sc
, cfe
) == 0) {
227 aprint_error_dev(self
, "unable to allocate space for Ethernet portion\n");
231 /* Enable the card. */
232 pcmcia_function_init(pa
->pf
, cfe
);
234 if (pcmcia_io_map(sc
->sc_pf
, PCMCIA_WIDTH_IO8
, &sc
->sc_modem_pcioh
,
235 &sc
->sc_modem_io_window
)) {
236 aprint_error_dev(sc
->sc_dev
, "unable to map I/O space\n");
239 sc
->sc_flags
|= MHZC_MODEM_MAPPED
;
241 if (pcmcia_io_map(sc
->sc_pf
, PCMCIA_WIDTH_AUTO
, &sc
->sc_ethernet_pcioh
,
242 &sc
->sc_ethernet_io_window
)) {
243 aprint_error_dev(sc
->sc_dev
, "unable to map I/O space\n");
246 sc
->sc_flags
|= MHZC_ETHERNET_MAPPED
;
248 error
= mhzc_enable(sc
, MHZC_MODEM_ENABLED
|MHZC_ETHERNET_ENABLED
);
253 sc
->sc_modem
= config_found(self
, __UNCONST("com"), mhzc_print
);
255 sc
->sc_ethernet
= config_found(self
, __UNCONST("sm"), mhzc_print
);
257 mhzc_disable(sc
, MHZC_MODEM_ENABLED
|MHZC_ETHERNET_ENABLED
);
261 /* I/O spaces will be freed by detach. */
266 mhzc_check_cfe(struct mhzc_softc
*sc
, struct pcmcia_config_entry
*cfe
)
269 if (cfe
->num_memspace
!= 0)
272 if (cfe
->num_iospace
!= 1)
275 if (pcmcia_io_alloc(sc
->sc_pf
,
276 cfe
->iospace
[0].start
,
277 cfe
->iospace
[0].length
,
278 cfe
->iospace
[0].length
,
279 &sc
->sc_modem_pcioh
) == 0) {
280 /* Found one for the modem! */
281 sc
->sc_flags
|= MHZC_MODEM_ALLOCED
;
289 mhzc_alloc_ethernet(struct mhzc_softc
*sc
, struct pcmcia_config_entry
*cfe
)
291 bus_addr_t addr
, maxaddr
;
293 addr
= cfe
->iospace
[0].start
+ cfe
->iospace
[0].length
;
297 * Now round it up so that it starts on a 16-byte boundary.
299 addr
= roundup(addr
, 0x10);
301 for (; (addr
+ 0x10) < maxaddr
; addr
+= 0x10) {
304 if (pcmcia_io_alloc(sc
->sc_pf
, addr
, 0x10, 0x10,
305 &sc
->sc_ethernet_pcioh
) == 0) {
306 /* Found one for the ethernet! */
307 sc
->sc_flags
|= MHZC_ETHERNET_ALLOCED
;
316 mhzc_print(void *aux
, const char *pnp
)
318 const char *name
= aux
;
321 aprint_normal("%s at %s(*)", name
, pnp
);
327 mhzc_childdet(device_t self
, device_t child
)
329 struct mhzc_softc
*sc
= device_private(self
);
331 if (sc
->sc_ethernet
== child
)
332 sc
->sc_ethernet
= NULL
;
333 if (sc
->sc_modem
== child
)
338 mhzc_detach(device_t self
, int flags
)
340 struct mhzc_softc
*sc
= device_private(self
);
343 if (sc
->sc_ethernet
!= NULL
) {
344 if ((rv
= config_detach(sc
->sc_ethernet
, flags
)) != 0)
348 if (sc
->sc_modem
!= NULL
) {
349 if ((rv
= config_detach(sc
->sc_modem
, flags
)) != 0)
353 /* Unmap our i/o windows. */
354 if (sc
->sc_flags
& MHZC_MODEM_MAPPED
)
355 pcmcia_io_unmap(sc
->sc_pf
, sc
->sc_modem_io_window
);
356 if (sc
->sc_flags
& MHZC_ETHERNET_MAPPED
)
357 pcmcia_io_unmap(sc
->sc_pf
, sc
->sc_ethernet_io_window
);
359 /* Free our i/o spaces. */
360 if (sc
->sc_flags
& MHZC_ETHERNET_ALLOCED
)
361 pcmcia_io_free(sc
->sc_pf
, &sc
->sc_modem_pcioh
);
362 if (sc
->sc_flags
& MHZC_MODEM_ALLOCED
)
363 pcmcia_io_free(sc
->sc_pf
, &sc
->sc_ethernet_pcioh
);
373 struct mhzc_softc
*sc
= arg
;
377 if (sc
->sc_modem
!= NULL
&&
378 (sc
->sc_flags
& MHZC_MODEM_ENABLED
) != 0)
379 rval
|= comintr(sc
->sc_modem
);
383 if (sc
->sc_ethernet
!= NULL
&&
384 (sc
->sc_flags
& MHZC_ETHERNET_ENABLED
) != 0)
385 rval
|= smc91cxx_intr(sc
->sc_ethernet
);
392 mhzc_enable(struct mhzc_softc
*sc
, int flag
)
396 if ((sc
->sc_flags
& flag
) == flag
) {
397 printf("%s: already enabled\n", device_xname(sc
->sc_dev
));
401 if ((sc
->sc_flags
& (MHZC_MODEM_ENABLED
|MHZC_ETHERNET_ENABLED
)) != 0) {
402 sc
->sc_flags
|= flag
;
407 * Establish our interrupt handler.
409 * XXX Note, we establish this at IPL_NET. This is suboptimal
410 * XXX the Modem portion, but is necessary to make the Ethernet
411 * XXX portion have the correct interrupt level semantics.
413 * XXX Eventually we should use the `enabled' bits in the
414 * XXX flags word to determine which level we should be at.
416 sc
->sc_ih
= pcmcia_intr_establish(sc
->sc_pf
, IPL_NET
,
421 error
= pcmcia_function_enable(sc
->sc_pf
);
423 pcmcia_intr_disestablish(sc
->sc_pf
, sc
->sc_ih
);
429 * Perform any special enable magic necessary.
431 if (sc
->sc_product
->mp_enable
!= NULL
&&
432 (*sc
->sc_product
->mp_enable
)(sc
) != 0) {
433 pcmcia_function_disable(sc
->sc_pf
);
434 pcmcia_intr_disestablish(sc
->sc_pf
, sc
->sc_ih
);
438 sc
->sc_flags
|= flag
;
443 mhzc_disable(struct mhzc_softc
*sc
, int flag
)
446 if ((sc
->sc_flags
& flag
) == 0) {
447 printf("%s: already disabled\n", device_xname(sc
->sc_dev
));
451 sc
->sc_flags
&= ~flag
;
452 if ((sc
->sc_flags
& (MHZC_MODEM_ENABLED
|MHZC_ETHERNET_ENABLED
)) != 0)
455 pcmcia_function_disable(sc
->sc_pf
);
456 pcmcia_intr_disestablish(sc
->sc_pf
, sc
->sc_ih
);
460 /*****************************************************************************
461 * Megahertz EM3336 (and compatibles) support
462 *****************************************************************************/
464 int mhzc_em3336_lannid_ciscallback(struct pcmcia_tuple
*, void *);
465 int mhzc_em3336_ascii_enaddr(const char *cisstr
, u_int8_t
*);
468 mhzc_em3336_enaddr(struct mhzc_softc
*sc
, u_int8_t
*myla
)
471 /* Get the station address from CIS tuple 0x81. */
472 if (pcmcia_scan_cis(device_parent(sc
->sc_dev
),
473 mhzc_em3336_lannid_ciscallback
, myla
) != 1) {
474 printf("%s: unable to get Ethernet address from CIS\n",
475 device_xname(sc
->sc_dev
));
483 mhzc_em3336_enable(struct mhzc_softc
*sc
)
485 struct pcmcia_mem_handle memh
;
490 * Bring the chip to live by touching its registers in the correct
491 * way (as per my reference... the Linux smc91c92_cs.c driver by
495 /* Map the ISRPOWEREG. */
496 if (pcmcia_mem_alloc(sc
->sc_pf
, 0x1000, &memh
) != 0) {
497 aprint_error_dev(sc
->sc_dev
, "unable to allocate memory space\n");
501 if (pcmcia_mem_map(sc
->sc_pf
, PCMCIA_MEM_ATTR
, 0, 0x1000,
502 &memh
, &memoff
, &memwin
)) {
503 aprint_error_dev(sc
->sc_dev
, "unable to map memory space\n");
504 pcmcia_mem_free(sc
->sc_pf
, &memh
);
509 * The magic sequence:
511 * - read/write the CCR option register.
512 * - read the ISRPOWEREG 2 times.
513 * - read/write the CCR option register again.
516 reg
= pcmcia_ccr_read(sc
->sc_pf
, PCMCIA_CCR_OPTION
);
517 pcmcia_ccr_write(sc
->sc_pf
, PCMCIA_CCR_OPTION
, reg
);
519 reg
= bus_space_read_1(memh
.memt
, memh
.memh
, 0x380);
521 reg
= bus_space_read_1(memh
.memt
, memh
.memh
, 0x380);
523 tsleep(&mhzc_em3336_enable
, PWAIT
, "mhz3en", hz
* 200 / 1000);
525 reg
= pcmcia_ccr_read(sc
->sc_pf
, PCMCIA_CCR_OPTION
);
527 pcmcia_ccr_write(sc
->sc_pf
, PCMCIA_CCR_OPTION
, reg
);
529 pcmcia_mem_unmap(sc
->sc_pf
, memwin
);
530 pcmcia_mem_free(sc
->sc_pf
, &memh
);
536 mhzc_em3336_lannid_ciscallback(struct pcmcia_tuple
*tuple
, void *arg
)
538 u_int8_t
*myla
= arg
, addr_str
[ETHER_ADDR_LEN
* 2];
541 if (tuple
->code
== 0x81) {
543 * We have a string-encoded address. Length includes
546 if (tuple
->length
!= (ETHER_ADDR_LEN
* 2) + 1)
549 for (i
= 0; i
< tuple
->length
- 1; i
++)
550 addr_str
[i
] = pcmcia_tuple_read_1(tuple
, i
);
553 * Decode the string into `myla'.
555 return (mhzc_em3336_ascii_enaddr(addr_str
, myla
));
560 /* XXX This should be shared w/ if_sm_pcmcia.c */
562 mhzc_em3336_ascii_enaddr(const char *cisstr
, u_int8_t
*myla
)
567 memset(myla
, 0, ETHER_ADDR_LEN
);
569 for (i
= 0, digit
= 0; i
< (ETHER_ADDR_LEN
* 2); i
++) {
570 if (cisstr
[i
] >= '0' && cisstr
[i
] <= '9')
571 digit
|= cisstr
[i
] - '0';
572 else if (cisstr
[i
] >= 'a' && cisstr
[i
] <= 'f')
573 digit
|= (cisstr
[i
] - 'a') + 10;
574 else if (cisstr
[i
] >= 'A' && cisstr
[i
] <= 'F')
575 digit
|= (cisstr
[i
] - 'A') + 10;
581 /* Compensate for ordering of digits. */
583 myla
[i
>> 1] = digit
;
592 /****** Here begins the com attachment code. ******/
595 int com_mhzc_match(device_t
, cfdata_t
, void *);
596 void com_mhzc_attach(device_t
, device_t
, void *);
597 int com_mhzc_detach(device_t
, int);
599 /* No mhzc-specific goo in the softc; it's all in the parent. */
600 CFATTACH_DECL_NEW(com_mhzc
, sizeof(struct com_softc
),
601 com_mhzc_match
, com_mhzc_attach
, com_detach
, NULL
);
603 int com_mhzc_enable(struct com_softc
*);
604 void com_mhzc_disable(struct com_softc
*);
607 com_mhzc_match(device_t parent
, cfdata_t match
, void *aux
)
609 extern struct cfdriver com_cd
;
610 const char *name
= aux
;
612 /* Device is always present. */
613 if (strcmp(name
, com_cd
.cd_name
) == 0)
620 com_mhzc_attach(device_t parent
, device_t self
, void *aux
)
622 struct com_softc
*sc
= device_private(self
);
623 struct mhzc_softc
*msc
= device_private(parent
);
628 COM_INIT_REGS(sc
->sc_regs
,
629 msc
->sc_modem_pcioh
.iot
,
630 msc
->sc_modem_pcioh
.ioh
,
635 sc
->sc_frequency
= COM_FREQ
;
637 sc
->enable
= com_mhzc_enable
;
638 sc
->disable
= com_mhzc_disable
;
640 aprint_normal("%s", device_xname(self
));
648 com_mhzc_enable(struct com_softc
*sc
)
651 return (mhzc_enable(device_private(device_parent(sc
->sc_dev
)),
652 MHZC_MODEM_ENABLED
));
656 com_mhzc_disable(struct com_softc
*sc
)
659 mhzc_disable(device_private(device_parent(sc
->sc_dev
)),
663 #endif /* NCOM_MHZC > 0 */
665 /****** Here begins the sm attachment code. ******/
668 int sm_mhzc_match(device_t
, cfdata_t
, void *);
669 void sm_mhzc_attach(device_t
, device_t
, void *);
671 /* No mhzc-specific goo in the softc; it's all in the parent. */
672 CFATTACH_DECL(sm_mhzc
, sizeof(struct smc91cxx_softc
),
673 sm_mhzc_match
, sm_mhzc_attach
, smc91cxx_detach
, smc91cxx_activate
);
675 int sm_mhzc_enable(struct smc91cxx_softc
*);
676 void sm_mhzc_disable(struct smc91cxx_softc
*);
679 sm_mhzc_match(device_t parent
, cfdata_t match
, void *aux
)
681 extern struct cfdriver sm_cd
;
682 const char *name
= aux
;
684 /* Device is always present. */
685 if (strcmp(name
, sm_cd
.cd_name
) == 0)
692 sm_mhzc_attach(device_t parent
, device_t self
, void *aux
)
694 struct smc91cxx_softc
*sc
= device_private(self
);
695 struct mhzc_softc
*msc
= device_private(parent
);
696 u_int8_t myla
[ETHER_ADDR_LEN
];
700 sc
->sc_bst
= msc
->sc_ethernet_pcioh
.iot
;
701 sc
->sc_bsh
= msc
->sc_ethernet_pcioh
.ioh
;
703 sc
->sc_enable
= sm_mhzc_enable
;
704 sc
->sc_disable
= sm_mhzc_disable
;
706 if ((*msc
->sc_product
->mp_enaddr
)(msc
, myla
) != 1)
709 /* Perform generic initialization. */
710 smc91cxx_attach(sc
, myla
);
714 sm_mhzc_enable(struct smc91cxx_softc
*sc
)
716 struct mhzc_softc
*xsc
= device_private(device_parent(&sc
->sc_dev
));
718 return mhzc_enable(xsc
, MHZC_ETHERNET_ENABLED
);
722 sm_mhzc_disable(struct smc91cxx_softc
*sc
)
724 struct mhzc_softc
*xsc
= device_private(device_parent(&sc
->sc_dev
));
726 mhzc_disable(xsc
, MHZC_ETHERNET_ENABLED
);
729 #endif /* NSM_MHZC > 0 */