1139749Simp/*-
293611Simp * Copyright (c) 1997, 1998, 1999
393611Simp *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
493611Simp *
593611Simp * Redistribution and use in source and binary forms, with or without
693611Simp * modification, are permitted provided that the following conditions
793611Simp * are met:
893611Simp * 1. Redistributions of source code must retain the above copyright
993611Simp *    notice, this list of conditions and the following disclaimer.
1093611Simp * 2. Redistributions in binary form must reproduce the above copyright
1193611Simp *    notice, this list of conditions and the following disclaimer in the
1293611Simp *    documentation and/or other materials provided with the distribution.
1393611Simp * 3. All advertising materials mentioning features or use of this software
1493611Simp *    must display the following acknowledgement:
1593611Simp *	This product includes software developed by Bill Paul.
1693611Simp * 4. Neither the name of the author nor the names of any co-contributors
1793611Simp *    may be used to endorse or promote products derived from this software
1893611Simp *    without specific prior written permission.
1993611Simp *
2093611Simp * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2193611Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2293611Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2393611Simp * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
2493611Simp * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2593611Simp * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2693611Simp * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2793611Simp * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2893611Simp * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2993611Simp * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
3093611Simp * THE POSSIBILITY OF SUCH DAMAGE.
3195754Simp *
3295754Simp * $FreeBSD$
3393611Simp */
3493611Simp
3593611Simp/*
3693611Simp * Lucent WaveLAN/IEEE 802.11 PCMCIA driver for FreeBSD.
3793611Simp *
3893611Simp * Written by Bill Paul <wpaul@ctr.columbia.edu>
3993611Simp * Electrical Engineering Department
4093611Simp * Columbia University, New York City
4193611Simp */
4293611Simp
4393611Simp#include <sys/param.h>
4493611Simp#include <sys/kernel.h>
4593611Simp#include <sys/socket.h>
4695534Simp#include <sys/systm.h>
4793611Simp#include <sys/module.h>
4893611Simp#include <sys/bus.h>
4993611Simp
5093611Simp#include <machine/bus.h>
5193611Simp#include <machine/resource.h>
5293611Simp#include <sys/rman.h>
5393611Simp
54119287Simp#include <dev/pci/pcireg.h>
55119287Simp#include <dev/pci/pcivar.h>
5693611Simp
5793611Simp#include <net/if.h>
5893611Simp#include <net/if_arp.h>
5993611Simp#include <net/ethernet.h>
6093611Simp#include <net/if_media.h>
6193611Simp#include <net/if_types.h>
6293611Simp
63116951Ssam#include <net80211/ieee80211_var.h>
64119784Ssam#include <net80211/ieee80211_radiotap.h>
65116951Ssam
6693611Simp#include <dev/wi/if_wavelan_ieee.h>
67119784Ssam#include <dev/wi/if_wireg.h>
6893611Simp#include <dev/wi/if_wivar.h>
6993611Simp
7093611Simpstatic int wi_pci_probe(device_t);
7193611Simpstatic int wi_pci_attach(device_t);
72109259Smdoddstatic int wi_pci_suspend(device_t);
73109259Smdoddstatic int wi_pci_resume(device_t);
7493611Simp
7593611Simpstatic device_method_t wi_pci_methods[] = {
7693611Simp	/* Device interface */
7793611Simp	DEVMETHOD(device_probe,		wi_pci_probe),
7893611Simp	DEVMETHOD(device_attach,	wi_pci_attach),
79109323Ssam	DEVMETHOD(device_detach,	wi_detach),
8093611Simp	DEVMETHOD(device_shutdown,	wi_shutdown),
81109259Smdodd	DEVMETHOD(device_suspend,	wi_pci_suspend),
82109259Smdodd	DEVMETHOD(device_resume,	wi_pci_resume),
8393611Simp
8493611Simp	{ 0, 0 }
8593611Simp};
8693611Simp
8793611Simpstatic driver_t wi_pci_driver = {
8893611Simp	"wi",
8993611Simp	wi_pci_methods,
9093611Simp	sizeof(struct wi_softc)
9193611Simp};
9293611Simp
9393611Simpstatic struct {
9493611Simp	unsigned int vendor,device;
9593611Simp	int bus_type;
9693611Simp	char *desc;
9793611Simp} pci_ids[] = {
9895603Simp	/* Sorted by description */
9995559Simp	{0x10b7, 0x7770, WI_BUS_PCI_PLX, "3Com Airconnect"},
10095603Simp	{0x16ab, 0x1101, WI_BUS_PCI_PLX, "GLPRISM2 WaveLAN"},
101118679Smarcel	{0x1260, 0x3872, WI_BUS_PCI_NATIVE, "Intersil Prism3"},
10295603Simp	{0x1260, 0x3873, WI_BUS_PCI_NATIVE, "Intersil Prism2.5"},
10395603Simp	{0x16ab, 0x1102, WI_BUS_PCI_PLX, "Linksys WDT11"},
10495603Simp	{0x1385, 0x4100, WI_BUS_PCI_PLX, "Netgear MA301"},
10595603Simp	{0x1638, 0x1100, WI_BUS_PCI_PLX, "PRISM2STA WaveLAN"},
10695559Simp	{0x111a, 0x1023, WI_BUS_PCI_PLX, "Siemens SpeedStream"},
107112360Simp	{0x10b5, 0x9050, WI_BUS_PCI_PLX, "SMC 2602W"},
10898220Snsayer	{0x16ec, 0x3685, WI_BUS_PCI_PLX, "US Robotics 2415"},
109117768Simp	{0x4033, 0x7001, WI_BUS_PCI_PLX, "Addtron AWA-100 PCI"},
11093611Simp	{0, 0, 0, NULL}
11193611Simp};
11293611Simp
113113506SmdoddDRIVER_MODULE(wi, pci, wi_pci_driver, wi_devclass, 0, 0);
114113506SmdoddMODULE_DEPEND(wi, pci, 1, 1, 1);
115113506SmdoddMODULE_DEPEND(wi, wlan, 1, 1, 1);
11693611Simp
11793611Simpstatic int
11893611Simpwi_pci_probe(dev)
11993611Simp	device_t	dev;
12093611Simp{
12193611Simp	struct wi_softc		*sc;
12293611Simp	int i;
12393611Simp
12493611Simp	sc = device_get_softc(dev);
12593611Simp	for(i=0; pci_ids[i].vendor != 0; i++) {
12693611Simp		if ((pci_get_vendor(dev) == pci_ids[i].vendor) &&
12793611Simp			(pci_get_device(dev) == pci_ids[i].device)) {
12893611Simp			sc->wi_bus_type = pci_ids[i].bus_type;
12993611Simp			device_set_desc(dev, pci_ids[i].desc);
130142880Simp			return (BUS_PROBE_DEFAULT);
13193611Simp		}
13293611Simp	}
13393611Simp	return(ENXIO);
13493611Simp}
13593611Simp
13693611Simpstatic int
13793611Simpwi_pci_attach(device_t dev)
13893611Simp{
13993611Simp	struct wi_softc		*sc;
140254263Sscottl	u_int32_t		command;
14193611Simp	u_int16_t		reg;
14293611Simp	int			error;
14393611Simp	int			timeout;
14493611Simp
14593611Simp	sc = device_get_softc(dev);
14693611Simp
14793611Simp	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE) {
14893611Simp		error = wi_alloc(dev, WI_PCI_IORES);
14993611Simp		if (error)
15093611Simp			return (error);
15193611Simp
15293611Simp		/* Make sure interrupts are disabled. */
15393611Simp		CSR_WRITE_2(sc, WI_INT_EN, 0);
15493611Simp		CSR_WRITE_2(sc, WI_EVENT_ACK, 0xFFFF);
15593611Simp
15693611Simp		/* We have to do a magic PLX poke to enable interrupts */
15793611Simp		sc->local_rid = WI_PCI_LOCALRES;
158127135Snjl		sc->local = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
159127135Snjl		    &sc->local_rid, RF_ACTIVE);
16093611Simp		sc->wi_localtag = rman_get_bustag(sc->local);
16193611Simp		sc->wi_localhandle = rman_get_bushandle(sc->local);
16293611Simp		command = bus_space_read_4(sc->wi_localtag, sc->wi_localhandle,
16393611Simp		    WI_LOCAL_INTCSR);
16493611Simp		command |= WI_LOCAL_INTEN;
16593611Simp		bus_space_write_4(sc->wi_localtag, sc->wi_localhandle,
16693611Simp		    WI_LOCAL_INTCSR, command);
16793611Simp		bus_release_resource(dev, SYS_RES_IOPORT, sc->local_rid,
16893611Simp		    sc->local);
16993611Simp		sc->local = NULL;
17093611Simp
17193611Simp		sc->mem_rid = WI_PCI_MEMRES;
172127135Snjl		sc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
173127135Snjl					&sc->mem_rid, RF_ACTIVE);
17493611Simp		if (sc->mem == NULL) {
17593611Simp			device_printf(dev, "couldn't allocate memory\n");
17693611Simp			wi_free(dev);
17793611Simp			return (ENXIO);
17893611Simp		}
17993611Simp		sc->wi_bmemtag = rman_get_bustag(sc->mem);
18093611Simp		sc->wi_bmemhandle = rman_get_bushandle(sc->mem);
18193611Simp
18293611Simp		/*
18393611Simp		 * Write COR to enable PC card
18493611Simp		 * This is a subset of the protocol that the pccard bus code
185181209Simp		 * would do.  In theory, we should parse the CIS to find the
186181209Simp		 * COR offset.  In practice, the COR_OFFSET is always 0x3e0.
18793611Simp		 */
18893611Simp		CSM_WRITE_1(sc, WI_COR_OFFSET, WI_COR_VALUE);
18993611Simp		reg = CSM_READ_1(sc, WI_COR_OFFSET);
19093611Simp		if (reg != WI_COR_VALUE) {
19193611Simp			device_printf(dev, "CSM_READ_1(WI_COR_OFFSET) "
19293611Simp			    "wanted %d, got %d\n", WI_COR_VALUE, reg);
19393611Simp			wi_free(dev);
19493611Simp			return (ENXIO);
19593611Simp		}
19693611Simp	} else {
19793611Simp		error = wi_alloc(dev, WI_PCI_LMEMRES);
19893611Simp		if (error)
19993611Simp			return (error);
20093611Simp
201181209Simp		CSR_WRITE_2(sc, WI_PCICOR_OFF, WI_PCICOR_RESET);
20293611Simp		DELAY(250000);
20393611Simp
204181209Simp		CSR_WRITE_2(sc, WI_PCICOR_OFF, 0x0000);
20593611Simp		DELAY(500000);
20693611Simp
20793611Simp		timeout=2000000;
20893611Simp		while ((--timeout > 0) &&
20993611Simp		    (CSR_READ_2(sc, WI_COMMAND) & WI_CMD_BUSY))
21093611Simp			DELAY(10);
21193611Simp
21293611Simp		if (timeout == 0) {
213181209Simp			device_printf(dev, "couldn't reset prism pci core.\n");
21493611Simp			wi_free(dev);
21593611Simp			return(ENXIO);
21693611Simp		}
21793611Simp	}
21893611Simp
21993611Simp	CSR_WRITE_2(sc, WI_HFA384X_SWSUPPORT0_OFF, WI_PRISM2STA_MAGIC);
22093611Simp	reg = CSR_READ_2(sc, WI_HFA384X_SWSUPPORT0_OFF);
22193611Simp	if (reg != WI_PRISM2STA_MAGIC) {
22293611Simp		device_printf(dev,
22393611Simp		    "CSR_READ_2(WI_HFA384X_SWSUPPORT0_OFF) "
22493611Simp		    "wanted %d, got %d\n", WI_PRISM2STA_MAGIC, reg);
22593611Simp		wi_free(dev);
22693611Simp		return (ENXIO);
22793611Simp	}
22893611Simp
229109323Ssam	error = wi_attach(dev);
23093611Simp	if (error != 0)
231116205Simp		wi_free(dev);
232116205Simp	return (error);
23393611Simp}
234109259Smdodd
235109259Smdoddstatic int
236109323Ssamwi_pci_suspend(device_t dev)
237109259Smdodd{
238138571Ssam	struct wi_softc	*sc = device_get_softc(dev);
239109259Smdodd
240178354Ssam	wi_stop(sc, 1);
241109259Smdodd
242109259Smdodd	return (0);
243109259Smdodd}
244109259Smdodd
245109259Smdoddstatic int
246109323Ssamwi_pci_resume(device_t dev)
247109259Smdodd{
248138571Ssam	struct wi_softc	*sc = device_get_softc(dev);
249178354Ssam	struct ifnet *ifp = sc->sc_ifp;
250109259Smdodd
251109259Smdodd	if (sc->wi_bus_type != WI_BUS_PCI_NATIVE)
252109259Smdodd		return (0);
253109259Smdodd
254109259Smdodd	if (ifp->if_flags & IFF_UP) {
255118068Sjdp		ifp->if_init(ifp->if_softc);
256148887Srwatson		if (ifp->if_drv_flags & IFF_DRV_RUNNING)
257118068Sjdp			ifp->if_start(ifp);
258109259Smdodd	}
259109259Smdodd
260109259Smdodd	return (0);
261109259Smdodd}
262