if_an_pci.c revision 123978
1284184Sdim/*-
2284184Sdim * Copyright (c) 1997, 1998, 1999
3284184Sdim *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4284184Sdim *
5284184Sdim * Redistribution and use in source and binary forms, with or without
6284184Sdim * modification, are permitted provided that the following conditions
7284184Sdim * are met:
8284184Sdim * 1. Redistributions of source code must retain the above copyright
9284184Sdim *    notice, this list of conditions and the following disclaimer.
10284184Sdim * 2. Redistributions in binary form must reproduce the above copyright
11284184Sdim *    notice, this list of conditions and the following disclaimer in the
12284184Sdim *    documentation and/or other materials provided with the distribution.
13284184Sdim * 3. All advertising materials mentioning features or use of this software
14284184Sdim *    must display the following acknowledgement:
15284184Sdim *	This product includes software developed by Bill Paul.
16284184Sdim * 4. Neither the name of the author nor the names of any co-contributors
17284184Sdim *    may be used to endorse or promote products derived from this software
18284184Sdim *    without specific prior written permission.
19284184Sdim *
20284184Sdim * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21284184Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22284184Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23284184Sdim * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
24284184Sdim * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25284184Sdim * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26284184Sdim * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27284184Sdim * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28284184Sdim * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29284184Sdim * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
30284184Sdim * THE POSSIBILITY OF SUCH DAMAGE.
31284184Sdim */
32284184Sdim
33296417Sdim#include <sys/cdefs.h>
34284184Sdim__FBSDID("$FreeBSD: head/sys/dev/an/if_an_pci.c 123978 2003-12-30 01:07:12Z ambrisko $");
35284184Sdim
36284184Sdim/*
37284184Sdim * This is a PCI shim for the Aironet PC4500/4800 wireless network
38284184Sdim * driver. Aironet makes PCMCIA, ISA and PCI versions of these devices,
39284184Sdim * which all have basically the same interface. The ISA and PCI cards
40284184Sdim * are actually bridge adapters with PCMCIA cards inserted into them,
41296417Sdim * however they appear as normal PCI or ISA devices to the host.
42284184Sdim *
43284184Sdim * All we do here is handle the PCI probe and attach and set up an
44284184Sdim * interrupt handler entry point. The PCI version of the card uses
45284184Sdim * a PLX 9050 PCI to "dumb bus" bridge chip, which provides us with
46284184Sdim * multiple PCI address space mappings. The primary mapping at PCI
47284184Sdim * register 0x14 is for the PLX chip itself, *NOT* the Aironet card.
48284184Sdim * The I/O address of the Aironet is actually at register 0x18, which
49284184Sdim * is the local bus mapping register for bus space 0. There are also
50284184Sdim * registers for additional register spaces at registers 0x1C and
51284184Sdim * 0x20, but these are unused in the Aironet devices. To find out
52284184Sdim * more, you need a datasheet for the 9050 from PLX, but you have
53284184Sdim * to go through their sales office to get it. Bleh.
54284184Sdim */
55284734Sdim
56284734Sdim#include "opt_inet.h"
57284734Sdim
58284734Sdim#ifdef INET
59284734Sdim#define ANCACHE
60284184Sdim#endif
61284184Sdim
62284184Sdim#include <sys/param.h>
63284184Sdim#include <sys/systm.h>
64284184Sdim#include <sys/sockio.h>
65284184Sdim#include <sys/mbuf.h>
66284184Sdim#include <sys/kernel.h>
67296417Sdim#include <sys/socket.h>
68284184Sdim
69284184Sdim#include <sys/module.h>
70284184Sdim#include <sys/bus.h>
71284184Sdim#include <machine/bus.h>
72284184Sdim#include <sys/rman.h>
73284184Sdim#include <sys/lock.h>
74284184Sdim#include <sys/mutex.h>
75286684Sdim#include <machine/resource.h>
76286684Sdim
77286684Sdim#include <net/if.h>
78284184Sdim#include <net/if_arp.h>
79286684Sdim#include <net/ethernet.h>
80286684Sdim#include <net/if_dl.h>
81286684Sdim#include <net/if_media.h>
82284184Sdim
83296417Sdim#include <dev/pci/pcireg.h>
84296417Sdim#include <dev/pci/pcivar.h>
85296417Sdim
86286684Sdim#include <dev/an/if_aironet_ieee.h>
87284184Sdim#include <dev/an/if_anreg.h>
88284184Sdim
89284184Sdimstruct an_type {
90284184Sdim	u_int16_t		an_vid;
91296417Sdim	u_int16_t		an_did;
92296417Sdim	char			*an_name;
93284184Sdim};
94296417Sdim
95284184Sdim#define AIRONET_VENDORID	0x14B9
96284184Sdim#define AIRONET_DEVICEID_35x	0x0350
97284184Sdim#define AIRONET_DEVICEID_4500	0x4500
98284184Sdim#define AIRONET_DEVICEID_4800	0x4800
99296417Sdim#define AIRONET_DEVICEID_4xxx	0x0001
100284184Sdim#define AIRONET_DEVICEID_MPI350	0xA504
101284184Sdim#define AN_PCI_PLX_LOIO		0x14	/* PLX chip iobase */
102284184Sdim#define AN_PCI_LOIO		0x18	/* Aironet iobase */
103284184Sdim
104284184Sdimstatic struct an_type an_devs[] = {
105284184Sdim	{ AIRONET_VENDORID, AIRONET_DEVICEID_35x, "Cisco Aironet 350 Series" },
106284184Sdim	{ AIRONET_VENDORID, AIRONET_DEVICEID_4500, "Aironet PCI4500" },
107284184Sdim	{ AIRONET_VENDORID, AIRONET_DEVICEID_4800, "Aironet PCI4800" },
108286684Sdim	{ AIRONET_VENDORID, AIRONET_DEVICEID_4xxx, "Aironet PCI4500/PCI4800" },
109286684Sdim	{ 0, 0, NULL }
110286684Sdim};
111286684Sdim
112284184Sdimstatic int an_probe_pci		(device_t);
113296417Sdimstatic int an_attach_pci	(device_t);
114296417Sdimstatic int an_suspend_pci	(device_t);
115296417Sdimstatic int an_resume_pci	(device_t);
116296417Sdim
117284184Sdimstatic int
118284184Sdiman_probe_pci(device_t dev)
119296417Sdim{
120296417Sdim	struct an_type		*t;
121296417Sdim
122296417Sdim	t = an_devs;
123296417Sdim
124296417Sdim	while (t->an_name != NULL) {
125284184Sdim		if (pci_get_vendor(dev) == t->an_vid &&
126284184Sdim		    pci_get_device(dev) == t->an_did) {
127284184Sdim			device_set_desc(dev, t->an_name);
128296417Sdim			return(0);
129296417Sdim		}
130296417Sdim		t++;
131296417Sdim	}
132296417Sdim
133284184Sdim	if (pci_get_vendor(dev) == AIRONET_VENDORID &&
134284184Sdim	    pci_get_device(dev) == AIRONET_DEVICEID_MPI350) {
135284184Sdim		device_set_desc(dev, "Cisco Aironet MPI350");
136284184Sdim		return(0);
137296417Sdim	}
138284184Sdim
139284734Sdim	return(ENXIO);
140284184Sdim}
141284184Sdim
142296417Sdimstatic int
143296417Sdiman_attach_pci(dev)
144284184Sdim	device_t		dev;
145284184Sdim{
146284184Sdim	u_int32_t		command;
147284184Sdim	struct an_softc		*sc;
148284184Sdim	int 			unit, flags, error = 0;
149296417Sdim
150284184Sdim	sc = device_get_softc(dev);
151296417Sdim	unit = device_get_unit(dev);
152296417Sdim	flags = device_get_flags(dev);
153296417Sdim	bzero(sc, sizeof(struct an_softc));
154296417Sdim
155296417Sdim	if (pci_get_vendor(dev) == AIRONET_VENDORID &&
156296417Sdim	    pci_get_device(dev) == AIRONET_DEVICEID_MPI350) {
157296417Sdim		sc->mpi350 = 1;
158296417Sdim		sc->port_rid = PCIR_BAR(0);
159296417Sdim	} else {
160296417Sdim		/*
161296417Sdim		 * Map control/status registers.
162296417Sdim	 	 */
163296417Sdim		command = pci_read_config(dev, PCIR_COMMAND, 4);
164296417Sdim		command |= PCIM_CMD_PORTEN;
165296417Sdim		pci_write_config(dev, PCIR_COMMAND, command, 4);
166296417Sdim		command = pci_read_config(dev, PCIR_COMMAND, 4);
167296417Sdim
168296417Sdim		if (!(command & PCIM_CMD_PORTEN)) {
169296417Sdim			printf("an%d: failed to enable I/O ports!\n", unit);
170296417Sdim			error = ENXIO;
171296417Sdim			goto fail;
172296417Sdim		}
173296417Sdim		sc->port_rid = AN_PCI_LOIO;
174296417Sdim	}
175296417Sdim	error = an_alloc_port(dev, sc->port_rid, 1);
176296417Sdim
177296417Sdim	if (error) {
178296417Sdim		printf("an%d: couldn't map ports\n", unit);
179296417Sdim		goto fail;
180296417Sdim	}
181296417Sdim
182296417Sdim	sc->an_btag = rman_get_bustag(sc->port_res);
183296417Sdim	sc->an_bhandle = rman_get_bushandle(sc->port_res);
184296417Sdim
185296417Sdim	/* Allocate memory for MPI350 */
186296417Sdim	if (sc->mpi350) {
187296417Sdim		/* Allocate memory */
188296417Sdim		sc->mem_rid = PCIR_BAR(1);
189296417Sdim		error = an_alloc_memory(dev, sc->mem_rid, 1);
190296417Sdim		if (error) {
191296417Sdim			printf("an%d: couldn't map memory\n", unit);
192296417Sdim			goto fail;
193296417Sdim		}
194296417Sdim		sc->an_mem_btag = rman_get_bustag(sc->mem_res);
195296417Sdim		sc->an_mem_bhandle = rman_get_bushandle(sc->mem_res);
196296417Sdim
197292735Sdim		/* Allocate aux. memory */
198296417Sdim		sc->mem_aux_rid = PCIR_BAR(2);
199296417Sdim		error = an_alloc_aux_memory(dev, sc->mem_aux_rid,
200296417Sdim		    AN_AUX_MEM_SIZE);
201296417Sdim		if (error) {
202296417Sdim			printf("an%d: couldn't map aux memory\n", unit);
203296417Sdim			goto fail;
204296417Sdim		}
205296417Sdim		sc->an_mem_aux_btag = rman_get_bustag(sc->mem_aux_res);
206296417Sdim		sc->an_mem_aux_bhandle = rman_get_bushandle(sc->mem_aux_res);
207296417Sdim
208296417Sdim		/* Allocate DMA region */
209296417Sdim		error = bus_dma_tag_create(NULL,	/* parent */
210296417Sdim			       1, 0,			/* alignment, bounds */
211296417Sdim			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
212296417Sdim			       BUS_SPACE_MAXADDR,	/* highaddr */
213296417Sdim			       NULL, NULL,		/* filter, filterarg */
214296417Sdim			       0x3ffff,			/* maxsize XXX */
215296417Sdim			       1,			/* nsegments */
216296417Sdim			       0xffff,			/* maxsegsize XXX */
217296417Sdim			       BUS_DMA_ALLOCNOW,	/* flags */
218296417Sdim			       NULL,			/* lockfunc */
219296417Sdim			       NULL,			/* lockarg */
220296417Sdim			       &sc->an_dtag);
221296417Sdim		if (error) {
222296417Sdim			printf("an%d: couldn't get DMA region\n", unit);
223296417Sdim			goto fail;
224296417Sdim		}
225296417Sdim	}
226296417Sdim
227296417Sdim	/* Allocate interrupt */
228296417Sdim	error = an_alloc_irq(dev, 0, RF_SHAREABLE);
229296417Sdim	if (error) {
230296417Sdim		goto fail;
231296417Sdim        }
232296417Sdim
233296417Sdim	sc->an_dev = dev;
234296417Sdim	error = an_attach(sc, device_get_unit(dev), flags);
235296417Sdim	if (error) {
236296417Sdim		goto fail;
237296417Sdim	}
238296417Sdim
239296417Sdim	/*
240296417Sdim	 * Must setup the interrupt after the an_attach to prevent racing.
241296417Sdim	 */
242296417Sdim	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET,
243296417Sdim	    an_intr, sc, &sc->irq_handle);
244296417Sdim
245296417Sdimfail:
246296417Sdim	if (error)
247296417Sdim		an_release_resources(dev);
248296417Sdim	return(error);
249296417Sdim}
250296417Sdim
251296417Sdimstatic int
252296417Sdiman_suspend_pci(device_t dev)
253296417Sdim{
254296417Sdim	an_shutdown(dev);
255296417Sdim
256296417Sdim	return (0);
257296417Sdim}
258296417Sdim
259296417Sdimstatic int
260284184Sdiman_resume_pci(device_t dev)
261296417Sdim{
262296417Sdim	an_resume(dev);
263296417Sdim
264296417Sdim	return (0);
265284184Sdim}
266284184Sdim
267284184Sdimstatic device_method_t an_pci_methods[] = {
268284184Sdim        /* Device interface */
269284184Sdim        DEVMETHOD(device_probe,         an_probe_pci),
270284184Sdim        DEVMETHOD(device_attach,        an_attach_pci),
271284184Sdim	DEVMETHOD(device_detach,	an_detach),
272284184Sdim	DEVMETHOD(device_shutdown,	an_shutdown),
273284184Sdim	DEVMETHOD(device_suspend,	an_suspend_pci),
274284184Sdim	DEVMETHOD(device_resume,	an_resume_pci),
275284184Sdim        { 0, 0 }
276284184Sdim};
277284184Sdim
278284184Sdimstatic driver_t an_pci_driver = {
279284184Sdim        "an",
280284184Sdim        an_pci_methods,
281284184Sdim        sizeof(struct an_softc),
282296417Sdim};
283296417Sdim
284296417Sdimstatic devclass_t an_devclass;
285296417Sdim
286296417SdimDRIVER_MODULE(an, pci, an_pci_driver, an_devclass, 0, 0);
287296417SdimMODULE_DEPEND(an, pci, 1, 1, 1);
288296417SdimMODULE_DEPEND(an, wlan, 1, 1, 1);
289296417Sdim