if_ed_pccard.c revision 140830
1218822Sdim/*-
2107492Sobrien * Copyright (c) 1995, David Greenman
3218822Sdim * All rights reserved.
4218822Sdim *
5218822Sdim * Redistribution and use in source and binary forms, with or without
6218822Sdim * modification, are permitted provided that the following conditions
7107492Sobrien * are met:
8218822Sdim * 1. Redistributions of source code must retain the above copyright
9107492Sobrien *    notice unmodified, this list of conditions, and the following
10218822Sdim *    disclaimer.
11218822Sdim * 2. Redistributions in binary form must reproduce the above copyright
12218822Sdim *    notice, this list of conditions and the following disclaimer in the
13218822Sdim *    documentation and/or other materials provided with the distribution.
14218822Sdim *
15218822Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16107492Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17218822Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18107492Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21107492Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22218822Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23107492Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24218822Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25218822Sdim * SUCH DAMAGE.
26218822Sdim *
27107492Sobrien * $FreeBSD: head/sys/dev/ed/if_ed_pccard.c 140830 2005-01-25 18:50:25Z imp $
28218822Sdim */
29107492Sobrien
30218822Sdim#include "opt_ed.h"
31218822Sdim
32218822Sdim#include <sys/param.h>
33218822Sdim#include <sys/systm.h>
34218822Sdim#include <sys/socket.h>
35218822Sdim#include <sys/kernel.h>
36218822Sdim#include <sys/conf.h>
37218822Sdim#include <sys/uio.h>
38218822Sdim
39218822Sdim#include <sys/module.h>
40218822Sdim#include <sys/bus.h>
41218822Sdim#include <machine/bus.h>
42218822Sdim#include <sys/rman.h>
43218822Sdim#include <machine/resource.h>
44218822Sdim
45218822Sdim#include <net/ethernet.h>
46218822Sdim#include <net/if.h>
47218822Sdim#include <net/if_arp.h>
48218822Sdim#include <net/if_mib.h>
49218822Sdim#include <net/if_media.h>
50218822Sdim
51104834Sobrien#include <dev/ed/if_edreg.h>
52218822Sdim#include <dev/ed/if_edvar.h>
53218822Sdim#include <dev/pccard/pccardvar.h>
54218822Sdim#include <dev/pccard/pccard_cis.h>
55218822Sdim#ifndef ED_NO_MIIBUS
56218822Sdim#include <dev/mii/mii.h>
57218822Sdim#include <dev/mii/miivar.h>
58218822Sdim#endif
59218822Sdim
60218822Sdim#include "card_if.h"
61218822Sdim#ifndef ED_NO_MIIBUS
62218822Sdim/* "device miibus" required.  See GENERIC if you get errors here. */
63218822Sdim#include "miibus_if.h"
64218822Sdim#endif
65218822Sdim#include "pccarddevs.h"
66218822Sdim
67218822Sdim#ifndef ED_NO_MIIBUS
68218822SdimMODULE_DEPEND(ed, miibus, 1, 1, 1);
69218822Sdim#endif
70218822SdimMODULE_DEPEND(ed, ether, 1, 1, 1);
71218822Sdim
72218822Sdim/*
73218822Sdim *      PC-Card (PCMCIA) specific code.
74218822Sdim */
75218822Sdimstatic int	ed_pccard_match(device_t);
76218822Sdimstatic int	ed_pccard_probe(device_t);
77218822Sdimstatic int	ed_pccard_attach(device_t);
78218822Sdimstatic int	ed_pccard_detach(device_t);
79218822Sdim
80218822Sdimstatic int	ed_pccard_Linksys(device_t dev);
81218822Sdimstatic int	ed_pccard_ax88190(device_t dev);
82218822Sdim
83218822Sdimstatic void	ax88190_geteprom(struct ed_softc *);
84218822Sdimstatic int	ed_pccard_memwrite(device_t dev, off_t offset, u_char byte);
85218822Sdim#ifndef ED_NO_MIIBUS
86218822Sdimstatic void	ed_pccard_dlink_mii_reset(struct ed_softc *sc);
87218822Sdimstatic u_int	ed_pccard_dlink_mii_readbits(struct ed_softc *sc, int nbits);
88218822Sdimstatic void	ed_pccard_dlink_mii_writebits(struct ed_softc *sc, u_int val,
89218822Sdim    int nbits);
90218822Sdim#endif
91218822Sdim
92218822Sdim/*
93218822Sdim *      ed_pccard_detach - unload the driver and clear the table.
94218822Sdim */
95218822Sdimstatic int
96218822Sdimed_pccard_detach(device_t dev)
97218822Sdim{
98218822Sdim	struct ed_softc *sc = device_get_softc(dev);
99218822Sdim	struct ifnet *ifp = &sc->arpcom.ac_if;
100218822Sdim
101218822Sdim	if (sc->gone) {
102218822Sdim		device_printf(dev, "already unloaded\n");
103218822Sdim		return (0);
104218822Sdim	}
105104834Sobrien	ed_stop(sc);
106218822Sdim	ifp->if_flags &= ~IFF_RUNNING;
107104834Sobrien	ether_ifdetach(ifp);
108218822Sdim	sc->gone = 1;
109218822Sdim	bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
110218822Sdim	ed_release_resources(dev);
111218822Sdim	return (0);
112104834Sobrien}
113218822Sdim
114104834Sobrienstatic const struct ed_product {
115218822Sdim	struct pccard_product	prod;
116104834Sobrien	int flags;
117218822Sdim#define	NE2000DVF_DL10019	0x0001		/* chip is D-Link DL10019 */
118104834Sobrien#define	NE2000DVF_AX88190	0x0002		/* chip is ASIX AX88190 */
119218822Sdim} ed_pccard_products[] = {
120218822Sdim	{ PCMCIA_CARD(ACCTON, EN2212, 0), 0},
121218822Sdim	{ PCMCIA_CARD(ACCTON, EN2216, 0), 0},
122218822Sdim	{ PCMCIA_CARD(ALLIEDTELESIS, LA_PCM, 0), 0},
123218822Sdim	{ PCMCIA_CARD(AMBICOM, AMB8002T, 0), 0},
124218822Sdim	{ PCMCIA_CARD(BILLIONTON, LNT10TN, 0), 0},
125218822Sdim	{ PCMCIA_CARD(BILLIONTON, CFLT10N, 0), 0},
126218822Sdim	{ PCMCIA_CARD(BUFFALO, LPC3_CLT,  0), 0},
127218822Sdim	{ PCMCIA_CARD(BUFFALO, LPC3_CLX,  0), NE2000DVF_AX88190},
128218822Sdim	{ PCMCIA_CARD(BUFFALO, LPC_CF_CLT,  0), 0},
129218822Sdim	{ PCMCIA_CARD(CNET, NE2000, 0), 0},
130104834Sobrien	{ PCMCIA_CARD(COMPEX, LINKPORT_ENET_B, 0), 0},
131218822Sdim	{ PCMCIA_CARD(COREGA, ETHER_II_PCC_T, 0), 0},
132104834Sobrien	{ PCMCIA_CARD(COREGA, ETHER_II_PCC_TD, 0), 0},
133218822Sdim	{ PCMCIA_CARD(COREGA, ETHER_PCC_T, 0), 0},
134218822Sdim	{ PCMCIA_CARD(COREGA, ETHER_PCC_TD, 0), 0},
135218822Sdim	{ PCMCIA_CARD(COREGA, FAST_ETHER_PCC_TX, 0), NE2000DVF_DL10019 },
136218822Sdim	{ PCMCIA_CARD(COREGA, FETHER_PCC_TXD, 0), NE2000DVF_AX88190 },
137104834Sobrien	{ PCMCIA_CARD(COREGA, FETHER_PCC_TXF, 0), NE2000DVF_DL10019 },
138218822Sdim	{ PCMCIA_CARD(DAYNA, COMMUNICARD_E_1, 0), 0},
139104834Sobrien	{ PCMCIA_CARD(DAYNA, COMMUNICARD_E_2, 0), 0},
140218822Sdim	{ PCMCIA_CARD(DIGITAL2, DEPCMXX, 0), 0 },
141218822Sdim	{ PCMCIA_CARD(DLINK, DE650, 0), 0},
142104834Sobrien	{ PCMCIA_CARD(DLINK, DE660, 0), 0 },
143218822Sdim	{ PCMCIA_CARD(DLINK, DE660PLUS, 0), 0},
144104834Sobrien	{ PCMCIA_CARD(DLINK, DFE670TXD, 0), NE2000DVF_DL10019},
145218822Sdim	{ PCMCIA_CARD(DYNALINK, L10C, 0), 0},
146218822Sdim	{ PCMCIA_CARD(EDIMAX, EP4000A, 0), 0},
147104834Sobrien	{ PCMCIA_CARD(EPSON, EEN10B, 0), 0},
148218822Sdim	{ PCMCIA_CARD(EXP, THINLANCOMBO, 0), 0},
149104834Sobrien	{ PCMCIA_CARD(IBM, INFOMOVER, 0), 0},
150218822Sdim	{ PCMCIA_CARD(IODATA, PCLAT, 0), 0},
151218822Sdim	{ PCMCIA_CARD(IODATA, PCLATE, 0), 0},
152218822Sdim	{ PCMCIA_CARD(KINGSTON, KNE2, 0), 0},
153104834Sobrien	{ PCMCIA_CARD(LANTECH, FASTNETTX, 0),NE2000DVF_AX88190 },
154218822Sdim	{ PCMCIA_CARD(LINKSYS, COMBO_ECARD, 0), NE2000DVF_DL10019 },
155104834Sobrien	{ PCMCIA_CARD(LINKSYS, ECARD_1, 0), 0},
156218822Sdim	{ PCMCIA_CARD(LINKSYS, ECARD_2, 0), 0},
157218822Sdim	{ PCMCIA_CARD(LINKSYS, ETHERFAST, 0), NE2000DVF_DL10019 },
158218822Sdim	{ PCMCIA_CARD(LINKSYS, PCM100, 0), 0},
159218822Sdim	{ PCMCIA_CARD(LINKSYS, TRUST_COMBO_ECARD, 0), 0},
160218822Sdim	{ PCMCIA_CARD(LINKSYS, ETHERFAST, 0), NE2000DVF_DL10019 },
161104834Sobrien	{ PCMCIA_CARD(MACNICA, ME1_JEIDA, 0), 0},
162218822Sdim	{ PCMCIA_CARD(MAGICRAM, ETHER, 0), 0},
163104834Sobrien	{ PCMCIA_CARD(MELCO, LPC3_CLX,  0), NE2000DVF_AX88190},
164218822Sdim	{ PCMCIA_CARD(MELCO, LPC3_TX, 0), NE2000DVF_AX88190 },
165218822Sdim	{ PCMCIA_CARD(NDC, ND5100_E, 0), 0},
166218822Sdim	{ PCMCIA_CARD(NETGEAR, FA410TXC, 0), NE2000DVF_DL10019},
167218822Sdim	{ PCMCIA_CARD(NETGEAR, FA411, 0), NE2000DVF_AX88190},
168218822Sdim	{ PCMCIA_CARD(PLANET, SMARTCOM2000, 0), 0 },
169218822Sdim	{ PCMCIA_CARD(PREMAX, PE200, 0), 0},
170218822Sdim	{ PCMCIA_CARD(RPTI, EP400, 0), 0},
171104834Sobrien	{ PCMCIA_CARD(RPTI, EP401, 0), 0},
172218822Sdim	{ PCMCIA_CARD(SMC, EZCARD, 0), 0},
173218822Sdim	{ PCMCIA_CARD(SOCKET, EA_ETHER, 0), 0},
174218822Sdim	{ PCMCIA_CARD(SOCKET, LP_ETHER, 0), 0},
175104834Sobrien	{ PCMCIA_CARD(SOCKET, LP_ETHER_CF, 0), 0},
176218822Sdim	{ PCMCIA_CARD(SOCKET, LP_ETH_10_100_CF, 0), NE2000DVF_DL10019},
177104834Sobrien	{ PCMCIA_CARD(SVEC, COMBOCARD, 0), 0},
178218822Sdim	{ PCMCIA_CARD(SVEC, LANCARD, 0), 0},
179218822Sdim	{ PCMCIA_CARD(SYNERGY21, S21810, 0), 0},
180218822Sdim	{ PCMCIA_CARD(TDK, LAK_CD031, 0), 0},
181104834Sobrien	{ PCMCIA_CARD(TELECOMDEVICE, TCD_HPC100, 0), NE2000DVF_AX88190 },
182218822Sdim	{ PCMCIA_CARD(XIRCOM, CFE_10, 0), 0},
183104834Sobrien	{ PCMCIA_CARD(ZONET, ZEN, 0), 0},
184218822Sdim	{ { NULL } }
185104834Sobrien};
186218822Sdim
187104834Sobrienstatic int
188218822Sdimed_pccard_match(device_t dev)
189218822Sdim{
190104834Sobrien	const struct ed_product *pp;
191218822Sdim	int		error;
192218822Sdim	uint32_t	fcn = PCCARD_FUNCTION_UNSPEC;
193218822Sdim
194104834Sobrien	/* Make sure we're a network function */
195218822Sdim	error = pccard_get_function(dev, &fcn);
196218822Sdim	if (error != 0)
197218822Sdim		return (error);
198218822Sdim	if (fcn != PCCARD_FUNCTION_NETWORK)
199218822Sdim		return (ENXIO);
200104834Sobrien
201218822Sdim	if ((pp = (const struct ed_product *) pccard_product_lookup(dev,
202104834Sobrien	    (const struct pccard_product *) ed_pccard_products,
203218822Sdim	    sizeof(ed_pccard_products[0]), NULL)) != NULL) {
204218822Sdim		if (pp->prod.pp_name != NULL)
205218822Sdim			device_set_desc(dev, pp->prod.pp_name);
206218822Sdim		if (pp->flags & NE2000DVF_DL10019)
207218822Sdim			device_set_flags(dev, ED_FLAGS_LINKSYS);
208218822Sdim		else if (pp->flags & NE2000DVF_AX88190)
209218822Sdim			device_set_flags(dev, ED_FLAGS_AX88190);
210218822Sdim		return (0);
211218822Sdim	}
212218822Sdim	return (ENXIO);
213218822Sdim}
214218822Sdim
215218822Sdim/*
216218822Sdim * Probe framework for pccards.  Replicates the standard framework,
217218822Sdim * minus the pccard driver registration and ignores the ether address
218218822Sdim * supplied (from the CIS), relying on the probe to find it instead.
219218822Sdim */
220218822Sdimstatic int
221218822Sdimed_pccard_probe(device_t dev)
222218822Sdim{
223218822Sdim	int	error;
224218822Sdim	int	flags = device_get_flags(dev);
225218822Sdim
226218822Sdim	if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_AX88190) {
227218822Sdim		error = ed_pccard_ax88190(dev);
228218822Sdim		goto end2;
229218822Sdim	}
230218822Sdim
231218822Sdim	error = ed_probe_Novell(dev, 0, flags);
232218822Sdim	if (error == 0)
233218822Sdim		goto end;
234218822Sdim	ed_release_resources(dev);
235218822Sdim
236218822Sdim	error = ed_probe_WD80x3(dev, 0, flags);
237218822Sdim	if (error == 0)
238218822Sdim		goto end;
239218822Sdim	ed_release_resources(dev);
240218822Sdim	goto end2;
241218822Sdim
242218822Sdimend:
243218822Sdim	if (ED_FLAGS_GETTYPE(flags) & ED_FLAGS_LINKSYS)
244218822Sdim		ed_pccard_Linksys(dev);
245218822Sdimend2:
246218822Sdim	if (error == 0)
247218822Sdim		error = ed_alloc_irq(dev, 0, 0);
248218822Sdim
249218822Sdim	ed_release_resources(dev);
250130561Sobrien	return (error);
251104834Sobrien}
252218822Sdim
253218822Sdimstatic int
254104834Sobriened_pccard_attach(device_t dev)
255218822Sdim{
256218822Sdim	int error;
257218822Sdim	int i;
258218822Sdim	struct ed_softc *sc = device_get_softc(dev);
259218822Sdim	u_char sum;
260218822Sdim	u_char ether_addr[ETHER_ADDR_LEN];
261218822Sdim
262218822Sdim	if (sc->port_used > 0)
263104834Sobrien		ed_alloc_port(dev, sc->port_rid, sc->port_used);
264218822Sdim	if (sc->mem_used)
265104834Sobrien		ed_alloc_memory(dev, sc->mem_rid, sc->mem_used);
266218822Sdim	ed_alloc_irq(dev, sc->irq_rid, 0);
267218822Sdim
268218822Sdim	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
269104834Sobrien			       edintr, sc, &sc->irq_handle);
270218822Sdim	if (error) {
271104834Sobrien		printf("setup intr failed %d \n", error);
272218822Sdim		ed_release_resources(dev);
273218822Sdim		return (error);
274104834Sobrien	}
275218822Sdim
276104834Sobrien	if (sc->vendor != ED_VENDOR_LINKSYS) {
277218822Sdim		pccard_get_ether(dev, ether_addr);
278218822Sdim		for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++)
279218822Sdim			sum |= ether_addr[i];
280218822Sdim		if (sum)
281104834Sobrien			bcopy(ether_addr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
282218822Sdim	}
283218822Sdim
284218822Sdim	error = ed_attach(dev);
285218822Sdim#ifndef ED_NO_MIIBUS
286104834Sobrien	if (error == 0 && sc->vendor == ED_VENDOR_LINKSYS) {
287218822Sdim		/* Probe for an MII bus, but ignore errors. */
288104834Sobrien		ed_pccard_dlink_mii_reset(sc);
289218822Sdim		sc->mii_readbits = ed_pccard_dlink_mii_readbits;
290218822Sdim		sc->mii_writebits = ed_pccard_dlink_mii_writebits;
291104834Sobrien		mii_phy_probe(dev, &sc->miibus, ed_ifmedia_upd,
292218822Sdim		    ed_ifmedia_sts);
293104834Sobrien	}
294218822Sdim#endif
295218822Sdim
296218822Sdim	return (error);
297218822Sdim}
298104834Sobrien
299218822Sdimstatic void
300104834Sobrienax88190_geteprom(struct ed_softc *sc)
301218822Sdim{
302218822Sdim	int prom[16],i;
303218822Sdim	u_char tmp;
304218822Sdim	struct {
305218822Sdim		unsigned char offset, value;
306104834Sobrien	} pg_seq[] = {
307218822Sdim		{ED_P0_CR, ED_CR_RD2|ED_CR_STP},/* Select Page0 */
308104834Sobrien		{ED_P0_DCR, 0x01},
309218822Sdim		{ED_P0_RBCR0, 0x00},		/* Clear the count regs. */
310218822Sdim		{ED_P0_RBCR1, 0x00},
311218822Sdim		{ED_P0_IMR, 0x00},		/* Mask completion irq. */
312218822Sdim		{ED_P0_ISR, 0xff},
313104834Sobrien		{ED_P0_RCR, ED_RCR_MON | ED_RCR_INTT}, /* Set To Monitor */
314218822Sdim		{ED_P0_TCR, ED_TCR_LB0},	/* loopback mode. */
315218822Sdim		{ED_P0_RBCR0, 32},
316104834Sobrien		{ED_P0_RBCR1, 0x00},
317218822Sdim		{ED_P0_RSAR0, 0x00},
318218822Sdim		{ED_P0_RSAR1, 0x04},
319104834Sobrien		{ED_P0_CR ,ED_CR_RD0 | ED_CR_STA},
320218822Sdim	};
321218822Sdim
322104834Sobrien	/* Reset Card */
323218822Sdim	tmp = ed_asic_inb(sc, ED_NOVELL_RESET);
324218822Sdim	ed_asic_outb(sc, ED_NOVELL_RESET, tmp);
325218822Sdim	DELAY(5000);
326104834Sobrien	ed_asic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
327218822Sdim	DELAY(5000);
328218822Sdim
329218822Sdim	/* Card Settings */
330218822Sdim	for (i = 0; i < sizeof(pg_seq) / sizeof(pg_seq[0]); i++)
331218822Sdim		ed_nic_outb(sc, pg_seq[i].offset, pg_seq[i].value);
332218822Sdim
333218822Sdim	/* Get Data */
334218822Sdim	for (i = 0; i < 16; i++)
335218822Sdim		prom[i] = ed_asic_inb(sc, 0);
336218822Sdim	sc->arpcom.ac_enaddr[0] = prom[0] & 0xff;
337218822Sdim	sc->arpcom.ac_enaddr[1] = prom[0] >> 8;
338218822Sdim	sc->arpcom.ac_enaddr[2] = prom[1] & 0xff;
339218822Sdim	sc->arpcom.ac_enaddr[3] = prom[1] >> 8;
340218822Sdim	sc->arpcom.ac_enaddr[4] = prom[2] & 0xff;
341218822Sdim	sc->arpcom.ac_enaddr[5] = prom[2] >> 8;
342218822Sdim}
343218822Sdim
344218822Sdimstatic int
345218822Sdimed_pccard_memwrite(device_t dev, off_t offset, u_char byte)
346218822Sdim{
347218822Sdim	int cis_rid;
348218822Sdim	struct resource *cis;
349218822Sdim
350218822Sdim	cis_rid = 0;
351218822Sdim	cis = bus_alloc_resource(dev, SYS_RES_MEMORY, &cis_rid, 0, ~0,
352218822Sdim	    4 << 10, RF_ACTIVE | RF_SHAREABLE);
353218822Sdim	if (cis == NULL)
354218822Sdim		return (ENXIO);
355218822Sdim	CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY,
356218822Sdim	    cis_rid, PCCARD_A_MEM_ATTR);
357218822Sdim
358218822Sdim	bus_space_write_1(rman_get_bustag(cis), rman_get_bushandle(cis),
359218822Sdim	    offset, byte);
360218822Sdim
361218822Sdim	bus_deactivate_resource(dev, SYS_RES_MEMORY, cis_rid, cis);
362218822Sdim	bus_release_resource(dev, SYS_RES_MEMORY, cis_rid, cis);
363218822Sdim
364218822Sdim	return (0);
365218822Sdim}
366218822Sdim
367218822Sdim/*
368218822Sdim * Probe the Ethernet MAC addrees for PCMCIA Linksys EtherFast 10/100
369218822Sdim * and compatible cards (DL10019C Ethernet controller).
370218822Sdim *
371218822Sdim * Note: The PAO patches try to use more memory for the card, but that
372218822Sdim * seems to fail for my card.  A future optimization would add this back
373218822Sdim * conditionally.
374218822Sdim */
375218822Sdimstatic int
376218822Sdimed_pccard_Linksys(device_t dev)
377218822Sdim{
378218822Sdim	struct ed_softc *sc = device_get_softc(dev);
379218822Sdim	u_char sum;
380218822Sdim	int i;
381218822Sdim
382218822Sdim	/*
383218822Sdim	 * Linksys registers(offset from ASIC base)
384218822Sdim	 *
385218822Sdim	 * 0x04-0x09 : Physical Address Register 0-5 (PAR0-PAR5)
386218822Sdim	 * 0x0A      : Card ID Register (CIR)
387218822Sdim	 * 0x0B      : Check Sum Register (SR)
388218822Sdim	 */
389218822Sdim	for (sum = 0, i = 0x04; i < 0x0c; i++)
390218822Sdim		sum += ed_asic_inb(sc, i);
391218822Sdim	if (sum != 0xff)
392218822Sdim		return (0);		/* invalid DL10019C */
393218822Sdim	for (i = 0; i < ETHER_ADDR_LEN; i++) {
394218822Sdim		sc->arpcom.ac_enaddr[i] = ed_asic_inb(sc, 0x04 + i);
395218822Sdim	}
396218822Sdim
397218822Sdim	ed_nic_outb(sc, ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS);
398218822Sdim	sc->isa16bit = 1;
399218822Sdim	sc->vendor = ED_VENDOR_LINKSYS;
400218822Sdim	sc->type = ED_TYPE_NE2000;
401218822Sdim	sc->type_str = "Linksys";
402218822Sdim
403218822Sdim	return (1);
404218822Sdim}
405218822Sdim
406218822Sdim/*
407218822Sdim * Special setup for AX88190
408218822Sdim */
409218822Sdimstatic int
410218822Sdimed_pccard_ax88190(device_t dev)
411218822Sdim{
412218822Sdim	int	error;
413218822Sdim	int	flags = device_get_flags(dev);
414218822Sdim	int	iobase;
415218822Sdim	struct	ed_softc *sc = device_get_softc(dev);
416218822Sdim
417218822Sdim	/* Allocate the port resource during setup. */
418218822Sdim	error = ed_alloc_port(dev, 0, ED_NOVELL_IO_PORTS);
419218822Sdim	if (error)
420218822Sdim		return (error);
421218822Sdim
422218822Sdim	sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
423218822Sdim	sc->nic_offset  = ED_NOVELL_NIC_OFFSET;
424218822Sdim	sc->chip_type = ED_CHIP_TYPE_AX88190;
425218822Sdim
426218822Sdim	/*
427218822Sdim	 * Set Attribute Memory IOBASE Register.  Is this a deficiency in
428218822Sdim	 * the PC Card layer, or an ax88190 specific issue? xxx
429218822Sdim	 */
430218822Sdim	iobase = rman_get_start(sc->port_res);
431218822Sdim	ed_pccard_memwrite(dev, ED_AX88190_IOBASE0, iobase & 0xff);
432218822Sdim	ed_pccard_memwrite(dev, ED_AX88190_IOBASE1, (iobase >> 8) & 0xff);
433218822Sdim	ax88190_geteprom(sc);
434218822Sdim	ed_release_resources(dev);
435218822Sdim	error = ed_probe_Novell(dev, 0, flags);
436218822Sdim	if (error == 0) {
437218822Sdim		sc->vendor = ED_VENDOR_PCCARD;
438218822Sdim		sc->type = ED_TYPE_NE2000;
439218822Sdim		sc->type_str = "AX88190";
440218822Sdim	}
441218822Sdim	return (error);
442218822Sdim}
443218822Sdim
444218822Sdim#ifndef ED_NO_MIIBUS
445218822Sdim/* MII bit-twiddling routines for cards using Dlink chipset */
446218822Sdim#define DLINK_MIISET(sc, x) ed_asic_outb(sc, ED_DLINK_MIIBUS, \
447218822Sdim    ed_asic_inb(sc, ED_DLINK_MIIBUS) | (x))
448218822Sdim#define DLINK_MIICLR(sc, x) ed_asic_outb(sc, ED_DLINK_MIIBUS, \
449218822Sdim    ed_asic_inb(sc, ED_DLINK_MIIBUS) & ~(x))
450218822Sdim
451218822Sdimstatic void
452218822Sdimed_pccard_dlink_mii_reset(sc)
453218822Sdim	struct ed_softc *sc;
454218822Sdim{
455218822Sdim	ed_asic_outb(sc, ED_DLINK_MIIBUS, 0);
456218822Sdim	DELAY(10);
457218822Sdim	DLINK_MIISET(sc, ED_DLINK_MII_RESET2);
458218822Sdim	DELAY(10);
459218822Sdim	DLINK_MIISET(sc, ED_DLINK_MII_RESET1);
460218822Sdim	DELAY(10);
461218822Sdim	DLINK_MIICLR(sc, ED_DLINK_MII_RESET1);
462218822Sdim	DELAY(10);
463218822Sdim	DLINK_MIICLR(sc, ED_DLINK_MII_RESET2);
464218822Sdim	DELAY(10);
465218822Sdim}
466218822Sdim
467218822Sdimstatic void
468218822Sdimed_pccard_dlink_mii_writebits(sc, val, nbits)
469218822Sdim	struct ed_softc *sc;
470218822Sdim	u_int val;
471218822Sdim	int nbits;
472218822Sdim{
473218822Sdim	int i;
474218822Sdim
475218822Sdim	DLINK_MIISET(sc, ED_DLINK_MII_DIROUT);
476218822Sdim
477218822Sdim	for (i = nbits - 1; i >= 0; i--) {
478218822Sdim		if ((val >> i) & 1)
479218822Sdim			DLINK_MIISET(sc, ED_DLINK_MII_DATAOUT);
480218822Sdim		else
481218822Sdim			DLINK_MIICLR(sc, ED_DLINK_MII_DATAOUT);
482218822Sdim		DELAY(10);
483218822Sdim		DLINK_MIISET(sc, ED_DLINK_MII_CLK);
484218822Sdim		DELAY(10);
485218822Sdim		DLINK_MIICLR(sc, ED_DLINK_MII_CLK);
486218822Sdim		DELAY(10);
487218822Sdim	}
488218822Sdim}
489218822Sdim
490218822Sdimstatic u_int
491218822Sdimed_pccard_dlink_mii_readbits(sc, nbits)
492218822Sdim	struct ed_softc *sc;
493218822Sdim	int nbits;
494218822Sdim{
495218822Sdim	int i;
496218822Sdim	u_int val = 0;
497218822Sdim
498218822Sdim	DLINK_MIICLR(sc, ED_DLINK_MII_DIROUT);
499218822Sdim
500218822Sdim	for (i = nbits - 1; i >= 0; i--) {
501218822Sdim		DLINK_MIISET(sc, ED_DLINK_MII_CLK);
502218822Sdim		DELAY(10);
503218822Sdim		val <<= 1;
504218822Sdim		if (ed_asic_inb(sc, ED_DLINK_MIIBUS) & ED_DLINK_MII_DATATIN)
505218822Sdim			val++;
506218822Sdim		DLINK_MIICLR(sc, ED_DLINK_MII_CLK);
507218822Sdim		DELAY(10);
508218822Sdim	}
509218822Sdim
510218822Sdim	return val;
511218822Sdim}
512218822Sdim#endif
513218822Sdim
514218822Sdimstatic device_method_t ed_pccard_methods[] = {
515218822Sdim	/* Device interface */
516218822Sdim	DEVMETHOD(device_probe,		pccard_compat_probe),
517218822Sdim	DEVMETHOD(device_attach,	pccard_compat_attach),
518218822Sdim	DEVMETHOD(device_detach,	ed_pccard_detach),
519218822Sdim
520218822Sdim#ifndef ED_NO_MIIBUS
521218822Sdim	/* Bus interface */
522218822Sdim	DEVMETHOD(bus_child_detached,	ed_child_detached),
523218822Sdim
524218822Sdim	/* MII interface */
525218822Sdim	DEVMETHOD(miibus_readreg,	ed_miibus_readreg),
526218822Sdim	DEVMETHOD(miibus_writereg,	ed_miibus_writereg),
527218822Sdim#endif
528218822Sdim
529218822Sdim	/* Card interface */
530218822Sdim	DEVMETHOD(card_compat_match,	ed_pccard_match),
531218822Sdim	DEVMETHOD(card_compat_probe,	ed_pccard_probe),
532218822Sdim	DEVMETHOD(card_compat_attach,	ed_pccard_attach),
533218822Sdim	{ 0, 0 }
534218822Sdim};
535218822Sdim
536218822Sdimstatic driver_t ed_pccard_driver = {
537218822Sdim	"ed",
538218822Sdim	ed_pccard_methods,
539218822Sdim	sizeof(struct ed_softc)
540218822Sdim};
541218822Sdim
542218822SdimDRIVER_MODULE(ed, pccard, ed_pccard_driver, ed_devclass, 0, 0);
543218822Sdim#ifndef ED_NO_MIIBUS
544218822SdimDRIVER_MODULE(miibus, ed, miibus_driver, miibus_devclass, 0, 0);
545218822Sdim#endif
546218822Sdim