if_ep_pccard.c revision 51677
1/*
2 * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by Herb Peyerl.
16 * 4. The name of Herb Peyerl may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * $FreeBSD: head/sys/dev/ep/if_ep_pccard.c 51677 1999-09-26 18:04:26Z mdodd $
31 */
32
33/*
34 * Pccard support for 3C589 by:
35 *		HAMADA Naoki
36 *		nao@tom-yam.or.jp
37 */
38
39#include <sys/param.h>
40#include <sys/kernel.h>
41#include <sys/systm.h>
42#include <sys/malloc.h>
43#include <sys/mbuf.h>
44#include <sys/socket.h>
45#include <sys/sockio.h>
46
47#include <net/ethernet.h>
48#include <net/if.h>
49#include <netinet/in.h>
50#include <netinet/if_ether.h>
51
52#include <machine/clock.h>
53
54#include <sys/select.h>
55#include <sys/module.h>
56#include <pccard/cardinfo.h>
57#include <pccard/slot.h>
58
59#include <dev/ep/if_epreg.h>
60#include <dev/ep/if_epvar.h>
61
62/*
63 * PC-Card (PCMCIA) specific code.
64 */
65static int	ep_pccard_init		(struct pccard_devinfo *);
66static int	ep_pccard_attach	(struct pccard_devinfo *);
67static void	ep_pccard_unload	(struct pccard_devinfo *);
68static int	ep_pccard_intr		(struct pccard_devinfo *);
69static int	ep_pccard_identify	(struct ep_board *epb, int unit);
70
71PCCARD_MODULE(ep, ep_pccard_init, ep_pccard_unload, ep_pccard_intr, 0, net_imask);
72
73/*
74 * Initialize the device - called from Slot manager.
75 */
76static int
77ep_pccard_init(devi)
78    struct pccard_devinfo *devi;
79{
80    struct isa_device *is = &devi->isahd;
81    struct ep_softc *sc = ep_softc[is->id_unit];
82    struct ep_board *epb;
83    int i;
84
85    epb = &ep_board[is->id_unit];
86
87    if (sc == 0) {
88	if ((sc = ep_alloc(is->id_unit, epb)) == 0) {
89	    return (ENXIO);
90	}
91	ep_unit++;
92    }
93
94    /* get_e() requires these. */
95    sc->ep_io_addr = is->id_iobase;
96    sc->unit = is->id_unit;
97    epb->epb_addr = is->id_iobase;
98    epb->epb_used = 1;
99
100    /*
101     * XXX - Certain (newer?) 3Com cards need epb->cmd_off == 2. Sadly,
102     * you need to have a correct cmd_off in order to identify the card.
103     * So we have to hit it with both and cross our virtual fingers. There's
104     * got to be a better way to do this. jyoung@accessus.net 09/11/1999
105     */
106
107    epb->cmd_off = 0;
108    epb->prod_id = get_e(sc, EEPROM_PROD_ID);
109    if (!ep_pccard_identify(epb, is->id_unit)) {
110	if (bootverbose) printf("ep%d: Pass 1 of 2 detection failed (nonfatal)\n", is->id_unit);
111	epb->cmd_off = 2;
112	epb->prod_id = get_e(sc, EEPROM_PROD_ID);
113	if (!ep_pccard_identify(epb, is->id_unit)) {
114	    if (bootverbose) printf("ep%d: Pass 2 of 2 detection failed (fatal!)\n", is->id_unit);
115	    printf("ep%d: Unit failed to come ready or product ID unknown! (id 0x%x)\n", is->id_unit, epb->prod_id);
116	    return (ENXIO);
117	}
118    }
119
120    epb->res_cfg = get_e(sc, EEPROM_RESOURCE_CFG);
121    for (i = 0; i < 3; i++)
122	sc->epb->eth_addr[i] = get_e(sc, EEPROM_NODE_ADDR_0 + i);
123
124    if (ep_pccard_attach(devi) == 0)
125	return (ENXIO);
126
127    sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen;
128    return (0);
129}
130
131static int
132ep_pccard_identify(epb, unit)
133    struct ep_board *epb;
134    int unit;
135{
136    /* Determine device type and associated MII capabilities  */
137    switch (epb->prod_id) {
138	case 0x6055: /* 3C556 */
139	    if (bootverbose) printf("ep%d: 3Com 3C556\n", unit);
140	    epb->mii_trans = 1;
141	    return (1);
142	    break; /* NOTREACHED */
143	case 0x4057: /* 3C574 */
144	    if (bootverbose) printf("ep%d: 3Com 3C574\n", unit);
145	    epb->mii_trans = 1;
146	    return (1);
147	    break; /* NOTREACHED */
148	case 0x4b57: /* 3C574B */
149	    if (bootverbose) printf("ep%d: 3Com 3C574B, Megahertz 3CCFE574BT or Fast Etherlink 3C574-TX\n", unit);
150	    epb->mii_trans = 1;
151	    return (1);
152	    break; /* NOTREACHED */
153	case 0x9058: /* 3C589 */
154	    if (bootverbose) printf("ep%d: 3Com Etherlink III 3C589[B/C/D]\n", unit);
155	    epb->mii_trans = 0;
156	    return (1);
157	    break; /* NOTREACHED */
158    }
159    return (0);
160}
161
162static int
163ep_pccard_attach(devi)
164    struct pccard_devinfo *devi;
165{
166    struct isa_device *is = &devi->isahd;
167    struct ep_softc *sc = ep_softc[is->id_unit];
168    u_short config;
169
170    sc->ep_connectors = 0;
171    config = inw(IS_BASE + EP_W0_CONFIG_CTRL);
172    if (config & IS_BNC) {
173	sc->ep_connectors |= BNC;
174    }
175    if (config & IS_UTP) {
176	sc->ep_connectors |= UTP;
177    }
178    if (!(sc->ep_connectors & 7))
179	/* (Apparently) non-fatal */
180	if(bootverbose) printf("ep%d: No connectors or MII.\n", is->id_unit);
181
182    sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
183
184    /* ROM size = 0, ROM base = 0 */
185    /* For now, ignore AUTO SELECT feature of 3C589B and later. */
186    outw(BASE + EP_W0_ADDRESS_CFG, get_e(sc, EEPROM_ADDR_CFG) & 0xc000);
187
188    /* Fake IRQ must be 3 */
189    outw(BASE + EP_W0_RESOURCE_CFG, (sc->epb->res_cfg & 0x0fff) | 0x3000);
190
191    outw(BASE + EP_W0_PRODUCT_ID, sc->epb->prod_id);
192
193    if (sc->epb->mii_trans) {
194	/*
195	 * turn on the MII transciever
196	 */
197	GO_WINDOW(3);
198	outw(BASE + EP_W3_OPTIONS, 0x8040);
199	DELAY(1000);
200	outw(BASE + EP_W3_OPTIONS, 0xc040);
201	outw(BASE + EP_COMMAND, RX_RESET);
202	outw(BASE + EP_COMMAND, TX_RESET);
203	while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
204	DELAY(1000);
205	outw(BASE + EP_W3_OPTIONS, 0x8040);
206    }
207
208    ep_attach(sc);
209
210    return 1;
211}
212
213static void
214ep_pccard_unload(devi)
215    struct pccard_devinfo *devi;
216{
217    struct ep_softc *sc = ep_softc[devi->isahd.id_unit];
218
219    if (sc->gone) {
220        printf("ep%d: already unloaded\n", devi->isahd.id_unit);
221	return;
222    }
223    sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
224    sc->gone = 1;
225    printf("ep%d: unload\n", devi->isahd.id_unit);
226}
227
228/*
229 * card_intr - Shared interrupt called from
230 * front end of PC-Card handler.
231 */
232static int
233ep_pccard_intr(devi)
234    struct pccard_devinfo *devi;
235{
236    struct ep_softc *sc = ep_softc[devi->isahd.id_unit];
237
238    if (sc->gone) {
239        return;
240    }
241
242    ep_intr((void *)sc);
243
244    return(1);
245}
246