xref: /freebsd/sys/dev/rtwn/pci/rtwn_pci_attach.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
17453645fSAndriy Voskoboinyk /*	$OpenBSD: if_urtwn.c,v 1.16 2011/02/10 17:26:40 jakemsr Exp $	*/
27453645fSAndriy Voskoboinyk 
37453645fSAndriy Voskoboinyk /*-
47453645fSAndriy Voskoboinyk  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
57453645fSAndriy Voskoboinyk  * Copyright (c) 2014 Kevin Lo <kevlo@FreeBSD.org>
67453645fSAndriy Voskoboinyk  * Copyright (c) 2016 Andriy Voskoboinyk <avos@FreeBSD.org>
77453645fSAndriy Voskoboinyk  *
87453645fSAndriy Voskoboinyk  * Permission to use, copy, modify, and distribute this software for any
97453645fSAndriy Voskoboinyk  * purpose with or without fee is hereby granted, provided that the above
107453645fSAndriy Voskoboinyk  * copyright notice and this permission notice appear in all copies.
117453645fSAndriy Voskoboinyk  *
127453645fSAndriy Voskoboinyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
137453645fSAndriy Voskoboinyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
147453645fSAndriy Voskoboinyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
157453645fSAndriy Voskoboinyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
167453645fSAndriy Voskoboinyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
177453645fSAndriy Voskoboinyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
187453645fSAndriy Voskoboinyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
197453645fSAndriy Voskoboinyk  */
207453645fSAndriy Voskoboinyk 
217453645fSAndriy Voskoboinyk #include <sys/param.h>
227453645fSAndriy Voskoboinyk #include <sys/sysctl.h>
237453645fSAndriy Voskoboinyk #include <sys/lock.h>
247453645fSAndriy Voskoboinyk #include <sys/mutex.h>
257453645fSAndriy Voskoboinyk #include <sys/mbuf.h>
267453645fSAndriy Voskoboinyk #include <sys/kernel.h>
277453645fSAndriy Voskoboinyk #include <sys/socket.h>
287453645fSAndriy Voskoboinyk #include <sys/systm.h>
297453645fSAndriy Voskoboinyk #include <sys/malloc.h>
307453645fSAndriy Voskoboinyk #include <sys/module.h>
317453645fSAndriy Voskoboinyk #include <sys/bus.h>
327453645fSAndriy Voskoboinyk #include <sys/endian.h>
337453645fSAndriy Voskoboinyk #include <sys/linker.h>
347453645fSAndriy Voskoboinyk #include <sys/kdb.h>
357453645fSAndriy Voskoboinyk 
367453645fSAndriy Voskoboinyk #include <machine/bus.h>
377453645fSAndriy Voskoboinyk #include <machine/resource.h>
387453645fSAndriy Voskoboinyk #include <sys/rman.h>
397453645fSAndriy Voskoboinyk 
407453645fSAndriy Voskoboinyk #include <dev/pci/pcireg.h>
417453645fSAndriy Voskoboinyk #include <dev/pci/pcivar.h>
427453645fSAndriy Voskoboinyk 
437453645fSAndriy Voskoboinyk #include <net/if.h>
447453645fSAndriy Voskoboinyk #include <net/ethernet.h>
457453645fSAndriy Voskoboinyk #include <net/if_media.h>
467453645fSAndriy Voskoboinyk 
477453645fSAndriy Voskoboinyk #include <net80211/ieee80211_var.h>
487453645fSAndriy Voskoboinyk 
49519e6c0fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnreg.h>
507453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwnvar.h>
517453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_nop.h>
527453645fSAndriy Voskoboinyk #include <dev/rtwn/if_rtwn_debug.h>
537453645fSAndriy Voskoboinyk 
547453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_var.h>
557453645fSAndriy Voskoboinyk 
567453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_attach.h>
577453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_reg.h>
587453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_rx.h>
597453645fSAndriy Voskoboinyk #include <dev/rtwn/pci/rtwn_pci_tx.h>
607453645fSAndriy Voskoboinyk 
617453645fSAndriy Voskoboinyk #include <dev/rtwn/rtl8192c/pci/r92ce_reg.h>
627453645fSAndriy Voskoboinyk 
637453645fSAndriy Voskoboinyk static device_probe_t	rtwn_pci_probe;
647453645fSAndriy Voskoboinyk static device_attach_t	rtwn_pci_attach;
657453645fSAndriy Voskoboinyk static device_detach_t	rtwn_pci_detach;
667453645fSAndriy Voskoboinyk static device_shutdown_t rtwn_pci_shutdown;
677453645fSAndriy Voskoboinyk static device_suspend_t	rtwn_pci_suspend;
687453645fSAndriy Voskoboinyk static device_resume_t	rtwn_pci_resume;
697453645fSAndriy Voskoboinyk 
707453645fSAndriy Voskoboinyk static int	rtwn_pci_alloc_rx_list(struct rtwn_softc *);
717453645fSAndriy Voskoboinyk static void	rtwn_pci_reset_rx_list(struct rtwn_softc *);
727453645fSAndriy Voskoboinyk static void	rtwn_pci_free_rx_list(struct rtwn_softc *);
737453645fSAndriy Voskoboinyk static int	rtwn_pci_alloc_tx_list(struct rtwn_softc *, int);
74519e6c0fSAndriy Voskoboinyk static void	rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *, int);
75519e6c0fSAndriy Voskoboinyk static void	rtwn_pci_reset_beacon_ring(struct rtwn_softc *, int);
767453645fSAndriy Voskoboinyk static void	rtwn_pci_reset_tx_list(struct rtwn_softc *,
777453645fSAndriy Voskoboinyk 		    struct ieee80211vap *, int);
787453645fSAndriy Voskoboinyk static void	rtwn_pci_free_tx_list(struct rtwn_softc *, int);
797453645fSAndriy Voskoboinyk static void	rtwn_pci_reset_lists(struct rtwn_softc *,
807453645fSAndriy Voskoboinyk 		    struct ieee80211vap *);
817453645fSAndriy Voskoboinyk static int	rtwn_pci_fw_write_block(struct rtwn_softc *,
827453645fSAndriy Voskoboinyk 		    const uint8_t *, uint16_t, int);
837453645fSAndriy Voskoboinyk static uint16_t	rtwn_pci_get_qmap(struct rtwn_softc *);
847453645fSAndriy Voskoboinyk static void	rtwn_pci_set_desc_addr(struct rtwn_softc *);
85d067ef0fSAndriy Voskoboinyk static void	rtwn_pci_beacon_update_begin(struct rtwn_softc *,
86d067ef0fSAndriy Voskoboinyk 		    struct ieee80211vap *);
87d067ef0fSAndriy Voskoboinyk static void	rtwn_pci_beacon_update_end(struct rtwn_softc *,
88d067ef0fSAndriy Voskoboinyk 		    struct ieee80211vap *);
897453645fSAndriy Voskoboinyk static void	rtwn_pci_attach_methods(struct rtwn_softc *);
907453645fSAndriy Voskoboinyk 
91ae60d856SAndriy Voskoboinyk static const struct rtwn_pci_ident *
rtwn_pci_probe_sub(device_t dev)92ae60d856SAndriy Voskoboinyk rtwn_pci_probe_sub(device_t dev)
93ae60d856SAndriy Voskoboinyk {
9428180b62SHiroki Sato 	int i, vendor_id, device_id;
95ae60d856SAndriy Voskoboinyk 
96ae60d856SAndriy Voskoboinyk 	vendor_id = pci_get_vendor(dev);
97ae60d856SAndriy Voskoboinyk 	device_id = pci_get_device(dev);
98ae60d856SAndriy Voskoboinyk 
9928180b62SHiroki Sato 	for (i = 0; i < nitems(rtwn_pci_ident_table); i++) {
10028180b62SHiroki Sato 		if (vendor_id == rtwn_pci_ident_table[i].vendor &&
10128180b62SHiroki Sato 		    device_id == rtwn_pci_ident_table[i].device)
10228180b62SHiroki Sato 			return (&rtwn_pci_ident_table[i]);
10328180b62SHiroki Sato 	}
104ae60d856SAndriy Voskoboinyk 
105ae60d856SAndriy Voskoboinyk 	return (NULL);
106ae60d856SAndriy Voskoboinyk }
1077453645fSAndriy Voskoboinyk 
1087453645fSAndriy Voskoboinyk static int
rtwn_pci_probe(device_t dev)1097453645fSAndriy Voskoboinyk rtwn_pci_probe(device_t dev)
1107453645fSAndriy Voskoboinyk {
1117453645fSAndriy Voskoboinyk 	const struct rtwn_pci_ident *ident;
1127453645fSAndriy Voskoboinyk 
113ae60d856SAndriy Voskoboinyk 	ident = rtwn_pci_probe_sub(dev);
114ae60d856SAndriy Voskoboinyk 	if (ident != NULL) {
1157453645fSAndriy Voskoboinyk 		device_set_desc(dev, ident->name);
1167453645fSAndriy Voskoboinyk 		return (BUS_PROBE_DEFAULT);
1177453645fSAndriy Voskoboinyk 	}
1187453645fSAndriy Voskoboinyk 	return (ENXIO);
1197453645fSAndriy Voskoboinyk }
1207453645fSAndriy Voskoboinyk 
1217453645fSAndriy Voskoboinyk static int
rtwn_pci_alloc_rx_list(struct rtwn_softc * sc)1227453645fSAndriy Voskoboinyk rtwn_pci_alloc_rx_list(struct rtwn_softc *sc)
1237453645fSAndriy Voskoboinyk {
1247453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
1257453645fSAndriy Voskoboinyk 	struct rtwn_rx_ring *rx_ring = &pc->rx_ring;
1267453645fSAndriy Voskoboinyk 	struct rtwn_rx_data *rx_data;
1277453645fSAndriy Voskoboinyk 	bus_size_t size;
1287453645fSAndriy Voskoboinyk 	int i, error;
1297453645fSAndriy Voskoboinyk 
1307453645fSAndriy Voskoboinyk 	/* Allocate Rx descriptors. */
131c5ad99fcSAndriy Voskoboinyk 	size = sizeof(struct rtwn_rx_stat_pci) * RTWN_PCI_RX_LIST_COUNT;
1327453645fSAndriy Voskoboinyk 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
1337453645fSAndriy Voskoboinyk 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
1347453645fSAndriy Voskoboinyk 	    size, 1, size, 0, NULL, NULL, &rx_ring->desc_dmat);
1357453645fSAndriy Voskoboinyk 	if (error != 0) {
1367453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not create rx desc DMA tag\n");
1377453645fSAndriy Voskoboinyk 		goto fail;
1387453645fSAndriy Voskoboinyk 	}
1397453645fSAndriy Voskoboinyk 
1407453645fSAndriy Voskoboinyk 	error = bus_dmamem_alloc(rx_ring->desc_dmat, (void **)&rx_ring->desc,
1417453645fSAndriy Voskoboinyk 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT,
1427453645fSAndriy Voskoboinyk 	    &rx_ring->desc_map);
1437453645fSAndriy Voskoboinyk 	if (error != 0) {
1447453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not allocate rx desc\n");
1457453645fSAndriy Voskoboinyk 		goto fail;
1467453645fSAndriy Voskoboinyk 	}
1477453645fSAndriy Voskoboinyk 	error = bus_dmamap_load(rx_ring->desc_dmat, rx_ring->desc_map,
1487453645fSAndriy Voskoboinyk 	    rx_ring->desc, size, rtwn_pci_dma_map_addr, &rx_ring->paddr, 0);
1497453645fSAndriy Voskoboinyk 	if (error != 0) {
1507453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not load rx desc DMA map\n");
1517453645fSAndriy Voskoboinyk 		goto fail;
1527453645fSAndriy Voskoboinyk 	}
1537453645fSAndriy Voskoboinyk 	bus_dmamap_sync(rx_ring->desc_dmat, rx_ring->desc_map,
1547453645fSAndriy Voskoboinyk 	    BUS_DMASYNC_PREWRITE);
1557453645fSAndriy Voskoboinyk 
1567453645fSAndriy Voskoboinyk 	/* Create RX buffer DMA tag. */
1577453645fSAndriy Voskoboinyk 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
158a14f9888SAndriy Voskoboinyk 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
159a14f9888SAndriy Voskoboinyk 	    MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &rx_ring->data_dmat);
1607453645fSAndriy Voskoboinyk 	if (error != 0) {
1617453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not create rx buf DMA tag\n");
1627453645fSAndriy Voskoboinyk 		goto fail;
1637453645fSAndriy Voskoboinyk 	}
1647453645fSAndriy Voskoboinyk 
1657453645fSAndriy Voskoboinyk 	/* Allocate Rx buffers. */
1667453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) {
1677453645fSAndriy Voskoboinyk 		rx_data = &rx_ring->rx_data[i];
1687453645fSAndriy Voskoboinyk 		error = bus_dmamap_create(rx_ring->data_dmat, 0, &rx_data->map);
1697453645fSAndriy Voskoboinyk 		if (error != 0) {
1707453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1717453645fSAndriy Voskoboinyk 			    "could not create rx buf DMA map\n");
1727453645fSAndriy Voskoboinyk 			goto fail;
1737453645fSAndriy Voskoboinyk 		}
1747453645fSAndriy Voskoboinyk 
175a14f9888SAndriy Voskoboinyk 		rx_data->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
176a14f9888SAndriy Voskoboinyk 		    MJUMPAGESIZE);
1777453645fSAndriy Voskoboinyk 		if (rx_data->m == NULL) {
1787453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1797453645fSAndriy Voskoboinyk 			    "could not allocate rx mbuf\n");
1807453645fSAndriy Voskoboinyk 			error = ENOMEM;
1817453645fSAndriy Voskoboinyk 			goto fail;
1827453645fSAndriy Voskoboinyk 		}
1837453645fSAndriy Voskoboinyk 
1847453645fSAndriy Voskoboinyk 		error = bus_dmamap_load(rx_ring->data_dmat, rx_data->map,
185a14f9888SAndriy Voskoboinyk 		    mtod(rx_data->m, void *), MJUMPAGESIZE,
186a14f9888SAndriy Voskoboinyk 		    rtwn_pci_dma_map_addr, &rx_data->paddr, BUS_DMA_NOWAIT);
1877453645fSAndriy Voskoboinyk 		if (error != 0) {
1887453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
1897453645fSAndriy Voskoboinyk 			    "could not load rx buf DMA map");
1907453645fSAndriy Voskoboinyk 			goto fail;
1917453645fSAndriy Voskoboinyk 		}
1927453645fSAndriy Voskoboinyk 
1937453645fSAndriy Voskoboinyk 		rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i], rx_data->paddr,
194a14f9888SAndriy Voskoboinyk 		    MJUMPAGESIZE, i);
1957453645fSAndriy Voskoboinyk 	}
1967453645fSAndriy Voskoboinyk 	rx_ring->cur = 0;
1977453645fSAndriy Voskoboinyk 
1987453645fSAndriy Voskoboinyk 	return (0);
1997453645fSAndriy Voskoboinyk 
2007453645fSAndriy Voskoboinyk fail:
2017453645fSAndriy Voskoboinyk 	rtwn_pci_free_rx_list(sc);
2027453645fSAndriy Voskoboinyk 	return (error);
2037453645fSAndriy Voskoboinyk }
2047453645fSAndriy Voskoboinyk 
2057453645fSAndriy Voskoboinyk static void
rtwn_pci_reset_rx_list(struct rtwn_softc * sc)2067453645fSAndriy Voskoboinyk rtwn_pci_reset_rx_list(struct rtwn_softc *sc)
2077453645fSAndriy Voskoboinyk {
2087453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
2097453645fSAndriy Voskoboinyk 	struct rtwn_rx_ring *rx_ring = &pc->rx_ring;
2107453645fSAndriy Voskoboinyk 	struct rtwn_rx_data *rx_data;
2117453645fSAndriy Voskoboinyk 	int i;
2127453645fSAndriy Voskoboinyk 
2137453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) {
2147453645fSAndriy Voskoboinyk 		rx_data = &rx_ring->rx_data[i];
2157453645fSAndriy Voskoboinyk 		rtwn_pci_setup_rx_desc(pc, &rx_ring->desc[i],
216a14f9888SAndriy Voskoboinyk 		    rx_data->paddr, MJUMPAGESIZE, i);
2177453645fSAndriy Voskoboinyk 	}
2187453645fSAndriy Voskoboinyk 	rx_ring->cur = 0;
2197453645fSAndriy Voskoboinyk }
2207453645fSAndriy Voskoboinyk 
2217453645fSAndriy Voskoboinyk static void
rtwn_pci_free_rx_list(struct rtwn_softc * sc)2227453645fSAndriy Voskoboinyk rtwn_pci_free_rx_list(struct rtwn_softc *sc)
2237453645fSAndriy Voskoboinyk {
2247453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
2257453645fSAndriy Voskoboinyk 	struct rtwn_rx_ring *rx_ring = &pc->rx_ring;
2267453645fSAndriy Voskoboinyk 	struct rtwn_rx_data *rx_data;
2277453645fSAndriy Voskoboinyk 	int i;
2287453645fSAndriy Voskoboinyk 
2297453645fSAndriy Voskoboinyk 	if (rx_ring->desc_dmat != NULL) {
2307453645fSAndriy Voskoboinyk 		if (rx_ring->desc != NULL) {
2317453645fSAndriy Voskoboinyk 			bus_dmamap_sync(rx_ring->desc_dmat,
2327453645fSAndriy Voskoboinyk 			    rx_ring->desc_map,
2337453645fSAndriy Voskoboinyk 			    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2347453645fSAndriy Voskoboinyk 			bus_dmamap_unload(rx_ring->desc_dmat,
2357453645fSAndriy Voskoboinyk 			    rx_ring->desc_map);
2367453645fSAndriy Voskoboinyk 			bus_dmamem_free(rx_ring->desc_dmat, rx_ring->desc,
2377453645fSAndriy Voskoboinyk 			    rx_ring->desc_map);
2387453645fSAndriy Voskoboinyk 			rx_ring->desc = NULL;
2397453645fSAndriy Voskoboinyk 		}
2407453645fSAndriy Voskoboinyk 		bus_dma_tag_destroy(rx_ring->desc_dmat);
2417453645fSAndriy Voskoboinyk 		rx_ring->desc_dmat = NULL;
2427453645fSAndriy Voskoboinyk 	}
2437453645fSAndriy Voskoboinyk 
2447453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_RX_LIST_COUNT; i++) {
2457453645fSAndriy Voskoboinyk 		rx_data = &rx_ring->rx_data[i];
2467453645fSAndriy Voskoboinyk 
2477453645fSAndriy Voskoboinyk 		if (rx_data->m != NULL) {
2487453645fSAndriy Voskoboinyk 			bus_dmamap_sync(rx_ring->data_dmat,
2497453645fSAndriy Voskoboinyk 			    rx_data->map, BUS_DMASYNC_POSTREAD);
2507453645fSAndriy Voskoboinyk 			bus_dmamap_unload(rx_ring->data_dmat, rx_data->map);
2517453645fSAndriy Voskoboinyk 			m_freem(rx_data->m);
2527453645fSAndriy Voskoboinyk 			rx_data->m = NULL;
2537453645fSAndriy Voskoboinyk 		}
2547453645fSAndriy Voskoboinyk 		bus_dmamap_destroy(rx_ring->data_dmat, rx_data->map);
2557453645fSAndriy Voskoboinyk 		rx_data->map = NULL;
2567453645fSAndriy Voskoboinyk 	}
2577453645fSAndriy Voskoboinyk 	if (rx_ring->data_dmat != NULL) {
2587453645fSAndriy Voskoboinyk 		bus_dma_tag_destroy(rx_ring->data_dmat);
2597453645fSAndriy Voskoboinyk 		rx_ring->data_dmat = NULL;
2607453645fSAndriy Voskoboinyk 	}
2617453645fSAndriy Voskoboinyk }
2627453645fSAndriy Voskoboinyk 
2637453645fSAndriy Voskoboinyk static int
rtwn_pci_alloc_tx_list(struct rtwn_softc * sc,int qid)2647453645fSAndriy Voskoboinyk rtwn_pci_alloc_tx_list(struct rtwn_softc *sc, int qid)
2657453645fSAndriy Voskoboinyk {
2667453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
2677453645fSAndriy Voskoboinyk 	struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid];
2687453645fSAndriy Voskoboinyk 	bus_size_t size;
2697453645fSAndriy Voskoboinyk 	int i, error;
2707453645fSAndriy Voskoboinyk 
2717453645fSAndriy Voskoboinyk 	size = sc->txdesc_len * RTWN_PCI_TX_LIST_COUNT;
2727453645fSAndriy Voskoboinyk 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), PAGE_SIZE, 0,
2737453645fSAndriy Voskoboinyk 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
2747453645fSAndriy Voskoboinyk 	    size, 1, size, 0, NULL, NULL, &tx_ring->desc_dmat);
2757453645fSAndriy Voskoboinyk 	if (error != 0) {
2767453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not create tx ring DMA tag\n");
2777453645fSAndriy Voskoboinyk 		goto fail;
2787453645fSAndriy Voskoboinyk 	}
2797453645fSAndriy Voskoboinyk 
2807453645fSAndriy Voskoboinyk 	error = bus_dmamem_alloc(tx_ring->desc_dmat, &tx_ring->desc,
2817453645fSAndriy Voskoboinyk 	    BUS_DMA_NOWAIT | BUS_DMA_ZERO, &tx_ring->desc_map);
2827453645fSAndriy Voskoboinyk 	if (error != 0) {
2837453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "can't map tx ring DMA memory\n");
2847453645fSAndriy Voskoboinyk 		goto fail;
2857453645fSAndriy Voskoboinyk 	}
2867453645fSAndriy Voskoboinyk 	error = bus_dmamap_load(tx_ring->desc_dmat, tx_ring->desc_map,
2877453645fSAndriy Voskoboinyk 	    tx_ring->desc, size, rtwn_pci_dma_map_addr, &tx_ring->paddr,
2887453645fSAndriy Voskoboinyk 	    BUS_DMA_NOWAIT);
2897453645fSAndriy Voskoboinyk 	if (error != 0) {
2907453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not load desc DMA map\n");
2917453645fSAndriy Voskoboinyk 		goto fail;
2927453645fSAndriy Voskoboinyk 	}
2937453645fSAndriy Voskoboinyk 	bus_dmamap_sync(tx_ring->desc_dmat, tx_ring->desc_map,
2947453645fSAndriy Voskoboinyk 	    BUS_DMASYNC_PREWRITE);
2957453645fSAndriy Voskoboinyk 
2967453645fSAndriy Voskoboinyk 	error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0,
297a14f9888SAndriy Voskoboinyk 	    BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
298a14f9888SAndriy Voskoboinyk 	    MJUMPAGESIZE, 1, MJUMPAGESIZE, 0, NULL, NULL, &tx_ring->data_dmat);
2997453645fSAndriy Voskoboinyk 	if (error != 0) {
3007453645fSAndriy Voskoboinyk 		device_printf(sc->sc_dev, "could not create tx buf DMA tag\n");
3017453645fSAndriy Voskoboinyk 		goto fail;
3027453645fSAndriy Voskoboinyk 	}
3037453645fSAndriy Voskoboinyk 
3047453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) {
3057453645fSAndriy Voskoboinyk 		struct rtwn_tx_data *tx_data = &tx_ring->tx_data[i];
3067453645fSAndriy Voskoboinyk 		void *tx_desc = (uint8_t *)tx_ring->desc + sc->txdesc_len * i;
3077453645fSAndriy Voskoboinyk 		uint32_t next_desc_addr = tx_ring->paddr +
3087453645fSAndriy Voskoboinyk 		    sc->txdesc_len * ((i + 1) % RTWN_PCI_TX_LIST_COUNT);
3097453645fSAndriy Voskoboinyk 
3107453645fSAndriy Voskoboinyk 		rtwn_pci_setup_tx_desc(pc, tx_desc, next_desc_addr);
3117453645fSAndriy Voskoboinyk 
3127453645fSAndriy Voskoboinyk 		error = bus_dmamap_create(tx_ring->data_dmat, 0, &tx_data->map);
3137453645fSAndriy Voskoboinyk 		if (error != 0) {
3147453645fSAndriy Voskoboinyk 			device_printf(sc->sc_dev,
3157453645fSAndriy Voskoboinyk 			    "could not create tx buf DMA map\n");
3167453645fSAndriy Voskoboinyk 			return (error);
3177453645fSAndriy Voskoboinyk 		}
3187453645fSAndriy Voskoboinyk 		tx_data->m = NULL;
3197453645fSAndriy Voskoboinyk 		tx_data->ni = NULL;
3207453645fSAndriy Voskoboinyk 	}
3217453645fSAndriy Voskoboinyk 	return (0);
3227453645fSAndriy Voskoboinyk 
3237453645fSAndriy Voskoboinyk fail:
3247453645fSAndriy Voskoboinyk 	rtwn_pci_free_tx_list(sc, qid);
3257453645fSAndriy Voskoboinyk 	return (error);
3267453645fSAndriy Voskoboinyk }
3277453645fSAndriy Voskoboinyk 
3287453645fSAndriy Voskoboinyk static void
rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc * sc,int qid)329519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_ring_stopped(struct rtwn_softc *sc, int qid)
3307453645fSAndriy Voskoboinyk {
3317453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
332519e6c0fSAndriy Voskoboinyk 	struct rtwn_tx_ring *ring = &pc->tx_ring[qid];
333519e6c0fSAndriy Voskoboinyk 	int i;
3347453645fSAndriy Voskoboinyk 
3357453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) {
336519e6c0fSAndriy Voskoboinyk 		struct rtwn_tx_data *data = &ring->tx_data[i];
337519e6c0fSAndriy Voskoboinyk 		void *desc = (uint8_t *)ring->desc + sc->txdesc_len * i;
3387453645fSAndriy Voskoboinyk 
339519e6c0fSAndriy Voskoboinyk 		rtwn_pci_copy_tx_desc(pc, desc, NULL);
3407453645fSAndriy Voskoboinyk 
341519e6c0fSAndriy Voskoboinyk 		if (data->m != NULL) {
342519e6c0fSAndriy Voskoboinyk 			bus_dmamap_sync(ring->data_dmat, data->map,
343519e6c0fSAndriy Voskoboinyk 			    BUS_DMASYNC_POSTWRITE);
344519e6c0fSAndriy Voskoboinyk 			bus_dmamap_unload(ring->data_dmat, data->map);
345519e6c0fSAndriy Voskoboinyk 			m_freem(data->m);
346519e6c0fSAndriy Voskoboinyk 			data->m = NULL;
3477453645fSAndriy Voskoboinyk 		}
348519e6c0fSAndriy Voskoboinyk 		if (data->ni != NULL) {
349519e6c0fSAndriy Voskoboinyk 			ieee80211_free_node(data->ni);
350519e6c0fSAndriy Voskoboinyk 			data->ni = NULL;
3517453645fSAndriy Voskoboinyk 		}
3527453645fSAndriy Voskoboinyk 	}
3537453645fSAndriy Voskoboinyk 
354519e6c0fSAndriy Voskoboinyk 	bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
3557453645fSAndriy Voskoboinyk 	    BUS_DMASYNC_POSTWRITE);
3567453645fSAndriy Voskoboinyk 
3577453645fSAndriy Voskoboinyk 	sc->qfullmsk &= ~(1 << qid);
358519e6c0fSAndriy Voskoboinyk 	ring->queued = 0;
359519e6c0fSAndriy Voskoboinyk 	ring->last = ring->cur = 0;
360519e6c0fSAndriy Voskoboinyk }
361519e6c0fSAndriy Voskoboinyk 
362519e6c0fSAndriy Voskoboinyk /*
363519e6c0fSAndriy Voskoboinyk  * Clear entry 0 (or 1) in the beacon queue (other are not used).
364519e6c0fSAndriy Voskoboinyk  */
365519e6c0fSAndriy Voskoboinyk static void
rtwn_pci_reset_beacon_ring(struct rtwn_softc * sc,int id)366519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_beacon_ring(struct rtwn_softc *sc, int id)
367519e6c0fSAndriy Voskoboinyk {
368519e6c0fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
369519e6c0fSAndriy Voskoboinyk 	struct rtwn_tx_ring *ring = &pc->tx_ring[RTWN_PCI_BEACON_QUEUE];
370519e6c0fSAndriy Voskoboinyk 	struct rtwn_tx_data *data = &ring->tx_data[id];
371519e6c0fSAndriy Voskoboinyk 	struct rtwn_tx_desc_common *txd = (struct rtwn_tx_desc_common *)
372519e6c0fSAndriy Voskoboinyk 	    ((uint8_t *)ring->desc + id * sc->txdesc_len);
373519e6c0fSAndriy Voskoboinyk 
374519e6c0fSAndriy Voskoboinyk 	bus_dmamap_sync(ring->desc_dmat, ring->desc_map, BUS_DMASYNC_POSTREAD);
375519e6c0fSAndriy Voskoboinyk 	if (txd->flags0 & RTWN_FLAGS0_OWN) {
376519e6c0fSAndriy Voskoboinyk 		/* Clear OWN bit. */
377519e6c0fSAndriy Voskoboinyk 		txd->flags0 &= ~RTWN_FLAGS0_OWN;
378519e6c0fSAndriy Voskoboinyk 		bus_dmamap_sync(ring->desc_dmat, ring->desc_map,
379519e6c0fSAndriy Voskoboinyk 		    BUS_DMASYNC_PREWRITE);
380519e6c0fSAndriy Voskoboinyk 
381519e6c0fSAndriy Voskoboinyk 		/* Unload mbuf. */
382519e6c0fSAndriy Voskoboinyk 		bus_dmamap_sync(ring->data_dmat, data->map,
383519e6c0fSAndriy Voskoboinyk 		    BUS_DMASYNC_POSTWRITE);
384519e6c0fSAndriy Voskoboinyk 		bus_dmamap_unload(ring->data_dmat, data->map);
385519e6c0fSAndriy Voskoboinyk 	}
386519e6c0fSAndriy Voskoboinyk }
387519e6c0fSAndriy Voskoboinyk 
388519e6c0fSAndriy Voskoboinyk /*
389519e6c0fSAndriy Voskoboinyk  * Drop stale entries from Tx ring before the vap will be deleted.
390519e6c0fSAndriy Voskoboinyk  * In case if vap is NULL just free everything and reset cur / last pointers.
391519e6c0fSAndriy Voskoboinyk  */
392519e6c0fSAndriy Voskoboinyk static void
rtwn_pci_reset_tx_list(struct rtwn_softc * sc,struct ieee80211vap * vap,int qid)393519e6c0fSAndriy Voskoboinyk rtwn_pci_reset_tx_list(struct rtwn_softc *sc, struct ieee80211vap *vap,
394519e6c0fSAndriy Voskoboinyk     int qid)
395519e6c0fSAndriy Voskoboinyk {
396519e6c0fSAndriy Voskoboinyk 	int i;
397519e6c0fSAndriy Voskoboinyk 
398519e6c0fSAndriy Voskoboinyk 	if (vap == NULL) {
399519e6c0fSAndriy Voskoboinyk 		if (qid != RTWN_PCI_BEACON_QUEUE) {
400519e6c0fSAndriy Voskoboinyk 			/*
401519e6c0fSAndriy Voskoboinyk 			 * Device was stopped; just clear all entries.
402519e6c0fSAndriy Voskoboinyk 			 */
403519e6c0fSAndriy Voskoboinyk 			rtwn_pci_reset_tx_ring_stopped(sc, qid);
404519e6c0fSAndriy Voskoboinyk 		} else {
405519e6c0fSAndriy Voskoboinyk 			for (i = 0; i < RTWN_PORT_COUNT; i++)
406519e6c0fSAndriy Voskoboinyk 				rtwn_pci_reset_beacon_ring(sc, i);
407519e6c0fSAndriy Voskoboinyk 		}
408519e6c0fSAndriy Voskoboinyk 	} else if (qid == RTWN_PCI_BEACON_QUEUE &&
409519e6c0fSAndriy Voskoboinyk 		   (vap->iv_opmode == IEEE80211_M_HOSTAP ||
410519e6c0fSAndriy Voskoboinyk 		    vap->iv_opmode == IEEE80211_M_IBSS)) {
411519e6c0fSAndriy Voskoboinyk 		struct rtwn_vap *uvp = RTWN_VAP(vap);
412519e6c0fSAndriy Voskoboinyk 
413519e6c0fSAndriy Voskoboinyk 		rtwn_pci_reset_beacon_ring(sc, uvp->id);
414519e6c0fSAndriy Voskoboinyk 	} else {
415519e6c0fSAndriy Voskoboinyk 		struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
416519e6c0fSAndriy Voskoboinyk 		struct rtwn_tx_ring *ring = &pc->tx_ring[qid];
417519e6c0fSAndriy Voskoboinyk 
418519e6c0fSAndriy Voskoboinyk 		for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) {
419519e6c0fSAndriy Voskoboinyk 			struct rtwn_tx_data *data = &ring->tx_data[i];
420519e6c0fSAndriy Voskoboinyk 			if (data->ni != NULL && data->ni->ni_vap == vap) {
421519e6c0fSAndriy Voskoboinyk 				/*
422519e6c0fSAndriy Voskoboinyk 				 * NB: if some vap is still running
423519e6c0fSAndriy Voskoboinyk 				 * rtwn_pci_tx_done() will free the mbuf;
424519e6c0fSAndriy Voskoboinyk 				 * otherwise, rtwn_stop() will reset all rings
425519e6c0fSAndriy Voskoboinyk 				 * after device shutdown.
426519e6c0fSAndriy Voskoboinyk 				 */
427519e6c0fSAndriy Voskoboinyk 				ieee80211_free_node(data->ni);
428519e6c0fSAndriy Voskoboinyk 				data->ni = NULL;
429519e6c0fSAndriy Voskoboinyk 			}
430519e6c0fSAndriy Voskoboinyk 		}
431519e6c0fSAndriy Voskoboinyk 	}
4327453645fSAndriy Voskoboinyk }
4337453645fSAndriy Voskoboinyk 
4347453645fSAndriy Voskoboinyk static void
rtwn_pci_free_tx_list(struct rtwn_softc * sc,int qid)4357453645fSAndriy Voskoboinyk rtwn_pci_free_tx_list(struct rtwn_softc *sc, int qid)
4367453645fSAndriy Voskoboinyk {
4377453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
4387453645fSAndriy Voskoboinyk 	struct rtwn_tx_ring *tx_ring = &pc->tx_ring[qid];
4397453645fSAndriy Voskoboinyk 	struct rtwn_tx_data *tx_data;
4407453645fSAndriy Voskoboinyk 	int i;
4417453645fSAndriy Voskoboinyk 
4427453645fSAndriy Voskoboinyk 	if (tx_ring->desc_dmat != NULL) {
4437453645fSAndriy Voskoboinyk 		if (tx_ring->desc != NULL) {
4447453645fSAndriy Voskoboinyk 			bus_dmamap_sync(tx_ring->desc_dmat,
4457453645fSAndriy Voskoboinyk 			    tx_ring->desc_map, BUS_DMASYNC_POSTWRITE);
4467453645fSAndriy Voskoboinyk 			bus_dmamap_unload(tx_ring->desc_dmat,
4477453645fSAndriy Voskoboinyk 			    tx_ring->desc_map);
4487453645fSAndriy Voskoboinyk 			bus_dmamem_free(tx_ring->desc_dmat, tx_ring->desc,
4497453645fSAndriy Voskoboinyk 			    tx_ring->desc_map);
4507453645fSAndriy Voskoboinyk 		}
4517453645fSAndriy Voskoboinyk 		bus_dma_tag_destroy(tx_ring->desc_dmat);
4527453645fSAndriy Voskoboinyk 	}
4537453645fSAndriy Voskoboinyk 
4547453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_TX_LIST_COUNT; i++) {
4557453645fSAndriy Voskoboinyk 		tx_data = &tx_ring->tx_data[i];
4567453645fSAndriy Voskoboinyk 
4577453645fSAndriy Voskoboinyk 		if (tx_data->m != NULL) {
4587453645fSAndriy Voskoboinyk 			bus_dmamap_sync(tx_ring->data_dmat, tx_data->map,
4597453645fSAndriy Voskoboinyk 			    BUS_DMASYNC_POSTWRITE);
4607453645fSAndriy Voskoboinyk 			bus_dmamap_unload(tx_ring->data_dmat, tx_data->map);
4617453645fSAndriy Voskoboinyk 			m_freem(tx_data->m);
4627453645fSAndriy Voskoboinyk 			tx_data->m = NULL;
4637453645fSAndriy Voskoboinyk 		}
4647453645fSAndriy Voskoboinyk 	}
4657453645fSAndriy Voskoboinyk 	if (tx_ring->data_dmat != NULL) {
4667453645fSAndriy Voskoboinyk 		bus_dma_tag_destroy(tx_ring->data_dmat);
4677453645fSAndriy Voskoboinyk 		tx_ring->data_dmat = NULL;
4687453645fSAndriy Voskoboinyk 	}
4697453645fSAndriy Voskoboinyk 
4707453645fSAndriy Voskoboinyk 	sc->qfullmsk &= ~(1 << qid);
4717453645fSAndriy Voskoboinyk 	tx_ring->queued = 0;
4727453645fSAndriy Voskoboinyk 	tx_ring->last = tx_ring->cur = 0;
4737453645fSAndriy Voskoboinyk }
4747453645fSAndriy Voskoboinyk 
4757453645fSAndriy Voskoboinyk static void
rtwn_pci_reset_lists(struct rtwn_softc * sc,struct ieee80211vap * vap)4767453645fSAndriy Voskoboinyk rtwn_pci_reset_lists(struct rtwn_softc *sc, struct ieee80211vap *vap)
4777453645fSAndriy Voskoboinyk {
4787453645fSAndriy Voskoboinyk 	int i;
4797453645fSAndriy Voskoboinyk 
4807453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_NTXQUEUES; i++)
4817453645fSAndriy Voskoboinyk 		rtwn_pci_reset_tx_list(sc, vap, i);
4827453645fSAndriy Voskoboinyk 
4837453645fSAndriy Voskoboinyk 	if (vap == NULL) {
4847453645fSAndriy Voskoboinyk 		sc->qfullmsk = 0;
4857453645fSAndriy Voskoboinyk 		rtwn_pci_reset_rx_list(sc);
4867453645fSAndriy Voskoboinyk 	}
4877453645fSAndriy Voskoboinyk }
4887453645fSAndriy Voskoboinyk 
4897453645fSAndriy Voskoboinyk static int
rtwn_pci_fw_write_block(struct rtwn_softc * sc,const uint8_t * buf,uint16_t reg,int mlen)4907453645fSAndriy Voskoboinyk rtwn_pci_fw_write_block(struct rtwn_softc *sc, const uint8_t *buf,
4917453645fSAndriy Voskoboinyk     uint16_t reg, int mlen)
4927453645fSAndriy Voskoboinyk {
4937453645fSAndriy Voskoboinyk 	int i;
4947453645fSAndriy Voskoboinyk 
4957453645fSAndriy Voskoboinyk 	for (i = 0; i < mlen; i++)
4967453645fSAndriy Voskoboinyk 		rtwn_pci_write_1(sc, reg++, buf[i]);
4977453645fSAndriy Voskoboinyk 
4987453645fSAndriy Voskoboinyk 	/* NB: cannot fail */
4997453645fSAndriy Voskoboinyk 	return (0);
5007453645fSAndriy Voskoboinyk }
5017453645fSAndriy Voskoboinyk 
5027453645fSAndriy Voskoboinyk static uint16_t
rtwn_pci_get_qmap(struct rtwn_softc * sc)5037453645fSAndriy Voskoboinyk rtwn_pci_get_qmap(struct rtwn_softc *sc)
5047453645fSAndriy Voskoboinyk {
5057453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
5067453645fSAndriy Voskoboinyk 
5077453645fSAndriy Voskoboinyk 	KASSERT(pc->pc_qmap != 0, ("%s: qmap is not set!\n", __func__));
5087453645fSAndriy Voskoboinyk 
5097453645fSAndriy Voskoboinyk 	return (pc->pc_qmap);
5107453645fSAndriy Voskoboinyk }
5117453645fSAndriy Voskoboinyk 
5127453645fSAndriy Voskoboinyk static void
rtwn_pci_set_desc_addr(struct rtwn_softc * sc)5137453645fSAndriy Voskoboinyk rtwn_pci_set_desc_addr(struct rtwn_softc *sc)
5147453645fSAndriy Voskoboinyk {
5157453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = RTWN_PCI_SOFTC(sc);
5167453645fSAndriy Voskoboinyk 
5177453645fSAndriy Voskoboinyk 	RTWN_DPRINTF(sc, RTWN_DEBUG_RESET, "%s: addresses:\n"
5187453645fSAndriy Voskoboinyk 	    "bk: %08jX, be: %08jX, vi: %08jX, vo: %08jX\n"
5197453645fSAndriy Voskoboinyk 	    "bcn: %08jX, mgt: %08jX, high: %08jX, rx: %08jX\n",
5207453645fSAndriy Voskoboinyk 	    __func__, (uintmax_t)pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr,
5217453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr,
5227453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr,
5237453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr,
5247453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr,
5257453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr,
5267453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr,
5277453645fSAndriy Voskoboinyk 	    (uintmax_t)pc->rx_ring.paddr);
5287453645fSAndriy Voskoboinyk 
5297453645fSAndriy Voskoboinyk 	/* Set Tx Configuration Register. */
5307453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_TCR, pc->tcr);
5317453645fSAndriy Voskoboinyk 
5327453645fSAndriy Voskoboinyk 	/* Configure Tx DMA. */
5337453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_BKQ_DESA,
5347453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_BK_QUEUE].paddr);
5357453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_BEQ_DESA,
5367453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_BE_QUEUE].paddr);
5377453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_VIQ_DESA,
5387453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_VI_QUEUE].paddr);
5397453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_VOQ_DESA,
5407453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_VO_QUEUE].paddr);
5417453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_BCNQ_DESA,
5427453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_BEACON_QUEUE].paddr);
5437453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_MGQ_DESA,
5447453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_MGNT_QUEUE].paddr);
5457453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_HQ_DESA,
5467453645fSAndriy Voskoboinyk 	    pc->tx_ring[RTWN_PCI_HIGH_QUEUE].paddr);
5477453645fSAndriy Voskoboinyk 
5487453645fSAndriy Voskoboinyk 	/* Configure Rx DMA. */
5497453645fSAndriy Voskoboinyk 	rtwn_pci_write_4(sc, R92C_RX_DESA, pc->rx_ring.paddr);
5507453645fSAndriy Voskoboinyk }
5517453645fSAndriy Voskoboinyk 
5527453645fSAndriy Voskoboinyk static void
rtwn_pci_beacon_update_begin(struct rtwn_softc * sc,struct ieee80211vap * vap)553d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_begin(struct rtwn_softc *sc, struct ieee80211vap *vap)
554d067ef0fSAndriy Voskoboinyk {
555d067ef0fSAndriy Voskoboinyk 	struct rtwn_vap *rvp = RTWN_VAP(vap);
556d067ef0fSAndriy Voskoboinyk 
557d067ef0fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
558d067ef0fSAndriy Voskoboinyk 
559d067ef0fSAndriy Voskoboinyk 	rtwn_beacon_enable(sc, rvp->id, 0);
560d067ef0fSAndriy Voskoboinyk }
561d067ef0fSAndriy Voskoboinyk 
562d067ef0fSAndriy Voskoboinyk static void
rtwn_pci_beacon_update_end(struct rtwn_softc * sc,struct ieee80211vap * vap)563d067ef0fSAndriy Voskoboinyk rtwn_pci_beacon_update_end(struct rtwn_softc *sc, struct ieee80211vap *vap)
564d067ef0fSAndriy Voskoboinyk {
565d067ef0fSAndriy Voskoboinyk 	struct rtwn_vap *rvp = RTWN_VAP(vap);
566d067ef0fSAndriy Voskoboinyk 
567d067ef0fSAndriy Voskoboinyk 	RTWN_ASSERT_LOCKED(sc);
568d067ef0fSAndriy Voskoboinyk 
569d067ef0fSAndriy Voskoboinyk 	if (rvp->curr_mode != R92C_MSR_NOLINK)
570d067ef0fSAndriy Voskoboinyk 		rtwn_beacon_enable(sc, rvp->id, 1);
571d067ef0fSAndriy Voskoboinyk }
572d067ef0fSAndriy Voskoboinyk 
573d067ef0fSAndriy Voskoboinyk static void
rtwn_pci_attach_methods(struct rtwn_softc * sc)5747453645fSAndriy Voskoboinyk rtwn_pci_attach_methods(struct rtwn_softc *sc)
5757453645fSAndriy Voskoboinyk {
5767453645fSAndriy Voskoboinyk 	sc->sc_write_1		= rtwn_pci_write_1;
5777453645fSAndriy Voskoboinyk 	sc->sc_write_2		= rtwn_pci_write_2;
5787453645fSAndriy Voskoboinyk 	sc->sc_write_4		= rtwn_pci_write_4;
5797453645fSAndriy Voskoboinyk 	sc->sc_read_1		= rtwn_pci_read_1;
5807453645fSAndriy Voskoboinyk 	sc->sc_read_2		= rtwn_pci_read_2;
5817453645fSAndriy Voskoboinyk 	sc->sc_read_4		= rtwn_pci_read_4;
5827453645fSAndriy Voskoboinyk 	sc->sc_delay		= rtwn_pci_delay;
5837453645fSAndriy Voskoboinyk 	sc->sc_tx_start		= rtwn_pci_tx_start;
5847453645fSAndriy Voskoboinyk 	sc->sc_reset_lists	= rtwn_pci_reset_lists;
5857453645fSAndriy Voskoboinyk 	sc->sc_abort_xfers	= rtwn_nop_softc;
5867453645fSAndriy Voskoboinyk 	sc->sc_fw_write_block	= rtwn_pci_fw_write_block;
5877453645fSAndriy Voskoboinyk 	sc->sc_get_qmap		= rtwn_pci_get_qmap;
5887453645fSAndriy Voskoboinyk 	sc->sc_set_desc_addr	= rtwn_pci_set_desc_addr;
5897453645fSAndriy Voskoboinyk 	sc->sc_drop_incorrect_tx = rtwn_nop_softc;
590d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_update_begin = rtwn_pci_beacon_update_begin;
591d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_update_end = rtwn_pci_beacon_update_end;
592d067ef0fSAndriy Voskoboinyk 	sc->sc_beacon_unload	= rtwn_pci_reset_beacon_ring;
593d067ef0fSAndriy Voskoboinyk 
594d067ef0fSAndriy Voskoboinyk 	sc->bcn_check_interval	= 25000;
5957453645fSAndriy Voskoboinyk }
5967453645fSAndriy Voskoboinyk 
5977453645fSAndriy Voskoboinyk static int
rtwn_pci_attach(device_t dev)5987453645fSAndriy Voskoboinyk rtwn_pci_attach(device_t dev)
5997453645fSAndriy Voskoboinyk {
600ae60d856SAndriy Voskoboinyk 	const struct rtwn_pci_ident *ident;
6017453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = device_get_softc(dev);
6027453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &pc->pc_sc;
6037453645fSAndriy Voskoboinyk 	struct ieee80211com *ic = &sc->sc_ic;
6047453645fSAndriy Voskoboinyk 	uint32_t lcsr;
6057453645fSAndriy Voskoboinyk 	int cap_off, i, error, rid;
6067453645fSAndriy Voskoboinyk 
607ae60d856SAndriy Voskoboinyk 	ident = rtwn_pci_probe_sub(dev);
608ae60d856SAndriy Voskoboinyk 	if (ident == NULL)
6097453645fSAndriy Voskoboinyk 		return (ENXIO);
6107453645fSAndriy Voskoboinyk 
6117453645fSAndriy Voskoboinyk 	/*
6127453645fSAndriy Voskoboinyk 	 * Get the offset of the PCI Express Capability Structure in PCI
6137453645fSAndriy Voskoboinyk 	 * Configuration Space.
6147453645fSAndriy Voskoboinyk 	 */
6157453645fSAndriy Voskoboinyk 	error = pci_find_cap(dev, PCIY_EXPRESS, &cap_off);
6167453645fSAndriy Voskoboinyk 	if (error != 0) {
6177453645fSAndriy Voskoboinyk 		device_printf(dev, "PCIe capability structure not found!\n");
6187453645fSAndriy Voskoboinyk 		return (error);
6197453645fSAndriy Voskoboinyk 	}
6207453645fSAndriy Voskoboinyk 
6217453645fSAndriy Voskoboinyk 	/* Enable bus-mastering. */
6227453645fSAndriy Voskoboinyk 	pci_enable_busmaster(dev);
6237453645fSAndriy Voskoboinyk 
6247453645fSAndriy Voskoboinyk 	rid = PCIR_BAR(2);
6257453645fSAndriy Voskoboinyk 	pc->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
6267453645fSAndriy Voskoboinyk 	    RF_ACTIVE);
6277453645fSAndriy Voskoboinyk 	if (pc->mem == NULL) {
6287453645fSAndriy Voskoboinyk 		device_printf(dev, "can't map mem space\n");
6297453645fSAndriy Voskoboinyk 		return (ENOMEM);
6307453645fSAndriy Voskoboinyk 	}
6317453645fSAndriy Voskoboinyk 	pc->pc_st = rman_get_bustag(pc->mem);
6327453645fSAndriy Voskoboinyk 	pc->pc_sh = rman_get_bushandle(pc->mem);
6337453645fSAndriy Voskoboinyk 
6347453645fSAndriy Voskoboinyk 	/* Install interrupt handler. */
6357453645fSAndriy Voskoboinyk 	rid = 1;
6367453645fSAndriy Voskoboinyk 	if (pci_alloc_msi(dev, &rid) == 0)
6377453645fSAndriy Voskoboinyk 		rid = 1;
6387453645fSAndriy Voskoboinyk 	else
6397453645fSAndriy Voskoboinyk 		rid = 0;
6407453645fSAndriy Voskoboinyk 	pc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE |
6417453645fSAndriy Voskoboinyk 	    (rid != 0 ? 0 : RF_SHAREABLE));
6427453645fSAndriy Voskoboinyk 	if (pc->irq == NULL) {
6437453645fSAndriy Voskoboinyk 		device_printf(dev, "can't map interrupt\n");
6447453645fSAndriy Voskoboinyk 		goto detach;
6457453645fSAndriy Voskoboinyk 	}
6467453645fSAndriy Voskoboinyk 
6477453645fSAndriy Voskoboinyk 	/* Disable PCIe Active State Power Management (ASPM). */
6487453645fSAndriy Voskoboinyk 	lcsr = pci_read_config(dev, cap_off + PCIER_LINK_CTL, 4);
6497453645fSAndriy Voskoboinyk 	lcsr &= ~PCIEM_LINK_CTL_ASPMC;
6507453645fSAndriy Voskoboinyk 	pci_write_config(dev, cap_off + PCIER_LINK_CTL, lcsr, 4);
6517453645fSAndriy Voskoboinyk 
6527453645fSAndriy Voskoboinyk 	sc->sc_dev = dev;
6537453645fSAndriy Voskoboinyk 	ic->ic_name = device_get_nameunit(dev);
6547453645fSAndriy Voskoboinyk 
6557453645fSAndriy Voskoboinyk 	/* Need to be initialized early. */
6567453645fSAndriy Voskoboinyk 	rtwn_sysctlattach(sc);
6577453645fSAndriy Voskoboinyk 	mtx_init(&sc->sc_mtx, ic->ic_name, MTX_NETWORK_LOCK, MTX_DEF);
6587453645fSAndriy Voskoboinyk 
6597453645fSAndriy Voskoboinyk 	rtwn_pci_attach_methods(sc);
660ae60d856SAndriy Voskoboinyk 	rtwn_pci_attach_private(pc, ident->chip);
6617453645fSAndriy Voskoboinyk 
6627453645fSAndriy Voskoboinyk 	/* Allocate Tx/Rx buffers. */
6637453645fSAndriy Voskoboinyk 	error = rtwn_pci_alloc_rx_list(sc);
6647453645fSAndriy Voskoboinyk 	if (error != 0) {
6657453645fSAndriy Voskoboinyk 		device_printf(dev,
6667453645fSAndriy Voskoboinyk 		    "could not allocate Rx buffers, error %d\n",
6677453645fSAndriy Voskoboinyk 		    error);
6687453645fSAndriy Voskoboinyk 		goto detach;
6697453645fSAndriy Voskoboinyk 	}
6707453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_NTXQUEUES; i++) {
6717453645fSAndriy Voskoboinyk 		error = rtwn_pci_alloc_tx_list(sc, i);
6727453645fSAndriy Voskoboinyk 		if (error != 0) {
6737453645fSAndriy Voskoboinyk 			device_printf(dev,
6747453645fSAndriy Voskoboinyk 			    "could not allocate Tx buffers, error %d\n",
6757453645fSAndriy Voskoboinyk 			    error);
6767453645fSAndriy Voskoboinyk 			goto detach;
6777453645fSAndriy Voskoboinyk 		}
6787453645fSAndriy Voskoboinyk 	}
6797453645fSAndriy Voskoboinyk 
6807453645fSAndriy Voskoboinyk 	/* Generic attach. */
6817453645fSAndriy Voskoboinyk 	error = rtwn_attach(sc);
6827453645fSAndriy Voskoboinyk 	if (error != 0)
6837453645fSAndriy Voskoboinyk 		goto detach;
6847453645fSAndriy Voskoboinyk 
6857453645fSAndriy Voskoboinyk 	/*
6867453645fSAndriy Voskoboinyk 	 * Hook our interrupt after all initialization is complete.
6877453645fSAndriy Voskoboinyk 	 */
6887453645fSAndriy Voskoboinyk 	error = bus_setup_intr(dev, pc->irq, INTR_TYPE_NET | INTR_MPSAFE,
6897453645fSAndriy Voskoboinyk 	    NULL, rtwn_pci_intr, sc, &pc->pc_ih);
6907453645fSAndriy Voskoboinyk 	if (error != 0) {
6917453645fSAndriy Voskoboinyk 		device_printf(dev, "can't establish interrupt, error %d\n",
6927453645fSAndriy Voskoboinyk 		    error);
6937453645fSAndriy Voskoboinyk 		goto detach;
6947453645fSAndriy Voskoboinyk 	}
6957453645fSAndriy Voskoboinyk 
6967453645fSAndriy Voskoboinyk 	return (0);
6977453645fSAndriy Voskoboinyk 
6987453645fSAndriy Voskoboinyk detach:
6997453645fSAndriy Voskoboinyk 	rtwn_pci_detach(dev);		/* failure */
7007453645fSAndriy Voskoboinyk 	return (ENXIO);
7017453645fSAndriy Voskoboinyk }
7027453645fSAndriy Voskoboinyk 
7037453645fSAndriy Voskoboinyk static int
rtwn_pci_detach(device_t dev)7047453645fSAndriy Voskoboinyk rtwn_pci_detach(device_t dev)
7057453645fSAndriy Voskoboinyk {
7067453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = device_get_softc(dev);
7077453645fSAndriy Voskoboinyk 	struct rtwn_softc *sc = &pc->pc_sc;
7087453645fSAndriy Voskoboinyk 	int i;
7097453645fSAndriy Voskoboinyk 
7107453645fSAndriy Voskoboinyk 	/* Generic detach. */
7117453645fSAndriy Voskoboinyk 	rtwn_detach(sc);
7127453645fSAndriy Voskoboinyk 
7137453645fSAndriy Voskoboinyk 	/* Uninstall interrupt handler. */
7147453645fSAndriy Voskoboinyk 	if (pc->irq != NULL) {
7157453645fSAndriy Voskoboinyk 		bus_teardown_intr(dev, pc->irq, pc->pc_ih);
7167453645fSAndriy Voskoboinyk 		bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(pc->irq),
7177453645fSAndriy Voskoboinyk 		    pc->irq);
7187453645fSAndriy Voskoboinyk 		pci_release_msi(dev);
7197453645fSAndriy Voskoboinyk 	}
7207453645fSAndriy Voskoboinyk 
7217453645fSAndriy Voskoboinyk 	/* Free Tx/Rx buffers. */
7227453645fSAndriy Voskoboinyk 	for (i = 0; i < RTWN_PCI_NTXQUEUES; i++)
7237453645fSAndriy Voskoboinyk 		rtwn_pci_free_tx_list(sc, i);
7247453645fSAndriy Voskoboinyk 	rtwn_pci_free_rx_list(sc);
7257453645fSAndriy Voskoboinyk 
7267453645fSAndriy Voskoboinyk 	if (pc->mem != NULL)
7277453645fSAndriy Voskoboinyk 		bus_release_resource(dev, SYS_RES_MEMORY,
7287453645fSAndriy Voskoboinyk 		    rman_get_rid(pc->mem), pc->mem);
7297453645fSAndriy Voskoboinyk 
7307453645fSAndriy Voskoboinyk 	rtwn_detach_private(sc);
7317453645fSAndriy Voskoboinyk 	mtx_destroy(&sc->sc_mtx);
7327453645fSAndriy Voskoboinyk 
7337453645fSAndriy Voskoboinyk 	return (0);
7347453645fSAndriy Voskoboinyk }
7357453645fSAndriy Voskoboinyk 
7367453645fSAndriy Voskoboinyk static int
rtwn_pci_shutdown(device_t self)7377453645fSAndriy Voskoboinyk rtwn_pci_shutdown(device_t self)
7387453645fSAndriy Voskoboinyk {
7397453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = device_get_softc(self);
7407453645fSAndriy Voskoboinyk 
7417453645fSAndriy Voskoboinyk 	ieee80211_stop_all(&pc->pc_sc.sc_ic);
7427453645fSAndriy Voskoboinyk 	return (0);
7437453645fSAndriy Voskoboinyk }
7447453645fSAndriy Voskoboinyk 
7457453645fSAndriy Voskoboinyk static int
rtwn_pci_suspend(device_t self)7467453645fSAndriy Voskoboinyk rtwn_pci_suspend(device_t self)
7477453645fSAndriy Voskoboinyk {
7487453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = device_get_softc(self);
7497453645fSAndriy Voskoboinyk 
7507453645fSAndriy Voskoboinyk 	rtwn_suspend(&pc->pc_sc);
7517453645fSAndriy Voskoboinyk 
7527453645fSAndriy Voskoboinyk 	return (0);
7537453645fSAndriy Voskoboinyk }
7547453645fSAndriy Voskoboinyk 
7557453645fSAndriy Voskoboinyk static int
rtwn_pci_resume(device_t self)7567453645fSAndriy Voskoboinyk rtwn_pci_resume(device_t self)
7577453645fSAndriy Voskoboinyk {
7587453645fSAndriy Voskoboinyk 	struct rtwn_pci_softc *pc = device_get_softc(self);
7597453645fSAndriy Voskoboinyk 
7607453645fSAndriy Voskoboinyk 	rtwn_resume(&pc->pc_sc);
7617453645fSAndriy Voskoboinyk 
7627453645fSAndriy Voskoboinyk 	return (0);
7637453645fSAndriy Voskoboinyk }
7647453645fSAndriy Voskoboinyk 
7657453645fSAndriy Voskoboinyk static device_method_t rtwn_pci_methods[] = {
7667453645fSAndriy Voskoboinyk 	/* Device interface */
7677453645fSAndriy Voskoboinyk 	DEVMETHOD(device_probe,		rtwn_pci_probe),
7687453645fSAndriy Voskoboinyk 	DEVMETHOD(device_attach,	rtwn_pci_attach),
7697453645fSAndriy Voskoboinyk 	DEVMETHOD(device_detach,	rtwn_pci_detach),
7707453645fSAndriy Voskoboinyk 	DEVMETHOD(device_shutdown,	rtwn_pci_shutdown),
7717453645fSAndriy Voskoboinyk 	DEVMETHOD(device_suspend,	rtwn_pci_suspend),
7727453645fSAndriy Voskoboinyk 	DEVMETHOD(device_resume,	rtwn_pci_resume),
7737453645fSAndriy Voskoboinyk 
7747453645fSAndriy Voskoboinyk 	DEVMETHOD_END
7757453645fSAndriy Voskoboinyk };
7767453645fSAndriy Voskoboinyk 
7777453645fSAndriy Voskoboinyk static driver_t rtwn_pci_driver = {
7787453645fSAndriy Voskoboinyk 	"rtwn",
7797453645fSAndriy Voskoboinyk 	rtwn_pci_methods,
7807453645fSAndriy Voskoboinyk 	sizeof(struct rtwn_pci_softc)
7817453645fSAndriy Voskoboinyk };
7827453645fSAndriy Voskoboinyk 
783*1bbc46a8SJohn Baldwin DRIVER_MODULE(rtwn_pci, pci, rtwn_pci_driver, NULL, NULL);
7847453645fSAndriy Voskoboinyk MODULE_VERSION(rtwn_pci, 1);
78528180b62SHiroki Sato MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, rtwn, rtwn_pci_ident_table,
78628180b62SHiroki Sato     nitems(rtwn_pci_ident_table));
7877453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, pci, 1, 1, 1);
7887453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, wlan, 1, 1, 1);
7897453645fSAndriy Voskoboinyk MODULE_DEPEND(rtwn_pci, rtwn, 2, 2, 2);
790