if_ed_pccard.c revision 69964
1/*
2 * Copyright (c) 1995, David Greenman
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 unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: head/sys/dev/ed/if_ed_pccard.c 69964 2000-12-13 06:27:23Z imp $
28 */
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/socket.h>
33#include <sys/kernel.h>
34#include <sys/conf.h>
35#include <sys/uio.h>
36#include <sys/select.h>
37
38#include <sys/module.h>
39#include <sys/bus.h>
40#include <machine/bus.h>
41#include <sys/rman.h>
42#include <machine/resource.h>
43
44#include <net/ethernet.h>
45#include <net/if.h>
46#include <net/if_arp.h>
47#include <net/if_mib.h>
48
49#include <dev/ed/if_edreg.h>
50#include <dev/ed/if_edvar.h>
51#include <dev/pccard/pccardvar.h>
52#include <dev/pccard/pccarddevs.h>
53
54#include "card_if.h"
55
56/*
57 *      PC-Card (PCMCIA) specific code.
58 */
59static int	ed_pccard_match(device_t);
60static int	ed_pccard_probe(device_t);
61static int	ed_pccard_attach(device_t);
62static int	ed_pccard_detach(device_t);
63
64static void	ax88190_geteprom(struct ed_softc *);
65static int	ed_pccard_memwrite(device_t dev, off_t offset, u_char byte);
66static int	linksys;
67
68static device_method_t ed_pccard_methods[] = {
69	/* Device interface */
70	DEVMETHOD(device_probe,		pccard_compat_probe),
71	DEVMETHOD(device_attach,	pccard_compat_attach),
72	DEVMETHOD(device_detach,	ed_pccard_detach),
73
74	/* Card interface */
75	DEVMETHOD(card_compat_match,	ed_pccard_match),
76	DEVMETHOD(card_compat_probe,	ed_pccard_probe),
77	DEVMETHOD(card_compat_attach,	ed_pccard_attach),
78	{ 0, 0 }
79};
80
81static driver_t ed_pccard_driver = {
82	"ed",
83	ed_pccard_methods,
84	sizeof(struct ed_softc)
85};
86
87static devclass_t ed_pccard_devclass;
88
89DRIVER_MODULE(if_ed, pccard, ed_pccard_driver, ed_pccard_devclass, 0, 0);
90
91/*
92 *      ed_pccard_detach - unload the driver and clear the table.
93 *      XXX TODO:
94 *      This is usually called when the card is ejected, but
95 *      can be caused by a modunload of a controller driver.
96 *      The idea is to reset the driver's view of the device
97 *      and ensure that any driver entry points such as
98 *      read and write do not hang.
99 */
100static int
101ed_pccard_detach(device_t dev)
102{
103	struct ed_softc *sc = device_get_softc(dev);
104	struct ifnet *ifp = &sc->arpcom.ac_if;
105
106	if (sc->gone) {
107		device_printf(dev, "already unloaded\n");
108		return (0);
109	}
110	ed_stop(sc);
111	ifp->if_flags &= ~IFF_RUNNING;
112	ether_ifdetach(ifp, ETHER_BPF_SUPPORTED);
113	sc->gone = 1;
114	bus_teardown_intr(dev, sc->irq_res, sc->irq_handle);
115	ed_release_resources(dev);
116	return (0);
117}
118
119static const struct pccard_product ed_pccard_products[] = {
120	{ PCCARD_STR_KINGSTON_KNE2,		PCCARD_VENDOR_KINGSTON,
121	  PCCARD_PRODUCT_KINGSTON_KNE2,		0, NULL, NULL },
122	{ NULL }
123};
124
125static int
126ed_pccard_match(device_t dev)
127{
128	const struct pccard_product *pp;
129
130	if ((pp = pccard_product_lookup(dev, ed_pccard_products,
131	    sizeof(ed_pccard_products[0]), NULL)) != NULL) {
132		device_set_desc(dev, pp->pp_name);
133		return 0;
134	}
135	return EIO;
136}
137
138/*
139 * Probe framework for pccards.  Replicates the standard framework,
140 * minus the pccard driver registration and ignores the ether address
141 * supplied (from the CIS), relying on the probe to find it instead.
142 */
143static int
144ed_pccard_probe(device_t dev)
145{
146	struct	ed_softc *sc = device_get_softc(dev);
147	int	flags = device_get_flags(dev);
148	int	error;
149
150	if (ED_FLAGS_GETTYPE(flags) == ED_FLAGS_AX88190) {
151		/* Special setup for AX88190 */
152		int iobase;
153
154		/* Allocate the port resource during setup. */
155		error = ed_alloc_port(dev, 0, ED_NOVELL_IO_PORTS);
156		if (error)
157			return (error);
158
159		sc->asic_offset = ED_NOVELL_ASIC_OFFSET;
160		sc->nic_offset  = ED_NOVELL_NIC_OFFSET;
161
162		sc->chip_type = ED_CHIP_TYPE_AX88190;
163
164		/*
165		 * Set Attribute Memory IOBASE Register
166		 */
167		iobase = rman_get_start(sc->port_res);
168		ed_pccard_memwrite(dev, ED_AX88190_IOBASE0,
169				   iobase & 0xff);
170		ed_pccard_memwrite(dev, ED_AX88190_IOBASE1,
171				   (iobase >> 8) & 0xff);
172		ax88190_geteprom(sc);
173
174		ed_release_resources(dev);
175		goto end2;
176	}
177
178	error = ed_probe_Novell(dev, 0, flags);
179	if (error == 0)
180		goto end;
181	ed_release_resources(dev);
182
183	error = ed_probe_WD80x3(dev, 0, flags);
184	if (error == 0)
185		goto end;
186	ed_release_resources(dev);
187	goto end2;
188
189end:
190	linksys = ed_get_Linksys(dev);
191end2:
192	if (error == 0)
193		error = ed_alloc_irq(dev, 0, 0);
194
195	ed_release_resources(dev);
196	return (error);
197}
198
199static int
200ed_pccard_attach(device_t dev)
201{
202	struct ed_softc *sc = device_get_softc(dev);
203	int flags = device_get_flags(dev);
204	int error;
205	int i;
206	u_char sum;
207	u_char ether_addr[ETHER_ADDR_LEN];
208
209	if (sc->port_used > 0)
210		ed_alloc_port(dev, sc->port_rid, sc->port_used);
211	if (sc->mem_used)
212		ed_alloc_memory(dev, sc->mem_rid, sc->mem_used);
213	ed_alloc_irq(dev, sc->irq_rid, 0);
214
215	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
216			       edintr, sc, &sc->irq_handle);
217	if (error) {
218		printf("setup intr failed %d \n", error);
219		ed_release_resources(dev);
220		return (error);
221	}
222
223	if (linksys == 0) {
224		pccard_get_ether(dev, ether_addr);
225		for (i = 0, sum = 0; i < ETHER_ADDR_LEN; i++)
226			sum |= ether_addr[i];
227		if (sum)
228			bcopy(ether_addr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
229	}
230
231	error = ed_attach(sc, device_get_unit(dev), flags);
232	return (error);
233}
234
235static void
236ax88190_geteprom(struct ed_softc *sc)
237{
238	int prom[16],i;
239	u_char tmp;
240	struct {
241		unsigned char offset, value;
242	} pg_seq[] = {
243		{ED_P0_CR, ED_CR_RD2|ED_CR_STP},	/* Select Page0 */
244		{ED_P0_DCR, 0x01},
245		{ED_P0_RBCR0, 0x00},	/* Clear the count regs. */
246		{ED_P0_RBCR1, 0x00},
247		{ED_P0_IMR, 0x00},	/* Mask completion irq. */
248		{ED_P0_ISR, 0xff},
249		{ED_P0_RCR, ED_RCR_MON | ED_RCR_INTT},	/* Set To Monitor */
250		{ED_P0_TCR, ED_TCR_LB0},	/* loopback mode. */
251		{ED_P0_RBCR0, 32},
252		{ED_P0_RBCR1, 0x00},
253		{ED_P0_RSAR0, 0x00},
254		{ED_P0_RSAR1, 0x04},
255		{ED_P0_CR ,ED_CR_RD0 | ED_CR_STA},
256	};
257
258	/* Reset Card */
259	tmp = ed_asic_inb(sc, ED_NOVELL_RESET);
260	ed_asic_outb(sc, ED_NOVELL_RESET, tmp);
261	DELAY(5000);
262	ed_asic_outb(sc, ED_P0_CR, ED_CR_RD2 | ED_CR_STP);
263	DELAY(5000);
264
265	/* Card Settings */
266	for (i = 0; i < sizeof(pg_seq) / sizeof(pg_seq[0]); i++)
267		ed_nic_outb(sc, pg_seq[i].offset, pg_seq[i].value);
268
269	/* Get Data */
270	for (i = 0; i < 16; i++)
271		prom[i] = ed_asic_inb(sc, 0);
272/*
273	for (i = 0; i < 16; i++)
274		printf("ax88190 eprom [%02d] %02x %02x\n",
275			i,prom[i] & 0xff,prom[i] >> 8);
276*/
277	sc->arpcom.ac_enaddr[0] = prom[0] & 0xff;
278	sc->arpcom.ac_enaddr[1] = prom[0] >> 8;
279	sc->arpcom.ac_enaddr[2] = prom[1] & 0xff;
280	sc->arpcom.ac_enaddr[3] = prom[1] >> 8;
281	sc->arpcom.ac_enaddr[4] = prom[2] & 0xff;
282	sc->arpcom.ac_enaddr[5] = prom[2] >> 8;
283}
284
285static int
286ed_pccard_memwrite(device_t dev, off_t offset, u_char byte)
287{
288	int cis_rid;
289	struct resource *cis;
290
291	cis_rid = 0;
292	cis = bus_alloc_resource(dev, SYS_RES_MEMORY, &cis_rid, 0, ~0, 4 << 10, RF_ACTIVE | RF_SHAREABLE);
293	if (cis == NULL)
294		return (ENXIO);
295	CARD_SET_RES_FLAGS(device_get_parent(dev), dev, SYS_RES_MEMORY, cis_rid, PCCARD_A_MEM_ATTR);
296
297	bus_space_write_1(rman_get_bustag(cis), rman_get_bushandle(cis), offset, byte);
298
299	bus_deactivate_resource(dev, SYS_RES_MEMORY, cis_rid, cis);
300	bus_release_resource(dev, SYS_RES_MEMORY, cis_rid, cis);
301
302	return (0);
303}
304