1193240Ssam/*-
2193240Ssam * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting
3193240Ssam * Copyright (c) 2007-2009 Marvell Semiconductor, Inc.
4193240Ssam * All rights reserved.
5193240Ssam *
6193240Ssam * Redistribution and use in source and binary forms, with or without
7193240Ssam * modification, are permitted provided that the following conditions
8193240Ssam * are met:
9193240Ssam * 1. Redistributions of source code must retain the above copyright
10193240Ssam *    notice, this list of conditions and the following disclaimer,
11193240Ssam *    without modification.
12193240Ssam * 2. Redistributions in binary form must reproduce at minimum a disclaimer
13193240Ssam *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
14193240Ssam *    redistribution must be conditioned upon including a substantially
15193240Ssam *    similar Disclaimer requirement for further binary redistribution.
16193240Ssam *
17193240Ssam * NO WARRANTY
18193240Ssam * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19193240Ssam * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20193240Ssam * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
21193240Ssam * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22193240Ssam * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
23193240Ssam * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24193240Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25193240Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
26193240Ssam * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27193240Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28193240Ssam * THE POSSIBILITY OF SUCH DAMAGES.
29193240Ssam */
30193240Ssam
31193240Ssam#include <sys/cdefs.h>
32193240Ssam#ifdef __FreeBSD__
33193240Ssam__FBSDID("$FreeBSD: releng/10.2/sys/dev/mwl/if_mwl_pci.c 278415 2015-02-08 22:27:17Z marius $");
34193240Ssam#endif
35193240Ssam
36193240Ssam/*
37193240Ssam * PCI front-end for the Marvell Wireless LAN controller driver.
38193240Ssam */
39193240Ssam
40193240Ssam#include <sys/param.h>
41193240Ssam#include <sys/systm.h>
42193240Ssam#include <sys/module.h>
43193240Ssam#include <sys/kernel.h>
44193240Ssam#include <sys/lock.h>
45193240Ssam#include <sys/mutex.h>
46193240Ssam#include <sys/errno.h>
47193240Ssam
48193240Ssam#include <machine/bus.h>
49193240Ssam#include <machine/resource.h>
50193240Ssam#include <sys/bus.h>
51193240Ssam#include <sys/rman.h>
52193240Ssam
53193240Ssam#include <sys/socket.h>
54193240Ssam
55193240Ssam#include <net/if.h>
56193240Ssam#include <net/if_media.h>
57193240Ssam#include <net/if_arp.h>
58193240Ssam
59193240Ssam#include <net80211/ieee80211_var.h>
60193240Ssam
61193240Ssam#include <dev/mwl/if_mwlvar.h>
62193240Ssam
63193240Ssam#include <dev/pci/pcivar.h>
64193240Ssam#include <dev/pci/pcireg.h>
65193240Ssam
66193240Ssam/*
67193240Ssam * PCI glue.
68193240Ssam */
69193240Ssam
70193240Ssamstruct mwl_pci_softc {
71193240Ssam	struct mwl_softc	sc_sc;
72193240Ssam	struct resource		*sc_sr0;	/* BAR0 memory resource */
73193240Ssam	struct resource		*sc_sr1;	/* BAR1 memory resource */
74193240Ssam	struct resource		*sc_irq;	/* irq resource */
75193240Ssam	void			*sc_ih;		/* interrupt handler */
76193240Ssam};
77193240Ssam
78193240Ssam#define	BS_BAR0	0x10
79193240Ssam#define	BS_BAR1	0x14
80193240Ssam
81193240Ssamstruct mwl_pci_ident {
82193240Ssam	uint16_t	vendor;
83193240Ssam	uint16_t	device;
84193240Ssam	const char	*name;
85193240Ssam};
86193240Ssam
87193240Ssamstatic const struct mwl_pci_ident mwl_pci_ids[] = {
88193240Ssam	{ 0x11ab, 0x2a02, "Marvell 88W8363" },
89193240Ssam	{ 0x11ab, 0x2a03, "Marvell 88W8363" },
90193240Ssam	{ 0x11ab, 0x2a0a, "Marvell 88W8363" },
91193240Ssam	{ 0x11ab, 0x2a0b, "Marvell 88W8363" },
92193240Ssam	{ 0x11ab, 0x2a0c, "Marvell 88W8363" },
93193240Ssam	{ 0x11ab, 0x2a21, "Marvell 88W8363" },
94193240Ssam	{ 0x11ab, 0x2a24, "Marvell 88W8363" },
95193240Ssam
96193240Ssam	{ 0, 0, NULL }
97193240Ssam};
98193240Ssam
99193240Ssamconst static struct mwl_pci_ident *
100193240Ssammwl_pci_lookup(int vendor, int device)
101193240Ssam{
102193240Ssam	const struct mwl_pci_ident *ident;
103193240Ssam
104193240Ssam	for (ident = mwl_pci_ids; ident->name != NULL; ident++)
105193240Ssam		if (vendor == ident->vendor && device == ident->device)
106193240Ssam			return ident;
107193240Ssam	return NULL;
108193240Ssam}
109193240Ssam
110193240Ssamstatic int
111193240Ssammwl_pci_probe(device_t dev)
112193240Ssam{
113193240Ssam	const struct mwl_pci_ident *ident;
114193240Ssam
115193240Ssam	ident = mwl_pci_lookup(pci_get_vendor(dev), pci_get_device(dev));
116193240Ssam	if (ident != NULL) {
117193240Ssam		device_set_desc(dev, ident->name);
118193240Ssam		return BUS_PROBE_DEFAULT;
119193240Ssam	}
120193240Ssam	return ENXIO;
121193240Ssam}
122193240Ssam
123193240Ssamstatic int
124193240Ssammwl_pci_attach(device_t dev)
125193240Ssam{
126193240Ssam	struct mwl_pci_softc *psc = device_get_softc(dev);
127193240Ssam	struct mwl_softc *sc = &psc->sc_sc;
128193240Ssam	int rid, error = ENXIO;
129193240Ssam
130193240Ssam	sc->sc_dev = dev;
131193240Ssam
132254263Sscottl	pci_enable_busmaster(dev);
133254263Sscottl
134193240Ssam	/*
135193240Ssam	 * Setup memory-mapping of PCI registers.
136193240Ssam	 */
137193240Ssam	rid = BS_BAR0;
138193240Ssam	psc->sc_sr0 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
139193240Ssam					    RF_ACTIVE);
140193240Ssam	if (psc->sc_sr0 == NULL) {
141193240Ssam		device_printf(dev, "cannot map BAR0 register space\n");
142193240Ssam		goto bad;
143193240Ssam	}
144193240Ssam	rid = BS_BAR1;
145193240Ssam	psc->sc_sr1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
146193240Ssam					    RF_ACTIVE);
147193240Ssam	if (psc->sc_sr1 == NULL) {
148193240Ssam		device_printf(dev, "cannot map BAR1 register space\n");
149193240Ssam		goto bad1;
150193240Ssam	}
151193240Ssam	sc->sc_invalid = 1;
152193240Ssam
153193240Ssam	/*
154193240Ssam	 * Arrange interrupt line.
155193240Ssam	 */
156193240Ssam	rid = 0;
157193240Ssam	psc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
158193240Ssam					     RF_SHAREABLE|RF_ACTIVE);
159193240Ssam	if (psc->sc_irq == NULL) {
160193240Ssam		device_printf(dev, "could not map interrupt\n");
161193240Ssam		goto bad2;
162193240Ssam	}
163193240Ssam	if (bus_setup_intr(dev, psc->sc_irq,
164193240Ssam			   INTR_TYPE_NET | INTR_MPSAFE,
165193240Ssam			   NULL, mwl_intr, sc, &psc->sc_ih)) {
166193240Ssam		device_printf(dev, "could not establish interrupt\n");
167193240Ssam		goto bad3;
168193240Ssam	}
169193240Ssam
170193240Ssam	/*
171193240Ssam	 * Setup DMA descriptor area.
172193240Ssam	 */
173193240Ssam	if (bus_dma_tag_create(bus_get_dma_tag(dev),	/* parent */
174193240Ssam			       1, 0,			/* alignment, bounds */
175193240Ssam			       BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
176193240Ssam			       BUS_SPACE_MAXADDR,	/* highaddr */
177193240Ssam			       NULL, NULL,		/* filter, filterarg */
178278415Smarius			       BUS_SPACE_MAXSIZE,	/* maxsize */
179193240Ssam			       MWL_TXDESC,		/* nsegments */
180278415Smarius			       BUS_SPACE_MAXSIZE,	/* maxsegsize */
181198366Srpaulo			       0,			/* flags */
182193240Ssam			       NULL,			/* lockfunc */
183193240Ssam			       NULL,			/* lockarg */
184193240Ssam			       &sc->sc_dmat)) {
185193240Ssam		device_printf(dev, "cannot allocate DMA tag\n");
186193240Ssam		goto bad4;
187193240Ssam	}
188193240Ssam
189193240Ssam	/*
190193240Ssam	 * Finish off the attach.
191193240Ssam	 */
192193240Ssam	MWL_LOCK_INIT(sc);
193193240Ssam	sc->sc_io0t = rman_get_bustag(psc->sc_sr0);
194193240Ssam	sc->sc_io0h = rman_get_bushandle(psc->sc_sr0);
195193240Ssam	sc->sc_io1t = rman_get_bustag(psc->sc_sr1);
196193240Ssam	sc->sc_io1h = rman_get_bushandle(psc->sc_sr1);
197193240Ssam	if (mwl_attach(pci_get_device(dev), sc) == 0)
198193240Ssam		return (0);
199193240Ssam
200193240Ssam	MWL_LOCK_DESTROY(sc);
201193240Ssam	bus_dma_tag_destroy(sc->sc_dmat);
202193240Ssambad4:
203193240Ssam	bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
204193240Ssambad3:
205193240Ssam	bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
206193240Ssambad2:
207193240Ssam	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR1, psc->sc_sr1);
208193240Ssambad1:
209193240Ssam	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR0, psc->sc_sr0);
210193240Ssambad:
211193240Ssam	return (error);
212193240Ssam}
213193240Ssam
214193240Ssamstatic int
215193240Ssammwl_pci_detach(device_t dev)
216193240Ssam{
217193240Ssam	struct mwl_pci_softc *psc = device_get_softc(dev);
218193240Ssam	struct mwl_softc *sc = &psc->sc_sc;
219193240Ssam
220193240Ssam	/* check if device was removed */
221193240Ssam	sc->sc_invalid = !bus_child_present(dev);
222193240Ssam
223193240Ssam	mwl_detach(sc);
224193240Ssam
225193240Ssam	bus_generic_detach(dev);
226193240Ssam	bus_teardown_intr(dev, psc->sc_irq, psc->sc_ih);
227193240Ssam	bus_release_resource(dev, SYS_RES_IRQ, 0, psc->sc_irq);
228193240Ssam
229193240Ssam	bus_dma_tag_destroy(sc->sc_dmat);
230193240Ssam	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR1, psc->sc_sr1);
231193240Ssam	bus_release_resource(dev, SYS_RES_MEMORY, BS_BAR0, psc->sc_sr0);
232193240Ssam
233193240Ssam	MWL_LOCK_DESTROY(sc);
234193240Ssam
235193240Ssam	return (0);
236193240Ssam}
237193240Ssam
238193240Ssamstatic int
239193240Ssammwl_pci_shutdown(device_t dev)
240193240Ssam{
241193240Ssam	struct mwl_pci_softc *psc = device_get_softc(dev);
242193240Ssam
243193240Ssam	mwl_shutdown(&psc->sc_sc);
244193240Ssam	return (0);
245193240Ssam}
246193240Ssam
247193240Ssamstatic int
248193240Ssammwl_pci_suspend(device_t dev)
249193240Ssam{
250193240Ssam	struct mwl_pci_softc *psc = device_get_softc(dev);
251193240Ssam
252193240Ssam	mwl_suspend(&psc->sc_sc);
253193240Ssam
254193240Ssam	return (0);
255193240Ssam}
256193240Ssam
257193240Ssamstatic int
258193240Ssammwl_pci_resume(device_t dev)
259193240Ssam{
260193240Ssam	struct mwl_pci_softc *psc = device_get_softc(dev);
261193240Ssam
262254263Sscottl	pci_enable_busmaster(dev);
263193240Ssam
264193240Ssam	mwl_resume(&psc->sc_sc);
265193240Ssam
266193240Ssam	return (0);
267193240Ssam}
268193240Ssam
269193240Ssamstatic device_method_t mwl_pci_methods[] = {
270193240Ssam	/* Device interface */
271193240Ssam	DEVMETHOD(device_probe,		mwl_pci_probe),
272193240Ssam	DEVMETHOD(device_attach,	mwl_pci_attach),
273193240Ssam	DEVMETHOD(device_detach,	mwl_pci_detach),
274193240Ssam	DEVMETHOD(device_shutdown,	mwl_pci_shutdown),
275193240Ssam	DEVMETHOD(device_suspend,	mwl_pci_suspend),
276193240Ssam	DEVMETHOD(device_resume,	mwl_pci_resume),
277193240Ssam
278193240Ssam	{ 0,0 }
279193240Ssam};
280193240Ssamstatic driver_t mwl_pci_driver = {
281193240Ssam	"mwl",
282193240Ssam	mwl_pci_methods,
283193240Ssam	sizeof (struct mwl_pci_softc)
284193240Ssam};
285193240Ssamstatic	devclass_t mwl_devclass;
286193240SsamDRIVER_MODULE(mwl, pci, mwl_pci_driver, mwl_devclass, 0, 0);
287193240SsamMODULE_VERSION(mwl, 1);
288193240SsamMODULE_DEPEND(mwl, wlan, 1, 1, 1);		/* 802.11 media layer */
289212410SbschmidtMODULE_DEPEND(mwl, firmware, 1, 1, 1);
290