if_ep_pccard.c revision 52472
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 52472 1999-10-25 02:52:16Z imp $
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#include <sys/bus.h>
47#include <machine/bus.h>
48#include <sys/rman.h>
49#include <machine/resource.h>
50
51#include <net/ethernet.h>
52#include <net/if.h>
53#include <netinet/in.h>
54#include <netinet/if_ether.h>
55
56#include <machine/clock.h>
57
58#include <dev/ep/if_epreg.h>
59#include <dev/ep/if_epvar.h>
60
61/* XXX should die XXX */
62#include <sys/select.h>
63#include <sys/module.h>
64#include <pccard/cardinfo.h>
65#include <pccard/slot.h>
66
67static const char *ep_pccard_identify(u_short id);
68
69/*
70 * Initialize the device - called from Slot manager.
71 */
72static int
73ep_pccard_probe(device_t dev)
74{
75	struct ep_board ep;
76	struct ep_board *epb;
77	u_long	port_start, port_count;
78	struct ep_softc fake_softc;
79	struct ep_softc *sc = &fake_softc;
80	const char *desc;
81	int error;
82	const char *name;
83
84	name = pccard_get_name(dev);
85	printf("ep_pccard_probe: Does %s match?\n", name);
86	if (strcmp(name, "ep"))
87		return ENXIO;
88
89	epb = &ep;
90	error = bus_get_resource(dev, SYS_RES_IOPORT, 0, &port_start,
91	    &port_count);
92	if (error != 0)
93		return error;
94	/* get_e() requires these. */
95	bzero(sc, sizeof(*sc));
96	sc->ep_io_addr = port_start;
97	sc->unit = device_get_unit(dev);
98	sc->epb = epb;
99
100	/*
101	 * XXX - Certain (newer?) 3Com cards need epb->cmd_off ==
102	 * 2. Sadly, you need to have a correct cmd_off in order to
103	 * identify the card.  So we have to hit it with both and
104	 * cross our virtual fingers.  There's got to be a better way
105	 * to do this.  jyoung@accessus.net 09/11/1999
106	 */
107
108	epb->cmd_off = 0;
109	epb->prod_id = get_e(sc, EEPROM_PROD_ID);
110	if ((desc = ep_pccard_identify(epb->prod_id)) == NULL) {
111		if (bootverbose)
112			device_printf(dev, "Pass 1 of 2 detection "
113			    "failed (nonfatal)\n");
114		epb->cmd_off = 2;
115		epb->prod_id = get_e(sc, EEPROM_PROD_ID);
116		if ((desc = ep_pccard_identify(epb->prod_id))) {
117			device_printf(dev, "Unit failed to come ready or "
118			    "product ID unknown! (id 0x%x)\n", epb->prod_id);
119			return (ENXIO);
120		}
121	}
122
123	return (0);
124}
125
126static const char *
127ep_pccard_identify(u_short id)
128{
129	/* Determine device type and associated MII capabilities  */
130	switch (id) {
131	case 0x6055: /* 3C556 */
132		return ("3Com 3C556");
133	case 0x4057: /* 3C574 */
134		return("3Com 3C574");
135	case 0x4b57: /* 3C574B */
136		return("3Com 3C574B, Megahertz 3CCFE574BT or "
137		    "Fast Etherlink 3C574-TX");
138	case 0x9058: /* 3C589 */
139		return("3Com Etherlink III 3C589[B/C/D]");
140	}
141	return (0);
142}
143
144static int
145ep_pccard_card_attach(struct ep_board *epb)
146{
147	/* Determine device type and associated MII capabilities  */
148	switch (epb->prod_id) {
149	case 0x6055: /* 3C556 */
150		epb->mii_trans = 1;
151		return (1);
152	case 0x4057: /* 3C574 */
153		epb->mii_trans = 1;
154		return (1);
155	case 0x4b57: /* 3C574B */
156		epb->mii_trans = 1;
157		return (1);
158	case 0x9058: /* 3C589 */
159		epb->mii_trans = 0;
160		return (1);
161	}
162	return (0);
163}
164
165static int
166ep_pccard_attach(device_t dev)
167{
168	struct ep_softc *sc = 0;
169	struct ep_board *epb;
170	struct resource *io = 0;
171	struct resource *irq = 0;
172	int unit = device_get_unit(dev);
173	int i, rid;
174	u_short config;
175
176	rid = 0;
177	io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid,
178				0, ~0, 1, RF_ACTIVE);
179	if (!io) {
180		device_printf(dev, "No I/O space?!\n");
181		goto bad;
182	}
183
184	epb = &ep_board[ep_boards];
185
186	epb->epb_addr = rman_get_start(io);
187	epb->epb_used = 1;
188
189	if ((sc = ep_alloc(unit, epb)) == 0)
190		goto bad;
191	ep_boards++;
192
193	epb->cmd_off = 0;
194	epb->prod_id = get_e(sc, EEPROM_PROD_ID);
195	if (!ep_pccard_card_attach(epb)) {
196		epb->cmd_off = 2;
197		epb->prod_id = get_e(sc, EEPROM_PROD_ID);
198		if (!ep_pccard_card_attach(epb)) {
199			device_printf(dev,
200			    "Probe found ID, attach failed so ignore card!\n");
201			ep_free(sc);
202			return (ENXIO);
203		}
204	}
205
206	sc->ep_connectors = 0;
207	epb->res_cfg = get_e(sc, EEPROM_RESOURCE_CFG);
208	for (i = 0; i < 3; i++)
209		sc->epb->eth_addr[i] = get_e(sc, EEPROM_NODE_ADDR_0 + i);
210
211	config = inw(rman_get_start(io) + EP_W0_CONFIG_CTRL);
212	if (config & IS_BNC) {
213		sc->ep_connectors |= BNC;
214	}
215	if (config & IS_UTP) {
216		sc->ep_connectors |= UTP;
217	}
218	if (!(sc->ep_connectors & 7))
219		/* (Apparently) non-fatal */
220		if (bootverbose)
221			device_printf(dev, "No connectors or MII.\n");
222
223	sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS;
224
225	/* ROM size = 0, ROM base = 0 */
226	/* For now, ignore AUTO SELECT feature of 3C589B and later. */
227	outw(BASE + EP_W0_ADDRESS_CFG, get_e(sc, EEPROM_ADDR_CFG) & 0xc000);
228
229	/* Fake IRQ must be 3 */
230	outw(BASE + EP_W0_RESOURCE_CFG, (sc->epb->res_cfg & 0x0fff) | 0x3000);
231
232	outw(BASE + EP_W0_PRODUCT_ID, sc->epb->prod_id);
233
234	if (sc->epb->mii_trans) {
235		/*
236		 * turn on the MII transciever
237		 */
238		GO_WINDOW(3);
239		outw(BASE + EP_W3_OPTIONS, 0x8040);
240		DELAY(1000);
241		outw(BASE + EP_W3_OPTIONS, 0xc040);
242		outw(BASE + EP_COMMAND, RX_RESET);
243		outw(BASE + EP_COMMAND, TX_RESET);
244		while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS);
245		DELAY(1000);
246		outw(BASE + EP_W3_OPTIONS, 0x8040);
247	}
248
249	sc->irq = irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
250				 0, ~0, 1, RF_ACTIVE);
251	if (!irq) {
252		device_printf(dev, "No irq?!\n");
253		goto bad;
254	}
255
256	ep_attach(sc);
257	if (bus_setup_intr(dev, irq, INTR_TYPE_NET, ep_intr, sc, &sc->ih)) {
258		device_printf(dev, "cannot setup intr\n");
259		goto bad;
260	}
261	sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen;
262
263	return 0;
264bad:
265	if (io)
266		bus_release_resource(dev, SYS_RES_IOPORT, 0, io);
267	if (irq)
268		bus_release_resource(dev, SYS_RES_IRQ, 0, irq);
269	if (sc)
270		ep_free(sc);
271	return ENXIO;
272}
273
274static void
275ep_pccard_detach(device_t dev)
276{
277	struct ep_softc *sc = device_get_softc(dev);
278
279	printf("detach\n");
280
281	if (sc->gone) {
282		device_printf(dev, "already unloaded\n");
283		return;
284	}
285	sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING;
286	if_down(&sc->arpcom.ac_if);
287	sc->gone = 1;
288	bus_teardown_intr(dev, sc->irq, sc->ih);
289	device_printf(dev, "unload\n");
290}
291
292static device_method_t ep_pccard_methods[] = {
293	/* Device interface */
294	DEVMETHOD(device_probe,		ep_pccard_probe),
295	DEVMETHOD(device_attach,	ep_pccard_attach),
296	DEVMETHOD(device_detach,	ep_pccard_detach),
297
298	{ 0, 0 }
299};
300
301static driver_t ep_pccard_driver = {
302	"ep",
303	ep_pccard_methods,
304	1,			/* unused */
305};
306
307extern devclass_t ep_devclass;
308
309DRIVER_MODULE(ep, pccard, ep_pccard_driver, ep_devclass, 0, 0);
310